diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 45a05499..4a8c97ca 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ mac-builder: - build/install-x64/* script: - 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 -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.14.sdk" -D"CMAKE_OSX_DEPLOYMENT_TARGET=10.9" - cmake --build build -j 9 - cmake --install build - PROJECT_VERSION=$(grep -E '^set\(PROJECT_VERSION_FULL "(.*)' CMakeLists.txt | awk '{print $2}' | tr -d '")') @@ -99,7 +99,7 @@ trigger-pipeline: stage: trigger-libopenshot script: - "curl -X POST -F token=$LIBOPENSHOT_PIPELINE_TOKEN -F ref=$CI_COMMIT_REF_NAME http://gitlab.openshot.org/api/v4/projects/1/trigger/pipeline" - when: always + when: on_success dependencies: [] except: - tags diff --git a/CMakeLists.txt b/CMakeLists.txt index b98125d7..8bf9978a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,11 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules") set(PROJECT_VERSION_FULL "0.3.3") set(PROJECT_SO_VERSION 9) +#### Set C++ standard level +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + # Remove the dash and anything following, to get the #.#.# version for project() string(REGEX REPLACE "\-.*$" "" VERSION_NUM "${PROJECT_VERSION_FULL}") @@ -240,6 +245,7 @@ if(APPLE) "-framework AudioToolbox" "-framework QuartzCore" "-framework Accelerate" + "-framework Security" "-lobjc" ) target_compile_options(openshot-audio PRIVATE diff --git a/JuceLibraryCode/AppConfig.h b/JuceLibraryCode/AppConfig.h index 41381940..a11b9d66 100644 --- a/JuceLibraryCode/AppConfig.h +++ b/JuceLibraryCode/AppConfig.h @@ -23,12 +23,12 @@ /* ============================================================================== - In accordance with the terms of the JUCE 5 End-Use License Agreement, the + In accordance with the terms of the JUCE 7 End-Use License Agreement, the JUCE Code in SECTION A cannot be removed, changed or otherwise rendered ineffective unless you have a JUCE Indie or Pro license, or are using JUCE under the GPL v3 license. - End User License Agreement: www.juce.com/juce-5-licence + End User License Agreement: www.juce.com/juce-7-licence ============================================================================== */ @@ -36,18 +36,14 @@ // BEGIN SECTION A #ifndef JUCE_DISPLAY_SPLASH_SCREEN - #define JUCE_DISPLAY_SPLASH_SCREEN 0 -#endif - -#ifndef JUCE_REPORT_APP_USAGE - #define JUCE_REPORT_APP_USAGE 0 + #define JUCE_DISPLAY_SPLASH_SCREEN 1 #endif // END SECTION A #define JUCE_USE_DARK_SPLASH_SCREEN 1 -#define JUCE_PROJUCER_VERSION 0x50407 +#define JUCE_PROJUCER_VERSION 0x7000a //============================================================================== #define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 @@ -68,17 +64,13 @@ #endif #ifndef JUCE_ASIO - //#define JUCE_ASIO 0 + #define JUCE_ASIO 1 #endif #ifndef JUCE_WASAPI //#define JUCE_WASAPI 1 #endif -#ifndef JUCE_WASAPI_EXCLUSIVE - //#define JUCE_WASAPI_EXCLUSIVE 0 -#endif - #ifndef JUCE_DIRECTSOUND //#define JUCE_DIRECTSOUND 1 #endif @@ -96,7 +88,11 @@ #endif #ifndef JUCE_USE_ANDROID_OBOE - //#define JUCE_USE_ANDROID_OBOE 0 + //#define JUCE_USE_ANDROID_OBOE 1 +#endif + +#ifndef JUCE_USE_OBOE_STABILIZED_CALLBACK + //#define JUCE_USE_OBOE_STABILIZED_CALLBACK 0 #endif #ifndef JUCE_USE_ANDROID_OPENSLES @@ -173,6 +169,10 @@ //#define JUCE_STRICT_REFCOUNTEDPOINTER 0 #endif +#ifndef JUCE_ENABLE_ALLOCATION_HOOKS + //#define JUCE_ENABLE_ALLOCATION_HOOKS 0 +#endif + //============================================================================== // juce_dsp flags: diff --git a/JuceLibraryCode/JuceHeader.h b/JuceLibraryCode/JuceHeader.h index 240dc0a0..7c9ab48d 100644 --- a/JuceLibraryCode/JuceHeader.h +++ b/JuceLibraryCode/JuceHeader.h @@ -27,7 +27,7 @@ /** 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. + remove the JUCE_PROJUCER_VERSION define. */ #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 diff --git a/JuceLibraryCode/THESE_HEADERS_ARE_IGNORED b/JuceLibraryCode/THESE_HEADERS_ARE_IGNORED deleted file mode 100644 index 12fdcccb..00000000 --- a/JuceLibraryCode/THESE_HEADERS_ARE_IGNORED +++ /dev/null @@ -1 +0,0 @@ -See ../include/*.h.in, template files for the headers generated by CMake diff --git a/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.cpp b/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.cpp new file mode 100644 index 00000000..4be6508a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.cpp @@ -0,0 +1,31 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +bool AudioPlayHead::canControlTransport() { return false; } +void AudioPlayHead::transportPlay ([[maybe_unused]] bool shouldStartPlaying) {} +void AudioPlayHead::transportRecord ([[maybe_unused]] bool shouldStartRecording) {} +void AudioPlayHead::transportRewind() {} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h b/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h index 25ebba17..10f4364e 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h +++ b/JuceLibraryCode/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -60,29 +60,174 @@ class JUCE_API AudioPlayHead fpsUnknown = 99 }; + /** More descriptive frame rate type. */ + class JUCE_API FrameRate + { + public: + /** Creates a frame rate with a base rate of 0. */ + FrameRate() = default; + + /** Creates a FrameRate instance from a FrameRateType. */ + FrameRate (FrameRateType type) : FrameRate (fromType (type)) {} + + /** Gets the FrameRateType that matches the state of this FrameRate. + + Returns fpsUnknown if this FrameRate cannot be represented by any of the + other enum fields. + */ + FrameRateType getType() const + { + switch (base) + { + case 24: return pulldown ? fps23976 : fps24; + case 25: return fps25; + case 30: return pulldown ? (drop ? fps2997drop : fps2997) + : (drop ? fps30drop : fps30); + case 60: return drop ? fps60drop : fps60; + } + + return fpsUnknown; + } + + /** Returns the plain rate, without taking pulldown into account. */ + int getBaseRate() const { return base; } + + /** Returns true if drop-frame timecode is in use. */ + bool isDrop() const { return drop; } + + /** Returns true if the effective framerate is actually equal to the base rate divided by 1.001 */ + bool isPullDown() const { return pulldown; } + + /** Returns the actual rate described by this object, taking pulldown into account. */ + double getEffectiveRate() const { return pulldown ? (double) base / 1.001 : (double) base; } + + /** Returns a copy of this object with the specified base rate. */ + [[nodiscard]] FrameRate withBaseRate (int x) const { return with (&FrameRate::base, x); } + + /** Returns a copy of this object with drop frames enabled or disabled, as specified. */ + [[nodiscard]] FrameRate withDrop (bool x = true) const { return with (&FrameRate::drop, x); } + + /** Returns a copy of this object with pulldown enabled or disabled, as specified. */ + [[nodiscard]] FrameRate withPullDown (bool x = true) const { return with (&FrameRate::pulldown, x); } + + /** Returns true if this instance is equal to other. */ + bool operator== (const FrameRate& other) const + { + const auto tie = [] (const FrameRate& x) { return std::tie (x.base, x.drop, x.pulldown); }; + return tie (*this) == tie (other); + } + + /** Returns true if this instance is not equal to other. */ + bool operator!= (const FrameRate& other) const { return ! (*this == other); } + + private: + static FrameRate fromType (FrameRateType type) + { + switch (type) + { + case fps23976: return FrameRate().withBaseRate (24).withPullDown(); + case fps24: return FrameRate().withBaseRate (24); + case fps25: return FrameRate().withBaseRate (25); + case fps2997: return FrameRate().withBaseRate (30).withPullDown(); + case fps30: return FrameRate().withBaseRate (30); + case fps2997drop: return FrameRate().withBaseRate (30).withDrop().withPullDown(); + case fps30drop: return FrameRate().withBaseRate (30).withDrop(); + case fps60: return FrameRate().withBaseRate (60); + case fps60drop: return FrameRate().withBaseRate (60).withDrop(); + case fpsUnknown: break; + } + + return {}; + } + + template + FrameRate with (Member&& member, Value&& value) const + { + auto copy = *this; + copy.*member = std::forward (value); + return copy; + } + + int base = 0; + bool drop = false, pulldown = false; + }; + + /** Describes a musical time signature. + + @see PositionInfo::getTimeSignature() PositionInfo::setTimeSignature() + */ + struct JUCE_API TimeSignature + { + /** Time signature numerator, e.g. the 3 of a 3/4 time sig */ + int numerator = 4; + + /** Time signature denominator, e.g. the 4 of a 3/4 time sig */ + int denominator = 4; + + bool operator== (const TimeSignature& other) const + { + const auto tie = [] (auto& x) { return std::tie (x.numerator, x.denominator); }; + return tie (*this) == tie (other); + } + + bool operator!= (const TimeSignature& other) const + { + return ! operator== (other); + } + }; + + /** Holds the begin and end points of a looped region. + + @see PositionInfo::getIsLooping() PositionInfo::setIsLooping() PositionInfo::getLoopPoints() PositionInfo::setLoopPoints() + */ + struct JUCE_API LoopPoints + { + /** The current cycle start position in units of quarter-notes. */ + double ppqStart = 0; + + /** The current cycle end position in units of quarter-notes. */ + double ppqEnd = 0; + + bool operator== (const LoopPoints& other) const + { + const auto tie = [] (auto& x) { return std::tie (x.ppqStart, x.ppqEnd); }; + return tie (*this) == tie (other); + } + + bool operator!= (const LoopPoints& other) const + { + return ! operator== (other); + } + }; + //============================================================================== - /** This structure is filled-in by the AudioPlayHead::getCurrentPosition() method. + /** This type is deprecated; prefer PositionInfo instead. + + Some position info may be unavailable, depending on the host or plugin format. + Unfortunately, CurrentPositionInfo doesn't have any way of differentiating between + default values and values that have been set explicitly. */ struct JUCE_API CurrentPositionInfo { /** The tempo in BPM */ - double bpm; + double bpm = 120.0; /** Time signature numerator, e.g. the 3 of a 3/4 time sig */ - int timeSigNumerator; + int timeSigNumerator = 4; + /** Time signature denominator, e.g. the 4 of a 3/4 time sig */ - int timeSigDenominator; + int timeSigDenominator = 4; /** The current play position, in samples from the start of the timeline. */ - int64 timeInSamples; + int64 timeInSamples = 0; /** The current play position, in seconds from the start of the timeline. */ - double timeInSeconds; + double timeInSeconds = 0; /** For timecode, the position of the start of the timeline, in seconds from 00:00:00:00. */ - double editOriginTime; + double editOriginTime = 0; /** The current play position, in units of quarter-notes. */ - double ppqPosition; + double ppqPosition = 0; /** The position of the start of the last bar, in units of quarter-notes. @@ -92,44 +237,272 @@ class JUCE_API AudioPlayHead Note - this value may be unavailable on some hosts, e.g. Pro-Tools. If it's not available, the value will be 0. */ - double ppqPositionOfLastBarStart; + double ppqPositionOfLastBarStart = 0; /** The video frame rate, if applicable. */ - FrameRateType frameRate; + FrameRate frameRate = FrameRateType::fps23976; /** True if the transport is currently playing. */ - bool isPlaying; + bool isPlaying = false; /** True if the transport is currently recording. (When isRecording is true, then isPlaying will also be true). */ - bool isRecording; + bool isRecording = false; /** The current cycle start position in units of quarter-notes. Note that not all hosts or plugin formats may provide this value. @see isLooping */ - double ppqLoopStart; + double ppqLoopStart = 0; /** The current cycle end position in units of quarter-notes. Note that not all hosts or plugin formats may provide this value. @see isLooping */ - double ppqLoopEnd; + double ppqLoopEnd = 0; /** True if the transport is currently looping. */ - bool isLooping; + bool isLooping = false; //============================================================================== - bool operator== (const CurrentPositionInfo& other) const noexcept; - bool operator!= (const CurrentPositionInfo& other) const noexcept; + bool operator== (const CurrentPositionInfo& other) const noexcept + { + const auto tie = [] (const CurrentPositionInfo& i) + { + return std::tie (i.timeInSamples, + i.ppqPosition, + i.editOriginTime, + i.ppqPositionOfLastBarStart, + i.frameRate, + i.isPlaying, + i.isRecording, + i.bpm, + i.timeSigNumerator, + i.timeSigDenominator, + i.ppqLoopStart, + i.ppqLoopEnd, + i.isLooping); + }; + + return tie (*this) == tie (other); + } + + bool operator!= (const CurrentPositionInfo& other) const noexcept + { + return ! operator== (other); + } + + void resetToDefault() + { + *this = CurrentPositionInfo{}; + } + }; + + //============================================================================== + /** + Describes the time at the start of the current audio callback. + + Not all hosts and plugin formats can provide all of the possible time + information, so most of the getter functions in this class return + an Optional that will only be engaged if the host provides the corresponding + information. As a plugin developer, you should code defensively so that + the plugin behaves sensibly even when the host fails to provide timing + information. + + A default-constructed instance of this class will return nullopt from + all functions that return an Optional. + */ + class PositionInfo + { + public: + /** Returns the number of samples that have elapsed. */ + Optional getTimeInSamples() const { return getOptional (flagTimeSamples, timeInSamples); } + + /** @see getTimeInSamples() */ + void setTimeInSamples (Optional timeInSamplesIn) { setOptional (flagTimeSamples, timeInSamples, timeInSamplesIn); } + + /** Returns the number of seconds that have elapsed. */ + Optional getTimeInSeconds() const { return getOptional (flagTimeSeconds, timeInSeconds); } + + /** @see getTimeInSamples() */ + void setTimeInSeconds (Optional timeInSecondsIn) { setOptional (flagTimeSeconds, timeInSeconds, timeInSecondsIn); } + + /** Returns the bpm, if available. */ + Optional getBpm() const { return getOptional (flagTempo, tempoBpm); } + + /** @see getBpm() */ + void setBpm (Optional bpmIn) { setOptional (flagTempo, tempoBpm, bpmIn); } + + /** Returns the time signature, if available. */ + Optional getTimeSignature() const { return getOptional (flagTimeSignature, timeSignature); } + + /** @see getTimeSignature() */ + void setTimeSignature (Optional timeSignatureIn) { setOptional (flagTimeSignature, timeSignature, timeSignatureIn); } + + /** Returns host loop points, if available. */ + Optional getLoopPoints() const { return getOptional (flagLoopPoints, loopPoints); } + + /** @see getLoopPoints() */ + void setLoopPoints (Optional loopPointsIn) { setOptional (flagLoopPoints, loopPoints, loopPointsIn); } + + /** The number of bars since the beginning of the timeline. + + This value isn't available in all hosts or in all plugin formats. + */ + Optional getBarCount() const { return getOptional (flagBarCount, barCount); } + + /** @see getBarCount() */ + void setBarCount (Optional barCountIn) { setOptional (flagBarCount, barCount, barCountIn); } + + /** The position of the start of the last bar, in units of quarter-notes. + + This is the time from the start of the timeline to the start of the current + bar, in ppq units. - void resetToDefault(); + Note - this value may be unavailable on some hosts, e.g. Pro-Tools. + */ + Optional getPpqPositionOfLastBarStart() const { return getOptional (flagLastBarStartPpq, lastBarStartPpq); } + + /** @see getPpqPositionOfLastBarStart() */ + void setPpqPositionOfLastBarStart (Optional positionIn) { setOptional (flagLastBarStartPpq, lastBarStartPpq, positionIn); } + + /** The video frame rate, if available. */ + Optional getFrameRate() const { return getOptional (flagFrameRate, frame); } + + /** @see getFrameRate() */ + void setFrameRate (Optional frameRateIn) { setOptional (flagFrameRate, frame, frameRateIn); } + + /** The current play position, in units of quarter-notes. */ + Optional getPpqPosition() const { return getOptional (flagPpqPosition, positionPpq); } + + /** @see getPpqPosition() */ + void setPpqPosition (Optional ppqPositionIn) { setOptional (flagPpqPosition, positionPpq, ppqPositionIn); } + + /** For timecode, the position of the start of the timeline, in seconds from 00:00:00:00. */ + Optional getEditOriginTime() const { return getOptional (flagOriginTime, originTime); } + + /** @see getEditOriginTime() */ + void setEditOriginTime (Optional editOriginTimeIn) { setOptional (flagOriginTime, originTime, editOriginTimeIn); } + + /** Get the host's callback time in nanoseconds, if available. */ + Optional getHostTimeNs() const { return getOptional (flagHostTimeNs, hostTimeNs); } + + /** @see getHostTimeNs() */ + void setHostTimeNs (Optional hostTimeNsIn) { setOptional (flagHostTimeNs, hostTimeNs, hostTimeNsIn); } + + /** True if the transport is currently playing. */ + bool getIsPlaying() const { return getFlag (flagIsPlaying); } + + /** @see getIsPlaying() */ + void setIsPlaying (bool isPlayingIn) { setFlag (flagIsPlaying, isPlayingIn); } + + /** True if the transport is currently recording. + + (When isRecording is true, then isPlaying will also be true). + */ + bool getIsRecording() const { return getFlag (flagIsRecording); } + + /** @see getIsRecording() */ + void setIsRecording (bool isRecordingIn) { setFlag (flagIsRecording, isRecordingIn); } + + /** True if the transport is currently looping. */ + bool getIsLooping() const { return getFlag (flagIsLooping); } + + /** @see getIsLooping() */ + void setIsLooping (bool isLoopingIn) { setFlag (flagIsLooping, isLoopingIn); } + + bool operator== (const PositionInfo& other) const noexcept + { + const auto tie = [] (const PositionInfo& i) + { + return std::make_tuple (i.getTimeInSamples(), + i.getTimeInSeconds(), + i.getPpqPosition(), + i.getEditOriginTime(), + i.getPpqPositionOfLastBarStart(), + i.getFrameRate(), + i.getBarCount(), + i.getTimeSignature(), + i.getBpm(), + i.getLoopPoints(), + i.getHostTimeNs(), + i.getIsPlaying(), + i.getIsRecording(), + i.getIsLooping()); + }; + + return tie (*this) == tie (other); + } + + bool operator!= (const PositionInfo& other) const noexcept + { + return ! operator== (other); + } + + private: + bool getFlag (int64_t flagToCheck) const + { + return (flagToCheck & flags) != 0; + } + + void setFlag (int64_t flagToCheck, bool value) + { + flags = (value ? flags | flagToCheck : flags & ~flagToCheck); + } + + template + Optional getOptional (int64_t flagToCheck, Value value) const + { + return getFlag (flagToCheck) ? makeOptional (std::move (value)) : nullopt; + } + + template + void setOptional (int64_t flagToCheck, Value& value, Optional opt) + { + if (opt.hasValue()) + value = *opt; + + setFlag (flagToCheck, opt.hasValue()); + } + + enum + { + flagTimeSignature = 1 << 0, + flagLoopPoints = 1 << 1, + flagFrameRate = 1 << 2, + flagTimeSeconds = 1 << 3, + flagLastBarStartPpq = 1 << 4, + flagPpqPosition = 1 << 5, + flagOriginTime = 1 << 6, + flagTempo = 1 << 7, + flagTimeSamples = 1 << 8, + flagBarCount = 1 << 9, + flagHostTimeNs = 1 << 10, + flagIsPlaying = 1 << 11, + flagIsRecording = 1 << 12, + flagIsLooping = 1 << 13 + }; + + TimeSignature timeSignature; + LoopPoints loopPoints; + FrameRate frame = FrameRateType::fps23976; + double timeInSeconds = 0.0; + double lastBarStartPpq = 0.0; + double positionPpq = 0.0; + double originTime = 0.0; + double tempoBpm = 0.0; + int64_t timeInSamples = 0; + int64_t barCount = 0; + uint64_t hostTimeNs = 0; + int64_t flags = 0; }; //============================================================================== - /** Fills-in the given structure with details about the transport's + /** Deprecated, use getPosition() instead. + + Fills-in the given structure with details about the transport's position at the start of the current processing block. If this method returns false then the current play head position is not available and the given structure will be undefined. @@ -139,19 +512,82 @@ class JUCE_API AudioPlayHead in which a time would make sense, and some hosts will almost certainly have multithreading issues if it's not called on the audio thread. */ - virtual bool getCurrentPosition (CurrentPositionInfo& result) = 0; + [[deprecated ("Use getPosition instead. Not all hosts are able to provide all time position information; getPosition differentiates clearly between set and unset fields.")]] + bool getCurrentPosition (CurrentPositionInfo& result) + { + if (const auto pos = getPosition()) + { + result.resetToDefault(); + + if (const auto sig = pos->getTimeSignature()) + { + result.timeSigNumerator = sig->numerator; + result.timeSigDenominator = sig->denominator; + } + + if (const auto loop = pos->getLoopPoints()) + { + result.ppqLoopStart = loop->ppqStart; + result.ppqLoopEnd = loop->ppqEnd; + } + + if (const auto frame = pos->getFrameRate()) + result.frameRate = *frame; + + if (const auto timeInSeconds = pos->getTimeInSeconds()) + result.timeInSeconds = *timeInSeconds; + + if (const auto lastBarStartPpq = pos->getPpqPositionOfLastBarStart()) + result.ppqPositionOfLastBarStart = *lastBarStartPpq; + + if (const auto ppqPosition = pos->getPpqPosition()) + result.ppqPosition = *ppqPosition; + + if (const auto originTime = pos->getEditOriginTime()) + result.editOriginTime = *originTime; + + if (const auto bpm = pos->getBpm()) + result.bpm = *bpm; + + if (const auto timeInSamples = pos->getTimeInSamples()) + result.timeInSamples = *timeInSamples; + + result.isPlaying = pos->getIsPlaying(); + result.isRecording = pos->getIsRecording(); + result.isLooping = pos->getIsLooping(); + + return true; + } + + return false; + } + + /** Fetches details about the transport's position at the start of the current + processing block. If this method returns nullopt then the current play head + position is not available. + + A non-null return value just indicates that the host was able to provide + *some* relevant timing information. Individual PositionInfo getters may + still return nullopt. + + You can ONLY call this from your processBlock() method! Calling it at other + times will produce undefined behaviour, as the host may not have any context + in which a time would make sense, and some hosts will almost certainly have + multithreading issues if it's not called on the audio thread. + */ + virtual Optional getPosition() const = 0; /** Returns true if this object can control the transport. */ - virtual bool canControlTransport() { return false; } + virtual bool canControlTransport(); /** Starts or stops the audio. */ - virtual void transportPlay (bool shouldStartPlaying) { ignoreUnused (shouldStartPlaying); } + virtual void transportPlay (bool shouldStartPlaying); /** Starts or stops recording the audio. */ - virtual void transportRecord (bool shouldStartRecording) { ignoreUnused (shouldStartRecording); } + virtual void transportRecord (bool shouldStartRecording); /** Rewinds the audio. */ - virtual void transportRewind() {} + virtual void transportRewind(); }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp index 195d7861..e6993b1b 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -29,7 +29,7 @@ AudioChannelSet::AudioChannelSet (uint32 c) : channels (static_cast (c)) { } -AudioChannelSet::AudioChannelSet (const Array& c) +AudioChannelSet::AudioChannelSet (const std::initializer_list& c) { for (auto channel : c) addChannel (channel); @@ -46,56 +46,107 @@ String AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type) switch (type) { - case left: return NEEDS_TRANS("Left"); - case right: return NEEDS_TRANS("Right"); - case centre: return NEEDS_TRANS("Centre"); - case LFE: return NEEDS_TRANS("LFE"); - case leftSurround: return NEEDS_TRANS("Left Surround"); - case rightSurround: return NEEDS_TRANS("Right Surround"); - case leftCentre: return NEEDS_TRANS("Left Centre"); - case rightCentre: return NEEDS_TRANS("Right Centre"); - case centreSurround: return NEEDS_TRANS("Centre Surround"); - case leftSurroundRear: return NEEDS_TRANS("Left Surround Rear"); - case rightSurroundRear: return NEEDS_TRANS("Right Surround Rear"); - case topMiddle: return NEEDS_TRANS("Top Middle"); - case topFrontLeft: return NEEDS_TRANS("Top Front Left"); - case topFrontCentre: return NEEDS_TRANS("Top Front Centre"); - case topFrontRight: return NEEDS_TRANS("Top Front Right"); - case topRearLeft: return NEEDS_TRANS("Top Rear Left"); - case topRearCentre: return NEEDS_TRANS("Top Rear Centre"); - case topRearRight: return NEEDS_TRANS("Top Rear Right"); - case wideLeft: return NEEDS_TRANS("Wide Left"); - case wideRight: return NEEDS_TRANS("Wide Right"); - case LFE2: return NEEDS_TRANS("LFE 2"); - case leftSurroundSide: return NEEDS_TRANS("Left Surround Side"); - case rightSurroundSide: return NEEDS_TRANS("Right Surround Side"); - case ambisonicW: return NEEDS_TRANS("Ambisonic W"); - case ambisonicX: return NEEDS_TRANS("Ambisonic X"); - case ambisonicY: return NEEDS_TRANS("Ambisonic Y"); - case ambisonicZ: return NEEDS_TRANS("Ambisonic Z"); - case topSideLeft: return NEEDS_TRANS("Top Side Left"); - case topSideRight: return NEEDS_TRANS("Top Side Right"); - case ambisonicACN4: return NEEDS_TRANS("Ambisonic 4"); - case ambisonicACN5: return NEEDS_TRANS("Ambisonic 5"); - case ambisonicACN6: return NEEDS_TRANS("Ambisonic 6"); - case ambisonicACN7: return NEEDS_TRANS("Ambisonic 7"); - case ambisonicACN8: return NEEDS_TRANS("Ambisonic 8"); - case ambisonicACN9: return NEEDS_TRANS("Ambisonic 9"); - case ambisonicACN10: return NEEDS_TRANS("Ambisonic 10"); - case ambisonicACN11: return NEEDS_TRANS("Ambisonic 11"); - case ambisonicACN12: return NEEDS_TRANS("Ambisonic 12"); - case ambisonicACN13: return NEEDS_TRANS("Ambisonic 13"); - case ambisonicACN14: return NEEDS_TRANS("Ambisonic 14"); - case ambisonicACN15: return NEEDS_TRANS("Ambisonic 15"); - case bottomFrontLeft: return NEEDS_TRANS("Bottom Front Left"); - case bottomFrontCentre: return NEEDS_TRANS("Bottom Front Centre"); - case bottomFrontRight: return NEEDS_TRANS("Bottom Front Right"); - case bottomSideLeft: return NEEDS_TRANS("Bottom Side Left"); - case bottomSideRight: return NEEDS_TRANS("Bottom Side Right"); - case bottomRearLeft: return NEEDS_TRANS("Bottom Rear Left"); - case bottomRearCentre: return NEEDS_TRANS("Bottom Rear Centre"); - case bottomRearRight: return NEEDS_TRANS("Bottom Rear Right"); - case discreteChannel0: return NEEDS_TRANS("Discrete channel"); + case left: return NEEDS_TRANS ("Left"); + case right: return NEEDS_TRANS ("Right"); + case centre: return NEEDS_TRANS ("Centre"); + case LFE: return NEEDS_TRANS ("LFE"); + case leftSurround: return NEEDS_TRANS ("Left Surround"); + case rightSurround: return NEEDS_TRANS ("Right Surround"); + case leftCentre: return NEEDS_TRANS ("Left Centre"); + case rightCentre: return NEEDS_TRANS ("Right Centre"); + case centreSurround: return NEEDS_TRANS ("Centre Surround"); + case leftSurroundRear: return NEEDS_TRANS ("Left Surround Rear"); + case rightSurroundRear: return NEEDS_TRANS ("Right Surround Rear"); + case topMiddle: return NEEDS_TRANS ("Top Middle"); + case topFrontLeft: return NEEDS_TRANS ("Top Front Left"); + case topFrontCentre: return NEEDS_TRANS ("Top Front Centre"); + case topFrontRight: return NEEDS_TRANS ("Top Front Right"); + case topRearLeft: return NEEDS_TRANS ("Top Rear Left"); + case topRearCentre: return NEEDS_TRANS ("Top Rear Centre"); + case topRearRight: return NEEDS_TRANS ("Top Rear Right"); + case wideLeft: return NEEDS_TRANS ("Wide Left"); + case wideRight: return NEEDS_TRANS ("Wide Right"); + case LFE2: return NEEDS_TRANS ("LFE 2"); + case leftSurroundSide: return NEEDS_TRANS ("Left Surround Side"); + case rightSurroundSide: return NEEDS_TRANS ("Right Surround Side"); + case ambisonicW: return NEEDS_TRANS ("Ambisonic W"); + case ambisonicX: return NEEDS_TRANS ("Ambisonic X"); + case ambisonicY: return NEEDS_TRANS ("Ambisonic Y"); + case ambisonicZ: return NEEDS_TRANS ("Ambisonic Z"); + case topSideLeft: return NEEDS_TRANS ("Top Side Left"); + case topSideRight: return NEEDS_TRANS ("Top Side Right"); + case ambisonicACN4: return NEEDS_TRANS ("Ambisonic 4"); + case ambisonicACN5: return NEEDS_TRANS ("Ambisonic 5"); + case ambisonicACN6: return NEEDS_TRANS ("Ambisonic 6"); + case ambisonicACN7: return NEEDS_TRANS ("Ambisonic 7"); + case ambisonicACN8: return NEEDS_TRANS ("Ambisonic 8"); + case ambisonicACN9: return NEEDS_TRANS ("Ambisonic 9"); + case ambisonicACN10: return NEEDS_TRANS ("Ambisonic 10"); + case ambisonicACN11: return NEEDS_TRANS ("Ambisonic 11"); + case ambisonicACN12: return NEEDS_TRANS ("Ambisonic 12"); + case ambisonicACN13: return NEEDS_TRANS ("Ambisonic 13"); + case ambisonicACN14: return NEEDS_TRANS ("Ambisonic 14"); + case ambisonicACN15: return NEEDS_TRANS ("Ambisonic 15"); + case ambisonicACN16: return NEEDS_TRANS ("Ambisonic 16"); + case ambisonicACN17: return NEEDS_TRANS ("Ambisonic 17"); + case ambisonicACN18: return NEEDS_TRANS ("Ambisonic 18"); + case ambisonicACN19: return NEEDS_TRANS ("Ambisonic 19"); + case ambisonicACN20: return NEEDS_TRANS ("Ambisonic 20"); + case ambisonicACN21: return NEEDS_TRANS ("Ambisonic 21"); + case ambisonicACN22: return NEEDS_TRANS ("Ambisonic 22"); + case ambisonicACN23: return NEEDS_TRANS ("Ambisonic 23"); + case ambisonicACN24: return NEEDS_TRANS ("Ambisonic 24"); + case ambisonicACN25: return NEEDS_TRANS ("Ambisonic 25"); + case ambisonicACN26: return NEEDS_TRANS ("Ambisonic 26"); + case ambisonicACN27: return NEEDS_TRANS ("Ambisonic 27"); + case ambisonicACN28: return NEEDS_TRANS ("Ambisonic 28"); + case ambisonicACN29: return NEEDS_TRANS ("Ambisonic 29"); + case ambisonicACN30: return NEEDS_TRANS ("Ambisonic 30"); + case ambisonicACN31: return NEEDS_TRANS ("Ambisonic 31"); + case ambisonicACN32: return NEEDS_TRANS ("Ambisonic 32"); + case ambisonicACN33: return NEEDS_TRANS ("Ambisonic 33"); + case ambisonicACN34: return NEEDS_TRANS ("Ambisonic 34"); + case ambisonicACN35: return NEEDS_TRANS ("Ambisonic 35"); + case ambisonicACN36: return NEEDS_TRANS ("Ambisonic 36"); + case ambisonicACN37: return NEEDS_TRANS ("Ambisonic 37"); + case ambisonicACN38: return NEEDS_TRANS ("Ambisonic 38"); + case ambisonicACN39: return NEEDS_TRANS ("Ambisonic 39"); + case ambisonicACN40: return NEEDS_TRANS ("Ambisonic 40"); + case ambisonicACN41: return NEEDS_TRANS ("Ambisonic 41"); + case ambisonicACN42: return NEEDS_TRANS ("Ambisonic 42"); + case ambisonicACN43: return NEEDS_TRANS ("Ambisonic 43"); + case ambisonicACN44: return NEEDS_TRANS ("Ambisonic 44"); + case ambisonicACN45: return NEEDS_TRANS ("Ambisonic 45"); + case ambisonicACN46: return NEEDS_TRANS ("Ambisonic 46"); + case ambisonicACN47: return NEEDS_TRANS ("Ambisonic 47"); + case ambisonicACN48: return NEEDS_TRANS ("Ambisonic 48"); + case ambisonicACN49: return NEEDS_TRANS ("Ambisonic 49"); + case ambisonicACN50: return NEEDS_TRANS ("Ambisonic 50"); + case ambisonicACN51: return NEEDS_TRANS ("Ambisonic 51"); + case ambisonicACN52: return NEEDS_TRANS ("Ambisonic 52"); + case ambisonicACN53: return NEEDS_TRANS ("Ambisonic 53"); + case ambisonicACN54: return NEEDS_TRANS ("Ambisonic 54"); + case ambisonicACN55: return NEEDS_TRANS ("Ambisonic 55"); + case ambisonicACN56: return NEEDS_TRANS ("Ambisonic 56"); + case ambisonicACN57: return NEEDS_TRANS ("Ambisonic 57"); + case ambisonicACN58: return NEEDS_TRANS ("Ambisonic 58"); + case ambisonicACN59: return NEEDS_TRANS ("Ambisonic 59"); + case ambisonicACN60: return NEEDS_TRANS ("Ambisonic 60"); + case ambisonicACN61: return NEEDS_TRANS ("Ambisonic 61"); + case ambisonicACN62: return NEEDS_TRANS ("Ambisonic 62"); + case ambisonicACN63: return NEEDS_TRANS ("Ambisonic 63"); + case bottomFrontLeft: return NEEDS_TRANS ("Bottom Front Left"); + case bottomFrontCentre: return NEEDS_TRANS ("Bottom Front Centre"); + case bottomFrontRight: return NEEDS_TRANS ("Bottom Front Right"); + case proximityLeft: return NEEDS_TRANS ("Proximity Left"); + case proximityRight: return NEEDS_TRANS ("Proximity Right"); + case bottomSideLeft: return NEEDS_TRANS ("Bottom Side Left"); + case bottomSideRight: return NEEDS_TRANS ("Bottom Side Right"); + case bottomRearLeft: return NEEDS_TRANS ("Bottom Rear Left"); + case bottomRearCentre: return NEEDS_TRANS ("Bottom Rear Centre"); + case bottomRearRight: return NEEDS_TRANS ("Bottom Rear Right"); + case discreteChannel0: + case unknown: default: break; } @@ -148,22 +199,71 @@ String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelT case ambisonicACN13: return "ACN13"; case ambisonicACN14: return "ACN14"; case ambisonicACN15: return "ACN15"; + case ambisonicACN16: return "ACN16"; + case ambisonicACN17: return "ACN17"; + case ambisonicACN18: return "ACN18"; + case ambisonicACN19: return "ACN19"; + case ambisonicACN20: return "ACN20"; + case ambisonicACN21: return "ACN21"; + case ambisonicACN22: return "ACN22"; + case ambisonicACN23: return "ACN23"; + case ambisonicACN24: return "ACN24"; + case ambisonicACN25: return "ACN25"; + case ambisonicACN26: return "ACN26"; + case ambisonicACN27: return "ACN27"; + case ambisonicACN28: return "ACN28"; + case ambisonicACN29: return "ACN29"; + case ambisonicACN30: return "ACN30"; + case ambisonicACN31: return "ACN31"; + case ambisonicACN32: return "ACN32"; + case ambisonicACN33: return "ACN33"; + case ambisonicACN34: return "ACN34"; + case ambisonicACN35: return "ACN35"; + case ambisonicACN36: return "ACN36"; + case ambisonicACN37: return "ACN37"; + case ambisonicACN38: return "ACN38"; + case ambisonicACN39: return "ACN39"; + case ambisonicACN40: return "ACN40"; + case ambisonicACN41: return "ACN41"; + case ambisonicACN42: return "ACN42"; + case ambisonicACN43: return "ACN43"; + case ambisonicACN44: return "ACN44"; + case ambisonicACN45: return "ACN45"; + case ambisonicACN46: return "ACN46"; + case ambisonicACN47: return "ACN47"; + case ambisonicACN48: return "ACN48"; + case ambisonicACN49: return "ACN49"; + case ambisonicACN50: return "ACN50"; + case ambisonicACN51: return "ACN51"; + case ambisonicACN52: return "ACN52"; + case ambisonicACN53: return "ACN53"; + case ambisonicACN54: return "ACN54"; + case ambisonicACN55: return "ACN55"; + case ambisonicACN56: return "ACN56"; + case ambisonicACN57: return "ACN57"; + case ambisonicACN58: return "ACN58"; + case ambisonicACN59: return "ACN59"; + case ambisonicACN60: return "ACN60"; + case ambisonicACN61: return "ACN61"; + case ambisonicACN62: return "ACN62"; + case ambisonicACN63: return "ACN63"; case topSideLeft: return "Tsl"; case topSideRight: return "Tsr"; case bottomFrontLeft: return "Bfl"; case bottomFrontCentre: return "Bfc"; case bottomFrontRight: return "Bfr"; + case proximityLeft: return "Pl"; + case proximityRight: return "Pr"; case bottomSideLeft: return "Bsl"; case bottomSideRight: return "Bsr"; case bottomRearLeft: return "Brl"; case bottomRearCentre: return "Brc"; case bottomRearRight: return "Brr"; + case discreteChannel0: + case unknown: default: break; } - if (type >= ambisonicACN4 && type <= ambisonicACN35) - return "ACN" + String (type - ambisonicACN4 + 4); - return {}; } @@ -216,6 +316,54 @@ AudioChannelSet::ChannelType AudioChannelSet::getChannelTypeFromAbbreviation (co if (abbr == "ACN13") return ambisonicACN13; if (abbr == "ACN14") return ambisonicACN14; if (abbr == "ACN15") return ambisonicACN15; + if (abbr == "ACN16") return ambisonicACN16; + if (abbr == "ACN17") return ambisonicACN17; + if (abbr == "ACN18") return ambisonicACN18; + if (abbr == "ACN19") return ambisonicACN19; + if (abbr == "ACN20") return ambisonicACN20; + if (abbr == "ACN21") return ambisonicACN21; + if (abbr == "ACN22") return ambisonicACN22; + if (abbr == "ACN23") return ambisonicACN23; + if (abbr == "ACN24") return ambisonicACN24; + if (abbr == "ACN25") return ambisonicACN25; + if (abbr == "ACN26") return ambisonicACN26; + if (abbr == "ACN27") return ambisonicACN27; + if (abbr == "ACN28") return ambisonicACN28; + if (abbr == "ACN29") return ambisonicACN29; + if (abbr == "ACN30") return ambisonicACN30; + if (abbr == "ACN31") return ambisonicACN31; + if (abbr == "ACN32") return ambisonicACN32; + if (abbr == "ACN33") return ambisonicACN33; + if (abbr == "ACN34") return ambisonicACN34; + if (abbr == "ACN35") return ambisonicACN35; + if (abbr == "ACN36") return ambisonicACN36; + if (abbr == "ACN37") return ambisonicACN37; + if (abbr == "ACN38") return ambisonicACN38; + if (abbr == "ACN39") return ambisonicACN39; + if (abbr == "ACN40") return ambisonicACN40; + if (abbr == "ACN41") return ambisonicACN41; + if (abbr == "ACN42") return ambisonicACN42; + if (abbr == "ACN43") return ambisonicACN43; + if (abbr == "ACN44") return ambisonicACN44; + if (abbr == "ACN45") return ambisonicACN45; + if (abbr == "ACN46") return ambisonicACN46; + if (abbr == "ACN47") return ambisonicACN47; + if (abbr == "ACN48") return ambisonicACN48; + if (abbr == "ACN49") return ambisonicACN49; + if (abbr == "ACN50") return ambisonicACN50; + if (abbr == "ACN51") return ambisonicACN51; + if (abbr == "ACN52") return ambisonicACN52; + if (abbr == "ACN53") return ambisonicACN53; + if (abbr == "ACN54") return ambisonicACN54; + if (abbr == "ACN55") return ambisonicACN55; + if (abbr == "ACN56") return ambisonicACN56; + if (abbr == "ACN57") return ambisonicACN57; + if (abbr == "ACN58") return ambisonicACN58; + if (abbr == "ACN59") return ambisonicACN59; + if (abbr == "ACN60") return ambisonicACN60; + if (abbr == "ACN61") return ambisonicACN61; + if (abbr == "ACN62") return ambisonicACN62; + if (abbr == "ACN63") return ambisonicACN63; if (abbr == "Tsl") return topSideLeft; if (abbr == "Tsr") return topSideRight; if (abbr == "Bfl") return bottomFrontLeft; @@ -271,7 +419,11 @@ String AudioChannelSet::getDescription() const if (*this == createLCRS()) return "LCRS"; if (*this == create5point0()) return "5.0 Surround"; + if (*this == create5point0point2()) return "5.0.2 Surround"; + if (*this == create5point0point4()) return "5.0.4 Surround"; if (*this == create5point1()) return "5.1 Surround"; + if (*this == create5point1point2()) return "5.1.2 Surround"; + if (*this == create5point1point4()) return "5.1.4 Surround"; if (*this == create6point0()) return "6.0 Surround"; if (*this == create6point1()) return "6.1 Surround"; if (*this == create6point0Music()) return "6.0 (Music) Surround"; @@ -281,7 +433,15 @@ String AudioChannelSet::getDescription() const if (*this == create7point0SDDS()) return "7.0 Surround SDDS"; if (*this == create7point1SDDS()) return "7.1 Surround SDDS"; if (*this == create7point0point2()) return "7.0.2 Surround"; + if (*this == create7point0point4()) return "7.0.4 Surround"; + if (*this == create7point0point6()) return "7.0.6 Surround"; if (*this == create7point1point2()) return "7.1.2 Surround"; + if (*this == create7point1point4()) return "7.1.4 Surround"; + if (*this == create7point1point6()) return "7.1.6 Surround"; + if (*this == create9point0point4()) return "9.0.4 Surround"; + if (*this == create9point1point4()) return "9.1.4 Surround"; + if (*this == create9point0point6()) return "9.0.6 Surround"; + if (*this == create9point1point6()) return "9.1.6 Surround"; if (*this == quadraphonic()) return "Quadraphonic"; if (*this == pentagonal()) return "Pentagonal"; @@ -313,11 +473,11 @@ String AudioChannelSet::getDescription() const bool AudioChannelSet::isDiscreteLayout() const noexcept { - for (auto& speaker : getChannelTypes()) - if (speaker <= ambisonicACN35) - return false; + const auto channelTypes = getChannelTypes(); - return true; + return std::none_of (std::begin (channelTypes), + std::end (channelTypes), + [] (const auto& t) { return t < discreteChannel0; }); } int AudioChannelSet::size() const noexcept @@ -327,7 +487,7 @@ int AudioChannelSet::size() const noexcept AudioChannelSet::ChannelType AudioChannelSet::getTypeOfChannel (int index) const noexcept { - int bit = channels.findNextSetBit(0); + int bit = channels.findNextSetBit (0); for (int i = 0; i < index && bit >= 0; ++i) bit = channels.findNextSetBit (bit + 1); @@ -354,7 +514,7 @@ Array AudioChannelSet::getChannelTypes() const { Array result; - for (int bit = channels.findNextSetBit(0); bit >= 0; bit = channels.findNextSetBit (bit + 1)) + for (int bit = channels.findNextSetBit (0); bit >= 0; bit = channels.findNextSetBit (bit + 1)) result.add (static_cast (bit)); return result; @@ -375,39 +535,63 @@ void AudioChannelSet::removeChannel (ChannelType newChannel) } AudioChannelSet AudioChannelSet::disabled() { return {}; } -AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet (1u << centre); } -AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); } -AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre)); } -AudioChannelSet AudioChannelSet::createLRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surround)); } -AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surround)); } -AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround)); } -AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround)); } -AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround)); } -AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround)); } -AudioChannelSet AudioChannelSet::create6point0Music() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide)); } -AudioChannelSet AudioChannelSet::create6point1Music() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide)); } -AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::create7point0SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); } -AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::create7point1SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); } -AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround)); } -AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << centreSurround) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround) | (1u << wideLeft) | (1u << wideRight)); } -AudioChannelSet AudioChannelSet::create7point0point2() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topSideLeft) | (1u << topSideRight)); } -AudioChannelSet AudioChannelSet::create7point1point2() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topSideLeft) | (1u << topSideRight)); } +AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet ({ centre }); } +AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ({ left, right }); } +AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ({ left, right, centre }); } +AudioChannelSet AudioChannelSet::createLRS() { return AudioChannelSet ({ left, right, surround }); } +AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ({ left, right, centre, surround }); } +AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround }); } +AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround }); } +AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, centreSurround }); } +AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, centreSurround }); } +AudioChannelSet AudioChannelSet::create6point0Music() { return AudioChannelSet ({ left, right, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide }); } +AudioChannelSet AudioChannelSet::create6point1Music() { return AudioChannelSet ({ left, right, LFE, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide }); } +AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear }); } +AudioChannelSet AudioChannelSet::create7point0SDDS() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, leftCentre, rightCentre }); } +AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear }); } +AudioChannelSet AudioChannelSet::create7point1SDDS() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre }); } +AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ({ left, right, leftSurround, rightSurround }); } +AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ({ left, right, centre, leftSurroundRear, rightSurroundRear }); } +AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ({ left, right, centre, centreSurround, leftSurroundRear, rightSurroundRear }); } +AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, centreSurround, wideLeft, wideRight }); } +AudioChannelSet AudioChannelSet::create5point0point2() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, topSideLeft, topSideRight }); } +AudioChannelSet AudioChannelSet::create5point1point2() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, topSideLeft, topSideRight }); } +AudioChannelSet AudioChannelSet::create5point0point4() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create5point1point4() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create7point0point2() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight }); } +AudioChannelSet AudioChannelSet::create7point1point2() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight }); } +AudioChannelSet AudioChannelSet::create7point0point4() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create7point1point4() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create7point0point6() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create7point1point6() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create9point0point4() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create9point1point4() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create9point0point6() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight }); } +AudioChannelSet AudioChannelSet::create9point1point6() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight }); } AudioChannelSet AudioChannelSet::ambisonic (int order) { - jassert (isPositiveAndBelow (order, 6)); + jassert (isPositiveAndBelow (order, 8)); + + static constexpr std::pair continuousRanges[] { { ambisonicACN0, ambisonicACN3 }, + { ambisonicACN4, ambisonicACN35 }, + { ambisonicACN36, ambisonicACN63 } }; - if (order == 0) - return AudioChannelSet ((uint32) (1 << ambisonicACN0)); + AudioChannelSet set; - AudioChannelSet set ((1u << ambisonicACN0) | (1u << ambisonicACN1) | (1u << ambisonicACN2) | (1u << ambisonicACN3)); + const auto setBits = [&set] (auto range, auto maxNumToSet) + { + const auto numToSet = std::min (maxNumToSet, range.second - range.first + 1); + set.channels.setRange (range.first, numToSet, true); + return numToSet; + }; - auto numAmbisonicChannels = (order + 1) * (order + 1); - set.channels.setRange (ambisonicACN4, numAmbisonicChannels - 4, true); + const auto numAmbisonicChannels = square (order + 1); + + for (int rangeIdx = 0, bitsSet = 0; bitsSet < numAmbisonicChannels; ++rangeIdx) + { + bitsSet += setBits (continuousRanges[rangeIdx], numAmbisonicChannels - bitsSet); + } return set; } @@ -465,49 +649,55 @@ Array AudioChannelSet::channelSetsWithNumberOfChannels (int num { retval.add (AudioChannelSet::discreteChannels (numChannels)); - if (numChannels == 1) - { - retval.add (AudioChannelSet::mono()); - } - else if (numChannels == 2) - { - retval.add (AudioChannelSet::stereo()); - } - else if (numChannels == 3) - { - retval.add (AudioChannelSet::createLCR()); - retval.add (AudioChannelSet::createLRS()); - } - else if (numChannels == 4) + retval.addArray ([numChannels]() -> Array { - retval.add (AudioChannelSet::quadraphonic()); - retval.add (AudioChannelSet::createLCRS()); - } - else if (numChannels == 5) - { - retval.add (AudioChannelSet::create5point0()); - retval.add (AudioChannelSet::pentagonal()); - } - else if (numChannels == 6) - { - retval.add (AudioChannelSet::create5point1()); - retval.add (AudioChannelSet::create6point0()); - retval.add (AudioChannelSet::create6point0Music()); - retval.add (AudioChannelSet::hexagonal()); - } - else if (numChannels == 7) - { - retval.add (AudioChannelSet::create7point0()); - retval.add (AudioChannelSet::create7point0SDDS()); - retval.add (AudioChannelSet::create6point1()); - retval.add (AudioChannelSet::create6point1Music()); - } - else if (numChannels == 8) - { - retval.add (AudioChannelSet::create7point1()); - retval.add (AudioChannelSet::create7point1SDDS()); - retval.add (AudioChannelSet::octagonal()); - } + switch (numChannels) + { + case 1: + return { AudioChannelSet::mono() }; + case 2: + return { AudioChannelSet::stereo() }; + case 3: + return { AudioChannelSet::createLCR(), + AudioChannelSet::createLRS() }; + case 4: + return { AudioChannelSet::quadraphonic(), + AudioChannelSet::createLCRS() }; + case 5: + return { AudioChannelSet::create5point0(), + AudioChannelSet::pentagonal() }; + case 6: + return { AudioChannelSet::create5point1(), + AudioChannelSet::create6point0(), + AudioChannelSet::create6point0Music(), + AudioChannelSet::hexagonal() }; + case 7: + return { AudioChannelSet::create7point0(), + AudioChannelSet::create7point0SDDS(), + AudioChannelSet::create6point1(), + AudioChannelSet::create6point1Music() }; + case 8: + return { AudioChannelSet::create7point1(), + AudioChannelSet::create7point1SDDS(), + AudioChannelSet::octagonal(), + AudioChannelSet::create5point1point2() }; + case 9: + return { AudioChannelSet::create7point0point2() }; + case 10: + return { AudioChannelSet::create5point1point4(), + AudioChannelSet::create7point1point2() }; + case 11: + return { AudioChannelSet::create7point0point4() }; + case 12: + return { AudioChannelSet::create7point1point4() }; + case 14: + return { AudioChannelSet::create7point1point6() }; + case 16: + return { AudioChannelSet::create9point1point6() }; + } + + return {}; + }()); auto order = getAmbisonicOrderForNumChannels (numChannels); if (order >= 0) @@ -546,15 +736,13 @@ int32 AudioChannelSet::getWaveChannelMask() const noexcept } //============================================================================== -int JUCE_CALLTYPE AudioChannelSet::getAmbisonicOrderForNumChannels (int numChannels) +int AudioChannelSet::getAmbisonicOrderForNumChannels (int numChannels, int maxOrderToCheck) { - auto sqrtMinusOne = std::sqrt (static_cast (numChannels)) - 1.0f; - auto ambisonicOrder = jmax (0, static_cast (std::floor (sqrtMinusOne))); + for (auto i = 0; i <= maxOrderToCheck; ++i) + if (numChannels == square (i + 1)) + return i; - if (ambisonicOrder > 5) - return -1; - - return (static_cast (ambisonicOrder) == sqrtMinusOne ? ambisonicOrder : -1); + return -1; } @@ -562,7 +750,7 @@ int JUCE_CALLTYPE AudioChannelSet::getAmbisonicOrderForNumChannels (int numChann //============================================================================== #if JUCE_UNIT_TESTS -class AudioChannelSetUnitTest : public UnitTest +class AudioChannelSetUnitTest final : public UnitTest { public: AudioChannelSetUnitTest() diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h index 9d0f99ee..051d9a89 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -196,6 +196,30 @@ class JUCE_API AudioChannelSet */ static AudioChannelSet JUCE_CALLTYPE create7point1SDDS(); + /** Creates a set for a 5.0.2 surround setup (left, right, centre, leftSurround, rightSurround, topSideLeft, topSideRight). + + Is equivalent to: AAX_eStemFormat_5_0_2 (AAX). + */ + static AudioChannelSet JUCE_CALLTYPE create5point0point2(); + + /** Creates a set for a 5.1.2 surround setup (left, right, centre, LFE, leftSurround, rightSurround, topSideLeft, topSideRight). + + Is equivalent to: kAudioChannelLayoutTag_Atmos_5_1_2 (CoreAudio). + */ + static AudioChannelSet JUCE_CALLTYPE create5point1point2(); + + /** Creates a set for a 5.0.4 surround setup (left, right, centre, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight). + + Is equivalent to: AAX_eStemFormat_5_0_4 (AAX). + */ + static AudioChannelSet JUCE_CALLTYPE create5point0point4(); + + /** Creates a set for a 5.1.4 surround setup (left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight). + + Is equivalent to: kAudioChannelLayoutTag_Atmos_5_1_4 (CoreAudio). + */ + static AudioChannelSet JUCE_CALLTYPE create5point1point4(); + /** Creates a set for Dolby Atmos 7.0.2 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight). Is equivalent to: n/a (VST), AAX_eStemFormat_7_0_2 (AAX), n/a (CoreAudio) @@ -204,10 +228,63 @@ class JUCE_API AudioChannelSet /** Creates a set for Dolby Atmos 7.1.2 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE, topSideLeft, topSideRight). - Is equivalent to: k71_2 (VST), AAX_eStemFormat_7_1_2 (AAX), n/a (CoreAudio) + Is equivalent to: k71_2 (VST), AAX_eStemFormat_7_1_2 (AAX), kAudioChannelLayoutTag_Atmos_7_1_2 (CoreAudio) */ static AudioChannelSet JUCE_CALLTYPE create7point1point2(); + /** Creates a set for Dolby Atmos 7.0.4 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topRearLeft, topRearRight). + + Is equivalent to: n/a (VST), n/a (AAX), n/a (CoreAudio) + */ + static AudioChannelSet JUCE_CALLTYPE create7point0point4(); + + /** Creates a set for Dolby Atmos 7.1.4 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE, topFrontLeft, topFrontRight, topRearLeft, topRearRight). + + Is equivalent to: k71_4 (VST), n/a (AAX), kAudioChannelLayoutTag_Atmos_7_1_4 (CoreAudio) + */ + static AudioChannelSet JUCE_CALLTYPE create7point1point4(); + + /** Creates a set for 7.0.6 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight). + + Is equivalent to: AAX_eStemFormat_7_0_6 (AAX). + */ + static AudioChannelSet JUCE_CALLTYPE create7point0point6(); + + /** Creates a set for Dolby Atmos 7.1.6 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight). + + Is equivalent to: k71_6 (VST), n/a (AAX), n/a (CoreAudio) + */ + static AudioChannelSet JUCE_CALLTYPE create7point1point6(); + + /** Creates a set for a 9.0.4 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topRearLeft, topRearRight). + + Is equivalent to: k90_4 (VST3), AAX_eStemFormat_9_0_4 (AAX). + */ + static AudioChannelSet JUCE_CALLTYPE create9point0point4(); + + /** Creates a set for a 9.1.4 surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topRearLeft, topRearRight). + + Is equivalent to: k91_4 (VST3), AAX_eStemFormat_9_1_4 (AAX). + */ + static AudioChannelSet JUCE_CALLTYPE create9point1point4(); + + /** Creates a set for a 9.0.6 surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight). + + Is equivalent to: k90_6 (VST3), AAX_eStemFormat_9_0_6 (AAX). + */ + static AudioChannelSet JUCE_CALLTYPE create9point0point6(); + + /** Creates a set for a 9.1.6 surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight). + + Note that the VST3 layout arranges the front speakers "L Lc C Rc R", but the JUCE layout + uses the arrangement "wideLeft left centre right wideRight". To maintain the relative + positions of the speakers, the channels will be remapped accordingly. This means that the + VST3 host's "L" channel will be received on a JUCE plugin's "wideLeft" channel, the + "Lc" channel will be received on a JUCE plugin's "left" channel, and so on. + + Is equivalent to: k91_6 (VST3), kAudioChannelLayoutTag_Atmos_9_1_6 (CoreAudio). + */ + static AudioChannelSet JUCE_CALLTYPE create9point1point6(); //============================================================================== /** Creates a set for quadraphonic surround setup (left, right, leftSurround, rightSurround) @@ -306,8 +383,8 @@ class JUCE_API AudioChannelSet //============================================================================== // Used by Dolby Atmos 7.0.2 and 7.1.2 - topSideLeft = 28, /**< Lts (AAX), Tsl (VST) channel for Dolby Atmos. */ - topSideRight = 29, /**< Rts (AAX), Tsr (VST) channel for Dolby Atmos. */ + topSideLeft = 28, /**< Lts (AAX), Tsl (VST), Ltm (AU) channel for Dolby Atmos. */ + topSideRight = 29, /**< Rts (AAX), Tsr (VST), Rtm (AU) channel for Dolby Atmos. */ //============================================================================== // Ambisonic ACN formats - all channels are SN3D normalised @@ -369,7 +446,7 @@ class JUCE_API AudioChannelSet bottomFrontCentre = 63, /**< Bottom Front Centre (Bfc) */ bottomFrontRight = 64, /**< Bottom Front Right (Bfr) */ - proxymityLeft = 65, /**< Proximity Left (Pl) */ + proximityLeft = 65, /**< Proximity Left (Pl) */ proximityRight = 66, /**< Proximity Right (Pr) */ bottomSideLeft = 67, /**< Bottom Side Left (Bsl) */ @@ -378,6 +455,40 @@ class JUCE_API AudioChannelSet bottomRearCentre = 70, /**< Bottom Rear Center (Brc) */ bottomRearRight = 71, /**< Bottom Rear Right (Brr) */ + //============================================================================== + + // sixth-order ambisonic + ambisonicACN36 = 72, /**< Sixth-order ambisonic channel number 36. */ + ambisonicACN37 = 73, /**< Sixth-order ambisonic channel number 37. */ + ambisonicACN38 = 74, /**< Sixth-order ambisonic channel number 38. */ + ambisonicACN39 = 75, /**< Sixth-order ambisonic channel number 39. */ + ambisonicACN40 = 76, /**< Sixth-order ambisonic channel number 40. */ + ambisonicACN41 = 77, /**< Sixth-order ambisonic channel number 41. */ + ambisonicACN42 = 78, /**< Sixth-order ambisonic channel number 42. */ + ambisonicACN43 = 79, /**< Sixth-order ambisonic channel number 43. */ + ambisonicACN44 = 80, /**< Sixth-order ambisonic channel number 44. */ + ambisonicACN45 = 81, /**< Sixth-order ambisonic channel number 45. */ + ambisonicACN46 = 82, /**< Sixth-order ambisonic channel number 46. */ + ambisonicACN47 = 83, /**< Sixth-order ambisonic channel number 47. */ + ambisonicACN48 = 84, /**< Sixth-order ambisonic channel number 48. */ + + // seventh-order ambisonic + ambisonicACN49 = 85, /**< Seventh-order ambisonic channel number 49. */ + ambisonicACN50 = 86, /**< Seventh-order ambisonic channel number 50. */ + ambisonicACN51 = 87, /**< Seventh-order ambisonic channel number 51. */ + ambisonicACN52 = 88, /**< Seventh-order ambisonic channel number 52. */ + ambisonicACN53 = 89, /**< Seventh-order ambisonic channel number 53. */ + ambisonicACN54 = 90, /**< Seventh-order ambisonic channel number 54. */ + ambisonicACN55 = 91, /**< Seventh-order ambisonic channel number 55. */ + ambisonicACN56 = 92, /**< Seventh-order ambisonic channel number 56. */ + ambisonicACN57 = 93, /**< Seventh-order ambisonic channel number 57. */ + ambisonicACN58 = 94, /**< Seventh-order ambisonic channel number 58. */ + ambisonicACN59 = 95, /**< Seventh-order ambisonic channel number 59. */ + ambisonicACN60 = 96, /**< Seventh-order ambisonic channel number 60. */ + ambisonicACN61 = 97, /**< Seventh-order ambisonic channel number 61. */ + ambisonicACN62 = 98, /**< Seventh-order ambisonic channel number 62. */ + ambisonicACN63 = 99, /**< Seventh-order ambisonic channel number 63. */ + //============================================================================== discreteChannel0 = 128 /**< Non-typed individual channels are indexed upwards from this value. */ }; @@ -394,7 +505,7 @@ class JUCE_API AudioChannelSet //============================================================================== enum { - maxChannelsOfNamedLayout = 36 + maxChannelsOfNamedLayout = 64 }; /** Adds a channel to the set. */ @@ -464,6 +575,12 @@ class JUCE_API AudioChannelSet */ int32 getWaveChannelMask() const noexcept; + //============================================================================== + /** Returns the ambisonic order that includes exactly numChannels, or -1 if no + supported ambisonic order contains exactly numChannels. + */ + static int getAmbisonicOrderForNumChannels (int numChannels, int maxOrderToCheck = 7); + //============================================================================== bool operator== (const AudioChannelSet&) const noexcept; bool operator!= (const AudioChannelSet&) const noexcept; @@ -475,10 +592,8 @@ class JUCE_API AudioChannelSet //============================================================================== explicit AudioChannelSet (uint32); - explicit AudioChannelSet (const Array&); + explicit AudioChannelSet (const std::initializer_list&); - //============================================================================== - static int JUCE_CALLTYPE getAmbisonicOrderForNumChannels (int); }; } // 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 0014be52..0410fc60 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,9 @@ namespace juce { +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, int destBytesPerSample) { auto maxVal = (double) 0x7fff; @@ -32,7 +35,7 @@ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest { for (int i = 0; i < numSamples; ++i) { - *reinterpret_cast (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + *unalignedPointerCast (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); intData += destBytesPerSample; } } @@ -43,7 +46,7 @@ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest for (int i = numSamples; --i >= 0;) { intData -= destBytesPerSample; - *reinterpret_cast (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + *unalignedPointerCast (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); } } } @@ -57,7 +60,7 @@ void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest { for (int i = 0; i < numSamples; ++i) { - *reinterpret_cast (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + *unalignedPointerCast (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); intData += destBytesPerSample; } } @@ -68,7 +71,7 @@ void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest for (int i = numSamples; --i >= 0;) { intData -= destBytesPerSample; - *reinterpret_cast (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + *unalignedPointerCast (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); } } } @@ -132,7 +135,7 @@ void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest { for (int i = 0; i < numSamples; ++i) { - *reinterpret_cast (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + *unalignedPointerCast (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); intData += destBytesPerSample; } } @@ -143,7 +146,7 @@ void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest for (int i = numSamples; --i >= 0;) { intData -= destBytesPerSample; - *reinterpret_cast (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + *unalignedPointerCast (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); } } } @@ -157,7 +160,7 @@ void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest { for (int i = 0; i < numSamples; ++i) { - *reinterpret_cast (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + *unalignedPointerCast (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); intData += destBytesPerSample; } } @@ -168,7 +171,7 @@ void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest for (int i = numSamples; --i >= 0;) { intData -= destBytesPerSample; - *reinterpret_cast (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); + *unalignedPointerCast (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); } } } @@ -181,10 +184,10 @@ void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* de for (int i = 0; i < numSamples; ++i) { - *reinterpret_cast (d) = source[i]; + *unalignedPointerCast (d) = source[i]; #if JUCE_BIG_ENDIAN - *reinterpret_cast (d) = ByteOrder::swap (*reinterpret_cast (d)); + *unalignedPointerCast (d) = ByteOrder::swap (*unalignedPointerCast (d)); #endif d += destBytesPerSample; @@ -199,10 +202,10 @@ void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* de for (int i = 0; i < numSamples; ++i) { - *reinterpret_cast (d) = source[i]; + *unalignedPointerCast (d) = source[i]; #if JUCE_LITTLE_ENDIAN - *reinterpret_cast (d) = ByteOrder::swap (*reinterpret_cast (d)); + *unalignedPointerCast (d) = ByteOrder::swap (*unalignedPointerCast (d)); #endif d += destBytesPerSample; @@ -219,7 +222,7 @@ void AudioDataConverters::convertInt16LEToFloat (const void* source, float* dest { for (int i = 0; i < numSamples; ++i) { - dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*reinterpret_cast (intData)); + dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*unalignedPointerCast (intData)); intData += srcBytesPerSample; } } @@ -230,7 +233,7 @@ void AudioDataConverters::convertInt16LEToFloat (const void* source, float* dest for (int i = numSamples; --i >= 0;) { intData -= srcBytesPerSample; - dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*reinterpret_cast (intData)); + dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*unalignedPointerCast (intData)); } } } @@ -244,7 +247,7 @@ void AudioDataConverters::convertInt16BEToFloat (const void* source, float* dest { for (int i = 0; i < numSamples; ++i) { - dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*reinterpret_cast (intData)); + dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*unalignedPointerCast (intData)); intData += srcBytesPerSample; } } @@ -255,7 +258,7 @@ void AudioDataConverters::convertInt16BEToFloat (const void* source, float* dest for (int i = numSamples; --i >= 0;) { intData -= srcBytesPerSample; - dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*reinterpret_cast (intData)); + dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*unalignedPointerCast (intData)); } } } @@ -319,7 +322,7 @@ void AudioDataConverters::convertInt32LEToFloat (const void* source, float* dest { for (int i = 0; i < numSamples; ++i) { - dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*reinterpret_cast (intData)); + dest[i] = scale * (float) ByteOrder::swapIfBigEndian (*unalignedPointerCast (intData)); intData += srcBytesPerSample; } } @@ -330,7 +333,7 @@ void AudioDataConverters::convertInt32LEToFloat (const void* source, float* dest for (int i = numSamples; --i >= 0;) { intData -= srcBytesPerSample; - dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*reinterpret_cast (intData)); + dest[i] = scale * (float) ByteOrder::swapIfBigEndian (*unalignedPointerCast (intData)); } } } @@ -344,7 +347,7 @@ void AudioDataConverters::convertInt32BEToFloat (const void* source, float* dest { for (int i = 0; i < numSamples; ++i) { - dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*reinterpret_cast (intData)); + dest[i] = scale * (float) ByteOrder::swapIfLittleEndian (*unalignedPointerCast (intData)); intData += srcBytesPerSample; } } @@ -355,7 +358,7 @@ void AudioDataConverters::convertInt32BEToFloat (const void* source, float* dest for (int i = numSamples; --i >= 0;) { intData -= srcBytesPerSample; - dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*reinterpret_cast (intData)); + dest[i] = scale * (float) ByteOrder::swapIfLittleEndian (*unalignedPointerCast (intData)); } } } @@ -366,10 +369,10 @@ void AudioDataConverters::convertFloat32LEToFloat (const void* source, float* de for (int i = 0; i < numSamples; ++i) { - dest[i] = *reinterpret_cast (s); + dest[i] = *unalignedPointerCast (s); #if JUCE_BIG_ENDIAN - auto d = reinterpret_cast (dest + i); + auto d = unalignedPointerCast (dest + i); *d = ByteOrder::swap (*d); #endif @@ -383,10 +386,10 @@ void AudioDataConverters::convertFloat32BEToFloat (const void* source, float* de for (int i = 0; i < numSamples; ++i) { - dest[i] = *reinterpret_cast (s); + dest[i] = *unalignedPointerCast (s); #if JUCE_LITTLE_ENDIAN - auto d = reinterpret_cast (dest + i); + auto d = unalignedPointerCast (dest + i); *d = ByteOrder::swap (*d); #endif @@ -431,40 +434,27 @@ void AudioDataConverters::convertFormatToFloat (DataFormat sourceFormat, const v //============================================================================== void AudioDataConverters::interleaveSamples (const float** source, float* dest, int numSamples, int numChannels) { - for (int chan = 0; chan < numChannels; ++chan) - { - auto i = chan; - auto src = source [chan]; + using Format = AudioData::Format; - for (int j = 0; j < numSamples; ++j) - { - dest [i] = src [j]; - i += numChannels; - } - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { source, numChannels }, + AudioData::InterleavedDest { dest, numChannels }, + numSamples); } void AudioDataConverters::deinterleaveSamples (const float* source, float** dest, int numSamples, int numChannels) { - for (int chan = 0; chan < numChannels; ++chan) - { - auto i = chan; - auto dst = dest [chan]; + using Format = AudioData::Format; - for (int j = 0; j < numSamples; ++j) - { - dst [j] = source [i]; - i += numChannels; - } - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { source, numChannels }, + AudioData::NonInterleavedDest { dest, numChannels }, + numSamples); } - //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS -class AudioConversionTests : public UnitTest +class AudioConversionTests final : public UnitTest { public: AudioConversionTests() @@ -480,6 +470,7 @@ class AudioConversionTests : public UnitTest test (unitTest, true, r); } + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262) static void test (UnitTest& unitTest, bool inPlace, Random& r) { const int numSamples = 2048; @@ -537,6 +528,7 @@ class AudioConversionTests : public UnitTest unitTest.expect (biggestDiff <= errorMargin); } } + JUCE_END_IGNORE_WARNINGS_MSVC }; template @@ -586,6 +578,50 @@ class AudioConversionTests : public UnitTest Test1 ::test (*this, r); beginTest ("Round-trip conversion: Float32"); Test1 ::test (*this, r); + + using Format = AudioData::Format; + + beginTest ("Interleaving"); + { + constexpr auto numChannels = 4; + constexpr auto numSamples = 512; + + AudioBuffer sourceBuffer { numChannels, numSamples }, + destBuffer { 1, numChannels * numSamples }; + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + sourceBuffer.setSample (ch, i, r.nextFloat()); + + AudioData::interleaveSamples (AudioData::NonInterleavedSource { sourceBuffer.getArrayOfReadPointers(), numChannels }, + AudioData::InterleavedDest { destBuffer.getWritePointer (0), numChannels }, + numSamples); + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + expectEquals (destBuffer.getSample (0, ch + (i * numChannels)), sourceBuffer.getSample (ch, i)); + } + + beginTest ("Deinterleaving"); + { + constexpr auto numChannels = 4; + constexpr auto numSamples = 512; + + AudioBuffer sourceBuffer { 1, numChannels * numSamples }, + destBuffer { numChannels, numSamples }; + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + sourceBuffer.setSample (0, ch + (i * numChannels), r.nextFloat()); + + AudioData::deinterleaveSamples (AudioData::InterleavedSource { sourceBuffer.getReadPointer (0), numChannels }, + AudioData::NonInterleavedDest { destBuffer.getArrayOfWritePointers(), numChannels }, + numSamples); + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + expectEquals (sourceBuffer.getSample (0, ch + (i * numChannels)), destBuffer.getSample (ch, i)); + } } }; @@ -593,4 +629,7 @@ static AudioConversionTests audioConversionUnitTests; #endif +JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h index 9880d454..fb8c8aca 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -69,22 +69,22 @@ class JUCE_API AudioData class BigEndian { public: - template static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatBE(); } - template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatBE (newValue); } - template static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32BE(); } - template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32BE (newValue); } - template static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromBE (source); } + template static float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatBE(); } + template static void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatBE (newValue); } + template static int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32BE(); } + template static void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32BE (newValue); } + template static void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromBE (source); } enum { isBigEndian = 1 }; }; class LittleEndian { public: - template static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatLE(); } - template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatLE (newValue); } - template static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32LE(); } - template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32LE (newValue); } - template static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromLE (source); } + template static float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatLE(); } + template static void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatLE (newValue); } + template static int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32LE(); } + template static void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32LE (newValue); } + template static void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromLE (source); } enum { isBigEndian = 0 }; }; @@ -279,12 +279,12 @@ class JUCE_API AudioData public: inline NonInterleaved() = default; inline NonInterleaved (const NonInterleaved&) = default; - inline NonInterleaved (const int) noexcept {} + inline NonInterleaved (int) noexcept {} inline void copyFrom (const NonInterleaved&) noexcept {} template inline void advanceData (SampleFormatType& s) noexcept { s.advance(); } template inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numSamples); } template inline void clear (SampleFormatType& s, int numSamples) noexcept { s.clearMultiple (numSamples); } - template inline static int getNumBytesBetweenSamples (const SampleFormatType&) noexcept { return SampleFormatType::bytesPerSample; } + template static int getNumBytesBetweenSamples (const SampleFormatType&) noexcept { return SampleFormatType::bytesPerSample; } enum { isInterleavedType = 0, numInterleavedChannels = 1 }; }; @@ -309,7 +309,7 @@ class JUCE_API AudioData { public: using VoidType = void; - static inline void* toVoidPtr (VoidType* v) noexcept { return v; } + static void* toVoidPtr (VoidType* v) noexcept { return v; } enum { isConst = 0 }; }; @@ -317,7 +317,7 @@ class JUCE_API AudioData { public: using VoidType = const void; - static inline void* toVoidPtr (VoidType* v) noexcept { return const_cast (v); } + static void* toVoidPtr (VoidType* v) noexcept { return const_cast (v); } enum { isConst = 1 }; }; #endif @@ -436,6 +436,9 @@ class JUCE_API AudioData /** Adds a number of samples to the pointer's position. */ Pointer& operator+= (int samplesToJump) noexcept { this->advanceDataBy (data, samplesToJump); return *this; } + /** Returns a new pointer with the specified offset from this pointer's position. */ + Pointer operator+ (int samplesToJump) const { return Pointer { *this } += samplesToJump; } + /** Writes a stream of samples into this pointer from another pointer. This will copy the specified number of samples, converting between formats appropriately. */ @@ -528,8 +531,8 @@ class JUCE_API AudioData if (v < mn) mn = v; } - return Range (mn * (float) (1.0 / (1.0 + (double) Int32::maxValue)), - mx * (float) (1.0 / (1.0 + (double) Int32::maxValue))); + return Range ((float) mn * (float) (1.0 / (1.0 + (double) Int32::maxValue)), + (float) mx * (float) (1.0 / (1.0 + (double) Int32::maxValue))); } /** Scans a block of data, returning the lowest and highest levels as floats */ @@ -639,11 +642,152 @@ class JUCE_API AudioData const int sourceChannels, destChannels; }; -}; + //============================================================================== + /** A struct that contains a SampleFormat and Endianness to be used with the source and + destination types when calling the interleaveSamples() and deinterleaveSamples() helpers. + + @see interleaveSamples, deinterleaveSamples + */ + template + struct Format + { + using DataFormat = DataFormatIn; + using Endianness = EndiannessIn; + }; + +private: + template + struct ChannelDataSubtypes; + + template + struct ChannelDataSubtypes + { + using ElementType = std::remove_pointer_t; + using ChannelType = std::conditional_t; + using DataType = std::conditional_t; + using PointerType = Pointer, + std::conditional_t>; + }; + + template + struct ChannelDataSubtypes> + { + using Subtypes = ChannelDataSubtypes; + using DataType = typename Subtypes::DataType; + using PointerType = typename Subtypes::PointerType; + }; + + template + struct ChannelData + { + using Subtypes = ChannelDataSubtypes; + using DataType = typename Subtypes::DataType; + using PointerType = typename Subtypes::PointerType; + + DataType data; + int channels; + }; + +public: + //============================================================================== + /** A sequence of interleaved samples used as the source for the deinterleaveSamples() method. */ + template using InterleavedSource = ChannelData; + /** A sequence of interleaved samples used as the destination for the interleaveSamples() method. */ + template using InterleavedDest = ChannelData; + /** A sequence of non-interleaved samples used as the source for the interleaveSamples() method. */ + template using NonInterleavedSource = ChannelData; + /** A sequence of non-interleaved samples used as the destination for the deinterleaveSamples() method. */ + template using NonInterleavedDest = ChannelData; + + /** A helper function for converting a sequence of samples from a non-interleaved source + to an interleaved destination. + + When calling this method you need to specify the source and destination data format and endianness + from the AudioData SampleFormat and Endianness types and provide the data and number of channels + for each. For example, to convert a floating-point stream of big endian samples to an interleaved, + native endian stream of 16-bit integer samples you would do the following: + + @code + using SourceFormat = AudioData::Format; + using DestFormat = AudioData::Format; + + AudioData::interleaveSamples (AudioData::NonInterleavedSource { sourceData, numSourceChannels }, + AudioData::InterleavedDest { destData, numDestChannels }, + numSamples); + @endcode + */ + template + static void interleaveSamples (NonInterleavedSource source, + InterleavedDest dest, + int numSamples) + { + using SourceType = typename decltype (source)::PointerType; + using DestType = typename decltype (dest) ::PointerType; + + for (int i = 0; i < dest.channels; ++i) + { + const DestType destType (addBytesToPointer (dest.data, i * DestType::getBytesPerSample()), dest.channels); + + if (i < source.channels) + { + if (*source.data != nullptr) + { + destType.convertSamples (SourceType { *source.data }, numSamples); + ++source.data; + } + } + else + { + destType.clearSamples (numSamples); + } + } + } + + /** A helper function for converting a sequence of samples from an interleaved source + to a non-interleaved destination. + When calling this method you need to specify the source and destination data format and endianness + from the AudioData SampleFormat and Endianness types and provide the data and number of channels + for each. For example, to convert a floating-point stream of big endian samples to an non-interleaved, + native endian stream of 16-bit integer samples you would do the following: + + @code + using SourceFormat = AudioData::Format; + using DestFormat = AudioData::Format; + + AudioData::deinterleaveSamples (AudioData::InterleavedSource { sourceData, numSourceChannels }, + AudioData::NonInterleavedDest { destData, numDestChannels }, + numSamples); + @endcode + */ + template + static void deinterleaveSamples (InterleavedSource source, + NonInterleavedDest dest, + int numSamples) + { + using SourceType = typename decltype (source)::PointerType; + using DestType = typename decltype (dest) ::PointerType; + + for (int i = 0; i < dest.channels; ++i) + { + if (auto* targetChan = dest.data[i]) + { + const DestType destType (targetChan); + + if (i < source.channels) + destType.convertSamples (SourceType (addBytesToPointer (source.data, i * SourceType::getBytesPerSample()), source.channels), numSamples); + else + destType.clearSamples (numSamples); + } + } + } +}; //============================================================================== +#ifndef DOXYGEN /** A set of routines to convert buffers of 32-bit floating point data to and from various integer formats. @@ -653,7 +797,7 @@ class JUCE_API AudioData @tags{Audio} */ -class JUCE_API AudioDataConverters +class [[deprecated]] JUCE_API AudioDataConverters { public: //============================================================================== @@ -710,7 +854,7 @@ class JUCE_API AudioDataConverters private: AudioDataConverters(); - JUCE_DECLARE_NON_COPYABLE (AudioDataConverters) }; +#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp index 1e6c91fd..442f4cee 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp @@ -2,20 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). + 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 @@ -27,8 +23,8 @@ namespace juce { -AudioProcessLoadMeasurer::AudioProcessLoadMeasurer() {} -AudioProcessLoadMeasurer::~AudioProcessLoadMeasurer() {} +AudioProcessLoadMeasurer::AudioProcessLoadMeasurer() = default; +AudioProcessLoadMeasurer::~AudioProcessLoadMeasurer() = default; void AudioProcessLoadMeasurer::reset() { @@ -37,43 +33,67 @@ void AudioProcessLoadMeasurer::reset() void AudioProcessLoadMeasurer::reset (double sampleRate, int blockSize) { - cpuUsageMs = 0; + const SpinLock::ScopedLockType lock (mutex); + + cpuUsageProportion = 0; xruns = 0; - if (sampleRate > 0.0 && blockSize > 0) - { - msPerBlock = 1000.0 * blockSize / sampleRate; - timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0; - } - else - { - msPerBlock = 0; - timeToCpuScale = 0; - } + samplesPerBlock = blockSize; + msPerSample = (sampleRate > 0.0 && blockSize > 0) ? 1000.0 / sampleRate : 0; } void AudioProcessLoadMeasurer::registerBlockRenderTime (double milliseconds) { - const double filterAmount = 0.2; - cpuUsageMs += filterAmount * (milliseconds - cpuUsageMs); + const SpinLock::ScopedTryLockType lock (mutex); + + if (lock.isLocked()) + registerRenderTimeLocked (milliseconds, samplesPerBlock); +} + +void AudioProcessLoadMeasurer::registerRenderTime (double milliseconds, int numSamples) +{ + const SpinLock::ScopedTryLockType lock (mutex); - if (milliseconds > msPerBlock) + if (lock.isLocked()) + registerRenderTimeLocked (milliseconds, numSamples); +} + +void AudioProcessLoadMeasurer::registerRenderTimeLocked (double milliseconds, int numSamples) +{ + if (approximatelyEqual (msPerSample, 0.0)) + return; + + const auto maxMilliseconds = numSamples * msPerSample; + const auto usedProportion = milliseconds / maxMilliseconds; + const auto filterAmount = 0.2; + const auto proportion = cpuUsageProportion.load(); + cpuUsageProportion = proportion + filterAmount * (usedProportion - proportion); + + if (milliseconds > maxMilliseconds) ++xruns; } -double AudioProcessLoadMeasurer::getLoadAsProportion() const { return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs); } +double AudioProcessLoadMeasurer::getLoadAsProportion() const { return jlimit (0.0, 1.0, cpuUsageProportion.load()); } double AudioProcessLoadMeasurer::getLoadAsPercentage() const { return 100.0 * getLoadAsProportion(); } int AudioProcessLoadMeasurer::getXRunCount() const { return xruns; } AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p) - : owner (p), startTime (Time::getMillisecondCounterHiRes()) + : ScopedTimer (p, p.samplesPerBlock) +{ +} + +AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p, int numSamplesInBlock) + : owner (p), startTime (Time::getMillisecondCounterHiRes()), samplesInBlock (numSamplesInBlock) { + // numSamplesInBlock should never be zero. Did you remember to call AudioProcessLoadMeasurer::reset(), + // passing the expected samples per block? + jassert (numSamplesInBlock); } AudioProcessLoadMeasurer::ScopedTimer::~ScopedTimer() { - owner.registerBlockRenderTime (Time::getMillisecondCounterHiRes() - startTime); + owner.registerRenderTime (Time::getMillisecondCounterHiRes() - startTime, samplesInBlock); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h index 7c1ec175..744221e1 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h @@ -2,20 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). + 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 @@ -76,11 +72,13 @@ class JUCE_API AudioProcessLoadMeasurer struct JUCE_API ScopedTimer { ScopedTimer (AudioProcessLoadMeasurer&); + ScopedTimer (AudioProcessLoadMeasurer&, int numSamplesInBlock); ~ScopedTimer(); private: AudioProcessLoadMeasurer& owner; double startTime; + int samplesInBlock; JUCE_DECLARE_NON_COPYABLE (ScopedTimer) }; @@ -91,9 +89,20 @@ class JUCE_API AudioProcessLoadMeasurer */ void registerBlockRenderTime (double millisecondsTaken); + /** Can be called manually to add the time of a callback to the stats. + Normally you probably would never call this - it's simpler and more robust to + use a ScopedTimer to measure the time using an RAII pattern. + */ + void registerRenderTime (double millisecondsTaken, int numSamples); + private: - double cpuUsageMs = 0, timeToCpuScale = 0, msPerBlock = 0; - int xruns = 0; + void registerRenderTimeLocked (double, int); + + SpinLock mutex; + int samplesPerBlock = 0; + double msPerSample = 0; + std::atomic cpuUsageProportion { 0 }; + std::atomic xruns { 0 }; }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h index f62e90c5..ddff0df8 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -145,6 +145,7 @@ class AudioBuffer } /** Copies another buffer onto this one. + This buffer's size will be changed to that of the other buffer. */ AudioBuffer& operator= (const AudioBuffer& other) @@ -170,17 +171,18 @@ class AudioBuffer } /** Destructor. + This will free any memory allocated by the buffer. */ ~AudioBuffer() = default; - /** Move constructor */ + /** Move constructor. */ AudioBuffer (AudioBuffer&& other) noexcept : numChannels (other.numChannels), size (other.size), allocatedBytes (other.allocatedBytes), allocatedData (std::move (other.allocatedData)), - isClear (other.isClear.load()) + isClear (other.isClear) { if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) { @@ -199,14 +201,14 @@ class AudioBuffer other.allocatedBytes = 0; } - /** Move assignment */ + /** Move assignment. */ AudioBuffer& operator= (AudioBuffer&& other) noexcept { numChannels = other.numChannels; size = other.size; allocatedBytes = other.allocatedBytes; allocatedData = std::move (other.allocatedData); - isClear = other.isClear.load(); + isClear = other.isClear; if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) { @@ -228,18 +230,22 @@ class AudioBuffer //============================================================================== /** Returns the number of channels of audio data that this buffer contains. + @see getNumSamples, getReadPointer, getWritePointer */ int getNumChannels() const noexcept { return numChannels; } /** Returns the number of samples allocated in each of the buffer's channels. + @see getNumChannels, getReadPointer, getWritePointer */ int getNumSamples() const noexcept { return size; } /** Returns a pointer to an array of read-only samples in one of the buffer's channels. + For speed, this doesn't check whether the channel number is out of range, so be careful when using it! + If you need to write to the data, do NOT call this method and const_cast the result! Instead, you must call getWritePointer so that the buffer knows you're planning on modifying the data. @@ -251,8 +257,10 @@ class AudioBuffer } /** Returns a pointer to an array of read-only samples in one of the buffer's channels. + For speed, this doesn't check whether the channel number or index are out of range, so be careful when using it! + If you need to write to the data, do NOT call this method and const_cast the result! Instead, you must call getWritePointer so that the buffer knows you're planning on modifying the data. @@ -265,10 +273,20 @@ class AudioBuffer } /** Returns a writeable pointer to one of the buffer's channels. + For speed, this doesn't check whether the channel number is out of range, so be careful when using it! + Note that if you're not planning on writing to the data, you should always use getReadPointer instead. + + This will mark the buffer as not cleared and the hasBeenCleared method will return + false after this call. If you retain this write pointer and write some data to + the buffer after calling its clear method, subsequent clear calls will do nothing. + To avoid this either call this method each time you need to write data, or use the + setNotClear method to force the internal cleared flag to false. + + @see setNotClear */ Type* getWritePointer (int channelNumber) noexcept { @@ -278,10 +296,20 @@ class AudioBuffer } /** Returns a writeable pointer to one of the buffer's channels. + For speed, this doesn't check whether the channel number or index are out of range, so be careful when using it! + Note that if you're not planning on writing to the data, you should use getReadPointer instead. + + This will mark the buffer as not cleared and the hasBeenCleared method will return + false after this call. If you retain this write pointer and write some data to + the buffer after calling its clear method, subsequent clear calls will do nothing. + To avoid this either call this method each time you need to write data, or use the + setNotClear method to force the internal cleared flag to false. + + @see setNotClear */ Type* getWritePointer (int channelNumber, int sampleIndex) noexcept { @@ -296,37 +324,45 @@ class AudioBuffer Don't modify any of the pointers that are returned, and bear in mind that these will become invalid if the buffer is resized. */ - const Type** getArrayOfReadPointers() const noexcept { return const_cast (channels); } + const Type* const* getArrayOfReadPointers() const noexcept { return channels; } /** Returns an array of pointers to the channels in the buffer. Don't modify any of the pointers that are returned, and bear in mind that these will become invalid if the buffer is resized. + + This will mark the buffer as not cleared and the hasBeenCleared method will return + false after this call. If you retain this write pointer and write some data to + the buffer after calling its clear method, subsequent clear calls will do nothing. + To avoid this either call this method each time you need to write data, or use the + setNotClear method to force the internal cleared flag to false. + + @see setNotClear */ - Type** getArrayOfWritePointers() noexcept { isClear = false; return channels; } + Type* const* getArrayOfWritePointers() noexcept { isClear = false; return channels; } //============================================================================== /** Changes the buffer's size or number of channels. This can expand or contract the buffer's length, and add or remove channels. - If keepExistingContent is true, it will try to preserve as much of the - old data as it can in the new buffer. - - If clearExtraSpace is true, then any extra channels or space that is - allocated will be also be cleared. If false, then this space is left - uninitialised. - - If avoidReallocating is true, then changing the buffer's size won't reduce the - amount of memory that is currently allocated (but it will still increase it if - the new size is bigger than the amount it currently has). If this is false, then - a new allocation will be done so that the buffer uses takes up the minimum amount - of memory that it needs. - Note that if keepExistingContent and avoidReallocating are both true, then it will only avoid reallocating if neither the channel count or length in samples increase. If the required memory can't be allocated, this will throw a std::bad_alloc exception. + + @param newNumChannels the new number of channels. + @param newNumSamples the new number of samples. + @param keepExistingContent if this is true, it will try to preserve as much of the + old data as it can in the new buffer. + @param clearExtraSpace if this is true, then any extra channels or space that is + allocated will be also be cleared. If false, then this space is left + uninitialised. + @param avoidReallocating if this is true, then changing the buffer's size won't reduce the + amount of memory that is currently allocated (but it will still + increase it if the new size is bigger than the amount it currently has). + If this is false, then a new allocation will be done so that the buffer + uses takes up the minimum amount of memory that it needs. */ void setSize (int newNumChannels, int newNumSamples, @@ -357,8 +393,8 @@ class AudioBuffer auto numSamplesToCopy = (size_t) jmin (newNumSamples, size); - auto newChannels = reinterpret_cast (newData.get()); - auto newChan = reinterpret_cast (newData + channelListSize); + auto newChannels = unalignedPointerCast (newData.get()); + auto newChan = unalignedPointerCast (newData + channelListSize); for (int j = 0; j < newNumChannels; ++j) { @@ -390,10 +426,10 @@ class AudioBuffer { allocatedBytes = newTotalBytes; allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear); - channels = reinterpret_cast (allocatedData.get()); + channels = unalignedPointerCast (allocatedData.get()); } - auto* chan = reinterpret_cast (allocatedData + channelListSize); + auto* chan = unalignedPointerCast (allocatedData + channelListSize); for (int i = 0; i < newNumChannels; ++i) { @@ -417,6 +453,8 @@ class AudioBuffer will re-allocate memory internally and copy the existing data to this new area, so it will then stop directly addressing this memory. + The hasBeenCleared method will return false after this call. + @param dataToReferTo a pre-allocated array containing pointers to the data for each channel that should be used by this buffer. The buffer will only refer to this memory, it won't try to delete @@ -427,7 +465,7 @@ class AudioBuffer @param newNumSamples the number of samples to use - this must correspond to the size of the arrays passed in */ - void setDataToReferTo (Type** dataToReferTo, + void setDataToReferTo (Type* const* dataToReferTo, int newNumChannels, int newStartSample, int newNumSamples) @@ -457,6 +495,8 @@ class AudioBuffer will re-allocate memory internally and copy the existing data to this new area, so it will then stop directly addressing this memory. + The hasBeenCleared method will return false after this call. + @param dataToReferTo a pre-allocated array containing pointers to the data for each channel that should be used by this buffer. The buffer will only refer to this memory, it won't try to delete @@ -466,7 +506,7 @@ class AudioBuffer @param newNumSamples the number of samples to use - this must correspond to the size of the arrays passed in */ - void setDataToReferTo (Type** dataToReferTo, + void setDataToReferTo (Type* const* dataToReferTo, int newNumChannels, int newNumSamples) { @@ -474,8 +514,12 @@ class AudioBuffer } /** Resizes this buffer to match the given one, and copies all of its content across. + The source buffer can contain a different floating point type, so this can be used to convert between 32 and 64 bit float buffer types. + + The hasBeenCleared method will return false after this call if the other buffer + contains data. */ template void makeCopyOf (const AudioBuffer& other, bool avoidReallocating = false) @@ -502,13 +546,23 @@ class AudioBuffer } //============================================================================== - /** Clears all the samples in all channels. */ + /** Clears all the samples in all channels and marks the buffer as cleared. + + This method will do nothing if the buffer has been marked as cleared (i.e. the + hasBeenCleared method returns true.) + + @see hasBeenCleared, setNotClear + */ void clear() noexcept { if (! isClear) { for (int i = 0; i < numChannels; ++i) + { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4661) FloatVectorOperations::clear (channels[i], size); + JUCE_END_IGNORE_WARNINGS_MSVC + } isClear = true; } @@ -516,8 +570,15 @@ class AudioBuffer /** Clears a specified region of all the channels. + This will mark the buffer as cleared if the entire buffer contents are cleared. + For speed, this doesn't check whether the channel and sample number are in-range, so be careful! + + This method will do nothing if the buffer has been marked as cleared (i.e. the + hasBeenCleared method returns true.) + + @see hasBeenCleared, setNotClear */ void clear (int startSample, int numSamples) noexcept { @@ -525,11 +586,10 @@ class AudioBuffer if (! isClear) { - if (startSample == 0 && numSamples == size) - isClear = true; - for (int i = 0; i < numChannels; ++i) FloatVectorOperations::clear (channels[i] + startSample, numSamples); + + isClear = (startSample == 0 && numSamples == size); } } @@ -537,6 +597,11 @@ class AudioBuffer For speed, this doesn't check whether the channel and sample number are in-range, so be careful! + + This method will do nothing if the buffer has been marked as cleared (i.e. the + hasBeenCleared method returns true.) + + @see hasBeenCleared, setNotClear */ void clear (int channel, int startSample, int numSamples) noexcept { @@ -548,15 +613,26 @@ class AudioBuffer } /** Returns true if the buffer has been entirely cleared. + Note that this does not actually measure the contents of the buffer - it simply returns a flag that is set when the buffer is cleared, and which is reset whenever - functions like getWritePointer() are invoked. That means the method does not take - any time, but it may return false negatives when in fact the buffer is still empty. + functions like getWritePointer are invoked. That means the method is quick, but it + may return false negatives when in fact the buffer is still empty. */ bool hasBeenCleared() const noexcept { return isClear; } + /** Forces the internal cleared flag of the buffer to false. + + This may be useful in the case where you are holding on to a write pointer and call + the clear method before writing some data. You can then use this method to mark the + buffer as containing data so that subsequent clear calls will succeed. However a + better solution is to call getWritePointer each time you need to write data. + */ + void setNotClear() noexcept { isClear = false; } + //============================================================================== /** Returns a sample from the buffer. + The channel and index are not checked - they are expected to be in-range. If not, an assertion will be thrown, but in a release build, you're into 'undefined behaviour' territory. @@ -569,9 +645,12 @@ class AudioBuffer } /** Sets a sample in the buffer. + The channel and index are not checked - they are expected to be in-range. If not, an assertion will be thrown, but in a release build, you're into 'undefined behaviour' territory. + + The hasBeenCleared method will return false after this call. */ void setSample (int destChannel, int destSample, Type newValue) noexcept { @@ -582,9 +661,12 @@ class AudioBuffer } /** Adds a value to a sample in the buffer. + The channel and index are not checked - they are expected to be in-range. If not, an assertion will be thrown, but in a release build, you're into 'undefined behaviour' territory. + + The hasBeenCleared method will return false after this call. */ void addSample (int destChannel, int destSample, Type valueToAdd) noexcept { @@ -604,11 +686,11 @@ class AudioBuffer jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - if (gain != Type (1) && ! isClear) + if (! approximatelyEqual (gain, Type (1)) && ! isClear) { auto* d = channels[channel] + startSample; - if (gain == Type()) + if (approximatelyEqual (gain, Type())) FloatVectorOperations::clear (d, numSamples); else FloatVectorOperations::multiply (d, gain, numSamples); @@ -646,7 +728,7 @@ class AudioBuffer { if (! isClear) { - if (startGain == endGain) + if (approximatelyEqual (startGain, endGain)) { applyGain (channel, startSample, numSamples, startGain); } @@ -685,6 +767,9 @@ class AudioBuffer /** Adds samples from another buffer to this one. + The hasBeenCleared method will return false after this call if samples have + been added. + @param destChannel the channel within this buffer to add the samples to @param destStartSample the start sample within this buffer's channel @param source the source buffer to add from @@ -704,38 +789,48 @@ class AudioBuffer int numSamples, Type gainToApplyToSource = Type (1)) noexcept { - jassert (&source != this || sourceChannel != destChannel); + jassert (&source != this + || sourceChannel != destChannel + || sourceStartSample + numSamples <= destStartSample + || destStartSample + numSamples <= sourceStartSample); jassert (isPositiveAndBelow (destChannel, numChannels)); jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); - if (gainToApplyToSource != 0 && numSamples > 0 && ! source.isClear) + if (! approximatelyEqual (gainToApplyToSource, (Type) 0) && numSamples > 0 && ! source.isClear) { auto* d = channels[destChannel] + destStartSample; auto* s = source.channels[sourceChannel] + sourceStartSample; + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4661) + if (isClear) { isClear = false; - if (gainToApplyToSource != Type (1)) + if (! approximatelyEqual (gainToApplyToSource, Type (1))) FloatVectorOperations::copyWithMultiply (d, s, gainToApplyToSource, numSamples); else FloatVectorOperations::copy (d, s, numSamples); } else { - if (gainToApplyToSource != Type (1)) + if (! approximatelyEqual (gainToApplyToSource, Type (1))) FloatVectorOperations::addWithMultiply (d, s, gainToApplyToSource, numSamples); else FloatVectorOperations::add (d, s, numSamples); } + + JUCE_END_IGNORE_WARNINGS_MSVC } } /** Adds samples from an array of floats to one of the channels. + The hasBeenCleared method will return false after this call if samples have + been added. + @param destChannel the channel within this buffer to add the samples to @param destStartSample the start sample within this buffer's channel @param source the source data to use @@ -755,7 +850,7 @@ class AudioBuffer jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); jassert (source != nullptr); - if (gainToApplyToSource != 0 && numSamples > 0) + if (! approximatelyEqual (gainToApplyToSource, Type()) && numSamples > 0) { auto* d = channels[destChannel] + destStartSample; @@ -763,14 +858,14 @@ class AudioBuffer { isClear = false; - if (gainToApplyToSource != Type (1)) + if (! approximatelyEqual (gainToApplyToSource, Type (1))) FloatVectorOperations::copyWithMultiply (d, source, gainToApplyToSource, numSamples); else FloatVectorOperations::copy (d, source, numSamples); } else { - if (gainToApplyToSource != Type (1)) + if (! approximatelyEqual (gainToApplyToSource, Type (1))) FloatVectorOperations::addWithMultiply (d, source, gainToApplyToSource, numSamples); else FloatVectorOperations::add (d, source, numSamples); @@ -781,13 +876,20 @@ class AudioBuffer /** Adds samples from an array of floats, applying a gain ramp to them. + The hasBeenCleared method will return false after this call if samples have + been added. + @param destChannel the channel within this buffer to add the samples to @param destStartSample the start sample within this buffer's channel @param source the source data to use @param numSamples the number of samples to process @param startGain the gain to apply to the first sample (this is multiplied with the source samples before they are added to this buffer) - @param endGain the gain to apply to the final sample. The gain is linearly + @param endGain The gain that would apply to the sample after the final sample. + The gain that applies to the final sample is + (numSamples - 1) / numSamples * (endGain - startGain). This + ensures a continuous ramp when supplying the same value in + endGain and startGain in subsequent blocks. The gain is linearly interpolated between the first and last samples. */ void addFromWithRamp (int destChannel, @@ -797,7 +899,7 @@ class AudioBuffer Type startGain, Type endGain) noexcept { - if (startGain == endGain) + if (approximatelyEqual (startGain, endGain)) { addFrom (destChannel, destStartSample, source, numSamples, startGain); } @@ -810,7 +912,7 @@ class AudioBuffer if (numSamples > 0) { isClear = false; - const auto increment = (endGain - startGain) / numSamples; + const auto increment = (endGain - startGain) / (Type) numSamples; auto* d = channels[destChannel] + destStartSample; while (--numSamples >= 0) @@ -840,7 +942,10 @@ class AudioBuffer int sourceStartSample, int numSamples) noexcept { - jassert (&source != this || sourceChannel != destChannel); + jassert (&source != this + || sourceChannel != destChannel + || sourceStartSample + numSamples <= destStartSample + || destStartSample + numSamples <= sourceStartSample); jassert (isPositiveAndBelow (destChannel, numChannels)); jassert (destStartSample >= 0 && destStartSample + numSamples <= size); jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); @@ -865,6 +970,9 @@ class AudioBuffer /** Copies samples from an array of floats into one of the channels. + The hasBeenCleared method will return false after this call if samples have + been copied. + @param destChannel the channel within this buffer to copy the samples to @param destStartSample the start sample within this buffer's channel @param source the source buffer to read from @@ -890,6 +998,9 @@ class AudioBuffer /** Copies samples from an array of floats into one of the channels, applying a gain to it. + The hasBeenCleared method will return false after this call if samples have + been copied. + @param destChannel the channel within this buffer to copy the samples to @param destStartSample the start sample within this buffer's channel @param source the source buffer to read from @@ -912,9 +1023,9 @@ class AudioBuffer { auto* d = channels[destChannel] + destStartSample; - if (gain != Type (1)) + if (! approximatelyEqual (gain, Type (1))) { - if (gain == Type()) + if (approximatelyEqual (gain, Type())) { if (! isClear) FloatVectorOperations::clear (d, numSamples); @@ -935,13 +1046,20 @@ class AudioBuffer /** Copies samples from an array of floats into one of the channels, applying a gain ramp. + The hasBeenCleared method will return false after this call if samples have + been copied. + @param destChannel the channel within this buffer to copy the samples to @param destStartSample the start sample within this buffer's channel @param source the source buffer to read from @param numSamples the number of samples to process @param startGain the gain to apply to the first sample (this is multiplied with the source samples before they are copied to this buffer) - @param endGain the gain to apply to the final sample. The gain is linearly + @param endGain The gain that would apply to the sample after the final sample. + The gain that applies to the final sample is + (numSamples - 1) / numSamples * (endGain - startGain). This + ensures a continuous ramp when supplying the same value in + endGain and startGain in subsequent blocks. The gain is linearly interpolated between the first and last samples. @see addFrom @@ -953,7 +1071,7 @@ class AudioBuffer Type startGain, Type endGain) noexcept { - if (startGain == endGain) + if (approximatelyEqual (startGain, endGain)) { copyFrom (destChannel, destStartSample, source, numSamples, startGain); } @@ -966,7 +1084,7 @@ class AudioBuffer if (numSamples > 0) { isClear = false; - const auto increment = (endGain - startGain) / numSamples; + const auto increment = (endGain - startGain) / (Type) numSamples; auto* d = channels[destChannel] + destStartSample; while (--numSamples >= 0) @@ -1066,21 +1184,16 @@ class AudioBuffer private: //============================================================================== - int numChannels = 0, size = 0; - size_t allocatedBytes = 0; - Type** channels; - HeapBlock allocatedData; - Type* preallocatedChannelSpace[32]; - std::atomic isClear { false }; - void allocateData() { - static_assert (std::alignment_of::value <= std::alignment_of::value, + #if (! JUCE_GCC || (__GNUC__ * 100 + __GNUC_MINOR__) >= 409) + static_assert (alignof (Type) <= maxAlignment, "AudioBuffer cannot hold types with alignment requirements larger than that guaranteed by malloc"); + #endif jassert (size >= 0); auto channelListSize = (size_t) (numChannels + 1) * sizeof (Type*); - auto requiredSampleAlignment = std::alignment_of::value; + auto requiredSampleAlignment = std::alignment_of_v; size_t alignmentOverflow = channelListSize % requiredSampleAlignment; if (alignmentOverflow != 0) @@ -1088,8 +1201,8 @@ class AudioBuffer allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (Type) + channelListSize + 32; allocatedData.malloc (allocatedBytes); - channels = reinterpret_cast (allocatedData.get()); - auto chan = reinterpret_cast (allocatedData + channelListSize); + channels = unalignedPointerCast (allocatedData.get()); + auto chan = unalignedPointerCast (allocatedData + channelListSize); for (int i = 0; i < numChannels; ++i) { @@ -1113,7 +1226,7 @@ class AudioBuffer else { allocatedData.malloc (numChannels + 1, sizeof (Type*)); - channels = reinterpret_cast (allocatedData.get()); + channels = unalignedPointerCast (allocatedData.get()); } for (int i = 0; i < numChannels; ++i) @@ -1127,9 +1240,71 @@ class AudioBuffer isClear = false; } + /* On iOS/arm7 the alignment of `double` is greater than the alignment of + `std::max_align_t`, so we can't trust max_align_t. Instead, we query + lots of primitive types and use the maximum alignment of all of them. + */ + static constexpr size_t getMaxAlignment() noexcept + { + constexpr size_t alignments[] { alignof (std::max_align_t), + alignof (void*), + alignof (float), + alignof (double), + alignof (long double), + alignof (short int), + alignof (int), + alignof (long int), + alignof (long long int), + alignof (bool), + alignof (char), + alignof (char16_t), + alignof (char32_t), + alignof (wchar_t) }; + + size_t max = 0; + + for (const auto elem : alignments) + max = jmax (max, elem); + + return max; + } + + int numChannels = 0, size = 0; + size_t allocatedBytes = 0; + Type** channels; + HeapBlock allocatedData; + Type* preallocatedChannelSpace[32]; + bool isClear = false; + static constexpr size_t maxAlignment = getMaxAlignment(); + JUCE_LEAK_DETECTOR (AudioBuffer) }; +//============================================================================== +template +bool operator== (const AudioBuffer& a, const AudioBuffer& b) +{ + if (a.getNumChannels() != b.getNumChannels()) + return false; + + for (auto c = 0; c < a.getNumChannels(); ++c) + { + const auto begin = [c] (auto& x) { return x.getReadPointer (c); }; + const auto end = [c] (auto& x) { return x.getReadPointer (c) + x.getNumSamples(); }; + + if (! std::equal (begin (a), end (a), begin (b), end (b))) + return false; + } + + return true; +} + +template +bool operator!= (const AudioBuffer& a, const AudioBuffer& b) +{ + return ! (a == b); +} + //============================================================================== /** A multi-channel buffer of 32-bit floating point audio samples. diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index 26b33f29..8ade8760 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -30,7 +30,7 @@ namespace FloatVectorHelpers #define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); #if JUCE_USE_SSE_INTRINSICS - inline static bool isAligned (const void* p) noexcept + static bool isAligned (const void* p) noexcept { return (((pointer_sized_int) p) & 15) == 0; } @@ -104,13 +104,13 @@ namespace FloatVectorHelpers #define JUCE_BEGIN_VEC_OP \ using Mode = FloatVectorHelpers::ModeType::Mode; \ { \ - const int numLongOps = num / Mode::numParallel; + const auto numLongOps = num / Mode::numParallel; #define JUCE_FINISH_VEC_OP(normalOp) \ num &= (Mode::numParallel - 1); \ if (num == 0) return; \ } \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ JUCE_BEGIN_VEC_OP \ @@ -268,13 +268,13 @@ namespace FloatVectorHelpers using Mode = FloatVectorHelpers::ModeType::Mode; \ if (Mode::numParallel > 1) \ { \ - const int numLongOps = num / Mode::numParallel; + const auto numLongOps = num / Mode::numParallel; #define JUCE_FINISH_VEC_OP(normalOp) \ num &= (Mode::numParallel - 1); \ if (num == 0) return; \ } \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ JUCE_BEGIN_VEC_OP \ @@ -304,22 +304,22 @@ namespace FloatVectorHelpers //============================================================================== #else #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; + for (auto i = (decltype (num)) 0; i < num; ++i) normalOp; #endif //============================================================================== #define JUCE_VEC_LOOP(vecOp, srcLoad, dstLoad, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ + for (auto i = (decltype (numLongOps)) 0; i < numLongOps; ++i) \ { \ locals (srcLoad, dstLoad); \ dstStore (dest, vecOp); \ @@ -327,7 +327,7 @@ namespace FloatVectorHelpers } #define JUCE_VEC_LOOP_TWO_SOURCES(vecOp, src1Load, src2Load, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ + for (auto i = (decltype (numLongOps)) 0; i < numLongOps; ++i) \ { \ locals (src1Load, src2Load); \ dstStore (dest, vecOp); \ @@ -335,7 +335,7 @@ namespace FloatVectorHelpers } #define JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD(vecOp, src1Load, src2Load, dstLoad, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ + for (auto i = (decltype (numLongOps)) 0; i < numLongOps; ++i) \ { \ locals (src1Load, src2Load, dstLoad); \ dstStore (dest, vecOp); \ @@ -353,8 +353,8 @@ namespace FloatVectorHelpers union signMask64 { double d; uint64 i; }; #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - template struct ModeType { using Mode = BasicOps32; }; - template<> struct ModeType<8> { using Mode = BasicOps64; }; + template struct ModeType { using Mode = BasicOps32; }; + template <> struct ModeType<8> { using Mode = BasicOps64; }; template struct MinMax @@ -362,9 +362,10 @@ namespace FloatVectorHelpers using Type = typename Mode::Type; using ParallelType = typename Mode::ParallelType; - static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept + template + static Type findMinOrMax (const Type* src, Size num, const bool isMinimum) noexcept { - int numLongOps = num / Mode::numParallel; + auto numLongOps = num / Mode::numParallel; if (numLongOps > 1) { @@ -421,20 +422,24 @@ namespace FloatVectorHelpers num &= (Mode::numParallel - 1); src += Mode::numParallel; - for (int i = 0; i < num; ++i) + for (auto i = (decltype (num)) 0; i < num; ++i) result = isMinimum ? jmin (result, src[i]) : jmax (result, src[i]); return result; } - return isMinimum ? juce::findMinimum (src, num) - : juce::findMaximum (src, num); + if (num <= 0) + return 0; + + return isMinimum ? *std::min_element (src, src + num) + : *std::max_element (src, src + num); } - static Range findMinAndMax (const Type* src, int num) noexcept + template + static Range findMinAndMax (const Type* src, Size num) noexcept { - int numLongOps = num / Mode::numParallel; + auto numLongOps = num / Mode::numParallel; if (numLongOps > 1) { @@ -475,7 +480,7 @@ namespace FloatVectorHelpers num &= (Mode::numParallel - 1); src += Mode::numParallel; - for (int i = 0; i < num; ++i) + for (auto i = (decltype (num)) 0; i < num; ++i) result = result.getUnionWith (src[i]); return result; @@ -485,591 +490,999 @@ namespace FloatVectorHelpers } }; #endif -} //============================================================================== namespace { - #if JUCE_USE_VDSP_FRAMEWORK - // This casts away constness to account for slightly different vDSP function signatures - // in OSX 10.8 SDK and below. Can be safely removed once those SDKs are obsolete. - template - ValueType* osx108sdkCompatibilityCast (const ValueType* arg) noexcept { return const_cast (arg); } - #endif -} + template + void clear (float* dest, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclr (dest, 1, (vDSP_Length) num); + #else + zeromem (dest, (size_t) num * sizeof (float)); + #endif + } -//============================================================================== -void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclr (dest, 1, (size_t) num); - #else - zeromem (dest, (size_t) num * sizeof (float)); - #endif -} + template + void clear (double* dest, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclrD (dest, 1, (vDSP_Length) num); + #else + zeromem (dest, (size_t) num * sizeof (double)); + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclrD (dest, 1, (size_t) num); - #else - zeromem (dest, (size_t) num * sizeof (double)); - #endif -} + template + void fill (float* dest, float valueToFill, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vfill (&valueToFill, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, + val, + JUCE_LOAD_NONE, + const Mode::ParallelType val = Mode::load1 (valueToFill);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::fill (float* dest, float valueToFill, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vfill (&valueToFill, dest, 1, (size_t) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, - const Mode::ParallelType val = Mode::load1 (valueToFill);) - #endif -} + template + void fill (double* dest, double valueToFill, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vfillD (&valueToFill, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, + val, + JUCE_LOAD_NONE, + const Mode::ParallelType val = Mode::load1 (valueToFill);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::fill (double* dest, double valueToFill, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vfillD (&valueToFill, dest, 1, (size_t) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, - const Mode::ParallelType val = Mode::load1 (valueToFill);) - #endif -} + template + void copyWithMultiply (float* dest, const float* src, float multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmul (src, 1, &multiplier, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + Mode::mul (mult, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::copy (float* dest, const float* src, int num) noexcept -{ - memcpy (dest, src, (size_t) num * sizeof (float)); -} + template + void copyWithMultiply (double* dest, const double* src, double multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmulD (src, 1, &multiplier, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + Mode::mul (mult, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src, int num) noexcept -{ - memcpy (dest, src, (size_t) num * sizeof (double)); -} + template + void add (float* dest, float amount, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsadd (dest, 1, &amount, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, + Mode::add (d, amountToAdd), + JUCE_LOAD_DEST, + const Mode::ParallelType amountToAdd = Mode::load1 (amount);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmul (src, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} + template + void add (double* dest, double amount, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, + Mode::add (d, amountToAdd), + JUCE_LOAD_DEST, + const Mode::ParallelType amountToAdd = Mode::load1 (amount);) + } -void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmulD (src, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} + template + void add (float* dest, const float* src, float amount, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsadd (src, 1, &amount, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, + Mode::add (am, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType am = Mode::load1 (amount);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsadd (dest, 1, &amount, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, - const Mode::ParallelType amountToAdd = Mode::load1 (amount);) - #endif -} + template + void add (double* dest, const double* src, double amount, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsaddD (src, 1, &amount, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, + Mode::add (am, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType am = Mode::load1 (amount);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, - const Mode::ParallelType amountToAdd = Mode::load1 (amount);) -} + template + void add (float* dest, const float* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vadd (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], + Mode::add (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, float amount, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsadd (osx108sdkCompatibilityCast (src), 1, &amount, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType am = Mode::load1 (amount);) - #endif -} + template + void add (double* dest, const double* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vaddD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], + Mode::add (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, double amount, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsaddD (osx108sdkCompatibilityCast (src), 1, &amount, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType am = Mode::load1 (amount);) - #endif -} + template + void add (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vadd (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], + Mode::add (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vadd (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} + template + void add (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vaddD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], + Mode::add (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vaddD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} + template + void subtract (float* dest, const float* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsub (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], + Mode::sub (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vadd (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void subtract (double* dest, const double* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsubD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], + Mode::sub (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vaddD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void subtract (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsub (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], + Mode::sub (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsub (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} + template + void subtract (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsubD (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], + Mode::sub (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsubD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} + template + void addWithMultiply (float* dest, const float* src, float multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsma (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, + Mode::add (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsub (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void addWithMultiply (double* dest, const double* src, double multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmaD (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, + Mode::add (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsubD (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void addWithMultiply (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vma ((float*) src1, 1, (float*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], + Mode::add (d, Mode::mul (s1, s2)), + JUCE_LOAD_SRC1_SRC2_DEST, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsma (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} + template + void addWithMultiply (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmaD ((double*) src1, 1, (double*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], + Mode::add (d, Mode::mul (s1, s2)), + JUCE_LOAD_SRC1_SRC2_DEST, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmaD (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} + template + void subtractWithMultiply (float* dest, const float* src, float multiplier, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i] * multiplier, + Mode::sub (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + } -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vma ((float*) src1, 1, (float*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], Mode::add (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void subtractWithMultiply (double* dest, const double* src, double multiplier, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i] * multiplier, + Mode::sub (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + } -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmaD ((double*) src1, 1, (double*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], Mode::add (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} + template + void subtractWithMultiply (float* dest, const float* src1, const float* src2, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] -= src1[i] * src2[i], + Mode::sub (d, Mode::mul (s1, s2)), + JUCE_LOAD_SRC1_SRC2_DEST, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + } -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i] * multiplier, Mode::sub (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, + template + void subtractWithMultiply (double* dest, const double* src1, const double* src2, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] -= src1[i] * src2[i], + Mode::sub (d, Mode::mul (s1, s2)), + JUCE_LOAD_SRC1_SRC2_DEST, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + } + + template + void multiply (float* dest, const float* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmul (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], + Mode::mul (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } + + template + void multiply (double* dest, const double* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmulD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], + Mode::mul (d, s), + JUCE_LOAD_SRC_DEST, + JUCE_INCREMENT_SRC_DEST, ) + #endif + } + + template + void multiply (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmul (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], + Mode::mul (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void multiply (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmulD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], + Mode::mul (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void multiply (float* dest, float multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmul (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, + Mode::mul (d, mult), + JUCE_LOAD_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) -} + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i] * multiplier, Mode::sub (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, + template + void multiply (double* dest, double multiplier, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmulD (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, + Mode::mul (d, mult), + JUCE_LOAD_DEST, const Mode::ParallelType mult = Mode::load1 (multiplier);) -} + #endif + } -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] -= src1[i] * src2[i], Mode::sub (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) -} + template + void multiply (float* dest, const float* src, float multiplier, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + Mode::mul (mult, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + } -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept + template + void multiply (double* dest, const double* src, double multiplier, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + Mode::mul (mult, s), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + } + + template + void negate (float* dest, const float* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vneg ((float*) src, 1, dest, 1, (vDSP_Length) num); + #else + copyWithMultiply (dest, src, -1.0f, num); + #endif + } + + template + void negate (double* dest, const double* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vnegD ((double*) src, 1, dest, 1, (vDSP_Length) num); + #else + copyWithMultiply (dest, src, -1.0f, num); + #endif + } + + template + void abs (float* dest, const float* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num); + #else + [[maybe_unused]] FloatVectorHelpers::signMask32 signMask; + signMask.i = 0x7fffffffUL; + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = std::abs (src[i]), + Mode::bit_and (s, mask), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mask = Mode::load1 (signMask.f);) + #endif + } + + template + void abs (double* dest, const double* src, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vabsD ((double*) src, 1, dest, 1, (vDSP_Length) num); + #else + [[maybe_unused]] FloatVectorHelpers::signMask64 signMask; + signMask.i = 0x7fffffffffffffffULL; + + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = std::abs (src[i]), + Mode::bit_and (s, mask), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mask = Mode::load1 (signMask.d);) + #endif + } + + template + void min (float* dest, const float* src, float comp, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), + Mode::min (s, cmp), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) + } + + template + void min (double* dest, const double* src, double comp, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), + Mode::min (s, cmp), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) + } + + template + void min (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmin ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), + Mode::min (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void min (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vminD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), + Mode::min (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void max (float* dest, const float* src, float comp, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), + Mode::max (s, cmp), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) + } + + template + void max (double* dest, const double* src, double comp, Size num) noexcept + { + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), + Mode::max (s, cmp), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType cmp = Mode::load1 (comp);) + } + + template + void max (float* dest, const float* src1, const float* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmax ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), + Mode::max (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void max (double* dest, const double* src1, const double* src2, Size num) noexcept + { + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmaxD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), + Mode::max (s1, s2), + JUCE_LOAD_SRC1_SRC2, + JUCE_INCREMENT_SRC1_SRC2_DEST, ) + #endif + } + + template + void clip (float* dest, const float* src, float low, float high, Size num) noexcept + { + jassert (high >= low); + + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclip ((float*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), + Mode::max (Mode::min (s, hi), lo), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType lo = Mode::load1 (low); + const Mode::ParallelType hi = Mode::load1 (high);) + #endif + } + + template + void clip (double* dest, const double* src, double low, double high, Size num) noexcept + { + jassert (high >= low); + + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclipD ((double*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), + Mode::max (Mode::min (s, hi), lo), + JUCE_LOAD_SRC, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType lo = Mode::load1 (low); + const Mode::ParallelType hi = Mode::load1 (high);) + #endif + } + + template + Range findMinAndMax (const float* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinAndMax (src, num); + #else + return Range::findMinAndMax (src, num); + #endif + } + + template + Range findMinAndMax (const double* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinAndMax (src, num); + #else + return Range::findMinAndMax (src, num); + #endif + } + + template + float findMinimum (const float* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); + #else + return juce::findMinimum (src, num); + #endif + } + + template + double findMinimum (const double* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); + #else + return juce::findMinimum (src, num); + #endif + } + + template + float findMaximum (const float* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); + #else + return juce::findMaximum (src, num); + #endif + } + + template + double findMaximum (const double* src, Size num) noexcept + { + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); + #else + return juce::findMaximum (src, num); + #endif + } + + template + void convertFixedToFloat (float* dest, const int* src, float multiplier, Size num) noexcept + { + #if JUCE_USE_ARM_NEON + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = (float) src[i] * multiplier, + vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier), + JUCE_LOAD_NONE, + JUCE_INCREMENT_SRC_DEST, ) + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = (float) src[i] * multiplier, + Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 (reinterpret_cast (src)))), + JUCE_LOAD_NONE, + JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif + } + +} // namespace +} // namespace FloatVectorHelpers + +//============================================================================== +template +void JUCE_CALLTYPE FloatVectorOperationsBase::clear (FloatType* dest, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] -= src1[i] * src2[i], Mode::sub (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) + FloatVectorHelpers::clear (dest, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::fill (FloatType* dest, + FloatType valueToFill, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmul (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif + FloatVectorHelpers::fill (dest, valueToFill, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::copy (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmulD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif + memcpy (dest, src, (size_t) numValues * sizeof (FloatType)); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src1, const float* src2, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::copyWithMultiply (FloatType* dest, + const FloatType* src, + FloatType multiplier, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmul (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::copyWithMultiply (dest, src, multiplier, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src1, const double* src2, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::add (FloatType* dest, + FloatType amountToAdd, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmulD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::add (dest, amountToAdd, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplier, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::add (FloatType* dest, + const FloatType* src, + FloatType amount, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmul (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif + FloatVectorHelpers::add (dest, src, amount, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::add (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmulD (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif + FloatVectorHelpers::add (dest, src, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, float multiplier, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::add (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) + FloatVectorHelpers::add (dest, src1, src2, num); } -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, double multiplier, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::subtract (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) + FloatVectorHelpers::subtract (dest, src, numValues); } -void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::subtract (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vneg ((float*) src, 1, dest, 1, (vDSP_Length) num); - #else - copyWithMultiply (dest, src, -1.0f, num); - #endif + FloatVectorHelpers::subtract (dest, src1, src2, num); } -void FloatVectorOperations::negate (double* dest, const double* src, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::addWithMultiply (FloatType* dest, + const FloatType* src, + FloatType multiplier, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vnegD ((double*) src, 1, dest, 1, (vDSP_Length) num); - #else - copyWithMultiply (dest, src, -1.0f, num); - #endif + FloatVectorHelpers::addWithMultiply (dest, src, multiplier, numValues); } -void FloatVectorOperations::abs (float* dest, const float* src, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::addWithMultiply (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num); - #else - FloatVectorHelpers::signMask32 signMask; - signMask.i = 0x7fffffffUL; - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = std::abs (src[i]), Mode::bit_and (s, mask), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mask = Mode::load1 (signMask.f);) - - ignoreUnused (signMask); - #endif + FloatVectorHelpers::addWithMultiply (dest, src1, src2, num); } -void FloatVectorOperations::abs (double* dest, const double* src, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::subtractWithMultiply (FloatType* dest, + const FloatType* src, + FloatType multiplier, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vabsD ((double*) src, 1, dest, 1, (vDSP_Length) num); - #else - FloatVectorHelpers::signMask64 signMask; - signMask.i = 0x7fffffffffffffffULL; - - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = std::abs (src[i]), Mode::bit_and (s, mask), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mask = Mode::load1 (signMask.d);) - - ignoreUnused (signMask); - #endif + FloatVectorHelpers::subtractWithMultiply (dest, src, multiplier, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::subtractWithMultiply (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - #if JUCE_USE_ARM_NEON - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, - vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier), - JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, ) - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = (float) src[i] * multiplier, - Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 (reinterpret_cast (src)))), - JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif + FloatVectorHelpers::subtractWithMultiply (dest, src1, src2, num); } -void JUCE_CALLTYPE FloatVectorOperations::min (float* dest, const float* src, float comp, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::multiply (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), Mode::min (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) + FloatVectorHelpers::multiply (dest, src, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::min (double* dest, const double* src, double comp, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::multiply (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), Mode::min (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) + FloatVectorHelpers::multiply (dest, src1, src2, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::min (float* dest, const float* src1, const float* src2, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::multiply (FloatType* dest, + FloatType multiplier, + CountType numValues) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmin ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), Mode::min (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::multiply (dest, multiplier, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::min (double* dest, const double* src1, const double* src2, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::multiply (FloatType* dest, + const FloatType* src, + FloatType multiplier, + CountType num) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vminD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), Mode::min (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::multiply (dest, src, multiplier, num); } -void JUCE_CALLTYPE FloatVectorOperations::max (float* dest, const float* src, float comp, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::negate (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), Mode::max (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) + FloatVectorHelpers::negate (dest, src, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::max (double* dest, const double* src, double comp, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::abs (FloatType* dest, + const FloatType* src, + CountType numValues) noexcept { - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), Mode::max (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) + FloatVectorHelpers::abs (dest, src, numValues); } -void JUCE_CALLTYPE FloatVectorOperations::max (float* dest, const float* src1, const float* src2, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::min (FloatType* dest, + const FloatType* src, + FloatType comp, + CountType num) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmax ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), Mode::max (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::min (dest, src, comp, num); } -void JUCE_CALLTYPE FloatVectorOperations::max (double* dest, const double* src1, const double* src2, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::min (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmaxD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), Mode::max (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif + FloatVectorHelpers::min (dest, src1, src2, num); } -void JUCE_CALLTYPE FloatVectorOperations::clip (float* dest, const float* src, float low, float high, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::max (FloatType* dest, + const FloatType* src, + FloatType comp, + CountType num) noexcept { - jassert(high >= low); - - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclip ((float*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), Mode::max (Mode::min (s, hi), lo), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType lo = Mode::load1 (low); const Mode::ParallelType hi = Mode::load1 (high);) - #endif + FloatVectorHelpers::max (dest, src, comp, num); } -void JUCE_CALLTYPE FloatVectorOperations::clip (double* dest, const double* src, double low, double high, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::max (FloatType* dest, + const FloatType* src1, + const FloatType* src2, + CountType num) noexcept { - jassert(high >= low); - - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclipD ((double*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), Mode::max (Mode::min (s, hi), lo), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType lo = Mode::load1 (low); const Mode::ParallelType hi = Mode::load1 (high);) - #endif + FloatVectorHelpers::max (dest, src1, src2, num); } -Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num) noexcept +template +void JUCE_CALLTYPE FloatVectorOperationsBase::clip (FloatType* dest, + const FloatType* src, + FloatType low, + FloatType high, + CountType num) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinAndMax (src, num); - #else - return Range::findMinAndMax (src, num); - #endif + FloatVectorHelpers::clip (dest, src, low, high, num); } -Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const double* src, int num) noexcept +template +Range JUCE_CALLTYPE FloatVectorOperationsBase::findMinAndMax (const FloatType* src, + CountType numValues) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinAndMax (src, num); - #else - return Range::findMinAndMax (src, num); - #endif + return FloatVectorHelpers::findMinAndMax (src, numValues); } -float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int num) noexcept +template +FloatType JUCE_CALLTYPE FloatVectorOperationsBase::findMinimum (const FloatType* src, + CountType numValues) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); - #else - return juce::findMinimum (src, num); - #endif + return FloatVectorHelpers::findMinimum (src, numValues); } -double JUCE_CALLTYPE FloatVectorOperations::findMinimum (const double* src, int num) noexcept +template +FloatType JUCE_CALLTYPE FloatVectorOperationsBase::findMaximum (const FloatType* src, + CountType numValues) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); - #else - return juce::findMinimum (src, num); - #endif + return FloatVectorHelpers::findMaximum (src, numValues); } -float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int num) noexcept +template struct FloatVectorOperationsBase; +template struct FloatVectorOperationsBase; +template struct FloatVectorOperationsBase; +template struct FloatVectorOperationsBase; + +void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, size_t num) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); - #else - return juce::findMaximum (src, num); - #endif + FloatVectorHelpers::convertFixedToFloat (dest, src, multiplier, num); } -double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int num) noexcept +void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); - #else - return juce::findMaximum (src, num); - #endif + FloatVectorHelpers::convertFixedToFloat (dest, src, multiplier, num); } intptr_t JUCE_CALLTYPE FloatVectorOperations::getFpStatusRegister() noexcept { intptr_t fpsr = 0; - #if JUCE_INTEL && JUCE_USE_SSE_INTRINSICS + #if JUCE_INTEL && JUCE_USE_SSE_INTRINSICS fpsr = static_cast (_mm_getcsr()); - #elif defined (__arm64__) || defined (__aarch64__) || JUCE_USE_ARM_NEON - #if defined (__arm64__) || defined (__aarch64__) - asm volatile("mrs %0, fpcr" : "=r" (fpsr)); + #elif (JUCE_64BIT && JUCE_ARM) || JUCE_USE_ARM_NEON + #if _MSC_VER + // _control87 returns static values for x86 bits that don't exist on arm + // to emulate x86 behaviour. We are only ever interested in de-normal bits + // so mask out only those. + fpsr = (intptr_t) (_control87 (0, 0) & _MCW_DN); + #else + #if JUCE_64BIT + asm volatile("mrs %0, fpcr" + : "=r"(fpsr)); #elif JUCE_USE_ARM_NEON - asm volatile("vmrs %0, fpscr" : "=r" (fpsr)); + asm volatile("vmrs %0, fpscr" + : "=r"(fpsr)); #endif - #else - #if ! (defined (JUCE_INTEL) || defined (JUCE_ARM)) + #endif + #else + #if ! (defined (JUCE_INTEL) || defined (JUCE_ARM)) jassertfalse; // No support for getting the floating point status register for your platform - #endif #endif + #endif return fpsr; } -void JUCE_CALLTYPE FloatVectorOperations::setFpStatusRegister (intptr_t fpsr) noexcept +void JUCE_CALLTYPE FloatVectorOperations::setFpStatusRegister ([[maybe_unused]] intptr_t fpsr) noexcept { - #if JUCE_INTEL && JUCE_USE_SSE_INTRINSICS - auto fpsr_w = static_cast (fpsr); + #if JUCE_INTEL && JUCE_USE_SSE_INTRINSICS + // the volatile keyword here is needed to workaround a bug in AppleClang 13.0 + // which aggressively optimises away the variable otherwise + volatile auto fpsr_w = static_cast (fpsr); _mm_setcsr (fpsr_w); - #elif defined (__arm64__) || defined (__aarch64__) || JUCE_USE_ARM_NEON - #if defined (__arm64__) || defined (__aarch64__) - asm volatile("msr fpcr, %0" : : "ri" (fpsr)); + #elif (JUCE_64BIT && JUCE_ARM) || JUCE_USE_ARM_NEON + #if _MSC_VER + _control87 ((unsigned int) fpsr, _MCW_DN); + #else + #if JUCE_64BIT + asm volatile("msr fpcr, %0" + : + : "ri"(fpsr)); #elif JUCE_USE_ARM_NEON - asm volatile("vmsr fpscr, %0" : : "ri" (fpsr)); + asm volatile("vmsr fpscr, %0" + : + : "ri"(fpsr)); #endif - #else - #if ! (defined (JUCE_INTEL) || defined (JUCE_ARM)) + #endif + #else + #if ! (defined (JUCE_INTEL) || defined (JUCE_ARM)) jassertfalse; // No support for getting the floating point status register for your platform - #endif - ignoreUnused (fpsr); #endif + #endif } -void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept +void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode ([[maybe_unused]] bool shouldEnable) noexcept { - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || (JUCE_64BIT && JUCE_ARM)) #if JUCE_USE_SSE_INTRINSICS intptr_t mask = _MM_FLUSH_ZERO_MASK; #else /*JUCE_USE_ARM_NEON*/ @@ -1080,13 +1493,12 @@ void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnab #if ! (defined (JUCE_INTEL) || defined (JUCE_ARM)) jassertfalse; // No support for flush to zero mode on your platform #endif - ignoreUnused (shouldEnable); #endif } -void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport (bool shouldDisable) noexcept +void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport ([[maybe_unused]] bool shouldDisable) noexcept { - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || (JUCE_64BIT && JUCE_ARM)) #if JUCE_USE_SSE_INTRINSICS intptr_t mask = 0x8040; #else /*JUCE_USE_ARM_NEON*/ @@ -1095,7 +1507,6 @@ void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport (bool setFpStatusRegister ((getFpStatusRegister() & (~mask)) | (shouldDisable ? mask : 0)); #else - ignoreUnused (shouldDisable); #if ! (defined (JUCE_INTEL) || defined (JUCE_ARM)) jassertfalse; // No support for disable denormals mode on your platform @@ -1105,7 +1516,7 @@ void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport (bool bool JUCE_CALLTYPE FloatVectorOperations::areDenormalsDisabled() noexcept { - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || (JUCE_64BIT && JUCE_ARM)) #if JUCE_USE_SSE_INTRINSICS intptr_t mask = 0x8040; #else /*JUCE_USE_ARM_NEON*/ @@ -1120,7 +1531,7 @@ bool JUCE_CALLTYPE FloatVectorOperations::areDenormalsDisabled() noexcept ScopedNoDenormals::ScopedNoDenormals() noexcept { - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || (JUCE_64BIT && JUCE_ARM)) #if JUCE_USE_SSE_INTRINSICS intptr_t mask = 0x8040; #else /*JUCE_USE_ARM_NEON*/ @@ -1134,9 +1545,9 @@ ScopedNoDenormals::ScopedNoDenormals() noexcept ScopedNoDenormals::~ScopedNoDenormals() noexcept { - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || (JUCE_64BIT && JUCE_ARM)) FloatVectorOperations::setFpStatusRegister (fpsr); - #endif + #endif } @@ -1144,7 +1555,7 @@ ScopedNoDenormals::~ScopedNoDenormals() noexcept //============================================================================== #if JUCE_UNIT_TESTS -class FloatVectorOperationsTests : public UnitTest +class FloatVectorOperationsTests final : public UnitTest { public: FloatVectorOperationsTests() @@ -1160,7 +1571,7 @@ class FloatVectorOperationsTests : public UnitTest const int num = random.nextInt (range) + 1; HeapBlock buffer1 (num + 16), buffer2 (num + 16); - HeapBlock buffer3 (num + 16); + HeapBlock buffer3 (num + 16, true); #if JUCE_ARM ValueType* const data1 = buffer1; @@ -1265,7 +1676,7 @@ class FloatVectorOperationsTests : public UnitTest static bool areAllValuesEqual (const ValueType* d, int num, ValueType target) { while (--num >= 0) - if (*d++ != target) + if (! exactlyEqual (*d++, target)) return false; return true; diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h index b43c820e..de919eac 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -34,185 +34,158 @@ class ScopedNoDenormals; //============================================================================== /** - A collection of simple vector operations on arrays of floats, accelerated with - SIMD instructions where possible. + A collection of simple vector operations on arrays of floating point numbers, + accelerated with SIMD instructions where possible, usually accessed from + the FloatVectorOperations class. - @tags{Audio} -*/ -class JUCE_API FloatVectorOperations -{ -public: - //============================================================================== - /** Clears a vector of floats. */ - static void JUCE_CALLTYPE clear (float* dest, int numValues) noexcept; - - /** Clears a vector of doubles. */ - static void JUCE_CALLTYPE clear (double* dest, int numValues) noexcept; + @code + float data[64]; - /** Copies a repeated value into a vector of floats. */ - static void JUCE_CALLTYPE fill (float* dest, float valueToFill, int numValues) noexcept; + // The following two function calls are equivalent: + FloatVectorOperationsBase::clear (data, 64); + FloatVectorOperations::clear (data, 64); + @endcode - /** Copies a repeated value into a vector of doubles. */ - static void JUCE_CALLTYPE fill (double* dest, double valueToFill, int numValues) noexcept; + @see FloatVectorOperations - /** Copies a vector of floats. */ - static void JUCE_CALLTYPE copy (float* dest, const float* src, int numValues) noexcept; - - /** Copies a vector of doubles. */ - static void JUCE_CALLTYPE copy (double* dest, const double* src, int numValues) noexcept; + @tags{Audio} +*/ +template +struct FloatVectorOperationsBase +{ + /** Clears a vector of floating point numbers. */ + static void JUCE_CALLTYPE clear (FloatType* dest, CountType numValues) noexcept; - /** Copies a vector of floats, multiplying each value by a given multiplier */ - static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; + /** Copies a repeated value into a vector of floating point numbers. */ + static void JUCE_CALLTYPE fill (FloatType* dest, FloatType valueToFill, CountType numValues) noexcept; - /** Copies a vector of doubles, multiplying each value by a given multiplier */ - static void JUCE_CALLTYPE copyWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; + /** Copies a vector of floating point numbers. */ + static void JUCE_CALLTYPE copy (FloatType* dest, const FloatType* src, CountType numValues) noexcept; - /** Adds a fixed value to the destination values. */ - static void JUCE_CALLTYPE add (float* dest, float amountToAdd, int numValues) noexcept; + /** Copies a vector of floating point numbers, multiplying each value by a given multiplier */ + static void JUCE_CALLTYPE copyWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept; /** Adds a fixed value to the destination values. */ - static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept; - - /** Adds a fixed value to each source value and stores it in the destination array. */ - static void JUCE_CALLTYPE add (float* dest, const float* src, float amount, int numValues) noexcept; + static void JUCE_CALLTYPE add (FloatType* dest, FloatType amountToAdd, CountType numValues) noexcept; /** Adds a fixed value to each source value and stores it in the destination array. */ - static void JUCE_CALLTYPE add (double* dest, const double* src, double amount, int numValues) noexcept; - - /** Adds the source values to the destination values. */ - static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept; + static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src, FloatType amount, CountType numValues) noexcept; /** Adds the source values to the destination values. */ - static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept; + static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src, CountType numValues) noexcept; /** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE add (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE add (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Subtracts the source values from the destination values. */ - static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept; + static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; /** Subtracts the source values from the destination values. */ - static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept; + static void JUCE_CALLTYPE subtract (FloatType* dest, const FloatType* src, CountType numValues) noexcept; /** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE subtract (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE subtract (double* dest, const double* src1, const double* src2, int num) noexcept; + static void JUCE_CALLTYPE subtract (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; - - /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; + static void JUCE_CALLTYPE addWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept; /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Multiplies each source value by the given multiplier, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; + static void JUCE_CALLTYPE addWithMultiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; /** Multiplies each source value by the given multiplier, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; - - /** Multiplies each source1 value by the corresponding source2 value, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept; + static void JUCE_CALLTYPE subtractWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept; /** Multiplies each source1 value by the corresponding source2 value, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Multiplies the destination values by the source values. */ - static void JUCE_CALLTYPE multiply (float* dest, const float* src, int numValues) noexcept; + static void JUCE_CALLTYPE subtractWithMultiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; /** Multiplies the destination values by the source values. */ - static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept; + static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src, CountType numValues) noexcept; - /** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */ - static void JUCE_CALLTYPE multiply (float* dest, const float* src1, const float* src2, int numValues) noexcept; - - /** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */ - static void JUCE_CALLTYPE multiply (double* dest, const double* src1, const double* src2, int numValues) noexcept; + /** Multiplies each source1 value by the corresponding source2 value, then stores it in the destination array. */ + static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType numValues) noexcept; /** Multiplies each of the destination values by a fixed multiplier. */ - static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept; - - /** Multiplies each of the destination values by a fixed multiplier. */ - static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept; - - /** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */ - static void JUCE_CALLTYPE multiply (float* dest, const float* src, float multiplier, int num) noexcept; + static void JUCE_CALLTYPE multiply (FloatType* dest, FloatType multiplier, CountType numValues) noexcept; /** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */ - static void JUCE_CALLTYPE multiply (double* dest, const double* src, double multiplier, int num) noexcept; - - /** Copies a source vector to a destination, negating each value. */ - static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept; + static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType num) noexcept; /** Copies a source vector to a destination, negating each value. */ - static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept; - - /** Copies a source vector to a destination, taking the absolute of each value. */ - static void JUCE_CALLTYPE abs (float* dest, const float* src, int numValues) noexcept; + static void JUCE_CALLTYPE negate (FloatType* dest, const FloatType* src, CountType numValues) noexcept; /** Copies a source vector to a destination, taking the absolute of each value. */ - static void JUCE_CALLTYPE abs (double* dest, const double* src, int numValues) noexcept; - - /** Converts a stream of integers to floats, multiplying each one by the given multiplier. */ - static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept; + static void JUCE_CALLTYPE abs (FloatType* dest, const FloatType* src, CountType numValues) noexcept; /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE min (float* dest, const float* src, float comp, int num) noexcept; - - /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE min (double* dest, const double* src, double comp, int num) noexcept; - - /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE min (float* dest, const float* src1, const float* src2, int num) noexcept; + static void JUCE_CALLTYPE min (FloatType* dest, const FloatType* src, FloatType comp, CountType num) noexcept; /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE min (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE max (float* dest, const float* src, float comp, int num) noexcept; + static void JUCE_CALLTYPE min (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE max (double* dest, const double* src, double comp, int num) noexcept; - - /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE max (float* dest, const float* src1, const float* src2, int num) noexcept; + static void JUCE_CALLTYPE max (FloatType* dest, const FloatType* src, FloatType comp, CountType num) noexcept; /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE max (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ - static void JUCE_CALLTYPE clip (float* dest, const float* src, float low, float high, int num) noexcept; + static void JUCE_CALLTYPE max (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ - static void JUCE_CALLTYPE clip (double* dest, const double* src, double low, double high, int num) noexcept; + static void JUCE_CALLTYPE clip (FloatType* dest, const FloatType* src, FloatType low, FloatType high, CountType num) noexcept; /** Finds the minimum and maximum values in the given array. */ - static Range JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept; - - /** Finds the minimum and maximum values in the given array. */ - static Range JUCE_CALLTYPE findMinAndMax (const double* src, int numValues) noexcept; - - /** Finds the minimum value in the given array. */ - static float JUCE_CALLTYPE findMinimum (const float* src, int numValues) noexcept; + static Range JUCE_CALLTYPE findMinAndMax (const FloatType* src, CountType numValues) noexcept; /** Finds the minimum value in the given array. */ - static double JUCE_CALLTYPE findMinimum (const double* src, int numValues) noexcept; + static FloatType JUCE_CALLTYPE findMinimum (const FloatType* src, CountType numValues) noexcept; /** Finds the maximum value in the given array. */ - static float JUCE_CALLTYPE findMaximum (const float* src, int numValues) noexcept; + static FloatType JUCE_CALLTYPE findMaximum (const FloatType* src, CountType numValues) noexcept; +}; - /** Finds the maximum value in the given array. */ - static double JUCE_CALLTYPE findMaximum (const double* src, int numValues) noexcept; +#if ! DOXYGEN +namespace detail +{ + +template +struct NameForwarder : public Bases... +{ + using Bases::clear..., + Bases::fill..., + Bases::copy..., + Bases::copyWithMultiply..., + Bases::add..., + Bases::subtract..., + Bases::addWithMultiply..., + Bases::subtractWithMultiply..., + Bases::multiply..., + Bases::negate..., + Bases::abs..., + Bases::min..., + Bases::max..., + Bases::clip..., + Bases::findMinAndMax..., + Bases::findMinimum..., + Bases::findMaximum...; +}; + +} // namespace detail +#endif + +//============================================================================== +/** + A collection of simple vector operations on arrays of floating point numbers, + accelerated with SIMD instructions where possible and providing all methods + from FloatVectorOperationsBase. + + @see FloatVectorOperationsBase + + @tags{Audio} +*/ +class JUCE_API FloatVectorOperations : public detail::NameForwarder, + FloatVectorOperationsBase, + FloatVectorOperationsBase, + FloatVectorOperationsBase> +{ +public: + static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept; + + static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, size_t num) noexcept; /** This method enables or disables the SSE/NEON flush-to-zero mode. */ static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept; @@ -249,7 +222,7 @@ class ScopedNoDenormals ~ScopedNoDenormals() noexcept; private: - #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) + #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || (JUCE_64BIT && JUCE_ARM)) intptr_t fpsr; #endif }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp index 74cfe45c..777575e9 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -39,13 +39,18 @@ #include #endif -#ifndef JUCE_USE_VDSP_FRAMEWORK - #define JUCE_USE_VDSP_FRAMEWORK 1 -#endif +#if JUCE_MAC || JUCE_IOS + #ifndef JUCE_USE_VDSP_FRAMEWORK + #define JUCE_USE_VDSP_FRAMEWORK 1 + #endif + + #if JUCE_USE_VDSP_FRAMEWORK + #include + #endif + + #include "native/juce_AudioWorkgroup_mac.h" -#if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK - #include -#else +#elif JUCE_USE_VDSP_FRAMEWORK #undef JUCE_USE_VDSP_FRAMEWORK #endif @@ -59,7 +64,8 @@ #include "buffers/juce_AudioProcessLoadMeasurer.cpp" #include "utilities/juce_IIRFilter.cpp" #include "utilities/juce_LagrangeInterpolator.cpp" -#include "utilities/juce_CatmullRomInterpolator.cpp" +#include "utilities/juce_WindowedSincInterpolator.cpp" +#include "utilities/juce_Interpolators.cpp" #include "utilities/juce_SmoothedValue.cpp" #include "midi/juce_MidiBuffer.cpp" #include "midi/juce_MidiFile.cpp" @@ -84,4 +90,19 @@ #include "sources/juce_ResamplingAudioSource.cpp" #include "sources/juce_ReverbAudioSource.cpp" #include "sources/juce_ToneGeneratorAudioSource.cpp" +#include "sources/juce_PositionableAudioSource.cpp" #include "synthesisers/juce_Synthesiser.cpp" +#include "audio_play_head/juce_AudioPlayHead.cpp" +#include "midi/juce_MidiDataConcatenator.h" +#include "midi/ump/juce_UMP.h" +#include "midi/ump/juce_UMPUtils.cpp" +#include "midi/ump/juce_UMPView.cpp" +#include "midi/ump/juce_UMPSysEx7.cpp" +#include "midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp" +#include "midi/ump/juce_UMPIterator.cpp" +#include "utilities/juce_AudioWorkgroup.cpp" + +#if JUCE_UNIT_TESTS + #include "utilities/juce_ADSR_test.cpp" + #include "midi/ump/juce_UMP_test.cpp" +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h index 60f11a76..8a049267 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,18 +25,19 @@ The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. For details about the syntax and how to create or use a module, see the - JUCE Module Format.txt file. + JUCE Module Format.md file. BEGIN_JUCE_MODULE_DECLARATION ID: juce_audio_basics vendor: juce - version: 5.4.7 + version: 7.0.10 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 + minimumCppStandard: 17 dependencies: juce_core OSXFrameworks: Accelerate @@ -82,14 +83,16 @@ //============================================================================== #include "buffers/juce_AudioDataConverters.h" +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4661) #include "buffers/juce_FloatVectorOperations.h" +JUCE_END_IGNORE_WARNINGS_MSVC #include "buffers/juce_AudioSampleBuffer.h" #include "buffers/juce_AudioChannelSet.h" #include "buffers/juce_AudioProcessLoadMeasurer.h" #include "utilities/juce_Decibels.h" #include "utilities/juce_IIRFilter.h" -#include "utilities/juce_LagrangeInterpolator.h" -#include "utilities/juce_CatmullRomInterpolator.h" +#include "utilities/juce_GenericInterpolator.h" +#include "utilities/juce_Interpolators.h" #include "utilities/juce_SmoothedValue.h" #include "utilities/juce_Reverb.h" #include "utilities/juce_ADSR.h" @@ -120,3 +123,11 @@ #include "sources/juce_ToneGeneratorAudioSource.h" #include "synthesisers/juce_Synthesiser.h" #include "audio_play_head/juce_AudioPlayHead.h" +#include "utilities/juce_AudioWorkgroup.h" +#include "midi/ump/juce_UMPBytesOnGroup.h" +#include "midi/ump/juce_UMPDeviceInfo.h" + +namespace juce +{ + namespace ump = universal_midi_packets; +} diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm index c52bbb31..84aefde8 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp index 3419690f..1c122113 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -60,9 +60,8 @@ namespace MidiBufferHelpers if (maxBytes == 1) return 1; - int n; - auto bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n); - return jmin (maxBytes, n + 2 + bytesLeft); + const auto var = MidiMessage::readVariableLengthValue (data + 1, maxBytes - 1); + return jmin (maxBytes, var.value + 2 + var.bytesUsed); } if (byte >= 0x80) @@ -81,17 +80,27 @@ namespace MidiBufferHelpers } //============================================================================== -MidiBuffer::MidiBuffer() noexcept {} -MidiBuffer::~MidiBuffer() {} +MidiBufferIterator& MidiBufferIterator::operator++() noexcept +{ + data += sizeof (int32) + sizeof (uint16) + size_t (MidiBufferHelpers::getEventDataSize (data)); + return *this; +} -MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept : data (other.data) {} +MidiBufferIterator MidiBufferIterator::operator++ (int) noexcept +{ + auto copy = *this; + ++(*this); + return copy; +} -MidiBuffer& MidiBuffer::operator= (const MidiBuffer& other) noexcept +MidiBufferIterator::reference MidiBufferIterator::operator*() const noexcept { - data = other.data; - return *this; + return { data + sizeof (int32) + sizeof (uint16), + MidiBufferHelpers::getEventDataSize (data), + MidiBufferHelpers::getEventTime (data) }; } +//============================================================================== MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept { addEvent (message, 0); @@ -107,47 +116,53 @@ void MidiBuffer::clear (int startSample, int numSamples) 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())); + data.removeRange ((int) (start - data.begin()), (int) (end - start)); } -void MidiBuffer::addEvent (const MidiMessage& m, int sampleNumber) +bool MidiBuffer::addEvent (const MidiMessage& m, int sampleNumber) { - addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber); + return addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber); } -void MidiBuffer::addEvent (const void* newData, int maxBytes, int sampleNumber) +bool MidiBuffer::addEvent (const void* newData, int maxBytes, int sampleNumber) { auto numBytes = MidiBufferHelpers::findActualEventLength (static_cast (newData), maxBytes); - if (numBytes > 0) + if (numBytes <= 0) + return true; + + if (std::numeric_limits::max() < numBytes) { - auto newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16); - auto offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin()); + // This method only supports messages smaller than (1 << 16) bytes + return false; + } - data.insertMultiple (offset, 0, (int) newItemSize); + auto newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16); + auto offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin()); - auto* d = data.begin() + offset; - writeUnaligned (d, sampleNumber); - d += sizeof (int32); - writeUnaligned (d, static_cast (numBytes)); - d += sizeof (uint16); - memcpy (d, newData, (size_t) numBytes); - } + data.insertMultiple (offset, 0, (int) newItemSize); + + auto* d = data.begin() + offset; + writeUnaligned (d, sampleNumber); + d += sizeof (int32); + writeUnaligned (d, static_cast (numBytes)); + d += sizeof (uint16); + memcpy (d, newData, (size_t) numBytes); + + return true; } void MidiBuffer::addEvents (const MidiBuffer& otherBuffer, int startSample, int numSamples, int sampleDeltaToAdd) { - Iterator i (otherBuffer); - i.setNextSamplePosition (startSample); + for (auto i = otherBuffer.findNextSamplePosition (startSample); i != otherBuffer.cend(); ++i) + { + const auto metadata = *i; - const uint8* eventData; - int eventSize, position; + if (metadata.samplePosition >= startSample + numSamples && numSamples >= 0) + break; - while (i.getNextEvent (eventData, eventSize, position) - && (position < startSample + numSamples || numSamples < 0)) - { - addEvent (eventData, eventSize, position + sampleDeltaToAdd); + addEvent (metadata.data, metadata.numBytes, metadata.samplePosition + sampleDeltaToAdd); } } @@ -185,48 +200,121 @@ int MidiBuffer::getLastEventTime() const noexcept } } +MidiBufferIterator MidiBuffer::findNextSamplePosition (int samplePosition) const noexcept +{ + return std::find_if (cbegin(), cend(), [&] (const MidiMessageMetadata& metadata) noexcept + { + return metadata.samplePosition >= samplePosition; + }); +} + //============================================================================== +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept - : buffer (b), data (b.data.begin()) + : buffer (b), iterator (b.data.begin()) { } -MidiBuffer::Iterator::~Iterator() noexcept {} - void MidiBuffer::Iterator::setNextSamplePosition (int samplePosition) noexcept { - data = buffer.data.begin(); - auto dataEnd = buffer.data.end(); - - while (data < dataEnd && MidiBufferHelpers::getEventTime (data) < samplePosition) - data += MidiBufferHelpers::getEventTotalSize (data); + iterator = buffer.findNextSamplePosition (samplePosition); } bool MidiBuffer::Iterator::getNextEvent (const uint8*& midiData, int& numBytes, int& samplePosition) noexcept { - if (data >= buffer.data.end()) + if (iterator == buffer.cend()) return false; - samplePosition = MidiBufferHelpers::getEventTime (data); - auto itemSize = MidiBufferHelpers::getEventDataSize (data); - numBytes = itemSize; - midiData = data + sizeof (int32) + sizeof (uint16); - data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize; - + const auto metadata = *iterator++; + midiData = metadata.data; + numBytes = metadata.numBytes; + samplePosition = metadata.samplePosition; return true; } bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept { - if (data >= buffer.data.end()) + if (iterator == buffer.cend()) return false; - samplePosition = MidiBufferHelpers::getEventTime (data); - auto itemSize = MidiBufferHelpers::getEventDataSize (data); - result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition); - data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize; - + const auto metadata = *iterator++; + result = metadata.getMessage(); + samplePosition = metadata.samplePosition; return true; } +JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +struct MidiBufferTest final : public UnitTest +{ + MidiBufferTest() + : UnitTest ("MidiBuffer", UnitTestCategories::midi) + {} + + void runTest() override + { + beginTest ("Clear messages"); + { + const auto message = MidiMessage::noteOn (1, 64, 0.5f); + + const auto testBuffer = [&] + { + MidiBuffer buffer; + buffer.addEvent (message, 0); + buffer.addEvent (message, 10); + buffer.addEvent (message, 20); + buffer.addEvent (message, 30); + return buffer; + }(); + + { + auto buffer = testBuffer; + buffer.clear (10, 0); + expectEquals (buffer.getNumEvents(), 4); + } + + { + auto buffer = testBuffer; + buffer.clear (10, 1); + expectEquals (buffer.getNumEvents(), 3); + } + + { + auto buffer = testBuffer; + buffer.clear (10, 10); + expectEquals (buffer.getNumEvents(), 3); + } + + { + auto buffer = testBuffer; + buffer.clear (10, 20); + expectEquals (buffer.getNumEvents(), 2); + } + + { + auto buffer = testBuffer; + buffer.clear (10, 30); + expectEquals (buffer.getNumEvents(), 1); + } + + { + auto buffer = testBuffer; + buffer.clear (10, 300); + expectEquals (buffer.getNumEvents(), 1); + } + } + } +}; + +static MidiBufferTest midiBufferTest; + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h index 96751974..988348d2 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,108 @@ namespace juce { +//============================================================================== +/** + A view of MIDI message data stored in a contiguous buffer. + + Instances of this class do *not* own the midi data bytes that they point to. + Instead, they expect the midi data to live in a separate buffer that outlives + the MidiMessageMetadata instance. + + @tags{Audio} +*/ +struct MidiMessageMetadata +{ + MidiMessageMetadata() noexcept = default; + + MidiMessageMetadata (const uint8* dataIn, int numBytesIn, int positionIn) noexcept + : data (dataIn), numBytes (numBytesIn), samplePosition (positionIn) + { + } + + /** Constructs a new MidiMessage instance from the data that this object is viewing. + + Note that MidiMessage owns its data storage, whereas MidiMessageMetadata does not. + */ + MidiMessage getMessage() const { return MidiMessage (data, numBytes, samplePosition); } + + /** Pointer to the first byte of a MIDI message. */ + const uint8* data = nullptr; + + /** The number of bytes in the MIDI message. */ + int numBytes = 0; + + /** The MIDI message's timestamp. */ + int samplePosition = 0; +}; + +//============================================================================== +/** + An iterator to move over contiguous raw MIDI data, which Allows iterating + over a MidiBuffer using C++11 range-for syntax. + + In the following example, we log all three-byte messages in a midi buffer. + @code + void processBlock (AudioBuffer&, MidiBuffer& midiBuffer) override + { + for (const MidiMessageMetadata metadata : midiBuffer) + if (metadata.numBytes == 3) + Logger::writeToLog (metadata.getMessage().getDescription()); + } + @endcode + + @tags{Audio} +*/ +class JUCE_API MidiBufferIterator +{ + using Ptr = const uint8*; + +public: + MidiBufferIterator() = default; + + /** Constructs an iterator pointing at the message starting at the byte `dataIn`. + `dataIn` must point to the start of a valid MIDI message. If it does not, + calling other member functions on the iterator will result in undefined + behaviour. + */ + explicit MidiBufferIterator (const uint8* dataIn) noexcept + : data (dataIn) + { + } + + using difference_type = std::iterator_traits::difference_type; + using value_type = MidiMessageMetadata; + using reference = MidiMessageMetadata; + using pointer = void; + using iterator_category = std::input_iterator_tag; + + /** Make this iterator point to the next message in the buffer. */ + MidiBufferIterator& operator++() noexcept; + + /** Create a copy of this object, make this iterator point to the next message in + the buffer, then return the copy. + */ + MidiBufferIterator operator++ (int) noexcept; + + /** Return true if this iterator points to the same message as another + iterator instance, otherwise return false. + */ + bool operator== (const MidiBufferIterator& other) const noexcept { return data == other.data; } + + /** Return false if this iterator points to the same message as another + iterator instance, otherwise returns true. + */ + bool operator!= (const MidiBufferIterator& other) const noexcept { return ! operator== (other); } + + /** Return an instance of MidiMessageMetadata which describes the message to which + the iterator is currently pointing. + */ + reference operator*() const noexcept; + +private: + Ptr data = nullptr; +}; + //============================================================================== /** Holds a sequence of time-stamped midi events. @@ -44,20 +146,11 @@ class JUCE_API MidiBuffer public: //============================================================================== /** Creates an empty MidiBuffer. */ - MidiBuffer() noexcept; + MidiBuffer() noexcept = default; /** Creates a MidiBuffer containing a single midi message. */ explicit MidiBuffer (const MidiMessage& message) noexcept; - /** Creates a copy of another MidiBuffer. */ - MidiBuffer (const MidiBuffer&) noexcept; - - /** Makes a copy of another MidiBuffer. */ - MidiBuffer& operator= (const MidiBuffer&) noexcept; - - /** Destructor */ - ~MidiBuffer(); - //============================================================================== /** Removes all events from the buffer. */ void clear() noexcept; @@ -70,7 +163,7 @@ class JUCE_API MidiBuffer void clear (int start, int numSamples); /** Returns true if the buffer is empty. - To actually retrieve the events, use a MidiBuffer::Iterator object + To actually retrieve the events, use a MidiBufferIterator object */ bool isEmpty() const noexcept; @@ -91,9 +184,11 @@ class JUCE_API MidiBuffer If an event is added whose sample position is the same as one or more events already in the buffer, the new event will be placed after the existing ones. - To retrieve events, use a MidiBuffer::Iterator object + To retrieve events, use a MidiBufferIterator object. + + Returns true on success, or false on failure. */ - void addEvent (const MidiMessage& midiMessage, int sampleNumber); + bool addEvent (const MidiMessage& midiMessage, int sampleNumber); /** Adds an event to the buffer from raw midi data. @@ -109,9 +204,11 @@ class JUCE_API MidiBuffer it'll actually only store 3 bytes. If the midi data is invalid, it might not add an event at all. - To retrieve events, use a MidiBuffer::Iterator object + To retrieve events, use a MidiBufferIterator object. + + Returns true on success, or false on failure. */ - void addEvent (const void* rawMidiData, + bool addEvent (const void* rawMidiData, int maxBytesOfMidiData, int sampleNumber); @@ -158,8 +255,27 @@ class JUCE_API MidiBuffer */ void ensureSize (size_t minimumNumBytes); + /** Get a read-only iterator pointing to the beginning of this buffer. */ + MidiBufferIterator begin() const noexcept { return cbegin(); } + + /** Get a read-only iterator pointing one past the end of this buffer. */ + MidiBufferIterator end() const noexcept { return cend(); } + + /** Get a read-only iterator pointing to the beginning of this buffer. */ + MidiBufferIterator cbegin() const noexcept { return MidiBufferIterator (data.begin()); } + + /** Get a read-only iterator pointing one past the end of this buffer. */ + MidiBufferIterator cend() const noexcept { return MidiBufferIterator (data.end()); } + + /** Get an iterator pointing to the first event with a timestamp greater-than or + equal-to `samplePosition`. + */ + MidiBufferIterator findNextSamplePosition (int samplePosition) const noexcept; + //============================================================================== - /** + #ifndef DOXYGEN + /** This class is now deprecated in favour of MidiBufferIterator. + Used to iterate through the events in a MidiBuffer. Note that altering the buffer while an iterator is using it will produce @@ -167,18 +283,12 @@ class JUCE_API MidiBuffer @see MidiBuffer */ - class JUCE_API Iterator + class [[deprecated]] JUCE_API Iterator { public: //============================================================================== /** Creates an Iterator for this MidiBuffer. */ - Iterator (const MidiBuffer&) noexcept; - - /** Creates a copy of an iterator. */ - Iterator (const Iterator&) = default; - - /** Destructor. */ - ~Iterator() noexcept; + Iterator (const MidiBuffer& b) noexcept; //============================================================================== /** Repositions the iterator so that the next event retrieved will be the first @@ -218,8 +328,9 @@ class JUCE_API MidiBuffer private: //============================================================================== const MidiBuffer& buffer; - const uint8* data; + MidiBufferIterator iterator; }; + #endif /** The raw data holding this buffer. Obviously access to this data is provided at your own risk. Its internal format could diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiDataConcatenator.h similarity index 99% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h rename to JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiDataConcatenator.h index dd0aacb5..b9bffecb 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiDataConcatenator.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp index 7850be8d..6547e63f 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -46,23 +46,65 @@ namespace MidiFileHelpers } } - static bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) noexcept + template + struct ReadTrait; + + template <> + struct ReadTrait { static constexpr auto read = ByteOrder::bigEndianInt; }; + + template <> + struct ReadTrait { static constexpr auto read = ByteOrder::bigEndianShort; }; + + template + Optional tryRead (const uint8*& data, size_t& remaining) + { + using Trait = ReadTrait; + constexpr auto size = sizeof (Integral); + + if (remaining < size) + return {}; + + const Optional result { Trait::read (data) }; + + data += size; + remaining -= size; + + return result; + } + + struct HeaderDetails + { + size_t bytesRead = 0; + short timeFormat = 0; + short fileType = 0; + short numberOfTracks = 0; + }; + + static Optional parseMidiHeader (const uint8* const initialData, + const size_t maxSize) { - auto ch = ByteOrder::bigEndianInt (data); - data += 4; + auto* data = initialData; + auto remaining = maxSize; + + auto ch = tryRead (data, remaining); - if (ch != ByteOrder::bigEndianInt ("MThd")) + if (! ch.hasValue()) + return {}; + + if (*ch != ByteOrder::bigEndianInt ("MThd")) { - bool ok = false; + auto ok = false; - if (ch == ByteOrder::bigEndianInt ("RIFF")) + if (*ch == ByteOrder::bigEndianInt ("RIFF")) { for (int i = 0; i < 8; ++i) { - ch = ByteOrder::bigEndianInt (data); - data += 4; + ch = tryRead (data, remaining); + + if (! ch.hasValue()) + return {}; - if (ch == ByteOrder::bigEndianInt ("MThd")) + if (*ch == ByteOrder::bigEndianInt ("MThd")) { ok = true; break; @@ -71,21 +113,37 @@ namespace MidiFileHelpers } if (! ok) - return false; + return {}; } - auto bytesRemaining = ByteOrder::bigEndianInt (data); - data += 4; - fileType = (short) ByteOrder::bigEndianShort (data); - data += 2; - numberOfTracks = (short) ByteOrder::bigEndianShort (data); - data += 2; - timeFormat = (short) ByteOrder::bigEndianShort (data); - data += 2; - bytesRemaining -= 6; - data += bytesRemaining; - - return true; + const auto bytesRemaining = tryRead (data, remaining); + + if (! bytesRemaining.hasValue() || *bytesRemaining > remaining) + return {}; + + const auto optFileType = tryRead (data, remaining); + + if (! optFileType.hasValue() || 2 < *optFileType) + return {}; + + const auto optNumTracks = tryRead (data, remaining); + + if (! optNumTracks.hasValue() || (*optFileType == 0 && *optNumTracks != 1)) + return {}; + + const auto optTimeFormat = tryRead (data, remaining); + + if (! optTimeFormat.hasValue()) + return {}; + + HeaderDetails result; + + result.fileType = (short) *optFileType; + result.timeFormat = (short) *optTimeFormat; + result.numberOfTracks = (short) *optNumTracks; + result.bytesRead = maxSize - remaining; + + return { result }; } static double convertTicksToSeconds (double time, @@ -102,7 +160,7 @@ namespace MidiFileHelpers for (int i = 0; i < numEvents; ++i) { - auto& m = tempoEvents.getEventPointer(i)->message; + auto& m = tempoEvents.getEventPointer (i)->message; auto eventTime = m.getTimeStamp(); if (eventTime >= time) @@ -116,9 +174,9 @@ namespace MidiFileHelpers while (i + 1 < numEvents) { - auto& m2 = tempoEvents.getEventPointer(i + 1)->message; + auto& m2 = tempoEvents.getEventPointer (i + 1)->message; - if (m2.getTimeStamp() != eventTime) + if (! approximatelyEqual (m2.getTimeStamp(), eventTime)) break; if (m2.isTempoMetaEvent()) @@ -142,18 +200,58 @@ namespace MidiFileHelpers for (int j = 0; j < numEvents; ++j) { - auto& m = track->getEventPointer(j)->message; + auto& m = track->getEventPointer (j)->message; if ((m.*method)()) results.addEvent (m); } } } + + static MidiMessageSequence readTrack (const uint8* data, int size) + { + double time = 0; + uint8 lastStatusByte = 0; + + MidiMessageSequence result; + + while (size > 0) + { + const auto delay = MidiMessage::readVariableLengthValue (data, (int) size); + + if (! delay.isValid()) + break; + + data += delay.bytesUsed; + size -= delay.bytesUsed; + time += delay.value; + + if (size <= 0) + break; + + int messSize = 0; + const MidiMessage mm (data, size, messSize, lastStatusByte, time); + + if (messSize <= 0) + break; + + size -= messSize; + data += messSize; + + result.addEvent (mm); + + auto firstByte = *(mm.getRawData()); + + if ((firstByte & 0xf0) != 0xf0) + lastStatusByte = firstByte; + } + + return result; + } } //============================================================================== MidiFile::MidiFile() : timeFormat ((short) (unsigned short) 0xe728) {} -MidiFile::~MidiFile() {} MidiFile::MidiFile (const MidiFile& other) : timeFormat (other.timeFormat) { @@ -245,7 +343,9 @@ double MidiFile::getLastTimestamp() const } //============================================================================== -bool MidiFile::readFrom (InputStream& sourceStream, bool createMatchingNoteOffs) +bool MidiFile::readFrom (InputStream& sourceStream, + bool createMatchingNoteOffs, + int* fileType) { clear(); MemoryBlock data; @@ -253,78 +353,61 @@ bool MidiFile::readFrom (InputStream& sourceStream, bool createMatchingNoteOffs) const int maxSensibleMidiFileSize = 200 * 1024 * 1024; // (put a sanity-check on the file size, as midi files are generally small) - if (sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize)) - { - auto size = data.getSize(); - auto d = static_cast (data.getData()); - short fileType, expectedTracks; + if (! sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize)) + return false; - if (size > 16 && MidiFileHelpers::parseMidiHeader (d, timeFormat, fileType, expectedTracks)) - { - size -= (size_t) (d - static_cast (data.getData())); - int track = 0; + auto size = data.getSize(); + auto d = static_cast (data.getData()); - for (;;) - { - auto chunkType = (int) ByteOrder::bigEndianInt (d); - d += 4; - auto chunkSize = (int) ByteOrder::bigEndianInt (d); - d += 4; + const auto optHeader = MidiFileHelpers::parseMidiHeader (d, size); - if (chunkSize <= 0 || (size_t) chunkSize > size) - break; + if (! optHeader.hasValue()) + return false; - if (chunkType == (int) ByteOrder::bigEndianInt ("MTrk")) - readNextTrack (d, chunkSize, createMatchingNoteOffs); + const auto header = *optHeader; + timeFormat = header.timeFormat; - if (++track >= expectedTracks) - break; + d += header.bytesRead; + size -= (size_t) header.bytesRead; - size -= (size_t) chunkSize + 8; - d += chunkSize; - } + for (int track = 0; track < header.numberOfTracks; ++track) + { + const auto optChunkType = MidiFileHelpers::tryRead (d, size); - return true; - } - } + if (! optChunkType.hasValue()) + return false; - return false; -} + const auto optChunkSize = MidiFileHelpers::tryRead (d, size); -void MidiFile::readNextTrack (const uint8* data, int size, bool createMatchingNoteOffs) -{ - double time = 0; - uint8 lastStatusByte = 0; + if (! optChunkSize.hasValue()) + return false; - MidiMessageSequence result; + const auto chunkSize = *optChunkSize; - while (size > 0) - { - int bytesUsed; - auto delay = MidiMessage::readVariableLengthVal (data, bytesUsed); - data += bytesUsed; - size -= bytesUsed; - time += delay; + if (size < chunkSize) + return false; - int messSize = 0; - const MidiMessage mm (data, size, messSize, lastStatusByte, time); + if (*optChunkType == ByteOrder::bigEndianInt ("MTrk")) + readNextTrack (d, (int) chunkSize, createMatchingNoteOffs); - if (messSize <= 0) - break; + size -= chunkSize; + d += chunkSize; + } - size -= messSize; - data += messSize; + const auto successful = (size == 0); - result.addEvent (mm); + if (successful && fileType != nullptr) + *fileType = header.fileType; - auto firstByte = *(mm.getRawData()); + return successful; +} - if ((firstByte & 0xf0) != 0xf0) - lastStatusByte = firstByte; - } +void MidiFile::readNextTrack (const uint8* data, int size, bool createMatchingNoteOffs) +{ + auto sequence = MidiFileHelpers::readTrack (data, size); // sort so that we put all the note-offs before note-ons that have the same time - std::stable_sort (result.list.begin(), result.list.end(), + std::stable_sort (sequence.list.begin(), sequence.list.end(), [] (const MidiMessageSequence::MidiEventHolder* a, const MidiMessageSequence::MidiEventHolder* b) { @@ -337,10 +420,10 @@ void MidiFile::readNextTrack (const uint8* data, int size, bool createMatchingNo return a->message.isNoteOff() && b->message.isNoteOn(); }); - addTrack (result); - if (createMatchingNoteOffs) - tracks.getLast()->updateMatchedPairs(); + sequence.updateMatchedPairs(); + + addTrack (sequence); } //============================================================================== @@ -356,7 +439,7 @@ void MidiFile::convertTimestampTicksToSeconds() { for (int j = ms->getNumEvents(); --j >= 0;) { - auto& m = ms->getEventPointer(j)->message; + auto& m = ms->getEventPointer (j)->message; m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), tempoEvents, timeFormat)); } } @@ -392,7 +475,7 @@ bool MidiFile::writeTrack (OutputStream& mainOut, const MidiMessageSequence& ms) for (int i = 0; i < ms.getNumEvents(); ++i) { - auto& mm = ms.getEventPointer(i)->message; + auto& mm = ms.getEventPointer (i)->message; if (mm.isEndOfTrackMetaEvent()) endOfTrackEventWritten = true; @@ -443,4 +526,269 @@ bool MidiFile::writeTrack (OutputStream& mainOut, const MidiMessageSequence& ms) return true; } +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +struct MidiFileTest final : public UnitTest +{ + MidiFileTest() + : UnitTest ("MidiFile", UnitTestCategories::midi) + {} + + void runTest() override + { + beginTest ("ReadTrack respects running status"); + { + const auto sequence = parseSequence ([] (OutputStream& os) + { + MidiFileHelpers::writeVariableLengthInt (os, 100); + writeBytes (os, { 0x90, 0x40, 0x40 }); + MidiFileHelpers::writeVariableLengthInt (os, 200); + writeBytes (os, { 0x40, 0x40 }); + MidiFileHelpers::writeVariableLengthInt (os, 300); + writeBytes (os, { 0xff, 0x2f, 0x00 }); + }); + + expectEquals (sequence.getNumEvents(), 3); + expect (sequence.getEventPointer (0)->message.isNoteOn()); + expect (sequence.getEventPointer (1)->message.isNoteOn()); + expect (sequence.getEventPointer (2)->message.isEndOfTrackMetaEvent()); + } + + beginTest ("ReadTrack returns available messages if input is truncated"); + { + { + const auto sequence = parseSequence ([] (OutputStream& os) + { + // Incomplete delta time + writeBytes (os, { 0xff }); + }); + + expectEquals (sequence.getNumEvents(), 0); + } + + { + const auto sequence = parseSequence ([] (OutputStream& os) + { + // Complete delta with no following event + MidiFileHelpers::writeVariableLengthInt (os, 0xffff); + }); + + expectEquals (sequence.getNumEvents(), 0); + } + + { + const auto sequence = parseSequence ([] (OutputStream& os) + { + // Complete delta with malformed following event + MidiFileHelpers::writeVariableLengthInt (os, 0xffff); + writeBytes (os, { 0x90, 0x40 }); + }); + + expectEquals (sequence.getNumEvents(), 1); + expect (sequence.getEventPointer (0)->message.isNoteOff()); + expectEquals (sequence.getEventPointer (0)->message.getNoteNumber(), 0x40); + expectEquals (sequence.getEventPointer (0)->message.getVelocity(), (uint8) 0x00); + } + } + + beginTest ("Header parsing works"); + { + { + // No data + const auto header = parseHeader ([] (OutputStream&) {}); + expect (! header.hasValue()); + } + + { + // Invalid initial byte + const auto header = parseHeader ([] (OutputStream& os) + { + writeBytes (os, { 0xff }); + }); + + expect (! header.hasValue()); + } + + { + // Type block, but no header data + const auto header = parseHeader ([] (OutputStream& os) + { + writeBytes (os, { 'M', 'T', 'h', 'd' }); + }); + + expect (! header.hasValue()); + } + + { + // We (ll-formed header, but track type is 0 and channels != 1 + const auto header = parseHeader ([] (OutputStream& os) + { + writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 0, 0, 16, 0, 1 }); + }); + + expect (! header.hasValue()); + } + + { + // Well-formed header, but track type is 5 + const auto header = parseHeader ([] (OutputStream& os) + { + writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 5, 0, 16, 0, 1 }); + }); + + expect (! header.hasValue()); + } + + { + // Well-formed header + const auto header = parseHeader ([] (OutputStream& os) + { + writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 16, 0, 1 }); + }); + + expect (header.hasValue()); + + expectEquals (header->fileType, (short) 1); + expectEquals (header->numberOfTracks, (short) 16); + expectEquals (header->timeFormat, (short) 1); + expectEquals ((int) header->bytesRead, 14); + } + } + + beginTest ("Read from stream"); + { + { + // Empty input + const auto file = parseFile ([] (OutputStream&) {}); + expect (! file.hasValue()); + } + + { + // Malformed header + const auto file = parseFile ([] (OutputStream& os) + { + writeBytes (os, { 'M', 'T', 'h', 'd' }); + }); + + expect (! file.hasValue()); + } + + { + // Header, no channels + const auto file = parseFile ([] (OutputStream& os) + { + writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 0, 0, 1 }); + }); + + expect (file.hasValue()); + expectEquals (file->getNumTracks(), 0); + } + + { + // Header, one malformed channel + const auto file = parseFile ([] (OutputStream& os) + { + writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 }); + writeBytes (os, { 'M', 'T', 'r', '?' }); + }); + + expect (! file.hasValue()); + } + + { + // Header, one channel with malformed message + const auto file = parseFile ([] (OutputStream& os) + { + writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 }); + writeBytes (os, { 'M', 'T', 'r', 'k', 0, 0, 0, 1, 0xff }); + }); + + expect (file.hasValue()); + expectEquals (file->getNumTracks(), 1); + expectEquals (file->getTrack (0)->getNumEvents(), 0); + } + + { + // Header, one channel with incorrect length message + const auto file = parseFile ([] (OutputStream& os) + { + writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 }); + writeBytes (os, { 'M', 'T', 'r', 'k', 0x0f, 0, 0, 0, 0xff }); + }); + + expect (! file.hasValue()); + } + + { + // Header, one channel, all well-formed + const auto file = parseFile ([] (OutputStream& os) + { + writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 }); + writeBytes (os, { 'M', 'T', 'r', 'k', 0, 0, 0, 4 }); + + MidiFileHelpers::writeVariableLengthInt (os, 0x0f); + writeBytes (os, { 0x80, 0x00, 0x00 }); + }); + + expect (file.hasValue()); + expectEquals (file->getNumTracks(), 1); + + auto& track = *file->getTrack (0); + expectEquals (track.getNumEvents(), 1); + expect (track.getEventPointer (0)->message.isNoteOff()); + expectEquals (track.getEventPointer (0)->message.getTimeStamp(), (double) 0x0f); + } + } + } + + template + static MidiMessageSequence parseSequence (Fn&& fn) + { + MemoryOutputStream os; + fn (os); + + return MidiFileHelpers::readTrack (reinterpret_cast (os.getData()), + (int) os.getDataSize()); + } + + template + static Optional parseHeader (Fn&& fn) + { + MemoryOutputStream os; + fn (os); + + return MidiFileHelpers::parseMidiHeader (reinterpret_cast (os.getData()), + os.getDataSize()); + } + + template + static Optional parseFile (Fn&& fn) + { + MemoryOutputStream os; + fn (os); + + MemoryInputStream is (os.getData(), os.getDataSize(), false); + MidiFile mf; + + int fileType = 0; + + if (mf.readFrom (is, true, &fileType)) + return mf; + + return {}; + } + + static void writeBytes (OutputStream& os, const std::vector& bytes) + { + for (const auto& byte : bytes) + os.writeByte ((char) byte); + } +}; + +static MidiFileTest midiFileTests; + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h index 323523ed..ea54495e 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -45,9 +45,6 @@ class JUCE_API MidiFile /** Creates an empty MidiFile object. */ MidiFile(); - /** Destructor. */ - ~MidiFile(); - /** Creates a copy of another MidiFile. */ MidiFile (const MidiFile&); @@ -136,7 +133,7 @@ class JUCE_API MidiFile */ void findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const; - /** Makes a list of all the time-signature meta-events from all tracks in the midi file. + /** Makes a list of all the key-signature meta-events from all tracks in the midi file. @param keySigEvents a list to which all the events will be added */ void findAllKeySigEvents (MidiMessageSequence& keySigEvents) const; @@ -160,10 +157,14 @@ class JUCE_API MidiFile @param createMatchingNoteOffs if true, any missing note-offs for previous note-ons will be automatically added at the end of the file by calling MidiMessageSequence::updateMatchedPairs on each track. + @param midiFileType if not nullptr, the integer at this address will be set + to 0, 1, or 2 depending on the type of the midi file @returns true if the stream was read successfully */ - bool readFrom (InputStream& sourceStream, bool createMatchingNoteOffs = true); + bool readFrom (InputStream& sourceStream, + bool createMatchingNoteOffs = true, + int* midiFileType = nullptr); /** Writes the midi tracks as a standard midi file. The midiFileType value is written as the file's format type, which can be 0, 1 diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp index 6c5a3dfe..9fe9090d 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,10 +28,6 @@ MidiKeyboardState::MidiKeyboardState() zerostruct (noteStates); } -MidiKeyboardState::~MidiKeyboardState() -{ -} - //============================================================================== void MidiKeyboardState::reset() { @@ -42,7 +38,7 @@ void MidiKeyboardState::reset() bool MidiKeyboardState::isNoteOn (const int midiChannel, const int n) const noexcept { - jassert (midiChannel >= 0 && midiChannel <= 16); + jassert (midiChannel > 0 && midiChannel <= 16); return isPositiveAndBelow (n, 128) && (noteStates[n] & (1 << (midiChannel - 1))) != 0; @@ -56,7 +52,7 @@ bool MidiKeyboardState::isNoteOnForChannels (const int midiChannelMask, const in void MidiKeyboardState::noteOn (const int midiChannel, const int midiNoteNumber, const float velocity) { - jassert (midiChannel >= 0 && midiChannel <= 16); + jassert (midiChannel > 0 && midiChannel <= 16); jassert (isPositiveAndBelow (midiNoteNumber, 128)); const ScopedLock sl (lock); @@ -75,10 +71,8 @@ void MidiKeyboardState::noteOnInternal (const int midiChannel, const int midiNo { if (isPositiveAndBelow (midiNoteNumber, 128)) { - noteStates [midiNoteNumber] |= (1 << (midiChannel - 1)); - - for (int i = listeners.size(); --i >= 0;) - listeners.getUnchecked(i)->handleNoteOn (this, midiChannel, midiNoteNumber, velocity); + noteStates[midiNoteNumber] = static_cast (noteStates[midiNoteNumber] | (1 << (midiChannel - 1))); + listeners.call ([&] (Listener& l) { l.handleNoteOn (this, midiChannel, midiNoteNumber, velocity); }); } } @@ -100,10 +94,8 @@ void MidiKeyboardState::noteOffInternal (const int midiChannel, const int midiN { if (isNoteOn (midiChannel, midiNoteNumber)) { - noteStates [midiNoteNumber] &= ~(1 << (midiChannel - 1)); - - for (int i = listeners.size(); --i >= 0;) - listeners.getUnchecked(i)->handleNoteOff (this, midiChannel, midiNoteNumber, velocity); + noteStates[midiNoteNumber] = static_cast (noteStates[midiNoteNumber] & ~(1 << (midiChannel - 1))); + listeners.call ([&] (Listener& l) { l.handleNoteOff (this, midiChannel, midiNoteNumber, velocity); }); } } @@ -145,25 +137,20 @@ void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, const int numSamples, const bool injectIndirectEvents) { - MidiBuffer::Iterator i (buffer); - MidiMessage message; - int time; - const ScopedLock sl (lock); - while (i.getNextEvent (message, time)) - processNextMidiEvent (message); + for (const auto metadata : buffer) + processNextMidiEvent (metadata.getMessage()); if (injectIndirectEvents) { - MidiBuffer::Iterator i2 (eventsToAdd); const int firstEventToAdd = eventsToAdd.getFirstEventTime(); const double scaleFactor = numSamples / (double) (eventsToAdd.getLastEventTime() + 1 - firstEventToAdd); - while (i2.getNextEvent (message, time)) + for (const auto metadata : eventsToAdd) { - const int pos = jlimit (0, numSamples - 1, roundToInt ((time - firstEventToAdd) * scaleFactor)); - buffer.addEvent (message, startSample + pos); + const auto pos = jlimit (0, numSamples - 1, roundToInt ((metadata.samplePosition - firstEventToAdd) * scaleFactor)); + buffer.addEvent (metadata.getMessage(), startSample + pos); } } @@ -171,16 +158,16 @@ void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, } //============================================================================== -void MidiKeyboardState::addListener (MidiKeyboardStateListener* const listener) +void MidiKeyboardState::addListener (Listener* listener) { const ScopedLock sl (lock); - listeners.addIfNotAlreadyThere (listener); + listeners.add (listener); } -void MidiKeyboardState::removeListener (MidiKeyboardStateListener* const listener) +void MidiKeyboardState::removeListener (Listener* listener) { const ScopedLock sl (lock); - listeners.removeFirstMatchingValue (listener); + listeners.remove (listener); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h index cd958baf..4b4eb8a1 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,51 +23,6 @@ namespace juce { -class MidiKeyboardState; - - -//============================================================================== -/** - Receives events from a MidiKeyboardState object. - - @see MidiKeyboardState - - @tags{Audio} -*/ -class JUCE_API MidiKeyboardStateListener -{ -public: - //============================================================================== - MidiKeyboardStateListener() = default; - virtual ~MidiKeyboardStateListener() = default; - - //============================================================================== - /** Called when one of the MidiKeyboardState's keys is pressed. - - This will be called synchronously when the state is either processing a - buffer in its MidiKeyboardState::processNextMidiBuffer() method, or - when a note is being played with its MidiKeyboardState::noteOn() method. - - Note that this callback could happen from an audio callback thread, so be - careful not to block, and avoid any UI activity in the callback. - */ - virtual void handleNoteOn (MidiKeyboardState* source, - int midiChannel, int midiNoteNumber, float velocity) = 0; - - /** Called when one of the MidiKeyboardState's keys is released. - - This will be called synchronously when the state is either processing a - buffer in its MidiKeyboardState::processNextMidiBuffer() method, or - when a note is being played with its MidiKeyboardState::noteOff() method. - - Note that this callback could happen from an audio callback thread, so be - careful not to block, and avoid any UI activity in the callback. - */ - virtual void handleNoteOff (MidiKeyboardState* source, - int midiChannel, int midiNoteNumber, float velocity) = 0; -}; - - //============================================================================== /** Represents a piano keyboard, keeping track of which keys are currently pressed. @@ -88,7 +43,6 @@ class JUCE_API MidiKeyboardState public: //============================================================================== MidiKeyboardState(); - ~MidiKeyboardState(); //============================================================================== /** Resets the state of the object. @@ -180,27 +134,62 @@ class JUCE_API MidiKeyboardState bool injectIndirectEvents); //============================================================================== + /** Receives events from a MidiKeyboardState object. */ + class JUCE_API Listener + { + public: + //============================================================================== + virtual ~Listener() = default; + + //============================================================================== + /** Called when one of the MidiKeyboardState's keys is pressed. + + This will be called synchronously when the state is either processing a + buffer in its MidiKeyboardState::processNextMidiBuffer() method, or + when a note is being played with its MidiKeyboardState::noteOn() method. + + Note that this callback could happen from an audio callback thread, so be + careful not to block, and avoid any UI activity in the callback. + */ + virtual void handleNoteOn (MidiKeyboardState* source, + int midiChannel, int midiNoteNumber, float velocity) = 0; + + /** Called when one of the MidiKeyboardState's keys is released. + + This will be called synchronously when the state is either processing a + buffer in its MidiKeyboardState::processNextMidiBuffer() method, or + when a note is being played with its MidiKeyboardState::noteOff() method. + + Note that this callback could happen from an audio callback thread, so be + careful not to block, and avoid any UI activity in the callback. + */ + virtual void handleNoteOff (MidiKeyboardState* source, + int midiChannel, int midiNoteNumber, float velocity) = 0; + }; + /** Registers a listener for callbacks when keys go up or down. @see removeListener */ - void addListener (MidiKeyboardStateListener* listener); + void addListener (Listener* listener); /** Deregisters a listener. @see addListener */ - void removeListener (MidiKeyboardStateListener* listener); + void removeListener (Listener* listener); private: //============================================================================== CriticalSection lock; - uint16 noteStates [128]; + std::atomic noteStates[128]; MidiBuffer eventsToAdd; - Array listeners; + ListenerList listeners; - void noteOnInternal (int midiChannel, int midiNoteNumber, float velocity); + void noteOnInternal (int midiChannel, int midiNoteNumber, float velocity); void noteOffInternal (int midiChannel, int midiNoteNumber, float velocity); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardState) }; +using MidiKeyboardStateListener = MidiKeyboardState::Listener; + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp index 89de06ab..5219b987 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -57,6 +57,31 @@ uint16 MidiMessage::pitchbendToPitchwheelPos (const float pitchbend, } //============================================================================== +MidiMessage::VariableLengthValue MidiMessage::readVariableLengthValue (const uint8* data, int maxBytesToUse) noexcept +{ + uint32 v = 0; + + // The largest allowable variable-length value is 0x0f'ff'ff'ff which is + // represented by the 4-byte stream 0xff 0xff 0xff 0x7f. + // Longer bytestreams risk overflowing a 32-bit signed int. + const auto limit = jmin (maxBytesToUse, 4); + + for (int numBytesUsed = 0; numBytesUsed < limit; ++numBytesUsed) + { + const auto i = data[numBytesUsed]; + v = (v << 7) + (i & 0x7f); + + if (! (i & 0x80)) + return { (int) v, numBytesUsed + 1 }; + } + + // If this is hit, the input was malformed. Either there were not enough + // bytes of input to construct a full value, or no terminating byte was + // found. This implementation only supports variable-length values of up + // to four bytes. + return {}; +} + int MidiMessage::readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept { numBytesUsed = 0; @@ -224,16 +249,8 @@ MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const } else if (byte == 0xff) { - if (sz == 1) - { - size = 1; - } - else - { - int n; - const int bytesLeft = readVariableLengthVal (src + 1, n); - size = jmin (sz + 1, n + 2 + bytesLeft); - } + const auto bytesLeft = readVariableLengthValue (src + 1, sz - 1); + size = jmin (sz + 1, bytesLeft.bytesUsed + 2 + bytesLeft.value); auto dest = allocateSpace (size); *dest = (uint8) byte; @@ -270,11 +287,14 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other) { if (other.isHeapAllocated()) { - if (isHeapAllocated()) - packedData.allocatedData = static_cast (std::realloc (packedData.allocatedData, (size_t) other.size)); - else - packedData.allocatedData = static_cast (std::malloc ((size_t) other.size)); + auto* newStorage = static_cast (isHeapAllocated() + ? std::realloc (packedData.allocatedData, (size_t) other.size) + : std::malloc ((size_t) other.size)); + if (newStorage == nullptr) + throw std::bad_alloc{}; // The midi message has not been adjusted at this point + + packedData.allocatedData = newStorage; memcpy (packedData.allocatedData, other.packedData.allocatedData, (size_t) other.size); } else @@ -384,7 +404,7 @@ void MidiMessage::setChannel (const int channel) noexcept if ((data[0] & 0xf0) != (uint8) 0xf0) data[0] = (uint8) ((data[0] & (uint8) 0xf0) - | (uint8)(channel - 1)); + | (uint8) (channel - 1)); } bool MidiMessage::isNoteOn (const bool returnTrueForVelocity0) const noexcept @@ -665,6 +685,11 @@ MidiMessage MidiMessage::createSysExMessage (const void* sysexData, const int da return MidiMessage (m, dataSize + 2); } +MidiMessage MidiMessage::createSysExMessage (Span data) +{ + return createSysExMessage (data.data(), (int) data.size()); +} + const uint8* MidiMessage::getSysExData() const noexcept { return isSysEx() ? getRawData() + 1 : nullptr; @@ -682,7 +707,7 @@ bool MidiMessage::isActiveSense() const noexcept { return *getRawData() == 0x int MidiMessage::getMetaEventType() const noexcept { auto data = getRawData(); - return *data != 0xff ? -1 : data[1]; + return (size < 2 || *data != 0xff) ? -1 : data[1]; } int MidiMessage::getMetaEventLength() const noexcept @@ -691,8 +716,8 @@ int MidiMessage::getMetaEventLength() const noexcept if (*data == 0xff) { - int n; - return jmin (size - 2, readVariableLengthVal (data + 2, n)); + const auto var = readVariableLengthValue (data + 2, size - 2); + return jmax (0, jmin (size - 2 - var.bytesUsed, var.value)); } return 0; @@ -702,10 +727,9 @@ const uint8* MidiMessage::getMetaEventData() const noexcept { jassert (isMetaEvent()); - int n; auto d = getRawData() + 2; - readVariableLengthVal (d, n); - return d + n; + const auto var = readVariableLengthValue (d, size - 2); + return d + var.bytesUsed; } bool MidiMessage::isTrackMetaEvent() const noexcept { return getMetaEventType() == 0; } @@ -1029,38 +1053,38 @@ const char* MidiMessage::getGMInstrumentName (const int n) { static const char* names[] = { - NEEDS_TRANS("Acoustic Grand Piano"), NEEDS_TRANS("Bright Acoustic Piano"), NEEDS_TRANS("Electric Grand Piano"), NEEDS_TRANS("Honky-tonk Piano"), - NEEDS_TRANS("Electric Piano 1"), NEEDS_TRANS("Electric Piano 2"), NEEDS_TRANS("Harpsichord"), NEEDS_TRANS("Clavinet"), - NEEDS_TRANS("Celesta"), NEEDS_TRANS("Glockenspiel"), NEEDS_TRANS("Music Box"), NEEDS_TRANS("Vibraphone"), - NEEDS_TRANS("Marimba"), NEEDS_TRANS("Xylophone"), NEEDS_TRANS("Tubular Bells"), NEEDS_TRANS("Dulcimer"), - NEEDS_TRANS("Drawbar Organ"), NEEDS_TRANS("Percussive Organ"), NEEDS_TRANS("Rock Organ"), NEEDS_TRANS("Church Organ"), - NEEDS_TRANS("Reed Organ"), NEEDS_TRANS("Accordion"), NEEDS_TRANS("Harmonica"), NEEDS_TRANS("Tango Accordion"), - NEEDS_TRANS("Acoustic Guitar (nylon)"), NEEDS_TRANS("Acoustic Guitar (steel)"), NEEDS_TRANS("Electric Guitar (jazz)"), NEEDS_TRANS("Electric Guitar (clean)"), - NEEDS_TRANS("Electric Guitar (mute)"), NEEDS_TRANS("Overdriven Guitar"), NEEDS_TRANS("Distortion Guitar"), NEEDS_TRANS("Guitar Harmonics"), - NEEDS_TRANS("Acoustic Bass"), NEEDS_TRANS("Electric Bass (finger)"), NEEDS_TRANS("Electric Bass (pick)"), NEEDS_TRANS("Fretless Bass"), - NEEDS_TRANS("Slap Bass 1"), NEEDS_TRANS("Slap Bass 2"), NEEDS_TRANS("Synth Bass 1"), NEEDS_TRANS("Synth Bass 2"), - NEEDS_TRANS("Violin"), NEEDS_TRANS("Viola"), NEEDS_TRANS("Cello"), NEEDS_TRANS("Contrabass"), - NEEDS_TRANS("Tremolo Strings"), NEEDS_TRANS("Pizzicato Strings"), NEEDS_TRANS("Orchestral Harp"), NEEDS_TRANS("Timpani"), - NEEDS_TRANS("String Ensemble 1"), NEEDS_TRANS("String Ensemble 2"), NEEDS_TRANS("SynthStrings 1"), NEEDS_TRANS("SynthStrings 2"), - NEEDS_TRANS("Choir Aahs"), NEEDS_TRANS("Voice Oohs"), NEEDS_TRANS("Synth Voice"), NEEDS_TRANS("Orchestra Hit"), - NEEDS_TRANS("Trumpet"), NEEDS_TRANS("Trombone"), NEEDS_TRANS("Tuba"), NEEDS_TRANS("Muted Trumpet"), - NEEDS_TRANS("French Horn"), NEEDS_TRANS("Brass Section"), NEEDS_TRANS("SynthBrass 1"), NEEDS_TRANS("SynthBrass 2"), - NEEDS_TRANS("Soprano Sax"), NEEDS_TRANS("Alto Sax"), NEEDS_TRANS("Tenor Sax"), NEEDS_TRANS("Baritone Sax"), - NEEDS_TRANS("Oboe"), NEEDS_TRANS("English Horn"), NEEDS_TRANS("Bassoon"), NEEDS_TRANS("Clarinet"), - NEEDS_TRANS("Piccolo"), NEEDS_TRANS("Flute"), NEEDS_TRANS("Recorder"), NEEDS_TRANS("Pan Flute"), - NEEDS_TRANS("Blown Bottle"), NEEDS_TRANS("Shakuhachi"), NEEDS_TRANS("Whistle"), NEEDS_TRANS("Ocarina"), - NEEDS_TRANS("Lead 1 (square)"), NEEDS_TRANS("Lead 2 (sawtooth)"), NEEDS_TRANS("Lead 3 (calliope)"), NEEDS_TRANS("Lead 4 (chiff)"), - NEEDS_TRANS("Lead 5 (charang)"), NEEDS_TRANS("Lead 6 (voice)"), NEEDS_TRANS("Lead 7 (fifths)"), NEEDS_TRANS("Lead 8 (bass+lead)"), - NEEDS_TRANS("Pad 1 (new age)"), NEEDS_TRANS("Pad 2 (warm)"), NEEDS_TRANS("Pad 3 (polysynth)"), NEEDS_TRANS("Pad 4 (choir)"), - NEEDS_TRANS("Pad 5 (bowed)"), NEEDS_TRANS("Pad 6 (metallic)"), NEEDS_TRANS("Pad 7 (halo)"), NEEDS_TRANS("Pad 8 (sweep)"), - NEEDS_TRANS("FX 1 (rain)"), NEEDS_TRANS("FX 2 (soundtrack)"), NEEDS_TRANS("FX 3 (crystal)"), NEEDS_TRANS("FX 4 (atmosphere)"), - NEEDS_TRANS("FX 5 (brightness)"), NEEDS_TRANS("FX 6 (goblins)"), NEEDS_TRANS("FX 7 (echoes)"), NEEDS_TRANS("FX 8 (sci-fi)"), - NEEDS_TRANS("Sitar"), NEEDS_TRANS("Banjo"), NEEDS_TRANS("Shamisen"), NEEDS_TRANS("Koto"), - NEEDS_TRANS("Kalimba"), NEEDS_TRANS("Bag pipe"), NEEDS_TRANS("Fiddle"), NEEDS_TRANS("Shanai"), - NEEDS_TRANS("Tinkle Bell"), NEEDS_TRANS("Agogo"), NEEDS_TRANS("Steel Drums"), NEEDS_TRANS("Woodblock"), - NEEDS_TRANS("Taiko Drum"), NEEDS_TRANS("Melodic Tom"), NEEDS_TRANS("Synth Drum"), NEEDS_TRANS("Reverse Cymbal"), - NEEDS_TRANS("Guitar Fret Noise"), NEEDS_TRANS("Breath Noise"), NEEDS_TRANS("Seashore"), NEEDS_TRANS("Bird Tweet"), - NEEDS_TRANS("Telephone Ring"), NEEDS_TRANS("Helicopter"), NEEDS_TRANS("Applause"), NEEDS_TRANS("Gunshot") + NEEDS_TRANS ("Acoustic Grand Piano"), NEEDS_TRANS ("Bright Acoustic Piano"), NEEDS_TRANS ("Electric Grand Piano"), NEEDS_TRANS ("Honky-tonk Piano"), + NEEDS_TRANS ("Electric Piano 1"), NEEDS_TRANS ("Electric Piano 2"), NEEDS_TRANS ("Harpsichord"), NEEDS_TRANS ("Clavinet"), + NEEDS_TRANS ("Celesta"), NEEDS_TRANS ("Glockenspiel"), NEEDS_TRANS ("Music Box"), NEEDS_TRANS ("Vibraphone"), + NEEDS_TRANS ("Marimba"), NEEDS_TRANS ("Xylophone"), NEEDS_TRANS ("Tubular Bells"), NEEDS_TRANS ("Dulcimer"), + NEEDS_TRANS ("Drawbar Organ"), NEEDS_TRANS ("Percussive Organ"), NEEDS_TRANS ("Rock Organ"), NEEDS_TRANS ("Church Organ"), + NEEDS_TRANS ("Reed Organ"), NEEDS_TRANS ("Accordion"), NEEDS_TRANS ("Harmonica"), NEEDS_TRANS ("Tango Accordion"), + NEEDS_TRANS ("Acoustic Guitar (nylon)"), NEEDS_TRANS ("Acoustic Guitar (steel)"), NEEDS_TRANS ("Electric Guitar (jazz)"), NEEDS_TRANS ("Electric Guitar (clean)"), + NEEDS_TRANS ("Electric Guitar (mute)"), NEEDS_TRANS ("Overdriven Guitar"), NEEDS_TRANS ("Distortion Guitar"), NEEDS_TRANS ("Guitar Harmonics"), + NEEDS_TRANS ("Acoustic Bass"), NEEDS_TRANS ("Electric Bass (finger)"), NEEDS_TRANS ("Electric Bass (pick)"), NEEDS_TRANS ("Fretless Bass"), + NEEDS_TRANS ("Slap Bass 1"), NEEDS_TRANS ("Slap Bass 2"), NEEDS_TRANS ("Synth Bass 1"), NEEDS_TRANS ("Synth Bass 2"), + NEEDS_TRANS ("Violin"), NEEDS_TRANS ("Viola"), NEEDS_TRANS ("Cello"), NEEDS_TRANS ("Contrabass"), + NEEDS_TRANS ("Tremolo Strings"), NEEDS_TRANS ("Pizzicato Strings"), NEEDS_TRANS ("Orchestral Harp"), NEEDS_TRANS ("Timpani"), + NEEDS_TRANS ("String Ensemble 1"), NEEDS_TRANS ("String Ensemble 2"), NEEDS_TRANS ("SynthStrings 1"), NEEDS_TRANS ("SynthStrings 2"), + NEEDS_TRANS ("Choir Aahs"), NEEDS_TRANS ("Voice Oohs"), NEEDS_TRANS ("Synth Voice"), NEEDS_TRANS ("Orchestra Hit"), + NEEDS_TRANS ("Trumpet"), NEEDS_TRANS ("Trombone"), NEEDS_TRANS ("Tuba"), NEEDS_TRANS ("Muted Trumpet"), + NEEDS_TRANS ("French Horn"), NEEDS_TRANS ("Brass Section"), NEEDS_TRANS ("SynthBrass 1"), NEEDS_TRANS ("SynthBrass 2"), + NEEDS_TRANS ("Soprano Sax"), NEEDS_TRANS ("Alto Sax"), NEEDS_TRANS ("Tenor Sax"), NEEDS_TRANS ("Baritone Sax"), + NEEDS_TRANS ("Oboe"), NEEDS_TRANS ("English Horn"), NEEDS_TRANS ("Bassoon"), NEEDS_TRANS ("Clarinet"), + NEEDS_TRANS ("Piccolo"), NEEDS_TRANS ("Flute"), NEEDS_TRANS ("Recorder"), NEEDS_TRANS ("Pan Flute"), + NEEDS_TRANS ("Blown Bottle"), NEEDS_TRANS ("Shakuhachi"), NEEDS_TRANS ("Whistle"), NEEDS_TRANS ("Ocarina"), + NEEDS_TRANS ("Lead 1 (square)"), NEEDS_TRANS ("Lead 2 (sawtooth)"), NEEDS_TRANS ("Lead 3 (calliope)"), NEEDS_TRANS ("Lead 4 (chiff)"), + NEEDS_TRANS ("Lead 5 (charang)"), NEEDS_TRANS ("Lead 6 (voice)"), NEEDS_TRANS ("Lead 7 (fifths)"), NEEDS_TRANS ("Lead 8 (bass+lead)"), + NEEDS_TRANS ("Pad 1 (new age)"), NEEDS_TRANS ("Pad 2 (warm)"), NEEDS_TRANS ("Pad 3 (polysynth)"), NEEDS_TRANS ("Pad 4 (choir)"), + NEEDS_TRANS ("Pad 5 (bowed)"), NEEDS_TRANS ("Pad 6 (metallic)"), NEEDS_TRANS ("Pad 7 (halo)"), NEEDS_TRANS ("Pad 8 (sweep)"), + NEEDS_TRANS ("FX 1 (rain)"), NEEDS_TRANS ("FX 2 (soundtrack)"), NEEDS_TRANS ("FX 3 (crystal)"), NEEDS_TRANS ("FX 4 (atmosphere)"), + NEEDS_TRANS ("FX 5 (brightness)"), NEEDS_TRANS ("FX 6 (goblins)"), NEEDS_TRANS ("FX 7 (echoes)"), NEEDS_TRANS ("FX 8 (sci-fi)"), + NEEDS_TRANS ("Sitar"), NEEDS_TRANS ("Banjo"), NEEDS_TRANS ("Shamisen"), NEEDS_TRANS ("Koto"), + NEEDS_TRANS ("Kalimba"), NEEDS_TRANS ("Bag pipe"), NEEDS_TRANS ("Fiddle"), NEEDS_TRANS ("Shanai"), + NEEDS_TRANS ("Tinkle Bell"), NEEDS_TRANS ("Agogo"), NEEDS_TRANS ("Steel Drums"), NEEDS_TRANS ("Woodblock"), + NEEDS_TRANS ("Taiko Drum"), NEEDS_TRANS ("Melodic Tom"), NEEDS_TRANS ("Synth Drum"), NEEDS_TRANS ("Reverse Cymbal"), + NEEDS_TRANS ("Guitar Fret Noise"), NEEDS_TRANS ("Breath Noise"), NEEDS_TRANS ("Seashore"), NEEDS_TRANS ("Bird Tweet"), + NEEDS_TRANS ("Telephone Ring"), NEEDS_TRANS ("Helicopter"), NEEDS_TRANS ("Applause"), NEEDS_TRANS ("Gunshot") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; @@ -1070,10 +1094,10 @@ const char* MidiMessage::getGMInstrumentBankName (const int n) { static const char* names[] = { - NEEDS_TRANS("Piano"), NEEDS_TRANS("Chromatic Percussion"), NEEDS_TRANS("Organ"), NEEDS_TRANS("Guitar"), - NEEDS_TRANS("Bass"), NEEDS_TRANS("Strings"), NEEDS_TRANS("Ensemble"), NEEDS_TRANS("Brass"), - NEEDS_TRANS("Reed"), NEEDS_TRANS("Pipe"), NEEDS_TRANS("Synth Lead"), NEEDS_TRANS("Synth Pad"), - NEEDS_TRANS("Synth Effects"), NEEDS_TRANS("Ethnic"), NEEDS_TRANS("Percussive"), NEEDS_TRANS("Sound Effects") + NEEDS_TRANS ("Piano"), NEEDS_TRANS ("Chromatic Percussion"), NEEDS_TRANS ("Organ"), NEEDS_TRANS ("Guitar"), + NEEDS_TRANS ("Bass"), NEEDS_TRANS ("Strings"), NEEDS_TRANS ("Ensemble"), NEEDS_TRANS ("Brass"), + NEEDS_TRANS ("Reed"), NEEDS_TRANS ("Pipe"), NEEDS_TRANS ("Synth Lead"), NEEDS_TRANS ("Synth Pad"), + NEEDS_TRANS ("Synth Effects"), NEEDS_TRANS ("Ethnic"), NEEDS_TRANS ("Percussive"), NEEDS_TRANS ("Sound Effects") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; @@ -1083,18 +1107,18 @@ const char* MidiMessage::getRhythmInstrumentName (const int n) { static const char* names[] = { - NEEDS_TRANS("Acoustic Bass Drum"), NEEDS_TRANS("Bass Drum 1"), NEEDS_TRANS("Side Stick"), NEEDS_TRANS("Acoustic Snare"), - NEEDS_TRANS("Hand Clap"), NEEDS_TRANS("Electric Snare"), NEEDS_TRANS("Low Floor Tom"), NEEDS_TRANS("Closed Hi-Hat"), - NEEDS_TRANS("High Floor Tom"), NEEDS_TRANS("Pedal Hi-Hat"), NEEDS_TRANS("Low Tom"), NEEDS_TRANS("Open Hi-Hat"), - NEEDS_TRANS("Low-Mid Tom"), NEEDS_TRANS("Hi-Mid Tom"), NEEDS_TRANS("Crash Cymbal 1"), NEEDS_TRANS("High Tom"), - NEEDS_TRANS("Ride Cymbal 1"), NEEDS_TRANS("Chinese Cymbal"), NEEDS_TRANS("Ride Bell"), NEEDS_TRANS("Tambourine"), - NEEDS_TRANS("Splash Cymbal"), NEEDS_TRANS("Cowbell"), NEEDS_TRANS("Crash Cymbal 2"), NEEDS_TRANS("Vibraslap"), - NEEDS_TRANS("Ride Cymbal 2"), NEEDS_TRANS("Hi Bongo"), NEEDS_TRANS("Low Bongo"), NEEDS_TRANS("Mute Hi Conga"), - NEEDS_TRANS("Open Hi Conga"), NEEDS_TRANS("Low Conga"), NEEDS_TRANS("High Timbale"), NEEDS_TRANS("Low Timbale"), - NEEDS_TRANS("High Agogo"), NEEDS_TRANS("Low Agogo"), NEEDS_TRANS("Cabasa"), NEEDS_TRANS("Maracas"), - NEEDS_TRANS("Short Whistle"), NEEDS_TRANS("Long Whistle"), NEEDS_TRANS("Short Guiro"), NEEDS_TRANS("Long Guiro"), - NEEDS_TRANS("Claves"), NEEDS_TRANS("Hi Wood Block"), NEEDS_TRANS("Low Wood Block"), NEEDS_TRANS("Mute Cuica"), - NEEDS_TRANS("Open Cuica"), NEEDS_TRANS("Mute Triangle"), NEEDS_TRANS("Open Triangle") + NEEDS_TRANS ("Acoustic Bass Drum"), NEEDS_TRANS ("Bass Drum 1"), NEEDS_TRANS ("Side Stick"), NEEDS_TRANS ("Acoustic Snare"), + NEEDS_TRANS ("Hand Clap"), NEEDS_TRANS ("Electric Snare"), NEEDS_TRANS ("Low Floor Tom"), NEEDS_TRANS ("Closed Hi-Hat"), + NEEDS_TRANS ("High Floor Tom"), NEEDS_TRANS ("Pedal Hi-Hat"), NEEDS_TRANS ("Low Tom"), NEEDS_TRANS ("Open Hi-Hat"), + NEEDS_TRANS ("Low-Mid Tom"), NEEDS_TRANS ("Hi-Mid Tom"), NEEDS_TRANS ("Crash Cymbal 1"), NEEDS_TRANS ("High Tom"), + NEEDS_TRANS ("Ride Cymbal 1"), NEEDS_TRANS ("Chinese Cymbal"), NEEDS_TRANS ("Ride Bell"), NEEDS_TRANS ("Tambourine"), + NEEDS_TRANS ("Splash Cymbal"), NEEDS_TRANS ("Cowbell"), NEEDS_TRANS ("Crash Cymbal 2"), NEEDS_TRANS ("Vibraslap"), + NEEDS_TRANS ("Ride Cymbal 2"), NEEDS_TRANS ("Hi Bongo"), NEEDS_TRANS ("Low Bongo"), NEEDS_TRANS ("Mute Hi Conga"), + NEEDS_TRANS ("Open Hi Conga"), NEEDS_TRANS ("Low Conga"), NEEDS_TRANS ("High Timbale"), NEEDS_TRANS ("Low Timbale"), + NEEDS_TRANS ("High Agogo"), NEEDS_TRANS ("Low Agogo"), NEEDS_TRANS ("Cabasa"), NEEDS_TRANS ("Maracas"), + NEEDS_TRANS ("Short Whistle"), NEEDS_TRANS ("Long Whistle"), NEEDS_TRANS ("Short Guiro"), NEEDS_TRANS ("Long Guiro"), + NEEDS_TRANS ("Claves"), NEEDS_TRANS ("Hi Wood Block"), NEEDS_TRANS ("Low Wood Block"), NEEDS_TRANS ("Mute Cuica"), + NEEDS_TRANS ("Open Cuica"), NEEDS_TRANS ("Mute Triangle"), NEEDS_TRANS ("Open Triangle") }; return (n >= 35 && n <= 81) ? names[n - 35] : nullptr; @@ -1104,41 +1128,209 @@ const char* MidiMessage::getControllerName (const int n) { static const char* names[] = { - NEEDS_TRANS("Bank Select"), NEEDS_TRANS("Modulation Wheel (coarse)"), NEEDS_TRANS("Breath controller (coarse)"), + NEEDS_TRANS ("Bank Select"), NEEDS_TRANS ("Modulation Wheel (coarse)"), NEEDS_TRANS ("Breath controller (coarse)"), nullptr, - NEEDS_TRANS("Foot Pedal (coarse)"), NEEDS_TRANS("Portamento Time (coarse)"), NEEDS_TRANS("Data Entry (coarse)"), - NEEDS_TRANS("Volume (coarse)"), NEEDS_TRANS("Balance (coarse)"), + NEEDS_TRANS ("Foot Pedal (coarse)"), NEEDS_TRANS ("Portamento Time (coarse)"), NEEDS_TRANS ("Data Entry (coarse)"), + NEEDS_TRANS ("Volume (coarse)"), NEEDS_TRANS ("Balance (coarse)"), nullptr, - NEEDS_TRANS("Pan position (coarse)"), NEEDS_TRANS("Expression (coarse)"), NEEDS_TRANS("Effect Control 1 (coarse)"), - NEEDS_TRANS("Effect Control 2 (coarse)"), + NEEDS_TRANS ("Pan position (coarse)"), NEEDS_TRANS ("Expression (coarse)"), NEEDS_TRANS ("Effect Control 1 (coarse)"), + NEEDS_TRANS ("Effect Control 2 (coarse)"), nullptr, nullptr, - NEEDS_TRANS("General Purpose Slider 1"), NEEDS_TRANS("General Purpose Slider 2"), - NEEDS_TRANS("General Purpose Slider 3"), NEEDS_TRANS("General Purpose Slider 4"), + NEEDS_TRANS ("General Purpose Slider 1"), NEEDS_TRANS ("General Purpose Slider 2"), + NEEDS_TRANS ("General Purpose Slider 3"), NEEDS_TRANS ("General Purpose Slider 4"), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - NEEDS_TRANS("Bank Select (fine)"), NEEDS_TRANS("Modulation Wheel (fine)"), NEEDS_TRANS("Breath controller (fine)"), + NEEDS_TRANS ("Bank Select (fine)"), NEEDS_TRANS ("Modulation Wheel (fine)"), NEEDS_TRANS ("Breath controller (fine)"), nullptr, - NEEDS_TRANS("Foot Pedal (fine)"), NEEDS_TRANS("Portamento Time (fine)"), NEEDS_TRANS("Data Entry (fine)"), NEEDS_TRANS("Volume (fine)"), - NEEDS_TRANS("Balance (fine)"), nullptr, NEEDS_TRANS("Pan position (fine)"), NEEDS_TRANS("Expression (fine)"), - NEEDS_TRANS("Effect Control 1 (fine)"), NEEDS_TRANS("Effect Control 2 (fine)"), + NEEDS_TRANS ("Foot Pedal (fine)"), NEEDS_TRANS ("Portamento Time (fine)"), NEEDS_TRANS ("Data Entry (fine)"), NEEDS_TRANS ("Volume (fine)"), + NEEDS_TRANS ("Balance (fine)"), nullptr, NEEDS_TRANS ("Pan position (fine)"), NEEDS_TRANS ("Expression (fine)"), + NEEDS_TRANS ("Effect Control 1 (fine)"), NEEDS_TRANS ("Effect Control 2 (fine)"), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - NEEDS_TRANS("Hold Pedal (on/off)"), NEEDS_TRANS("Portamento (on/off)"), NEEDS_TRANS("Sustenuto Pedal (on/off)"), NEEDS_TRANS("Soft Pedal (on/off)"), - NEEDS_TRANS("Legato Pedal (on/off)"), NEEDS_TRANS("Hold 2 Pedal (on/off)"), NEEDS_TRANS("Sound Variation"), NEEDS_TRANS("Sound Timbre"), - NEEDS_TRANS("Sound Release Time"), NEEDS_TRANS("Sound Attack Time"), NEEDS_TRANS("Sound Brightness"), NEEDS_TRANS("Sound Control 6"), - NEEDS_TRANS("Sound Control 7"), NEEDS_TRANS("Sound Control 8"), NEEDS_TRANS("Sound Control 9"), NEEDS_TRANS("Sound Control 10"), - NEEDS_TRANS("General Purpose Button 1 (on/off)"), NEEDS_TRANS("General Purpose Button 2 (on/off)"), - NEEDS_TRANS("General Purpose Button 3 (on/off)"), NEEDS_TRANS("General Purpose Button 4 (on/off)"), + NEEDS_TRANS ("Hold Pedal (on/off)"), NEEDS_TRANS ("Portamento (on/off)"), NEEDS_TRANS ("Sustenuto Pedal (on/off)"), NEEDS_TRANS ("Soft Pedal (on/off)"), + NEEDS_TRANS ("Legato Pedal (on/off)"), NEEDS_TRANS ("Hold 2 Pedal (on/off)"), NEEDS_TRANS ("Sound Variation"), NEEDS_TRANS ("Sound Timbre"), + NEEDS_TRANS ("Sound Release Time"), NEEDS_TRANS ("Sound Attack Time"), NEEDS_TRANS ("Sound Brightness"), NEEDS_TRANS ("Sound Control 6"), + NEEDS_TRANS ("Sound Control 7"), NEEDS_TRANS ("Sound Control 8"), NEEDS_TRANS ("Sound Control 9"), NEEDS_TRANS ("Sound Control 10"), + NEEDS_TRANS ("General Purpose Button 1 (on/off)"), NEEDS_TRANS ("General Purpose Button 2 (on/off)"), + NEEDS_TRANS ("General Purpose Button 3 (on/off)"), NEEDS_TRANS ("General Purpose Button 4 (on/off)"), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - NEEDS_TRANS("Reverb Level"), NEEDS_TRANS("Tremolo Level"), NEEDS_TRANS("Chorus Level"), NEEDS_TRANS("Celeste Level"), - NEEDS_TRANS("Phaser Level"), NEEDS_TRANS("Data Button increment"), NEEDS_TRANS("Data Button decrement"), NEEDS_TRANS("Non-registered Parameter (fine)"), - NEEDS_TRANS("Non-registered Parameter (coarse)"), NEEDS_TRANS("Registered Parameter (fine)"), NEEDS_TRANS("Registered Parameter (coarse)"), + NEEDS_TRANS ("Reverb Level"), NEEDS_TRANS ("Tremolo Level"), NEEDS_TRANS ("Chorus Level"), NEEDS_TRANS ("Celeste Level"), + NEEDS_TRANS ("Phaser Level"), NEEDS_TRANS ("Data Button increment"), NEEDS_TRANS ("Data Button decrement"), NEEDS_TRANS ("Non-registered Parameter (fine)"), + NEEDS_TRANS ("Non-registered Parameter (coarse)"), NEEDS_TRANS ("Registered Parameter (fine)"), NEEDS_TRANS ("Registered Parameter (coarse)"), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - NEEDS_TRANS("All Sound Off"), NEEDS_TRANS("All Controllers Off"), NEEDS_TRANS("Local Keyboard (on/off)"), NEEDS_TRANS("All Notes Off"), - NEEDS_TRANS("Omni Mode Off"), NEEDS_TRANS("Omni Mode On"), NEEDS_TRANS("Mono Operation"), NEEDS_TRANS("Poly Operation") + NEEDS_TRANS ("All Sound Off"), NEEDS_TRANS ("All Controllers Off"), NEEDS_TRANS ("Local Keyboard (on/off)"), NEEDS_TRANS ("All Notes Off"), + NEEDS_TRANS ("Omni Mode Off"), NEEDS_TRANS ("Omni Mode On"), NEEDS_TRANS ("Mono Operation"), NEEDS_TRANS ("Poly Operation") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; } +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +struct MidiMessageTest final : public UnitTest +{ + MidiMessageTest() + : UnitTest ("MidiMessage", UnitTestCategories::midi) + {} + + void runTest() override + { + using std::begin; + using std::end; + + beginTest ("ReadVariableLengthValue should return valid, backward-compatible results"); + { + const std::vector inputs[] + { + { 0x00 }, + { 0x40 }, + { 0x7f }, + { 0x81, 0x00 }, + { 0xc0, 0x00 }, + { 0xff, 0x7f }, + { 0x81, 0x80, 0x00 }, + { 0xc0, 0x80, 0x00 }, + { 0xff, 0xff, 0x7f }, + { 0x81, 0x80, 0x80, 0x00 }, + { 0xc0, 0x80, 0x80, 0x00 }, + { 0xff, 0xff, 0xff, 0x7f } + }; + + const int outputs[] + { + 0x00, + 0x40, + 0x7f, + 0x80, + 0x2000, + 0x3fff, + 0x4000, + 0x100000, + 0x1fffff, + 0x200000, + 0x8000000, + 0xfffffff, + }; + + expectEquals (std::distance (begin (inputs), end (inputs)), + std::distance (begin (outputs), end (outputs))); + + size_t index = 0; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + + for (const auto& input : inputs) + { + auto copy = input; + + while (copy.size() < 16) + copy.push_back (0); + + const auto result = MidiMessage::readVariableLengthValue (copy.data(), + (int) copy.size()); + + expect (result.isValid()); + expectEquals (result.value, outputs[index]); + expectEquals (result.bytesUsed, (int) inputs[index].size()); + + int legacyNumUsed = 0; + const auto legacyResult = MidiMessage::readVariableLengthVal (copy.data(), + legacyNumUsed); + + expectEquals (result.value, legacyResult); + expectEquals (result.bytesUsed, legacyNumUsed); + + ++index; + } + + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_WARNINGS_MSVC + } + + beginTest ("ReadVariableLengthVal should return 0 if input is truncated"); + { + for (size_t i = 0; i != 16; ++i) + { + std::vector input; + input.resize (i, 0xFF); + + const auto result = MidiMessage::readVariableLengthValue (input.data(), + (int) input.size()); + + expect (! result.isValid()); + expectEquals (result.value, 0); + expectEquals (result.bytesUsed, 0); + } + } + + const std::vector metaEvents[] + { + // Format is 0xff, followed by a 'kind' byte, followed by a variable-length + // 'data-length' value, followed by that many data bytes + { 0xff, 0x00, 0x02, 0x00, 0x00 }, // Sequence number + { 0xff, 0x01, 0x00 }, // Text event + { 0xff, 0x02, 0x00 }, // Copyright notice + { 0xff, 0x03, 0x00 }, // Track name + { 0xff, 0x04, 0x00 }, // Instrument name + { 0xff, 0x05, 0x00 }, // Lyric + { 0xff, 0x06, 0x00 }, // Marker + { 0xff, 0x07, 0x00 }, // Cue point + { 0xff, 0x20, 0x01, 0x00 }, // Channel prefix + { 0xff, 0x2f, 0x00 }, // End of track + { 0xff, 0x51, 0x03, 0x01, 0x02, 0x03 }, // Set tempo + { 0xff, 0x54, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05 }, // SMPTE offset + { 0xff, 0x58, 0x04, 0x01, 0x02, 0x03, 0x04 }, // Time signature + { 0xff, 0x59, 0x02, 0x01, 0x02 }, // Key signature + { 0xff, 0x7f, 0x00 }, // Sequencer-specific + }; + + beginTest ("MidiMessage data constructor works for well-formed meta-events"); + { + const auto status = (uint8) 0x90; + + for (const auto& input : metaEvents) + { + int bytesUsed = 0; + const MidiMessage msg (input.data(), (int) input.size(), bytesUsed, status); + + expect (msg.isMetaEvent()); + expectEquals (msg.getMetaEventLength(), (int) input.size() - 3); + expectEquals (msg.getMetaEventType(), (int) input[1]); + } + } + + beginTest ("MidiMessage data constructor works for malformed meta-events"); + { + const auto status = (uint8) 0x90; + + const auto runTest = [&] (const std::vector& input) + { + int bytesUsed = 0; + const MidiMessage msg (input.data(), (int) input.size(), bytesUsed, status); + + expect (msg.isMetaEvent()); + expectEquals (msg.getMetaEventLength(), jmax (0, (int) input.size() - 3)); + expectEquals (msg.getMetaEventType(), input.size() >= 2 ? input[1] : -1); + }; + + runTest ({ 0xff }); + + for (const auto& input : metaEvents) + { + auto copy = input; + copy[2] = 0x40; // Set the size of the message to more bytes than are present + + runTest (copy); + } + } + } +}; + +static MidiMessageTest midiMessageTests; + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h index c0fc47a9..d2580603 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -102,7 +102,8 @@ class JUCE_API MidiMessage double timeStamp = 0, bool sysexHasEmbeddedLength = true); - /** Creates an active-sense message. + /** Creates an empty sysex message. + Since the MidiMessage has to contain a valid message, this default constructor just initialises it with an empty sysex message. */ @@ -217,6 +218,13 @@ class JUCE_API MidiMessage */ int getSysExDataSize() const noexcept; + /** Returns a span that bounds the sysex body bytes contained in this message. */ + Span getSysExDataSpan() const noexcept + { + return { reinterpret_cast (getSysExData()), + (size_t) getSysExDataSize() }; + } + //============================================================================== /** Returns true if this message is a 'key-down' event. @@ -401,7 +409,7 @@ class JUCE_API MidiMessage /** Returns true if the message is an aftertouch event. For aftertouch events, use the getNoteNumber() method to find out the key - that it applies to, and getAftertouchValue() to find out the amount. Use + that it applies to, and getAfterTouchValue() to find out the amount. Use getChannel() to find out the channel. @see getAftertouchValue, getNoteNumber @@ -854,15 +862,52 @@ class JUCE_API MidiMessage static MidiMessage createSysExMessage (const void* sysexData, int dataSize); + /** Creates a system-exclusive message. + The data passed in is wrapped with header and tail bytes of 0xf0 and 0xf7. + */ + static MidiMessage createSysExMessage (Span data); //============================================================================== + #ifndef DOXYGEN /** Reads a midi variable-length integer. - @param data the data to read the number from - @param numBytesUsed on return, this will be set to the number of bytes that were read + The `data` argument indicates the data to read the number from, + and `numBytesUsed` is used as an out-parameter to indicate the number + of bytes that were read. */ - static int readVariableLengthVal (const uint8* data, - int& numBytesUsed) noexcept; + [[deprecated ("This signature has been deprecated in favour of the safer readVariableLengthValue.")]] + static int readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept; + #endif + + /** Holds information about a variable-length value which was parsed + from a stream of bytes. + + A valid value requires that `bytesUsed` is greater than 0. + */ + struct VariableLengthValue + { + VariableLengthValue() = default; + + VariableLengthValue (int valueIn, int bytesUsedIn) + : value (valueIn), bytesUsed (bytesUsedIn) {} + + bool isValid() const noexcept { return bytesUsed > 0; } + + int value = 0; + int bytesUsed = 0; + }; + + /** Reads a midi variable-length integer, with protection against buffer overflow. + + @param data the data to read the number from + @param maxBytesToUse the number of bytes in the region following `data` + @returns a struct containing the parsed value, and the number + of bytes that were read. If parsing fails, both the + `value` and `bytesUsed` fields will be set to 0 and + `isValid()` will return false + */ + static VariableLengthValue readVariableLengthValue (const uint8* data, + int maxBytesToUse) noexcept; /** Based on the first byte of a short midi message, this uses a lookup table to return the message length (either 1, 2, or 3 bytes). diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp index 41d55d51..7a15a083 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,7 +25,6 @@ namespace juce MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) : message (mm) {} MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (std::move (mm)) {} -MidiMessageSequence::MidiEventHolder::~MidiEventHolder() {} //============================================================================== MidiMessageSequence::MidiMessageSequence() @@ -41,7 +40,7 @@ MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other) auto noteOffIndex = other.getIndexOfMatchingKeyUp (i); if (noteOffIndex >= 0) - list.getUnchecked(i)->noteOffObject = list.getUnchecked (noteOffIndex); + list.getUnchecked (i)->noteOffObject = list.getUnchecked (noteOffIndex); } } @@ -63,10 +62,6 @@ MidiMessageSequence& MidiMessageSequence::operator= (MidiMessageSequence&& other return *this; } -MidiMessageSequence::~MidiMessageSequence() -{ -} - void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept { list.swapWith (other.list); @@ -108,7 +103,7 @@ int MidiMessageSequence::getIndexOfMatchingKeyUp (int index) const noexcept if (auto* noteOff = meh->noteOffObject) { for (int i = index; i < list.size(); ++i) - if (list.getUnchecked(i) == noteOff) + if (list.getUnchecked (i) == noteOff) return i; jassertfalse; // we've somehow got a pointer to a note-off object that isn't in the sequence @@ -129,7 +124,7 @@ int MidiMessageSequence::getNextIndexAtTime (double timeStamp) const noexcept int i; for (i = 0; i < numEvents; ++i) - if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp) + if (list.getUnchecked (i)->message.getTimeStamp() >= timeStamp) break; return i; @@ -162,7 +157,7 @@ MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (MidiEventHo int i; for (i = list.size(); --i >= 0;) - if (list.getUnchecked(i)->message.getTimeStamp() <= time) + if (list.getUnchecked (i)->message.getTimeStamp() <= time) break; list.insert (i + 1, newEvent); @@ -232,7 +227,7 @@ void MidiMessageSequence::updateMatchedPairs() noexcept { for (int i = 0; i < list.size(); ++i) { - auto* meh = list.getUnchecked(i); + auto* meh = list.getUnchecked (i); auto& m1 = meh->message; if (m1.isNoteOn()) @@ -244,7 +239,7 @@ void MidiMessageSequence::updateMatchedPairs() noexcept for (int j = i + 1; j < len; ++j) { - auto* meh2 = list.getUnchecked(j); + auto* meh2 = list.getUnchecked (j); auto& m = meh2->message; if (m.getNoteNumber() == note && m.getChannel() == chan) @@ -271,7 +266,7 @@ void MidiMessageSequence::updateMatchedPairs() noexcept void MidiMessageSequence::addTimeToMessages (double delta) noexcept { - if (delta != 0) + if (! approximatelyEqual (delta, 0.0)) for (auto* m : list) m->message.addToTimeStamp (delta); } @@ -297,61 +292,192 @@ void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequenc void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove) { for (int i = list.size(); --i >= 0;) - if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove)) - list.remove(i); + if (list.getUnchecked (i)->message.isForChannel (channelNumberToRemove)) + list.remove (i); } void MidiMessageSequence::deleteSysExMessages() { for (int i = list.size(); --i >= 0;) - if (list.getUnchecked(i)->message.isSysEx()) - list.remove(i); + if (list.getUnchecked (i)->message.isSysEx()) + list.remove (i); } //============================================================================== -void MidiMessageSequence::createControllerUpdatesForTime (int channelNumber, double time, Array& dest) +class OptionalPitchWheel { - bool doneProg = false; - bool donePitchWheel = false; - bool doneControllers[128] = {}; + Optional value; - for (int i = list.size(); --i >= 0;) +public: + void emit (int channel, Array& out) const + { + if (value.hasValue()) + out.add (MidiMessage::pitchWheel (channel, *value)); + } + + void set (int v) { - auto& mm = list.getUnchecked(i)->message; + value = v; + } +}; - if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time) +class OptionalControllerValues +{ + Optional values[128]; + +public: + void emit (int channel, Array& out) const + { + for (auto it = std::begin (values); it != std::end (values); ++it) + if (it->hasValue()) + out.add (MidiMessage::controllerEvent (channel, (int) std::distance (std::begin (values), it), **it)); + } + + void set (int controller, int value) + { + values[controller] = (char) value; + } +}; + +class OptionalProgramChange +{ + Optional value, bankLSB, bankMSB; + +public: + void emit (int channel, double time, Array& out) const + { + if (! value.hasValue()) + return; + + if (bankLSB.hasValue() && bankMSB.hasValue()) { - if (mm.isProgramChange() && ! doneProg) - { - doneProg = true; - dest.add (MidiMessage (mm, 0.0)); - } - else if (mm.isPitchWheel() && ! donePitchWheel) + out.add (MidiMessage::controllerEvent (channel, 0x00, *bankMSB).withTimeStamp (time)); + out.add (MidiMessage::controllerEvent (channel, 0x20, *bankLSB).withTimeStamp (time)); + } + + out.add (MidiMessage::programChange (channel, *value).withTimeStamp (time)); + } + + // Returns true if this is a bank number change, and false otherwise. + bool trySetBank (int controller, int v) + { + switch (controller) + { + case 0x00: bankMSB = (char) v; return true; + case 0x20: bankLSB = (char) v; return true; + } + + return false; + } + + void setProgram (int v) { value = (char) v; } +}; + +class ParameterNumberState +{ + enum class Kind { rpn, nrpn }; + + Optional newestRpnLsb, newestRpnMsb, newestNrpnLsb, newestNrpnMsb, lastSentLsb, lastSentMsb; + Kind lastSentKind = Kind::rpn, newestKind = Kind::rpn; + +public: + // If the effective parameter number has changed since the last time this function was called, + // this will emit the current parameter in full (MSB and LSB). + // This should be called before each data message (entry, increment, decrement: 0x06, 0x26, 0x60, 0x61) + // to ensure that the data message operates on the correct parameter number. + void sendIfNecessary (int channel, double time, Array& out) + { + const auto newestMsb = newestKind == Kind::rpn ? newestRpnMsb : newestNrpnMsb; + const auto newestLsb = newestKind == Kind::rpn ? newestRpnLsb : newestNrpnLsb; + + auto lastSent = std::tie (lastSentKind, lastSentMsb, lastSentLsb); + const auto newest = std::tie (newestKind, newestMsb, newestLsb); + + if (lastSent == newest || ! newestMsb.hasValue() || ! newestLsb.hasValue()) + return; + + out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x65 : 0x63, *newestMsb).withTimeStamp (time)); + out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x64 : 0x62, *newestLsb).withTimeStamp (time)); + + lastSent = newest; + } + + // Returns true if this is a parameter number change, and false otherwise. + bool trySetProgramNumber (int controller, int value) + { + switch (controller) + { + case 0x65: newestRpnMsb = (char) value; newestKind = Kind::rpn; return true; + case 0x64: newestRpnLsb = (char) value; newestKind = Kind::rpn; return true; + case 0x63: newestNrpnMsb = (char) value; newestKind = Kind::nrpn; return true; + case 0x62: newestNrpnLsb = (char) value; newestKind = Kind::nrpn; return true; + } + + return false; + } +}; + +void MidiMessageSequence::createControllerUpdatesForTime (int channel, double time, Array& dest) +{ + OptionalProgramChange programChange; + OptionalControllerValues controllers; + OptionalPitchWheel pitchWheel; + ParameterNumberState parameterNumberState; + + for (const auto& item : list) + { + const auto& mm = item->message; + + if (! (mm.isForChannel (channel) && mm.getTimeStamp() <= time)) + continue; + + if (mm.isController()) + { + const auto num = mm.getControllerNumber(); + + if (parameterNumberState.trySetProgramNumber (num, mm.getControllerValue())) + continue; + + if (programChange.trySetBank (num, mm.getControllerValue())) + continue; + + constexpr int passthroughs[] { 0x06, 0x26, 0x60, 0x61 }; + + if (std::find (std::begin (passthroughs), std::end (passthroughs), num) != std::end (passthroughs)) { - donePitchWheel = true; - dest.add (MidiMessage (mm, 0.0)); + parameterNumberState.sendIfNecessary (channel, mm.getTimeStamp(), dest); + dest.add (mm); } - else if (mm.isController()) + else { - auto controllerNumber = mm.getControllerNumber(); - jassert (isPositiveAndBelow (controllerNumber, 128)); - - if (! doneControllers[controllerNumber]) - { - doneControllers[controllerNumber] = true; - dest.add (MidiMessage (mm, 0.0)); - } + controllers.set (num, mm.getControllerValue()); } } + else if (mm.isProgramChange()) + { + programChange.setProgram (mm.getProgramChangeNumber()); + } + else if (mm.isPitchWheel()) + { + pitchWheel.set (mm.getPitchWheelValue()); + } } -} + pitchWheel.emit (channel, dest); + controllers.emit (channel, dest); + + // Also emits bank change messages if necessary. + programChange.emit (channel, time, dest); + + // Set the parameter number to its final state. + parameterNumberState.sendIfNecessary (channel, time, dest); +} //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS -struct MidiMessageSequenceTest : public UnitTest +struct MidiMessageSequenceTest final : public UnitTest { MidiMessageSequenceTest() : UnitTest ("MidiMessageSequence", UnitTestCategories::midi) @@ -402,6 +528,338 @@ struct MidiMessageSequenceTest : public UnitTest expectEquals (s.getNumEvents(), 7); expectEquals (s.getIndexOfMatchingKeyUp (0), -1); // Truncated note, should be no note off expectEquals (s.getTimeOfMatchingKeyUp (1), 5.0); + + struct ControlValue { int control, value; }; + + struct DataEntry + { + int controllerBase, channel, parameter, value; + double time; + + std::array getControlValues() const + { + return { { { controllerBase + 1, (parameter >> 7) & 0x7f }, + { controllerBase + 0, (parameter >> 0) & 0x7f }, + { 0x06, (value >> 7) & 0x7f }, + { 0x26, (value >> 0) & 0x7f } } }; + } + + void addToSequence (MidiMessageSequence& s) const + { + for (const auto& pair : getControlValues()) + s.addEvent (MidiMessage::controllerEvent (channel, pair.control, pair.value), time); + } + + bool matches (const MidiMessage* begin, const MidiMessage* end) const + { + const auto isEqual = [this] (const ControlValue& cv, const MidiMessage& msg) + { + return exactlyEqual (msg.getTimeStamp(), time) + && msg.isController() + && msg.getChannel() == channel + && msg.getControllerNumber() == cv.control + && msg.getControllerValue() == cv.value; + }; + + const auto pairs = getControlValues(); + return std::equal (pairs.begin(), pairs.end(), begin, end, isEqual); + } + }; + + const auto addNrpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0) + { + DataEntry { 0x62, channel, parameter, value, time }.addToSequence (seq); + }; + + const auto addRpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0) + { + DataEntry { 0x64, channel, parameter, value, time }.addToSequence (seq); + }; + + const auto checkNrpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0) + { + expect (DataEntry { 0x62, channel, parameter, value, time }.matches (begin, end)); + }; + + const auto checkRpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0) + { + expect (DataEntry { 0x64, channel, parameter, value, time }.matches (begin, end)); + }; + + beginTest ("createControllerUpdatesForTime should emit (N)RPN components in the correct order"); + { + const auto channel = 1; + const auto number = 200; + const auto value = 300; + + MidiMessageSequence sequence; + addNrpn (sequence, channel, number, value); + + Array m; + sequence.createControllerUpdatesForTime (channel, 1.0, m); + + checkNrpn (m.begin(), m.end(), channel, number, value); + } + + beginTest ("createControllerUpdatesForTime ignores (N)RPNs after the final requested time"); + { + const auto channel = 2; + const auto number = 123; + const auto value = 456; + + MidiMessageSequence sequence; + addRpn (sequence, channel, number, value, 0.5); + addRpn (sequence, channel, 111, 222, 1.5); + addRpn (sequence, channel, 333, 444, 2.5); + + Array m; + sequence.createControllerUpdatesForTime (channel, 1.0, m); + + checkRpn (m.begin(), std::next (m.begin(), 4), channel, number, value, 0.5); + } + + beginTest ("createControllerUpdatesForTime should emit separate (N)RPN messages when appropriate"); + { + const auto channel = 2; + const auto numberA = 1111; + const auto valueA = 9999; + + const auto numberB = 8888; + const auto valueB = 2222; + + const auto numberC = 7777; + const auto valueC = 3333; + + const auto numberD = 6666; + const auto valueD = 4444; + + const auto time = 0.5; + + MidiMessageSequence sequence; + addRpn (sequence, channel, numberA, valueA, time); + addRpn (sequence, channel, numberB, valueB, time); + addNrpn (sequence, channel, numberC, valueC, time); + addNrpn (sequence, channel, numberD, valueD, time); + + Array m; + sequence.createControllerUpdatesForTime (channel, time * 2, m); + + checkRpn (std::next (m.begin(), 0), std::next (m.begin(), 4), channel, numberA, valueA, time); + checkRpn (std::next (m.begin(), 4), std::next (m.begin(), 8), channel, numberB, valueB, time); + checkNrpn (std::next (m.begin(), 8), std::next (m.begin(), 12), channel, numberC, valueC, time); + checkNrpn (std::next (m.begin(), 12), std::next (m.begin(), 16), channel, numberD, valueD, time); + } + + beginTest ("createControllerUpdatesForTime correctly emits (N)RPN messages on multiple channels"); + { + struct Info { int channel, number, value; }; + + const Info infos[] { { 2, 1111, 9999 }, + { 8, 8888, 2222 }, + { 5, 7777, 3333 }, + { 1, 6666, 4444 } }; + + const auto time = 0.5; + + MidiMessageSequence sequence; + + for (const auto& info : infos) + addRpn (sequence, info.channel, info.number, info.value, time); + + for (const auto& info : infos) + { + Array m; + sequence.createControllerUpdatesForTime (info.channel, time * 2, m); + checkRpn (std::next (m.begin(), 0), std::next (m.begin(), 4), info.channel, info.number, info.value, time); + } + } + + const auto messagesAreEqual = [] (const MidiMessage& a, const MidiMessage& b) + { + return std::equal (a.getRawData(), a.getRawData() + a.getRawDataSize(), + b.getRawData(), b.getRawData() + b.getRawDataSize()); + }; + + beginTest ("createControllerUpdatesForTime sends bank select messages when the next program is in a new bank"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + sequence.addEvent (MidiMessage::programChange (channel, 5), time); + + sequence.addEvent (MidiMessage::controllerEvent (channel, 0x00, 128), time); + sequence.addEvent (MidiMessage::controllerEvent (channel, 0x20, 64), time); + sequence.addEvent (MidiMessage::programChange (channel, 63), time); + + const Array finalEvents { MidiMessage::controllerEvent (channel, 0x00, 50), + MidiMessage::controllerEvent (channel, 0x20, 40), + MidiMessage::programChange (channel, 30) }; + + for (const auto& e : finalEvents) + sequence.addEvent (e); + + Array m; + sequence.createControllerUpdatesForTime (channel, 1.0, m); + + expect (std::equal (m.begin(), m.end(), finalEvents.begin(), finalEvents.end(), messagesAreEqual)); + } + + beginTest ("createControllerUpdatesForTime preserves all Data Increment and Data Decrement messages"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + const Array messages { MidiMessage::controllerEvent (channel, 0x60, 0), + MidiMessage::controllerEvent (channel, 0x06, 100), + MidiMessage::controllerEvent (channel, 0x26, 50), + MidiMessage::controllerEvent (channel, 0x60, 10), + MidiMessage::controllerEvent (channel, 0x61, 10), + MidiMessage::controllerEvent (channel, 0x06, 20), + MidiMessage::controllerEvent (channel, 0x26, 30), + MidiMessage::controllerEvent (channel, 0x61, 10), + MidiMessage::controllerEvent (channel, 0x61, 20) }; + + for (const auto& m : messages) + sequence.addEvent (m, time); + + Array m; + sequence.createControllerUpdatesForTime (channel, 1.0, m); + + expect (std::equal (m.begin(), m.end(), messages.begin(), messages.end(), messagesAreEqual)); + } + + beginTest ("createControllerUpdatesForTime does not emit redundant parameter number changes"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + const Array messages { MidiMessage::controllerEvent (channel, 0x65, 0), + MidiMessage::controllerEvent (channel, 0x64, 100), + MidiMessage::controllerEvent (channel, 0x63, 50), + MidiMessage::controllerEvent (channel, 0x62, 10), + MidiMessage::controllerEvent (channel, 0x06, 10) }; + + for (const auto& m : messages) + sequence.addEvent (m, time); + + Array m; + sequence.createControllerUpdatesForTime (channel, 1.0, m); + + const Array expected { MidiMessage::controllerEvent (channel, 0x63, 50), + MidiMessage::controllerEvent (channel, 0x62, 10), + MidiMessage::controllerEvent (channel, 0x06, 10) }; + + expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual)); + } + + beginTest ("createControllerUpdatesForTime sets parameter number correctly at end of sequence"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + const Array messages { MidiMessage::controllerEvent (channel, 0x65, 0), + MidiMessage::controllerEvent (channel, 0x64, 100), + MidiMessage::controllerEvent (channel, 0x63, 50), + MidiMessage::controllerEvent (channel, 0x62, 10), + MidiMessage::controllerEvent (channel, 0x06, 10), + MidiMessage::controllerEvent (channel, 0x64, 5) }; + + for (const auto& m : messages) + sequence.addEvent (m, time); + + const auto finalTime = 1.0; + + Array m; + sequence.createControllerUpdatesForTime (channel, finalTime, m); + + const Array expected { MidiMessage::controllerEvent (channel, 0x63, 50), + MidiMessage::controllerEvent (channel, 0x62, 10), + MidiMessage::controllerEvent (channel, 0x06, 10), + // Note: we should send both the MSB and LSB! + MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime), + MidiMessage::controllerEvent (channel, 0x64, 5).withTimeStamp (finalTime) }; + + expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual)); + } + + beginTest ("createControllerUpdatesForTime does not emit duplicate parameter number change messages"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + const Array messages { MidiMessage::controllerEvent (channel, 0x65, 1), + MidiMessage::controllerEvent (channel, 0x64, 2), + MidiMessage::controllerEvent (channel, 0x63, 3), + MidiMessage::controllerEvent (channel, 0x62, 4), + MidiMessage::controllerEvent (channel, 0x06, 10), + MidiMessage::controllerEvent (channel, 0x63, 30), + MidiMessage::controllerEvent (channel, 0x62, 40), + MidiMessage::controllerEvent (channel, 0x63, 3), + MidiMessage::controllerEvent (channel, 0x62, 4), + MidiMessage::controllerEvent (channel, 0x60, 5), + MidiMessage::controllerEvent (channel, 0x65, 10) }; + + for (const auto& m : messages) + sequence.addEvent (m, time); + + const auto finalTime = 1.0; + + Array m; + sequence.createControllerUpdatesForTime (channel, finalTime, m); + + const Array expected { MidiMessage::controllerEvent (channel, 0x63, 3), + MidiMessage::controllerEvent (channel, 0x62, 4), + MidiMessage::controllerEvent (channel, 0x06, 10), + // Parameter number is set to (30, 40) then back to (3, 4), + // so there is no need to resend it + MidiMessage::controllerEvent (channel, 0x60, 5), + // Set parameter number to final value + MidiMessage::controllerEvent (channel, 0x65, 10).withTimeStamp (finalTime), + MidiMessage::controllerEvent (channel, 0x64, 2) .withTimeStamp (finalTime) }; + + expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual)); + } + + beginTest ("createControllerUpdatesForTime emits bank change messages immediately before program change"); + { + MidiMessageSequence sequence; + + const auto time = 0.0; + const auto channel = 1; + + const Array messages { MidiMessage::controllerEvent (channel, 0x00, 1), + MidiMessage::controllerEvent (channel, 0x20, 2), + MidiMessage::controllerEvent (channel, 0x65, 0), + MidiMessage::controllerEvent (channel, 0x64, 0), + MidiMessage::programChange (channel, 5) }; + + for (const auto& m : messages) + sequence.addEvent (m, time); + + const auto finalTime = 1.0; + + Array m; + sequence.createControllerUpdatesForTime (channel, finalTime, m); + + const Array expected { MidiMessage::controllerEvent (channel, 0x00, 1), + MidiMessage::controllerEvent (channel, 0x20, 2), + MidiMessage::programChange (channel, 5), + MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime), + MidiMessage::controllerEvent (channel, 0x64, 0).withTimeStamp (finalTime) }; + + + expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual)); + } } }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h index 9068ce1f..88d794f6 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -53,9 +53,6 @@ class JUCE_API MidiMessageSequence /** Move assignment operator */ MidiMessageSequence& operator= (MidiMessageSequence&&) noexcept; - /** Destructor. */ - ~MidiMessageSequence(); - //============================================================================== /** Structure used to hold midi events in the sequence. @@ -68,9 +65,6 @@ class JUCE_API MidiMessageSequence { public: //============================================================================== - /** Destructor. */ - ~MidiEventHolder(); - /** The message itself, whose timestamp is used to specify the event's time. */ MidiMessage message; @@ -277,6 +271,21 @@ class JUCE_API MidiMessageSequence As well as controllers, it will also recreate the midi program number and pitch bend position. + This function has special handling for the "bank select" and "data entry" + controllers (0x00, 0x20, 0x06, 0x26, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65). + + If the sequence contains multiple bank select and program change messages, + only the bank select messages immediately preceding the final program change + message will be kept. + + All "data increment" and "data decrement" messages will be retained. Some hardware will + ignore the requested increment/decrement values, so retaining all messages is the only + way to ensure compatibility with all hardware. + + "Parameter number" changes will be slightly condensed. Only the parameter number + events immediately preceding each data entry event will be kept. The parameter number + will also be set to its final value at the end of the sequence, if necessary. + @param channelNumber the midi channel to look for, in the range 1 to 16. Controllers for other channels will be ignored. @param time the time at which you want to find out the state - there are diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp index 586e7db3..426e43b0 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,47 +23,46 @@ namespace juce { -MidiRPNDetector::MidiRPNDetector() noexcept -{ -} - -MidiRPNDetector::~MidiRPNDetector() noexcept -{ -} - bool MidiRPNDetector::parseControllerMessage (int midiChannel, int controllerNumber, int controllerValue, MidiRPNMessage& result) noexcept { - jassert (midiChannel >= 1 && midiChannel <= 16); + auto parsed = tryParse (midiChannel, controllerNumber, controllerValue); + + if (! parsed.has_value()) + return false; + + result = *parsed; + return true; +} + +std::optional MidiRPNDetector::tryParse (int midiChannel, + int controllerNumber, + int controllerValue) +{ + jassert (midiChannel > 0 && midiChannel <= 16); jassert (controllerNumber >= 0 && controllerNumber < 128); jassert (controllerValue >= 0 && controllerValue < 128); - return states[midiChannel - 1].handleController (midiChannel, controllerNumber, controllerValue, result); + return states[midiChannel - 1].handleController (midiChannel, controllerNumber, controllerValue); } void MidiRPNDetector::reset() noexcept { - for (int i = 0; i < 16; ++i) + for (auto& state : states) { - states[i].parameterMSB = 0xff; - states[i].parameterLSB = 0xff; - states[i].resetValue(); - states[i].isNRPN = false; + state.parameterMSB = 0xff; + state.parameterLSB = 0xff; + state.resetValue(); + state.isNRPN = false; } } //============================================================================== -MidiRPNDetector::ChannelState::ChannelState() noexcept - : parameterMSB (0xff), parameterLSB (0xff), valueMSB (0xff), valueLSB (0xff), isNRPN (false) -{ -} - -bool MidiRPNDetector::ChannelState::handleController (int channel, - int controllerNumber, - int value, - MidiRPNMessage& result) noexcept +std::optional MidiRPNDetector::ChannelState::handleController (int channel, + int controllerNumber, + int value) noexcept { switch (controllerNumber) { @@ -73,13 +72,11 @@ bool MidiRPNDetector::ChannelState::handleController (int channel, case 0x64: parameterLSB = uint8 (value); resetValue(); isNRPN = false; break; case 0x65: parameterMSB = uint8 (value); resetValue(); isNRPN = false; break; - case 0x06: valueMSB = uint8 (value); return sendIfReady (channel, result); - case 0x26: valueLSB = uint8 (value); break; - - default: break; + case 0x06: valueMSB = uint8 (value); valueLSB = 0xff; return sendIfReady (channel); + case 0x26: valueLSB = uint8 (value); return sendIfReady (channel); } - return false; + return {}; } void MidiRPNDetector::ChannelState::resetValue() noexcept @@ -89,32 +86,28 @@ void MidiRPNDetector::ChannelState::resetValue() noexcept } //============================================================================== -bool MidiRPNDetector::ChannelState::sendIfReady (int channel, MidiRPNMessage& result) noexcept +std::optional MidiRPNDetector::ChannelState::sendIfReady (int channel) noexcept { - if (parameterMSB < 0x80 && parameterLSB < 0x80) - { - if (valueMSB < 0x80) - { - result.channel = channel; - result.parameterNumber = (parameterMSB << 7) + parameterLSB; - result.isNRPN = isNRPN; + if (parameterMSB >= 0x80 || parameterLSB >= 0x80 || valueMSB >= 0x80) + return {}; - if (valueLSB < 0x80) - { - result.value = (valueMSB << 7) + valueLSB; - result.is14BitValue = true; - } - else - { - result.value = valueMSB; - result.is14BitValue = false; - } + MidiRPNMessage result{}; + result.channel = channel; + result.parameterNumber = (parameterMSB << 7) + parameterLSB; + result.isNRPN = isNRPN; - return true; - } + if (valueLSB < 0x80) + { + result.value = (valueMSB << 7) + valueLSB; + result.is14BitValue = true; + } + else + { + result.value = valueMSB; + result.is14BitValue = false; } - return false; + return result; } //============================================================================== @@ -137,25 +130,26 @@ MidiBuffer MidiRPNGenerator::generate (int midiChannel, jassert (parameterNumber >= 0 && parameterNumber < 16384); jassert (value >= 0 && value < (use14BitValue ? 16384 : 128)); - uint8 parameterLSB = uint8 (parameterNumber & 0x0000007f); - uint8 parameterMSB = uint8 (parameterNumber >> 7); + auto parameterLSB = uint8 (parameterNumber & 0x0000007f); + auto parameterMSB = uint8 (parameterNumber >> 7); uint8 valueLSB = use14BitValue ? uint8 (value & 0x0000007f) : 0x00; uint8 valueMSB = use14BitValue ? uint8 (value >> 7) : uint8 (value); - uint8 channelByte = uint8 (0xb0 + midiChannel - 1); + auto channelByte = uint8 (0xb0 + midiChannel - 1); MidiBuffer buffer; buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x62 : 0x64, parameterLSB), 0); buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x63 : 0x65, parameterMSB), 0); - // sending the value LSB is optional, but must come before sending the value MSB: + buffer.addEvent (MidiMessage (channelByte, 0x06, valueMSB), 0); + + // According to the MIDI spec, whenever a MSB is received, the corresponding LSB will + // be reset. Therefore, the LSB should be sent after the MSB. if (use14BitValue) buffer.addEvent (MidiMessage (channelByte, 0x26, valueLSB), 0); - buffer.addEvent (MidiMessage (channelByte, 0x06, valueMSB), 0); - return buffer; } @@ -164,7 +158,7 @@ MidiBuffer MidiRPNGenerator::generate (int midiChannel, //============================================================================== #if JUCE_UNIT_TESTS -class MidiRPNDetectorTests : public UnitTest +class MidiRPNDetectorTests final : public UnitTest { public: MidiRPNDetectorTests() @@ -173,134 +167,196 @@ class MidiRPNDetectorTests : public UnitTest void runTest() override { - beginTest ("7-bit RPN"); + // From the MIDI 1.0 spec: + // If 128 steps of resolution is sufficient the second byte (LSB) of the data value can be + // omitted. If both the MSB and LSB are sent initially, a subsequent fine adjustment only + // requires the sending of the LSB. The MSB does not have to be retransmitted. If a + // subsequent major adjustment is necessary the MSB must be transmitted again. When an MSB + // is received, the receiver should set its concept of the LSB to zero. + + beginTest ("Individual MSB is parsed as 7-bit"); + { + MidiRPNDetector detector; + expect (! detector.tryParse (2, 101, 0)); + expect (! detector.tryParse (2, 100, 7)); + + auto parsed = detector.tryParse (2, 6, 42); + expect (parsed.has_value()); + + expectEquals (parsed->channel, 2); + expectEquals (parsed->parameterNumber, 7); + expectEquals (parsed->value, 42); + expect (! parsed->isNRPN); + expect (! parsed->is14BitValue); + } + + beginTest ("LSB without preceding MSB is ignored"); { MidiRPNDetector detector; - MidiRPNMessage rpn; - expect (! detector.parseControllerMessage (2, 101, 0, rpn)); - expect (! detector.parseControllerMessage (2, 100, 7, rpn)); - expect (detector.parseControllerMessage (2, 6, 42, rpn)); - - expectEquals (rpn.channel, 2); - expectEquals (rpn.parameterNumber, 7); - expectEquals (rpn.value, 42); - expect (! rpn.isNRPN); - expect (! rpn.is14BitValue); + expect (! detector.tryParse (2, 101, 0)); + expect (! detector.tryParse (2, 100, 7)); + expect (! detector.tryParse (2, 38, 42)); } - beginTest ("14-bit RPN"); + beginTest ("LSB following MSB is parsed as 14-bit"); { MidiRPNDetector detector; - MidiRPNMessage rpn; - expect (! detector.parseControllerMessage (1, 100, 44, rpn)); - expect (! detector.parseControllerMessage (1, 101, 2, rpn)); - expect (! detector.parseControllerMessage (1, 38, 94, rpn)); - expect (detector.parseControllerMessage (1, 6, 1, rpn)); - - expectEquals (rpn.channel, 1); - expectEquals (rpn.parameterNumber, 300); - expectEquals (rpn.value, 222); - expect (! rpn.isNRPN); - expect (rpn.is14BitValue); + expect (! detector.tryParse (1, 101, 2)); + expect (! detector.tryParse (1, 100, 44)); + + expect (detector.tryParse (1, 6, 1).has_value()); + + auto lsbParsed = detector.tryParse (1, 38, 94); + expect (lsbParsed.has_value()); + + expectEquals (lsbParsed->channel, 1); + expectEquals (lsbParsed->parameterNumber, 300); + expectEquals (lsbParsed->value, 222); + expect (! lsbParsed->isNRPN); + expect (lsbParsed->is14BitValue); + } + + beginTest ("Multiple LSB following MSB re-use the MSB"); + { + MidiRPNDetector detector; + expect (! detector.tryParse (1, 101, 2)); + expect (! detector.tryParse (1, 100, 43)); + + expect (detector.tryParse (1, 6, 1).has_value()); + + expect (detector.tryParse (1, 38, 94).has_value()); + expect (detector.tryParse (1, 38, 95).has_value()); + expect (detector.tryParse (1, 38, 96).has_value()); + + auto lsbParsed = detector.tryParse (1, 38, 97); + expect (lsbParsed.has_value()); + + expectEquals (lsbParsed->channel, 1); + expectEquals (lsbParsed->parameterNumber, 299); + expectEquals (lsbParsed->value, 225); + expect (! lsbParsed->isNRPN); + expect (lsbParsed->is14BitValue); + } + + beginTest ("Sending a new MSB resets the LSB"); + { + MidiRPNDetector detector; + expect (! detector.tryParse (1, 101, 3)); + expect (! detector.tryParse (1, 100, 43)); + + expect (detector.tryParse (1, 6, 1).has_value()); + expect (detector.tryParse (1, 38, 94).has_value()); + + auto newMsb = detector.tryParse (1, 6, 2); + expect (newMsb.has_value()); + + expectEquals (newMsb->channel, 1); + expectEquals (newMsb->parameterNumber, 427); + expectEquals (newMsb->value, 2); + expect (! newMsb->isNRPN); + expect (! newMsb->is14BitValue); } beginTest ("RPNs on multiple channels simultaneously"); { MidiRPNDetector detector; - MidiRPNMessage rpn; - expect (! detector.parseControllerMessage (1, 100, 44, rpn)); - expect (! detector.parseControllerMessage (2, 101, 0, rpn)); - expect (! detector.parseControllerMessage (1, 101, 2, rpn)); - expect (! detector.parseControllerMessage (2, 100, 7, rpn)); - expect (! detector.parseControllerMessage (1, 38, 94, rpn)); - expect (detector.parseControllerMessage (2, 6, 42, rpn)); - - expectEquals (rpn.channel, 2); - expectEquals (rpn.parameterNumber, 7); - expectEquals (rpn.value, 42); - expect (! rpn.isNRPN); - expect (! rpn.is14BitValue); - - expect (detector.parseControllerMessage (1, 6, 1, rpn)); - - expectEquals (rpn.channel, 1); - expectEquals (rpn.parameterNumber, 300); - expectEquals (rpn.value, 222); - expect (! rpn.isNRPN); - expect (rpn.is14BitValue); + expect (! detector.tryParse (1, 100, 44)); + expect (! detector.tryParse (2, 101, 0)); + expect (! detector.tryParse (1, 101, 2)); + expect (! detector.tryParse (2, 100, 7)); + expect (detector.tryParse (1, 6, 1).has_value()); + + auto channelTwo = detector.tryParse (2, 6, 42); + expect (channelTwo.has_value()); + + expectEquals (channelTwo->channel, 2); + expectEquals (channelTwo->parameterNumber, 7); + expectEquals (channelTwo->value, 42); + expect (! channelTwo->isNRPN); + expect (! channelTwo->is14BitValue); + + auto channelOne = detector.tryParse (1, 38, 94); + expect (channelOne.has_value()); + + expectEquals (channelOne->channel, 1); + expectEquals (channelOne->parameterNumber, 300); + expectEquals (channelOne->value, 222); + expect (! channelOne->isNRPN); + expect (channelOne->is14BitValue); } beginTest ("14-bit RPN with value within 7-bit range"); { MidiRPNDetector detector; - MidiRPNMessage rpn; - expect (! detector.parseControllerMessage (16, 100, 0 , rpn)); - expect (! detector.parseControllerMessage (16, 101, 0, rpn)); - expect (! detector.parseControllerMessage (16, 38, 3, rpn)); - expect (detector.parseControllerMessage (16, 6, 0, rpn)); - - expectEquals (rpn.channel, 16); - expectEquals (rpn.parameterNumber, 0); - expectEquals (rpn.value, 3); - expect (! rpn.isNRPN); - expect (rpn.is14BitValue); + expect (! detector.tryParse (16, 100, 0)); + expect (! detector.tryParse (16, 101, 0)); + expect (detector.tryParse (16, 6, 0).has_value()); + + auto parsed = detector.tryParse (16, 38, 3); + expect (parsed.has_value()); + + expectEquals (parsed->channel, 16); + expectEquals (parsed->parameterNumber, 0); + expectEquals (parsed->value, 3); + expect (! parsed->isNRPN); + expect (parsed->is14BitValue); } beginTest ("invalid RPN (wrong order)"); { MidiRPNDetector detector; - MidiRPNMessage rpn; - expect (! detector.parseControllerMessage (2, 6, 42, rpn)); - expect (! detector.parseControllerMessage (2, 101, 0, rpn)); - expect (! detector.parseControllerMessage (2, 100, 7, rpn)); + expect (! detector.tryParse (2, 6, 42)); + expect (! detector.tryParse (2, 101, 0)); + expect (! detector.tryParse (2, 100, 7)); } beginTest ("14-bit RPN interspersed with unrelated CC messages"); { MidiRPNDetector detector; - MidiRPNMessage rpn; - expect (! detector.parseControllerMessage (16, 3, 80, rpn)); - expect (! detector.parseControllerMessage (16, 100, 0 , rpn)); - expect (! detector.parseControllerMessage (16, 4, 81, rpn)); - expect (! detector.parseControllerMessage (16, 101, 0, rpn)); - expect (! detector.parseControllerMessage (16, 5, 82, rpn)); - expect (! detector.parseControllerMessage (16, 5, 83, rpn)); - expect (! detector.parseControllerMessage (16, 38, 3, rpn)); - expect (! detector.parseControllerMessage (16, 4, 84, rpn)); - expect (! detector.parseControllerMessage (16, 3, 85, rpn)); - expect (detector.parseControllerMessage (16, 6, 0, rpn)); - - expectEquals (rpn.channel, 16); - expectEquals (rpn.parameterNumber, 0); - expectEquals (rpn.value, 3); - expect (! rpn.isNRPN); - expect (rpn.is14BitValue); + expect (! detector.tryParse (16, 3, 80)); + expect (! detector.tryParse (16, 100, 0)); + expect (! detector.tryParse (16, 4, 81)); + expect (! detector.tryParse (16, 101, 0)); + expect (! detector.tryParse (16, 5, 82)); + expect (! detector.tryParse (16, 5, 83)); + expect (detector.tryParse (16, 6, 0).has_value()); + expect (! detector.tryParse (16, 4, 84).has_value()); + expect (! detector.tryParse (16, 3, 85).has_value()); + + auto parsed = detector.tryParse (16, 38, 3); + expect (parsed.has_value()); + + expectEquals (parsed->channel, 16); + expectEquals (parsed->parameterNumber, 0); + expectEquals (parsed->value, 3); + expect (! parsed->isNRPN); + expect (parsed->is14BitValue); } beginTest ("14-bit NRPN"); { MidiRPNDetector detector; - MidiRPNMessage rpn; - expect (! detector.parseControllerMessage (1, 98, 44, rpn)); - expect (! detector.parseControllerMessage (1, 99 , 2, rpn)); - expect (! detector.parseControllerMessage (1, 38, 94, rpn)); - expect (detector.parseControllerMessage (1, 6, 1, rpn)); - - expectEquals (rpn.channel, 1); - expectEquals (rpn.parameterNumber, 300); - expectEquals (rpn.value, 222); - expect (rpn.isNRPN); - expect (rpn.is14BitValue); + expect (! detector.tryParse (1, 98, 44)); + expect (! detector.tryParse (1, 99 , 2)); + expect (detector.tryParse (1, 6, 1).has_value()); + + auto parsed = detector.tryParse (1, 38, 94); + expect (parsed.has_value()); + + expectEquals (parsed->channel, 1); + expectEquals (parsed->parameterNumber, 300); + expectEquals (parsed->value, 222); + expect (parsed->isNRPN); + expect (parsed->is14BitValue); } beginTest ("reset"); { MidiRPNDetector detector; - MidiRPNMessage rpn; - expect (! detector.parseControllerMessage (2, 101, 0, rpn)); + expect (! detector.tryParse (2, 101, 0)); detector.reset(); - expect (! detector.parseControllerMessage (2, 100, 7, rpn)); - expect (! detector.parseControllerMessage (2, 6, 42, rpn)); + expect (! detector.tryParse (2, 100, 7)); + expect (! detector.tryParse (2, 6, 42)); } } }; @@ -308,7 +364,7 @@ class MidiRPNDetectorTests : public UnitTest static MidiRPNDetectorTests MidiRPNDetectorUnitTests; //============================================================================== -class MidiRPNGeneratorTests : public UnitTest +class MidiRPNGeneratorTests final : public UnitTest { public: MidiRPNGeneratorTests() @@ -351,26 +407,24 @@ class MidiRPNGeneratorTests : public UnitTest //============================================================================== void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected) { - MidiBuffer::Iterator iter (midiBuffer); - MidiMessage midiMessage; - MidiRPNMessage result = MidiRPNMessage(); + std::optional result; MidiRPNDetector detector; - int samplePosition; // not actually used, so no need to initialise. - while (iter.getNextEvent (midiMessage, samplePosition)) + for (const auto metadata : midiBuffer) { - if (detector.parseControllerMessage (midiMessage.getChannel(), - midiMessage.getControllerNumber(), - midiMessage.getControllerValue(), - result)) - break; + const auto midiMessage = metadata.getMessage(); + + result = detector.tryParse (midiMessage.getChannel(), + midiMessage.getControllerNumber(), + midiMessage.getControllerValue()); } - expectEquals (result.channel, expected.channel); - expectEquals (result.parameterNumber, expected.parameterNumber); - expectEquals (result.value, expected.value); - expect (result.isNRPN == expected.isNRPN); - expect (result.is14BitValue == expected.is14BitValue); + expect (result.has_value()); + expectEquals (result->channel, expected.channel); + expectEquals (result->parameterNumber, expected.parameterNumber); + expectEquals (result->value, expected.value); + expect (result->isNRPN == expected.isNRPN); + expect (result->is14BitValue == expected.is14BitValue); } }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.h index b22138e3..dc63002d 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -68,10 +68,10 @@ class JUCE_API MidiRPNDetector { public: /** Constructor. */ - MidiRPNDetector() noexcept; + MidiRPNDetector() noexcept = default; /** Destructor. */ - ~MidiRPNDetector() noexcept; + ~MidiRPNDetector() noexcept = default; /** Resets the RPN detector's internal state, so that it forgets about previously received MIDI CC messages. @@ -79,30 +79,42 @@ class JUCE_API MidiRPNDetector void reset() noexcept; //============================================================================== - /** Takes the next in a stream of incoming MIDI CC messages and returns true - if it forms the last of a sequence that makes an RPN or NPRN. - - If this returns true, then the RPNMessage object supplied will be - filled-out with the message's details. - (If it returns false then the RPNMessage object will be unchanged). - */ + /** @see tryParse() */ + [[deprecated ("Use tryParse() instead")]] bool parseControllerMessage (int midiChannel, int controllerNumber, int controllerValue, MidiRPNMessage& result) noexcept; + /** Takes the next in a stream of incoming MIDI CC messages and returns + a MidiRPNMessage if the current message produces a well-formed RPN or NRPN. + + Note that senders are expected to send the MSB before the LSB, but senders are + not required to send a LSB at all. Therefore, tryParse() will return a non-null + optional on all MSB messages (provided a parameter number has been set), and will + also return a non-null optional for each LSB that follows the initial MSB. + + This behaviour allows senders to transmit a single MSB followed by multiple LSB + messages to facilitate fine-tuning of parameters. + + The result of parsing a MSB will always be a 7-bit value. + The result of parsing a LSB that follows an MSB will always be a 14-bit value. + */ + std::optional tryParse (int midiChannel, + int controllerNumber, + int controllerValue); + private: //============================================================================== struct ChannelState { - ChannelState() noexcept; - bool handleController (int channel, int controllerNumber, - int value, MidiRPNMessage&) noexcept; + std::optional handleController (int channel, int controllerNumber, + int value) noexcept; void resetValue() noexcept; - bool sendIfReady (int channel, MidiRPNMessage&) noexcept; + std::optional sendIfReady (int channel) noexcept; - uint8 parameterMSB, parameterLSB, valueMSB, valueLSB; - bool isNRPN; + uint8 parameterMSB = 0xff, parameterLSB = 0xff, valueMSB = 0xff, valueLSB = 0xff; + bool isNRPN = false; }; //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_ClangBugWorkaround.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h similarity index 53% rename from JuceLibraryCode/modules/juce_core/native/juce_mac_ClangBugWorkaround.h rename to JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h index 0cb8a5c6..9f478e99 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_ClangBugWorkaround.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -20,22 +20,26 @@ ============================================================================== */ -#if JUCE_PROJUCER_LIVE_BUILD && (defined (__APPLE_CPP__) || defined(__APPLE_CC__)) +#include "juce_UMPProtocols.h" +#include "juce_UMPUtils.h" +#include "juce_UMPacket.h" +#include "juce_UMPSysEx7.h" +#include "juce_UMPView.h" +#include "juce_UMPIterator.h" +#include "juce_UMPackets.h" +#include "juce_UMPFactory.h" +#include "juce_UMPConversion.h" +#include "juce_UMPMidi1ToBytestreamTranslator.h" +#include "juce_UMPMidi1ToMidi2DefaultTranslator.h" +#include "juce_UMPConverters.h" +#include "juce_UMPDispatcher.h" +#include "juce_UMPReceiver.h" + +#ifndef DOXYGEN + +namespace juce +{ +namespace ump = universal_midi_packets; +} - // 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 - - // This is a workaround for the Xcode 9 version of NSUUID.h causing some errors - // 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_audio_basics/midi/ump/juce_UMPBytesOnGroup.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h new file mode 100644 index 00000000..5622d6de --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h @@ -0,0 +1,38 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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::universal_midi_packets +{ + +/** + Holds a UMP group, and a span of bytes that were received or are to be + sent on that group. Helpful when working with sysex messages. + + @tags{Audio} +*/ +struct BytesOnGroup +{ + uint8_t group{}; + Span bytes; +}; + +} // namespace juce::universal_midi_packets diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h new file mode 100644 index 00000000..c000b0b7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h @@ -0,0 +1,355 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** Represents a MIDI message that happened at a particular time. + + Unlike MidiMessage, BytestreamMidiView is non-owning. +*/ +struct BytestreamMidiView +{ + constexpr BytestreamMidiView (Span bytesIn, double timestampIn) + : bytes (bytesIn), timestamp (timestampIn) {} + + /** Creates a view over the provided message. + + Note that the argument is a pointer, not a reference, in order to avoid taking a reference + to a temporary. + */ + explicit BytestreamMidiView (const MidiMessage* msg) + : bytes (unalignedPointerCast (msg->getRawData()), + static_cast (msg->getRawDataSize())), + timestamp (msg->getTimeStamp()) {} + + explicit BytestreamMidiView (const MidiMessageMetadata msg) + : bytes (unalignedPointerCast (msg.data), + static_cast (msg.numBytes)), + timestamp (msg.samplePosition) {} + + MidiMessage getMessage() const + { + return MidiMessage (bytes.data(), (int) bytes.size(), timestamp); + } + + bool isSysEx() const + { + return ! bytes.empty() && bytes.front() == std::byte { 0xf0 }; + } + + Span bytes; + double timestamp = 0.0; +}; + +/** + Functions to assist conversion of UMP messages to/from other formats, + especially older 'bytestream' formatted MidiMessages. + + @tags{Audio} +*/ +struct Conversion +{ + /** Converts from a MIDI 1 bytestream to MIDI 1 on Universal MIDI Packets. + + `callback` is a function which accepts a single View argument. + */ + template + static void toMidi1 (const BytestreamMidiView& m, PacketCallbackFunction&& callback) + { + const auto size = m.bytes.size(); + + if (size <= 0) + return; + + const auto* data = m.bytes.data(); + const auto firstByte = data[0]; + + if (firstByte != std::byte { 0xf0 }) + { + const auto mask = [size]() -> uint32_t + { + switch (size) + { + case 0: return 0xff000000; + case 1: return 0xffff0000; + case 2: return 0xffffff00; + case 3: return 0xffffffff; + } + + return 0x00000000; + }(); + + const auto extraByte = ((((firstByte & std::byte { 0xf0 }) == std::byte { 0xf0 }) ? std::byte { 0x1 } : std::byte { 0x2 }) << 0x4); + const PacketX1 packet { mask & Utils::bytesToWord (extraByte, data[0], data[1], data[2]) }; + callback (View (packet.data())); + return; + } + + const auto numSysExBytes = (ssize_t) (size - 2); + const auto numMessages = SysEx7::getNumPacketsRequiredForDataSize ((uint32_t) numSysExBytes); + auto* dataOffset = data + 1; + + if (numMessages <= 1) + { + const auto packet = Factory::makeSysExIn1Packet (0, (uint8_t) numSysExBytes, dataOffset); + callback (View (packet.data())); + return; + } + + constexpr ssize_t byteIncrement = 6; + + for (auto i = static_cast (numSysExBytes); i > 0; i -= byteIncrement, dataOffset += byteIncrement) + { + const auto func = [&] + { + if (i == numSysExBytes) + return Factory::makeSysExStart; + + if (i <= byteIncrement) + return Factory::makeSysExEnd; + + return Factory::makeSysExContinue; + }(); + + const auto bytesNow = std::min (byteIncrement, i); + const auto packet = func (0, (uint8_t) bytesNow, dataOffset); + callback (View (packet.data())); + } + } + + /** Widens a 7-bit MIDI 1.0 value to a 8-bit MIDI 2.0 value. */ + static uint8_t scaleTo8 (uint8_t word7Bit) + { + const auto shifted = (uint8_t) (word7Bit << 0x1); + const auto repeat = (uint8_t) (word7Bit & 0x3f); + const auto mask = (uint8_t) (word7Bit <= 0x40 ? 0x0 : 0xff); + return (uint8_t) (shifted | ((repeat >> 5) & mask)); + } + + /** Widens a 7-bit MIDI 1.0 value to a 16-bit MIDI 2.0 value. */ + static uint16_t scaleTo16 (uint8_t word7Bit) + { + const auto shifted = (uint16_t) (word7Bit << 0x9); + const auto repeat = (uint16_t) (word7Bit & 0x3f); + const auto mask = (uint16_t) (word7Bit <= 0x40 ? 0x0 : 0xffff); + return (uint16_t) (shifted | (((repeat << 3) | (repeat >> 3)) & mask)); + } + + /** Widens a 14-bit MIDI 1.0 value to a 16-bit MIDI 2.0 value. */ + static uint16_t scaleTo16 (uint16_t word14Bit) + { + const auto shifted = (uint16_t) (word14Bit << 0x2); + const auto repeat = (uint16_t) (word14Bit & 0x1fff); + const auto mask = (uint16_t) (word14Bit <= 0x2000 ? 0x0 : 0xffff); + return (uint16_t) (shifted | ((repeat >> 11) & mask)); + } + + /** Widens a 7-bit MIDI 1.0 value to a 32-bit MIDI 2.0 value. */ + static uint32_t scaleTo32 (uint8_t word7Bit) + { + const auto shifted = (uint32_t) (word7Bit << 0x19); + const auto repeat = (uint32_t) (word7Bit & 0x3f); + const auto mask = (uint32_t) (word7Bit <= 0x40 ? 0x0 : 0xffffffff); + return (uint32_t) (shifted | (((repeat << 19) + | (repeat << 13) + | (repeat << 7) + | (repeat << 1) + | (repeat >> 5)) & mask)); + } + + /** Widens a 14-bit MIDI 1.0 value to a 32-bit MIDI 2.0 value. */ + static uint32_t scaleTo32 (uint16_t word14Bit) + { + const auto shifted = (uint32_t) (word14Bit << 0x12); + const auto repeat = (uint32_t) (word14Bit & 0x1fff); + const auto mask = (uint32_t) (word14Bit <= 0x2000 ? 0x0 : 0xffffffff); + return (uint32_t) (shifted | (((repeat << 5) | (repeat >> 8)) & mask)); + } + + /** Narrows a 16-bit MIDI 2.0 value to a 7-bit MIDI 1.0 value. */ + static uint8_t scaleTo7 (uint8_t word8Bit) { return (uint8_t) (word8Bit >> 1); } + + /** Narrows a 16-bit MIDI 2.0 value to a 7-bit MIDI 1.0 value. */ + static uint8_t scaleTo7 (uint16_t word16Bit) { return (uint8_t) (word16Bit >> 9); } + + /** Narrows a 32-bit MIDI 2.0 value to a 7-bit MIDI 1.0 value. */ + static uint8_t scaleTo7 (uint32_t word32Bit) { return (uint8_t) (word32Bit >> 25); } + + /** Narrows a 32-bit MIDI 2.0 value to a 14-bit MIDI 1.0 value. */ + static uint16_t scaleTo14 (uint16_t word16Bit) { return (uint16_t) (word16Bit >> 2); } + + /** Narrows a 32-bit MIDI 2.0 value to a 14-bit MIDI 1.0 value. */ + static uint16_t scaleTo14 (uint32_t word32Bit) { return (uint16_t) (word32Bit >> 18); } + + /** Converts UMP messages which may include MIDI 2.0 channel voice messages into + equivalent MIDI 1.0 messages (still in UMP format). + + `callback` is a function that accepts a single View argument and will be + called with each converted packet. + + Note that not all MIDI 2.0 messages have MIDI 1.0 equivalents, so such + messages will be ignored. + */ + template + static void midi2ToMidi1DefaultTranslation (const View& v, Callback&& callback) + { + const auto firstWord = v[0]; + + if (Utils::getMessageType (firstWord) != 0x4) + { + callback (v); + return; + } + + const auto status = Utils::getStatus (firstWord); + const auto typeAndGroup = ((std::byte { 0x2 } << 0x4) | std::byte { Utils::getGroup (firstWord) }); + + switch (status) + { + case 0x8: // note off + case 0x9: // note on + case 0xa: // poly pressure + case 0xb: // control change + { + const auto statusAndChannel = std::byte ((firstWord >> 0x10) & 0xff); + const auto byte2 = std::byte ((firstWord >> 0x08) & 0xff); + const auto byte3 = std::byte { scaleTo7 (v[1]) }; + + // If this is a note-on, and the scaled byte is 0, + // the scaled velocity should be 1 instead of 0 + const auto needsCorrection = status == 0x9 && byte3 == std::byte { 0 }; + const auto correctedByte = needsCorrection ? std::byte { 1 } : byte3; + + const auto shouldIgnore = status == 0xb && [&] + { + switch (uint8_t (byte2)) + { + case 0: + case 6: + case 32: + case 38: + case 98: + case 99: + case 100: + case 101: + return true; + } + + return false; + }(); + + if (shouldIgnore) + return; + + const PacketX1 packet { Utils::bytesToWord (typeAndGroup, + statusAndChannel, + byte2, + correctedByte) }; + callback (View (packet.data())); + return; + } + + case 0xd: // channel pressure + { + const auto statusAndChannel = std::byte ((firstWord >> 0x10) & 0xff); + const auto byte2 = std::byte { scaleTo7 (v[1]) }; + + const PacketX1 packet { Utils::bytesToWord (typeAndGroup, + statusAndChannel, + byte2, + std::byte { 0 }) }; + callback (View (packet.data())); + return; + } + + case 0x2: // rpn + case 0x3: // nrpn + { + const auto ccX = status == 0x2 ? std::byte { 101 } : std::byte { 99 }; + const auto ccY = status == 0x2 ? std::byte { 100 } : std::byte { 98 }; + const auto statusAndChannel = std::byte ((0xb << 0x4) | Utils::getChannel (firstWord)); + const auto data = scaleTo14 (v[1]); + + const PacketX1 packets[] + { + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, ccX, std::byte ((firstWord >> 0x8) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, ccY, std::byte ((firstWord >> 0x0) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, std::byte { 6 }, std::byte ((data >> 0x7) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, std::byte { 38 }, std::byte ((data >> 0x0) & 0x7f)) }, + }; + + for (const auto& packet : packets) + callback (View (packet.data())); + + return; + } + + case 0xc: // program change / bank select + { + if (firstWord & 1) + { + const auto statusAndChannel = std::byte ((0xb << 0x4) | Utils::getChannel (firstWord)); + const auto secondWord = v[1]; + + const PacketX1 packets[] + { + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, std::byte { 0 }, std::byte ((secondWord >> 0x8) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, std::byte { 32 }, std::byte ((secondWord >> 0x0) & 0x7f)) }, + }; + + for (const auto& packet : packets) + callback (View (packet.data())); + } + + const auto statusAndChannel = std::byte ((0xc << 0x4) | Utils::getChannel (firstWord)); + const PacketX1 packet { Utils::bytesToWord (typeAndGroup, + statusAndChannel, + std::byte ((v[1] >> 0x18) & 0x7f), + std::byte { 0 }) }; + callback (View (packet.data())); + return; + } + + case 0xe: // pitch bend + { + const auto data = scaleTo14 (v[1]); + const auto statusAndChannel = std::byte ((firstWord >> 0x10) & 0xff); + const PacketX1 packet { Utils::bytesToWord (typeAndGroup, + statusAndChannel, + std::byte (data & 0x7f), + std::byte ((data >> 7) & 0x7f)) }; + callback (View (packet.data())); + return; + } + + default: // other message types do not translate + return; + } + } +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h new file mode 100644 index 00000000..3a6acc4a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h @@ -0,0 +1,185 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + /** + Allows conversion from bytestream- or Universal MIDI Packet-formatted + messages to MIDI 1.0 messages in UMP format. + + @tags{Audio} + */ + struct ToUMP1Converter + { + template + void convert (const BytestreamMidiView& m, Fn&& fn) + { + Conversion::toMidi1 (m, std::forward (fn)); + } + + template + void convert (const View& v, Fn&& fn) + { + Conversion::midi2ToMidi1DefaultTranslation (v, std::forward (fn)); + } + }; + + /** + Allows conversion from bytestream- or Universal MIDI Packet-formatted + messages to MIDI 2.0 messages in UMP format. + + @tags{Audio} + */ + struct ToUMP2Converter + { + template + void convert (const BytestreamMidiView& m, Fn&& fn) + { + Conversion::toMidi1 (m, [&] (const View& v) + { + translator.dispatch (v, fn); + }); + } + + template + void convert (const View& v, Fn&& fn) + { + translator.dispatch (v, std::forward (fn)); + } + + void reset() + { + translator.reset(); + } + + Midi1ToMidi2DefaultTranslator translator; + }; + + /** + Allows conversion from bytestream- or Universal MIDI Packet-formatted + messages to UMP format. + + The packet protocol can be selected using the constructor parameter. + + @tags{Audio} + */ + class GenericUMPConverter + { + template + static void visit (This& t, Args&&... args) + { + if (t.mode == PacketProtocol::MIDI_1_0) + convertImpl (std::get<0> (t.converters), std::forward (args)...); + else + convertImpl (std::get<1> (t.converters), std::forward (args)...); + } + + public: + explicit GenericUMPConverter (PacketProtocol m) + : mode (m) {} + + void reset() + { + std::get<1> (converters).reset(); + } + + template + static void convertImpl (Converter& converter, const BytestreamMidiView& m, Fn&& fn) + { + converter.convert (m, std::forward (fn)); + } + + template + static void convertImpl (Converter& converter, const View& m, Fn&& fn) + { + converter.convert (m, std::forward (fn)); + } + + template + static void convertImpl (Converter& converter, Iterator b, Iterator e, Fn&& fn) + { + std::for_each (b, e, [&] (const auto& v) + { + convertImpl (converter, v, fn); + }); + } + + template + void convert (const BytestreamMidiView& m, Fn&& fn) + { + visit (*this, m, std::forward (fn)); + } + + template + void convert (const View& v, Fn&& fn) + { + visit (*this, v, std::forward (fn)); + } + + template + void convert (Iterator begin, Iterator end, Fn&& fn) + { + visit (*this, begin, end, std::forward (fn)); + } + + PacketProtocol getProtocol() const noexcept { return mode; } + + private: + std::tuple converters; + const PacketProtocol mode{}; + }; + + /** + Allows conversion from bytestream- or Universal MIDI Packet-formatted + messages to bytestream format. + + @tags{Audio} + */ + struct ToBytestreamConverter + { + explicit ToBytestreamConverter (int storageSize) + : translator (storageSize) {} + + template + void convert (const MidiMessage& m, Fn&& fn) + { + fn (m); + } + + template + void convert (const View& v, double time, Fn&& fn) + { + Conversion::midi2ToMidi1DefaultTranslation (v, [&] (const View& midi1) + { + translator.dispatch (midi1, time, fn); + }); + } + + void reset() { translator.reset(); } + + Midi1ToBytestreamTranslator translator; + }; +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h new file mode 100644 index 00000000..107165d1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h @@ -0,0 +1,58 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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::universal_midi_packets +{ + +/** + Holds MIDI device info that may be required by certain UMP messages and + MIDI-CI messages. + + @tags{Audio} +*/ +struct DeviceInfo +{ + std::array manufacturer; ///< LSB first + std::array family; ///< LSB first + std::array modelNumber; ///< LSB first + std::array revision; + +private: + auto tie() const { return std::tie (manufacturer, family, modelNumber, revision); } + +public: + bool operator== (const DeviceInfo& other) const { return tie() == other.tie(); } + bool operator!= (const DeviceInfo& other) const { return tie() != other.tie(); } + + static constexpr auto marshallingVersion = std::nullopt; + + template + static auto serialise (Archive& archive, This& t) + { + return archive (named ("manufacturer", t.manufacturer), + named ("family", t.family), + named ("modelNumber", t.modelNumber), + named ("revision", t.revision)); + } +}; + +} // namespace juce::universal_midi_packets diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h new file mode 100644 index 00000000..885e4588 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h @@ -0,0 +1,199 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + Parses a raw stream of uint32_t, and calls a user-provided callback every time + a full Universal MIDI Packet is encountered. + + @tags{Audio} +*/ +class Dispatcher +{ +public: + /** Clears the dispatcher. */ + void reset() { currentPacketLen = 0; } + + /** Calls `callback` with a View of each packet encountered in the range delimited + by `begin` and `end`. + + If the range ends part-way through a packet, the next call to `dispatch` will + continue from that point in the packet (unless `reset` is called first). + */ + template + void dispatch (const uint32_t* begin, + const uint32_t* end, + double timeStamp, + PacketCallbackFunction&& callback) + { + std::for_each (begin, end, [&] (uint32_t word) + { + nextPacket[currentPacketLen++] = word; + + if (currentPacketLen == Utils::getNumWordsForMessageType (nextPacket.front())) + { + callback (View (nextPacket.data()), timeStamp); + currentPacketLen = 0; + } + }); + } + +private: + std::array nextPacket; + size_t currentPacketLen = 0; +}; + +//============================================================================== +/** + Parses a stream of bytes representing a sequence of bytestream-encoded MIDI 1.0 messages, + converting the messages to UMP format and passing the packets to a user-provided callback + as they become ready. + + @tags{Audio} +*/ +class BytestreamToUMPDispatcher +{ +public: + /** Initialises the dispatcher. + + Channel messages will be converted to the requested protocol format `pp`. + `storageSize` bytes will be allocated to store incomplete messages. + */ + explicit BytestreamToUMPDispatcher (PacketProtocol pp, int storageSize) + : concatenator (storageSize), + converter (pp) + {} + + void reset() + { + concatenator.reset(); + converter.reset(); + } + + /** Calls `callback` with a View of each converted packet as it becomes ready. + + @param begin the first byte in a range of bytes representing bytestream-encoded MIDI messages. + @param end one-past the last byte in a range of bytes representing bytestream-encoded MIDI messages. + @param timestamp a timestamp to apply to the created packets. + @param callback a callback which will be passed a View pointing to each new packet as it becomes ready. + */ + template + void dispatch (const uint8_t* begin, + const uint8_t* end, + double timestamp, + PacketCallbackFunction&& callback) + { + using CallbackPtr = decltype (std::addressof (callback)); + + #if JUCE_MINGW + #define JUCE_MINGW_HIDDEN_VISIBILITY __attribute__ ((visibility ("hidden"))) + #else + #define JUCE_MINGW_HIDDEN_VISIBILITY + #endif + + struct JUCE_MINGW_HIDDEN_VISIBILITY Callback + { + Callback (BytestreamToUMPDispatcher& d, CallbackPtr c) + : dispatch (d), callbackPtr (c) {} + + void handleIncomingMidiMessage (void*, const MidiMessage& msg) const + { + Conversion::toMidi1 (BytestreamMidiView (&msg), [&] (const View& view) + { + dispatch.converter.convert (view, *callbackPtr); + }); + } + + void handlePartialSysexMessage (void*, const uint8_t*, int, double) const {} + + BytestreamToUMPDispatcher& dispatch; + CallbackPtr callbackPtr = nullptr; + }; + + #undef JUCE_MINGW_HIDDEN_VISIBILITY + + Callback inputCallback { *this, &callback }; + concatenator.pushMidiData (begin, int (end - begin), timestamp, (void*) nullptr, inputCallback); + } + +private: + MidiDataConcatenator concatenator; + GenericUMPConverter converter; +}; + +//============================================================================== +/** + Parses a stream of 32-bit words representing a sequence of UMP-encoded MIDI messages, + converting the messages to MIDI 1.0 bytestream format and passing them to a user-provided + callback as they become ready. + + @tags{Audio} +*/ +class ToBytestreamDispatcher +{ +public: + /** Initialises the dispatcher. + + `storageSize` bytes will be allocated to store incomplete messages. + */ + explicit ToBytestreamDispatcher (int storageSize) + : converter (storageSize) {} + + /** Clears the dispatcher. */ + void reset() + { + dispatcher.reset(); + converter.reset(); + } + + /** Calls `callback` with converted bytestream-formatted MidiMessage whenever + a new message becomes available. + + @param begin the first word in a stream of words representing UMP-encoded MIDI packets. + @param end one-past the last word in a stream of words representing UMP-encoded MIDI packets. + @param timestamp a timestamp to apply to converted messages. + @param callback a callback which will be passed a MidiMessage each time a new message becomes ready. + */ + template + void dispatch (const uint32_t* begin, + const uint32_t* end, + double timestamp, + BytestreamMessageCallback&& callback) + { + dispatcher.dispatch (begin, end, timestamp, [&] (const View& view, double time) + { + converter.convert (view, time, callback); + }); + } + +private: + Dispatcher dispatcher; + ToBytestreamConverter converter; +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h new file mode 100644 index 00000000..f31d4dcf --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h @@ -0,0 +1,531 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + This struct holds functions that can be used to create different kinds + of Universal MIDI Packet. + + @tags{Audio} +*/ +struct Factory +{ + /** @internal */ + struct Detail + { + static PacketX1 makeSystem() { return PacketX1{}.withMessageType (1); } + static PacketX1 makeV1() { return PacketX1{}.withMessageType (2); } + static PacketX2 makeV2() { return PacketX2{}.withMessageType (4); } + + static PacketX2 makeSysEx (uint8_t group, + uint8_t status, + uint8_t numBytes, + const std::byte* data) + { + jassert (numBytes <= 6); + + std::array bytes{{}}; + bytes[0] = (0x3 << 0x4) | group; + bytes[1] = (uint8_t) (status << 0x4) | numBytes; + + std::memcpy (bytes.data() + 2, data, numBytes); + + std::array words; + + for (const auto [index, word] : enumerate (words)) + word = ByteOrder::bigEndianInt (bytes.data() + 4 * index); + + return PacketX2 { words }; + } + + static PacketX4 makeSysEx8 (uint8_t group, + uint8_t status, + uint8_t numBytes, + uint8_t dataStart, + const uint8_t* data) + { + jassert (numBytes <= 16 - dataStart); + + std::array bytes{{}}; + bytes[0] = (0x5 << 0x4) | group; + bytes[1] = (uint8_t) (status << 0x4) | numBytes; + + std::memcpy (bytes.data() + dataStart, data, numBytes); + + std::array words; + + for (const auto [index, word] : enumerate (words)) + word = ByteOrder::bigEndianInt (bytes.data() + 4 * index); + + return PacketX4 { words }; + } + }; + + static PacketX1 makeNoop (uint8_t group) + { + return PacketX1{}.withGroup (group); + } + + static PacketX1 makeJRClock (uint8_t group, uint16_t time) + { + return PacketX1 { time }.withStatus (1).withGroup (group); + } + + static PacketX1 makeJRTimestamp (uint8_t group, uint16_t time) + { + return PacketX1 { time }.withStatus (2).withGroup (group); + } + + static PacketX1 makeTimeCode (uint8_t group, uint8_t code) + { + return Detail::makeSystem().withGroup (group) + .withU8<1> (0xf1) + .withU8<2> (code & 0x7f); + } + + static PacketX1 makeSongPositionPointer (uint8_t group, uint16_t pos) + { + return Detail::makeSystem().withGroup (group) + .withU8<1> (0xf2) + .withU8<2> (pos & 0x7f) + .withU8<3> ((pos >> 7) & 0x7f); + } + + static PacketX1 makeSongSelect (uint8_t group, uint8_t song) + { + return Detail::makeSystem().withGroup (group) + .withU8<1> (0xf3) + .withU8<2> (song & 0x7f); + } + + static PacketX1 makeTuneRequest (uint8_t group) + { + return Detail::makeSystem().withGroup (group) + .withU8<1> (0xf6); + } + + static PacketX1 makeTimingClock (uint8_t group) + { + return Detail::makeSystem().withGroup (group) + .withU8<1> (0xf8); + } + + static PacketX1 makeStart (uint8_t group) + { + return Detail::makeSystem().withGroup (group) + .withU8<1> (0xfa); + } + + static PacketX1 makeContinue (uint8_t group) + { + return Detail::makeSystem().withGroup (group) + .withU8<1> (0xfb); + } + + static PacketX1 makeStop (uint8_t group) + { + return Detail::makeSystem().withGroup (group) + .withU8<1> (0xfc); + } + + static PacketX1 makeActiveSensing (uint8_t group) + { + return Detail::makeSystem().withGroup (group) + .withU8<1> (0xfe); + } + + static PacketX1 makeReset (uint8_t group) + { + return Detail::makeSystem().withGroup (group) + .withU8<1> (0xff); + } + + static PacketX1 makeNoteOffV1 (uint8_t group, + uint8_t channel, + uint8_t note, + uint8_t velocity) + { + return Detail::makeV1().withGroup (group) + .withStatus (0x8) + .withChannel (channel) + .withU8<2> (note & 0x7f) + .withU8<3> (velocity & 0x7f); + } + + static PacketX1 makeNoteOnV1 (uint8_t group, + uint8_t channel, + uint8_t note, + uint8_t velocity) + { + return Detail::makeV1().withGroup (group) + .withStatus (0x9) + .withChannel (channel) + .withU8<2> (note & 0x7f) + .withU8<3> (velocity & 0x7f); + } + + static PacketX1 makePolyPressureV1 (uint8_t group, + uint8_t channel, + uint8_t note, + uint8_t pressure) + { + return Detail::makeV1().withGroup (group) + .withStatus (0xa) + .withChannel (channel) + .withU8<2> (note & 0x7f) + .withU8<3> (pressure & 0x7f); + } + + static PacketX1 makeControlChangeV1 (uint8_t group, + uint8_t channel, + uint8_t controller, + uint8_t value) + { + return Detail::makeV1().withGroup (group) + .withStatus (0xb) + .withChannel (channel) + .withU8<2> (controller & 0x7f) + .withU8<3> (value & 0x7f); + } + + static PacketX1 makeProgramChangeV1 (uint8_t group, + uint8_t channel, + uint8_t program) + { + return Detail::makeV1().withGroup (group) + .withStatus (0xc) + .withChannel (channel) + .withU8<2> (program & 0x7f); + } + + static PacketX1 makeChannelPressureV1 (uint8_t group, + uint8_t channel, + uint8_t pressure) + { + return Detail::makeV1().withGroup (group) + .withStatus (0xd) + .withChannel (channel) + .withU8<2> (pressure & 0x7f); + } + + static PacketX1 makePitchBend (uint8_t group, + uint8_t channel, + uint16_t pitchbend) + { + return Detail::makeV1().withGroup (group) + .withStatus (0xe) + .withChannel (channel) + .withU8<2> (pitchbend & 0x7f) + .withU8<3> ((pitchbend >> 7) & 0x7f); + } + + static PacketX2 makeSysExIn1Packet (uint8_t group, + uint8_t numBytes, + const std::byte* data) + { + return Detail::makeSysEx (group, 0x0, numBytes, data); + } + + static PacketX2 makeSysExStart (uint8_t group, + uint8_t numBytes, + const std::byte* data) + { + return Detail::makeSysEx (group, 0x1, numBytes, data); + } + + static PacketX2 makeSysExContinue (uint8_t group, + uint8_t numBytes, + const std::byte* data) + { + return Detail::makeSysEx (group, 0x2, numBytes, data); + } + + static PacketX2 makeSysExEnd (uint8_t group, + uint8_t numBytes, + const std::byte* data) + { + return Detail::makeSysEx (group, 0x3, numBytes, data); + } + + static PacketX2 makeRegisteredPerNoteControllerV2 (uint8_t group, + uint8_t channel, + uint8_t note, + uint8_t controller, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0x0) + .withChannel (channel) + .withU8<2> (note & 0x7f) + .withU8<3> (controller & 0x7f) + .withU32<1> (data); + } + + static PacketX2 makeAssignablePerNoteControllerV2 (uint8_t group, + uint8_t channel, + uint8_t note, + uint8_t controller, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0x1) + .withChannel (channel) + .withU8<2> (note & 0x7f) + .withU8<3> (controller & 0x7f) + .withU32<1> (data); + } + + static PacketX2 makeRegisteredControllerV2 (uint8_t group, + uint8_t channel, + uint8_t bank, + uint8_t index, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0x2) + .withChannel (channel) + .withU8<2> (bank & 0x7f) + .withU8<3> (index & 0x7f) + .withU32<1> (data); + } + + static PacketX2 makeAssignableControllerV2 (uint8_t group, + uint8_t channel, + uint8_t bank, + uint8_t index, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0x3) + .withChannel (channel) + .withU8<2> (bank & 0x7f) + .withU8<3> (index & 0x7f) + .withU32<1> (data); + } + + static PacketX2 makeRelativeRegisteredControllerV2 (uint8_t group, + uint8_t channel, + uint8_t bank, + uint8_t index, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0x4) + .withChannel (channel) + .withU8<2> (bank & 0x7f) + .withU8<3> (index & 0x7f) + .withU32<1> (data); + } + + static PacketX2 makeRelativeAssignableControllerV2 (uint8_t group, + uint8_t channel, + uint8_t bank, + uint8_t index, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0x5) + .withChannel (channel) + .withU8<2> (bank & 0x7f) + .withU8<3> (index & 0x7f) + .withU32<1> (data); + } + + static PacketX2 makePerNotePitchBendV2 (uint8_t group, + uint8_t channel, + uint8_t note, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0x6) + .withChannel (channel) + .withU8<2> (note & 0x7f) + .withU32<1> (data); + } + + enum class NoteAttributeKind : uint8_t + { + none = 0x00, + manufacturer = 0x01, + profile = 0x02, + pitch7_9 = 0x03 + }; + + static PacketX2 makeNoteOffV2 (uint8_t group, + uint8_t channel, + uint8_t note, + NoteAttributeKind attribute, + uint16_t velocity, + uint16_t attributeValue) + { + return Detail::makeV2().withGroup (group) + .withStatus (0x8) + .withChannel (channel) + .withU8<2> (note & 0x7f) + .withU8<3> ((uint8_t) attribute) + .withU16<2> (velocity) + .withU16<3> (attributeValue); + } + + static PacketX2 makeNoteOnV2 (uint8_t group, + uint8_t channel, + uint8_t note, + NoteAttributeKind attribute, + uint16_t velocity, + uint16_t attributeValue) + { + return Detail::makeV2().withGroup (group) + .withStatus (0x9) + .withChannel (channel) + .withU8<2> (note & 0x7f) + .withU8<3> ((uint8_t) attribute) + .withU16<2> (velocity) + .withU16<3> (attributeValue); + } + + static PacketX2 makePolyPressureV2 (uint8_t group, + uint8_t channel, + uint8_t note, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0xa) + .withChannel (channel) + .withU8<2> (note & 0x7f) + .withU32<1> (data); + } + + static PacketX2 makeControlChangeV2 (uint8_t group, + uint8_t channel, + uint8_t controller, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0xb) + .withChannel (channel) + .withU8<2> (controller & 0x7f) + .withU32<1> (data); + } + + static PacketX2 makeProgramChangeV2 (uint8_t group, + uint8_t channel, + uint8_t optionFlags, + uint8_t program, + uint8_t bankMsb, + uint8_t bankLsb) + { + return Detail::makeV2().withGroup (group) + .withStatus (0xc) + .withChannel (channel) + .withU8<3> (optionFlags) + .withU8<4> (program) + .withU8<6> (bankMsb) + .withU8<7> (bankLsb); + } + + static PacketX2 makeChannelPressureV2 (uint8_t group, + uint8_t channel, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0xd) + .withChannel (channel) + .withU32<1> (data); + } + + static PacketX2 makePitchBendV2 (uint8_t group, + uint8_t channel, + uint32_t data) + { + return Detail::makeV2().withGroup (group) + .withStatus (0xe) + .withChannel (channel) + .withU32<1> (data); + } + + static PacketX2 makePerNoteManagementV2 (uint8_t group, + uint8_t channel, + uint8_t note, + uint8_t optionFlags) + { + return Detail::makeV2().withGroup (group) + .withStatus (0xf) + .withChannel (channel) + .withU8<2> (note) + .withU8<3> (optionFlags); + } + + + static PacketX4 makeSysEx8in1Packet (uint8_t group, + uint8_t numBytes, + uint8_t streamId, + const uint8_t* data) + { + return Detail::makeSysEx8 (group, 0x0, numBytes, 3, data).withU8<2> (streamId); + } + + static PacketX4 makeSysEx8Start (uint8_t group, + uint8_t numBytes, + uint8_t streamId, + const uint8_t* data) + { + return Detail::makeSysEx8 (group, 0x1, numBytes, 3, data).withU8<2> (streamId); + } + + static PacketX4 makeSysEx8Continue (uint8_t group, + uint8_t numBytes, + uint8_t streamId, + const uint8_t* data) + { + return Detail::makeSysEx8 (group, 0x2, numBytes, 3, data).withU8<2> (streamId); + } + + static PacketX4 makeSysEx8End (uint8_t group, + uint8_t numBytes, + uint8_t streamId, + const uint8_t* data) + { + return Detail::makeSysEx8 (group, 0x3, numBytes, 3, data).withU8<2> (streamId); + } + + static PacketX4 makeMixedDataSetHeader (uint8_t group, + uint8_t dataSetId, + const uint8_t* data) + { + return Detail::makeSysEx8 (group, 0x8, 14, 2, data).withChannel (dataSetId); + } + + static PacketX4 makeDataSetPayload (uint8_t group, + uint8_t dataSetId, + const uint8_t* data) + { + return Detail::makeSysEx8 (group, 0x9, 14, 2, data).withChannel (dataSetId); + } +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp new file mode 100644 index 00000000..b86ffac3 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp @@ -0,0 +1,34 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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::universal_midi_packets +{ + +Iterator::Iterator (const uint32_t* ptr, [[maybe_unused]] size_t bytes) noexcept + : view (ptr) + #if JUCE_DEBUG + , bytesRemaining (bytes) + #endif +{ +} + +} // namespace juce::universal_midi_packets diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPIterator.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPIterator.h new file mode 100644 index 00000000..5ba93a57 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPIterator.h @@ -0,0 +1,120 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + Enables iteration over a collection of Universal MIDI Packets stored as + a contiguous range of 32-bit words. + + This iterator is used by Packets to allow access to the messages + that it contains. + + @tags{Audio} +*/ +class Iterator +{ +public: + /** Creates an invalid (singular) iterator. */ + Iterator() noexcept = default; + + /** Creates an iterator pointing at `ptr`. */ + explicit Iterator (const uint32_t* ptr, size_t bytes) noexcept; + + using difference_type = std::iterator_traits::difference_type; + using value_type = View; + using reference = const View&; + using pointer = const View*; + using iterator_category = std::forward_iterator_tag; + + /** Moves this iterator to the next packet in the range. */ + Iterator& operator++() noexcept + { + const auto increment = view.size(); + + #if JUCE_DEBUG + // If you hit this, the memory region contained a truncated or otherwise + // malformed Universal MIDI Packet. + // The Iterator can only be used on regions containing complete packets! + jassert (increment <= bytesRemaining); + bytesRemaining -= increment; + #endif + + view = View (view.data() + increment); + return *this; + } + + /** Moves this iterator to the next packet in the range, + returning the value of the iterator before it was + incremented. + */ + Iterator operator++ (int) noexcept + { + auto copy = *this; + ++(*this); + return copy; + } + + /** Returns true if this iterator points to the same address + as another iterator. + */ + bool operator== (const Iterator& other) const noexcept + { + return view == other.view; + } + + /** Returns false if this iterator points to the same address + as another iterator. + */ + bool operator!= (const Iterator& other) const noexcept + { + return ! operator== (other); + } + + /** Returns a reference to a View of the packet currently + pointed-to by this iterator. + + The View can be queried for its size and content. + */ + reference operator*() noexcept { return view; } + + /** Returns a pointer to a View of the packet currently + pointed-to by this iterator. + + The View can be queried for its size and content. + */ + pointer operator->() noexcept { return &view; } + +private: + View view; + + #if JUCE_DEBUG + size_t bytesRemaining = 0; + #endif +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h new file mode 100644 index 00000000..de7c86c3 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h @@ -0,0 +1,215 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + Parses a raw stream of uint32_t holding a series of Universal MIDI Packets using + the MIDI 1.0 Protocol, converting to plain (non-UMP) MidiMessages. + + @tags{Audio} +*/ +class Midi1ToBytestreamTranslator +{ +public: + /** Ensures that there is room in the internal buffer for a sysex message of at least + `initialBufferSize` bytes. + */ + explicit Midi1ToBytestreamTranslator (int initialBufferSize) + { + pendingSysExData.reserve (size_t (initialBufferSize)); + } + + /** Clears the concatenator. */ + void reset() + { + pendingSysExData.clear(); + pendingSysExTime = 0.0; + } + + /** Converts a Universal MIDI Packet using the MIDI 1.0 Protocol to + an equivalent MidiMessage. Accumulates SysEx packets into a single + MidiMessage, as appropriate. + + @param packet a packet which is using the MIDI 1.0 Protocol. + @param time the timestamp to be applied to these messages. + @param callback a callback which will be called with each converted MidiMessage. + */ + template + void dispatch (const View& packet, double time, MessageCallback&& callback) + { + const auto firstWord = *packet.data(); + + if (! pendingSysExData.empty() && shouldPacketTerminateSysExEarly (firstWord)) + pendingSysExData.clear(); + + switch (packet.size()) + { + case 1: + { + // Utility messages don't translate to bytestream format + if (Utils::getMessageType (firstWord) != 0x00) + { + const auto message = fromUmp (PacketX1 { firstWord }, time); + callback (BytestreamMidiView (&message)); + } + + break; + } + + case 2: + { + if (Utils::getMessageType (firstWord) == 0x3) + processSysEx (PacketX2 { packet[0], packet[1] }, time, callback); + + break; + } + + case 3: // no 3-word packets in the current spec + case 4: // no 4-word packets translate to bytestream format + default: + break; + } + } + + /** Converts from a Universal MIDI Packet to MIDI 1 bytestream format. + + This is only capable of converting a single Universal MIDI Packet to + an equivalent bytestream MIDI message. This function cannot understand + multi-packet messages, like SysEx7 messages. + + To convert multi-packet messages, use `Midi1ToBytestreamTranslator` + to convert from a UMP MIDI 1.0 stream, or `ToBytestreamDispatcher` + to convert from both MIDI 2.0 and MIDI 1.0. + */ + static MidiMessage fromUmp (const PacketX1& m, double time = 0) + { + const auto word = m.front(); + jassert (Utils::getNumWordsForMessageType (word) == 1); + + const std::array bytes { { uint8_t ((word >> 0x10) & 0xff), + uint8_t ((word >> 0x08) & 0xff), + uint8_t ((word >> 0x00) & 0xff) } }; + const auto numBytes = MidiMessage::getMessageLengthFromFirstByte (bytes.front()); + return MidiMessage (bytes.data(), numBytes, time); + } + +private: + template + void processSysEx (const PacketX2& packet, + double time, + MessageCallback&& callback) + { + switch (getSysEx7Kind (packet[0])) + { + case SysEx7::Kind::complete: + startSysExMessage (time); + pushBytes (packet); + terminateSysExMessage (callback); + break; + + case SysEx7::Kind::begin: + startSysExMessage (time); + pushBytes (packet); + break; + + case SysEx7::Kind::continuation: + if (pendingSysExData.empty()) + break; + + pushBytes (packet); + break; + + case SysEx7::Kind::end: + if (pendingSysExData.empty()) + break; + + pushBytes (packet); + terminateSysExMessage (callback); + break; + } + } + + void pushBytes (const PacketX2& packet) + { + const auto bytes = SysEx7::getDataBytes (packet); + pendingSysExData.insert (pendingSysExData.end(), + bytes.data.begin(), + bytes.data.begin() + bytes.size); + } + + void startSysExMessage (double time) + { + pendingSysExTime = time; + pendingSysExData.push_back (std::byte { 0xf0 }); + } + + template + void terminateSysExMessage (MessageCallback&& callback) + { + pendingSysExData.push_back (std::byte { 0xf7 }); + callback (BytestreamMidiView (pendingSysExData, pendingSysExTime)); + pendingSysExData.clear(); + } + + static bool shouldPacketTerminateSysExEarly (uint32_t firstWord) + { + return ! (isSysExContinuation (firstWord) + || isSystemRealTime (firstWord) + || isJROrNOP (firstWord)); + } + + static SysEx7::Kind getSysEx7Kind (uint32_t word) + { + return SysEx7::Kind ((word >> 0x14) & 0xf); + } + + static bool isJROrNOP (uint32_t word) + { + return Utils::getMessageType (word) == 0x0; + } + + static bool isSysExContinuation (uint32_t word) + { + if (Utils::getMessageType (word) != 0x3) + return false; + + const auto kind = getSysEx7Kind (word); + return kind == SysEx7::Kind::continuation || kind == SysEx7::Kind::end; + } + + static bool isSystemRealTime (uint32_t word) + { + return Utils::getMessageType (word) == 0x1 && ((word >> 0x10) & 0xff) >= 0xf8; + } + + std::vector pendingSysExData; + + double pendingSysExTime = 0.0; +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp new file mode 100644 index 00000000..d6d44097 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp @@ -0,0 +1,198 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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::universal_midi_packets +{ + +PacketX2 Midi1ToMidi2DefaultTranslator::processNoteOnOrOff (const HelperValues helpers) +{ + const auto velocity = helpers.byte2; + const auto needsConversion = (helpers.byte0 & std::byte { 0xf0 }) == std::byte { 0x90 } && velocity == std::byte { 0 }; + const auto firstByte = needsConversion ? (std::byte { 0x80 } | (helpers.byte0 & std::byte { 0xf })) + : helpers.byte0; + + return PacketX2 + { + Utils::bytesToWord (helpers.typeAndGroup, firstByte, helpers.byte1, std::byte { 0 }), + (uint32_t) (Conversion::scaleTo16 (uint8_t (velocity)) << 0x10) + }; +} + +PacketX2 Midi1ToMidi2DefaultTranslator::processPolyPressure (const HelperValues helpers) +{ + return PacketX2 + { + Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, helpers.byte1, std::byte { 0 }), + Conversion::scaleTo32 (uint8_t (helpers.byte2)) + }; +} + +bool Midi1ToMidi2DefaultTranslator::processControlChange (const HelperValues helpers, + PacketX2& packet) +{ + const auto statusAndChannel = helpers.byte0; + const auto cc = uint8_t (helpers.byte1); + + const auto shouldAccumulate = [&] + { + switch (cc) + { + case 6: + case 38: + case 98: + case 99: + case 100: + case 101: + return true; + } + + return false; + }(); + + const auto group = (uint8_t) (helpers.typeAndGroup & std::byte { 0xf }); + const auto channel = (uint8_t) (statusAndChannel & std::byte { 0xf }); + const auto byte = helpers.byte2; + + if (shouldAccumulate) + { + auto& accumulator = groupAccumulators[group][channel]; + + if (accumulator.addByte (cc, byte)) + { + const auto& bytes = accumulator.getBytes(); + const auto bank = bytes[0]; + const auto index = bytes[1]; + const auto msb = bytes[2]; + const auto lsb = bytes[3]; + + const auto value = uint16_t ((uint16_t (msb & std::byte { 0x7f }) << 7) | uint16_t (lsb & std::byte { 0x7f })); + + const auto newStatus = (uint8_t) (accumulator.getKind() == PnKind::nrpn ? 0x3 : 0x2); + + packet = PacketX2 + { + Utils::bytesToWord (helpers.typeAndGroup, std::byte ((newStatus << 0x4) | channel), bank, index), + Conversion::scaleTo32 (value) + }; + return true; + } + + return false; + } + + if (cc == 0) + { + groupBanks[group][channel].setMsb (uint8_t (byte)); + return false; + } + + if (cc == 32) + { + groupBanks[group][channel].setLsb (uint8_t (byte)); + return false; + } + + packet = PacketX2 + { + Utils::bytesToWord (helpers.typeAndGroup, statusAndChannel, std::byte { cc }, std::byte { 0 }), + Conversion::scaleTo32 (uint8_t (helpers.byte2)) + }; + return true; +} + +PacketX2 Midi1ToMidi2DefaultTranslator::processProgramChange (const HelperValues helpers) const +{ + const auto group = (uint8_t) (helpers.typeAndGroup & std::byte { 0xf }); + const auto channel = (uint8_t) (helpers.byte0 & std::byte { 0xf }); + const auto bank = groupBanks[group][channel]; + const auto valid = bank.isValid(); + + return PacketX2 + { + Utils::bytesToWord (helpers.typeAndGroup, + helpers.byte0, + std::byte { 0 }, + valid ? std::byte { 1 } : std::byte { 0 }), + Utils::bytesToWord (helpers.byte1, + std::byte { 0 }, + valid ? std::byte { bank.getMsb() } : std::byte { 0 }, + valid ? std::byte { bank.getLsb() } : std::byte { 0 }) + }; +} + +PacketX2 Midi1ToMidi2DefaultTranslator::processChannelPressure (const HelperValues helpers) +{ + return PacketX2 + { + Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, std::byte { 0 }, std::byte { 0 }), + Conversion::scaleTo32 (uint8_t (helpers.byte1)) + }; +} + +PacketX2 Midi1ToMidi2DefaultTranslator::processPitchBend (const HelperValues helpers) +{ + const auto lsb = helpers.byte1; + const auto msb = helpers.byte2; + const auto value = uint16_t (uint16_t (msb) << 7 | uint16_t (lsb)); + + return PacketX2 + { + Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, std::byte { 0 }, std::byte { 0 }), + Conversion::scaleTo32 (value) + }; +} + +bool Midi1ToMidi2DefaultTranslator::PnAccumulator::addByte (uint8_t cc, std::byte byte) +{ + const auto isStart = cc == 99 || cc == 101; + + if (isStart) + { + kind = cc == 99 ? PnKind::nrpn : PnKind::rpn; + index = 0; + } + + bytes[index] = byte; + + const auto shouldContinue = [&] + { + switch (index) + { + case 0: return isStart; + case 1: return kind == PnKind::nrpn ? cc == 98 : cc == 100; + case 2: return cc == 6; + case 3: return cc == 38; + } + + return false; + }(); + + index = shouldContinue ? index + 1 : 0; + + if (index != bytes.size()) + return false; + + index = 0; + return true; +} + +} // namespace juce::universal_midi_packets diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h new file mode 100644 index 00000000..9c997b26 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h @@ -0,0 +1,188 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + Translates a series of MIDI 1 Universal MIDI Packets to corresponding MIDI 2 + packets. + + @tags{Audio} +*/ +class Midi1ToMidi2DefaultTranslator +{ +public: + Midi1ToMidi2DefaultTranslator() = default; + + /** Converts MIDI 1 Universal MIDI Packets to corresponding MIDI 2 packets, + calling `callback` with each converted packet. + + In some cases (such as RPN/NRPN messages) multiple MIDI 1 packets will + convert to a single MIDI 2 packet. In these cases, the translator will + accumulate the full message internally, and send a single callback with + the completed message, once all the individual MIDI 1 packets have been + processed. + */ + template + void dispatch (const View& v, PacketCallback&& callback) + { + const auto firstWord = v[0]; + const auto messageType = Utils::getMessageType (firstWord); + + if (messageType != 0x2) + { + callback (v); + return; + } + + const HelperValues helperValues + { + std::byte ((0x4 << 0x4) | Utils::getGroup (firstWord)), + std::byte ((firstWord >> 0x10) & 0xff), + std::byte ((firstWord >> 0x08) & 0x7f), + std::byte ((firstWord >> 0x00) & 0x7f), + }; + + switch (Utils::getStatus (firstWord)) + { + case 0x8: + case 0x9: + { + const auto packet = processNoteOnOrOff (helperValues); + callback (View (packet.data())); + return; + } + + case 0xa: + { + const auto packet = processPolyPressure (helperValues); + callback (View (packet.data())); + return; + } + + case 0xb: + { + PacketX2 packet; + + if (processControlChange (helperValues, packet)) + callback (View (packet.data())); + + return; + } + + case 0xc: + { + const auto packet = processProgramChange (helperValues); + callback (View (packet.data())); + return; + } + + case 0xd: + { + const auto packet = processChannelPressure (helperValues); + callback (View (packet.data())); + return; + } + + case 0xe: + { + const auto packet = processPitchBend (helperValues); + callback (View (packet.data())); + return; + } + } + } + + void reset() + { + groupAccumulators = {}; + groupBanks = {}; + } + +private: + enum class PnKind { nrpn, rpn }; + + struct HelperValues + { + std::byte typeAndGroup; + std::byte byte0; + std::byte byte1; + std::byte byte2; + }; + + static PacketX2 processNoteOnOrOff (HelperValues helpers); + static PacketX2 processPolyPressure (HelperValues helpers); + + bool processControlChange (HelperValues helpers, PacketX2& packet); + + PacketX2 processProgramChange (HelperValues helpers) const; + + static PacketX2 processChannelPressure (HelperValues helpers); + static PacketX2 processPitchBend (HelperValues helpers); + + class PnAccumulator + { + public: + bool addByte (uint8_t cc, std::byte byte); + + const std::array& getBytes() const noexcept { return bytes; } + PnKind getKind() const noexcept { return kind; } + + private: + std::array bytes; + uint8_t index = 0; + PnKind kind = PnKind::nrpn; + }; + + class Bank + { + public: + bool isValid() const noexcept { return ! (msb & 0x80); } + + uint8_t getMsb() const noexcept { return msb & 0x7f; } + uint8_t getLsb() const noexcept { return lsb & 0x7f; } + + void setMsb (uint8_t i) noexcept { msb = i & 0x7f; } + void setLsb (uint8_t i) noexcept { msb &= 0x7f; lsb = i & 0x7f; } + + private: + // We use the top bit to indicate whether this bank is valid. + // After reading the spec, it's not clear how we should determine whether + // there are valid values, so we'll just assume that the bank is valid + // once either the lsb or msb have been written. + uint8_t msb = 0x80; + uint8_t lsb = 0x00; + }; + + using ChannelAccumulators = std::array; + std::array groupAccumulators; + + using ChannelBanks = std::array; + std::array groupBanks; +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPProtocols.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPProtocols.h new file mode 100644 index 00000000..a103248a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPProtocols.h @@ -0,0 +1,45 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** The kinds of MIDI protocol that can be formatted into Universal MIDI Packets. */ +enum class PacketProtocol +{ + MIDI_1_0, + MIDI_2_0, +}; + +/** All kinds of MIDI protocol understood by JUCE. */ +enum class MidiProtocol +{ + bytestream, + UMP_MIDI_1_0, + UMP_MIDI_2_0, +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPReceiver.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPReceiver.h new file mode 100644 index 00000000..86e888a8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPReceiver.h @@ -0,0 +1,43 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + A base class for classes which receive Universal MIDI Packets from an input. + + @tags{Audio} +*/ +struct Receiver +{ + virtual ~Receiver() noexcept = default; + + /** This will be called each time a new packet is ready for processing. */ + virtual void packetReceived (const View& packet, double time) = 0; +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp new file mode 100644 index 00000000..61ab7852 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp @@ -0,0 +1,50 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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::universal_midi_packets +{ + +uint32_t SysEx7::getNumPacketsRequiredForDataSize (uint32_t size) +{ + constexpr auto denom = 6; + return (size / denom) + ((size % denom) != 0); +} + +SysEx7::PacketBytes SysEx7::getDataBytes (const PacketX2& packet) +{ + const auto numBytes = Utils::getChannel (packet[0]); + constexpr uint8_t maxBytes = 6; + jassert (numBytes <= maxBytes); + + return + { + { { std::byte { packet.getU8<2>() }, + std::byte { packet.getU8<3>() }, + std::byte { packet.getU8<4>() }, + std::byte { packet.getU8<5>() }, + std::byte { packet.getU8<6>() }, + std::byte { packet.getU8<7>() } } }, + jmin (numBytes, maxBytes) + }; +} + +} // namespace juce::universal_midi_packets diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h new file mode 100644 index 00000000..d9d8bf80 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h @@ -0,0 +1,74 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + This struct acts as a single-file namespace for Universal MIDI Packet + functionality related to 7-bit SysEx. + + @tags{Audio} +*/ +struct SysEx7 +{ + /** Returns the number of 64-bit packets required to hold a series of + SysEx bytes. + + The number passed to this function should exclude the leading/trailing + SysEx bytes used in an old midi bytestream, as these are not required + when using Universal MIDI Packets. + */ + static uint32_t getNumPacketsRequiredForDataSize (uint32_t); + + /** The different kinds of UMP SysEx-7 message. */ + enum class Kind : uint8_t + { + /** The whole message fits in a single 2-word packet. */ + complete = 0, + + /** The packet begins a SysEx message that will continue in subsequent packets. */ + begin = 1, + + /** The packet is a continuation of an ongoing SysEx message. */ + continuation = 2, + + /** The packet terminates an ongoing SysEx message. */ + end = 3 + }; + + /** Holds the bytes from a single SysEx-7 packet. */ + struct PacketBytes + { + std::array data; + uint8_t size; + }; + + /** Extracts the data bytes from a 64-bit data message. */ + static PacketBytes getDataBytes (const PacketX2& packet); +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.cpp new file mode 100644 index 00000000..3f0711c8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.cpp @@ -0,0 +1,56 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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::universal_midi_packets +{ + +uint32_t Utils::getNumWordsForMessageType (uint32_t mt) +{ + switch (Utils::getMessageType (mt)) + { + case 0x0: + case 0x1: + case 0x2: + case 0x6: + case 0x7: + return 1; + case 0x3: + case 0x4: + case 0x8: + case 0x9: + case 0xa: + return 2; + case 0xb: + case 0xc: + return 3; + case 0x5: + case 0xd: + case 0xe: + case 0xf: + return 4; + } + + jassertfalse; + return 1; +} + +} // namespace juce::universal_midi_packets diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h new file mode 100644 index 00000000..2792a011 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h @@ -0,0 +1,117 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + Helpful types and functions for interacting with Universal MIDI Packets. + + @tags{Audio} +*/ +struct Utils +{ + /** Joins 4 bytes into a single 32-bit word. */ + static constexpr uint32_t bytesToWord (std::byte a, std::byte b, std::byte c, std::byte d) + { + return uint32_t (a) << 0x18 + | uint32_t (b) << 0x10 + | uint32_t (c) << 0x08 + | uint32_t (d) << 0x00; + } + + /** Returns the expected number of 32-bit words in a Universal MIDI Packet, given + the first word of the packet. + + The result will be between 1 and 4 inclusive. + A result of 1 means that the word is itself a complete packet. + */ + static uint32_t getNumWordsForMessageType (uint32_t); + + /** + Helper functions for setting/getting 4-bit ranges inside a 32-bit word. + */ + template + struct U4 + { + static constexpr uint32_t shift = (uint32_t) 0x1c - Index * 4; + + static constexpr uint32_t set (uint32_t word, uint8_t value) + { + return (word & ~((uint32_t) 0xf << shift)) | (uint32_t) ((value & 0xf) << shift); + } + + static constexpr uint8_t get (uint32_t word) + { + return (uint8_t) ((word >> shift) & 0xf); + } + }; + + /** + Helper functions for setting/getting 8-bit ranges inside a 32-bit word. + */ + template + struct U8 + { + static constexpr uint32_t shift = (uint32_t) 0x18 - Index * 8; + + static constexpr uint32_t set (uint32_t word, uint8_t value) + { + return (word & ~((uint32_t) 0xff << shift)) | (uint32_t) (value << shift); + } + + static constexpr uint8_t get (uint32_t word) + { + return (uint8_t) ((word >> shift) & 0xff); + } + }; + + /** + Helper functions for setting/getting 16-bit ranges inside a 32-bit word. + */ + template + struct U16 + { + static constexpr uint32_t shift = (uint32_t) 0x10 - Index * 16; + + static constexpr uint32_t set (uint32_t word, uint16_t value) + { + return (word & ~((uint32_t) 0xffff << shift)) | (uint32_t) (value << shift); + } + + static constexpr uint16_t get (uint32_t word) + { + return (uint16_t) ((word >> shift) & 0xffff); + } + }; + + static constexpr uint8_t getMessageType (uint32_t w) noexcept { return U4<0>::get (w); } + static constexpr uint8_t getGroup (uint32_t w) noexcept { return U4<1>::get (w); } + static constexpr uint8_t getStatus (uint32_t w) noexcept { return U4<2>::get (w); } + static constexpr uint8_t getChannel (uint32_t w) noexcept { return U4<3>::get (w); } +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPView.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPView.cpp new file mode 100644 index 00000000..20869827 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPView.cpp @@ -0,0 +1,32 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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::universal_midi_packets +{ + +uint32_t View::size() const noexcept +{ + jassert (ptr != nullptr); + return Utils::getNumWordsForMessageType (*ptr); +} + +} // namespace juce::universal_midi_packets diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPView.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPView.h new file mode 100644 index 00000000..8b58a677 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPView.h @@ -0,0 +1,89 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + Points to a single Universal MIDI Packet. + + The packet must be well-formed for member functions to work correctly. + + Specifically, the constructor argument must be the beginning of a region of + uint32_t that contains at least `getNumWordsForMessageType (*data)` items, + where `data` is the constructor argument. + + NOTE: Instances of this class do not own the memory that they point to! + If you need to store a packet pointed-to by a View for later use, copy + the view contents to a Packets collection, or use the Utils::PacketX types. + + @tags{Audio} +*/ +class View +{ +public: + /** Create an invalid view. */ + View() noexcept = default; + + /** Create a view of the packet starting at address `d`. */ + explicit View (const uint32_t* data) noexcept : ptr (data) {} + + /** Get a pointer to the first word in the Universal MIDI Packet currently + pointed-to by this view. + */ + const uint32_t* data() const noexcept { return ptr; } + + /** Get the number of 32-words (between 1 and 4 inclusive) in the Universal + MIDI Packet currently pointed-to by this view. + */ + uint32_t size() const noexcept; + + /** Get a specific word from this packet. + + Passing an `index` that is greater than or equal to the result of `size` + will cause undefined behaviour. + */ + const uint32_t& operator[] (size_t index) const noexcept { return ptr[index]; } + + /** Get an iterator pointing to the first word in the packet. */ + const uint32_t* begin() const noexcept { return ptr; } + const uint32_t* cbegin() const noexcept { return ptr; } + + /** Get an iterator pointing one-past the last word in the packet. */ + const uint32_t* end() const noexcept { return ptr + size(); } + const uint32_t* cend() const noexcept { return ptr + size(); } + + /** Return true if this view is pointing to the same address as another view. */ + bool operator== (const View& other) const noexcept { return ptr == other.ptr; } + + /** Return false if this view is pointing to the same address as another view. */ + bool operator!= (const View& other) const noexcept { return ! operator== (other); } + +private: + const uint32_t* ptr = nullptr; +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp new file mode 100644 index 00000000..b7e587b8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp @@ -0,0 +1,1006 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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::universal_midi_packets +{ + +constexpr uint8_t operator""_u8 (unsigned long long int i) { return static_cast (i); } +constexpr uint16_t operator""_u16 (unsigned long long int i) { return static_cast (i); } +constexpr uint32_t operator""_u32 (unsigned long long int i) { return static_cast (i); } +constexpr uint64_t operator""_u64 (unsigned long long int i) { return static_cast (i); } + +class UniversalMidiPacketTests final : public UnitTest +{ +public: + UniversalMidiPacketTests() + : UnitTest ("Universal MIDI Packet", UnitTestCategories::midi) + { + } + + void runTest() override + { + auto random = getRandom(); + + beginTest ("Short bytestream midi messages can be round-tripped through the UMP converter"); + { + Midi1ToBytestreamTranslator translator (0); + + forEachNonSysExTestMessage (random, [&] (const MidiMessage& m) + { + const auto packets = toMidi1 (m); + expect (packets.size() == 1); + + // Make sure that the message type is correct + const auto msgType = Utils::getMessageType (packets.data()[0]); + expect (msgType == ((m.getRawData()[0] >> 0x4) == 0xf ? 0x1 : 0x2)); + + translator.dispatch (View {packets.data() }, + 0, + [&] (const BytestreamMidiView& roundTripped) + { + expect (equal (m, roundTripped.getMessage())); + }); + }); + } + + beginTest ("Bytestream SysEx converts to universal packets"); + { + { + // Zero length message + const auto packets = toMidi1 (createRandomSysEx (random, 0)); + expect (packets.size() == 2); + + expect (packets.data()[0] == 0x30000000); + expect (packets.data()[1] == 0x00000000); + } + + { + const auto message = createRandomSysEx (random, 1); + const auto packets = toMidi1 (message); + expect (packets.size() == 2); + + const auto* sysEx = message.getSysExData(); + expect (packets.data()[0] == Utils::bytesToWord (std::byte { 0x30 }, + std::byte { 0x01 }, + std::byte { sysEx[0] }, + std::byte { 0 })); + expect (packets.data()[1] == 0x00000000); + } + + { + const auto message = createRandomSysEx (random, 6); + const auto packets = toMidi1 (message); + expect (packets.size() == 2); + + const auto* sysEx = message.getSysExData(); + expect (packets.data()[0] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x06 }, std::byte { sysEx[0] }, std::byte { sysEx[1] })); + expect (packets.data()[1] == Utils::bytesToWord (std::byte { sysEx[2] }, std::byte { sysEx[3] }, std::byte { sysEx[4] }, std::byte { sysEx[5] })); + } + + { + const auto message = createRandomSysEx (random, 12); + const auto packets = toMidi1 (message); + expect (packets.size() == 4); + + const auto* sysEx = message.getSysExData(); + expect (packets.data()[0] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x16 }, std::byte { sysEx[0] }, std::byte { sysEx[1] })); + expect (packets.data()[1] == Utils::bytesToWord (std::byte { sysEx[2] }, std::byte { sysEx[3] }, std::byte { sysEx[4] }, std::byte { sysEx[5] })); + expect (packets.data()[2] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x36 }, std::byte { sysEx[6] }, std::byte { sysEx[7] })); + expect (packets.data()[3] == Utils::bytesToWord (std::byte { sysEx[8] }, std::byte { sysEx[9] }, std::byte { sysEx[10] }, std::byte { sysEx[11] })); + } + + { + const auto message = createRandomSysEx (random, 13); + const auto packets = toMidi1 (message); + expect (packets.size() == 6); + + const auto* sysEx = message.getSysExData(); + expect (packets.data()[0] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x16 }, std::byte { sysEx[0] }, std::byte { sysEx[1] })); + expect (packets.data()[1] == Utils::bytesToWord (std::byte { sysEx[2] }, std::byte { sysEx[3] }, std::byte { sysEx[4] }, std::byte { sysEx[5] })); + expect (packets.data()[2] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x26 }, std::byte { sysEx[6] }, std::byte { sysEx[7] })); + expect (packets.data()[3] == Utils::bytesToWord (std::byte { sysEx[8] }, std::byte { sysEx[9] }, std::byte { sysEx[10] }, std::byte { sysEx[11] })); + expect (packets.data()[4] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x31 }, std::byte { sysEx[12] }, std::byte { 0 })); + expect (packets.data()[5] == 0x00000000); + } + } + + ToBytestreamDispatcher converter (0); + Packets packets; + + const auto checkRoundTrip = [&] (const MidiBuffer& expected) + { + for (const auto meta : expected) + Conversion::toMidi1 (ump::BytestreamMidiView (meta), [&] (const auto p) { packets.add (p); }); + + MidiBuffer output; + converter.dispatch (packets.data(), + packets.data() + packets.size(), + 0, + [&] (const BytestreamMidiView& roundTripped) + { + output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); + }); + packets.clear(); + + expect (equal (expected, output)); + }; + + beginTest ("Long SysEx bytestream midi messages can be round-tripped through the UMP converter"); + { + for (auto length : { 0, 1, 2, 3, 4, 5, 6, 7, 13, 20, 100, 1000 }) + { + MidiBuffer expected; + expected.addEvent (createRandomSysEx (random, size_t (length)), 0); + checkRoundTrip (expected); + } + } + + beginTest ("UMP SysEx7 messages interspersed with utility messages convert to bytestream"); + { + const auto sysEx = createRandomSysEx (random, 100); + const auto originalPackets = toMidi1 (sysEx); + + Packets modifiedPackets; + + const auto addRandomUtilityUMP = [&] + { + const auto newPacket = createRandomUtilityUMP (random); + modifiedPackets.add (View (newPacket.data())); + }; + + for (const auto& packet : originalPackets) + { + addRandomUtilityUMP(); + modifiedPackets.add (packet); + addRandomUtilityUMP(); + } + + MidiBuffer output; + converter.dispatch (modifiedPackets.data(), + modifiedPackets.data() + modifiedPackets.size(), + 0, + [&] (const BytestreamMidiView& roundTripped) + { + output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); + }); + + // All Utility messages should have been ignored + expect (output.getNumEvents() == 1); + + for (const auto meta : output) + expect (equal (meta.getMessage(), sysEx)); + } + + beginTest ("UMP SysEx7 messages interspersed with System Realtime messages convert to bytestream"); + { + const auto sysEx = createRandomSysEx (random, 200); + const auto originalPackets = toMidi1 (sysEx); + + Packets modifiedPackets; + MidiBuffer realtimeMessages; + + const auto addRandomRealtimeUMP = [&] + { + const auto newPacket = createRandomRealtimeUMP (random); + modifiedPackets.add (View (newPacket.data())); + realtimeMessages.addEvent (Midi1ToBytestreamTranslator::fromUmp (newPacket), 0); + }; + + for (const auto& packet : originalPackets) + { + addRandomRealtimeUMP(); + modifiedPackets.add (packet); + addRandomRealtimeUMP(); + } + + MidiBuffer output; + converter.dispatch (modifiedPackets.data(), + modifiedPackets.data() + modifiedPackets.size(), + 0, + [&] (const BytestreamMidiView& roundTripped) + { + output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); + }); + + const auto numOutputs = output.getNumEvents(); + const auto numInputs = realtimeMessages.getNumEvents(); + expect (numOutputs == numInputs + 1); + + if (numOutputs == numInputs + 1) + { + const auto isMetadataEquivalent = [] (const MidiMessageMetadata& a, + const MidiMessageMetadata& b) + { + return equal (a.getMessage(), b.getMessage()); + }; + + auto it = output.begin(); + + for (const auto meta : realtimeMessages) + { + if (! isMetadataEquivalent (*it, meta)) + { + expect (equal ((*it).getMessage(), sysEx)); + ++it; + } + + expect (isMetadataEquivalent (*it, meta)); + ++it; + } + } + } + + beginTest ("UMP SysEx7 messages interspersed with System Realtime and Utility messages convert to bytestream"); + { + const auto sysEx = createRandomSysEx (random, 300); + const auto originalPackets = toMidi1 (sysEx); + + Packets modifiedPackets; + MidiBuffer realtimeMessages; + + const auto addRandomRealtimeUMP = [&] + { + const auto newPacket = createRandomRealtimeUMP (random); + modifiedPackets.add (View (newPacket.data())); + realtimeMessages.addEvent (Midi1ToBytestreamTranslator::fromUmp (newPacket), 0); + }; + + const auto addRandomUtilityUMP = [&] + { + const auto newPacket = createRandomUtilityUMP (random); + modifiedPackets.add (View (newPacket.data())); + }; + + for (const auto& packet : originalPackets) + { + addRandomRealtimeUMP(); + addRandomUtilityUMP(); + modifiedPackets.add (packet); + addRandomRealtimeUMP(); + addRandomUtilityUMP(); + } + + MidiBuffer output; + converter.dispatch (modifiedPackets.data(), + modifiedPackets.data() + modifiedPackets.size(), + 0, + [&] (const BytestreamMidiView& roundTripped) + { + output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); + }); + + const auto numOutputs = output.getNumEvents(); + const auto numInputs = realtimeMessages.getNumEvents(); + expect (numOutputs == numInputs + 1); + + if (numOutputs == numInputs + 1) + { + const auto isMetadataEquivalent = [] (const MidiMessageMetadata& a, const MidiMessageMetadata& b) + { + return equal (a.getMessage(), b.getMessage()); + }; + + auto it = output.begin(); + + for (const auto meta : realtimeMessages) + { + if (! isMetadataEquivalent (*it, meta)) + { + expect (equal ((*it).getMessage(), sysEx)); + ++it; + } + + expect (isMetadataEquivalent (*it, meta)); + ++it; + } + } + } + + beginTest ("SysEx messages are terminated by non-Utility, non-Realtime messages"); + { + const auto noteOn = [&] + { + MidiBuffer b; + b.addEvent (MidiMessage::noteOn (1, uint8_t (64), uint8_t (64)), 0); + return b; + }(); + + const auto noteOnPackets = [&] + { + Packets p; + + for (const auto meta : noteOn) + Conversion::toMidi1 (ump::BytestreamMidiView (meta), [&] (const auto packet) { p.add (packet); }); + + return p; + }(); + + const auto sysEx = createRandomSysEx (random, 300); + + const auto originalPackets = toMidi1 (sysEx); + + const auto modifiedPackets = [&] + { + Packets p; + + const auto insertionPoint = std::next (originalPackets.begin(), 10); + std::for_each (originalPackets.begin(), + insertionPoint, + [&] (const View& view) { p.add (view); }); + + for (const auto& view : noteOnPackets) + p.add (view); + + std::for_each (insertionPoint, + originalPackets.end(), + [&] (const View& view) { p.add (view); }); + + return p; + }(); + + // modifiedPackets now contains some SysEx packets interrupted by a MIDI 1 noteOn + + MidiBuffer output; + + const auto pushToOutput = [&] (const Packets& p) + { + converter.dispatch (p.data(), + p.data() + p.size(), + 0, + [&] (const BytestreamMidiView& roundTripped) + { + output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); + }); + }; + + pushToOutput (modifiedPackets); + + // Interrupted sysEx shouldn't be present + expect (equal (output, noteOn)); + + const auto newSysEx = createRandomSysEx (random, 300); + const auto newSysExPackets = toMidi1 (newSysEx); + + // If we push another midi event without interrupting it, + // it should get through without being modified, + // and it shouldn't be affected by the previous (interrupted) sysex. + + output.clear(); + pushToOutput (newSysExPackets); + + expect (output.getNumEvents() == 1); + + for (const auto meta : output) + expect (equal (meta.getMessage(), newSysEx)); + } + + beginTest ("Widening conversions work"); + { + // This is similar to the 'slow' example code from the MIDI 2.0 spec + const auto baselineScale = [] (uint32_t srcVal, uint32_t srcBits, uint32_t dstBits) + { + const auto scaleBits = (uint32_t) (dstBits - srcBits); + + auto bitShiftedValue = (uint32_t) (srcVal << scaleBits); + + const auto srcCenter = (uint32_t) (1 << (srcBits - 1)); + + if (srcVal <= srcCenter) + return bitShiftedValue; + + const auto repeatBits = (uint32_t) (srcBits - 1); + const auto repeatMask = (uint32_t) ((1 << repeatBits) - 1); + + auto repeatValue = (uint32_t) (srcVal & repeatMask); + + if (scaleBits > repeatBits) + repeatValue <<= scaleBits - repeatBits; + else + repeatValue >>= repeatBits - scaleBits; + + while (repeatValue != 0) + { + bitShiftedValue |= repeatValue; + repeatValue >>= repeatBits; + } + + return bitShiftedValue; + }; + + const auto baselineScale7To8 = [&] (uint8_t in) + { + return baselineScale (in, 7, 8); + }; + + const auto baselineScale7To16 = [&] (uint8_t in) + { + return baselineScale (in, 7, 16); + }; + + const auto baselineScale14To16 = [&] (uint16_t in) + { + return baselineScale (in, 14, 16); + }; + + const auto baselineScale7To32 = [&] (uint8_t in) + { + return baselineScale (in, 7, 32); + }; + + const auto baselineScale14To32 = [&] (uint16_t in) + { + return baselineScale (in, 14, 32); + }; + + for (auto i = 0; i != 100; ++i) + { + const auto rand = (uint8_t) random.nextInt (0x80); + expectEquals ((int64_t) Conversion::scaleTo8 (rand), + (int64_t) baselineScale7To8 (rand)); + } + + expectEquals ((int64_t) Conversion::scaleTo16 ((uint8_t) 0x00), (int64_t) 0x0000); + expectEquals ((int64_t) Conversion::scaleTo16 ((uint8_t) 0x0a), (int64_t) 0x1400); + expectEquals ((int64_t) Conversion::scaleTo16 ((uint8_t) 0x40), (int64_t) 0x8000); + expectEquals ((int64_t) Conversion::scaleTo16 ((uint8_t) 0x57), (int64_t) 0xaeba); + expectEquals ((int64_t) Conversion::scaleTo16 ((uint8_t) 0x7f), (int64_t) 0xffff); + + for (auto i = 0; i != 100; ++i) + { + const auto rand = (uint8_t) random.nextInt (0x80); + expectEquals ((int64_t) Conversion::scaleTo16 (rand), + (int64_t) baselineScale7To16 (rand)); + } + + for (auto i = 0; i != 100; ++i) + { + const auto rand = (uint16_t) random.nextInt (0x4000); + expectEquals ((int64_t) Conversion::scaleTo16 (rand), + (int64_t) baselineScale14To16 (rand)); + } + + for (auto i = 0; i != 100; ++i) + { + const auto rand = (uint8_t) random.nextInt (0x80); + expectEquals ((int64_t) Conversion::scaleTo32 (rand), + (int64_t) baselineScale7To32 (rand)); + } + + expectEquals ((int64_t) Conversion::scaleTo32 ((uint16_t) 0x0000), (int64_t) 0x00000000); + expectEquals ((int64_t) Conversion::scaleTo32 ((uint16_t) 0x2000), (int64_t) 0x80000000); + expectEquals ((int64_t) Conversion::scaleTo32 ((uint16_t) 0x3fff), (int64_t) 0xffffffff); + + for (auto i = 0; i != 100; ++i) + { + const auto rand = (uint16_t) random.nextInt (0x4000); + expectEquals ((int64_t) Conversion::scaleTo32 (rand), + (int64_t) baselineScale14To32 (rand)); + } + } + + beginTest ("Round-trip widening/narrowing conversions work"); + { + for (auto i = 0; i != 100; ++i) + { + { + const auto rand = (uint8_t) random.nextInt (0x80); + expectEquals (Conversion::scaleTo7 (Conversion::scaleTo8 (rand)), rand); + } + + { + const auto rand = (uint8_t) random.nextInt (0x80); + expectEquals (Conversion::scaleTo7 (Conversion::scaleTo16 (rand)), rand); + } + + { + const auto rand = (uint8_t) random.nextInt (0x80); + expectEquals (Conversion::scaleTo7 (Conversion::scaleTo32 (rand)), rand); + } + + { + const auto rand = (uint16_t) random.nextInt (0x4000); + expectEquals ((uint64_t) Conversion::scaleTo14 (Conversion::scaleTo16 (rand)), (uint64_t) rand); + } + + { + const auto rand = (uint16_t) random.nextInt (0x4000); + expectEquals ((uint64_t) Conversion::scaleTo14 (Conversion::scaleTo32 (rand)), (uint64_t) rand); + } + } + } + + beginTest ("MIDI 2 -> 1 note on conversions"); + { + { + Packets midi2; + midi2.add (PacketX2 { 0x41946410, 0x12345678 }); + + Packets midi1; + midi1.add (PacketX1 { 0x21946409 }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + + { + // If the velocity is close to 0, the output velocity should still be 1 + Packets midi2; + midi2.add (PacketX2 { 0x4295327f, 0x00345678 }); + + Packets midi1; + midi1.add (PacketX1 { 0x22953201 }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + } + + beginTest ("MIDI 2 -> 1 note off conversion"); + { + Packets midi2; + midi2.add (PacketX2 { 0x448b0520, 0xfedcba98 }); + + Packets midi1; + midi1.add (PacketX1 { 0x248b057f }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + + beginTest ("MIDI 2 -> 1 poly pressure conversion"); + { + Packets midi2; + midi2.add (PacketX2 { 0x49af0520, 0x80dcba98 }); + + Packets midi1; + midi1.add (PacketX1 { 0x29af0540 }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + + beginTest ("MIDI 2 -> 1 control change conversion"); + { + Packets midi2; + midi2.add (PacketX2 { 0x49b00520, 0x80dcba98 }); + + Packets midi1; + midi1.add (PacketX1 { 0x29b00540 }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + + beginTest ("MIDI 2 -> 1 channel pressure conversion"); + { + Packets midi2; + midi2.add (PacketX2 { 0x40d20520, 0x80dcba98 }); + + Packets midi1; + midi1.add (PacketX1 { 0x20d24000 }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + + beginTest ("MIDI 2 -> 1 nrpn rpn conversion"); + { + { + Packets midi2; + midi2.add (PacketX2 { 0x44240123, 0x456789ab }); + + Packets midi1; + midi1.add (PacketX1 { 0x24b46501 }); + midi1.add (PacketX1 { 0x24b46423 }); + midi1.add (PacketX1 { 0x24b40622 }); + midi1.add (PacketX1 { 0x24b42659 }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + + { + Packets midi2; + midi2.add (PacketX2 { 0x48347f7f, 0xffffffff }); + + Packets midi1; + midi1.add (PacketX1 { 0x28b4637f }); + midi1.add (PacketX1 { 0x28b4627f }); + midi1.add (PacketX1 { 0x28b4067f }); + midi1.add (PacketX1 { 0x28b4267f }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + } + + beginTest ("MIDI 2 -> 1 program change and bank select conversion"); + { + { + // If the bank valid bit is 0, just emit a program change + Packets midi2; + midi2.add (PacketX2 { 0x4cc10000, 0x70004020 }); + + Packets midi1; + midi1.add (PacketX1 { 0x2cc17000 }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + + { + // If the bank valid bit is 1, emit bank select control changes and a program change + Packets midi2; + midi2.add (PacketX2 { 0x4bc20001, 0x70004020 }); + + Packets midi1; + midi1.add (PacketX1 { 0x2bb20040 }); + midi1.add (PacketX1 { 0x2bb22020 }); + midi1.add (PacketX1 { 0x2bc27000 }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + } + + beginTest ("MIDI 2 -> 1 pitch bend conversion"); + { + Packets midi2; + midi2.add (PacketX2 { 0x4eee0000, 0x12340000 }); + + Packets midi1; + midi1.add (PacketX1 { 0x2eee0d09 }); + + checkMidi2ToMidi1Conversion (midi2, midi1); + } + + beginTest ("MIDI 2 -> 1 messages which don't convert"); + { + const std::byte opcodes[] { std::byte { 0x0 }, + std::byte { 0x1 }, + std::byte { 0x4 }, + std::byte { 0x5 }, + std::byte { 0x6 }, + std::byte { 0xf } }; + + for (const auto opcode : opcodes) + { + Packets midi2; + midi2.add (PacketX2 { Utils::bytesToWord (std::byte { 0x40 }, std::byte { opcode << 0x4 }, std::byte { 0 }, std::byte { 0 }), 0x0 }); + checkMidi2ToMidi1Conversion (midi2, {}); + } + } + + beginTest ("MIDI 2 -> 1 messages which are passed through"); + { + const uint8_t typecodesX1[] { 0x0, 0x1, 0x2 }; + + for (const auto typecode : typecodesX1) + { + Packets p; + p.add (PacketX1 { (uint32_t) ((int64_t) typecode << 0x1c | (random.nextInt64() & 0xffffff)) }); + + checkMidi2ToMidi1Conversion (p, p); + } + + { + Packets p; + p.add (PacketX2 { (uint32_t) (0x3 << 0x1c | (random.nextInt64() & 0xffffff)), + (uint32_t) (random.nextInt64() & 0xffffffff) }); + + checkMidi2ToMidi1Conversion (p, p); + } + + { + Packets p; + p.add (PacketX4 { (uint32_t) (0x5 << 0x1c | (random.nextInt64() & 0xffffff)), + (uint32_t) (random.nextInt64() & 0xffffffff), + (uint32_t) (random.nextInt64() & 0xffffffff), + (uint32_t) (random.nextInt64() & 0xffffffff) }); + + checkMidi2ToMidi1Conversion (p, p); + } + } + + beginTest ("MIDI 2 -> 1 control changes which should be ignored"); + { + const uint8_t CCs[] { 6, 38, 98, 99, 100, 101, 0, 32 }; + + for (const auto cc : CCs) + { + Packets midi2; + midi2.add (PacketX2 { (uint32_t) (0x40b00000 | (cc << 0x8)), 0x00000000 }); + + checkMidi2ToMidi1Conversion (midi2, {}); + } + } + + beginTest ("MIDI 1 -> 2 note on conversions"); + { + { + Packets midi1; + midi1.add (PacketX1 { 0x20904040 }); + + Packets midi2; + midi2.add (PacketX2 { 0x40904000, static_cast (Conversion::scaleTo16 (0x40_u8)) << 0x10 }); + + checkMidi1ToMidi2Conversion (midi1, midi2); + } + + // If velocity is 0, convert to a note-off + { + Packets midi1; + midi1.add (PacketX1 { 0x23935100 }); + + Packets midi2; + midi2.add (PacketX2 { 0x43835100, 0x0 }); + + checkMidi1ToMidi2Conversion (midi1, midi2); + } + } + + beginTest ("MIDI 1 -> 2 note off conversions"); + { + Packets midi1; + midi1.add (PacketX1 { 0x21831020 }); + + Packets midi2; + midi2.add (PacketX2 { 0x41831000, static_cast (Conversion::scaleTo16 (0x20_u8)) << 0x10 }); + + checkMidi1ToMidi2Conversion (midi1, midi2); + } + + beginTest ("MIDI 1 -> 2 poly pressure conversions"); + { + Packets midi1; + midi1.add (PacketX1 { 0x20af7330 }); + + Packets midi2; + midi2.add (PacketX2 { 0x40af7300, Conversion::scaleTo32 (0x30_u8) }); + + checkMidi1ToMidi2Conversion (midi1, midi2); + } + + beginTest ("individual MIDI 1 -> 2 control changes which should be ignored"); + { + const uint8_t CCs[] { 6, 38, 98, 99, 100, 101, 0, 32 }; + + for (const auto cc : CCs) + { + Packets midi1; + midi1.add (PacketX1 { Utils::bytesToWord (std::byte { 0x20 }, std::byte { 0xb0 }, std::byte { cc }, std::byte { 0x00 }) }); + + checkMidi1ToMidi2Conversion (midi1, {}); + } + } + + beginTest ("MIDI 1 -> 2 control change conversions"); + { + // normal control change + { + Packets midi1; + midi1.add (PacketX1 { 0x29b1017f }); + + Packets midi2; + midi2.add (PacketX2 { 0x49b10100, Conversion::scaleTo32 (0x7f_u8) }); + + checkMidi1ToMidi2Conversion (midi1, midi2); + } + + // nrpn + { + Packets midi1; + midi1.add (PacketX1 { 0x20b06301 }); + midi1.add (PacketX1 { 0x20b06223 }); + midi1.add (PacketX1 { 0x20b00645 }); + midi1.add (PacketX1 { 0x20b02667 }); + + Packets midi2; + midi2.add (PacketX2 { 0x40300123, Conversion::scaleTo32 (static_cast ((0x45 << 7) | 0x67)) }); + + checkMidi1ToMidi2Conversion (midi1, midi2); + } + + // rpn + { + Packets midi1; + midi1.add (PacketX1 { 0x20b06543 }); + midi1.add (PacketX1 { 0x20b06421 }); + midi1.add (PacketX1 { 0x20b00601 }); + midi1.add (PacketX1 { 0x20b02623 }); + + Packets midi2; + midi2.add (PacketX2 { 0x40204321, Conversion::scaleTo32 (static_cast ((0x01 << 7) | 0x23)) }); + + checkMidi1ToMidi2Conversion (midi1, midi2); + } + } + + beginTest ("MIDI 1 -> MIDI 2 program change and bank select"); + { + Packets midi1; + // program change with bank + midi1.add (PacketX1 { 0x2bb20030 }); + midi1.add (PacketX1 { 0x2bb22010 }); + midi1.add (PacketX1 { 0x2bc24000 }); + // program change without bank (different group and channel) + midi1.add (PacketX1 { 0x20c01000 }); + + Packets midi2; + midi2.add (PacketX2 { 0x4bc20001, 0x40003010 }); + midi2.add (PacketX2 { 0x40c00000, 0x10000000 }); + + checkMidi1ToMidi2Conversion (midi1, midi2); + } + + beginTest ("MIDI 1 -> MIDI 2 channel pressure conversions"); + { + Packets midi1; + midi1.add (PacketX1 { 0x20df3000 }); + + Packets midi2; + midi2.add (PacketX2 { 0x40df0000, Conversion::scaleTo32 (0x30_u8) }); + + checkMidi1ToMidi2Conversion (midi1, midi2); + } + + beginTest ("MIDI 1 -> MIDI 2 pitch bend conversions"); + { + Packets midi1; + midi1.add (PacketX1 { 0x20e74567 }); + + Packets midi2; + midi2.add (PacketX2 { 0x40e70000, Conversion::scaleTo32 (static_cast ((0x67 << 7) | 0x45)) }); + + checkMidi1ToMidi2Conversion (midi1, midi2); + } + } + +private: + static Packets toMidi1 (const MidiMessage& msg) + { + Packets packets; + Conversion::toMidi1 (ump::BytestreamMidiView (&msg), [&] (const auto p) { packets.add (p); }); + return packets; + } + + static Packets convertMidi2ToMidi1 (const Packets& midi2) + { + Packets r; + + for (const auto& packet : midi2) + Conversion::midi2ToMidi1DefaultTranslation (packet, [&r] (const View& v) { r.add (v); }); + + return r; + } + + static Packets convertMidi1ToMidi2 (const Packets& midi1) + { + Packets r; + Midi1ToMidi2DefaultTranslator translator; + + for (const auto& packet : midi1) + translator.dispatch (packet, [&r] (const View& v) { r.add (v); }); + + return r; + } + + void checkBytestreamConversion (const Packets& actual, const Packets& expected) + { + expectEquals ((int) actual.size(), (int) expected.size()); + + if (actual.size() != expected.size()) + return; + + auto actualPtr = actual.data(); + + std::for_each (expected.data(), + expected.data() + expected.size(), + [&] (const uint32_t word) { expectEquals ((uint64_t) *actualPtr++, (uint64_t) word); }); + } + + void checkMidi2ToMidi1Conversion (const Packets& midi2, const Packets& expected) + { + checkBytestreamConversion (convertMidi2ToMidi1 (midi2), expected); + } + + void checkMidi1ToMidi2Conversion (const Packets& midi1, const Packets& expected) + { + checkBytestreamConversion (convertMidi1ToMidi2 (midi1), expected); + } + + MidiMessage createRandomSysEx (Random& random, size_t sysExBytes) + { + std::vector data; + data.reserve (sysExBytes); + + for (size_t i = 0; i != sysExBytes; ++i) + data.push_back (uint8_t (random.nextInt (0x80))); + + return MidiMessage::createSysExMessage (data.data(), int (data.size())); + } + + PacketX1 createRandomUtilityUMP (Random& random) + { + const auto status = random.nextInt (3); + + return PacketX1 { Utils::bytesToWord (std::byte { 0 }, + std::byte (status << 0x4), + std::byte (status == 0 ? 0 : random.nextInt (0x100)), + std::byte (status == 0 ? 0 : random.nextInt (0x100))) }; + } + + PacketX1 createRandomRealtimeUMP (Random& random) + { + const auto status = [&] + { + switch (random.nextInt (6)) + { + case 0: return std::byte { 0xf8 }; + case 1: return std::byte { 0xfa }; + case 2: return std::byte { 0xfb }; + case 3: return std::byte { 0xfc }; + case 4: return std::byte { 0xfe }; + case 5: return std::byte { 0xff }; + } + + jassertfalse; + return std::byte { 0x00 }; + }(); + + return PacketX1 { Utils::bytesToWord (std::byte { 0x10 }, status, std::byte { 0x00 }, std::byte { 0x00 }) }; + } + + template + void forEachNonSysExTestMessage (Random& random, Fn&& fn) + { + for (uint16_t counter = 0x80; counter != 0x100; ++counter) + { + const auto firstByte = (uint8_t) counter; + + if (firstByte == 0xf0 || firstByte == 0xf7) + continue; // sysEx is tested separately + + const auto length = MidiMessage::getMessageLengthFromFirstByte (firstByte); + const auto getDataByte = [&] { return uint8_t (random.nextInt (256) & 0x7f); }; + + const auto message = [&] + { + switch (length) + { + case 1: return MidiMessage (firstByte); + case 2: return MidiMessage (firstByte, getDataByte()); + case 3: return MidiMessage (firstByte, getDataByte(), getDataByte()); + } + + return MidiMessage(); + }(); + + fn (message); + } + } + + static bool equal (const MidiMessage& a, const MidiMessage& b) noexcept + { + return a.getRawDataSize() == b.getRawDataSize() + && std::equal (a.getRawData(), a.getRawData() + a.getRawDataSize(), b.getRawData()); + } + + static bool equal (const MidiBuffer& a, const MidiBuffer& b) noexcept + { + return a.data == b.data; + } +}; + +static UniversalMidiPacketTests universalMidiPacketTests; + +} // namespace juce::universal_midi_packets diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPacket.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPacket.h new file mode 100644 index 00000000..539f663f --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPacket.h @@ -0,0 +1,190 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + Holds a single Universal MIDI Packet. + + @tags{Audio} +*/ +template +class Packet +{ +public: + Packet() = default; + + template = 0> + Packet (uint32_t a) + : contents { { a } } + { + jassert (Utils::getNumWordsForMessageType (a) == 1); + } + + template = 0> + Packet (uint32_t a, uint32_t b) + : contents { { a, b } } + { + jassert (Utils::getNumWordsForMessageType (a) == 2); + } + + template = 0> + Packet (uint32_t a, uint32_t b, uint32_t c) + : contents { { a, b, c } } + { + jassert (Utils::getNumWordsForMessageType (a) == 3); + } + + template = 0> + Packet (uint32_t a, uint32_t b, uint32_t c, uint32_t d) + : contents { { a, b, c, d } } + { + jassert (Utils::getNumWordsForMessageType (a) == 4); + } + + template = 0> + explicit Packet (const std::array& fullPacket) + : contents (fullPacket) + { + jassert (Utils::getNumWordsForMessageType (fullPacket.front()) == numWords); + } + + Packet withMessageType (uint8_t type) const noexcept + { + return withU4<0> (type); + } + + Packet withGroup (uint8_t group) const noexcept + { + return withU4<1> (group); + } + + Packet withStatus (uint8_t status) const noexcept + { + return withU4<2> (status); + } + + Packet withChannel (uint8_t channel) const noexcept + { + return withU4<3> (channel); + } + + uint8_t getMessageType() const noexcept { return getU4<0>(); } + + uint8_t getGroup() const noexcept { return getU4<1>(); } + + uint8_t getStatus() const noexcept { return getU4<2>(); } + + uint8_t getChannel() const noexcept { return getU4<3>(); } + + template + Packet withU4 (uint8_t value) const noexcept + { + constexpr auto word = index / 8; + auto copy = *this; + std::get (copy.contents) = Utils::U4::set (copy.template getU32(), value); + return copy; + } + + template + Packet withU8 (uint8_t value) const noexcept + { + constexpr auto word = index / 4; + auto copy = *this; + std::get (copy.contents) = Utils::U8::set (copy.template getU32(), value); + return copy; + } + + template + Packet withU16 (uint16_t value) const noexcept + { + constexpr auto word = index / 2; + auto copy = *this; + std::get (copy.contents) = Utils::U16::set (copy.template getU32(), value); + return copy; + } + + template + Packet withU32 (uint32_t value) const noexcept + { + auto copy = *this; + std::get (copy.contents) = value; + return copy; + } + + template + uint8_t getU4() const noexcept + { + return Utils::U4::get (this->template getU32()); + } + + template + uint8_t getU8() const noexcept + { + return Utils::U8::get (this->template getU32()); + } + + template + uint16_t getU16() const noexcept + { + return Utils::U16::get (this->template getU32()); + } + + template + uint32_t getU32() const noexcept + { + return std::get (contents); + } + + //============================================================================== + using Contents = std::array; + + using const_iterator = typename Contents::const_iterator; + + const_iterator begin() const noexcept { return contents.begin(); } + const_iterator cbegin() const noexcept { return contents.begin(); } + + const_iterator end() const noexcept { return contents.end(); } + const_iterator cend() const noexcept { return contents.end(); } + + const uint32_t* data() const noexcept { return contents.data(); } + + const uint32_t& front() const noexcept { return contents.front(); } + const uint32_t& back() const noexcept { return contents.back(); } + + const uint32_t& operator[] (size_t index) const noexcept { return contents[index]; } + +private: + Contents contents { {} }; +}; + +using PacketX1 = Packet<1>; +using PacketX2 = Packet<2>; +using PacketX3 = Packet<3>; +using PacketX4 = Packet<4>; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPackets.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPackets.h new file mode 100644 index 00000000..6317f0c8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPackets.h @@ -0,0 +1,93 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + Holds a collection of Universal MIDI Packets. + + Unlike MidiBuffer, this collection does not store any additional information + (e.g. timestamps) alongside the raw messages. + + If timestamps are required, these can be added to the container in UMP format, + as Jitter Reduction Utility messages. + + @tags{Audio} +*/ +class Packets +{ +public: + /** Adds a single packet to the collection. + + The View must be valid for this to work. If the view + points to a malformed message, or if the view points to a region + too short for the contained message, this call will result in + undefined behaviour. + */ + void add (const View& v) { storage.insert (storage.end(), v.cbegin(), v.cend()); } + + void add (const PacketX1& p) { addImpl (p); } + void add (const PacketX2& p) { addImpl (p); } + void add (const PacketX3& p) { addImpl (p); } + void add (const PacketX4& p) { addImpl (p); } + + /** Pre-allocates space for at least `numWords` 32-bit words in this collection. */ + void reserve (size_t numWords) { storage.reserve (numWords); } + + /** Removes all previously-added packets from this collection. */ + void clear() { storage.clear(); } + + /** Gets an iterator pointing to the first packet in this collection. */ + Iterator cbegin() const noexcept { return Iterator (data(), size()); } + Iterator begin() const noexcept { return cbegin(); } + + /** Gets an iterator pointing one-past the last packet in this collection. */ + Iterator cend() const noexcept { return Iterator (data() + size(), 0); } + Iterator end() const noexcept { return cend(); } + + /** Gets a pointer to the contents of the collection as a range of raw 32-bit words. */ + const uint32_t* data() const noexcept { return storage.data(); } + + /** Returns the number of uint32_t words in storage. + + Note that this is likely to be larger than the number of packets + currently being stored, as some packets span multiple words. + */ + size_t size() const noexcept { return storage.size(); } + +private: + template + void addImpl (const Packet& p) + { + jassert (Utils::getNumWordsForMessageType (p[0]) == numWords); + add (View (p.data())); + } + + std::vector storage; +}; + +} // namespace juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp index 0b867891..142b6e4e 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -27,56 +27,91 @@ namespace { const uint8 noLSBValueReceived = 0xff; const Range allChannels { 1, 17 }; + + template + void mpeInstrumentFill (Range& range, const Value& value) + { + std::fill (std::begin (range), std::end (range), value); + } } //============================================================================== MPEInstrument::MPEInstrument() noexcept { - std::fill_n (lastPressureLowerBitReceivedOnChannel, 16, noLSBValueReceived); - std::fill_n (lastTimbreLowerBitReceivedOnChannel, 16, noLSBValueReceived); - std::fill_n (isMemberChannelSustained, 16, false); + mpeInstrumentFill (lastPressureLowerBitReceivedOnChannel, noLSBValueReceived); + mpeInstrumentFill (lastTimbreLowerBitReceivedOnChannel, noLSBValueReceived); + mpeInstrumentFill (isMemberChannelSustained, false); pitchbendDimension.value = &MPENote::pitchbend; - pressureDimension.value = &MPENote::pressure; - timbreDimension.value = &MPENote::timbre; + pressureDimension.value = &MPENote::pressure; + timbreDimension.value = &MPENote::timbre; - // the default value for pressure is 0, for all other dimension it is centre (= default MPEValue) - std::fill_n (pressureDimension.lastValueReceivedOnChannel, 16, MPEValue::minValue()); + resetLastReceivedValues(); - legacyMode.isEnabled = false; - legacyMode.pitchbendRange = 2; legacyMode.channelRange = allChannels; } -MPEInstrument::~MPEInstrument() +MPEInstrument::MPEInstrument (MPEZoneLayout layout) + : MPEInstrument() { + setZoneLayout (layout); } +MPEInstrument::~MPEInstrument() = default; + //============================================================================== MPEZoneLayout MPEInstrument::getZoneLayout() const noexcept { return zoneLayout; } +void MPEInstrument::resetLastReceivedValues() +{ + struct Defaults + { + MPEDimension& dimension; + MPEValue defaultValue; + }; + + // The default value for pressure is 0, for all other dimensions it is centre + for (const auto& pair : { Defaults { pressureDimension, MPEValue::minValue() }, + Defaults { pitchbendDimension, MPEValue::centreValue() }, + Defaults { timbreDimension, MPEValue::centreValue() } }) + { + mpeInstrumentFill (pair.dimension.lastValueReceivedOnChannel, pair.defaultValue); + } +} + void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout) { releaseAllNotes(); const ScopedLock sl (lock); legacyMode.isEnabled = false; - zoneLayout = newLayout; + + if (zoneLayout != newLayout) + { + zoneLayout = newLayout; + listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); }); + } } //============================================================================== void MPEInstrument::enableLegacyMode (int pitchbendRange, Range channelRange) { + if (legacyMode.isEnabled) + return; + releaseAllNotes(); const ScopedLock sl (lock); + legacyMode.isEnabled = true; legacyMode.pitchbendRange = pitchbendRange; legacyMode.channelRange = channelRange; + zoneLayout.clearAllZones(); + listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); }); } bool MPEInstrument::isLegacyModeEnabled() const noexcept @@ -95,7 +130,12 @@ void MPEInstrument::setLegacyModeChannelRange (Range channelRange) releaseAllNotes(); const ScopedLock sl (lock); - legacyMode.channelRange = channelRange; + + if (legacyMode.channelRange != channelRange) + { + legacyMode.channelRange = channelRange; + listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); }); + } } int MPEInstrument::getLegacyModePitchbendRange() const noexcept @@ -109,7 +149,12 @@ void MPEInstrument::setLegacyModePitchbendRange (int pitchbendRange) releaseAllNotes(); const ScopedLock sl (lock); - legacyMode.pitchbendRange = pitchbendRange; + + if (legacyMode.pitchbendRange != pitchbendRange) + { + legacyMode.pitchbendRange = pitchbendRange; + listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); }); + } } //============================================================================== @@ -220,7 +265,7 @@ void MPEInstrument::processMidiResetAllControllersMessage (const MidiMessage& me if (legacyMode.isEnabled && legacyMode.channelRange.contains (message.getChannel())) { - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -238,7 +283,7 @@ void MPEInstrument::processMidiResetAllControllersMessage (const MidiMessage& me auto zone = (message.getChannel() == 1 ? zoneLayout.getLowerZone() : zoneLayout.getUpperZone()); - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -326,18 +371,18 @@ void MPEInstrument::noteOff (int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity) { + const ScopedLock sl (lock); + if (notes.isEmpty() || ! isUsingChannel (midiChannel)) return; - const ScopedLock sl (lock); - if (auto* note = getNotePtr (midiChannel, midiNoteNumber)) { note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off; note->noteOffVelocity = midiNoteOffVelocity; - // If no more notes are playing on this channel, reset the dimension values - if (getLastNotePlayedPtr (midiChannel) == nullptr) + // If no more notes are playing on this channel in mpe mode, reset the dimension values + if (! legacyMode.isEnabled && getLastNotePlayedPtr (midiChannel) == nullptr) { pressureDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::minValue(); pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue(); @@ -379,7 +424,7 @@ void MPEInstrument::polyAftertouch (int midiChannel, int midiNoteNumber, MPEValu { const ScopedLock sl (lock); - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -395,7 +440,7 @@ void MPEInstrument::polyAftertouch (int midiChannel, int midiNoteNumber, MPEValu MPEValue MPEInstrument::getInitialValueForNewNote (int midiChannel, MPEDimension& dimension) const { - if (getLastNotePlayedPtr (midiChannel) != nullptr) + if (! legacyMode.isEnabled && getLastNotePlayedPtr (midiChannel) != nullptr) return &dimension == &pressureDimension ? MPEValue::minValue() : MPEValue::centreValue(); return dimension.lastValueReceivedOnChannel[midiChannel - 1]; @@ -413,7 +458,7 @@ void MPEInstrument::updateDimension (int midiChannel, MPEDimension& dimension, M { if (dimension.trackingMode == allNotesOnChannel) { - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -442,7 +487,7 @@ void MPEInstrument::updateDimensionMaster (bool isLowerZone, MPEDimension& dimen if (! zone.isActive()) return; - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -491,17 +536,19 @@ void MPEInstrument::updateNoteTotalPitchbend (MPENote& note) { if (legacyMode.isEnabled) { - note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * legacyMode.pitchbendRange; + note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * (float) legacyMode.pitchbendRange; } else { auto zone = zoneLayout.getLowerZone(); - if (! zone.isUsing (note.midiChannel)) + if (! zone.isActive() || ! zone.isUsing (note.midiChannel)) { - if (zoneLayout.getUpperZone().isUsing (note.midiChannel)) + auto upperZone = zoneLayout.getUpperZone(); + + if (upperZone.isActive() && upperZone.isUsing (note.midiChannel)) { - zone = zoneLayout.getUpperZone(); + zone = upperZone; } else { @@ -514,11 +561,11 @@ void MPEInstrument::updateNoteTotalPitchbend (MPENote& note) auto notePitchbendInSemitones = 0.0f; if (zone.isUsingChannelAsMemberChannel (note.midiChannel)) - notePitchbendInSemitones = note.pitchbend.asSignedFloat() * zone.perNotePitchbendRange; + notePitchbendInSemitones = note.pitchbend.asSignedFloat() * (float) zone.perNotePitchbendRange; auto masterPitchbendInSemitones = pitchbendDimension.lastValueReceivedOnChannel[zone.getMasterChannel() - 1] .asSignedFloat() - * zone.masterPitchbendRange; + * (float) zone.masterPitchbendRange; note.totalPitchbendInSemitones = notePitchbendInSemitones + masterPitchbendInSemitones; } @@ -549,7 +596,7 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool auto zone = (midiChannel == 1 ? zoneLayout.getLowerZone() : zoneLayout.getUpperZone()); - for (auto i = notes.size(); --i >= 0;) + for (int i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -576,18 +623,20 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool if (! isSostenuto) { - if (legacyMode.isEnabled) - { - isMemberChannelSustained[midiChannel - 1] = isDown; - } - else + isMemberChannelSustained[midiChannel - 1] = isDown; + + if (! legacyMode.isEnabled) { if (zone.isLowerZone()) - for (auto i = zone.getFirstMemberChannel(); i <= zone.getLastMemberChannel(); ++i) + { + for (int i = zone.getFirstMemberChannel(); i <= zone.getLastMemberChannel(); ++i) isMemberChannelSustained[i - 1] = isDown; + } else - for (auto i = zone.getFirstMemberChannel(); i >= zone.getLastMemberChannel(); --i) + { + for (int i = zone.getFirstMemberChannel(); i >= zone.getLastMemberChannel(); --i) isMemberChannelSustained[i - 1] = isDown; + } } } } @@ -642,6 +691,17 @@ MPENote MPEInstrument::getNote (int index) const noexcept return notes[index]; } +MPENote MPEInstrument::getNoteWithID (uint16 noteID) const noexcept +{ + const ScopedLock sl (lock); + + for (auto& note : notes) + if (note.noteID == noteID) + return note; + + return {}; +} + //============================================================================== MPENote MPEInstrument::getMostRecentNote (int midiChannel) const noexcept { @@ -705,6 +765,8 @@ MPENote* MPEInstrument::getNotePtr (int midiChannel, TrackingMode mode) noexcept //============================================================================== const MPENote* MPEInstrument::getLastNotePlayedPtr (int midiChannel) const noexcept { + const ScopedLock sl (lock); + for (auto i = notes.size(); --i >= 0;) { auto& note = notes.getReference (i); @@ -791,12 +853,20 @@ void MPEInstrument::releaseAllNotes() notes.clear(); } +//============================================================================== +void MPEInstrument::Listener::noteAdded ([[maybe_unused]] MPENote newNote) {} +void MPEInstrument::Listener::notePressureChanged ([[maybe_unused]] MPENote changedNote) {} +void MPEInstrument::Listener::notePitchbendChanged ([[maybe_unused]] MPENote changedNote) {} +void MPEInstrument::Listener::noteTimbreChanged ([[maybe_unused]] MPENote changedNote) {} +void MPEInstrument::Listener::noteKeyStateChanged ([[maybe_unused]] MPENote changedNote) {} +void MPEInstrument::Listener::noteReleased ([[maybe_unused]] MPENote finishedNote) {} +void MPEInstrument::Listener::zoneLayoutChanged() {} //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS -class MPEInstrumentTests : public UnitTest +class MPEInstrumentTests final : public UnitTest { public: MPEInstrumentTests() @@ -811,6 +881,7 @@ class MPEInstrumentTests : public UnitTest testLayout.setUpperZone (6); } + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262) void runTest() override { beginTest ("initial zone layout"); @@ -1834,12 +1905,8 @@ class MPEInstrumentTests : public UnitTest buffer.addEvents (MPEMessages::setLowerZone (5), 0, -1, 0); buffer.addEvents (MPEMessages::setUpperZone (6), 0, -1, 0); - MidiBuffer::Iterator iter (buffer); - MidiMessage message; - int samplePosition; // not actually used, so no need to initialise. - - while (iter.getNextEvent (message, samplePosition)) - test.processNextMidiEvent (message); + for (const auto metadata : buffer) + test.processNextMidiEvent (metadata.getMessage()); expect (test.getZoneLayout().getLowerZone().isActive()); expect (test.getZoneLayout().getUpperZone().isActive()); @@ -2127,14 +2194,15 @@ class MPEInstrumentTests : public UnitTest } } } + JUCE_END_IGNORE_WARNINGS_MSVC private: //============================================================================== /* This mock class is used for unit testing whether the methods of MPEInstrument are called correctly. */ - class UnitTestInstrument : public MPEInstrument, - private MPEInstrument::Listener + class UnitTestInstrument final : public MPEInstrument, + private MPEInstrument::Listener { using Base = MPEInstrument; diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h index 7754759d..a397d38e 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEInstrument.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -38,10 +38,8 @@ namespace juce MPE. If you pass it a message, it will know what notes on what channels (if any) should be affected by that message. - The class has a Listener class with the three callbacks MPENoteAdded, - MPENoteChanged, and MPENoteFinished. Implement such a - Listener class to react to note changes and trigger some functionality for - your application that depends on the MPE note state. + The class has a Listener class that can be used to react to note and + state changes and trigger some functionality for your application. For example, you can use this class to write an MPE visualiser. If you want to write a real-time audio synth with MPE functionality, @@ -59,11 +57,14 @@ class JUCE_API MPEInstrument This will construct an MPE instrument with inactive lower and upper zones. - In order to process incoming MIDI, call setZoneLayout, define the layout - via MIDI RPN messages, or set the instrument to legacy mode. + In order to process incoming MIDI messages call setZoneLayout, use the MPEZoneLayout + constructor, define the layout via MIDI RPN messages, or set the instrument to legacy mode. */ MPEInstrument() noexcept; + /** Constructs an MPE instrument with the specified zone layout. */ + MPEInstrument (MPEZoneLayout layout); + /** Destructor. */ virtual ~MPEInstrument(); @@ -229,6 +230,9 @@ class JUCE_API MPEInstrument */ MPENote getNote (int midiChannel, int midiNoteNumber) const noexcept; + /** Returns the note with a given ID. */ + MPENote getNoteWithID (uint16 noteID) const noexcept; + /** Returns the most recent note that is playing on the given midiChannel (this will be the note which has received the most recent note-on without a corresponding note-off), if there is such a note. Otherwise, this returns an @@ -244,8 +248,8 @@ class JUCE_API MPEInstrument MPENote getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept; //============================================================================== - /** Derive from this class to be informed about any changes in the expressive - MIDI notes played by this instrument. + /** Derive from this class to be informed about any changes in the MPE notes played + by this instrument, and any changes to its zone layout. Note: This listener type receives its callbacks immediately, and not via the message thread (so you might be for example in the MIDI thread). @@ -261,12 +265,12 @@ class JUCE_API MPEInstrument /** Implement this callback to be informed whenever a new expressive MIDI note is triggered. */ - virtual void noteAdded (MPENote newNote) { ignoreUnused (newNote); } + virtual void noteAdded (MPENote newNote); /** Implement this callback to be informed whenever a currently playing MPE note's pressure value changes. */ - virtual void notePressureChanged (MPENote changedNote) { ignoreUnused (changedNote); } + virtual void notePressureChanged (MPENote changedNote); /** Implement this callback to be informed whenever a currently playing MPE note's pitchbend value changes. @@ -275,12 +279,12 @@ class JUCE_API MPEInstrument master channel pitchbend event, or if both occur simultaneously. Call MPENote::getFrequencyInHertz to get the effective note frequency. */ - virtual void notePitchbendChanged (MPENote changedNote) { ignoreUnused (changedNote); } + virtual void notePitchbendChanged (MPENote changedNote); /** Implement this callback to be informed whenever a currently playing MPE note's timbre value changes. */ - virtual void noteTimbreChanged (MPENote changedNote) { ignoreUnused (changedNote); } + virtual void noteTimbreChanged (MPENote 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 @@ -289,14 +293,19 @@ class JUCE_API MPEInstrument Note: If the key state changes to MPENote::off, noteReleased is called instead. */ - virtual void noteKeyStateChanged (MPENote changedNote) { ignoreUnused (changedNote); } + virtual void noteKeyStateChanged (MPENote 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) { ignoreUnused (finishedNote); } + virtual void noteReleased (MPENote finishedNote); + + /** Implement this callback to be informed whenever the MPE zone layout + or legacy mode settings of this instrument have been changed. + */ + virtual void zoneLayoutChanged(); }; //============================================================================== @@ -307,7 +316,9 @@ class JUCE_API MPEInstrument void removeListener (Listener* listenerToRemove); //============================================================================== - /** Puts the instrument into legacy mode. + /** Puts the instrument into legacy mode. If legacy mode is already enabled this method + does nothing. + As a side effect, this will discard all currently playing notes, and call noteReleased for all of them. @@ -360,9 +371,9 @@ class JUCE_API MPEInstrument struct LegacyMode { - bool isEnabled; + bool isEnabled = false; Range channelRange; - int pitchbendRange; + int pitchbendRange = 2; }; struct MPEDimension @@ -376,6 +387,8 @@ class JUCE_API MPEInstrument LegacyMode legacyMode; MPEDimension pitchbendDimension, pressureDimension, timbreDimension; + void resetLastReceivedValues(); + void updateDimension (int midiChannel, MPEDimension&, MPEValue); void updateDimensionMaster (bool, MPEDimension&, MPEValue); void updateDimensionForNote (MPENote&, MPEDimension&, MPEValue); diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp index f233f797..677ab1fb 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -111,7 +111,7 @@ MidiBuffer MPEMessages::setZoneLayout (MPEZoneLayout layout) //============================================================================== #if JUCE_UNIT_TESTS -class MPEMessagesTests : public UnitTest +class MPEMessagesTests final : public UnitTest { public: MPEMessagesTests() @@ -216,14 +216,11 @@ class MPEMessagesTests : public UnitTest void extractRawBinaryData (const MidiBuffer& midiBuffer, const uint8* bufferToCopyTo, std::size_t maxBytes) { std::size_t pos = 0; - MidiBuffer::Iterator iter (midiBuffer); - MidiMessage midiMessage; - int samplePosition; // Note: Not actually used, so no need to initialise. - while (iter.getNextEvent (midiMessage, samplePosition)) + for (const auto metadata : midiBuffer) { - const uint8* data = midiMessage.getRawData(); - std::size_t dataSize = (std::size_t) midiMessage.getRawDataSize(); + const uint8* data = metadata.data; + std::size_t dataSize = (std::size_t) metadata.numBytes; if (pos + dataSize > maxBytes) return; diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.h index 96357b62..993e8171 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEMessages.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.cpp index 792a3db4..9d9fece6 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -89,7 +89,7 @@ bool MPENote::operator!= (const MPENote& other) const noexcept //============================================================================== #if JUCE_UNIT_TESTS -class MPENoteTests : public UnitTest +class MPENoteTests final : public UnitTest { public: MPENoteTests() diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.h index 0f273514..db26da72 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPENote.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -115,7 +115,7 @@ struct JUCE_API MPENote */ MPEValue noteOnVelocity { MPEValue::minValue() }; - /** Current per-note pitchbend of the note (in units of MIDI pitchwheel + /** Current per-note pitchbend of the note (in units of MIDI pitchwheel position). This dimension can be modulated while the note sounds. Note: This value is not aware of the currently used pitchbend range, diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp index d891a210..e601daef 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,12 +25,10 @@ namespace juce MPESynthesiser::MPESynthesiser() { - MPEZoneLayout zoneLayout; - zoneLayout.setLowerZone (15); - setZoneLayout (zoneLayout); } -MPESynthesiser::MPESynthesiser (MPEInstrument* mpeInstrument) : MPESynthesiserBase (mpeInstrument) +MPESynthesiser::MPESynthesiser (MPEInstrument& mpeInstrument) + : MPESynthesiserBase (mpeInstrument) { } @@ -129,7 +127,7 @@ void MPESynthesiser::noteReleased (MPENote finishedNote) { auto* voice = voices.getUnchecked (i); - if (voice->isCurrentlyPlayingNote(finishedNote)) + if (voice->isCurrentlyPlayingNote (finishedNote)) stopVoice (voice, finishedNote, true); } } @@ -186,15 +184,19 @@ MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceF MPESynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase MPESynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase + // All major OSes use double-locking so this will be lock- and wait-free as long as stealLock is not + // contended. This is always the case if you do not call findVoiceToSteal on multiple threads at + // the same time. + const ScopedLock sl (stealLock); + // this is a list of voices we can steal, sorted by how long they've been running - Array usableVoices; - usableVoices.ensureStorageAllocated (voices.size()); + usableVoicesToStealArray.clear(); for (auto* voice : voices) { jassert (voice->isActive()); // We wouldn't be here otherwise - usableVoices.add (voice); + usableVoicesToStealArray.add (voice); // NB: Using a functor rather than a lambda here due to scare-stories about // compilers generating code containing heap allocations.. @@ -203,7 +205,7 @@ MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceF bool operator() (const MPESynthesiserVoice* a, const MPESynthesiserVoice* b) const noexcept { return a->noteOnTime < b->noteOnTime; } }; - std::sort (usableVoices.begin(), usableVoices.end(), Sorter()); + std::sort (usableVoicesToStealArray.begin(), usableVoicesToStealArray.end(), Sorter()); if (! voice->isPlayingButReleased()) // Don't protect released notes { @@ -224,24 +226,24 @@ MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceF // If we want to re-use the voice to trigger a new note, // then The oldest note that's playing the same note number is ideal. if (noteToStealVoiceFor.isValid()) - for (auto* voice : usableVoices) + for (auto* voice : usableVoicesToStealArray) if (voice->getCurrentlyPlayingNote().initialNote == noteToStealVoiceFor.initialNote) return voice; // Oldest voice that has been released (no finger on it and not held by sustain pedal) - for (auto* voice : usableVoices) + for (auto* voice : usableVoicesToStealArray) if (voice != low && voice != top && voice->isPlayingButReleased()) return voice; // Oldest voice that doesn't have a finger on it: - for (auto* voice : usableVoices) + for (auto* voice : usableVoicesToStealArray) if (voice != low && voice != top && voice->getCurrentlyPlayingNote().keyState != MPENote::keyDown && voice->getCurrentlyPlayingNote().keyState != MPENote::keyDownAndSustained) return voice; // Oldest voice that isn't protected - for (auto* voice : usableVoices) + for (auto* voice : usableVoicesToStealArray) if (voice != low && voice != top) return voice; @@ -258,9 +260,16 @@ MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceF //============================================================================== void MPESynthesiser::addVoice (MPESynthesiserVoice* const newVoice) { - const ScopedLock sl (voicesLock); - newVoice->setCurrentSampleRate (getSampleRate()); - voices.add (newVoice); + { + const ScopedLock sl (voicesLock); + newVoice->setCurrentSampleRate (getSampleRate()); + voices.add (newVoice); + } + + { + const ScopedLock sl (stealLock); + usableVoicesToStealArray.ensureStorageAllocated (voices.size() + 1); + } } void MPESynthesiser::clearVoices() @@ -305,11 +314,16 @@ 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->currentlyPlayingNote.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number + voice->currentlyPlayingNote.keyState = MPENote::off; + voice->noteStopped (allowTailOff); + } } // finally make sure the MPE Instrument also doesn't have any notes anymore. - instrument->releaseAllNotes(); + instrument.releaseAllNotes(); } //============================================================================== diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h index 1fbd0b05..70539192 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -65,11 +65,10 @@ class JUCE_API MPESynthesiser : public MPESynthesiserBase /** Constructor to pass to the synthesiser a custom MPEInstrument object to handle the MPE note state, MIDI channel assignment etc. (in case you need custom logic for this that goes beyond MIDI and MPE). - The synthesiser will take ownership of this object. @see MPESynthesiserBase, MPEInstrument */ - MPESynthesiser (MPEInstrument* instrumentToUse); + MPESynthesiser (MPEInstrument& instrumentToUse); /** Destructor. */ ~MPESynthesiser() override; @@ -303,8 +302,10 @@ class JUCE_API MPESynthesiser : public MPESynthesiserBase private: //============================================================================== - bool shouldStealVoices = false; + std::atomic shouldStealVoices { false }; uint32 lastNoteOnCounter = 0; + mutable CriticalSection stealLock; + mutable Array usableVoicesToStealArray; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiser) }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp index 0a61dfc6..45641d1b 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -24,80 +24,79 @@ namespace juce { MPESynthesiserBase::MPESynthesiserBase() - : instrument (new MPEInstrument) + : instrument (defaultInstrument) { - instrument->addListener (this); + instrument.addListener (this); } -MPESynthesiserBase::MPESynthesiserBase (MPEInstrument* inst) +MPESynthesiserBase::MPESynthesiserBase (MPEInstrument& inst) : instrument (inst) { - jassert (instrument != nullptr); - instrument->addListener (this); + instrument.addListener (this); } //============================================================================== MPEZoneLayout MPESynthesiserBase::getZoneLayout() const noexcept { - return instrument->getZoneLayout(); + return instrument.getZoneLayout(); } void MPESynthesiserBase::setZoneLayout (MPEZoneLayout newLayout) { - instrument->setZoneLayout (newLayout); + instrument.setZoneLayout (newLayout); } //============================================================================== void MPESynthesiserBase::enableLegacyMode (int pitchbendRange, Range channelRange) { - instrument->enableLegacyMode (pitchbendRange, channelRange); + instrument.enableLegacyMode (pitchbendRange, channelRange); } bool MPESynthesiserBase::isLegacyModeEnabled() const noexcept { - return instrument->isLegacyModeEnabled(); + return instrument.isLegacyModeEnabled(); } Range MPESynthesiserBase::getLegacyModeChannelRange() const noexcept { - return instrument->getLegacyModeChannelRange(); + return instrument.getLegacyModeChannelRange(); } void MPESynthesiserBase::setLegacyModeChannelRange (Range channelRange) { - instrument->setLegacyModeChannelRange (channelRange); + instrument.setLegacyModeChannelRange (channelRange); } int MPESynthesiserBase::getLegacyModePitchbendRange() const noexcept { - return instrument->getLegacyModePitchbendRange(); + return instrument.getLegacyModePitchbendRange(); } void MPESynthesiserBase::setLegacyModePitchbendRange (int pitchbendRange) { - instrument->setLegacyModePitchbendRange (pitchbendRange); + instrument.setLegacyModePitchbendRange (pitchbendRange); } //============================================================================== void MPESynthesiserBase::setPressureTrackingMode (TrackingMode modeToUse) { - instrument->setPressureTrackingMode (modeToUse); + instrument.setPressureTrackingMode (modeToUse); } void MPESynthesiserBase::setPitchbendTrackingMode (TrackingMode modeToUse) { - instrument->setPitchbendTrackingMode (modeToUse); + instrument.setPitchbendTrackingMode (modeToUse); } void MPESynthesiserBase::setTimbreTrackingMode (TrackingMode modeToUse) { - instrument->setTimbreTrackingMode (modeToUse); + instrument.setTimbreTrackingMode (modeToUse); } //============================================================================== void MPESynthesiserBase::handleMidiEvent (const MidiMessage& m) { - instrument->processNextMidiEvent (m); + instrument.processNextMidiEvent (m); } //============================================================================== @@ -108,50 +107,34 @@ void MPESynthesiserBase::renderNextBlock (AudioBuffer& outputAudio, int numSamples) { // you must set the sample rate before using this! - jassert (sampleRate != 0); - - MidiBuffer::Iterator midiIterator (inputMidi); - midiIterator.setNextSamplePosition (startSample); - - bool firstEvent = true; - int midiEventPos; - MidiMessage m; + jassert (! approximatelyEqual (sampleRate, 0.0)); const ScopedLock sl (noteStateLock); - while (numSamples > 0) - { - if (! midiIterator.getNextEvent (m, midiEventPos)) - { - renderNextSubBlock (outputAudio, startSample, numSamples); - return; - } + auto prevSample = startSample; + const auto endSample = startSample + numSamples; - auto samplesToNextMidiMessage = midiEventPos - startSample; + for (auto it = inputMidi.findNextSamplePosition (startSample); it != inputMidi.cend(); ++it) + { + const auto metadata = *it; - if (samplesToNextMidiMessage >= numSamples) - { - renderNextSubBlock (outputAudio, startSample, numSamples); - handleMidiEvent (m); + if (metadata.samplePosition >= endSample) break; - } - if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize)) + const auto smallBlockAllowed = (prevSample == startSample && ! subBlockSubdivisionIsStrict); + const auto thisBlockSize = smallBlockAllowed ? 1 : minimumSubBlockSize; + + if (metadata.samplePosition >= prevSample + thisBlockSize) { - handleMidiEvent (m); - continue; + renderNextSubBlock (outputAudio, prevSample, metadata.samplePosition - prevSample); + prevSample = metadata.samplePosition; } - firstEvent = false; - - renderNextSubBlock (outputAudio, startSample, samplesToNextMidiMessage); - handleMidiEvent (m); - startSample += samplesToNextMidiMessage; - numSamples -= samplesToNextMidiMessage; + handleMidiEvent (metadata.getMessage()); } - while (midiIterator.getNextEvent (m, midiEventPos)) - handleMidiEvent (m); + if (prevSample < endSample) + renderNextSubBlock (outputAudio, prevSample, endSample - prevSample); } // explicit instantiation for supported float types: @@ -161,10 +144,10 @@ template void MPESynthesiserBase::renderNextBlock (AudioBuffer&, //============================================================================== void MPESynthesiserBase::setCurrentPlaybackSampleRate (const double newRate) { - if (sampleRate != newRate) + if (! approximatelyEqual (sampleRate, newRate)) { const ScopedLock sl (noteStateLock); - instrument->releaseAllNotes(); + instrument.releaseAllNotes(); sampleRate = newRate; } } @@ -177,4 +160,216 @@ void MPESynthesiserBase::setMinimumRenderingSubdivisionSize (int numSamples, boo subBlockSubdivisionIsStrict = shouldBeStrict; } +#if JUCE_UNIT_TESTS + +namespace +{ + class MpeSynthesiserBaseTests final : public UnitTest + { + enum class CallbackKind { process, midi }; + + struct StartAndLength + { + StartAndLength (int s, int l) : start (s), length (l) {} + + int start = 0; + int length = 0; + + std::tuple tie() const noexcept { return std::tie (start, length); } + + bool operator== (const StartAndLength& other) const noexcept { return tie() == other.tie(); } + bool operator!= (const StartAndLength& other) const noexcept { return tie() != other.tie(); } + + bool operator< (const StartAndLength& other) const noexcept { return tie() < other.tie(); } + }; + + struct Events + { + std::vector blocks; + std::vector messages; + std::vector order; + }; + + class MockSynthesiser final : public MPESynthesiserBase + { + public: + Events events; + + void handleMidiEvent (const MidiMessage& m) override + { + events.messages.emplace_back (m); + events.order.emplace_back (CallbackKind::midi); + } + + private: + using MPESynthesiserBase::renderNextSubBlock; + + void renderNextSubBlock (AudioBuffer&, + int startSample, + int numSamples) override + { + events.blocks.push_back ({ startSample, numSamples }); + events.order.emplace_back (CallbackKind::process); + } + }; + + static MidiBuffer makeTestBuffer (const int bufferLength) + { + MidiBuffer result; + + for (int i = 0; i != bufferLength; ++i) + result.addEvent ({}, i); + + return result; + } + + public: + MpeSynthesiserBaseTests() + : UnitTest ("MPE Synthesiser Base", UnitTestCategories::midi) {} + + void runTest() override + { + const auto sumBlockLengths = [] (const std::vector& b) + { + const auto addBlock = [] (int acc, const StartAndLength& info) { return acc + info.length; }; + return std::accumulate (b.begin(), b.end(), 0, addBlock); + }; + + beginTest ("Rendering sparse subblocks works"); + { + const int blockSize = 512; + const auto midi = [&] { MidiBuffer b; b.addEvent ({}, blockSize / 2); return b; }(); + AudioBuffer audio (1, blockSize); + + const auto processEvents = [&] (int start, int length) + { + MockSynthesiser synth; + synth.setMinimumRenderingSubdivisionSize (1, false); + synth.setCurrentPlaybackSampleRate (44100); + synth.renderNextBlock (audio, midi, start, length); + return synth.events; + }; + + { + const auto e = processEvents (0, blockSize); + expect (e.blocks.size() == 2); + expect (e.messages.size() == 1); + expect (std::is_sorted (e.blocks.begin(), e.blocks.end())); + expect (sumBlockLengths (e.blocks) == blockSize); + expect (e.order == std::vector { CallbackKind::process, + CallbackKind::midi, + CallbackKind::process }); + } + } + + beginTest ("Rendering subblocks processes only contained midi events"); + { + const int blockSize = 512; + const auto midi = makeTestBuffer (blockSize); + AudioBuffer audio (1, blockSize); + + const auto processEvents = [&] (int start, int length) + { + MockSynthesiser synth; + synth.setMinimumRenderingSubdivisionSize (1, false); + synth.setCurrentPlaybackSampleRate (44100); + synth.renderNextBlock (audio, midi, start, length); + return synth.events; + }; + + { + const int subBlockLength = 0; + const auto e = processEvents (0, subBlockLength); + expect (e.blocks.size() == 0); + expect (e.messages.size() == 0); + expect (std::is_sorted (e.blocks.begin(), e.blocks.end())); + expect (sumBlockLengths (e.blocks) == subBlockLength); + } + + { + const int subBlockLength = 0; + const auto e = processEvents (1, subBlockLength); + expect (e.blocks.size() == 0); + expect (e.messages.size() == 0); + expect (std::is_sorted (e.blocks.begin(), e.blocks.end())); + expect (sumBlockLengths (e.blocks) == subBlockLength); + } + + { + const int subBlockLength = 1; + const auto e = processEvents (1, subBlockLength); + expect (e.blocks.size() == 1); + expect (e.messages.size() == 1); + expect (std::is_sorted (e.blocks.begin(), e.blocks.end())); + expect (sumBlockLengths (e.blocks) == subBlockLength); + expect (e.order == std::vector { CallbackKind::midi, + CallbackKind::process }); + } + + { + const auto e = processEvents (0, blockSize); + expect (e.blocks.size() == blockSize); + expect (e.messages.size() == blockSize); + expect (std::is_sorted (e.blocks.begin(), e.blocks.end())); + expect (sumBlockLengths (e.blocks) == blockSize); + expect (e.order.front() == CallbackKind::midi); + } + } + + beginTest ("Subblocks respect their minimum size"); + { + const int blockSize = 512; + const auto midi = makeTestBuffer (blockSize); + AudioBuffer audio (1, blockSize); + + const auto blockLengthsAreValid = [] (const std::vector& info, int minLength, bool strict) + { + if (info.size() <= 1) + return true; + + const auto lengthIsValid = [&] (const StartAndLength& s) { return minLength <= s.length; }; + const auto begin = strict ? info.begin() : std::next (info.begin()); + // The final block is allowed to be shorter than the minLength + return std::all_of (begin, std::prev (info.end()), lengthIsValid); + }; + + for (auto strict : { false, true }) + { + for (auto subblockSize : { 1, 16, 32, 64, 1024 }) + { + MockSynthesiser synth; + synth.setMinimumRenderingSubdivisionSize (subblockSize, strict); + synth.setCurrentPlaybackSampleRate (44100); + synth.renderNextBlock (audio, midi, 0, blockSize); + + const auto& e = synth.events; + expectWithinAbsoluteError (float (e.blocks.size()), + std::ceil ((float) blockSize / (float) subblockSize), + 1.0f); + expect (e.messages.size() == blockSize); + expect (std::is_sorted (e.blocks.begin(), e.blocks.end())); + expect (sumBlockLengths (e.blocks) == blockSize); + expect (blockLengthsAreValid (e.blocks, subblockSize, strict)); + } + } + + { + MockSynthesiser synth; + synth.setMinimumRenderingSubdivisionSize (32, true); + synth.setCurrentPlaybackSampleRate (44100); + synth.renderNextBlock (audio, MidiBuffer{}, 0, 16); + + expect (synth.events.blocks == std::vector { { 0, 16 } }); + expect (synth.events.order == std::vector { CallbackKind::process }); + expect (synth.events.messages.empty()); + } + } + } + }; + + MpeSynthesiserBaseTests mpeSynthesiserBaseTests; +} + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h index fc6723ce..a8924d14 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -52,13 +52,12 @@ struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener /** Constructor. - If you use this constructor, the synthesiser will take ownership of the - provided instrument object, and will use it internally to handle the - MPE note state logic. + If you use this constructor, the synthesiser will use the provided instrument + object to handle the MPE note state logic. This is useful if you want to use an instance of your own class derived from MPEInstrument for the MPE logic. */ - MPESynthesiserBase (MPEInstrument* instrument); + MPESynthesiserBase (MPEInstrument& instrument); //============================================================================== /** Returns the synthesiser's internal MPE zone layout. @@ -88,9 +87,14 @@ struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener Call this to make sound. This will chop up the AudioBuffer into subBlock pieces separated by events in the MIDI buffer, and then call - processNextSubBlock on each one of them. In between you will get calls + renderNextSubBlock on each one of them. In between you will get calls to noteAdded/Changed/Finished, where you can update parameters that depend on those notes to use for your audio rendering. + + @param outputAudio Buffer into which audio will be rendered + @param inputMidi MIDI events to process + @param startSample The first sample to process in both buffers + @param numSamples The number of samples to process */ template void renderNextBlock (AudioBuffer& outputAudio, @@ -195,10 +199,12 @@ struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener protected: //============================================================================== /** @internal */ - std::unique_ptr instrument; + MPEInstrument& instrument; private: //============================================================================== + MPEInstrument defaultInstrument { MPEZone (MPEZone::Type::lower, 15) }; + CriticalSection noteStateLock; double sampleRate = 0.0; int minimumSubBlockSize = 32; diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp index 54fc02ba..31ce0394 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h index 93b89750..6a3a8cbb 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp index 8ff2bdd6..068fc923 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -52,25 +52,25 @@ int MPEChannelAssigner::findMidiChannelForNewNote (int noteNumber) noexcept if (numChannels <= 1) return firstChannel; - for (auto ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement) + for (int ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement) { - if (midiChannels[ch].isFree() && midiChannels[ch].lastNotePlayed == noteNumber) + if (midiChannels[(size_t) ch].isFree() && midiChannels[(size_t) ch].lastNotePlayed == noteNumber) { midiChannelLastAssigned = ch; - midiChannels[ch].notes.add (noteNumber); + midiChannels[(size_t) ch].notes.add (noteNumber); return ch; } } - for (auto ch = midiChannelLastAssigned + channelIncrement; ; ch += channelIncrement) + for (int ch = midiChannelLastAssigned + channelIncrement; ; ch += channelIncrement) { if (ch == lastChannel + channelIncrement) // loop wrap-around ch = firstChannel; - if (midiChannels[ch].isFree()) + if (midiChannels[(size_t) ch].isFree()) { midiChannelLastAssigned = ch; - midiChannels[ch].notes.add (noteNumber); + midiChannels[(size_t) ch].notes.add (noteNumber); return ch; } @@ -79,11 +79,21 @@ int MPEChannelAssigner::findMidiChannelForNewNote (int noteNumber) noexcept } midiChannelLastAssigned = findMidiChannelPlayingClosestNonequalNote (noteNumber); - midiChannels[midiChannelLastAssigned].notes.add (noteNumber); + midiChannels[(size_t) midiChannelLastAssigned].notes.add (noteNumber); return midiChannelLastAssigned; } +int MPEChannelAssigner::findMidiChannelForExistingNote (int noteNumber) noexcept +{ + const auto iter = std::find_if (midiChannels.cbegin(), midiChannels.cend(), [&] (auto& ch) + { + return std::find (ch.notes.begin(), ch.notes.end(), noteNumber) != ch.notes.end(); + }); + + return iter != midiChannels.cend() ? (int) std::distance (midiChannels.cbegin(), iter) : -1; +} + void MPEChannelAssigner::noteOff (int noteNumber, int midiChannel) { const auto removeNote = [] (MidiChannel& ch, int noteNum) @@ -97,9 +107,9 @@ void MPEChannelAssigner::noteOff (int noteNumber, int midiChannel) return false; }; - if (midiChannel >= 0 && midiChannel < 17) + if (midiChannel >= 0 && midiChannel <= 16) { - removeNote (midiChannels[midiChannel], noteNumber); + removeNote (midiChannels[(size_t) midiChannel], noteNumber); return; } @@ -126,9 +136,9 @@ int MPEChannelAssigner::findMidiChannelPlayingClosestNonequalNote (int noteNumbe auto channelWithClosestNote = firstChannel; int closestNoteDistance = 127; - for (auto ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement) + for (int ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement) { - for (auto note : midiChannels[ch].notes) + for (auto note : midiChannels[(size_t) ch].notes) { auto noteDistance = std::abs (note - noteNumber); @@ -274,7 +284,7 @@ void MPEChannelRemapper::zeroArrays() //============================================================================== #if JUCE_UNIT_TESTS -struct MPEUtilsUnitTests : public UnitTest +struct MPEUtilsUnitTests final : public UnitTest { MPEUtilsUnitTests() : UnitTest ("MPE Utilities", UnitTestCategories::midi) @@ -296,24 +306,35 @@ struct MPEUtilsUnitTests : public UnitTest // check that channels are assigned in correct order int noteNum = 60; for (int ch = 2; ch <= 16; ++ch) - expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum++), ch); + { + expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum), ch); + expectEquals (channelAssigner.findMidiChannelForExistingNote (noteNum), ch); + + ++noteNum; + } // check that note-offs are processed channelAssigner.noteOff (60); expectEquals (channelAssigner.findMidiChannelForNewNote (60), 2); + expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 2); channelAssigner.noteOff (61); expectEquals (channelAssigner.findMidiChannelForNewNote (61), 3); + expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 3); // check that assigned channel was last to play note channelAssigner.noteOff (65); channelAssigner.noteOff (66); expectEquals (channelAssigner.findMidiChannelForNewNote (66), 8); expectEquals (channelAssigner.findMidiChannelForNewNote (65), 7); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 8); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 7); // find closest channel playing nonequal note expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 2); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 2); // all notes off channelAssigner.allNotesOff(); @@ -323,10 +344,16 @@ struct MPEUtilsUnitTests : public UnitTest expectEquals (channelAssigner.findMidiChannelForNewNote (65), 7); expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 2); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 8); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 7); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 2); // normal assignment expectEquals (channelAssigner.findMidiChannelForNewNote (101), 3); expectEquals (channelAssigner.findMidiChannelForNewNote (20), 4); + expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 3); + expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 4); } // upper @@ -339,24 +366,35 @@ struct MPEUtilsUnitTests : public UnitTest // check that channels are assigned in correct order int noteNum = 60; for (int ch = 15; ch >= 1; --ch) - expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum++), ch); + { + expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum), ch); + expectEquals (channelAssigner.findMidiChannelForExistingNote (noteNum), ch); + + ++noteNum; + } // check that note-offs are processed channelAssigner.noteOff (60); expectEquals (channelAssigner.findMidiChannelForNewNote (60), 15); + expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 15); channelAssigner.noteOff (61); expectEquals (channelAssigner.findMidiChannelForNewNote (61), 14); + expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 14); // check that assigned channel was last to play note channelAssigner.noteOff (65); channelAssigner.noteOff (66); expectEquals (channelAssigner.findMidiChannelForNewNote (66), 9); expectEquals (channelAssigner.findMidiChannelForNewNote (65), 10); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 9); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 10); // find closest channel playing nonequal note expectEquals (channelAssigner.findMidiChannelForNewNote (80), 1); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 15); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 1); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 15); // all notes off channelAssigner.allNotesOff(); @@ -366,10 +404,16 @@ struct MPEUtilsUnitTests : public UnitTest expectEquals (channelAssigner.findMidiChannelForNewNote (65), 10); expectEquals (channelAssigner.findMidiChannelForNewNote (80), 1); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 15); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 9); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 10); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 1); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 15); // normal assignment expectEquals (channelAssigner.findMidiChannelForNewNote (101), 14); expectEquals (channelAssigner.findMidiChannelForNewNote (20), 13); + expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 14); + expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 13); } // legacy @@ -379,24 +423,35 @@ struct MPEUtilsUnitTests : public UnitTest // check that channels are assigned in correct order int noteNum = 60; for (int ch = 1; ch <= 16; ++ch) - expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum++), ch); + { + expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum), ch); + expectEquals (channelAssigner.findMidiChannelForExistingNote (noteNum), ch); + + ++noteNum; + } // check that note-offs are processed channelAssigner.noteOff (60); expectEquals (channelAssigner.findMidiChannelForNewNote (60), 1); + expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 1); channelAssigner.noteOff (61); expectEquals (channelAssigner.findMidiChannelForNewNote (61), 2); + expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 2); // check that assigned channel was last to play note channelAssigner.noteOff (65); channelAssigner.noteOff (66); expectEquals (channelAssigner.findMidiChannelForNewNote (66), 7); expectEquals (channelAssigner.findMidiChannelForNewNote (65), 6); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 7); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 6); // find closest channel playing nonequal note expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 1); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 1); // all notes off channelAssigner.allNotesOff(); @@ -406,10 +461,16 @@ struct MPEUtilsUnitTests : public UnitTest expectEquals (channelAssigner.findMidiChannelForNewNote (65), 6); expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16); expectEquals (channelAssigner.findMidiChannelForNewNote (55), 1); + expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 7); + expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 6); + expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16); + expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 1); // normal assignment expectEquals (channelAssigner.findMidiChannelForNewNote (101), 2); expectEquals (channelAssigner.findMidiChannelForNewNote (20), 3); + expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 2); + expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 3); } } diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h index ad4c741e..7dc3a555 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEUtils.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -63,6 +63,11 @@ class MPEChannelAssigner */ int findMidiChannelForNewNote (int noteNumber) noexcept; + /** If a note has been added using findMidiChannelForNewNote() this will return the channel + to which it was assigned, otherwise it will return -1. + */ + int findMidiChannelForExistingNote (int initialNoteOnNumber) noexcept; + /** 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. @@ -86,7 +91,7 @@ class MPEChannelAssigner int lastNotePlayed = -1; bool isFree() const noexcept { return notes.isEmpty(); } }; - MidiChannel midiChannels[17]; + std::array midiChannels; //============================================================================== int findMidiChannelPlayingClosestNonequalNote (int noteNumber) noexcept; diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp index 6d23451d..fe00a54d 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -43,6 +43,18 @@ MPEValue MPEValue::from14BitInt (int value) noexcept return { value }; } +MPEValue MPEValue::fromUnsignedFloat (float value) noexcept +{ + jassert (0.0f <= value && value <= 1.0f); + return { roundToInt (value * 16383.0f) }; +} + +MPEValue MPEValue::fromSignedFloat (float value) noexcept +{ + jassert (-1.0f <= value && value <= 1.0f); + return { roundToInt (((value + 1.0f) * 16383.0f) / 2.0f) }; +} + //============================================================================== MPEValue MPEValue::minValue() noexcept { return MPEValue::from7BitInt (0); } MPEValue MPEValue::centreValue() noexcept { return MPEValue::from7BitInt (64); } @@ -87,7 +99,7 @@ bool MPEValue::operator!= (const MPEValue& other) const noexcept //============================================================================== #if JUCE_UNIT_TESTS -class MPEValueTests : public UnitTest +class MPEValueTests final : public UnitTest { public: MPEValueTests() @@ -121,26 +133,34 @@ class MPEValueTests : public UnitTest beginTest ("zero/minimum value"); { - expectValuesConsistent (MPEValue::from7BitInt (0), 0, 0, -1.0f, 0.0f); - expectValuesConsistent (MPEValue::from14BitInt (0), 0, 0, -1.0f, 0.0f); + expectValuesConsistent (MPEValue::from7BitInt (0), 0, 0, -1.0f, 0.0f); + expectValuesConsistent (MPEValue::from14BitInt (0), 0, 0, -1.0f, 0.0f); + expectValuesConsistent (MPEValue::fromUnsignedFloat (0.0f), 0, 0, -1.0f, 0.0f); + expectValuesConsistent (MPEValue::fromSignedFloat (-1.0f), 0, 0, -1.0f, 0.0f); } beginTest ("maximum value"); { - expectValuesConsistent (MPEValue::from7BitInt (127), 127, 16383, 1.0f, 1.0f); - expectValuesConsistent (MPEValue::from14BitInt (16383), 127, 16383, 1.0f, 1.0f); + expectValuesConsistent (MPEValue::from7BitInt (127), 127, 16383, 1.0f, 1.0f); + expectValuesConsistent (MPEValue::from14BitInt (16383), 127, 16383, 1.0f, 1.0f); + expectValuesConsistent (MPEValue::fromUnsignedFloat (1.0f), 127, 16383, 1.0f, 1.0f); + expectValuesConsistent (MPEValue::fromSignedFloat (1.0f), 127, 16383, 1.0f, 1.0f); } beginTest ("centre value"); { - expectValuesConsistent (MPEValue::from7BitInt (64), 64, 8192, 0.0f, 0.5f); - expectValuesConsistent (MPEValue::from14BitInt (8192), 64, 8192, 0.0f, 0.5f); + expectValuesConsistent (MPEValue::from7BitInt (64), 64, 8192, 0.0f, 0.5f); + expectValuesConsistent (MPEValue::from14BitInt (8192), 64, 8192, 0.0f, 0.5f); + expectValuesConsistent (MPEValue::fromUnsignedFloat (0.5f), 64, 8192, 0.0f, 0.5f); + expectValuesConsistent (MPEValue::fromSignedFloat (0.0f), 64, 8192, 0.0f, 0.5f); } beginTest ("value halfway between min and centre"); { - expectValuesConsistent (MPEValue::from7BitInt (32), 32, 4096, -0.5f, 0.25f); - expectValuesConsistent (MPEValue::from14BitInt (4096), 32, 4096, -0.5f, 0.25f); + expectValuesConsistent (MPEValue::from7BitInt (32), 32, 4096, -0.5f, 0.25f); + expectValuesConsistent (MPEValue::from14BitInt (4096), 32, 4096, -0.5f, 0.25f); + expectValuesConsistent (MPEValue::fromUnsignedFloat (0.25f), 32, 4096, -0.5f, 0.25f); + expectValuesConsistent (MPEValue::fromSignedFloat (-0.5f), 32, 4096, -0.5f, 0.25f); } } diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.h index ad9d272c..45157ac4 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEValue.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -53,6 +53,12 @@ class JUCE_API MPEValue */ static MPEValue from14BitInt (int value) noexcept; + /** Constructs an MPEValue from a float between 0.0f and 1.0f. */ + static MPEValue fromUnsignedFloat (float value) noexcept; + + /** Constructs an MPEValue from a float between -1.0f and 1.0f. */ + static MPEValue fromSignedFloat (float value) noexcept; + /** Constructs an MPEValue corresponding to the centre value. */ static MPEValue centreValue() noexcept; diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp index aa10b3cc..417eccda 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,7 +23,17 @@ namespace juce { -MPEZoneLayout::MPEZoneLayout() noexcept {} +MPEZoneLayout::MPEZoneLayout (MPEZone lower, MPEZone upper) + : lowerZone (lower), upperZone (upper) +{ +} + +MPEZoneLayout::MPEZoneLayout (MPEZone zone) + : lowerZone (zone.isLowerZone() ? zone : MPEZone()), + upperZone (! zone.isLowerZone() ? zone : MPEZone()) +{ +} + MPEZoneLayout::MPEZoneLayout (const MPEZoneLayout& other) : lowerZone (other.lowerZone), @@ -54,9 +64,9 @@ void MPEZoneLayout::setZone (bool isLower, int numMemberChannels, int perNotePit checkAndLimitZoneParameters (0, 96, masterPitchbendRange); if (isLower) - lowerZone = { true, numMemberChannels, perNotePitchbendRange, masterPitchbendRange }; + lowerZone = { MPEZone::Type::lower, numMemberChannels, perNotePitchbendRange, masterPitchbendRange }; else - upperZone = { false, numMemberChannels, perNotePitchbendRange, masterPitchbendRange }; + upperZone = { MPEZone::Type::upper, numMemberChannels, perNotePitchbendRange, masterPitchbendRange }; if (numMemberChannels > 0) { @@ -86,8 +96,8 @@ void MPEZoneLayout::setUpperZone (int numMemberChannels, int perNotePitchbendRan void MPEZoneLayout::clearAllZones() { - lowerZone = { true, 0 }; - upperZone = { false, 0 }; + lowerZone = { MPEZone::Type::lower, 0 }; + upperZone = { MPEZone::Type::upper, 0 }; sendLayoutChangeMessage(); } @@ -98,14 +108,11 @@ void MPEZoneLayout::processNextMidiEvent (const MidiMessage& message) if (! message.isController()) return; - MidiRPNMessage rpn; - - if (rpnDetector.parseControllerMessage (message.getChannel(), + if (auto parsed = rpnDetector.tryParse (message.getChannel(), message.getControllerNumber(), - message.getControllerValue(), - rpn)) + message.getControllerValue())) { - processRpnMessage (rpn); + processRpnMessage (*parsed); } } @@ -128,7 +135,7 @@ void MPEZoneLayout::processZoneLayoutRpnMessage (MidiRPNMessage rpn) } } -void MPEZoneLayout::updateMasterPitchbend (Zone& zone, int value) +void MPEZoneLayout::updateMasterPitchbend (MPEZone& zone, int value) { if (zone.masterPitchbendRange != value) { @@ -138,7 +145,7 @@ void MPEZoneLayout::updateMasterPitchbend (Zone& zone, int value) } } -void MPEZoneLayout::updatePerNotePitchbendRange (Zone& zone, int value) +void MPEZoneLayout::updatePerNotePitchbendRange (MPEZone& zone, int value) { if (zone.perNotePitchbendRange != value) { @@ -169,12 +176,8 @@ void MPEZoneLayout::processPitchbendRangeRpnMessage (MidiRPNMessage rpn) void MPEZoneLayout::processNextMidiBuffer (const MidiBuffer& buffer) { - MidiBuffer::Iterator iter (buffer); - MidiMessage message; - int samplePosition; // not actually used, so no need to initialise. - - while (iter.getNextEvent (message, samplePosition)) - processNextMidiEvent (message); + for (const auto metadata : buffer) + processNextMidiEvent (metadata.getMessage()); } //============================================================================== @@ -210,7 +213,7 @@ void MPEZoneLayout::checkAndLimitZoneParameters (int minValue, int maxValue, //============================================================================== #if JUCE_UNIT_TESTS -class MPEZoneLayoutTests : public UnitTest +class MPEZoneLayoutTests final : public UnitTest { public: MPEZoneLayoutTests() @@ -378,6 +381,17 @@ class MPEZoneLayoutTests : public UnitTest expectEquals (layout.getLowerZone().numMemberChannels, 3); expectEquals (layout.getLowerZone().perNotePitchbendRange, 48); expectEquals (layout.getLowerZone().masterPitchbendRange, 2); + + const auto masterPitchBend = 0x0c; + layout.processNextMidiEvent ({ 0xb0, 0x64, 0x00 }); + layout.processNextMidiEvent ({ 0xb0, 0x06, masterPitchBend }); + + expectEquals (layout.getLowerZone().masterPitchbendRange, masterPitchBend); + + const auto newPitchBend = 0x0d; + layout.processNextMidiEvent ({ 0xb0, 0x06, newPitchBend }); + + expectEquals (layout.getLowerZone().masterPitchbendRange, newPitchBend); } } }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h index 32d1a2f5..46969b87 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,84 @@ namespace juce { +//============================================================================== +/** + This struct represents an MPE zone. + + It can either be a lower or an upper zone, where: + - A lower zone encompasses master channel 1 and an arbitrary number of ascending + MIDI channels, increasing from channel 2. + - An upper zone encompasses master channel 16 and an arbitrary number of descending + MIDI channels, decreasing from channel 15. + + It also defines a pitchbend range (in semitones) to be applied for per-note pitchbends and + master pitchbends, respectively. + + @tags{Audio} +*/ +struct MPEZone +{ + enum class Type { lower, upper }; + + MPEZone() = default; + + MPEZone (Type type, int memberChannels = 0, int perNotePitchbend = 48, int masterPitchbend = 2) + : zoneType (type), + numMemberChannels (memberChannels), + perNotePitchbendRange (perNotePitchbend), + masterPitchbendRange (masterPitchbend) + {} + + bool isLowerZone() const noexcept { return zoneType == Type::lower; } + bool isUpperZone() const noexcept { return zoneType == Type::upper; } + + bool isActive() const noexcept { return numMemberChannels > 0; } + + int getMasterChannel() const noexcept { return isLowerZone() ? lowerZoneMasterChannel : upperZoneMasterChannel; } + int getFirstMemberChannel() const noexcept { return isLowerZone() ? lowerZoneMasterChannel + 1 : upperZoneMasterChannel - 1; } + int getLastMemberChannel() const noexcept { return isLowerZone() ? (lowerZoneMasterChannel + numMemberChannels) + : (upperZoneMasterChannel - numMemberChannels); } + + bool isUsingChannelAsMemberChannel (int channel) const noexcept + { + return isLowerZone() ? (lowerZoneMasterChannel < channel && channel <= getLastMemberChannel()) + : (channel < upperZoneMasterChannel && getLastMemberChannel() <= channel); + } + + bool isUsing (int channel) const noexcept + { + return isUsingChannelAsMemberChannel (channel) || channel == getMasterChannel(); + } + + static auto tie (const MPEZone& z) + { + return std::tie (z.zoneType, + z.numMemberChannels, + z.perNotePitchbendRange, + z.masterPitchbendRange); + } + + bool operator== (const MPEZone& other) const + { + return tie (*this) == tie (other); + } + + bool operator!= (const MPEZone& other) const + { + return tie (*this) != tie (other); + } + + //============================================================================== + static constexpr int lowerZoneMasterChannel = 1, + upperZoneMasterChannel = 16; + + Type zoneType = Type::lower; + + int numMemberChannels = 0; + int perNotePitchbendRange = 48; + int masterPitchbendRange = 2; +}; + //============================================================================== /** This class represents the current MPE zone layout of a device capable of handling MPE. @@ -44,89 +122,28 @@ namespace juce class JUCE_API MPEZoneLayout { public: - /** Default constructor. - - This will create a layout with inactive lower and upper zones, representing - a device with MPE mode disabled. + //============================================================================== + /** Creates a layout with inactive upper and lower zones. */ + MPEZoneLayout() = default; - You can set the lower or upper MPE zones using the setZone() method. + /** Creates a layout with the given upper and lower zones. */ + MPEZoneLayout (MPEZone lower, MPEZone upper); - @see setZone - */ - MPEZoneLayout() noexcept; + /** Creates a layout with a single upper or lower zone, leaving the other zone uninitialised. */ + MPEZoneLayout (MPEZone singleZone); - /** Copy constuctor. - This will not copy the listeners registered to the MPEZoneLayout. - */ MPEZoneLayout (const MPEZoneLayout& other); - - /** Copy assignment operator. - This will not copy the listeners registered to the MPEZoneLayout. - */ MPEZoneLayout& operator= (const MPEZoneLayout& other); - //============================================================================== - /** - This struct represents an MPE zone. - - It can either be a lower or an upper zone, where: - - A lower zone encompasses master channel 1 and an arbitrary number of ascending - MIDI channels, increasing from channel 2. - - An upper zone encompasses master channel 16 and an arbitrary number of descending - MIDI channels, decreasing from channel 15. - - It also defines a pitchbend range (in semitones) to be applied for per-note pitchbends and - master pitchbends, respectively. - */ - struct Zone - { - Zone (const Zone& other) = default; - - bool isLowerZone() const noexcept { return lowerZone; } - bool isUpperZone() const noexcept { return ! lowerZone; } - - bool isActive() const noexcept { return numMemberChannels > 0; } - - int getMasterChannel() const noexcept { return lowerZone ? 1 : 16; } - int getFirstMemberChannel() const noexcept { return lowerZone ? 2 : 15; } - int getLastMemberChannel() const noexcept { return lowerZone ? (1 + numMemberChannels) - : (16 - numMemberChannels); } - - bool isUsingChannelAsMemberChannel (int channel) const noexcept - { - return lowerZone ? (channel > 1 && channel <= 1 + numMemberChannels) - : (channel < 16 && channel >= 16 - numMemberChannels); - } - - bool isUsing (int channel) const noexcept - { - return isUsingChannelAsMemberChannel (channel) || channel == getMasterChannel(); - } + bool operator== (const MPEZoneLayout& other) const { return lowerZone == other.lowerZone && upperZone == other.upperZone; } + bool operator!= (const MPEZoneLayout& other) const { return ! operator== (other); } - bool operator== (const Zone& other) const noexcept { return lowerZone == other.lowerZone - && numMemberChannels == other.numMemberChannels - && perNotePitchbendRange == other.perNotePitchbendRange - && masterPitchbendRange == other.masterPitchbendRange; } - - bool operator!= (const Zone& other) const noexcept { return ! operator== (other); } - - int numMemberChannels; - int perNotePitchbendRange; - int masterPitchbendRange; - - private: - friend class MPEZoneLayout; - - Zone (bool lower, int memberChans = 0, int perNotePb = 48, int masterPb = 2) noexcept - : numMemberChannels (memberChans), - perNotePitchbendRange (perNotePb), - masterPitchbendRange (masterPb), - lowerZone (lower) - { - } + //============================================================================== + /** Returns a struct representing the lower MPE zone. */ + MPEZone getLowerZone() const noexcept { return lowerZone; } - bool lowerZone; - }; + /** Returns a struct representing the upper MPE zone. */ + MPEZone getUpperZone() const noexcept { return upperZone; } /** Sets the lower zone of this layout. */ void setLowerZone (int numMemberChannels = 0, @@ -138,17 +155,14 @@ class JUCE_API MPEZoneLayout int perNotePitchbendRange = 48, int masterPitchbendRange = 2) noexcept; - /** Returns a struct representing the lower MPE zone. */ - const Zone getLowerZone() const noexcept { return lowerZone; } - - /** Returns a struct representing the upper MPE zone. */ - const Zone getUpperZone() const noexcept { return upperZone; } - /** Clears the lower and upper zones of this layout, making them both inactive and disabling MPE mode. */ void clearAllZones(); + /** Returns true if either of the zones are active. */ + bool isActive() const { return lowerZone.isActive() || upperZone.isActive(); } + //============================================================================== /** Pass incoming MIDI messages to an object of this class if you want the zone layout to properly react to MPE RPN messages like an @@ -200,10 +214,14 @@ class JUCE_API MPEZoneLayout /** Removes a listener. */ void removeListener (Listener* const listenerToRemove) noexcept; + #ifndef DOXYGEN + using Zone = MPEZone; + #endif + private: //============================================================================== - Zone lowerZone { true, 0 }; - Zone upperZone { false, 0 }; + MPEZone lowerZone { MPEZone::Type::lower, 0 }; + MPEZone upperZone { MPEZone::Type::upper, 0 }; MidiRPNDetector rpnDetector; ListenerList listeners; @@ -215,8 +233,8 @@ class JUCE_API MPEZoneLayout void processZoneLayoutRpnMessage (MidiRPNMessage); void processPitchbendRangeRpnMessage (MidiRPNMessage); - void updateMasterPitchbend (Zone&, int); - void updatePerNotePitchbendRange (Zone&, int); + void updateMasterPitchbend (MPEZone&, int); + void updatePerNotePitchbendRange (MPEZone&, int); void sendLayoutChangeMessage(); void checkAndLimitZoneParameters (int, int, int&) noexcept; diff --git a/JuceLibraryCode/modules/juce_audio_basics/native/juce_AudioWorkgroup_mac.h b/JuceLibraryCode/modules/juce_audio_basics/native/juce_AudioWorkgroup_mac.h new file mode 100644 index 00000000..710bd735 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/native/juce_AudioWorkgroup_mac.h @@ -0,0 +1,35 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ +#if (defined (MAC_OS_VERSION_11_0) || defined (__IPHONE_14_0)) + #define JUCE_AUDIOWORKGROUP_TYPES_AVAILABLE 1 +#else + #define JUCE_AUDIOWORKGROUP_TYPES_AVAILABLE 0 +#endif + +#if JUCE_AUDIOWORKGROUP_TYPES_AVAILABLE + AudioWorkgroup makeRealAudioWorkgroup (os_workgroup_t handle); +#endif + +} diff --git a/JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioLayouts_mac.h b/JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioLayouts_mac.h new file mode 100644 index 00000000..e13dab20 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioLayouts_mac.h @@ -0,0 +1,344 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +#if ! defined (DOXYGEN) && (JUCE_MAC || JUCE_IOS) + +struct CoreAudioLayouts +{ + //============================================================================== + struct LayoutTagSpeakerList + { + AudioChannelLayoutTag tag; + AudioChannelSet::ChannelType channelTypes[16]; + }; + + //============================================================================== + // This list has been derived from https://pastebin.com/24dQ4BPJ + // Apple channel labels have been replaced by JUCE channel names + // This means that some layouts will be identical in JUCE but not in CoreAudio + + // In Apple's official definition the following tags exist with the same speaker layout and order + // even when *not* represented in JUCE channels + // kAudioChannelLayoutTag_Binaural = kAudioChannelLayoutTag_Stereo + // kAudioChannelLayoutTag_MPEG_5_0_B = kAudioChannelLayoutTag_Pentagonal + // kAudioChannelLayoutTag_ITU_2_2 = kAudioChannelLayoutTag_Quadraphonic + // kAudioChannelLayoutTag_AudioUnit_6_0 = kAudioChannelLayoutTag_Hexagonal + struct SpeakerLayoutTable : AudioChannelSet // save us some typing + { + template + static constexpr auto getArray (Items... items) + { + return std::array { { items... } }; + } + + static constexpr auto get() + { + using List = LayoutTagSpeakerList; + + return getArray (List { kAudioChannelLayoutTag_Mono, { centre } }, + List { kAudioChannelLayoutTag_Stereo, { left, right } }, + List { kAudioChannelLayoutTag_MPEG_3_0_A, { left, right, centre } }, + List { kAudioChannelLayoutTag_ITU_2_1, { left, right, centreSurround } }, + List { kAudioChannelLayoutTag_MPEG_4_0_A, { left, right, centre, centreSurround } }, + List { kAudioChannelLayoutTag_MPEG_5_0_A, { left, right, centre, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_MPEG_5_1_A, { left, right, centre, LFE, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_AudioUnit_6_0, { left, right, leftSurround, rightSurround, centre, centreSurround } }, + List { kAudioChannelLayoutTag_MPEG_6_1_A, { left, right, centre, LFE, leftSurround, rightSurround, centreSurround } }, + List { kAudioChannelLayoutTag_DTS_6_0_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_DTS_6_1_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_AudioUnit_7_0, { left, right, leftSurroundSide, rightSurroundSide, centre, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_AudioUnit_7_0_Front, { left, right, leftSurround, rightSurround, centre, leftCentre, rightCentre } }, + List { kAudioChannelLayoutTag_MPEG_7_1_C, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_MPEG_7_1_A, { left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre } }, + List { kAudioChannelLayoutTag_Ambisonic_B_Format, { ambisonicW, ambisonicX, ambisonicY, ambisonicZ } }, + List { kAudioChannelLayoutTag_Quadraphonic, { left, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_Pentagonal, { left, right, leftSurroundRear, rightSurroundRear, centre } }, + List { kAudioChannelLayoutTag_Hexagonal, { left, right, leftSurroundRear, rightSurroundRear, centre, centreSurround } }, + List { kAudioChannelLayoutTag_Octagonal, { left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight } }, + + #if defined (MAC_OS_VERSION_11_0) + List { kAudioChannelLayoutTag_Atmos_5_1_4, { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight } }, + List { kAudioChannelLayoutTag_Atmos_7_1_2, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight } }, + #endif + + #if defined (MAC_OS_X_VERSION_10_15) + List { kAudioChannelLayoutTag_Atmos_5_1_2, { left, right, centre, LFE, leftSurround, rightSurround, topSideLeft, topSideRight } }, + List { kAudioChannelLayoutTag_Atmos_7_1_4, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topRearLeft, topRearRight } }, + List { kAudioChannelLayoutTag_Atmos_9_1_6, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight } }, + #endif + + // More uncommon layouts... + List { kAudioChannelLayoutTag_StereoHeadphones, { left, right } }, + List { kAudioChannelLayoutTag_MatrixStereo, { left, right } }, + List { kAudioChannelLayoutTag_MidSide, { centre, discreteChannel0 } }, + List { kAudioChannelLayoutTag_XY, { ambisonicX, ambisonicY } }, + List { kAudioChannelLayoutTag_Binaural, { left, right } }, + List { kAudioChannelLayoutTag_Cube, { left, right, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight } }, + List { kAudioChannelLayoutTag_MPEG_3_0_B, { centre, left, right } }, + List { kAudioChannelLayoutTag_MPEG_4_0_B, { centre, left, right, centreSurround } }, + List { kAudioChannelLayoutTag_MPEG_5_0_B, { left, right, leftSurround, rightSurround, centre } }, + List { kAudioChannelLayoutTag_MPEG_5_0_C, { left, centre, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_MPEG_5_0_D, { centre, left, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_MPEG_5_1_B, { left, right, leftSurround, rightSurround, centre, LFE } }, + List { kAudioChannelLayoutTag_MPEG_5_1_C, { left, centre, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_MPEG_5_1_D, { centre, left, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_MPEG_7_1_B, { centre, leftCentre, rightCentre, left, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_Emagic_Default_7_1, { left, right, leftSurround, rightSurround, centre, LFE, leftCentre, rightCentre } }, + List { kAudioChannelLayoutTag_SMPTE_DTV, { left, right, centre, LFE, leftSurround, rightSurround, discreteChannel0 /* leftMatrixTotal */, (ChannelType) (discreteChannel0 + 1) /* rightMatrixTotal */} }, + List { kAudioChannelLayoutTag_ITU_2_2, { left, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_DVD_4, { left, right, LFE } }, + List { kAudioChannelLayoutTag_DVD_5, { left, right, LFE, centreSurround } }, + List { kAudioChannelLayoutTag_DVD_6, { left, right, LFE, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_DVD_10, { left, right, centre, LFE } }, + List { kAudioChannelLayoutTag_DVD_11, { left, right, centre, LFE, centreSurround } }, + List { kAudioChannelLayoutTag_DVD_18, { left, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_AAC_6_0, { centre, left, right, leftSurround, rightSurround, centreSurround } }, + List { kAudioChannelLayoutTag_AAC_6_1, { centre, left, right, leftSurround, rightSurround, centreSurround, LFE } }, + List { kAudioChannelLayoutTag_AAC_7_0, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_AAC_7_1_B, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } }, + List { kAudioChannelLayoutTag_AAC_7_1_C, { centre, left, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } }, + List { kAudioChannelLayoutTag_AAC_Octagonal, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, centreSurround } }, + List { kAudioChannelLayoutTag_TMH_10_2_std, { left, right, centre, topFrontCentre, leftSurroundSide, rightSurroundSide, leftSurround, rightSurround, topFrontLeft, topFrontRight, wideLeft, wideRight, topRearCentre, centreSurround, LFE, LFE2 } }, + List { kAudioChannelLayoutTag_AC3_1_0_1, { centre, LFE } }, + List { kAudioChannelLayoutTag_AC3_3_0, { left, centre, right } }, + List { kAudioChannelLayoutTag_AC3_3_1, { left, centre, right, centreSurround } }, + List { kAudioChannelLayoutTag_AC3_3_0_1, { left, centre, right, LFE } }, + List { kAudioChannelLayoutTag_AC3_2_1_1, { left, right, centreSurround, LFE } }, + List { kAudioChannelLayoutTag_AC3_3_1_1, { left, centre, right, centreSurround, LFE } }, + List { kAudioChannelLayoutTag_EAC_6_0_A, { left, centre, right, leftSurround, rightSurround, centreSurround } }, + List { kAudioChannelLayoutTag_EAC_7_0_A, { left, centre, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_EAC3_6_1_A, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } }, + List { kAudioChannelLayoutTag_EAC3_6_1_B, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } }, + List { kAudioChannelLayoutTag_EAC3_6_1_C, { left, centre, right, leftSurround, rightSurround, LFE, topFrontCentre } }, + List { kAudioChannelLayoutTag_EAC3_7_1_A, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_EAC3_7_1_B, { left, centre, right, leftSurround, rightSurround, LFE, leftCentre, rightCentre } }, + List { kAudioChannelLayoutTag_EAC3_7_1_C, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundSide, rightSurroundSide } }, + List { kAudioChannelLayoutTag_EAC3_7_1_D, { left, centre, right, leftSurround, rightSurround, LFE, wideLeft, wideRight } }, + List { kAudioChannelLayoutTag_EAC3_7_1_E, { left, centre, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } }, + List { kAudioChannelLayoutTag_EAC3_7_1_F, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topMiddle } }, + List { kAudioChannelLayoutTag_EAC3_7_1_G, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } }, + List { kAudioChannelLayoutTag_EAC3_7_1_H, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } }, + List { kAudioChannelLayoutTag_DTS_3_1, { centre, left, right, LFE } }, + List { kAudioChannelLayoutTag_DTS_4_1, { centre, left, right, centreSurround, LFE } }, + List { kAudioChannelLayoutTag_DTS_6_0_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround } }, + List { kAudioChannelLayoutTag_DTS_6_0_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_DTS_6_1_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround, LFE } }, + List { kAudioChannelLayoutTag_DTS_6_1_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear, LFE } }, + List { kAudioChannelLayoutTag_DTS_6_1_D, { centre, left, right, leftSurround, rightSurround, LFE, centreSurround } }, + List { kAudioChannelLayoutTag_DTS_7_0, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround } }, + List { kAudioChannelLayoutTag_DTS_7_1, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround, LFE } }, + List { kAudioChannelLayoutTag_DTS_8_0_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, + List { kAudioChannelLayoutTag_DTS_8_0_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround } }, + List { kAudioChannelLayoutTag_DTS_8_1_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } }, + List { kAudioChannelLayoutTag_DTS_8_1_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround, LFE } }); + } + }; + +public: + //============================================================================== + enum + { + coreAudioHOASN3DLayoutTag = (190U<<16) | 0 // kAudioChannelLayoutTag_HOA_ACN_SN3D + }; + + //============================================================================== + /** Convert CoreAudio's native AudioChannelLayout to JUCE's AudioChannelSet. + + Note that this method cannot preserve the order of channels. + */ + static AudioChannelSet fromCoreAudio (const AudioChannelLayout& layout) + { + return AudioChannelSet::channelSetWithChannels (getCoreAudioLayoutChannels (layout)); + } + + /** Convert CoreAudio's native AudioChannelLayoutTag to JUCE's AudioChannelSet. + + Note that this method cannot preserve the order of channels. + */ + static AudioChannelSet fromCoreAudio (AudioChannelLayoutTag layoutTag) + { + return AudioChannelSet::channelSetWithChannels (getSpeakerLayoutForCoreAudioTag (layoutTag)); + } + + /** Convert JUCE's AudioChannelSet to CoreAudio's AudioChannelLayoutTag. + + Note that this method cannot preserve the order of channels. + */ + static AudioChannelLayoutTag toCoreAudio (const AudioChannelSet& set) + { + if (set.getAmbisonicOrder() >= 0) + return coreAudioHOASN3DLayoutTag | static_cast (set.size()); + + for (const auto& item : SpeakerLayoutTable::get()) + { + AudioChannelSet caSet; + + for (int i = 0; i < numElementsInArray (item.channelTypes) + && item.channelTypes[i] != AudioChannelSet::unknown; ++i) + caSet.addChannel (item.channelTypes[i]); + + if (caSet == set) + return item.tag; + } + + return kAudioChannelLayoutTag_DiscreteInOrder | static_cast (set.size()); + } + + static const Array& getKnownCoreAudioTags() + { + static Array tags (createKnownCoreAudioTags()); + return tags; + } + + //============================================================================== + /** Convert CoreAudio's native AudioChannelLayout to an array of JUCE ChannelTypes. */ + static Array getCoreAudioLayoutChannels (const AudioChannelLayout& layout) + { + switch (layout.mChannelLayoutTag & 0xffff0000) + { + case kAudioChannelLayoutTag_UseChannelBitmap: + return AudioChannelSet::fromWaveChannelMask (static_cast (layout.mChannelBitmap)).getChannelTypes(); + case kAudioChannelLayoutTag_UseChannelDescriptions: + { + Array channels; + + for (UInt32 i = 0; i < layout.mNumberChannelDescriptions; ++i) + channels.addIfNotAlreadyThere (getChannelTypeFromAudioChannelLabel (layout.mChannelDescriptions[i].mChannelLabel)); + + // different speaker mappings may point to the same JUCE speaker so fill up + // this array with discrete channels + for (int j = 0; channels.size() < static_cast (layout.mNumberChannelDescriptions); ++j) + channels.addIfNotAlreadyThere (static_cast (AudioChannelSet::discreteChannel0 + j)); + + return channels; + } + case kAudioChannelLayoutTag_DiscreteInOrder: + return AudioChannelSet::discreteChannels (static_cast (layout.mChannelLayoutTag) & 0xffff).getChannelTypes(); + default: + break; + } + + return getSpeakerLayoutForCoreAudioTag (layout.mChannelLayoutTag); + } + + static Array getSpeakerLayoutForCoreAudioTag (AudioChannelLayoutTag tag) + { + // You need to specify the full AudioChannelLayout when using + // the UseChannelBitmap and UseChannelDescriptions layout tag + jassert (tag != kAudioChannelLayoutTag_UseChannelBitmap && tag != kAudioChannelLayoutTag_UseChannelDescriptions); + + Array speakers; + + for (const auto& item : SpeakerLayoutTable::get()) + { + if (tag == item.tag) + { + for (int i = 0; i < numElementsInArray (item.channelTypes) + && item.channelTypes[i] != AudioChannelSet::unknown; ++i) + speakers.add (item.channelTypes[i]); + + return speakers; + } + } + + const auto numChannels = tag & 0xffff; + + if (tag >= coreAudioHOASN3DLayoutTag && tag <= (coreAudioHOASN3DLayoutTag | 0xffff)) + { + const auto ambisonicOrder = AudioChannelSet::getAmbisonicOrderForNumChannels (static_cast (numChannels)); + + if (ambisonicOrder != -1) + return AudioChannelSet::ambisonic (ambisonicOrder).getChannelTypes(); + } + + for (UInt32 i = 0; i < numChannels; ++i) + speakers.add (static_cast (AudioChannelSet::discreteChannel0 + i)); + + return speakers; + } + +private: + static Array createKnownCoreAudioTags() + { + Array tags; + + for (const auto& item : SpeakerLayoutTable::get()) + tags.addIfNotAlreadyThere (item.tag); + + for (unsigned order = 0; order <= 5; ++order) + tags.addIfNotAlreadyThere (coreAudioHOASN3DLayoutTag | ((order + 1) * (order + 1))); + + return tags; + } + + //============================================================================== + static AudioChannelSet::ChannelType getChannelTypeFromAudioChannelLabel (AudioChannelLabel label) noexcept + { + if (label >= kAudioChannelLabel_Discrete_0 && label <= kAudioChannelLabel_Discrete_65535) + { + const unsigned int discreteChannelNum = label - kAudioChannelLabel_Discrete_0; + return static_cast (AudioChannelSet::discreteChannel0 + discreteChannelNum); + } + + switch (label) + { + case kAudioChannelLabel_Center: + case kAudioChannelLabel_Mono: return AudioChannelSet::centre; + case kAudioChannelLabel_Left: + case kAudioChannelLabel_HeadphonesLeft: return AudioChannelSet::left; + case kAudioChannelLabel_Right: + case kAudioChannelLabel_HeadphonesRight: return AudioChannelSet::right; + case kAudioChannelLabel_LFEScreen: return AudioChannelSet::LFE; + case kAudioChannelLabel_LeftSurround: return AudioChannelSet::leftSurround; + case kAudioChannelLabel_RightSurround: return AudioChannelSet::rightSurround; + case kAudioChannelLabel_LeftCenter: return AudioChannelSet::leftCentre; + case kAudioChannelLabel_RightCenter: return AudioChannelSet::rightCentre; + case kAudioChannelLabel_CenterSurround: return AudioChannelSet::surround; + case kAudioChannelLabel_LeftSurroundDirect: return AudioChannelSet::leftSurroundSide; + case kAudioChannelLabel_RightSurroundDirect: return AudioChannelSet::rightSurroundSide; + case kAudioChannelLabel_TopCenterSurround: return AudioChannelSet::topMiddle; + case kAudioChannelLabel_VerticalHeightLeft: return AudioChannelSet::topFrontLeft; + case kAudioChannelLabel_VerticalHeightRight: return AudioChannelSet::topFrontRight; + case kAudioChannelLabel_VerticalHeightCenter: return AudioChannelSet::topFrontCentre; + case kAudioChannelLabel_TopBackLeft: return AudioChannelSet::topRearLeft; + case kAudioChannelLabel_RearSurroundLeft: return AudioChannelSet::leftSurroundRear; + case kAudioChannelLabel_TopBackRight: return AudioChannelSet::topRearRight; + case kAudioChannelLabel_RearSurroundRight: return AudioChannelSet::rightSurroundRear; + case kAudioChannelLabel_TopBackCenter: return AudioChannelSet::topRearCentre; + case kAudioChannelLabel_LFE2: return AudioChannelSet::LFE2; + case kAudioChannelLabel_LeftWide: return AudioChannelSet::wideLeft; + case kAudioChannelLabel_RightWide: return AudioChannelSet::wideRight; + case kAudioChannelLabel_Ambisonic_W: return AudioChannelSet::ambisonicW; + case kAudioChannelLabel_Ambisonic_X: return AudioChannelSet::ambisonicX; + case kAudioChannelLabel_Ambisonic_Y: return AudioChannelSet::ambisonicY; + case kAudioChannelLabel_Ambisonic_Z: return AudioChannelSet::ambisonicZ; + default: return AudioChannelSet::unknown; + } + } +}; + +#endif + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioTimeConversions_mac.h b/JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioTimeConversions_mac.h new file mode 100644 index 00000000..795d6424 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioTimeConversions_mac.h @@ -0,0 +1,80 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + + +// This file will be included directly by macOS/iOS-specific .cpps +#pragma once + +#if ! DOXYGEN + +#include + +namespace juce +{ + +struct CoreAudioTimeConversions +{ +public: + CoreAudioTimeConversions() + { + mach_timebase_info_data_t info{}; + mach_timebase_info (&info); + numerator = info.numer; + denominator = info.denom; + } + + uint64_t hostTimeToNanos (uint64_t hostTime) const + { + return multiplyByRatio (hostTime, numerator, denominator); + } + + uint64_t nanosToHostTime (uint64_t nanos) const + { + return multiplyByRatio (nanos, denominator, numerator); + } + +private: + // Adapted from CAHostTimeBase.h in the Core Audio Utility Classes + static uint64_t multiplyByRatio (uint64_t toMultiply, uint64_t numerator, uint64_t denominator) + { + #if defined (__SIZEOF_INT128__) + unsigned __int128 + #else + long double + #endif + result = toMultiply; + + if (numerator != denominator) + { + result *= numerator; + result /= denominator; + } + + return (uint64_t) result; + } + + uint64_t numerator = 0, denominator = 0; +}; + +} // namespace juce + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h b/JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h deleted file mode 100644 index 798197da..00000000 --- a/JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h +++ /dev/null @@ -1,330 +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 -{ - -#if ! DOXYGEN && (JUCE_MAC || JUCE_IOS) - -struct CoreAudioLayouts -{ - //============================================================================== - enum - { - coreAudioHOASN3DLayoutTag = (190U<<16) | 0 // kAudioChannelLayoutTag_HOA_ACN_SN3D - }; - - //============================================================================== - /** Convert CoreAudio's native AudioChannelLayout to JUCE's AudioChannelSet. - - Note that this method cannot preserve the order of channels. - */ - static AudioChannelSet fromCoreAudio (const AudioChannelLayout& layout) - { - return AudioChannelSet::channelSetWithChannels (getCoreAudioLayoutChannels (layout)); - } - - /** Convert CoreAudio's native AudioChannelLayoutTag to JUCE's AudioChannelSet. - - Note that this method cannot preserve the order of channels. - */ - static AudioChannelSet fromCoreAudio (AudioChannelLayoutTag layoutTag) - { - return AudioChannelSet::channelSetWithChannels (getSpeakerLayoutForCoreAudioTag (layoutTag)); - } - - /** Convert JUCE's AudioChannelSet to CoreAudio's AudioChannelLayoutTag. - - Note that this method cannot preserve the order of channels. - */ - static AudioChannelLayoutTag toCoreAudio (const AudioChannelSet& set) - { - if (set.getAmbisonicOrder() >= 0) - return coreAudioHOASN3DLayoutTag | static_cast (set.size()); - - for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) - { - AudioChannelSet caSet; - - for (int i = 0; i < numElementsInArray (tbl->channelTypes) - && tbl->channelTypes[i] != AudioChannelSet::unknown; ++i) - caSet.addChannel (tbl->channelTypes[i]); - - if (caSet == set) - return tbl->tag; - } - - return kAudioChannelLayoutTag_DiscreteInOrder | static_cast (set.size()); - } - - static const Array& getKnownCoreAudioTags() - { - static Array tags (createKnownCoreAudioTags()); - return tags; - } - - //============================================================================== - /** Convert CoreAudio's native AudioChannelLayout to an array of JUCE ChannelTypes. */ - static Array getCoreAudioLayoutChannels (const AudioChannelLayout& layout) - { - switch (layout.mChannelLayoutTag & 0xffff0000) - { - case kAudioChannelLayoutTag_UseChannelBitmap: - return AudioChannelSet::fromWaveChannelMask (static_cast (layout.mChannelBitmap)).getChannelTypes(); - case kAudioChannelLayoutTag_UseChannelDescriptions: - { - Array channels; - - for (UInt32 i = 0; i < layout.mNumberChannelDescriptions; ++i) - channels.addIfNotAlreadyThere (getChannelTypeFromAudioChannelLabel (layout.mChannelDescriptions[i].mChannelLabel)); - - // different speaker mappings may point to the same JUCE speaker so fill up - // this array with discrete channels - for (int j = 0; channels.size() < static_cast (layout.mNumberChannelDescriptions); ++j) - channels.addIfNotAlreadyThere (static_cast (AudioChannelSet::discreteChannel0 + j)); - - return channels; - } - case kAudioChannelLayoutTag_DiscreteInOrder: - return AudioChannelSet::discreteChannels (static_cast (layout.mChannelLayoutTag) & 0xffff).getChannelTypes(); - default: - break; - } - - return getSpeakerLayoutForCoreAudioTag (layout.mChannelLayoutTag); - } - - static Array getSpeakerLayoutForCoreAudioTag (AudioChannelLayoutTag tag) - { - // You need to specify the full AudioChannelLayout when using - // the UseChannelBitmap and UseChannelDescriptions layout tag - jassert (tag != kAudioChannelLayoutTag_UseChannelBitmap && tag != kAudioChannelLayoutTag_UseChannelDescriptions); - - Array speakers; - - for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) - { - if (tag == tbl->tag) - { - for (int i = 0; i < numElementsInArray (tbl->channelTypes) - && tbl->channelTypes[i] != AudioChannelSet::unknown; ++i) - speakers.add (tbl->channelTypes[i]); - - return speakers; - } - } - - auto numChannels = tag & 0xffff; - if (tag >= coreAudioHOASN3DLayoutTag && tag <= (coreAudioHOASN3DLayoutTag | 0xffff)) - { - auto sqrtMinusOne = std::sqrt (static_cast (numChannels)) - 1.0f; - auto ambisonicOrder = jmax (0, static_cast (std::floor (sqrtMinusOne))); - - if (static_cast (ambisonicOrder) == sqrtMinusOne) - return AudioChannelSet::ambisonic (ambisonicOrder).getChannelTypes(); - } - - for (UInt32 i = 0; i < numChannels; ++i) - speakers.add (static_cast (AudioChannelSet::discreteChannel0 + i)); - - return speakers; - } - -private: - //============================================================================== - struct LayoutTagSpeakerList - { - AudioChannelLayoutTag tag; - AudioChannelSet::ChannelType channelTypes[16]; - }; - - static Array createKnownCoreAudioTags() - { - Array tags; - - for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) - tags.addIfNotAlreadyThere (tbl->tag); - - for (unsigned order = 0; order <= 5; ++order) - tags.addIfNotAlreadyThere (coreAudioHOASN3DLayoutTag | ((order + 1) * (order + 1))); - - return tags; - } - - //============================================================================== - // This list has been derived from https://pastebin.com/24dQ4BPJ - // Apple channel labels have been replaced by JUCE channel names - // This means that some layouts will be identical in JUCE but not in CoreAudio - - // In Apple's official definition the following tags exist with the same speaker layout and order - // even when *not* represented in JUCE channels - // kAudioChannelLayoutTag_Binaural = kAudioChannelLayoutTag_Stereo - // kAudioChannelLayoutTag_MPEG_5_0_B = kAudioChannelLayoutTag_Pentagonal - // kAudioChannelLayoutTag_ITU_2_2 = kAudioChannelLayoutTag_Quadraphonic - // kAudioChannelLayoutTag_AudioUnit_6_0 = kAudioChannelLayoutTag_Hexagonal - struct SpeakerLayoutTable : AudioChannelSet // save us some typing - { - static LayoutTagSpeakerList* get() noexcept - { - static LayoutTagSpeakerList tbl[] = { - // list layouts for which there is a corresponding named AudioChannelSet first - { kAudioChannelLayoutTag_Mono, { centre } }, - { kAudioChannelLayoutTag_Stereo, { left, right } }, - { kAudioChannelLayoutTag_MPEG_3_0_A, { left, right, centre } }, - { kAudioChannelLayoutTag_ITU_2_1, { left, right, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_4_0_A, { left, right, centre, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_5_0_A, { left, right, centre, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_MPEG_5_1_A, { left, right, centre, LFE, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_AudioUnit_6_0, { left, right, leftSurround, rightSurround, centre, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_6_1_A, { left, right, centre, LFE, leftSurround, rightSurround, centreSurround } }, - { kAudioChannelLayoutTag_DTS_6_0_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DTS_6_1_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_AudioUnit_7_0, { left, right, leftSurroundSide, rightSurroundSide, centre, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_AudioUnit_7_0_Front, { left, right, leftSurround, rightSurround, centre, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_MPEG_7_1_C, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_MPEG_7_1_A, { left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_Ambisonic_B_Format, { ambisonicW, ambisonicX, ambisonicY, ambisonicZ } }, - { kAudioChannelLayoutTag_Quadraphonic, { left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_Pentagonal, { left, right, leftSurroundRear, rightSurroundRear, centre } }, - { kAudioChannelLayoutTag_Hexagonal, { left, right, leftSurroundRear, rightSurroundRear, centre, centreSurround } }, - { kAudioChannelLayoutTag_Octagonal, { left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight } }, - - // more uncommon layouts - { kAudioChannelLayoutTag_StereoHeadphones, { left, right } }, - { kAudioChannelLayoutTag_MatrixStereo, { left, right } }, - { kAudioChannelLayoutTag_MidSide, { centre, discreteChannel0 } }, - { kAudioChannelLayoutTag_XY, { ambisonicX, ambisonicY } }, - { kAudioChannelLayoutTag_Binaural, { left, right } }, - { kAudioChannelLayoutTag_Cube, { left, right, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight } }, - { kAudioChannelLayoutTag_MPEG_3_0_B, { centre, left, right } }, - { kAudioChannelLayoutTag_MPEG_4_0_B, { centre, left, right, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_5_0_B, { left, right, leftSurround, rightSurround, centre } }, - { kAudioChannelLayoutTag_MPEG_5_0_C, { left, centre, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_MPEG_5_0_D, { centre, left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_MPEG_5_1_B, { left, right, leftSurround, rightSurround, centre, LFE } }, - { kAudioChannelLayoutTag_MPEG_5_1_C, { left, centre, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_MPEG_5_1_D, { centre, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_MPEG_7_1_B, { centre, leftCentre, rightCentre, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_Emagic_Default_7_1, { left, right, leftSurround, rightSurround, centre, LFE, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_SMPTE_DTV, { left, right, centre, LFE, leftSurround, rightSurround, discreteChannel0 /* leftMatrixTotal */, (ChannelType) (discreteChannel0 + 1) /* rightMatrixTotal */} }, - { kAudioChannelLayoutTag_ITU_2_2, { left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DVD_4, { left, right, LFE } }, - { kAudioChannelLayoutTag_DVD_5, { left, right, LFE, centreSurround } }, - { kAudioChannelLayoutTag_DVD_6, { left, right, LFE, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DVD_10, { left, right, centre, LFE } }, - { kAudioChannelLayoutTag_DVD_11, { left, right, centre, LFE, centreSurround } }, - { kAudioChannelLayoutTag_DVD_18, { left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_AAC_6_0, { centre, left, right, leftSurround, rightSurround, centreSurround } }, - { kAudioChannelLayoutTag_AAC_6_1, { centre, left, right, leftSurround, rightSurround, centreSurround, LFE } }, - { kAudioChannelLayoutTag_AAC_7_0, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_AAC_7_1_B, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } }, - { kAudioChannelLayoutTag_AAC_7_1_C, { centre, left, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } }, - { kAudioChannelLayoutTag_AAC_Octagonal, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, centreSurround } }, - { kAudioChannelLayoutTag_TMH_10_2_std, { left, right, centre, topFrontCentre, leftSurroundSide, rightSurroundSide, leftSurround, rightSurround, topFrontLeft, topFrontRight, wideLeft, wideRight, topRearCentre, centreSurround, LFE, LFE2 } }, - { kAudioChannelLayoutTag_AC3_1_0_1, { centre, LFE } }, - { kAudioChannelLayoutTag_AC3_3_0, { left, centre, right } }, - { kAudioChannelLayoutTag_AC3_3_1, { left, centre, right, centreSurround } }, - { kAudioChannelLayoutTag_AC3_3_0_1, { left, centre, right, LFE } }, - { kAudioChannelLayoutTag_AC3_2_1_1, { left, right, centreSurround, LFE } }, - { kAudioChannelLayoutTag_AC3_3_1_1, { left, centre, right, centreSurround, LFE } }, - { kAudioChannelLayoutTag_EAC_6_0_A, { left, centre, right, leftSurround, rightSurround, centreSurround } }, - { kAudioChannelLayoutTag_EAC_7_0_A, { left, centre, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_EAC3_6_1_A, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } }, - { kAudioChannelLayoutTag_EAC3_6_1_B, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } }, - { kAudioChannelLayoutTag_EAC3_6_1_C, { left, centre, right, leftSurround, rightSurround, LFE, topFrontCentre } }, - { kAudioChannelLayoutTag_EAC3_7_1_A, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_EAC3_7_1_B, { left, centre, right, leftSurround, rightSurround, LFE, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_EAC3_7_1_C, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundSide, rightSurroundSide } }, - { kAudioChannelLayoutTag_EAC3_7_1_D, { left, centre, right, leftSurround, rightSurround, LFE, wideLeft, wideRight } }, - { kAudioChannelLayoutTag_EAC3_7_1_E, { left, centre, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } }, - { kAudioChannelLayoutTag_EAC3_7_1_F, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topMiddle } }, - { kAudioChannelLayoutTag_EAC3_7_1_G, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } }, - { kAudioChannelLayoutTag_EAC3_7_1_H, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } }, - { kAudioChannelLayoutTag_DTS_3_1, { centre, left, right, LFE } }, - { kAudioChannelLayoutTag_DTS_4_1, { centre, left, right, centreSurround, LFE } }, - { kAudioChannelLayoutTag_DTS_6_0_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround } }, - { kAudioChannelLayoutTag_DTS_6_0_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_DTS_6_1_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround, LFE } }, - { kAudioChannelLayoutTag_DTS_6_1_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear, LFE } }, - { kAudioChannelLayoutTag_DTS_6_1_D, { centre, left, right, leftSurround, rightSurround, LFE, centreSurround } }, - { kAudioChannelLayoutTag_DTS_7_0, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DTS_7_1, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_DTS_8_0_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_DTS_8_0_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround } }, - { kAudioChannelLayoutTag_DTS_8_1_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } }, - { kAudioChannelLayoutTag_DTS_8_1_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround, LFE } }, - { 0, {} } - }; - - return tbl; - } - }; - - //============================================================================== - static AudioChannelSet::ChannelType getChannelTypeFromAudioChannelLabel (AudioChannelLabel label) noexcept - { - if (label >= kAudioChannelLabel_Discrete_0 && label <= kAudioChannelLabel_Discrete_65535) - { - const unsigned int discreteChannelNum = label - kAudioChannelLabel_Discrete_0; - return static_cast (AudioChannelSet::discreteChannel0 + discreteChannelNum); - } - - switch (label) - { - case kAudioChannelLabel_Center: - case kAudioChannelLabel_Mono: return AudioChannelSet::centre; - case kAudioChannelLabel_Left: - case kAudioChannelLabel_HeadphonesLeft: return AudioChannelSet::left; - case kAudioChannelLabel_Right: - case kAudioChannelLabel_HeadphonesRight: return AudioChannelSet::right; - case kAudioChannelLabel_LFEScreen: return AudioChannelSet::LFE; - case kAudioChannelLabel_LeftSurround: return AudioChannelSet::leftSurround; - case kAudioChannelLabel_RightSurround: return AudioChannelSet::rightSurround; - case kAudioChannelLabel_LeftCenter: return AudioChannelSet::leftCentre; - case kAudioChannelLabel_RightCenter: return AudioChannelSet::rightCentre; - case kAudioChannelLabel_CenterSurround: return AudioChannelSet::surround; - case kAudioChannelLabel_LeftSurroundDirect: return AudioChannelSet::leftSurroundSide; - case kAudioChannelLabel_RightSurroundDirect: return AudioChannelSet::rightSurroundSide; - case kAudioChannelLabel_TopCenterSurround: return AudioChannelSet::topMiddle; - case kAudioChannelLabel_VerticalHeightLeft: return AudioChannelSet::topFrontLeft; - case kAudioChannelLabel_VerticalHeightRight: return AudioChannelSet::topFrontRight; - case kAudioChannelLabel_VerticalHeightCenter: return AudioChannelSet::topFrontCentre; - case kAudioChannelLabel_TopBackLeft: return AudioChannelSet::topRearLeft; - case kAudioChannelLabel_RearSurroundLeft: return AudioChannelSet::leftSurroundRear; - case kAudioChannelLabel_TopBackRight: return AudioChannelSet::topRearRight; - case kAudioChannelLabel_RearSurroundRight: return AudioChannelSet::rightSurroundRear; - case kAudioChannelLabel_TopBackCenter: return AudioChannelSet::topRearCentre; - case kAudioChannelLabel_LFE2: return AudioChannelSet::LFE2; - case kAudioChannelLabel_LeftWide: return AudioChannelSet::wideLeft; - case kAudioChannelLabel_RightWide: return AudioChannelSet::wideRight; - case kAudioChannelLabel_Ambisonic_W: return AudioChannelSet::ambisonicW; - case kAudioChannelLabel_Ambisonic_X: return AudioChannelSet::ambisonicX; - case kAudioChannelLabel_Ambisonic_Y: return AudioChannelSet::ambisonicY; - case kAudioChannelLabel_Ambisonic_Z: return AudioChannelSet::ambisonicZ; - default: return AudioChannelSet::unknown; - } - } -}; - -#endif - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h index bb681439..2f49c186 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp index 5dd781b1..956eb177 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -51,7 +51,7 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double ne { auto bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer); - if (newSampleRate != sampleRate + if (! approximatelyEqual (newSampleRate, sampleRate) || bufferSizeNeeded != buffer.getNumSamples() || ! isPrepared) { @@ -65,6 +65,8 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double ne buffer.setSize (numberOfChannels, bufferSizeNeeded); buffer.clear(); + const ScopedLock sl (bufferRangeLock); + bufferValidStart = 0; bufferValidEnd = 0; @@ -72,6 +74,8 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double ne do { + const ScopedUnlock ul (bufferRangeLock); + backgroundThread.moveToFrontOfQueue (this); Thread::sleep (5); } @@ -87,109 +91,106 @@ void BufferingAudioSource::releaseResources() buffer.setSize (numberOfChannels, 0); - // MSVC2015 seems to need this if statement to not generate a warning during linking. + // MSVC2017 seems to need this if statement to not generate a warning during linking. // As source is set in the constructor, there is no way that source could - // ever equal this, but it seems to make MSVC2015 happy. + // ever equal this, but it seems to make MSVC2017 happy. if (source != this) source->releaseResources(); } void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { - const ScopedLock sl (bufferStartPosLock); - - auto start = bufferValidStart.load(); - auto end = bufferValidEnd.load(); - auto pos = nextPlayPos.load(); - - auto validStart = (int) (jlimit (start, end, pos) - pos); - auto validEnd = (int) (jlimit (start, end, pos + info.numSamples) - pos); + const auto bufferRange = getValidBufferRange (info.numSamples); - if (validStart == validEnd) + if (bufferRange.isEmpty()) { // total cache miss info.clearActiveBufferRegion(); + return; } - else - { - if (validStart > 0) - info.buffer->clear (info.startSample, validStart); // partial cache miss at start - if (validEnd < info.numSamples) - info.buffer->clear (info.startSample + validEnd, - info.numSamples - validEnd); // partial cache miss at end + const auto validStart = bufferRange.getStart(); + const auto validEnd = bufferRange.getEnd(); + + const ScopedLock sl (callbackLock); + + if (validStart > 0) + info.buffer->clear (info.startSample, validStart); // partial cache miss at start - if (validStart < validEnd) + if (validEnd < info.numSamples) + info.buffer->clear (info.startSample + validEnd, + info.numSamples - validEnd); // partial cache miss at end + + if (validStart < validEnd) + { + for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;) { - for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;) + jassert (buffer.getNumSamples() > 0); + + const auto startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples()); + const auto endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples()); + + if (startBufferIndex < endBufferIndex) { - jassert (buffer.getNumSamples() > 0); - auto startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples()); - auto endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples()); - - if (startBufferIndex < endBufferIndex) - { - info.buffer->copyFrom (chan, info.startSample + validStart, - buffer, - chan, startBufferIndex, - validEnd - validStart); - } - else - { - auto initialSize = buffer.getNumSamples() - startBufferIndex; - - info.buffer->copyFrom (chan, info.startSample + validStart, - buffer, - chan, startBufferIndex, - initialSize); - - info.buffer->copyFrom (chan, info.startSample + validStart + initialSize, - buffer, - chan, 0, - (validEnd - validStart) - initialSize); - } + info.buffer->copyFrom (chan, info.startSample + validStart, + buffer, + chan, startBufferIndex, + validEnd - validStart); } - } + else + { + const auto initialSize = buffer.getNumSamples() - startBufferIndex; - nextPlayPos += info.numSamples; + info.buffer->copyFrom (chan, info.startSample + validStart, + buffer, + chan, startBufferIndex, + initialSize); + + info.buffer->copyFrom (chan, info.startSample + validStart + initialSize, + buffer, + chan, 0, + (validEnd - validStart) - initialSize); + } + } } + + nextPlayPos += info.numSamples; } bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, uint32 timeout) { - if (!source || source->getTotalLength() <= 0) + if (source == nullptr || source->getTotalLength() <= 0) return false; - if (nextPlayPos + info.numSamples < 0) - return true; - - if (! isLooping() && nextPlayPos > getTotalLength()) + if ((nextPlayPos + info.numSamples < 0) + || (! isLooping() && nextPlayPos > getTotalLength())) return true; - auto now = Time::getMillisecondCounter(); - auto startTime = now; + const auto startTime = Time::getMillisecondCounter(); + auto now = startTime; auto elapsed = (now >= startTime ? now - startTime : (std::numeric_limits::max() - startTime) + now); while (elapsed <= timeout) { - { - const ScopedLock sl (bufferStartPosLock); - - auto start = bufferValidStart.load(); - auto end = bufferValidEnd.load(); - auto pos = nextPlayPos.load(); + const auto bufferRange = getValidBufferRange (info.numSamples); - auto validStart = static_cast (jlimit (start, end, pos) - pos); - auto validEnd = static_cast (jlimit (start, end, pos + info.numSamples) - pos); + const auto validStart = bufferRange.getStart(); + const auto validEnd = bufferRange.getEnd(); - if (validStart <= 0 && validStart < validEnd && validEnd >= info.numSamples) - return true; + if (validStart <= 0 + && validStart < validEnd + && validEnd >= info.numSamples) + { + return true; } - if (elapsed < timeout && (! bufferReadyEvent.wait (static_cast (timeout - elapsed)))) + if (elapsed < timeout + && ! bufferReadyEvent.wait (static_cast (timeout - elapsed))) + { return false; + } now = Time::getMillisecondCounter(); elapsed = (now >= startTime ? now - startTime @@ -202,7 +203,7 @@ bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelI int64 BufferingAudioSource::getNextReadPosition() const { jassert (source->getTotalLength() > 0); - auto pos = nextPlayPos.load(); + const auto pos = nextPlayPos.load(); return (source->isLooping() && nextPlayPos > 0) ? pos % source->getTotalLength() @@ -211,18 +212,28 @@ int64 BufferingAudioSource::getNextReadPosition() const void BufferingAudioSource::setNextReadPosition (int64 newPosition) { - const ScopedLock sl (bufferStartPosLock); + const ScopedLock sl (bufferRangeLock); nextPlayPos = newPosition; backgroundThread.moveToFrontOfQueue (this); } +Range BufferingAudioSource::getValidBufferRange (int numSamples) const +{ + const ScopedLock sl (bufferRangeLock); + + const auto pos = nextPlayPos.load(); + + return { (int) (jlimit (bufferValidStart, bufferValidEnd, pos) - pos), + (int) (jlimit (bufferValidStart, bufferValidEnd, pos + numSamples) - pos) }; +} + bool BufferingAudioSource::readNextBufferChunk() { int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd; { - const ScopedLock sl (bufferStartPosLock); + const ScopedLock sl (bufferRangeLock); if (wasSourceLooping != isLooping()) { @@ -236,7 +247,7 @@ bool BufferingAudioSource::readNextBufferChunk() sectionToReadStart = 0; sectionToReadEnd = 0; - const int maxChunkSize = 2048; + constexpr int maxChunkSize = 2048; if (newBVS < bufferValidStart || newBVS >= bufferValidEnd) { @@ -257,7 +268,7 @@ bool BufferingAudioSource::readNextBufferChunk() sectionToReadEnd = newBVE; bufferValidStart = newBVS; - bufferValidEnd = jmin (bufferValidEnd.load(), newBVE); + bufferValidEnd = jmin (bufferValidEnd, newBVE); } } @@ -265,8 +276,9 @@ bool BufferingAudioSource::readNextBufferChunk() return false; jassert (buffer.getNumSamples() > 0); - auto bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples()); - auto bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples()); + + const auto bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples()); + const auto bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples()); if (bufferIndexStart < bufferIndexEnd) { @@ -276,7 +288,7 @@ bool BufferingAudioSource::readNextBufferChunk() } else { - auto initialSize = buffer.getNumSamples() - bufferIndexStart; + const auto initialSize = buffer.getNumSamples() - bufferIndexStart; readBufferSection (sectionToReadStart, initialSize, @@ -288,7 +300,7 @@ bool BufferingAudioSource::readNextBufferChunk() } { - const ScopedLock sl2 (bufferStartPosLock); + const ScopedLock sl2 (bufferRangeLock); bufferValidStart = newBVS; bufferValidEnd = newBVE; @@ -304,6 +316,8 @@ void BufferingAudioSource::readBufferSection (int64 start, int length, int buffe source->setNextReadPosition (start); AudioSourceChannelInfo info (&buffer, bufferOffset, length); + + const ScopedLock sl (callbackLock); source->getNextAudioBlock (info); } diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h index 7ac13d02..37d28be0 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -95,24 +95,29 @@ class JUCE_API BufferingAudioSource : public PositionableAudioSource, This is useful for offline rendering. */ - bool waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout); + bool waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, uint32 timeout); private: + //============================================================================== + Range getValidBufferRange (int numSamples) const; + bool readNextBufferChunk(); + void readBufferSection (int64 start, int length, int bufferOffset); + int useTimeSlice() override; + //============================================================================== OptionalScopedPointer source; TimeSliceThread& backgroundThread; int numberOfSamplesToBuffer, numberOfChannels; AudioBuffer buffer; - CriticalSection bufferStartPosLock; + CriticalSection callbackLock, bufferRangeLock; WaitableEvent bufferReadyEvent; - std::atomic bufferValidStart { 0 }, bufferValidEnd { 0 }, nextPlayPos { 0 }; + int64 bufferValidStart = 0, bufferValidEnd = 0; + std::atomic nextPlayPos { 0 }; double sampleRate = 0; - bool wasSourceLooping = false, isPrepared = false, prefillBuffer; - - bool readNextBufferChunk(); - void readBufferSection (int64 start, int length, int bufferOffset); - int useTimeSlice() override; + bool wasSourceLooping = false, isPrepared = false; + const bool prefillBuffer; + //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioSource) }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp index cfcc6d8d..b97241cd 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -153,10 +153,10 @@ std::unique_ptr ChannelRemappingAudioSource::createXml() const const ScopedLock sl (lock); for (int i = 0; i < remappedInputs.size(); ++i) - ins << remappedInputs.getUnchecked(i) << ' '; + ins << remappedInputs.getUnchecked (i) << ' '; for (int i = 0; i < remappedOutputs.size(); ++i) - outs << remappedOutputs.getUnchecked(i) << ' '; + outs << remappedOutputs.getUnchecked (i) << ' '; e->setAttribute ("inputs", ins.trimEnd()); e->setAttribute ("outputs", outs.trimEnd()); diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h index 710f335e..412b4e52 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp index dc4ac464..062add13 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -39,13 +39,13 @@ IIRFilterAudioSource::~IIRFilterAudioSource() {} void IIRFilterAudioSource::setCoefficients (const IIRCoefficients& newCoefficients) { for (int i = iirFilters.size(); --i >= 0;) - iirFilters.getUnchecked(i)->setCoefficients (newCoefficients); + iirFilters.getUnchecked (i)->setCoefficients (newCoefficients); } void IIRFilterAudioSource::makeInactive() { for (int i = iirFilters.size(); --i >= 0;) - iirFilters.getUnchecked(i)->makeInactive(); + iirFilters.getUnchecked (i)->makeInactive(); } //============================================================================== @@ -54,7 +54,7 @@ void IIRFilterAudioSource::prepareToPlay (int samplesPerBlockExpected, double sa input->prepareToPlay (samplesPerBlockExpected, sampleRate); for (int i = iirFilters.size(); --i >= 0;) - iirFilters.getUnchecked(i)->reset(); + iirFilters.getUnchecked (i)->reset(); } void IIRFilterAudioSource::releaseResources() @@ -72,7 +72,7 @@ void IIRFilterAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& buff iirFilters.add (new IIRFilter (*iirFilters.getUnchecked (0))); for (int i = 0; i < numChannels; ++i) - iirFilters.getUnchecked(i) + iirFilters.getUnchecked (i) ->processSamples (bufferToFill.buffer->getWritePointer (i, bufferToFill.startSample), bufferToFill.numSamples); } diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h index 7b2f320e..596b1f4c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp index ac0aa22d..0d3c7533 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -24,7 +24,7 @@ namespace juce { MemoryAudioSource::MemoryAudioSource (AudioBuffer& bufferToUse, bool copyMemory, bool shouldLoop) - : isLooping (shouldLoop) + : isCurrentlyLooping (shouldLoop) { if (copyMemory) buffer.makeCopyOf (bufferToUse); @@ -44,13 +44,20 @@ void MemoryAudioSource::releaseResources() {} void MemoryAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) { + if (buffer.getNumSamples() == 0) + { + bufferToFill.clearActiveBufferRegion(); + return; + } + auto& dst = *bufferToFill.buffer; auto channels = jmin (dst.getNumChannels(), buffer.getNumChannels()); - auto max = 0, pos = 0; - auto n = buffer.getNumSamples(), m = bufferToFill.numSamples; + int max = 0, pos = 0; + auto n = buffer.getNumSamples(); + auto m = bufferToFill.numSamples; - int i; - for (i = position; (i < n || isLooping) && (pos < m); i += max) + int i = position; + for (; (i < n || isCurrentlyLooping) && (pos < m); i += max) { max = jmin (m - pos, n - (i % n)); @@ -67,7 +74,168 @@ void MemoryAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferT if (pos < m) dst.clear (bufferToFill.startSample + pos, m - pos); - position = (i % n); + position = i; +} + +//============================================================================== +void MemoryAudioSource::setNextReadPosition (int64 newPosition) +{ + position = (int) newPosition; +} + +int64 MemoryAudioSource::getNextReadPosition() const +{ + return position; +} + +int64 MemoryAudioSource::getTotalLength() const +{ + return buffer.getNumSamples(); +} + +//============================================================================== +bool MemoryAudioSource::isLooping() const +{ + return isCurrentlyLooping; } +void MemoryAudioSource::setLooping (bool shouldLoop) +{ + isCurrentlyLooping = shouldLoop; +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +struct MemoryAudioSourceTests final : public UnitTest +{ + MemoryAudioSourceTests() : UnitTest ("MemoryAudioSource", UnitTestCategories::audio) {} + + void runTest() override + { + constexpr int blockSize = 512; + AudioBuffer bufferToFill { 2, blockSize }; + AudioSourceChannelInfo channelInfo { bufferToFill }; + + beginTest ("A zero-length buffer produces silence, whether or not looping is enabled"); + { + for (const bool enableLooping : { false, true }) + { + AudioBuffer buffer; + MemoryAudioSource source { buffer, true, false }; + source.setLooping (enableLooping); + source.prepareToPlay (blockSize, 44100.0); + + for (int i = 0; i < 2; ++i) + { + play (source, channelInfo); + expect (isSilent (bufferToFill)); + } + } + } + + beginTest ("A short buffer without looping is played once and followed by silence"); + { + auto buffer = getShortBuffer(); + MemoryAudioSource source { buffer, true, false }; + source.setLooping (false); + source.prepareToPlay (blockSize, 44100.0); + + play (source, channelInfo); + + auto copy = buffer; + copy.setSize (buffer.getNumChannels(), blockSize, true, true, false); + + expect (bufferToFill == copy); + + play (source, channelInfo); + + expect (isSilent (bufferToFill)); + } + + beginTest ("A short buffer with looping is played multiple times"); + { + auto buffer = getShortBuffer(); + MemoryAudioSource source { buffer, true, false }; + source.setLooping (true); + source.prepareToPlay (blockSize, 44100.0); + + play (source, channelInfo); + + for (int sample = 0; sample < buffer.getNumSamples(); ++sample) + expectEquals (bufferToFill.getSample (0, sample + buffer.getNumSamples()), buffer.getSample (0, sample)); + + expect (! isSilent (bufferToFill)); + } + + beginTest ("A long buffer without looping is played once"); + { + auto buffer = getLongBuffer(); + MemoryAudioSource source { buffer, true, false }; + source.setLooping (false); + source.prepareToPlay (blockSize, 44100.0); + + play (source, channelInfo); + + auto copy = buffer; + copy.setSize (buffer.getNumChannels(), blockSize, true, true, false); + + expect (bufferToFill == copy); + + for (int i = 0; i < 10; ++i) + play (source, channelInfo); + + expect (isSilent (bufferToFill)); + } + + beginTest ("A long buffer with looping is played multiple times"); + { + auto buffer = getLongBuffer(); + MemoryAudioSource source { buffer, true, false }; + source.setLooping (true); + source.prepareToPlay (blockSize, 44100.0); + + for (int i = 0; i < 100; ++i) + { + play (source, channelInfo); + expectEquals (bufferToFill.getSample (0, 0), buffer.getSample (0, (i * blockSize) % buffer.getNumSamples())); + } + } + } + + static AudioBuffer getTestBuffer (int length) + { + AudioBuffer buffer { 2, length }; + + for (int channel = 0; channel < buffer.getNumChannels(); ++channel) + for (int sample = 0; sample < buffer.getNumSamples(); ++sample) + buffer.setSample (channel, sample, jmap ((float) sample, 0.0f, (float) length, -1.0f, 1.0f)); + + return buffer; + } + + static AudioBuffer getShortBuffer() { return getTestBuffer (5); } + static AudioBuffer getLongBuffer() { return getTestBuffer (1000); } + + static void play (MemoryAudioSource& source, AudioSourceChannelInfo& info) + { + info.clearActiveBufferRegion(); + source.getNextAudioBlock (info); + } + + static bool isSilent (const AudioBuffer& b) + { + for (int channel = 0; channel < b.getNumChannels(); ++channel) + if (b.findMinMax (channel, 0, b.getNumSamples()) != Range{}) + return false; + + return true; + } +}; + +static MemoryAudioSourceTests memoryAudioSourceTests; + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.h index b8986820..0fdb2635 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -29,7 +29,7 @@ namespace juce @tags{Audio} */ -class JUCE_API MemoryAudioSource : public AudioSource +class JUCE_API MemoryAudioSource : public PositionableAudioSource { public: //============================================================================== @@ -52,11 +52,28 @@ class JUCE_API MemoryAudioSource : public AudioSource /** Implementation of the AudioSource method. */ void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override; + //============================================================================== + /** Implementation of the PositionableAudioSource method. */ + void setNextReadPosition (int64 newPosition) override; + + /** Implementation of the PositionableAudioSource method. */ + int64 getNextReadPosition() const override; + + /** Implementation of the PositionableAudioSource method. */ + int64 getTotalLength() const override; + + //============================================================================== + /** Implementation of the PositionableAudioSource method. */ + bool isLooping() const override; + + /** Implementation of the PositionableAudioSource method. */ + void setLooping (bool shouldLoop) override; + private: //============================================================================== AudioBuffer buffer; int position = 0; - bool isLooping; + bool isCurrentlyLooping; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryAudioSource) diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp index 5d33d92c..2da0814e 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -90,13 +90,13 @@ void MixerAudioSource::removeAllInputs() for (int i = inputs.size(); --i >= 0;) if (inputsToDelete[i]) - toDelete.add (inputs.getUnchecked(i)); + toDelete.add (inputs.getUnchecked (i)); inputs.clear(); } for (int i = toDelete.size(); --i >= 0;) - toDelete.getUnchecked(i)->releaseResources(); + toDelete.getUnchecked (i)->releaseResources(); } void MixerAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) @@ -109,7 +109,7 @@ void MixerAudioSource::prepareToPlay (int samplesPerBlockExpected, double sample bufferSizeExpected = samplesPerBlockExpected; for (int i = inputs.size(); --i >= 0;) - inputs.getUnchecked(i)->prepareToPlay (samplesPerBlockExpected, sampleRate); + inputs.getUnchecked (i)->prepareToPlay (samplesPerBlockExpected, sampleRate); } void MixerAudioSource::releaseResources() @@ -117,7 +117,7 @@ void MixerAudioSource::releaseResources() const ScopedLock sl (lock); for (int i = inputs.size(); --i >= 0;) - inputs.getUnchecked(i)->releaseResources(); + inputs.getUnchecked (i)->releaseResources(); tempBuffer.setSize (2, 0); @@ -131,7 +131,7 @@ void MixerAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) if (inputs.size() > 0) { - inputs.getUnchecked(0)->getNextAudioBlock (info); + inputs.getUnchecked (0)->getNextAudioBlock (info); if (inputs.size() > 1) { @@ -142,7 +142,7 @@ void MixerAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) for (int i = 1; i < inputs.size(); ++i) { - inputs.getUnchecked(i)->getNextAudioBlock (info2); + inputs.getUnchecked (i)->getNextAudioBlock (info2); for (int chan = 0; chan < info.buffer->getNumChannels(); ++chan) info.buffer->addFrom (chan, info.startSample, tempBuffer, chan, 0, info.numSamples); diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h index 635a519f..cb4cc4d2 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.cpp new file mode 100644 index 00000000..8b446c11 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.cpp @@ -0,0 +1,28 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +void PositionableAudioSource::setLooping ([[maybe_unused]] bool shouldLoop) {} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h index d9667b9f..1383771c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -70,7 +70,7 @@ class JUCE_API PositionableAudioSource : public AudioSource virtual bool isLooping() const = 0; /** Tells the source whether you'd like it to play in a loop. */ - virtual void setLooping (bool shouldLoop) { ignoreUnused (shouldLoop); } + virtual void setLooping (bool shouldLoop); }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp index 1b59d955..6c89a672 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -88,7 +88,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf localRatio = ratio; } - if (lastRatio != localRatio) + if (! approximatelyEqual (lastRatio, localRatio)) { createLowPass (localRatio); lastRatio = localRatio; diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h index 56b3d260..9ae413de 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp index 51c1216f..7fe933a1 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h index 0de651ca..1d36d6a3 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp index 54c83216..9e9d3391 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -62,7 +62,7 @@ void ToneGeneratorAudioSource::releaseResources() void ToneGeneratorAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { - if (phasePerSample == 0.0) + if (approximatelyEqual (phasePerSample, 0.0)) phasePerSample = MathConstants::twoPi / (sampleRate / frequency); for (int i = 0; i < info.numSamples; ++i) diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h index 5a031677..cfd43792 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 414bcf49..081406d5 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -98,9 +98,20 @@ void Synthesiser::clearVoices() SynthesiserVoice* Synthesiser::addVoice (SynthesiserVoice* const newVoice) { - const ScopedLock sl (lock); - newVoice->setCurrentPlaybackSampleRate (sampleRate); - return voices.add (newVoice); + SynthesiserVoice* voice; + + { + const ScopedLock sl (lock); + newVoice->setCurrentPlaybackSampleRate (sampleRate); + voice = voices.add (newVoice); + } + + { + const ScopedLock sl (stealLock); + usableVoicesToStealArray.ensureStorageAllocated (voices.size() + 1); + } + + return voice; } void Synthesiser::removeVoice (const int index) @@ -142,7 +153,7 @@ void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples, bool shoul //============================================================================== void Synthesiser::setCurrentPlaybackSampleRate (const double newRate) { - if (sampleRate != newRate) + if (! approximatelyEqual (sampleRate, newRate)) { const ScopedLock sl (lock); allNotesOff (0, false); @@ -160,21 +171,18 @@ void Synthesiser::processNextBlock (AudioBuffer& outputAudio, int numSamples) { // must set the sample rate before using this! - jassert (sampleRate != 0); + jassert (! exactlyEqual (sampleRate, 0.0)); const int targetChannels = outputAudio.getNumChannels(); - MidiBuffer::Iterator midiIterator (midiData); - midiIterator.setNextSamplePosition (startSample); + auto midiIterator = midiData.findNextSamplePosition (startSample); bool firstEvent = true; - int midiEventPos; - MidiMessage m; const ScopedLock sl (lock); - while (numSamples > 0) + for (; numSamples > 0; ++midiIterator) { - if (! midiIterator.getNextEvent (m, midiEventPos)) + if (midiIterator == midiData.cend()) { if (targetChannels > 0) renderVoices (outputAudio, startSample, numSamples); @@ -182,20 +190,21 @@ void Synthesiser::processNextBlock (AudioBuffer& outputAudio, return; } - const int samplesToNextMidiMessage = midiEventPos - startSample; + const auto metadata = *midiIterator; + const int samplesToNextMidiMessage = metadata.samplePosition - startSample; if (samplesToNextMidiMessage >= numSamples) { if (targetChannels > 0) renderVoices (outputAudio, startSample, numSamples); - handleMidiEvent (m); + handleMidiEvent (metadata.getMessage()); break; } if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize)) { - handleMidiEvent (m); + handleMidiEvent (metadata.getMessage()); continue; } @@ -204,13 +213,14 @@ void Synthesiser::processNextBlock (AudioBuffer& outputAudio, if (targetChannels > 0) renderVoices (outputAudio, startSample, samplesToNextMidiMessage); - handleMidiEvent (m); + handleMidiEvent (metadata.getMessage()); startSample += samplesToNextMidiMessage; numSamples -= samplesToNextMidiMessage; } - while (midiIterator.getNextEvent (m, midiEventPos)) - handleMidiEvent (m); + std::for_each (midiIterator, + midiData.cend(), + [&] (const MidiMessageMetadata& meta) { handleMidiEvent (meta.getMessage()); }); } // explicit template instantiation @@ -472,15 +482,14 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown) } } -void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) +void Synthesiser::handleSoftPedal ([[maybe_unused]] int midiChannel, bool /*isDown*/) { - ignoreUnused (midiChannel); jassert (midiChannel > 0 && midiChannel <= 16); } -void Synthesiser::handleProgramChange (int midiChannel, int programNumber) +void Synthesiser::handleProgramChange ([[maybe_unused]] int midiChannel, + [[maybe_unused]] int programNumber) { - ignoreUnused (midiChannel, programNumber); jassert (midiChannel > 0 && midiChannel <= 16); } @@ -515,9 +524,13 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase + // All major OSes use double-locking so this will be lock- and wait-free as long as the lock is not + // contended. This is always the case if you do not call findVoiceToSteal on multiple threads at + // the same time. + const ScopedLock sl (stealLock); + // this is a list of voices we can steal, sorted by how long they've been running - Array usableVoices; - usableVoices.ensureStorageAllocated (voices.size()); + usableVoicesToStealArray.clear(); for (auto* voice : voices) { @@ -525,7 +538,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, { jassert (voice->isVoiceActive()); // We wouldn't be here otherwise - usableVoices.add (voice); + usableVoicesToStealArray.add (voice); // NB: Using a functor rather than a lambda here due to scare-stories about // compilers generating code containing heap allocations.. @@ -534,7 +547,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, bool operator() (const SynthesiserVoice* a, const SynthesiserVoice* b) const noexcept { return a->wasStartedBefore (*b); } }; - std::sort (usableVoices.begin(), usableVoices.end(), Sorter()); + std::sort (usableVoicesToStealArray.begin(), usableVoicesToStealArray.end(), Sorter()); if (! voice->isPlayingButReleased()) // Don't protect released notes { @@ -554,22 +567,22 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, top = nullptr; // The oldest note that's playing with the target pitch is ideal.. - for (auto* voice : usableVoices) + for (auto* voice : usableVoicesToStealArray) if (voice->getCurrentlyPlayingNote() == midiNoteNumber) return voice; // Oldest voice that has been released (no finger on it and not held by sustain pedal) - for (auto* voice : usableVoices) + for (auto* voice : usableVoicesToStealArray) if (voice != low && voice != top && voice->isPlayingButReleased()) return voice; // Oldest voice that doesn't have a finger on it: - for (auto* voice : usableVoices) + for (auto* voice : usableVoicesToStealArray) if (voice != low && voice != top && ! voice->isKeyDown()) return voice; // Oldest voice that isn't protected - for (auto* voice : usableVoices) + for (auto* voice : usableVoicesToStealArray) if (voice != low && voice != top) return voice; diff --git a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index f1836e1c..661b6d95 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -627,18 +627,12 @@ class JUCE_API Synthesiser bool subBlockSubdivisionIsStrict = false; bool shouldStealNotes = true; BigInteger sustainPedalsDown; + mutable CriticalSection stealLock; + mutable Array usableVoicesToStealArray; template void processNextBlock (AudioBuffer&, const MidiBuffer&, int startSample, int numSamples); - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // Note the new parameters for these methods. - virtual int findFreeVoice (const bool) const { return 0; } - virtual int noteOff (int, int, int) { return 0; } - virtual int findFreeVoice (SynthesiserSound*, const bool) { return 0; } - virtual int findVoiceToSteal (SynthesiserSound*) const { return 0; } - #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser) }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h index 88b3701c..1660be4a 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -31,16 +31,19 @@ namespace juce 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. + Do not change the parameters during playback. If you change the parameters before the + release stage has completed then you must call reset() before the next call to + noteOn(). + @tags{Audio} */ -class ADSR +class JUCE_API ADSR { public: //============================================================================== ADSR() { - setSampleRate (44100.0); - setParameters ({}); + recalculateRates(); } //============================================================================== @@ -49,19 +52,22 @@ class ADSR @tags{Audio} */ - struct Parameters + struct JUCE_API Parameters { - /** Attack time in seconds. */ - float attack = 0.1f; - - /** Decay time in seconds. */ - float decay = 0.1f; - - /** Sustain level. */ - float sustain = 1.0f; + Parameters() = default; + + Parameters (float attackTimeSeconds, + float decayTimeSeconds, + float sustainLevel, + float releaseTimeSeconds) + : attack (attackTimeSeconds), + decay (decayTimeSeconds), + sustain (sustainLevel), + release (releaseTimeSeconds) + { + } - /** Release time in seconds. */ - float release = 0.1f; + float attack = 0.1f, decay = 0.1f, sustain = 1.0f, release = 0.1f; }; /** Sets the parameters that will be used by an ADSR object. @@ -73,70 +79,69 @@ class ADSR */ void setParameters (const Parameters& newParameters) { - currentParameters = newParameters; - - sustainLevel = newParameters.sustain; - calculateRates (newParameters); + // need to call setSampleRate() first! + jassert (sampleRate > 0.0); - if (currentState != State::idle) - checkCurrentState(); + parameters = newParameters; + recalculateRates(); } /** Returns the parameters currently being used by an ADSR object. @see setParameters */ - const Parameters& getParameters() const { return currentParameters; } + const Parameters& getParameters() const noexcept { return parameters; } /** Returns true if the envelope is in its attack, decay, sustain or release stage. */ - bool isActive() const noexcept { return currentState != State::idle; } + bool isActive() const noexcept { return state != State::idle; } //============================================================================== /** Sets the sample rate that will be used for the envelope. This must be called before the getNextSample() or setParameters() methods. */ - void setSampleRate (double sampleRate) + void setSampleRate (double newSampleRate) noexcept { - jassert (sampleRate > 0.0); - sr = sampleRate; + jassert (newSampleRate > 0.0); + sampleRate = newSampleRate; } //============================================================================== /** Resets the envelope to an idle state. */ - void reset() + void reset() noexcept { envelopeVal = 0.0f; - currentState = State::idle; + state = State::idle; } /** Starts the attack phase of the envelope. */ - void noteOn() + void noteOn() noexcept { if (attackRate > 0.0f) { - currentState = State::attack; + state = State::attack; } else if (decayRate > 0.0f) { envelopeVal = 1.0f; - currentState = State::decay; + state = State::decay; } else { - currentState = State::sustain; + envelopeVal = parameters.sustain; + state = State::sustain; } } /** Starts the release phase of the envelope. */ - void noteOff() + void noteOff() noexcept { - if (currentState != State::idle) + if (state != State::idle) { - if (currentParameters.release > 0.0f) + if (parameters.release > 0.0f) { - releaseRate = static_cast (envelopeVal / (currentParameters.release * sr)); - currentState = State::release; + releaseRate = (float) (envelopeVal / (parameters.release * sampleRate)); + state = State::release; } else { @@ -150,45 +155,56 @@ class ADSR @see applyEnvelopeToBuffer */ - float getNextSample() + float getNextSample() noexcept { - if (currentState == State::idle) - return 0.0f; - - if (currentState == State::attack) + switch (state) { - envelopeVal += attackRate; + case State::idle: + { + return 0.0f; + } - if (envelopeVal >= 1.0f) + case State::attack: { - envelopeVal = 1.0f; + envelopeVal += attackRate; - if (decayRate > 0.0f) - currentState = State::decay; - else - currentState = State::sustain; + if (envelopeVal >= 1.0f) + { + envelopeVal = 1.0f; + goToNextState(); + } + + break; } - } - else if (currentState == State::decay) - { - envelopeVal -= decayRate; - if (envelopeVal <= sustainLevel) + case State::decay: { - envelopeVal = sustainLevel; - currentState = State::sustain; + envelopeVal -= decayRate; + + if (envelopeVal <= parameters.sustain) + { + envelopeVal = parameters.sustain; + goToNextState(); + } + + break; } - } - else if (currentState == State::sustain) - { - envelopeVal = sustainLevel; - } - else if (currentState == State::release) - { - envelopeVal -= releaseRate; - if (envelopeVal <= 0.0f) - reset(); + case State::sustain: + { + envelopeVal = parameters.sustain; + break; + } + + case State::release: + { + envelopeVal -= releaseRate; + + if (envelopeVal <= 0.0f) + goToNextState(); + + break; + } } return envelopeVal; @@ -199,11 +215,23 @@ class ADSR @see getNextSample */ - template + template void applyEnvelopeToBuffer (AudioBuffer& buffer, int startSample, int numSamples) { jassert (startSample + numSamples <= buffer.getNumSamples()); + if (state == State::idle) + { + buffer.clear (startSample, numSamples); + return; + } + + if (state == State::sustain) + { + buffer.applyGain (startSample, numSamples, parameters.sustain); + return; + } + auto numChannels = buffer.getNumChannels(); while (--numSamples >= 0) @@ -219,30 +247,51 @@ class ADSR private: //============================================================================== - void calculateRates (const Parameters& parameters) + void recalculateRates() noexcept { - // need to call setSampleRate() first! - jassert (sr > 0.0); + auto getRate = [] (float distance, float timeInSeconds, double sr) + { + return timeInSeconds > 0.0f ? (float) (distance / (timeInSeconds * sr)) : -1.0f; + }; + + attackRate = getRate (1.0f, parameters.attack, sampleRate); + decayRate = getRate (1.0f - parameters.sustain, parameters.decay, sampleRate); + releaseRate = getRate (parameters.sustain, parameters.release, sampleRate); - 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); + if ((state == State::attack && attackRate <= 0.0f) + || (state == State::decay && (decayRate <= 0.0f || envelopeVal <= parameters.sustain)) + || (state == State::release && releaseRate <= 0.0f)) + { + goToNextState(); + } } - void checkCurrentState() + void goToNextState() noexcept { - if (currentState == State::attack && attackRate <= 0.0f) currentState = decayRate > 0.0f ? State::decay : State::sustain; - else if (currentState == State::decay && decayRate <= 0.0f) currentState = State::sustain; - else if (currentState == State::release && releaseRate <= 0.0f) reset(); + if (state == State::attack) + { + state = (decayRate > 0.0f ? State::decay : State::sustain); + return; + } + + if (state == State::decay) + { + state = State::sustain; + return; + } + + if (state == State::release) + reset(); } //============================================================================== enum class State { idle, attack, decay, sustain, release }; - State currentState = State::idle; - Parameters currentParameters; + State state = State::idle; + Parameters parameters; - double sr = 0.0; - float envelopeVal = 0.0f, sustainLevel = 0.0f, attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f; + double sampleRate = 44100.0; + float envelopeVal = 0.0f, attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f; }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR_test.cpp b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR_test.cpp new file mode 100644 index 00000000..e073c620 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_ADSR_test.cpp @@ -0,0 +1,257 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +struct ADSRTests final : public UnitTest +{ + ADSRTests() : UnitTest ("ADSR", UnitTestCategories::audio) {} + + void runTest() override + { + constexpr double sampleRate = 44100.0; + const ADSR::Parameters parameters { 0.1f, 0.1f, 0.5f, 0.1f }; + + ADSR adsr; + adsr.setSampleRate (sampleRate); + adsr.setParameters (parameters); + + beginTest ("Idle"); + { + adsr.reset(); + + expect (! adsr.isActive()); + expectEquals (adsr.getNextSample(), 0.0f); + } + + beginTest ("Attack"); + { + adsr.reset(); + + adsr.noteOn(); + expect (adsr.isActive()); + + auto buffer = getTestBuffer (sampleRate, parameters.attack); + adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples()); + + expect (isIncreasing (buffer)); + } + + beginTest ("Decay"); + { + adsr.reset(); + + adsr.noteOn(); + advanceADSR (adsr, roundToInt (parameters.attack * sampleRate)); + + auto buffer = getTestBuffer (sampleRate, parameters.decay); + adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples()); + + expect (isDecreasing (buffer)); + } + + beginTest ("Sustain"); + { + adsr.reset(); + + adsr.noteOn(); + advanceADSR (adsr, roundToInt ((parameters.attack + parameters.decay + 0.01) * sampleRate)); + + auto random = getRandom(); + + for (int numTests = 0; numTests < 100; ++numTests) + { + const auto sustainLevel = random.nextFloat(); + const auto sustainLength = jmax (0.1f, random.nextFloat()); + + adsr.setParameters ({ parameters.attack, parameters.decay, sustainLevel, parameters.release }); + + auto buffer = getTestBuffer (sampleRate, sustainLength); + adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples()); + + expect (isSustained (buffer, sustainLevel)); + } + } + + beginTest ("Release"); + { + adsr.reset(); + + adsr.noteOn(); + advanceADSR (adsr, roundToInt ((parameters.attack + parameters.decay) * sampleRate)); + adsr.noteOff(); + + auto buffer = getTestBuffer (sampleRate, parameters.release); + adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples()); + + expect (isDecreasing (buffer)); + } + + beginTest ("Zero-length attack jumps to decay"); + { + adsr.reset(); + adsr.setParameters ({ 0.0f, parameters.decay, parameters.sustain, parameters.release }); + + adsr.noteOn(); + + auto buffer = getTestBuffer (sampleRate, parameters.decay); + adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples()); + + expect (isDecreasing (buffer)); + } + + beginTest ("Zero-length decay jumps to sustain"); + { + adsr.reset(); + adsr.setParameters ({ parameters.attack, 0.0f, parameters.sustain, parameters.release }); + + adsr.noteOn(); + advanceADSR (adsr, roundToInt (parameters.attack * sampleRate)); + adsr.getNextSample(); + + expectEquals (adsr.getNextSample(), parameters.sustain); + + auto buffer = getTestBuffer (sampleRate, 1); + adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples()); + + expect (isSustained (buffer, parameters.sustain)); + } + + beginTest ("Zero-length attack and decay jumps to sustain"); + { + adsr.reset(); + adsr.setParameters ({ 0.0f, 0.0f, parameters.sustain, parameters.release }); + + adsr.noteOn(); + + expectEquals (adsr.getNextSample(), parameters.sustain); + + auto buffer = getTestBuffer (sampleRate, 1); + adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples()); + + expect (isSustained (buffer, parameters.sustain)); + } + + beginTest ("Zero-length attack and decay releases correctly"); + { + adsr.reset(); + adsr.setParameters ({ 0.0f, 0.0f, parameters.sustain, parameters.release }); + + adsr.noteOn(); + adsr.noteOff(); + + auto buffer = getTestBuffer (sampleRate, parameters.release); + adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples()); + + expect (isDecreasing (buffer)); + } + + beginTest ("Zero-length release resets to idle"); + { + adsr.reset(); + adsr.setParameters ({ parameters.attack, parameters.decay, parameters.sustain, 0.0f }); + + adsr.noteOn(); + advanceADSR (adsr, roundToInt ((parameters.attack + parameters.decay) * sampleRate)); + adsr.noteOff(); + + expect (! adsr.isActive()); + } + } + + static void advanceADSR (ADSR& adsr, int numSamplesToAdvance) + { + while (--numSamplesToAdvance >= 0) + adsr.getNextSample(); + } + + static AudioBuffer getTestBuffer (double sampleRate, float lengthInSeconds) + { + AudioBuffer buffer { 2, roundToInt (lengthInSeconds * sampleRate) }; + + for (int channel = 0; channel < buffer.getNumChannels(); ++channel) + for (int sample = 0; sample < buffer.getNumSamples(); ++sample) + buffer.setSample (channel, sample, 1.0f); + + return buffer; + } + + static bool isIncreasing (const AudioBuffer& b) + { + jassert (b.getNumChannels() > 0 && b.getNumSamples() > 0); + + for (int channel = 0; channel < b.getNumChannels(); ++channel) + { + float previousSample = -1.0f; + + for (int sample = 0; sample < b.getNumSamples(); ++sample) + { + const auto currentSample = b.getSample (channel, sample); + + if (currentSample <= previousSample) + return false; + + previousSample = currentSample; + } + } + + return true; + } + + static bool isDecreasing (const AudioBuffer& b) + { + jassert (b.getNumChannels() > 0 && b.getNumSamples() > 0); + + for (int channel = 0; channel < b.getNumChannels(); ++channel) + { + float previousSample = std::numeric_limits::max(); + + for (int sample = 0; sample < b.getNumSamples(); ++sample) + { + const auto currentSample = b.getSample (channel, sample); + + if (currentSample >= previousSample) + return false; + + previousSample = currentSample; + } + } + + return true; + } + + static bool isSustained (const AudioBuffer& b, float sustainLevel) + { + jassert (b.getNumChannels() > 0 && b.getNumSamples() > 0); + + for (int channel = 0; channel < b.getNumChannels(); ++channel) + if (b.findMinMax (channel, 0, b.getNumSamples()) != Range { sustainLevel, sustainLevel }) + return false; + + return true; + } +}; + +static ADSRTests adsrTests; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_AudioWorkgroup.cpp b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_AudioWorkgroup.cpp new file mode 100644 index 00000000..99130154 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_AudioWorkgroup.cpp @@ -0,0 +1,236 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +#if JUCE_AUDIOWORKGROUP_TYPES_AVAILABLE + +class WorkgroupToken::TokenProvider +{ +public: + explicit TokenProvider (os_workgroup_t wg) + : workgroup (wg), attached (attach (wg, token)) {} + + ~TokenProvider() + { + if (attached) + detach (workgroup, token); + } + + TokenProvider (const TokenProvider&) = delete; + TokenProvider (TokenProvider&& other) noexcept + : workgroup (std::exchange (other.workgroup, os_workgroup_t{})), + token (std::exchange (other.token, os_workgroup_join_token_s{})), + attached (std::exchange (other.attached, false)) {} + + TokenProvider& operator= (const TokenProvider&) = delete; + TokenProvider& operator= (TokenProvider&& other) noexcept + { + TokenProvider { std::move (other) }.swap (*this); + return *this; + } + + bool isAttached() const { return attached; } + os_workgroup_t getHandle() const { return workgroup; } + +private: + static void detach (os_workgroup_t wg, os_workgroup_join_token_s token) + { + if (@available (macos 11.0, ios 14.0, *)) + os_workgroup_leave (wg, &token); + } + + static bool attach (os_workgroup_t wg, os_workgroup_join_token_s& tokenOut) + { + if (@available (macos 11.0, ios 14.0, *)) + { + if (wg != nullptr && os_workgroup_join (wg, &tokenOut) == 0) + return true; + } + + return false; + } + + void swap (TokenProvider& other) noexcept + { + std::swap (other.workgroup, workgroup); + std::swap (other.token, token); + std::swap (other.attached, attached); + } + + os_workgroup_t workgroup; + os_workgroup_join_token_s token; + bool attached; +}; + +class AudioWorkgroup::WorkgroupProvider +{ +public: + explicit WorkgroupProvider (os_workgroup_t ptr) : handle { ptr } {} + + void join (WorkgroupToken& token) const + { + if (const auto* tokenProvider = token.getTokenProvider()) + if (tokenProvider->isAttached() && tokenProvider->getHandle() == handle.get()) + return; + + // Explicit reset before constructing the new workgroup to ensure that the old workgroup + // is left before the new one is joined. + token.reset(); + + if (handle.get() != nullptr) + token = WorkgroupToken { [provider = WorkgroupToken::TokenProvider { handle.get() }] { return &provider; } }; + } + + static os_workgroup_t getWorkgroup (const AudioWorkgroup& wg) + { + if (auto* provider = wg.getWorkgroupProvider()) + return provider->handle.get(); + + return nullptr; + } + +private: + struct ScopedWorkgroupRetainer + { + ScopedWorkgroupRetainer (os_workgroup_t wg) : handle { wg } + { + if (handle != nullptr) + os_retain (handle); + } + + ~ScopedWorkgroupRetainer() + { + if (handle != nullptr) + os_release (handle); + } + + ScopedWorkgroupRetainer (const ScopedWorkgroupRetainer& other) + : ScopedWorkgroupRetainer { other.handle } {} + + ScopedWorkgroupRetainer& operator= (const ScopedWorkgroupRetainer& other) + { + ScopedWorkgroupRetainer { other }.swap (*this); + return *this; + } + + ScopedWorkgroupRetainer (ScopedWorkgroupRetainer&& other) noexcept + { + swap (other); + } + + ScopedWorkgroupRetainer& operator= (ScopedWorkgroupRetainer&& other) noexcept + { + swap (other); + return *this; + } + + void swap (ScopedWorkgroupRetainer& other) noexcept + { + std::swap (handle, other.handle); + } + + os_workgroup_t get() const noexcept { return handle; } + + private: + os_workgroup_t handle { nullptr }; + }; + + ScopedWorkgroupRetainer handle; +}; + +#else + +class WorkgroupToken::TokenProvider {}; + +class AudioWorkgroup::WorkgroupProvider +{ +public: + explicit WorkgroupProvider() = default; + + void join (WorkgroupToken& t) const { t.reset(); } + + static void* getWorkgroup (const AudioWorkgroup&) { return nullptr; } +}; + +#endif + +AudioWorkgroup::AudioWorkgroup (const AudioWorkgroup& other) + : erased ([&]() -> Erased + { + if (auto* p = other.getWorkgroupProvider()) + return [provider = *p] { return &provider; }; + + return nullptr; + }()) {} + +bool AudioWorkgroup::operator== (const AudioWorkgroup& other) const +{ + return WorkgroupProvider::getWorkgroup (*this) == WorkgroupProvider::getWorkgroup (other); +} + +void AudioWorkgroup::join (WorkgroupToken& token) const +{ + #if JUCE_AUDIOWORKGROUP_TYPES_AVAILABLE + + if (const auto* p = getWorkgroupProvider()) + { + p->join (token); + return; + } + + #endif + + token.reset(); +} + +size_t AudioWorkgroup::getMaxParallelThreadCount() const +{ + #if JUCE_AUDIOWORKGROUP_TYPES_AVAILABLE + + if (@available (macos 11.0, ios 14.0, *)) + { + if (auto wg = WorkgroupProvider::getWorkgroup (*this)) + return (size_t) os_workgroup_max_parallel_threads (wg, nullptr); + } + + #endif + + return 0; +} + +AudioWorkgroup::operator bool() const { return WorkgroupProvider::getWorkgroup (*this) != nullptr; } + +#if JUCE_AUDIOWORKGROUP_TYPES_AVAILABLE + +AudioWorkgroup makeRealAudioWorkgroup (os_workgroup_t handle) +{ + if (handle == nullptr) + return AudioWorkgroup{}; + + return AudioWorkgroup { [provider = AudioWorkgroup::WorkgroupProvider { handle }] { return &provider; } }; +} + +#endif + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_AudioWorkgroup.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_AudioWorkgroup.h new file mode 100644 index 00000000..d3fbb5e4 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_AudioWorkgroup.h @@ -0,0 +1,221 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +//============================================================================== +/** + Created by AudioWorkgroup to join the calling thread to a workgroup. + To leave the workgroup again, destroy the WorkgroupToken. + + @see AudioWorkgroup + + @tags{Audio} +*/ +class WorkgroupToken +{ +public: + /** @internal */ + class TokenProvider; + + /** @internal */ + using Erased = FixedSizeFunction<64, const TokenProvider*()>; + + /** @internal + + Creates a WorkgroupToken from a function returning a TokenProvider. + */ + explicit WorkgroupToken (Erased e) : erased (std::move (e)) {} + + /** @internal + + Creates a disengaged WorkgroupToken, i.e. create a token without joining the thread to a + workgroup. + */ + WorkgroupToken() = default; + + /** If the token joined the calling thread to a workgroup during construction, the destructor + will cause the calling thread to leave that workgroup. + */ + ~WorkgroupToken() = default; + + /** @internal */ + WorkgroupToken (const WorkgroupToken&) = delete; + + WorkgroupToken (WorkgroupToken&&) noexcept = default; + + /** @internal */ + WorkgroupToken& operator= (const WorkgroupToken&) = delete; + + WorkgroupToken& operator= (WorkgroupToken&&) = default; + + /** Returns true if and only if getTokenProvider() returns non-null. */ + explicit operator bool() const { return getTokenProvider() != nullptr; } + + /** The result of this function can be compared to nullptr to check whether the token + successfully joined the calling thread to a workgroup. + + Used in the implementation to provide platform-specific information about this token. + */ + [[nodiscard]] const TokenProvider* getTokenProvider() const { return erased != nullptr ? erased() : nullptr; } + + /** If this token was engaged by joining a workgroup, leaves that workgroup and disengages the token. + + After this call, getTokenProvider() will return nullptr. + */ + void reset() { erased = nullptr; } + +private: + Erased erased; +}; + +//============================================================================== +/** + A handle to an audio workgroup, which is a collection of realtime threads + working together to produce audio by a common deadline. + + You can use this class to join a real-time worker thread to a workgroup. + Rather than constructing instances of this class directly, you should use + functions like AudioProcessor::audioWorkgroupContextChanged() and + AudioIODevice::getWorkgroup() to fetch an engaged workgroup from the system. + + The class contains a single method, join(). Call this from your real-time + thread to with register this workgroup. + + Here's an example of how you might use this class: + + @code + Constructor() + { + startRealtimeThread (RealtimeThreadOptions{}.withApproximateAudioProcessingTime (samplesPerFrame, sampleRate)); + or + startRealtimeThread (RealtimeThreadOptions{}.withProcessingTimeMs (10)); + } + + void Thread::run() override + { + WorkgroupToken token; + + getWorkgroup().join (token); + + while (wait (-1) && ! threadShouldExit()) + { + // If the workgroup has changed, rejoin the workgroup with the same token. + if (workgroupChanged()) + getWorkgroup().join (token); + + // Perform the work here + } + } + + void AudioProcessor::processBlock() + { + workerThread->notify(); + } + @endcode + + @see Thread, AudioProcessor, WorkgroupToken + + @tags{Audio} +*/ +class AudioWorkgroup +{ +public: + /** @internal */ + class WorkgroupProvider; + + /** @internal */ + using Erased = FixedSizeFunction<64, const WorkgroupProvider*()>; + + /** @internal + + Creates an AudioWorkgroup from a function returning a WorkgroupProvider. + */ + explicit AudioWorkgroup (Erased e) : erased (std::move (e)) {} + + /** Move constructor. */ + AudioWorkgroup (AudioWorkgroup&&) = default; + + /** Move assignment operator. */ + AudioWorkgroup& operator= (AudioWorkgroup&&) = default; + + /** Copy constructor. */ + AudioWorkgroup (const AudioWorkgroup&); + + /** Copy assignment operator. */ + AudioWorkgroup& operator= (const AudioWorkgroup& other) + { + AudioWorkgroup { other }.swap (*this); + return *this; + } + + /** Constructs a disengaged handle that does not represent any workgroup. */ + AudioWorkgroup() = default; + + /** + This method attempts to join the calling thread to this workgroup. + + If the join operation is successful, the token will be engaged, i.e. its + getTokenProvider() function will return non-null. + + If the token is already engaged and represents a join to another workgroup, + the thread will leave that workgroup before joining the workgroup represented by this + object. If the 'token' is already engaged and is passed to the same workgroup, the method + will not perform any action. + + It's important to note that the lifetime of the token should not exceed the lifetime + of the associated thread and must be destroyed on the same thread. + */ + void join (WorkgroupToken& token) const; + + /** Equality operator. */ + bool operator== (const AudioWorkgroup& other) const; + + /** Inequality operator. */ + bool operator!= (const AudioWorkgroup& other) const { return ! operator== (other); } + + /** Returns true if and only if this object represents a workgroup. */ + explicit operator bool() const; + + /** Disengages this instance so that it no longer represents a workgroup. + + After this call, operator bool() will return false. + */ + void reset() { erased = nullptr; } + + /** Returns the recommended maximum number of parallel threads that should join this workgroup. + + This recommendation is based on the workgroup attributes and current hardware, but not on + system load. On a very busy system, it may be more effective to use fewer parallel threads. + */ + size_t getMaxParallelThreadCount() const; + +private: + const WorkgroupProvider* getWorkgroupProvider() const { return erased != nullptr ? erased() : nullptr; } + + void swap (AudioWorkgroup& other) noexcept { std::swap (other.erased, erased); } + + Erased erased; +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_CatmullRomInterpolator.cpp b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_CatmullRomInterpolator.cpp deleted file mode 100644 index dbe986c4..00000000 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_CatmullRomInterpolator.cpp +++ /dev/null @@ -1,75 +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 -{ - -struct CatmullRomAlgorithm -{ - static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept - { - auto y0 = inputs[3]; - auto y1 = inputs[2]; - auto y2 = inputs[1]; - auto y3 = inputs[0]; - - auto halfY0 = 0.5f * y0; - auto halfY3 = 0.5f * y3; - - return y1 + offset * ((0.5f * y2 - halfY0) - + (offset * (((y0 + 2.0f * y2) - (halfY3 + 2.5f * y1)) - + (offset * ((halfY3 + 1.5f * y1) - (halfY0 + 1.5f * y2)))))); - } -}; - -CatmullRomInterpolator::CatmullRomInterpolator() noexcept { reset(); } -CatmullRomInterpolator::~CatmullRomInterpolator() noexcept {} - -void CatmullRomInterpolator::reset() noexcept -{ - subSamplePos = 1.0; - - for (auto& s : lastInputSamples) - s = 0; -} - -int CatmullRomInterpolator::process (double actualRatio, const float* in, float* out, int numOut, int available, int wrap) noexcept -{ - return interpolate (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap); -} - -int CatmullRomInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept -{ - return interpolate (lastInputSamples, subSamplePos, actualRatio, in, out, numOut); -} - -int CatmullRomInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, int available, int wrap, float gain) noexcept -{ - return interpolateAdding (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap, gain); -} - -int CatmullRomInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept -{ - return interpolateAdding (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain); -} - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_CatmullRomInterpolator.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_CatmullRomInterpolator.h deleted file mode 100644 index 0972acbc..00000000 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_CatmullRomInterpolator.h +++ /dev/null @@ -1,146 +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 -{ - -/** - Interpolator for resampling a stream of floats using Catmull-Rom interpolation. - - Note that the resampler is stateful, so when there's a break in the continuity - of the input stream you're feeding it, you should call reset() before feeding - it any new data. And like with any other stateful filter, if you're resampling - multiple channels, make sure each one uses its own CatmullRomInterpolator - object. - - @see LagrangeInterpolator - - @tags{Audio} -*/ -class JUCE_API CatmullRomInterpolator -{ -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. - */ - void reset() noexcept; - - /** Resamples a stream of samples. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results into - @param numOutputSamplesToProduce the number of output samples that should be created - - @returns the actual number of input samples that were used - */ - int process (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce) noexcept; - - /** Resamples a stream of samples. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results into - @param numOutputSamplesToProduce the number of output samples that should be created - @param available the number of available input samples. If it needs more samples - than available, it either wraps back for wrapAround samples, or - it feeds zeroes - @param wrapAround if the stream exceeds available samples, it wraps back for - wrapAround samples. If wrapAround is set to 0, it will feed zeroes. - - @returns the actual number of input samples that were used - */ - int process (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce, - int available, - int wrapAround) noexcept; - - /** Resamples a stream of samples, adding the results to the output data - with a gain. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results to - the result values will be added - to any pre-existing data in this buffer after being multiplied by - the gain factor - @param numOutputSamplesToProduce the number of output samples that should be created - @param gain a gain factor to multiply the resulting samples by before - adding them to the destination buffer - - @returns the actual number of input samples that were used - */ - int processAdding (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce, - float gain) noexcept; - - /** Resamples a stream of samples, adding the results to the output data - with a gain. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results to - the result values will be added - to any pre-existing data in this buffer after being multiplied by - the gain factor - @param numOutputSamplesToProduce the number of output samples that should be created - @param available the number of available input samples. If it needs more samples - than available, it either wraps back for wrapAround samples, or - it feeds zeroes - @param wrapAround if the stream exceeds available samples, it wraps back for - wrapAround samples. If wrapAround is set to 0, it will feed zeroes. - @param gain a gain factor to multiply the resulting samples by before - adding them to the destination buffer - - @returns the actual number of input samples that were used - */ - int processAdding (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce, - int available, - int wrapAround, - float gain) noexcept; - -private: - float lastInputSamples[5]; - double subSamplePos; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CatmullRomInterpolator) -}; - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Decibels.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Decibels.h index 999ec550..dd936318 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Decibels.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Decibels.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -60,6 +60,20 @@ class Decibels : minusInfinityDb; } + /** Restricts a gain value based on a lower bound specified in dBFS. + + This is useful if you want to make sure a gain value never reaches zero. + */ + template + static Type gainWithLowerBound (Type gain, Type lowerBoundDb) + { + // You probably want to use a negative decibel value or the gain will + // be restricted to boosting only! + jassert (lowerBoundDb < (Type) 0.0); + + return jmax ((Type) gain, Decibels::decibelsToGain (lowerBoundDb, lowerBoundDb - (Type) 1.0)); + } + //============================================================================== /** Converts a decibel reading to a string. diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_GenericInterpolator.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_GenericInterpolator.h new file mode 100644 index 00000000..afb8bb35 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_GenericInterpolator.h @@ -0,0 +1,386 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +/** + An interpolator base class for resampling streams of floats. + + Note that the resamplers are stateful, so when there's a break in the continuity + of the input stream you're feeding it, you should call reset() before feeding + it any new data. And like with any other stateful filter, if you're resampling + multiple channels, make sure each one uses its own interpolator object. + + @see LagrangeInterpolator, CatmullRomInterpolator, WindowedSincInterpolator, + LinearInterpolator, ZeroOrderHoldInterpolator + + @tags{Audio} +*/ +template +class JUCE_API GenericInterpolator +{ + static auto processReplacingCallback() + { + return [] (auto, auto newValue) { return newValue; }; + } + + static auto processAddingCallback (float gain) + { + return [gain] (auto oldValue, auto newValue) { return oldValue + gain * newValue; }; + } + +public: + GenericInterpolator() noexcept { reset(); } + + GenericInterpolator (GenericInterpolator&&) noexcept = default; + GenericInterpolator& operator= (GenericInterpolator&&) noexcept = default; + + /** Returns the latency of the interpolation algorithm in isolation. + + In the context of resampling the total latency of a process using + the interpolator is the base latency divided by the speed ratio. + */ + static constexpr float getBaseLatency() noexcept + { + return InterpolatorTraits::algorithmicLatency; + } + + /** Resets the state of the interpolator. + + Call this when there's a break in the continuity of the input data stream. + */ + void reset() noexcept + { + indexBuffer = 0; + subSamplePos = 1.0; + std::fill (std::begin (lastInputSamples), std::end (lastInputSamples), 0.0f); + } + + /** Resamples a stream of samples. + + @param speedRatio the number of input samples to use for each output sample + @param inputSamples the source data to read from. This must contain at + least (speedRatio * numOutputSamplesToProduce) samples. + @param outputSamples the buffer to write the results into + @param numOutputSamplesToProduce the number of output samples that should be created + + @returns the actual number of input samples that were used + */ + int process (double speedRatio, + const float* inputSamples, + float* outputSamples, + int numOutputSamplesToProduce) noexcept + { + return interpolateImpl (speedRatio, + inputSamples, + outputSamples, + numOutputSamplesToProduce, + processReplacingCallback()); + } + + /** Resamples a stream of samples. + + @param speedRatio the number of input samples to use for each output sample + @param inputSamples the source data to read from. This must contain at + least (speedRatio * numOutputSamplesToProduce) samples. + @param outputSamples the buffer to write the results into + @param numOutputSamplesToProduce the number of output samples that should be created + @param numInputSamplesAvailable the number of available input samples. If it needs more samples + than available, it either wraps back for wrapAround samples, or + it feeds zeroes + @param wrapAround if the stream exceeds available samples, it wraps back for + wrapAround samples. If wrapAround is set to 0, it will feed zeroes. + + @returns the actual number of input samples that were used + */ + int process (double speedRatio, + const float* inputSamples, + float* outputSamples, + int numOutputSamplesToProduce, + int numInputSamplesAvailable, + int wrapAround) noexcept + { + return interpolateImpl (speedRatio, + inputSamples, + outputSamples, + numOutputSamplesToProduce, + numInputSamplesAvailable, + wrapAround, + processReplacingCallback()); + } + + /** Resamples a stream of samples, adding the results to the output data + with a gain. + + @param speedRatio the number of input samples to use for each output sample + @param inputSamples the source data to read from. This must contain at + least (speedRatio * numOutputSamplesToProduce) samples. + @param outputSamples the buffer to write the results to - the result values will be added + to any pre-existing data in this buffer after being multiplied by + the gain factor + @param numOutputSamplesToProduce the number of output samples that should be created + @param gain a gain factor to multiply the resulting samples by before + adding them to the destination buffer + + @returns the actual number of input samples that were used + */ + int processAdding (double speedRatio, + const float* inputSamples, + float* outputSamples, + int numOutputSamplesToProduce, + float gain) noexcept + { + return interpolateImpl (speedRatio, + inputSamples, + outputSamples, + numOutputSamplesToProduce, + processAddingCallback (gain)); + } + + /** Resamples a stream of samples, adding the results to the output data + with a gain. + + @param speedRatio the number of input samples to use for each output sample + @param inputSamples the source data to read from. This must contain at + least (speedRatio * numOutputSamplesToProduce) samples. + @param outputSamples the buffer to write the results to - the result values will be added + to any pre-existing data in this buffer after being multiplied by + the gain factor + @param numOutputSamplesToProduce the number of output samples that should be created + @param numInputSamplesAvailable the number of available input samples. If it needs more samples + than available, it either wraps back for wrapAround samples, or + it feeds zeroes + @param wrapAround if the stream exceeds available samples, it wraps back for + wrapAround samples. If wrapAround is set to 0, it will feed zeroes. + @param gain a gain factor to multiply the resulting samples by before + adding them to the destination buffer + + @returns the actual number of input samples that were used + */ + int processAdding (double speedRatio, + const float* inputSamples, + float* outputSamples, + int numOutputSamplesToProduce, + int numInputSamplesAvailable, + int wrapAround, + float gain) noexcept + { + return interpolateImpl (speedRatio, + inputSamples, + outputSamples, + numOutputSamplesToProduce, + numInputSamplesAvailable, + wrapAround, + processAddingCallback (gain)); + } + +private: + //============================================================================== + forcedinline void pushInterpolationSample (float newValue) noexcept + { + lastInputSamples[indexBuffer] = newValue; + + if (++indexBuffer == memorySize) + indexBuffer = 0; + } + + forcedinline void pushInterpolationSamples (const float* input, + int numOutputSamplesToProduce) noexcept + { + if (numOutputSamplesToProduce >= memorySize) + { + const auto* const offsetInput = input + (numOutputSamplesToProduce - memorySize); + + for (int i = 0; i < memorySize; ++i) + pushInterpolationSample (offsetInput[i]); + } + else + { + for (int i = 0; i < numOutputSamplesToProduce; ++i) + pushInterpolationSample (input[i]); + } + } + + forcedinline void pushInterpolationSamples (const float* input, + int numOutputSamplesToProduce, + int numInputSamplesAvailable, + int wrapAround) noexcept + { + if (numOutputSamplesToProduce >= memorySize) + { + if (numInputSamplesAvailable >= memorySize) + { + pushInterpolationSamples (input, + numOutputSamplesToProduce); + } + else + { + pushInterpolationSamples (input + ((numOutputSamplesToProduce - numInputSamplesAvailable) - 1), + numInputSamplesAvailable); + + if (wrapAround > 0) + { + numOutputSamplesToProduce -= wrapAround; + + pushInterpolationSamples (input + ((numOutputSamplesToProduce - (memorySize - numInputSamplesAvailable)) - 1), + memorySize - numInputSamplesAvailable); + } + else + { + for (int i = numInputSamplesAvailable; i < memorySize; ++i) + pushInterpolationSample (0.0f); + } + } + } + else + { + if (numOutputSamplesToProduce > numInputSamplesAvailable) + { + for (int i = 0; i < numInputSamplesAvailable; ++i) + pushInterpolationSample (input[i]); + + const auto extraSamples = numOutputSamplesToProduce - numInputSamplesAvailable; + + if (wrapAround > 0) + { + const auto* const offsetInput = input + (numInputSamplesAvailable - wrapAround); + + for (int i = 0; i < extraSamples; ++i) + pushInterpolationSample (offsetInput[i]); + } + else + { + for (int i = 0; i < extraSamples; ++i) + pushInterpolationSample (0.0f); + } + } + else + { + for (int i = 0; i < numOutputSamplesToProduce; ++i) + pushInterpolationSample (input[i]); + } + } + } + + //============================================================================== + template + int interpolateImpl (double speedRatio, + const float* input, + float* output, + int numOutputSamplesToProduce, + int numInputSamplesAvailable, + int wrap, + Process process) + { + auto originalIn = input; + bool exceeded = false; + + const auto pushSample = [&] + { + if (exceeded) + { + pushInterpolationSample (0.0); + } + else + { + pushInterpolationSample (*input++); + + if (--numInputSamplesAvailable <= 0) + { + if (wrap > 0) + { + input -= wrap; + numInputSamplesAvailable += wrap; + } + else + { + exceeded = true; + } + } + } + }; + + interpolateImpl (speedRatio, + output, + numOutputSamplesToProduce, + process, + pushSample); + + if (wrap == 0) + return (int) (input - originalIn); + + return ((int) (input - originalIn) + wrap) % wrap; + } + + template + int interpolateImpl (double speedRatio, + const float* input, + float* output, + int numOutputSamplesToProduce, + Process process) + { + int numUsed = 0; + + interpolateImpl (speedRatio, + output, + numOutputSamplesToProduce, + process, + [this, input, &numUsed] { pushInterpolationSample (input[numUsed++]); }); + + return numUsed; + } + + template + void interpolateImpl (double speedRatio, + float* output, + int numOutputSamplesToProduce, + Process process, + PushSample pushSample) + { + auto pos = subSamplePos; + + for (auto i = 0; i < numOutputSamplesToProduce; ++i) + { + while (pos >= 1.0) + { + pushSample(); + pos -= 1.0; + } + + *output = process (*output, InterpolatorTraits::valueAtOffset (lastInputSamples, (float) pos, indexBuffer)); + ++output; + pos += speedRatio; + } + + subSamplePos = pos; + } + + //============================================================================== + float lastInputSamples[(size_t) memorySize]; + double subSamplePos = 1.0; + int indexBuffer = 0; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericInterpolator) +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.cpp b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.cpp index 3938a6be..c4fd95a1 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,8 @@ namespace juce { +constexpr auto minimumDecibels = -300.0f; + IIRCoefficients::IIRCoefficients() noexcept { zeromem (coefficients, sizeof (coefficients)); @@ -44,7 +46,7 @@ IIRCoefficients& IIRCoefficients::operator= (const IIRCoefficients& other) noexc IIRCoefficients::IIRCoefficients (double c1, double c2, double c3, double c4, double c5, double c6) noexcept { - auto a = 1.0 / c4; + const auto a = 1.0 / c4; coefficients[0] = (float) (c1 * a); coefficients[1] = (float) (c2 * a); @@ -67,9 +69,9 @@ IIRCoefficients IIRCoefficients::makeLowPass (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - auto n = 1.0 / std::tan (MathConstants::pi * frequency / sampleRate); - auto nSquared = n * n; - auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); + const auto n = 1.0 / std::tan (MathConstants::pi * frequency / sampleRate); + const auto nSquared = n * n; + const auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); return IIRCoefficients (c1, c1 * 2.0, @@ -82,7 +84,7 @@ IIRCoefficients IIRCoefficients::makeLowPass (double sampleRate, IIRCoefficients IIRCoefficients::makeHighPass (double sampleRate, double frequency) noexcept { - return makeHighPass (sampleRate, frequency, 1.0 / std::sqrt(2.0)); + return makeHighPass (sampleRate, frequency, 1.0 / std::sqrt (2.0)); } IIRCoefficients IIRCoefficients::makeHighPass (double sampleRate, @@ -93,9 +95,9 @@ IIRCoefficients IIRCoefficients::makeHighPass (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - auto n = std::tan (MathConstants::pi * frequency / sampleRate); - auto nSquared = n * n; - auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); + const auto n = std::tan (MathConstants::pi * frequency / sampleRate); + const auto nSquared = n * n; + const auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); return IIRCoefficients (c1, c1 * -2.0, @@ -119,9 +121,9 @@ IIRCoefficients IIRCoefficients::makeBandPass (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - auto n = 1.0 / std::tan (MathConstants::pi * frequency / sampleRate); - auto nSquared = n * n; - auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); + const auto n = 1.0 / std::tan (MathConstants::pi * frequency / sampleRate); + const auto nSquared = n * n; + const auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); return IIRCoefficients (c1 * n / Q, 0.0, @@ -145,9 +147,9 @@ IIRCoefficients IIRCoefficients::makeNotchFilter (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - auto n = 1.0 / std::tan (MathConstants::pi * frequency / sampleRate); - auto nSquared = n * n; - auto c1 = 1.0 / (1.0 + n / Q + nSquared); + const auto n = 1.0 / std::tan (MathConstants::pi * frequency / sampleRate); + const auto nSquared = n * n; + const auto c1 = 1.0 / (1.0 + n / Q + nSquared); return IIRCoefficients (c1 * (1.0 + nSquared), 2.0 * c1 * (1.0 - nSquared), @@ -171,9 +173,9 @@ IIRCoefficients IIRCoefficients::makeAllPass (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - auto n = 1.0 / std::tan (MathConstants::pi * frequency / sampleRate); - auto nSquared = n * n; - auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); + const auto n = 1.0 / std::tan (MathConstants::pi * frequency / sampleRate); + const auto nSquared = n * n; + const auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); return IIRCoefficients (c1 * (1.0 - n / Q + nSquared), c1 * 2.0 * (1.0 - nSquared), @@ -192,13 +194,13 @@ IIRCoefficients IIRCoefficients::makeLowShelf (double sampleRate, jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); jassert (Q > 0.0); - auto A = jmax (0.0f, std::sqrt (gainFactor)); - auto aminus1 = A - 1.0; - auto aplus1 = A + 1.0; - auto omega = (MathConstants::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate; - auto coso = std::cos (omega); - auto beta = std::sin (omega) * std::sqrt (A) / Q; - auto aminus1TimesCoso = aminus1 * coso; + const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, minimumDecibels)); + const auto aminus1 = A - 1.0; + const auto aplus1 = A + 1.0; + const auto omega = (MathConstants::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate; + const auto coso = std::cos (omega); + const auto beta = std::sin (omega) * std::sqrt (A) / Q; + const auto aminus1TimesCoso = aminus1 * coso; return IIRCoefficients (A * (aplus1 - aminus1TimesCoso + beta), A * 2.0 * (aminus1 - aplus1 * coso), @@ -217,13 +219,13 @@ IIRCoefficients IIRCoefficients::makeHighShelf (double sampleRate, jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); jassert (Q > 0.0); - auto A = jmax (0.0f, std::sqrt (gainFactor)); - auto aminus1 = A - 1.0; - auto aplus1 = A + 1.0; - auto omega = (MathConstants::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate; - auto coso = std::cos (omega); - auto beta = std::sin (omega) * std::sqrt (A) / Q; - auto aminus1TimesCoso = aminus1 * coso; + const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, minimumDecibels)); + const auto aminus1 = A - 1.0; + const auto aplus1 = A + 1.0; + const auto omega = (MathConstants::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate; + const auto coso = std::cos (omega); + const auto beta = std::sin (omega) * std::sqrt (A) / Q; + const auto aminus1TimesCoso = aminus1 * coso; return IIRCoefficients (A * (aplus1 + aminus1TimesCoso + beta), A * -2.0 * (aminus1 + aplus1 * coso), @@ -242,12 +244,12 @@ IIRCoefficients IIRCoefficients::makePeakFilter (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - auto A = jmax (0.0f, std::sqrt (gainFactor)); - auto omega = (MathConstants::twoPi * jmax (frequency, 2.0)) / sampleRate; - auto alpha = 0.5 * std::sin (omega) / Q; - auto c2 = -2.0 * std::cos (omega); - auto alphaTimesA = alpha * A; - auto alphaOverA = alpha / A; + const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, minimumDecibels)); + const auto omega = (MathConstants::twoPi * jmax (frequency, 2.0)) / sampleRate; + const auto alpha = 0.5 * std::sin (omega) / Q; + const auto c2 = -2.0 * std::cos (omega); + const auto alphaTimesA = alpha * A; + const auto alphaOverA = alpha / A; return IIRCoefficients (1.0 + alphaTimesA, c2, @@ -258,42 +260,42 @@ IIRCoefficients IIRCoefficients::makePeakFilter (double sampleRate, } //============================================================================== -IIRFilter::IIRFilter() noexcept -{ -} +template +IIRFilterBase::IIRFilterBase() noexcept = default; -IIRFilter::IIRFilter (const IIRFilter& other) noexcept : active (other.active) +template +IIRFilterBase::IIRFilterBase (const IIRFilterBase& other) noexcept : active (other.active) { - const SpinLock::ScopedLockType sl (other.processLock); + const typename Mutex::ScopedLockType sl (other.processLock); coefficients = other.coefficients; } -IIRFilter::~IIRFilter() noexcept -{ -} - //============================================================================== -void IIRFilter::makeInactive() noexcept +template +void IIRFilterBase::makeInactive() noexcept { - const SpinLock::ScopedLockType sl (processLock); + const typename Mutex::ScopedLockType sl (processLock); active = false; } -void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcept +template +void IIRFilterBase::setCoefficients (const IIRCoefficients& newCoefficients) noexcept { - const SpinLock::ScopedLockType sl (processLock); + const typename Mutex::ScopedLockType sl (processLock); coefficients = newCoefficients; active = true; } //============================================================================== -void IIRFilter::reset() noexcept +template +void IIRFilterBase::reset() noexcept { - const SpinLock::ScopedLockType sl (processLock); + const typename Mutex::ScopedLockType sl (processLock); v1 = v2 = 0.0; } -float IIRFilter::processSingleSampleRaw (float in) noexcept +template +float IIRFilterBase::processSingleSampleRaw (float in) noexcept { auto out = coefficients.coefficients[0] * in + v1; @@ -305,9 +307,10 @@ float IIRFilter::processSingleSampleRaw (float in) noexcept return out; } -void IIRFilter::processSamples (float* const samples, const int numSamples) noexcept +template +void IIRFilterBase::processSamples (float* const samples, const int numSamples) noexcept { - const SpinLock::ScopedLockType sl (processLock); + const typename Mutex::ScopedLockType sl (processLock); if (active) { @@ -333,4 +336,7 @@ void IIRFilter::processSamples (float* const samples, const int numSamples) noex } } +template class IIRFilterBase; +template class IIRFilterBase; + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.h index 5eed4e29..b31945a1 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -153,7 +153,8 @@ class JUCE_API IIRCoefficients @tags{Audio} */ -class JUCE_API IIRFilter +template +class JUCE_API IIRFilterBase { public: //============================================================================== @@ -163,13 +164,10 @@ class JUCE_API IIRFilter you process with it. Use the setCoefficients() method to turn it into the type of filter needed. */ - IIRFilter() noexcept; + IIRFilterBase() noexcept; /** Creates a copy of another filter. */ - IIRFilter (const IIRFilter&) noexcept; - - /** Destructor. */ - ~IIRFilter() noexcept; + IIRFilterBase (const IIRFilterBase&) noexcept; //============================================================================== /** Clears the filter so that any incoming data passes through unchanged. */ @@ -202,7 +200,7 @@ class JUCE_API IIRFilter protected: //============================================================================== - SpinLock processLock; + Mutex processLock; IIRCoefficients coefficients; float v1 = 0, v2 = 0; bool active = false; @@ -214,4 +212,43 @@ class JUCE_API IIRFilter JUCE_LEAK_DETECTOR (IIRFilter) }; +/** + An IIR filter that can perform low, high, or band-pass filtering on an + audio signal, and which attempts to implement basic thread-safety. + + This class synchronises calls to some of its member functions, making it + safe (although not necessarily real-time-safe) to reset the filter or + apply new coefficients while the filter is processing on another thread. + In most cases this style of internal locking should not be used, and you + should attempt to provide thread-safety at a higher level in your program. + If you can guarantee that calls to the filter will be synchronised externally, + you could consider switching to SingleThreadedIIRFilter instead. + + @see SingleThreadedIIRFilter, IIRCoefficient, IIRFilterAudioSource + + @tags{Audio} +*/ +class IIRFilter : public IIRFilterBase +{ +public: + using IIRFilterBase::IIRFilterBase; +}; + +/** + An IIR filter that can perform low, high, or band-pass filtering on an + audio signal, with no thread-safety guarantees. + + You should use this class if you need an IIR filter, and don't plan to + call its member functions from multiple threads at once. + + @see IIRFilter, IIRCoefficient, IIRFilterAudioSource + + @tags{Audio} +*/ +class SingleThreadedIIRFilter : public IIRFilterBase +{ +public: + using IIRFilterBase::IIRFilterBase; +}; + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Interpolators.cpp b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Interpolators.cpp new file mode 100644 index 00000000..07dbd52d --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Interpolators.cpp @@ -0,0 +1,188 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +#if JUCE_UNIT_TESTS + +class InterpolatorTests final : public UnitTest +{ +public: + InterpolatorTests() + : UnitTest ("InterpolatorTests", UnitTestCategories::audio) + { + } + +private: + template + void runInterplatorTests (const String& interpolatorName) + { + auto createGaussian = [] (std::vector& destination, float scale, float centreInSamples, float width) + { + for (size_t i = 0; i < destination.size(); ++i) + { + auto x = (((float) i) - centreInSamples) * width; + destination[i] = std::exp (-(x * x)); + } + + FloatVectorOperations::multiply (destination.data(), scale, (int) destination.size()); + }; + + auto findGaussianPeak = [] (const std::vector& input) -> float + { + auto max = std::max_element (std::begin (input), std::end (input)); + auto maxPrev = max - 1; + jassert (maxPrev >= std::begin (input)); + auto maxNext = max + 1; + jassert (maxNext < std::end (input)); + auto quadraticMaxLoc = (*maxPrev - *maxNext) / (2.0f * ((*maxNext + *maxPrev) - (2.0f * *max))); + return quadraticMaxLoc + (float) std::distance (std::begin (input), max); + }; + + auto expectAllElementsWithin = [this] (const std::vector& v1, const std::vector& v2, float tolerance) + { + expectEquals ((int) v1.size(), (int) v2.size()); + + for (size_t i = 0; i < v1.size(); ++i) + expectWithinAbsoluteError (v1[i], v2[i], tolerance); + }; + + InterpolatorType interpolator; + + constexpr size_t inputSize = 1001; + static_assert (inputSize > 800 + InterpolatorType::getBaseLatency(), + "The test InterpolatorTests input buffer is too small"); + + std::vector input (inputSize); + constexpr auto inputGaussianMidpoint = (float) (inputSize - 1) / 2.0f; + constexpr auto inputGaussianValueAtEnds = 0.000001f; + const auto inputGaussianWidth = std::sqrt (-std::log (inputGaussianValueAtEnds)) / inputGaussianMidpoint; + + createGaussian (input, 1.0f, inputGaussianMidpoint, inputGaussianWidth); + + for (auto speedRatio : { 0.4, 0.8263, 1.0, 1.05, 1.2384, 1.6 }) + { + const auto expectedGaussianMidpoint = (inputGaussianMidpoint + InterpolatorType::getBaseLatency()) / (float) speedRatio; + const auto expectedGaussianWidth = inputGaussianWidth * (float) speedRatio; + + const auto outputBufferSize = (size_t) std::floor ((float) input.size() / speedRatio); + + for (int numBlocks : { 1, 5 }) + { + const auto inputBlockSize = (float) input.size() / (float) numBlocks; + const auto outputBlockSize = (int) std::floor (inputBlockSize / speedRatio); + + std::vector output (outputBufferSize, std::numeric_limits::min()); + + beginTest (interpolatorName + " process " + String (numBlocks) + " blocks ratio " + String (speedRatio)); + + interpolator.reset(); + + { + auto* inputPtr = input.data(); + auto* outputPtr = output.data(); + + for (int i = 0; i < numBlocks; ++i) + { + auto numInputSamplesRead = interpolator.process (speedRatio, inputPtr, outputPtr, outputBlockSize); + inputPtr += numInputSamplesRead; + outputPtr += outputBlockSize; + } + } + + expectWithinAbsoluteError (findGaussianPeak (output), expectedGaussianMidpoint, 0.1f); + + std::vector expectedOutput (output.size()); + createGaussian (expectedOutput, 1.0f, expectedGaussianMidpoint, expectedGaussianWidth); + + expectAllElementsWithin (output, expectedOutput, 0.02f); + + beginTest (interpolatorName + " process adding " + String (numBlocks) + " blocks ratio " + String (speedRatio)); + + interpolator.reset(); + + constexpr float addingGain = 0.7384f; + + { + auto* inputPtr = input.data(); + auto* outputPtr = output.data(); + + for (int i = 0; i < numBlocks; ++i) + { + auto numInputSamplesRead = interpolator.processAdding (speedRatio, inputPtr, outputPtr, outputBlockSize, addingGain); + inputPtr += numInputSamplesRead; + outputPtr += outputBlockSize; + } + } + + expectWithinAbsoluteError (findGaussianPeak (output), expectedGaussianMidpoint, 0.1f); + + std::vector additionalOutput (output.size()); + createGaussian (additionalOutput, addingGain, expectedGaussianMidpoint, expectedGaussianWidth); + FloatVectorOperations::add (expectedOutput.data(), additionalOutput.data(), (int) additionalOutput.size()); + + expectAllElementsWithin (output, expectedOutput, 0.02f); + } + + beginTest (interpolatorName + " process wrap 0 ratio " + String (speedRatio)); + + std::vector doubleLengthOutput (2 * outputBufferSize, std::numeric_limits::min()); + + interpolator.reset(); + interpolator.process (speedRatio, input.data(), doubleLengthOutput.data(), (int) doubleLengthOutput.size(), + (int) input.size(), 0); + + std::vector expectedDoubleLengthOutput (doubleLengthOutput.size()); + createGaussian (expectedDoubleLengthOutput, 1.0f, expectedGaussianMidpoint, expectedGaussianWidth); + + expectAllElementsWithin (doubleLengthOutput, expectedDoubleLengthOutput, 0.02f); + + beginTest (interpolatorName + " process wrap double ratio " + String (speedRatio)); + + interpolator.reset(); + interpolator.process (speedRatio, input.data(), doubleLengthOutput.data(), (int) doubleLengthOutput.size(), + (int) input.size(), (int) input.size()); + + std::vector secondGaussian (doubleLengthOutput.size()); + createGaussian (secondGaussian, 1.0f, expectedGaussianMidpoint + (float) outputBufferSize, expectedGaussianWidth); + FloatVectorOperations::add (expectedDoubleLengthOutput.data(), secondGaussian.data(), (int) expectedDoubleLengthOutput.size()); + + expectAllElementsWithin (doubleLengthOutput, expectedDoubleLengthOutput, 0.02f); + } + } + +public: + void runTest() override + { + runInterplatorTests ("WindowedSincInterpolator"); + runInterplatorTests ("LagrangeInterpolator"); + runInterplatorTests ("CatmullRomInterpolator"); + runInterplatorTests ("LinearInterpolator"); + } +}; + +static InterpolatorTests interpolatorTests; + +#endif + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Interpolators.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Interpolators.h new file mode 100644 index 00000000..de17fa6e --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Interpolators.h @@ -0,0 +1,243 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +/** + A collection of different interpolators for resampling streams of floats. + + @see GenericInterpolator, WindowedSincInterpolator, LagrangeInterpolator, + CatmullRomInterpolator, LinearInterpolator, ZeroOrderHoldInterpolator + + @tags{Audio} +*/ +class Interpolators +{ +private: + struct WindowedSincTraits + { + static constexpr float algorithmicLatency = 100.0f; + + static forcedinline float windowedSinc (float firstFrac, int index) noexcept + { + auto index2 = index + 1; + auto frac = firstFrac; + + auto value1 = lookupTable[index]; + auto value2 = lookupTable[index2]; + + return value1 + (frac * (value2 - value1)); + } + + static forcedinline float valueAtOffset (const float* const inputs, const float offset, int indexBuffer) noexcept + { + const int numCrossings = 100; + const float floatCrossings = (float) numCrossings; + float result = 0.0f; + + auto samplePosition = indexBuffer; + float firstFrac = 0.0f; + float lastSincPosition = -1.0f; + int index = 0, sign = -1; + + for (int i = -numCrossings; i <= numCrossings; ++i) + { + auto sincPosition = (1.0f - offset) + (float) i; + + if (i == -numCrossings || (sincPosition >= 0 && lastSincPosition < 0)) + { + auto indexFloat = (sincPosition >= 0.f ? sincPosition : -sincPosition) * 100.0f; + auto indexFloored = std::floor (indexFloat); + index = (int) indexFloored; + firstFrac = indexFloat - indexFloored; + sign = (sincPosition < 0 ? -1 : 1); + } + + if (exactlyEqual (sincPosition, 0.0f)) + result += inputs[samplePosition]; + else if (sincPosition < floatCrossings && sincPosition > -floatCrossings) + result += inputs[samplePosition] * windowedSinc (firstFrac, index); + + if (++samplePosition == numCrossings * 2) + samplePosition = 0; + + lastSincPosition = sincPosition; + index += 100 * sign; + } + + return result; + } + + static const float lookupTable[10001]; + }; + + struct LagrangeTraits + { + static constexpr float algorithmicLatency = 2.0f; + + static float valueAtOffset (const float*, float, int) noexcept; + }; + + struct CatmullRomTraits + { + //============================================================================== + static constexpr float algorithmicLatency = 2.0f; + + static forcedinline float valueAtOffset (const float* const inputs, const float offset, int index) noexcept + { + auto y0 = inputs[index]; if (++index == 4) index = 0; + auto y1 = inputs[index]; if (++index == 4) index = 0; + auto y2 = inputs[index]; if (++index == 4) index = 0; + auto y3 = inputs[index]; + + auto halfY0 = 0.5f * y0; + auto halfY3 = 0.5f * y3; + + return y1 + offset * ((0.5f * y2 - halfY0) + + (offset * (((y0 + 2.0f * y2) - (halfY3 + 2.5f * y1)) + + (offset * ((halfY3 + 1.5f * y1) - (halfY0 + 1.5f * y2)))))); + } + }; + + struct LinearTraits + { + static constexpr float algorithmicLatency = 1.0f; + + static forcedinline float valueAtOffset (const float* const inputs, const float offset, int index) noexcept + { + auto y0 = inputs[index]; + auto y1 = inputs[index == 0 ? 1 : 0]; + + return y1 * offset + y0 * (1.0f - offset); + } + }; + + struct ZeroOrderHoldTraits + { + static constexpr float algorithmicLatency = 0.0f; + + static forcedinline float valueAtOffset (const float* const inputs, const float, int) noexcept + { + return inputs[0]; + } + }; + +public: + using WindowedSinc = GenericInterpolator; + using Lagrange = GenericInterpolator; + using CatmullRom = GenericInterpolator; + using Linear = GenericInterpolator; + using ZeroOrderHold = GenericInterpolator; +}; + +//============================================================================== +/** + An interpolator for resampling a stream of floats using high order windowed + (hann) sinc interpolation, recommended for high quality resampling. + + Note that the resampler is stateful, so when there's a break in the continuity + of the input stream you're feeding it, you should call reset() before feeding + it any new data. And like with any other stateful filter, if you're resampling + multiple channels, make sure each one uses its own WindowedSincInterpolator + object. + + @see GenericInterpolator + + @see LagrangeInterpolator, CatmullRomInterpolator, LinearInterpolator, + ZeroOrderHoldInterpolator + + @tags{Audio} +*/ +using WindowedSincInterpolator = Interpolators::WindowedSinc; + +/** + An interpolator for resampling a stream of floats using 4-point lagrange interpolation. + + Note that the resampler is stateful, so when there's a break in the continuity + of the input stream you're feeding it, you should call reset() before feeding + it any new data. And like with any other stateful filter, if you're resampling + multiple channels, make sure each one uses its own LagrangeInterpolator object. + + @see GenericInterpolator + + @see CatmullRomInterpolator, WindowedSincInterpolator, LinearInterpolator, + ZeroOrderHoldInterpolator + + @tags{Audio} +*/ +using LagrangeInterpolator = Interpolators::Lagrange; + +/** + An interpolator for resampling a stream of floats using Catmull-Rom interpolation. + + Note that the resampler is stateful, so when there's a break in the continuity + of the input stream you're feeding it, you should call reset() before feeding + it any new data. And like with any other stateful filter, if you're resampling + multiple channels, make sure each one uses its own CatmullRomInterpolator object. + + @see GenericInterpolator + + @see LagrangeInterpolator, WindowedSincInterpolator, LinearInterpolator, + ZeroOrderHoldInterpolator + + @tags{Audio} +*/ +using CatmullRomInterpolator = Interpolators::CatmullRom; + +/** + An interpolator for resampling a stream of floats using linear interpolation. + + Note that the resampler is stateful, so when there's a break in the continuity + of the input stream you're feeding it, you should call reset() before feeding + it any new data. And like with any other stateful filter, if you're resampling + multiple channels, make sure each one uses its own LinearInterpolator object. + + @see GenericInterpolator + + @see LagrangeInterpolator, CatmullRomInterpolator, WindowedSincInterpolator, + ZeroOrderHoldInterpolator + + @tags{Audio} +*/ +using LinearInterpolator = Interpolators::Linear; + +/** + An interpolator for resampling a stream of floats using zero order hold + interpolation. + + Note that the resampler is stateful, so when there's a break in the continuity + of the input stream you're feeding it, you should call reset() before feeding + it any new data. And like with any other stateful filter, if you're resampling + multiple channels, make sure each one uses its own ZeroOrderHoldInterpolator + object. + + @see GenericInterpolator + + @see LagrangeInterpolator, CatmullRomInterpolator, WindowedSincInterpolator, + LinearInterpolator + + @tags{Audio} +*/ +using ZeroOrderHoldInterpolator = Interpolators::ZeroOrderHold; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.cpp b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.cpp index 16af330d..33a43928 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,445 +23,40 @@ namespace juce { -namespace -{ - static forcedinline void pushInterpolationSample (float* lastInputSamples, float newValue) noexcept - { - lastInputSamples[4] = lastInputSamples[3]; - lastInputSamples[3] = lastInputSamples[2]; - lastInputSamples[2] = lastInputSamples[1]; - lastInputSamples[1] = lastInputSamples[0]; - lastInputSamples[0] = newValue; - } - - static forcedinline void pushInterpolationSamples (float* lastInputSamples, const float* input, int numOut) noexcept - { - if (numOut >= 5) - { - for (int i = 0; i < 5; ++i) - lastInputSamples[i] = input[--numOut]; - } - else - { - for (int i = 0; i < numOut; ++i) - pushInterpolationSample (lastInputSamples, input[i]); - } - } - - static forcedinline void pushInterpolationSamples (float* lastInputSamples, const float* input, - int numOut, int available, int wrapAround) noexcept - { - if (numOut >= 5) - { - if (available >= 5) - { - for (int i = 0; i < 5; ++i) - lastInputSamples[i] = input[--numOut]; - } - else - { - for (int i = 0; i < available; ++i) - lastInputSamples[i] = input[--numOut]; - - if (wrapAround > 0) - { - numOut -= wrapAround; - - for (int i = available; i < 5; ++i) - lastInputSamples[i] = input[--numOut]; - } - else - { - for (int i = available; i < 5; ++i) - lastInputSamples[i] = 0.0f; - } - } - } - else - { - if (numOut > available) - { - for (int i = 0; i < available; ++i) - pushInterpolationSample (lastInputSamples, input[i]); - - if (wrapAround > 0) - { - for (int i = 0; i < numOut - available; ++i) - pushInterpolationSample (lastInputSamples, input[i + available - wrapAround]); - } - else - { - for (int i = 0; i < numOut - available; ++i) - pushInterpolationSample (lastInputSamples, 0); - } - } - else - { - for (int i = 0; i < numOut; ++i) - pushInterpolationSample (lastInputSamples, input[i]); - } - } - } - - template - static int interpolate (float* lastInputSamples, double& subSamplePos, double actualRatio, - const float* in, float* out, int numOut) noexcept - { - auto pos = subSamplePos; - - if (actualRatio == 1.0 && pos == 1.0) - { - memcpy (out, in, (size_t) numOut * sizeof (float)); - pushInterpolationSamples (lastInputSamples, in, numOut); - return numOut; - } - - int numUsed = 0; - - while (numOut > 0) - { - while (pos >= 1.0) - { - pushInterpolationSample (lastInputSamples, in[numUsed++]); - pos -= 1.0; - } - - *out++ = InterpolatorType::valueAtOffset (lastInputSamples, (float) pos); - pos += actualRatio; - --numOut; - } - - subSamplePos = pos; - return numUsed; - } - - template - static int interpolate (float* lastInputSamples, double& subSamplePos, double actualRatio, - const float* in, float* out, int numOut, int available, int wrap) noexcept - { - if (actualRatio == 1.0) - { - if (available >= numOut) - { - memcpy (out, in, (size_t) numOut * sizeof (float)); - pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap); - } - else - { - memcpy (out, in, (size_t) available * sizeof (float)); - pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap); - - if (wrap > 0) - { - memcpy (out + available, in + available - wrap, (size_t) (numOut - available) * sizeof (float)); - pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap); - } - else - { - for (int i = 0; i < numOut - available; ++i) - pushInterpolationSample (lastInputSamples, 0); - } - } - - return numOut; - } - - auto originalIn = in; - auto pos = subSamplePos; - bool exceeded = false; - - if (actualRatio < 1.0) - { - for (int i = numOut; --i >= 0;) - { - if (pos >= 1.0) - { - if (exceeded) - { - pushInterpolationSample (lastInputSamples, 0); - } - else - { - pushInterpolationSample (lastInputSamples, *in++); - - if (--available <= 0) - { - if (wrap > 0) - { - in -= wrap; - available += wrap; - } - else - { - exceeded = true; - } - } - } - - pos -= 1.0; - } - - *out++ = InterpolatorType::valueAtOffset (lastInputSamples, (float) pos); - pos += actualRatio; - } - } - else - { - for (int i = numOut; --i >= 0;) - { - while (pos < actualRatio) - { - if (exceeded) - { - pushInterpolationSample (lastInputSamples, 0); - } - else - { - pushInterpolationSample (lastInputSamples, *in++); - - if (--available <= 0) - { - if (wrap > 0) - { - in -= wrap; - available += wrap; - } - else - { - exceeded = true; - } - } - } - - pos += 1.0; - } - - pos -= actualRatio; - *out++ = InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos)); - } - } - - subSamplePos = pos; - - if (wrap == 0) - return (int) (in - originalIn); - - return ((int) (in - originalIn) + wrap) % wrap; - } - - template - static int interpolateAdding (float* lastInputSamples, double& subSamplePos, double actualRatio, - const float* in, float* out, int numOut, - int available, int wrap, float gain) noexcept - { - if (actualRatio == 1.0) - { - if (available >= numOut) - { - FloatVectorOperations::addWithMultiply (out, in, gain, numOut); - pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap); - } - else - { - FloatVectorOperations::addWithMultiply (out, in, gain, available); - pushInterpolationSamples (lastInputSamples, in, available, available, wrap); - - if (wrap > 0) - { - FloatVectorOperations::addWithMultiply (out, in - wrap, gain, numOut - available); - pushInterpolationSamples (lastInputSamples, in - wrap, numOut - available, available, wrap); - } - else - { - for (int i = 0; i < numOut-available; ++i) - pushInterpolationSample (lastInputSamples, 0.0); - } - } - - return numOut; - } - - auto originalIn = in; - auto pos = subSamplePos; - bool exceeded = false; - - if (actualRatio < 1.0) - { - for (int i = numOut; --i >= 0;) - { - if (pos >= 1.0) - { - if (exceeded) - { - pushInterpolationSample (lastInputSamples, 0.0); - } - else - { - pushInterpolationSample (lastInputSamples, *in++); - - if (--available <= 0) - { - if (wrap > 0) - { - in -= wrap; - available += wrap; - } - else - { - exceeded = true; - } - } - } - - pos -= 1.0; - } - - *out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, (float) pos); - pos += actualRatio; - } - } - else - { - for (int i = numOut; --i >= 0;) - { - while (pos < actualRatio) - { - if (exceeded) - { - pushInterpolationSample (lastInputSamples, 0.0); - } - else - { - pushInterpolationSample (lastInputSamples, *in++); - - if (--available <= 0) - { - if (wrap > 0) - { - in -= wrap; - available += wrap; - } - else - { - exceeded = true; - } - } - } - - pos += 1.0; - } - - pos -= actualRatio; - *out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos)); - } - } - - subSamplePos = pos; - - if (wrap == 0) - return (int) (in - originalIn); - - return ((int) (in - originalIn) + wrap) % wrap; - } - - template - static int interpolateAdding (float* lastInputSamples, double& subSamplePos, double actualRatio, - const float* in, float* out, int numOut, float gain) noexcept - { - auto pos = subSamplePos; - - if (actualRatio == 1.0 && pos == 1.0) - { - FloatVectorOperations::addWithMultiply (out, in, gain, numOut); - pushInterpolationSamples (lastInputSamples, in, numOut); - return numOut; - } - - int numUsed = 0; - - while (numOut > 0) - { - while (pos >= 1.0) - { - pushInterpolationSample (lastInputSamples, in[numUsed++]); - pos -= 1.0; - } - - *out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, (float) pos); - pos += actualRatio; - --numOut; - } - - subSamplePos = pos; - return numUsed; - } -} - -//============================================================================== template struct LagrangeResampleHelper { static forcedinline void calc (float& a, float b) noexcept { a *= b * (1.0f / k); } }; -template<> +template <> struct LagrangeResampleHelper<0> { static forcedinline void calc (float&, float) noexcept {} }; -struct LagrangeAlgorithm -{ - static forcedinline float valueAtOffset (const float* inputs, float offset) noexcept - { - return calcCoefficient<0> (inputs[4], offset) - + calcCoefficient<1> (inputs[3], offset) - + calcCoefficient<2> (inputs[2], offset) - + calcCoefficient<3> (inputs[1], offset) - + calcCoefficient<4> (inputs[0], offset); - } - - template - static forcedinline float calcCoefficient (float input, float offset) noexcept - { - LagrangeResampleHelper<0 - k>::calc (input, -2.0f - offset); - LagrangeResampleHelper<1 - k>::calc (input, -1.0f - offset); - LagrangeResampleHelper<2 - k>::calc (input, 0.0f - offset); - LagrangeResampleHelper<3 - k>::calc (input, 1.0f - offset); - LagrangeResampleHelper<4 - k>::calc (input, 2.0f - offset); - return input; - } -}; - -LagrangeInterpolator::LagrangeInterpolator() noexcept { reset(); } -LagrangeInterpolator::~LagrangeInterpolator() noexcept {} - -void LagrangeInterpolator::reset() noexcept -{ - subSamplePos = 1.0; - - for (auto& s : lastInputSamples) - s = 0; -} - -int LagrangeInterpolator::process (double actualRatio, const float* in, float* out, int numOut, int available, int wrap) noexcept -{ - return interpolate (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap); +template +static float calcCoefficient (float input, float offset) noexcept +{ + LagrangeResampleHelper<0 - k>::calc (input, -2.0f - offset); + LagrangeResampleHelper<1 - k>::calc (input, -1.0f - offset); + LagrangeResampleHelper<2 - k>::calc (input, 0.0f - offset); + LagrangeResampleHelper<3 - k>::calc (input, 1.0f - offset); + LagrangeResampleHelper<4 - k>::calc (input, 2.0f - offset); + return input; } -int LagrangeInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept +float Interpolators::LagrangeTraits::valueAtOffset (const float* inputs, float offset, int index) noexcept { - return interpolate (lastInputSamples, subSamplePos, actualRatio, in, out, numOut); -} + float result = 0.0f; -int LagrangeInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, int available, int wrap, float gain) noexcept -{ - return interpolateAdding (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap, gain); -} + result += calcCoefficient<0> (inputs[index], offset); if (++index == 5) index = 0; + result += calcCoefficient<1> (inputs[index], offset); if (++index == 5) index = 0; + result += calcCoefficient<2> (inputs[index], offset); if (++index == 5) index = 0; + result += calcCoefficient<3> (inputs[index], offset); if (++index == 5) index = 0; + result += calcCoefficient<4> (inputs[index], offset); -int LagrangeInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept -{ - return interpolateAdding (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain); + return result; } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.h deleted file mode 100644 index 3489795a..00000000 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.h +++ /dev/null @@ -1,146 +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 -{ - -/** - Interpolator for resampling a stream of floats using 4-point lagrange interpolation. - - Note that the resampler is stateful, so when there's a break in the continuity - of the input stream you're feeding it, you should call reset() before feeding - it any new data. And like with any other stateful filter, if you're resampling - multiple channels, make sure each one uses its own LagrangeInterpolator - object. - - @see CatmullRomInterpolator - - @tags{Audio} -*/ -class JUCE_API LagrangeInterpolator -{ -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. - */ - void reset() noexcept; - - /** Resamples a stream of samples. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results into - @param numOutputSamplesToProduce the number of output samples that should be created - - @returns the actual number of input samples that were used - */ - int process (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce) noexcept; - - /** Resamples a stream of samples. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results into - @param numOutputSamplesToProduce the number of output samples that should be created - @param available the number of available input samples. If it needs more samples - than available, it either wraps back for wrapAround samples, or - it feeds zeroes - @param wrapAround if the stream exceeds available samples, it wraps back for - wrapAround samples. If wrapAround is set to 0, it will feed zeroes. - - @returns the actual number of input samples that were used - */ - int process (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce, - int available, - int wrapAround) noexcept; - - /** Resamples a stream of samples, adding the results to the output data - with a gain. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results to - the result values will be added - to any pre-existing data in this buffer after being multiplied by - the gain factor - @param numOutputSamplesToProduce the number of output samples that should be created - @param gain a gain factor to multiply the resulting samples by before - adding them to the destination buffer - - @returns the actual number of input samples that were used - */ - int processAdding (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce, - float gain) noexcept; - - /** Resamples a stream of samples, adding the results to the output data - with a gain. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results to - the result values will be added - to any pre-existing data in this buffer after being multiplied by - the gain factor - @param numOutputSamplesToProduce the number of output samples that should be created - @param available the number of available input samples. If it needs more samples - than available, it either wraps back for wrapAround samples, or - it feeds zeroes - @param wrapAround if the stream exceeds available samples, it wraps back for - wrapAround samples. If wrapAround is set to 0, it will feed zeroes. - @param gain a gain factor to multiply the resulting samples by before - adding them to the destination buffer - - @returns the actual number of input samples that were used - */ - int processAdding (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce, - int available, - int wrapAround, - float gain) noexcept; - -private: - float lastInputSamples[5]; - double subSamplePos; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator) -}; - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Reverb.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Reverb.h index b88c7f7b..b63c844f 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Reverb.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Reverb.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -131,10 +131,12 @@ class Reverb /** Applies the reverb to two stereo channels of audio data. */ void processStereo (float* const left, float* const right, const int numSamples) noexcept { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011) jassert (left != nullptr && right != nullptr); for (int i = 0; i < numSamples; ++i) { + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) const float input = (left[i] + right[i]) * gain; float outL = 0, outR = 0; @@ -160,11 +162,13 @@ class Reverb left[i] = outL * wet1 + outR * wet2 + left[i] * dry; right[i] = outR * wet1 + outL * wet2 + right[i] * dry; } + JUCE_END_IGNORE_WARNINGS_MSVC } /** Applies the reverb to a single mono channel of audio data. */ void processMono (float* const samples, const int numSamples) noexcept { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011) jassert (samples != nullptr); for (int i = 0; i < numSamples; ++i) @@ -186,6 +190,7 @@ class Reverb samples[i] = output * wet1 + samples[i] * dry; } + JUCE_END_IGNORE_WARNINGS_MSVC } private: diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp index 6c7d5163..6e03bab9 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2018 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,7 +28,7 @@ namespace juce static CommonSmoothedValueTests > commonLinearSmoothedValueTests; static CommonSmoothedValueTests > commonMultiplicativeSmoothedValueTests; -class SmoothedValueTests : public UnitTest +class SmoothedValueTests final : public UnitTest { public: SmoothedValueTests() diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h index 36570aa5..0497dd18 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -58,8 +58,6 @@ class SmoothedValueBase /** Constructor. */ SmoothedValueBase() = default; - virtual ~SmoothedValueBase() {} - //============================================================================== /** Returns true if the current value is currently being interpolated. */ bool isSmoothing() const noexcept { return countdown > 0; } @@ -231,7 +229,7 @@ class SmoothedValue : public SmoothedValueBase ::value ? 0 : 1)) + : SmoothedValue ((FloatType) (std::is_same_v ? 0 : 1)) { } @@ -239,7 +237,8 @@ class SmoothedValue : public SmoothedValueBase ::value && initialValue == 0)); + jassert (! (std::is_same_v + && approximatelyEqual (initialValue, (FloatType) 0))); // Visual Studio can't handle base class initialisation with CRTP this->currentValue = initialValue; @@ -272,7 +271,7 @@ class SmoothedValue : public SmoothedValueBase target) + if (approximatelyEqual (newValue, this->target)) return; if (stepsToTarget <= 0) @@ -282,7 +281,8 @@ class SmoothedValue : public SmoothedValueBase ::value && newValue == 0)); + jassert (! (std::is_same_v + && approximatelyEqual (newValue, (FloatType) 0))); this->target = newValue; this->countdown = stepsToTarget; @@ -330,9 +330,8 @@ class SmoothedValue : public SmoothedValueBase lsv.setTargetValue (x); lsv.setValue (x, true); -> lsv.setCurrentAndTargetValue (x); @@ -340,7 +339,8 @@ class SmoothedValue : public SmoothedValueBase - using LinearVoid = typename std::enable_if ::value, void>::type; - - template - using MultiplicativeVoid = typename std::enable_if ::value, void>::type; - //============================================================================== template - LinearVoid setStepSize() noexcept + void setStepSize() noexcept { - step = (this->target - this->currentValue) / (FloatType) this->countdown; - } - - template - MultiplicativeVoid setStepSize() - { - step = std::exp ((std::log (std::abs (this->target)) - std::log (std::abs (this->currentValue))) / this->countdown); + if constexpr (std::is_same_v) + { + step = (this->target - this->currentValue) / (FloatType) this->countdown; + } + else if constexpr (std::is_same_v) + { + step = std::exp ((std::log (std::abs (this->target)) - std::log (std::abs (this->currentValue))) / (FloatType) this->countdown); + } } //============================================================================== template - LinearVoid setNextValue() noexcept - { - this->currentValue += step; - } - - template - MultiplicativeVoid setNextValue() noexcept + void setNextValue() noexcept { - this->currentValue *= step; + if constexpr (std::is_same_v) + { + this->currentValue += step; + } + else if constexpr (std::is_same_v) + { + this->currentValue *= step; + } } //============================================================================== template - LinearVoid skipCurrentValue (int numSamples) noexcept + void skipCurrentValue (int numSamples) noexcept { - this->currentValue += step * (FloatType) numSamples; - } - - template - MultiplicativeVoid skipCurrentValue (int numSamples) - { - this->currentValue *= (FloatType) std::pow (step, numSamples); + if constexpr (std::is_same_v) + { + this->currentValue += step * (FloatType) numSamples; + } + else if constexpr (std::is_same_v) + { + this->currentValue *= (FloatType) std::pow (step, numSamples); + } } //============================================================================== @@ -510,7 +507,7 @@ class CommonSmoothedValueTests : public UnitTest expect (referenceData.getSample (0, 10) < sv.getTargetValue()); expectWithinAbsoluteError (referenceData.getSample (0, 11), sv.getTargetValue(), - 1.0e-7f); + 2.0e-7f); auto getUnitData = [] (int numSamplesToGenerate) { @@ -522,13 +519,13 @@ class CommonSmoothedValueTests : public UnitTest return result; }; - auto compareData = [this](const AudioBuffer& test, - const AudioBuffer& reference) + auto compareData = [this] (const AudioBuffer& test, + const AudioBuffer& reference) { for (int i = 0; i < test.getNumSamples(); ++i) expectWithinAbsoluteError (test.getSample (0, i), reference.getSample (0, i), - 1.0e-7f); + 2.0e-7f); }; auto testData = getUnitData (numSamples); diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_WindowedSincInterpolator.cpp b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_WindowedSincInterpolator.cpp new file mode 100644 index 00000000..ba9aa42e --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_WindowedSincInterpolator.cpp @@ -0,0 +1,10030 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +const float Interpolators::WindowedSincTraits::lookupTable[] { + 1.000000000000000000e+00f, + 9.998321499902942389e-01f, + 9.993287014045605376e-01f, + 9.984899584999463729e-01f, + 9.973164281284172539e-01f, + 9.958088193720359138e-01f, + 9.939680430327538785e-01f, + 9.917952109771142055e-01f, + 9.892916353363806481e-01f, + 9.864588275627174108e-01f, + 9.832984973421626806e-01f, + 9.798125513652424790e-01f, + 9.760030919561943907e-01f, + 9.718724155618675420e-01f, + 9.674230111014888722e-01f, + 9.626575581785837832e-01f, + 9.575789251564525983e-01f, + 9.521901670987075184e-01f, + 9.464945235764864462e-01f, + 9.404954163440536474e-01f, + 9.341964468846075675e-01f, + 9.276013938282199334e-01f, + 9.207142102439148657e-01f, + 9.135390208080165220e-01f, + 9.060801188509671755e-01f, + 8.983419632849326542e-01f, + 8.903291754145852277e-01f, + 8.820465356335611684e-01f, + 8.734989800091675916e-01f, + 8.646915967580026674e-01f, + 8.556296226152392270e-01f, + 8.463184391004026086e-01f, + 8.367635686825474206e-01f, + 8.269706708478274937e-01f, + 8.169455380725142435e-01f, + 8.066940917045994919e-01f, + 7.962223777571848515e-01f, + 7.855365626169290572e-01f, + 7.746429286708844675e-01f, + 7.635478698551215748e-01f, + 7.522578871285925395e-01f, + 7.407795838757467166e-01f, + 7.291196612414603262e-01f, + 7.172849134018910444e-01f, + 7.052822227749226958e-01f, + 6.931185551739007522e-01f, + 6.808009549084045320e-01f, + 6.683365398358420695e-01f, + 6.557324963676841589e-01f, + 6.429960744341879364e-01f, + 6.301345824114912286e-01f, + 6.171553820149802139e-01f, + 6.040658831628585856e-01f, + 5.908735388138642852e-01f, + 5.775858397830959667e-01f, + 5.642103095399239043e-01f, + 5.507544989919713752e-01f, + 5.372259812591544392e-01f, + 5.236323464417774742e-01f, + 5.099811963866732745e-01f, + 4.962801394553849055e-01f, + 4.825367852983662442e-01f, + 4.687587396391774686e-01f, + 4.549535990726381041e-01f, + 4.411289458808779762e-01f, + 4.272923428712149252e-01f, + 4.134513282397586642e-01f, + 3.996134104646188456e-01f, + 3.857860632325655903e-01f, + 3.719767204029549301e-01f, + 3.581927710127054132e-01f, + 3.444415543260631041e-01f, + 3.307303549328605707e-01f, + 3.170663978989262666e-01f, + 3.034568439722529765e-01f, + 2.899087848484924179e-01f, + 2.764292384992768636e-01f, + 2.630251445668272403e-01f, + 2.497033598282397682e-01f, + 2.364706537327855851e-01f, + 2.233337040154965702e-01f, + 2.102990923902373910e-01f, + 1.973733003254070084e-01f, + 1.845627049053287227e-01f, + 1.718735747803255554e-01f, + 1.593120662083951089e-01f, + 1.468842191913231343e-01f, + 1.345959537079954305e-01f, + 1.224530660475812854e-01f, + 1.104612252451845633e-01f, + 9.862596962246501786e-02f, + 8.695270343564839854e-02f, + 7.544669363325365308e-02f, + 6.411306672577052335e-02f, + 5.295680576943356810e-02f, + 4.198274746613558234e-02f, + 3.119557938143482229e-02f, + 2.059983728250635790e-02f, + 1.019990259779169663e-02f, + 3.897210076277699062e-17f, + -9.995804885990083877e-03f, + -1.978360764887690526e-02f, + -2.935966712899029663e-02f, + -3.872040732740943125e-02f, + -4.786241918501666498e-02f, + -5.678246223045373131e-02f, + -6.547746609606562573e-02f, + -7.394453190101335505e-02f, + -8.218093350085642346e-02f, + -9.018411860300616645e-02f, + -9.795170974756001181e-02f, + -1.054815051531433207e-01f, + -1.127714794274846111e-01f, + -1.198197841425640842e-01f, + -1.266247482742862418e-01f, + -1.331848785067307750e-01f, + -1.394988594011491723e-01f, + -1.455655534299787057e-01f, + -1.513840008762573630e-01f, + -1.569534195989277237e-01f, + -1.622732046646232218e-01f, + -1.673429278466408709e-01f, + -1.721623369919041413e-01f, + -1.767313552568272883e-01f, + -1.810500802130950160e-01f, + -1.851187828244720002e-01f, + -1.889379062958614630e-01f, + -1.925080647959259983e-01f, + -1.958300420546892351e-01f, + -1.989047898376287982e-01f, + -2.017334262978687787e-01f, + -2.043172342081740200e-01f, + -2.066576590745419495e-01f, + -2.087563071332755593e-01f, + -2.106149432335147065e-01f, + -2.122354886072851110e-01f, + -2.136200185292154441e-01f, + -2.147707598681500851e-01f, + -2.156900885329726125e-01f, + -2.163805268150296168e-01f, + -2.168447406296239166e-01f, + -2.170855366591212532e-01f, + -2.171058594002854003e-01f, + -2.169087881185291500e-01f, + -2.164975337118347498e-01f, + -2.158754354871634251e-01f, + -2.150459578522372073e-01f, + -2.140126869256352149e-01f, + -2.127793270682061255e-01f, + -2.113496973388519218e-01f, + -2.097277278777923137e-01f, + -2.079174562204673671e-01f, + -2.059230235452841895e-01f, + -2.037486708584572426e-01f, + -2.013987351192330633e-01f, + -1.988776453088292318e-01f, + -1.961899184464536705e-01f, + -1.933401555558005303e-01f, + -1.903330375854543621e-01f, + -1.871733212866534823e-01f, + -1.838658350518955764e-01f, + -1.804154747178837448e-01f, + -1.768271993363320083e-01f, + -1.731060269161642517e-01f, + -1.692570301406509103e-01f, + -1.652853320630413847e-01f, + -1.611961017842489241e-01f, + -1.569945501161558454e-01f, + -1.526859252341015438e-01f, + -1.482755083221153625e-01f, + -1.437686092144501160e-01f, + -1.391705620369629026e-01f, + -1.344867208518796264e-01f, + -1.297224553094619426e-01f, + -1.248831463100814054e-01f, + -1.199741816801844635e-01f, + -1.150009518656041618e-01f, + -1.099688456456556773e-01f, + -1.048832458714182708e-01f, + -9.974952523157622208e-02f, + -9.457304204915860379e-02f, + -8.935913611247510435e-02f, + -8.411312454351556334e-02f, + -7.884029770702483120e-02f, + -7.354591516343182700e-02f, + -6.823520166875686466e-02f, + -6.291334322457314832e-02f, + -5.758548318104762115e-02f, + -5.225671839602590713e-02f, + -4.693209545307914371e-02f, + -4.161660694135679156e-02f, + -3.631518780004174690e-02f, + -3.103271173013015527e-02f, + -2.577398767619723877e-02f, + -2.054375638074063329e-02f, + -1.534668701362280202e-02f, + -1.018737387906320201e-02f, + -5.070333202553640312e-03f, + -3.894325756689964161e-17f, + 5.019274968688979509e-03f, + 9.983228159422152803e-03f, + 1.488768611509041877e-02f, + 1.972856824609843865e-02f, + 2.450188951836063458e-02f, + 2.920376304695326483e-02f, + 3.383040259369500535e-02f, + 3.837812496701634851e-02f, + 4.284335232256628045e-02f, + 4.722261436309905386e-02f, + 5.151255043626536828e-02f, + 5.570991152903332494e-02f, + 5.981156215754928479e-02f, + 6.381448215134405411e-02f, + 6.771576833088721603e-02f, + 7.151263607757946117e-02f, + 7.520242079537053925e-02f, + 7.878257926329076954e-02f, + 8.225069087826682168e-02f, + 8.560445878770081563e-02f, + 8.884171091137989251e-02f, + 9.196040085238070538e-02f, + 9.495860869672850813e-02f, + 9.783454170166935859e-02f, + 1.005865348725034220e-01f, + 1.032130514280261008e-01f, + 1.057126831547209184e-01f, + 1.080841506499338051e-01f, + 1.103263034543625065e-01f, + 1.124381200742803483e-01f, + 1.144187078940081537e-01f, + 1.162673029792436336e-01f, + 1.179832697719408463e-01f, + 1.195661006775294716e-01f, + 1.210154155453490032e-01f, + 1.223309610432607719e-01f, + 1.235126099274892336e-01f, + 1.245603602088281303e-01f, + 1.254743342164319486e-01f, + 1.262547775604956901e-01f, + 1.269020579952085670e-01f, + 1.274166641834456049e-01f, + 1.277992043647412090e-01f, + 1.280504049281650791e-01f, + 1.281711088917953933e-01f, + 1.281622742905593748e-01f, + 1.280249724742810735e-01f, + 1.277603863178479460e-01f, + 1.273698083454741226e-01f, + 1.268546387711058931e-01f, + 1.262163834570771692e-01f, + 1.254566517931865743e-01f, + 1.245771544984256851e-01f, + 1.235797013476476885e-01f, + 1.224661988255190126e-01f, + 1.212386477101517479e-01f, + 1.198991405888646528e-01f, + 1.184498593085685503e-01f, + 1.168930723633207630e-01f, + 1.152311322216329759e-01f, + 1.134664725961638115e-01f, + 1.116016056584608895e-01f, + 1.096391192014578575e-01f, + 1.075816737524645378e-01f, + 1.054319996394198350e-01f, + 1.031928940132054995e-01f, + 1.008672178288464866e-01f, + 9.845789278844846448e-02f, + 9.596789824874003838e-02f, + 9.340026809611248759e-02f, + 9.075808759205977738e-02f, + 8.804449019193870407e-02f, + 8.526265433997994025e-02f, + 8.241580024348756084e-02f, + 7.950718662917011237e-02f, + 7.654010748455197799e-02f, + 7.351788878741070954e-02f, + 7.044388522618756643e-02f, + 6.732147691431115966e-02f, + 6.415406610136678567e-02f, + 6.094507388403559711e-02f, + 5.769793691971696753e-02f, + 5.441610414572558424e-02f, + 5.110303350694805208e-02f, + 4.776218869481312773e-02f, + 4.439703590041457065e-02f, + 4.101104058459904139e-02f, + 3.760766426780321608e-02f, + 3.419036134239462205e-02f, + 3.076257591024030361e-02f, + 2.732773864819467574e-02f, + 2.388926370415486722e-02f, + 2.045054562630352346e-02f, + 1.701495632810792399e-02f, + 1.358584209160797196e-02f, + 1.016652061148060233e-02f, + 6.760278082314462311e-03f, + 3.370366331490179130e-03f, + 3.889521720231377172e-17f, + -3.347646226505052690e-03f, + -6.669440334209695946e-03f, + -9.962295642995425485e-03f, + -1.322317341155379231e-02f, + -1.644908538369398501e-02f, + -1.963709627384800302e-02f, + -2.278432618984587571e-02f, + -2.588795299108807499e-02f, + -2.894521458031795724e-02f, + -3.195341112725825355e-02f, + -3.490990722244410177e-02f, + -3.781213395966125795e-02f, + -4.065759094545921132e-02f, + -4.344384823429099651e-02f, + -4.616854818789777049e-02f, + -4.882940725763235124e-02f, + -5.142421768849166769e-02f, + -5.395084914370364831e-02f, + -5.640725024878998073e-02f, + -5.879145005410169045e-02f, + -6.110155941490798270e-02f, + -6.333577228818905447e-02f, + -6.549236694536995917e-02f, + -6.756970710030717198e-02f, + -6.956624295191975171e-02f, + -7.148051214093797956e-02f, + -7.331114062032219325e-02f, + -7.505684343898266775e-02f, + -7.671642543851386886e-02f, + -7.828878186273431627e-02f, + -7.977289887990460238e-02f, + -8.116785401757553586e-02f, + -8.247281651009769066e-02f, + -8.368704755890117586e-02f, + -8.480990050573701533e-02f, + -8.584082091914502222e-02f, + -8.677934659449468291e-02f, + -8.762510746802033845e-02f, + -8.837782544534880658e-02f, + -8.903731414509374886e-02f, + -8.960347855816586105e-02f, + -9.007631462352130858e-02f, + -9.045590872114415981e-02f, + -9.074243708313121937e-02f, + -9.093616512381758055e-02f, + -9.103744668995188138e-02f, + -9.104672323199845818e-02f, + -9.096452289771120303e-02f, + -9.079145954918992067e-02f, + -9.052823170469484482e-02f, + -9.017562140655861758e-02f, + -8.973449301659575106e-02f, + -8.920579194047170701e-02f, + -8.859054328255137889e-02f, + -8.788985043280389542e-02f, + -8.710489358739707810e-02f, + -8.623692820466778708e-02f, + -8.528728339820713933e-02f, + -8.425736026884980689e-02f, + -8.314863017740434237e-02f, + -8.196263296000964871e-02f, + -8.070097508804566222e-02f, + -7.936532777456997700e-02f, + -7.795742502929374484e-02f, + -7.647906166414730755e-02f, + -7.493209125152387740e-02f, + -7.331842403732392877e-02f, + -7.164002481095702035e-02f, + -6.989891073448464831e-02f, + -6.809714913312167606e-02f, + -6.623685524933471680e-02f, + -6.432018996280304546e-02f, + -6.234935747852995286e-02f, + -6.032660298541026728e-02f, + -5.825421028757805930e-02f, + -5.613449941087523404e-02f, + -5.396982418679260179e-02f, + -5.176256981624846598e-02f, + -4.951515041557684460e-02f, + -4.723000654710355489e-02f, + -4.490960273669408059e-02f, + -4.255642498066016660e-02f, + -4.017297824440700660e-02f, + -3.776178395520819048e-02f, + -3.532537749148359496e-02f, + -3.286630567095341626e-02f, + -3.038712424003185350e-02f, + -2.789039536681189174e-02f, + -2.537868513997948439e-02f, + -2.285456107598123413e-02f, + -2.032058963675330288e-02f, + -1.777933376029512333e-02f, + -1.523335040635749593e-02f, + -1.268518811948355154e-02f, + -1.013738461162063884e-02f, + -7.592464366493653291e-03f, + -5.052936267896138492e-03f, + -2.521291254032028643e-03f, + -3.882802707905923401e-17f, + 2.508489369517248428e-03f, + 5.001753535334178304e-03f, + 7.477396204630696805e-03f, + 9.933050295659225093e-03f, + 1.236638008438947431e-02f, + 1.477508331123026167e-02f, + 1.715689324600980226e-02f, + 1.950958070945080572e-02f, + 2.183095604941610521e-02f, + 2.411887107025306037e-02f, + 2.637122091361803269e-02f, + 2.858594588921111249e-02f, + 3.076103325390444967e-02f, + 3.289451893780558772e-02f, + 3.498448921585006754e-02f, + 3.702908232357217388e-02f, + 3.902649001576798810e-02f, + 4.097495906681519057e-02f, + 4.287279271148002058e-02f, + 4.471835202509810703e-02f, + 4.651005724208144565e-02f, + 4.824638901176293154e-02f, + 4.992588959065285409e-02f, + 5.154716397024793756e-02f, + 5.310888093959532930e-02f, + 5.460977408187722010e-02f, + 5.604864270435185730e-02f, + 5.742435270104698924e-02f, + 5.873583734766929521e-02f, + 5.998209802826119652e-02f, + 6.116220489320042114e-02f, + 6.227529744820328728e-02f, + 6.332058507406373993e-02f, + 6.429734747692100738e-02f, + 6.520493506891764102e-02f, + 6.604276927917780704e-02f, + 6.681034279509827367e-02f, + 6.750721973401248299e-02f, + 6.813303574535393980e-02f, + 6.868749804350914034e-02f, + 6.917038537161544764e-02f, + 6.958154789662478190e-02f, + 6.992090703601620827e-02f, + 7.018845521660418973e-02f, + 7.038425556595274968e-02f, + 7.050844153696640693e-02f, + 7.056121646629046062e-02f, + 7.054285306721332083e-02f, + 7.045369285782319968e-02f, + 7.029414552522963988e-02f, + 7.006468822671796381e-02f, + 6.976586482876150075e-02f, + 6.939828508487122516e-02f, + 6.896262375331772831e-02f, + 6.845961965581227882e-02f, + 6.789007467828668541e-02f, + 6.725485271496202400e-02f, + 6.655487855694536270e-02f, + 6.579113672664134438e-02f, + 6.496467025931394745e-02f, + 6.407657943317585092e-02f, + 6.312802044942868174e-02f, + 6.212020406372047165e-02f, + 6.105439417052395401e-02f, + 5.993190634198172773e-02f, + 5.875410632280082118e-02f, + 5.752240848281407054e-02f, + 5.623827422886059496e-02f, + 5.490321037767217222e-02f, + 5.351876749147910922e-02f, + 5.208653817808057279e-02f, + 5.060815535715459945e-02f, + 4.908529049460156474e-02f, + 4.751965180674513900e-02f, + 4.591298243623419262e-02f, + 4.426705860150847993e-02f, + 4.258368772170978422e-02f, + 4.086470651893956557e-02f, + 3.911197909977250781e-02f, + 3.732739501795391546e-02f, + 3.551286732021802228e-02f, + 3.367033057717121886e-02f, + 3.180173890119344232e-02f, + 2.990906395331716441e-02f, + 2.799429294104273033e-02f, + 2.605942660905504957e-02f, + 2.410647722480178945e-02f, + 2.213746656089659454e-02f, + 2.015442387629913859e-02f, + 1.815938389822458268e-02f, + 1.615438480672493765e-02f, + 1.414146622387316317e-02f, + 1.212266720947206833e-02f, + 1.010002426519698848e-02f, + 8.075569349063350597e-03f, + 6.051327902095616530e-03f, + 4.029316889058113388e-03f, + 2.011542855081033601e-03f, + 3.874175350567572650e-17f, + -2.003331727797961342e-03f, + -3.996488516257723500e-03f, + -5.977524551525892349e-03f, + -7.944513855095518967e-03f, + -9.895552094544582400e-03f, + -1.182875836621732034e-02f, + -1.374227694822771975e-02f, + -1.563427902220560287e-02f, + -1.750296436223446397e-02f, + -1.934656298946970526e-02f, + -2.116333679096629727e-02f, + -2.295158110128149576e-02f, + -2.470962624546017145e-02f, + -2.643583904205206586e-02f, + -2.812862426485629452e-02f, + -2.978642606212655863e-02f, + -3.140772933202141265e-02f, + -3.299106105312992615e-02f, + -3.453499156894547506e-02f, + -3.603813582521487452e-02f, + -3.749915455913428036e-02f, + -3.891675543941802012e-02f, + -4.028969415631109691e-02f, + -4.161677546067264061e-02f, + -4.289685415130901208e-02f, + -4.412883600978426135e-02f, + -4.531167868199265092e-02f, + -4.644439250583275169e-02f, + -4.752604128436910269e-02f, + -4.855574300393437209e-02f, + -4.953267049666788130e-02f, + -5.045605204704892155e-02f, + -5.132517194203765898e-02f, + -5.213937096448940933e-02f, + -5.289804682956641274e-02f, + -5.360065456392785338e-02f, + -5.424670682753111178e-02f, + -5.483577417793748798e-02f, + -5.536748527706834150e-02f, + -5.584152704041558252e-02f, + -5.625764472876377403e-02f, + -5.661564198253816321e-02f, + -5.691538079894768037e-02f, + -5.715678145214542205e-02f, + -5.733982235668498573e-02f, + -5.746453987460405782e-02f, + -5.753102806651958639e-02f, + -5.753943838717312520e-02f, + -5.748997932591520116e-02f, + -5.738291599267093812e-02f, + -5.721856964997878298e-02f, + -5.699731719174516015e-02f, + -5.671959056940642507e-02f, + -5.638587616623912807e-02f, + -5.599671412060634040e-02f, + -5.555269759897443066e-02f, + -5.505447201958245385e-02f, + -5.450273422768755344e-02f, + -5.389823162335748846e-02f, + -5.324176124282154615e-02f, + -5.253416879443297688e-02f, + -5.177634765033799907e-02f, + -5.096923779498445384e-02f, + -5.011382473164045781e-02f, + -4.921113834813311411e-02f, + -4.826225174304939192e-02f, + -4.726828001367859577e-02f, + -4.623037900700535663e-02f, + -4.514974403509764561e-02f, + -4.402760855626140085e-02f, + -4.286524282336175162e-02f, + -4.166395250074144546e-02f, + -4.042507725118998224e-02f, + -3.914998929443946202e-02f, + -3.784009193869437848e-02f, + -3.649681808671064592e-02f, + -3.512162871797221836e-02f, + -3.371601134852347803e-02f, + -3.228147847003334792e-02f, + -3.081956596968622730e-02f, + -2.933183153250423780e-02f, + -2.781985302771665045e-02f, + -2.628522688080791453e-02f, + -2.472956643287790510e-02f, + -2.315450028896036058e-02f, + -2.156167065694567300e-02f, + -1.995273167876449386e-02f, + -1.832934775548567333e-02f, + -1.669319186798288307e-02f, + -1.504594389482788452e-02f, + -1.338928892906133400e-02f, + -1.172491559548742221e-02f, + -1.005451437013836664e-02f, + -8.379775903540833196e-03f, + -6.702389349412756978e-03f, + -5.024040700403363362e-03f, + -3.346411132483765802e-03f, + -1.671175359577019423e-03f, + -3.863648162376423474e-17f, + 1.665458043716377392e-03f, + 3.323553190096430274e-03f, + 4.972652767551836474e-03f, + 6.611138585273553490e-03f, + 8.237408484711907647e-03f, + 9.849877870182622078e-03f, + 1.144698121717139215e-02f, + 1.302717355693413029e-02f, + 1.458893193601275978e-02f, + 1.613075684932130729e-02f, + 1.765117364548211831e-02f, + 1.914873390312786527e-02f, + 2.062201677691140322e-02f, + 2.206963031200360678e-02f, + 2.349021272589388309e-02f, + 2.488243365633890924e-02f, + 2.624499537434637694e-02f, + 2.757663396111778922e-02f, + 2.887612044790845850e-02f, + 3.014226191780771530e-02f, + 3.137390256847804454e-02f, + 3.256992473493734719e-02f, + 3.372924987150486414e-02f, + 3.485083949207803955e-02f, + 3.593369606795079885e-02f, + 3.697686388242336919e-02f, + 3.797942984150279311e-02f, + 3.894052424003760504e-02f, + 3.985932148267041369e-02f, + 4.073504075904751942e-02f, + 4.156694667276145810e-02f, + 4.235434982355489736e-02f, + 4.309660734236082924e-02f, + 4.379312337879810030e-02f, + 4.444334954079259131e-02f, + 4.504678528604074556e-02f, + 4.560297826507851010e-02f, + 4.611152461576945721e-02f, + 4.657206920907153852e-02f, + 4.698430584599172510e-02f, + 4.734797740568404828e-02f, + 4.766287594469555827e-02f, + 4.792884274741274209e-02f, + 4.814576832780684268e-02f, + 4.831359238262550315e-02f, + 4.843230369622460174e-02f, + 4.850193999728014821e-02f, + 4.852258776766780329e-02f, + 4.849438200384208891e-02f, + 4.841750593109390294e-02f, + 4.829219067110925023e-02f, + 4.811871486329679509e-02f, + 4.789740424039561073e-02f, + 4.762863115891774468e-02f, + 4.731281408502291397e-02f, + 4.695041703646362502e-02f, + 4.654194898128217323e-02f, + 4.608796319397825969e-02f, + 4.558905656990844268e-02f, + 4.504586889871579902e-02f, + 4.445908209762516189e-02f, + 4.382941940547854798e-02f, + 4.315764453841920700e-02f, + 4.244456080816757376e-02f, + 4.169101020386845047e-02f, + 4.089787243851888271e-02f, + 4.006606396102018602e-02f, + 3.919653693492577279e-02f, + 3.829027818498920155e-02f, + 3.734830811264253092e-02f, + 3.637167958156230230e-02f, + 3.536147677450902915e-02f, + 3.431881402264831682e-02f, + 3.324483460858414924e-02f, + 3.214070954436337030e-02f, + 3.100763632572139938e-02f, + 2.984683766386852025e-02f, + 2.865956019612891231e-02f, + 2.744707317676135447e-02f, + 2.621066714931024125e-02f, + 2.495165260184606035e-02f, + 2.367135860646735895e-02f, + 2.237113144445156826e-02f, + 2.105233321844750261e-02f, + 1.971634045311482925e-02f, + 1.836454268561935055e-02f, + 1.699834104740382407e-02f, + 1.561914683865467043e-02f, + 1.422838009688787327e-02f, + 1.282746816108333576e-02f, + 1.141784423279322037e-02f, + 1.000094593564865818e-02f, + 8.578213874691159244e-03f, + 7.151090196945879447e-03f, + 5.721017154652988448e-03f, + 4.289435672562395829e-03f, + 2.857783920694851112e-03f, + 1.427495893958731022e-03f, + 3.851231532396259107e-17f, + -1.423282343340041833e-03f, + -2.840938099612009063e-03f, + -4.251563993045895384e-03f, + -5.653767874212517128e-03f, + -7.046170070757393038e-03f, + -8.427404721911885624e-03f, + -9.796121095514335672e-03f, + -1.115098488629330704e-02f, + -1.249067949418276348e-02f, + -1.381390728146736184e-02f, + -1.511939080757592098e-02f, + -1.640587404037048558e-02f, + -1.767212354279941791e-02f, + -1.891692963381457968e-02f, + -2.013910752248064540e-02f, + -2.133749841423027843e-02f, + -2.251097058825281272e-02f, + -2.365842044503514013e-02f, + -2.477877352310193029e-02f, + -2.587098548403953327e-02f, + -2.693404306491827341e-02f, + -2.796696499726556220e-02f, + -2.896880289177323695e-02f, + -2.993864208796185450e-02f, + -3.087560246806111269e-02f, + -3.177883923439979441e-02f, + -3.264754364963919769e-02f, + -3.348094373922329975e-02f, + -3.427830495545182432e-02f, + -3.503893080263100618e-02f, + -3.576216342278743660e-02f, + -3.644738414147634387e-02f, + -3.709401397325490235e-02f, + -3.770151408643012542e-02f, + -3.826938622673421719e-02f, + -3.879717309962135707e-02f, + -3.928445871091951203e-02f, + -3.973086866561512109e-02f, + -4.013607042458825630e-02f, + -4.049977351915948903e-02f, + -4.082172972334963124e-02f, + -4.110173318379706925e-02f, + -4.133962050731876392e-02f, + -4.153527080614199951e-02f, + -4.168860570087641604e-02f, + -4.179958928133744450e-02f, + -4.186822802537215615e-02f, + -4.189457067588118339e-02f, + -4.187870807626980357e-02f, + -4.182077296460211258e-02f, + -4.172093972677210633e-02f, + -4.157942410904505448e-02f, + -4.139648289036141487e-02f, + -4.117241351483449247e-02f, + -4.090755368491114791e-02f, + -4.060228091570139419e-02f, + -4.025701205102193148e-02f, + -3.987220274173181034e-02f, + -3.944834688697711200e-02f, + -3.898597603899465330e-02f, + -3.848565877215900932e-02f, + -3.794800001699184799e-02f, + -3.737364035988401112e-02f, + -3.676325530931268126e-02f, + -3.611755452936806637e-02f, + -3.543728104143289981e-02f, + -3.472321039488834166e-02f, + -3.397614980774645715e-02f, + -3.319693727813939621e-02f, + -3.238644066761971890e-02f, + -3.154555675725123631e-02f, + -3.067521027749671447e-02f, + -2.977635291292965100e-02f, + -2.884996228281870578e-02f, + -2.789704089865994302e-02f, + -2.691861509974309216e-02f, + -2.591573396786606143e-02f, + -2.488946822232386211e-02f, + -2.384090909631557051e-02f, + -2.277116719593108921e-02f, + -2.168137134289090454e-02f, + -2.057266740222485346e-02f, + -1.944621709609118537e-02f, + -1.830319680494362292e-02f, + -1.714479635726702186e-02f, + -1.597221780910678887e-02f, + -1.478667421462878662e-02f, + -1.358938838894852355e-02f, + -1.238159166447296011e-02f, + -1.116452264200503558e-02f, + -9.939425937859529617e-03f, + -8.707550928239674026e-03f, + -7.470150492127149849e-03f, + -6.228479753931915469e-03f, + -4.983794827149027815e-03f, + -3.737351560261789520e-03f, + -2.490404286130025371e-03f, + -1.244204576092051940e-03f, + -3.836937714341790683e-17f, + 1.240967106592762116e-03f, + 2.477460866896080867e-03f, + 3.708253077802783210e-03f, + 4.932124414085086068e-03f, + 6.147865620704987162e-03f, + 7.354278692082365058e-03f, + 8.550178037189383809e-03f, + 9.734391629351839964e-03f, + 1.090576213965407243e-02f, + 1.206314805286936957e-02f, + 1.320542476485127813e-02f, + 1.433148566034425815e-02f, + 1.544024317019840505e-02f, + 1.653062980698680531e-02f, + 1.760159917805776894e-02f, + 1.865212697506991868e-02f, + 1.968121193909236344e-02f, + 2.068787680036934112e-02f, + 2.167116919188432494e-02f, + 2.263016253588231996e-02f, + 2.356395690253657721e-02f, + 2.447167983997980412e-02f, + 2.535248717494724760e-02f, + 2.620556378330855113e-02f, + 2.703012432980111263e-02f, + 2.782541397630681687e-02f, + 2.859070905804561927e-02f, + 2.932531772709678214e-02f, + 3.002858056268817993e-02f, + 3.069987114772966194e-02f, + 3.133859661110379585e-02f, + 3.194419813525677804e-02f, + 3.251615142867379793e-02f, + 3.305396716285353975e-02f, + 3.355719137343705916e-02f, + 3.402540582517922518e-02f, + 3.445822834048845734e-02f, + 3.485531309129908523e-02f, + 3.521635085407364557e-02f, + 3.554106922777330868e-02f, + 3.582923281466892512e-02f, + 3.608064336390364585e-02f, + 3.629513987775428302e-02f, + 3.647259868057688842e-02f, + 3.661293345045708858e-02f, + 3.671609521362455969e-02f, + 3.678207230172601816e-02f, + 3.681089027208912096e-02f, + 3.680261179114461406e-02f, + 3.675733648121128072e-02f, + 3.667520073088332416e-02f, + 3.655637746929544379e-02f, + 3.640107590457612730e-02f, + 3.620954122683402165e-02f, + 3.598205427605740553e-02f, + 3.571893117533903733e-02f, + 3.542052292987402790e-02f, + 3.508721499220928547e-02f, + 3.471942679425710915e-02f, + 3.431761124661530937e-02f, + 3.388225420576965186e-02f, + 3.341387390978267646e-02f, + 3.291302038310394557e-02f, + 3.238027481116542705e-02f, + 3.181624888545521446e-02f, + 3.122158411978745787e-02f, + 3.059695113851780904e-02f, + 2.994304893747514948e-02f, + 2.926060411840791450e-02f, + 2.855037009776879506e-02f, + 2.781312629068145512e-02f, + 2.704967727096032129e-02f, + 2.626085190807075573e-02f, + 2.544750248194389483e-02f, + 2.461050377657450841e-02f, + 2.375075215335195958e-02f, + 2.286916460509363230e-02f, + 2.196667779176278423e-02f, + 2.104424705887289471e-02f, + 2.010284543959429265e-02f, + 1.914346264158977087e-02f, + 1.816710401962278246e-02f, + 1.717478953499241082e-02f, + 1.616755270285690979e-02f, + 1.514643952852146903e-02f, + 1.411250743377312315e-02f, + 1.306682417434898782e-02f, + 1.201046674963833320e-02f, + 1.094452030571538628e-02f, + 9.870077032812075643e-03f, + 8.788235058334580366e-03f, + 7.700097336538626325e-03f, + 6.606770535970839416e-03f, + 5.509363925787424780e-03f, + 4.408988262061687424e-03f, + 3.306754675183733866e-03f, + 2.203773559457802150e-03f, + 1.101153465996373331e-03f, + 3.820780814485725242e-17f, + -1.098585276486098786e-03f, + -2.193505935265162299e-03f, + -3.283671765059895803e-03f, + -4.367999845902193791e-03f, + -5.445415613796268477e-03f, + -6.514853914614496255e-03f, + -7.575260046206469533e-03f, + -8.625590787711353430e-03f, + -9.664815415077104843e-03f, + -1.069191670181151264e-02f, + -1.170589190400125879e-02f, + -1.270575372865473569e-02f, + -1.369053128444692319e-02f, + -1.465927101395613288e-02f, + -1.561103760651160084e-02f, + -1.654491489078413133e-02f, + -1.746000670628177626e-02f, + -1.835543775292705626e-02f, + -1.923035441792335376e-02f, + -2.008392557913836474e-02f, + -2.091534338425668146e-02f, + -2.172382400498286056e-02f, + -2.250860836560024739e-02f, + -2.326896284521650510e-02f, + -2.400417995305826127e-02f, + -2.471357897620288582e-02f, + -2.539650659916279848e-02f, + -2.605233749477112518e-02f, + -2.668047488584292248e-02f, + -2.728035107711810139e-02f, + -2.785142795702474916e-02f, + -2.839319746882786391e-02f, + -2.890518205076497357e-02f, + -2.938693504479734583e-02f, + -2.983804107364120342e-02f, + -3.025811638577266885e-02f, + -3.064680916813381298e-02f, + -3.100379982630099840e-02f, + -3.132880123190676103e-02f, + -3.162155893714221899e-02f, + -3.188185135619839294e-02f, + -3.210948991353843734e-02f, + -3.230431915892578026e-02f, + -3.246621684916723566e-02f, + -3.259509399656147777e-02f, + -3.269089488407847521e-02f, + -3.275359704732655475e-02f, + -3.278321122339819538e-02f, + -3.277978126671703824e-02f, + -3.274338403204220954e-02f, + -3.267412922481752885e-02f, + -3.257215921908560019e-02f, + -3.243764884321845982e-02f, + -3.227080513374773474e-02f, + -3.207186705760879664e-02f, + -3.184110520314317044e-02f, + -3.157882144023508447e-02f, + -3.128534854998637343e-02f, + -3.096104982436503894e-02f, + -3.060631863628960475e-02f, + -3.022157798064227172e-02f, + -2.980727998672943677e-02f, + -2.936390540273656549e-02f, + -2.889196305275088666e-02f, + -2.839198926695188710e-02f, + -2.786454728559313240e-02f, + -2.731022663742679185e-02f, + -2.672964249324305541e-02f, + -2.612343499522145479e-02f, + -2.549226856281475509e-02f, + -2.483683117590470071e-02f, + -2.415783363599420183e-02f, + -2.345600880621569531e-02f, + -2.273211083096061610e-02f, + -2.198691433594817182e-02f, + -2.122121360957183317e-02f, + -2.043582176638020914e-02f, + -1.963156989356096283e-02f, + -1.880930618131524887e-02f, + -1.796989503802343471e-02f, + -1.711421619111335238e-02f, + -1.624316377455840843e-02f, + -1.535764540394315113e-02f, + -1.445858124004167390e-02f, + -1.354690304186740787e-02f, + -1.262355321016035703e-02f, + -1.168948382228147259e-02f, + -1.074565565949768715e-02f, + -9.793037227638747358e-03f, + -8.832603772118776536e-03f, + -7.865336288311713595e-03f, + -6.892220528280442078e-03f, + -5.914246004853488010e-03f, + -4.932404994047567826e-03f, + -3.947691536835270382e-03f, + -2.961100441250685226e-03f, + -1.973626285828549223e-03f, + -9.862624253675238758e-04f, + -3.802776777737574016e-17f, + 9.841730514465974952e-04f, + 1.965272972826986615e-03f, + 2.942321165824257449e-03f, + 3.914345157906578068e-03f, + 4.880379562147291184e-03f, + 5.839467027964861424e-03f, + 6.790659181857343525e-03f, + 7.733017557214086520e-03f, + 8.665614512298646516e-03f, + 9.587534135515569239e-03f, + 1.049787313708307364e-02f, + 1.139574172625059875e-02f, + 1.228026447321984953e-02f, + 1.315058115493771343e-02f, + 1.400584758395503719e-02f, + 1.484523641955660424e-02f, + 1.566793796039387537e-02f, + 1.647316091786455577e-02f, + 1.726013316951060736e-02f, + 1.802810249172396087e-02f, + 1.877633727107054853e-02f, + 1.950412719356916028e-02f, + 2.021078391128274671e-02f, + 2.089564168560189075e-02f, + 2.155805800663082583e-02f, + 2.219741418810250791e-02f, + 2.281311593728354431e-02f, + 2.340459389934918671e-02f, + 2.397130417573897082e-02f, + 2.451272881602797882e-02f, + 2.502837628287757701e-02f, + 2.551778188965732330e-02f, + 2.598050821035589528e-02f, + 2.641614546142960138e-02f, + 2.682431185526281195e-02f, + 2.720465392494783932e-02f, + 2.755684682011533618e-02f, + 2.788059457358115292e-02f, + 2.817563033860144908e-02f, + 2.844171659655807607e-02f, + 2.867864533492812257e-02f, + 2.888623819541892568e-02f, + 2.906434659218049346e-02f, + 2.921285180003846058e-02f, + 2.933166501271905480e-02f, + 2.942072737106802074e-02f, + 2.948000996129596790e-02f, + 2.950951378331144459e-02f, + 2.950926968923310578e-02f, + 2.947933829220211049e-02f, + 2.941980984564465662e-02f, + 2.933080409316416587e-02f, + 2.921247008927130642e-02f, + 2.906498599118855983e-02f, + 2.888855882199470315e-02f, + 2.868342420540245102e-02f, + 2.844984607248968178e-02f, + 2.818811634073307634e-02f, + 2.789855456571845721e-02f, + 2.758150756593012731e-02f, + 2.723734902104646763e-02f, + 2.686647904419447314e-02f, + 2.646932372864202465e-02f, + 2.604633466943028636e-02f, + 2.559798846047255452e-02f, + 2.512478616767030437e-02f, + 2.462725277861903608e-02f, + 2.410593662949834029e-02f, + 2.356140880976381052e-02f, + 2.299426254527723104e-02f, + 2.240511256053460618e-02f, + 2.179459442066713701e-02f, + 2.116336385391225788e-02f, + 2.051209605527045191e-02f, + 1.984148497207598519e-02f, + 1.915224257223392679e-02f, + 1.844509809588383564e-02f, + 1.772079729127159706e-02f, + 1.698010163562099079e-02f, + 1.622378754181329208e-02f, + 1.545264555169341587e-02f, + 1.466747951683269359e-02f, + 1.386910576759178837e-02f, + 1.305835227133488388e-02f, + 1.223605778065572393e-02f, + 1.140307097248631669e-02f, + 1.056024957896441879e-02f, + 9.708459510942056905e-03f, + 8.848573975026030602e-03f, + 7.981472585042176035e-03f, + 7.108040468822868913e-03f, + 6.229167371217365738e-03f, + 5.345746754226499142e-03f, + 4.458674895167382204e-03f, + 3.568849983769900418e-03f, + 2.677171219111589007e-03f, + 1.784537907287459457e-03f, + 8.918485607199820456e-04f, + 1.375963414501481870e-16f, + -8.901135408467832598e-04f, + -1.777601304782279023e-03f, + -2.661576896985846409e-03f, + -3.541159164019371322e-03f, + -4.415473066061055551e-03f, + -5.283650541348036689e-03f, + -6.144831361984745034e-03f, + -6.998163980273221924e-03f, + -7.842806364743631908e-03f, + -8.677926825066678909e-03f, + -9.502704825047591716e-03f, + -1.031633178291084009e-02f, + -1.111801185810167188e-02f, + -1.190696272384546804e-02f, + -1.268241632471873645e-02f, + -1.344361961850560477e-02f, + -1.418983530162710137e-02f, + -1.492034251745096018e-02f, + -1.563443754680784656e-02f, + -1.633143448005669809e-02f, + -1.701066587006366121e-02f, + -1.767148336547917642e-02f, + -1.831325832371670168e-02f, + -1.893538240305999570e-02f, + -1.953726813334768359e-02f, + -2.011834946470413346e-02f, + -2.067808229381145821e-02f, + -2.121594496723701664e-02f, + -2.173143876136030342e-02f, + -2.222408833845995052e-02f, + -2.269344217855321158e-02f, + -2.313907298660076647e-02f, + -2.356057807471537494e-02f, + -2.395757971904149269e-02f, + -2.432972549099499809e-02f, + -2.467668856258116092e-02f, + -2.499816798553347250e-02f, + -2.529388894404376378e-02f, + -2.556360298088074245e-02f, + -2.580708819671996904e-02f, + -2.602414942253634608e-02f, + -2.621461836493719708e-02f, + -2.637835372434049752e-02f, + -2.651524128593106544e-02f, + -2.662519398335435120e-02f, + -2.670815193513444846e-02f, + -2.676408245383069331e-02f, + -2.679298002797401990e-02f, + -2.679486627685148323e-02f, + -2.676978987823442138e-02f, + -2.671782646917223933e-02f, + -2.663907852000062249e-02f, + -2.653367518173962297e-02f, + -2.640177210708273547e-02f, + -2.624355124520492619e-02f, + -2.605922061064226344e-02f, + -2.584901402652179520e-02f, + -2.561319084244592648e-02f, + -2.535203562735873287e-02f, + -2.506585783774740658e-02f, + -2.475499146155598143e-02f, + -2.441979463821075669e-02f, + -2.406064925518100819e-02f, + -2.367796052152183056e-02f, + -2.327215651886596973e-02f, + -2.284368773035500011e-02f, + -2.239302654802176329e-02f, + -2.192066675915328375e-02f, + -2.142712301218852727e-02f, + -2.091293026271947089e-02f, + -2.037864320018679606e-02f, + -1.982483565587724039e-02f, + -1.925209999284902343e-02f, + -1.866104647842747183e-02f, + -1.805230263992868914e-02f, + -1.742651260428690341e-02f, + -1.678433642227292188e-02f, + -1.612644937800953443e-02f, + -1.545354128449657159e-02f, + -1.476631576587938456e-02f, + -1.406548952719811375e-02f, + -1.335179161237267514e-02f, + -1.262596265118436090e-02f, + -1.188875409602925537e-02f, + -1.114092744922038150e-02f, + -1.038325348163284326e-02f, + -9.616511443484248786e-03f, + -8.841488268055055361e-03f, + -8.058977769158835736e-03f, + -7.269779833172840267e-03f, + -6.474699606447542784e-03f, + -5.674546678917565326e-03f, + -4.870134264732259473e-03f, + -4.062278380735639960e-03f, + -3.251797023616004832e-03f, + -2.439509346553158861e-03f, + -1.626234836185142018e-03f, + -8.127924907184056955e-04f, + -3.761300170173902632e-17f, + 8.113270716327609542e-04f, + 1.620376098912416430e-03f, + 2.426338204803805097e-03f, + 3.228409064469819033e-03f, + 4.025789703243586731e-03f, + 4.817687287821219300e-03f, + 5.603315909897037261e-03f, + 6.381897361469502858e-03f, + 7.152661901059502447e-03f, + 7.914849010088072581e-03f, + 8.667708138679361060e-03f, + 9.410499440156691597e-03f, + 1.014249449352174620e-02f, + 1.086297701321397788e-02f, + 1.157124354546169333e-02f, + 1.226660415055517636e-02f, + 1.294838307038302834e-02f, + 1.361591938058873304e-02f, + 1.426856762672633859e-02f, + 1.490569844380274823e-02f, + 1.552669915862028849e-02f, + 1.613097437434502304e-02f, + 1.671794653674912173e-02f, + 1.728705648159101563e-02f, + 1.783776396262254024e-02f, + 1.836954815972504512e-02f, + 1.888190816670458697e-02f, + 1.937436345829177120e-02f, + 1.984645433591701591e-02f, + 2.029774235185137846e-02f, + 2.072781071132718048e-02f, + 2.113626465227521484e-02f, + 2.152273180233675934e-02f, + 2.188686251283411544e-02f, + 2.222833016940416184e-02f, + 2.254683147902725865e-02f, + 2.284208673320283281e-02f, + 2.311384004705211576e-02f, + 2.336185957414964667e-02f, + 2.358593769691042299e-02f, + 2.378589119238579130e-02f, + 2.396156137334416389e-02f, + 2.411281420453805294e-02f, + 2.423954039408468888e-02f, + 2.434165545991162638e-02f, + 2.441909977124352010e-02f, + 2.447183856513244701e-02f, + 2.449986193805772303e-02f, + 2.450318481264677009e-02f, + 2.448184687959331640e-02f, + 2.443591251487354371e-02f, + 2.436547067238563694e-02f, + 2.427063475216228658e-02f, + 2.415154244432998407e-02f, + 2.400835554901284072e-02f, + 2.384125977240247538e-02f, + 2.365046449923850422e-02f, + 2.343620254196809860e-02f, + 2.319872986687494115e-02f, + 2.293832529749150265e-02f, + 2.265529019562997209e-02f, + 2.234994812038883921e-02f, + 2.202264446551434676e-02f, + 2.167374607551639937e-02f, + 2.130364084095871455e-02f, + 2.091273727336449839e-02f, + 2.050146406019705617e-02f, + 2.007026960039413541e-02f, + 1.961962152095459225e-02f, + 1.915000617509209774e-02f, + 1.866192812249098040e-02f, + 1.815590959221258585e-02f, + 1.763248992881976882e-02f, + 1.709222502230338045e-02f, + 1.653568672240555287e-02f, + 1.596346223795577085e-02f, + 1.537615352184286931e-02f, + 1.477437664226473150e-02f, + 1.415876114090650062e-02f, + 1.352994937871285740e-02f, + 1.288859586992923897e-02f, + 1.223536660509748700e-02f, + 1.157093836370297682e-02f, + 1.089599801717802338e-02f, + 1.021124182297453685e-02f, + 9.517374710428586071e-03f, + 8.815109559144568979e-03f, + 8.105166470632777995e-03f, + 7.388272033942047505e-03f, + 6.665158586030823715e-03f, + 5.936563467627156351e-03f, + 5.203228275329005968e-03f, + 4.465898110698729960e-03f, + 3.725320827109799334e-03f, + 2.982246275101344511e-03f, + 2.237425547001025122e-03f, + 1.491610221569002930e-03f, + 7.455516094238633746e-04f, + -4.603360121389620097e-17f, + -7.442960892099980894e-04f, + -1.486590660381493286e-03f, + -2.226140979308201089e-03f, + -2.962208314840183033e-03f, + -3.694058673076393295e-03f, + -4.420963525583790008e-03f, + -5.142200530928310413e-03f, + -5.857054248801788626e-03f, + -6.564816846046666980e-03f, + -7.264788793882578921e-03f, + -7.956279555653332503e-03f, + -8.638608264420137572e-03f, + -9.311104389740483545e-03f, + -9.973108392984011278e-03f, + -1.062397237054689761e-02f, + -1.126306068434249248e-02f, + -1.188975057895664926e-02f, + -1.250343278487292369e-02f, + -1.310351210718740250e-02f, + -1.368940799924666003e-02f, + -1.426055512066000397e-02f, + -1.481640387915378679e-02f, + -1.535642095575079358e-02f, + -1.588008981277712686e-02f, + -1.638691118421688533e-02f, + -1.687640354795164402e-02f, + -1.734810357944292433e-02f, + -1.780156658643218884e-02f, + -1.823636692425711220e-02f, + -1.865209839139694153e-02f, + -1.904837460488604042e-02f, + -1.942482935525157578e-02f, + -1.978111694065281395e-02f, + -2.011691247992323031e-02f, + -2.043191220423512186e-02f, + -2.072583372713042438e-02f, + -2.099841629268202337e-02f, + -2.124942100157323904e-02f, + -2.147863101490525214e-02f, + -2.168585173556427867e-02f, + -2.187091096700358173e-02f, + -2.203365904931835206e-02f, + -2.217396897251361007e-02f, + -2.229173646688911367e-02f, + -2.238688007048804385e-02f, + -2.245934117357874585e-02f, + -2.250908404016256636e-02f, + -2.253609580652320837e-02f, + -2.254038645685636430e-02f, + -2.252198877604117550e-02f, + -2.248095827963758273e-02f, + -2.241737312121652378e-02f, + -2.233133397715249616e-02f, + -2.222296390902992261e-02f, + -2.209240820383755882e-02f, + -2.193983419214646027e-02f, + -2.176543104448896288e-02f, + -2.156940954617817680e-02f, + -2.135200185082751848e-02f, + -2.111346121285143443e-02f, + -2.085406169924960626e-02f, + -2.057409788099558851e-02f, + -2.027388450437186615e-02f, + -1.995375614261341718e-02f, + -1.961406682823892828e-02f, + -1.925518966646928148e-02f, + -1.887751643015150166e-02f, + -1.848145713662151246e-02f, + -1.806743960696060095e-02f, + -1.763590900811298073e-02f, + -1.718732737835180352e-02f, + -1.672217313659431215e-02f, + -1.624094057608432493e-02f, + -1.574413934297350451e-02f, + -1.523229390034704089e-02f, + -1.470594297825480838e-02f, + -1.416563901031964515e-02f, + -1.361194755751050575e-02f, + -1.304544671967466062e-02f, + -1.246672653544142993e-02f, + -1.187638837111369521e-02f, + -1.127504429917872836e-02f, + -1.066331646707572613e-02f, + -1.004183645686986676e-02f, + -9.411244636485087758e-03f, + -8.772189503162801036e-03f, + -8.125327019812878665e-03f, + -7.471319944934195698e-03f, + -6.810837156787309839e-03f, + -6.144552972502608131e-03f, + -5.473146462815121210e-03f, + -4.797300763120759294e-03f, + -4.117702381546011793e-03f, + -3.435040504733359156e-03f, + -2.750006302037344939e-03f, + -2.063292228833295185e-03f, + -1.375591329636196821e-03f, + -6.875965417295367256e-04f, + -3.712671580871972529e-17f, + 6.865076563281155998e-04f, + 1.371237974366756743e-03f, + 2.053505374957837522e-03f, + 2.732627836105371629e-03f, + 3.407927571765508035e-03f, + 4.078731705322887272e-03f, + 4.744372937087731369e-03f, + 5.404190205152949872e-03f, + 6.057529338961288590e-03f, + 6.703743704936756392e-03f, + 7.342194843549852169e-03f, + 7.972253097187404283e-03f, + 8.593298228216065052e-03f, + 9.204720026634211766e-03f, + 9.805918906718476616e-03f, + 1.039630649208695898e-02f, + 1.097530618860973091e-02f, + 1.154235374461037682e-02f, + 1.209689779782041619e-02f, + 1.263840040855540407e-02f, + 1.316633757860326444e-02f, + 1.368019975532520915e-02f, + 1.417949232048846230e-02f, + 1.466373606336292788e-02f, + 1.513246763763515662e-02f, + 1.558524000170355243e-02f, + 1.602162284194235189e-02f, + 1.644120297853483154e-02f, + 1.684358475349722120e-02f, + 1.722839040053121898e-02f, + 1.759526039636302719e-02f, + 1.794385379324603749e-02f, + 1.827384853232209144e-02f, + 1.858494173755794940e-02f, + 1.887684998999094102e-02f, + 1.914930958204142525e-02f, + 1.940207675166573387e-02f, + 1.963492789614775663e-02f, + 1.984765976534554055e-02f, + 2.004008963423044992e-02f, + 2.021205545457873884e-02f, + 2.036341598569508388e-02f, + 2.049405090406888896e-02f, + 2.060386089188653500e-02f, + 2.069276770434292853e-02f, + 2.076071421571710346e-02f, + 2.080766444419874725e-02f, + 2.083360355547297926e-02f, + 2.083853784509219140e-02f, + 2.082249469968529276e-02f, + 2.078552253707531203e-02f, + 2.072769072539770024e-02f, + 2.064908948133232364e-02f, + 2.054982974758300454e-02f, + 2.043004304975895233e-02f, + 2.028988133283307627e-02f, + 2.012951677737172956e-02f, + 1.994914159575129214e-02f, + 1.974896780859568615e-02f, + 1.952922700168944689e-02f, + 1.929017006363938427e-02f, + 1.903206690457660832e-02f, + 1.875520615621007955e-02f, + 1.845989485356032758e-02f, + 1.814645809871968940e-02f, + 1.781523870700396292e-02f, + 1.746659683587628575e-02f, + 1.710090959704086561e-02f, + 1.671857065212146304e-02f, + 1.631998979235366534e-02f, + 1.590559250273780936e-02f, + 1.547581951111101986e-02f, + 1.503112632261381296e-02f, + 1.457198274004087128e-02f, + 1.409887237057541251e-02f, + 1.361229211942506165e-02f, + 1.311275167088374154e-02f, + 1.260077295736035038e-02f, + 1.207688961692321428e-02f, + 1.154164643992243444e-02f, + 1.099559880526047861e-02f, + 1.043931210689104556e-02f, + 9.873361171136581238e-03f, + 9.298329665421793330e-03f, + 8.714809499028142170e-03f, + 8.123400216482846645e-03f, + 7.524708384200954649e-03f, + 6.919346971004516206e-03f, + 6.307934723150263913e-03f, + 5.691095534499056308e-03f, + 5.069457812466930842e-03f, + 4.443653840398907248e-03f, + 3.814319137009273761e-03f, + 3.182091813536080255e-03f, + 2.547611929256090018e-03f, + 1.911520846010968415e-03f, + 1.274460582389453983e-03f, + 6.370731682176064472e-04f, + 1.081397080984820549e-16f, + -6.361188020413131080e-04f, + -1.270645034688974500e-03f, + -1.902943049573664223e-03f, + -2.532380387505061333e-03f, + -3.158328408659244208e-03f, + -3.780162917995376869e-03f, + -4.397264785285395425e-03f, + -5.009020559139008844e-03f, + -5.614823074421015914e-03f, + -6.214072052459541223e-03f, + -6.806174693455702487e-03f, + -7.390546260511059347e-03f, + -7.966610654700514993e-03f, + -8.533800980628097194e-03f, + -9.091560101911401459e-03f, + -9.639341186054086255e-03f, + -1.017660823817454450e-02f, + -1.070283662307290606e-02f, + -1.121751357513068974e-02f, + -1.172013869554890987e-02f, + -1.221022443644520679e-02f, + -1.268729657134461120e-02f, + -1.315089465161116758e-02f, + -1.360057244838422906e-02f, + -1.403589837959812528e-02f, + -1.445645592167801516e-02f, + -1.486184400552278267e-02f, + -1.525167739639943926e-02f, + -1.562558705739423154e-02f, + -1.598322049607744932e-02f, + -1.632424209406116086e-02f, + -1.664833341914373141e-02f, + -1.695519351975287997e-02f, + -1.724453920141957169e-02f, + -1.751610528503054487e-02f, + -1.776964484662796329e-02f, + -1.800492943854209438e-02f, + -1.822174929166297391e-02f, + -1.841991349867593691e-02f, + -1.859925017810457049e-02f, + -1.875960661902498217e-02f, + -1.890084940633438015e-02f, + -1.902286452647648438e-02f, + -1.912555745354639625e-02f, + -1.920885321571724064e-02f, + -1.927269644195031550e-02f, + -1.931705138897083646e-02f, + -1.934190194851057379e-02f, + -1.934725163483910423e-02f, + -1.933312355262460300e-02f, + -1.929956034518492611e-02f, + -1.924662412320928689e-02f, + -1.917439637405042932e-02f, + -1.908297785170610245e-02f, + -1.897248844762847511e-02f, + -1.884306704251837231e-02f, + -1.869487133928060194e-02f, + -1.852807767733529593e-02f, + -1.834288082849799481e-02f, + -1.813949377465985668e-02f, + -1.791814746751756518e-02f, + -1.767909057061923042e-02f, + -1.742258918401042023e-02f, + -1.714892655178226061e-02f, + -1.685840275283847015e-02f, + -1.655133437521590783e-02f, + -1.622805417430966263e-02f, + -1.588891071536682048e-02f, + -1.553426800063207637e-02f, + -1.516450508153929268e-02f, + -1.478001565636058098e-02f, + -1.438120765373634632e-02f, + -1.396850280252506793e-02f, + -1.354233618842332412e-02f, + -1.310315579781915797e-02f, + -1.265142204935544926e-02f, + -1.218760731368947504e-02f, + -1.171219542194896698e-02f, + -1.122568116339099642e-02f, + -1.072856977278602644e-02f, + -1.022137640805312678e-02f, + -9.704625618685933791e-03f, + -9.178850805514197561e-03f, + -8.644593672357078762e-03f, + -8.102403670126549079e-03f, + -7.552837433952721592e-03f, + -6.996458213902527305e-03f, + -6.433835299872929167e-03f, + -5.865543441244903435e-03f, + -5.292162261885334160e-03f, + -4.714275671091273240e-03f, + -4.132471271074346537e-03f, + -3.547339761581234756e-03f, + -2.959474342255256820e-03f, + -2.369470113338262643e-03f, + -1.777923475318417051e-03f, + -1.185431528126037071e-03f, + -5.925914704819672469e-04f, + -3.657082924657467151e-17f, + 5.917472853565048079e-04f, + 1.182056483865000056e-03f, + 1.770335984084810870e-03f, + 2.355997055758636289e-03f, + 2.938454436983550105e-03f, + 3.517126917070770645e-03f, + 4.091437914513436185e-03f, + 4.660816049491952121e-03f, + 5.224695710346304901e-03f, + 5.782517613461616614e-03f, + 6.333729356008878375e-03f, + 6.877785961000690458e-03f, + 7.414150414123792977e-03f, + 7.942294191823096386e-03f, + 8.461697780117830373e-03f, + 8.971851183644980604e-03f, + 9.472254424431190978e-03f, + 9.962418029908697709e-03f, + 1.044186350969892804e-02f, + 1.091012382070481734e-02f, + 1.136674382005683007e-02f, + 1.181128070548041059e-02f, + 1.224330444265740725e-02f, + 1.266239817917228644e-02f, + 1.306815864464697975e-02f, + 1.346019653668105442e-02f, + 1.383813689223166964e-02f, + 1.420161944407984794e-02f, + 1.455029896204724192e-02f, + 1.488384557864121625e-02f, + 1.520194509882482302e-02f, + 1.550429929362267742e-02f, + 1.579062617728919579e-02f, + 1.606066026778677283e-02f, + 1.631415283033408117e-02f, + 1.655087210380375498e-02f, + 1.677060350976731021e-02f, + 1.697314984400075458e-02f, + 1.715833145028389933e-02f, + 1.732598637634284386e-02f, + 1.747597051180499114e-02f, + 1.760815770805244929e-02f, + 1.772243987987905459e-02f, + 1.781872708887428242e-02f, + 1.789694760847562044e-02f, + 1.795704797064969285e-02f, + 1.799899299418070836e-02f, + 1.802276579456344244e-02f, + 1.802836777551610967e-02f, + 1.801581860214731406e-02f, + 1.798515615582916075e-02f, + 1.793643647084725012e-02f, + 1.786973365291627536e-02f, + 1.778513977966790505e-02f, + 1.768276478323590681e-02f, + 1.756273631508084576e-02f, + 1.742519959321422313e-02f, + 1.727031723200050592e-02f, + 1.709826905473061168e-02f, + 1.690925188917933447e-02f, + 1.670347934637539389e-02f, + 1.648118158282851561e-02f, + 1.624260504647542444e-02f, + 1.598801220662182665e-02f, + 1.571768126817298800e-02f, + 1.543190587046214431e-02f, + 1.513099477099881518e-02f, + 1.481527151447596842e-02f, + 1.448507408738769486e-02f, + 1.414075455862428936e-02f, + 1.378267870642391028e-02f, + 1.341122563207439514e-02f, + 1.302678736077021032e-02f, + 1.262976843004335811e-02f, + 1.222058546619729687e-02f, + 1.179966674918666268e-02f, + 1.136745176639336087e-02f, + 1.092439075576322714e-02f, + 1.047094423877575897e-02f, + 1.000758254372885632e-02f, + 9.534785319831791742e-03f, + 9.053041042603243369e-03f, + 8.562846511087114296e-03f, + 8.064706337396969349e-03f, + 7.559132429115091208e-03f, + 7.046643465073196971e-03f, + 6.527764365051815301e-03f, + 6.003025753936333010e-03f, + 5.472963420876755282e-03f, + 4.938117773999208156e-03f, + 4.399033291224269636e-03f, + 3.856257967746062627e-03f, + 3.310342760733056815e-03f, + 2.761841031809351716e-03f, + 2.211307987882755663e-03f, + 1.659300120877342261e-03f, + 1.106374646939666147e-03f, + 5.530889456780919066e-04f, + -2.562212629853384453e-17f, + -5.523361628926795478e-04f, + -1.103365028790336249e-03f, + -1.652534151487132074e-03f, + -2.199293704964780296e-03f, + -2.743097032196961139e-03f, + -3.283401190029548548e-03f, + -3.819667489593037233e-03f, + -4.351362031714790628e-03f, + -4.877956236795205104e-03f, + -5.398927368629055645e-03f, + -5.913759051650750720e-03f, + -6.421941781095417100e-03f, + -6.922973425571511859e-03f, + -7.416359721553146198e-03f, + -7.901614759304937882e-03f, + -8.378261459764673943e-03f, + -8.845832041915659832e-03f, + -9.303868480194682489e-03f, + -9.751922951488184790e-03f, + -1.018955827128389773e-02f, + -1.061634831855159944e-02f, + -1.103187844894640973e-02f, + -1.143574589593108516e-02f, + -1.182756015943418025e-02f, + -1.220694338166910739e-02f, + -1.257353070975459307e-02f, + -1.292697064479079137e-02f, + -1.326692537705756285e-02f, + -1.359307110701880525e-02f, + -1.390509835182837141e-02f, + -1.420271223705014962e-02f, + -1.448563277331862270e-02f, + -1.475359511768305730e-02f, + -1.500634981939290638e-02f, + -1.524366304989917265e-02f, + -1.546531681686134224e-02f, + -1.567110916196697012e-02f, + -1.586085434238630049e-02f, + -1.603438299570249595e-02f, + -1.619154228817324542e-02f, + -1.633219604619749049e-02f, + -1.645622487087768787e-02f, + -1.656352623558475859e-02f, + -1.665401456645098158e-02f, + -1.672762130573241662e-02f, + -1.678429495799975413e-02f, + -1.682400111913467597e-02f, + -1.684672248812496553e-02f, + -1.685245886166945525e-02f, + -1.684122711162113276e-02f, + -1.681306114531371312e-02f, + -1.676801184883415854e-02f, + -1.670614701332074833e-02f, + -1.662755124438308404e-02f, + -1.653232585475732791e-02f, + -1.642058874032645624e-02f, + -1.629247423965218475e-02f, + -1.614813297718115451e-02f, + -1.598773169030448180e-02f, + -1.581145304046532168e-02f, + -1.561949540852535824e-02f, + -1.541207267461631135e-02f, + -1.518941398271757108e-02f, + -1.495176349021716013e-02f, + -1.469938010272622657e-02f, + -1.443253719443338481e-02f, + -1.415152231429803349e-02f, + -1.385663687839711923e-02f, + -1.354819584875125403e-02f, + -1.322652739897053030e-02f, + -1.289197256707447573e-02f, + -1.254488489584905733e-02f, + -1.218563006111956712e-02f, + -1.181458548832869243e-02f, + -1.143213995781868463e-02f, + -1.103869319922974775e-02f, + -1.063465547543513724e-02f, + -1.022044715644565907e-02f, + -9.796498283722697084e-03f, + -9.363248125350869672e-03f, + -8.921144722528617441e-03f, + -8.470644427841999072e-03f, + -8.012211435799141551e-03f, + -7.546317306102053729e-03f, + -7.073440480148323294e-03f, + -6.594065791253660332e-03f, + -6.108683969096916598e-03f, + -5.617791138891340720e-03f, + -5.121888315793346122e-03f, + -4.621480895060380706e-03f, + -4.117078138476948368e-03f, + -3.609192657567809014e-03f, + -3.098339894123382839e-03f, + -2.585037598558611194e-03f, + -2.069805306636893476e-03f, + -1.553163815081216479e-03f, + -1.035634656603983328e-03f, + -5.177395748811913952e-04f, + -3.594753584560424697e-17f, + 5.170634750964393008e-04f, + 1.032931616641648070e-03f, + 1.547087070033249757e-03f, + 2.059014877257667187e-03f, + 2.568202991233530484e-03f, + 3.074142786564025911e-03f, + 3.576329566188138527e-03f, + 4.074263063429547048e-03f, + 4.567447938941549164e-03f, + 5.055394272060948803e-03f, + 5.537618046079833753e-03f, + 6.013641626959552108e-03f, + 6.482994235012814500e-03f, + 6.945212409090805589e-03f, + 7.399840462817311795e-03f, + 7.846430932424257321e-03f, + 8.284545015748243785e-03f, + 8.713753001960018382e-03f, + 9.133634691605685765e-03f, + 9.543779806553546671e-03f, + 9.943788389443802139e-03f, + 1.033327119225793331e-02f, + 1.071185005362884461e-02f, + 1.107915826452845684e-02f, + 1.143484092198099322e-02f, + 1.177855527046098826e-02f, + 1.210997103065078462e-02f, + 1.242877071524243864e-02f, + 1.273464993148456723e-02f, + 1.302731767018629021e-02f, + 1.330649658090683578e-02f, + 1.357192323307192076e-02f, + 1.382334836277147004e-02f, + 1.406053710501154602e-02f, + 1.428326921120452730e-02f, + 1.449133925169799296e-02f, + 1.468455680315914995e-02f, + 1.486274662064523792e-02f, + 1.502574879420732167e-02f, + 1.517341888988939583e-02f, + 1.530562807500203924e-02f, + 1.542226322756440338e-02f, + 1.552322702982500066e-02f, + 1.560843804578798157e-02f, + 1.567783078268716879e-02f, + 1.573135573636695247e-02f, + 1.576897942054480972e-02f, + 1.579068437994683369e-02f, + 1.579646918732351482e-02f, + 1.578634842436942021e-02f, + 1.576035264658639731e-02f, + 1.571852833214611861e-02f, + 1.566093781482364236e-02f, + 1.558765920108961048e-02f, + 1.549878627146459784e-02f, + 1.539442836625458041e-02f, + 1.527471025580193729e-02f, + 1.513977199540266459e-02f, + 1.498976876505400853e-02f, + 1.482487069421324286e-02f, + 1.464526267176276438e-02f, + 1.445114414139052512e-02f, + 1.424272888261022586e-02f, + 1.402024477765924718e-02f, + 1.378393356452596412e-02f, + 1.353405057637283200e-02f, + 1.327086446763305755e-02f, + 1.299465692707358656e-02f, + 1.270572237812851030e-02f, + 1.240436766682034841e-02f, + 1.209091173759783630e-02f, + 1.176568529743146629e-02f, + 1.142903046851825692e-02f, + 1.108130042995956500e-02f, + 1.072285904878491486e-02f, + 1.035408050070699311e-02f, + 9.975348881000042217e-03f, + 9.587057805905971689e-03f, + 9.189610004980062336e-03f, + 8.783416904796481281e-03f, + 8.368898204444107092e-03f, + 7.946481443246316417e-03f, + 7.516601561152745289e-03f, + 7.079700452249429281e-03f, + 6.636226511847269707e-03f, + 6.186634177609999508e-03f, + 5.731383465191587125e-03f, + 5.270939498854294337e-03f, + 4.805772037546594995e-03f, + 4.336354996921345552e-03f, + 3.863165967780911834e-03f, + 3.386685731435247395e-03f, + 2.907397772465186801e-03f, + 2.425787789381585267e-03f, + 1.942343203677738541e-03f, + 1.457552667765169528e-03f, + 9.719055722931030025e-04f, + 4.858915533437137995e-04f, + 8.998451329969168033e-17f, + -4.852804372203572384e-04f, + -9.694623365727714258e-04f, + -1.452059993443630213e-03f, + -1.932589906371710072e-03f, + -2.410571260246647390e-03f, + -2.885526406203937043e-03f, + -3.356981337736776681e-03f, + -3.824466162554774652e-03f, + -4.287515569716226875e-03f, + -4.745669291575628328e-03f, + -5.198472560085647988e-03f, + -5.645476557004205746e-03f, + -6.086238857560498199e-03f, + -6.520323867144547751e-03f, + -6.947303250588880534e-03f, + -7.366756353621596760e-03f, + -7.778270616075868595e-03f, + -8.181441976453041054e-03f, + -8.575875267442242128e-03f, + -8.961184602012178466e-03f, + -9.336993749696280778e-03f, + -9.702936502709572433e-03f, + -1.005865703153830532e-02f, + -1.040381022966105004e-02f, + -1.073806204706719697e-02f, + -1.106108981225218876e-02f, + -1.137258254238081648e-02f, + -1.167224124132068379e-02f, + -1.195977918526302392e-02f, + -1.223492219565827481e-02f, + -1.249740889920878534e-02f, + -1.274699097467286470e-02f, + -1.298343338624937185e-02f, + -1.320651460332445369e-02f, + -1.341602680637729757e-02f, + -1.361177607885452785e-02f, + -1.379358258483857035e-02f, + -1.396128073234834122e-02f, + -1.411471932212687332e-02f, + -1.425376168178382232e-02f, + -1.437828578517633127e-02f, + -1.448818435692682781e-02f, + -1.458336496199062575e-02f, + -1.466375008020243120e-02f, + -1.472927716574510063e-02f, + -1.477989869149929497e-02f, + -1.481558217824864229e-02f, + -1.483631020872940888e-02f, + -1.484208042652921528e-02f, + -1.483290551985472217e-02f, + -1.480881319020316278e-02f, + -1.476984610598764888e-02f, + -1.471606184118155226e-02f, + -1.464753279906179893e-02f, + -1.456434612114611976e-02f, + -1.446660358143379496e-02f, + -1.435442146607433973e-02f, + -1.422793043860267476e-02f, + -1.408727539089408773e-02f, + -1.393261528000593226e-02f, + -1.376412295108748671e-02f, + -1.358198494655285873e-02f, + -1.338640130172517699e-02f, + -1.317758532717458811e-02f, + -1.295576337798411137e-02f, + -1.272117461019173744e-02f, + -1.247407072466863269e-02f, + -1.221471569870679480e-02f, + -1.194338550559997762e-02f, + -1.166036782251413627e-02f, + -1.136596172695676817e-02f, + -1.106047738216157407e-02f, + -1.074423571171940345e-02f, + -1.041756806379570341e-02f, + -1.008081586528326509e-02f, + -9.734330266250949479e-03f, + -9.378471775056540852e-03f, + -9.013609884502808353e-03f, + -8.640122689421677812e-03f, + -8.258396496082181723e-03f, + -7.868825423824252902e-03f, + -7.471810999326916093e-03f, + -7.067761743930141109e-03f, + -6.657092754429278468e-03f, + -6.240225277775035030e-03f, + -5.817586280110769240e-03f, + -5.389608010588473400e-03f, + -4.956727560406810548e-03f, + -4.519386417521461217e-03f, + -4.078030017478383173e-03f, + -3.633107290827413370e-03f, + -3.185070207573783597e-03f, + -2.734373319130519765e-03f, + -2.281473298231648152e-03f, + -1.826828477275332062e-03f, + -1.370898385557953137e-03f, + -9.141432858685172996e-04f, + -4.570237109078288933e-04f, + -3.525929546010641032e-17f, + 4.564681634404971059e-04f, + 9.119222132217494076e-04f, + 1.365905160004667876e-03f, + 1.817962048792683442e-03f, + 2.267640413821652746e-03f, + 2.714490730398470830e-03f, + 3.158066863236440400e-03f, + 3.597926510843086571e-03f, + 4.033631645515631595e-03f, + 4.464748948511986122e-03f, + 4.890850239961540627e-03f, + 5.311512903093372658e-03f, + 5.726320302360849920e-03f, + 6.134862195051104215e-03f, + 6.536735135972274353e-03f, + 6.931542874822234357e-03f, + 7.318896745846920765e-03f, + 7.698416049407258259e-03f, + 8.069728425079554859e-03f, + 8.432470215927469551e-03f, + 8.786286823586514014e-03f, + 9.130833053819266409e-03f, + 9.465773452203021798e-03f, + 9.790782629625502387e-03f, + 1.010554557727417341e-02f, + 1.040975797081422800e-02f, + 1.070312646346415798e-02f, + 1.098536896768670043e-02f, + 1.125621492522658335e-02f, + 1.151540556523688447e-02f, + 1.176269415025001618e-02f, + 1.199784620976064760e-02f, + 1.222063976119941615e-02f, + 1.243086551809268608e-02f, + 1.262832708521319930e-02f, + 1.281284114054117608e-02f, + 1.298423760386959412e-02f, + 1.314235979189961462e-02f, + 1.328706455968696638e-02f, + 1.341822242831278503e-02f, + 1.353571769866840878e-02f, + 1.363944855125505048e-02f, + 1.372932713191544682e-02f, + 1.380527962342858322e-02f, + 1.386724630291150812e-02f, + 1.391518158498825965e-02f, + 1.394905405069915734e-02f, + 1.396884646213872837e-02f, + 1.397455576282423569e-02f, + 1.396619306381172501e-02f, + 1.394378361559041324e-02f, + 1.390736676580064946e-02f, + 1.385699590283483162e-02f, + 1.379273838539456926e-02f, + 1.371467545809203735e-02f, + 1.362290215319624312e-02f, + 1.351752717864009370e-02f, + 1.339867279241668298e-02f, + 1.326647466350721130e-02f, + 1.312108171949675703e-02f, + 1.296265598104565965e-02f, + 1.279137238340016282e-02f, + 1.260741858513507452e-02f, + 1.241099476433735194e-02f, + 1.220231340244982976e-02f, + 1.198159905600587605e-02f, + 1.174908811650030374e-02f, + 1.150502855865119002e-02f, + 1.124967967731822956e-02f, + 1.098331181335730246e-02f, + 1.070620606869824784e-02f, + 1.041865401094608894e-02f, + 1.012095736781302990e-02f, + 9.813427711702541953e-03f, + 9.496386134773091015e-03f, + 9.170162914817906255e-03f, + 8.835097172309763769e-03f, + 8.491536518964717603e-03f, + 8.139836698186228570e-03f, + 7.780361217764370299e-03f, + 7.413480975203700848e-03f, + 7.039573876068914819e-03f, + 6.659024445737531453e-03f, + 6.272223434958205251e-03f, + 5.879567419619150664e-03f, + 5.481458395132861143e-03f, + 5.078303365855517913e-03f, + 4.670513929954199853e-03f, + 4.258505860148748337e-03f, + 3.842698680752674144e-03f, + 3.423515241440691897e-03f, + 3.001381288178317595e-03f, + 2.576725031746534850e-03f, + 2.149976714294795244e-03f, + 1.721568174367767547e-03f, + 1.291932410834989269e-03f, + 8.615031461717679489e-04f, + 4.307143895238358311e-04f, + -1.330885637378024658e-17f, + -4.302067493707025154e-04f, + -8.594736065816692546e-04f, + -1.287369774051150230e-03f, + -1.713466340056074740e-03f, + -2.137336707761578608e-03f, + -2.558557021419832853e-03f, + -2.976706589314977430e-03f, + -3.391368303029395053e-03f, + -3.802129052617245642e-03f, + -4.208580137271933364e-03f, + -4.610317671079266870e-03f, + -5.006942983457264747e-03f, + -5.398063013881818258e-03f, + -5.783290700513624497e-03f, + -6.162245362338608992e-03f, + -6.534553074447273235e-03f, + -6.899847036085403514e-03f, + -7.257767931111701668e-03f, + -7.607964280510494219e-03f, + -7.950092786617034279e-03f, + -8.283818668712937539e-03f, + -8.608815989672613522e-03f, + -8.924767973335575449e-03f, + -9.231367312302321895e-03f, + -9.528316465851647404e-03f, + -9.815327947693202118e-03f, + -1.009212460327926254e-02f, + -1.035843987640591192e-02f, + -1.061401806485215774e-02f, + -1.085861456480968767e-02f, + -1.109199610387191955e-02f, + -1.131394096236322706e-02f, + -1.152423918279525498e-02f, + -1.172269276725893851e-02f, + -1.190911586256368677e-02f, + -1.208333493295382470e-02f, + -1.224518892024368394e-02f, + -1.239452939122312469e-02f, + -1.253122067220243524e-02f, + -1.265513997057455924e-02f, + -1.276617748328832784e-02f, + -1.286423649213934428e-02f, + -1.294923344579684485e-02f, + -1.302109802850088746e-02f, + -1.307977321537517543e-02f, + -1.312521531431613646e-02f, + -1.315739399443144331e-02f, + -1.317629230101505927e-02f, + -1.318190665705944176e-02f, + -1.317424685131877472e-02f, + -1.315333601295100296e-02f, + -1.311921057277957818e-02f, + -1.307192021122943054e-02f, + -1.301152779300470720e-02f, + -1.293810928858945414e-02f, + -1.285175368266549059e-02f, + -1.275256286955382679e-02f, + -1.264065153580123772e-02f, + -1.251614703004294733e-02f, + -1.237918922028838700e-02f, + -1.222993033878752925e-02f, + -1.206853481464722870e-02f, + -1.189517909438159854e-02f, + -1.171005145058865056e-02f, + -1.151335177896088170e-02f, + -1.130529138384659196e-02f, + -1.108609275258971849e-02f, + -1.085598931889009827e-02f, + -1.061522521543183453e-02f, + -1.036405501604320198e-02f, + -1.010274346765773629e-02f, + -9.831565212357994479e-03f, + -9.550804499793438818e-03f, + -9.260754890270634504e-03f, + -8.961718948827171807e-03f, + -8.654007930606116492e-03f, + -8.337941457855730515e-03f, + -8.013847188892069678e-03f, + -7.682060479362260927e-03f, + -7.342924036161159809e-03f, + -6.996787564356483392e-03f, + -6.644007407482657376e-03f, + -6.284946181578719530e-03f, + -5.919972403337297497e-03f, + -5.549460112752437578e-03f, + -5.173788490645978184e-03f, + -4.793341471466837367e-03f, + -4.408507351756223852e-03f, + -4.019678394675591307e-03f, + -3.627250431002364273e-03f, + -3.231622456996959773e-03f, + -2.833196229547020346e-03f, + -2.432375859003425757e-03f, + -2.029567400113351655e-03f, + -1.625178441472032017e-03f, + -1.219617693898372018e-03f, + -8.132945781564861155e-04f, + -4.066188124353766527e-04f, + -1.255183464083940188e-16f, + 4.061527825679167288e-04f, + 8.114313961304521613e-04f, + 1.215429048341218040e-03f, + 1.617740701170319395e-03f, + 2.017963476201877198e-03f, + 2.415697057295686061e-03f, + 2.810544090212879591e-03f, + 3.202110578810242990e-03f, + 3.590006277401872493e-03f, + 3.973845078907196904e-03f, + 4.353245398391843331e-03f, + 4.727830551627599208e-03f, + 5.097229128292139061e-03f, + 5.461075359441391712e-03f, + 5.819009478893136939e-03f, + 6.170678078162634105e-03f, + 6.515734454602445554e-03f, + 6.853838952405788033e-03f, + 7.184659296134813210e-03f, + 7.507870916452765928e-03f, + 7.823157267734456854e-03f, + 8.130210137252638636e-03f, + 8.428729945633239504e-03f, + 8.718426038289772001e-03f, + 8.999016967555456201e-03f, + 9.270230765236708165e-03f, + 9.531805205328850242e-03f, + 9.783488056638036182e-03f, + 1.002503732506817256e-02f, + 1.025622148534208536e-02f, + 1.047681970193385138e-02f, + 1.068662203900417294e-02f, + 1.088542965913968583e-02f, + 1.107305501070874297e-02f, + 1.124932200365959603e-02f, + 1.141406617359526365e-02f, + 1.156713483397583953e-02f, + 1.170838721630671625e-02f, + 1.183769459818636779e-02f, + 1.195494041909911376e-02f, + 1.206002038384902317e-02f, + 1.215284255354657367e-02f, + 1.223332742406933564e-02f, + 1.230140799193264051e-02f, + 1.235702980751857485e-02f, + 1.240015101562292471e-02f, + 1.243074238329506739e-02f, + 1.244878731495606579e-02f, + 1.245428185479465800e-02f, + 1.244723467645303394e-02f, + 1.242766706002694684e-02f, + 1.239561285641773217e-02f, + 1.235111843908625294e-02f, + 1.229424264327155618e-02f, + 1.222505669274922754e-02f, + 1.214364411421739454e-02f, + 1.205010063941019949e-02f, + 1.194453409505086793e-02f, + 1.182706428076905555e-02f, + 1.169782283511792108e-02f, + 1.155695308984023249e-02f, + 1.140460991254168763e-02f, + 1.124095953794393248e-02f, + 1.106617938789930186e-02f, + 1.088045788035994950e-02f, + 1.068399422750770586e-02f, + 1.047699822325738259e-02f, + 1.025969002036108101e-02f, + 1.003229989734856151e-02f, + 9.795068015547647028e-03f, + 9.548244166444083345e-03f, + 9.292087509640511306e-03f, + 9.026866301692599431e-03f, + 8.752857616103554630e-03f, + 8.470347054767179973e-03f, + 8.179628451162134981e-03f, + 7.881003565601285565e-03f, + 7.574781772854489656e-03f, + 7.261279742466121394e-03f, + 6.940821112095421283e-03f, + 6.613736154219424825e-03f, + 6.280361436535699012e-03f, + 5.941039476420039225e-03f, + 5.596118389785657496e-03f, + 5.245951534708952028e-03f, + 4.890897150183600751e-03f, + 4.531317990370226784e-03f, + 4.167580954717115850e-03f, + 3.800056714327025055e-03f, + 3.429119334948160357e-03f, + 3.055145896976142229e-03f, + 2.678516112847336220e-03f, + 2.299611942215314033e-03f, + 1.918817205297107184e-03f, + 1.536517194780740196e-03f, + 1.153098286686640977e-03f, + 7.689475505726472288e-04f, + 3.844523594784173275e-04f, + 7.713589460388476895e-17f, + -3.840227171148354263e-04f, + -7.672298454569775623e-04f, + -1.149236690145042554e-03f, + -1.529660193323781423e-03f, + -1.908119317586332020e-03f, + -2.284235426939436406e-03f, + -2.657632664926935610e-03f, + -3.027938329540865066e-03f, + -3.394783244538977351e-03f, + -3.757802126807970565e-03f, + -4.116633949400874508e-03f, + -4.470922299891161820e-03f, + -4.820315733689854894e-03f, + -5.164468121973136636e-03f, + -5.503038993878216732e-03f, + -5.835693872631364174e-03f, + -6.162104605273097113e-03f, + -6.481949685660806648e-03f, + -6.794914570427616589e-03f, + -7.100691987590134792e-03f, + -7.398982237502254970e-03f, + -7.689493485860325432e-03f, + -7.971942048476048787e-03f, + -8.246052667536663341e-03f, + -8.511558779087890353e-03f, + -8.768202771477389898e-03f, + -9.015736234510263528e-03f, + -9.253920199077345435e-03f, + -9.482525367023937612e-03f, + -9.701332331039827506e-03f, + -9.910131784361380508e-03f, + -1.010872472008433366e-02f, + -1.029692261989997165e-02f, + -1.047454763207558877e-02f, + -1.064143273851355688e-02f, + -1.079742191073151426e-02f, + -1.094237025462001418e-02f, + -1.107614414384500319e-02f, + -1.119862134177232113e-02f, + -1.130969111180511570e-02f, + -1.140925431603640253e-02f, + -1.149722350212928346e-02f, + -1.157352297835191959e-02f, + -1.163808887670370745e-02f, + -1.169086920408306425e-02f, + -1.173182388145829064e-02f, + -1.176092477101533564e-02f, + -1.177815569126870569e-02f, + -1.178351242013314648e-02f, + -1.177700268596670700e-02f, + -1.175864614660714416e-02f, + -1.172847435643616099e-02f, + -1.168653072151761875e-02f, + -1.163287044286803393e-02f, + -1.156756044792955386e-02f, + -1.149067931032693446e-02f, + -1.140231715800252288e-02f, + -1.130257556983401725e-02f, + -1.119156746085197413e-02f, + -1.106941695618434904e-02f, + -1.093625925386791653e-02f, + -1.079224047667651058e-02f, + -1.063751751312634955e-02f, + -1.047225784783175143e-02f, + -1.029663938139156396e-02f, + -1.011085024000046503e-02f, + -9.915088574987292874e-03f, + -9.709562352492116799e-03f, + -9.494489133505876546e-03f, + -9.270095844503068935e-03f, + -9.036618538908790590e-03f, + -8.794302149650478143e-03f, + -8.543400233051267126e-03f, + -8.284174704334627132e-03f, + -8.016895565012135613e-03f, + -7.741840622440153072e-03f, + -7.459295201834709003e-03f, + -7.169551851041272746e-03f, + -6.872910038367072844e-03f, + -6.569675843783693604e-03f, + -6.260161643820879619e-03f, + -5.944685790473217164e-03f, + -5.623572284449645554e-03f, + -5.297150443101252340e-03f, + -4.965754563364804634e-03f, + -4.629723580069068818e-03f, + -4.289400719951269245e-03f, + -3.945133151734599684e-03f, + -3.597271632626723118e-03f, + -3.246170151593960954e-03f, + -2.892185569777141942e-03f, + -2.535677258412258675e-03f, + -2.177006734621175950e-03f, + -1.816537295443911102e-03f, + -1.454633650479946480e-03f, + -1.091661553510198676e-03f, + -7.279874334719225624e-04f, + -3.639780251556599890e-04f, + -3.369908401361916715e-17f, + 3.635804026494091068e-04f, + 7.263977434430309238e-04f, + 1.088087749724279355e-03f, + 1.448287680655482423e-03f, + 1.806636690402828576e-03f, + 2.162776189022377980e-03f, + 2.516350200682577424e-03f, + 2.867005718868611067e-03f, + 3.214393058213963015e-03f, + 3.558166202608354332e-03f, + 3.897983149238446517e-03f, + 4.233506248216929377e-03f, + 4.564402537464701606e-03f, + 4.890344072516150664e-03f, + 5.211008250917694221e-03f, + 5.526078130903779376e-03f, + 5.835242744032488431e-03f, + 6.138197401474478412e-03f, + 6.434643993655418670e-03f, + 6.724291282955277560e-03f, + 7.006855189179749324e-03f, + 7.282059067523997259e-03f, + 7.549633978756757104e-03f, + 7.809318951363577316e-03f, + 8.060861235391183002e-03f, + 8.304016547750365032e-03f, + 8.538549308736993748e-03f, + 8.764232869544244173e-03f, + 8.980849730547869544e-03f, + 9.188191750152601561e-03f, + 9.386060344002900707e-03f, + 9.574266674365642868e-03f, + 9.752631829506541034e-03f, + 9.920986992890266226e-03f, + 1.007917360204479626e-02f, + 1.022704349694264145e-02f, + 1.036445905775955530e-02f, + 1.049129333188454291e-02f, + 1.060743015006561894e-02f, + 1.071276423158474926e-02f, + 1.080720127836987295e-02f, + 1.089065805795989307e-02f, + 1.096306247525190812e-02f, + 1.102435363297093844e-02f, + 1.107448188081278095e-02f, + 1.111340885322339295e-02f, + 1.114110749578882571e-02f, + 1.115756208022166719e-02f, + 1.116276820794095091e-02f, + 1.115673280225443556e-02f, + 1.113947408916339532e-02f, + 1.111102156682138194e-02f, + 1.107141596369005596e-02f, + 1.102070918544608043e-02f, + 1.095896425070500523e-02f, + 1.088625521563825124e-02f, + 1.080266708757151098e-02f, + 1.070829572766292780e-02f, + 1.060324774277076644e-02f, + 1.048764036663119362e-02f, + 1.036160133047638564e-02f, + 1.022526872323565802e-02f, + 1.007879084146987361e-02f, + 9.922326029202397854e-03f, + 9.756042507818011092e-03f, + 9.580118196210777107e-03f, + 9.394740521373458678e-03f, + 9.200106219628658571e-03f, + 8.996421128710744425e-03f, + 8.783899970919039138e-03f, + 8.562766127568535030e-03f, + 8.333251404975441923e-03f, + 8.095595792220260448e-03f, + 7.850047210942715306e-03f, + 7.596861257427847160e-03f, + 7.336300937249773911e-03f, + 7.068636392749921664e-03f, + 6.794144623630500764e-03f, + 6.513109200950242123e-03f, + 6.225819974820153910e-03f, + 5.932572776096196529e-03f, + 5.633669112378537902e-03f, + 5.329415858627153425e-03f, + 5.020124942711203311e-03f, + 4.706113026214525825e-03f, + 4.387701180821052503e-03f, + 4.065214560613866247e-03f, + 3.738982070617613389e-03f, + 3.409336031925057006e-03f, + 3.076611843746769430e-03f, + 2.741147642725723539e-03f, + 2.403283959864889810e-03f, + 2.063363375414229133e-03f, + 1.721730172063813159e-03f, + 1.378729986799676023e-03f, + 1.034709461766141576e-03f, + 6.900158944939007019e-04f, + 3.449968878405673157e-04f, + 0.000000000000000000e+00f, + -3.446276050693368302e-04f, + -6.885395064473497171e-04f, + -1.031390373933058213e-03f, + -1.372836314259132063e-03f, + -1.712535215489931510e-03f, + -2.050147089260192981e-03f, + -2.385334410513898287e-03f, + -2.717762454401503706e-03f, + -3.047099630002055894e-03f, + -3.373017810537275608e-03f, + -3.695192659748559091e-03f, + -4.013303954115152519e-03f, + -4.327035900590155311e-03f, + -4.636077449543945204e-03f, + -4.940122602601876300e-03f, + -5.238870715073626685e-03f, + -5.532026792676994140e-03f, + -5.819301782261442936e-03f, + -6.100412856246560135e-03f, + -6.375083690498107615e-03f, + -6.643044735364164385e-03f, + -6.904033479612515893e-03f, + -7.157794707005572452e-03f, + -7.404080745267220716e-03f, + -7.642651707196129220e-03f, + -7.873275723692764461e-03f, + -8.095729168475295640e-03f, + -8.309796874264679251e-03f, + -8.515272340233715279e-03f, + -8.711957930518144516e-03f, + -8.899665063600716020e-03f, + -9.078214392388751081e-03f, + -9.247435974810619408e-03f, + -9.407169434773821060e-03f, + -9.557264113329744237e-03f, + -9.697579209904895123e-03f, + -9.827983913467562213e-03f, + -9.948357523507136632e-03f, + -1.005858956071708186e-02f, + -1.015857986727966046e-02f, + -1.024823869666328068e-02f, + -1.032748679285360252e-02f, + -1.039625545894891021e-02f, + -1.045448661506321979e-02f, + -1.050213284548926623e-02f, + -1.053915743508587223e-02f, + -1.056553439486431802e-02f, + -1.058124847675941289e-02f, + -1.058629517758200196e-02f, + -1.058068073216014530e-02f, + -1.056442209568751386e-02f, + -1.053754691530808460e-02f, + -1.050009349097712034e-02f, + -1.045211072564902234e-02f, + -1.039365806485362080e-02f, + -1.032480542573286329e-02f, + -1.024563311561991173e-02f, + -1.015623174025468214e-02f, + -1.005670210173764852e-02f, + -9.947155086336527396e-03f, + -9.827711542269031153e-03f, + -9.698502147594538492e-03f, + -9.559667268359363368e-03f, + -9.411356807146858960e-03f, + -9.253730042196249908e-03f, + -9.086955457261344099e-03f, + -8.911210562389308004e-03f, + -8.726681705811039844e-03f, + -8.533563877139486997e-03f, + -8.332060502084964390e-03f, + -8.122383228901621996e-03f, + -7.904751706788885890e-03f, + -7.679393356479695225e-03f, + -7.446543133253041326e-03f, + -7.206443282618896044e-03f, + -6.959343088928202255e-03f, + -6.705498617166942392e-03f, + -6.445172448203926978e-03f, + -6.178633407762102944e-03f, + -5.906156289395311614e-03f, + -5.628021571754467968e-03f, + -5.344515130431467045e-03f, + -5.055927944681215189e-03f, + -4.762555799315829584e-03f, + -4.464698982081711544e-03f, + -4.162661976823887194e-03f, + -3.856753152753953674e-03f, + -3.547284450136960633e-03f, + -3.234571062715784710e-03f, + -2.918931117198290178e-03f, + -2.600685350131433544e-03f, + -2.280156782488628776e-03f, + -1.957670392303650399e-03f, + -1.633552785677109664e-03f, + -1.308131866494795974e-03f, + -9.817365051840280538e-04f, + -6.546962068479288350e-04f, + -3.273407791097072712e-04f, + 4.043570989833329463e-17f, + 3.269967137754016561e-04f, + 6.533206378577746144e-04f, + 9.786440702132328823e-04f, + 1.302640659735354979e-03f, + 1.624985733145793004e-03f, + 1.945356619862989578e-03f, + 2.263432974515657958e-03f, + 2.578897096781404205e-03f, + 2.891434248225943724e-03f, + 3.200732965834694743e-03f, + 3.506485371918194750e-03f, + 3.808387480088745985e-03f, + 4.106139497000982005e-03f, + 4.399446119558816405e-03f, + 4.688016827295758519e-03f, + 4.971566169637211575e-03f, + 5.249814047762459135e-03f, + 5.522485990789751178e-03f, + 5.789313426009380474e-03f, + 6.050033942903848549e-03f, + 6.304391550690290578e-03f, + 6.552136929139138963e-03f, + 6.793027672418951342e-03f, + 7.026828525731477169e-03f, + 7.253311614507366133e-03f, + 7.472256665937161355e-03f, + 7.683451222625906421e-03f, + 7.886690848162170725e-03f, + 8.081779324404252465e-03f, + 8.268528840294541229e-03f, + 8.446760172019297450e-03f, + 8.616302854343038317e-03f, + 8.776995342953978693e-03f, + 8.928685167666237238e-03f, + 9.071229076335495314e-03f, + 9.204493169351136636e-03f, + 9.328353024581554839e-03f, + 9.442693812655188201e-03f, + 9.547410402472212773e-03f, + 9.642407456851329745e-03f, + 9.727599518224548311e-03f, + 9.802911084305539202e-03f, + 9.868276673664756402e-03f, + 9.923640881156430005e-03f, + 9.968958423152556383e-03f, + 1.000419417254796474e-02f, + 1.002932318351319220e-02f, + 1.004433070597993659e-02f, + 1.004921218985568680e-02f, + 1.004397327897390071e-02f, + 1.002862979479642036e-02f, + 1.000320770989520693e-02f, + 9.967743111250705959e-03f, + 9.922282153414410666e-03f, + 9.866881001593018341e-03f, + 9.801605764722259168e-03f, + 9.726532418607795535e-03f, + 9.641746719220965420e-03f, + 9.547344106246909851e-03f, + 9.443429596991709252e-03f, + 9.330117670766156779e-03f, + 9.207532143871193647e-03f, + 9.075806035321471940e-03f, + 8.935081423451458893e-03f, + 8.785509293556969038e-03f, + 8.627249376736262007e-03f, + 8.460469980100025419e-03f, + 8.285347808531550937e-03f, + 8.102067778184653124e-03f, + 7.910822821914111069e-03f, + 7.711813686845882775e-03f, + 7.505248724294873164e-03f, + 7.291343672252590877e-03f, + 7.070321430670069122e-03f, + 6.842411829768484458e-03f, + 6.607851391620159567e-03f, + 6.366883085243799263e-03f, + 6.119756075469458441e-03f, + 5.866725465831302069e-03f, + 5.608052035751829104e-03f, + 5.344001972290637024e-03f, + 5.074846596729006530e-03f, + 4.800862086276046561e-03f, + 4.522329191175403912e-03f, + 4.239532947506496723e-03f, + 3.952762385971762653e-03f, + 3.662310236965878128e-03f, + 3.368472632229732935e-03f, + 3.071548803391624394e-03f, + 2.771840777700751261e-03f, + 2.469653071265261093e-03f, + 2.165292380101938724e-03f, + 1.859067269314046813e-03f, + 1.551287860709699411e-03f, + 1.242265519177247843e-03f, + 9.323125381350914204e-04f, + 6.217418243711637623e-04f, + 3.108665825923342669e-04f, + 6.717146213127583512e-17f, + -3.105450687918437651e-04f, + -6.204564161774286978e-04f, + -9.294227950051989944e-04f, + -1.237134230733572199e-03f, + -1.543282331983834290e-03f, + -1.847560599181306829e-03f, + -2.149664730972687243e-03f, + -2.449292928118728188e-03f, + -2.746146194552807982e-03f, + -3.039928635312416725e-03f, + -3.330347751041712323e-03f, + -3.617114728774667616e-03f, + -3.899944728711154593e-03f, + -4.178557166699342108e-03f, + -4.452675992145861683e-03f, + -4.722029961080219732e-03f, + -4.986352904100606331e-03f, + -5.245383988940638745e-03f, + -5.498867977395256908e-03f, + -5.746555476355140642e-03f, + -5.988203182702574526e-03f, + -6.223574121828233212e-03f, + -6.452437879537288590e-03f, + -6.674570827115577747e-03f, + -6.889756339339682574e-03f, + -7.097785005216259401e-03f, + -7.298454831247235333e-03f, + -7.491571437024912607e-03f, + -7.676948242966399882e-03f, + -7.854406650007556001e-03f, + -8.023776211084653054e-03f, + -8.184894794238147323e-03f, + -8.337608737184420971e-03f, + -8.481772993207853037e-03f, + -8.617251268236572961e-03f, + -8.743916148971660543e-03f, + -8.861649221950897853e-03f, + -8.970341183436947655e-03f, + -9.069891940027947907e-03f, + -9.160210699899310610e-03f, + -9.241216054595012552e-03f, + -9.312836051294730300e-03f, + -9.375008255495077131e-03f, + -9.427679804050792534e-03f, + -9.470807448532998427e-03f, + -9.504357588870533669e-03f, + -9.528306297250627166e-03f, + -9.542639332264922503e-03f, + -9.547352143296350749e-03f, + -9.542449865152758184e-03f, + -9.527947302962450418e-03f, + -9.503868907356978132e-03f, + -9.470248739975935248e-03f, + -9.427130429338478543e-03f, + -9.374567117135889846e-03f, + -9.312621395008722006e-03f, + -9.241365231882207529e-03f, + -9.160879891942457637e-03f, + -9.071255843345765041e-03f, + -8.972592657761846446e-03f, + -8.864998900862054027e-03f, + -8.748592013871738307e-03f, + -8.623498186314530051e-03f, + -8.489852220086737886e-03f, + -8.347797385005961690e-03f, + -8.197485265989208972e-03f, + -8.039075602022388231e-03f, + -7.872736117090798832e-03f, + -7.698642343250202899e-03f, + -7.516977436023509172e-03f, + -7.327931982316876480e-03f, + -7.131703801056546270e-03f, + -6.928497736752988953e-03f, + -6.718525446209536699e-03f, + -6.502005178594330234e-03f, + -6.279161549105892817e-03f, + -6.050225306465603983e-03f, + -5.815433094476260209e-03f, + -5.575027207895101936e-03f, + -5.329255342869666442e-03f, + -5.078370342195653343e-03f, + -4.822629935656748412e-03f, + -4.562296475713070905e-03f, + -4.297636668809446704e-03f, + -4.028921302576533685e-03f, + -3.756424969205551280e-03f, + -3.480425785277831575e-03f, + -3.201205108333276313e-03f, + -2.919047250469300383e-03f, + -2.634239189257616548e-03f, + -2.347070276275515198e-03f, + -2.057831943546030353e-03f, + -1.766817408183218516e-03f, + -1.474321375543928428e-03f, + -1.180639741184265909e-03f, + -8.860692919223937727e-04f, + -5.909074063099545391e-04f, + -2.954517548118973532e-04f, + -3.191480037364917763e-17f, + 2.951505029420960018e-04f, + 5.897030050987488040e-04f, + 8.833616617872707506e-04f, + 1.175831829311610932e-03f, + 1.466820360203527135e-03f, + 1.756035896659971941e-03f, + 2.043189161879899619e-03f, + 2.327993249011680189e-03f, + 2.610163907422230218e-03f, + 2.889419826001986703e-03f, + 3.165482913225662470e-03f, + 3.438078573687941564e-03f, + 3.706935980840707877e-03f, + 3.971788345662486015e-03f, + 4.232373180990945533e-03f, + 4.488432561260610569e-03f, + 4.739713377386114979e-03f, + 4.985967586540838471e-03f, + 5.226952456585757904e-03f, + 5.462430804905994176e-03f, + 5.692171231422111718e-03f, + 5.915948345547154125e-03f, + 6.133542986866762428e-03f, + 6.344742439328332831e-03f, + 6.549340638727800534e-03f, + 6.747138373295004030e-03f, + 6.937943477180526231e-03f, + 7.121571016657480234e-03f, + 7.297843468859207573e-03f, + 7.466590892878511958e-03f, + 7.627651093066531961e-03f, + 7.780869774372829901e-03f, + 7.926100689579733752e-03f, + 8.063205778290625028e-03f, + 8.192055297540449046e-03f, + 8.312527943906498212e-03f, + 8.424510967003927958e-03f, + 8.527900274261378949e-03f, + 8.622600526880391109e-03f, + 8.708525226889919993e-03f, + 8.785596795218742969e-03f, + 8.853746640715155422e-03f, + 8.912915220054325877e-03f, + 8.963052088482293539e-03f, + 9.004115941354405919e-03f, + 9.036074646436131896e-03f, + 9.058905266942841872e-03f, + 9.072594075305043249e-03f, + 9.077136557654227889e-03f, + 9.072537409034447248e-03f, + 9.058810519353838187e-03f, + 9.035978950099398527e-03f, + 9.004074901847999857e-03f, + 8.963139672615380235e-03f, + 8.913223607094592979e-03f, + 8.854386036843783780e-03f, + 8.786695211493011221e-03f, + 8.710228221048086500e-03f, + 8.625070909378744488e-03f, + 8.531317778987176914e-03f, + 8.429071887160986820e-03f, + 8.318444733624745513e-03f, + 8.199556139810507127e-03f, + 8.072534119878281914e-03f, + 7.937514743624142846e-03f, + 7.794641991421518389e-03f, + 7.644067601350621258e-03f, + 7.485950908677277416e-03f, + 7.320458677849650715e-03f, + 7.147764927190735675e-03f, + 6.968050746469218841e-03f, + 6.781504107540342781e-03f, + 6.588319668252944766e-03f, + 6.388698569828255708e-03f, + 6.182848227920212554e-03f, + 5.970982117573007432e-03f, + 5.753319552299874899e-03f, + 5.530085457510665325e-03f, + 5.301510138520690307e-03f, + 5.067829043382217347e-03f, + 4.829282520779390811e-03f, + 4.586115573237751521e-03f, + 4.338577605899722467e-03f, + 4.086922171123733884e-03f, + 3.831406709168724920e-03f, + 3.572292285227009461e-03f, + 3.309843323076701056e-03f, + 3.044327335621568181e-03f, + 2.776014652595465773e-03f, + 2.505178145706950243e-03f, + 2.232092951502079141e-03f, + 1.957036192228632423e-03f, + 1.680286694983667639e-03f, + 1.402124709426648972e-03f, + 1.122831624348555194e-03f, + 8.426896833769130903e-04f, + 5.619817001094690135e-04f, + 2.809907729590478470e-04f, + 0.000000000000000000e+00f, + -2.807078058981391780e-04f, + -5.608504013647517628e-04f, + -8.401463959614267896e-04f, + -1.118315534473937048e-03f, + -1.395078977776910336e-03f, + -1.670159581991349264e-03f, + -1.943282175656975462e-03f, + -2.214173834640201406e-03f, + -2.482564154505302687e-03f, + -2.748185520076693727e-03f, + -3.010773371923358872e-03f, + -3.270066469502342678e-03f, + -3.525807150696843618e-03f, + -3.777741587494939240e-03f, + -4.025620037552721069e-03f, + -4.269197091394013634e-03f, + -4.508231915003397400e-03f, + -4.742488487571064684e-03f, + -4.971735834156138824e-03f, + -5.195748253041122811e-03f, + -5.414305537549929381e-03f, + -5.627193192117104295e-03f, + -5.834202642391930586e-03f, + -6.035131439175642903e-03f, + -6.229783455990234309e-03f, + -6.417969080087524217e-03f, + -6.599505396713676100e-03f, + -6.774216366448439740e-03f, + -6.941932995450164623e-03f, + -7.102493498440299972e-03f, + -7.255743454271620582e-03f, + -7.401535953931983637e-03f, + -7.539731740839664825e-03f, + -7.670199343300234535e-03f, + -7.792815198996800620e-03f, + -7.907463771397596833e-03f, + -8.014037657972214804e-03f, + -8.112437690114479572e-03f, + -8.202573024681410890e-03f, + -8.284361227063144831e-03f, + -8.357728345709419457e-03f, + -8.422608978046395439e-03f, + -8.478946327725278564e-03f, + -8.526692253154843221e-03f, + -8.565807307276868815e-03f, + -8.596260768553768902e-03f, + -8.618030663145858153e-03f, + -8.631103778264705498e-03f, + -8.635475666698075314e-03f, + -8.631150642510639071e-03f, + -8.618141767933615305e-03f, + -8.596470831465410564e-03f, + -8.566168317214016212e-03f, + -8.527273365520683310e-03f, + -8.479833724913378445e-03f, + -8.423905695446994024e-03f, + -8.359554063495495263e-03f, + -8.286852028071115678e-03f, + -8.205881118751968267e-03f, + -8.116731105309984984e-03f, + -8.019499899138108831e-03f, + -7.914293446583613167e-03f, + -7.801225614304001070e-03f, + -7.680418066767507809e-03f, + -7.552000136030336853e-03f, + -7.416108683929066407e-03f, + -7.272887956833709935e-03f, + -7.122489433116511483e-03f, + -6.965071663495360846e-03f, + -6.800800104421189651e-03f, + -6.629846944682925639e-03f, + -6.452390925411476304e-03f, + -6.268617153670886545e-03f, + -6.078716909829352984e-03f, + -5.882887448911673298e-03f, + -5.681331796138239937e-03f, + -5.474258536861224822e-03f, + -5.261881601117059278e-03f, + -5.044420043014674268e-03f, + -4.822097815188827914e-03f, + -4.595143538549655710e-03f, + -4.363790267563028361e-03f, + -4.128275251306491068e-03f, + -3.888839690540101934e-03f, + -3.645728491045435640e-03f, + -3.399190013480753690e-03f, + -3.149475820010231263e-03f, + -2.896840417964342308e-03f, + -2.641541000791211135e-03f, + -2.383837186564273774e-03f, + -2.123990754310813977e-03f, + -1.862265378427587111e-03f, + -1.598926361455722204e-03f, + -1.334240365481033667e-03f, + -1.068475142436885140e-03f, + -8.018992635760308074e-04f, + -5.347818483891565765e-04f, + -2.673922932415280962e-04f, + -9.079959879021497232e-17f, + 2.671258950737993680e-04f, + 5.337167918945780664e-04f, + 7.995048963151187319e-04f, + 1.064223488808225876e-03f, + 1.327607191801307438e-03f, + 1.589392235393608270e-03f, + 1.849316721191365896e-03f, + 2.107120883999156515e-03f, + 2.362547351101621299e-03f, + 2.615341398882998439e-03f, + 2.865251206523531432e-03f, + 3.112028106524595324e-03f, + 3.355426831810707448e-03f, + 3.595205759164358736e-03f, + 3.831127148753331844e-03f, + 4.062957379511429948e-03f, + 4.290467180140932618e-03f, + 4.513431855509694376e-03f, + 4.731631508217054409e-03f, + 4.944851255114078407e-03f, + 5.152881438560737853e-03f, + 5.355517832217622683e-03f, + 5.552561841166678107e-03f, + 5.743820696166867384e-03f, + 5.929107641855835276e-03f, + 6.108242118712046975e-03f, + 6.281049938603144346e-03f, + 6.447363453748070593e-03f, + 6.607021718930415448e-03f, + 6.759870646807101223e-03f, + 6.905763156161545159e-03f, + 7.044559312960290212e-03f, + 7.176126464077911202e-03f, + 7.300339363562619553e-03f, + 7.417080291323936703e-03f, + 7.526239164128890706e-03f, + 7.627713638804527879e-03f, + 7.721409207549118775e-03f, + 7.807239285264657921e-03f, + 7.885125288831035251e-03f, + 7.954996708249086751e-03f, + 8.016791169590116797e-03f, + 8.070454489695856257e-03f, + 8.115940722582272102e-03f, + 8.153212197509116660e-03f, + 8.182239548684301991e-03f, + 8.203001736582492612e-03f, + 8.215486060864016227e-03f, + 8.219688164889776480e-03f, + 8.215612031835866150e-03f, + 8.203269972420074163e-03f, + 8.182682604260985149e-03f, + 8.153878822898639675e-03f, + 8.116895764514334491e-03f, + 8.071778760395010785e-03f, + 8.018581283196542925e-03f, + 7.957364885067841689e-03f, + 7.888199127706180952e-03f, + 7.811161504422274408e-03f, + 7.726337354300897436e-03f, + 7.633819768552089179e-03f, + 7.533709489153867997e-03f, + 7.426114799896956116e-03f, + 7.311151409948320190e-03f, + 7.188942330057568331e-03f, + 7.059617741539236935e-03f, + 6.923314858168461497e-03f, + 6.780177781137243509e-03f, + 6.630357347223709771e-03f, + 6.474010970332770391e-03f, + 6.311302476576784518e-03f, + 6.142401933065200215e-03f, + 5.967485470584375090e-03f, + 5.786735100350979578e-03f, + 5.600338525028452849e-03f, + 5.408488944204285429e-03f, + 5.211384854526987694e-03f, + 5.009229844711029882e-03f, + 4.802232385620449663e-03f, + 4.590605615646119360e-03f, + 4.374567121599736569e-03f, + 4.154338715345936607e-03f, + 3.930146206405986734e-03f, + 3.702219170760879478e-03f, + 3.470790716094145426e-03f, + 3.236097243712496643e-03f, + 2.998378207386371425e-03f, + 2.757875869357837798e-03f, + 2.514835053763304993e-03f, + 2.269502897720573724e-03f, + 2.022128600335697159e-03f, + 1.772963169880989280e-03f, + 1.522259169403208240e-03f, + 1.270270461017685608e-03f, + 1.017251949147536440e-03f, + 7.634593229678904650e-04f, + 5.091487983134088255e-04f, + 2.545768593114422170e-04f, + 5.893834195817279027e-17f, + -2.543255338102004309e-04f, + -5.081440021684953965e-04f, + -7.612004278544677463e-04f, + -1.013240852227075906e-03f, + -1.264012589796192692e-03f, + -1.513264481263194719e-03f, + -1.760747144774524840e-03f, + -2.006213225141300748e-03f, + -2.249417640770990170e-03f, + -2.490117828070589987e-03f, + -2.728073983073443527e-03f, + -2.963049300051106942e-03f, + -3.194810206873958253e-03f, + -3.423126596884991813e-03f, + -3.647772057057938316e-03f, + -3.868524092214752682e-03f, + -4.085164345078219975e-03f, + -4.297478811945374191e-03f, + -4.505258053766428641e-03f, + -4.708297402422947572e-03f, + -4.906397162001897849e-03f, + -5.099362804867554194e-03f, + -5.287005162340526442e-03f, + -5.469140609795000892e-03f, + -5.645591245996113387e-03f, + -5.816185066500464615e-03f, + -5.980756130952048950e-03f, + -6.139144724111944909e-03f, + -6.291197510464445995e-03f, + -6.436767682251153498e-03f, + -6.575715100791147336e-03f, + -6.707906430950256842e-03f, + -6.833215268632009988e-03f, + -6.951522261168058443e-03f, + -7.062715220494850468e-03f, + -7.166689229008634497e-03f, + -7.263346738000107591e-03f, + -7.352597658577248937e-03f, + -7.434359444991439289e-03f, + -7.508557170290937506e-03f, + -7.575123594233416392e-03f, + -7.633999223396016783e-03f, + -7.685132363431001340e-03f, + -7.728479163421451623e-03f, + -7.764003652300489888e-03f, + -7.791677767305085246e-03f, + -7.811481374443590177e-03f, + -7.823402280964399449e-03f, + -7.827436239820800487e-03f, + -7.823586946135632075e-03f, + -7.811866025676963803e-03f, + -7.792293015364394268e-03f, + -7.764895335833248320e-03f, + -7.729708256092200462e-03f, + -7.686774850317674296e-03f, + -7.636145946835978977e-03f, + -7.577880069352489409e-03f, + -7.512043370494353445e-03f, + -7.438709557741495296e-03f, + -7.357959811827293611e-03f, + -7.269882697699128586e-03f, + -7.174574068135403972e-03f, + -7.072136960122816021e-03f, + -6.962681484106240416e-03f, + -6.846324706228426420e-03f, + -6.723190523685911024e-03f, + -6.593409533333032847e-03f, + -6.457118893672204353e-03f, + -6.314462180376954545e-03f, + -6.165589235498591079e-03f, + -6.010656010514694655e-03f, + -5.849824403383760418e-03f, + -5.683262089774722935e-03f, + -5.511142348648704649e-03f, + -5.333643882371955774e-03f, + -5.150950631548067808e-03f, + -4.963251584760376527e-03f, + -4.770740583420043056e-03f, + -4.573616121923121738e-03f, + -4.372081143319735304e-03f, + -4.166342830707624179e-03f, + -3.956612394562757443e-03f, + -3.743104856225459159e-03f, + -3.526038827764110296e-03f, + -3.305636288440114059e-03f, + -3.082122358004176291e-03f, + -2.855725067054290869e-03f, + -2.626675124688376134e-03f, + -2.395205683690529615e-03f, + -2.161552103486554573e-03f, + -1.925951711112042913e-03f, + -1.688643560434465851e-03f, + -1.449868189872311657e-03f, + -1.209867378858563115e-03f, + -9.688839032931976462e-04f, + -7.271612902323416720e-04f, + -4.849435720621983882e-04f, + -2.424750404039046129e-04f, + -2.993458375907816894e-17f, + 2.422374771728505677e-04f, + 4.839937973155881718e-04f, + 7.250260894964933667e-04f, + 9.650924493755287268e-04f, + 1.203952181715645022e-03f, + 1.441366041441651457e-03f, + 1.677096473002762708e-03f, + 1.910907847800708860e-03f, + 2.142566699447316300e-03f, + 2.371841956615023273e-03f, + 2.598505173249376302e-03f, + 2.822330755915376365e-03f, + 3.043096188049002060e-03f, + 3.260582250895563590e-03f, + 3.474573240909645862e-03f, + 3.684857183408437892e-03f, + 3.891226042261025642e-03f, + 4.093475925410680442e-03f, + 4.291407286024813764e-03f, + 4.484825119075430515e-03f, + 4.673539153157502297e-03f, + 4.857364037355267616e-03f, + 5.036119522974105353e-03f, + 5.209630639960896592e-03f, + 5.377727867838498661e-03f, + 5.540247300990066084e-03f, + 5.697030808130492709e-03f, + 5.847926185810888196e-03f, + 5.992787305808217578e-03f, + 6.131474256255946623e-03f, + 6.263853476381850416e-03f, + 6.389797884721984053e-03f, + 6.509187000688825345e-03f, + 6.621907059378241700e-03f, + 6.727851119505293652e-03f, + 6.826919164367567169e-03f, + 6.919018195741605953e-03f, + 7.004062320623680518e-03f, + 7.081972830736373535e-03f, + 7.152678274725585150e-03f, + 7.216114522985169963e-03f, + 7.272224825048948307e-03f, + 7.320959859500953196e-03f, + 7.362277776360223042e-03f, + 7.396144231905143855e-03f, + 7.422532415909789162e-03f, + 7.441423071271871179e-03f, + 7.452804506020377015e-03f, + 7.456672597697913858e-03f, + 7.453030790120977571e-03f, + 7.441890082528809791e-03f, + 7.423269011139091954e-03f, + 7.397193623136587293e-03f, + 7.363697443128093892e-03f, + 7.322821432105069799e-03f, + 7.274613938962194541e-03f, + 7.219130644628227352e-03f, + 7.156434498872478629e-03f, + 7.086595649857397282e-03f, + 7.009691366515777115e-03f, + 6.925805953836947082e-03f, + 6.835030661154887534e-03f, + 6.737463583536625229e-03f, + 6.633209556377676723e-03f, + 6.522380043316587661e-03f, + 6.405093017587785986e-03f, + 6.281472836939533053e-03f, + 6.151650112247645033e-03f, + 6.015761569964638532e-03f, + 5.873949908548246022e-03f, + 5.726363649019010349e-03f, + 5.573156979804561387e-03f, + 5.414489596030440909e-03f, + 5.250526533426200064e-03f, + 5.081437997018541791e-03f, + 4.907399184788277351e-03f, + 4.728590106474792054e-03f, + 4.545195397714440512e-03f, + 4.357404129703668430e-03f, + 4.165409614584718312e-03f, + 3.969409206751546570e-03f, + 3.769604100281869798e-03f, + 3.566199122702250202e-03f, + 3.359402525296319511e-03f, + 3.149425770172853845e-03f, + 2.936483314308355533e-03f, + 2.720792390786955402e-03f, + 2.502572787458282825e-03f, + 2.282046623241240982e-03f, + 2.059438122297536054e-03f, + 1.834973386307630792e-03f, + 1.608880165078690333e-03f, + 1.381387625716168745e-03f, + 1.152726120594538663e-03f, + 9.231269543609431621e-04f, + 6.928221502061572470e-04f, + 4.620442156413446209e-04f, + 2.310259080137343159e-04f, + 0.000000000000000000e+00f, + -2.308009546876257196e-04f, + -4.611448551480424674e-04f, + -6.908002864531671621e-04f, + -9.195367518942560445e-04f, + -1.147124904077175120e-03f, + -1.373336774636224914e-03f, + -1.597946002337608667e-03f, + -1.820728059342699347e-03f, + -2.041460475406135615e-03f, + -2.259923059786094965e-03f, + -2.475898120643176686e-03f, + -2.689170681712491420e-03f, + -2.899528696030911467e-03f, + -3.106763256509465377e-03f, + -3.310668803139505106e-03f, + -3.511043326630707874e-03f, + -3.707688568276315544e-03f, + -3.900410215849520103e-03f, + -4.089018095338427944e-03f, + -4.273326358329034881e-03f, + -4.453153664852741063e-03f, + -4.628323361519555770e-03f, + -4.798663654760284072e-03f, + -4.964007779010489244e-03f, + -5.124194159669920833e-03f, + -5.279066570679284004e-03f, + -5.428474286561691883e-03f, + -5.572272228779376162e-03f, + -5.710321106265994241e-03f, + -5.842487549997011850e-03f, + -5.968644241469281582e-03f, + -6.088670034966893116e-03f, + -6.202450073495178670e-03f, + -6.309875898273007572e-03f, + -6.410845551679807824e-03f, + -6.505263673558881257e-03f, + -6.593041590787944613e-03f, + -6.674097400032071199e-03f, + -6.748356043603334110e-03f, + -6.815749378356467103e-03f, + -6.876216237559267495e-03f, + -6.929702485681070272e-03f, + -6.976161066051603243e-03f, + -7.015552041349363589e-03f, + -7.047842626885069371e-03f, + -7.073007216654443596e-03f, + -7.091027402140489926e-03f, + -7.101891983853740598e-03f, + -7.105596975605809294e-03f, + -7.102145601518914352e-03f, + -7.091548285781580294e-03f, + -7.073822635167690588e-03f, + -7.048993414343677349e-03f, + -7.017092513995498021e-03f, + -6.978158911814610725e-03f, + -6.932238626389115345e-03f, + -6.879384664053103844e-03f, + -6.819656958754997597e-03f, + -6.753122305011623888e-03f, + -6.679854284022603873e-03f, + -6.599933183026034100e-03f, + -6.513445907982721647e-03f, + -6.420485889683830161e-03f, + -6.321152983382738975e-03f, + -6.215553362058191117e-03f, + -6.103799403422135138e-03f, + -5.986009570792679216e-03f, + -5.862308287957658688e-03f, + -5.732825808160025433e-03f, + -5.597698077343937588e-03f, + -5.457066591803097846e-03f, + -5.311078250381432928e-03f, + -5.159885201379617314e-03f, + -5.003644684325990639e-03f, + -4.842518866777322309e-03f, + -4.676674676317842463e-03f, + -4.506283627929590085e-03f, + -4.331521646914106120e-03f, + -4.152568887545756218e-03f, + -3.969609547645198966e-03f, + -3.782831679262896318e-03f, + -3.592426995666025014e-03f, + -3.398590674828782209e-03f, + -3.201521159624679935e-03f, + -3.001419954927217991e-03f, + -2.798491421825367344e-03f, + -2.592942569162944196e-03f, + -2.384982842615481630e-03f, + -2.174823911519171114e-03f, + -1.962679453667881199e-03f, + -1.748764938296653790e-03f, + -1.533297407473102874e-03f, + -1.316495256117520629e-03f, + -1.098578010873439530e-03f, + -8.797661080547270916e-04f, + -6.602806708906880156e-04f, + -4.403432862964624440e-04f, + -2.201757813928862664e-04f, + 2.040351603294562245e-17f, + 2.199624206685035094e-04f, + 4.394902728878673139e-04f, + 6.583630006562409291e-04f, + 8.763609210572648189e-04f, + 1.093265444530637075e-03f, + 1.308859293828115375e-03f, + 1.522926721436776171e-03f, + 1.735253725253141800e-03f, + 1.945628262290407520e-03f, + 2.153840460208438813e-03f, + 2.359682826453400172e-03f, + 2.562950454799774267e-03f, + 2.763441229090153693e-03f, + 2.960956023969243955e-03f, + 3.155298902413077380e-03f, + 3.346277309858471712e-03f, + 3.533702264740658325e-03f, + 3.717388545249705484e-03f, + 3.897154872122122679e-03f, + 4.072824087288320731e-03f, + 4.244223328198080784e-03f, + 4.411184197655197183e-03f, + 4.573542928992711636e-03f, + 4.731140546427802310e-03f, + 4.883823020440311580e-03f, + 5.031441418021543807e-03f, + 5.173852047649186298e-03f, + 5.310916598845810341e-03f, + 5.442502276186440141e-03f, + 5.568481927626215550e-03f, + 5.688734167023349123e-03f, + 5.803143490740288873e-03f, + 5.911600388211953397e-03f, + 6.014001446374228968e-03f, + 6.110249447855176444e-03f, + 6.200253462834825341e-03f, + 6.283928934487773878e-03f, + 6.361197757929115161e-03f, + 6.431988352589909121e-03f, + 6.496235727955871307e-03f, + 6.553881542609762793e-03f, + 6.604874156524493181e-03f, + 6.649168676560485158e-03f, + 6.686726995128343919e-03f, + 6.717517821984657529e-03f, + 6.741516709135119897e-03f, + 6.758706068827057510e-03f, + 6.769075184619558622e-03f, + 6.772620215526905380e-03f, + 6.769344193237896226e-03f, + 6.759257012420358431e-03f, + 6.742375414127462462e-03f, + 6.718722962329050537e-03f, + 6.688330013598418541e-03f, + 6.651233679991463629e-03f, + 6.607477785162285504e-03f, + 6.557112813765961223e-03f, + 6.500195854205608435e-03f, + 6.436790534788347680e-03f, + 6.366966953360074857e-03f, + 6.290801600496780284e-03f, + 6.208377276335759996e-03f, + 6.119783001136048020e-03f, + 6.025113919665214118e-03f, + 5.924471199513612826e-03f, + 5.817961923445152776e-03f, + 5.705698975897815182e-03f, + 5.587800923754974829e-03f, + 5.464391891511682919e-03f, + 5.335601430968130686e-03f, + 5.201564385586319485e-03f, + 5.062420749651009438e-03f, + 4.918315522382780622e-03f, + 4.769398557154420036e-03f, + 4.615824405966431621e-03f, + 4.457752159344428866e-03f, + 4.295345281821967565e-03f, + 4.128771443180331673e-03f, + 3.958202345618636216e-03f, + 3.783813547031247577e-03f, + 3.605784280576109987e-03f, + 3.424297270716869252e-03f, + 3.239538545929211699e-03f, + 3.051697248262471695e-03f, + 2.860965439950404268e-03f, + 2.667537907270099533e-03f, + 2.471611961848510855e-03f, + 2.273387239617670301e-03f, + 2.073065497626217380e-03f, + 1.870850408910636978e-03f, + 1.666947355636944159e-03f, + 1.461563220720618573e-03f, + 1.254906178138427263e-03f, + 1.047185482141736864e-03f, + 8.386112555867926812e-04f, + 6.293942775949764364e-04f, + 4.197457707565461658e-04f, + 2.098771880941680316e-04f, + 5.182902902306673378e-17f, + -2.096745186397723764e-04f, + -4.189355009176130525e-04f, + -6.275726997620592390e-04f, + -8.353766989667140222e-04f, + -1.042139123179063390e-03f, + -1.247652846640042714e-03f, + -1.451712200463105501e-03f, + -1.654113178248326063e-03f, + -1.854653639823899535e-03f, + -2.053133512912701415e-03f, + -2.249354992523720179e-03f, + -2.443122737868256775e-03f, + -2.634244066605816777e-03f, + -2.822529146228016077e-03f, + -3.007791182387730897e-03f, + -3.189846603989948855e-03f, + -3.368515244858865266e-03f, + -3.543620521803565997e-03f, + -3.714989608904360665e-03f, + -3.882453607850389066e-03f, + -4.045847714158852700e-03f, + -4.205011379113398479e-03f, + -4.359788467263430063e-03f, + -4.510027409328340973e-03f, + -4.655581350359307320e-03f, + -4.796308293012311458e-03f, + -4.932071235793691541e-03f, + -5.062738306144363987e-03f, + -5.188182888232652744e-03f, + -5.308283745332808071e-03f, + -5.422925136671735008e-03f, + -5.531996928630324509e-03f, + -5.635394700194489362e-03f, + -5.733019842553966489e-03f, + -5.824779652754896557e-03f, + -5.910587421317968493e-03f, + -5.990362513738736391e-03f, + -6.064030445795148827e-03f, + -6.131522952591644815e-03f, + -6.192778051276466925e-03f, + -6.247740097375581447e-03f, + -6.296359834692139695e-03f, + -6.338594438727926730e-03f, + -6.374407553588771515e-03f, + -6.403769322343668378e-03f, + -6.426656410812841419e-03f, + -6.443052024767408938e-03f, + -6.452945920529682723e-03f, + -6.456334408969627466e-03f, + -6.453220352899971361e-03f, + -6.443613157878890461e-03f, + -6.427528756435856722e-03f, + -6.404989585742853347e-03f, + -6.376024558759821499e-03f, + -6.340669028889607886e-03f, + -6.298964748184136819e-03f, + -6.250959819150409338e-03f, + -6.196708640210494418e-03f, + -6.136271844877025546e-03f, + -6.069716234711278365e-03f, + -5.997114706136989597e-03f, + -5.918546171190292200e-03f, + -5.834095472290020540e-03f, + -5.743853291122007730e-03f, + -5.647916051732237638e-03f, + -5.546385817934258548e-03f, + -5.439370185137868373e-03f, + -5.326982166714451343e-03f, + -5.209340075018284791e-03f, + -5.086567397188134117e-03f, + -4.958792665860112768e-03f, + -4.826149324926455597e-03f, + -4.688775590479438843e-03f, + -4.546814307086526931e-03f, + -4.400412799544136007e-03f, + -4.249722720265064653e-03f, + -4.094899892456855747e-03f, + -3.936104149252287777e-03f, + -3.773499168959473186e-03f, + -3.607252306599120252e-03f, + -3.437534421903807366e-03f, + -3.264519703955130747e-03f, + -3.088385492637782913e-03f, + -2.909312097094693232e-03f, + -2.727482611368168587e-03f, + -2.543082727414555134e-03f, + -2.356300545685272984e-03f, + -2.167326383463741464e-03f, + -1.976352581158412423e-03f, + -1.783573306742843468e-03f, + -1.589184358546649181e-03f, + -1.393382966593760411e-03f, + -1.196367592691694598e-03f, + -9.983377294733853085e-04f, + -7.994936985940647280e-04f, + -6.000364482887320626e-04f, + -4.001673504937983648e-04f, + -2.000879977366511349e-04f, + -2.778966336884198701e-17f, + 1.998952182369787911e-04f, + 3.993966205749271080e-04f, + 5.983037606844037121e-04f, + 7.964169835089038611e-04f, + 9.935376254762236150e-04f, + 1.189468213520454305e-03f, + 1.384012662713176006e-03f, + 1.576976472307146200e-03f, + 1.768166919997466715e-03f, + 1.957393254204812128e-03f, + 2.144466884189903406e-03f, + 2.329201567810756240e-03f, + 2.511413596733711156e-03f, + 2.690921978917853227e-03f, + 2.867548618186650019e-03f, + 3.041118490714745520e-03f, + 3.211459818250186199e-03f, + 3.378404237904315821e-03f, + 3.541786968339587227e-03f, + 3.701446972192292979e-03f, + 3.857227114570970297e-03f, + 4.008974317473392411e-03f, + 4.156539709971333353e-03f, + 4.299778774016645676e-03f, + 4.438551485724410994e-03f, + 4.572722451997321066e-03f, + 4.702161042356552134e-03f, + 4.826741515851794284e-03f, + 4.946343142927884552e-03f, + 5.060850322128870871e-03f, + 5.170152691528663233e-03f, + 5.274145234779758183e-03f, + 5.372728381679093071e-03f, + 5.465808103155446809e-03f, + 5.553296000587267314e-03f, + 5.635109389366979755e-03f, + 5.711171376633489283e-03f, + 5.781410933099281717e-03f, + 5.845762958906964256e-03f, + 5.904168343452670387e-03f, + 5.956574019124233517e-03f, + 6.002933008903923832e-03f, + 6.043204467795012733e-03f, + 6.077353718035662794e-03f, + 6.105352278070978224e-03f, + 6.127177885260168266e-03f, + 6.142814512301600360e-03f, + 6.152252377365692929e-03f, + 6.155487947931146189e-03f, + 6.152523938326903176e-03f, + 6.143369300988309016e-03f, + 6.128039211442289080e-03f, + 6.106555047042762537e-03f, + 6.078944359483547284e-03f, + 6.045240841122706543e-03f, + 6.005484285157759554e-03f, + 5.959720539698134222e-03f, + 5.908001455786886062e-03f, + 5.850384829429483569e-03f, + 5.786934337694401610e-03f, + 5.717719468954846757e-03f, + 5.642815447348050774e-03f, + 5.562303151533275723e-03f, + 5.476269027836244349e-03f, + 5.384804997872509429e-03f, + 5.288008360747798245e-03f, + 5.185981689939928203e-03f, + 5.078832724969969611e-03f, + 4.966674257977718963e-03f, + 4.849624015320279195e-03f, + 4.727804534317043249e-03f, + 4.601343035271158316e-03f, + 4.470371288899312352e-03f, + 4.335025479308940791e-03f, + 4.195446062664641082e-03f, + 4.051777621689569946e-03f, + 3.904168716153416478e-03f, + 3.752771729500774690e-03f, + 3.597742711777335116e-03f, + 3.439241219017221893e-03f, + 3.277430149254536695e-03f, + 3.112475575329156242e-03f, + 2.944546574657440699e-03f, + 2.773815056141443278e-03f, + 2.600455584395403811e-03f, + 2.424645201466829776e-03f, + 2.246563246236072363e-03f, + 2.066391171676635035e-03f, + 1.884312360164382659e-03f, + 1.700511937020543225e-03f, + 1.515176582480621701e-03f, + 1.328494342278832389e-03f, + 1.140654437039362876e-03f, + 9.518470706689883707e-04f, + 7.622632379441338004e-04f, + 5.720945314859912732e-04f, + 3.815329483207086005e-04f, + 1.907706962172203138e-04f, + 0.000000000000000000e+00f, + -1.905870919690720166e-04f, + -3.807989009460705778e-04f, + -5.704443104011880719e-04f, + -7.593329578651235225e-04f, + -9.472754258310465579e-04f, + -1.134083431521770496e-03f, + -1.319570015332951216e-03f, + -1.503549727762363468e-03f, + -1.685838814639319377e-03f, + -1.866255400470208034e-03f, + -2.044619669715354044e-03f, + -2.220754045819160229e-03f, + -2.394483367813274831e-03f, + -2.565635064319175124e-03f, + -2.734039324775422008e-03f, + -2.899529267722648820e-03f, + -3.061941105977069883e-03f, + -3.221114308530460043e-03f, + -3.376891759017279938e-03f, + -3.529119910591425012e-03f, + -3.677648937060845960e-03f, + -3.822332880132129837e-03f, + -3.963029792618880109e-03f, + -4.099601877475613464e-03f, + -4.231915622519577709e-03f, + -4.359841930709697395e-03f, + -4.483256245856345398e-03f, + -4.602038673638307598e-03f, + -4.716074097811356024e-03f, + -4.825252291494681184e-03f, + -4.929468023428436492e-03f, + -5.028621159100716588e-03f, + -5.122616756646161500e-03f, + -5.211365157425208207e-03f, + -5.294782071198274320e-03f, + -5.372788655813301971e-03f, + -5.445311591332913613e-03f, + -5.512283148530877468e-03f, + -5.573641251695150671e-03f, + -5.629329535678900300e-03f, + -5.679297397148663600e-03f, + -5.723500039982650292e-03f, + -5.761898514779588421e-03f, + -5.794459752444061987e-03f, + -5.821156591819873563e-03f, + -5.841967801349767786e-03f, + -5.856878094745045400e-03f, + -5.865878140655318707e-03f, + -5.868964566334378651e-03f, + -5.866139955304165016e-03f, + -5.857412839025080524e-03f, + -5.842797682586693166e-03f, + -5.822314864439076185e-03f, + -5.795990650190794430e-03f, + -5.763857160505719661e-03f, + -5.725952333136686070e-03f, + -5.682319879139611715e-03f, + -5.633009233318153446e-03f, + -5.578075498953882598e-03f, + -5.517579386883428826e-03f, + -5.451587148989341623e-03f, + -5.380170506176684860e-03f, + -5.303406570913458962e-03f, + -5.221377764418140034e-03f, + -5.134171728582574354e-03f, + -5.041881232723846064e-03f, + -4.944604075264455272e-03f, + -4.842442980444399032e-03f, + -4.735505490173459210e-03f, + -4.623903851138305189e-03f, + -4.507754897281265882e-03f, + -4.387179927774727435e-03f, + -4.262304580617813123e-03f, + -4.133258701986374309e-03f, + -4.000176211472765767e-03f, + -3.863194963354634262e-03f, + -3.722456604035540191e-03f, + -3.578106425806113627e-03f, + -3.430293217074662122e-03f, + -3.279169109222944672e-03f, + -3.124889420243918709e-03f, + -2.967612495321265455e-03f, + -2.807499544515807202e-03f, + -2.644714477722959114e-03f, + -2.479423737071674658e-03f, + -2.311796126935456146e-03f, + -2.142002641728153745e-03f, + -1.970216291661061950e-03f, + -1.796611926638601179e-03f, + -1.621366058471044799e-03f, + -1.444656681584796090e-03f, + -1.266663092413158409e-03f, + -1.087565707650077398e-03f, + -9.075458815500990310e-04f, + -7.267857224613794324e-04f, + -5.454679087747773278e-04f, + -3.637755044768799673e-04f, + -1.818917744922234167e-04f, + -6.738072953108251659e-17f, + 1.817167060868667974e-04f, + 3.630755832968266678e-04f, + 5.438944072423667351e-04f, + 7.239916725462016735e-04f, + 9.031867748685456793e-04f, + 1.081300191851528036e-03f, + 1.258153662800393048e-03f, + 1.433570366922595502e-03f, + 1.607375099944760447e-03f, + 1.779394448933648617e-03f, + 1.949456965144877449e-03f, + 2.117393334728095468e-03f, + 2.283036547119419220e-03f, + 2.446222060952827054e-03f, + 2.606787967325983370e-03f, + 2.764575150259260348e-03f, + 2.919427444189183745e-03f, + 3.071191788339645758e-03f, + 3.219718377819110952e-03f, + 3.364860811295488155e-03f, + 3.506476235101599771e-03f, + 3.644425483631654871e-03f, + 3.778573215889237825e-03f, + 3.908788048053789092e-03f, + 4.034942681936477468e-03f, + 4.156914029198645506e-03f, + 4.274583331213595815e-03f, + 4.387836274453780008e-03f, + 4.496563101292173729e-03f, + 4.600658716111065936e-03f, + 4.700022786615127950e-03f, + 4.794559840251754780e-03f, + 4.884179355646818196e-03f, + 4.968795848967465581e-03f, + 5.048328955131142076e-03f, + 5.122703503783020661e-03f, + 5.191849589970850935e-03f, + 5.255702639451336616e-03f, + 5.314203468567116513e-03f, + 5.367298338639234223e-03f, + 5.414939004826021217e-03f, + 5.457082759404304449e-03f, + 5.493692469434578556e-03f, + 5.524736608777790041e-03f, + 5.550189284437050813e-03f, + 5.570030257202884111e-03f, + 5.584244956587067731e-03f, + 5.592824490035200498e-03f, + 5.595765646414466166e-03f, + 5.593070893778484716e-03f, + 5.584748371416978392e-03f, + 5.570811876203813663e-03f, + 5.551280843262597353e-03f, + 5.526180320974835580e-03f, + 5.495540940361090358e-03f, + 5.459398878871563054e-03f, + 5.417795818627875980e-03f, + 5.370778899163226178e-03f, + 5.318400664714218601e-03f, + 5.260719006122124147e-03f, + 5.197797097407722174e-03f, + 5.129703327088560444e-03f, + 5.056511224312396427e-03f, + 4.978299379887104678e-03f, + 4.895151362290443668e-03f, + 4.807155628749874669e-03f, + 4.714405431485941232e-03f, + 4.616998719219183103e-03f, + 4.515038034043206486e-03f, + 4.408630403773131437e-03f, + 4.297887229881802197e-03f, + 4.182924171140340805e-03f, + 4.063861023085189352e-03f, + 3.940821593436607748e-03f, + 3.813933573597338717e-03f, + 3.683328406365983000e-03f, + 3.549141150000212786e-03f, + 3.411510338771623577e-03f, + 3.270577840155389476e-03f, + 3.126488708801145999e-03f, + 2.979391037436725231e-03f, + 2.829435804855929236e-03f, + 2.676776721147767438e-03f, + 2.521570070325003087e-03f, + 2.363974550512310240e-03f, + 2.204151111858506946e-03f, + 2.042262792337711051e-03f, + 1.878474551605653284e-03f, + 1.712953103082745163e-03f, + 1.545866744432049548e-03f, + 1.377385186606322909e-03f, + 1.207679381635921347e-03f, + 1.036921349334177364e-03f, + 8.652840030934819777e-04f, + 6.929409749502218625e-04f, + 5.200664400946462904e-04f, + 3.468349410021595147e-04f, + 1.734212113648546741e-04f, + 4.550251594910668594e-17f, + -1.732541050876819561e-04f, + -3.461668523119788257e-04f, + -5.185645016036600878e-04f, + -6.902739988253162214e-04f, + -8.611231493274518678e-04f, + -1.030940790473021396e-03f, + -1.199556962955737344e-03f, + -1.366803080743145661e-03f, + -1.532512099472770496e-03f, + -1.696518683134078977e-03f, + -1.858659368870755474e-03f, + -2.018772729737835104e-03f, + -2.176699535252429873e-03f, + -2.332282909579588056e-03f, + -2.485368487193882845e-03f, + -2.635804565865009202e-03f, + -2.783442256814043243e-03f, + -2.928135631893509418e-03f, + -3.069741867644090218e-03f, + -3.208121386087987512e-03f, + -3.343137992118676207e-03f, + -3.474659007352662483e-03f, + -3.602555400312496166e-03f, + -3.726701912812024550e-03f, + -3.846977182422010779e-03f, + -3.963263860895238633e-03f, + -4.075448728436278055e-03f, + -4.183422803705368759e-03f, + -4.287081449448809645e-03f, + -4.386324473654241302e-03f, + -4.481056226133709602e-03f, + -4.571185690440540857e-03f, + -4.656626571033325307e-03f, + -4.737297375602708346e-03f, + -4.813121492483313744e-03f, + -4.884027263077780033e-03f, + -4.949948049224051072e-03f, + -5.010822295443905297e-03f, + -5.066593586014279949e-03f, + -5.117210696809050205e-03f, + -5.162627641864468188e-03f, + -5.202803714625973616e-03f, + -5.237703523840436977e-03f, + -5.267297024062328296e-03f, + -5.291559540748885272e-03f, + -5.310471789923680921e-03f, + -5.324019892394351271e-03f, + -5.332195382515387931e-03f, + -5.334995211492236412e-03f, + -5.332421745228884194e-03f, + -5.324482756726165410e-03f, + -5.311191413043757879e-03f, + -5.292566256844271830e-03f, + -5.268631182543137906e-03f, + -5.239415407093645990e-03f, + -5.204953435441478891e-03f, + -5.165285020688987344e-03f, + -5.120455119014034190e-03f, + -5.070513839394155328e-03f, + -5.015516388191593698e-03f, + -4.955523008659636248e-03f, + -4.890598915436772734e-03f, + -4.820814224098224007e-03f, + -4.746243875842361994e-03f, + -4.666967557390416105e-03f, + -4.583069616186550947e-03f, + -4.494638970986890303e-03f, + -4.401769017932832123e-03f, + -4.304557532207274433e-03f, + -4.203106565376605631e-03f, + -4.097522338526705075e-03f, + -3.987915131304275475e-03f, + -3.874399166978647214e-03f, + -3.757092493644813031e-03f, + -3.636116861689554780e-03f, + -3.511597597648849663e-03f, + -3.383663474586599915e-03f, + -3.252446579127917913e-03f, + -3.118082175285466148e-03f, + -2.980708565217376257e-03f, + -2.840466947061273412e-03f, + -2.697501269989864385e-03f, + -2.551958086636046431e-03f, + -2.403986403039817116e-03f, + -2.253737526269895732e-03f, + -2.101364909875016573e-03f, + -1.947023997324411840e-03f, + -1.790872063594110223e-03f, + -1.633068055064607060e-03f, + -1.473772427887728434e-03f, + -1.313146984991218642e-03f, + -1.151354711883460216e-03f, + -9.885596114266794344e-04f, + -8.249265377453095672e-04f, + -6.606210294368872974e-04f, + -4.958091422553860149e-04f, + -3.306572814353150912e-04f, + -1.653320338249910076e-04f, + -2.551386587880797004e-17f, + 1.651723734761380335e-04f, + 3.300189614740527827e-04f, + 4.943741272349469777e-04f, + 6.580728886553833265e-04f, + 8.209510837512715370e-04f, + 9.828455351376394825e-04f, + 1.143594213357676742e-03f, + 1.303036398898462693e-03f, + 1.461012842732159695e-03f, + 1.617365925221177182e-03f, + 1.771939813229538550e-03f, + 1.924580615284706168e-03f, + 2.075136534633631631e-03f, + 2.223458020043932865e-03f, + 2.369397914196333301e-03f, + 2.512811599526118631e-03f, + 2.653557141365132238e-03f, + 2.791495428245575894e-03f, + 2.926490309225380886e-03f, + 3.058408728100418372e-03f, + 3.187120854371934629e-03f, + 3.312500210839413109e-03f, + 3.434423797694213451e-03f, + 3.552772212992939740e-03f, + 3.667429769391414990e-03f, + 3.778284607026904680e-03f, + 3.885228802437357565e-03f, + 3.988158473412363458e-03f, + 4.086973879674651519e-03f, + 4.181579519293593741e-03f, + 4.271884220739164024e-03f, + 4.357801230486726615e-03f, + 4.439248296089245809e-03f, + 4.516147744637950996e-03f, + 4.588426556536215166e-03f, + 4.656016434517300413e-03f, + 4.718853867841324261e-03f, + 4.776880191610652533e-03f, + 4.830041641149988331e-03f, + 4.878289401399377341e-03f, + 4.921579651277259264e-03f, + 4.959873602972050821e-03f, + 4.993137536128673497e-03f, + 5.021342826899958915e-03f, + 5.044465971838839151e-03f, + 5.062488606612373584e-03f, + 5.075397519523472574e-03f, + 5.083184659832008616e-03f, + 5.085847140871759681e-03f, + 5.083387237965185726e-03f, + 5.075812381143095242e-03f, + 5.063135142681596430e-03f, + 5.045373219473861526e-03f, + 5.022549410259432613e-03f, + 4.994691587739028341e-03f, + 4.961832665607723673e-03f, + 4.924010560544767219e-03f, + 4.881268149203150074e-03f, + 4.833653220246830577e-03f, + 4.781218421489157218e-03f, + 4.724021202189871739e-03f, + 4.662123750573998575e-03f, + 4.595592926639679825e-03f, + 4.524500190327651952e-03f, + 4.448921525128770975e-03f, + 4.368937357210857936e-03f, + 4.284632470151218328e-03f, + 4.196095915364042757e-03f, + 4.103420918317851433e-03f, + 4.006704780641143562e-03f, + 3.906048778218370893e-03f, + 3.801558055383750991e-03f, + 3.693341515321936323e-03f, + 3.581511706790642402e-03f, + 3.466184707282398206e-03f, + 3.347480002746349489e-03f, + 3.225520363994377124e-03f, + 3.100431719920501098e-03f, + 2.972343027662367743e-03f, + 2.841386139839377056e-03f, + 2.707695669005051858e-03f, + 2.571408849450755249e-03f, + 2.432665396503416125e-03f, + 2.291607363462537170e-03f, + 2.148378996320580991e-03f, + 2.003126586416237656e-03f, + 1.855998321172221827e-03f, + 1.707144133066539190e-03f, + 1.556715546994994403e-03f, + 1.404865526175997237e-03f, + 1.251748316758327694e-03f, + 1.097519291286103453e-03f, + 9.423347911819565052e-04f, + 7.863519684062989651e-04f, + 6.297286264551491203e-04f, + 4.726230608546003620e-04f, + 3.151938993142525320e-04f, + 1.575999417021628837e-04f, + 7.784442283331923853e-17f, + -1.574472615986340432e-04f, + -3.145834862862096032e-04f, + -4.712507837750268031e-04f, + -6.272918888083930770e-04f, + -7.825503188873347447e-04f, + -9.368705310580024981e-04f, + -1.090098077600495973e-03f, + -1.242079760465078378e-03f, + -1.392663784299297746e-03f, + -1.541699907915918119e-03f, + -1.689039594049394563e-03f, + -1.834536157250591781e-03f, + -1.978044909775040876e-03f, + -2.119423305318431615e-03f, + -2.258531080456323715e-03f, + -2.395230393648445800e-03f, + -2.529385961670430668e-03f, + -2.660865193337756500e-03f, + -2.789538320388635587e-03f, + -2.915278525399034055e-03f, + -3.037962066602765791e-03f, + -3.157468399492973938e-03f, + -3.273680295087870538e-03f, + -3.386483954743054609e-03f, + -3.495769121399434042e-03f, + -3.601429187156589447e-03f, + -3.703361297068739032e-03f, + -3.801466449061457163e-03f, + -3.895649589871598342e-03f, + -3.985819706919274674e-03f, + -4.071889916022448409e-03f, + -4.153777544869190555e-03f, + -4.231404212169127994e-03f, + -4.304695902407762974e-03f, + -4.373583036132095911e-03f, + -4.438000535702448651e-03f, + -4.497887886447530723e-03f, + -4.553189193166368338e-03f, + -4.603853231924096880e-03f, + -4.649833497094323954e-03f, + -4.691088243605770480e-03f, + -4.727580524354188865e-03f, + -4.759278222747725245e-03f, + -4.786154080356886005e-03f, + -4.808185719646517267e-03f, + -4.825355661771149086e-03f, + -4.837651339421160553e-03f, + -4.845065104711230967e-03f, + -4.847594232107986685e-03f, + -4.845240916398877579e-03f, + -4.838012265708960952e-03f, + -4.825920289577491362e-03f, + -4.808981882111157055e-03f, + -4.787218800235539494e-03f, + -4.760657637071705889e-03f, + -4.729329790469200510e-03f, + -4.693271426731996689e-03f, + -4.652523439578401879e-03f, + -4.607131404381258066e-03f, + -4.557145527738585764e-03f, + -4.502620592430119431e-03f, + -4.443615897820203317e-03f, + -4.380195195770275780e-03f, + -4.312426622131352791e-03f, + -4.240382623888087031e-03f, + -4.164139882033739311e-03f, + -4.083779230256361775e-03f, + -3.999385569522737396e-03f, + -3.911047778650672546e-03f, + -3.818858620962382270e-03f, + -3.722914647117278510e-03f, + -3.623316094226521674e-03f, + -3.520166781352995246e-03f, + -3.413574001506695325e-03f, + -3.303648410246657133e-03f, + -3.190503911006380925e-03f, + -3.074257537259608274e-03f, + -2.955029331648956637e-03f, + -2.832942222203055620e-03f, + -2.708121895767779796e-03f, + -2.580696668782614736e-03f, + -2.450797355535883071e-03f, + -2.318557134031954108e-03f, + -2.184111409608732648e-03f, + -2.047597676446088772e-03f, + -1.909155377104432803e-03f, + -1.768925760238825853e-03f, + -1.627051736631422978e-03f, + -1.483677733692766133e-03f, + -1.338949548574867041e-03f, + -1.193014200049233526e-03f, + -1.046019779297893606e-03f, + -8.981152997709223890e-04f, + -7.494505462600843963e-04f, + -6.001759233424719181e-04f, + -4.504423033485727233e-04f, + -3.004008740059210055e-04f, + -1.502029859132318287e-04f, + 0.000000000000000000e+00f, + 1.500568648773128620e-04f, + 2.998166840477804458e-04f, + 4.491289786934320964e-04f, + 5.978438669215414185e-04f, + 7.458122140882831198e-04f, + 8.928857822246706166e-04f, + 1.038917378413064868e-03f, + 1.183761001968396554e-03f, + 1.327271990275858586e-03f, + 1.469307163138363920e-03f, + 1.609724965492229879e-03f, + 1.748385608347759190e-03f, + 1.885151207813307852e-03f, + 2.019885922067833454e-03f, + 2.152456086142171161e-03f, + 2.282730344379329916e-03f, + 2.410579780439001552e-03f, + 2.535878044720715069e-03f, + 2.658501479078634582e-03f, + 2.778329238704075124e-03f, + 2.895243411057801981e-03f, + 3.009129131734148907e-03f, + 3.119874697142386698e-03f, + 3.227371673896960082e-03f, + 3.331515004807996620e-03f, + 3.432203111369802693e-03f, + 3.529337992646119828e-03f, + 3.622825320457822695e-03f, + 3.712574530779899368e-03f, + 3.798498911258710829e-03f, + 3.880515684766671931e-03f, + 3.958546088913222558e-03f, + 4.032515451435444119e-03f, + 4.102353261397662619e-03f, + 4.167993236131739207e-03f, + 4.229373383854271966e-03f, + 4.286436061903056705e-03f, + 4.339128030537392315e-03f, + 4.387400502253299348e-03f, + 4.431209186566910548e-03f, + 4.470514330227219786e-03f, + 4.505280752820716751e-03f, + 4.535477877737257441e-03f, + 4.561079758469966522e-03f, + 4.582065100227887371e-03f, + 4.598417276843720300e-03f, + 4.610124342964186731e-03f, + 4.617179041515659735e-03f, + 4.619578806441900290e-03f, + 4.617325760715732842e-03f, + 4.610426709631436880e-03f, + 4.598893129389029603e-03f, + 4.582741150986642878e-03f, + 4.561991539441702728e-03f, + 4.536669668366456049e-03f, + 4.506805489927790190e-03f, + 4.472433500226501331e-03f, + 4.433592700134834745e-03f, + 4.390326551636334652e-03f, + 4.342682929716723365e-03f, + 4.290714069857965926e-03f, + 4.234476511192970805e-03f, + 4.174031035382547508e-03f, + 4.109442601280248290e-03f, + 4.040780275454714486e-03f, + 3.968117158643618379e-03f, + 3.891530308217908476e-03f, + 3.811100656737222755e-03f, + 3.726912926682747658e-03f, + 3.639055541457847723e-03f, + 3.547620532748325950e-03f, + 3.452703444340344142e-03f, + 3.354403232495308754e-03f, + 3.252822162986789261e-03f, + 3.148065704904799266e-03f, + 3.040242421338187288e-03f, + 2.929463857049219994e-03f, + 2.815844423254667064e-03f, + 2.699501279633036762e-03f, + 2.580554213680439322e-03f, + 2.459125517537331940e-03f, + 2.335339862413496597e-03f, + 2.209324170741059669e-03f, + 2.081207486184459728e-03f, + 1.951120841642063749e-03f, + 1.819197125372980488e-03f, + 1.685570945387657215e-03f, + 1.550378492237483022e-03f, + 1.413757400346775340e-03f, + 1.275846608024381462e-03f, + 1.136786216300893085e-03f, + 9.967173467317232399e-04f, + 8.557819983114523963e-04f, + 7.141229036455263310e-04f, + 5.718833845226121287e-04f, + 4.292072070347436501e-04f, + 2.862384363927389136e-04f, + 1.431212915810999909e-04f, + 3.976073045763746639e-17f, + -1.429813477578194541e-04f, + -2.856789428524072441e-04f, + -4.279494027150967353e-04f, + -5.696499149979476154e-04f, + -7.106383808068707187e-04f, + -8.507735570776003928e-04f, + -9.899151979507884221e-04f, + -1.127924195003721704e-03f, + -1.264662716200822120e-03f, + -1.399994343423132705e-03f, + -1.533784208438585678e-03f, + -1.665899127179875554e-03f, + -1.796207732195136955e-03f, + -1.924580603139482322e-03f, + -2.050890395178425665e-03f, + -2.175011965176371249e-03f, + -2.296822495544894628e-03f, + -2.416201615627299338e-03f, + -2.533031520501645515e-03f, + -2.647197087084067826e-03f, + -2.758585987417253894e-03f, + -2.867088799034697190e-03f, + -2.972599112290812313e-03f, + -3.075013634552971071e-03f, + -3.174232291152105818e-03f, + -3.270158322995199603e-03f, + -3.362698380743715538e-03f, + -3.451762615465765304e-03f, + -3.537264765675776721e-03f, + -3.619122240676629892e-03f, + -3.697256200123399950e-03f, + -3.771591629733630612e-03f, + -3.842057413070905456e-03f, + -3.908586399332766859e-03f, + -3.971115467079878135e-03f, + -4.029585583845121145e-03f, + -4.083941861567453452e-03f, + -4.134133607797787699e-03f, + -4.180114372630844115e-03f, + -4.221841991318395042e-03f, + -4.259278622526630094e-03f, + -4.292390782202119143e-03f, + -4.321149373017542893e-03f, + -4.345529709371441680e-03f, + -4.365511537920943770e-03f, + -4.381079053631709713e-03f, + -4.392220911332943636e-03f, + -4.398930232770211640e-03f, + -4.401204609153669053e-03f, + -4.399046099203249988e-03f, + -4.392461222697331344e-03f, + -4.381460949535757529e-03f, + -4.366060684332634563e-03f, + -4.346280246558699781e-03f, + -4.322143846257833419e-03f, + -4.293680055366262162e-03f, + -4.260921774667682412e-03f, + -4.223906196422148006e-03f, + -4.182674762710170135e-03f, + -4.137273119538274584e-03f, + -4.087751066756856698e-03f, + -4.034162503844153022e-03f, + -3.976565371615301513e-03f, + -3.915021589919383607e-03f, + -3.849596991390978259e-03f, + -3.780361251326535416e-03f, + -3.707387813760094821e-03f, + -3.630753813817203315e-03f, + -3.550539996427677474e-03f, + -3.466830631483622076e-03f, + -3.379713425530830959e-03f, + -3.289279430087042790e-03f, + -3.195622946681347856e-03f, + -3.098841428714084380e-03f, + -2.999035380240086535e-03f, + -2.896308251778681887e-03f, + -2.790766333258949596e-03f, + -2.682518644211763417e-03f, + -2.571676821320250696e-03f, + -2.458355003445216510e-03f, + -2.342669714244821622e-03f, + -2.224739742507099091e-03f, + -2.104686020319705697e-03f, + -1.982631499200445194e-03f, + -1.858701024317035294e-03f, + -1.733021206922603788e-03f, + -1.605720295138181623e-03f, + -1.476928043214278908e-03f, + -1.346775579404299638e-03f, + -1.215395272583683463e-03f, + -1.082920597750686208e-03f, + -9.494860005465256434e-04f, + -8.152267609301555324e-04f, + -6.802788561468293017e-04f, + -5.447788231302409367e-04f, + -4.088636204751821168e-04f, + -2.726704901212254279e-04f, + -1.363368188880890964e-04f, + -8.417792963206818356e-17f, + 1.362027052598085422e-04f, + 2.721343056259442508e-04f, + 4.076582175196397572e-04f, + 5.426384021722333592e-04f, + 6.769395020654857481e-04f, + 8.104269765506644935e-04f, + 9.429672365128469180e-04f, + 1.074427777944659190e-03f, + 1.204677314294974296e-03f, + 1.333585907462621518e-03f, + 1.461025097303440606e-03f, + 1.586868029520704633e-03f, + 1.710989581813802214e-03f, + 1.833266488158666763e-03f, + 1.953577461096326229e-03f, + 2.071803311908859517e-03f, + 2.187827068564226606e-03f, + 2.301534091313028798e-03f, + 2.412812185822113195e-03f, + 2.521551713735463440e-03f, + 2.627645700551932938e-03f, + 2.730989940714999460e-03f, + 2.831483099809944254e-03f, + 2.929026813770171413e-03f, + 3.023525784994855574e-03f, + 3.114887875283365529e-03f, + 3.203024195497624661e-03f, + 3.287849191864475069e-03f, + 3.369280728833792034e-03f, + 3.447240168413722491e-03f, + 3.521652445905853156e-03f, + 3.592446141966990254e-03f, + 3.659553550929970766e-03f, + 3.722910745317228355e-03f, + 3.782457636486660606e-03f, + 3.838138031351646592e-03f, + 3.889899685123011756e-03f, + 3.937694350023167832e-03f, + 3.981477819927187536e-03f, + 4.021209970890143003e-03f, + 4.056854797524065380e-03f, + 4.088380445191790516e-03f, + 4.115759237989136295e-03f, + 4.138967702491964246e-03f, + 4.157986587248014773e-03f, + 4.172800877997954669e-03f, + 4.183399808614909378e-03f, + 4.189776867755429235e-03f, + 4.191929801219356974e-03f, + 4.189860610020690324e-03f, + 4.183575544175400195e-03f, + 4.173085092216739442e-03f, + 4.158403966452908773e-03f, + 4.139551083985980491e-03f, + 4.116549543515387807e-03f, + 4.089426597953691074e-03f, + 4.058213622886065394e-03f, + 4.022946080909405378e-03f, + 3.983663481891361151e-03f, + 3.940409339192868857e-03f, + 3.893231121902388826e-03f, + 3.842180203134375367e-03f, + 3.787311804446883475e-03f, + 3.728684936439468408e-03f, + 3.666362335593461707e-03f, + 3.600410397423470070e-03f, + 3.530899106009756632e-03f, + 3.457901959986858006e-03f, + 3.381495895065876871e-03f, + 3.301761203172812187e-03f, + 3.218781448286551963e-03f, + 3.132643379065020751e-03f, + 3.043436838351391838e-03f, + 2.951254669653231681e-03f, + 2.856192620692367314e-03f, + 2.758349244126263259e-03f, + 2.657825795542260918e-03f, + 2.554726128830706305e-03f, + 2.449156589045835622e-03f, + 2.341225902863081770e-03f, + 2.231045066746887174e-03f, + 2.118727232942822086e-03f, + 2.004387593412520346e-03f, + 1.888143261828470910e-03f, + 1.770113153750305698e-03f, + 1.650417865105977249e-03f, + 1.529179549100039177e-03f, + 1.406521791675539998e-03f, + 1.282569485656734343e-03f, + 1.157448703700234525e-03f, + 1.031286570183274475e-03f, + 9.042111321594720934e-04f, + 7.763512295141050895e-04f, + 6.478363644484194097e-04f, + 5.187965704260827501e-04f, + 3.893622807153351221e-04f, + 2.596641966575497277e-04f, + 1.298331557961695734e-04f, + 0.000000000000000000e+00f, + -1.297045562884773494e-04f, + -2.591500575455231805e-04f, + -3.882064382682380425e-04f, + -5.167441535531211438e-04f, + -6.446343090221472215e-04f, + -7.717487899651593111e-04f, + -8.979603895682745050e-04f, + -1.023142936102136233e-03f, + -1.147171418941898315e-03f, + -1.269922113292209966e-03f, + -1.391272703494883479e-03f, + -1.511102404795431394e-03f, + -1.629292083446334978e-03f, + -1.745724375030405316e-03f, + -1.860283800883534306e-03f, + -1.972856882504857214e-03f, + -2.083332253837852453e-03f, + -2.191600771314119924e-03f, + -2.297555621549458339e-03f, + -2.401092426587344512e-03f, + -2.502109346584675646e-03f, + -2.600507179840733183e-03f, + -2.696189460070382710e-03f, + -2.789062550825482587e-03f, + -2.879035736973885838e-03f, + -2.966021313145917292e-03f, + -3.049934669061640530e-03f, + -3.130694371657468528e-03f, + -3.208222243931873989e-03f, + -3.282443440433375874e-03f, + -3.353286519319528813e-03f, + -3.420683510916569352e-03f, + -3.484569982714951809e-03f, + -3.544885100737864888e-03f, + -3.601571687225699116e-03f, + -3.654576274581390269e-03f, + -3.703849155525655665e-03f, + -3.749344429416276783e-03f, + -3.791020044687998507e-03f, + -3.828837837374106986e-03f, + -3.862763565674989016e-03f, + -3.892766940542859502e-03f, + -3.918821652255656887e-03f, + -3.940905392957132734e-03f, + -3.958999875144887486e-03f, + -3.973090846091481033e-03f, + -3.983168098188016350e-03f, + -3.989225475204148490e-03f, + -3.991260874462022705e-03f, + -3.989276244926093229e-03f, + -3.983277581214771101e-03f, + -3.973274913544052635e-03f, + -3.959282293617159831e-03f, + -3.941317776478633732e-03f, + -3.919403398354929027e-03f, + -3.893565150507910695e-03f, + -3.863832949131735649e-03f, + -3.830240601327010268e-03f, + -3.792825767190527789e-03f, + -3.751629918062933215e-03f, + -3.706698290979619330e-03f, + -3.658079839375025927e-03f, + -3.605827180092993720e-03f, + -3.549996536761576733e-03f, + -3.490647679591208329e-03f, + -3.427843861661717968e-03f, + -3.361651751765054510e-03f, + -3.292141363875685824e-03f, + -3.219385983321982822e-03f, + -3.143462089736679104e-03f, + -3.064449276867864478e-03f, + -2.982430169333176711e-03f, + -2.897490336404670141e-03f, + -2.809718202914781877e-03f, + -2.719204957374711431e-03f, + -2.626044457401114501e-03f, + -2.530333132549811032e-03f, + -2.432169884655409342e-03f, + -2.331655985780983504e-03f, + -2.228894973881932924e-03f, + -2.123992546292832913e-03f, + -2.017056451144963979e-03f, + -1.908196376826750383e-03f, + -1.797523839601251243e-03f, + -1.685152069494012198e-03f, + -1.571195894568736418e-03f, + -1.455771623709985587e-03f, + -1.338996928029888246e-03f, + -1.220990721022854842e-03f, + -1.101873037586910105e-03f, + -9.817649120378681756e-04f, + -8.607882552375694957e-04f, + -7.390657309618451016e-04f, + -6.167206316344765651e-04f, + -4.938767535509734369e-04f, + -3.706582717193119820e-04f, + -2.471896144450557429e-04f, + -1.235953377853419590e-04f, + -4.959890100963354769e-17f, + 1.234719638747178042e-04f, + 2.466963672028767455e-04f, + 3.695493965506171539e-04f, + 4.919077359883478268e-04f, + 6.136486907666624611e-04f, + 7.346503102443730469e-04f, + 8.547915099445561275e-04f, + 9.739521926155840070e-04f, + 1.092013368178181270e-03f, + 1.208857272437798046e-03f, + 1.324367484443061309e-03f, + 1.438429042375458577e-03f, + 1.550928557853214172e-03f, + 1.661754328537675893e-03f, + 1.770796448927353453e-03f, + 1.877946919233882935e-03f, + 1.983099752229050636e-03f, + 2.086151077959072422e-03f, + 2.186999246221175627e-03f, + 2.285544926703325219e-03f, + 2.381691206687666998e-03f, + 2.475343686220975951e-03f, + 2.566410570660377331e-03f, + 2.654802760502929671e-03f, + 2.740433938410591005e-03f, + 2.823220653347242176e-03f, + 2.903082401745072944e-03f, + 2.979941705620986828e-03f, + 3.053724187568778933e-03f, + 3.124358642553495477e-03f, + 3.191777106439702780e-03f, + 3.255914921186875855e-03f, + 3.316710796650810048e-03f, + 3.374106868931566437e-03f, + 3.428048755212193075e-03f, + 3.478485605037506118e-03f, + 3.525370147984284441e-03f, + 3.568658737678078504e-03f, + 3.608311392117048363e-03f, + 3.644291830264629207e-03f, + 3.676567504879116322e-03f, + 3.705109631549780621e-03f, + 3.729893213914910709e-03f, + 3.750897065039809040e-03f, + 3.768103824936940016e-03f, + 3.781499974214817122e-03f, + 3.791075843845443261e-03f, + 3.796825621044523733e-03f, + 3.798747351262270894e-03f, + 3.796842936286857866e-03f, + 3.791118128466208596e-03f, + 3.781582521057830042e-03f, + 3.768249534720393822e-03f, + 3.751136400164309208e-03f, + 3.730264136982911188e-03f, + 3.705657528689156911e-03f, + 3.677345093986788237e-03f, + 3.645359054308939216e-03f, + 3.609735297660244310e-03f, + 3.570513338802807409e-03f, + 3.527736275829614857e-03f, + 3.481450743173182798e-03f, + 3.431706861099539409e-03f, + 3.378558181742752180e-03f, + 3.322061631737472948e-03f, + 3.262277451510458420e-03f, + 3.199269131295992552e-03f, + 3.133103343942434792e-03f, + 3.063849874581625003e-03f, + 2.991581547234064588e-03f, + 2.916374148427355699e-03f, + 2.838306347908428270e-03f, + 2.757459616531154987e-03f, + 2.673918141405439829e-03f, + 2.587768738396670036e-03f, + 2.499100762065002874e-03f, + 2.408006013138405590e-03f, + 2.314578643615823259e-03f, + 2.218915059596944257e-03f, + 2.121113821939972673e-03f, + 2.021275544848643961e-03f, + 1.919502792494091415e-03f, + 1.815899973775997930e-03f, + 1.710573235331645401e-03f, + 1.603630352903246976e-03f, + 1.495180621172954036e-03f, + 1.385334742178912079e-03f, + 1.274204712426525677e-03f, + 1.161903708809525985e-03f, + 1.048545973456544876e-03f, + 9.342466976205273310e-04f, + 8.191219047299183901e-04f, + 7.032883327184241478e-04f, + 5.868633157534830069e-04f, + 4.699646654841184022e-04f, + 3.527105519262883665e-04f, + 2.352193841077147634e-04f, + 1.176096905914035935e-04f, + -1.816279043680416122e-17f, + -1.174912783408097822e-04f, + -2.347459735184355596e-04f, + -3.516462717890174536e-04f, + -4.680748348545702049e-04f, + -5.839149175424285850e-04f, + -6.990504847680064412e-04f, + -8.133663276656900908e-04f, + -9.267481787707859599e-04f, + -1.039082826136560620e-03f, + -1.150258226274457644e-03f, + -1.260163615803283491e-03f, + -1.368689621697415744e-03f, + -1.475728370022750873e-03f, + -1.581173593054908304e-03f, + -1.684920734670451118e-03f, + -1.786867053909769803e-03f, + -1.886911726606242201e-03f, + -1.984955944983589262e-03f, + -2.080903015122214511e-03f, + -2.174658452197730102e-03f, + -2.266130073399595744e-03f, + -2.355228088437774474e-03f, + -2.441865187547984182e-03f, + -2.525956626910913581e-03f, + -2.607420311401180066e-03f, + -2.686176874584755934e-03f, + -2.762149755888469640e-03f, + -2.835265274865544395e-03f, + -2.905452702486107687e-03f, + -2.972644329382842596e-03f, + -3.036775530987343309e-03f, + -3.097784829494083791e-03f, + -3.155613952592319516e-03f, + -3.210207888911055518e-03f, + -3.261514940123977010e-03f, + -3.309486769664863031e-03f, + -3.354078448008697769e-03f, + -3.395248494475826620e-03f, + -3.432958915520433179e-03f, + -3.467175239468593684e-03f, + -3.497866547674528641e-03f, + -3.525005502067194568e-03f, + -3.548568369062852102e-03f, + -3.568535039823742677e-03f, + -3.584889046845683540e-03f, + -3.597617576861870340e-03f, + -3.606711480053335483e-03f, + -3.612165275560820770e-03f, + -3.613977153296067668e-03f, + -3.612148972054406073e-03f, + -3.606686253934421674e-03f, + -3.597598175073908090e-03f, + -3.584897552715274240e-03f, + -3.568600828617066786e-03f, + -3.548728048831968108e-03f, + -3.525302839875490025e-03f, + -3.498352381312806562e-03f, + -3.467907374795066184e-03f, + -3.434002009579821546e-03f, + -3.396673924574244215e-03f, + -3.355964166942346574e-03f, + -3.311917147321609932e-03f, + -3.264580591697720767e-03f, + -3.214005489989122874e-03f, + -3.160246041396339717e-03f, + -3.103359596574423087e-03f, + -3.043406596690569153e-03f, + -2.980450509430481246e-03f, + -2.914557762021494861e-03f, + -2.845797671343432839e-03f, + -2.774242371199536934e-03f, + -2.699966736824086378e-03f, + -2.623048306706114718e-03f, + -2.543567201809550458e-03f, + -2.461606042274317591e-03f, + -2.377249861685521663e-03f, + -2.290586018998170062e-03f, + -2.201704108209648781e-03f, + -2.110695865872344850e-03f, + -2.017655076542951807e-03f, + -1.922677476264368895e-03f, + -1.825860654179994359e-03f, + -1.727303952382239390e-03f, + -1.627108364096408778e-03f, + -1.525376430304876962e-03f, + -1.422212134918130876e-03f, + -1.317720798598115244e-03f, + -1.212008971343053179e-03f, + -1.105184323943502606e-03f, + -9.973555384197396820e-04f, + -8.886321975514789584e-04f, + -7.791246736123785127e-04f, + -6.689440164231939519e-04f, + -5.582018408351800513e-04f, + -4.470102137591960715e-04f, + -3.354815408535864675e-04f, + -2.237284529868988829e-04f, + -1.118636925881364959e-04f, + -2.071470033548900873e-17f, + 1.117500000493719706e-04f, + 2.232739117441697070e-04f, + 3.344596811326808606e-04f, + 4.451957086194700233e-04f, + 5.553709608865004844e-04f, + 6.648750821302810846e-04f, + 7.735985045027994698e-04f, + 8.814325576476730742e-04f, + 9.882695772205538597e-04f, + 1.094003012286658312e-03f, + 1.198527531486872971e-03f, + 1.301739127868568706e-03f, + 1.403535222275920568e-03f, + 1.503814765196793968e-03f, + 1.602478336965568358e-03f, + 1.699428246223005749e-03f, + 1.794568626535442959e-03f, + 1.887805531077050170e-03f, + 1.979047025283286153e-03f, + 2.068203277383505716e-03f, + 2.155186646722974993e-03f, + 2.239911769789048289e-03f, + 2.322295643856428504e-03f, + 2.402257708169032186e-03f, + 2.479719922580636039e-03f, + 2.554606843576476966e-03f, + 2.626845697602746450e-03f, + 2.696366451631782273e-03f, + 2.763101880895990278e-03f, + 2.826987633724481735e-03f, + 2.887962293419586799e-03f, + 2.945967437115027624e-03f, + 3.000947691558917239e-03f, + 3.052850785768143008e-03f, + 3.101627600505197247e-03f, + 3.147232214530348154e-03f, + 3.189621947585512783e-03f, + 3.228757400070635542e-03f, + 3.264602489375515067e-03f, + 3.297124482833800954e-03f, + 3.326294027269616291e-03f, + 3.352085175110586929e-03f, + 3.374475407044334914e-03f, + 3.393445651198981165e-03f, + 3.408980298832289899e-03f, + 3.421067216516832533e-03f, + 3.429697754812713849e-03f, + 3.434866753422505335e-03f, + 3.436572542826982733e-03f, + 3.434816942403465419e-03f, + 3.429605255032289191e-03f, + 3.420946258200424379e-03f, + 3.408852191614762661e-03f, + 3.393338741341164321e-03f, + 3.374425020488695717e-03f, + 3.352133546462037709e-03f, + 3.326490214808674843e-03f, + 3.297524269690380733e-03f, + 3.265268271012441645e-03f, + 3.229758058246862303e-03f, + 3.191032710989869740e-03f, + 3.149134506296154788e-03f, + 3.104108872836428955e-03f, + 3.056004341927853838e-03f, + 3.004872495489709604e-03f, + 2.950767910979716115e-03f, + 2.893748103369611722e-03f, + 2.833873464221980509e-03f, + 2.771207197931811923e-03f, + 2.705815255200260967e-03f, + 2.637766263811031336e-03f, + 2.567131456780837821e-03f, + 2.493984597959450489e-03f, + 2.418401905157508119e-03f, + 2.340461970880830869e-03f, + 2.260245680754550089e-03f, + 2.177836129720765214e-03f, + 2.093318536097558535e-03f, + 2.006780153586758438e-03f, + 1.918310181321814228e-03f, + 1.827999672049080305e-03f, + 1.735941438535555485e-03f, + 1.642229958299797413e-03f, + 1.546961276764474095e-03f, + 1.450232908928234844e-03f, + 1.352143739658173346e-03f, + 1.252793922705604940e-03f, + 1.152284778546037915e-03f, + 1.050718691150112800e-03f, + 9.481990037877905493e-04f, + 8.448299139744811252e-04f, + 7.407163676634808108e-04f, + 6.359639527935932865e-04f, + 5.306787922987215612e-04f, + 4.249674366892458523e-04f, + 3.189367563120465432e-04f, + 2.126938333988559937e-04f, + 1.063458540127559251e-04f, + 5.580875061456703527e-17f, + -1.062366589427715403e-04f, + -2.122572729776368709e-04f, + -3.179553195023174803e-04f, + -4.232247100881988380e-04f, + -5.279598968701624921e-04f, + -6.320559782838149239e-04f, + -7.354088040427114292e-04f, + -8.379150792517338136e-04f, + -9.394724675511614224e-04f, + -1.039979693190289578e-03f, + -1.139336641928036893e-03f, + -1.237444460659308312e-03f, + -1.334205655669700157e-03f, + -1.429524189420069849e-03f, + -1.523305575764726449e-03f, + -1.615456973509339471e-03f, + -1.705887278216311432e-03f, + -1.794507212166710506e-03f, + -1.881229412389207739e-03f, + -1.965968516670845660e-03f, + -2.048641247464260805e-03f, + -2.129166493608392920e-03f, + -2.207465389784019332e-03f, + -2.283461393625221911e-03f, + -2.357080360412389282e-03f, + -2.428250615272878506e-03f, + -2.496903022820505004e-03f, + -2.562971054165676978e-03f, + -2.626390851230865024e-03f, + -2.687101288310514684e-03f, + -2.745044030815619023e-03f, + -2.800163591146226632e-03f, + -2.852407381639556275e-03f, + -2.901725764542836122e-03f, + -2.948072098963223399e-03f, + -2.991402784751522655e-03f, + -3.031677303277889764e-03f, + -3.068858255062130534e-03f, + -3.102911394223503531e-03f, + -3.133805659718869735e-03f, + -3.161513203341241758e-03f, + -3.186009414453190580e-03f, + -3.207272941434331694e-03f, + -3.225285709824096680e-03f, + -3.240032937145292686e-03f, + -3.251503144396590377e-03f, + -3.259688164206146245e-03f, + -3.264583145641404907e-03f, + -3.266186555673606436e-03f, + -3.264500177299172780e-03f, + -3.259529104323136660e-03f, + -3.251281732813429259e-03f, + -3.239769749237986739e-03f, + -3.225008115300036140e-03f, + -3.207015049490388476e-03f, + -3.185812005378460370e-03f, + -3.161423646667512952e-03f, + -3.133877819042292512e-03f, + -3.103205518841122348e-03f, + -3.069440858586877912e-03f, + -3.032621029414926759e-03f, + -2.992786260439455476e-03f, + -2.949979775101472298e-03f, + -2.904247744546611422e-03f, + -2.855639238081619617e-03f, + -2.804206170763586491e-03f, + -2.750003248176786843e-03f, + -2.693087908455917955e-03f, + -2.633520261617509212e-03f, + -2.571363026262532200e-03f, + -2.506681463717044551e-03f, + -2.439543309680427315e-03f, + -2.370018703451564571e-03f, + -2.298180114807654630e-03f, + -2.224102268611033526e-03f, + -2.147862067223294154e-03f, + -2.069538510805925442e-03f, + -1.989212615590480685e-03f, + -1.906967330203344566e-03f, + -1.822887450130181207e-03f, + -1.737059530408692877e-03f, + -1.649571796640214366e-03f, + -1.560514054410137245e-03f, + -1.469977597210741713e-03f, + -1.378055112961429048e-03f, + -1.284840589220490525e-03f, + -1.190429217186597059e-03f, + -1.094917294586464536e-03f, + -9.984021275502952091e-04f, + -9.009819315715012801e-04f, + -8.027557316540494606e-04f, + -7.038232617473004970e-04f, + -6.042848635718732616e-04f, + -5.042413849374186366e-04f, + -4.037940776560225800e-04f, + -3.030444951553179869e-04f, + -2.020943898931319878e-04f, + -1.010456106780149166e-04f, + 0.000000000000000000e+00f, + 1.009407085266458343e-04f, + 2.016749925046095251e-04f, + 3.021016427750876994e-04f, + 4.021198650188813296e-04f, + 5.016293808337416761e-04f, + 6.005305281877486908e-04f, + 6.987243611467527623e-04f, + 7.961127487778794053e-04f, + 8.925984731294979088e-04f, + 9.880853261891541211e-04f, + 1.082478205724416668e-03f, + 1.175683209910467547e-03f, + 1.267607730649530483e-03f, + 1.358160545491613001e-03f, + 1.447251908062810368e-03f, + 1.534793636914284342e-03f, + 1.620699202701525671e-03f, + 1.704883813609845864e-03f, + 1.787264498941020874e-03f, + 1.867760190778164960e-03f, + 1.946291803649916963e-03f, + 2.022782312115050697e-03f, + 2.097156826190940942e-03f, + 2.169342664553446445e-03f, + 2.239269425435684406e-03f, + 2.306869055157482768e-03f, + 2.372075914217887990e-03f, + 2.434826840887931946e-03f, + 2.495061212241576985e-03f, + 2.552721002565519828e-03f, + 2.607750839092839740e-03f, + 2.660098055006482071e-03f, + 2.709712739661699482e-03f, + 2.756547785980618080e-03f, + 2.800558934973623130e-03f, + 2.841704817345394509e-03f, + 2.879946992147500422e-03f, + 2.915249982440988770e-03f, + 2.947581307936787947e-03f, + 2.976911514583215936e-03f, + 3.003214201075224757e-03f, + 3.026466042260897698e-03f, + 3.046646809425394842e-03f, + 3.063739387434716906e-03f, + 3.077729788725812928e-03f, + 3.088607164131847160e-03f, + 3.096363810535085210e-03f, + 3.100995175343106407e-03f, + 3.102499857787075627e-03f, + 3.100879607044029155e-03f, + 3.096139317188489372e-03f, + 3.088287018981677665e-03f, + 3.077333868510032794e-03f, + 3.063294132687598148e-03f, + 3.046185171640329852e-03f, + 3.026027417993118994e-03f, + 3.002844353083898229e-03f, + 2.976662480131725321e-03f, + 2.947511294389127540e-03f, + 2.915423250312139825e-03f, + 2.880433725783904282e-03f, + 2.842580983431017543e-03f, + 2.801906129074849608e-03f, + 2.758453067362536676e-03f, + 2.712268454625132129e-03f, + 2.663401649013388741e-03f, + 2.611904657964648872e-03f, + 2.557832083055883686e-03f, + 2.501241062301404455e-03f, + 2.442191209956580140e-03f, + 2.380744553889864272e-03f, + 2.316965470589540683e-03f, + 2.250920617872494492e-03f, + 2.182678865366110329e-03f, + 2.112311222834577142e-03f, + 2.039890766424525773e-03f, + 1.965492562907071757e-03f, + 1.889193591993552652e-03f, + 1.811072666805763759e-03f, + 1.731210352583364151e-03f, + 1.649688883710983146e-03f, + 1.566592079150942642e-03f, + 1.482005256369143794e-03f, + 1.396015143841002710e-03f, + 1.308709792228250146e-03f, + 1.220178484316535592e-03f, + 1.130511643807194247e-03f, + 1.039800743054220596e-03f, + 9.481382098429354081e-04f, + 8.556173333027085976e-04f, + 7.623321690519257836e-04f, + 6.683774436695268453e-04f, + 5.738484585908526416e-04f, + 4.788409935259931006e-04f, + 3.834512094969080875e-04f, + 2.877755515921519335e-04f, + 1.919106515382334099e-04f, + 9.595323018441329955e-05f, + 2.961728936009675573e-17f, + -9.585243231666533902e-05f, + -1.915076630217971002e-04f, + -2.868695882022341873e-04f, + -3.818425002487855048e-04f, + -4.763311838281015891e-04f, + -5.702410112587507447e-04f, + -6.634780371952385403e-04f, + -7.559490925245654922e-04f, + -8.475618773830773632e-04f, + -9.382250532000561190e-04f, + -1.027848333675624067e-03f, + -1.116342574603918251e-03f, + -1.203619862451413194e-03f, + -1.289593601602254461e-03f, + -1.374178600184431222e-03f, + -1.457291154392131340e-03f, + -1.538849131220664879e-03f, + -1.618772049531532397e-03f, + -1.696981159369096662e-03f, + -1.773399519450076641e-03f, + -1.847952072749128047e-03f, + -1.920565720107696725e-03f, + -1.991169391792951045e-03f, + -2.059694116937665648e-03f, + -2.126073090792308593e-03f, + -2.190241739725098773e-03f, + -2.252137783906322643e-03f, + -2.311701297615694015e-03f, + -2.368874767115570264e-03f, + -2.423603146033716672e-03f, + -2.475833908202009024e-03f, + -2.525517097901476115e-03f, + -2.572605377465290739e-03f, + -2.617054072194128448e-03f, + -2.658821212542411203e-03f, + -2.697867573534989526e-03f, + -2.734156711378059063e-03f, + -2.767654997229715939e-03f, + -2.798331648100023990e-03f, + -2.826158754851514684e-03f, + -2.851111307275881745e-03f, + -2.873167216223860253e-03f, + -2.892307332769767248e-03f, + -2.908515464394205354e-03f, + -2.921778388171670463e-03f, + -2.932085860953250172e-03f, + -2.939430626537135362e-03f, + -2.943808419822793298e-03f, + -2.945217967948095782e-03f, + -2.943660988411156160e-03f, + -2.939142184182125771e-03f, + -2.931669235812944720e-03f, + -2.921252790556253757e-03f, + -2.907906448507473381e-03f, + -2.891646745787318507e-03f, + -2.872493134784635110e-03f, + -2.850467961482642393e-03f, + -2.825596439894601745e-03f, + -2.797906623637558873e-03f, + -2.767429374675792631e-03f, + -2.734198329268788634e-03f, + -2.698249861160541534e-03f, + -2.659623042050382122e-03f, + -2.618359599388156086e-03f, + -2.574503871538993439e-03f, + -2.528102760365492311e-03f, + -2.479205681277817097e-03f, + -2.427864510805224571e-03f, + -2.374133531743618380e-03f, + -2.318069375937653262e-03f, + -2.259730964756961977e-03f, + -2.199179447329721766e-03f, + -2.136478136597202548e-03f, + -2.071692443256429803e-03f, + -2.004889807660292528e-03f, + -1.936139629744864461e-03f, + -1.865513197057068430e-03f, + -1.793083610957797377e-03f, + -1.718925711075672356e-03f, + -1.643115998089900122e-03f, + -1.565732554922428339e-03f, + -1.486854966419205639e-03f, + -1.406564237604140694e-03f, + -1.324942710588779184e-03f, + -1.242073980223971008e-03f, + -1.158042808578502083e-03f, + -1.072935038332762650e-03f, + -9.868375051760999109e-04f, + -8.998379492968846009e-04f, + -8.120249260550908496e-04f, + -7.234877159284703501e-04f, + -6.343162338246114773e-04f, + -5.446009378495034713e-04f, + -4.544327376257831556e-04f, + -3.639029022542405989e-04f, + -2.731029680102384292e-04f, + -1.821246458690281339e-04f, + -9.105972895404852794e-05f, + 2.249668800172135414e-17f, + 9.096286107495146821e-05f, + 1.817373693953476012e-04f, + 2.722323270597744641e-04f, + 3.623569146858229449e-04f, + 4.520207824765948832e-04f, + 5.411341407166387263e-04f, + 6.296078496082298161e-04f, + 7.173535083574583433e-04f, + 8.042835434204669747e-04f, + 8.903112958232611450e-04f, + 9.753511074673767468e-04f, + 1.059318406334858621e-03f, + 1.142129790509272816e-03f, + 1.223703110928572564e-03f, + 1.303957552787602704e-03f, + 1.382813715509965393e-03f, + 1.460193691210495345e-03f, + 1.536021141570667560e-03f, + 1.610221373050487641e-03f, + 1.682721410364203859e-03f, + 1.753450068146556471e-03f, + 1.822338020740059362e-03f, + 1.889317870034044975e-03f, + 1.954324211290346669e-03f, + 2.017293696890905524e-03f, + 2.078165097944757213e-03f, + 2.136879363695696771e-03f, + 2.193379678672519300e-03f, + 2.247611517526280130e-03f, + 2.299522697502668928e-03f, + 2.349063428498731048e-03f, + 2.396186360655621055e-03f, + 2.440846629443004217e-03f, + 2.483001898191590853e-03f, + 2.522612398034211525e-03f, + 2.559640965217286051e-03f, + 2.594053075748732184e-03f, + 2.625816877349718484e-03f, + 2.654903218680989673e-03f, + 2.681285675817290850e-03f, + 2.704940575946320012e-03f, + 2.725847018271134597e-03f, + 2.743986892097757119e-03f, + 2.759344892093201339e-03f, + 2.771908530701254220e-03f, + 2.781668147706524256e-03f, + 2.788616916940388039e-03f, + 2.792750850124972248e-03f, + 2.794068797854331460e-03f, + 2.792572447715026187e-03f, + 2.788266319550961395e-03f, + 2.781157757880268772e-03f, + 2.771256921475067844e-03f, + 2.758576770117475430e-03f, + 2.743133048548284346e-03f, + 2.724944267627588904e-03f, + 2.704031682729127542e-03f, + 2.680419269393146536e-03f, + 2.654133696265428919e-03f, + 2.625204295352332774e-03f, + 2.593663029624824687e-03f, + 2.559544458007235838e-03f, + 2.522885697788194298e-03f, + 2.483726384495194266e-03f, + 2.442108629274998555e-03f, + 2.398076973826478078e-03f, + 2.351678342933034675e-03f, + 2.302961994645597293e-03f, + 2.251979468168421438e-03f, + 2.198784529503366135e-03f, + 2.143433114908959661e-03f, + 2.085983272233949380e-03f, + 2.026495100187157301e-03f, + 1.965030685606198105e-03f, + 1.901654038790769660e-03f, + 1.836431026968307709e-03f, + 1.769429305960019570e-03f, + 1.700718250118553624e-03f, + 1.630368880610306992e-03f, + 1.558453792115264015e-03f, + 1.485047078020867951e-03f, + 1.410224254186159386e-03f, + 1.334062181355563604e-03f, + 1.256638986300702377e-03f, + 1.178033981771610395e-03f, + 1.098327585339919834e-03f, + 1.017601237215742414e-03f, + 9.359373171227939204e-04f, + 8.534190603167809844e-04f, + 7.701304728322726310e-04f, + 6.861562460439943638e-04f, + 6.015816706295592760e-04f, + 5.164925500217144947e-04f, + 4.309751134365081789e-04f, + 3.451159285661133287e-04f, + 2.590018140253294793e-04f, + 1.727197516388577871e-04f, + 8.635679865857319275e-05f, + 0.000000000000000000e+00f, + -8.626369941502123060e-05f, + -1.723475421202091517e-04f, + -2.581650452840001070e-04f, + -3.436300875175078229e-04f, + -4.286569952255241947e-04f, + -5.131606284134141056e-04f, + -5.970564658633488713e-04f, + -6.802606895961190543e-04f, + -7.626902685335093437e-04f, + -8.442630412771376771e-04f, + -9.248977979226647274e-04f, + -1.004514360827295531e-03f, + -1.083033664249621842e-03f, + -1.160377832784585195e-03f, + -1.236470258513610188e-03f, + -1.311235676795854824e-03f, + -1.384600240623468786e-03f, + -1.456491593469362491e-03f, + -1.526838940554491693e-03f, + -1.595573118465392530e-03f, + -1.662626663052574382e-03f, + -1.727933875544430866e-03f, + -1.791430886811391570e-03f, + -1.853055719717038941e-03f, + -1.912748349496546643e-03f, + -1.970450762103117051e-03f, + -2.026107010465434590e-03f, + -2.079663268602624775e-03f, + -2.131067883544039959e-03f, + -2.180271425003505598e-03f, + -2.227226732761336536e-03f, + -2.271888961708070937e-03f, + -2.314215624507658369e-03f, + -2.354166631838931854e-03f, + -2.391704330178351379e-03f, + -2.426793537088085383e-03f, + -2.459401573976375491e-03f, + -2.489498296300512482e-03f, + -2.517056121184375189e-03f, + -2.542050052425464269e-03f, + -2.564457702869174776e-03f, + -2.584259314130737690e-03f, + -2.601437773647668344e-03f, + -2.615978629048301869e-03f, + -2.627870099825200655e-03f, + -2.637103086304351703e-03f, + -2.643671175903983435e-03f, + -2.647570646679886421e-03f, + -2.648800468156370640e-03f, + -2.647362299445082338e-03f, + -2.643260484656465709e-03f, + -2.636502045611470533e-03f, + -2.627096671863705067e-03f, + -2.615056708045107071e-03f, + -2.600397138550693873e-03f, + -2.583135569580662441e-03f, + -2.563292208561044304e-03f, + -2.540889840966167532e-03f, + -2.515953804569251093e-03f, + -2.488511961150026430e-03f, + -2.458594665690286458e-03f, + -2.426234733091499601e-03f, + -2.391467402450208560e-03f, + -2.354330298930778966e-03f, + -2.314863393275341449e-03f, + -2.273108958995205108e-03f, + -2.229111527288808303e-03f, + -2.182917839734724828e-03f, + -2.134576798809103074e-03f, + -2.084139416280047803e-03f, + -2.031658759533671622e-03f, + -1.977189895887380630e-03f, + -1.920789834949013144e-03f, + -1.862517469082529712e-03f, + -1.802433512041404706e-03f, + -1.740600435833956081e-03f, + -1.677082405886637763e-03f, + -1.611945214571482154e-03f, + -1.545256213167279678e-03f, + -1.477084242324090734e-03f, + -1.407499561103702770e-03f, + -1.336573774667974704e-03f, + -1.264379760689891439e-03f, + -1.190991594563459415e-03f, + -1.116484473487968287e-03f, + -1.040934639504879694e-03f, + -9.644193015667102938e-04f, + -8.870165567158059617e-04f, + -8.088053104554945427e-04f, + -7.298651963925226931e-04f, + -6.502764952346872223e-04f, + -5.701200532242376492e-04f, + -4.894772000905328905e-04f, + -4.084296666057908869e-04f, + -3.270595018261514210e-04f, + -2.454489901024086416e-04f, + -1.636805679449392656e-04f, + -8.183674082536929162e-05f, + -3.536521155687695770e-17f, + 8.174726056185484973e-05f, + 1.633228270606075061e-04f, + 2.446447484770642063e-04f, + 3.256314188286605011e-04f, + 4.062016589873803664e-04f, + 4.862747979789591962e-04f, + 5.657707536813316989e-04f, + 6.446101128410288350e-04f, + 7.227142103289445341e-04f, + 8.000052075557968854e-04f, + 8.764061699685609655e-04f, + 9.518411435521427575e-04f, + 1.026235230259113333e-03f, + 1.099514662293880982e-03f, + 1.171606875175669731e-03f, + 1.242440579510656730e-03f, + 1.311945831400333469e-03f, + 1.380054101417801157e-03f, + 1.446698342082995128e-03f, + 1.511813053771716119e-03f, + 1.575334348993146514e-03f, + 1.637200014972404940e-03f, + 1.697349574477985271e-03f, + 1.755724344834148822e-03f, + 1.812267495060354474e-03f, + 1.866924101083174007e-03f, + 1.919641198966668804e-03f, + 1.970367836109312640e-03f, + 2.019055120359112502e-03f, + 2.065656266998848294e-03f, + 2.110126643557067375e-03f, + 2.152423812401290867e-03f, + 2.192507571073842951e-03f, + 2.230339990331663483e-03f, + 2.265885449854030363e-03f, + 2.299110671585404518e-03f, + 2.329984750682085725e-03f, + 2.358479184033766222e-03f, + 2.384567896334751410e-03f, + 2.408227263680306953e-03f, + 2.429436134667956135e-03f, + 2.448175848984440826e-03f, + 2.464430253462965986e-03f, + 2.478185715597053772e-03f, + 2.489431134500083229e-03f, + 2.498157949302601787e-03f, + 2.504360144981462345e-03f, + 2.508034255617927130e-03f, + 2.509179365084111650e-03f, + 2.507797105160097621e-03f, + 2.503891651086268943e-03f, + 2.497469714558282327e-03f, + 2.488540534174480279e-03f, + 2.477115863348166615e-03f, + 2.463209955699809280e-03f, + 2.446839547946541571e-03f, + 2.428023840308957040e-03f, + 2.406784474457862241e-03f, + 2.383145509025629081e-03f, + 2.357133392709743602e-03f, + 2.328776934998069703e-03f, + 2.298107274548326535e-03f, + 2.265157845255661752e-03f, + 2.229964340045592890e-03f, + 2.192564672431069738e-03f, + 2.152998935874733330e-03f, + 2.111309361000000308e-03f, + 2.067540270696137036e-03f, + 2.021738033165410009e-03f, + 1.973951012961244755e-03f, + 1.924229520069206407e-03f, + 1.872625757084688284e-03f, + 1.819193764541884861e-03f, + 1.763989364451496421e-03f, + 1.707070102106509864e-03f, + 1.648495186215746637e-03f, + 1.588325427427724965e-03f, + 1.526623175309085347e-03f, + 1.463452253841756060e-03f, + 1.398877895506348739e-03f, + 1.332966674019096277e-03f, + 1.265786435792502209e-03f, + 1.197406230189095033e-03f, + 1.127896238640391496e-03f, + 1.057327702704293892e-03f, + 9.857728511335100193e-04f, + 9.133048260301522472e-04f, + 8.399976081621432457e-04f, + 7.659259415173617783e-04f, + 6.911652571721143201e-04f, + 6.157915965516007339e-04f, + 5.398815341610210556e-04f, + 4.635120998645952885e-04f, + 3.867607007918734689e-04f, + 3.097050429510698013e-04f, + 2.324230526274301040e-04f, + 1.549927976471593514e-04f, + 7.749240858556649941e-05f, + 0.000000000000000000e+00f, + -7.740640823447261787e-05f, + -1.546489694066701043e-04f, + -2.316500881890203695e-04f, + -3.083324985177912040e-04f, + -3.846193410553054690e-04f, + -4.604342401558712806e-04f, + -5.357013802596746321e-04f, + -6.103455816376617384e-04f, + -6.842923754112235652e-04f, + -7.574680777731551771e-04f, + -8.297998633349399841e-04f, + -9.012158375281924391e-04f, + -9.716451079872685803e-04f, + -1.041017854843918943e-03f, + -1.109265399862380090e-03f, + -1.176320274348555822e-03f, + -1.242116285764278049e-03f, + -1.306588582982534663e-03f, + -1.369673720118763302e-03f, + -1.431309718874978175e-03f, + -1.491436129336623072e-03f, + -1.549994089162047605e-03f, + -1.606926381106289162e-03f, + -1.662177488824047355e-03f, + -1.715693650896988340e-03f, + -1.767422913032514007e-03f, + -1.817315178384378135e-03f, + -1.865322255945725321e-03f, + -1.911397906968511154e-03f, + -1.955497889364013041e-03f, + -1.997580000042790960e-03f, + -2.037604115153256439e-03f, + -2.075532228180405718e-03f, + -2.111328485869323496e-03f, + -2.144959221939368461e-03f, + -2.176392988557203194e-03f, + -2.205600585540104519e-03f, + -2.232555087262260568e-03f, + -2.257231867239448825e-03f, + -2.279608620370068926e-03f, + -2.299665382812773017e-03f, + -2.317384549483214162e-03f, + -2.332750889154779940e-03f, + -2.345751557151114393e-03f, + -2.356376105620029086e-03f, + -2.364616491381328796e-03f, + -2.370467081343168980e-03f, + -2.373924655484415357e-03f, + -2.374988407402560224e-03f, + -2.373659942429402498e-03f, + -2.369943273319134042e-03f, + -2.363844813515844894e-03f, + -2.355373368009962363e-03f, + -2.344540121795531118e-03f, + -2.331358625942570734e-03f, + -2.315844781301389533e-03f, + -2.298016819857692100e-03f, + -2.277895283760072130e-03f, + -2.255503002043453362e-03f, + -2.230865065074793489e-03f, + -2.204008796749007838e-03f, + -2.174963724465795757e-03f, + -2.143761546920198904e-03f, + -2.110436099741713409e-03f, + -2.075023319018890790e-03f, + -2.037561202748555591e-03f, + -1.998089770251173809e-03f, + -1.956651019594995121e-03f, + -1.913288883074297899e-03f, + -1.868049180789196646e-03f, + -1.820979572375211594e-03f, + -1.772129506933642308e-03f, + -1.721550171215585189e-03f, + -1.669294436113043234e-03f, + -1.615416801513268677e-03f, + -1.559973339574200445e-03f, + -1.503021636479029855e-03f, + -1.444620732731039447e-03f, + -1.384831062049924873e-03f, + -1.323714388933566266e-03f, + -1.261333744948669490e-03f, + -1.197753363816366759e-03f, + -1.133038615360030133e-03f, + -1.067255938382161932e-03f, + -1.000472772539666867e-03f, + -9.327574892878164135e-04f, + -8.641793219624978621e-04f, + -7.948082950727303935e-04f, + -7.247151528757850725e-04f, + -6.539712873074342899e-04f, + -5.826486653404457788e-04f, + -5.108197558453200652e-04f, + -4.385574560281823480e-04f, + -3.659350175192179139e-04f, + -2.930259721875476224e-04f, + -2.199040577568145362e-04f, + -1.466431432977234172e-04f, + -7.331715467149604577e-05f, + -1.583863635030426600e-17f, + 7.323450476167464791e-05f, + 1.463127100768226222e-04f, + 2.191612068271459025e-04f, + 2.917068999868720029e-04f, + 3.638770818979462419e-04f, + 4.355995050724186771e-04f, + 5.068024544486168575e-04f, + 5.774148190301226381e-04f, + 6.473661628350382271e-04f, + 7.165867950855838104e-04f, + 7.850078395671508278e-04f, + 8.525613030890458839e-04f, + 9.191801429783286312e-04f, + 9.847983335396156852e-04f, + 1.049350931415344917e-03f, + 1.112774139782065014e-03f, + 1.175005371319211289e-03f, + 1.235983309887723515e-03f, + 1.295647970858855580e-03f, + 1.353940760033357088e-03f, + 1.410804531092786597e-03f, + 1.466183641527706851e-03f, + 1.520024006987588487e-03f, + 1.572273153999021768e-03f, + 1.622880271001914641e-03f, + 1.671796257653338251e-03f, + 1.718973772351856891e-03f, + 1.764367277935770294e-03f, + 1.807933085512109829e-03f, + 1.849629396373906774e-03f, + 1.889416341965344378e-03f, + 1.927256021857423661e-03f, + 1.963112539697747360e-03f, + 1.996952037100198536e-03f, + 2.028742725443338692e-03f, + 2.058454915547453749e-03f, + 2.086061045202587064e-03f, + 2.111535704522703519e-03f, + 2.134855659102633321e-03f, + 2.155999870956868715e-03f, + 2.174949517221738769e-03f, + 2.191688006604752131e-03f, + 2.206200993566935931e-03f, + 2.218476390226378162e-03f, + 2.228504375973802080e-03f, + 2.236277404792865950e-03f, + 2.241790210280533453e-03f, + 2.245039808364943448e-03f, + 2.246025497720799922e-03f, + 2.244748857884364644e-03f, + 2.241213745072582770e-03f, + 2.235426285713204824e-03f, + 2.227394867694926922e-03f, + 2.217130129349095119e-03f, + 2.204644946176507331e-03f, + 2.189954415335300473e-03f, + 2.173075837908174709e-03f, + 2.154028698969212043e-03f, + 2.132834645472967295e-03f, + 2.109517461990493593e-03f, + 2.084103044319416103e-03f, + 2.056619370996764975e-03f, + 2.027096472745798170e-03f, + 1.995566399890039318e-03f, + 1.962063187769620600e-03f, + 1.926622820196907150e-03f, + 1.889283190990552431e-03f, + 1.850084063629192947e-03f, + 1.809067029067121680e-03f, + 1.766275461756697642e-03f, + 1.721754473924256863e-03f, + 1.675550868146924400e-03f, + 1.627713088280349860e-03f, + 1.578291168789072458e-03f, + 1.527336682531643414e-03f, + 1.474902687055531115e-03f, + 1.421043669457084475e-03f, + 1.365815489864515746e-03f, + 1.309275323601538730e-03f, + 1.251481602091836709e-03f, + 1.192493952565831317e-03f, + 1.132373136630967277e-03f, + 1.071180987769143524e-03f, + 1.008980347826006095e-03f, + 9.458350025562797456e-04f, + 8.818096162916508503e-04f, + 8.169696657985963404e-04f, + 7.513813733923349223e-04f, + 6.851116393769219248e-04f, + 6.182279738784716289e-04f, + 5.507984281427341774e-04f, + 4.828915253653128263e-04f, + 4.145761911257870138e-04f, + 3.459216834955630552e-04f, + 2.769975228912344525e-04f, + 2.078734217432808354e-04f, + 1.386192140517258974e-04f, + 6.930478490043769405e-05f, + 3.850678557016620008e-17f, + -6.922536466905694359e-05f, + -1.383016930421246003e-04f, + -2.071595989147220697e-04f, + -2.757299955756169576e-04f, + -3.439441650576845872e-04f, + -4.117338269385969812e-04f, + -4.790312066215028815e-04f, + -5.457691030282071530e-04f, + -6.118809556363564821e-04f, + -6.773009107949520824e-04f, + -7.419638872516609194e-04f, + -8.058056408262444324e-04f, + -8.687628281669950792e-04f, + -9.307730695263674928e-04f, + -9.917750104935054486e-04f, + -1.051708382622899233e-03f, + -1.110514062899539038e-03f, + -1.168134131981801499e-03f, + -1.224511931164258289e-03f, + -1.279592118005431134e-03f, + -1.333320720565457380e-03f, + -1.385645190200188951e-03f, + -1.436514452861111955e-03f, + -1.485878958850318310e-03f, + -1.533690730982715860e-03f, + -1.579903411108077825e-03f, + -1.624472304948763358e-03f, + -1.667354425209425627e-03f, + -1.708508532916933748e-03f, + -1.747895176951601770e-03f, + -1.785476731731549735e-03f, + -1.821217433014094391e-03f, + -1.855083411780856367e-03f, + -1.887042726174312105e-03f, + -1.917065391455589646e-03f, + -1.945123407956183582e-03f, + -1.971190786997232842e-03f, + -1.995243574752887921e-03f, + -2.017259874035835007e-03f, + -2.037219863985523585e-03f, + -2.055105817641905350e-03f, + -2.070902117388888439e-03f, + -2.084595268254915961e-03f, + -2.096173909059424311e-03f, + -2.105628821396557989e-03f, + -2.112952936449467856e-03f, + -2.118141339630974854e-03f, + -2.121191273048350125e-03f, + -2.122102135792170319e-03f, + -2.120875482051600824e-03f, + -2.117515017060413347e-03f, + -2.112026590880362788e-03f, + -2.104418190030685459e-03f, + -2.094699926974599290e-03f, + -2.082884027475962168e-03f, + -2.068984815841183947e-03f, + -2.053018698063778860e-03f, + -2.035004142890892240e-03f, + -2.014961660833440929e-03f, + -1.992913781143200273e-03f, + -1.968885026782406667e-03f, + -1.942901887413728572e-03f, + -1.914992790439534482e-03f, + -1.885188070122604002e-03f, + -1.853519934820884200e-03f, + -1.820022432372197857e-03f, + -1.784731413665374862e-03f, + -1.747684494436782749e-03f, + -1.708921015333154415e-03f, + -1.668482000282380450e-03f, + -1.626410113216474832e-03f, + -1.582749613192508320e-03f, + -1.537546307957996655e-03f, + -1.490847506009852948e-03f, + -1.442701967196525136e-03f, + -1.393159851915445457e-03f, + -1.342272668957837298e-03f, + -1.290093222055339146e-03f, + -1.236675555184266412e-03f, + -1.182074896683267323e-03f, + -1.126347602242422363e-03f, + -1.069551096823031762e-03f, + -1.011743815566986619e-03f, + -9.529851437568570560e-04f, + -8.933353558887932458e-04f, + -8.328555539196910067e-04f, + -7.716076047526932357e-04f, + -7.096540770239413622e-04f, + -6.470581772568036877e-04f, + -5.838836854464510024e-04f, + -5.201948901420694023e-04f, + -4.560565230917139183e-04f, + -3.915336935171430800e-04f, + -3.266918220842130881e-04f, + -2.615965746362512961e-04f, + -1.963137957579778382e-04f, + -1.309094422360982627e-04f, + -6.544951648425400908e-05f, + 0.000000000000000000e+00f, + 6.537321308021720669e-05f, + 1.306043822607082607e-04f, + 1.956279867336831537e-04f, + 2.603787911287086792e-04f, + 3.247919108976276181e-04f, + 3.888028772701663366e-04f, + 4.523477017143444248e-04f, + 5.153629398383980818e-04f, + 5.777857546699370411e-04f, + 6.395539792487880222e-04f, + 7.006061784722651719e-04f, + 7.608817101308743870e-04f, + 8.203207850733748325e-04f, + 8.788645264429036270e-04f, + 9.364550279239102458e-04f, + 9.930354109440190161e-04f, + 1.048549880772794290e-03f, + 1.102943781463442676e-03f, + 1.156163649582898304e-03f, + 1.208157266677142242e-03f, + 1.258873710421226384e-03f, + 1.308263404403530459e-03f, + 1.356278166495282879e-03f, + 1.402871255759119157e-03f, + 1.447997417850374223e-03f, + 1.491612928867637770e-03f, + 1.533675637609531331e-03f, + 1.574145006197766577e-03f, + 1.612982149027046997e-03f, + 1.650149870004234296e-03f, + 1.685612698041884239e-03f, + 1.719336920772085442e-03f, + 1.751290616448417667e-03f, + 1.781443684006613678e-03f, + 1.809767871255424246e-03f, + 1.836236801171283600e-03f, + 1.860825996272981193e-03f, + 1.883512901053582605e-03f, + 1.904276902449677372e-03f, + 1.923099348328973467e-03f, + 1.939963563980765578e-03f, + 1.954854866594344254e-03f, + 1.967760577713456756e-03f, + 1.978670033656380715e-03f, + 1.987574593893788513e-03f, + 1.994467647378149553e-03f, + 1.999344616820692734e-03f, + 2.002202960914168783e-03f, + 2.003042174501439791e-03f, + 2.001863786692200525e-03f, + 1.998671356932133197e-03f, + 1.993470469030853877e-03f, + 1.986268723157068750e-03f, + 1.977075725811386266e-03f, + 1.965903077789286045e-03f, + 1.952764360148673750e-03f, + 1.937675118198656623e-03f, + 1.920652843527819766e-03f, + 1.901716954092479844e-03f, + 1.880888772387422731e-03f, + 1.858191501723117835e-03f, + 1.833650200635677813e-03f, + 1.807291755457605693e-03f, + 1.779144851079101806e-03f, + 1.749239939931425750e-03f, + 1.717609209225661468e-03f, + 1.684286546482327823e-03f, + 1.649307503388023569e-03f, + 1.612709258017795862e-03f, + 1.574530575463451180e-03f, + 1.534811766908859409e-03f, + 1.493594647195765511e-03f, + 1.450922490924287005e-03f, + 1.406839987134609086e-03f, + 1.361393192616601943e-03f, + 1.314629483896238502e-03f, + 1.266597507949261160e-03f, + 1.217347131692460890e-03f, + 1.166929390305300809e-03f, + 1.115396434435750447e-03f, + 1.062801476344078485e-03f, + 1.009198735040490926e-03f, + 9.546433804735578524e-04f, + 8.991914768259076738e-04f, + 8.428999249761178706e-04f, + 7.858264041852376820e-04f, + 7.280293130684387321e-04f, + 6.695677099108536621e-04f, + 6.105012523900892442e-04f, + 5.508901367652243143e-04f, + 4.907950365958332764e-04f, + 4.302770410520321762e-04f, + 3.693975928787344835e-04f, + 3.082184260775406768e-04f, + 2.468015033684312445e-04f, + 1.852089534950353614e-04f, + 1.235030084373483811e-04f, + 6.174594059430125743e-05f, + 2.096293806310516030e-17f, + -6.167264836248043244e-05f, + -1.232099870881719884e-04f, + -1.845502086537135426e-04f, + -2.456317774361367959e-04f, + -3.063934913834511463e-04f, + -3.667745432764744212e-04f, + -4.267145815201261821e-04f, + -4.861537704029955404e-04f, + -5.450328497660459227e-04f, + -6.032931940205293235e-04f, + -6.608768704559289274e-04f, + -7.177266967809564115e-04f, + -7.737862978399837359e-04f, + -8.290001614485750878e-04f, + -8.833136932931055871e-04f, + -9.366732708404333684e-04f, + -9.890262962043143858e-04f, + -1.040321247916032189e-03f, + -1.090507731549242732e-03f, + -1.139536529148920665e-03f, + -1.187359647415603143e-03f, + -1.233930364598734937e-03f, + -1.279203276052626528e-03f, + -1.323134338411260068e-03f, + -1.365680912338360217e-03f, + -1.406801803812153832e-03f, + -1.446457303904537337e-03f, + -1.484609227016050938e-03f, + -1.521220947530623618e-03f, + -1.556257434854668676e-03f, + -1.589685286806867721e-03f, + -1.621472761327541912e-03f, + -1.651589806477331930e-03f, + -1.680008088696766707e-03f, + -1.706701019300830170e-03f, + -1.731643779183499347e-03f, + -1.754813341709760346e-03f, + -1.776188493773842719e-03f, + -1.795749855005169008e-03f, + -1.813479895104255042e-03f, + -1.829362949293911016e-03f, + -1.843385231871876419e-03f, + -1.855534847853930556e-03f, + -1.865801802697809961e-03f, + -1.874178010100366288e-03f, + -1.880657297862605663e-03f, + -1.885235411818870100e-03f, + -1.887910017828480034e-03f, + -1.888680701830295272e-03f, + -1.887548967962307056e-03f, + -1.884518234750538193e-03f, + -1.879593829373407774e-03f, + -1.872782980009604853e-03f, + -1.864094806279484534e-03f, + -1.853540307791953041e-03f, + -1.841132350810590528e-03f, + -1.826885653054665781e-03f, + -1.810816766652771246e-03f, + -1.792944059268252940e-03f, + -1.773287693417722457e-03f, + -1.751869604005822220e-03f, + -1.728713474100759947e-03f, + -1.703844708977252705e-03f, + -1.677290408455194264e-03f, + -1.649079337563943938e-03f, + -1.619241895563655999e-03f, + -1.587810083356924057e-03f, + -1.554817469325785705e-03f, + -1.520299153629931172e-03f, + -1.484291731004316970e-03f, + -1.446833252095214512e-03f, + -1.407963183375834622e-03f, + -1.367722365683123140e-03f, + -1.326152971419345067e-03f, + -1.283298460463622734e-03f, + -1.239203534838731165e-03f, + -1.193914092180647360e-03f, + -1.147477178059556511e-03f, + -1.099940937201084957e-03f, + -1.051354563658511796e-03f, + -1.001768249987922176e-03f, + -9.512331354778615728e-04f, + -8.998012534875231673e-04f, + -8.475254779470752691e-04f, + -7.944594690758058310e-04f, + -7.406576183728511139e-04f, + -6.861749929372944591e-04f, + -6.310672791747117981e-04f, + -5.753907259474574801e-04f, + -5.192020872264638655e-04f, + -4.625585643030877768e-04f, + -4.055177476203069577e-04f, + -3.481375582814421167e-04f, + -2.904761892961940780e-04f, + -2.325920466240080111e-04f, + -1.745436900735062170e-04f, + -1.163897741181921285e-04f, + -5.818898868865390200e-05f, + -3.951477669352645278e-17f, + 5.811860852513058267e-05f, + 1.161083951789106388e-04f, + 1.739111186764226750e-04f, + 2.314687965911455317e-04f, + 2.887237634590489712e-04f, + 3.456187284925843007e-04f, + 4.020968328478077619e-04f, + 4.581017063869804681e-04f, + 5.135775238795969085e-04f, + 5.684690605867797827e-04f, + 6.227217471732920040e-04f, + 6.762817238922043568e-04f, + 7.290958939893588033e-04f, + 7.811119762742447376e-04f, + 8.322785568051721625e-04f, + 8.825451396379117895e-04f, + 9.318621965879654058e-04f, + 9.801812159573458638e-04f, + 1.027454750177579689e-03f, + 1.073636462323048543e-03f, + 1.118681171448473750e-03f, + 1.162544896706745696e-03f, + 1.205184900203477068e-03f, + 1.246559728547386213e-03f, + 1.286629253055807933e-03f, + 1.325354708576141497e-03f, + 1.362698730886437387e-03f, + 1.398625392638811350e-03f, + 1.433100237810905602e-03f, + 1.466090314633128333e-03f, + 1.497564206959974605e-03f, + 1.527492064055464794e-03f, + 1.555845628765160742e-03f, + 1.582598264047852974e-03f, + 1.607724977842486065e-03f, + 1.631202446246891868e-03f, + 1.653009034987472543e-03f, + 1.673124819160033650e-03f, + 1.691531601223876481e-03f, + 1.708212927233296189e-03f, + 1.723154101292263601e-03f, + 1.736342198219845579e-03f, + 1.747766074415657220e-03f, + 1.757416376916791541e-03f, + 1.765285550639219018e-03f, + 1.771367843798416159e-03f, + 1.775659311506234831e-03f, + 1.778157817542435270e-03f, + 1.778863034301330324e-03f, + 1.777776440915891046e-03f, + 1.774901319563383737e-03f, + 1.770242749958432374e-03f, + 1.763807602041371232e-03f, + 1.755604526871366649e-03f, + 1.745643945735682106e-03f, + 1.733938037488337493e-03f, + 1.720500724132964119e-03f, + 1.705347654666533944e-03f, + 1.688496187202536726e-03f, + 1.669965369393517111e-03f, + 1.649775917174820059e-03f, + 1.627950191853253523e-03f, + 1.604512175565338738e-03f, + 1.579487445132431861e-03f, + 1.552903144340421785e-03f, + 1.524787954674487571e-03f, + 1.495172064539795074e-03f, + 1.464087137001309370e-03f, + 1.431566276076835656e-03f, + 1.397643991619413597e-03f, + 1.362356162825694014e-03f, + 1.325740000408985944e-03f, + 1.287834007477042493e-03f, + 1.248677939155022100e-03f, + 1.208312760996174511e-03f, + 1.166780606223920529e-03f, + 1.124124731849302948e-03f, + 1.080389473709662384e-03f, + 1.035620200475557496e-03f, + 9.898632666728486313e-04f, + 9.431659647690965754e-04f, + 8.955764763732448116e-04f, + 8.471438225995464935e-04f, + 7.979178136459807611e-04f, + 7.479489976393288025e-04f, + 6.972886087997711303e-04f, + 6.459885149773112378e-04f, + 5.941011646141058575e-04f, + 5.416795331870154695e-04f, + 4.887770691848273815e-04f, + 4.354476396749826271e-04f, + 3.817454755153243395e-04f, + 3.277251162670002699e-04f, + 2.734413548635745875e-04f, + 2.189491820928326409e-04f, + 1.643037309479273775e-04f, + 1.095602209032437102e-04f, + 5.477390217168007973e-05f, + 0.000000000000000000e+00f, + -5.470634094252695156e-05f, + -1.092901120256077629e-04f, + -1.636964959141602542e-04f, + -2.178709215633056469e-04f, + -2.717591188977905368e-04f, + -3.253071731207118249e-04f, + -3.784615785968173970e-04f, + -4.311692922574608179e-04f, + -4.833777864735189634e-04f, + -5.350351013432206020e-04f, + -5.860898963437366797e-04f, + -6.364915012948296367e-04f, + -6.861899665835708047e-04f, + -7.351361126015605119e-04f, + -7.832815783443825139e-04f, + -8.305788691267564604e-04f, + -8.769814033650232248e-04f, + -9.224435583820854738e-04f, + -9.669207151891035966e-04f, + -1.010369302200565247e-03f, + -1.052746837839338510e-03f, + -1.094011971990893574e-03f, + -1.134124526265950332e-03f, + -1.173045533032109934e-03f, + -1.210737273177340265e-03f, + -1.247163312568430474e-03f, + -1.282288537169006893e-03f, + -1.316079186783988613e-03f, + -1.348502887397804448e-03f, + -1.379528682075299327e-03f, + -1.409127060396503806e-03f, + -1.437269986396952099e-03f, + -1.463930924987541645e-03f, + -1.489084866828790932e-03f, + -1.512708351636820334e-03f, + -1.534779489899245307e-03f, + -1.555277982980894256e-03f, + -1.574185141601471948e-03f, + -1.591483902668316052e-03f, + -1.607158844449279840e-03f, + -1.621196200072613954e-03f, + -1.633583869342330195e-03f, + -1.644311428859175465e-03f, + -1.653370140439019498e-03f, + -1.660752957822507871e-03f, + -1.666454531671149753e-03f, + -1.670471212846983723e-03f, + -1.672801053974688886e-03f, + -1.673443809286669515e-03f, + -1.672400932753375074e-03f, + -1.669675574502914881e-03f, + -1.665272575535695033e-03f, + -1.659198460741494619e-03f, + -1.651461430228174168e-03f, + -1.642071348972851856e-03f, + -1.631039734807969606e-03f, + -1.618379744756649665e-03f, + -1.604106159732930550e-03f, + -1.588235367624443381e-03f, + -1.570785344776701219e-03f, + -1.551775635899458553e-03f, + -1.531227332417528422e-03f, + -1.509163049289567434e-03f, + -1.485606900320671090e-03f, + -1.460584471994783983e-03f, + -1.434122795855729757e-03f, + -1.406250319466170913e-03f, + -1.376996875975918010e-03f, + -1.346393652331567652e-03f, + -1.314473156161391357e-03f, + -1.281269181370805988e-03f, + -1.246816772484174701e-03f, + -1.211152187770746713e-03f, + -1.174312861193657241e-03f, + -1.136337363221307049e-03f, + -1.097265360542288778e-03f, + -1.057137574726171045e-03f, + -1.015995739872469503e-03f, + -9.738825592923504131e-04f, + -9.308416612674603454e-04f, + -8.869175539322839185e-04f, + -8.421555793258690996e-04f, + -7.966018666606091347e-04f, + -7.503032848565528582e-04f, + -7.033073943892899100e-04f, + -6.556623985011526476e-04f, + -6.074170938261656567e-04f, + -5.586208204781806371e-04f, + -5.093234116545103949e-04f, + -4.595751428051017724e-04f, + -4.094266804203762987e-04f, + -3.589290304887579673e-04f, + -3.081334866766899275e-04f, + -2.570915782841462450e-04f, + -2.058550180275716367e-04f, + -1.544756497035134832e-04f, + -1.030053957862678347e-04f, + -5.149620501158499536e-05f, + -2.384103929205831118e-17f, + 5.143137502784089743e-05f, + 1.027462063910287337e-04f, + 1.538929628910742572e-04f, + 2.048203475057252935e-04f, + 2.554773487812054080e-04f, + 3.058132918724349066e-04f, + 3.557778891797814462e-04f, + 4.053212905313948854e-04f, + 4.543941328619231237e-04f, + 5.029475893377426772e-04f, + 5.509334178794880440e-04f, + 5.983040090345575221e-04f, + 6.450124331513995514e-04f, + 6.910124868096819382e-04f, + 7.362587384591508678e-04f, + 7.807065732238670707e-04f, + 8.243122368263766566e-04f, + 8.670328785894033810e-04f, + 9.088265934721979824e-04f, + 9.496524631011158801e-04f, + 9.894705957539746879e-04f, + 1.028242165258874252e-03f, + 1.065929448770301484e-03f, + 1.102495863385499807e-03f, + 1.137906001565360849e-03f, + 1.172125665326259570e-03f, + 1.205121899169577957e-03f, + 1.236863021717049538e-03f, + 1.267318656022278301e-03f, + 1.296459758529015880e-03f, + 1.324258646649080235e-03f, + 1.350689024933432675e-03f, + 1.375726009812287777e-03f, + 1.399346152880900310e-03f, + 1.421527462709165374e-03f, + 1.442249425155372978e-03f, + 1.461493022165247842e-03f, + 1.479240749039111274e-03f, + 1.495476630152138510e-03f, + 1.510186233113277763e-03f, + 1.523356681351052851e-03f, + 1.534976665115079944e-03f, + 1.545036450884612481e-03f, + 1.553527889176402723e-03f, + 1.560444420746071904e-03f, + 1.565781081178877019e-03f, + 1.569534503867175447e-03f, + 1.571702921373700117e-03f, + 1.572286165181262111e-03f, + 1.571285663831225405e-03f, + 1.568704439454675749e-03f, + 1.564547102701767346e-03f, + 1.558819846076479193e-03f, + 1.551530435685405255e-03f, + 1.542688201411057929e-03f, + 1.532304025521471613e-03f, + 1.520390329729599246e-03f, + 1.506961060717611732e-03f, + 1.492031674142519585e-03f, + 1.475619117141234709e-03f, + 1.457741809354577093e-03f, + 1.438419622491405236e-03f, + 1.417673858455001249e-03f, + 1.395527226055938864e-03f, + 1.372003816336560527e-03f, + 1.347129076533619364e-03f, + 1.320929782707246439e-03f, + 1.293434011065339607e-03f, + 1.264671108014265698e-03f, + 1.234671658967309224e-03f, + 1.203467455944029225e-03f, + 1.171091463995042447e-03f, + 1.137577786487074328e-03f, + 1.102961629284945943e-03f, + 1.067279263868331236e-03f, + 1.030567989421257843e-03f, + 9.928660939341588630e-04f, + 9.542128143592528474e-04f, + 9.146482958600143324e-04f, + 8.742135501974977914e-04f, + 8.329504132961628631e-04f, + 7.909015020336008844e-04f, + 7.481101702980445106e-04f, + 7.046204643591989737e-04f, + 6.604770775986386951e-04f, + 6.157253046455315841e-04f, + 5.704109949650640419e-04f, + 5.245805059471973005e-04f, + 4.782806555435268323e-04f, + 4.315586745004174507e-04f, + 3.844621582371941467e-04f, + 3.370390184187921768e-04f, + 2.893374342713311603e-04f, + 2.414058036903930554e-04f, + 1.932926941919554573e-04f, + 1.450467937548224429e-04f, + 9.671686160493662174e-05f, + 4.835167899075133784e-05f, + 0.000000000000000000e+00f, + -4.828949753345398863e-05f, + -9.646826098282474608e-05f, + -1.444879116922577944e-04f, + -1.923002935030318679e-04f, + -2.398575209931877504e-04f, + -2.871120273819045897e-04f, + -3.340166120515270496e-04f, + -3.805244876394365660e-04f, + -4.265893266524579085e-04f, + -4.721653075582231689e-04f, + -5.172071603070152717e-04f, + -5.616702112394265662e-04f, + -6.055104273346537417e-04f, + -6.486844597567101994e-04f, + -6.911496866543188596e-04f, + -7.328642551735467438e-04f, + -7.737871226406448826e-04f, + -8.138780968755867382e-04f, + -8.530978755963916508e-04f, + -8.914080848753665014e-04f, + -9.287713166103488344e-04f, + -9.651511649741133930e-04f, + -1.000512261806224762e-03f, + -1.034820310913592929e-03f, + -1.068042121246285314e-03f, + -1.100145638916268705e-03f, + -1.131099978028938862e-03f, + -1.160875450297352155e-03f, + -1.189443593411230841e-03f, + -1.216777198133303000e-03f, + -1.242850334097847032e-03f, + -1.267638374286776453e-03f, + -1.291118018160179043e-03f, + -1.313267313420094689e-03f, + -1.334065676387146760e-03f, + -1.353493910971104207e-03f, + -1.371534226218442407e-03f, + -1.388170252420803863e-03f, + -1.403387055769956442e-03f, + -1.417171151546464019e-03f, + -1.429510515830663278e-03f, + -1.440394595726007159e-03f, + -1.449814318086319878e-03f, + -1.457762096740287039e-03f, + -1.464231838207676094e-03f, + -1.469218945903567495e-03f, + -1.472720322828254208e-03f, + -1.474734372742105958e-03f, + -1.475260999826186192e-03f, + -1.474301606830856525e-03f, + -1.471859091716282196e-03f, + -1.467937842790105170e-03f, + -1.462543732349215952e-03f, + -1.455684108833884020e-03f, + -1.447367787504157772e-03f, + -1.437605039649846777e-03f, + -1.426407580346835237e-03f, + -1.413788554773978299e-03f, + -1.399762523106214953e-03f, + -1.384345444001102527e-03f, + -1.367554656697077568e-03f, + -1.349408861743359471e-03f, + -1.329928100382799265e-03f, + -1.309133732610135619e-03f, + -1.287048413929449148e-03f, + -1.263696070835965916e-03f, + -1.239101875048799527e-03f, + -1.213292216521904285e-03f, + -1.186294675262172631e-03f, + -1.158137991984896625e-03f, + -1.128852037637259912e-03f, + -1.098467781822281067e-03f, + -1.067017260156693896e-03f, + -1.034533540596639092e-03f, + -1.001050688766642752e-03f, + -9.666037323284525605e-04f, + -9.312286244263419952e-04f, + -8.949622062474039356e-04f, + -8.578421687353971186e-04f, + -8.199070134983350812e-04f, + -7.811960129497022435e-04f, + -7.417491697247534464e-04f, + -7.016071754140860680e-04f, + -6.608113686563731959e-04f, + -6.194036926336412294e-04f, + -5.774266520130750657e-04f, + -5.349232693788336665e-04f, + -4.919370411988257261e-04f, + -4.485118933715837031e-04f, + -4.046921363984502134e-04f, + -3.605224202266125153e-04f, + -3.160476888090664981e-04f, + -2.713131344280844096e-04f, + -2.263641518278198237e-04f, + -1.812462922031631745e-04f, + -1.360052170909635560e-04f, + -9.068665221087721649e-05f, + -4.533634130168751519e-05f, + -1.119205495635177410e-17f, + 4.527673019179228291e-05f, + 9.044832780293992876e-05f, + 1.354694371103048339e-04f, + 1.802949136270320799e-04f, + 2.248798693188531084e-04f, + 2.691797175024917524e-04f, + 3.131502173809525350e-04f, + 3.567475181720213820e-04f, + 3.999282027853717433e-04f, + 4.426493310053094079e-04f, + 4.848684821356317727e-04f, + 5.265437970650557525e-04f, + 5.676340197111919006e-04f, + 6.080985378017700815e-04f, + 6.478974229535403896e-04f, + 6.869914700090091119e-04f, + 7.253422355920121755e-04f, + 7.629120758449196762e-04f, + 7.996641833101642005e-04f, + 8.355626229197331365e-04f, + 8.705723670581514643e-04f, + 9.046593296642918905e-04f, + 9.377903993392572779e-04f, + 9.699334714277904395e-04f, + 1.001057479042750638e-03f, + 1.031132423002631329e-03f, + 1.060129400652862165e-03f, + 1.088020633543997301e-03f, + 1.114779493940055944e-03f, + 1.140380530131902001e-03f, + 1.164799490531472428e-03f, + 1.188013346524613607e-03f, + 1.210000314060736106e-03f, + 1.230739873959466437e-03f, + 1.250212790915166154e-03f, + 1.268401131182280697e-03f, + 1.285288278925214404e-03f, + 1.300858951217885064e-03f, + 1.315099211679921550e-03f, + 1.327996482737379275e-03f, + 1.339539556497333726e-03f, + 1.349718604227470977e-03f, + 1.358525184432789320e-03f, + 1.365952249523105658e-03f, + 1.371994151066654273e-03f, + 1.376646643626202498e-03f, + 1.379906887175804363e-03f, + 1.381773448097536276e-03f, + 1.382246298759191167e-03f, + 1.381326815675158054e-03f, + 1.379017776254279782e-03f, + 1.375323354139781646e-03f, + 1.370249113147892998e-03f, + 1.363801999813117269e-03f, + 1.355990334549495588e-03f, + 1.346823801438608650e-03f, + 1.336313436656531026e-03f, + 1.324471615553049963e-03f, + 1.311312038398147750e-03f, + 1.296849714811698206e-03f, + 1.281100946894079716e-03f, + 1.264083311076190895e-03f, + 1.245815638708950906e-03f, + 1.226317995413717370e-03f, + 1.205611659215808989e-03f, + 1.183719097484903357e-03f, + 1.160663942707366959e-03f, + 1.136470967116147033e-03f, + 1.111166056205482996e-03f, + 1.084776181158849176e-03f, + 1.057329370218964941e-03f, + 1.028854679030497465e-03f, + 9.993821599864546878e-04f, + 9.689428306109160735e-04f, + 9.375686410107936589e-04f, + 9.052924404308006949e-04f, + 8.721479429470286020e-04f, + 8.381696923342349089e-04f, + 8.033930261436254697e-04f, + 7.678540390285455838e-04f, + 7.315895453557641741e-04f, + 6.946370411414883386e-04f, + 6.570346653508382520e-04f, + 6.188211606008807691e-04f, + 5.800358333080047517e-04f, + 5.407185133199405181e-04f, + 5.009095130741225872e-04f, + 4.606495863245328032e-04f, + 4.199798864786004988e-04f, + 3.789419245869958758e-04f, + 3.375775270294791236e-04f, + 2.959287929392034368e-04f, + 2.540380514093228668e-04f, + 2.119478185249197815e-04f, + 1.697007542644115945e-04f, + 1.273396193133593797e-04f, + 8.490723183468631443e-05f, + 4.244642423929934338e-05f, + 2.489132717092975244e-17f, + -4.238930944730621606e-05f, + -8.467888767193799395e-05f, + -1.268262760429604032e-04f, + -1.687892163075062216e-04f, + -2.105256929093874817e-04f, + -2.519939750065616508e-04f, + -2.931526581448565673e-04f, + -3.339607055468352646e-04f, + -3.743774889740940815e-04f, + -4.143628291230573081e-04f, + -4.538770355138195783e-04f, + -4.928809458321817922e-04f, + -5.313359646865749471e-04f, + -5.692041017412580173e-04f, + -6.064480091878560880e-04f, + -6.430310185189792816e-04f, + -6.789171765674388570e-04f, + -7.140712807754315923e-04f, + -7.484589136597800171e-04f, + -7.820464764390349256e-04f, + -8.148012217900297898e-04f, + -8.466912857015470575e-04f, + -8.776857183947638259e-04f, + -9.077545142802680937e-04f, + -9.368686409224388084e-04f, + -9.650000669841776822e-04f, + -9.921217891239340544e-04f, + -1.018207857820698729e-03f, + -1.043233402101169816e-03f, + -1.067174653146455267e-03f, + -1.090008966755562705e-03f, + -1.111714844644859455e-03f, + -1.132271954563242268e-03f, + -1.151661149204728438e-03f, + -1.169864483900832875e-03f, + -1.186865233076385287e-03f, + -1.202647905454177829e-03f, + -1.217198257994649147e-03f, + -1.230503308558112416e-03f, + -1.242551347278776424e-03f, + -1.253331946640669171e-03f, + -1.262835970246984230e-03f, + -1.271055580275981447e-03f, + -1.277984243617538056e-03f, + -1.283616736686040089e-03f, + -1.287949148906458816e-03f, + -1.290978884872014988e-03f, + -1.292704665172990245e-03f, + -1.293126525897621585e-03f, + -1.292245816807471729e-03f, + -1.290065198190826342e-03f, + -1.286588636399150143e-03f, + -1.281821398072865732e-03f, + -1.275770043064026376e-03f, + -1.268442416064888972e-03f, + -1.259847636952461181e-03f, + -1.249996089860607850e-03f, + -1.238899410992382174e-03f, + -1.226570475186708736e-03f, + -1.213023381254520064e-03f, + -1.198273436100851744e-03f, + -1.182337137650689993e-03f, + -1.165232156597218035e-03f, + -1.146977316992536366e-03f, + -1.127592575702190524e-03f, + -1.107099000745486849e-03f, + -1.085518748545065719e-03f, + -1.062875040110370555e-03f, + -1.039192136180123365e-03f, + -1.014495311350580541e-03f, + -9.888108272168318318e-04f, + -9.621659045558878355e-04f, + -9.345886945805340160e-04f, + -9.061082492943768954e-04f, + -8.767544909794978232e-04f, + -8.465581808478967385e-04f, + -8.155508868906216624e-04f, + -7.837649509569552704e-04f, + -7.512334550985514227e-04f, + -7.179901872135618750e-04f, + -6.840696060258972568e-04f, + -6.495068054360894584e-04f, + -6.143374782808354653e-04f, + -5.785978795380620956e-04f, + -5.423247890156327302e-04f, + -5.055554735623122827e-04f, + -4.683276488392193763e-04f, + -4.306794406911591301e-04f, + -3.926493461576842912e-04f, + -3.542761941630523837e-04f, + -3.155991059257000266e-04f, + -2.766574551271435236e-04f, + -2.374908278813562743e-04f, + -1.981389825446179249e-04f, + -1.586418094068897395e-04f, + -1.190392903058594805e-04f, + -7.937145820385292140e-05f, + -3.967835676875259741e-05f, + 0.000000000000000000e+00f, + 3.962366806029014220e-05f, + 7.915281349029238485e-05f, + 1.185477524848153186e-04f, + 1.577689911456342596e-04f, + 1.967772650253714708e-04f, + 2.355335783858113143e-04f, + 2.739992431308870902e-04f, + 3.121359173761859267e-04f, + 3.499056436162521094e-04f, + 3.872708864514444720e-04f, + 4.241945698375216493e-04f, + 4.606401138207244462e-04f, + 4.965714707217308132e-04f, + 5.319531607333346764e-04f, + 5.667503068964558190e-04f, + 6.009286694197791883e-04f, + 6.344546793099163770e-04f, + 6.672954712786205531e-04f, + 6.994189158952225562e-04f, + 7.307936509524349481e-04f, + 7.613891120155314102e-04f, + 7.911755621249407519e-04f, + 8.201241206232327396e-04f, + 8.482067910791271214e-04f, + 8.753964882812977381e-04f, + 9.016670642761047145e-04f, + 9.269933334240764138e-04f, + 9.513510964518079316e-04f, + 9.747171634758099818e-04f, + 9.970693759769062425e-04f, + 1.018386627704094679e-03f, + 1.038648884488686591e-03f, + 1.057837202950029669e-03f, + 1.075933748075338925e-03f, + 1.092921809657792587e-03f, + 1.108785817577722472e-03f, + 1.123511355912956439e-03f, + 1.137085175865973829e-03f, + 1.149495207496349907e-03f, + 1.160730570248186353e-03f, + 1.170781582263828594e-03f, + 1.179639768475975034e-03f, + 1.187297867471814568e-03f, + 1.193749837123847697e-03f, + 1.198990858983596747e-03f, + 1.203017341435374133e-03f, + 1.205826921608644554e-03f, + 1.207418466048858175e-03f, + 1.207792070147742499e-03f, + 1.206949056335355347e-03f, + 1.204891971037466616e-03f, + 1.201624580403049501e-03f, + 1.197151864807891562e-03f, + 1.191480012141583498e-03f, + 1.184616409886339811e-03f, + 1.176569635997314197e-03f, + 1.167349448595342331e-03f, + 1.156966774484061893e-03f, + 1.145433696504678973e-03f, + 1.132763439742820021e-03f, + 1.118970356602857433e-03f, + 1.104069910766385543e-03f, + 1.088078660052710107e-03f, + 1.071014238199964868e-03f, + 1.052895335586819490e-03f, + 1.033741678915853354e-03f, + 1.013574009880266035e-03f, + 9.924140628370767443e-04f, + 9.702845415105564161e-04f, + 9.472090947510056159e-04f, + 9.232122913743140771e-04f, + 8.983195941091211278e-04f, + 8.725573326793036348e-04f, + 8.459526760497619287e-04f, + 8.185336038648948062e-04f, + 7.903288771097757327e-04f, + 7.613680080244982808e-04f, + 7.316812293033188221e-04f, + 7.012994626104158578e-04f, + 6.702542864450650520e-04f, + 6.385779033898540073e-04f, + 6.063031067753177393e-04f, + 5.734632467957093789e-04f, + 5.400921961110872679e-04f, + 5.062243149706446149e-04f, + 4.718944158933642270e-04f, + 4.371377279425392405e-04f, + 4.019898606301749847e-04f, + 3.664867674887073131e-04f, + 3.306647093468514844e-04f, + 2.945602173475719322e-04f, + 2.582100557452125193e-04f, + 2.216511845199241977e-04f, + 1.849207218476427064e-04f, + 1.480559064630949787e-04f, + 1.110940599542368509e-04f, + 7.407254902655125068e-05f, + 3.702874777472067589e-05f, + 1.371254292486084993e-17f, + -3.697641838836769948e-05f, + -7.386333685080383616e-05f, + -1.106237275354719183e-04f, + -1.472207424002393370e-04f, + -1.836177501005376326e-04f, + -2.197783726069668013e-04f, + -2.556665215157765151e-04f, + -2.912464340157351313e-04f, + -3.264827084761888032e-04f, + -3.613403396206332991e-04f, + -3.957847532506383946e-04f, + -4.297818404862966193e-04f, + -4.632979914890506846e-04f, + -4.963001286333401738e-04f, + -5.287557390949833508e-04f, + -5.606329068237270391e-04f, + -5.919003438689767176e-04f, + -6.225274210275311962e-04f, + -6.524841977839308848e-04f, + -6.817414515139428016e-04f, + -7.102707059225399746e-04f, + -7.380442586892487162e-04f, + -7.650352082938549928e-04f, + -7.912174799963587243e-04f, + -8.165658509465838124e-04f, + -8.410559743992581695e-04f, + -8.646644030108421603e-04f, + -8.873686111969257854e-04f, + -9.091470165279773525e-04f, + -9.299790001438900484e-04f, + -9.498449261678874270e-04f, + -9.687261601014614341e-04f, + -9.866050861835819630e-04f, + -1.003465123697943424e-03f, + -1.019290742213164363e-03f, + -1.034067475742384131e-03f, + -1.047781935809330650e-03f, + -1.060421823409143580e-03f, + -1.071975939853697006e-03f, + -1.082434196491879799e-03f, + -1.091787623296736802e-03f, + -1.100028376312290647e-03f, + -1.107149743954318269e-03f, + -1.113146152160276754e-03f, + -1.118013168384754657e-03f, + -1.121747504438212967e-03f, + -1.124347018167691677e-03f, + -1.125810713979439898e-03f, + -1.126138742204658245e-03f, + -1.125332397310586279e-03f, + -1.123394114960401657e-03f, + -1.120327467926549893e-03f, + -1.116137160863255560e-03f, + -1.110829023945101137e-03f, + -1.104410005379749494e-03f, + -1.096888162803940974e-03f, + -1.088272653573009452e-03f, + -1.078573723955405161e-03f, + -1.067802697244547546e-03f, + -1.055971960801616169e-03f, + -1.043094952043956310e-03f, + -1.029186143394582309e-03f, + -1.014261026209532400e-03f, + -9.983360937008427163e-04f, + -9.814288228736027086e-04f, + -9.635576554969104172e-04f, + -9.447419781291415113e-04f, + -9.250021012192831361e-04f, + -9.043592373064512696e-04f, + -8.828354783409939847e-04f, + -8.604537721515002468e-04f, + -8.372378980823497055e-04f, + -8.132124418277322840e-04f, + -7.884027694888517653e-04f, + -7.628350008812852896e-04f, + -7.365359821204960078e-04f, + -7.095332575148247155e-04f, + -6.818550407942288718e-04f, + -6.535301857058335630e-04f, + -6.245881560060952261e-04f, + -5.950589948810114296e-04f, + -5.649732938262623386e-04f, + -5.343621610190059943e-04f, + -5.032571892141756960e-04f, + -4.716904231986256981e-04f, + -4.396943268360438742e-04f, + -4.073017497369382724e-04f, + -3.745458935874993508e-04f, + -3.414602781722681792e-04f, + -3.080787071247825435e-04f, + -2.744352334413967037e-04f, + -2.405641247936734649e-04f, + -2.064998286741144789e-04f, + -1.722769374108738480e-04f, + -1.379301530872341895e-04f, + -1.034942524008109811e-04f, + -6.900405149830530008e-05f, + -3.449437082158992495e-05f, + 0.000000000000000000e+00f, + 3.444433717537312282e-05f, + 6.880401766040344078e-05f, + 1.030445539143494854e-04f, + 1.371316284694892038e-04f, + 1.710311282792226217e-04f, + 2.047091788099896501e-04f, + 2.381321778435391604e-04f, + 2.712668289555900309e-04f, + 3.040801746373052975e-04f, + 3.365396290272290244e-04f, + 3.686130102209533182e-04f, + 4.002685721262641268e-04f, + 4.314750358328316138e-04f, + 4.622016204652085376e-04f, + 4.924180734885345943e-04f, + 5.220947004376862242e-04f, + 5.512023940402758712e-04f, + 5.797126627053133911e-04f, + 6.075976583493116747e-04f, + 6.348302035331886400e-04f, + 6.613838178833868642e-04f, + 6.872327437713459134e-04f, + 7.123519712269649109e-04f, + 7.367172620617903596e-04f, + 7.603051731785161974e-04f, + 7.830930790451201724e-04f, + 8.050591933112499331e-04f, + 8.261825895472138482e-04f, + 8.464432210854224304e-04f, + 8.658219399455064280e-04f, + 8.843005148257124162e-04f, + 9.018616481435636715e-04f, + 9.184889921098693746e-04f, + 9.341671638215605540e-04f, + 9.488817593593959444e-04f, + 9.626193668776509334e-04f, + 9.753675786743276274e-04f, + 9.871150022309649764e-04f, + 9.978512702125698873e-04f, + 1.007567049418917103e-03f, + 1.016254048679934505e-03f, + 1.023905025688631211e-03f, + 1.030513792766163958e-03f, + 1.036075221554958858e-03f, + 1.040585246636590612e-03f, + 1.044040868072351400e-03f, + 1.046440152865581131e-03f, + 1.047782235345830204e-03f, + 1.048067316476006608e-03f, + 1.047296662084868128e-03f, + 1.045472600028134798e-03f, + 1.042598516282698499e-03f, + 1.038678849979428548e-03f, + 1.033719087381135097e-03f, + 1.027725754813292765e-03f, + 1.020706410556256006e-03f, + 1.012669635708591783e-03f, + 1.003625024032280683e-03f, + 9.935831707915917296e-04f, + 9.825556605982765750e-04f, + 9.705550542768091827e-04f, + 9.575948747645024592e-04f, + 9.436895920619294822e-04f, + 9.288546072504160834e-04f, + 9.131062355939434513e-04f, + 8.964616887440624146e-04f, + 8.789390560668727381e-04f, + 8.605572851122642768e-04f, + 8.413361612465680012e-04f, + 8.212962864701133696e-04f, + 8.004590574423870028e-04f, + 7.788466427383010891e-04f, + 7.564819593593443900e-04f, + 7.333886485245473975e-04f, + 7.095910507669888961e-04f, + 6.851141803613563976e-04f, + 6.599836991102931479e-04f, + 6.342258895159663982e-04f, + 6.078676273653686388e-04f, + 5.809363537579391706e-04f, + 5.534600466042048465e-04f, + 5.254671916251264134e-04f, + 4.969867528824211626e-04f, + 4.680481428698100464e-04f, + 4.386811921962611408e-04f, + 4.089161188926386807e-04f, + 3.787834973728029320e-04f, + 3.483142270813974271e-04f, + 3.175395008601156806e-04f, + 2.864907730651890965e-04f, + 2.551997274681527816e-04f, + 2.236982449728073510e-04f, + 1.920183711814943037e-04f, + 1.601922838431261954e-04f, + 1.282522602162407851e-04f, + 9.623064438038904595e-05f, + 6.415981452841493812e-05f, + 3.207215027289962711e-05f, + 0.000000000000000000e+00f, + -3.202435169696419165e-05f, + -6.396871647107672993e-05f, + -9.580103453013783627e-05f, + -1.274894067670554485e-04f, + -1.590021266806003833e-04f, + -1.903077120544014096e-04f, + -2.213749363623242509e-04f, + -2.521728598694509757e-04f, + -2.826708603974881218e-04f, + -3.128386637238008165e-04f, + -3.426463735844534645e-04f, + -3.720645012513128301e-04f, + -4.010639946537739552e-04f, + -4.296162670168992745e-04f, + -4.576932249873889727e-04f, + -4.852672962200506594e-04f, + -5.123114563973639067e-04f, + -5.387992556561681809e-04f, + -5.647048443954880668e-04f, + -5.900029984401524482e-04f, + -6.146691435362195647e-04f, + -6.386793791542513593e-04f, + -6.620105015772862009e-04f, + -6.846400262516423845e-04f, + -7.065462093789202968e-04f, + -7.277080687284316705e-04f, + -7.481054036504530087e-04f, + -7.677188142710961042e-04f, + -7.865297198508461448e-04f, + -8.045203762893993540e-04f, + -8.216738927602709611e-04f, + -8.379742474599812278e-04f, + -8.534063024570928127e-04f, + -8.679558176273523317e-04f, + -8.816094636625075804e-04f, + -8.943548341408386320e-04f, + -9.061804566487988806e-04f, + -9.170758029437478922e-04f, + -9.270312981492120604e-04f, + -9.360383289747114437e-04f, + -9.440892509532578013e-04f, + -9.511773946908641834e-04f, + -9.572970711231403289e-04f, + -9.624435757751192066e-04f, + -9.666131920216366113e-04f, + -9.698031933463969285e-04f, + -9.720118445989208052e-04f, + -9.732384022497203710e-04f, + -9.734831136448424821e-04f, + -9.727472152621121065e-04f, + -9.710329299722829204e-04f, + -9.683434633094111085e-04f, + -9.646829987556522013e-04f, + -9.600566920467877531e-04f, + -9.544706645056394391e-04f, + -9.479319954115860043e-04f, + -9.404487134153821952e-04f, + -9.320297870093062660e-04f, + -9.226851140636999115e-04f, + -9.124255104419481694e-04f, + -9.012626977066420320e-04f, + -8.892092899308199835e-04f, + -8.762787796288336881e-04f, + -8.624855228225505183e-04f, + -8.478447232590492723e-04f, + -8.323724157971091513e-04f, + -8.160854489806475048e-04f, + -7.990014668176755822e-04f, + -7.811388897844821115e-04f, + -7.625168950755145496e-04f, + -7.431553961197853112e-04f, + -7.230750213856750215e-04f, + -7.022970924967909898e-04f, + -6.808436016816696431e-04f, + -6.587371885813887371e-04f, + -6.360011164391399848e-04f, + -6.126592476972289288e-04f, + -5.887360190265250072e-04f, + -5.642564158146420091e-04f, + -5.392459461393457349e-04f, + -5.137306142544784474e-04f, + -4.877368936155090711e-04f, + -4.612916994728182745e-04f, + -4.344223610612315406e-04f, + -4.071565934140700962e-04f, + -3.795224688310699706e-04f, + -3.515483880292340994e-04f, + -3.232630510065746267e-04f, + -2.946954276481456654e-04f, + -2.658747281046207941e-04f, + -2.368303729738942764e-04f, + -2.075919633156370829e-04f, + -1.781892505295539549e-04f, + -1.486521061281701210e-04f, + -1.190104914343625921e-04f, + -8.929442723454692366e-05f, + -5.953396341846094902e-05f, + -2.975914863569874771e-05f, + -1.469383061752921166e-17f, + 2.971352712842046073e-05f, + 5.935156925200977569e-05f, + 8.888438470625297655e-05f, + 1.182823834615285115e-04f, + 1.475161567260801273e-04f, + 1.765565063211114430e-04f, + 2.053744737984555227e-04f, + 2.339413692715925134e-04f, + 2.622287999318306112e-04f, + 2.902086982211098056e-04f, + 3.178533496333097114e-04f, + 3.451354201170366942e-04f, + 3.720279830524620595e-04f, + 3.985045457759202716e-04f, + 4.245390756258103919e-04f, + 4.501060254847104552e-04f, + 4.751803587924769172e-04f, + 4.997375740056823846e-04f, + 5.237537284800026236e-04f, + 5.472054617520811680e-04f, + 5.700700181981503386e-04f, + 5.923252690478518056e-04f, + 6.139497337318547063e-04f, + 6.349226005425641405e-04f, + 6.552237465884745531e-04f, + 6.748337570230266303e-04f, + 6.937339435292449487e-04f, + 7.119063620434466517e-04f, + 7.293338297005069531e-04f, + 7.459999409853247188e-04f, + 7.618890830752056463e-04f, + 7.769864503587980151e-04f, + 7.912780581184908910e-04f, + 8.047507553635035353e-04f, + 8.173922368022044829e-04f, + 8.291910539426864280e-04f, + 8.401366253119868770e-04f, + 8.502192457848813112e-04f, + 8.594300950141066115e-04f, + 8.677612449551008961e-04f, + 8.752056664789573028e-04f, + 8.817572350682475763e-04f, + 8.874107355915474612e-04f, + 8.921618661531745048e-04f, + 8.960072410156486164e-04f, + 8.989443925934634261e-04f, + 9.009717725175072972e-04f, + 9.020887517705425190e-04f, + 9.022956198950072941e-04f, + 9.015935832754222229e-04f, + 8.999847624985231375e-04f, + 8.974721887952514209e-04f, + 8.940597995695863213e-04f, + 8.897524330201562462e-04f, + 8.845558218614805405e-04f, + 8.784765861525543331e-04f, + 8.715222252413793759e-04f, + 8.637011088350353069e-04f, + 8.550224672055297846e-04f, + 8.454963805427627172e-04f, + 8.351337674665857812e-04f, + 8.239463727109863026e-04f, + 8.119467539939648334e-04f, + 7.991482680876762579e-04f, + 7.855650561042518912e-04f, + 7.712120280131833138e-04f, + 7.561048464071653402e-04f, + 7.402599095340949053e-04f, + 7.236943336132475825e-04f, + 7.064259344547092496e-04f, + 6.884732084018442772e-04f, + 6.698553126167958562e-04f, + 6.505920447301977031e-04f, + 6.307038218764186291e-04f, + 6.102116591367832835e-04f, + 5.891371474128742191e-04f, + 5.675024307539662440e-04f, + 5.453301831613886286e-04f, + 5.226435848948802945e-04f, + 4.994662983050689653e-04f, + 4.758224432173879066e-04f, + 4.517365718931811943e-04f, + 4.272336435935402852e-04f, + 4.023389987724907877e-04f, + 3.770783329259057943e-04f, + 3.514776701234272852e-04f, + 3.255633362501970246e-04f, + 2.993619319860350001e-04f, + 2.729003055499868075e-04f, + 2.462055252376792419e-04f, + 2.193048517797492180e-04f, + 1.922257105497324930e-04f, + 1.649956636492767566e-04f, + 1.376423818992599316e-04f, + 1.101936167654500256e-04f, + 8.267717224668583544e-05f, + 5.512087675440251894e-05f, + 2.755255501160192006e-05f, + 0.000000000000000000e+00f, + -2.750905501689023422e-05f, + -5.494696436636345655e-05f, + -8.228619770668268139e-05f, + -1.094993676073878112e-04f, + -1.365592569419782647e-04f, + -1.634388460653853027e-04f, + -1.901113397495802181e-04f, + -2.165501938502928923e-04f, + -2.427291416781312899e-04f, + -2.686222200484022333e-04f, + -2.942037949834695591e-04f, + -3.194485870425304262e-04f, + -3.443316962534629451e-04f, + -3.688286266226424733e-04f, + -3.929153101984506676e-04f, + -4.165681306646761548e-04f, + -4.397639464411628790e-04f, + -4.624801132689337598e-04f, + -4.846945062576402791e-04f, + -5.063855413743057745e-04f, + -5.275321963523644251e-04f, + -5.481140310006175909e-04f, + -5.681112068929672181e-04f, + -5.875045064196726674e-04f, + -6.062753511821435647e-04f, + -6.244058197136283239e-04f, + -6.418786645088341578e-04f, + -6.586773283467862658e-04f, + -6.747859598912580702e-04f, + -6.901894285541135439e-04f, + -7.048733386080151741e-04f, + -7.188240425351924510e-04f, + -7.320286536001779053e-04f, + -7.444750576348447895e-04f, + -7.561519240253451084e-04f, + -7.670487158910010940e-04f, + -7.771556994460668971e-04f, + -7.864639525364079805e-04f, + -7.949653723436894259e-04f, + -8.026526822505372595e-04f, + -8.095194378612496173e-04f, + -8.155600321732297967e-04f, + -8.207696998952141061e-04f, + -8.251445209094316778e-04f, + -8.286814228754654961e-04f, + -8.313781829745840735e-04f, + -8.332334287941438105e-04f, + -8.342466383525489566e-04f, + -8.344181392661235552e-04f, + -8.337491070600727781e-04f, + -8.322415626266423810e-04f, + -8.298983688343719274e-04f, + -8.267232262932451212e-04f, + -8.227206682813439833e-04f, + -8.178960548394521293e-04f, + -8.122555660409656268e-04f, + -8.058061944451279162e-04f, + -7.985557367425892995e-04f, + -7.905127846029615362e-04f, + -7.816867147349541335e-04f, + -7.720876781703026525e-04f, + -7.617265887835813565e-04f, + -7.506151110607858345e-04f, + -7.387656471301075898e-04f, + -7.261913230692261566e-04f, + -7.129059745042091046e-04f, + -6.989241315155087344e-04f, + -6.842610028674840552e-04f, + -6.689324595785751861e-04f, + -6.529550178495424672e-04f, + -6.363458213682250285e-04f, + -6.191226230095402778e-04f, + -6.013037659503773147e-04f, + -5.829081642191303019e-04f, + -5.639552827005720349e-04f, + -5.444651166170623056e-04f, + -5.244581705078699748e-04f, + -5.039554367282136470e-04f, + -4.829783734907900630e-04f, + -4.615488824727865158e-04f, + -4.396892860113637874e-04f, + -4.174223039115742672e-04f, + -3.947710298905243330e-04f, + -3.717589076824774933e-04f, + -3.484097068291740662e-04f, + -3.247474981805008798e-04f, + -3.007966291308984577e-04f, + -2.765816986165605708e-04f, + -2.521275318992345573e-04f, + -2.274591551626225019e-04f, + -2.026017699469456171e-04f, + -1.775807274479320418e-04f, + -1.524215027065971577e-04f, + -1.271496687156270196e-04f, + -1.017908704690000349e-04f, + -7.637079898086366356e-05f, + -5.091516530030798363e-05f, + -2.544967454785041741e-05f, + 0.000000000000000000e+00f, + 2.540824275175630965e-05f, + 5.074952154213560791e-05f, + 7.599841324713188062e-05f, + 1.011296292473662763e-04f, + 1.261180407145145923e-04f, + 1.509387036954135824e-04f, + 1.755668839686425260e-04f, + 1.999780816492812915e-04f, + 2.241480555170535154e-04f, + 2.480528470440098191e-04f, + 2.716688040976286797e-04f, + 2.949726042963528197e-04f, + 3.179412779943405166e-04f, + 3.405522308726428231e-04f, + 3.627832661150276234e-04f, + 3.846126061465318354e-04f, + 4.060189139133383055e-04f, + 4.269813136835948837e-04f, + 4.474794113487838955e-04f, + 4.674933142057642490e-04f, + 4.870036502007597822e-04f, + 5.059915866164069404e-04f, + 5.244388481841380598e-04f, + 5.423277346042669565e-04f, + 5.596411374573758216e-04f, + 5.763625564908231172e-04f, + 5.924761152646927393e-04f, + 6.079665761428002836e-04f, + 6.228193546145188893e-04f, + 6.370205329341033923e-04f, + 6.505568730647005556e-04f, + 6.634158289153857567e-04f, + 6.755855578598123036e-04f, + 6.870549315261518814e-04f, + 6.978135458484613016e-04f, + 7.078517303707442540e-04f, + 7.171605567954273622e-04f, + 7.257318467687813622e-04f, + 7.335581788968564615e-04f, + 7.406328949860011826e-04f, + 7.469501055028914937e-04f, + 7.525046942499604482e-04f, + 7.572923222527377452e-04f, + 7.613094308564032336e-04f, + 7.645532440298472834e-04f, + 7.670217698761347402e-04f, + 7.687138013491711264e-04f, + 7.696289161771383436e-04f, + 7.697674759941319994e-04f, + 7.691306246821071795e-04f, + 7.677202859261749368e-04f, + 7.655391599869575776e-04f, + 7.625907196945825228e-04f, + 7.588792056696464167e-04f, + 7.544096207772237267e-04f, + 7.491877238207874951e-04f, + 7.432200224837051672e-04f, + 7.365137655266115572e-04f, + 7.290769342498153649e-04f, + 7.209182332305319723e-04f, + 7.120470803455674876e-04f, + 7.024735960906073294e-04f, + 6.922085922080986782e-04f, + 6.812635596364262705e-04f, + 6.696506557935362697e-04f, + 6.573826912090139371e-04f, + 6.444731155192746008e-04f, + 6.309360028408991924e-04f, + 6.167860365379857716e-04f, + 6.020384934000156665e-04f, + 5.867092272469688922e-04f, + 5.708146519793728339e-04f, + 5.543717240911742901e-04f, + 5.373979246641968867e-04f, + 5.199112408629773122e-04f, + 5.019301469495465393e-04f, + 4.834735848383831772e-04f, + 4.645609442115968056e-04f, + 4.452120422152791297e-04f, + 4.254471027583006421e-04f, + 4.052867354349356968e-04f, + 3.847519140935204570e-04f, + 3.638639550730222647e-04f, + 3.426444951302156034e-04f, + 3.211154690804265380e-04f, + 2.992990871745817845e-04f, + 2.772178122359930490e-04f, + 2.548943365805551777e-04f, + 2.323515587436648325e-04f, + 2.096125600378590220e-04f, + 1.867005809653108641e-04f, + 1.636389975088607706e-04f, + 1.404512973260482907e-04f, + 1.171610558701005774e-04f, + 9.379191246244331451e-05f, + 7.036754634058427660e-05f, + 4.691165270578145734e-05f, + 2.344791879487271815e-05f, + 1.447139493247874122e-17f, + -2.340850393945113271e-05f, + -4.675407264888852574e-05f, + -7.001328871061514335e-05f, + -9.316286111268942802e-05f, + -1.161796485306134486e-04f, + -1.390406824191185795e-04f, + -1.617231898906969537e-04f, + -1.842046163583829115e-04f, + -2.064626479199869254e-04f, + -2.284752334619991466e-04f, + -2.502206064611250252e-04f, + -2.716773064617690556e-04f, + -2.928242002086798895e-04f, + -3.136405024138114671e-04f, + -3.341057961368736713e-04f, + -3.542000527599929792e-04f, + -3.739036515368039746e-04f, + -3.931973986967992316e-04f, + -4.120625460867281776e-04f, + -4.304808093307158469e-04f, + -4.484343854917819923e-04f, + -4.659059702174845512e-04f, + -4.828787743535818602e-04f, + -4.993365400096493739e-04f, + -5.152635560612082175e-04f, + -5.306446730741198057e-04f, + -5.454653176364612647e-04f, + -5.597115060851909612e-04f, + -5.733698576140564907e-04f, + -5.864276067510603525e-04f, + -5.988726151936528234e-04f, + -6.106933829909588687e-04f, + -6.218790590626611069e-04f, + -6.324194510452570833e-04f, + -6.423050344567614702e-04f, + -6.515269611716821042e-04f, + -6.600770671990646883e-04f, + -6.679478797567967160e-04f, + -6.751326236362062069e-04f, + -6.816252268518211213e-04f, + -6.874203255717539112e-04f, + -6.925132683248836490e-04f, + -6.969001194819386376e-04f, + -7.005776620080852567e-04f, + -7.035433994855702167e-04f, + -7.057955574054943209e-04f, + -7.073330837287954750e-04f, + -7.081556487170125340e-04f, + -7.082636440342657963e-04f, + -7.076581811226278617e-04f, + -7.063410888537339193e-04f, + -7.043149104602236445e-04f, + -7.015828997513892445e-04f, + -6.981490166179902005e-04f, + -6.940179218320851728e-04f, + -6.891949711482260339e-04f, + -6.836862087132502142e-04f, + -6.774983597924347884e-04f, + -6.706388228206039545e-04f, + -6.631156607872861451e-04f, + -6.549375919657807231e-04f, + -6.461139799966924375e-04f, + -6.366548233369270175e-04f, + -6.265707440859499865e-04f, + -6.158729762017194510e-04f, + -6.045733531191070480e-04f, + -5.926842947843961179e-04f, + -5.802187941200323941e-04f, + -5.671904029341022849e-04f, + -5.536132172898791236e-04f, + -5.395018623509823539e-04f, + -5.248714767185811651e-04f, + -5.097376962770673765e-04f, + -4.941166375654838707e-04f, + -4.780248806924582670e-04f, + -4.614794518122551344e-04f, + -4.444978051810356389e-04f, + -4.270978048115207926e-04f, + -4.092977057456325968e-04f, + -3.911161349647420196e-04f, + -3.725720719571632062e-04f, + -3.536848289632427730e-04f, + -3.344740309187047944e-04f, + -3.149595951167335258e-04f, + -2.951617106099559715e-04f, + -2.751008173737486736e-04f, + -2.547975852520000407e-04f, + -2.342728927071314657e-04f, + -2.135478053963316245e-04f, + -1.926435545956132946e-04f, + -1.715815154940239279e-04f, + -1.503831853799458774e-04f, + -1.290701617419976258e-04f, + -1.076641203064520068e-04f, + -8.618679303362720165e-05f, + -6.465994609572296963e-05f, + -4.310535785803166131e-05f, + -2.154479688593094298e-05f, + 0.000000000000000000e+00f, + 2.150734959899484319e-05f, + 4.295564411312584395e-05f, + 6.432337281627700971e-05f, + 8.558914358778801847e-05f, + 1.067317042889163222e-04f, + 1.277299639610350547e-04f, + 1.485630138241603702e-04f, + 1.692101480553038985e-04f, + 1.896508843258684759e-04f, + 2.098649840776009373e-04f, + 2.298324725174021228e-04f, + 2.495336583111203762e-04f, + 2.689491529567958729e-04f, + 2.880598898186698250e-04f, + 3.068471428031533201e-04f, + 3.252925446583616268e-04f, + 3.433781048797137779e-04f, + 3.610862272039176181e-04f, + 3.783997266745809350e-04f, + 3.953018462627122791e-04f, + 4.117762730263829513e-04f, + 4.278071537938969346e-04f, + 4.433791103553099983e-04f, + 4.584772541480976356e-04f, + 4.730872004228363326e-04f, + 4.871950818755382503e-04f, + 5.007875617336743747e-04f, + 5.138518462839280912e-04f, + 5.263756968296791783e-04f, + 5.383474410673522387e-04f, + 5.497559838709404180e-04f, + 5.605908174750933934e-04f, + 5.708420310473718206e-04f, + 5.805003196410431958e-04f, + 5.895569925205944436e-04f, + 5.980039808525682747e-04f, + 6.058338447549957298e-04f, + 6.130397796995799755e-04f, + 6.196156222612016684e-04f, + 6.255558552100400158e-04f, + 6.308556119424503159e-04f, + 6.355106802471486953e-04f, + 6.395175054041730848e-04f, + 6.428731926145374613e-04f, + 6.455755087594301040e-04f, + 6.476228834882815982e-04f, + 6.490144096357687228e-04f, + 6.497498429685649897e-04f, + 6.498296012632502226e-04f, + 6.492547627174425229e-04f, + 6.480270636970132886e-04f, + 6.461488958227519508e-04f, + 6.436233024006336820e-04f, + 6.404539742004272803e-04f, + 6.366452445880759490e-04f, + 6.322020840178591873e-04f, + 6.271300938911197539e-04f, + 6.214354997887679231e-04f, + 6.151251440855713855e-04f, + 6.082064779547860164e-04f, + 6.006875527722377360e-04f, + 5.925770109296073186e-04f, + 5.838840760673158665e-04f, + 5.746185427377923640e-04f, + 5.647907655106131422e-04f, + 5.544116475315949552e-04f, + 5.434926285482021132e-04f, + 5.320456724144572097e-04f, + 5.200832540887690824e-04f, + 5.076183461388823585e-04f, + 4.946644047682729551e-04f, + 4.812353553790131599e-04f, + 4.673455776866451889e-04f, + 4.530098904026903664e-04f, + 4.382435355011193574e-04f, + 4.230621620855109567e-04f, + 4.074818098737383954e-04f, + 3.915188923177147684e-04f, + 3.751901793757405701e-04f, + 3.585127799555410831e-04f, + 3.415041240464490179e-04f, + 3.241819445590930424e-04f, + 3.065642588915733278e-04f, + 2.886693502413833915e-04f, + 2.705157486821331970e-04f, + 2.521222120247453859e-04f, + 2.335077064829918579e-04f, + 2.146913871629504706e-04f, + 1.956925783966609658e-04f, + 1.765307539399361904e-04f, + 1.572255170548387095e-04f, + 1.377965804968402466e-04f, + 1.182637464271945108e-04f, + 9.864688627111849629e-05f, + 7.896592054191218808e-05f, + 5.924079865161975436e-05f, + 3.949147872881094546e-05f, + 1.973790746354780164e-05f, + 0.000000000000000000e+00f, + -1.970238010284559686e-05f, + -3.934944082275756825e-05f, + -5.892148151755434546e-05f, + -7.839891264075306743e-05f, + -9.776227530943462282e-05f, + -1.169922607052078226e-04f, + -1.360697292888009540e-04f, + -1.549757298090502554e-04f, + -1.736915180877542444e-04f, + -1.921985755616629511e-04f, + -2.104786275631484471e-04f, + -2.285136613218662050e-04f, + -2.462859436695555878e-04f, + -2.637780384304916933e-04f, + -2.809728234808804288e-04f, + -2.978535074603023448e-04f, + -3.144036461191332854e-04f, + -3.306071582858338361e-04f, + -3.464483414389500442e-04f, + -3.619118868686585066e-04f, + -3.769828944131547489e-04f, + -3.916468867560180043e-04f, + -4.058898232707585751e-04f, + -4.196981133992728607e-04f, + -4.330586295517413042e-04f, + -4.459587195157615211e-04f, + -4.583862183627433859e-04f, + -4.703294598410216135e-04f, + -4.817772872445234334e-04f, + -4.927190637473565383e-04f, + -5.031446821946852560e-04f, + -5.130445743409007284e-04f, + -5.224097195269496782e-04f, + -5.312316527889542495e-04f, + -5.395024723908887479e-04f, + -5.472148467749195742e-04f, + -5.543620209233284749e-04f, + -5.609378221266176105e-04f, + -5.669366651531786106e-04f, + -5.723535568162687118e-04f, + -5.771840999348496837e-04f, + -5.814244966853034362e-04f, + -5.850715513418501545e-04f, + -5.881226724039281687e-04f, + -5.905758741095224786e-04f, + -5.924297773340420807e-04f, + -5.936836098749646720e-04f, + -5.943372061230182968e-04f, + -5.943910061214307226e-04f, + -5.938460540152537288e-04f, + -5.927039958934639209e-04f, + -5.909670770271131714e-04f, + -5.886381385074528462e-04f, + -5.857206132884568801e-04f, + -5.822185216389217429e-04f, + -5.781364660097343096e-04f, + -5.734796253225910197e-04f, + -5.682537486870293739e-04f, + -5.624651485531170217e-04f, + -5.561206933077393524e-04f, + -5.492277993230843978e-04f, + -5.417944224662266713e-04f, + -5.338290490794490428e-04f, + -5.253406864414295312e-04f, + -5.163388527197619636e-04f, + -5.068335664260176796e-04f, + -4.968353353848055896e-04f, + -4.863551452290165310e-04f, + -4.754044474335747460e-04f, + -4.639951469007321709e-04f, + -4.521395891103186892e-04f, + -4.398505468486206245e-04f, + -4.271412065301010948e-04f, + -4.140251541266666644e-04f, + -4.005163607192301599e-04f, + -3.866291676868795061e-04f, + -3.723782715496072680e-04f, + -3.577787084799967311e-04f, + -3.428458385006848889e-04f, + -3.275953293837695238e-04f, + -3.120431402691025606e-04f, + -2.962055050186841124e-04f, + -2.800989153242122590e-04f, + -2.637401035854408898e-04f, + -2.471460255771968360e-04f, + -2.303338429226980228e-04f, + -2.133209053914711568e-04f, + -1.961247330399053007e-04f, + -1.787629982130247515e-04f, + -1.612535074256399813e-04f, + -1.436141831415453836e-04f, + -1.258630454695097874e-04f, + -1.080181937944321024e-04f, + -9.009778836248835666e-05f, + -7.212003183910606612e-05f, + -5.410315085817400264e-05f, + -3.606537758129155149e-05f, + -1.802493128582126862e-05f, + -1.334841303324302416e-17f, + 1.799127779618168215e-05f, + 3.593083138609340919e-05f, + 5.380067592915723640e-05f, + 7.158293045321104055e-05f, + 8.925983570849392351e-05f, + 1.068137718653035880e-04f, + 1.242272760380188604e-04f, + 1.414830596179534239e-04f, + 1.585640253977377951e-04f, + 1.754532844705931341e-04f, + 1.921341728876877549e-04f, + 2.085902680570638303e-04f, + 2.248054048683053446e-04f, + 2.407636915270301768e-04f, + 2.564495250836037240e-04f, + 2.718476066412367303e-04f, + 2.869429562284523142e-04f, + 3.017209273216939929e-04f, + 3.161672210038205064e-04f, + 3.302678997451251666e-04f, + 3.440094007935225488e-04f, + 3.573785491609914131e-04f, + 3.703625701941320385e-04f, + 3.829491017167877117e-04f, + 3.951262057331397641e-04f, + 4.068823796806154101e-04f, + 4.182065672215850910e-04f, + 4.290881685642916978e-04f, + 4.395170503031860959e-04f, + 4.494835547696116843e-04f, + 4.589785088844519070e-04f, + 4.679932325046681232e-04f, + 4.765195462561217800e-04f, + 4.845497788459313506e-04f, + 4.920767738478327470e-04f, + 4.990938959546221661e-04f, + 5.055950366925160639e-04f, + 5.115746195925331957e-04f, + 5.170276048147706216e-04f, + 5.219494932218531839e-04f, + 5.263363298985630810e-04f, + 5.301847071150910407e-04f, + 5.334917667319233727e-04f, + 5.362552020450717756e-04f, + 5.384732590707796192e-04f, + 5.401447372694501321e-04f, + 5.412689897091851800e-04f, + 5.418459226697609071e-04f, + 5.418759946885404492e-04f, + 5.413602150503207117e-04f, + 5.403001417237128035e-04f, + 5.386978787471504403e-04f, + 5.365560730682539118e-04f, + 5.338779108407521986e-04f, + 5.306671131837313420e-04f, + 5.269279314085348852e-04f, + 5.226651417191434303e-04f, + 5.178840393923420668e-04f, + 5.125904324446496933e-04f, + 5.067906347932736285e-04f, + 5.004914589190106924e-04f, + 4.937002080395005770e-04f, + 4.864246678015905052e-04f, + 4.786730975021753222e-04f, + 4.704542208472601629e-04f, + 4.617772162595178012e-04f, + 4.526517067449197353e-04f, + 4.430877493295452938e-04f, + 4.330958240781504313e-04f, + 4.226868227062654973e-04f, + 4.118720367981569454e-04f, + 4.006631456434114435e-04f, + 3.890722037049972786e-04f, + 3.771116277322665187e-04f, + 3.647941835326885492e-04f, + 3.521329724160614195e-04f, + 3.391414173259668587e-04f, + 3.258332486726308631e-04f, + 3.122224898823369732e-04f, + 2.983234426785867904e-04f, + 2.841506721102148633e-04f, + 2.697189913421606591e-04f, + 2.550434462248571854e-04f, + 2.401392996580377489e-04f, + 2.250220157652570890e-04f, + 2.097072438956290921e-04f, + 1.942108024690222747e-04f, + 1.785486626815674697e-04f, + 1.627369320880489372e-04f, + 1.467918380782401184e-04f, + 1.307297112638300613e-04f, + 1.145669687930290083e-04f, + 9.832009760999397456e-05f, + 8.200563767584910741e-05f, + 6.564016516846416676e-05f, + 4.924027567815219766e-05f, + 3.282256741602081771e-05f, + 1.640362445206236378e-05f, + 0.000000000000000000e+00f, + -1.637180023455527615e-05f, + -3.269533462575305538e-05f, + -4.895424210242561722e-05f, + -6.513225850968472699e-05f, + -8.121323284108457146e-05f, + -9.718114332507469882e-05f, + -1.130201133496294995e-04f, + -1.287144272095247152e-04f, + -1.442485456605490041e-04f, + -1.596071212651732553e-04f, + -1.747750135148174182e-04f, + -1.897373037137282015e-04f, + -2.044793096097578128e-04f, + -2.189865997580020041e-04f, + -2.332450076030689249e-04f, + -2.472406452664517324e-04f, + -2.609599170254186065e-04f, + -2.743895324706472098e-04f, + -2.875165193297870496e-04f, + -3.003282359445427004e-04f, + -3.128123833895385809e-04f, + -3.249570172213065876e-04f, + -3.367505588461240803e-04f, + -3.481818064961628547e-04f, + -3.592399458034899098e-04f, + -3.699145599619731075e-04f, + -3.801956394677196631e-04f, + -3.900735914289291096e-04f, + -3.995392484366653077e-04f, + -4.085838769884034077e-04f, + -4.171991854566158182e-04f, + -4.253773315953880327e-04f, + -4.331109295782838197e-04f, + -4.403930565612024142e-04f, + -4.472172587646578920e-04f, + -4.535775570701503011e-04f, + -4.594684521259947778e-04f, + -4.648849289583206301e-04f, + -4.698224610836477188e-04f, + -4.742770141197898636e-04f, + -4.782450488924013302e-04f, + -4.817235240350806647e-04f, + -4.847098980813407910e-04f, + -4.872021310473242527e-04f, + -4.891986855046963432e-04f, + -4.906985271436078684e-04f, + -4.917011248261453924e-04f, + -4.922064501312485964e-04f, + -4.922149763925275263e-04f, + -4.917276772309548717e-04f, + -4.907460245849183233e-04f, + -4.892719862405896280e-04f, + -4.873080228661261920e-04f, + -4.848570845536456569e-04f, + -4.819226068734645927e-04f, + -4.785085064455284654e-04f, + -4.746191760334893435e-04f, + -4.702594791673116756e-04f, + -4.654347443007680212e-04f, + -4.601507585106936983e-04f, + -4.544137607452196142e-04f, + -4.482304346287416166e-04f, + -4.416079008317367900e-04f, + -4.345537090140839465e-04f, + -4.270758293507777911e-04f, + -4.191826436494728442e-04f, + -4.108829360697150140e-04f, + -4.021858834539161960e-04f, + -3.931010452806725248e-04f, + -3.836383532514024155e-04f, + -3.738081005214428367e-04f, + -3.636209305872444616e-04f, + -3.530878258416693307e-04f, + -3.422200958094793414e-04f, + -3.310293650756770912e-04f, + -3.195275609193634256e-04f, + -3.077269006664550737e-04f, + -2.956398787743567697e-04f, + -2.832792536622980853e-04f, + -2.706580343011211979e-04f, + -2.577894665766888188e-04f, + -2.446870194409517208e-04f, + -2.313643708652139079e-04f, + -2.178353936102799936e-04f, + -2.041141408280510209e-04f, + -1.902148315096290467e-04f, + -1.761518357948250199e-04f, + -1.619396601583952301e-04f, + -1.475929324880076194e-04f, + -1.331263870693465308e-04f, + -1.185548494938537772e-04f, + -1.038932215042936427e-04f, + -8.915646579370446799e-05f, + -7.435959077332714683e-05f, + -5.951763532474445284e-05f, + -4.464565355180115499e-05f, + -2.975869954784884257e-05f, + -1.487181219344128483e-05f, + 0.000000000000000000e+00f, + 1.484177398541613872e-05f, + 2.963860719904158576e-05f, + 4.437567248797690006e-05f, + 5.903823293163951847e-05f, + 7.361165654203178363e-05f, + 8.808143082849284827e-05f, + 1.024331772123446792e-04f, + 1.166526652770380289e-04f, + 1.307258268399512900e-04f, + 1.446387698318426075e-04f, + 1.583777919701953075e-04f, + 1.719293942132778794e-04f, + 1.852802939815504897e-04f, + 1.984174381336592573e-04f, + 2.113280156841942253e-04f, + 2.239994702510968052e-04f, + 2.364195122205586396e-04f, + 2.485761306175752138e-04f, + 2.604576046709465229e-04f, + 2.720525150615433867e-04f, + 2.833497548429932469e-04f, + 2.943385400246083098e-04f, + 3.050084198064263339e-04f, + 3.153492864566233744e-04f, + 3.253513848222112969e-04f, + 3.350053214640939868e-04f, + 3.443020734077864120e-04f, + 3.532329965021145852e-04f, + 3.617898333778522433e-04f, + 3.699647209993159932e-04f, + 3.777501978020017677e-04f, + 3.851392104098344126e-04f, + 3.921251199262121398e-04f, + 3.987017077932429100e-04f, + 4.048631812141973915e-04f, + 4.106041781344828734e-04f, + 4.159197717771173663e-04f, + 4.208054747289555891e-04f, + 4.252572425744124453e-04f, + 4.292714770740190195e-04f, + 4.328450288854669421e-04f, + 4.359751998253025848e-04f, + 4.386597446699808871e-04f, + 4.408968724953503567e-04f, + 4.426852475541531284e-04f, + 4.440239896916363025e-04f, + 4.449126742997511793e-04f, + 4.453513318109698395e-04f, + 4.453404467331309718e-04f, + 4.448809562272690614e-04f, + 4.439742482307769997e-04f, + 4.426221591287388775e-04f, + 4.408269709767168466e-04f, + 4.385914082787080990e-04f, + 4.359186343244749818e-04f, + 4.328122470908071003e-04f, + 4.292762747118040057e-04f, + 4.253151705236267433e-04f, + 4.209338076896006566e-04f, + 4.161374734119831350e-04f, + 4.109318627370653408e-04f, + 4.053230719607911351e-04f, + 3.993175916422970020e-04f, + 3.929222992332770761e-04f, + 3.861444513314888326e-04f, + 3.789916755669021876e-04f, + 3.714719621295073122e-04f, + 3.635936549481525153e-04f, + 3.553654425299459119e-04f, + 3.467963484702393361e-04f, + 3.378957216435445662e-04f, + 3.286732260858342197e-04f, + 3.191388305792130522e-04f, + 3.093027979500185209e-04f, + 2.991756740919330917e-04f, + 2.887682767254974537e-04f, + 2.780916839063471402e-04f, + 2.671572222938670333e-04f, + 2.559764551930417240e-04f, + 2.445611703817801011e-04f, + 2.329233677365916448e-04f, + 2.210752466696249048e-04f, + 2.090291933900031019e-04f, + 1.967977680028590282e-04f, + 1.843936914593410459e-04f, + 1.718298323712722203e-04f, + 1.591191937038941441e-04f, + 1.462748993604876967e-04f, + 1.333101806727993762e-04f, + 1.202383628109180238e-04f, + 1.070728511266315022e-04f, + 9.382711744432409014e-05f, + 8.051468631319089826e-05f, + 6.714912123485984206e-05f, + 5.374401088052293570e-05f, + 4.031295531131948081e-05f, + 2.686955221609669864e-05f, + 1.342738318029786585e-05f, + 0.000000000000000000e+00f, + -1.339908894537428966e-05f, + -2.675643224788563980e-05f, + -4.005864893978847017e-05f, + -5.329244186287420863e-05f, + -6.644461092502548262e-05f, + -7.950206623069222111e-05f, + -9.245184107248077213e-05f, + -1.052811047708897691e-04f, + -1.179771753494133872e-04f, + -1.305275320327536741e-04f, + -1.429198275556728382e-04f, + -1.551419002705676728e-04f, + -1.671817860417565296e-04f, + -1.790277299150954120e-04f, + -1.906681975514930251e-04f, + -2.020918864131418622e-04f, + -2.132877366918702598e-04f, + -2.242449419689906455e-04f, + -2.349529595963345725e-04f, + -2.454015207887385404e-04f, + -2.555806404182784557e-04f, + -2.654806265008874768e-04f, + -2.750920893665808145e-04f, + -2.844059505045345624e-04f, + -2.934134510748270851e-04f, + -3.021061600789069551e-04f, + -3.104759821811575835e-04f, + -3.185151651745712952e-04f, + -3.262163070835866359e-04f, + -3.335723628976332462e-04f, + -3.405766509294967594e-04f, + -3.472228587927296962e-04f, + -3.535050489929618660e-04f, + -3.594176641281607277e-04f, + -3.649555316935231171e-04f, + -3.701138684869255359e-04f, + -3.748882846112615666e-04f, + -3.792747870705806133e-04f, + -3.832697829571900684e-04f, + -3.868700822273337075e-04f, + -3.900729000635904330e-04f, + -3.928758588224299966e-04f, + -3.952769895658223660e-04f, + -3.972747331762911953e-04f, + -3.988679410551167681e-04f, + -4.000558754039121919e-04f, + -4.008382090901287695e-04f, + -4.012150250975650445e-04f, + -4.011868155632819922e-04f, + -4.007544804028019764e-04f, + -3.999193255258539845e-04f, + -3.986830606453539640e-04f, + -3.970477966826979296e-04f, + -3.950160427728647184e-04f, + -3.925907028732052292e-04f, + -3.897750719802241096e-04f, + -3.865728319589897214e-04f, + -3.829880469902513843e-04f, + -3.790251586406653394e-04f, + -3.746889805619719256e-04f, + -3.699846928252342087e-04f, + -3.649178358966703251e-04f, + -3.594943042620016315e-04f, + -3.537203397064425225e-04f, + -3.476025242579046902e-04f, + -3.411477728013451389e-04f, + -3.343633253723441497e-04f, + -3.272567391384403391e-04f, + -3.198358800770878963e-04f, + -3.121089143591786099e-04f, + -3.040842994476012479e-04f, + -2.957707749203746634e-04f, + -2.871773530283563362e-04f, + -2.783133089975229658e-04f, + -2.691881710862687534e-04f, + -2.598117104082893068e-04f, + -2.501939305319653465e-04f, + -2.403450568670490101e-04f, + -2.302755258500093155e-04f, + -2.199959739394581452e-04f, + -2.095172264330585757e-04f, + -1.988502861177488868e-04f, + -1.880063217650495839e-04f, + -1.769966564835590396e-04f, + -1.658327559405899988e-04f, + -1.545262164652106557e-04f, + -1.430887530451063164e-04f, + -1.315321872294559114e-04f, + -1.198684349503653277e-04f, + -1.081094942754574683e-04f, + -9.626743310399233169e-05f, + -8.435437681917366869e-05f, + -7.238249590935302358e-05f, + -6.036399357051934749e-05f, + -4.831109330284252872e-05f, + -3.623602651371357099e-05f, + -2.415102013999141309e-05f, + -1.206828430174254195e-05f, + 0.000000000000000000e+00f, + 1.204169312897504154e-05f, + 2.404470899092571738e-05f, + 3.599702712991723069e-05f, + 4.788670473170228393e-05f, + 5.970188852229866780e-05f, + 7.143082654988841410e-05f, + 8.306187983826528306e-05f, + 9.458353390051201588e-05f, + 1.059844101013897441e-04f, + 1.172532768573817302e-04f, + 1.283790606632529297e-04f, + 1.393508569345262404e-04f, + 1.501579406552106444e-04f, + 1.607897768203369465e-04f, + 1.712360306633601821e-04f, + 1.814865776584426626e-04f, + 1.915315132879176962e-04f, + 2.013611625657108102e-04f, + 2.109660893075433882e-04f, + 2.203371051389893943e-04f, + 2.294652782330296192e-04f, + 2.383419417686963803e-04f, + 2.469587021029656941e-04f, + 2.553074466481195588e-04f, + 2.633803514474035304e-04f, + 2.711698884419244454e-04f, + 2.786688324219942182e-04f, + 2.858702676567446673e-04f, + 2.927675941959327296e-04f, + 2.993545338382966954e-04f, + 3.056251357610912951e-04f, + 3.115737818059637109e-04f, + 3.171951914164931581e-04f, + 3.224844262232151398e-04f, + 3.274368942722000880e-04f, + 3.320483538937818360e-04f, + 3.363149172082760107e-04f, + 3.402330532659012994e-04f, + 3.437995908186134266e-04f, + 3.470117207218301322e-04f, + 3.498669979643943373e-04f, + 3.523633433256204432e-04f, + 3.544990446585373207e-04f, + 3.562727577988377661e-04f, + 3.576835070995022250e-04f, + 3.587306855913434921e-04f, + 3.594140547701693143e-04f, + 3.597337440115922912e-04f, + 3.596902496149280318e-04f, + 3.592844334779736470e-04f, + 3.585175214048202355e-04f, + 3.573911010492562368e-04f, + 3.559071194966300722e-04f, + 3.540678804874658795e-04f, + 3.518760412864115083e-04f, + 3.493346092005057690e-04f, + 3.464469377510762094e-04f, + 3.432167225039049318e-04f, + 3.396479965626749342e-04f, + 3.357451257309863640e-04f, + 3.315128033486363182e-04f, + 3.269560448080700604e-04f, + 3.220801817572939820e-04f, + 3.168908559958683816e-04f, + 3.113940130707873932e-04f, + 3.055958955794286277e-04f, + 2.995030361870785703e-04f, + 2.931222503666503615e-04f, + 2.864606288686135879e-04f, + 2.795255299294358681e-04f, + 2.723245712269062205e-04f, + 2.648656215911465348e-04f, + 2.571567924801972712e-04f, + 2.492064292294373924e-04f, + 2.410231020841035532e-04f, + 2.326155970245017573e-04f, + 2.239929063938051238e-04f, + 2.151642193382072939e-04f, + 2.061389120696203742e-04f, + 1.969265379611917521e-04f, + 1.875368174860041030e-04f, + 1.779796280096115294e-04f, + 1.682649934469443648e-04f, + 1.584030737944329339e-04f, + 1.484041545483235042e-04f, + 1.382786360200012393e-04f, + 1.280370225594695995e-04f, + 1.176899116981768777e-04f, + 1.072479832222307631e-04f, + 9.672198818729011848e-05f, + 8.612273788649634355e-05f, + 7.546109278254378857e-05f, + 6.474795141533576512e-05f, + 5.399423929640585659e-05f, + 4.321089780154245503e-05f, + 3.240887307268698056e-05f, + 2.159910494042194029e-05f, + 1.079251587830625557e-05f, + 0.000000000000000000e+00f, + -1.076758788963704548e-05f, + -2.149944317856241301e-05f, + -3.218482225573323713e-05f, + -4.281305323283309065e-05f, + -5.337354656864646082e-05f, + -6.385580558576757243e-05f, + -7.424943686907754835e-05f, + -8.454416053583386234e-05f, + -9.472982036711456591e-05f, + -1.047963937908457071e-04f, + -1.147340017065515562e-04f, + -1.245329181421548671e-04f, + -1.341835797335974307e-04f, + -1.436765950179864047e-04f, + -1.530027535312186607e-04f, + -1.621530347014577759e-04f, + -1.711186165298485740e-04f, + -1.798908840500873269e-04f, + -1.884614375589472831e-04f, + -1.968221006098050266e-04f, + -2.049649277617257863e-04f, + -2.128822120766777921e-04f, + -2.205664923580282378e-04f, + -2.280105601234851175e-04f, + -2.352074663059836263e-04f, + -2.421505276765508960e-04f, + -2.488333329829885750e-04f, + -2.552497487991398626e-04f, + -2.613939250791943547e-04f, + -2.672603004122885910e-04f, + -2.728436069726557201e-04f, + -2.781388751610882780e-04f, + -2.831414379336380212e-04f, + -2.878469348139965243e-04f, + -2.922513155861687336e-04f, + -2.963508436644055012e-04f, + -3.001420991378207152e-04f, + -3.036219814873026852e-04f, + -3.067877119727112443e-04f, + -3.096368356887807777e-04f, + -3.121672232883608046e-04f, + -3.143770723720249469e-04f, + -3.162649085434524054e-04f, + -3.178295861302663341e-04f, + -3.190702885703869526e-04f, + -3.199865284642772981e-04f, + -3.205781472938346467e-04f, + -3.208453148089736979e-04f, + -3.207885280832979214e-04f, + -3.204086102406083158e-04f, + -3.197067088542942777e-04f, + -3.186842940220061915e-04f, + -3.173431561183079385e-04f, + -3.156854032283730655e-04f, + -3.137134582660339763e-04f, + -3.114300557798812749e-04f, + -3.088382384513568856e-04f, + -3.059413532891418143e-04f, + -3.027430475243637962e-04f, + -2.992472642115744834e-04f, + -2.954582375405406932e-04f, + -2.913804878643640249e-04f, + -2.870188164496020400e-04f, + -2.823782999543770502e-04f, + -2.774642846407175700e-04f, + -2.722823803275761204e-04f, + -2.668384540913915862e-04f, + -2.611386237210131574e-04f, + -2.551892509343225957e-04f, + -2.489969343639438274e-04f, + -2.425685023197270455e-04f, + -2.359110053357878537e-04f, + -2.290317085103517163e-04f, + -2.219380836464229334e-04f, + -2.146378012019111564e-04f, + -2.071387220576676933e-04f, + -1.994488891124380440e-04f, + -1.915765187133722193e-04f, + -1.835299919314087671e-04f, + -1.753178456905652674e-04f, + -1.669487637607092330e-04f, + -1.584315676229787050e-04f, + -1.497752072176369788e-04f, + -1.409887515839194623e-04f, + -1.320813794016571026e-04f, + -1.230623694444957564e-04f, + -1.139410909545171644e-04f, + -1.047269939484425535e-04f, + -9.542959946509581922e-05f, + -8.605848976438549571e-05f, + -7.662329848777562182e-05f, + -6.713370079033298594e-05f, + -5.759940345433426073e-05f, + -4.803013499474702882e-05f, + -3.843563576630159076e-05f, + -2.882564808243503731e-05f, + -1.920990635599448016e-05f, + -9.598127271662273025e-06f, + 0.000000000000000000e+00f, + 9.574823537103811872e-06f, + 1.911673833977519833e-05f, + 2.861619593263633836e-05f, + 3.806371388693437398e-05f, + 4.744988525275912425e-05f, + 5.676538789203427777e-05f, + 6.600099370314859348e-05f, + 7.514757772787978142e-05f, + 8.419612713200058348e-05f, + 9.313775005050625494e-05f, + 1.019636842889295287e-04f, + 1.106653058722401322e-04f, + 1.192341374330203445e-04f, + 1.276618564308877656e-04f, + 1.359403031950059157e-04f, + 1.440614887822626452e-04f, + 1.520176026433699178e-04f, + 1.598010200896498841e-04f, + 1.674043095534119953e-04f, + 1.748202396351221399e-04f, + 1.820417859305353066e-04f, + 1.890621376316773902e-04f, + 1.958747038953250451e-04f, + 2.024731199731614757e-04f, + 2.088512530979652713e-04f, + 2.150032081205004760e-04f, + 2.209233328918379412e-04f, + 2.266062233865404583e-04f, + 2.320467285618302712e-04f, + 2.372399549487279324e-04f, + 2.421812709709947786e-04f, + 2.468663109882154221e-04f, + 2.512909790595921756e-04f, + 2.554514524253027516e-04f, + 2.593441847026279235e-04f, + 2.629659087941870811e-04f, + 2.663136395061924231e-04f, + 2.693846758746589278e-04f, + 2.721766031979693879e-04f, + 2.746872947744395253e-04f, + 2.769149133438811154e-04f, + 2.788579122323372257e-04f, + 2.805150361996677278e-04f, + 2.818853219897804355e-04f, + 2.829680985836939208e-04f, + 2.837629871559238016e-04f, + 2.842699007349416287e-04f, + 2.844890435688079782e-04f, + 2.844209101973068656e-04f, + 2.840662842322885578e-04f, + 2.834262368481505552e-04f, + 2.825021249847073845e-04f, + 2.812955892649861810e-04f, + 2.798085516307519159e-04f, + 2.780432126988811935e-04f, + 2.760020488419260187e-04f, + 2.736878089965273718e-04f, + 2.711035112035945611e-04f, + 2.682524388844043702e-04f, + 2.651381368570786159e-04f, + 2.617644070980710701e-04f, + 2.581353042536924927e-04f, + 2.542551309067299522e-04f, + 2.501284326036580432e-04f, + 2.457599926480392495e-04f, + 2.411548266659954384e-04f, + 2.363181769497842621e-04f, + 2.312555065858830860e-04f, + 2.259724933739240289e-04f, + 2.204750235432793924e-04f, + 2.147691852741365984e-04f, + 2.088612620301268357e-04f, + 2.027577257097531386e-04f, + 1.964652296239523634e-04f, + 1.899906013075300696e-04f, + 1.833408351718813763e-04f, + 1.765230850071902420e-04f, + 1.695446563417966825e-04f, + 1.624129986670188350e-04f, + 1.551356975356094168e-04f, + 1.477204665421056229e-04f, + 1.401751391936988090e-04f, + 1.325076606798998916e-04f, + 1.247260795497873826e-04f, + 1.168385393054425164e-04f, + 1.088532699203247078e-04f, + 1.007785792914005370e-04f, + 9.262284463377021455e-05f, + 8.439450382689701034e-05f, + 7.610204672104689636e-05f, + 6.775400641307872908e-05f, + 5.935895050042970316e-05f, + 5.092547232225660091e-05f, + 4.246218219656006581e-05f, + 3.397769866242637815e-05f, + 2.548063973595517562e-05f, + 1.697961418894217532e-05f, + 8.483212859017624744e-06f, + 0.000000000000000000e+00f, + -8.461495318869855788e-06f, + -1.689278775621859074e-05f, + -2.528544417573815790e-05f, + -3.363109204902172219e-05f, + -4.192142777555209638e-05f, + -5.014822491167240422e-05f, + -5.830334230039153014e-05f, + -6.637873209411312210e-05f, + -7.436644766219583183e-05f, + -8.225865137590645756e-05f, + -9.004762226295531552e-05f, + -9.772576352426085543e-05f, + -1.052856099056577086e-04f, + -1.127198349174101543e-04f, + -1.200212578946849995e-04f, + -1.271828508920051467e-04f, + -1.341977454053923206e-04f, + -1.410592389156038187e-04f, + -1.477608012463634826e-04f, + -1.542960807315748824e-04f, + -1.606589101858277410e-04f, + -1.668433126724388452e-04f, + -1.728435070639570102e-04f, + -1.786539133898150888e-04f, + -1.842691579663201804e-04f, + -1.896840783043778154e-04f, + -1.948937277903842115e-04f, + -1.998933801363103201e-04f, + -2.046785335949608540e-04f, + -2.092449149366440572e-04f, + -2.135884831840112908e-04f, + -2.177054331017576675e-04f, + -2.215921984383175921e-04f, + -2.252454549169022465e-04f, + -2.286621229734833933e-04f, + -2.318393702396226929e-04f, + -2.347746137681952429e-04f, + -2.374655220005397348e-04f, + -2.399100164736049493e-04f, + -2.421062732660711372e-04f, + -2.440527241826416158e-04f, + -2.457480576760017301e-04f, + -2.471912195061305153e-04f, + -2.483814131370483272e-04f, + -2.493180998712305895e-04f, + -2.500009987222372404e-04f, + -2.504300860263741746e-04f, + -2.506055947944560284e-04f, + -2.505280138049976969e-04f, + -2.501980864404289577e-04f, + -2.496168092681825537e-04f, + -2.487854303687649961e-04f, + -2.477054474131509354e-04f, + -2.463786054921205315e-04f, + -2.448068947003734946e-04f, + -2.429925474785403472e-04f, + -2.409380357163841445e-04f, + -2.386460676207832049e-04f, + -2.361195843522810752e-04f, + -2.333617564342224063e-04f, + -2.303759799387175490e-04f, + -2.271658724538751066e-04f, + -2.237352688370258271e-04f, + -2.200882167587254817e-04f, + -2.162289720426820811e-04f, + -2.121619938068343967e-04f, + -2.078919394110624542e-04f, + -2.034236592171233195e-04f, + -1.987621911667231361e-04f, + -1.939127551835801355e-04f, + -1.888807474057235954e-04f, + -1.836717342542969276e-04f, + -1.782914463453192841e-04f, + -1.727457722510192901e-04f, + -1.670407521174195056e-04f, + -1.611825711451174772e-04f, + -1.551775529402107034e-04f, + -1.490321527423956686e-04f, + -1.427529505375406372e-04f, + -1.363466440619829170e-04f, + -1.298200417059508136e-04f, + -1.231800553235984548e-04f, + -1.164336929571301011e-04f, + -1.095880514828144936e-04f, + -1.026503091863229081e-04f, + -9.562771827530503561e-05f, + -8.852759733689483329e-05f, + -8.135732374798783709e-05f, + -7.412432604604385269e-05f, + -6.683607626845965535e-05f, + -5.950008226812831386e-05f, + -5.212388001323979862e-05f, + -4.471502587911416735e-05f, + -3.728108893993130443e-05f, + -2.982964326820167396e-05f, + -2.236826024970074687e-05f, + -1.490450092183243395e-05f, + -7.445908342878064974e-06f, + 0.000000000000000000e+00f, + 7.425739736475704763e-06f, + 1.482386710490919136e-05f, + 2.218698638297385948e-05f, + 2.950775724944274754e-05f, + 3.677890207012403487e-05f, + 4.399321310054077095e-05f, + 5.114355959833892794e-05f, + 5.822289483842819512e-05f, + 6.522426302397340788e-05f, + 7.214080608657170231e-05f, + 7.896577036881257869e-05f, + 8.569251318300049464e-05f, + 9.231450923951847092e-05f, + 9.882535693872643605e-05f, + 1.052187845203471448e-04f, + 1.114886560645364380e-04f, + 1.176289773387779070e-04f, + 1.236339014852947521e-04f, + 1.294977345434665896e-04f, + 1.352149408021485982e-04f, + 1.407801479768994418e-04f, + 1.461881522073272849e-04f, + 1.514339228700169496e-04f, + 1.565126072024935349e-04f, + 1.614195347342348315e-04f, + 1.661502215206463087e-04f, + 1.707003741761581588e-04f, + 1.750658937030649676e-04f, + 1.792428791126197800e-04f, + 1.832276308353748221e-04f, + 1.870166539177443887e-04f, + 1.906066610022775826e-04f, + 1.939945750890921376e-04f, + 1.971775320762906734e-04f, + 2.001528830773817175e-04f, + 2.029181965139526369e-04f, + 2.054712599821023459e-04f, + 2.078100818912826861e-04f, + 2.099328928746136498e-04f, + 2.118381469697695253e-04f, + 2.135245225699071707e-04f, + 2.149909231442836445e-04f, + 2.162364777284813886e-04f, + 2.172605411843266446e-04f, + 2.180626942299304465e-04f, + 2.186427432404015815e-04f, + 2.190007198200883928e-04f, + 2.191368801473969510e-04f, + 2.190517040934920065e-04f, + 2.187458941163810658e-04f, + 2.182203739321322280e-04f, + 2.174762869651921987e-04f, + 2.165149945799773028e-04f, + 2.153380740961488441e-04f, + 2.139473165901802359e-04f, + 2.123447244860358321e-04f, + 2.105325089380130263e-04f, + 2.085130870089506230e-04f, + 2.062890786472634959e-04f, + 2.038633034664208931e-04f, + 2.012387773306926119e-04f, + 1.984187087511740796e-04f, + 1.954064950962511793e-04f, + 1.922057186209438458e-04f, + 1.888201423195703249e-04f, + 1.852537056065179267e-04f, + 1.815105198299485519e-04f, + 1.775948636234848943e-04f, + 1.735111781010148112e-04f, + 1.692640619000253591e-04f, + 1.648582660788071937e-04f, + 1.602986888732223973e-04f, + 1.555903703187100698e-04f, + 1.507384867433899029e-04f, + 1.457483451382290192e-04f, + 1.406253774102959029e-04f, + 1.353751345253480795e-04f, + 1.300032805459848623e-04f, + 1.245155865716643785e-04f, + 1.189179245871088876e-04f, + 1.132162612255646118e-04f, + 1.074166514535061193e-04f, + 1.015252321834391158e-04f, + 9.554821582143662340e-05f, + 8.949188375630919609e-05f, + 8.336257979698908499e-05f, + 7.716670356510142823e-05f, + 7.091070384951115571e-05f, + 6.460107192972675580e-05f, + 5.824433487497955288e-05f, + 5.184704882602284872e-05f, + 4.541579226631734119e-05f, + 3.895715928963878430e-05f, + 3.247775287090116139e-05f, + 2.598417814704357540e-05f, + 1.948303571479631031e-05f, + 1.298091495202827620e-05f, + 6.484387369576324119e-06f, + 0.000000000000000000e+00f, + -6.465731169933958513e-06f, + -1.290632771657884088e-05f, + -1.931535523998722761e-05f, + -2.568642976144864805e-05f, + -3.201322405145375435e-05f, + -3.828947388208588727e-05f, + -4.450898419751903002e-05f, + -5.066563519664518423e-05f, + -5.675338832188378036e-05f, + -6.276629214833436183e-05f, + -6.869848816762920277e-05f, + -7.454421646074183764e-05f, + -8.029782125450870081e-05f, + -8.595375635638307819e-05f, + -9.150659046230223595e-05f, + -9.695101233261035672e-05f, + -1.022818358312054416e-04f, + -1.074940048230266600e-04f, + -1.125825979255044379e-04f, + -1.175428331093993729e-04f, + -1.223700721448543901e-04f, + -1.270598248885527321e-04f, + -1.316077534080812585e-04f, + -1.360096759398110458e-04f, + -1.402615706766127137e-04f, + -1.443595793822601773e-04f, + -1.483000108290968150e-04f, + -1.520793440561845624e-04f, + -1.556942314450015955e-04f, + -1.591415016101448729e-04f, + -1.624181621026041017e-04f, + -1.655214019234404826e-04f, + -1.684485938457612995e-04f, + -1.711972965433097588e-04f, + -1.737652565239656850e-04f, + -1.761504098667907340e-04f, + -1.783508837614152301e-04f, + -1.803649978487851233e-04f, + -1.821912653624264543e-04f, + -1.838283940697296227e-04f, + -1.852752870127996935e-04f, + -1.865310430487398998e-04f, + -1.875949571893940915e-04f, + -1.884665207407716647e-04f, + -1.891454212426279299e-04f, + -1.896315422087889631e-04f, + -1.899249626691283926e-04f, + -1.900259565141956190e-04f, + -1.899349916437565312e-04f, + -1.896527289206770522e-04f, + -1.891800209317994808e-04f, + -1.885179105576068514e-04f, + -1.876676293527271906e-04f, + -1.866305957394497354e-04f, + -1.854084130166551892e-04f, + -1.840028671867227714e-04f, + -1.824159246031662005e-04f, + -1.806497294418971144e-04f, + -1.787066009992514435e-04f, + -1.765890308199928246e-04f, + -1.742996796587417414e-04f, + -1.718413742784095856e-04f, + -1.692171040893801167e-04f, + -1.664300176333031469e-04f, + -1.634834189156041844e-04f, + -1.603807635908042269e-04f, + -1.571256550050320762e-04f, + -1.537218401001562711e-04f, + -1.501732051841159817e-04f, + -1.464837715721702366e-04f, + -1.426576911038343165e-04f, + -1.386992415405413062e-04f, + -1.346128218489411334e-04f, + -1.304029473750683709e-04f, + -1.260742449145932711e-04f, + -1.216314476844448554e-04f, + -1.170793902013302941e-04f, + -1.124230030725036544e-04f, + -1.076673077044100317e-04f, + -1.028174109349222682e-04f, + -9.787849959470351147e-05f, + -9.285583500357369501e-05f, + -8.775474740762446567e-05f, + -8.258063036294733172e-05f, + -7.733893507179906781e-05f, + -7.203516467725131118e-05f, + -6.667486852207986337e-05f, + -6.126363637798329517e-05f, + -5.580709265104459485e-05f, + -5.031089056941896167e-05f, + -4.478070635923045353e-05f, + -3.922223341459007002e-05f, + -3.364117646783503998e-05f, + -2.804324576573561864e-05f, + -2.243415125772841104e-05f, + -1.681959680200483478e-05f, + -1.120527439532200600e-05f, + -5.596858432283831980e-06f, + 0.000000000000000000e+00f, + 5.579678786350002526e-06f, + 1.113659040131433947e-05f, + 1.666518747469618845e-05f, + 2.215996829939644689e-05f, + 2.761548227605910744e-05f, + 3.302633528924318619e-05f, + 3.838719500969946610e-05f, + 4.369279611777013815e-05f, + 4.893794544267782349e-05f, + 5.411752701278543923e-05f, + 5.922650701192542296e-05f, + 6.425993863709400348e-05f, + 6.921296685271543028e-05f, + 7.408083303713561935e-05f, + 7.885887951679374148e-05f, + 8.354255398385457916e-05f, + 8.812741379313725198e-05f, + 9.260913013432653981e-05f, + 9.698349207564205016e-05f, + 1.012464104751122735e-04f, + 1.053939217560361348e-04f, + 1.094221915430763865e-04f, + 1.133275181557522266e-04f, + 1.171063359561930271e-04f, + 1.207552185482215309e-04f, + 1.242708818248938415e-04f, + 1.276501868619005589e-04f, + 1.308901426543747110e-04f, + 1.339879086947369589e-04f, + 1.369407973894841402e-04f, + 1.397462763129272100e-04f, + 1.424019702960989549e-04f, + 1.449056633492160203e-04f, + 1.472553004161910543e-04f, + 1.494489889600180950e-04f, + 1.514850003778564957e-04f, + 1.533617712449332308e-04f, + 1.550779043865114734e-04f, + 1.566321697773859439e-04f, + 1.580235052684658486e-04f, + 1.592510171403173336e-04f, + 1.603139804835744661e-04f, + 1.612118394063934590e-04f, + 1.619442070692711196e-04f, + 1.625108655477295867e-04f, + 1.629117655235336551e-04f, + 1.631470258052963916e-04f, + 1.632169326794885361e-04f, + 1.631219390930484540e-04f, + 1.628626636689312806e-04f, + 1.624398895561451970e-04f, + 1.618545631159456772e-04f, + 1.611077924460503359e-04f, + 1.602008457448726990e-04f, + 1.591351495179624102e-04f, + 1.579122866289444445e-04f, + 1.565339941974661477e-04f, + 1.550021613467485204e-04f, + 1.533188268035131036e-04f, + 1.514861763532199430e-04f, + 1.495065401536325306e-04f, + 1.473823899099098260e-04f, + 1.451163359145451060e-04f, + 1.427111239555983182e-04f, + 1.401696320967573881e-04f, + 1.374948673329850774e-04f, + 1.346899621254734052e-04f, + 1.317581708198773619e-04f, + 1.287028659518289764e-04f, + 1.255275344438603485e-04f, + 1.222357736979611648e-04f, + 1.188312875880578206e-04f, + 1.153178823568962413e-04f, + 1.116994624217160199e-04f, + 1.079800260933631265e-04f, + 1.041636612134171070e-04f, + 1.002545407141449194e-04f, + 9.625691810593006150e-05f, + 9.217512289710953982e-05f, + 8.801355595103532931e-05f, + 8.377668478539886923e-05f, + 7.946903881866790591e-05f, + 7.509520456875234419e-05f, + 7.065982080891584666e-05f, + 6.616757368602117870e-05f, + 6.162319180617105993e-05f, + 5.703144129297408308e-05f, + 5.239712082341619980e-05f, + 4.772505664658922863e-05f, + 4.302009759036991954e-05f, + 3.828711006119116657e-05f, + 3.353097304204050925e-05f, + 2.875657309374543029e-05f, + 2.396879936476407346e-05f, + 1.917253861437825900e-05f, + 1.437267025445376776e-05f, + 9.574061414716567378e-06f, + 4.781562036525325622e-06f, + 0.000000000000000000e+00f, + -4.765823710467572352e-06f, + -9.511139797770952161e-06f, + -1.423121539568319248e-05f, + -1.892135876000095219e-05f, + -2.357692390252032815e-05f, + -2.819331516332007593e-05f, + -3.276599171692918774e-05f, + -3.729047200786769084e-05f, + -4.176233811142703061e-05f, + -4.617724001535500485e-05f, + -5.053089981838045005e-05f, + -5.481911584154466779e-05f, + -5.903776664846981624e-05f, + -6.318281497064232792e-05f, + -6.725031153416303120e-05f, + -7.123639878426539594e-05f, + -7.513731450417200934e-05f, + -7.894939532493793865e-05f, + -8.266908012303817257e-05f, + -8.629291330264409008e-05f, + -8.981754795950380526e-05f, + -9.323974892372517958e-05f, + -9.655639567864357899e-05f, + -9.976448515324246556e-05f, + -1.028611343856743177e-04f, + -1.058435830556215613e-04f, + -1.087091958832444314e-04f, + -1.114554648928591494e-04f, + -1.140800115393030879e-04f, + -1.165805886954139077e-04f, + -1.189550824989598954e-04f, + -1.212015140576370412e-04f, + -1.233180410108325074e-04f, + -1.253029589470822374e-04f, + -1.271547026761611632e-04f, + -1.288718473550913872e-04f, + -1.304531094673266076e-04f, + -1.318973476546234471e-04f, + -1.332035634012388348e-04f, + -1.343709015702383085e-04f, + -1.353986507918701350e-04f, + -1.362862437040613826e-04f, + -1.370332570453443202e-04f, + -1.376394116005415203e-04f, + -1.381045719998066825e-04f, + -1.384287463716805618e-04f, + -1.386120858510235900e-04f, + -1.386548839427931658e-04f, + -1.385575757428196932e-04f, + -1.383207370168273054e-04f, + -1.379450831391416433e-04f, + -1.374314678926119766e-04f, + -1.367808821314652607e-04f, + -1.359944523088843157e-04f, + -1.350734388713058730e-04f, + -1.340192345214945692e-04f, + -1.328333623526271043e-04f, + -1.315174738557150504e-04f, + -1.300733468028466666e-04f, + -1.285028830087897415e-04f, + -1.268081059737171702e-04f, + -1.249911584097946639e-04f, + -1.230542996545981637e-04f, + -1.209999029743675174e-04f, + -1.188304527602330177e-04f, + -1.165485416206351921e-04f, + -1.141568673732511950e-04f, + -1.116582299398947146e-04f, + -1.090555281478102406e-04f, + -1.063517564410224357e-04f, + -1.035500015053626303e-04f, + -1.006534388109464010e-04f, + -9.766532907587492868e-05f, + -9.458901465512266783e-05f, + -9.142791585846299308e-05f, + -8.818552720148400341e-05f, + -8.486541359380171191e-05f, + -8.147120646848431092e-05f, + -7.800659985692548243e-05f, + -7.447534641334087790e-05f, + -7.088125339309563760e-05f, + -6.722817858922308187e-05f, + -6.352002623131395697e-05f, + -5.976074285119170978e-05f, + -5.595431311967590562e-05f, + -5.210475565880340852e-05f, + -4.821611883387434797e-05f, + -4.429247652966449879e-05f, + -4.033792391527355533e-05f, + -3.635657320185136960e-05f, + -3.235254939766243925e-05f, + -2.832998606480489144e-05f, + -2.429302108193049522e-05f, + -2.024579241723850257e-05f, + -1.619243391613931063e-05f, + -1.213707110770494373e-05f, + -8.083817034238642389e-06f, + -4.036768108108601457e-06f, + 0.000000000000000000e+00f, + 4.022436437324076239e-06f, + 8.026519205701394352e-06f, + 1.200825915466515436e-05f, + 1.596370392708562708e-05f, + 1.988894185342372352e-05f, + 2.378010579081847397e-05f, + 2.763337690325799793e-05f, + 3.144498837921355557e-05f, + 3.521122908304340009e-05f, + 3.892844713680019853e-05f, + 4.259305342891470467e-05f, + 4.620152504646010591e-05f, + 4.975040862773826440e-05f, + 5.323632363207353587e-05f, + 5.665596552365893460e-05f, + 6.000610886662674704e-05f, + 6.328361032838766603e-05f, + 6.648541158852724214e-05f, + 6.960854215060292317e-05f, + 7.265012205430279883e-05f, + 7.560736448556652138e-05f, + 7.847757828228031152e-05f, + 8.125817033344999527e-05f, + 8.394664786969736913e-05f, + 8.654062064313692193e-05f, + 8.903780299482053388e-05f, + 9.143601580795497152e-05f, + 9.373318834539185970e-05f, + 9.592735996986403448e-05f, + 9.801668174559524367e-05f, + 9.999941792013674572e-05f, + 1.018739472852775956e-04f, + 1.036387644160876654e-04f, + 1.052924807872517206e-04f, + 1.068338257660048085e-04f, + 1.082616474810422510e-04f, + 1.095749135670224514e-04f, + 1.107727117842931974e-04f, + 1.118542505136681491e-04f, + 1.128188591261970936e-04f, + 1.136659882279879026e-04f, + 1.143952097802875201e-04f, + 1.150062170951232128e-04f, + 1.154988247069874726e-04f, + 1.158729681211121310e-04f, + 1.161287034390441527e-04f, + 1.162662068623534605e-04f, + 1.162857740754014629e-04f, + 1.161878195082552873e-04f, + 1.159728754809229666e-04f, + 1.156415912302182657e-04f, + 1.151947318206731616e-04f, + 1.146331769410351999e-04f, + 1.139579195879890020e-04f, + 1.131700646388664324e-04f, + 1.122708273152050117e-04f, + 1.112615315391279159e-04f, + 1.101436081846065776e-04f, + 1.089185932257956109e-04f, + 1.075881257847022211e-04f, + 1.061539460805467204e-04f, + 1.046178932833044002e-04f, + 1.029819032739389583e-04f, + 1.012480063139884254e-04f, + 9.941832462721362205e-05f, + 9.749506989611064225e-05f, + 9.548054067616838824e-05f, + 9.337711973079915206e-05f, + 9.118727129001866780e-05f, + 8.891353823589849862e-05f, + 8.655853921798483167e-05f, + 8.412496570189726970e-05f, + 8.161557895436345137e-05f, + 7.903320696801201077e-05f, + 7.638074132934370719e-05f, + 7.366113403320848053e-05f, + 7.087739424737539320e-05f, + 6.803258503057929422e-05f, + 6.512982000765911157e-05f, + 6.217226000532499622e-05f, + 5.916310965216279986e-05f, + 5.610561394646146512e-05f, + 5.300305479558382887e-05f, + 4.985874753042966284e-05f, + 4.667603739873827257e-05f, + 4.345829604087496334e-05f, + 4.020891795178530215e-05f, + 3.693131693280893166e-05f, + 3.362892253699031918e-05f, + 3.030517651164982165e-05f, + 2.696352924175526110e-05f, + 2.360743619783213642e-05f, + 2.024035439200125525e-05f, + 1.686573884576587145e-05f, + 1.348703907309045250e-05f, + 1.010769558241483277e-05f, + 6.731136401002770680e-06f, + 3.360773625198592391e-06f, + 0.000000000000000000e+00f, + -3.347814468650473784e-06f, + -6.679325865479849946e-06f, + -9.991219678161110501e-06f, + -1.328021406688385974e-05f, + -1.654306308815880257e-05f, + -1.977655986962290348e-05f, + -2.297753973280750991e-05f, + -2.614288326084785529e-05f, + -2.926951930822502027e-05f, + -3.235442794958472064e-05f, + -3.539464336494777658e-05f, + -3.838725665850617859e-05f, + -4.132941860839394398e-05f, + -4.421834234486570842e-05f, + -4.705130595439784442e-05f, + -4.982565500735158521e-05f, + -5.253880500681173683e-05f, + -5.518824375649152762e-05f, + -5.777153364550003561e-05f, + -6.028631384797333815e-05f, + -6.273030243562301752e-05f, + -6.510129840139457593e-05f, + -6.739718359242333320e-05f, + -6.961592455074339493e-05f, + -7.175557426013427060e-05f, + -7.381427379772556206e-05f, + -7.579025388895909592e-05f, + -7.768183636476383978e-05f, + -7.948743551975573677e-05f, + -8.120555937048918389e-05f, + -8.283481081280139394e-05f, + -8.437388867751212612e-05f, + -8.582158868373181084e-05f, + -8.717680428922131703e-05f, + -8.843852743731167663e-05f, + -8.960584920002047696e-05f, + -9.067796031711479563e-05f, + -9.165415163093462887e-05f, + -9.253381441698060807e-05f, + -9.331644061029057509e-05f, + -9.400162292779464348e-05f, + -9.458905488691879318e-05f, + -9.507853072082816524e-05f, + -9.546994519078202349e-05f, + -9.576329329621814776e-05f, + -9.595866988323466294e-05f, + -9.605626915229089865e-05f, + -9.605638406600895262e-05f, + -9.595940565808516048e-05f, + -9.576582224440011919e-05f, + -9.547621853752977987e-05f, + -9.509127466593055836e-05f, + -9.461176509920040452e-05f, + -9.403855748086969134e-05f, + -9.337261137029724230e-05f, + -9.261497689531161055e-05f, + -9.176679331735186228e-05f, + -9.082928751089830238e-05f, + -8.980377235912744038e-05f, + -8.869164506774372268e-05f, + -8.749438539906182540e-05f, + -8.621355382846783715e-05f, + -8.485078962544303713e-05f, + -8.340780886146616484e-05f, + -8.188640234708234958e-05f, + -8.028843350057289605e-05f, + -7.861583615068542543e-05f, + -7.687061227593963936e-05f, + -7.505482968307974337e-05f, + -7.317061962734104652e-05f, + -7.122017437715785402e-05f, + -6.920574472607888617e-05f, + -6.712963745464778354e-05f, + -6.499421274506274500e-05f, + -6.280188155146197997e-05f, + -6.055510292871261671e-05f, + -5.825638132264141731e-05f, + -5.590826382463881251e-05f, + -5.351333739358024485e-05f, + -5.107422604809699472e-05f, + -4.859358803218347312e-05f, + -4.607411295717206477e-05f, + -4.351851892309470621e-05f, + -4.092954962253340614e-05f, + -3.830997142993496893e-05f, + -3.566257047950555911e-05f, + -3.299014973471602795e-05f, + -3.029552605247514392e-05f, + -2.758152724501864632e-05f, + -2.485098914252425363e-05f, + -2.210675265954265012e-05f, + -1.935166086816063607e-05f, + -1.658855608095106957e-05f, + -1.382027694664882434e-05f, + -1.104965556150336744e-05f, + -8.279514599188903814e-06f, + -5.512664462229372086e-06f, + -2.751900457687248369e-06f, + 0.000000000000000000e+00f, + 2.740280156290214512e-06f, + 5.466206651186378539e-06f, + 8.175072218564196470e-06f, + 1.086419834758086399e-05f, + 1.353093790349696553e-05f, + 1.617267770547626131e-05f, + 1.878684105880902989e-05f, + 2.137089023916455868e-05f, + 2.392232892650095790e-05f, + 2.643870458635503993e-05f, + 2.891761079620682311e-05f, + 3.135668951483809152e-05f, + 3.375363329250938944e-05f, + 3.610618741995035084e-05f, + 3.841215201419369719e-05f, + 4.066938403936110963e-05f, + 4.287579926060773578e-05f, + 4.502937412942688007e-05f, + 4.712814759873801425e-05f, + 4.917022286611355612e-05f, + 5.115376904367083672e-05f, + 5.307702275320218851e-05f, + 5.493828964523271308e-05f, + 5.673594584069856022e-05f, + 5.846843929414990829e-05f, + 6.013429107736878215e-05f, + 6.173209658237591092e-05f, + 6.326052664298879500e-05f, + 6.471832857406342385e-05f, + 6.610432712770806260e-05f, + 6.741742536584356324e-05f, + 6.865660544852231900e-05f, + 6.982092933759627743e-05f, + 7.090953941532209665e-05f, + 7.192165901764468636e-05f, + 7.285659288195965404e-05f, + 7.371372750925019328e-05f, + 7.449253144060441825e-05f, + 7.519255544814984439e-05f, + 7.581343264061586552e-05f, + 7.635487848374452387e-05f, + 7.681669073589902474e-05f, + 7.719874929929503027e-05f, + 7.750101598737158502e-05f, + 7.772353420888149114e-05f, + 7.786642856940284227e-05f, + 7.792990439101760668e-05f, + 7.791424715101048608e-05f, + 7.781982184050777879e-05f, + 7.764707224406404028e-05f, + 7.739652014127399414e-05f, + 7.706876443157189042e-05f, + 7.666448018344928735e-05f, + 7.618441760939247533e-05f, + 7.562940096792763350e-05f, + 7.500032739420971781e-05f, + 7.429816566067238063e-05f, + 7.352395486933000228e-05f, + 7.267880307736313862e-05f, + 7.176388585770229430e-05f, + 7.078044479637946680e-05f, + 6.972978592846818902e-05f, + 6.861327811449984264e-05f, + 6.743235135927742136e-05f, + 6.618849507510502698e-05f, + 6.488325629143087644e-05f, + 6.351823781301141390e-05f, + 6.209509632871848778e-05f, + 6.061554047316198814e-05f, + 5.908132884331902243e-05f, + 5.749426797245979628e-05f, + 5.585621026359582951e-05f, + 5.416905188480572637e-05f, + 5.243473062876452025e-05f, + 5.065522373885282526e-05f, + 4.883254570422391761e-05f, + 4.696874602629149840e-05f, + 4.506590695899598622e-05f, + 4.312614122539617790e-05f, + 4.115158971296881845e-05f, + 3.914441915014531378e-05f, + 3.710681976655663884e-05f, + 3.504100293947658921e-05f, + 3.294919882896850676e-05f, + 3.083365400421063813e-05f, + 2.869662906354593466e-05f, + 2.654039625068195532e-05f, + 2.436723706957190895e-05f, + 2.217943990043698863e-05f, + 1.997929761939633796e-05f, + 1.776910522413144273e-05f, + 1.555115746807415747e-05f, + 1.332774650545313544e-05f, + 1.110115954964658368e-05f, + 8.873676547184104307e-06f, + 6.647567869740949601e-06f, + 4.425092026438055660e-06f, + 2.208493398703637141e-06f, + 0.000000000000000000e+00f, + -2.198178737447286155e-06f, + -4.383854146707416475e-06f, + -6.554860478846427014e-06f, + -8.709057022211050465e-06f, + -1.084433018625070958e-05f, + -1.295859554782643898e-05f, + -1.504979985813177589e-05f, + -1.711592300826502171e-05f, + -1.915497995162542054e-05f, + -2.116502258133142226e-05f, + -2.314414156091539193e-05f, + -2.509046810662869737e-05f, + -2.700217571967648383e-05f, + -2.887748186688327340e-05f, + -3.071464960822724050e-05f, + -3.251198916982059171e-05f, + -3.426785946094130863e-05f, + -3.598066953382340031e-05f, + -3.764887998489755495e-05f, + -3.927100429636594712e-05f, + -4.084561011694002843e-05f, + -4.237132048071104532e-05f, + -4.384681496316908579e-05f, + -4.527083077345597480e-05f, + -4.664216378202475170e-05f, + -4.795966948291004048e-05f, + -4.922226388992814933e-05f, + -5.042892436618977834e-05f, + -5.157869038634893284e-05f, + -5.267066423112145597e-05f, + -5.370401161365963874e-05f, + -5.467796223744639814e-05f, + -5.559181028545831532e-05f, + -5.644491484037703408e-05f, + -5.723670023577102494e-05f, + -5.796665633817722128e-05f, + -5.863433876011653090e-05f, + -5.923936900414046228e-05f, + -5.978143453809256339e-05f, + -6.026028880180132370e-05f, + -6.067575114554413853e-05f, + -6.102770670064889110e-05f, + -6.131610618268607763e-05f, + -6.154096562777286157e-05f, + -6.170236606257552899e-05f, + -6.180045310866599611e-05f, + -6.183543652194144397e-05f, + -6.180758966790510722e-05f, + -6.171724893364212428e-05f, + -6.156481307740533954e-05f, + -6.135074251677746099e-05f, + -6.107555855644340075e-05f, + -6.073984255665435130e-05f, + -6.034423504353361045e-05f, + -5.988943476241735355e-05f, + -5.937619767549151724e-05f, + -5.880533590502611506e-05f, + -5.817771662356323390e-05f, + -5.749426089246688652e-05f, + -5.675594245028995661e-05f, + -5.596378645244941432e-05f, + -5.511886816375996041e-05f, + -5.422231160540798414e-05f, + -5.327528815799189400e-05f, + -5.227901512228311736e-05f, + -5.123475423943163698e-05f, + -5.014381017231689294e-05f, + -4.900752894983425071e-05f, + -4.782729637590838818e-05f, + -4.660453640505390237e-05f, + -4.534070948633992719e-05f, + -4.403731087762476868e-05f, + -4.269586893198487468e-05f, + -4.131794335821765057e-05f, + -3.990512345738859756e-05f, + -3.845902633734535553e-05f, + -3.698129510720333325e-05f, + -3.547359705373742065e-05f, + -3.393762180168319212e-05f, + -3.237507945996893324e-05f, + -3.078769875582971604e-05f, + -2.917722515883962726e-05f, + -2.754541899685784069e-05f, + -2.589405356588684348e-05f, + -2.422491323584912912e-05f, + -2.253979155425681131e-05f, + -2.084048934980605179e-05f, + -1.912881283781196876e-05f, + -1.740657172949882254e-05f, + -1.567557734707370159e-05f, + -1.393764074653252662e-05f, + -1.219457085009355482e-05f, + -1.044817259020866579e-05f, + -8.700245066967668165e-06f, + -6.952579720797126885e-06f, + -5.206958522265940783e-06f, + -3.465152180804485261e-06f, + -1.728918374115329284e-06f, + 0.000000000000000000e+00f, + 1.719876547628855035e-06f, + 3.429003076962415618e-06f, + 5.125691268998652680e-06f, + 6.808274319193801364e-06f, + 8.475108548458095382e-06f, + 1.012457498199488445e-05f, + 1.175508089444992680e-05f, + 1.336506131998390598e-05f, + 1.495298052582536945e-05f, + 1.651733344796259886e-05f, + 1.805664708766448577e-05f, + 1.956948186756530129e-05f, + 2.105443294611633769e-05f, + 2.251013148919803211e-05f, + 2.393524589783997623e-05f, + 2.532848299093816370e-05f, + 2.668858914198573803e-05f, + 2.801435136885023771e-05f, + 2.930459837571368657e-05f, + 3.055820154629149815e-05f, + 3.177407588758812874e-05f, + 3.295118092341596186e-05f, + 3.408852153701416018e-05f, + 3.518514876214479022e-05f, + 3.624016052209482158e-05f, + 3.725270231609142734e-05f, + 3.822196785265903228e-05f, + 3.914719962953973575e-05f, + 4.002768945985303464e-05f, + 4.086277894419950948e-05f, + 4.165185988850534184e-05f, + 4.239437466744202404e-05f, + 4.308981653332471270e-05f, + 4.373772987044972097e-05f, + 4.433771039486739487e-05f, + 4.488940529969052062e-05f, + 4.539251334604474478e-05f, + 4.584678489985638750e-05f, + 4.625202191471458748e-05f, + 4.660807786111483166e-05f, + 4.691485760241596739e-05f, + 4.717231721794196576e-05f, + 4.738046377366677880e-05f, + 4.753935504100859647e-05f, + 4.764909916429116248e-05f, + 4.770985427749255010e-05f, + 4.772182807094424499e-05f, + 4.768527730869501094e-05f, + 4.760050729730926094e-05f, + 4.746787130690479115e-05f, + 4.728776994528908763e-05f, + 4.706065048609898603e-05f, + 4.678700615188301687e-05f, + 4.646737535312531160e-05f, + 4.610234088422814617e-05f, + 4.569252907753687694e-05f, + 4.523860891650402077e-05f, + 4.474129110914775088e-05f, + 4.420132712297902665e-05f, + 4.361950818261826370e-05f, + 4.299666423135587823e-05f, + 4.233366285792369062e-05f, + 4.163140818980473975e-05f, + 4.089083975440875428e-05f, + 4.011293130948711432e-05f, + 3.929868964417050821e-05f, + 3.844915335206358214e-05f, + 3.756539157780807174e-05f, + 3.664850273859330111e-05f, + 3.569961322208241148e-05f, + 3.471987606224959691e-05f, + 3.371046959463910820e-05f, + 3.267259609256267485e-05f, + 3.160748038579001628e-05f, + 3.051636846325336133e-05f, + 2.940052606132795206e-05f, + 2.826123723927484845e-05f, + 2.709980294337047645e-05f, + 2.591753956132967314e-05f, + 2.471577746857528420e-05f, + 2.349585956792650177e-05f, + 2.225913982430233876e-05f, + 2.100698179596996784e-05f, + 1.974075716392971998e-05f, + 1.846184426098673837e-05f, + 1.717162660205524496e-05f, + 1.587149141723013810e-05f, + 1.456282818918089089e-05f, + 1.324702719634344427e-05f, + 1.192547806344194722e-05f, + 1.059956832081574480e-05f, + 9.270681974018148597e-06f, + 7.940198085143243810e-06f, + 6.609489367292647408e-06f, + 5.279920793626897963e-06f, + 3.952848222338136709e-06f, + 2.629617038940430149e-06f, + 1.311560817197689786e-06f, + 0.000000000000000000e+00f, + -1.303759398535709597e-06f, + -2.598427068487143802e-06f, + -3.882729655845572457e-06f, + -5.155411989361711008e-06f, + -6.415238281346936499e-06f, + -7.660993301316896363e-06f, + -8.891483521387926858e-06f, + -1.010553823239075562e-05f, + -1.130201062965311224e-05f, + -1.247977886752223057e-05f, + -1.363774708165704589e-05f, + -1.477484637820374348e-05f, + -1.589003578899699857e-05f, + -1.698230319197812681e-05f, + -1.805066619602823882e-05f, + -1.909417298952157264e-05f, + -2.011190315188001194e-05f, + -2.110296842749134194e-05f, + -2.206651346138940222e-05f, + -2.300171649612773008e-05f, + -2.390779002935145109e-05f, + -2.478398143156065395e-05f, + -2.562957352368102888e-05f, + -2.644388511403268635e-05f, + -2.722627149437084187e-05f, + -2.797612489471199853e-05f, + -2.869287489668749573e-05f, + -2.937598880525030119e-05f, + -3.002497197854884801e-05f, + -3.063936811588928184e-05f, + -3.121875950370617492e-05f, + -3.176276721952791073e-05f, + -3.227105129396500970e-05f, + -3.274331083079011072e-05f, + -3.317928408523108317e-05f, + -3.357874850062376207e-05f, + -3.394152070364435874e-05f, + -3.426745645835545690e-05f, + -3.455645057935714278e-05f, + -3.480843680437391041e-05f, + -3.502338762665158155e-05f, + -3.520131408756888464e-05f, + -3.534226552992613248e-05f, + -3.544632931239619902e-05f, + -3.551363048567052514e-05f, + -3.554433143086765164e-05f, + -3.553863146080914930e-05f, + -3.549676638480726685e-05f, + -3.541900803763526229e-05f, + -3.530566377340024634e-05f, + -3.515707592505213643e-05f, + -3.497362123031461193e-05f, + -3.475571022484123395e-05f, + -3.450378660343666126e-05f, + -3.421832655021186221e-05f, + -3.389983803856685431e-05f, + -3.354886010192489469e-05f, + -3.316596207616956312e-05f, + -3.275174281475231333e-05f, + -3.230682987747619881e-05f, + -3.183187869396395209e-05f, + -3.132757170286612567e-05f, + -3.079461746785293406e-05f, + -3.023374977148131389e-05f, + -2.964572668803002056e-05f, + -2.903132963641206598e-05f, + -2.839136241429471749e-05f, + -2.772665021457329989e-05f, + -2.703803862533824987e-05f, + -2.632639261451207689e-05f, + -2.559259550032325886e-05f, + -2.483754790880125541e-05f, + -2.406216671948075119e-05f, + -2.326738400050288322e-05f, + -2.245414593433224767e-05f, + -2.162341173525594566e-05f, + -2.077615255991108366e-05f, + -1.991335041200381325e-05f, + -1.903599704245014085e-05f, + -1.814509284612314400e-05f, + -1.724164575641496121e-05f, + -1.632667013878813598e-05f, + -1.540118568453151252e-05f, + -1.446621630586251739e-05f, + -1.352278903357414166e-05f, + -1.257193291837421751e-05f, + -1.161467793707110403e-05f, + -1.065205390473139910e-05f, + -9.685089393960801953e-06f, + -8.714810662380517595e-06f, + -7.742240589422758301e-06f, + -6.768397623508250075e-06f, + -5.794294740669309052e-06f, + -4.820938415660515435e-06f, + -3.849327606567640099e-06f, + -2.880452753939940837e-06f, + -1.915294795392051711e-06f, + -9.548241966549667941e-07f, + 0.000000000000000000e+00f, + 9.482311090557935979e-07f, + 1.888935715883699196e-06f, + 2.821194570845099363e-06f, + 3.744103462554891212e-06f, + 4.656774069485003328e-06f, + 5.558334789141776895e-06f, + 6.447931544071930670e-06f, + 7.324728563978056132e-06f, + 8.187909143270469283e-06f, + 9.036676373374730734e-06f, + 9.870253849204916702e-06f, + 1.068788634918657437e-05f, + 1.148884048828036910e-05f, + 1.227240534347507200e-05f, + 1.303789305126774333e-05f, + 1.378463937664554480e-05f, + 1.451200425317579174e-05f, + 1.521937229378508572e-05f, + 1.590615327188085737e-05f, + 1.657178257248893217e-05f, + 1.721572161311536899e-05f, + 1.783745823408358416e-05f, + 1.843650705811206880e-05f, + 1.901240981896380926e-05f, + 1.956473565900515327e-05f, + 2.009308139556312985e-05f, + 2.059707175598756487e-05f, + 2.107635958138817672e-05f, + 2.153062599901698230e-05f, + 2.195958056332863746e-05f, + 2.236296136575796357e-05f, + 2.274053511332031568e-05f, + 2.309209717614148783e-05f, + 2.341747160407918292e-05f, + 2.371651111261625155e-05f, + 2.398909703825292155e-05f, + 2.423513926363566866e-05f, + 2.445457611271804443e-05f, + 2.464737421625526288e-05f, + 2.481352834797943618e-05f, + 2.495306123182613461e-05f, + 2.506602332061461824e-05f, + 2.515249254661017629e-05f, + 2.521257404442358010e-05f, + 2.524639984673718367e-05f, + 2.525412855336374039e-05f, + 2.523594497417534085e-05f, + 2.519205974646584022e-05f, + 2.512270892732973375e-05f, + 2.502815356166727978e-05f, + 2.490867922644573402e-05f, + 2.476459555187455187e-05f, + 2.459623572016065511e-05f, + 2.440395594254615678e-05f, + 2.418813491533502720e-05f, + 2.394917325564173359e-05f, + 2.368749291761338170e-05f, + 2.340353658988623544e-05f, + 2.309776707505748077e-05f, + 2.277066665197028110e-05f, + 2.242273642161569420e-05f, + 2.205449563747421783e-05f, + 2.166648102113158283e-05f, + 2.125924606400766132e-05f, + 2.083336031605244481e-05f, + 2.038940866226966761e-05f, + 1.992799058793915062e-05f, + 1.944971943340819870e-05f, + 1.895522163933315886e-05f, + 1.844513598326477953e-05f, + 1.792011280845339264e-05f, + 1.738081324577142443e-05f, + 1.682790842964813208e-05f, + 1.626207870890487679e-05f, + 1.568401285338399734e-05f, + 1.509440725727218157e-05f, + 1.449396513998789486e-05f, + 1.388339574554209775e-05f, + 1.326341354123314987e-05f, + 1.263473741656307925e-05f, + 1.199808988324299363e-05f, + 1.135419627714907315e-05f, + 1.070378396308057514e-05f, + 1.004758154317770732e-05f, + 9.386318069818621378e-06f, + 8.720722263832011000e-06f, + 8.051521738837318949e-06f, + 7.379442232510721810e-06f, + 6.705206845569983647e-06f, + 6.029535289243417829e-06f, + 5.353143142001254931e-06f, + 4.676741116269359137e-06f, + 4.001034335873452788e-06f, + 3.326721624913985331e-06f, + 2.654494808772660456e-06f, + 1.985038027915242428e-06f, + 1.319027065167622758e-06f, + 6.571286870779092537e-07f, + 0.000000000000000000e+00f, + -6.517121785112947791e-07f, + -1.297371932417118936e-06f, + -1.936354843466996782e-06f, + -2.568048570502482838e-06f, + -3.191853411264083243e-06f, + -3.807182846237992518e-06f, + -4.413464064061555844e-06f, + -5.010138468054313338e-06f, + -5.596662163456622115e-06f, + -6.172506424989349086e-06f, + -6.737158144353800846e-06f, + -7.290120257347119171e-06f, + -7.830912150263285036e-06f, + -8.359070045291263937e-06f, + -8.874147364643450859e-06f, + -9.375715073171476542e-06f, + -9.863361999259346438e-06f, + -1.033669513378841540e-05f, + -1.079533990703045404e-05f, + -1.123894044331291381e-05f, + -1.166715979335118242e-05f, + -1.207968014415581310e-05f, + -1.247620300645970784e-05f, + -1.285644937961374137e-05f, + -1.322015989395755990e-05f, + -1.356709493066716393e-05f, + -1.389703471911742539e-05f, + -1.420977941183009213e-05f, + -1.450514913708843372e-05f, + -1.478298402933197354e-05f, + -1.504314423747151812e-05f, + -1.528550991127199633e-05f, + -1.550998116600103277e-05f, + -1.571647802553678724e-05f, + -1.590494034416860253e-05f, + -1.607532770733858182e-05f, + -1.622761931159687079e-05f, + -1.636181382406056196e-05f, + -1.647792922169452577e-05f, + -1.657600261074252277e-05f, + -1.665609002666740320e-05f, + -1.671826621496820962e-05f, + -1.676262439326730422e-05f, + -1.678927599507833507e-05f, + -1.679835039567758540e-05f, + -1.678999462052671192e-05f, + -1.676437303670283319e-05f, + -1.672166702781419629e-05f, + -1.666207465288855371e-05f, + -1.658581028974159263e-05f, + -1.649310426334120225e-05f, + -1.638420245970092517e-05f, + -1.625936592584267462e-05f, + -1.611887045638726128e-05f, + -1.596300616733423785e-05f, + -1.579207705761010350e-05f, + -1.560640055896726747e-05f, + -1.540630707483150961e-05f, + -1.519213950869398463e-05f, + -1.496425278266009707e-05f, + -1.472301334677091966e-05f, + -1.446879867971653761e-05f, + -1.420199678156508936e-05f, + -1.392300565914552601e-05f, + -1.363223280470680123e-05f, + -1.333009466849801069e-05f, + -1.301701612590346651e-05f, + -1.269342993977575572e-05f, + -1.235977621860146935e-05f, + -1.201650187114281883e-05f, + -1.166406005819747346e-05f, + -1.130290964210320150e-05f, + -1.093351463463301477e-05f, + -1.055634364390535124e-05f, + -1.017186932094184607e-05f, + -9.780567806489146604e-06f, + -9.382918178734033738e-06f, + -8.979401902509168731e-06f, + -8.570502280611855256e-06f, + -8.156703907820469213e-06f, + -7.738492128206995299e-06f, + -7.316352496326938200e-06f, + -6.890770242859669408e-06f, + -6.462229745260318158e-06f, + -6.031214003986479169e-06f, + -5.598204124829813786e-06f, + -5.163678807894833656e-06f, + -4.728113843739430985e-06f, + -4.291981617188459184e-06f, + -3.855750619313580188e-06f, + -3.419884968058363287e-06f, + -2.984843937987475483e-06f, + -2.551081499598767520e-06f, + -2.119045868650204413e-06f, + -1.689179065920102918e-06f, + -1.261916487812816974e-06f, + -8.376864881983608732e-07f, + -4.169099718748218678e-07f, + 0.000000000000000000e+00f, + 4.126385921509365552e-07f, + 8.206095647815305072e-07f, + 1.223525630207122030e-06f, + 1.621008796236126059e-06f, + 2.012690696024970467e-06f, + 2.398212904138698659e-06f, + 2.777227238591446030e-06f, + 3.149396048634148933e-06f, + 3.514392488089205312e-06f, + 3.871900774045785298e-06f, + 4.221616430751789221e-06f, + 4.563246518543961347e-06f, + 4.896509847698758431e-06f, + 5.221137177081410397e-06f, + 5.536871397505274410e-06f, + 5.843467699721675325e-06f, + 6.140693726988073073e-06f, + 6.428329712173628339e-06f, + 6.706168599377329854e-06f, + 6.974016150064908968e-06f, + 7.231691033732799246e-06f, + 7.479024903134298036e-06f, + 7.715862454119090782e-06f, + 7.942061470155300043e-06f, + 8.157492851613173758e-06f, + 8.362040629923898268e-06f, + 8.555601966717204414e-06f, + 8.738087138086968224e-06f, + 8.909419504128689090e-06f, + 9.069535463915167161e-06f, + 9.218384396096766158e-06f, + 9.355928585322825753e-06f, + 9.482143134692021052e-06f, + 9.597015864467235791e-06f, + 9.700547197287537744e-06f, + 9.792750030137629163e-06f, + 9.873649593339112619e-06f, + 9.943283296844531363e-06f, + 1.000170056413215178e-05f, + 1.004896265399734296e-05f, + 1.008514247056514156e-05f, + 1.011032436185004009e-05f, + 1.012460390719635526e-05f, + 1.012808769395258744e-05f, + 1.012089308373725277e-05f, + 1.010314796865800678e-05f, + 1.007499051786728413e-05f, + 1.003656891483040004e-05f, + 9.988041085702172294e-06f, + 9.929574419209254656e-06f, + 9.861345478440398626e-06f, + 9.783539704959164481e-06f, + 9.696351115651413914e-06f, + 9.599981992730900805e-06f, + 9.494642567327615031e-06f, + 9.380550697087179814e-06f, + 9.257931538216254041e-06f, + 9.127017212404176378e-06f, + 8.988046469065492662e-06f, + 8.841264343334779069e-06f, + 8.686921810258770176e-06f, + 8.525275435626184639e-06f, + 8.356587023873447459e-06f, + 8.181123263510028723e-06f, + 7.999155370499712271e-06f, + 7.810958730038508927e-06f, + 7.616812537163261916e-06f, + 7.416999436624914182e-06f, + 7.211805162456990385e-06f, + 7.001518177666605594e-06f, + 6.786429314468350961e-06f, + 6.566831415485732412e-06f, + 6.343018976325844570e-06f, + 6.115287789940895410e-06f, + 5.883934593178742717e-06f, + 5.649256715913496299e-06f, + 5.411551733154688679e-06f, + 5.171117120509288493e-06f, + 4.928249913373739521e-06f, + 4.683246370226741163e-06f, + 4.436401640376830997e-06f, + 4.188009436517276560e-06f, + 3.938361712429462095e-06f, + 3.687748346167128183e-06f, + 3.436456829041018457e-06f, + 3.184771960722456336e-06f, + 2.932975550760869950e-06f, + 2.681346126811560784e-06f, + 2.430158649854029301e-06f, + 2.179684236669581299e-06f, + 1.930189889838635696e-06f, + 1.681938235501949860e-06f, + 1.435187269127313838e-06f, + 1.190190109498106053e-06f, + 9.471947611406282867e-07f, + 7.064438853882597620e-07f, + 4.681745802695258113e-07f, + 2.326181693934783062e-07f, + 0.000000000000000000e+00f, + -2.294607496822518279e-07f, + -4.555512536303946545e-07f, + -6.780652116471304469e-07f, + -8.968030132995199105e-07f, + -1.111571892074599649e-06f, + -1.322186069669318649e-06f, + -1.528466890336115189e-06f, + -1.730242945231773840e-06f, + -1.927350186716386900e-06f, + -2.119632032570274826e-06f, + -2.306939460105835373e-06f, + -2.489131090168029246e-06f, + -2.666073261019773634e-06f, + -2.837640092136482743e-06f, + -3.003713537930782497e-06f, + -3.164183431450152543e-06f, + -3.318947518098628411e-06f, + -3.467911479445268386e-06f, + -3.610988947198002060e-06f, + -3.748101507421474508e-06f, + -3.879178695106657928e-06f, + -4.004157979193569750e-06f, + -4.122984738168565981e-06f, + -4.235612226366478793e-06f, + -4.342001531119113841e-06f, + -4.442121520894592501e-06f, + -4.535948784595755533e-06f, + -4.623467562178467021e-06f, + -4.704669666773933905e-06f, + -4.779554398499982279e-06f, + -4.848128450155944567e-06f, + -4.910405805006164935e-06f, + -4.966407626865209352e-06f, + -5.016162142699305776e-06f, + -5.059704517976509766e-06f, + -5.097076724993110270e-06f, + -5.128327404419833975e-06f, + -5.153511720311018673e-06f, + -5.172691208831328023e-06f, + -5.185933620952477709e-06f, + -5.193312759386582571e-06f, + -5.194908310018822646e-06f, + -5.190805668113285727e-06f, + -5.181095759564734449e-06f, + -5.165874857474497392e-06f, + -5.145244394333831128e-06f, + -5.119310770095809750e-06f, + -5.088185156423761042e-06f, + -5.051983297406574915e-06f, + -5.010825307025710792e-06f, + -4.964835463670194371e-06f, + -4.914142001988321737e-06f, + -4.858876902370465571e-06f, + -4.799175678352467995e-06f, + -4.735177162236423786e-06f, + -4.667023289215640662e-06f, + -4.594858880296614592e-06f, + -4.518831424306328092e-06f, + -4.439090859272008081e-06f, + -4.355789353459791187e-06f, + -4.269081086352130785e-06f, + -4.179122029845473253e-06f, + -4.086069729946137843e-06f, + -3.990083089234567233e-06f, + -3.891322150369041671e-06f, + -3.789947880894861469e-06f, + -3.686121959618365274e-06f, + -3.580006564802584169e-06f, + -3.471764164435954700e-06f, + -3.361557308819947231e-06f, + -3.249548425715061173e-06f, + -3.135899618280506402e-06f, + -3.020772466037677946e-06f, + -2.904327829076243898e-06f, + -2.786725655720860965e-06f, + -2.668124793866750565e-06f, + -2.548682806187309839e-06f, + -2.428555789406447020e-06f, + -2.307898197824789105e-06f, + -2.186862671278643330e-06f, + -2.065599867705792124e-06f, + -1.944258300480321568e-06f, + -1.822984180674020067e-06f, + -1.701921264393156922e-06f, + -1.581210705329814697e-06f, + -1.460990912660235641e-06f, + -1.341397414412057306e-06f, + -1.222562726419054144e-06f, + -1.104616226963808055e-06f, + -9.876840372108532818e-07f, + -8.718889075168336299e-07f, + -7.573501096980523593e-07f, + -6.441833353263205178e-07f, + -5.325006001176824594e-07f, + -4.224101544649153790e-07f, + -3.140164001606101186e-07f, + -2.074198133468546951e-07f, + -1.027168737185703904e-07f, + 0.000000000000000000e+00f, + 1.006425082962560188e-07f, + 1.991265228250998696e-07f, + 2.953721320865672257e-07f, + 3.893036808413146083e-07f, + 4.808498032861632466e-07f, + 5.699434500219715067e-07f, + 6.565219088541263631e-07f, + 7.405268194708413983e-07f, + 8.219041820579037521e-07f, + 9.006043599106572324e-07f, + 9.765820761143821518e-07f, + 1.049796404370967154e-06f, + 1.120210754056259219e-06f, + 1.187792849601788622e-06f, + 1.252514704296207476e-06f, + 1.314352588615897804e-06f, + 1.373286993192450102e-06f, + 1.429302586538256487e-06f, + 1.482388167650501341e-06f, + 1.532536613626721034e-06f, + 1.579744822422699780e-06f, + 1.624013650897105724e-06f, + 1.665347848284486069e-06f, + 1.703755985248906983e-06f, + 1.739250378670976522e-06f, + 1.771847012329051112e-06f, + 1.801565453633872846e-06f, + 1.828428766587697603e-06f, + 1.852463421131862093e-06f, + 1.873699199062873253e-06f, + 1.892169096686314783e-06f, + 1.907909224391436775e-06f, + 1.920958703325515738e-06f, + 1.931359559352847449e-06f, + 1.939156614480256200e-06f, + 1.944397375938704227e-06f, + 1.947131923107859303e-06f, + 1.947412792470748310e-06f, + 1.945294860790306117e-06f, + 1.940835226696380303e-06f, + 1.934093090874000321e-06f, + 1.925129635041577493e-06f, + 1.914007899911128539e-06f, + 1.900792662316812488e-06f, + 1.885550311703490783e-06f, + 1.868348726157043199e-06f, + 1.849257148168054309e-06f, + 1.828346060309234932e-06f, + 1.805687061008333849e-06f, + 1.781352740599210893e-06f, + 1.755416557827082551e-06f, + 1.727952716981022353e-06f, + 1.699036045830707816e-06f, + 1.668741874531441462e-06f, + 1.637145915666299804e-06f, + 1.604324145587388401e-06f, + 1.570352687212907484e-06f, + 1.535307694438660471e-06f, + 1.499265238310532749e-06f, + 1.462301195107119339e-06f, + 1.424491136473709433e-06f, + 1.385910221746217664e-06f, + 1.346633092594916054e-06f, + 1.306733770119299951e-06f, + 1.266285554515691827e-06f, + 1.225360927433250136e-06f, + 1.184031457134546194e-06f, + 1.142367706564255042e-06f, + 1.100439144428898935e-06f, + 1.058314059384136389e-06f, + 1.016059477418145401e-06f, + 9.737410825169423452e-07f, + 9.314231406881861787e-07f, + 8.891684274188879932e-07f, + 8.470381586302859329e-07f, + 8.050919251930470272e-07f, + 7.633876310564211735e-07f, + 7.219814350382115897e-07f, + 6.809276963207535929e-07f, + 6.402789236869417325e-07f, + 6.000857285259880680e-07f, + 5.603967816352038095e-07f, + 5.212587738328753669e-07f, + 4.827163803963257315e-07f, + 4.448122293282582582e-07f, + 4.075868734547717757e-07f, + 3.710787663445438682e-07f, + 3.353242420407537163e-07f, + 3.003574985858815201e-07f, + 2.662105853160385845e-07f, + 2.329133938960602585e-07f, + 2.004936530600967300e-07f, + 1.689769270175546171e-07f, + 1.383866174786039171e-07f, + 1.087439692488582841e-07f, + 8.006807933651531888e-08f, + 5.237590951157132493e-08f, + 2.568230225109257904e-08f, + 0.000000000000000000e+00f, + -2.466033232770569548e-08f, + -4.829008168683121073e-08f, + -7.088265816141993268e-08f, + -9.243346323606733125e-08f, + -1.129398551695146863e-07f, + -1.324011115659184628e-07f, + -1.508183892432858115e-07f, + -1.681946815011221211e-07f, + -1.845347728928822297e-07f, + -1.998451916121546072e-07f, + -2.141341596039432244e-07f, + -2.274115405148332402e-07f, + -2.396887855988568836e-07f, + -2.509788776975187057e-07f, + -2.612962734148978839e-07f, + -2.706568436096965910e-07f, + -2.790778123294188281e-07f, + -2.865776943115482094e-07f, + -2.931762311773865084e-07f, + -2.988943264486187393e-07f, + -3.037539795124567178e-07f, + -3.077782186659332945e-07f, + -3.109910333676799518e-07f, + -3.134173058274048922e-07f, + -3.150827420621620873e-07f, + -3.160138025468559683e-07f, + -3.162376325911064067e-07f, + -3.157819925664867536e-07f, + -3.146751881132046538e-07f, + -3.129460004522320042e-07f, + -3.106236169256348513e-07f, + -3.077375618915215213e-07f, + -3.043176280902072482e-07f, + -3.003938086063936412e-07f, + -2.959962295410709180e-07f, + -2.911550835096173451e-07f, + -2.859005640803434774e-07f, + -2.802628012618049063e-07f, + -2.742717981466135591e-07f, + -2.679573688192066510e-07f, + -2.613490776251264430e-07f, + -2.544761799041636744e-07f, + -2.473675642799770811e-07f, + -2.400516965988118414e-07f, + -2.325565656058897010e-07f, + -2.249096304424925012e-07f, + -2.171377700460247078e-07f, + -2.092672345282480135e-07f, + -2.013235986056343617e-07f, + -1.933317171473633572e-07f, + -1.853156829093616031e-07f, + -1.772987865099094365e-07f, + -1.693034787059287238e-07f, + -1.613513350178359175e-07f, + -1.534630227503889525e-07f, + -1.456582704530633583e-07f, + -1.379558398535355063e-07f, + -1.303735002995515733e-07f, + -1.229280057368551819e-07f, + -1.156350742435759375e-07f, + -1.085093701420685361e-07f, + -1.015644887007500715e-07f, + -9.481294343366335435e-08f, + -8.826615600362021996e-08f, + -8.193444872566309993e-08f, + -7.582703966755611925e-08f, + -6.995204033572834065e-08f, + -6.431645593190529234e-08f, + -5.892618816136430419e-08f, + -5.378604056816772665e-08f, + -4.889972636892558379e-08f, + -4.426987875170946693e-08f, + -3.989806360360980105e-08f, + -3.578479462389621212e-08f, + -3.192955077725143908e-08f, + -2.833079603795987602e-08f, + -2.498600136841822765e-08f, + -2.189166887745841221e-08f, + -1.904335809309968632e-08f, + -1.643571428675994097e-08f, + -1.406249877778086397e-08f, + -1.191662114752070737e-08f, + -9.990173286071856191e-09f, + -8.274465192170869078e-09f, + -6.760062445140820835e-09f, + -5.436825263828806997e-09f, + -4.293949063640696908e-09f, + -3.320006422643590005e-09f, + -2.502990363837817991e-09f, + -1.830358857702062856e-09f, + -1.289080449029138733e-09f, + -8.656809086711142593e-10f, + -5.462908092693271412e-10f, + -3.166939230097100569e-10f, + -1.623763376571505723e-10f, + -6.857618579388846575e-11f, + -2.033388168720926172e-11f, + -2.542758678303834900e-12f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f, + 0.000000000000000000e+00f +}; + +} // namespace juce 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 3e120f0b..e172d06e 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,35 +23,60 @@ namespace juce { +template +static auto getSetupInfo (Setup& s, bool isInput) +{ + struct SetupInfo + { + // double brackets so that we get the expression type, i.e. a (possibly const) reference + decltype ((s.inputDeviceName)) name; + decltype ((s.inputChannels)) channels; + decltype ((s.useDefaultInputChannels)) useDefault; + }; + + return isInput ? SetupInfo { s.inputDeviceName, s.inputChannels, s.useDefaultInputChannels } + : SetupInfo { s.outputDeviceName, s.outputChannels, s.useDefaultOutputChannels }; +} + +static auto tie (const AudioDeviceManager::AudioDeviceSetup& s) +{ + return std::tie (s.outputDeviceName, + s.inputDeviceName, + s.sampleRate, + s.bufferSize, + s.inputChannels, + s.useDefaultInputChannels, + s.outputChannels, + s.useDefaultOutputChannels); +} + bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const { - return outputDeviceName == other.outputDeviceName - && inputDeviceName == other.inputDeviceName - && sampleRate == other.sampleRate - && bufferSize == other.bufferSize - && inputChannels == other.inputChannels - && useDefaultInputChannels == other.useDefaultInputChannels - && outputChannels == other.outputChannels - && useDefaultOutputChannels == other.useDefaultOutputChannels; + return tie (*this) == tie (other); } bool AudioDeviceManager::AudioDeviceSetup::operator!= (const AudioDeviceManager::AudioDeviceSetup& other) const { - return ! operator== (other); + return tie (*this) != tie (other); } //============================================================================== -class AudioDeviceManager::CallbackHandler : public AudioIODeviceCallback, - public MidiInputCallback, - public AudioIODeviceType::Listener +class AudioDeviceManager::CallbackHandler final : public AudioIODeviceCallback, + public MidiInputCallback, + public AudioIODeviceType::Listener { public: CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {} private: - void audioDeviceIOCallback (const float** ins, int numIns, float** outs, int numOuts, int numSamples) override + void audioDeviceIOCallbackWithContext (const float* const* ins, + int numIns, + float* const* outs, + int numOuts, + int numSamples, + const AudioIODeviceCallbackContext& context) override { - owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples); + owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples, context); } void audioDeviceAboutToStart (AudioIODevice* device) override @@ -109,33 +134,77 @@ void AudioDeviceManager::createDeviceTypesIfNeeded() types.clear (false); - if (auto* first = availableDeviceTypes.getFirst()) - currentDeviceType = first->getTypeName(); + for (auto* type : availableDeviceTypes) + type->scanForDevices(); + + pickCurrentDeviceTypeWithDevices(); } } +void AudioDeviceManager::pickCurrentDeviceTypeWithDevices() +{ + const auto deviceTypeHasDevices = [] (const AudioIODeviceType* ptr) + { + return ! ptr->getDeviceNames (true) .isEmpty() + || ! ptr->getDeviceNames (false).isEmpty(); + }; + + if (auto* type = findType (currentDeviceType)) + if (deviceTypeHasDevices (type)) + return; + + const auto iter = std::find_if (availableDeviceTypes.begin(), + availableDeviceTypes.end(), + deviceTypeHasDevices); + + if (iter != availableDeviceTypes.end()) + currentDeviceType = (*iter)->getTypeName(); +} + const OwnedArray& AudioDeviceManager::getAvailableDeviceTypes() { scanDevicesIfNeeded(); return availableDeviceTypes; } +void AudioDeviceManager::updateCurrentSetup() +{ + if (currentAudioDevice != nullptr) + { + currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); + currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); + currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); + currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); + } +} + void AudioDeviceManager::audioDeviceListChanged() { if (currentAudioDevice != nullptr) { - auto isCurrentDeviceStillAvailable = [&] + auto currentDeviceStillAvailable = [&] { - for (auto* dt : availableDeviceTypes) - if (currentAudioDevice->getTypeName() == dt->getTypeName()) - for (auto& dn : dt->getDeviceNames()) - if (currentAudioDevice->getName() == dn) + auto currentTypeName = currentAudioDevice->getTypeName(); + auto currentDeviceName = currentAudioDevice->getName(); + + for (auto* deviceType : availableDeviceTypes) + { + if (currentTypeName == deviceType->getTypeName()) + { + for (auto& deviceName : deviceType->getDeviceNames (true)) + if (currentDeviceName == deviceName) + return true; + + for (auto& deviceName : deviceType->getDeviceNames (false)) + if (currentDeviceName == deviceName) return true; + } + } return false; - }; + }(); - if (! isCurrentDeviceStillAvailable()) + if (! currentDeviceStillAvailable) { closeAudioDevice(); @@ -145,18 +214,18 @@ void AudioDeviceManager::audioDeviceListChanged() initialiseDefault (preferredDeviceName, ¤tSetup); } - if (currentAudioDevice != nullptr) - { - currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); - currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); - currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); - currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); - } + updateCurrentSetup(); } sendChangeMessage(); } +void AudioDeviceManager::midiDeviceListChanged() +{ + openLastRequestedMidiDevices (midiDeviceInfosFromXml, defaultMidiOutputDeviceInfo); + sendChangeMessage(); +} + //============================================================================== static void addIfNotNull (OwnedArray& list, AudioIODeviceType* const device) { @@ -166,8 +235,9 @@ static void addIfNotNull (OwnedArray& list, AudioIODeviceType void AudioDeviceManager::createAudioDeviceTypes (OwnedArray& list) { - addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (false)); - addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (true)); + addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::shared)); + addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::exclusive)); + addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::sharedLowLatency)); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio()); @@ -227,6 +297,7 @@ String AudioDeviceManager::initialise (const int numInputChannelsNeeded, const AudioDeviceSetup* preferredSetupOptions) { scanDevicesIfNeeded(); + pickCurrentDeviceTypeWithDevices(); numInputChansNeeded = numInputChannelsNeeded; numOutputChansNeeded = numOutputChannelsNeeded; @@ -250,26 +321,67 @@ String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDevi } else if (preferredDefaultDeviceName.isNotEmpty()) { - for (auto* type : availableDeviceTypes) + const auto nameMatches = [&preferredDefaultDeviceName] (const String& name) + { + return name.matchesWildcard (preferredDefaultDeviceName, true); + }; + + struct WildcardMatch + { + String value; + bool successful; + }; + + const auto getWildcardMatch = [&nameMatches] (const StringArray& names) + { + const auto iter = std::find_if (names.begin(), names.end(), nameMatches); + return WildcardMatch { iter != names.end() ? *iter : String(), iter != names.end() }; + }; + + struct WildcardMatches + { + WildcardMatch input, output; + }; + + const auto getMatchesForType = [&getWildcardMatch] (const AudioIODeviceType* type) + { + return WildcardMatches { getWildcardMatch (type->getDeviceNames (true)), + getWildcardMatch (type->getDeviceNames (false)) }; + }; + + struct SearchResult + { + String type, input, output; + }; + + const auto result = [&] { - for (auto& out : type->getDeviceNames (false)) + // First, look for a device type with an input and output which matches the preferred name + for (auto* type : availableDeviceTypes) { - if (out.matchesWildcard (preferredDefaultDeviceName, true)) - { - setup.outputDeviceName = out; - break; - } + const auto matches = getMatchesForType (type); + + if (matches.input.successful && matches.output.successful) + return SearchResult { type->getTypeName(), matches.input.value, matches.output.value }; } - for (auto& in : type->getDeviceNames (true)) + // No device type has matching ins and outs, so fall back to a device where either the + // input or output match + for (auto* type : availableDeviceTypes) { - if (in.matchesWildcard (preferredDefaultDeviceName, true)) - { - setup.inputDeviceName = in; - break; - } + const auto matches = getMatchesForType (type); + + if (matches.input.successful || matches.output.successful) + return SearchResult { type->getTypeName(), matches.input.value, matches.output.value }; } - } + + // No devices match the query, so just use the default devices from the current type + return SearchResult { currentDeviceType, {}, {} }; + }(); + + currentDeviceType = result.type; + setup.inputDeviceName = result.input; + setup.outputDeviceName = result.output; } insertDefaultDeviceNames (setup); @@ -324,65 +436,62 @@ String AudioDeviceManager::initialiseFromXML (const XmlElement& xml, if (error.isNotEmpty() && selectDefaultDeviceOnFailure) error = initialise (numInputChansNeeded, numOutputChansNeeded, nullptr, false, preferredDefaultDeviceName); - midiDeviceInfosFromXml.clear(); enabledMidiInputs.clear(); - forEachXmlChildElementWithTagName (xml, c, "MIDIINPUT") - midiDeviceInfosFromXml.add ({ c->getStringAttribute ("name"), c->getStringAttribute ("identifier") }); - - auto isIdentifierAvailable = [] (const Array& available, const String& identifier) + const auto midiInputs = [&] { - for (auto& device : available) - if (device.identifier == identifier) - return true; + Array result; - return false; - }; + for (auto* c : xml.getChildWithTagNameIterator ("MIDIINPUT")) + result.add ({ c->getStringAttribute ("name"), c->getStringAttribute ("identifier") }); - auto getUpdatedIdentifierForName = [&] (const Array& available, const String& name) -> String - { - for (auto& device : available) - if (device.name == name) - return device.identifier; + return result; + }(); - return {}; - }; + const MidiDeviceInfo defaultOutputDeviceInfo (xml.getStringAttribute ("defaultMidiOutput"), + xml.getStringAttribute ("defaultMidiOutputDevice")); - auto inputs = MidiInput::getAvailableDevices(); + openLastRequestedMidiDevices (midiInputs, defaultOutputDeviceInfo); + + return error; +} - for (auto& info : midiDeviceInfosFromXml) +void AudioDeviceManager::openLastRequestedMidiDevices (const Array& desiredInputs, const MidiDeviceInfo& defaultOutput) +{ + const auto openDeviceIfAvailable = [&] (const Array& devices, + const MidiDeviceInfo& deviceToOpen, + auto&& doOpen) { - if (isIdentifierAvailable (inputs, info.identifier)) + const auto iterWithMatchingIdentifier = std::find_if (devices.begin(), devices.end(), [&] (const auto& x) + { + return x.identifier == deviceToOpen.identifier; + }); + + if (iterWithMatchingIdentifier != devices.end()) { - setMidiInputDeviceEnabled (info.identifier, true); + doOpen (deviceToOpen.identifier); + return; } - else + + const auto iterWithMatchingName = std::find_if (devices.begin(), devices.end(), [&] (const auto& x) { - auto identifier = getUpdatedIdentifierForName (inputs, info.name); + return x.name == deviceToOpen.name; + }); - if (identifier.isNotEmpty()) - setMidiInputDeviceEnabled (identifier, true); - } - } + if (iterWithMatchingName != devices.end()) + doOpen (iterWithMatchingName->identifier); + }; - MidiDeviceInfo defaultOutputDeviceInfo (xml.getStringAttribute ("defaultMidiOutput"), - xml.getStringAttribute ("defaultMidiOutputDevice")); + midiDeviceInfosFromXml = desiredInputs; - auto outputs = MidiOutput::getAvailableDevices(); + const auto inputs = MidiInput::getAvailableDevices(); - if (isIdentifierAvailable (outputs, defaultOutputDeviceInfo.identifier)) - { - setDefaultMidiOutputDevice (defaultOutputDeviceInfo.identifier); - } - else - { - auto identifier = getUpdatedIdentifierForName (outputs, defaultOutputDeviceInfo.name); + for (const auto& info : midiDeviceInfosFromXml) + openDeviceIfAvailable (inputs, info, [&] (const auto identifier) { setMidiInputDeviceEnabled (identifier, true); }); - if (identifier.isNotEmpty()) - setDefaultMidiOutputDevice (identifier); - } + const auto outputs = MidiOutput::getAvailableDevices(); - return error; + openDeviceIfAvailable (outputs, defaultOutput, [&] (const auto identifier) { setDefaultMidiOutputDevice (identifier); }); } String AudioDeviceManager::initialiseWithDefaultDevices (int numInputChannelsNeeded, @@ -396,13 +505,91 @@ String AudioDeviceManager::initialiseWithDefaultDevices (int numInputChannelsNee void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const { + enum class Direction { out, in }; + if (auto* type = getCurrentDeviceTypeObject()) { - if (numOutputChansNeeded > 0 && setup.outputDeviceName.isEmpty()) - setup.outputDeviceName = type->getDeviceNames (false) [type->getDefaultDeviceIndex (false)]; + // We avoid selecting a device pair that doesn't share a matching sample rate, if possible. + // If not, other parts of the AudioDeviceManager and AudioIODevice classes should generate + // an appropriate error message when opening or starting these devices. + const auto getDevicesToTestForMatchingSampleRate = [&setup, type, this] (Direction dir) + { + const auto isInput = dir == Direction::in; + const auto info = getSetupInfo (setup, isInput); + + if (! info.name.isEmpty()) + return StringArray { info.name }; + + const auto numChannelsNeeded = isInput ? numInputChansNeeded : numOutputChansNeeded; + auto deviceNames = numChannelsNeeded > 0 ? type->getDeviceNames (isInput) : StringArray {}; + deviceNames.move (type->getDefaultDeviceIndex (isInput), 0); + + return deviceNames; + }; + + std::map, Array> sampleRatesCache; + + const auto getSupportedSampleRates = [&sampleRatesCache, type] (Direction dir, const String& deviceName) + { + const auto key = std::make_pair (dir, deviceName); + + auto& entry = [&]() -> auto& + { + auto it = sampleRatesCache.find (key); + + if (it != sampleRatesCache.end()) + return it->second; + + auto& elem = sampleRatesCache[key]; + auto tempDevice = rawToUniquePtr (type->createDevice ((dir == Direction::in) ? "" : deviceName, + (dir == Direction::in) ? deviceName : "")); + if (tempDevice != nullptr) + elem = tempDevice->getAvailableSampleRates(); + + return elem; + }(); + + return entry; + }; + + const auto validate = [&getSupportedSampleRates] (const String& outputDeviceName, const String& inputDeviceName) + { + jassert (! outputDeviceName.isEmpty() && ! inputDeviceName.isEmpty()); + + const auto outputSampleRates = getSupportedSampleRates (Direction::out, outputDeviceName); + const auto inputSampleRates = getSupportedSampleRates (Direction::in, inputDeviceName); + + return std::any_of (inputSampleRates.begin(), + inputSampleRates.end(), + [&] (auto inputSampleRate) { return outputSampleRates.contains (inputSampleRate); }); + }; + + auto outputsToTest = getDevicesToTestForMatchingSampleRate (Direction::out); + auto inputsToTest = getDevicesToTestForMatchingSampleRate (Direction::in); + + // We set default device names, so in case no in-out pair passes the validation, we still + // produce the same result as before + if (setup.outputDeviceName.isEmpty() && ! outputsToTest.isEmpty()) + setup.outputDeviceName = outputsToTest[0]; + + if (setup.inputDeviceName.isEmpty() && ! inputsToTest.isEmpty()) + setup.inputDeviceName = inputsToTest[0]; + + // We check all possible in-out pairs until the first validation pass. If no pair passes we + // leave the setup unchanged. + for (const auto& out : outputsToTest) + { + for (const auto& in : inputsToTest) + { + if (validate (out, in)) + { + setup.outputDeviceName = out; + setup.inputDeviceName = in; - if (numInputChansNeeded > 0 && setup.inputDeviceName.isEmpty()) - setup.inputDeviceName = type->getDeviceNames (true) [type->getDefaultDeviceIndex (true)]; + return; + } + } + } } } @@ -472,7 +659,7 @@ void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, bool tre { for (int i = 0; i < availableDeviceTypes.size(); ++i) { - if (availableDeviceTypes.getUnchecked(i)->getTypeName() == type + if (availableDeviceTypes.getUnchecked (i)->getTypeName() == type && currentDeviceType != type) { if (currentAudioDevice != nullptr) @@ -484,7 +671,7 @@ void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, bool tre currentDeviceType = type; - AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked(i)); + AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked (i)); insertDefaultDeviceNames (s); setAudioDeviceSetup (s, treatAsChosenDevice); @@ -495,6 +682,11 @@ void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, bool tre } } +AudioWorkgroup AudioDeviceManager::getDeviceAudioWorkgroup() const +{ + return currentAudioDevice != nullptr ? currentAudioDevice->getWorkgroup() : AudioWorkgroup{}; +} + AudioIODeviceType* AudioDeviceManager::getCurrentDeviceTypeObject() const { for (auto* type : availableDeviceTypes) @@ -506,7 +698,7 @@ AudioIODeviceType* AudioDeviceManager::getCurrentDeviceTypeObject() const static void updateSetupChannels (AudioDeviceManager::AudioDeviceSetup& setup, int defaultNumIns, int defaultNumOuts) { - auto updateChannels = [](const String& deviceName, BigInteger& channels, int defaultNumChannels) + auto updateChannels = [] (const String& deviceName, BigInteger& channels, int defaultNumChannels) { if (deviceName.isEmpty()) { @@ -533,6 +725,8 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup else if (currentAudioDevice != nullptr) return {}; + stopDevice(); + if (getCurrentDeviceTypeObject() == nullptr || (newSetup.inputDeviceName.isEmpty() && newSetup.outputDeviceName.isEmpty())) { @@ -544,24 +738,26 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup return {}; } - stopDevice(); - String error; - if (currentSetup.inputDeviceName != newSetup.inputDeviceName - || currentSetup.outputDeviceName != newSetup.outputDeviceName - || currentAudioDevice == nullptr) + const auto needsNewDevice = currentSetup.inputDeviceName != newSetup.inputDeviceName + || currentSetup.outputDeviceName != newSetup.outputDeviceName + || currentAudioDevice == nullptr; + + if (needsNewDevice) { deleteCurrentDevice(); scanDevicesIfNeeded(); auto* type = getCurrentDeviceTypeObject(); - if (newSetup.outputDeviceName.isNotEmpty() && ! deviceListContains (type, false, newSetup.outputDeviceName)) - return "No such device: " + newSetup.outputDeviceName; + for (const auto isInput : { false, true }) + { + const auto name = getSetupInfo (newSetup, isInput).name; - if (newSetup.inputDeviceName.isNotEmpty() && ! deviceListContains (type, true, newSetup.inputDeviceName)) - return "No such device: " + newSetup.inputDeviceName; + if (name.isNotEmpty() && ! deviceListContains (type, isInput, name)) + return "No such device: " + name; + } currentAudioDevice.reset (type->createDevice (newSetup.outputDeviceName, newSetup.inputDeviceName)); @@ -608,10 +804,12 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup currentAudioDevice->start (callbackHandler.get()); - currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); - currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); - currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); - currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); + error = currentAudioDevice->getLastError(); + } + + if (error.isEmpty()) + { + updateCurrentSetup(); for (int i = 0; i < availableDeviceTypes.size(); ++i) if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType) @@ -692,7 +890,7 @@ void AudioDeviceManager::restartLastAudioDevice() { // This method will only reload the last device that was running // before closeAudioDevice() was called - you need to actually open - // one first, with setAudioDevice(). + // one first, with setAudioDeviceSetup(). jassertfalse; return; } @@ -792,32 +990,40 @@ void AudioDeviceManager::removeAudioCallback (AudioIODeviceCallback* callbackToR } } -void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelData, +void AudioDeviceManager::audioDeviceIOCallbackInt (const float* const* inputChannelData, int numInputChannels, - float** outputChannelData, + float* const* outputChannelData, int numOutputChannels, - int numSamples) + int numSamples, + const AudioIODeviceCallbackContext& context) { const ScopedLock sl (audioCallbackLock); inputLevelGetter->updateLevel (inputChannelData, numInputChannels, numSamples); - outputLevelGetter->updateLevel (const_cast (outputChannelData), numOutputChannels, numSamples); if (callbacks.size() > 0) { - AudioProcessLoadMeasurer::ScopedTimer timer (loadMeasurer); + AudioProcessLoadMeasurer::ScopedTimer timer (loadMeasurer, numSamples); tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true); - callbacks.getUnchecked(0)->audioDeviceIOCallback (inputChannelData, numInputChannels, - outputChannelData, numOutputChannels, numSamples); + callbacks.getUnchecked (0)->audioDeviceIOCallbackWithContext (inputChannelData, + numInputChannels, + outputChannelData, + numOutputChannels, + numSamples, + context); - auto** tempChans = tempBuffer.getArrayOfWritePointers(); + auto* const* tempChans = tempBuffer.getArrayOfWritePointers(); for (int i = callbacks.size(); --i > 0;) { - callbacks.getUnchecked(i)->audioDeviceIOCallback (inputChannelData, numInputChannels, - tempChans, numOutputChannels, numSamples); + callbacks.getUnchecked (i)->audioDeviceIOCallbackWithContext (inputChannelData, + numInputChannels, + tempChans, + numOutputChannels, + numSamples, + context); for (int chan = 0; chan < numOutputChannels; ++chan) { @@ -840,14 +1046,17 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat auto* src = testSound->getReadPointer (0, testSoundPosition); for (int i = 0; i < numOutputChannels; ++i) - for (int j = 0; j < numSamps; ++j) - outputChannelData [i][j] += src[j]; + if (auto* dst = outputChannelData [i]) + for (int j = 0; j < numSamps; ++j) + dst[j] += src[j]; testSoundPosition += numSamps; if (testSoundPosition >= testSound->getNumSamples()) testSound.reset(); } + + outputLevelGetter->updateLevel (outputChannelData, numOutputChannels, numSamples); } void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device) @@ -855,11 +1064,13 @@ void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device loadMeasurer.reset (device->getCurrentSampleRate(), device->getCurrentBufferSizeSamples()); + updateCurrentSetup(); + { const ScopedLock sl (audioCallbackLock); for (int i = callbacks.size(); --i >= 0;) - callbacks.getUnchecked(i)->audioDeviceAboutToStart (device); + callbacks.getUnchecked (i)->audioDeviceAboutToStart (device); } sendChangeMessage(); @@ -874,7 +1085,7 @@ void AudioDeviceManager::audioDeviceStoppedInt() loadMeasurer.reset(); for (int i = callbacks.size(); --i >= 0;) - callbacks.getUnchecked(i)->audioDeviceStopped(); + callbacks.getUnchecked (i)->audioDeviceStopped(); } void AudioDeviceManager::audioDeviceErrorInt (const String& message) @@ -882,7 +1093,7 @@ void AudioDeviceManager::audioDeviceErrorInt (const String& message) const ScopedLock sl (audioCallbackLock); for (int i = callbacks.size(); --i >= 0;) - callbacks.getUnchecked(i)->audioDeviceError (message); + callbacks.getUnchecked (i)->audioDeviceError (message); } double AudioDeviceManager::getCpuUsage() const @@ -966,6 +1177,7 @@ void AudioDeviceManager::setDefaultMidiOutputDevice (const String& identifier) { if (defaultMidiOutputDeviceInfo.identifier != identifier) { + std::unique_ptr oldMidiPort; Array oldCallbacks; { @@ -977,7 +1189,7 @@ void AudioDeviceManager::setDefaultMidiOutputDevice (const String& identifier) for (int i = oldCallbacks.size(); --i >= 0;) oldCallbacks.getUnchecked (i)->audioDeviceStopped(); - defaultMidiOutput.reset(); + std::swap (oldMidiPort, defaultMidiOutput); if (identifier.isNotEmpty()) defaultMidiOutput = MidiOutput::openDevice (identifier); @@ -997,7 +1209,7 @@ void AudioDeviceManager::setDefaultMidiOutputDevice (const String& identifier) } updateXml(); - sendChangeMessage(); + sendSynchronousChangeMessage(); } } @@ -1134,12 +1346,19 @@ void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCall void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputCallback* callbackToRemove) { - for (auto& device : MidiInput::getAvailableDevices()) + if (name.isEmpty()) { - if (device.name == name) + removeMidiInputDeviceCallback ({}, callbackToRemove); + } + else + { + for (auto& device : MidiInput::getAvailableDevices()) { - removeMidiInputDeviceCallback (device.identifier, callbackToRemove); - return; + if (device.name == name) + { + removeMidiInputDeviceCallback (device.identifier, callbackToRemove); + return; + } } } } @@ -1156,4 +1375,571 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& name) } } +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class AudioDeviceManagerTests final : public UnitTest +{ +public: + AudioDeviceManagerTests() : UnitTest ("AudioDeviceManager", UnitTestCategories::audio) {} + + void runTest() override + { + beginTest ("When the AudioDeviceSetup has non-empty device names, initialise uses the requested devices"); + { + AudioDeviceManager manager; + initialiseManager (manager); + + expectEquals (manager.getAvailableDeviceTypes().size(), 2); + + AudioDeviceManager::AudioDeviceSetup setup; + setup.outputDeviceName = "z"; + setup.inputDeviceName = "c"; + + expect (manager.initialise (2, 2, nullptr, true, String{}, &setup).isEmpty()); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + } + + beginTest ("When the AudioDeviceSetup has empty device names, initialise picks suitable default devices"); + { + AudioDeviceManager manager; + initialiseManager (manager); + + AudioDeviceManager::AudioDeviceSetup setup; + + expect (manager.initialise (2, 2, nullptr, true, String{}, &setup).isEmpty()); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, String ("x")); + expectEquals (newSetup.inputDeviceName, String ("a")); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + } + + beginTest ("When the preferred device name matches an input and an output on the same type, that type is used"); + { + AudioDeviceManager manager; + initialiseManagerWithDifferentDeviceNames (manager); + + expect (manager.initialise (2, 2, nullptr, true, "bar *").isEmpty()); + + expectEquals (manager.getCurrentAudioDeviceType(), String ("bar")); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, String ("bar out a")); + expectEquals (newSetup.inputDeviceName, String ("bar in a")); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + beginTest ("When the preferred device name matches either an input and an output, but not both, that type is used"); + { + AudioDeviceManager manager; + initialiseManagerWithDifferentDeviceNames (manager); + + expect (manager.initialise (2, 2, nullptr, true, "bar out b").isEmpty()); + + expectEquals (manager.getCurrentAudioDeviceType(), String ("bar")); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, String ("bar out b")); + expectEquals (newSetup.inputDeviceName, String ("bar in a")); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + beginTest ("When the preferred device name does not match any inputs or outputs, defaults are used"); + { + AudioDeviceManager manager; + initialiseManagerWithDifferentDeviceNames (manager); + + expect (manager.initialise (2, 2, nullptr, true, "unmatchable").isEmpty()); + + expectEquals (manager.getCurrentAudioDeviceType(), String ("foo")); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, String ("foo out a")); + expectEquals (newSetup.inputDeviceName, String ("foo in a")); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + beginTest ("When first device type has no devices, a device type with devices is used instead"); + { + AudioDeviceManager manager; + initialiseManagerWithEmptyDeviceType (manager); + + AudioDeviceManager::AudioDeviceSetup setup; + + expect (manager.initialise (2, 2, nullptr, true, {}, &setup).isEmpty()); + + const auto& newSetup = manager.getAudioDeviceSetup(); + + expectEquals (newSetup.outputDeviceName, String ("x")); + expectEquals (newSetup.inputDeviceName, String ("a")); + + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + } + + beginTest ("If a device type has been explicitly set to a type with devices, " + "initialisation should respect this choice"); + { + AudioDeviceManager manager; + initialiseManagerWithEmptyDeviceType (manager); + manager.setCurrentAudioDeviceType (mockBName, true); + + AudioDeviceManager::AudioDeviceSetup setup; + expect (manager.initialise (2, 2, nullptr, true, {}, &setup).isEmpty()); + + expectEquals (manager.getCurrentAudioDeviceType(), mockBName); + } + + beginTest ("If a device type has been explicitly set to a type without devices, " + "initialisation should pick a type with devices instead"); + { + AudioDeviceManager manager; + initialiseManagerWithEmptyDeviceType (manager); + manager.setCurrentAudioDeviceType (emptyName, true); + + AudioDeviceManager::AudioDeviceSetup setup; + expect (manager.initialise (2, 2, nullptr, true, {}, &setup).isEmpty()); + + expectEquals (manager.getCurrentAudioDeviceType(), mockAName); + } + + beginTest ("Carry out a long sequence of configuration changes"); + { + AudioDeviceManager manager; + initialiseManagerWithEmptyDeviceType (manager); + initialiseWithDefaultDevices (manager); + disableInputChannelsButLeaveDeviceOpen (manager); + selectANewInputDevice (manager); + disableInputDevice (manager); + reenableInputDeviceWithNoChannels (manager); + enableInputChannels (manager); + disableInputChannelsButLeaveDeviceOpen (manager); + switchDeviceType (manager); + enableInputChannels (manager); + closeDeviceByRequestingEmptyNames (manager); + } + + beginTest ("AudioDeviceManager updates its current settings before notifying callbacks when device restarts itself"); + { + AudioDeviceManager manager; + auto deviceType = std::make_unique ("foo", + StringArray { "foo in a", "foo in b" }, + StringArray { "foo out a", "foo out b" }); + auto* ptr = deviceType.get(); + manager.addAudioDeviceType (std::move (deviceType)); + + AudioDeviceManager::AudioDeviceSetup setup; + setup.sampleRate = 48000.0; + setup.bufferSize = 256; + setup.inputDeviceName = "foo in a"; + setup.outputDeviceName = "foo out a"; + setup.useDefaultInputChannels = true; + setup.useDefaultOutputChannels = true; + manager.setAudioDeviceSetup (setup, true); + + const auto currentSetup = manager.getAudioDeviceSetup(); + expectEquals (currentSetup.sampleRate, setup.sampleRate); + expectEquals (currentSetup.bufferSize, setup.bufferSize); + + MockCallback callback; + manager.addAudioCallback (&callback); + + constexpr auto newSr = 10000.0; + constexpr auto newBs = 1024; + auto numCalls = 0; + + // Compilers disagree about whether newSr and newBs need to be captured + callback.aboutToStart = [&] + { + ++numCalls; + const auto current = manager.getAudioDeviceSetup(); + expectEquals (current.sampleRate, newSr); + expectEquals (current.bufferSize, newBs); + }; + + ptr->restartDevices (newSr, newBs); + expectEquals (numCalls, 1); + } + } + +private: + void initialiseWithDefaultDevices (AudioDeviceManager& manager) + { + manager.initialiseWithDefaultDevices (2, 2); + const auto& setup = manager.getAudioDeviceSetup(); + + expectEquals (setup.inputChannels.countNumberOfSetBits(), 2); + expectEquals (setup.outputChannels.countNumberOfSetBits(), 2); + + expect (setup.useDefaultInputChannels); + expect (setup.useDefaultOutputChannels); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void disableInputChannelsButLeaveDeviceOpen (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputChannels.clear(); + setup.useDefaultInputChannels = false; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (! newSetup.useDefaultInputChannels); + expect (newSetup.useDefaultOutputChannels); + + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void selectANewInputDevice (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputDeviceName = "b"; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (! newSetup.useDefaultInputChannels); + expect (newSetup.useDefaultOutputChannels); + + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void disableInputDevice (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputDeviceName = ""; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (! newSetup.useDefaultInputChannels); + expect (newSetup.useDefaultOutputChannels); + + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void reenableInputDeviceWithNoChannels (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputDeviceName = "a"; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (! newSetup.useDefaultInputChannels); + expect (newSetup.useDefaultOutputChannels); + + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void enableInputChannels (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputDeviceName = manager.getCurrentDeviceTypeObject()->getDeviceNames (true)[0]; + setup.inputChannels = 3; + setup.useDefaultInputChannels = false; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (! newSetup.useDefaultInputChannels); + expect (newSetup.useDefaultOutputChannels); + + expectEquals (newSetup.inputDeviceName, setup.inputDeviceName); + expectEquals (newSetup.outputDeviceName, setup.outputDeviceName); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void switchDeviceType (AudioDeviceManager& manager) + { + const auto oldSetup = manager.getAudioDeviceSetup(); + + expectEquals (manager.getCurrentAudioDeviceType(), String (mockAName)); + + manager.setCurrentAudioDeviceType (mockBName, true); + + expectEquals (manager.getCurrentAudioDeviceType(), String (mockBName)); + + const auto newSetup = manager.getAudioDeviceSetup(); + + expect (newSetup.outputDeviceName.isNotEmpty()); + // We had no channels enabled, which means we don't need to open a new input device + expect (newSetup.inputDeviceName.isEmpty()); + + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (manager.getCurrentAudioDevice() != nullptr); + } + + void closeDeviceByRequestingEmptyNames (AudioDeviceManager& manager) + { + auto setup = manager.getAudioDeviceSetup(); + setup.inputDeviceName = ""; + setup.outputDeviceName = ""; + + expect (manager.setAudioDeviceSetup (setup, true).isEmpty()); + + const auto newSetup = manager.getAudioDeviceSetup(); + expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); + expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); + + expect (newSetup.inputDeviceName.isEmpty()); + expect (newSetup.outputDeviceName.isEmpty()); + + expect (manager.getCurrentAudioDevice() == nullptr); + } + + const String mockAName = "mockA"; + const String mockBName = "mockB"; + const String emptyName = "empty"; + + struct Restartable + { + virtual ~Restartable() = default; + virtual void restart (double newSr, int newBs) = 0; + }; + + class MockDevice final : public AudioIODevice, + private Restartable + { + public: + MockDevice (ListenerList& l, String typeNameIn, String outNameIn, String inNameIn) + : AudioIODevice ("mock", typeNameIn), listeners (l), outName (outNameIn), inName (inNameIn) + { + listeners.add (this); + } + + ~MockDevice() override + { + listeners.remove (this); + } + + StringArray getOutputChannelNames() override { return { "o1", "o2", "o3" }; } + StringArray getInputChannelNames() override { return { "i1", "i2", "i3" }; } + + Array getAvailableSampleRates() override { return { 44100.0, 48000.0 }; } + Array getAvailableBufferSizes() override { return { 128, 256 }; } + int getDefaultBufferSize() override { return 128; } + + String open (const BigInteger& inputs, const BigInteger& outputs, double sr, int bs) override + { + inChannels = inputs; + outChannels = outputs; + sampleRate = sr; + blockSize = bs; + on = true; + return {}; + } + + void close() override { on = false; } + bool isOpen() override { return on; } + + void start (AudioIODeviceCallback* c) override + { + callback = c; + callback->audioDeviceAboutToStart (this); + playing = true; + } + + void stop() override + { + playing = false; + callback->audioDeviceStopped(); + } + + bool isPlaying() override { return playing; } + + String getLastError() override { return {}; } + int getCurrentBufferSizeSamples() override { return blockSize; } + double getCurrentSampleRate() override { return sampleRate; } + int getCurrentBitDepth() override { return 16; } + + BigInteger getActiveOutputChannels() const override { return outChannels; } + BigInteger getActiveInputChannels() const override { return inChannels; } + + int getOutputLatencyInSamples() override { return 0; } + int getInputLatencyInSamples() override { return 0; } + + private: + void restart (double newSr, int newBs) override + { + stop(); + close(); + open (inChannels, outChannels, newSr, newBs); + start (callback); + } + + ListenerList& listeners; + AudioIODeviceCallback* callback = nullptr; + String outName, inName; + BigInteger outChannels, inChannels; + double sampleRate = 0.0; + int blockSize = 0; + bool on = false, playing = false; + }; + + class MockDeviceType final : public AudioIODeviceType + { + public: + explicit MockDeviceType (String kind) + : MockDeviceType (std::move (kind), { "a", "b", "c" }, { "x", "y", "z" }) {} + + MockDeviceType (String kind, StringArray inputNames, StringArray outputNames) + : AudioIODeviceType (std::move (kind)), + inNames (std::move (inputNames)), + outNames (std::move (outputNames)) {} + + ~MockDeviceType() override + { + // A Device outlived its DeviceType! + jassert (listeners.isEmpty()); + } + + void scanForDevices() override {} + + StringArray getDeviceNames (bool isInput) const override + { + return getNames (isInput); + } + + int getDefaultDeviceIndex (bool) const override { return 0; } + + int getIndexOfDevice (AudioIODevice* device, bool isInput) const override + { + return getNames (isInput).indexOf (device->getName()); + } + + bool hasSeparateInputsAndOutputs() const override { return true; } + + AudioIODevice* createDevice (const String& outputName, const String& inputName) override + { + if (inNames.contains (inputName) || outNames.contains (outputName)) + return new MockDevice (listeners, getTypeName(), outputName, inputName); + + return nullptr; + } + + // Call this to emulate the device restarting itself with new settings. + // This might happen e.g. when a user changes the ASIO settings. + void restartDevices (double newSr, int newBs) + { + listeners.call ([&] (auto& l) { return l.restart (newSr, newBs); }); + } + + private: + const StringArray& getNames (bool isInput) const { return isInput ? inNames : outNames; } + + const StringArray inNames, outNames; + ListenerList listeners; + }; + + class MockCallback final : public AudioIODeviceCallback + { + public: + std::function callback; + std::function aboutToStart; + std::function stopped; + std::function error; + + void audioDeviceIOCallbackWithContext (const float* const*, + int, + float* const*, + int, + int, + const AudioIODeviceCallbackContext&) override + { + NullCheckedInvocation::invoke (callback); + } + + void audioDeviceAboutToStart (AudioIODevice*) override { NullCheckedInvocation::invoke (aboutToStart); } + void audioDeviceStopped() override { NullCheckedInvocation::invoke (stopped); } + void audioDeviceError (const String&) override { NullCheckedInvocation::invoke (error); } + }; + + void initialiseManager (AudioDeviceManager& manager) + { + manager.addAudioDeviceType (std::make_unique (mockAName)); + manager.addAudioDeviceType (std::make_unique (mockBName)); + } + + void initialiseManagerWithEmptyDeviceType (AudioDeviceManager& manager) + { + manager.addAudioDeviceType (std::make_unique (emptyName, StringArray{}, StringArray{})); + initialiseManager (manager); + } + + void initialiseManagerWithDifferentDeviceNames (AudioDeviceManager& manager) + { + manager.addAudioDeviceType (std::make_unique ("foo", + StringArray { "foo in a", "foo in b" }, + StringArray { "foo out a", "foo out b" })); + + manager.addAudioDeviceType (std::make_unique ("bar", + StringArray { "bar in a", "bar in b" }, + StringArray { "bar out a", "bar out b" })); + } +}; + +static AudioDeviceManagerTests audioDeviceManagerTests; + +#endif + } // 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 1524fc15..881bcaac 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -93,13 +93,11 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster The name has to be one of the ones listed by the AudioDeviceManager's currently selected device type. This may be the same as the input device. - An empty string indicates the default device. */ String outputDeviceName; /** The name of the audio device used for input. This may be the same as the output device. - An empty string indicates the default device. */ String inputDeviceName; @@ -174,7 +172,10 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster @param preferredSetupOptions if this is non-null, the structure will be used as the set of preferred settings when opening the device. If you use this parameter, the preferredDefaultDeviceName - field will be ignored + field will be ignored. If you set the outputDeviceName + or inputDeviceName data members of the AudioDeviceSetup + to empty strings, then a default device will be used. + @returns an error message if anything went wrong, or an empty string if it worked ok. */ @@ -219,7 +220,11 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster settings, then tweak the appropriate fields in the AudioDeviceSetup structure, and pass it back into this method to apply the new settings. - @param newSetup the settings that you'd like to use + @param newSetup the settings that you'd like to use. + If you don't need an input or output device, set the + inputDeviceName or outputDeviceName data members respectively + to empty strings. Note that this behaviour differs from + the behaviour of initialise(). @param treatAsChosenDevice if this is true and if the device opens correctly, these new settings will be taken as having been explicitly chosen by the user, and the next time createStateXml() is called, these settings @@ -257,6 +262,9 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster */ void setCurrentAudioDeviceType (const String& type, bool treatAsChosenDevice); + /** Returns the current audio device workgroup, if supported. */ + AudioWorkgroup getDeviceAudioWorkgroup() const; + /** Closes the currently-open device. You can call restartLastAudioDevice() later to reopen it in the same state that it was just in. @@ -267,7 +275,7 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster Note that this only reloads the last device that was running before closeAudioDevice() was called - it doesn't reload any kind of saved-state, - and can only be called after a device has been opened with SetAudioDevice(). + and can only be called after a device has been opened with setAudioDeviceSetup(). If a device is already open, this call will do nothing. */ @@ -466,18 +474,20 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster int getXRunCount() const noexcept; //============================================================================== - /** Deprecated. */ + #ifndef DOXYGEN + [[deprecated ("Use setMidiInputDeviceEnabled instead.")]] void setMidiInputEnabled (const String&, bool); - /** Deprecated. */ + [[deprecated ("Use isMidiInputDeviceEnabled instead.")]] bool isMidiInputEnabled (const String&) const; - /** Deprecated. */ + [[deprecated ("Use addMidiInputDeviceCallback instead.")]] void addMidiInputCallback (const String&, MidiInputCallback*); - /** Deprecated. */ + [[deprecated ("Use removeMidiInputDeviceCallback instead.")]] void removeMidiInputCallback (const String&, MidiInputCallback*); - /** Deprecated. */ + [[deprecated ("Use setDefaultMidiOutputDevice instead.")]] void setDefaultMidiOutput (const String&); - /** Deprecated. */ + [[deprecated ("Use getDefaultMidiOutputIdentifier instead.")]] const String& getDefaultMidiOutputName() const noexcept { return defaultMidiOutputDeviceInfo.name; } + #endif private: //============================================================================== @@ -492,6 +502,10 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster std::unique_ptr lastExplicitSettings; mutable bool listNeedsScanning = true; AudioBuffer tempBuffer; + MidiDeviceListConnection midiDeviceListConnection = MidiDeviceListConnection::make ([this] + { + midiDeviceListChanged(); + }); struct MidiCallbackInfo { @@ -519,13 +533,18 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster class CallbackHandler; std::unique_ptr callbackHandler; - void audioDeviceIOCallbackInt (const float** inputChannelData, int totalNumInputChannels, - float** outputChannelData, int totalNumOutputChannels, int numSamples); + void audioDeviceIOCallbackInt (const float* const* inputChannelData, + int totalNumInputChannels, + float* const* outputChannelData, + int totalNumOutputChannels, + int numSamples, + const AudioIODeviceCallbackContext& context); void audioDeviceAboutToStartInt (AudioIODevice*); void audioDeviceStoppedInt(); void audioDeviceErrorInt (const String&); void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&); void audioDeviceListChanged(); + void midiDeviceListChanged(); String restartDevice (int blockSizeToUse, double sampleRateToUse, const BigInteger& ins, const BigInteger& outs); @@ -533,6 +552,7 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster void updateXml(); + void updateCurrentSetup(); void createDeviceTypesIfNeeded(); void scanDevicesIfNeeded(); void deleteCurrentDevice(); @@ -542,9 +562,11 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster String initialiseDefault (const String& preferredDefaultDeviceName, const AudioDeviceSetup*); String initialiseFromXML (const XmlElement&, bool selectDefaultDeviceOnFailure, const String& preferredDefaultDeviceName, const AudioDeviceSetup*); + void openLastRequestedMidiDevices (const Array&, const MidiDeviceInfo&); AudioIODeviceType* findType (const String& inputName, const String& outputName); AudioIODeviceType* findType (const String& typeName); + void pickCurrentDeviceTypeWithDevices(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceManager) }; diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp index 7af3fb9c..07109ac7 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,14 @@ namespace juce { +void AudioIODeviceCallback::audioDeviceIOCallbackWithContext ([[maybe_unused]] const float* const* inputChannelData, + [[maybe_unused]] int numInputChannels, + [[maybe_unused]] float* const* outputChannelData, + [[maybe_unused]] int numOutputChannels, + [[maybe_unused]] int numSamples, + [[maybe_unused]] const AudioIODeviceCallbackContext& context) {} + +//============================================================================== AudioIODevice::AudioIODevice (const String& deviceName, const String& deviceTypeName) : name (deviceName), typeName (deviceTypeName) { diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h index 910b572b..96475652 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,13 +25,25 @@ namespace juce class AudioIODevice; +/** + Additional information that may be passed to the AudioIODeviceCallback. + + @tags{Audio} +*/ +struct AudioIODeviceCallbackContext +{ + /** If the host provides this information, this field will be set to point to + an integer holding the current value; otherwise, this will be nullptr. + */ + const uint64_t* hostTimeNs = nullptr; +}; //============================================================================== /** One of these is passed to an AudioIODevice object to stream the audio data in and out. - The AudioIODevice will repeatedly call this class's audioDeviceIOCallback() + The AudioIODevice will repeatedly call this class's audioDeviceIOCallbackWithContext() method on its own high-priority audio thread, when it needs to send or receive the next block of data. @@ -82,12 +94,15 @@ class JUCE_API AudioIODeviceCallback processing into several smaller callbacks to ensure higher audio performance. So make sure your code can cope with reasonable changes in the buffer size from one callback to the next. + @param context Additional information that may be passed to the + AudioIODeviceCallback. */ - virtual void audioDeviceIOCallback (const float** inputChannelData, - int numInputChannels, - float** outputChannelData, - int numOutputChannels, - int numSamples) = 0; + virtual void audioDeviceIOCallbackWithContext (const float* const* inputChannelData, + int numInputChannels, + float* const* outputChannelData, + int numOutputChannels, + int numSamples, + const AudioIODeviceCallbackContext& context); /** Called to indicate that the device is about to start calling back. @@ -115,7 +130,6 @@ class JUCE_API AudioIODeviceCallback virtual void audioDeviceError (const String& errorMessage); }; - //============================================================================== /** Base class for an audio device with synchronised input and output channels. @@ -160,6 +174,21 @@ class JUCE_API AudioIODevice */ virtual StringArray getInputChannelNames() = 0; + //============================================================================== + /** For devices that support a default layout, returns the channels that are enabled in the + default layout. + + Returns nullopt if the device doesn't supply a default layout. + */ + virtual std::optional getDefaultOutputChannels() const { return {}; } + + /** For devices that support a default layout, returns the channels that are enabled in the + default layout. + + Returns nullopt if the device doesn't supply a default layout. + */ + virtual std::optional getDefaultInputChannels() const { return {}; } + //============================================================================== /** Returns the set of sample-rates this device supports. @see getCurrentSampleRate @@ -278,6 +307,8 @@ class JUCE_API AudioIODevice */ virtual int getInputLatencyInSamples() = 0; + /** Returns the workgroup for this device. */ + virtual AudioWorkgroup getWorkgroup() const { return {}; } //============================================================================== /** True if this device can show a pop-up control panel for editing its settings. diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp index fa67206b..4f4cfb60 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -42,48 +42,105 @@ void AudioIODeviceType::callDeviceChangeListeners() } //============================================================================== -#if ! JUCE_MAC -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } +#if JUCE_MAC + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return new CoreAudioClasses::CoreAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } #endif -#if ! JUCE_IOS -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; } +#if JUCE_IOS + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return new iOSAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; } #endif -#if ! (JUCE_WINDOWS && JUCE_WASAPI) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; } +#if JUCE_WINDOWS && JUCE_WASAPI + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode) + { + auto windowsVersion = SystemStats::getOperatingSystemType(); + + if (windowsVersion < SystemStats::WinVista + || (WasapiClasses::isLowLatencyMode (deviceMode) && windowsVersion < SystemStats::Windows10)) + return nullptr; + + return new WasapiClasses::WASAPIAudioIODeviceType (deviceMode); + } + + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode) + { + return createAudioIODeviceType_WASAPI (exclusiveMode ? WASAPIDeviceMode::exclusive + : WASAPIDeviceMode::shared); + } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode) { return nullptr; } + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; } #endif -#if ! (JUCE_WINDOWS && JUCE_DIRECTSOUND) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; } +#if JUCE_WINDOWS && JUCE_DIRECTSOUND + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return new DSoundAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; } #endif -#if ! (JUCE_WINDOWS && JUCE_ASIO) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; } +#if JUCE_WINDOWS && JUCE_ASIO + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return new ASIOAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; } #endif -#if ! (JUCE_LINUX && JUCE_ALSA) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; } +#if (JUCE_LINUX || JUCE_BSD) && JUCE_ALSA + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return createAudioIODeviceType_ALSA_PCMDevices(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; } #endif -#if ! (JUCE_LINUX && JUCE_JACK) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; } +#if (JUCE_LINUX || JUCE_BSD) && JUCE_JACK + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return new JackAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; } #endif -#if ! (JUCE_LINUX && JUCE_BELA) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; } +#if JUCE_LINUX && JUCE_BELA + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return new BelaAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; } #endif -#if ! JUCE_ANDROID -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; } +#if JUCE_ANDROID + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() + { + #if JUCE_USE_ANDROID_OBOE + if (isOboeAvailable()) + return nullptr; + #endif + + #if JUCE_USE_ANDROID_OPENSLES + if (isOpenSLAvailable()) + return nullptr; + #endif + + return new AndroidAudioIODeviceType(); + } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; } #endif -#if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; } +#if JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() + { + return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr; + } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; } #endif -#if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OBOE) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() { return nullptr; } +#if JUCE_ANDROID && JUCE_USE_ANDROID_OBOE + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() + { + return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr; + } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() { return nullptr; } #endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h index 1aa1a675..e3599a45 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -80,9 +80,8 @@ class JUCE_API AudioIODeviceType The scanForDevices() method must have been called to create this list. - @param wantInputNames only really used by DirectSound where devices are split up - into inputs and outputs, this indicates whether to use - the input or output name to refer to a pair of devices. + @param wantInputNames for devices which have separate inputs and outputs + this determines which list of names is returned */ virtual StringArray getDeviceNames (bool wantInputNames = false) const = 0; @@ -117,7 +116,7 @@ class JUCE_API AudioIODeviceType /** A class for receiving events when audio devices are inserted or removed. - You can register an AudioIODeviceType::Listener with an~AudioIODeviceType object + You can register an AudioIODeviceType::Listener with an AudioIODeviceType object using the AudioIODeviceType::addListener() method, and it will be called when devices of that type are added or removed. @@ -149,8 +148,8 @@ class JUCE_API AudioIODeviceType static AudioIODeviceType* createAudioIODeviceType_CoreAudio(); /** Creates an iOS device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_iOSAudio(); - /** Creates a WASAPI device type if it's available on this platform, or returns null. */ - static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode); + /** Creates a WASAPI device type in the specified mode if it's available on this platform, or returns null. */ + static AudioIODeviceType* createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode); /** Creates a DirectSound device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_DirectSound(); /** Creates an ASIO device type if it's available on this platform, or returns null. */ @@ -168,6 +167,11 @@ class JUCE_API AudioIODeviceType /** Creates a Bela device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_Bela(); + #ifndef DOXYGEN + [[deprecated ("You should call the method which takes a WASAPIDeviceMode instead.")]] + static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode); + #endif + protected: explicit AudioIODeviceType (const String& typeName); diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SampleRateHelpers.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SampleRateHelpers.cpp new file mode 100644 index 00000000..77ce16a8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SampleRateHelpers.cpp @@ -0,0 +1,45 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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::SampleRateHelpers +{ + +static inline const std::vector& getAllSampleRates() +{ + static auto sampleRates = [] + { + std::vector result; + constexpr double baseRates[] = { 8000.0, 11025.0, 12000.0 }; + constexpr double maxRate = 768000.0; + + for (auto rate : baseRates) + for (; rate <= maxRate; rate *= 2) + result.insert (std::upper_bound (result.begin(), result.end(), rate), + rate); + + return result; + }(); + + return sampleRates; +} + +} // namespace juce::SampleRateHelpers diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h index 3a0f934e..de00702b 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp index e59d80a0..81643a3f 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -45,7 +45,19 @@ #include "juce_audio_devices.h" +#include "audio_io/juce_SampleRateHelpers.cpp" +#include "midi_io/juce_MidiDevices.cpp" + //============================================================================== +#if JUCE_MAC || JUCE_IOS + #include + #include + #include + #include + #include "midi_io/ump/juce_UMPBytestreamInputHandler.h" + #include "midi_io/ump/juce_UMPU32InputHandler.h" +#endif + #if JUCE_MAC #define Point CarbonDummyPointName #define Component CarbonDummyCompName @@ -55,6 +67,9 @@ #undef Point #undef Component + #include "native/juce_CoreAudio_mac.cpp" + #include "native/juce_CoreMidi_mac.mm" + #elif JUCE_IOS #import #import @@ -64,13 +79,25 @@ #import #endif + #if JUCE_MODULE_AVAILABLE_juce_graphics + #include + #endif + + #include "native/juce_Audio_ios.cpp" + #include "native/juce_CoreMidi_mac.mm" + //============================================================================== #elif JUCE_WINDOWS #if JUCE_WASAPI #include + #include "native/juce_WASAPI_windows.cpp" #endif - #if JUCE_USE_WINRT_MIDI && JUCE_MSVC + #if JUCE_DIRECTSOUND + #include "native/juce_DirectSound_windows.cpp" + #endif + + #if JUCE_USE_WINRT_MIDI && (JUCE_MSVC || JUCE_CLANG) /* If you cannot find any of the header files below then you are probably attempting to use the Windows 10 Bluetooth Low Energy API. For this to work you need to install version 10.0.14393.0 of the Windows Standalone SDK and you may @@ -84,17 +111,18 @@ #include #include - #pragma warning (push) - #pragma warning (disable: 4265) + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4265) #include - #pragma warning (pop) + JUCE_END_IGNORE_WARNINGS_MSVC - #pragma warning (push) - #pragma warning (disable: 4467) + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4467) #include - #pragma warning (pop) + JUCE_END_IGNORE_WARNINGS_MSVC #endif + #include + #include "native/juce_Midi_windows.cpp" + #if JUCE_ASIO /* This is very frustrating - we only need to use a handful of definitions from a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy @@ -116,10 +144,11 @@ needed - so to simplify things, you could just copy these into your JUCE directory). */ #include + #include "native/juce_ASIO_windows.cpp" #endif //============================================================================== -#elif JUCE_LINUX +#elif JUCE_LINUX || JUCE_BSD #if JUCE_ALSA /* Got an include error here? If so, you've either not got ALSA installed, or you've not got your paths set up correctly to find its header files. @@ -129,7 +158,10 @@ If you don't have the ALSA library and don't want to build JUCE with audio support, just set the JUCE_ALSA flag to 0. */ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-length-array") #include + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + #include "native/juce_ALSA_linux.cpp" #endif #if JUCE_JACK @@ -142,102 +174,76 @@ JUCE with low latency audio support, just set the JUCE_JACK flag to 0. */ #include + #include "native/juce_JackAudio_linux.cpp" #endif - #if JUCE_BELA + #if (JUCE_LINUX && JUCE_BELA) /* Got an include error here? If so, you've either not got the bela headers installed, or you've not got your paths set up correctly to find its header files. */ #include #include + #include + #include "native/juce_Bela_linux.cpp" #endif #undef SIZEOF -//============================================================================== -#elif JUCE_ANDROID - - #if JUCE_USE_ANDROID_OPENSLES - #include - #include - #include - #endif - - #if JUCE_USE_ANDROID_OBOE - #if JUCE_USE_ANDROID_OPENSLES - #error "Oboe cannot be enabled at the same time as openSL! Please disable JUCE_USE_ANDROID_OPENSLES" - #endif - - #include + #if ! JUCE_BELA + #include + #include "native/juce_Midi_linux.cpp" #endif -#endif - -#include "audio_io/juce_AudioDeviceManager.cpp" -#include "audio_io/juce_AudioIODevice.cpp" -#include "audio_io/juce_AudioIODeviceType.cpp" -#include "midi_io/juce_MidiMessageCollector.cpp" -#include "midi_io/juce_MidiDevices.cpp" -#include "sources/juce_AudioSourcePlayer.cpp" -#include "sources/juce_AudioTransportSource.cpp" -#include "native/juce_MidiDataConcatenator.h" - -//============================================================================== -#if JUCE_MAC - #include "native/juce_mac_CoreAudio.cpp" - #include "native/juce_mac_CoreMidi.cpp" - -//============================================================================== -#elif JUCE_IOS - #include "native/juce_ios_Audio.cpp" - #include "native/juce_mac_CoreMidi.cpp" - //============================================================================== -#elif JUCE_WINDOWS - - #if JUCE_WASAPI - #include "native/juce_win32_WASAPI.cpp" - #endif +#elif JUCE_ANDROID - #if JUCE_DIRECTSOUND - #include "native/juce_win32_DirectSound.cpp" - #endif +namespace juce +{ + using RealtimeThreadFactory = pthread_t (*) (void* (*) (void*), void*); + RealtimeThreadFactory getAndroidRealtimeThreadFactory(); +} // namespace juce - #include "native/juce_win32_Midi.cpp" +#include "native/juce_Audio_android.cpp" - #if JUCE_ASIO - #include "native/juce_win32_ASIO.cpp" - #endif + #include + #include "native/juce_Midi_android.cpp" -//============================================================================== -#elif JUCE_LINUX - #if JUCE_ALSA - #include "native/juce_linux_ALSA.cpp" - #endif + #if JUCE_USE_ANDROID_OPENSLES || JUCE_USE_ANDROID_OBOE + #include "native/juce_HighPerformanceAudioHelpers_android.h" - #if JUCE_JACK - #include "native/juce_linux_JackAudio.cpp" - #endif + #if JUCE_USE_ANDROID_OPENSLES + #include + #include + #include + #include "native/juce_OpenSL_android.cpp" + #endif - #if JUCE_BELA - #include "native/juce_linux_Bela.cpp" + #if JUCE_USE_ANDROID_OBOE + #if JUCE_USE_ANDROID_OPENSLES + #error "Oboe cannot be enabled at the same time as openSL! Please disable JUCE_USE_ANDROID_OPENSLES" + #endif + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter", + "-Wzero-as-null-pointer-constant", + "-Winconsistent-missing-destructor-override", + "-Wshadow-field-in-constructor", + "-Wshadow-field", + "-Wsign-conversion", + "-Wswitch-enum") + #include + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + #include "native/juce_Oboe_android.cpp" + #endif #else - #include "native/juce_linux_Midi.cpp" - #endif - -//============================================================================== -#elif JUCE_ANDROID - #include "native/juce_android_Audio.cpp" - #include "native/juce_android_Midi.cpp" - - #if JUCE_USE_ANDROID_OPENSLES - #include "native/juce_android_OpenSL.cpp" +// No audio library, so no way to create realtime threads. + namespace juce + { + RealtimeThreadFactory getAndroidRealtimeThreadFactory() { return nullptr; } + } #endif - #if JUCE_USE_ANDROID_OBOE - #include "native/juce_android_Oboe.cpp" - #endif #endif #if ! JUCE_SYSTEMAUDIOVOL_IMPLEMENTED @@ -250,3 +256,10 @@ namespace juce bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; } } #endif + +#include "audio_io/juce_AudioDeviceManager.cpp" +#include "audio_io/juce_AudioIODevice.cpp" +#include "audio_io/juce_AudioIODeviceType.cpp" +#include "midi_io/juce_MidiMessageCollector.cpp" +#include "sources/juce_AudioSourcePlayer.cpp" +#include "sources/juce_AudioTransportSource.cpp" diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h index 2027b774..b022e36f 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,18 +25,19 @@ The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. For details about the syntax and how to create or use a module, see the - JUCE Module Format.txt file. + JUCE Module Format.md file. BEGIN_JUCE_MODULE_DECLARATION ID: juce_audio_devices vendor: juce - version: 5.4.7 + version: 7.0.10 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 + minimumCppStandard: 17 dependencies: juce_audio_basics, juce_events OSXFrameworks: CoreAudio CoreMIDI AudioToolbox @@ -63,9 +64,8 @@ /** Config: JUCE_USE_WINRT_MIDI 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 regular - Win32 MIDI API. + Update) and later. If you enable this flag then older 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 and you may need to add the path to the WinRT headers. The path to the @@ -89,21 +89,12 @@ #endif /** Config: JUCE_WASAPI - Enables WASAPI audio devices (Windows Vista and above). See also the - JUCE_WASAPI_EXCLUSIVE flag. + Enables WASAPI audio devices (Windows Vista and above). */ #ifndef JUCE_WASAPI #define JUCE_WASAPI 1 #endif -/** Config: JUCE_WASAPI_EXCLUSIVE - Enables WASAPI audio devices in exclusive mode (Windows Vista and above). -*/ -#ifndef JUCE_WASAPI_EXCLUSIVE - #define JUCE_WASAPI_EXCLUSIVE 0 -#endif - - /** Config: JUCE_DIRECTSOUND Enables DirectSound audio (MS Windows only). */ @@ -133,30 +124,27 @@ #endif /** Config: JUCE_USE_ANDROID_OBOE - *** - DEVELOPER PREVIEW - Oboe is currently in developer preview and - is in active development. This preview allows for early access - and evaluation for developers targeting Android platform. - *** - - Enables Oboe devices (Android only, API 16 or above). Requires - Oboe repository path to be specified in Android exporter. + Enables Oboe devices (Android only). */ - #ifndef JUCE_USE_ANDROID_OBOE - #define JUCE_USE_ANDROID_OBOE 0 + #define JUCE_USE_ANDROID_OBOE 1 #endif -#if JUCE_USE_ANDROID_OBOE && JUCE_ANDROID_API_VERSION < 16 - #undef JUCE_USE_ANDROID_OBOE - #define JUCE_USE_ANDROID_OBOE 0 +/** Config: JUCE_USE_OBOE_STABILIZED_CALLBACK + If JUCE_USE_ANDROID_OBOE is enabled, enabling this will wrap output audio + streams in the oboe::StabilizedCallback class. This class attempts to keep + the CPU spinning to avoid it being scaled down on certain devices. + (Android only). +*/ +#ifndef JUCE_USE_ANDROID_OBOE_STABILIZED_CALLBACK + #define JUCE_USE_ANDROID_OBOE_STABILIZED_CALLBACK 0 #endif /** Config: JUCE_USE_ANDROID_OPENSLES Enables OpenSLES devices (Android only). */ #ifndef JUCE_USE_ANDROID_OPENSLES - #if ! JUCE_USE_ANDROID_OBOE && JUCE_ANDROID_API_VERSION >= 9 + #if ! JUCE_USE_ANDROID_OBOE #define JUCE_USE_ANDROID_OPENSLES 1 #else #define JUCE_USE_ANDROID_OPENSLES 0 @@ -174,6 +162,22 @@ //============================================================================== #include "midi_io/juce_MidiDevices.h" #include "midi_io/juce_MidiMessageCollector.h" + +namespace juce +{ + /** Available modes for the WASAPI audio device. + + Pass one of these to the AudioIODeviceType::createAudioIODeviceType_WASAPI() + method to create a WASAPI AudioIODeviceType object in this mode. + */ + enum class WASAPIDeviceMode + { + shared, + exclusive, + sharedLowLatency + }; +} + #include "audio_io/juce_AudioIODevice.h" #include "audio_io/juce_AudioIODeviceType.h" #include "audio_io/juce_SystemAudioVolume.h" @@ -182,5 +186,5 @@ #include "audio_io/juce_AudioDeviceManager.h" #if JUCE_IOS - #include "native/juce_ios_Audio.h" + #include "native/juce_Audio_ios.h" #endif diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm index 6ff08e9b..9d624ed9 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp index ff8c1762..efb3df31 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,87 @@ namespace juce { +class MidiDeviceListConnectionBroadcaster final : private AsyncUpdater +{ +public: + ~MidiDeviceListConnectionBroadcaster() override + { + cancelPendingUpdate(); + } + + MidiDeviceListConnection::Key add (std::function callback) + { + JUCE_ASSERT_MESSAGE_THREAD + return callbacks.emplace (key++, std::move (callback)).first->first; + } + + void remove (const MidiDeviceListConnection::Key k) + { + JUCE_ASSERT_MESSAGE_THREAD + callbacks.erase (k); + } + + void notify() + { + if (MessageManager::getInstance()->isThisTheMessageThread()) + { + cancelPendingUpdate(); + + const State newState; + + if (std::exchange (lastNotifiedState, newState) != newState) + for (auto it = callbacks.begin(); it != callbacks.end();) + NullCheckedInvocation::invoke ((it++)->second); + } + else + { + triggerAsyncUpdate(); + } + } + + static auto& get() + { + static MidiDeviceListConnectionBroadcaster result; + return result; + } + +private: + MidiDeviceListConnectionBroadcaster() = default; + + class State + { + Array ins = MidiInput::getAvailableDevices(), outs = MidiOutput::getAvailableDevices(); + auto tie() const { return std::tie (ins, outs); } + + public: + bool operator== (const State& other) const { return tie() == other.tie(); } + bool operator!= (const State& other) const { return tie() != other.tie(); } + }; + + void handleAsyncUpdate() override + { + notify(); + } + + std::map> callbacks; + State lastNotifiedState; + MidiDeviceListConnection::Key key = 0; +}; + +//============================================================================== +MidiDeviceListConnection::~MidiDeviceListConnection() noexcept +{ + if (broadcaster != nullptr) + broadcaster->remove (key); +} + +//============================================================================== +void MidiInputCallback::handlePartialSysexMessage ([[maybe_unused]] MidiInput* source, + [[maybe_unused]] const uint8* messageData, + [[maybe_unused]] int numBytesSoFar, + [[maybe_unused]] double timestamp) {} + +//============================================================================== MidiOutput::MidiOutput (const String& deviceName, const String& deviceIdentifier) : Thread ("midi out"), deviceInfo (deviceName, deviceIdentifier) { @@ -30,12 +111,8 @@ MidiOutput::MidiOutput (const String& deviceName, const String& deviceIdentifier void MidiOutput::sendBlockOfMessagesNow (const MidiBuffer& buffer) { - MidiBuffer::Iterator i (buffer); - MidiMessage message; - int samplePosition; // Note: Not actually used, so no need to initialise. - - while (i.getNextEvent (message, samplePosition)) - sendMessageNow (message); + for (const auto metadata : buffer) + sendMessageNow (metadata.getMessage()); } void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer, @@ -50,13 +127,10 @@ void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer, auto timeScaleFactor = 1000.0 / samplesPerSecondForBuffer; - const uint8* data; - int len, time; - - for (MidiBuffer::Iterator i (buffer); i.getNextEvent (data, len, time);) + for (const auto metadata : buffer) { - auto eventTime = millisecondCounterToStartAt + timeScaleFactor * time; - auto* m = new PendingMessage (data, len, eventTime); + auto eventTime = millisecondCounterToStartAt + timeScaleFactor * metadata.samplePosition; + auto* m = new PendingMessage (metadata.data, metadata.numBytes, eventTime); const ScopedLock sl (lock); @@ -94,7 +168,7 @@ void MidiOutput::clearAllPendingMessages() void MidiOutput::startBackgroundThread() { - startThread (9); + startThread (Priority::high); } void MidiOutput::stopBackgroundThread() diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h index f56272bb..806fc720 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -22,6 +22,87 @@ namespace juce { + +class MidiDeviceListConnectionBroadcaster; + +/** + To find out when the available MIDI devices change, call MidiDeviceListConnection::make(), + passing a lambda that will be called on each configuration change. + + To stop the lambda receiving callbacks, destroy the MidiDeviceListConnection instance returned + from make(), or call reset() on it. + + @code + // Start listening for configuration changes + auto connection = MidiDeviceListConnection::make ([] + { + // This will print a message when devices are connected/disconnected + DBG ("MIDI devices changed"); + }); + + // Stop listening + connection.reset(); + @endcode + + @tags{Audio} +*/ +class MidiDeviceListConnection +{ +public: + using Key = uint64_t; + + /** Constructs an inactive connection. + */ + MidiDeviceListConnection() = default; + + MidiDeviceListConnection (const MidiDeviceListConnection&) = delete; + MidiDeviceListConnection (MidiDeviceListConnection&& other) noexcept + : broadcaster (std::exchange (other.broadcaster, nullptr)), + key (std::exchange (other.key, Key{})) + { + } + + MidiDeviceListConnection& operator= (const MidiDeviceListConnection&) = delete; + MidiDeviceListConnection& operator= (MidiDeviceListConnection&& other) noexcept + { + MidiDeviceListConnection (std::move (other)).swap (*this); + return *this; + } + + ~MidiDeviceListConnection() noexcept; + + /** Clears this connection. + + If this object had an active connection, that connection will be deactivated, and the + corresponding callback will be removed from the MidiDeviceListConnectionBroadcaster. + */ + void reset() noexcept + { + MidiDeviceListConnection().swap (*this); + } + + /** Registers a function to be called whenever the midi device list changes. + + The callback will only be active for as long as the return MidiDeviceListConnection remains + alive. To stop receiving device change notifications, destroy the Connection object, e.g. + by allowing it to fall out of scope. + */ + static MidiDeviceListConnection make (std::function); + +private: + MidiDeviceListConnection (MidiDeviceListConnectionBroadcaster* b, const Key k) + : broadcaster (b), key (k) {} + + void swap (MidiDeviceListConnection& other) noexcept + { + std::swap (other.broadcaster, broadcaster); + std::swap (other.key, key); + } + + MidiDeviceListConnectionBroadcaster* broadcaster = nullptr; + Key key = {}; +}; + //============================================================================== /** This struct contains information about a MIDI input or output device. @@ -61,8 +142,9 @@ struct MidiDeviceInfo 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); } + auto tie() const { return std::tie (name, identifier); } + bool operator== (const MidiDeviceInfo& other) const noexcept { return tie() == other.tie(); } + bool operator!= (const MidiDeviceInfo& other) const noexcept { return tie() != other.tie(); } }; class MidiInputCallback; @@ -108,7 +190,7 @@ class JUCE_API MidiInput final */ static std::unique_ptr openDevice (const String& deviceIdentifier, MidiInputCallback* callback); - #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN + #if JUCE_LINUX || JUCE_BSD || 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 @@ -157,19 +239,25 @@ class JUCE_API MidiInput final void setName (const String& newName) noexcept { deviceInfo.name = newName; } //============================================================================== - /** Deprecated. */ + #ifndef DOXYGEN + [[deprecated ("Use getAvailableDevices instead.")]] static StringArray getDevices(); - /** Deprecated. */ + [[deprecated ("Use getDefaultDevice instead.")]] static int getDefaultDeviceIndex(); - /** Deprecated. */ + [[deprecated ("Use openDevice that takes a device identifier instead.")]] static std::unique_ptr openDevice (int, MidiInputCallback*); + #endif + + /** @internal */ + class Pimpl; private: //============================================================================== explicit MidiInput (const String&, const String&); MidiDeviceInfo deviceInfo; - void* internal = nullptr; + + std::unique_ptr internal; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput) }; @@ -219,10 +307,7 @@ class JUCE_API MidiInputCallback virtual void handlePartialSysexMessage (MidiInput* source, const uint8* messageData, int numBytesSoFar, - double timestamp) - { - ignoreUnused (source, messageData, numBytesSoFar, timestamp); - } + double timestamp); }; //============================================================================== @@ -264,7 +349,7 @@ class JUCE_API MidiOutput final : private Thread */ static std::unique_ptr openDevice (const String& deviceIdentifier); - #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN + #if JUCE_LINUX || JUCE_BSD || 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 @@ -337,13 +422,23 @@ class JUCE_API MidiOutput final : private Thread */ void stopBackgroundThread(); + /** Returns true if the background thread used to send blocks of data is running. + @see startBackgroundThread, stopBackgroundThread + */ + bool isBackgroundThreadRunning() const noexcept { return isThreadRunning(); } + //============================================================================== - /** Deprecated. */ + #ifndef DOXYGEN + [[deprecated ("Use getAvailableDevices instead.")]] static StringArray getDevices(); - /** Deprecated. */ + [[deprecated ("Use getDefaultDevice instead.")]] static int getDefaultDeviceIndex(); - /** Deprecated. */ + [[deprecated ("Use openDevice that takes a device identifier instead.")]] static std::unique_ptr openDevice (int); + #endif + + /** @internal */ + class Pimpl; private: //============================================================================== @@ -363,7 +458,9 @@ class JUCE_API MidiOutput final : private Thread void run() override; MidiDeviceInfo deviceInfo; - void* internal = nullptr; + + std::unique_ptr internal; + CriticalSection lock; PendingMessage* firstMessage = nullptr; 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 77ff5789..f6815fb7 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -56,7 +56,7 @@ void MidiMessageCollector::addMessageToQueue (const MidiMessage& message) // the messages that come in here need to be time-stamped correctly - see MidiInput // for details of what the number should be. - jassert (message.getTimeStamp() != 0); + jassert (! approximatelyEqual (message.getTimeStamp(), 0.0)); auto sampleNumber = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate); @@ -90,33 +90,28 @@ void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, int startSample = 0; int scale = 1 << 16; - const uint8* midiData; - int numBytes, samplePosition; - - MidiBuffer::Iterator iter (incomingMessages); - if (numSourceSamples > numSamples) { // if our list of events is longer than the buffer we're being // asked for, scale them down to squeeze them all in.. const int maxBlockLengthToUse = numSamples << 5; + auto iter = incomingMessages.cbegin(); + if (numSourceSamples > maxBlockLengthToUse) { startSample = numSourceSamples - maxBlockLengthToUse; numSourceSamples = maxBlockLengthToUse; - iter.setNextSamplePosition (startSample); + iter = incomingMessages.findNextSamplePosition (startSample); } scale = (numSamples << 10) / numSourceSamples; - while (iter.getNextEvent (midiData, numBytes, samplePosition)) + std::for_each (iter, incomingMessages.cend(), [&] (const MidiMessageMetadata& meta) { - samplePosition = ((samplePosition - startSample) * scale) >> 10; - - destBuffer.addEvent (midiData, numBytes, - jlimit (0, numSamples - 1, samplePosition)); - } + const auto pos = ((meta.samplePosition - startSample) * scale) >> 10; + destBuffer.addEvent (meta.data, meta.numBytes, jlimit (0, numSamples - 1, pos)); + }); } else { @@ -124,17 +119,20 @@ void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, // towards the end of the buffer startSample = numSamples - numSourceSamples; - while (iter.getNextEvent (midiData, numBytes, samplePosition)) - { - destBuffer.addEvent (midiData, numBytes, - jlimit (0, numSamples - 1, samplePosition + startSample)); - } + for (const auto metadata : incomingMessages) + destBuffer.addEvent (metadata.data, metadata.numBytes, + jlimit (0, numSamples - 1, metadata.samplePosition + startSample)); } incomingMessages.clear(); } } +void MidiMessageCollector::ensureStorageAllocated (size_t bytes) +{ + incomingMessages.ensureSize (bytes); +} + //============================================================================== void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) { diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h index 17aa76f7..52cec2e8 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,14 +28,14 @@ namespace juce Collects incoming realtime MIDI messages and turns them into blocks suitable for processing by a block-based audio callback. - The class can also be used as either a MidiKeyboardStateListener or a MidiInputCallback + The class can also be used as either a MidiKeyboardState::Listener or a MidiInputCallback so it can easily use a midi input or keyboard component as its source. @see MidiMessage, MidiInput @tags{Audio} */ -class JUCE_API MidiMessageCollector : public MidiKeyboardStateListener, +class JUCE_API MidiMessageCollector : public MidiKeyboardState::Listener, public MidiInputCallback { public: @@ -80,6 +80,14 @@ class JUCE_API MidiMessageCollector : public MidiKeyboardStateListener, */ void removeNextBlockOfMessages (MidiBuffer& destBuffer, int numSamples); + /** Preallocates storage for collected messages. + + This can be called before audio processing begins to ensure that there + is sufficient space for the expected MIDI messages, in order to avoid + allocations within the audio callback. + */ + void ensureStorageAllocated (size_t bytes); + //============================================================================== /** @internal */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPBytestreamInputHandler.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPBytestreamInputHandler.h new file mode 100644 index 00000000..69008edb --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPBytestreamInputHandler.h @@ -0,0 +1,141 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + A base class for classes which convert bytestream midi to other formats. + + @tags{Audio} +*/ +struct BytestreamInputHandler +{ + virtual ~BytestreamInputHandler() noexcept = default; + + virtual void reset() = 0; + virtual void pushMidiData (const void* data, int bytes, double time) = 0; +}; + +/** + Parses a continuous bytestream and emits complete MidiMessages whenever a full + message is received. + + @tags{Audio} +*/ +struct BytestreamToBytestreamHandler : public BytestreamInputHandler +{ + BytestreamToBytestreamHandler (MidiInput& i, MidiInputCallback& c) + : input (i), callback (c), concatenator (2048) {} + + /** + Provides an `operator()` which can create an input handler for a given + MidiInput. + + All handler classes should have a similar Factory to facilitate + creation of handlers in generic contexts. + */ + class Factory + { + public: + explicit Factory (MidiInputCallback* c) + : callback (c) {} + + std::unique_ptr operator() (MidiInput& i) const + { + if (callback != nullptr) + return std::make_unique (i, *callback); + + jassertfalse; + return {}; + } + + private: + MidiInputCallback* callback = nullptr; + }; + + void reset() override { concatenator.reset(); } + + void pushMidiData (const void* data, int bytes, double time) override + { + concatenator.pushMidiData (data, bytes, time, &input, callback); + } + + MidiInput& input; + MidiInputCallback& callback; + MidiDataConcatenator concatenator; +}; + +/** + Parses a continuous MIDI 1.0 bytestream, and emits full messages in the requested + UMP format. + + @tags{Audio} +*/ +struct BytestreamToUMPHandler : public BytestreamInputHandler +{ + BytestreamToUMPHandler (PacketProtocol protocol, Receiver& c) + : recipient (c), dispatcher (protocol, 2048) {} + + /** + Provides an `operator()` which can create an input handler for a given + MidiInput. + + All handler classes should have a similar Factory to facilitate + creation of handlers in generic contexts. + */ + class Factory + { + public: + Factory (PacketProtocol p, Receiver& c) + : protocol (p), callback (c) {} + + std::unique_ptr operator() (MidiInput&) const + { + return std::make_unique (protocol, callback); + } + + private: + PacketProtocol protocol; + Receiver& callback; + }; + + void reset() override { dispatcher.reset(); } + + void pushMidiData (const void* data, int bytes, double time) override + { + const auto* ptr = static_cast (data); + dispatcher.dispatch (ptr, ptr + bytes, time, [&] (const View& v) + { + recipient.packetReceived (v, time); + }); + } + + Receiver& recipient; + BytestreamToUMPDispatcher dispatcher; +}; + +} // juce::universal_midi_packets + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h new file mode 100644 index 00000000..85af85f7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h @@ -0,0 +1,153 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#ifndef DOXYGEN + +namespace juce::universal_midi_packets +{ + +/** + A base class for classes which convert Universal MIDI Packets to other + formats. + + @tags{Audio} +*/ +struct U32InputHandler +{ + virtual ~U32InputHandler() noexcept = default; + + virtual void reset() = 0; + virtual void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) = 0; +}; + +/** + Parses a continuous stream of U32 words and emits complete MidiMessages whenever a full + message is received. + + @tags{Audio} +*/ +struct U32ToBytestreamHandler : public U32InputHandler +{ + U32ToBytestreamHandler (MidiInput& i, MidiInputCallback& c) + : input (i), callback (c), dispatcher (2048) {} + + /** + Provides an `operator()` which can create an input handler for a given + MidiInput. + + All handler classes should have a similar Factory to facilitate + creation of handlers in generic contexts. + */ + class Factory + { + public: + explicit Factory (MidiInputCallback* c) + : callback (c) {} + + std::unique_ptr operator() (MidiInput& i) const + { + if (callback != nullptr) + return std::make_unique (i, *callback); + + jassertfalse; + return {}; + } + + private: + MidiInputCallback* callback = nullptr; + }; + + void reset() override { dispatcher.reset(); } + + void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) override + { + dispatcher.dispatch (begin, end, time, [this] (const BytestreamMidiView& m) + { + callback.handleIncomingMidiMessage (&input, m.getMessage()); + }); + } + + MidiInput& input; + MidiInputCallback& callback; + ToBytestreamDispatcher dispatcher; +}; + +/** + Parses a continuous stream of U32 words and emits full messages in the requested + UMP format. + + @tags{Audio} +*/ +struct U32ToUMPHandler : public U32InputHandler +{ + U32ToUMPHandler (PacketProtocol protocol, Receiver& c) + : recipient (c), converter (protocol) {} + + /** + Provides an `operator()` which can create an input handler for a given + MidiInput. + + All handler classes should have a similar Factory to facilitate + creation of handlers in generic contexts. + */ + class Factory + { + public: + Factory (PacketProtocol p, Receiver& c) + : protocol (p), callback (c) {} + + std::unique_ptr operator() (MidiInput&) const + { + return std::make_unique (protocol, callback); + } + + private: + PacketProtocol protocol; + Receiver& callback; + }; + + void reset() override + { + dispatcher.reset(); + converter.reset(); + } + + void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) override + { + dispatcher.dispatch (begin, end, time, [this] (const View& view, double thisTime) + { + converter.convert (view, [&] (const View& converted) + { + recipient.packetReceived (converted, thisTime); + }); + }); + } + + Receiver& recipient; + Dispatcher dispatcher; + GenericUMPConverter converter; +}; + +} // namespace juce::universal_midi_packets + + +#endif 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/rmsl/juce/JuceMidiSupport.java similarity index 92% rename from JuceLibraryCode/modules/juce_audio_devices/native/java/app/com/roli/juce/JuceMidiSupport.java rename to JuceLibraryCode/modules/juce_audio_devices/native/java/app/com/rmsl/juce/JuceMidiSupport.java index 5bc4f3c4..bae47bff 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/rmsl/juce/JuceMidiSupport.java @@ -1,7 +1,30 @@ -package com.roli.juce; +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +package com.rmsl.juce; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; @@ -21,6 +44,7 @@ import android.bluetooth.BluetoothDevice; import android.media.midi.MidiOutputPort; import android.media.midi.MidiReceiver; +import android.os.Build; import android.os.ParcelUuid; import android.util.Log; import android.util.Pair; @@ -34,6 +58,7 @@ import java.util.List; import static android.content.Context.MIDI_SERVICE; +import static android.content.Context.BLUETOOTH_SERVICE; public class JuceMidiSupport { @@ -55,10 +80,18 @@ public interface JuceMidiPort String getName (); } + static BluetoothAdapter getDefaultBluetoothAdapter (Context ctx) + { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S_V2) + return BluetoothAdapter.getDefaultAdapter(); + + return ((BluetoothManager) ctx.getSystemService (BLUETOOTH_SERVICE)).getAdapter(); + } + //============================================================================== - public static class BluetoothManager extends ScanCallback + public static class BluetoothMidiManager extends ScanCallback { - BluetoothManager (Context contextToUse) + BluetoothMidiManager (Context contextToUse) { appContext = contextToUse; } @@ -70,7 +103,7 @@ public String[] getMidiBluetoothAddresses () public String getHumanReadableStringForBluetoothAddress (String address) { - BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter ().getRemoteDevice (address); + BluetoothDevice btDevice = getDefaultBluetoothAdapter (appContext).getRemoteDevice (address); return btDevice.getName (); } @@ -81,11 +114,11 @@ public int getBluetoothDeviceStatus (String address) public void startStopScan (boolean shouldStart) { - BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter (); + BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter (appContext); if (bluetoothAdapter == null) { - Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter"); + Log.d ("JUCE", "BluetoothMidiManager error: could not get default Bluetooth adapter"); return; } @@ -93,7 +126,7 @@ public void startStopScan (boolean shouldStart) if (bluetoothLeScanner == null) { - Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner"); + Log.d ("JUCE", "BluetoothMidiManager error: could not get Bluetooth LE scanner"); return; } @@ -118,7 +151,7 @@ public void startStopScan (boolean shouldStart) public boolean pairBluetoothMidiDevice (String address) { - BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter ().getRemoteDevice (address); + BluetoothDevice btDevice = getDefaultBluetoothAdapter (appContext).getRemoteDevice (address); if (btDevice == null) { @@ -521,12 +554,8 @@ public MidiDeviceManager (Context contextToUse) return; } - openPorts = new HashMap> (); - midiDevices = new ArrayList> (); - openTasks = new HashMap (); - btDevicesPairing = new HashMap (); - MidiDeviceInfo[] foundDevices = manager.getDevices (); + for (MidiDeviceInfo info : foundDevices) onDeviceAdded (info); @@ -788,6 +817,7 @@ public void removePort (MidiPortPath path) openPorts.remove (path); } + @Override public void onDeviceAdded (MidiDeviceInfo info) { // only add standard midi devices @@ -797,6 +827,7 @@ public void onDeviceAdded (MidiDeviceInfo info) manager.openDevice (info, this, null); } + @Override public void onDeviceRemoved (MidiDeviceInfo info) { synchronized (MidiDeviceManager.class) @@ -834,8 +865,11 @@ public void onDeviceRemoved (MidiDeviceInfo info) midiDevices.remove (devicePair); } } + + handleDevicesChanged(); } + @Override public void onDeviceStatusChanged (MidiDeviceStatus status) { } @@ -911,6 +945,7 @@ public void onDeviceOpenedDelayed (MidiDevice theDevice) BluetoothGatt gatt = openTasks.get (deviceID).getGatt (); openTasks.remove (deviceID); midiDevices.add (new Pair (theDevice, gatt)); + handleDevicesChanged(); } } else { @@ -951,7 +986,6 @@ public String getPortName (MidiPortPath path) { for (MidiDeviceInfo info : deviceInfos) { - int localIndex = 0; if (info.getId () == path.deviceId) { for (MidiDeviceInfo.PortInfo portInfo : info.getPorts ()) @@ -1026,11 +1060,11 @@ private Pair getMidiDevicePairForId (int deviceId) } private MidiManager manager; - private HashMap btDevicesPairing; - private HashMap openTasks; - private ArrayList> midiDevices; + private HashMap btDevicesPairing = new HashMap(); + private HashMap openTasks = new HashMap(); + private ArrayList> midiDevices = new ArrayList>(); private MidiDeviceInfo[] deviceInfos; - private HashMap> openPorts; + private HashMap> openPorts = new HashMap>(); private Context appContext = null; } @@ -1048,9 +1082,9 @@ public static MidiDeviceManager getAndroidMidiDeviceManager (Context context) return midiDeviceManager; } - public static BluetoothManager getAndroidBluetoothManager (Context context) + public static BluetoothMidiManager getAndroidBluetoothManager (Context context) { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter (); + BluetoothAdapter adapter = getDefaultBluetoothAdapter (context); if (adapter == null) return null; @@ -1061,12 +1095,15 @@ public static BluetoothManager getAndroidBluetoothManager (Context context) synchronized (JuceMidiSupport.class) { if (bluetoothManager == null) - bluetoothManager = new BluetoothManager (context); + bluetoothManager = new BluetoothMidiManager (context); } return bluetoothManager; } + // To be called when devices become (un)available + private native static void handleDevicesChanged(); + private static MidiDeviceManager midiDeviceManager = null; - private static BluetoothManager bluetoothManager = null; + private static BluetoothMidiManager bluetoothManager = null; } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ALSA_linux.cpp similarity index 96% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_ALSA_linux.cpp index 0211eb90..a389e79a 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ALSA_linux.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -50,17 +50,15 @@ namespace static void getDeviceSampleRates (snd_pcm_t* handle, Array& rates) { - const int ratesToTry[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; - snd_pcm_hw_params_t* hwParams; snd_pcm_hw_params_alloca (&hwParams); - for (int i = 0; ratesToTry[i] != 0; ++i) + for (const auto rateToTry : SampleRateHelpers::getAllSampleRates()) { if (snd_pcm_hw_params_any (handle, hwParams) >= 0 - && snd_pcm_hw_params_test_rate (handle, hwParams, (unsigned int) ratesToTry[i], 0) == 0) + && snd_pcm_hw_params_test_rate (handle, hwParams, (unsigned int) rateToTry, 0) == 0) { - rates.addIfNotAlreadyThere ((double) ratesToTry[i]); + rates.addIfNotAlreadyThere (rateToTry); } } } @@ -171,7 +169,7 @@ class ALSADevice error << "The device \"" << deviceID << "\" is not available."; else error << "Could not open " << (forInput ? "input" : "output") << " device \"" << deviceID - << "\": " << snd_strerror(err) << " (" << err << ")"; + << "\": " << snd_strerror (err) << " (" << err << ")"; JUCE_ALSA_LOG ("snd_pcm_open failed; " << error); } @@ -483,7 +481,7 @@ class ALSADevice }; //============================================================================== -class ALSAThread : public Thread +class ALSAThread final : public Thread { public: ALSAThread (const String& inputDeviceID, const String& outputDeviceID) @@ -620,7 +618,7 @@ class ALSAThread : public Thread if (outputDevice != nullptr && JUCE_ALSA_FAILED (snd_pcm_prepare (outputDevice->handle))) return; - startThread (9); + startThread (Priority::high); int count = 1000; @@ -713,11 +711,12 @@ class ALSAThread : public Thread if (callback != nullptr) { - callback->audioDeviceIOCallback (inputChannelDataForCallback.getRawDataPointer(), - inputChannelDataForCallback.size(), - outputChannelDataForCallback.getRawDataPointer(), - outputChannelDataForCallback.size(), - bufferSize); + callback->audioDeviceIOCallbackWithContext (inputChannelDataForCallback.getRawDataPointer(), + inputChannelDataForCallback.size(), + outputChannelDataForCallback.getRawDataPointer(), + outputChannelDataForCallback.size(), + bufferSize, + {}); } else { @@ -791,8 +790,8 @@ class ALSAThread : public Thread //============================================================================== const String inputId, outputId; std::unique_ptr outputDevice, inputDevice; - int numCallbacks = 0; - bool audioIoInProgress = false; + std::atomic numCallbacks { 0 }; + std::atomic audioIoInProgress { false }; CriticalSection callbackLock; @@ -839,7 +838,7 @@ class ALSAThread : public Thread //============================================================================== -class ALSAAudioIODevice : public AudioIODevice +class ALSAAudioIODevice final : public AudioIODevice { public: ALSAAudioIODevice (const String& deviceName, @@ -968,7 +967,7 @@ class ALSAAudioIODevice : public AudioIODevice //============================================================================== -class ALSAAudioIODeviceType : public AudioIODeviceType +class ALSAAudioIODeviceType final : public AudioIODeviceType { public: ALSAAudioIODeviceType (bool onlySoundcards, const String& deviceTypeName) @@ -980,7 +979,7 @@ class ALSAAudioIODeviceType : public AudioIODeviceType #endif } - ~ALSAAudioIODeviceType() + ~ALSAAudioIODeviceType() override { #if ! JUCE_ALSA_LOGGING snd_lib_error_set_handler (nullptr); @@ -990,7 +989,7 @@ class ALSAAudioIODeviceType : public AudioIODeviceType } //============================================================================== - void scanForDevices() + void scanForDevices() override { if (hasScanned) return; @@ -1012,14 +1011,14 @@ class ALSAAudioIODeviceType : public AudioIODeviceType outputNames.appendNumbersToDuplicates (false, true); } - StringArray getDeviceNames (bool wantInputNames) const + StringArray getDeviceNames (bool wantInputNames) const override { jassert (hasScanned); // need to call scanForDevices() before doing this return wantInputNames ? inputNames : outputNames; } - int getDefaultDeviceIndex (bool forInput) const + int getDefaultDeviceIndex (bool forInput) const override { jassert (hasScanned); // need to call scanForDevices() before doing this @@ -1027,9 +1026,9 @@ class ALSAAudioIODeviceType : public AudioIODeviceType return idx >= 0 ? idx : 0; } - bool hasSeparateInputsAndOutputs() const { return true; } + bool hasSeparateInputsAndOutputs() const override { return true; } - int getIndexOfDevice (AudioIODevice* device, bool asInput) const + int getIndexOfDevice (AudioIODevice* device, bool asInput) const override { jassert (hasScanned); // need to call scanForDevices() before doing this @@ -1041,7 +1040,7 @@ class ALSAAudioIODeviceType : public AudioIODeviceType } AudioIODevice* createDevice (const String& outputDeviceName, - const String& inputDeviceName) + const String& inputDeviceName) override { jassert (hasScanned); // need to call scanForDevices() before doing this @@ -1289,19 +1288,14 @@ class ALSAAudioIODeviceType : public AudioIODeviceType } //============================================================================== -AudioIODeviceType* createAudioIODeviceType_ALSA_Soundcards() +static inline AudioIODeviceType* createAudioIODeviceType_ALSA_Soundcards() { return new ALSAAudioIODeviceType (true, "ALSA HW"); } -AudioIODeviceType* createAudioIODeviceType_ALSA_PCMDevices() +static inline AudioIODeviceType* createAudioIODeviceType_ALSA_PCMDevices() { return new ALSAAudioIODeviceType (false, "ALSA"); } -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() -{ - return createAudioIODeviceType_ALSA_PCMDevices(); -} - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ASIO_windows.cpp similarity index 95% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_ASIO_windows.cpp index 1d89cba0..824becd5 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ASIO_windows.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -308,8 +308,8 @@ class ASIOAudioIODeviceType; static void sendASIODeviceChangeToListeners (ASIOAudioIODeviceType*); //============================================================================== -class ASIOAudioIODevice : public AudioIODevice, - private Timer +class ASIOAudioIODevice final : public AudioIODevice, + private Timer { public: ASIOAudioIODevice (ASIOAudioIODeviceType* ownerType, const String& devName, @@ -330,7 +330,7 @@ class ASIOAudioIODevice : public AudioIODevice, openDevice(); } - ~ASIOAudioIODevice() + ~ASIOAudioIODevice() override { for (int i = 0; i < maxNumASIODevices; ++i) if (currentASIODev[i] == this) @@ -349,13 +349,9 @@ class ASIOAudioIODevice : public AudioIODevice, Array newRates; if (asioObject != nullptr) - { - for (auto rate : { 8000, 11025, 16000, 22050, 32000, - 44100, 48000, 88200, 96000, 176400, - 192000, 352800, 384000, 705600, 768000 }) - if (asioObject->canSampleRate ((double) rate) == 0) - newRates.add ((double) rate); - } + for (const auto rate : SampleRateHelpers::getAllSampleRates()) + if (asioObject->canSampleRate (rate) == 0) + newRates.add (rate); if (newRates.isEmpty()) { @@ -399,7 +395,7 @@ class ASIOAudioIODevice : public AudioIODevice, jassert (currentCallback == nullptr); - if (bufferSizeSamples < 8 || bufferSizeSamples > 16384) + if (bufferSizeSamples < 8 || bufferSizeSamples > 32768) shouldUsePreferredSize = true; if (asioObject == nullptr) @@ -415,11 +411,8 @@ class ASIOAudioIODevice : public AudioIODevice, auto err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans); jassert (err == ASE_OK); - bufferSizeSamples = readBufferSizes (bufferSizeSamples); - auto sampleRate = sr; currentSampleRate = sampleRate; - currentBlockSizeSamples = bufferSizeSamples; currentChansOut.clear(); currentChansIn.clear(); @@ -441,6 +434,7 @@ class ASIOAudioIODevice : public AudioIODevice, buffersCreated = false; setSampleRate (sampleRate); + currentBlockSizeSamples = bufferSizeSamples = readBufferSizes (bufferSizeSamples); // (need to get this again in case a sample rate change affected the channel count) err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans); @@ -504,7 +498,7 @@ class ASIOAudioIODevice : public AudioIODevice, { inBuffers[n] = ioBufferSpace + (currentBlockSizeSamples * n); - ASIOChannelInfo channelInfo = { 0 }; + ASIOChannelInfo channelInfo = {}; channelInfo.channel = i; channelInfo.isInput = 1; asioObject->getChannelInfo (&channelInfo); @@ -526,7 +520,7 @@ class ASIOAudioIODevice : public AudioIODevice, { outBuffers[n] = ioBufferSpace + (currentBlockSizeSamples * (numActiveInputChans + n)); - ASIOChannelInfo channelInfo = { 0 }; + ASIOChannelInfo channelInfo = {}; channelInfo.channel = i; channelInfo.isInput = 0; asioObject->getChannelInfo (&channelInfo); @@ -673,10 +667,10 @@ class ASIOAudioIODevice : public AudioIODevice, lastCallback->audioDeviceStopped(); } - String getLastError() { return error; } - bool hasControlPanel() const { return true; } + String getLastError() override { return error; } + bool hasControlPanel() const override { return true; } - bool showControlPanel() + bool showControlPanel() override { JUCE_ASIO_LOG ("showing control panel"); @@ -767,7 +761,7 @@ class ASIOAudioIODevice : public AudioIODevice, bool deviceIsOpen = false, isStarted = false, buffersCreated = false; std::atomic calledback { false }; - bool littleEndian = false, postOutput = true, needToReset = false; + bool postOutput = true, needToReset = false; bool insideControlPanelModalLoop = false; bool shouldUsePreferredSize = false; int xruns = 0; @@ -785,7 +779,7 @@ class ASIOAudioIODevice : public AudioIODevice, String getChannelName (int index, bool isInput) const { - ASIOChannelInfo channelInfo = { 0 }; + ASIOChannelInfo channelInfo = {}; channelInfo.channel = index; channelInfo.isInput = isInput ? 1 : 0; asioObject->getChannelInfo (&channelInfo); @@ -821,7 +815,15 @@ class ASIOAudioIODevice : public AudioIODevice, long refreshBufferSizes() { - return asioObject->getBufferSize (&minBufferSize, &maxBufferSize, &preferredBufferSize, &bufferGranularity); + const auto err = asioObject->getBufferSize (&minBufferSize, &maxBufferSize, &preferredBufferSize, &bufferGranularity); + + if (err == ASE_OK) + { + bufferSizes.clear(); + addBufferSizes (minBufferSize, maxBufferSize, preferredBufferSize, bufferGranularity); + } + + return err; } int readBufferSizes (int bufferSizeSamples) @@ -913,7 +915,7 @@ class ASIOAudioIODevice : public AudioIODevice, { granularity = jmax (16, (int) granularity); - for (int i = jmax ((int) (minSize + 15) & ~15, (int) granularity); i < jmin (6400, (int) maxSize); i += granularity) + for (int i = jmax ((int) (minSize + 15) & ~15, (int) granularity); i <= jmin (6400, (int) maxSize); i += granularity) bufferSizes.addIfNotAlreadyThere (granularity * (i / granularity)); } else if (granularity < 0) @@ -945,15 +947,18 @@ class ASIOAudioIODevice : public AudioIODevice, { JUCE_ASIO_LOG ("rate change: " + String (currentSampleRate) + " to " + String (newRate)); auto err = asioObject->setSampleRate (newRate); + JUCE_ASIO_LOG_ERROR ("setSampleRate", err); + Thread::sleep (10); if (err == ASE_NoClock && numClockSources > 0) { JUCE_ASIO_LOG ("trying to set a clock source.."); - Thread::sleep (10); err = asioObject->setClockSource (clocks[0].index); JUCE_ASIO_LOG_ERROR ("setClockSource2", err); Thread::sleep (10); err = asioObject->setSampleRate (newRate); + JUCE_ASIO_LOG_ERROR ("setSampleRate", err); + Thread::sleep (10); } if (err == 0) @@ -1065,7 +1070,7 @@ class ASIOAudioIODevice : public AudioIODevice, for (int i = 0; i < totalNumOutputChans; ++i) { - ASIOChannelInfo channelInfo = { 0 }; + ASIOChannelInfo channelInfo = {}; channelInfo.channel = i; channelInfo.isInput = 0; asioObject->getChannelInfo (&channelInfo); @@ -1152,7 +1157,7 @@ class ASIOAudioIODevice : public AudioIODevice, // Get error message if init() failed, or if it's a buggy Denon driver, // which returns true from init() even when it fails. - if ((! initOk) || getName().containsIgnoreCase ("denon dj")) + if ((! initOk) || getName().containsIgnoreCase ("denon dj asio")) driverError = getLastDriverError(); if ((! initOk) && driverError.isEmpty()) @@ -1214,8 +1219,6 @@ class ASIOAudioIODevice : public AudioIODevice, if ((err = refreshBufferSizes()) == 0) { - addBufferSizes (minBufferSize, maxBufferSize, preferredBufferSize, bufferGranularity); - auto currentRate = getSampleRate(); if (currentRate < 1.0 || currentRate > 192001.0) @@ -1323,8 +1326,12 @@ class ASIOAudioIODevice : public AudioIODevice, inputFormat[i].convertToFloat (infos[i].buffers[bufferIndex], inBuffers[i], samps); } - currentCallback->audioDeviceIOCallback (const_cast (inBuffers.getData()), numActiveInputChans, - outBuffers, numActiveOutputChans, samps); + currentCallback->audioDeviceIOCallbackWithContext (inBuffers.getData(), + numActiveInputChans, + outBuffers, + numActiveOutputChans, + samps, + {}); for (int i = 0; i < numActiveOutputChans; ++i) { @@ -1431,7 +1438,7 @@ struct ASIOAudioIODevice::ASIOCallbackFunctions }; //============================================================================== -class ASIOAudioIODeviceType : public AudioIODeviceType +class ASIOAudioIODeviceType final : public AudioIODeviceType { public: ASIOAudioIODeviceType() : AudioIODeviceType ("ASIO") {} @@ -1446,7 +1453,7 @@ class ASIOAudioIODeviceType : public AudioIODeviceType HKEY hk = 0; int index = 0; - if (RegOpenKey (HKEY_LOCAL_MACHINE, _T("software\\asio"), &hk) == ERROR_SUCCESS) + if (RegOpenKey (HKEY_LOCAL_MACHINE, _T ("software\\asio"), &hk) == ERROR_SUCCESS) { TCHAR name[256] = {}; @@ -1547,7 +1554,7 @@ class ASIOAudioIODeviceType : public AudioIODeviceType HKEY hk = 0; bool ok = false; - if (RegOpenKey (HKEY_CLASSES_ROOT, _T("clsid"), &hk) == ERROR_SUCCESS) + if (RegOpenKey (HKEY_CLASSES_ROOT, _T ("clsid"), &hk) == ERROR_SUCCESS) { int index = 0; TCHAR name[512] = {}; @@ -1560,7 +1567,7 @@ class ASIOAudioIODeviceType : public AudioIODeviceType if (RegOpenKeyEx (hk, name, 0, KEY_READ, &subKey) == ERROR_SUCCESS) { - if (RegOpenKeyEx (subKey, _T("InprocServer32"), 0, KEY_READ, &pathKey) == ERROR_SUCCESS) + if (RegOpenKeyEx (subKey, _T ("InprocServer32"), 0, KEY_READ, &pathKey) == ERROR_SUCCESS) { TCHAR pathName[1024] = {}; DWORD dtype = REG_SZ; @@ -1603,7 +1610,7 @@ class ASIOAudioIODeviceType : public AudioIODeviceType DWORD dtype = REG_SZ; DWORD dsize = sizeof (buf); - if (RegQueryValueEx (subKey, _T("clsid"), 0, &dtype, (LPBYTE) buf, &dsize) == ERROR_SUCCESS) + if (RegQueryValueEx (subKey, _T ("clsid"), 0, &dtype, (LPBYTE) buf, &dsize) == ERROR_SUCCESS) { if (dsize > 0 && checkClassIsOk (buf)) { @@ -1615,7 +1622,7 @@ class ASIOAudioIODeviceType : public AudioIODeviceType dsize = sizeof (buf); String deviceName; - if (RegQueryValueEx (subKey, _T("description"), 0, &dtype, (LPBYTE) buf, &dsize) == ERROR_SUCCESS) + if (RegQueryValueEx (subKey, _T ("description"), 0, &dtype, (LPBYTE) buf, &dsize) == ERROR_SUCCESS) deviceName = buf; else deviceName = keyName; @@ -1640,9 +1647,4 @@ void sendASIODeviceChangeToListeners (ASIOAudioIODeviceType* type) type->sendDeviceChangeToListeners(); } -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() -{ - return new ASIOAudioIODeviceType(); -} - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_android.cpp similarity index 84% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_android.cpp index d470e74e..7671dde9 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_android.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -65,8 +65,8 @@ enum const char* const javaAudioTypeName = "Android Audio"; //============================================================================== -class AndroidAudioIODevice : public AudioIODevice, - public Thread +class AndroidAudioIODevice final : public AudioIODevice, + public Thread { public: //============================================================================== @@ -191,9 +191,9 @@ class AndroidAudioIODevice : public AudioIODevice, if (numClientOutputChannels > 0) { numDeviceOutputChannels = 2; - outputDevice = GlobalRef (LocalRef(env->NewObject (AudioTrack, AudioTrack.constructor, - STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT, - (jint) (minBufferSizeOut * numDeviceOutputChannels * static_cast (sizeof (int16))), MODE_STREAM))); + outputDevice = GlobalRef (LocalRef (env->NewObject (AudioTrack, AudioTrack.constructor, + STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT, + (jint) (minBufferSizeOut * numDeviceOutputChannels * static_cast (sizeof (int16))), MODE_STREAM))); const bool supportsUnderrunCount = (getAndroidSDKVersion() >= 24); getUnderrunCount = supportsUnderrunCount ? env->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : nullptr; @@ -225,11 +225,11 @@ class AndroidAudioIODevice : public AudioIODevice, else { numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable); - inputDevice = GlobalRef (LocalRef(env->NewObject (AudioRecord, AudioRecord.constructor, - 0 /* (default audio source) */, sampleRate, - numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO, - ENCODING_PCM_16BIT, - (jint) (minBufferSizeIn * numDeviceInputChannels * static_cast (sizeof (int16)))))); + inputDevice = GlobalRef (LocalRef (env->NewObject (AudioRecord, AudioRecord.constructor, + 0 /* (default audio source) */, sampleRate, + numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO, + ENCODING_PCM_16BIT, + (jint) (minBufferSizeIn * numDeviceInputChannels * static_cast (sizeof (int16)))))); int inputDeviceState = env->CallIntMethod (inputDevice, AudioRecord.getState); if (inputDeviceState > 0) @@ -253,7 +253,7 @@ class AndroidAudioIODevice : public AudioIODevice, if (inputDevice != nullptr) env->CallVoidMethod (inputDevice, AudioRecord.startRecording); - startThread (8); + startThread (Priority::high); } else { @@ -326,6 +326,9 @@ class AndroidAudioIODevice : public AudioIODevice, JNIEnv* env = getEnv(); jshortArray audioBuffer = env->NewShortArray (actualBufferSize * jmax (numDeviceOutputChannels, numDeviceInputChannels)); + using NativeInt16 = AudioData::Format; + using NativeFloat32 = AudioData::Format; + while (! threadShouldExit()) { if (inputDevice != nullptr) @@ -339,20 +342,9 @@ class AndroidAudioIODevice : public AudioIODevice, jshort* const src = env->GetShortArrayElements (audioBuffer, nullptr); - for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan) - { - AudioData::Pointer d (inputChannelBuffer.getWritePointer (chan)); - - if (chan < numDeviceInputChannels) - { - AudioData::Pointer s (src + chan, numDeviceInputChannels); - d.convertSamples (s, actualBufferSize); - } - else - { - d.clearSamples (actualBufferSize); - } - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { reinterpret_cast (src), numDeviceInputChannels }, + AudioData::NonInterleavedDest { inputChannelBuffer.getArrayOfWritePointers(), inputChannelBuffer.getNumChannels() }, + actualBufferSize); env->ReleaseShortArrayElements (audioBuffer, src, 0); } @@ -365,9 +357,11 @@ class AndroidAudioIODevice : public AudioIODevice, if (callback != nullptr) { - callback->audioDeviceIOCallback (inputChannelBuffer.getArrayOfReadPointers(), numClientInputChannels, - outputChannelBuffer.getArrayOfWritePointers(), numClientOutputChannels, - actualBufferSize); + callback->audioDeviceIOCallbackWithContext (inputChannelBuffer.getArrayOfReadPointers(), + numClientInputChannels, + outputChannelBuffer.getArrayOfWritePointers(), + numClientOutputChannels, + actualBufferSize, {}); } else { @@ -382,14 +376,9 @@ class AndroidAudioIODevice : public AudioIODevice, jshort* const dest = env->GetShortArrayElements (audioBuffer, nullptr); - for (int chan = 0; chan < numDeviceOutputChannels; ++chan) - { - AudioData::Pointer d (dest + chan, numDeviceOutputChannels); - - const float* const sourceChanData = outputChannelBuffer.getReadPointer (jmin (chan, outputChannelBuffer.getNumChannels() - 1)); - AudioData::Pointer s (sourceChanData); - d.convertSamples (s, actualBufferSize); - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { outputChannelBuffer.getArrayOfReadPointers(), outputChannelBuffer.getNumChannels() }, + AudioData::InterleavedDest { reinterpret_cast (dest), numDeviceOutputChannels }, + actualBufferSize); env->ReleaseShortArrayElements (audioBuffer, dest, 0); jint numWritten = env->CallIntMethod (outputDevice, AudioTrack.write, audioBuffer, 0, actualBufferSize * numDeviceOutputChannels); @@ -440,10 +429,10 @@ class AndroidAudioIODevice : public AudioIODevice, }; //============================================================================== -class AndroidAudioIODeviceType : public AudioIODeviceType +class AndroidAudioIODeviceType final : public AudioIODeviceType { public: - AndroidAudioIODeviceType() : AudioIODeviceType (javaAudioTypeName) {} + AndroidAudioIODeviceType() : AudioIODeviceType (javaAudioTypeName) {} //============================================================================== void scanForDevices() {} @@ -478,19 +467,4 @@ class AndroidAudioIODeviceType : public AudioIODeviceType extern bool isOboeAvailable(); extern bool isOpenSLAvailable(); -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() -{ - #if JUCE_USE_ANDROID_OBOE - if (isOboeAvailable()) - return nullptr; - #endif - - #if JUCE_USE_ANDROID_OPENSLES - if (isOpenSLAvailable()) - return nullptr; - #endif - - return new AndroidAudioIODeviceType(); -} - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.cpp similarity index 80% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.cpp index 2c2527d5..576ddefb 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,7 +25,13 @@ namespace juce class iOSAudioIODevice; -static const char* const iOSAudioDeviceName = "iOS Audio"; +constexpr const char* const iOSAudioDeviceName = "iOS Audio"; + +#ifndef JUCE_IOS_AUDIO_EXPLICIT_SAMPLERATES + #define JUCE_IOS_AUDIO_EXPLICIT_SAMPLERATES +#endif + +constexpr std::initializer_list iOSExplicitSampleRates { JUCE_IOS_AUDIO_EXPLICIT_SAMPLERATES }; //============================================================================== struct AudioSessionHolder @@ -58,6 +64,8 @@ static const char* getRoutingChangeReason (AVAudioSessionRouteChangeReason reaso } } +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") + bool getNotificationValueForKey (NSNotification* notification, NSString* key, NSUInteger& value) noexcept { if (notification != nil) @@ -76,7 +84,9 @@ bool getNotificationValueForKey (NSNotification* notification, NSString* key, NS return false; } -} // juce namespace +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + +} // namespace juce //============================================================================== @interface iOSAudioSessionNative : NSObject @@ -183,10 +193,6 @@ bool getNotificationValueForKey (NSNotification* notification, NSString* key, NS @end //============================================================================== -#if JUCE_MODULE_AVAILABLE_juce_graphics - #include -#endif - namespace juce { #ifndef JUCE_IOS_AUDIO_LOGGING @@ -211,8 +217,8 @@ static void logNSError (NSError* e) #define JUCE_NSERROR_CHECK(X) { NSError* error = nil; X; logNSError (error); } //============================================================================== -class iOSAudioIODeviceType : public AudioIODeviceType, - public AsyncUpdater +class iOSAudioIODeviceType final : public AudioIODeviceType, + public AsyncUpdater { public: iOSAudioIODeviceType(); @@ -240,8 +246,7 @@ class iOSAudioIODeviceType : public AudioIODeviceType, }; //============================================================================== -struct iOSAudioIODevice::Pimpl : public AudioPlayHead, - public AsyncUpdater +struct iOSAudioIODevice::Pimpl final : public AsyncUpdater { Pimpl (iOSAudioIODeviceType* ioDeviceType, iOSAudioIODevice& ioDevice) : deviceType (ioDeviceType), @@ -278,9 +283,14 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, #endif if (category == AVAudioSessionCategoryPlayAndRecord) - options |= (AVAudioSessionCategoryOptionDefaultToSpeaker - | AVAudioSessionCategoryOptionAllowBluetooth - | AVAudioSessionCategoryOptionAllowBluetoothA2DP); + { + options |= AVAudioSessionCategoryOptionDefaultToSpeaker + | AVAudioSessionCategoryOptionAllowBluetooth + | AVAudioSessionCategoryOptionAllowAirPlay; + + if (@available (iOS 10.0, *)) + options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP; + } JUCE_NSERROR_CHECK ([[AVAudioSession sharedInstance] setCategory: category withOptions: options @@ -356,6 +366,12 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, // depending on whether the headphones are plugged in or not! void updateAvailableSampleRates() { + if (iOSExplicitSampleRates.size() != 0) + { + availableSampleRates = Array (iOSExplicitSampleRates); + return; + } + availableSampleRates.clear(); AudioUnitRemovePropertyListenerWithUserData (audioUnit, @@ -542,145 +558,158 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, } //============================================================================== - bool canControlTransport() override { return interAppAudioConnected; } - - void transportPlay (bool shouldSartPlaying) override + class PlayHead final : public AudioPlayHead { - if (! canControlTransport()) - return; - - HostCallbackInfo callbackInfo; - fillHostCallbackInfo (callbackInfo); + public: + explicit PlayHead (Pimpl& implIn) : impl (implIn) {} - Boolean hostIsPlaying = NO; - OSStatus err = callbackInfo.transportStateProc2 (callbackInfo.hostUserData, - &hostIsPlaying, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr); + bool canControlTransport() override { return canControlTransportImpl(); } - ignoreUnused (err); - jassert (err == noErr); - - if (hostIsPlaying != shouldSartPlaying) - handleAudioTransportEvent (kAudioUnitRemoteControlEvent_TogglePlayPause); - } - - void transportRecord (bool shouldStartRecording) override - { - if (! canControlTransport()) - return; - - HostCallbackInfo callbackInfo; - fillHostCallbackInfo (callbackInfo); - - Boolean hostIsRecording = NO; - OSStatus err = callbackInfo.transportStateProc2 (callbackInfo.hostUserData, - nullptr, - &hostIsRecording, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr); - ignoreUnused (err); - jassert (err == noErr); - - if (hostIsRecording != shouldStartRecording) - handleAudioTransportEvent (kAudioUnitRemoteControlEvent_ToggleRecord); - } - - void transportRewind() override - { - if (canControlTransport()) - handleAudioTransportEvent (kAudioUnitRemoteControlEvent_Rewind); - } - - bool getCurrentPosition (CurrentPositionInfo& result) override - { - if (! canControlTransport()) - return false; - - zerostruct (result); - - HostCallbackInfo callbackInfo; - fillHostCallbackInfo (callbackInfo); + void transportPlay (bool shouldSartPlaying) override + { + if (! canControlTransport()) + return; - if (callbackInfo.hostUserData == nullptr) - return false; + HostCallbackInfo callbackInfo; + impl.fillHostCallbackInfo (callbackInfo); - Boolean hostIsPlaying = NO; - Boolean hostIsRecording = NO; - Float64 hostCurrentSampleInTimeLine = 0; - Boolean hostIsCycling = NO; - Float64 hostCycleStartBeat = 0; - Float64 hostCycleEndBeat = 0; - OSStatus err = callbackInfo.transportStateProc2 (callbackInfo.hostUserData, - &hostIsPlaying, - &hostIsRecording, - nullptr, - &hostCurrentSampleInTimeLine, - &hostIsCycling, - &hostCycleStartBeat, - &hostCycleEndBeat); - if (err == kAUGraphErr_CannotDoInCurrentContext) - return false; + Boolean hostIsPlaying = NO; + [[maybe_unused]] OSStatus err = callbackInfo.transportStateProc2 (callbackInfo.hostUserData, + &hostIsPlaying, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); - jassert (err == noErr); + jassert (err == noErr); - result.timeInSamples = (int64) hostCurrentSampleInTimeLine; - result.isPlaying = hostIsPlaying; - result.isRecording = hostIsRecording; - result.isLooping = hostIsCycling; - result.ppqLoopStart = hostCycleStartBeat; - result.ppqLoopEnd = hostCycleEndBeat; + if (hostIsPlaying != shouldSartPlaying) + impl.handleAudioTransportEvent (kAudioUnitRemoteControlEvent_TogglePlayPause); + } - result.timeInSeconds = result.timeInSamples / sampleRate; + void transportRecord (bool shouldStartRecording) override + { + if (! canControlTransport()) + return; - Float64 hostBeat = 0; - Float64 hostTempo = 0; - err = callbackInfo.beatAndTempoProc (callbackInfo.hostUserData, - &hostBeat, - &hostTempo); - jassert (err == noErr); + HostCallbackInfo callbackInfo; + impl.fillHostCallbackInfo (callbackInfo); + + Boolean hostIsRecording = NO; + [[maybe_unused]] OSStatus err = callbackInfo.transportStateProc2 (callbackInfo.hostUserData, + nullptr, + &hostIsRecording, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); + jassert (err == noErr); + + if (hostIsRecording != shouldStartRecording) + impl.handleAudioTransportEvent (kAudioUnitRemoteControlEvent_ToggleRecord); + } - result.ppqPosition = hostBeat; - result.bpm = hostTempo; - - Float32 hostTimeSigNumerator = 0; - UInt32 hostTimeSigDenominator = 0; - Float64 hostCurrentMeasureDownBeat = 0; - err = callbackInfo.musicalTimeLocationProc (callbackInfo.hostUserData, - nullptr, - &hostTimeSigNumerator, - &hostTimeSigDenominator, - &hostCurrentMeasureDownBeat); - jassert (err == noErr); + void transportRewind() override + { + if (canControlTransport()) + impl.handleAudioTransportEvent (kAudioUnitRemoteControlEvent_Rewind); + } - result.ppqPositionOfLastBarStart = hostCurrentMeasureDownBeat; - result.timeSigNumerator = (int) hostTimeSigNumerator; - result.timeSigDenominator = (int) hostTimeSigDenominator; + Optional getPosition() const override + { + if (! canControlTransportImpl()) + return {}; + + HostCallbackInfo callbackInfo; + impl.fillHostCallbackInfo (callbackInfo); + + if (callbackInfo.hostUserData == nullptr) + return {}; + + Boolean hostIsPlaying = NO; + Boolean hostIsRecording = NO; + Float64 hostCurrentSampleInTimeLine = 0; + Boolean hostIsCycling = NO; + Float64 hostCycleStartBeat = 0; + Float64 hostCycleEndBeat = 0; + auto transportErr = callbackInfo.transportStateProc2 (callbackInfo.hostUserData, + &hostIsPlaying, + &hostIsRecording, + nullptr, + &hostCurrentSampleInTimeLine, + &hostIsCycling, + &hostCycleStartBeat, + &hostCycleEndBeat); + if (transportErr == kAUGraphErr_CannotDoInCurrentContext) + return {}; + + jassert (transportErr == noErr); + + PositionInfo result; + + result.setTimeInSamples ((int64) hostCurrentSampleInTimeLine); + result.setIsPlaying (hostIsPlaying); + result.setIsRecording (hostIsRecording); + result.setIsLooping (hostIsCycling); + result.setLoopPoints (LoopPoints { hostCycleStartBeat, hostCycleEndBeat }); + result.setTimeInSeconds ((double) *result.getTimeInSamples() / impl.sampleRate); + + Float64 hostBeat = 0; + Float64 hostTempo = 0; + [[maybe_unused]] auto batErr = callbackInfo.beatAndTempoProc (callbackInfo.hostUserData, + &hostBeat, + &hostTempo); + jassert (batErr == noErr); + + result.setPpqPosition (hostBeat); + result.setBpm (hostTempo); + + Float32 hostTimeSigNumerator = 0; + UInt32 hostTimeSigDenominator = 0; + Float64 hostCurrentMeasureDownBeat = 0; + [[maybe_unused]] auto timeErr = callbackInfo.musicalTimeLocationProc (callbackInfo.hostUserData, + nullptr, + &hostTimeSigNumerator, + &hostTimeSigDenominator, + &hostCurrentMeasureDownBeat); + jassert (timeErr == noErr); + + result.setPpqPositionOfLastBarStart (hostCurrentMeasureDownBeat); + result.setTimeSignature (TimeSignature { (int) hostTimeSigNumerator, (int) hostTimeSigDenominator }); + + result.setFrameRate (AudioPlayHead::fpsUnknown); + + return result; + } - result.frameRate = AudioPlayHead::fpsUnknown; + private: + bool canControlTransportImpl() const { return impl.interAppAudioConnected; } - return true; - } + Pimpl& impl; + }; //============================================================================== #if JUCE_MODULE_AVAILABLE_juce_graphics + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") Image getIcon (int size) { - if (interAppAudioConnected) + #if TARGET_OS_MACCATALYST + if (@available (macCatalyst 14.0, *)) + #endif { - UIImage* hostUIImage = AudioOutputUnitGetHostIcon (audioUnit, size); - if (hostUIImage != nullptr) - return juce_createImageFromUIImage (hostUIImage); + if (interAppAudioConnected) + { + if (UIImage* hostUIImage = AudioOutputUnitGetHostIcon (audioUnit, (float) size)) + return juce_createImageFromUIImage (hostUIImage); + } } - return Image(); + + return {}; } + JUCE_END_IGNORE_WARNINGS_GCC_LIKE #endif void switchApplication() @@ -690,19 +719,26 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, CFURLRef hostUrl; UInt32 dataSize = sizeof (hostUrl); - OSStatus err = AudioUnitGetProperty(audioUnit, - kAudioUnitProperty_PeerURL, - kAudioUnitScope_Global, - 0, - &hostUrl, - &dataSize); + OSStatus err = AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_PeerURL, + kAudioUnitScope_Global, + 0, + &hostUrl, + &dataSize); if (err == noErr) { - #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]; - #endif + if (@available (iOS 10.0, *)) + { + [[UIApplication sharedApplication] openURL: (NSURL*) hostUrl + options: @{} + completionHandler: nil]; + + return; + } + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + [[UIApplication sharedApplication] openURL: (NSURL*) hostUrl]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } } @@ -746,9 +782,9 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, switch (reason) { case AVAudioSessionRouteChangeReasonCategoryChange: - case AVAudioSessionRouteChangeReasonOverride: case AVAudioSessionRouteChangeReasonRouteConfigurationChange: break; + case AVAudioSessionRouteChangeReasonOverride: case AVAudioSessionRouteChangeReasonUnknown: case AVAudioSessionRouteChangeReasonNewDeviceAvailable: case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: @@ -766,11 +802,9 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, void handleAudioUnitPropertyChange (AudioUnit, AudioUnitPropertyID propertyID, - AudioUnitScope scope, - AudioUnitElement element) + [[maybe_unused]] AudioUnitScope scope, + [[maybe_unused]] AudioUnitElement element) { - ignoreUnused (scope); - ignoreUnused (element); JUCE_IOS_AUDIO_LOG ("handleAudioUnitPropertyChange: propertyID: " << String (propertyID) << " scope: " << String (scope) << " element: " << String (element)); @@ -792,9 +826,8 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, { UInt32 connected; UInt32 dataSize = sizeof (connected); - OSStatus err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_IsInterAppConnected, - kAudioUnitScope_Global, 0, &connected, &dataSize); - ignoreUnused (err); + [[maybe_unused]] OSStatus err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_IsInterAppConnected, + kAudioUnitScope_Global, 0, &connected, &dataSize); jassert (err == noErr); JUCE_IOS_AUDIO_LOG ("handleInterAppAudioConnectionChange: " << (connected ? "connected" @@ -850,8 +883,8 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, if ((int) numFrames > channelData.getFloatBufferSize()) channelData.setFloatBufferSize ((int) numFrames); - float** const inputData = channelData.audioData.getArrayOfWritePointers(); - float** const outputData = inputData + channelData.inputs->numActiveChannels; + float* const* const inputData = channelData.audioData.getArrayOfWritePointers(); + float* const* const outputData = inputData + channelData.inputs->numActiveChannels; if (useInput) { @@ -867,9 +900,14 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, zeromem (inputData[c], channelDataSize); } - callback->audioDeviceIOCallback ((const float**) inputData, channelData.inputs ->numActiveChannels, - outputData, channelData.outputs->numActiveChannels, - (int) numFrames); + const auto nanos = time != nullptr ? timeConversions.hostTimeToNanos (time->mHostTime) : 0; + + callback->audioDeviceIOCallbackWithContext ((const float**) inputData, + channelData.inputs ->numActiveChannels, + outputData, + channelData.outputs->numActiveChannels, + (int) numFrames, + { (time != nullptr && (time->mFlags & kAudioTimeStampHostTimeValid) != 0) ? &nanos : nullptr }); for (int c = 0; c < channelData.outputs->numActiveChannels; ++c) { @@ -895,7 +933,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, { if (! firstHostTime) { - if ((time->mSampleTime - lastSampleTime) != lastNumFrames) + if (! approximatelyEqual ((time->mSampleTime - lastSampleTime), (double) lastNumFrames)) xrun++; } else @@ -948,7 +986,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, appDesc.componentFlags = 0; appDesc.componentFlagsMask = 0; OSStatus err = AudioOutputUnitPublish (&appDesc, - CFSTR(JucePlugin_IAAName), + CFSTR (JucePlugin_IAAName), JucePlugin_VersionCode, audioUnit); @@ -1022,6 +1060,19 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, } } + #if JUCE_AUDIOWORKGROUP_TYPES_AVAILABLE + workgroup = [this] + { + UInt32 dataSize = sizeof (os_workgroup_t); + os_workgroup_t wgHandle = nullptr; + + AudioUnitGetProperty (audioUnit, kAudioOutputUnitProperty_OSWorkgroup, + kAudioUnitScope_Global, 0, &wgHandle, &dataSize); + + return makeRealAudioWorkgroup (wgHandle); + }(); + #endif + AudioUnitAddPropertyListener (audioUnit, kAudioUnitProperty_StreamFormat, dispatchAudioUnitPropertyChange, this); return true; @@ -1031,21 +1082,19 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, { zerostruct (callbackInfo); UInt32 dataSize = sizeof (HostCallbackInfo); - OSStatus err = AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_HostCallbacks, - kAudioUnitScope_Global, - 0, - &callbackInfo, - &dataSize); - ignoreUnused (err); + [[maybe_unused]] OSStatus err = AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_HostCallbacks, + kAudioUnitScope_Global, + 0, + &callbackInfo, + &dataSize); jassert (err == noErr); } void handleAudioTransportEvent (AudioUnitRemoteControlEvent event) { - OSStatus err = AudioUnitSetProperty (audioUnit, kAudioOutputUnitProperty_RemoteControlToHost, - kAudioUnitScope_Global, 0, &event, sizeof (event)); - ignoreUnused (err); + [[maybe_unused]] OSStatus err = AudioUnitSetProperty (audioUnit, kAudioOutputUnitProperty_RemoteControlToHost, + kAudioUnitScope_Global, 0, &event, sizeof (event)); jassert (err == noErr); } @@ -1118,7 +1167,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, &desc, &dataSize); - if (desc.mSampleRate != 0 && desc.mSampleRate != sampleRate) + if (! approximatelyEqual (desc.mSampleRate, 0.0) && ! approximatelyEqual (desc.mSampleRate, sampleRate)) { JUCE_IOS_AUDIO_LOG ("Stream format has changed: Sample rate " << desc.mSampleRate); triggerAsyncUpdate(); @@ -1296,6 +1345,8 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, AudioBuffer audioData { 0, 0 }; }; + CoreAudioTimeConversions timeConversions; + IOChannelData channelData; BigInteger requestedInputChannels, requestedOutputChannels; @@ -1330,6 +1381,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, Atomic hardwareInfoNeedsUpdating { true }; AudioUnit audioUnit {}; + AudioWorkgroup workgroup; SharedResourcePointer sessionHolder; @@ -1337,6 +1389,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, Float64 lastSampleTime; unsigned int lastNumFrames; int xrun; + PlayHead playhead { *this }; JUCE_DECLARE_NON_COPYABLE (Pimpl) }; @@ -1385,9 +1438,10 @@ BigInteger iOSAudioIODevice::getActiveOutputChannels() const { return pim int iOSAudioIODevice::getInputLatencyInSamples() { return roundToInt (pimpl->sampleRate * [AVAudioSession sharedInstance].inputLatency); } int iOSAudioIODevice::getOutputLatencyInSamples() { return roundToInt (pimpl->sampleRate * [AVAudioSession sharedInstance].outputLatency); } int iOSAudioIODevice::getXRunCount() const noexcept { return pimpl->xrun; } +AudioWorkgroup iOSAudioIODevice::getWorkgroup() const { return pimpl->workgroup; } void iOSAudioIODevice::setMidiMessageCollector (MidiMessageCollector* collector) { pimpl->messageCollector = collector; } -AudioPlayHead* iOSAudioIODevice::getAudioPlayHead() const { return pimpl.get(); } +AudioPlayHead* iOSAudioIODevice::getAudioPlayHead() const { return &pimpl->playhead; } bool iOSAudioIODevice::isInterAppAudioConnected() const { return pimpl->interAppAudioConnected; } #if JUCE_MODULE_AVAILABLE_juce_graphics @@ -1429,12 +1483,6 @@ void iOSAudioIODeviceType::handleAsyncUpdate() callDeviceChangeListeners(); } -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() -{ - return new iOSAudioIODeviceType(); -} - //============================================================================== AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; } AudioSessionHolder::~AudioSessionHolder() { [nativeSession release]; } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.h b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.h similarity index 96% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.h rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.h index d5e2a4f8..78cd2e1a 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.h +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -64,6 +64,8 @@ class iOSAudioIODevice : public AudioIODevice int getXRunCount() const noexcept override; + AudioWorkgroup getWorkgroup() const override; + //============================================================================== void setMidiMessageCollector (MidiMessageCollector*); AudioPlayHead* getAudioPlayHead() const; @@ -84,7 +86,6 @@ class iOSAudioIODevice : public AudioIODevice friend struct AudioSessionHolder; struct Pimpl; - friend struct Pimpl; std::unique_ptr pimpl; JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice) diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Bela.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Bela_linux.cpp similarity index 89% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Bela.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_Bela_linux.cpp index bc27b0a8..494d3a84 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Bela.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Bela_linux.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -24,12 +24,12 @@ namespace juce { //============================================================================== -class BelaMidiInput +class MidiInput::Pimpl { public: - static Array midiInputs; + static Array midiInputs; - BelaMidiInput (const String& port, MidiInput* input, MidiInputCallback* callback) + Pimpl (const String& port, MidiInput* input, MidiInputCallback* callback) : midiInput (input), midiPort (port), midiCallback (callback) { jassert (midiCallback != nullptr); @@ -38,7 +38,7 @@ class BelaMidiInput buffer.resize (32); } - ~BelaMidiInput() + ~Pimpl() { stop(); midiInputs.removeAllInstancesOf (this); @@ -76,7 +76,7 @@ class BelaMidiInput } if (receivedBytes > 0) - pushMidiData (receivedBytes); + pushMidiData ((int) receivedBytes); } static Array getDevices (bool input) @@ -141,7 +141,7 @@ class BelaMidiInput snd_rawmidi_info_t* info; snd_rawmidi_info_alloca (&info); - snd_rawmidi_info_set_device (info, device); + snd_rawmidi_info_set_device (info, (unsigned int) device); snd_rawmidi_info_set_stream (info, input ? SND_RAWMIDI_STREAM_INPUT : SND_RAWMIDI_STREAM_OUTPUT); @@ -173,14 +173,14 @@ class BelaMidiInput Midi midi; MidiDataConcatenator concatenator { 512 }; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaMidiInput) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; -Array BelaMidiInput::midiInputs; +Array MidiInput::Pimpl::midiInputs; //============================================================================== -class BelaAudioIODevice : public AudioIODevice +class BelaAudioIODevice final : public AudioIODevice { public: BelaAudioIODevice() : AudioIODevice (BelaAudioIODevice::belaTypeName, @@ -366,7 +366,7 @@ class BelaAudioIODevice : public AudioIODevice String getLastError() override { return lastError; } //============================================================================== - int getCurrentBufferSizeSamples() override { return actualBufferSize; } + int getCurrentBufferSizeSamples() override { return (int) actualBufferSize; } double getCurrentSampleRate() override { return 44100.0; } int getCurrentBitDepth() override { return 16; } BigInteger getActiveOutputChannels() const override { BigInteger b; b.setRange (0, actualNumberOfOutputs, true); return b; } @@ -384,8 +384,8 @@ class BelaAudioIODevice : public AudioIODevice bool setup (BelaContext& context) { actualBufferSize = context.audioFrames; - actualNumberOfInputs = context.audioInChannels + context.analogInChannels; - actualNumberOfOutputs = context.audioOutChannels + context.analogOutChannels; + actualNumberOfInputs = (int) (context.audioInChannels + context.analogInChannels); + actualNumberOfOutputs = (int) (context.audioOutChannels + context.analogOutChannels); isBelaOpen = true; firstCallback = true; @@ -405,7 +405,7 @@ class BelaAudioIODevice : public AudioIODevice ScopedLock lock (callbackLock); // Check for and process and midi - for (auto midiInput : BelaMidiInput::midiInputs) + for (auto midiInput : MidiInput::Pimpl::midiInputs) midiInput->poll(); if (callback != nullptr) @@ -413,27 +413,32 @@ class BelaAudioIODevice : public AudioIODevice jassert (context.audioFrames <= actualBufferSize); jassert ((context.flags & BELA_FLAG_INTERLEAVED) == 0); + using Frames = decltype (context.audioFrames); + // Setup channelInBuffers for (int ch = 0; ch < actualNumberOfInputs; ++ch) { if (ch < analogChannelStart) - channelInBuffer[ch] = &context.audioIn[ch * context.audioFrames]; + channelInBuffer[ch] = &context.audioIn[(Frames) ch * context.audioFrames]; else - channelInBuffer[ch] = &context.analogIn[(ch - analogChannelStart) * context.analogFrames]; + channelInBuffer[ch] = &context.analogIn[(Frames) (ch - analogChannelStart) * context.analogFrames]; } // Setup channelOutBuffers for (int ch = 0; ch < actualNumberOfOutputs; ++ch) { if (ch < analogChannelStart) - channelOutBuffer[ch] = &context.audioOut[ch * context.audioFrames]; + channelOutBuffer[ch] = &context.audioOut[(Frames) ch * context.audioFrames]; else - channelOutBuffer[ch] = &context.analogOut[(ch - analogChannelStart) * context.audioFrames]; + channelOutBuffer[ch] = &context.analogOut[(Frames) (ch - analogChannelStart) * context.audioFrames]; } - callback->audioDeviceIOCallback (channelInBuffer.getData(), actualNumberOfInputs, - channelOutBuffer.getData(), actualNumberOfOutputs, - context.audioFrames); + callback->audioDeviceIOCallbackWithContext (channelInBuffer.getData(), + actualNumberOfInputs, + channelOutBuffer.getData(), + actualNumberOfOutputs, + (int) context.audioFrames, + {}); } } @@ -499,7 +504,7 @@ class BelaAudioIODevice : public AudioIODevice const char* const BelaAudioIODevice::belaTypeName = "Bela Analog"; //============================================================================== -struct BelaAudioIODeviceType : public AudioIODeviceType +struct BelaAudioIODeviceType final : public AudioIODeviceType { BelaAudioIODeviceType() : AudioIODeviceType ("Bela") {} @@ -521,25 +526,19 @@ struct BelaAudioIODeviceType : public AudioIODeviceType JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODeviceType) }; -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() -{ - return new BelaAudioIODeviceType(); -} - //============================================================================== MidiInput::MidiInput (const String& deviceName, const String& deviceID) : deviceInfo (deviceName, deviceID) { } -MidiInput::~MidiInput() { delete static_cast (internal); } -void MidiInput::start() { static_cast (internal)->start(); } -void MidiInput::stop() { static_cast (internal)->stop(); } +MidiInput::~MidiInput() = default; +void MidiInput::start() { internal->start(); } +void MidiInput::stop() { internal->stop(); } Array MidiInput::getAvailableDevices() { - return BelaMidiInput::getDevices (true); + return Pimpl::getDevices (true); } MidiDeviceInfo MidiInput::getDefaultDevice() @@ -553,7 +552,7 @@ std::unique_ptr MidiInput::openDevice (const String& deviceIdentifier return {}; std::unique_ptr midiInput (new MidiInput (deviceIdentifier, deviceIdentifier)); - midiInput->internal = new BelaMidiInput (deviceIdentifier, midiInput.get(), callback); + midiInput->internal = std::make_unique (deviceIdentifier, midiInput.get(), callback); return midiInput; } @@ -587,7 +586,8 @@ std::unique_ptr MidiInput::openDevice (int index, MidiInputCallback* //============================================================================== // TODO: Add Bela MidiOutput support -MidiOutput::~MidiOutput() {} +class MidiOutput::Pimpl {}; +MidiOutput::~MidiOutput() = default; void MidiOutput::sendMessageNow (const MidiMessage&) {} Array MidiOutput::getAvailableDevices() { return {}; } MidiDeviceInfo MidiOutput::getDefaultDevice() { return {}; } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreAudio_mac.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreAudio_mac.cpp new file mode 100644 index 00000000..4c5442f6 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreAudio_mac.cpp @@ -0,0 +1,2296 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +#if JUCE_COREAUDIO_LOGGING_ENABLED + #define JUCE_COREAUDIOLOG(a) { String camsg ("CoreAudio: "); camsg << a; Logger::writeToLog (camsg); } +#else + #define JUCE_COREAUDIOLOG(a) +#endif + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnonnull") + +constexpr auto juceAudioObjectPropertyElementMain = + #if defined (MAC_OS_VERSION_12_0) + kAudioObjectPropertyElementMain; + #else + kAudioObjectPropertyElementMaster; + #endif + +//============================================================================== +class ManagedAudioBufferList final : public AudioBufferList +{ +public: + struct Deleter + { + void operator() (ManagedAudioBufferList* p) const + { + if (p != nullptr) + p->~ManagedAudioBufferList(); + + delete[] reinterpret_cast (p); + } + }; + + using Ref = std::unique_ptr; + + //============================================================================== + static Ref create (std::size_t numBuffers) + { + static_assert (alignof (ManagedAudioBufferList) <= alignof (std::max_align_t)); + + if (std::unique_ptr storage { new std::byte[storageSizeForNumBuffers (numBuffers)] }) + return Ref { new (storage.release()) ManagedAudioBufferList (numBuffers) }; + + return nullptr; + } + + //============================================================================== + static std::size_t storageSizeForNumBuffers (std::size_t numBuffers) noexcept + { + return audioBufferListHeaderSize + (numBuffers * sizeof (::AudioBuffer)); + } + + static std::size_t numBuffersForStorageSize (std::size_t bytes) noexcept + { + bytes -= audioBufferListHeaderSize; + + // storage size ends between to buffers in AudioBufferList + jassert ((bytes % sizeof (::AudioBuffer)) == 0); + + return bytes / sizeof (::AudioBuffer); + } + +private: + // Do not call the base constructor here as this will zero-initialize the first buffer, + // for which no storage may be available though (when numBuffers == 0). + explicit ManagedAudioBufferList (std::size_t numBuffers) + { + mNumberBuffers = static_cast (numBuffers); + } + + static constexpr auto audioBufferListHeaderSize = sizeof (AudioBufferList) - sizeof (::AudioBuffer); + + JUCE_DECLARE_NON_COPYABLE (ManagedAudioBufferList) + JUCE_DECLARE_NON_MOVEABLE (ManagedAudioBufferList) +}; + +//============================================================================== +struct IgnoreUnused +{ + template + void operator() (Ts&&...) const {} +}; + +template +static auto getDataPtrAndSize (T& t) +{ + static_assert (std::is_pod_v); + return std::make_tuple (&t, (UInt32) sizeof (T)); +} + +static auto getDataPtrAndSize (ManagedAudioBufferList::Ref& t) +{ + const auto size = t.get() != nullptr + ? ManagedAudioBufferList::storageSizeForNumBuffers (t->mNumberBuffers) + : 0; + return std::make_tuple (t.get(), (UInt32) size); +} + +//============================================================================== +[[nodiscard]] static bool audioObjectHasProperty (AudioObjectID objectID, const AudioObjectPropertyAddress address) +{ + return objectID != kAudioObjectUnknown && AudioObjectHasProperty (objectID, &address); +} + +template +[[nodiscard]] static auto audioObjectGetProperty (AudioObjectID objectID, + const AudioObjectPropertyAddress address, + OnError&& onError = {}) +{ + using Result = std::conditional_t, ManagedAudioBufferList::Ref, std::optional>; + + if (! audioObjectHasProperty (objectID, address)) + return Result{}; + + auto result = [&] + { + if constexpr (std::is_same_v) + { + UInt32 size{}; + + if (auto status = AudioObjectGetPropertyDataSize (objectID, &address, 0, nullptr, &size); status != noErr) + { + onError (status); + return Result{}; + } + + return ManagedAudioBufferList::create (ManagedAudioBufferList::numBuffersForStorageSize (size)); + } + else + { + return T{}; + } + }(); + + auto [ptr, size] = getDataPtrAndSize (result); + + if (size == 0) + return Result{}; + + if (auto status = AudioObjectGetPropertyData (objectID, &address, 0, nullptr, &size, ptr); status != noErr) + { + onError (status); + return Result{}; + } + + return Result { std::move (result) }; +} + +template +static bool audioObjectSetProperty (AudioObjectID objectID, + const AudioObjectPropertyAddress address, + const T value, + OnError&& onError = {}) +{ + if (! audioObjectHasProperty (objectID, address)) + return false; + + Boolean isSettable = NO; + if (auto status = AudioObjectIsPropertySettable (objectID, &address, &isSettable); status != noErr) + { + onError (status); + return false; + } + + if (! isSettable) + return false; + + if (auto status = AudioObjectSetPropertyData (objectID, &address, 0, nullptr, static_cast (sizeof (T)), &value); status != noErr) + { + onError (status); + return false; + } + + return true; +} + +template +[[nodiscard]] static std::vector audioObjectGetProperties (AudioObjectID objectID, + const AudioObjectPropertyAddress address, + OnError&& onError = {}) +{ + if (! audioObjectHasProperty (objectID, address)) + return {}; + + UInt32 size{}; + + if (auto status = AudioObjectGetPropertyDataSize (objectID, &address, 0, nullptr, &size); status != noErr) + { + onError (status); + return {}; + } + + // If this is hit, the number of results is not integral, and the following + // AudioObjectGetPropertyData will probably write past the end of the result buffer. + jassert ((size % sizeof (T)) == 0); + std::vector result (size / sizeof (T)); + + if (auto status = AudioObjectGetPropertyData (objectID, &address, 0, nullptr, &size, result.data()); status != noErr) + { + onError (status); + return {}; + } + + return result; +} + +//============================================================================== +struct AsyncRestarter +{ + virtual ~AsyncRestarter() = default; + virtual void restartAsync() = 0; +}; + +struct SystemVol +{ + explicit SystemVol (AudioObjectPropertySelector selector) noexcept + : outputDeviceID (audioObjectGetProperty (kAudioObjectSystemObject, { kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, + juceAudioObjectPropertyElementMain }).value_or (kAudioObjectUnknown)), + addr { selector, kAudioDevicePropertyScopeOutput, juceAudioObjectPropertyElementMain } + {} + + float getGain() const noexcept + { + return audioObjectGetProperty (outputDeviceID, addr).value_or (0.0f); + } + + bool setGain (float gain) const noexcept + { + return audioObjectSetProperty (outputDeviceID, addr, static_cast (gain)); + } + + bool isMuted() const noexcept + { + return audioObjectGetProperty (outputDeviceID, addr).value_or (0) != 0; + } + + bool setMuted (bool mute) const noexcept + { + return audioObjectSetProperty (outputDeviceID, addr, static_cast (mute ? 1 : 0)); + } + +private: + AudioDeviceID outputDeviceID; + AudioObjectPropertyAddress addr; +}; + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + +constexpr auto juceAudioHardwareServiceDeviceProperty_VirtualMainVolume = + #if defined (MAC_OS_VERSION_12_0) + kAudioHardwareServiceDeviceProperty_VirtualMainVolume; + #else + kAudioHardwareServiceDeviceProperty_VirtualMasterVolume; + #endif + +#define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1 +float JUCE_CALLTYPE SystemAudioVolume::getGain() { return SystemVol (juceAudioHardwareServiceDeviceProperty_VirtualMainVolume).getGain(); } +bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return SystemVol (juceAudioHardwareServiceDeviceProperty_VirtualMainVolume).setGain (gain); } +bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return SystemVol (kAudioDevicePropertyMute).isMuted(); } +bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return SystemVol (kAudioDevicePropertyMute).setMuted (mute); } + +//============================================================================== +struct CoreAudioClasses +{ + +class CoreAudioIODeviceType; +class CoreAudioIODevice; + +//============================================================================== +class CoreAudioInternal final : private Timer, + private AsyncUpdater +{ +private: + // members with deduced return types need to be defined before they + // are used, so define it here. decltype doesn't help as you can't + // capture anything in lambdas inside a decltype context. + auto err2log() const { return [this] (OSStatus err) { OK (err); }; } + +public: + CoreAudioInternal (CoreAudioIODevice& d, AudioDeviceID id, bool hasInput, bool hasOutput) + : owner (d), + deviceID (id), + inStream (hasInput ? new Stream (true, *this, {}) : nullptr), + outStream (hasOutput ? new Stream (false, *this, {}) : nullptr) + { + jassert (deviceID != 0); + + updateDetailsFromDevice(); + JUCE_COREAUDIOLOG ("Creating CoreAudioInternal\n" + << (inStream != nullptr ? (" inputDeviceId " + String (deviceID) + "\n") : "") + << (outStream != nullptr ? (" outputDeviceId " + String (deviceID) + "\n") : "") + << getDeviceDetails().joinIntoString ("\n ")); + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioObjectPropertySelectorWildcard; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; + + AudioObjectAddPropertyListener (deviceID, &pa, deviceListenerProc, this); + } + + ~CoreAudioInternal() override + { + stopTimer(); + cancelPendingUpdate(); + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioObjectPropertySelectorWildcard; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; + + AudioObjectRemovePropertyListener (deviceID, &pa, deviceListenerProc, this); + + stop (false); + } + + auto getStreams() const { return std::array { { inStream.get(), outStream.get() } }; } + + void allocateTempBuffers() + { + auto tempBufSize = bufferSize + 4; + + auto streams = getStreams(); + const auto total = std::accumulate (streams.begin(), streams.end(), 0, + [] (int n, const auto& s) { return n + (s != nullptr ? s->channels : 0); }); + audioBuffer.calloc (total * tempBufSize); + + auto channels = 0; + for (auto* stream : streams) + channels += stream != nullptr ? stream->allocateTempBuffers (tempBufSize, channels, audioBuffer) : 0; + } + + struct CallbackDetailsForChannel + { + int streamNum; + int dataOffsetSamples; + int dataStrideSamples; + }; + + Array getSampleRatesFromDevice() const + { + Array newSampleRates; + + if (auto ranges = audioObjectGetProperties (deviceID, + { kAudioDevicePropertyAvailableNominalSampleRates, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }, + err2log()); ! ranges.empty()) + { + for (const auto rate : SampleRateHelpers::getAllSampleRates()) + { + for (auto range = ranges.rbegin(); range != ranges.rend(); ++range) + { + if (range->mMinimum - 2 <= rate && rate <= range->mMaximum + 2) + { + newSampleRates.add (rate); + break; + } + } + } + } + + if (newSampleRates.isEmpty() && sampleRate > 0) + newSampleRates.add (sampleRate); + + auto nominalRate = getNominalSampleRate(); + + if ((nominalRate > 0) && ! newSampleRates.contains (nominalRate)) + newSampleRates.addUsingDefaultSort (nominalRate); + + return newSampleRates; + } + + Array getBufferSizesFromDevice() const + { + Array newBufferSizes; + + if (auto ranges = audioObjectGetProperties (deviceID, { kAudioDevicePropertyBufferFrameSizeRange, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }, + err2log()); ! ranges.empty()) + { + newBufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15); + + for (int i = 32; i <= 2048; i += 32) + { + for (auto range = ranges.rbegin(); range != ranges.rend(); ++range) + { + if (i >= range->mMinimum && i <= range->mMaximum) + { + newBufferSizes.addIfNotAlreadyThere (i); + break; + } + } + } + + if (bufferSize > 0) + newBufferSizes.addIfNotAlreadyThere (bufferSize); + } + + if (newBufferSizes.isEmpty() && bufferSize > 0) + newBufferSizes.add (bufferSize); + + return newBufferSizes; + } + + int getFrameSizeFromDevice() const + { + return static_cast (audioObjectGetProperty (deviceID, { kAudioDevicePropertyBufferFrameSize, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }).value_or (0)); + } + + bool isDeviceAlive() const + { + return deviceID != 0 + && audioObjectGetProperty (deviceID, { kAudioDevicePropertyDeviceIsAlive, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }, err2log()).value_or (0) != 0; + } + + bool updateDetailsFromDevice (const BigInteger& activeIns, const BigInteger& activeOuts) + { + stopTimer(); + + if (! isDeviceAlive()) + return false; + + // this collects all the new details from the device without any locking, then + // locks + swaps them afterwards. + + auto newSampleRate = getNominalSampleRate(); + auto newBufferSize = getFrameSizeFromDevice(); + + auto newBufferSizes = getBufferSizesFromDevice(); + auto newSampleRates = getSampleRatesFromDevice(); + + auto newInput = rawToUniquePtr (inStream != nullptr ? new Stream (true, *this, activeIns) : nullptr); + auto newOutput = rawToUniquePtr (outStream != nullptr ? new Stream (false, *this, activeOuts) : nullptr); + + auto newBitDepth = jmax (getBitDepth (newInput), getBitDepth (newOutput)); + + #if JUCE_AUDIOWORKGROUP_TYPES_AVAILABLE + audioWorkgroup = [this]() -> AudioWorkgroup + { + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyIOThreadOSWorkgroup; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = juceAudioObjectPropertyElementMain; + + if (auto* workgroup = audioObjectGetProperty (deviceID, pa).value_or (nullptr)) + { + ScopeGuard scope { [&] { os_release (workgroup); } }; + return makeRealAudioWorkgroup (workgroup); + } + + return {}; + }(); + #endif + + { + const ScopedLock sl (callbackLock); + + bitDepth = newBitDepth > 0 ? newBitDepth : 32; + + if (newSampleRate > 0) + sampleRate = newSampleRate; + + bufferSize = newBufferSize; + + sampleRates.swapWith (newSampleRates); + bufferSizes.swapWith (newBufferSizes); + + std::swap (inStream, newInput); + std::swap (outStream, newOutput); + + allocateTempBuffers(); + } + + return true; + } + + bool updateDetailsFromDevice() + { + return updateDetailsFromDevice (getActiveChannels (inStream), getActiveChannels (outStream)); + } + + StringArray getDeviceDetails() + { + StringArray result; + + String availableSampleRates ("Available sample rates:"); + + for (auto& s : sampleRates) + availableSampleRates << " " << s; + + result.add (availableSampleRates); + result.add ("Sample rate: " + String (sampleRate)); + String availableBufferSizes ("Available buffer sizes:"); + + for (auto& b : bufferSizes) + availableBufferSizes << " " << b; + + result.add (availableBufferSizes); + result.add ("Buffer size: " + String (bufferSize)); + result.add ("Bit depth: " + String (bitDepth)); + result.add ("Input latency: " + String (getLatency (inStream))); + result.add ("Output latency: " + String (getLatency (outStream))); + result.add ("Input channel names: " + getChannelNames (inStream)); + result.add ("Output channel names: " + getChannelNames (outStream)); + + return result; + } + + static auto getScope (bool input) + { + return input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + } + + //============================================================================== + StringArray getSources (bool input) + { + StringArray s; + auto types = audioObjectGetProperties (deviceID, { kAudioDevicePropertyDataSources, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }); + + for (auto type : types) + { + AudioValueTranslation avt; + char buffer[256]; + + avt.mInputData = &type; + avt.mInputDataSize = sizeof (UInt32); + avt.mOutputData = buffer; + avt.mOutputDataSize = 256; + + UInt32 transSize = sizeof (avt); + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyDataSourceNameForID; + pa.mScope = getScope (input); + pa.mElement = juceAudioObjectPropertyElementMain; + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &transSize, &avt))) + s.add (buffer); + } + + return s; + } + + int getCurrentSourceIndex (bool input) const + { + if (deviceID != 0) + { + if (auto currentSourceID = audioObjectGetProperty (deviceID, { kAudioDevicePropertyDataSource, + getScope (input), + juceAudioObjectPropertyElementMain }, err2log())) + { + auto types = audioObjectGetProperties (deviceID, { kAudioDevicePropertyDataSources, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }); + + if (auto it = std::find (types.begin(), types.end(), *currentSourceID); it != types.end()) + return static_cast (std::distance (types.begin(), it)); + } + } + + return -1; + } + + void setCurrentSourceIndex (int index, bool input) + { + if (deviceID != 0) + { + auto types = audioObjectGetProperties (deviceID, { kAudioDevicePropertyDataSources, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }); + + if (isPositiveAndBelow (index, static_cast (types.size()))) + { + audioObjectSetProperty (deviceID, { kAudioDevicePropertyDataSource, + getScope (input), + juceAudioObjectPropertyElementMain }, + types[static_cast (index)], err2log()); + } + } + } + + double getNominalSampleRate() const + { + return static_cast (audioObjectGetProperty (deviceID, { kAudioDevicePropertyNominalSampleRate, + kAudioObjectPropertyScopeGlobal, + juceAudioObjectPropertyElementMain }, + err2log()).value_or (0.0)); + } + + bool setNominalSampleRate (double newSampleRate) const + { + if (std::abs (getNominalSampleRate() - newSampleRate) < 1.0) + return true; + + return audioObjectSetProperty (deviceID, { kAudioDevicePropertyNominalSampleRate, + kAudioObjectPropertyScopeGlobal, + juceAudioObjectPropertyElementMain }, + static_cast (newSampleRate), err2log()); + } + + //============================================================================== + String reopen (const BigInteger& ins, const BigInteger& outs, double newSampleRate, int bufferSizeSamples) + { + callbacksAllowed = false; + const ScopeGuard scope { [&] { callbacksAllowed = true; } }; + + stopTimer(); + + stop (false); + + if (! setNominalSampleRate (newSampleRate)) + { + updateDetailsFromDevice (ins, outs); + return "Couldn't change sample rate"; + } + + if (! audioObjectSetProperty (deviceID, { kAudioDevicePropertyBufferFrameSize, + kAudioObjectPropertyScopeGlobal, + juceAudioObjectPropertyElementMain }, + static_cast (bufferSizeSamples), err2log())) + { + updateDetailsFromDevice (ins, outs); + return "Couldn't change buffer size"; + } + + // Annoyingly, after changing the rate and buffer size, some devices fail to + // correctly report their new settings until some random time in the future, so + // after calling updateDetailsFromDevice, we need to manually bodge these values + // to make sure we're using the correct numbers.. + updateDetailsFromDevice (ins, outs); + sampleRate = newSampleRate; + bufferSize = bufferSizeSamples; + + if (sampleRates.size() == 0) + return "Device has no available sample-rates"; + + if (bufferSizes.size() == 0) + return "Device has no available buffer-sizes"; + + return {}; + } + + bool start (AudioIODeviceCallback* callbackToNotify) + { + const ScopedLock sl (callbackLock); + + if (callback == nullptr && callbackToNotify != nullptr) + { + callback = callbackToNotify; + callback->audioDeviceAboutToStart (&owner); + } + + for (auto* stream : getStreams()) + if (stream != nullptr) + stream->previousSampleTime = invalidSampleTime; + + owner.hadDiscontinuity = false; + + if (scopedProcID.get() == nullptr && deviceID != 0) + { + scopedProcID = [&self = *this, + &lock = callbackLock, + nextProcID = ScopedAudioDeviceIOProcID { *this, deviceID, audioIOProc }, + dID = deviceID]() mutable -> ScopedAudioDeviceIOProcID + { + // It *looks* like AudioDeviceStart may start the audio callback running, and then + // immediately lock an internal mutex. + // The same mutex is locked before calling the audioIOProc. + // If we get very unlucky, then we can end up with thread A taking the callbackLock + // and calling AudioDeviceStart, followed by thread B taking the CoreAudio lock + // and calling into audioIOProc, which waits on the callbackLock. When thread A + // continues it attempts to take the CoreAudio lock, and the program deadlocks. + + if (auto* procID = nextProcID.get()) + { + const ScopedUnlock su (lock); + + if (self.OK (AudioDeviceStart (dID, procID))) + return std::move (nextProcID); + } + + return {}; + }(); + } + + playing = scopedProcID.get() != nullptr && callback != nullptr; + + return scopedProcID.get() != nullptr; + } + + AudioIODeviceCallback* stop (bool leaveInterruptRunning) + { + const ScopedLock sl (callbackLock); + + auto result = std::exchange (callback, nullptr); + + if (scopedProcID.get() != nullptr && (deviceID != 0) && ! leaveInterruptRunning) + { + audioDeviceStopPending = true; + + // wait until AudioDeviceStop() has been called on the IO thread + for (int i = 40; --i >= 0;) + { + if (audioDeviceStopPending == false) + break; + + const ScopedUnlock ul (callbackLock); + Thread::sleep (50); + } + + scopedProcID = {}; + playing = false; + } + + return result; + } + + double getSampleRate() const { return sampleRate; } + int getBufferSize() const { return bufferSize; } + + void audioCallback (const AudioTimeStamp* inputTimestamp, + const AudioTimeStamp* outputTimestamp, + const AudioBufferList* inInputData, + AudioBufferList* outOutputData) + { + const ScopedLock sl (callbackLock); + + if (audioDeviceStopPending) + { + if (OK (AudioDeviceStop (deviceID, scopedProcID.get()))) + audioDeviceStopPending = false; + + return; + } + + const auto numInputChans = getChannels (inStream); + const auto numOutputChans = getChannels (outStream); + + if (callback != nullptr) + { + for (int i = numInputChans; --i >= 0;) + { + auto& info = inStream->channelInfo.getReference (i); + auto dest = inStream->tempBuffers[i]; + auto src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; + auto stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest++ = *src; + src += stride; + } + } + } + + for (auto* stream : getStreams()) + if (stream != nullptr) + owner.hadDiscontinuity |= stream->checkTimestampsForDiscontinuity (stream == inStream.get() ? inputTimestamp + : outputTimestamp); + + const auto* timeStamp = numOutputChans > 0 ? outputTimestamp : inputTimestamp; + const auto nanos = timeStamp != nullptr ? timeConversions.hostTimeToNanos (timeStamp->mHostTime) : 0; + const AudioIODeviceCallbackContext context + { + timeStamp != nullptr ? &nanos : nullptr, + }; + + callback->audioDeviceIOCallbackWithContext (getTempBuffers (inStream), numInputChans, + getTempBuffers (outStream), numOutputChans, + bufferSize, + context); + + for (int i = numOutputChans; --i >= 0;) + { + auto& info = outStream->channelInfo.getReference (i); + auto src = outStream->tempBuffers[i]; + auto dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; + auto stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest = *src++; + dest += stride; + } + } + } + } + else + { + for (UInt32 i = 0; i < outOutputData->mNumberBuffers; ++i) + zeromem (outOutputData->mBuffers[i].mData, + outOutputData->mBuffers[i].mDataByteSize); + } + + for (auto* stream : getStreams()) + if (stream != nullptr) + stream->previousSampleTime += static_cast (bufferSize); + } + + // called by callbacks (possibly off the main thread) + void deviceDetailsChanged() + { + if (callbacksAllowed.get() == 1) + startTimer (100); + } + + // called by callbacks (possibly off the main thread) + void deviceRequestedRestart() + { + owner.restart(); + triggerAsyncUpdate(); + } + + bool isPlaying() const { return playing.load(); } + + //============================================================================== + struct Stream + { + Stream (bool isInput, CoreAudioInternal& parent, const BigInteger& activeRequested) + : input (isInput), + latency (getLatencyFromDevice (isInput, parent)), + bitDepth (getBitDepthFromDevice (isInput, parent)), + chanNames (getChannelNames (isInput, parent)), + activeChans ([&activeRequested, clearFrom = chanNames.size()] + { + auto result = activeRequested; + result.setRange (clearFrom, result.getHighestBit() + 1 - clearFrom, false); + return result; + }()), + channelInfo (getChannelInfos (isInput, parent, activeChans)), + channels (static_cast (channelInfo.size())) + {} + + int allocateTempBuffers (int tempBufSize, int channelCount, HeapBlock& buffer) + { + tempBuffers.calloc (channels + 2); + + for (int i = 0; i < channels; ++i) + tempBuffers[i] = buffer + channelCount++ * tempBufSize; + + return channels; + } + + template + static auto visitChannels (bool isInput, CoreAudioInternal& parent, Visitor&& visitor) + { + struct Args { int stream, channelIdx, chanNum, streamChannels; }; + using VisitorResultType = typename std::invoke_result_t::value_type; + Array result; + int chanNum = 0; + + if (auto bufList = audioObjectGetProperty (parent.deviceID, { kAudioDevicePropertyStreamConfiguration, + getScope (isInput), + juceAudioObjectPropertyElementMain }, parent.err2log())) + { + const int numStreams = static_cast (bufList->mNumberBuffers); + + for (int i = 0; i < numStreams; ++i) + { + auto& b = bufList->mBuffers[i]; + + for (unsigned int j = 0; j < b.mNumberChannels; ++j) + { + // Passing an anonymous struct ensures that callback can't confuse the argument order + if (auto opt = visitor (Args { i, static_cast (j), chanNum++, static_cast (b.mNumberChannels) })) + result.add (std::move (*opt)); + } + } + } + + return result; + } + + static Array getChannelInfos (bool isInput, CoreAudioInternal& parent, const BigInteger& active) + { + return visitChannels (isInput, parent, + [&] (const auto& args) -> std::optional + { + if (! active[args.chanNum]) + return {}; + + return CallbackDetailsForChannel { args.stream, args.channelIdx, args.streamChannels }; + }); + } + + static StringArray getChannelNames (bool isInput, CoreAudioInternal& parent) + { + auto names = visitChannels (isInput, parent, + [&] (const auto& args) -> std::optional + { + String name; + const auto element = static_cast (args.chanNum + 1); + + if (auto nameNSString = audioObjectGetProperty (parent.deviceID, { kAudioObjectPropertyElementName, + getScope (isInput), + element }).value_or (nullptr)) + { + name = nsStringToJuce (nameNSString); + [nameNSString release]; + } + + if (name.isEmpty()) + name << (isInput ? "Input " : "Output ") << (args.chanNum + 1); + + return name; + }); + + return { names }; + } + + static int getBitDepthFromDevice (bool isInput, CoreAudioInternal& parent) + { + return static_cast (audioObjectGetProperty (parent.deviceID, { kAudioStreamPropertyPhysicalFormat, + getScope (isInput), + juceAudioObjectPropertyElementMain }, parent.err2log()) + .value_or (AudioStreamBasicDescription{}).mBitsPerChannel); + } + + static int getLatencyFromDevice (bool isInput, CoreAudioInternal& parent) + { + const auto scope = getScope (isInput); + + const auto deviceLatency = audioObjectGetProperty (parent.deviceID, { kAudioDevicePropertyLatency, + scope, + juceAudioObjectPropertyElementMain }).value_or (0); + + const auto safetyOffset = audioObjectGetProperty (parent.deviceID, { kAudioDevicePropertySafetyOffset, + scope, + juceAudioObjectPropertyElementMain }).value_or (0); + + const auto framesInBuffer = audioObjectGetProperty (parent.deviceID, { kAudioDevicePropertyBufferFrameSize, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }).value_or (0); + + UInt32 streamLatency = 0; + + if (auto streams = audioObjectGetProperties (parent.deviceID, { kAudioDevicePropertyStreams, + scope, + juceAudioObjectPropertyElementMain }); ! streams.empty()) + streamLatency = audioObjectGetProperty (streams.front(), { kAudioStreamPropertyLatency, + scope, + juceAudioObjectPropertyElementMain }).value_or (0); + + return static_cast (deviceLatency + safetyOffset + framesInBuffer + streamLatency); + } + + bool checkTimestampsForDiscontinuity (const AudioTimeStamp* timestamp) noexcept + { + if (channels > 0) + { + jassert (timestamp == nullptr || (((timestamp->mFlags & kAudioTimeStampSampleTimeValid) != 0) + && ((timestamp->mFlags & kAudioTimeStampHostTimeValid) != 0))); + + if (exactlyEqual (previousSampleTime, invalidSampleTime)) + previousSampleTime = timestamp != nullptr ? timestamp->mSampleTime : 0.0; + + if (timestamp != nullptr && std::fabs (previousSampleTime - timestamp->mSampleTime) >= 1.0) + { + previousSampleTime = timestamp->mSampleTime; + return true; + } + } + + return false; + } + + //============================================================================== + const bool input; + const int latency; + const int bitDepth; + const StringArray chanNames; + const BigInteger activeChans; + const Array channelInfo; + const int channels = 0; + Float64 previousSampleTime; + + HeapBlock tempBuffers; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Stream) + }; + + template + static auto getWithDefault (const std::unique_ptr& ptr, Callback&& callback) + { + return ptr != nullptr ? callback (*ptr) : decltype (callback (*ptr)) {}; + } + + template + static auto getWithDefault (const std::unique_ptr& ptr, Value (Stream::* member)) + { + return getWithDefault (ptr, [&] (Stream& s) { return s.*member; }); + } + + static int getLatency (const std::unique_ptr& ptr) { return getWithDefault (ptr, &Stream::latency); } + static int getBitDepth (const std::unique_ptr& ptr) { return getWithDefault (ptr, &Stream::bitDepth); } + static int getChannels (const std::unique_ptr& ptr) { return getWithDefault (ptr, &Stream::channels); } + static int getNumChannelNames (const std::unique_ptr& ptr) { return getWithDefault (ptr, &Stream::chanNames).size(); } + static String getChannelNames (const std::unique_ptr& ptr) { return getWithDefault (ptr, &Stream::chanNames).joinIntoString (" "); } + static BigInteger getActiveChannels (const std::unique_ptr& ptr) { return getWithDefault (ptr, &Stream::activeChans); } + static float** getTempBuffers (const std::unique_ptr& ptr) { return getWithDefault (ptr, [] (auto& s) { return s.tempBuffers.get(); }); } + + //============================================================================== + static constexpr Float64 invalidSampleTime = std::numeric_limits::max(); + + CoreAudioIODevice& owner; + int bitDepth = 32; + int xruns = 0; + Array sampleRates; + Array bufferSizes; + AudioDeviceID deviceID; + std::unique_ptr inStream, outStream; + + AudioWorkgroup audioWorkgroup; + +private: + class ScopedAudioDeviceIOProcID + { + public: + ScopedAudioDeviceIOProcID() = default; + + ScopedAudioDeviceIOProcID (CoreAudioInternal& coreAudio, AudioDeviceID d, AudioDeviceIOProc audioIOProc) + : deviceID (d) + { + if (! coreAudio.OK (AudioDeviceCreateIOProcID (deviceID, audioIOProc, &coreAudio, &proc))) + proc = {}; + } + + ~ScopedAudioDeviceIOProcID() noexcept + { + if (proc != AudioDeviceIOProcID{}) + AudioDeviceDestroyIOProcID (deviceID, proc); + } + + ScopedAudioDeviceIOProcID (ScopedAudioDeviceIOProcID&& other) noexcept + { + swap (other); + } + + ScopedAudioDeviceIOProcID& operator= (ScopedAudioDeviceIOProcID&& other) noexcept + { + ScopedAudioDeviceIOProcID { std::move (other) }.swap (*this); + return *this; + } + + AudioDeviceIOProcID get() const { return proc; } + + private: + void swap (ScopedAudioDeviceIOProcID& other) noexcept + { + std::swap (other.deviceID, deviceID); + std::swap (other.proc, proc); + } + + AudioDeviceID deviceID = {}; + AudioDeviceIOProcID proc = {}; + }; + + //============================================================================== + ScopedAudioDeviceIOProcID scopedProcID; + CoreAudioTimeConversions timeConversions; + AudioIODeviceCallback* callback = nullptr; + CriticalSection callbackLock; + bool audioDeviceStopPending = false; + std::atomic playing { false }; + double sampleRate = 0; + int bufferSize = 0; + HeapBlock audioBuffer; + Atomic callbacksAllowed { 1 }; + + //============================================================================== + void timerCallback() override + { + JUCE_COREAUDIOLOG ("Device changed"); + + stopTimer(); + auto oldSampleRate = sampleRate; + auto oldBufferSize = bufferSize; + + if (! updateDetailsFromDevice()) + owner.stopInternal(); + else if ((oldBufferSize != bufferSize || ! approximatelyEqual (oldSampleRate, sampleRate)) && owner.shouldRestartDevice()) + owner.restart(); + } + + void handleAsyncUpdate() override + { + if (owner.deviceType != nullptr) + owner.deviceType->audioDeviceListChanged(); + } + + static OSStatus audioIOProc (AudioDeviceID /*inDevice*/, + [[maybe_unused]] const AudioTimeStamp* inNow, + const AudioBufferList* inInputData, + const AudioTimeStamp* inInputTime, + AudioBufferList* outOutputData, + const AudioTimeStamp* inOutputTime, + void* device) + { + static_cast (device)->audioCallback (inInputTime, inOutputTime, inInputData, outOutputData); + return noErr; + } + + static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, + UInt32 numAddresses, + const AudioObjectPropertyAddress* pa, + void* inClientData) + { + auto& intern = *static_cast (inClientData); + + const auto xruns = std::count_if (pa, pa + numAddresses, [] (const AudioObjectPropertyAddress& x) + { + return x.mSelector == kAudioDeviceProcessorOverload; + }); + + intern.xruns += xruns; + + const auto detailsChanged = std::any_of (pa, pa + numAddresses, [] (const AudioObjectPropertyAddress& x) + { + constexpr UInt32 selectors[] + { + kAudioDevicePropertyBufferSize, + kAudioDevicePropertyBufferFrameSize, + kAudioDevicePropertyNominalSampleRate, + kAudioDevicePropertyStreamFormat, + kAudioDevicePropertyDeviceIsAlive, + kAudioStreamPropertyPhysicalFormat, + }; + + return std::find (std::begin (selectors), std::end (selectors), x.mSelector) != std::end (selectors); + }); + + const auto requestedRestart = std::any_of (pa, pa + numAddresses, [] (const AudioObjectPropertyAddress& x) + { + constexpr UInt32 selectors[] + { + kAudioDevicePropertyDeviceHasChanged, + kAudioObjectPropertyOwnedObjects, + }; + + return std::find (std::begin (selectors), std::end (selectors), x.mSelector) != std::end (selectors); + }); + + if (detailsChanged) + intern.deviceDetailsChanged(); + + if (requestedRestart) + intern.deviceRequestedRestart(); + + return noErr; + } + + //============================================================================== + bool OK (const OSStatus errorCode) const + { + if (errorCode == noErr) + return true; + + const String errorMessage ("CoreAudio error: " + String::toHexString ((int) errorCode)); + JUCE_COREAUDIOLOG (errorMessage); + + if (callback != nullptr) + callback->audioDeviceError (errorMessage); + + return false; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioInternal) +}; + + +//============================================================================== +class CoreAudioIODevice final : public AudioIODevice, + private Timer +{ +public: + CoreAudioIODevice (CoreAudioIODeviceType* dt, + const String& deviceName, + AudioDeviceID inputDeviceId, + AudioDeviceID outputDeviceId) + : AudioIODevice (deviceName, "CoreAudio"), + deviceType (dt) + { + internal = [this, &inputDeviceId, &outputDeviceId] + { + if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) + { + jassert (inputDeviceId != 0); + return std::make_unique (*this, inputDeviceId, true, outputDeviceId != 0); + } + + return std::make_unique (*this, outputDeviceId, false, true); + }(); + + jassert (internal != nullptr); + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioObjectPropertySelectorWildcard; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; + + AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal.get()); + } + + ~CoreAudioIODevice() override + { + close(); + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioObjectPropertySelectorWildcard; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; + + AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal.get()); + } + + StringArray getOutputChannelNames() override { return internal->outStream != nullptr ? internal->outStream->chanNames : StringArray(); } + StringArray getInputChannelNames() override { return internal->inStream != nullptr ? internal->inStream ->chanNames : StringArray(); } + + bool isOpen() override { return isOpen_; } + + Array getAvailableSampleRates() override { return internal->sampleRates; } + Array getAvailableBufferSizes() override { return internal->bufferSizes; } + + double getCurrentSampleRate() override { return internal->getSampleRate(); } + int getCurrentBitDepth() override { return internal->bitDepth; } + int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); } + int getXRunCount() const noexcept override { return internal->xruns; } + + int getIndexOfDevice (bool asInput) const { return deviceType->getDeviceNames (asInput).indexOf (getName()); } + + int getDefaultBufferSize() override + { + int best = 0; + + for (int i = 0; best < 512 && i < internal->bufferSizes.size(); ++i) + best = internal->bufferSizes.getUnchecked (i); + + if (best == 0) + best = 512; + + return best; + } + + String open (const BigInteger& inputChannels, + const BigInteger& outputChannels, + double sampleRate, int bufferSizeSamples) override + { + isOpen_ = true; + internal->xruns = 0; + + inputChannelsRequested = inputChannels; + outputChannelsRequested = outputChannels; + + if (bufferSizeSamples <= 0) + bufferSizeSamples = getDefaultBufferSize(); + + if (sampleRate <= 0) + sampleRate = internal->getNominalSampleRate(); + + lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); + JUCE_COREAUDIOLOG ("Opened: " << getName()); + + isOpen_ = lastError.isEmpty(); + + return lastError; + } + + void close() override + { + isOpen_ = false; + internal->stop (false); + } + + BigInteger getActiveOutputChannels() const override { return CoreAudioInternal::getActiveChannels (internal->outStream); } + BigInteger getActiveInputChannels() const override { return CoreAudioInternal::getActiveChannels (internal->inStream); } + int getOutputLatencyInSamples() override { return CoreAudioInternal::getLatency (internal->outStream); } + int getInputLatencyInSamples() override { return CoreAudioInternal::getLatency (internal->inStream); } + + void start (AudioIODeviceCallback* callback) override + { + if (internal->start (callback)) + previousCallback = callback; + } + + void stop() override + { + restartDevice = false; + stopAndGetLastCallback(); + } + + AudioIODeviceCallback* stopAndGetLastCallback() const + { + auto* lastCallback = internal->stop (true); + + if (lastCallback != nullptr) + lastCallback->audioDeviceStopped(); + + return lastCallback; + } + + AudioIODeviceCallback* stopInternal() + { + restartDevice = true; + return stopAndGetLastCallback(); + } + + AudioWorkgroup getWorkgroup() const override + { + return internal->audioWorkgroup; + } + + bool isPlaying() override + { + return internal->isPlaying(); + } + + String getLastError() override + { + return lastError; + } + + void audioDeviceListChanged() + { + if (deviceType != nullptr) + deviceType->audioDeviceListChanged(); + } + + // called by callbacks (possibly off the main thread) + void restart() + { + if (restarter != nullptr) + { + restarter->restartAsync(); + return; + } + + { + const ScopedLock sl (closeLock); + previousCallback = stopInternal(); + } + + startTimer (100); + } + + bool setCurrentSampleRate (double newSampleRate) + { + return internal->setNominalSampleRate (newSampleRate); + } + + void setAsyncRestarter (AsyncRestarter* restarterIn) + { + restarter = restarterIn; + } + + bool shouldRestartDevice() const noexcept { return restartDevice; } + + WeakReference deviceType; + bool hadDiscontinuity; + +private: + std::unique_ptr internal; + bool isOpen_ = false, restartDevice = true; + String lastError; + AudioIODeviceCallback* previousCallback = nullptr; + AsyncRestarter* restarter = nullptr; + BigInteger inputChannelsRequested, outputChannelsRequested; + CriticalSection closeLock; + + void timerCallback() override + { + stopTimer(); + + stopInternal(); + + internal->updateDetailsFromDevice(); + + open (inputChannelsRequested, outputChannelsRequested, + getCurrentSampleRate(), getCurrentBufferSizeSamples()); + start (previousCallback); + } + + static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, + UInt32 numAddresses, + const AudioObjectPropertyAddress* pa, + void* inClientData) + { + const auto detailsChanged = std::any_of (pa, pa + numAddresses, [] (const AudioObjectPropertyAddress& x) + { + return x.mSelector == kAudioHardwarePropertyDevices; + }); + + if (detailsChanged) + static_cast (inClientData)->deviceDetailsChanged(); + + return noErr; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODevice) +}; + + +//============================================================================== +class AudioIODeviceCombiner final : public AudioIODevice, + private AsyncRestarter, + private Timer +{ +public: + AudioIODeviceCombiner (const String& deviceName, CoreAudioIODeviceType* deviceType, + std::unique_ptr&& inputDevice, + std::unique_ptr&& outputDevice) + : AudioIODevice (deviceName, "CoreAudio"), + owner (deviceType), + currentSampleRate (inputDevice->getCurrentSampleRate()), + currentBufferSize (inputDevice->getCurrentBufferSizeSamples()), + inputWrapper (*this, std::move (inputDevice), true), + outputWrapper (*this, std::move (outputDevice), false) + { + if (getAvailableSampleRates().isEmpty()) + lastError = TRANS ("The input and output devices don't share a common sample rate!"); + } + + ~AudioIODeviceCombiner() override + { + close(); + } + + auto getDeviceWrappers() { return std::array< DeviceWrapper*, 2> { { &inputWrapper, &outputWrapper } }; } + auto getDeviceWrappers() const { return std::array { { &inputWrapper, &outputWrapper } }; } + + int getIndexOfDevice (bool asInput) const + { + return asInput ? inputWrapper.getIndexOfDevice (true) + : outputWrapper.getIndexOfDevice (false); + } + + StringArray getOutputChannelNames() override { return outputWrapper.getChannelNames(); } + StringArray getInputChannelNames() override { return inputWrapper .getChannelNames(); } + BigInteger getActiveOutputChannels() const override { return outputWrapper.getActiveChannels(); } + BigInteger getActiveInputChannels() const override { return inputWrapper .getActiveChannels(); } + + Array getAvailableSampleRates() override + { + auto commonRates = inputWrapper.getAvailableSampleRates(); + commonRates.removeValuesNotIn (outputWrapper.getAvailableSampleRates()); + + return commonRates; + } + + Array getAvailableBufferSizes() override + { + auto commonSizes = inputWrapper.getAvailableBufferSizes(); + commonSizes.removeValuesNotIn (outputWrapper.getAvailableBufferSizes()); + + return commonSizes; + } + + bool isOpen() override { return active; } + bool isPlaying() override { return callback != nullptr; } + double getCurrentSampleRate() override { return currentSampleRate; } + int getCurrentBufferSizeSamples() override { return currentBufferSize; } + + int getCurrentBitDepth() override + { + return jmin (32, inputWrapper.getCurrentBitDepth(), outputWrapper.getCurrentBitDepth()); + } + + int getDefaultBufferSize() override + { + return jmax (0, inputWrapper.getDefaultBufferSize(), outputWrapper.getDefaultBufferSize()); + } + + AudioWorkgroup getWorkgroup() const override + { + return inputWrapper.getWorkgroup(); + } + + String open (const BigInteger& inputChannels, + const BigInteger& outputChannels, + double sampleRate, int bufferSize) override + { + inputChannelsRequested = inputChannels; + outputChannelsRequested = outputChannels; + sampleRateRequested = sampleRate; + bufferSizeRequested = bufferSize; + + close(); + active = true; + + if (bufferSize <= 0) + bufferSize = getDefaultBufferSize(); + + if (sampleRate <= 0) + { + auto rates = getAvailableSampleRates(); + + for (int i = 0; i < rates.size() && sampleRate < 44100.0; ++i) + sampleRate = rates.getUnchecked (i); + } + + currentSampleRate = sampleRate; + currentBufferSize = bufferSize; + targetLatency = bufferSize; + + for (auto& d : getDeviceWrappers()) + { + auto err = d->open ( d->isInput() ? inputChannels : BigInteger(), + ! d->isInput() ? outputChannels : BigInteger(), + sampleRate, bufferSize); + + if (err.isNotEmpty()) + { + close(); + lastError = err; + return err; + } + + targetLatency += d->getLatencyInSamples(); + } + + const auto numOuts = outputWrapper.getChannelNames().size(); + + fifo.setSize (numOuts, targetLatency + (bufferSize * 2)); + scratchBuffer.setSize (numOuts, bufferSize); + + return {}; + } + + void close() override + { + stop(); + fifo.clear(); + active = false; + + for (auto& d : getDeviceWrappers()) + d->close(); + } + + void restart (AudioIODeviceCallback* cb) + { + const ScopedLock sl (closeLock); + + close(); + + auto newSampleRate = sampleRateRequested; + auto newBufferSize = bufferSizeRequested; + + for (auto& d : getDeviceWrappers()) + { + auto deviceSampleRate = d->getCurrentSampleRate(); + + if (! approximatelyEqual (deviceSampleRate, sampleRateRequested)) + { + if (! getAvailableSampleRates().contains (deviceSampleRate)) + return; + + for (auto& d2 : getDeviceWrappers()) + if (&d2 != &d) + d2->setCurrentSampleRate (deviceSampleRate); + + newSampleRate = deviceSampleRate; + break; + } + } + + for (auto& d : getDeviceWrappers()) + { + auto deviceBufferSize = d->getCurrentBufferSizeSamples(); + + if (deviceBufferSize != bufferSizeRequested) + { + if (! getAvailableBufferSizes().contains (deviceBufferSize)) + return; + + newBufferSize = deviceBufferSize; + break; + } + } + + open (inputChannelsRequested, outputChannelsRequested, newSampleRate, newBufferSize); + + start (cb); + } + + void restartAsync() override + { + { + const ScopedLock sl (closeLock); + + if (active) + { + if (callback != nullptr) + previousCallback = callback; + + close(); + } + } + + startTimer (100); + } + + int getOutputLatencyInSamples() override + { + return targetLatency - getInputLatencyInSamples(); + } + + int getInputLatencyInSamples() override + { + return inputWrapper.getLatencyInSamples(); + } + + void start (AudioIODeviceCallback* newCallback) override + { + const auto shouldStart = [&] + { + const ScopedLock sl (callbackLock); + return callback != newCallback; + }(); + + if (shouldStart) + { + stop(); + fifo.clear(); + reset(); + + { + ScopedErrorForwarder forwarder (*this, newCallback); + + for (auto& d : getDeviceWrappers()) + d->start (d); + + if (! forwarder.encounteredError() && newCallback != nullptr) + newCallback->audioDeviceAboutToStart (this); + else if (lastError.isEmpty()) + lastError = TRANS ("Failed to initialise all requested devices."); + } + + const ScopedLock sl (callbackLock); + previousCallback = callback = newCallback; + } + } + + void stop() override { shutdown ({}); } + + String getLastError() override + { + return lastError; + } + + int getXRunCount() const noexcept override + { + return xruns.load(); + } + +private: + static constexpr auto invalidSampleTime = std::numeric_limits::max(); + + WeakReference owner; + CriticalSection callbackLock; + AudioIODeviceCallback* callback = nullptr; + AudioIODeviceCallback* previousCallback = nullptr; + double currentSampleRate = 0; + int currentBufferSize = 0; + bool active = false; + String lastError; + AudioSampleBuffer fifo, scratchBuffer; + CriticalSection closeLock; + int targetLatency = 0; + std::atomic xruns { -1 }; + std::atomic lastValidReadPosition { invalidSampleTime }; + + BigInteger inputChannelsRequested, outputChannelsRequested; + double sampleRateRequested = 44100; + int bufferSizeRequested = 512; + + void timerCallback() override + { + stopTimer(); + + restart (previousCallback); + } + + void shutdown (const String& error) + { + AudioIODeviceCallback* lastCallback = nullptr; + + { + const ScopedLock sl (callbackLock); + std::swap (callback, lastCallback); + } + + for (auto& d : getDeviceWrappers()) + d->stopInternal(); + + if (lastCallback != nullptr) + { + if (error.isNotEmpty()) + lastCallback->audioDeviceError (error); + else + lastCallback->audioDeviceStopped(); + } + } + + void reset() + { + xruns.store (0); + fifo.clear(); + scratchBuffer.clear(); + + for (auto& d : getDeviceWrappers()) + d->reset(); + } + + // AbstractFifo cannot be used here for two reasons: + // 1) We use absolute timestamps as the fifo's read/write positions. This not only makes the code + // more readable (especially when checking for underruns/overflows) but also simplifies the + // initial setup when actual latency is not known yet until both callbacks have fired. + // 2) AbstractFifo doesn't have the necessary mechanics to recover from underrun/overflow conditions + // in a lock-free and data-race free way. It's great if you don't care (i.e. overwrite and/or + // read stale data) or can abort the operation entirely, but this is not the case here. We + // need bespoke underrun/overflow handling here which fits this use-case. + template + void accessFifo (const uint64_t startPos, const int numChannels, const int numItems, Callback&& operateOnRange) + { + const auto fifoSize = fifo.getNumSamples(); + auto fifoPos = static_cast (startPos % static_cast (fifoSize)); + + for (int pos = 0; pos < numItems;) + { + const auto max = std::min (numItems - pos, fifoSize - fifoPos); + + struct Args { int fifoPos, inputPos, nItems, channel; }; + + for (auto ch = 0; ch < numChannels; ++ch) + operateOnRange (Args { fifoPos, pos, max, ch }); + + fifoPos = (fifoPos + max) % fifoSize; + pos += max; + } + } + + void inputAudioCallback (const float* const* channels, int numChannels, int n, const AudioIODeviceCallbackContext& context) noexcept + { + auto& writePos = inputWrapper.sampleTime; + + { + ScopedLock lock (callbackLock); + + if (callback != nullptr) + { + const auto numActiveOutputChannels = outputWrapper.getActiveChannels().countNumberOfSetBits(); + jassert (numActiveOutputChannels <= scratchBuffer.getNumChannels()); + + callback->audioDeviceIOCallbackWithContext (channels, + numChannels, + scratchBuffer.getArrayOfWritePointers(), + numActiveOutputChannels, + n, + context); + } + else + { + scratchBuffer.clear(); + } + } + + auto currentWritePos = writePos.load(); + const auto nextWritePos = currentWritePos + static_cast (n); + + writePos.compare_exchange_strong (currentWritePos, nextWritePos); + + if (currentWritePos == invalidSampleTime) + return; + + const auto readPos = outputWrapper.sampleTime.load(); + + // check for fifo overflow + if (readPos != invalidSampleTime) + { + // write will overlap previous read + if (readPos > currentWritePos || (currentWritePos + static_cast (n) - readPos) > static_cast (fifo.getNumSamples())) + { + xrun(); + return; + } + } + + accessFifo (currentWritePos, scratchBuffer.getNumChannels(), n, [&] (const auto& args) + { + FloatVectorOperations::copy (fifo.getWritePointer (args.channel, args.fifoPos), + scratchBuffer.getReadPointer (args.channel, args.inputPos), + args.nItems); + }); + + { + auto invalid = invalidSampleTime; + lastValidReadPosition.compare_exchange_strong (invalid, nextWritePos); + } + } + + void outputAudioCallback (float* const* channels, int numChannels, int n) noexcept + { + auto& readPos = outputWrapper.sampleTime; + auto currentReadPos = readPos.load(); + + if (currentReadPos == invalidSampleTime) + return; + + const auto writePos = inputWrapper.sampleTime.load(); + + // check for fifo underrun + if (writePos != invalidSampleTime) + { + if ((currentReadPos + static_cast (n)) > writePos) + { + xrun(); + return; + } + } + + // If there was an xrun, we want to output zeros until we're sure that there's some valid + // input for us to read. + const auto longN = static_cast (n); + const auto nextReadPos = currentReadPos + longN; + const auto validReadPos = lastValidReadPosition.load(); + const auto sanitisedValidReadPos = validReadPos != invalidSampleTime ? validReadPos : nextReadPos; + const auto numZerosToWrite = sanitisedValidReadPos <= currentReadPos + ? 0 + : jmin (longN, sanitisedValidReadPos - currentReadPos); + + for (auto i = 0; i < numChannels; ++i) + std::fill (channels[i], channels[i] + numZerosToWrite, 0.0f); + + accessFifo (currentReadPos + numZerosToWrite, numChannels, static_cast (longN - numZerosToWrite), [&] (const auto& args) + { + FloatVectorOperations::copy (channels[args.channel] + args.inputPos + numZerosToWrite, + fifo.getReadPointer (args.channel, args.fifoPos), + args.nItems); + }); + + // use compare exchange here as we need to avoid the case + // where we overwrite readPos being equal to invalidSampleTime + readPos.compare_exchange_strong (currentReadPos, nextReadPos); + } + + void xrun() noexcept + { + for (auto& d : getDeviceWrappers()) + d->sampleTime.store (invalidSampleTime); + + ++xruns; + } + + void handleAudioDeviceAboutToStart (AudioIODevice* device) + { + const ScopedLock sl (callbackLock); + + auto newSampleRate = device->getCurrentSampleRate(); + auto commonRates = getAvailableSampleRates(); + + if (! commonRates.contains (newSampleRate)) + { + commonRates.sort(); + + if (newSampleRate < commonRates.getFirst() || newSampleRate > commonRates.getLast()) + { + newSampleRate = jlimit (commonRates.getFirst(), commonRates.getLast(), newSampleRate); + } + else + { + for (auto it = commonRates.begin(); it < commonRates.end() - 1; ++it) + { + if (it[0] < newSampleRate && it[1] > newSampleRate) + { + newSampleRate = newSampleRate - it[0] < it[1] - newSampleRate ? it[0] : it[1]; + break; + } + } + } + } + + currentSampleRate = newSampleRate; + bool anySampleRateChanges = false; + + for (auto& d : getDeviceWrappers()) + { + if (! approximatelyEqual (d->getCurrentSampleRate(), currentSampleRate)) + { + d->setCurrentSampleRate (currentSampleRate); + anySampleRateChanges = true; + } + } + + if (anySampleRateChanges && owner != nullptr) + owner->audioDeviceListChanged(); + + if (callback != nullptr) + callback->audioDeviceAboutToStart (device); + } + + void handleAudioDeviceStopped() { shutdown ({}); } + void handleAudioDeviceError (const String& errorMessage) { shutdown (errorMessage.isNotEmpty() ? errorMessage : String ("unknown")); } + + //============================================================================== + struct DeviceWrapper final : public AudioIODeviceCallback + { + DeviceWrapper (AudioIODeviceCombiner& cd, std::unique_ptr d, bool shouldBeInput) + : owner (cd), + device (std::move (d)), + input (shouldBeInput) + { + device->setAsyncRestarter (&owner); + } + + ~DeviceWrapper() override + { + device->close(); + } + + void reset() + { + sampleTime.store (invalidSampleTime); + } + + void audioDeviceIOCallbackWithContext (const float* const* inputChannelData, + int numInputChannels, + float* const* outputChannelData, + int numOutputChannels, + int numSamples, + const AudioIODeviceCallbackContext& context) override + { + if (std::exchange (device->hadDiscontinuity, false)) + owner.xrun(); + + updateSampleTimeFromContext (context); + + if (input) + owner.inputAudioCallback (inputChannelData, numInputChannels, numSamples, context); + else + owner.outputAudioCallback (outputChannelData, numOutputChannels, numSamples); + } + + void audioDeviceAboutToStart (AudioIODevice* d) override { owner.handleAudioDeviceAboutToStart (d); } + void audioDeviceStopped() override { owner.handleAudioDeviceStopped(); } + void audioDeviceError (const String& errorMessage) override { owner.handleAudioDeviceError (errorMessage); } + + bool setCurrentSampleRate (double newSampleRate) { return device->setCurrentSampleRate (newSampleRate); } + StringArray getChannelNames() const { return input ? device->getInputChannelNames() : device->getOutputChannelNames(); } + BigInteger getActiveChannels() const { return input ? device->getActiveInputChannels() : device->getActiveOutputChannels(); } + int getLatencyInSamples() const { return input ? device->getInputLatencyInSamples() : device->getOutputLatencyInSamples(); } + int getIndexOfDevice (bool asInput) const { return device->getIndexOfDevice (asInput); } + double getCurrentSampleRate() const { return device->getCurrentSampleRate(); } + int getCurrentBufferSizeSamples() const { return device->getCurrentBufferSizeSamples(); } + Array getAvailableSampleRates() const { return device->getAvailableSampleRates(); } + Array getAvailableBufferSizes() const { return device->getAvailableBufferSizes(); } + int getCurrentBitDepth() const { return device->getCurrentBitDepth(); } + int getDefaultBufferSize() const { return device->getDefaultBufferSize(); } + void start (AudioIODeviceCallback* callbackToNotify) const { return device->start (callbackToNotify); } + AudioIODeviceCallback* stopInternal() const { return device->stopInternal(); } + void close() const { return device->close(); } + AudioWorkgroup getWorkgroup() const { return device->getWorkgroup(); } + + String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, int bufferSizeSamples) const + { + return device->open (inputChannels, outputChannels, sampleRate, bufferSizeSamples); + } + + std::uint64_t nsToSampleTime (std::uint64_t ns) const noexcept + { + return static_cast (std::round (static_cast (ns) * device->getCurrentSampleRate() * 1e-9)); + } + + void updateSampleTimeFromContext (const AudioIODeviceCallbackContext& context) noexcept + { + auto callbackSampleTime = context.hostTimeNs != nullptr ? nsToSampleTime (*context.hostTimeNs) : 0; + + if (input) + callbackSampleTime += static_cast (owner.targetLatency); + + auto copy = invalidSampleTime; + + if (sampleTime.compare_exchange_strong (copy, callbackSampleTime) && (! input)) + owner.lastValidReadPosition = invalidSampleTime; + } + + bool isInput() const { return input; } + + std::atomic sampleTime { invalidSampleTime }; + + private: + + //============================================================================== + AudioIODeviceCombiner& owner; + std::unique_ptr device; + const bool input; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DeviceWrapper) + }; + + /* If the current AudioIODeviceCombiner::callback is nullptr, it sets itself as the callback + and forwards error related callbacks to the provided callback + */ + class ScopedErrorForwarder final : public AudioIODeviceCallback + { + public: + ScopedErrorForwarder (AudioIODeviceCombiner& ownerIn, AudioIODeviceCallback* cb) + : owner (ownerIn), + target (cb) + { + const ScopedLock sl (owner.callbackLock); + + if (owner.callback == nullptr) + owner.callback = this; + } + + ~ScopedErrorForwarder() override + { + const ScopedLock sl (owner.callbackLock); + + if (owner.callback == this) + owner.callback = nullptr; + } + + // We only want to be notified about error conditions when the owner's callback is nullptr. + // This class shouldn't be relied on for forwarding this call. + void audioDeviceAboutToStart (AudioIODevice*) override {} + + void audioDeviceStopped() override + { + if (target != nullptr) + target->audioDeviceStopped(); + + error = true; + } + + void audioDeviceError (const String& errorMessage) override + { + owner.lastError = errorMessage; + + if (target != nullptr) + target->audioDeviceError (errorMessage); + + error = true; + } + + bool encounteredError() const { return error; } + + private: + AudioIODeviceCombiner& owner; + AudioIODeviceCallback* target; + bool error = false; + }; + + DeviceWrapper inputWrapper, outputWrapper; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioIODeviceCombiner) +}; + + +//============================================================================== +class CoreAudioIODeviceType final : public AudioIODeviceType, + private AsyncUpdater +{ +public: + CoreAudioIODeviceType() : AudioIODeviceType ("CoreAudio") + { + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioHardwarePropertyDevices; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; + + AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this); + } + + ~CoreAudioIODeviceType() override + { + cancelPendingUpdate(); + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioHardwarePropertyDevices; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; + + AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this); + } + + //============================================================================== + void scanForDevices() override + { + hasScanned = true; + + inputDeviceNames.clear(); + outputDeviceNames.clear(); + inputIds.clear(); + outputIds.clear(); + + auto audioDevices = audioObjectGetProperties (kAudioObjectSystemObject, { kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }); + + for (const auto audioDevice : audioDevices) + { + if (const auto optionalName = audioObjectGetProperty (audioDevice, { kAudioDevicePropertyDeviceNameCFString, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain })) + { + if (const CFUniquePtr name { *optionalName }) + { + const auto nameString = String::fromCFString (name.get()); + + if (const auto numIns = getNumChannels (audioDevice, true); numIns > 0) + { + inputDeviceNames.add (nameString); + inputIds.add (audioDevice); + } + + if (const auto numOuts = getNumChannels (audioDevice, false); numOuts > 0) + { + outputDeviceNames.add (nameString); + outputIds.add (audioDevice); + } + } + } + } + + inputDeviceNames.appendNumbersToDuplicates (false, true); + outputDeviceNames.appendNumbersToDuplicates (false, true); + } + + StringArray getDeviceNames (bool wantInputNames) const override + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + return wantInputNames ? inputDeviceNames + : outputDeviceNames; + } + + int getDefaultDeviceIndex (bool forInput) const override + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + // if they're asking for any input channels at all, use the default input, so we + // get the built-in mic rather than the built-in output with no inputs.. + + AudioObjectPropertyAddress pa; + auto selector = forInput ? kAudioHardwarePropertyDefaultInputDevice + : kAudioHardwarePropertyDefaultOutputDevice; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = juceAudioObjectPropertyElementMain; + + if (auto deviceID = audioObjectGetProperty (kAudioObjectSystemObject, { selector, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain })) + { + auto& ids = forInput ? inputIds : outputIds; + + if (auto it = std::find (ids.begin(), ids.end(), deviceID); it != ids.end()) + return static_cast (std::distance (ids.begin(), it)); + } + + return 0; + } + + int getIndexOfDevice (AudioIODevice* device, bool asInput) const override + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + if (auto* d = dynamic_cast (device)) + return d->getIndexOfDevice (asInput); + + if (auto* d = dynamic_cast (device)) + return d->getIndexOfDevice (asInput); + + return -1; + } + + bool hasSeparateInputsAndOutputs() const override { return true; } + + AudioIODevice* createDevice (const String& outputDeviceName, + const String& inputDeviceName) override + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + auto inputIndex = inputDeviceNames.indexOf (inputDeviceName); + auto outputIndex = outputDeviceNames.indexOf (outputDeviceName); + + auto inputDeviceID = inputIds[inputIndex]; + auto outputDeviceID = outputIds[outputIndex]; + + if (inputDeviceID == 0 && outputDeviceID == 0) + return nullptr; + + auto combinedName = outputDeviceName.isEmpty() ? inputDeviceName + : outputDeviceName; + + if (inputDeviceID == outputDeviceID) + return std::make_unique (this, combinedName, inputDeviceID, outputDeviceID).release(); + + auto in = inputDeviceID != 0 ? std::make_unique (this, inputDeviceName, inputDeviceID, 0) + : nullptr; + + auto out = outputDeviceID != 0 ? std::make_unique (this, outputDeviceName, 0, outputDeviceID) + : nullptr; + + if (in == nullptr) return out.release(); + if (out == nullptr) return in.release(); + + auto combo = std::make_unique (combinedName, this, std::move (in), std::move (out)); + return combo.release(); + } + + void audioDeviceListChanged() + { + scanForDevices(); + callDeviceChangeListeners(); + } + + //============================================================================== +private: + StringArray inputDeviceNames, outputDeviceNames; + Array inputIds, outputIds; + + bool hasScanned = false; + + void handleAsyncUpdate() override + { + audioDeviceListChanged(); + } + + static int getNumChannels (AudioDeviceID deviceID, bool input) + { + int total = 0; + + if (auto bufList = audioObjectGetProperty (deviceID, { kAudioDevicePropertyStreamConfiguration, + CoreAudioInternal::getScope (input), + juceAudioObjectPropertyElementMain })) + { + auto numStreams = (int) bufList->mNumberBuffers; + + for (int i = 0; i < numStreams; ++i) + total += bufList->mBuffers[i].mNumberChannels; + } + + return total; + } + + static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData) + { + static_cast (clientData)->triggerAsyncUpdate(); + return noErr; + } + + JUCE_DECLARE_WEAK_REFERENCEABLE (CoreAudioIODeviceType) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType) +}; + +}; + +#undef JUCE_COREAUDIOLOG + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreMidi_mac.mm b/JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreMidi_mac.mm new file mode 100644 index 00000000..e83686f1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreMidi_mac.mm @@ -0,0 +1,1284 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +#ifndef JUCE_LOG_COREMIDI_ERRORS + #define JUCE_LOG_COREMIDI_ERRORS 1 +#endif + +namespace CoreMidiHelpers +{ + static bool checkError (OSStatus err, [[maybe_unused]] int lineNum) + { + if (err == noErr) + return true; + + #if JUCE_LOG_COREMIDI_ERRORS + Logger::writeToLog ("CoreMIDI error: " + String (lineNum) + " - " + String::toHexString ((int) err)); + #endif + + return false; + } + + #undef CHECK_ERROR + #define CHECK_ERROR(a) CoreMidiHelpers::checkError (a, __LINE__) + + enum class ImplementationStrategy + { + onlyNew, + both, + onlyOld + }; + + #if (defined (MAC_OS_VERSION_11_0) || defined (__IPHONE_14_0)) + #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 || __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_14_0) + #define JUCE_HAS_NEW_COREMIDI_API 1 + #define JUCE_HAS_OLD_COREMIDI_API 0 + constexpr auto implementationStrategy = ImplementationStrategy::onlyNew; + #else + #define JUCE_HAS_NEW_COREMIDI_API 1 + #define JUCE_HAS_OLD_COREMIDI_API 1 + constexpr auto implementationStrategy = ImplementationStrategy::both; + #endif + #else + #define JUCE_HAS_NEW_COREMIDI_API 0 + #define JUCE_HAS_OLD_COREMIDI_API 1 + constexpr auto implementationStrategy = ImplementationStrategy::onlyOld; + #endif + + struct SenderBase + { + virtual ~SenderBase() noexcept = default; + + virtual void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) = 0; + virtual void send (MIDIPortRef port, MIDIEndpointRef endpoint, ump::Iterator b, ump::Iterator e) = 0; + }; + + template + struct Sender; + + #if JUCE_HAS_NEW_COREMIDI_API + template <> + struct API_AVAILABLE (macos (11.0), ios (14.0)) Sender final : public SenderBase + { + void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) override + { + newSendImpl (port, endpoint, m); + } + + void send (MIDIPortRef port, MIDIEndpointRef endpoint, ump::Iterator b, ump::Iterator e) override + { + newSendImpl (port, endpoint, b, e); + } + + private: + ump::ToUMP1Converter umpConverter; + + static ump::PacketProtocol getProtocolForEndpoint (MIDIEndpointRef ep) noexcept + { + SInt32 protocol = 0; + CHECK_ERROR (MIDIObjectGetIntegerProperty (ep, kMIDIPropertyProtocolID, &protocol)); + + return protocol == kMIDIProtocol_2_0 ? ump::PacketProtocol::MIDI_2_0 + : ump::PacketProtocol::MIDI_1_0; + } + + template + void newSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, Params&&... params) + { + #if JUCE_IOS + const MIDITimeStamp timeStamp = mach_absolute_time(); + #else + const MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); + #endif + + MIDIEventList stackList = {}; + MIDIEventPacket* end = nullptr; + + const auto init = [&] + { + // At the moment, we can only send MIDI 1.0 protocol. If the device is using MIDI + // 2.0 protocol (as may be the case for the IAC driver), we trust in the system to + // translate it. + end = MIDIEventListInit (&stackList, kMIDIProtocol_1_0); + }; + + const auto send = [&] + { + CHECK_ERROR (port != 0 ? MIDISendEventList (port, endpoint, &stackList) + : MIDIReceivedEventList (endpoint, &stackList)); + }; + + const auto add = [&] (const ump::View& view) + { + static_assert (sizeof (uint32_t) == sizeof (UInt32) + && alignof (uint32_t) == alignof (UInt32), + "If this fails, the cast below will be broken too!"); + end = MIDIEventListAdd (&stackList, + sizeof (MIDIEventList::packet), + end, + timeStamp, + view.size(), + reinterpret_cast (view.data())); + }; + + init(); + + ump::GenericUMPConverter::convertImpl (umpConverter, params..., [&] (const auto& v) + { + umpConverter.convert (v, [&] (const ump::View& view) + { + add (view); + + if (end != nullptr) + return; + + send(); + init(); + add (view); + }); + }); + + send(); + } + }; + #endif + + #if JUCE_HAS_OLD_COREMIDI_API + template <> + struct Sender final : public SenderBase + { + void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) override + { + oldSendImpl (port, endpoint, m); + } + + void send (MIDIPortRef port, MIDIEndpointRef endpoint, ump::Iterator b, ump::Iterator e) override + { + std::for_each (b, e, [&] (const ump::View& v) + { + bytestreamConverter.convert (v, 0.0, [&] (const ump::BytestreamMidiView& m) + { + send (port, endpoint, m); + }); + }); + } + + private: + ump::ToBytestreamConverter bytestreamConverter { 2048 }; + + void oldSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& message) + { + #if JUCE_IOS + const MIDITimeStamp timeStamp = mach_absolute_time(); + #else + const MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); + #endif + + HeapBlock allocatedPackets; + MIDIPacketList stackPacket; + auto* packetToSend = &stackPacket; + auto dataSize = message.bytes.size(); + + if (message.isSysEx()) + { + const int maxPacketSize = 256; + int pos = 0, bytesLeft = (int) dataSize; + const int numPackets = (bytesLeft + maxPacketSize - 1) / maxPacketSize; + allocatedPackets.malloc ((size_t) (32 * (size_t) numPackets + dataSize), 1); + packetToSend = allocatedPackets; + packetToSend->numPackets = (UInt32) numPackets; + + auto* p = packetToSend->packet; + + for (int i = 0; i < numPackets; ++i) + { + p->timeStamp = timeStamp; + p->length = (UInt16) jmin (maxPacketSize, bytesLeft); + memcpy (p->data, message.bytes.data() + pos, p->length); + pos += p->length; + bytesLeft -= p->length; + p = MIDIPacketNext (p); + } + } + else if (dataSize < 65536) // max packet size + { + auto stackCapacity = sizeof (stackPacket.packet->data); + + if (dataSize > stackCapacity) + { + allocatedPackets.malloc ((sizeof (MIDIPacketList) - stackCapacity) + dataSize, 1); + packetToSend = allocatedPackets; + } + + packetToSend->numPackets = 1; + auto& p = *(packetToSend->packet); + p.timeStamp = timeStamp; + p.length = (UInt16) dataSize; + memcpy (p.data, message.bytes.data(), dataSize); + } + else + { + jassertfalse; // packet too large to send! + return; + } + + if (port != 0) + MIDISend (port, endpoint, packetToSend); + else + MIDIReceived (endpoint, packetToSend); + } + }; + #endif + + #if JUCE_HAS_NEW_COREMIDI_API && JUCE_HAS_OLD_COREMIDI_API + template <> + struct Sender + { + Sender() + : sender (makeImpl()) + {} + + void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) + { + sender->send (port, endpoint, m); + } + + void send (MIDIPortRef port, MIDIEndpointRef endpoint, ump::Iterator b, ump::Iterator e) + { + sender->send (port, endpoint, b, e); + } + + private: + static std::unique_ptr makeImpl() + { + if (@available (macOS 11, iOS 14, *)) + return std::make_unique>(); + + return std::make_unique>(); + } + + std::unique_ptr sender; + }; + #endif + + using SenderToUse = Sender; + + template + class ScopedMidiResource + { + public: + ScopedMidiResource() = default; + + explicit ScopedMidiResource (Resource r) : contents (r, {}) {} + + ~ScopedMidiResource() noexcept + { + auto ref = std::get<0> (contents); + + if (ref != 0) + std::get<1> (contents) (ref); + } + + ScopedMidiResource (const ScopedMidiResource& other) = delete; + ScopedMidiResource& operator= (const ScopedMidiResource& other) = delete; + + ScopedMidiResource (ScopedMidiResource&& other) noexcept { swap (other); } + + ScopedMidiResource& operator= (ScopedMidiResource&& other) noexcept + { + swap (other); + return *this; + } + + void swap (ScopedMidiResource& other) noexcept { std::swap (other.contents, contents); } + + Resource operator*() const noexcept { return std::get<0> (contents); } + + Resource release() noexcept + { + auto old = std::get<0> (contents); + std::get<0> (contents) = 0; + return old; + } + + private: + std::tuple contents { {}, {} }; + }; + + struct PortRefDestructor + { + void operator() (MIDIPortRef p) const noexcept { MIDIPortDispose (p); } + }; + + using ScopedPortRef = ScopedMidiResource; + + struct EndpointRefDestructor + { + void operator() (MIDIEndpointRef p) const noexcept { MIDIEndpointDispose (p); } + }; + + using ScopedEndpointRef = ScopedMidiResource; + + //============================================================================== + class MidiPortAndEndpoint + { + public: + MidiPortAndEndpoint (ScopedPortRef p, ScopedEndpointRef ep) noexcept + : port (std::move (p)), endpoint (std::move (ep)) + {} + + ~MidiPortAndEndpoint() noexcept + { + // if port != 0, it means we didn't create the endpoint, so it's not safe to delete it + if (*port != 0) + endpoint.release(); + } + + void send (const ump::BytestreamMidiView& m) + { + sender.send (*port, *endpoint, m); + } + + void send (ump::Iterator b, ump::Iterator e) + { + sender.send (*port, *endpoint, b, e); + } + + bool canStop() const noexcept { return *port != 0; } + void stop() const { CHECK_ERROR (MIDIPortDisconnectSource (*port, *endpoint)); } + + private: + ScopedPortRef port; + ScopedEndpointRef endpoint; + + SenderToUse sender; + }; + + static MidiDeviceInfo getMidiObjectInfo (MIDIObjectRef entity) + { + MidiDeviceInfo info; + + { + CFObjectHolder str; + + if (CHECK_ERROR (MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str.object))) + info.name = String::fromCFString (str.object); + } + + SInt32 objectID = 0; + + if (CHECK_ERROR (MIDIObjectGetIntegerProperty (entity, kMIDIPropertyUniqueID, &objectID))) + { + info.identifier = String (objectID); + } + else + { + CFObjectHolder str; + + if (CHECK_ERROR (MIDIObjectGetStringProperty (entity, kMIDIPropertyUniqueID, &str.object))) + info.identifier = String::fromCFString (str.object); + } + + return info; + } + + static MidiDeviceInfo getEndpointInfo (MIDIEndpointRef endpoint, bool isExternal) + { + // 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 getMidiObjectInfo (endpoint); + + auto result = getMidiObjectInfo (endpoint); + + // 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 info = getMidiObjectInfo (device); + + 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 = info; + } + else if (! result.name.startsWithIgnoreCase (info.name)) + { + // prepend the device name and identifier to the entity's + result.name = (info.name + " " + result.name).trimEnd(); + result.identifier = info.identifier + " " + result.identifier; + } + } + } + + return result; + } + + static MidiDeviceInfo getConnectedEndpointInfo (MIDIEndpointRef endpoint) + { + MidiDeviceInfo result; + + // Does the endpoint have connections? + CFObjectHolder connections; + int numConnections = 0; + + MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections.object); + + if (connections.object != nullptr) + { + numConnections = ((int) CFDataGetLength (connections.object)) / (int) sizeof (MIDIUniqueID); + + if (numConnections > 0) + { + auto* pid = reinterpret_cast (CFDataGetBytePtr (connections.object)); + + for (int i = 0; i < numConnections; ++i, ++pid) + { + auto id = (MIDIUniqueID) ByteOrder::swapIfLittleEndian ((uint32) *pid); + MIDIObjectRef connObject; + MIDIObjectType connObjectType; + auto err = MIDIObjectFindByUniqueID (id, &connObject, &connObjectType); + + if (err == noErr) + { + MidiDeviceInfo deviceInfo; + + if (connObjectType == kMIDIObjectType_ExternalSource + || connObjectType == kMIDIObjectType_ExternalDestination) + { + // Connected to an external device's endpoint (10.3 and later). + deviceInfo = getEndpointInfo (static_cast (connObject), true); + } + else + { + // Connected to an external device (10.2) (or something else, catch-all) + deviceInfo = getMidiObjectInfo (connObject); + } + + if (deviceInfo != MidiDeviceInfo()) + { + if (result.name.isNotEmpty()) result.name += ", "; + if (result.identifier.isNotEmpty()) result.identifier += ", "; + + result.name += deviceInfo.name; + result.identifier += deviceInfo.identifier; + } + } + } + } + } + + // 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 int createUniqueIDForMidiPort (String deviceName, bool isInput) + { + String uniqueID; + + #ifdef JucePlugin_CFBundleIdentifier + uniqueID = JUCE_STRINGIFY (JucePlugin_CFBundleIdentifier); + #else + auto appBundle = File::getSpecialLocation (File::currentApplicationFile); + CFUniquePtr appBundlePath (appBundle.getFullPathName().toCFString()); + + if (auto bundleURL = CFUniquePtr (CFURLCreateWithFileSystemPath (kCFAllocatorDefault, + appBundlePath.get(), + kCFURLPOSIXPathStyle, + true))) + if (auto bundleRef = CFUniquePtr (CFBundleCreate (kCFAllocatorDefault, bundleURL.get()))) + if (auto bundleId = CFBundleGetIdentifier (bundleRef.get())) + uniqueID = String::fromCFString (bundleId); + #endif + + if (uniqueID.isEmpty()) + uniqueID = String (Random::getSystemRandom().nextInt (1024)); + + uniqueID += "." + deviceName + (isInput ? ".input" : ".output"); + return uniqueID.hashCode(); + } + + static void enableSimulatorMidiSession() + { + #if TARGET_OS_SIMULATOR + static bool hasEnabledNetworkSession = false; + + if (! hasEnabledNetworkSession) + { + MIDINetworkSession* session = [MIDINetworkSession defaultSession]; + session.enabled = YES; + session.connectionPolicy = MIDINetworkConnectionPolicy_Anyone; + + hasEnabledNetworkSession = true; + } + #endif + } + + static void globalSystemChangeCallback (const MIDINotification* notification, void*) + { + if (notification != nullptr && notification->messageID == kMIDIMsgSetupChanged) + MidiDeviceListConnectionBroadcaster::get().notify(); + } + + static String getGlobalMidiClientName() + { + if (auto* app = JUCEApplicationBase::getInstance()) + return app->getApplicationName(); + + return "JUCE"; + } + + static MIDIClientRef getGlobalMidiClient() + { + static const auto globalMidiClient = [&] + { + // Since OSX 10.6, the MIDIClientCreate function will only work + // correctly when called from the message thread! + JUCE_ASSERT_MESSAGE_THREAD + + enableSimulatorMidiSession(); + + CFUniquePtr name (getGlobalMidiClientName().toCFString()); + MIDIClientRef result{}; + CHECK_ERROR (MIDIClientCreate (name.get(), globalSystemChangeCallback, nullptr, &result)); + return result; + }(); + + return globalMidiClient; + } + + 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. + JUCE_ASSERT_MESSAGE_THREAD + + if (getGlobalMidiClient() == 0) + { + jassertfalse; + return {}; + } + + enableSimulatorMidiSession(); + + Array devices; + auto numDevices = (forInput ? MIDIGetNumberOfSources() : MIDIGetNumberOfDestinations()); + + for (ItemCount i = 0; i < numDevices; ++i) + { + MidiDeviceInfo deviceInfo; + + if (auto dest = forInput ? MIDIGetSource (i) : MIDIGetDestination (i)) + deviceInfo = getConnectedEndpointInfo (dest); + + if (deviceInfo == MidiDeviceInfo()) + deviceInfo.name = deviceInfo.identifier = ""; + + devices.add (deviceInfo); + } + + return devices; + } + + //============================================================================== + template + struct Receiver; + + #if JUCE_HAS_NEW_COREMIDI_API + template <> + struct Receiver + { + Receiver (ump::PacketProtocol protocol, ump::Receiver& receiver) + : u32InputHandler (std::make_unique (protocol, receiver)) + {} + + Receiver (MidiInput& input, MidiInputCallback& callback) + : u32InputHandler (std::make_unique (input, callback)) + {} + + void dispatch (const MIDIEventList* list, double time) const + { + auto* packet = list->packet; + + for (uint32_t i = 0; i < list->numPackets; ++i) + { + static_assert (sizeof (uint32_t) == sizeof (UInt32) + && alignof (uint32_t) == alignof (UInt32), + "If this fails, the cast below will be broken too!"); + u32InputHandler->pushMidiData (reinterpret_cast (packet->words), + reinterpret_cast (packet->words + packet->wordCount), + time); + + packet = MIDIEventPacketNext (packet); + } + } + + private: + std::unique_ptr u32InputHandler; + }; + #endif + + #if JUCE_HAS_OLD_COREMIDI_API + template <> + struct Receiver + { + Receiver (ump::PacketProtocol protocol, ump::Receiver& receiver) + : bytestreamInputHandler (std::make_unique (protocol, receiver)) + {} + + Receiver (MidiInput& input, MidiInputCallback& callback) + : bytestreamInputHandler (std::make_unique (input, callback)) + {} + + void dispatch (const MIDIPacketList* list, double time) const + { + auto* packet = list->packet; + + for (unsigned int i = 0; i < list->numPackets; ++i) + { + auto len = readUnalignedlength)> (&(packet->length)); + bytestreamInputHandler->pushMidiData (packet->data, len, time); + + packet = MIDIPacketNext (packet); + } + } + + private: + std::unique_ptr bytestreamInputHandler; + }; + #endif + + #if JUCE_HAS_NEW_COREMIDI_API && JUCE_HAS_OLD_COREMIDI_API + template <> + struct Receiver + { + Receiver (ump::PacketProtocol protocol, ump::Receiver& receiver) + : newReceiver (protocol, receiver), oldReceiver (protocol, receiver) + {} + + Receiver (MidiInput& input, MidiInputCallback& callback) + : newReceiver (input, callback), oldReceiver (input, callback) + {} + + void dispatch (const MIDIEventList* list, double time) const + { + newReceiver.dispatch (list, time); + } + + void dispatch (const MIDIPacketList* list, double time) const + { + oldReceiver.dispatch (list, time); + } + + private: + Receiver newReceiver; + Receiver oldReceiver; + }; + #endif + + using ReceiverToUse = Receiver; + + class MidiPortAndCallback; + CriticalSection callbackLock; + Array activeCallbacks; + + class MidiPortAndCallback + { + public: + MidiPortAndCallback (MidiInput& inputIn, ReceiverToUse receiverIn) + : input (&inputIn), receiver (std::move (receiverIn)) + {} + + ~MidiPortAndCallback() + { + active = false; + + { + const ScopedLock sl (callbackLock); + activeCallbacks.removeFirstMatchingValue (this); + } + + if (portAndEndpoint != nullptr && portAndEndpoint->canStop()) + portAndEndpoint->stop(); + } + + template + void handlePackets (const EventList* list) + { + const auto time = Time::getMillisecondCounterHiRes() * 0.001; + + const ScopedLock sl (callbackLock); + + if (activeCallbacks.contains (this) && active) + receiver.dispatch (list, time); + } + + MidiInput* input = nullptr; + std::atomic active { false }; + + ReceiverToUse receiver; + + std::unique_ptr portAndEndpoint; + + private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiPortAndCallback) + }; + + //============================================================================== + 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; + } + + struct CreatorFunctionPointers + { + OSStatus (*createInputPort) (ump::PacketProtocol protocol, + MIDIClientRef client, + CFStringRef portName, + void* refCon, + MIDIPortRef* outPort); + + OSStatus (*createDestination) (ump::PacketProtocol protocol, + MIDIClientRef client, + CFStringRef name, + void* refCon, + MIDIEndpointRef* outDest); + + OSStatus (*createSource) (ump::PacketProtocol protocol, + MIDIClientRef client, + CFStringRef name, + MIDIEndpointRef* outSrc); + }; + + template + struct CreatorFunctions; + + #if JUCE_HAS_NEW_COREMIDI_API + template <> + struct API_AVAILABLE (macos (11.0), ios (14.0)) CreatorFunctions + { + static OSStatus createInputPort (ump::PacketProtocol protocol, + MIDIClientRef client, + CFStringRef portName, + void* refCon, + MIDIPortRef* outPort) + { + return MIDIInputPortCreateWithProtocol (client, + portName, + convertToPacketProtocol (protocol), + outPort, + ^void (const MIDIEventList* l, void* src) + { + newMidiInputProc (l, refCon, src); + }); + } + + static OSStatus createDestination (ump::PacketProtocol protocol, + MIDIClientRef client, + CFStringRef name, + void* refCon, + MIDIEndpointRef* outDest) + { + return MIDIDestinationCreateWithProtocol (client, + name, + convertToPacketProtocol (protocol), + outDest, + ^void (const MIDIEventList* l, void* src) + { + newMidiInputProc (l, refCon, src); + }); + } + + static OSStatus createSource (ump::PacketProtocol protocol, + MIDIClientRef client, + CFStringRef name, + MIDIEndpointRef* outSrc) + { + return MIDISourceCreateWithProtocol (client, + name, + convertToPacketProtocol (protocol), + outSrc); + } + + static constexpr CreatorFunctionPointers getCreatorFunctionPointers() + { + return { createInputPort, createDestination, createSource }; + } + + private: + static constexpr MIDIProtocolID convertToPacketProtocol (ump::PacketProtocol p) + { + return p == ump::PacketProtocol::MIDI_2_0 ? kMIDIProtocol_2_0 + : kMIDIProtocol_1_0; + } + + static void newMidiInputProc (const MIDIEventList* list, void* readProcRefCon, void*) + { + static_cast (readProcRefCon)->handlePackets (list); + } + }; + #endif + + #if JUCE_HAS_OLD_COREMIDI_API + template <> + struct CreatorFunctions + { + static OSStatus createInputPort (ump::PacketProtocol, + MIDIClientRef client, + CFStringRef portName, + void* refCon, + MIDIPortRef* outPort) + { + return MIDIInputPortCreate (client, portName, oldMidiInputProc, refCon, outPort); + } + + static OSStatus createDestination (ump::PacketProtocol, + MIDIClientRef client, + CFStringRef name, + void* refCon, + MIDIEndpointRef* outDest) + { + return MIDIDestinationCreate (client, name, oldMidiInputProc, refCon, outDest); + } + + static OSStatus createSource (ump::PacketProtocol, + MIDIClientRef client, + CFStringRef name, + MIDIEndpointRef* outSrc) + { + return MIDISourceCreate (client, name, outSrc); + } + + static constexpr CreatorFunctionPointers getCreatorFunctionPointers() + { + return { createInputPort, createDestination, createSource }; + } + + private: + static void oldMidiInputProc (const MIDIPacketList* list, void* readProcRefCon, void*) + { + static_cast (readProcRefCon)->handlePackets (list); + } + }; + #endif + + #if JUCE_HAS_NEW_COREMIDI_API && JUCE_HAS_OLD_COREMIDI_API + template <> + struct CreatorFunctions + { + static OSStatus createInputPort (ump::PacketProtocol protocol, + MIDIClientRef client, + CFStringRef portName, + void* refCon, + MIDIPortRef* outPort) + { + return getCreatorFunctionPointers().createInputPort (protocol, client, portName, refCon, outPort); + } + + static OSStatus createDestination (ump::PacketProtocol protocol, + MIDIClientRef client, + CFStringRef name, + void* refCon, + MIDIEndpointRef* outDest) + { + return getCreatorFunctionPointers().createDestination (protocol, client, name, refCon, outDest); + } + + static OSStatus createSource (ump::PacketProtocol protocol, + MIDIClientRef client, + CFStringRef name, + MIDIEndpointRef* outSrc) + { + return getCreatorFunctionPointers().createSource (protocol, client, name, outSrc); + } + + private: + static CreatorFunctionPointers getCreatorFunctionPointers() + { + if (@available (macOS 11, iOS 14, *)) + return CreatorFunctions::getCreatorFunctionPointers(); + + return CreatorFunctions::getCreatorFunctionPointers(); + } + }; + #endif + + using CreatorFunctionsToUse = CreatorFunctions; +} + +//============================================================================== +class MidiInput::Pimpl : public CoreMidiHelpers::MidiPortAndCallback +{ +public: + using MidiPortAndCallback::MidiPortAndCallback; + + static std::unique_ptr makePimpl (MidiInput& midiInput, + ump::PacketProtocol packetProtocol, + ump::Receiver& umpReceiver) + { + return std::make_unique (midiInput, CoreMidiHelpers::ReceiverToUse (packetProtocol, umpReceiver)); + } + + static std::unique_ptr makePimpl (MidiInput& midiInput, + MidiInputCallback* midiInputCallback) + { + if (midiInputCallback == nullptr) + return {}; + + return std::make_unique (midiInput, CoreMidiHelpers::ReceiverToUse (midiInput, *midiInputCallback)); + } + + template + static std::unique_ptr makeInput (const String& name, + const String& identifier, + Args&&... args) + { + using namespace CoreMidiHelpers; + + if (auto midiInput = rawToUniquePtr (new MidiInput (name, identifier))) + { + if ((midiInput->internal = makePimpl (*midiInput, std::forward (args)...))) + { + const ScopedLock sl (callbackLock); + activeCallbacks.add (midiInput->internal.get()); + + return midiInput; + } + } + + return {}; + } + + template + static std::unique_ptr openDevice (ump::PacketProtocol protocol, + const String& deviceIdentifier, + Args&&... args) + { + using namespace CoreMidiHelpers; + + if (deviceIdentifier.isEmpty()) + return {}; + + if (auto client = getGlobalMidiClient()) + { + for (auto& endpoint : getEndpoints (true)) + { + auto endpointInfo = getConnectedEndpointInfo (endpoint); + + if (deviceIdentifier != endpointInfo.identifier) + continue; + + CFObjectHolder cfName; + + if (! CHECK_ERROR (MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &cfName.object))) + continue; + + if (auto input = makeInput (endpointInfo.name, endpointInfo.identifier, std::forward (args)...)) + { + MIDIPortRef port; + + if (! CHECK_ERROR (CreatorFunctionsToUse::createInputPort (protocol, client, cfName.object, input->internal.get(), &port))) + continue; + + ScopedPortRef scopedPort { port }; + + if (! CHECK_ERROR (MIDIPortConnectSource (*scopedPort, endpoint, nullptr))) + continue; + + input->internal->portAndEndpoint = std::make_unique (std::move (scopedPort), ScopedEndpointRef { endpoint }); + return input; + } + } + } + + return {}; + } + + template + static std::unique_ptr createDevice (ump::PacketProtocol protocol, + const String& deviceName, + Args&&... args) + { + using namespace CoreMidiHelpers; + + if (auto client = getGlobalMidiClient()) + { + auto deviceIdentifier = createUniqueIDForMidiPort (deviceName, true); + + if (auto input = makeInput (deviceName, String (deviceIdentifier), std::forward (args)...)) + { + MIDIEndpointRef endpoint; + CFUniquePtr name (deviceName.toCFString()); + + auto err = CreatorFunctionsToUse::createDestination (protocol, client, name.get(), input->internal.get(), &endpoint); + ScopedEndpointRef scopedEndpoint { 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 {}; + } + #endif + + if (! CHECK_ERROR (err)) + return {}; + + if (! CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier))) + return {}; + + input->internal->portAndEndpoint = std::make_unique (ScopedPortRef{}, std::move (scopedEndpoint)); + return input; + } + } + + return {}; + } +}; + +//============================================================================== +Array MidiInput::getAvailableDevices() +{ + return CoreMidiHelpers::findDevices (true); +} + +MidiDeviceInfo MidiInput::getDefaultDevice() +{ + return getAvailableDevices().getFirst(); +} + +std::unique_ptr MidiInput::openDevice (const String& deviceIdentifier, MidiInputCallback* callback) +{ + if (callback == nullptr) + return nullptr; + + return Pimpl::openDevice (ump::PacketProtocol::MIDI_1_0, + deviceIdentifier, + callback); +} + +std::unique_ptr MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) +{ + return Pimpl::createDevice (ump::PacketProtocol::MIDI_1_0, + deviceName, + callback); +} + +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() = default; + +void MidiInput::start() +{ + const ScopedLock sl (CoreMidiHelpers::callbackLock); + internal->active = true; +} + +void MidiInput::stop() +{ + const ScopedLock sl (CoreMidiHelpers::callbackLock); + internal->active = false; +} + +//============================================================================== +class MidiOutput::Pimpl : public CoreMidiHelpers::MidiPortAndEndpoint +{ +public: + using MidiPortAndEndpoint::MidiPortAndEndpoint; +}; + +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 {}; + + using namespace CoreMidiHelpers; + + if (auto client = getGlobalMidiClient()) + { + for (auto& endpoint : getEndpoints (false)) + { + auto endpointInfo = getConnectedEndpointInfo (endpoint); + + if (deviceIdentifier != endpointInfo.identifier) + continue; + + CFObjectHolder cfName; + + if (! CHECK_ERROR (MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &cfName.object))) + continue; + + MIDIPortRef port; + + if (! CHECK_ERROR (MIDIOutputPortCreate (client, cfName.object, &port))) + continue; + + ScopedPortRef scopedPort { port }; + + auto midiOutput = rawToUniquePtr (new MidiOutput (endpointInfo.name, endpointInfo.identifier)); + midiOutput->internal = std::make_unique (std::move (scopedPort), ScopedEndpointRef { endpoint }); + + return midiOutput; + } + } + + return {}; +} + +std::unique_ptr MidiOutput::createNewDevice (const String& deviceName) +{ + using namespace CoreMidiHelpers; + + if (auto client = getGlobalMidiClient()) + { + MIDIEndpointRef endpoint; + + CFUniquePtr name (deviceName.toCFString()); + + auto err = CreatorFunctionsToUse::createSource (ump::PacketProtocol::MIDI_1_0, client, name.get(), &endpoint); + ScopedEndpointRef scopedEndpoint { 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 {}; + } + #endif + + if (! CHECK_ERROR (err)) + return {}; + + auto deviceIdentifier = createUniqueIDForMidiPort (deviceName, false); + + if (! CHECK_ERROR (MIDIObjectSetIntegerProperty (*scopedEndpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier))) + return {}; + + auto midiOutput = rawToUniquePtr (new MidiOutput (deviceName, String (deviceIdentifier))); + midiOutput->internal = std::make_unique (ScopedPortRef{}, std::move (scopedEndpoint)); + + 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() +{ + stopBackgroundThread(); +} + +void MidiOutput::sendMessageNow (const MidiMessage& message) +{ + internal->send (ump::BytestreamMidiView (&message)); +} + +MidiDeviceListConnection MidiDeviceListConnection::make (std::function cb) +{ + auto& broadcaster = MidiDeviceListConnectionBroadcaster::get(); + return { &broadcaster, broadcaster.add (std::move (cb)) }; +} + +#undef CHECK_ERROR + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_DirectSound_windows.cpp similarity index 80% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_DirectSound_windows.cpp index 3a2f501a..dcc6abed 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_DirectSound_windows.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -37,46 +37,46 @@ extern "C" #undef INTERFACE #define INTERFACE IDirectSound - DECLARE_INTERFACE_(IDirectSound, IUnknown) + DECLARE_INTERFACE_ (IDirectSound, IUnknown) { - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - STDMETHOD(CreateSoundBuffer) (THIS_ DSBUFFERDESC*, IDirectSoundBuffer**, LPUNKNOWN) PURE; - STDMETHOD(GetCaps) (THIS_ void*) PURE; - STDMETHOD(DuplicateSoundBuffer) (THIS_ IDirectSoundBuffer*, IDirectSoundBuffer**) PURE; - STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE; - STDMETHOD(Compact) (THIS) PURE; - STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD) PURE; - STDMETHOD(SetSpeakerConfig) (THIS_ DWORD) PURE; - STDMETHOD(Initialize) (THIS_ const GUID*) PURE; + STDMETHOD (QueryInterface) (THIS_ REFIID, LPVOID*) PURE; + STDMETHOD_ (ULONG,AddRef) (THIS) PURE; + STDMETHOD_ (ULONG,Release) (THIS) PURE; + STDMETHOD (CreateSoundBuffer) (THIS_ DSBUFFERDESC*, IDirectSoundBuffer**, LPUNKNOWN) PURE; + STDMETHOD (GetCaps) (THIS_ void*) PURE; + STDMETHOD (DuplicateSoundBuffer) (THIS_ IDirectSoundBuffer*, IDirectSoundBuffer**) PURE; + STDMETHOD (SetCooperativeLevel) (THIS_ HWND, DWORD) PURE; + STDMETHOD (Compact) (THIS) PURE; + STDMETHOD (GetSpeakerConfig) (THIS_ LPDWORD) PURE; + STDMETHOD (SetSpeakerConfig) (THIS_ DWORD) PURE; + STDMETHOD (Initialize) (THIS_ const GUID*) PURE; }; #undef INTERFACE #define INTERFACE IDirectSoundBuffer - DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown) + DECLARE_INTERFACE_ (IDirectSoundBuffer, IUnknown) { - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - STDMETHOD(GetCaps) (THIS_ void*) PURE; - STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; - STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; - STDMETHOD(GetVolume) (THIS_ LPLONG) PURE; - STDMETHOD(GetPan) (THIS_ LPLONG) PURE; - STDMETHOD(GetFrequency) (THIS_ LPDWORD) PURE; - STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE; - STDMETHOD(Initialize) (THIS_ IDirectSound*, DSBUFFERDESC*) PURE; - STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE; - STDMETHOD(Play) (THIS_ DWORD, DWORD, DWORD) PURE; - STDMETHOD(SetCurrentPosition) (THIS_ DWORD) PURE; - STDMETHOD(SetFormat) (THIS_ const WAVEFORMATEX*) PURE; - STDMETHOD(SetVolume) (THIS_ LONG) PURE; - STDMETHOD(SetPan) (THIS_ LONG) PURE; - STDMETHOD(SetFrequency) (THIS_ DWORD) PURE; - STDMETHOD(Stop) (THIS) PURE; - STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; - STDMETHOD(Restore) (THIS) PURE; + STDMETHOD (QueryInterface) (THIS_ REFIID, LPVOID*) PURE; + STDMETHOD_ (ULONG,AddRef) (THIS) PURE; + STDMETHOD_ (ULONG,Release) (THIS) PURE; + STDMETHOD (GetCaps) (THIS_ void*) PURE; + STDMETHOD (GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; + STDMETHOD (GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; + STDMETHOD (GetVolume) (THIS_ LPLONG) PURE; + STDMETHOD (GetPan) (THIS_ LPLONG) PURE; + STDMETHOD (GetFrequency) (THIS_ LPDWORD) PURE; + STDMETHOD (GetStatus) (THIS_ LPDWORD) PURE; + STDMETHOD (Initialize) (THIS_ IDirectSound*, DSBUFFERDESC*) PURE; + STDMETHOD (Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE; + STDMETHOD (Play) (THIS_ DWORD, DWORD, DWORD) PURE; + STDMETHOD (SetCurrentPosition) (THIS_ DWORD) PURE; + STDMETHOD (SetFormat) (THIS_ const WAVEFORMATEX*) PURE; + STDMETHOD (SetVolume) (THIS_ LONG) PURE; + STDMETHOD (SetPan) (THIS_ LONG) PURE; + STDMETHOD (SetFrequency) (THIS_ DWORD) PURE; + STDMETHOD (Stop) (THIS) PURE; + STDMETHOD (Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD (Restore) (THIS) PURE; }; //============================================================================== @@ -93,32 +93,32 @@ extern "C" #undef INTERFACE #define INTERFACE IDirectSoundCapture - DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown) + DECLARE_INTERFACE_ (IDirectSoundCapture, IUnknown) { - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - STDMETHOD(CreateCaptureBuffer) (THIS_ DSCBUFFERDESC*, IDirectSoundCaptureBuffer**, LPUNKNOWN) PURE; - STDMETHOD(GetCaps) (THIS_ void*) PURE; - STDMETHOD(Initialize) (THIS_ const GUID*) PURE; + STDMETHOD (QueryInterface) (THIS_ REFIID, LPVOID*) PURE; + STDMETHOD_ (ULONG,AddRef) (THIS) PURE; + STDMETHOD_ (ULONG,Release) (THIS) PURE; + STDMETHOD (CreateCaptureBuffer) (THIS_ DSCBUFFERDESC*, IDirectSoundCaptureBuffer**, LPUNKNOWN) PURE; + STDMETHOD (GetCaps) (THIS_ void*) PURE; + STDMETHOD (Initialize) (THIS_ const GUID*) PURE; }; #undef INTERFACE #define INTERFACE IDirectSoundCaptureBuffer - DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown) + DECLARE_INTERFACE_ (IDirectSoundCaptureBuffer, IUnknown) { - STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE; - STDMETHOD_(ULONG,AddRef) (THIS) PURE; - STDMETHOD_(ULONG,Release) (THIS) PURE; - STDMETHOD(GetCaps) (THIS_ void*) PURE; - STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; - STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; - STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE; - STDMETHOD(Initialize) (THIS_ IDirectSoundCapture*, DSCBUFFERDESC*) PURE; - STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE; - STDMETHOD(Start) (THIS_ DWORD) PURE; - STDMETHOD(Stop) (THIS) PURE; - STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD (QueryInterface) (THIS_ REFIID, LPVOID*) PURE; + STDMETHOD_ (ULONG,AddRef) (THIS) PURE; + STDMETHOD_ (ULONG,Release) (THIS) PURE; + STDMETHOD (GetCaps) (THIS_ void*) PURE; + STDMETHOD (GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; + STDMETHOD (GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; + STDMETHOD (GetStatus) (THIS_ LPDWORD) PURE; + STDMETHOD (Initialize) (THIS_ IDirectSoundCapture*, DSCBUFFERDESC*) PURE; + STDMETHOD (Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE; + STDMETHOD (Start) (THIS_ DWORD) PURE; + STDMETHOD (Stop) (THIS) PURE; + STDMETHOD (Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; }; #undef INTERFACE @@ -130,30 +130,30 @@ namespace juce //============================================================================== namespace DSoundLogging { - String getErrorMessage (HRESULT hr) + static String getErrorMessage (HRESULT hr) { const char* result = nullptr; switch (hr) { - case MAKE_HRESULT(1, 0x878, 10): result = "Device already allocated"; break; - case MAKE_HRESULT(1, 0x878, 30): result = "Control unavailable"; break; - case E_INVALIDARG: result = "Invalid parameter"; break; - case MAKE_HRESULT(1, 0x878, 50): result = "Invalid call"; break; - case E_FAIL: result = "Generic error"; break; - case MAKE_HRESULT(1, 0x878, 70): result = "Priority level error"; break; - case E_OUTOFMEMORY: result = "Out of memory"; break; - case MAKE_HRESULT(1, 0x878, 100): result = "Bad format"; break; - case E_NOTIMPL: result = "Unsupported function"; break; - case MAKE_HRESULT(1, 0x878, 120): result = "No driver"; break; - case MAKE_HRESULT(1, 0x878, 130): result = "Already initialised"; break; - case CLASS_E_NOAGGREGATION: result = "No aggregation"; break; - case MAKE_HRESULT(1, 0x878, 150): result = "Buffer lost"; break; - case MAKE_HRESULT(1, 0x878, 160): result = "Another app has priority"; break; - case MAKE_HRESULT(1, 0x878, 170): result = "Uninitialised"; break; - case E_NOINTERFACE: result = "No interface"; break; - case S_OK: result = "No error"; break; - default: return "Unknown error: " + String ((int) hr); + case MAKE_HRESULT (1, 0x878, 10): result = "Device already allocated"; break; + case MAKE_HRESULT (1, 0x878, 30): result = "Control unavailable"; break; + case E_INVALIDARG: result = "Invalid parameter"; break; + case MAKE_HRESULT (1, 0x878, 50): result = "Invalid call"; break; + case E_FAIL: result = "Generic error"; break; + case MAKE_HRESULT (1, 0x878, 70): result = "Priority level error"; break; + case E_OUTOFMEMORY: result = "Out of memory"; break; + case MAKE_HRESULT (1, 0x878, 100): result = "Bad format"; break; + case E_NOTIMPL: result = "Unsupported function"; break; + case MAKE_HRESULT (1, 0x878, 120): result = "No driver"; break; + case MAKE_HRESULT (1, 0x878, 130): result = "Already initialised"; break; + case CLASS_E_NOAGGREGATION: result = "No aggregation"; break; + case MAKE_HRESULT (1, 0x878, 150): result = "Buffer lost"; break; + case MAKE_HRESULT (1, 0x878, 160): result = "Another app has priority"; break; + case MAKE_HRESULT (1, 0x878, 170): result = "Uninitialised"; break; + case E_NOINTERFACE: result = "No interface"; break; + case S_OK: result = "No error"; break; + default: return "Unknown error: " + String ((int) hr); } return result; @@ -194,7 +194,9 @@ namespace static type##functionName ds##functionName = nullptr; #define DSOUND_FUNCTION_LOAD(functionName) \ - ds##functionName = (type##functionName) GetProcAddress (h, #functionName); \ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-function-type") \ + ds##functionName = (type##functionName) GetProcAddress (h, #functionName); \ + JUCE_END_IGNORE_WARNINGS_GCC_LIKE \ jassert (ds##functionName != nullptr); typedef BOOL (CALLBACK *LPDSENUMCALLBACKW) (LPGUID, LPCWSTR, LPCWSTR, LPVOID); @@ -209,12 +211,17 @@ namespace { if (dsDirectSoundCreate == nullptr) { - HMODULE h = LoadLibraryA ("dsound.dll"); + if (auto* h = LoadLibraryA ("dsound.dll")) + { + DSOUND_FUNCTION_LOAD (DirectSoundCreate) + DSOUND_FUNCTION_LOAD (DirectSoundCaptureCreate) + DSOUND_FUNCTION_LOAD (DirectSoundEnumerateW) + DSOUND_FUNCTION_LOAD (DirectSoundCaptureEnumerateW) - DSOUND_FUNCTION_LOAD (DirectSoundCreate) - DSOUND_FUNCTION_LOAD (DirectSoundCaptureCreate) - DSOUND_FUNCTION_LOAD (DirectSoundEnumerateW) - DSOUND_FUNCTION_LOAD (DirectSoundCaptureEnumerateW) + return; + } + + jassertfalse; } } @@ -244,8 +251,8 @@ class DSoundInternalOutChannel if (pOutputBuffer != nullptr) { JUCE_DS_LOG ("closing output: " + name); - HRESULT hr = pOutputBuffer->Stop(); - JUCE_DS_LOG_ERROR (hr); ignoreUnused (hr); + [[maybe_unused]] HRESULT hr = pOutputBuffer->Stop(); + JUCE_DS_LOG_ERROR (hr); pOutputBuffer->Release(); pOutputBuffer = nullptr; @@ -291,14 +298,14 @@ class DSoundInternalOutChannel { IDirectSoundBuffer* pPrimaryBuffer; - DSBUFFERDESC primaryDesc = { 0 }; + DSBUFFERDESC primaryDesc = {}; primaryDesc.dwSize = sizeof (DSBUFFERDESC); primaryDesc.dwFlags = 1 /* DSBCAPS_PRIMARYBUFFER */; primaryDesc.dwBufferBytes = 0; - primaryDesc.lpwfxFormat = 0; + primaryDesc.lpwfxFormat = nullptr; JUCE_DS_LOG ("co-op level set"); - hr = pDirectSound->CreateSoundBuffer (&primaryDesc, &pPrimaryBuffer, 0); + hr = pDirectSound->CreateSoundBuffer (&primaryDesc, &pPrimaryBuffer, nullptr); JUCE_DS_LOG_ERROR (hr); if (SUCCEEDED (hr)) @@ -317,14 +324,14 @@ class DSoundInternalOutChannel if (SUCCEEDED (hr)) { - DSBUFFERDESC secondaryDesc = { 0 }; + DSBUFFERDESC secondaryDesc = {}; secondaryDesc.dwSize = sizeof (DSBUFFERDESC); secondaryDesc.dwFlags = 0x8000 /* DSBCAPS_GLOBALFOCUS */ | 0x10000 /* DSBCAPS_GETCURRENTPOSITION2 */; secondaryDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer; secondaryDesc.lpwfxFormat = &wfFormat; - hr = pDirectSound->CreateSoundBuffer (&secondaryDesc, &pOutputBuffer, 0); + hr = pDirectSound->CreateSoundBuffer (&secondaryDesc, &pOutputBuffer, nullptr); JUCE_DS_LOG_ERROR (hr); if (SUCCEEDED (hr)) @@ -335,14 +342,14 @@ class DSoundInternalOutChannel unsigned char* pDSBuffData; hr = pOutputBuffer->Lock (0, (DWORD) totalBytesPerBuffer, - (LPVOID*) &pDSBuffData, &dwDataLen, 0, 0, 0); + (LPVOID*) &pDSBuffData, &dwDataLen, nullptr, nullptr, 0); JUCE_DS_LOG_ERROR (hr); if (SUCCEEDED (hr)) { zeromem (pDSBuffData, dwDataLen); - hr = pOutputBuffer->Unlock (pDSBuffData, dwDataLen, 0, 0); + hr = pOutputBuffer->Unlock (pDSBuffData, dwDataLen, nullptr, 0); if (SUCCEEDED (hr)) { @@ -379,7 +386,7 @@ class DSoundInternalOutChannel bool service() { - if (pOutputBuffer == 0) + if (pOutputBuffer == nullptr) return true; DWORD playCursor, writeCursor; @@ -481,7 +488,7 @@ class DSoundInternalOutChannel jassertfalse; } - writeOffset = (writeOffset + dwSize1 + dwSize2) % totalBytesPerBuffer; + writeOffset = (writeOffset + dwSize1 + dwSize2) % (DWORD) totalBytesPerBuffer; pOutputBuffer->Unlock (buf1, dwSize1, buf2, dwSize2); } @@ -518,7 +525,7 @@ class DSoundInternalOutChannel bool firstPlayTime; int64 lastPlayTime, ticksPerBuffer; - static inline int convertInputValues (const float l, const float r) noexcept + static int convertInputValues (const float l, const float r) noexcept { return jlimit (-32768, 32767, roundToInt (32767.0f * r)) << 16 | (0xffff & jlimit (-32768, 32767, roundToInt (32767.0f * l))); @@ -548,8 +555,8 @@ struct DSoundInternalInChannel if (pInputBuffer != nullptr) { JUCE_DS_LOG ("closing input: " + name); - HRESULT hr = pInputBuffer->Stop(); - JUCE_DS_LOG_ERROR (hr); ignoreUnused (hr); + [[maybe_unused]] HRESULT hr = pInputBuffer->Stop(); + JUCE_DS_LOG_ERROR (hr); pInputBuffer->Release(); pInputBuffer = nullptr; @@ -598,14 +605,14 @@ struct DSoundInternalInChannel wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; wfFormat.cbSize = 0; - DSCBUFFERDESC captureDesc = { 0 }; + DSCBUFFERDESC captureDesc = {}; captureDesc.dwSize = sizeof (DSCBUFFERDESC); captureDesc.dwFlags = 0; captureDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer; captureDesc.lpwfxFormat = &wfFormat; JUCE_DS_LOG ("object created"); - hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, 0); + hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, nullptr); if (SUCCEEDED (hr)) { @@ -634,7 +641,7 @@ struct DSoundInternalInChannel bool service() { - if (pInputBuffer == 0) + if (pInputBuffer == nullptr) return true; DWORD capturePos, readPos; @@ -692,7 +699,7 @@ struct DSoundInternalInChannel jassertfalse; } - readOffset = (readOffset + dwsize1 + dwsize2) % totalBytesPerBuffer; + readOffset = (readOffset + dwsize1 + dwsize2) % (DWORD) totalBytesPerBuffer; pInputBuffer->Unlock (buf1, dwsize1, buf2, dwsize2); } @@ -732,8 +739,8 @@ struct DSoundInternalInChannel }; //============================================================================== -class DSoundAudioIODevice : public AudioIODevice, - public Thread +class DSoundAudioIODevice final : public AudioIODevice, + public Thread { public: DSoundAudioIODevice (const String& deviceName, @@ -746,18 +753,18 @@ class DSoundAudioIODevice : public AudioIODevice, { if (outputDeviceIndex_ >= 0) { - outChannels.add (TRANS("Left")); - outChannels.add (TRANS("Right")); + outChannels.add (TRANS ("Left")); + outChannels.add (TRANS ("Right")); } if (inputDeviceIndex_ >= 0) { - inChannels.add (TRANS("Left")); - inChannels.add (TRANS("Right")); + inChannels.add (TRANS ("Left")); + inChannels.add (TRANS ("Right")); } } - ~DSoundAudioIODevice() + ~DSoundAudioIODevice() override { close(); } @@ -916,10 +923,10 @@ class DSoundAudioIODevice : public AudioIODevice, sleep (5); for (int i = 0; i < outChans.size(); ++i) - outChans.getUnchecked(i)->synchronisePosition(); + outChans.getUnchecked (i)->synchronisePosition(); for (int i = 0; i < inChans.size(); ++i) - inChans.getUnchecked(i)->synchronisePosition(); + inChans.getUnchecked (i)->synchronisePosition(); } } @@ -932,8 +939,8 @@ class DSoundAudioIODevice : public AudioIODevice, break; } - const int latencyMs = (int) (bufferSizeSamples * 1000.0 / sampleRate); - const int maxTimeMS = jmax (5, 3 * latencyMs); + const auto latencyMs = (uint32) (bufferSizeSamples * 1000.0 / sampleRate); + const auto maxTimeMS = jmax ((uint32) 5, 3 * latencyMs); while (! threadShouldExit()) { @@ -942,13 +949,13 @@ class DSoundAudioIODevice : public AudioIODevice, for (int i = inChans.size(); --i >= 0;) { - inChans.getUnchecked(i)->doneFlag = false; + inChans.getUnchecked (i)->doneFlag = false; ++numToDo; } for (int i = outChans.size(); --i >= 0;) { - outChans.getUnchecked(i)->doneFlag = false; + outChans.getUnchecked (i)->doneFlag = false; ++numToDo; } @@ -961,7 +968,7 @@ class DSoundAudioIODevice : public AudioIODevice, { for (int i = inChans.size(); --i >= 0;) { - DSoundInternalInChannel* const in = inChans.getUnchecked(i); + DSoundInternalInChannel* const in = inChans.getUnchecked (i); if ((! in->doneFlag) && in->service()) { @@ -972,7 +979,7 @@ class DSoundAudioIODevice : public AudioIODevice, for (int i = outChans.size(); --i >= 0;) { - DSoundInternalOutChannel* const out = outChans.getUnchecked(i); + DSoundInternalOutChannel* const out = outChans.getUnchecked (i); if ((! out->doneFlag) && out->service()) { @@ -1009,9 +1016,12 @@ class DSoundAudioIODevice : public AudioIODevice, if (isStarted) { - callback->audioDeviceIOCallback (inputBuffers.getArrayOfReadPointers(), inputBuffers.getNumChannels(), - outputBuffers.getArrayOfWritePointers(), outputBuffers.getNumChannels(), - bufferSizeSamples); + callback->audioDeviceIOCallbackWithContext (inputBuffers.getArrayOfReadPointers(), + inputBuffers.getNumChannels(), + outputBuffers.getArrayOfWritePointers(), + outputBuffers.getNumChannels(), + bufferSizeSamples, + {}); } else { @@ -1025,8 +1035,14 @@ class DSoundAudioIODevice : public AudioIODevice, }; //============================================================================== -struct DSoundDeviceList +class DSoundDeviceList { + auto tie() const + { + return std::tie (outputDeviceNames, inputDeviceNames, outputGuids, inputGuids); + } + +public: StringArray outputDeviceNames, inputDeviceNames; Array outputGuids, inputGuids; @@ -1037,20 +1053,15 @@ struct DSoundDeviceList outputGuids.clear(); inputGuids.clear(); - if (dsDirectSoundEnumerateW != 0) + if (dsDirectSoundEnumerateW != nullptr) { dsDirectSoundEnumerateW (outputEnumProcW, this); dsDirectSoundCaptureEnumerateW (inputEnumProcW, this); } } - bool operator!= (const DSoundDeviceList& other) const noexcept - { - return outputDeviceNames != other.outputDeviceNames - || inputDeviceNames != other.inputDeviceNames - || outputGuids != other.outputGuids - || inputGuids != other.inputGuids; - } + bool operator== (const DSoundDeviceList& other) const noexcept { return tie() == other.tie(); } + bool operator!= (const DSoundDeviceList& other) const noexcept { return tie() != other.tie(); } private: static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array& guids) @@ -1181,12 +1192,12 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, if (error.isEmpty()) { for (int i = 0; i < outChans.size(); ++i) - outChans.getUnchecked(i)->synchronisePosition(); + outChans.getUnchecked (i)->synchronisePosition(); for (int i = 0; i < inChans.size(); ++i) - inChans.getUnchecked(i)->synchronisePosition(); + inChans.getUnchecked (i)->synchronisePosition(); - startThread (9); + startThread (Priority::highest); sleep (10); notify(); @@ -1203,13 +1214,11 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, } //============================================================================== -class DSoundAudioIODeviceType : public AudioIODeviceType, - private DeviceChangeDetector +class DSoundAudioIODeviceType final : public AudioIODeviceType { public: DSoundAudioIODeviceType() - : AudioIODeviceType ("DirectSound"), - DeviceChangeDetector (L"DirectSound") + : AudioIODeviceType ("DirectSound") { initialiseDSoundFunctions(); } @@ -1264,28 +1273,20 @@ class DSoundAudioIODeviceType : public AudioIODeviceType, } private: + DeviceChangeDetector detector { L"DirectSound", [this] { systemDeviceChanged(); } }; DSoundDeviceList deviceList; bool hasScanned = false; - void systemDeviceChanged() override + void systemDeviceChanged() { DSoundDeviceList newList; newList.scan(); - if (newList != deviceList) - { - deviceList = newList; + if (std::exchange (deviceList, newList) != newList) callDeviceChangeListeners(); - } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType) }; -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() -{ - return new DSoundAudioIODeviceType(); -} - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_HighPerformanceAudioHelpers_android.h b/JuceLibraryCode/modules/juce_audio_devices/native/juce_HighPerformanceAudioHelpers_android.h new file mode 100644 index 00000000..39a26c8b --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_HighPerformanceAudioHelpers_android.h @@ -0,0 +1,127 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +//============================================================================== +/** + Some shared helpers methods for using the high-performance audio paths on + Android devices (OpenSL and Oboe). + + @tags{Audio} +*/ +namespace juce::AndroidHighPerformanceAudioHelpers +{ + //============================================================================== + static double getNativeSampleRate() + { + return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue(); + } + + static int getNativeBufferSizeHint() + { + // This property is a hint of a native buffer size but it does not guarantee the size used. + auto deviceBufferSize = audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER").getIntValue(); + + if (deviceBufferSize == 0) + return 192; + + return deviceBufferSize; + } + + static bool isProAudioDevice() + { + static bool isSapaSupported = SystemStats::getDeviceManufacturer().containsIgnoreCase ("SAMSUNG") + && DynamicLibrary().open ("libapa_jni.so"); + + return androidHasSystemFeature ("android.hardware.audio.pro") || isSapaSupported; + } + + static bool hasLowLatencyAudioPath() + { + return androidHasSystemFeature ("android.hardware.audio.low_latency"); + } + + static bool canUseHighPerformanceAudioPath (int nativeBufferSize, int requestedBufferSize, int requestedSampleRate) + { + return ((requestedBufferSize % nativeBufferSize) == 0) + && approximatelyEqual ((double) requestedSampleRate, getNativeSampleRate()) + && isProAudioDevice(); + } + + //============================================================================== + static int getMinimumBuffersToEnqueue (int nativeBufferSize, double requestedSampleRate) + { + if (canUseHighPerformanceAudioPath (nativeBufferSize, nativeBufferSize, (int) requestedSampleRate)) + { + // see https://developer.android.com/ndk/guides/audio/opensl/opensl-prog-notes.html#sandp + // "For Android 4.2 (API level 17) and earlier, a buffer count of two or more is required + // for lower latency. Beginning with Android 4.3 (API level 18), a buffer count of one + // is sufficient for lower latency." + return (getAndroidSDKVersion() >= 18 ? 1 : 2); + } + + // not using low-latency path so we can use the absolute minimum number of buffers to queue + return 1; + } + + static int buffersToQueueForBufferDuration (int nativeBufferSize, int bufferDurationInMs, double sampleRate) noexcept + { + auto maxBufferFrames = static_cast (std::ceil (bufferDurationInMs * sampleRate / 1000.0)); + auto maxNumBuffers = static_cast (std::ceil (static_cast (maxBufferFrames) + / static_cast (nativeBufferSize))); + + return jmax (getMinimumBuffersToEnqueue (nativeBufferSize, sampleRate), maxNumBuffers); + } + + static int getMaximumBuffersToEnqueue (int nativeBufferSize, double maximumSampleRate) noexcept + { + static constexpr int maxBufferSizeMs = 200; + + return jmax (8, buffersToQueueForBufferDuration (nativeBufferSize, maxBufferSizeMs, maximumSampleRate)); + } + + static Array getAvailableBufferSizes (int nativeBufferSize, Array availableSampleRates) + { + auto minBuffersToQueue = getMinimumBuffersToEnqueue (nativeBufferSize, getNativeSampleRate()); + auto maxBuffersToQueue = getMaximumBuffersToEnqueue (nativeBufferSize, findMaximum (availableSampleRates.getRawDataPointer(), + availableSampleRates.size())); + + Array bufferSizes; + + for (int i = minBuffersToQueue; i <= maxBuffersToQueue; ++i) + bufferSizes.add (i * nativeBufferSize); + + return bufferSizes; + } + + static int getDefaultBufferSize (int nativeBufferSize, double currentSampleRate) + { + static constexpr int defaultBufferSizeForLowLatencyDeviceMs = 40; + static constexpr int defaultBufferSizeForStandardLatencyDeviceMs = 100; + + auto defaultBufferLength = (hasLowLatencyAudioPath() ? defaultBufferSizeForLowLatencyDeviceMs + : defaultBufferSizeForStandardLatencyDeviceMs); + + auto defaultBuffersToEnqueue = buffersToQueueForBufferDuration (nativeBufferSize, defaultBufferLength, currentSampleRate); + return defaultBuffersToEnqueue * nativeBufferSize; + } + +} // namespace juce::AndroidHighPerformanceAudioHelpers diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_JackAudio_linux.cpp similarity index 61% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_JackAudio_linux.cpp index 3f44f332..7b49ddf1 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_JackAudio_linux.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -36,9 +36,11 @@ static void* juce_loadJackFunction (const char* const name) #define JUCE_DECL_JACK_FUNCTION(return_type, fn_name, argument_types, arguments) \ return_type fn_name argument_types \ { \ + using ReturnType = return_type; \ typedef return_type (*fn_type) argument_types; \ static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \ - return (fn != nullptr) ? ((*fn) arguments) : (return_type) 0; \ + jassert (fn != nullptr); \ + return (fn != nullptr) ? ((*fn) arguments) : ReturnType(); \ } #define JUCE_DECL_VOID_JACK_FUNCTION(fn_name, argument_types, arguments) \ @@ -46,30 +48,35 @@ static void* juce_loadJackFunction (const char* const name) { \ typedef void (*fn_type) argument_types; \ static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \ + jassert (fn != nullptr); \ if (fn != nullptr) (*fn) arguments; \ } //============================================================================== -JUCE_DECL_JACK_FUNCTION (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status, ...), (client_name, options, status)); -JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client)); -JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client)); -JUCE_DECL_JACK_FUNCTION (int, jack_deactivate, (jack_client_t* client), (client)); -JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_buffer_size, (jack_client_t* client), (client)); -JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_sample_rate, (jack_client_t* client), (client)); -JUCE_DECL_VOID_JACK_FUNCTION (jack_on_shutdown, (jack_client_t* client, void (*function)(void* arg), void* arg), (client, function, arg)); -JUCE_DECL_JACK_FUNCTION (void* , jack_port_get_buffer, (jack_port_t* port, jack_nframes_t nframes), (port, nframes)); -JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_port_get_total_latency, (jack_client_t* client, jack_port_t* port), (client, port)); -JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_register, (jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size)); -JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func)(const char*)), (func)); -JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg)); -JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags)); -JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port)); -JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port)); -JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg)); -JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id)); -JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port)); -JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name)); -JUCE_DECL_JACK_FUNCTION (int, jack_set_xrun_callback, (jack_client_t* client, JackXRunCallback xrun_callback, void* arg), (client, xrun_callback, arg)); +JUCE_DECL_JACK_FUNCTION (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status, ...), (client_name, options, status)) +JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client)) +JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client)) +JUCE_DECL_JACK_FUNCTION (int, jack_deactivate, (jack_client_t* client), (client)) +JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_buffer_size, (jack_client_t* client), (client)) +JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_sample_rate, (jack_client_t* client), (client)) +JUCE_DECL_VOID_JACK_FUNCTION (jack_on_shutdown, (jack_client_t* client, void (*function) (void* arg), void* arg), (client, function, arg)) +JUCE_DECL_VOID_JACK_FUNCTION (jack_on_info_shutdown, (jack_client_t* client, JackInfoShutdownCallback function, void* arg), (client, function, arg)) +JUCE_DECL_JACK_FUNCTION (void* , jack_port_get_buffer, (jack_port_t* port, jack_nframes_t nframes), (port, nframes)) +JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_port_get_total_latency, (jack_client_t* client, jack_port_t* port), (client, port)) +JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_register, (jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size)) +JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func) (const char*)), (func)) +JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg)) +JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags)) +JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port)) +JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port)) +JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg)) +JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id)) +JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port)) +JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name)) +JUCE_DECL_JACK_FUNCTION (int, jack_set_xrun_callback, (jack_client_t* client, JackXRunCallback xrun_callback, void* arg), (client, xrun_callback, arg)) +JUCE_DECL_JACK_FUNCTION (int, jack_port_flags, (const jack_port_t* port), (port)) +JUCE_DECL_JACK_FUNCTION (jack_port_t*, jack_port_by_name, (jack_client_t* client, const char* name), (client, name)) +JUCE_DECL_VOID_JACK_FUNCTION (jack_free, (void* ptr), (ptr)) #if JUCE_DEBUG #define JACK_LOGGING_ENABLED 1 @@ -115,56 +122,56 @@ namespace struct JackPortIterator { JackPortIterator (jack_client_t* const client, const bool forInput) - : ports (nullptr), index (-1) { if (client != nullptr) - ports = juce::jack_get_ports (client, nullptr, nullptr, - forInput ? JackPortIsOutput : JackPortIsInput); - // (NB: This looks like it's the wrong way round, but it is correct!) - } - - ~JackPortIterator() - { - ::free (ports); + ports.reset (juce::jack_get_ports (client, nullptr, nullptr, + forInput ? JackPortIsInput : JackPortIsOutput)); } bool next() { - if (ports == nullptr || ports [index + 1] == nullptr) + if (ports == nullptr || ports.get()[index + 1] == nullptr) return false; - name = CharPointer_UTF8 (ports[++index]); - clientName = name.upToFirstOccurrenceOf (":", false, false); + name = CharPointer_UTF8 (ports.get()[++index]); return true; } - const char** ports; - int index; + String getClientName() const + { + return name.upToFirstOccurrenceOf (":", false, false); + } + + String getChannelName() const + { + return name.fromFirstOccurrenceOf (":", false, false); + } + + struct Free + { + void operator() (const char** ptr) const noexcept { juce::jack_free (ptr); } + }; + + std::unique_ptr ports; + int index = -1; String name; - String clientName; }; -class JackAudioIODeviceType; -static Array activeDeviceTypes; - //============================================================================== -class JackAudioIODevice : public AudioIODevice +class JackAudioIODevice final : public AudioIODevice { public: - JackAudioIODevice (const String& deviceName, - const String& inId, - const String& outId) - : AudioIODevice (deviceName, "JACK"), - inputId (inId), - outputId (outId), - deviceIsOpen (false), - callback (nullptr), - totalNumberOfInputChannels (0), - totalNumberOfOutputChannels (0) - { - jassert (deviceName.isNotEmpty()); - - jack_status_t status; + JackAudioIODevice (const String& inName, + const String& outName, + std::function notifyIn) + : AudioIODevice (outName.isEmpty() ? inName : outName, "JACK"), + inputName (inName), + outputName (outName), + notifyChannelsChanged (std::move (notifyIn)) + { + jassert (outName.isNotEmpty() || inName.isNotEmpty()); + + jack_status_t status = {}; client = juce::jack_client_open (JUCE_JACK_CLIENT_NAME, JackNoStartServer, &status); if (client == nullptr) @@ -179,10 +186,10 @@ class JackAudioIODevice : public AudioIODevice const StringArray inputChannels (getInputChannelNames()); for (int i = 0; i < inputChannels.size(); ++i) { - String inputName; - inputName << "in_" << ++totalNumberOfInputChannels; + String inputChannelName; + inputChannelName << "in_" << ++totalNumberOfInputChannels; - inputPorts.add (juce::jack_port_register (client, inputName.toUTF8(), + inputPorts.add (juce::jack_port_register (client, inputChannelName.toUTF8(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)); } @@ -190,10 +197,10 @@ class JackAudioIODevice : public AudioIODevice const StringArray outputChannels (getOutputChannelNames()); for (int i = 0; i < outputChannels.size(); ++i) { - String outputName; - outputName << "out_" << ++totalNumberOfOutputChannels; + String outputChannelName; + outputChannelName << "out_" << ++totalNumberOfOutputChannels; - outputPorts.add (juce::jack_port_register (client, outputName.toUTF8(), + outputPorts.add (juce::jack_port_register (client, outputChannelName.toUTF8(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)); } @@ -202,7 +209,7 @@ class JackAudioIODevice : public AudioIODevice } } - ~JackAudioIODevice() + ~JackAudioIODevice() override { close(); if (client != nullptr) @@ -212,19 +219,19 @@ class JackAudioIODevice : public AudioIODevice } } - StringArray getChannelNames (bool forInput) const + StringArray getChannelNames (const String& clientName, bool forInput) const { StringArray names; for (JackPortIterator i (client, forInput); i.next();) - if (i.clientName == getName()) - names.add (i.name.fromFirstOccurrenceOf (":", false, false)); + if (i.getClientName() == clientName) + names.add (i.getChannelName()); return names; } - StringArray getOutputChannelNames() override { return getChannelNames (false); } - StringArray getInputChannelNames() override { return getChannelNames (true); } + StringArray getOutputChannelNames() override { return getChannelNames (outputName, true); } + StringArray getInputChannelNames() override { return getChannelNames (inputName, false); } Array getAvailableSampleRates() override { @@ -241,15 +248,29 @@ class JackAudioIODevice : public AudioIODevice Array sizes; if (client != nullptr) - sizes.add (juce::jack_get_buffer_size (client)); + sizes.add (static_cast (juce::jack_get_buffer_size (client))); return sizes; } int getDefaultBufferSize() override { return getCurrentBufferSizeSamples(); } - int getCurrentBufferSizeSamples() override { return client != nullptr ? juce::jack_get_buffer_size (client) : 0; } - double getCurrentSampleRate() override { return client != nullptr ? juce::jack_get_sample_rate (client) : 0; } + int getCurrentBufferSizeSamples() override { return client != nullptr ? static_cast (juce::jack_get_buffer_size (client)) : 0; } + double getCurrentSampleRate() override { return client != nullptr ? static_cast (juce::jack_get_sample_rate (client)) : 0; } + + template + void forEachClientChannel (const String& clientName, bool isInput, Fn&& fn) + { + auto index = 0; + for (JackPortIterator i (client, isInput); i.next();) + { + if (i.getClientName() != clientName) + continue; + + fn (i.ports.get()[i.index], index); + index += 1; + } + } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double /* sampleRate */, int /* bufferSizeSamples */) override @@ -263,38 +284,55 @@ class JackAudioIODevice : public AudioIODevice lastError.clear(); close(); - xruns = 0; + xruns.store (0, std::memory_order_relaxed); juce::jack_set_process_callback (client, processCallback, this); juce::jack_set_port_connect_callback (client, portConnectCallback, this); juce::jack_on_shutdown (client, shutdownCallback, this); + juce::jack_on_info_shutdown (client, infoShutdownCallback, this); juce::jack_set_xrun_callback (client, xrunCallback, this); juce::jack_activate (client); deviceIsOpen = true; if (! inputChannels.isZero()) { - for (JackPortIterator i (client, true); i.next();) + forEachClientChannel (inputName, false, [&] (const char* portName, int index) { - if (inputChannels [i.index] && i.clientName == getName()) - { - int error = juce::jack_connect (client, i.ports[i.index], juce::jack_port_name ((jack_port_t*) inputPorts[i.index])); - if (error != 0) - JUCE_JACK_LOG ("Cannot connect input port " + String (i.index) + " (" + i.name + "), error " + String (error)); - } - } + if (! inputChannels[index]) + return; + + jassert (index < inputPorts.size()); + + const auto* source = portName; + const auto* inputPort = inputPorts[index]; + + jassert (juce::jack_port_flags (juce::jack_port_by_name (client, source)) & JackPortIsOutput); + jassert (juce::jack_port_flags (inputPort) & JackPortIsInput); + + auto error = juce::jack_connect (client, source, juce::jack_port_name (inputPort)); + if (error != 0) + JUCE_JACK_LOG ("Cannot connect input port " + String (index) + " (" + portName + "), error " + String (error)); + }); } if (! outputChannels.isZero()) { - for (JackPortIterator i (client, false); i.next();) + forEachClientChannel (outputName, true, [&] (const char* portName, int index) { - if (outputChannels [i.index] && i.clientName == getName()) - { - int error = juce::jack_connect (client, juce::jack_port_name ((jack_port_t*) outputPorts[i.index]), i.ports[i.index]); - if (error != 0) - JUCE_JACK_LOG ("Cannot connect output port " + String (i.index) + " (" + i.name + "), error " + String (error)); - } - } + if (! outputChannels[index]) + return; + + jassert (index < outputPorts.size()); + + const auto* outputPort = outputPorts[index]; + const auto* destination = portName; + + jassert (juce::jack_port_flags (outputPort) & JackPortIsOutput); + jassert (juce::jack_port_flags (juce::jack_port_by_name (client, destination)) & JackPortIsInput); + + auto error = juce::jack_connect (client, juce::jack_port_name (outputPort), destination); + if (error != 0) + JUCE_JACK_LOG ("Cannot connect output port " + String (index) + " (" + portName + "), error " + String (error)); + }); } updateActivePorts(); @@ -308,12 +346,14 @@ class JackAudioIODevice : public AudioIODevice if (client != nullptr) { - juce::jack_deactivate (client); + [[maybe_unused]] const auto result = juce::jack_deactivate (client); + jassert (result == 0); juce::jack_set_xrun_callback (client, xrunCallback, nullptr); juce::jack_set_process_callback (client, processCallback, nullptr); juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr); juce::jack_on_shutdown (client, shutdownCallback, nullptr); + juce::jack_on_info_shutdown (client, infoShutdownCallback, nullptr); } deviceIsOpen = false; @@ -347,7 +387,7 @@ class JackAudioIODevice : public AudioIODevice bool isPlaying() override { return callback != nullptr; } int getCurrentBitDepth() override { return 32; } String getLastError() override { return lastError; } - int getXRunCount() const noexcept override { return xruns; } + int getXRunCount() const noexcept override { return xruns.load (std::memory_order_relaxed); } BigInteger getActiveOutputChannels() const override { return activeOutputChannels; } BigInteger getActiveInputChannels() const override { return activeInputChannels; } @@ -357,7 +397,7 @@ class JackAudioIODevice : public AudioIODevice int latency = 0; for (int i = 0; i < outputPorts.size(); i++) - latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) outputPorts [i])); + latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, outputPorts[i])); return latency; } @@ -367,14 +407,36 @@ class JackAudioIODevice : public AudioIODevice int latency = 0; for (int i = 0; i < inputPorts.size(); i++) - latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) inputPorts [i])); + latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, inputPorts[i])); return latency; } - String inputId, outputId; + String inputName, outputName; private: + //============================================================================== + class MainThreadDispatcher final : private AsyncUpdater + { + public: + explicit MainThreadDispatcher (JackAudioIODevice& device) : ref (device) {} + ~MainThreadDispatcher() override { cancelPendingUpdate(); } + + void updateActivePorts() + { + if (MessageManager::getInstance()->isThisTheMessageThread()) + handleAsyncUpdate(); + else + triggerAsyncUpdate(); + } + + private: + void handleAsyncUpdate() override { ref.updateActivePorts(); } + + JackAudioIODevice& ref; + }; + + //============================================================================== void process (const int numSamples) { int numActiveInChans = 0, numActiveOutChans = 0; @@ -382,17 +444,17 @@ class JackAudioIODevice : public AudioIODevice for (int i = 0; i < totalNumberOfInputChannels; ++i) { if (activeInputChannels[i]) - if (jack_default_audio_sample_t* in - = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) inputPorts.getUnchecked(i), numSamples)) - inChans [numActiveInChans++] = (float*) in; + if (auto* in = (jack_default_audio_sample_t*) juce::jack_port_get_buffer (inputPorts.getUnchecked (i), + static_cast (numSamples))) + inChans[numActiveInChans++] = (float*) in; } for (int i = 0; i < totalNumberOfOutputChannels; ++i) { if (activeOutputChannels[i]) - if (jack_default_audio_sample_t* out - = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) outputPorts.getUnchecked(i), numSamples)) - outChans [numActiveOutChans++] = (float*) out; + if (auto* out = (jack_default_audio_sample_t*) juce::jack_port_get_buffer (outputPorts.getUnchecked (i), + static_cast (numSamples))) + outChans[numActiveOutChans++] = (float*) out; } const ScopedLock sl (callbackLock); @@ -400,20 +462,24 @@ class JackAudioIODevice : public AudioIODevice if (callback != nullptr) { if ((numActiveInChans + numActiveOutChans) > 0) - callback->audioDeviceIOCallback (const_cast (inChans.getData()), numActiveInChans, - outChans, numActiveOutChans, numSamples); + callback->audioDeviceIOCallbackWithContext (inChans.getData(), + numActiveInChans, + outChans, + numActiveOutChans, + numSamples, + {}); } else { for (int i = 0; i < numActiveOutChans; ++i) - zeromem (outChans[i], sizeof (float) * numSamples); + zeromem (outChans[i], static_cast (numSamples) * sizeof (float)); } } static int processCallback (jack_nframes_t nframes, void* callbackArgument) { if (callbackArgument != nullptr) - ((JackAudioIODevice*) callbackArgument)->process (nframes); + ((JackAudioIODevice*) callbackArgument)->process (static_cast (nframes)); return 0; } @@ -431,11 +497,11 @@ class JackAudioIODevice : public AudioIODevice BigInteger newOutputChannels, newInputChannels; for (int i = 0; i < outputPorts.size(); ++i) - if (juce::jack_port_connected ((jack_port_t*) outputPorts.getUnchecked(i))) + if (juce::jack_port_connected (outputPorts.getUnchecked (i))) newOutputChannels.setBit (i); for (int i = 0; i < inputPorts.size(); ++i) - if (juce::jack_port_connected ((jack_port_t*) inputPorts.getUnchecked(i))) + if (juce::jack_port_connected (inputPorts.getUnchecked (i))) newInputChannels.setBit (i); if (newOutputChannels != activeOutputChannels @@ -451,14 +517,14 @@ class JackAudioIODevice : public AudioIODevice if (oldCallback != nullptr) start (oldCallback); - sendDeviceChangedCallback(); + NullCheckedInvocation::invoke (notifyChannelsChanged); } } static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg) { if (JackAudioIODevice* device = static_cast (arg)) - device->updateActivePorts(); + device->mainThreadDispatcher.updateActivePorts(); } static void threadInitCallback (void* /* callbackArgument */) @@ -477,81 +543,73 @@ class JackAudioIODevice : public AudioIODevice } } - static void errorCallback (const char* msg) + static void infoShutdownCallback ([[maybe_unused]] jack_status_t code, [[maybe_unused]] const char* reason, void* arg) { - JUCE_JACK_LOG ("JackAudioIODevice::errorCallback " + String (msg)); + jassert (code == 0); + + JUCE_JACK_LOG ("Shutting down with message:"); + JUCE_JACK_LOG (reason); + + shutdownCallback (arg); } - static void sendDeviceChangedCallback(); + static void errorCallback ([[maybe_unused]] const char* msg) + { + JUCE_JACK_LOG ("JackAudioIODevice::errorCallback " + String (msg)); + } - bool deviceIsOpen; - jack_client_t* client; + bool deviceIsOpen = false; + jack_client_t* client = nullptr; String lastError; - AudioIODeviceCallback* callback; + AudioIODeviceCallback* callback = nullptr; CriticalSection callbackLock; HeapBlock inChans, outChans; - int totalNumberOfInputChannels; - int totalNumberOfOutputChannels; - Array inputPorts, outputPorts; + int totalNumberOfInputChannels = 0; + int totalNumberOfOutputChannels = 0; + Array inputPorts, outputPorts; BigInteger activeInputChannels, activeOutputChannels; - int xruns; -}; + std::atomic xruns { 0 }; + std::function notifyChannelsChanged; + MainThreadDispatcher mainThreadDispatcher { *this }; +}; //============================================================================== -class JackAudioIODeviceType : public AudioIODeviceType +class JackAudioIODeviceType; + +class JackAudioIODeviceType final : public AudioIODeviceType { public: JackAudioIODeviceType() - : AudioIODeviceType ("JACK"), - hasScanned (false) - { - activeDeviceTypes.add (this); - } - - ~JackAudioIODeviceType() - { - activeDeviceTypes.removeFirstMatchingValue (this); - } + : AudioIODeviceType ("JACK") + {} void scanForDevices() { hasScanned = true; inputNames.clear(); - inputIds.clear(); outputNames.clear(); - outputIds.clear(); if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so.0", RTLD_LAZY); if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY); if (juce_libjackHandle == nullptr) return; - jack_status_t status; + jack_status_t status = {}; // open a dummy client - if (jack_client_t* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status)) + if (auto* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status)) { // scan for output devices for (JackPortIterator i (client, false); i.next();) - { - if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.clientName)) - { - inputNames.add (i.clientName); - inputIds.add (i.ports [i.index]); - } - } + if (i.getClientName() != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.getClientName())) + inputNames.add (i.getClientName()); // scan for input devices for (JackPortIterator i (client, true); i.next();) - { - if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.clientName)) - { - outputNames.add (i.clientName); - outputIds.add (i.ports [i.index]); - } - } + if (i.getClientName() != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.getClientName())) + outputNames.add (i.getClientName()); juce::jack_client_close (client); } @@ -580,8 +638,8 @@ class JackAudioIODeviceType : public AudioIODeviceType jassert (hasScanned); // need to call scanForDevices() before doing this if (JackAudioIODevice* d = dynamic_cast (device)) - return asInput ? inputIds.indexOf (d->inputId) - : outputIds.indexOf (d->outputId); + return asInput ? inputNames.indexOf (d->inputName) + : outputNames.indexOf (d->outputName); return -1; } @@ -595,34 +653,17 @@ class JackAudioIODeviceType : public AudioIODeviceType const int outputIndex = outputNames.indexOf (outputDeviceName); if (inputIndex >= 0 || outputIndex >= 0) - return new JackAudioIODevice (outputIndex >= 0 ? outputDeviceName - : inputDeviceName, - inputIds [inputIndex], - outputIds [outputIndex]); + return new JackAudioIODevice (inputDeviceName, outputDeviceName, + [this] { callDeviceChangeListeners(); }); return nullptr; } - void portConnectionChange() { callDeviceChangeListeners(); } - private: - StringArray inputNames, outputNames, inputIds, outputIds; - bool hasScanned; + StringArray inputNames, outputNames; + bool hasScanned = false; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType) }; -void JackAudioIODevice::sendDeviceChangedCallback() -{ - for (int i = activeDeviceTypes.size(); --i >= 0;) - if (JackAudioIODeviceType* d = activeDeviceTypes[i]) - d->portConnectionChange(); -} - -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() -{ - return new JackAudioIODeviceType(); -} - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_android.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_android.cpp new file mode 100644 index 00000000..00b03274 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_android.cpp @@ -0,0 +1,1171 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 byte-code is generated from native/java/com/rmsl/juce/JuceMidiSupport.java with min sdk version 23 +// See juce_core/native/java/README.txt on how to generate this byte-code. +constexpr unsigned char javaMidiByteCode[] +{ + 0x1f, 0x8b, 0x08, 0x08, 0xa3, 0xf2, 0xc6, 0x63, 0x00, 0x03, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x78, 0x00, 0x95, 0x7c, + 0x09, 0x7c, 0xdb, 0x47, 0x95, 0xff, 0x9b, 0x9f, 0x7e, 0xb2, 0x6c, 0xf9, + 0x92, 0x64, 0x27, 0x76, 0x1c, 0xc7, 0x96, 0x8f, 0xc4, 0xf7, 0x15, 0xc7, + 0xa9, 0x13, 0x3b, 0xa9, 0xef, 0xc4, 0xb1, 0x73, 0xd4, 0x56, 0xd2, 0x36, + 0x2e, 0xdb, 0x2a, 0xb6, 0x12, 0xab, 0xb1, 0x25, 0x45, 0x92, 0xd3, 0x04, + 0x4a, 0x9b, 0x1e, 0x6c, 0x12, 0x08, 0x10, 0x4a, 0x9b, 0x96, 0x12, 0xd8, + 0xd2, 0x1b, 0x28, 0xd0, 0x76, 0x39, 0xca, 0x6e, 0x77, 0x29, 0x6c, 0x97, + 0x2d, 0xf7, 0xd5, 0x85, 0x70, 0x2d, 0xa5, 0x84, 0xa5, 0x94, 0x02, 0x61, + 0xf7, 0xbf, 0x6c, 0xe8, 0xbf, 0x90, 0xfd, 0xbe, 0x99, 0xf9, 0x49, 0x3f, + 0x3b, 0x6e, 0x53, 0x92, 0xcf, 0x57, 0x6f, 0xe6, 0xcd, 0x9b, 0x99, 0x37, + 0x33, 0x6f, 0xde, 0xbc, 0xf9, 0xfd, 0x24, 0x4f, 0x85, 0x0e, 0xb9, 0x5b, + 0xdb, 0x3b, 0xe8, 0xae, 0x83, 0xd9, 0xd5, 0xe5, 0x6f, 0x37, 0xff, 0xb8, + 0x6a, 0xcf, 0xe9, 0xa7, 0xdf, 0xb2, 0xa7, 0xbd, 0xf5, 0xf0, 0x6b, 0xab, + 0x1b, 0x4f, 0x9c, 0xba, 0xff, 0x6c, 0xe9, 0x30, 0x51, 0x8c, 0x88, 0x0e, + 0xed, 0x5a, 0xe3, 0x23, 0xfd, 0x6f, 0x74, 0x33, 0xd1, 0x32, 0xa1, 0xf8, + 0x9b, 0x80, 0xe7, 0x4d, 0xa2, 0xab, 0x40, 0xcf, 0x39, 0x89, 0x2a, 0x40, + 0x3d, 0x6e, 0xa2, 0xbf, 0x07, 0x2d, 0xce, 0x26, 0xca, 0x02, 0x7d, 0xdc, + 0x83, 0x3a, 0x97, 0x11, 0x7d, 0xdb, 0x4b, 0x74, 0xa6, 0x89, 0xe8, 0x47, + 0xc0, 0x4f, 0x81, 0x17, 0x81, 0x5f, 0x02, 0xd9, 0xcd, 0x44, 0x4b, 0x80, + 0x15, 0x40, 0x05, 0x50, 0x03, 0x34, 0x01, 0xfd, 0xc0, 0x6d, 0xc0, 0x73, + 0xc0, 0x59, 0xe0, 0x57, 0xc0, 0x6f, 0x80, 0x73, 0x80, 0xb7, 0x85, 0xa8, + 0x13, 0xe8, 0x02, 0x7a, 0x81, 0x00, 0x30, 0x0d, 0x1c, 0x04, 0x4e, 0x00, + 0xef, 0x05, 0xde, 0x0f, 0xdc, 0x03, 0x7c, 0x08, 0x78, 0x00, 0x78, 0x04, + 0x78, 0x0c, 0x78, 0x12, 0x38, 0x03, 0xfc, 0x19, 0xf0, 0xb7, 0x12, 0x8d, + 0x00, 0x37, 0x02, 0x1f, 0x05, 0xbe, 0x05, 0xbc, 0x0a, 0x94, 0xb7, 0x11, + 0x6d, 0x01, 0x66, 0x81, 0x93, 0xc0, 0x3f, 0x01, 0x2f, 0x02, 0x7f, 0x06, + 0xca, 0x56, 0x63, 0x3c, 0xc0, 0x01, 0xe0, 0x1e, 0xe0, 0x69, 0xe0, 0xb7, + 0xc0, 0xaa, 0x76, 0xa2, 0xcd, 0xc0, 0x2c, 0x70, 0x12, 0x78, 0x0c, 0xf8, + 0x22, 0xf0, 0x03, 0xe0, 0x1c, 0x60, 0xae, 0xc1, 0xbc, 0x01, 0xcd, 0xc0, + 0x14, 0x70, 0x37, 0xf0, 0x35, 0xe0, 0x4f, 0xc0, 0xa6, 0x0e, 0xa2, 0x77, + 0x03, 0xcf, 0x00, 0xbf, 0x03, 0xf2, 0xd7, 0x12, 0xb5, 0x03, 0xe3, 0xc0, + 0x41, 0xe0, 0x2e, 0xe0, 0x09, 0xe0, 0x39, 0xe0, 0xc7, 0xc0, 0x39, 0xc0, + 0xc4, 0x9c, 0x16, 0x03, 0x2d, 0xc0, 0x36, 0x60, 0x16, 0xb8, 0x1d, 0xf8, + 0x20, 0xf0, 0x29, 0xe0, 0x59, 0xe0, 0x87, 0xc0, 0xef, 0x00, 0x47, 0x27, + 0xfa, 0x06, 0x1a, 0x80, 0x0d, 0xc0, 0x36, 0x20, 0x08, 0x1c, 0x02, 0xde, + 0x05, 0x7c, 0x13, 0x78, 0x11, 0x38, 0x07, 0xfc, 0x09, 0xf0, 0xaf, 0x23, + 0xaa, 0x02, 0x6a, 0x80, 0x46, 0x60, 0x35, 0xb0, 0x1e, 0xd8, 0x08, 0xf4, + 0x03, 0xc3, 0xc0, 0x0e, 0x60, 0x27, 0xb0, 0x1b, 0xb8, 0x0e, 0xd8, 0x0b, + 0xcc, 0x02, 0x49, 0xe0, 0x10, 0xf0, 0x36, 0xe0, 0x66, 0xe0, 0x36, 0xe0, + 0x28, 0xf0, 0x71, 0xe0, 0x47, 0x00, 0xad, 0xc7, 0x7a, 0x03, 0xf5, 0xc0, + 0x5a, 0xa0, 0x1f, 0xd8, 0x0c, 0xec, 0x04, 0x82, 0x40, 0x18, 0x48, 0x02, + 0x27, 0x81, 0xfb, 0x81, 0xa7, 0x80, 0xaf, 0x02, 0x3f, 0x01, 0xce, 0x02, + 0xbf, 0x01, 0xfe, 0x1b, 0x78, 0x15, 0x10, 0x5d, 0x44, 0x6e, 0xa0, 0x08, + 0xa8, 0x00, 0x9a, 0x81, 0x36, 0xa0, 0x0b, 0xd8, 0x0c, 0x04, 0x80, 0x6b, + 0x80, 0x13, 0xc0, 0x29, 0xe0, 0xf3, 0xc0, 0x8f, 0x80, 0x9f, 0x03, 0xbf, + 0x05, 0xfe, 0x0b, 0xf8, 0x0b, 0xb7, 0xd1, 0x8d, 0x36, 0x80, 0x76, 0x60, + 0x07, 0xb0, 0x0f, 0xb8, 0x11, 0x38, 0x05, 0x7c, 0x02, 0xf8, 0x1c, 0xf0, + 0x55, 0xe0, 0x3f, 0x81, 0x3f, 0x00, 0x62, 0x03, 0x51, 0x2e, 0xb0, 0x14, + 0xe8, 0x05, 0x36, 0x03, 0xdb, 0x81, 0xab, 0x80, 0x1b, 0x81, 0x87, 0x80, + 0x6f, 0x01, 0x2f, 0x03, 0xc6, 0x46, 0xd8, 0x38, 0x50, 0x0c, 0xd4, 0x00, + 0xeb, 0x80, 0x7e, 0x60, 0x0c, 0x98, 0x04, 0xe6, 0x80, 0x9b, 0x80, 0x3b, + 0x81, 0x07, 0x80, 0xc7, 0x80, 0xcf, 0x02, 0xff, 0x02, 0x7c, 0x1d, 0xf8, + 0x77, 0xe0, 0x45, 0xe0, 0x15, 0xe0, 0x8f, 0xc0, 0x5f, 0x00, 0xd7, 0xe5, + 0xe8, 0x1f, 0x58, 0x06, 0x34, 0x02, 0xeb, 0x80, 0xcb, 0x81, 0x41, 0xe0, + 0x6a, 0x20, 0x0e, 0x1c, 0x03, 0xfe, 0x0e, 0x78, 0x0a, 0xf8, 0x2a, 0xf0, + 0x53, 0xe0, 0x15, 0xe0, 0x35, 0xa0, 0xa0, 0x87, 0xa8, 0x1a, 0xe8, 0x02, + 0x06, 0x80, 0x31, 0x60, 0x0a, 0xb8, 0x09, 0x38, 0x01, 0xdc, 0x0b, 0xdc, + 0x0f, 0x3c, 0x05, 0x7c, 0x03, 0xf8, 0x21, 0xf0, 0x12, 0x20, 0x7a, 0x89, + 0x96, 0x03, 0x2d, 0xc0, 0xe5, 0xc0, 0x16, 0x60, 0x27, 0x70, 0x0d, 0x70, + 0x00, 0x78, 0x37, 0xf0, 0x30, 0xf0, 0x04, 0xf0, 0x8f, 0xc0, 0x33, 0xc0, + 0x19, 0xe0, 0x05, 0xe0, 0x15, 0xc0, 0xd5, 0x47, 0x94, 0x03, 0x2c, 0x01, + 0xca, 0x81, 0x1a, 0xa0, 0x09, 0x18, 0x06, 0xc6, 0x81, 0x29, 0xe0, 0x00, + 0x70, 0x0b, 0x70, 0x14, 0x78, 0x0f, 0xf0, 0x01, 0xe0, 0x51, 0xe0, 0x93, + 0xc0, 0x53, 0xc0, 0x33, 0xc0, 0x57, 0x80, 0xef, 0x01, 0x2f, 0x03, 0xce, + 0x7e, 0xb4, 0x05, 0xac, 0x02, 0x5a, 0x80, 0x3c, 0x98, 0x58, 0x01, 0x29, + 0x5f, 0x54, 0x09, 0x54, 0x01, 0xd5, 0xc0, 0x4a, 0x60, 0x15, 0x50, 0x03, + 0xd4, 0x02, 0x75, 0x40, 0x3d, 0xd0, 0x00, 0x34, 0x02, 0x70, 0x45, 0x04, + 0xd7, 0x42, 0x70, 0x0b, 0x04, 0x17, 0x40, 0xd8, 0xea, 0x84, 0x6d, 0x4d, + 0xd8, 0xba, 0x84, 0x2d, 0x4a, 0xd8, 0x96, 0x84, 0x6d, 0x47, 0xd8, 0x4e, + 0x84, 0x2d, 0x42, 0xda, 0x94, 0x09, 0xe6, 0x43, 0x30, 0x09, 0xc2, 0x52, + 0x13, 0x96, 0x86, 0x30, 0xbd, 0x84, 0xa1, 0x12, 0x54, 0x23, 0xa8, 0x43, + 0x03, 0xc0, 0x20, 0x30, 0xa4, 0xfd, 0x25, 0xdc, 0x27, 0xc1, 0xad, 0xd2, + 0x16, 0x60, 0x84, 0xfd, 0x29, 0xb0, 0x15, 0xd8, 0x06, 0x6c, 0x07, 0x76, + 0x00, 0x57, 0x00, 0x63, 0xc0, 0x38, 0x10, 0x00, 0x76, 0x02, 0x57, 0x02, + 0x57, 0x03, 0xbb, 0x81, 0x6b, 0x80, 0xbf, 0x01, 0xae, 0x05, 0x82, 0xc0, + 0x1e, 0x60, 0x12, 0xb8, 0x1e, 0x78, 0x2b, 0xf0, 0x76, 0xe0, 0x26, 0xe0, + 0x66, 0xe0, 0x08, 0x70, 0x0b, 0x70, 0x2b, 0xa9, 0xb9, 0xb1, 0xfe, 0xe5, + 0x6b, 0xba, 0x03, 0x83, 0xf7, 0xe8, 0xf4, 0x55, 0x48, 0x2f, 0x03, 0x35, + 0x74, 0xbe, 0x4c, 0xa7, 0xaf, 0xd3, 0x7c, 0x87, 0x8d, 0xef, 0xd0, 0x75, + 0x99, 0x6f, 0x6a, 0xbe, 0x5f, 0xa7, 0xa7, 0x35, 0x3f, 0xcb, 0x26, 0x8f, + 0xe3, 0x80, 0x92, 0x9a, 0x9f, 0xa3, 0xf9, 0x25, 0x5a, 0xa7, 0x1b, 0x35, + 0xdf, 0xd2, 0x89, 0xd3, 0x1e, 0x5b, 0xba, 0xc0, 0x26, 0xbf, 0x44, 0xcb, + 0x97, 0xe8, 0x32, 0xab, 0x6e, 0xa9, 0xad, 0xaf, 0x32, 0xad, 0x5b, 0x89, + 0xd6, 0x89, 0x65, 0xca, 0x74, 0x7a, 0x87, 0x4e, 0x57, 0xe8, 0x71, 0xb1, + 0x4c, 0xb5, 0x96, 0x29, 0xd5, 0xe9, 0xdb, 0x91, 0x5e, 0xa1, 0xd3, 0x27, + 0xb4, 0xfc, 0x2a, 0x5b, 0xdd, 0x1a, 0x5d, 0x77, 0x39, 0x29, 0x5b, 0xba, + 0x47, 0xeb, 0xd0, 0x6a, 0xd3, 0xb3, 0xcd, 0xa6, 0xdb, 0x6a, 0x9b, 0x6e, + 0x9c, 0xbe, 0x0f, 0xfc, 0x72, 0x9d, 0x7e, 0xb4, 0x31, 0xcd, 0xb7, 0xe6, + 0xb3, 0xdd, 0xd6, 0x4e, 0xbb, 0x4d, 0x7f, 0x4e, 0x3f, 0x6e, 0x4b, 0x5b, + 0x63, 0x5c, 0x6b, 0xeb, 0x6b, 0xbd, 0xad, 0x2f, 0xb6, 0xcd, 0xa7, 0x34, + 0xbf, 0x5b, 0xf3, 0xd9, 0x46, 0x2e, 0xd7, 0xe9, 0xfd, 0x3a, 0xcd, 0x75, + 0x67, 0x74, 0xfa, 0x19, 0xa4, 0x67, 0x75, 0xfa, 0x39, 0xa4, 0x23, 0x3a, + 0xfd, 0x3c, 0xd2, 0x51, 0x9d, 0x3e, 0x8b, 0xf4, 0x41, 0x9d, 0x7e, 0xa5, + 0x51, 0xc5, 0x02, 0x9c, 0x3e, 0x8f, 0xf4, 0x01, 0xab, 0x7d, 0x6c, 0xa8, + 0xa4, 0x4e, 0xe7, 0x20, 0x3d, 0xa7, 0xd3, 0x85, 0xb6, 0xb4, 0xbf, 0x29, + 0xdd, 0x66, 0xa3, 0x2d, 0x7d, 0x9d, 0xad, 0xaf, 0x35, 0x36, 0x7e, 0x77, + 0x53, 0xba, 0xdf, 0x01, 0x1b, 0x7f, 0x87, 0x2d, 0x7d, 0x95, 0xad, 0xdf, + 0xeb, 0x6c, 0xfc, 0x69, 0x5b, 0xdd, 0x18, 0xd2, 0x37, 0x58, 0x63, 0xb7, + 0xc9, 0x1f, 0x43, 0xfa, 0x90, 0x4e, 0x9f, 0xb4, 0xd5, 0x7d, 0xdc, 0xa6, + 0x0f, 0xaf, 0x9d, 0x25, 0xff, 0xa8, 0x8d, 0xbf, 0xc3, 0x96, 0x3e, 0x6d, + 0xeb, 0xeb, 0x41, 0xa4, 0x13, 0x56, 0x3b, 0x48, 0x1f, 0xd6, 0xe9, 0xa7, + 0x9a, 0xd2, 0x73, 0xf5, 0x0c, 0xd2, 0x71, 0x9d, 0xfe, 0x7a, 0x93, 0xda, + 0xc3, 0x3d, 0x7a, 0x8d, 0xde, 0xa6, 0xd3, 0xbc, 0x46, 0x37, 0xea, 0xf4, + 0x59, 0x5b, 0xfa, 0x3e, 0x5b, 0xda, 0xb2, 0x9f, 0x7e, 0x5d, 0x97, 0xd3, + 0x03, 0x36, 0x7b, 0x18, 0xb4, 0xd9, 0xc3, 0x90, 0xe6, 0x97, 0xe8, 0xf4, + 0x8d, 0xda, 0x9e, 0xa7, 0x79, 0x5d, 0x60, 0x8d, 0x1f, 0xd1, 0xd4, 0x21, + 0x78, 0xaf, 0x78, 0xe8, 0x28, 0x31, 0x6d, 0xa7, 0x77, 0x4a, 0xba, 0x8e, + 0x4e, 0x48, 0x9a, 0x45, 0x42, 0xb0, 0x9f, 0x5d, 0x46, 0x7f, 0x4b, 0x4c, + 0x7b, 0xe8, 0xab, 0x92, 0x0a, 0xfa, 0x96, 0xa4, 0x35, 0xf4, 0x3f, 0x92, + 0xd6, 0xd2, 0xab, 0xc4, 0xbe, 0x78, 0x89, 0x94, 0xab, 0xd2, 0xfc, 0x2a, + 0xcd, 0x5f, 0xa9, 0xf3, 0x4c, 0x3d, 0x82, 0xf7, 0x9a, 0x49, 0xef, 0x25, + 0xa6, 0x5e, 0xfa, 0x9e, 0xa4, 0xaa, 0x7c, 0x95, 0x2e, 0xaf, 0xd1, 0xfa, + 0xd4, 0xc0, 0x13, 0xbf, 0x47, 0xd2, 0x01, 0xba, 0x57, 0xd2, 0x62, 0xfa, + 0x8e, 0xa4, 0x6b, 0xe9, 0xdf, 0x75, 0xf9, 0x7f, 0x6b, 0xfa, 0xff, 0x48, + 0xed, 0xd5, 0x0f, 0x48, 0xda, 0x43, 0x5f, 0xd7, 0xf9, 0x3f, 0x11, 0x9f, + 0x05, 0x15, 0xf4, 0x2e, 0x49, 0x6b, 0xe8, 0x79, 0x62, 0x9f, 0x97, 0x45, + 0x8f, 0x49, 0xea, 0xa0, 0x4f, 0x4a, 0x9a, 0x41, 0xff, 0x42, 0xec, 0xf3, + 0x32, 0xe9, 0x2e, 0x49, 0xab, 0xe8, 0x21, 0x4d, 0xff, 0x89, 0xd8, 0xe7, + 0x35, 0xd0, 0xfb, 0x35, 0xfd, 0xa0, 0xa4, 0x4e, 0x7a, 0x5c, 0xd2, 0xed, + 0x74, 0x01, 0xd4, 0x09, 0x7e, 0x06, 0x68, 0x26, 0x6a, 0x3c, 0x48, 0xec, + 0x17, 0x87, 0x29, 0x47, 0x30, 0xbd, 0x8c, 0xf2, 0x41, 0xdd, 0xba, 0x3c, + 0x3b, 0x45, 0xb3, 0xe9, 0xa4, 0xa4, 0x6e, 0xca, 0x42, 0x79, 0xae, 0x6e, + 0x2f, 0x4f, 0x97, 0xe7, 0x81, 0x73, 0x52, 0xd2, 0x1c, 0x72, 0x09, 0x45, + 0x33, 0x05, 0xfb, 0xcc, 0x3c, 0xfa, 0x28, 0x31, 0xad, 0xa4, 0x67, 0x41, + 0xbd, 0x5a, 0x2f, 0x2f, 0x3c, 0xeb, 0xe7, 0x24, 0xf5, 0xd0, 0xef, 0x24, + 0xf5, 0xd2, 0x7f, 0x81, 0xfa, 0xb4, 0xfe, 0x1c, 0xdc, 0x7f, 0x41, 0xd3, + 0x7f, 0x25, 0xe5, 0x6f, 0x3f, 0x2b, 0xe9, 0x38, 0x7d, 0x51, 0x52, 0x1f, + 0x7d, 0x49, 0xf3, 0xb9, 0x7c, 0x89, 0x6e, 0x77, 0x09, 0x4e, 0x2f, 0x13, + 0xfd, 0x2e, 0xd5, 0x7a, 0x15, 0xe3, 0xb4, 0x7a, 0x52, 0xd2, 0x36, 0x7a, + 0x45, 0xd2, 0x2e, 0xfa, 0xad, 0xa4, 0x1b, 0xe9, 0x35, 0x49, 0x37, 0xd0, + 0x12, 0xc1, 0xf6, 0xa7, 0xea, 0x2f, 0x83, 0xc5, 0xdf, 0xa9, 0xe9, 0x07, + 0xa5, 0x2d, 0xaa, 0x76, 0x4a, 0xa1, 0xff, 0x03, 0xd2, 0x66, 0x0b, 0xe8, + 0x7e, 0x62, 0x5f, 0x69, 0xd0, 0x3d, 0xd2, 0x1e, 0x87, 0x64, 0x79, 0x05, + 0xd6, 0x53, 0x51, 0x41, 0x1f, 0x97, 0x74, 0x15, 0x7d, 0x5a, 0xd2, 0x5d, + 0xf4, 0x8f, 0x92, 0x6e, 0xa6, 0x33, 0x92, 0x36, 0xd2, 0x4b, 0x92, 0x36, + 0xd1, 0xaf, 0x25, 0x1d, 0xa3, 0xf3, 0x92, 0x8e, 0x50, 0xae, 0xb4, 0xeb, + 0x4d, 0x54, 0x28, 0xed, 0xb7, 0x57, 0xb6, 0x57, 0xa9, 0xf5, 0x62, 0xfa, + 0x61, 0x49, 0xd5, 0xfc, 0x54, 0x22, 0x2a, 0xf8, 0x37, 0x49, 0xb7, 0xd1, + 0x37, 0x74, 0xf9, 0x59, 0x49, 0xb7, 0xd2, 0xcb, 0x92, 0x8e, 0x52, 0x86, + 0x50, 0xfc, 0x6c, 0x4d, 0xf3, 0x04, 0xdb, 0x77, 0x8f, 0x6c, 0xb7, 0x4a, + 0xb7, 0x5b, 0xa5, 0xdb, 0xad, 0xd2, 0xed, 0x56, 0xe9, 0xf6, 0xaa, 0x74, + 0xfd, 0x2a, 0x5d, 0xbf, 0x4a, 0xd7, 0xaf, 0xd6, 0xf5, 0xaa, 0xb5, 0x7c, + 0xb5, 0x96, 0xaf, 0xd6, 0xf2, 0xd5, 0x5a, 0xbe, 0x5a, 0xcb, 0xaf, 0x44, + 0xd4, 0x91, 0x21, 0xf7, 0xd1, 0x1a, 0xfa, 0xa1, 0xa4, 0x1d, 0xf4, 0x23, + 0x4d, 0x7f, 0x2c, 0x69, 0x3b, 0xfd, 0x44, 0xd2, 0xb5, 0xf4, 0x53, 0x4d, + 0xff, 0x43, 0xf3, 0x7f, 0xa9, 0xe9, 0x7f, 0x4a, 0xba, 0x9a, 0x7e, 0xa5, + 0xe9, 0x6f, 0xe4, 0xbe, 0xeb, 0x97, 0xed, 0xae, 0x42, 0xff, 0xef, 0x93, + 0xb4, 0x8a, 0x3e, 0x26, 0xa9, 0x4b, 0xde, 0xf5, 0xf8, 0x6c, 0xfc, 0x8c, + 0xa4, 0x88, 0xa6, 0x84, 0xda, 0x6f, 0x19, 0x72, 0xdf, 0xa9, 0xf1, 0xd6, + 0xc0, 0x52, 0xfe, 0x4e, 0xd2, 0x12, 0xba, 0x4f, 0xd2, 0x1a, 0x7a, 0x58, + 0x52, 0xb5, 0x7e, 0x35, 0xb0, 0x9b, 0x27, 0x24, 0xbd, 0x92, 0x9e, 0x92, + 0x74, 0x17, 0x7d, 0x5e, 0xd3, 0x7f, 0x90, 0xb4, 0x90, 0x9e, 0x96, 0x74, + 0x25, 0xfd, 0xb3, 0xa4, 0xa5, 0xf4, 0x8c, 0xa4, 0xeb, 0xe9, 0x67, 0x92, + 0xae, 0xa3, 0x17, 0x34, 0xfd, 0xb9, 0xe6, 0xbf, 0x28, 0x69, 0x37, 0xfd, + 0x42, 0xfb, 0x85, 0xdf, 0x4b, 0x5a, 0x44, 0xe7, 0x24, 0x5d, 0x46, 0x7f, + 0x90, 0x74, 0x07, 0xfd, 0x51, 0xd2, 0x56, 0xfa, 0x5f, 0xed, 0x47, 0xfe, + 0x22, 0xe9, 0x26, 0x2a, 0x10, 0xec, 0x1f, 0x9a, 0xe5, 0x38, 0x6a, 0x11, + 0x91, 0x9d, 0xd6, 0xfe, 0xe2, 0x2b, 0xd2, 0x4f, 0x34, 0x60, 0x47, 0x2a, + 0x9a, 0xa1, 0xe9, 0x6d, 0x92, 0x2e, 0xa5, 0x47, 0x24, 0x5d, 0x4e, 0x8f, + 0x4a, 0x6a, 0xd2, 0xa7, 0x74, 0xf9, 0x97, 0x89, 0x63, 0x82, 0x46, 0x29, + 0xdf, 0xa6, 0xdb, 0x69, 0xc3, 0xca, 0xf9, 0x04, 0xd3, 0x32, 0x2a, 0x12, + 0x1c, 0x03, 0xa8, 0xf6, 0x56, 0xeb, 0x79, 0x5b, 0x8d, 0x28, 0xe4, 0x13, + 0xc4, 0x67, 0xbd, 0xea, 0xbf, 0x1d, 0xf3, 0xff, 0x35, 0xe2, 0x58, 0x74, + 0x50, 0xca, 0x75, 0x60, 0x67, 0xf0, 0x3e, 0x59, 0xab, 0xeb, 0xad, 0x85, + 0xdc, 0x3b, 0x74, 0xfe, 0x0e, 0x9d, 0x3f, 0x25, 0x69, 0x2d, 0x7d, 0x53, + 0xe7, 0xff, 0x4c, 0x2a, 0x5e, 0x70, 0x0b, 0xa6, 0x3b, 0xc9, 0x2b, 0x38, + 0xa6, 0xad, 0xa3, 0x63, 0xc4, 0x71, 0xad, 0x6a, 0xa7, 0x53, 0xd7, 0xef, + 0x84, 0xfc, 0xdd, 0x92, 0xfa, 0x65, 0x3f, 0x9d, 0x88, 0x98, 0xbf, 0x2d, + 0x69, 0x05, 0xfd, 0x7f, 0xcd, 0xe7, 0xf6, 0xd6, 0xe9, 0x7a, 0xeb, 0x74, + 0xff, 0xeb, 0x74, 0x3f, 0xeb, 0x74, 0x3f, 0xeb, 0x74, 0x3f, 0xeb, 0xa1, + 0xff, 0x73, 0xc4, 0xb4, 0x9c, 0x7e, 0x40, 0x1c, 0x9f, 0x28, 0xbd, 0xba, + 0x35, 0xdd, 0xa0, 0xdb, 0xd9, 0x80, 0xe8, 0xd8, 0x10, 0x1c, 0x4f, 0xab, + 0xfc, 0x46, 0x6d, 0x77, 0x1c, 0xb3, 0x09, 0x6e, 0x93, 0xd4, 0xbf, 0x62, + 0xe0, 0x1a, 0x04, 0xd5, 0x5f, 0xc2, 0x21, 0x77, 0x6a, 0x48, 0xc5, 0x6b, + 0x22, 0x23, 0x1d, 0x6f, 0x71, 0xf9, 0x8d, 0x28, 0x3f, 0xaf, 0x0f, 0xc1, + 0x95, 0x9a, 0xef, 0xb4, 0x95, 0x9f, 0x40, 0x79, 0xe1, 0xa0, 0xca, 0xaf, + 0xd2, 0xfc, 0x8d, 0xb6, 0xf2, 0xd3, 0x28, 0x1f, 0xd1, 0xe5, 0x35, 0xba, + 0xff, 0xa5, 0x40, 0x4f, 0xa3, 0x2a, 0x7f, 0x0c, 0xe5, 0x71, 0x5d, 0x5e, + 0xab, 0xeb, 0xd9, 0xfb, 0x7f, 0x0d, 0xe5, 0x2f, 0xeb, 0xf2, 0x3a, 0x5d, + 0xdf, 0x5e, 0x9e, 0x83, 0x0b, 0x81, 0xa9, 0x0f, 0xe3, 0x4a, 0x5d, 0xce, + 0x67, 0xf2, 0x66, 0xdd, 0x7e, 0x29, 0xca, 0x9b, 0x75, 0x79, 0x95, 0xad, + 0xbe, 0x55, 0xbe, 0x06, 0xe5, 0xd7, 0xeb, 0x72, 0x07, 0xfc, 0x24, 0xc7, + 0xeb, 0xf7, 0xd5, 0xa9, 0x58, 0x37, 0xe0, 0x71, 0xd0, 0x01, 0xcf, 0x71, + 0xee, 0x85, 0x22, 0x7e, 0x03, 0xbc, 0x1c, 0x63, 0x9d, 0x91, 0x4d, 0x25, + 0x46, 0x21, 0xfa, 0xf8, 0x10, 0x1d, 0xf0, 0xaf, 0x86, 0xbc, 0xcf, 0xc8, + 0x37, 0x94, 0xe4, 0x31, 0x2d, 0xf9, 0x76, 0x48, 0xba, 0xc1, 0xb5, 0xda, + 0xfb, 0x58, 0x9d, 0x8a, 0xdf, 0xe7, 0x4b, 0xcd, 0x6a, 0xa9, 0x74, 0xbf, + 0x9f, 0xaa, 0x53, 0x31, 0xfc, 0x62, 0xfd, 0x46, 0x3c, 0x4e, 0xc8, 0xe4, + 0x18, 0x5e, 0x29, 0x2f, 0xa4, 0xfc, 0x67, 0x2c, 0x79, 0xbf, 0x93, 0x22, + 0x9e, 0x8f, 0x4a, 0x6b, 0xa9, 0xf2, 0x0e, 0xa1, 0xce, 0xc7, 0x70, 0x52, + 0xe5, 0xc0, 0x4a, 0x86, 0x70, 0x52, 0xa9, 0xf6, 0x79, 0xdc, 0x9f, 0xaf, + 0xe3, 0xb3, 0x10, 0xf1, 0x9b, 0x27, 0x0f, 0xf9, 0x4a, 0xd8, 0x55, 0xcc, + 0xc3, 0x7e, 0x64, 0x02, 0x6d, 0x4f, 0xf8, 0x1c, 0xf2, 0x2e, 0x61, 0xca, + 0x53, 0x9b, 0xe8, 0xcb, 0x75, 0x6a, 0x9d, 0xe2, 0x9e, 0x4f, 0x20, 0x9f, + 0xe3, 0x88, 0x7b, 0x3e, 0x0e, 0xea, 0x86, 0xcd, 0xe6, 0x82, 0xf7, 0x18, + 0xf3, 0xd0, 0x7e, 0x2e, 0xf9, 0xbc, 0x91, 0xd6, 0xf5, 0x38, 0xdf, 0x6a, + 0xcf, 0xe5, 0xe9, 0x51, 0xa8, 0x7f, 0xdc, 0x5f, 0x9e, 0xb4, 0x15, 0x87, + 0xe4, 0x7e, 0xab, 0x4e, 0xed, 0x1b, 0x9f, 0x67, 0xb5, 0xc3, 0x49, 0x3e, + 0x7f, 0xbb, 0xc3, 0x0b, 0x9d, 0x7d, 0xe8, 0x2f, 0x07, 0x6d, 0x66, 0x53, + 0xa0, 0x82, 0xc7, 0x60, 0xea, 0x31, 0x3f, 0x82, 0xfa, 0xbe, 0x9e, 0x76, + 0x47, 0x39, 0x95, 0x38, 0x78, 0xae, 0xc3, 0x72, 0xae, 0x1d, 0x56, 0x0d, + 0x47, 0xa7, 0xc3, 0x47, 0x81, 0x6a, 0x55, 0xc3, 0x21, 0x6b, 0x3c, 0x0a, + 0xbe, 0xce, 0x39, 0x22, 0xfe, 0x0d, 0x58, 0xe3, 0x3c, 0xc4, 0x0b, 0x86, + 0xbc, 0x4f, 0xfd, 0xb4, 0x4e, 0xdd, 0xff, 0x02, 0xd7, 0xa5, 0xe7, 0xb5, + 0x44, 0x14, 0x62, 0xfc, 0x99, 0x54, 0xe2, 0xca, 0x91, 0x6d, 0x1f, 0x80, + 0x7c, 0x44, 0x5e, 0x9a, 0x72, 0x34, 0x3f, 0x3b, 0xc5, 0xef, 0x74, 0x5d, + 0x46, 0x95, 0xc8, 0xc7, 0x3c, 0xf9, 0xf0, 0xd8, 0x25, 0xc2, 0x44, 0x2b, + 0x6d, 0xd0, 0x30, 0x47, 0x44, 0xfc, 0x5e, 0xf8, 0xc4, 0x4a, 0x91, 0x87, + 0xb2, 0x02, 0xd6, 0xd9, 0x17, 0xf1, 0x2f, 0xc1, 0x3e, 0xcb, 0x71, 0xf8, + 0xcc, 0x88, 0x7f, 0x29, 0xfc, 0x3e, 0x52, 0x2b, 0x39, 0x35, 0x4e, 0x55, + 0x95, 0x7d, 0xe8, 0xc1, 0x83, 0x16, 0x72, 0x5c, 0x5b, 0x5d, 0x86, 0x79, + 0xc0, 0xf3, 0x61, 0x5e, 0x51, 0x57, 0xc4, 0x53, 0xa8, 0xda, 0xea, 0xc9, + 0xa1, 0x58, 0xb0, 0x16, 0xf5, 0x72, 0xe9, 0x3a, 0x39, 0x77, 0x96, 0x5d, + 0xfc, 0xbe, 0x4e, 0xed, 0xd9, 0xf9, 0xf6, 0x73, 0x04, 0x76, 0x91, 0xa7, + 0x57, 0x56, 0xfd, 0x33, 0xe4, 0xfc, 0xe6, 0xa7, 0xec, 0xe3, 0x7f, 0xea, + 0xd4, 0x1d, 0x32, 0xe0, 0x77, 0x63, 0x7e, 0xb3, 0x51, 0x27, 0x21, 0xed, + 0x82, 0x6d, 0x22, 0x03, 0xff, 0xb9, 0xe6, 0xab, 0xda, 0x86, 0x62, 0x1e, + 0xbe, 0xb9, 0x4f, 0x08, 0x37, 0x4d, 0x18, 0x2e, 0x9a, 0x70, 0x64, 0xd3, + 0x6e, 0x33, 0x0b, 0xde, 0xf5, 0x1a, 0x91, 0xa9, 0x75, 0x11, 0xb2, 0xaf, + 0xcc, 0x7a, 0x15, 0x0b, 0x07, 0xfc, 0x2e, 0x69, 0x0b, 0x11, 0x0f, 0xdf, + 0xfe, 0x6b, 0x33, 0x4b, 0x70, 0xc6, 0x94, 0x08, 0xaf, 0x9c, 0x33, 0x8f, + 0xec, 0xb1, 0x13, 0x23, 0x55, 0xbd, 0xde, 0x0c, 0x8e, 0x8f, 0x26, 0xc0, + 0x9b, 0x40, 0xad, 0x3c, 0xb9, 0x26, 0xdc, 0x9e, 0x10, 0x2d, 0x08, 0xbb, + 0x84, 0xd4, 0xa3, 0x00, 0xed, 0xba, 0x40, 0x23, 0x1e, 0x8e, 0xe2, 0xa3, + 0x9e, 0x93, 0xda, 0x7e, 0x84, 0x6d, 0x7c, 0x96, 0x4d, 0xe5, 0x41, 0xf7, + 0x4c, 0xd0, 0x65, 0xf5, 0xca, 0x9e, 0x37, 0x64, 0xe7, 0xd0, 0xf8, 0x2d, + 0x59, 0xe4, 0x3a, 0xe2, 0x7a, 0xbf, 0x78, 0x50, 0x7c, 0xc6, 0xfc, 0xf2, + 0xc1, 0xcc, 0x3e, 0x2d, 0x6b, 0xa6, 0x6e, 0xd7, 0xe9, 0xfa, 0xd6, 0xfc, + 0x54, 0xd6, 0xab, 0x98, 0x4c, 0x69, 0xeb, 0xd1, 0x63, 0xca, 0xa1, 0x9d, + 0x15, 0x99, 0xb4, 0x0e, 0xe3, 0x8e, 0xe0, 0x82, 0xe5, 0xc7, 0x8c, 0x5e, + 0xe3, 0xcf, 0x9c, 0x57, 0xaf, 0xee, 0x0d, 0xea, 0x75, 0xca, 0x7a, 0xcd, + 0x5c, 0x8f, 0xac, 0x7a, 0x17, 0xad, 0x11, 0xa5, 0xd7, 0xa8, 0xb5, 0xde, + 0x5a, 0xa3, 0x5c, 0xcc, 0x56, 0xde, 0xbc, 0x35, 0x62, 0xdd, 0xb9, 0x66, + 0x47, 0xbd, 0x5a, 0xff, 0x98, 0xe7, 0xbd, 0x72, 0x8d, 0x72, 0xb1, 0x46, + 0x39, 0x58, 0xa3, 0x3c, 0xb4, 0x6e, 0xad, 0x4b, 0x4f, 0x6a, 0x5d, 0x72, + 0xf4, 0xba, 0x54, 0x2f, 0xba, 0x2e, 0xb9, 0x7a, 0x5d, 0xf2, 0x6c, 0xeb, + 0x82, 0xf6, 0x50, 0x6b, 0xf1, 0x75, 0x19, 0x49, 0xad, 0xcb, 0x96, 0x79, + 0xeb, 0xe2, 0xd4, 0xda, 0x5d, 0x51, 0xaf, 0x9e, 0x35, 0x04, 0x3c, 0xba, + 0xdf, 0x9e, 0x95, 0xe4, 0xef, 0x47, 0xbf, 0xec, 0x23, 0x1d, 0x4e, 0xc1, + 0xfd, 0xae, 0x5e, 0xa4, 0xed, 0x85, 0x6b, 0xb2, 0x18, 0xcf, 0x21, 0x6b, + 0x60, 0xc4, 0xf5, 0x24, 0xd7, 0x7b, 0x42, 0x78, 0x30, 0x07, 0x3c, 0x13, + 0x13, 0x46, 0xbe, 0x1c, 0xbb, 0xe3, 0xa2, 0x3a, 0x8b, 0xad, 0xf7, 0xeb, + 0xf1, 0x78, 0xfe, 0xa7, 0xea, 0x49, 0xce, 0xb5, 0xaf, 0xb2, 0xbd, 0x2a, + 0x5f, 0xee, 0xdf, 0x2c, 0xec, 0xdf, 0x00, 0xda, 0x8f, 0x78, 0x32, 0xf5, + 0x1e, 0xbf, 0x09, 0xb3, 0xf4, 0xd7, 0xb6, 0xfd, 0x66, 0x78, 0x8b, 0xe9, + 0xbf, 0x18, 0xcf, 0xb2, 0x9f, 0xd9, 0x7a, 0xf5, 0xdc, 0x28, 0xe0, 0xf1, + 0x4a, 0x9d, 0x9d, 0x72, 0xd6, 0x1c, 0x14, 0xaf, 0x57, 0x67, 0x6f, 0x29, + 0x3e, 0x57, 0x60, 0xcf, 0x4e, 0xf4, 0x16, 0x60, 0xa5, 0x9e, 0x44, 0x8f, + 0x6e, 0x23, 0xd0, 0xef, 0xa5, 0x4e, 0x07, 0xfc, 0x85, 0xc7, 0x05, 0x49, + 0xce, 0x45, 0x3c, 0x19, 0x48, 0x4d, 0xf4, 0x7a, 0x91, 0xf3, 0xa1, 0xcc, + 0x05, 0xce, 0x32, 0xc9, 0xf1, 0xc1, 0x82, 0xf2, 0x8d, 0x5c, 0x51, 0x46, + 0x35, 0x82, 0xb5, 0x5d, 0x8a, 0xd6, 0x4b, 0xb5, 0x4e, 0xfc, 0xbc, 0x25, + 0x43, 0xda, 0x5a, 0xd5, 0xcd, 0x2d, 0xe5, 0x55, 0x52, 0x27, 0xd6, 0xea, + 0xd6, 0x7a, 0xeb, 0x6c, 0xf3, 0xa1, 0x1d, 0x3e, 0x79, 0xb9, 0x2f, 0x3f, + 0xb1, 0xc5, 0xe6, 0x6b, 0x1d, 0xd9, 0x8b, 0x1d, 0xad, 0x57, 0xcf, 0x08, + 0x95, 0x8e, 0x01, 0xf4, 0xd5, 0x29, 0x2a, 0x64, 0x0d, 0x39, 0xc7, 0x9e, + 0x2a, 0x50, 0xb7, 0xe1, 0x6b, 0x6f, 0x5f, 0xbd, 0x14, 0xdc, 0x4a, 0xc9, + 0x2d, 0x31, 0xde, 0x0d, 0x2b, 0x6d, 0x65, 0x7f, 0x2a, 0xca, 0x85, 0x43, + 0xed, 0x2d, 0x8f, 0x29, 0xcb, 0xca, 0x10, 0x11, 0x95, 0xc9, 0x93, 0xde, + 0x4b, 0x4a, 0x63, 0x35, 0x37, 0xd5, 0x28, 0x6d, 0xd0, 0xf6, 0x24, 0x10, + 0x6f, 0x39, 0xa5, 0x06, 0x44, 0x77, 0xd4, 0xab, 0xe7, 0x66, 0x13, 0xf0, + 0xfb, 0x31, 0xcf, 0x13, 0xd2, 0x86, 0x0a, 0xb0, 0x87, 0x60, 0xff, 0xa6, + 0x57, 0xda, 0xa7, 0xda, 0x47, 0x1f, 0xd6, 0x7b, 0x5b, 0xeb, 0xd9, 0x56, + 0x80, 0x5e, 0xb3, 0x28, 0xb0, 0x1a, 0xfa, 0x1a, 0x38, 0x6f, 0xfc, 0x37, + 0x40, 0x9b, 0x32, 0xbe, 0x41, 0xda, 0xfa, 0xcc, 0x97, 0x7d, 0xb1, 0x67, + 0x57, 0xbd, 0x3e, 0x5c, 0xaf, 0x9e, 0x2f, 0x95, 0x22, 0xe2, 0x41, 0x1b, + 0xc1, 0x65, 0xd0, 0xfa, 0x34, 0x8f, 0x0f, 0xa7, 0xf3, 0x00, 0xe2, 0x85, + 0xc0, 0x24, 0x73, 0xee, 0xb5, 0x9f, 0xab, 0x66, 0xa7, 0xb9, 0xd4, 0x3a, + 0x57, 0xcd, 0x72, 0xd3, 0x4f, 0x67, 0x9c, 0x86, 0x08, 0x0c, 0x08, 0x2a, + 0x37, 0xbd, 0x72, 0x96, 0x70, 0xea, 0x99, 0x5b, 0x4d, 0x61, 0x88, 0xb1, + 0xda, 0x5f, 0xf1, 0xd8, 0xb9, 0xff, 0x5a, 0xa3, 0x46, 0xd4, 0x5e, 0x50, + 0x3a, 0x34, 0xc9, 0x9e, 0x9b, 0xa1, 0x83, 0x3a, 0x33, 0x9e, 0xac, 0x57, + 0xcf, 0xac, 0x02, 0x3b, 0x54, 0x5f, 0x3c, 0x7a, 0xee, 0x8b, 0x63, 0x84, + 0x4e, 0xd1, 0x2a, 0xfb, 0x22, 0x39, 0xaf, 0x7e, 0x0a, 0x20, 0x88, 0x2e, + 0x37, 0x54, 0x3f, 0x86, 0x8c, 0x35, 0x78, 0x7d, 0xdd, 0x0e, 0xdf, 0x9a, + 0xf6, 0xfe, 0x5f, 0x5d, 0xe0, 0xd5, 0x60, 0x6e, 0x89, 0x43, 0xad, 0xc6, + 0x6a, 0xa4, 0xcb, 0x11, 0xdb, 0x74, 0x1a, 0x3f, 0xbc, 0xc0, 0xab, 0x21, + 0x6b, 0xf8, 0xef, 0xa0, 0x3d, 0x58, 0xbf, 0x4e, 0xe3, 0x5b, 0x17, 0xbc, + 0xc2, 0x97, 0xe1, 0xcd, 0xc8, 0xd0, 0x16, 0xf2, 0xcf, 0xa9, 0x75, 0x97, + 0x73, 0xd1, 0xab, 0xb4, 0xe1, 0x15, 0x64, 0x6d, 0x84, 0xac, 0x53, 0x2c, + 0xb5, 0x11, 0xb2, 0x5d, 0x68, 0x53, 0x05, 0x6d, 0x1c, 0x4a, 0x1b, 0x79, + 0x9a, 0xc3, 0x9a, 0x30, 0x43, 0x8e, 0xf6, 0xf1, 0x97, 0x2e, 0x60, 0xe4, + 0x46, 0x19, 0xf9, 0x9c, 0x5e, 0x67, 0xae, 0x53, 0x8e, 0xdf, 0x69, 0x8d, + 0xbf, 0x46, 0xf6, 0x56, 0x4b, 0x19, 0xf2, 0xcc, 0x17, 0xf4, 0xd5, 0x7a, + 0xf5, 0xbc, 0x86, 0x35, 0x74, 0x4a, 0x9b, 0x7c, 0xbf, 0x1c, 0xbb, 0xcf, + 0xe8, 0x84, 0x25, 0xe5, 0x1b, 0xa5, 0x02, 0xfa, 0x08, 0x8e, 0xc2, 0x4c, + 0x19, 0xd5, 0x98, 0x54, 0x26, 0x10, 0xc7, 0xf5, 0x2c, 0xb5, 0x6a, 0x18, + 0x3e, 0x47, 0xa4, 0xf5, 0x41, 0xf2, 0x3b, 0x02, 0x3d, 0x4b, 0xc0, 0x2b, + 0x92, 0x33, 0x58, 0x69, 0xac, 0xa4, 0x58, 0xeb, 0x95, 0xd4, 0x67, 0xfa, + 0x0a, 0x22, 0x3d, 0x0e, 0x72, 0x56, 0xe5, 0x48, 0xcf, 0x1f, 0xe8, 0x4b, + 0xd5, 0x73, 0x72, 0xad, 0x31, 0xe2, 0x73, 0xfc, 0x26, 0xdc, 0xcb, 0xd0, + 0xaa, 0x03, 0xba, 0x8a, 0x1a, 0xa7, 0x3a, 0x67, 0x3b, 0xa4, 0x9e, 0x7d, + 0xd2, 0x7f, 0x72, 0x1c, 0xfd, 0xe3, 0x7a, 0x92, 0x31, 0xba, 0xcf, 0x13, + 0xdb, 0x71, 0x23, 0x55, 0xf4, 0x73, 0x84, 0xc7, 0x91, 0x1e, 0x97, 0xbd, + 0xa8, 0xfd, 0x1e, 0xc7, 0x61, 0x3e, 0x23, 0x36, 0x76, 0x23, 0xf5, 0x7b, + 0x72, 0x4c, 0xaf, 0xc9, 0xcf, 0x33, 0x78, 0x2f, 0xbe, 0x84, 0xf2, 0xb7, + 0xa4, 0xe6, 0xb6, 0x33, 0xc7, 0x44, 0x34, 0x56, 0x6b, 0xf8, 0x2a, 0x22, + 0xad, 0x49, 0x2a, 0xce, 0xca, 0xc9, 0xf2, 0x89, 0xce, 0xac, 0x11, 0x0a, + 0xdc, 0x5a, 0x82, 0x75, 0xfb, 0x08, 0x6e, 0x24, 0x3c, 0xd7, 0x18, 0x27, + 0xac, 0x67, 0xec, 0x8e, 0x52, 0x8a, 0xf9, 0xe3, 0x98, 0xaf, 0x1c, 0xf0, + 0xd6, 0x51, 0xa0, 0x52, 0x59, 0x40, 0xa7, 0xd1, 0x8e, 0x76, 0x0a, 0x69, + 0xec, 0x74, 0x19, 0xea, 0x94, 0xd2, 0x97, 0x60, 0x75, 0x9d, 0x66, 0x09, + 0x55, 0xe6, 0x54, 0x92, 0xeb, 0x4b, 0xae, 0xdb, 0x5c, 0x77, 0x99, 0x0f, + 0x1f, 0xcc, 0xd8, 0x08, 0xfd, 0x6a, 0x3d, 0x63, 0xf7, 0xb2, 0xcc, 0x72, + 0xba, 0x9f, 0x72, 0xdc, 0x9d, 0xee, 0x4c, 0xc8, 0x54, 0x51, 0xac, 0x67, + 0x98, 0x4e, 0xdc, 0x5b, 0x6b, 0xb8, 0x4a, 0x3b, 0x73, 0xf2, 0x28, 0xf0, + 0x4e, 0xd4, 0xcc, 0xee, 0x40, 0x4f, 0x77, 0xe2, 0xde, 0xc5, 0xf3, 0x72, + 0x6f, 0x36, 0x7a, 0xcf, 0x91, 0xf6, 0x9b, 0x85, 0xfd, 0x93, 0xa5, 0xc6, + 0x17, 0x90, 0x73, 0xb2, 0x9b, 0x9c, 0xda, 0x76, 0x5f, 0xab, 0x57, 0xef, + 0x15, 0x02, 0xad, 0xcb, 0x31, 0xab, 0x0f, 0x68, 0xdb, 0xfd, 0xe4, 0x02, + 0x1b, 0x5e, 0x6e, 0xb3, 0xe1, 0x36, 0xec, 0x50, 0xc8, 0xfa, 0xef, 0x87, + 0xa7, 0xe6, 0xfc, 0x2a, 0xd4, 0x9b, 0x92, 0x16, 0xc5, 0x96, 0xd9, 0x27, + 0x47, 0x7e, 0x96, 0x2d, 0x13, 0xb3, 0x97, 0x8e, 0x49, 0x33, 0x1a, 0xd4, + 0x73, 0xea, 0x88, 0x7f, 0x86, 0xad, 0xda, 0xb1, 0x60, 0x4f, 0x62, 0x65, + 0xf3, 0x6d, 0xb1, 0x6e, 0x2d, 0xda, 0xbc, 0x4d, 0xf6, 0xdd, 0xde, 0x7b, + 0xee, 0x82, 0x97, 0x7c, 0x0e, 0xaf, 0x83, 0xb4, 0xce, 0xd8, 0xfd, 0x0d, + 0x4a, 0x67, 0xb5, 0x16, 0x6c, 0x6d, 0xb0, 0x2b, 0xb1, 0x0e, 0xbb, 0x34, + 0xe6, 0xbf, 0x9e, 0xe3, 0x40, 0x68, 0x8c, 0xb8, 0xb7, 0x10, 0x1e, 0xd6, + 0x0c, 0xb4, 0x2d, 0x95, 0x2b, 0xd2, 0xcb, 0xf6, 0x58, 0xd9, 0x29, 0x4c, + 0xe9, 0x77, 0x63, 0xfe, 0xfd, 0x2c, 0x87, 0xf9, 0x56, 0x79, 0xd8, 0xb9, + 0x99, 0x6f, 0xe6, 0x9a, 0x98, 0x27, 0x73, 0xbe, 0x8d, 0xe7, 0xcb, 0x3e, + 0x0d, 0x2a, 0x6b, 0x50, 0xef, 0x33, 0x76, 0xbe, 0xb0, 0x42, 0x5a, 0xb0, + 0xcf, 0x83, 0xd5, 0xf7, 0x2b, 0xcb, 0x0e, 0x7c, 0xb7, 0x88, 0x2a, 0xaa, + 0x7c, 0xa6, 0xcf, 0xd9, 0xd1, 0xd1, 0x47, 0x43, 0x19, 0x86, 0x93, 0xf7, + 0x51, 0x06, 0xb9, 0x5d, 0x63, 0x2f, 0x95, 0x52, 0xfb, 0xd1, 0x0e, 0xe8, + 0x58, 0x81, 0x7c, 0x8e, 0xab, 0xe2, 0x90, 0x2f, 0xab, 0xe3, 0x78, 0x13, + 0x0d, 0xb9, 0x5d, 0x59, 0x11, 0x4f, 0x39, 0xc6, 0xed, 0xce, 0x6e, 0xcf, + 0xae, 0x44, 0x79, 0x99, 0x4c, 0x8f, 0xbd, 0x52, 0x46, 0xed, 0x5f, 0x83, + 0x0d, 0x79, 0x56, 0x20, 0x9f, 0x93, 0xc7, 0x63, 0xcb, 0x93, 0xb3, 0x93, + 0x27, 0x3d, 0x35, 0xda, 0xc8, 0x2b, 0xa1, 0xef, 0x4b, 0xdf, 0x90, 0x87, + 0x74, 0x79, 0x5e, 0x3b, 0xac, 0xde, 0x9b, 0x77, 0x26, 0x2b, 0x4b, 0xd4, + 0x3e, 0x7f, 0xc6, 0xe9, 0x14, 0xb5, 0x5f, 0x28, 0x13, 0x25, 0x38, 0xc8, + 0xbc, 0x79, 0xb9, 0x79, 0x65, 0x02, 0xbb, 0x36, 0xaf, 0xf6, 0x02, 0x9f, + 0xe2, 0x9b, 0x31, 0x9a, 0x5d, 0x29, 0x5f, 0xbd, 0x53, 0xcf, 0xa5, 0x41, + 0xad, 0x0d, 0xea, 0x3d, 0x8f, 0x9a, 0xcb, 0x98, 0x27, 0xc2, 0x2b, 0x20, + 0x26, 0xda, 0x8a, 0x30, 0x2f, 0x95, 0xb8, 0x45, 0xc4, 0x3c, 0xa7, 0xf4, + 0xaa, 0xf1, 0x7c, 0xf1, 0xaa, 0xb1, 0x77, 0xe4, 0x55, 0x33, 0x6d, 0x96, + 0x61, 0x4a, 0x4b, 0xa8, 0x95, 0x11, 0x55, 0x81, 0xb4, 0x84, 0xbb, 0x11, + 0x95, 0xf1, 0x1a, 0xb2, 0x27, 0x3e, 0xe0, 0x79, 0xb7, 0x3e, 0xdd, 0xef, + 0x86, 0xe7, 0xae, 0x7d, 0x91, 0x3d, 0x34, 0xd6, 0xd6, 0xac, 0x1a, 0xe0, + 0xfb, 0xd3, 0x87, 0xa8, 0x47, 0xfa, 0x61, 0xdc, 0x9f, 0xe4, 0x0a, 0x40, + 0x67, 0xb3, 0xf6, 0x02, 0xaf, 0x01, 0xfb, 0xf8, 0x81, 0x94, 0xce, 0xfd, + 0x72, 0xed, 0xd9, 0x96, 0xfa, 0x1b, 0x52, 0xfb, 0xd8, 0x1f, 0xe5, 0x96, + 0x71, 0x2e, 0x59, 0x65, 0x9b, 0xad, 0x32, 0x7f, 0xba, 0x2c, 0x5f, 0xdf, + 0x4d, 0xb6, 0x36, 0xa8, 0x77, 0x0e, 0xe9, 0x71, 0x05, 0xbe, 0x83, 0x95, + 0xf3, 0xf9, 0x70, 0x83, 0xe8, 0xa8, 0x6e, 0xa1, 0x21, 0xa7, 0x30, 0x79, + 0xa5, 0xe0, 0x59, 0x32, 0x2a, 0xf6, 0xf9, 0x32, 0x3b, 0x0e, 0x55, 0xd2, + 0x50, 0x56, 0x46, 0x26, 0xaf, 0x54, 0x16, 0xb9, 0xdd, 0xed, 0x2f, 0xe3, + 0xfe, 0xe1, 0xae, 0x95, 0x67, 0xae, 0x13, 0xab, 0xe5, 0x2b, 0x6d, 0xff, + 0xa6, 0x03, 0xfa, 0xfa, 0x72, 0x78, 0xfd, 0x20, 0x91, 0x15, 0xeb, 0xb9, + 0x85, 0xfe, 0xe1, 0x34, 0x8f, 0xf3, 0x3e, 0x3a, 0x93, 0x99, 0x29, 0x6a, + 0x7f, 0x76, 0xc6, 0x34, 0x45, 0xed, 0x77, 0xbd, 0x72, 0xbe, 0x79, 0x4c, + 0x57, 0x37, 0xa8, 0xf7, 0x68, 0x31, 0xcf, 0x2a, 0xe4, 0x2b, 0xb1, 0xb2, + 0x31, 0x0f, 0x3f, 0x6f, 0x9a, 0x68, 0x5d, 0x3a, 0x2f, 0xb7, 0xdc, 0xa6, + 0xe5, 0x44, 0xeb, 0xb2, 0x79, 0x65, 0x25, 0x34, 0xb1, 0x66, 0x09, 0xce, + 0xe3, 0xef, 0x62, 0xf6, 0x8a, 0xe5, 0x7a, 0x94, 0x9b, 0xc5, 0xe0, 0x15, + 0xd3, 0x3a, 0xd3, 0x4d, 0x25, 0x26, 0xdf, 0xe2, 0x42, 0x32, 0x0a, 0x35, + 0xa5, 0xdd, 0xd4, 0x48, 0x99, 0x8a, 0x1e, 0x9f, 0xe8, 0xc0, 0x9d, 0x63, + 0xc8, 0x30, 0xb1, 0x12, 0x73, 0xb8, 0xa1, 0x9f, 0x11, 0x42, 0xd4, 0xfe, + 0x6f, 0x00, 0xf5, 0x10, 0x4f, 0x20, 0x38, 0x5e, 0x03, 0x69, 0x2b, 0x86, + 0x88, 0x41, 0x4f, 0x7e, 0x9e, 0x12, 0xe8, 0x29, 0x46, 0x2f, 0x0d, 0x58, + 0x23, 0xeb, 0x54, 0x61, 0xef, 0x7b, 0x1f, 0xa4, 0x02, 0xbd, 0xca, 0x63, + 0xa8, 0xf3, 0xe5, 0x93, 0x0b, 0xce, 0x99, 0x42, 0xdb, 0x39, 0x03, 0x8f, + 0xd1, 0xaf, 0x3c, 0x46, 0x95, 0xcc, 0xb3, 0xc7, 0x98, 0x64, 0xbf, 0xf6, + 0x6b, 0x8e, 0x9f, 0x96, 0xc3, 0xd2, 0x02, 0x3d, 0x25, 0xaf, 0xe7, 0x7d, + 0xd0, 0x56, 0x89, 0xe5, 0x7d, 0x50, 0xb7, 0x16, 0x6d, 0x95, 0xd8, 0xda, + 0xe2, 0xbd, 0x75, 0x97, 0x3c, 0x17, 0xcb, 0x8d, 0x6a, 0xc8, 0x5d, 0xc1, + 0xed, 0xfe, 0x62, 0xa2, 0xb7, 0x04, 0x6d, 0x2e, 0x76, 0x1a, 0x57, 0x2e, + 0x38, 0x8d, 0x0d, 0xf8, 0x61, 0x8f, 0xa4, 0xe5, 0x86, 0x29, 0x23, 0x37, + 0xc3, 0x96, 0xe3, 0x13, 0x2e, 0xe0, 0x85, 0xbf, 0x16, 0x5e, 0x19, 0xb9, + 0xc1, 0x23, 0xfc, 0x58, 0xb5, 0xcb, 0xcf, 0x78, 0x39, 0x42, 0x37, 0x75, + 0x7c, 0x62, 0xc5, 0x07, 0xec, 0x63, 0x1b, 0x20, 0xf7, 0xfe, 0x94, 0xdd, + 0xde, 0x21, 0x4c, 0x6d, 0x7f, 0x27, 0x1b, 0xd4, 0xb9, 0xc4, 0x67, 0x3e, + 0xfb, 0x3c, 0x5f, 0x5b, 0xbb, 0x87, 0x9f, 0x04, 0x04, 0xb0, 0x8a, 0x3e, + 0xdc, 0x49, 0xeb, 0xa8, 0xd5, 0xc7, 0x31, 0x68, 0x96, 0x3c, 0x93, 0x1c, + 0x74, 0x37, 0xe4, 0x8f, 0xd9, 0xfc, 0x5c, 0x09, 0x5a, 0xe7, 0xf8, 0xcd, + 0xaf, 0xe2, 0x37, 0x58, 0x6d, 0x60, 0x6e, 0x39, 0x6e, 0xa9, 0xef, 0xe1, + 0xf1, 0x67, 0xb0, 0x97, 0x0b, 0x91, 0xdb, 0xb9, 0xce, 0x39, 0x43, 0x3a, + 0xc6, 0x73, 0xfa, 0xd6, 0xb6, 0x87, 0x36, 0xc1, 0x13, 0xa6, 0xa2, 0x3c, + 0xa7, 0x8a, 0x2b, 0xae, 0x48, 0x45, 0x79, 0x9d, 0x56, 0x94, 0xe7, 0x08, + 0xcc, 0x29, 0x4f, 0xd9, 0x81, 0x7a, 0x9d, 0x4e, 0x44, 0xac, 0x07, 0x96, + 0xca, 0x79, 0x6e, 0x93, 0xb2, 0x26, 0xa9, 0xf2, 0x87, 0x50, 0xee, 0x2a, + 0xa8, 0x2d, 0xb5, 0x62, 0x43, 0xf6, 0xb8, 0x45, 0xd2, 0xe3, 0x62, 0x1c, + 0x5e, 0xcc, 0x1b, 0x24, 0x55, 0xec, 0xc8, 0xbb, 0x97, 0x67, 0x50, 0xc8, + 0x99, 0xcb, 0x94, 0x91, 0x5c, 0x25, 0x4e, 0x8b, 0xd8, 0xf8, 0x1e, 0x3a, + 0x70, 0x34, 0x70, 0x28, 0xa5, 0xb9, 0xc1, 0xa7, 0x55, 0xad, 0xa8, 0xcc, + 0xdc, 0x00, 0x0b, 0xff, 0x14, 0x24, 0x3b, 0x4d, 0x27, 0x15, 0x18, 0xdf, + 0x76, 0xd5, 0x3a, 0x0a, 0x8c, 0x29, 0xba, 0xa5, 0x32, 0xd2, 0xf3, 0x38, + 0x15, 0xaf, 0x96, 0x91, 0x60, 0x26, 0x3c, 0x74, 0x26, 0xef, 0xa3, 0x41, + 0xb4, 0xcb, 0xb7, 0xa4, 0x65, 0x98, 0xd9, 0x49, 0x1d, 0x8b, 0x1a, 0xf4, + 0x0e, 0x71, 0x73, 0xcb, 0xe4, 0x3b, 0x84, 0x8c, 0x9f, 0x0d, 0x19, 0x4b, + 0x3f, 0xd9, 0xa0, 0xde, 0x63, 0xa5, 0xe7, 0xd0, 0x99, 0x9e, 0x43, 0x11, + 0xe8, 0x53, 0x5a, 0x08, 0xf9, 0x24, 0xe3, 0x23, 0x38, 0x99, 0xd8, 0xde, + 0x60, 0xaf, 0x7d, 0x58, 0x63, 0xff, 0x07, 0x10, 0x5b, 0xb8, 0x8d, 0x75, + 0xc6, 0x95, 0x34, 0x5f, 0xee, 0x7e, 0x1d, 0x97, 0xb1, 0x1d, 0x87, 0xe4, + 0x08, 0xd8, 0xbe, 0x75, 0xb9, 0xe0, 0x39, 0x2a, 0x90, 0xd1, 0x57, 0xa5, + 0xc3, 0x8f, 0x08, 0xa6, 0x9d, 0xc6, 0xa5, 0x3f, 0x6c, 0xa3, 0x03, 0xf2, + 0x8d, 0x52, 0x6d, 0x4b, 0x5a, 0x8b, 0xd7, 0x8b, 0xbd, 0x97, 0xeb, 0x55, + 0xb1, 0xcf, 0x72, 0xe1, 0x9b, 0x98, 0x65, 0xa7, 0x8a, 0x97, 0x11, 0xaf, + 0x71, 0x0c, 0xc4, 0xf3, 0x14, 0x41, 0xc9, 0x01, 0x7d, 0xaf, 0xe0, 0x77, + 0x81, 0x2a, 0xf6, 0x3e, 0x78, 0x73, 0x4b, 0xf2, 0x20, 0xee, 0x12, 0xea, + 0x0c, 0x78, 0xae, 0x41, 0xbd, 0x9b, 0xb7, 0xe6, 0xc8, 0xcf, 0x67, 0x91, + 0xe0, 0x98, 0x65, 0x99, 0xec, 0x73, 0x34, 0x15, 0xb5, 0x5a, 0x3b, 0xc3, + 0x57, 0xe0, 0x2b, 0x6c, 0x5f, 0xd3, 0x8e, 0x53, 0x33, 0x70, 0x97, 0xda, + 0xb9, 0x1c, 0x8d, 0xf1, 0xce, 0x55, 0xf4, 0xe3, 0xec, 0x1f, 0x33, 0x3a, + 0x33, 0x7e, 0x7f, 0x81, 0x77, 0x1b, 0xfb, 0xd3, 0xf2, 0x8c, 0x5a, 0x1a, + 0xdb, 0x57, 0x6a, 0xb5, 0x9e, 0xd9, 0x7e, 0xf4, 0x77, 0x17, 0x02, 0x77, + 0xaa, 0x9d, 0x3c, 0x25, 0x3d, 0x98, 0xda, 0xc9, 0xca, 0x9b, 0xa9, 0x9d, + 0x6c, 0x52, 0xed, 0x37, 0x3a, 0x8d, 0x4c, 0xbd, 0x2b, 0xf5, 0x6e, 0xbc, + 0x97, 0x57, 0xe6, 0x83, 0xf0, 0x70, 0x65, 0x7a, 0x3e, 0x31, 0x5e, 0xb7, + 0xdc, 0x7f, 0x6e, 0x2b, 0x3e, 0xdd, 0x89, 0x31, 0xbe, 0x25, 0xb5, 0xff, + 0xae, 0x59, 0xf4, 0x59, 0x99, 0xb5, 0x27, 0xff, 0xa3, 0x41, 0xdd, 0xb9, + 0xad, 0x3d, 0x58, 0xab, 0xf7, 0xa0, 0xf5, 0x3c, 0xe8, 0xac, 0x3e, 0x4f, + 0x02, 0x9e, 0x12, 0x69, 0xfd, 0x7e, 0xe9, 0x23, 0x0d, 0x69, 0x5d, 0x2f, + 0x37, 0xa8, 0xef, 0x36, 0xe8, 0x08, 0x04, 0x1e, 0x9a, 0x77, 0xb3, 0xf6, + 0x93, 0xb6, 0xb8, 0xa3, 0x53, 0x14, 0xe9, 0xfc, 0xfd, 0xc8, 0xab, 0xdd, + 0xb4, 0x70, 0xf5, 0x54, 0xf9, 0x43, 0x28, 0xd7, 0xb1, 0x09, 0x6a, 0xf1, + 0x2d, 0x6f, 0xaf, 0xdc, 0xeb, 0xec, 0x2f, 0x59, 0xc6, 0xb6, 0x67, 0x20, + 0x5b, 0x61, 0xc5, 0x3b, 0x88, 0x63, 0x8a, 0x69, 0xc1, 0x0d, 0x06, 0x76, + 0x78, 0x08, 0x76, 0xc8, 0x56, 0x61, 0xf2, 0xb3, 0x09, 0x3e, 0x15, 0xd0, + 0x22, 0x9f, 0x0a, 0x85, 0xca, 0x4a, 0x64, 0xb4, 0xc3, 0xb1, 0xc1, 0x00, + 0xda, 0xde, 0xa6, 0x6d, 0x84, 0xbf, 0xcb, 0x91, 0x2d, 0x6d, 0x64, 0xe2, + 0xe6, 0x96, 0xb1, 0x09, 0x39, 0x56, 0x79, 0x23, 0x6e, 0x54, 0xcf, 0xa4, + 0x7d, 0x38, 0x63, 0x10, 0xd3, 0xa3, 0xdd, 0xf7, 0xc9, 0x48, 0x81, 0x29, + 0x7a, 0x33, 0x56, 0x57, 0x30, 0xb7, 0xdc, 0xc4, 0x2a, 0xb7, 0x95, 0xd1, + 0x58, 0x1f, 0xe2, 0x98, 0x8a, 0x7c, 0xda, 0xd9, 0xb6, 0x82, 0x76, 0xf6, + 0xad, 0x40, 0x3a, 0x0b, 0xfc, 0x52, 0x1a, 0x1b, 0x40, 0x3c, 0xd4, 0xeb, + 0x90, 0x4f, 0xce, 0xac, 0xfb, 0x2a, 0x7c, 0x66, 0xa3, 0xf5, 0x1d, 0x98, + 0x36, 0x1a, 0xeb, 0x2d, 0xc5, 0xde, 0x3f, 0x42, 0xdf, 0xf5, 0x1e, 0xa1, + 0xb1, 0xfe, 0x32, 0xea, 0xa8, 0xc2, 0x09, 0xeb, 0x28, 0x70, 0xdc, 0x4c, + 0x8f, 0xb7, 0xc5, 0xfc, 0xef, 0x42, 0xfc, 0x10, 0xf1, 0x9c, 0x90, 0x37, + 0xde, 0x9d, 0xbd, 0x88, 0xc4, 0x30, 0x93, 0xbe, 0x3f, 0xe0, 0x04, 0xf6, + 0xde, 0x81, 0xa5, 0xcd, 0xa7, 0xf4, 0x73, 0x1c, 0x7e, 0x54, 0x96, 0x7e, + 0x8e, 0x73, 0xb5, 0x28, 0xa5, 0x6b, 0x8c, 0x15, 0x74, 0xb5, 0xa3, 0x2c, + 0xf5, 0x1c, 0x87, 0x68, 0xb9, 0x7e, 0x2e, 0x7d, 0x1d, 0x50, 0x80, 0xd3, + 0xa0, 0x03, 0xeb, 0x71, 0x40, 0xd6, 0xe6, 0x9b, 0x41, 0x09, 0x1d, 0x97, + 0xe7, 0xab, 0x3a, 0x5b, 0x78, 0xb5, 0xb2, 0xf5, 0x53, 0x61, 0x87, 0x3e, + 0x23, 0x2b, 0x1b, 0xd5, 0x73, 0x7c, 0x7e, 0xce, 0x89, 0x12, 0xd8, 0xcf, + 0x3a, 0xb4, 0xed, 0x15, 0xe9, 0x67, 0x9b, 0x9c, 0x2b, 0xa5, 0x3a, 0xd8, + 0xc3, 0x1e, 0x51, 0x4e, 0xeb, 0x70, 0xbb, 0xad, 0x14, 0xbc, 0x6a, 0x6b, + 0x30, 0xde, 0x30, 0x38, 0x65, 0xb4, 0xc7, 0x28, 0xc7, 0x3d, 0x2b, 0x17, + 0x11, 0x68, 0x8d, 0xe1, 0x93, 0xb5, 0x84, 0x7e, 0xb6, 0xce, 0xff, 0x2b, + 0xc8, 0xea, 0xab, 0x45, 0x7f, 0x0f, 0xc3, 0x3a, 0xf5, 0x0d, 0xd9, 0x3e, + 0x46, 0x0f, 0x7d, 0xac, 0x1e, 0xfc, 0xba, 0x87, 0x1a, 0xf4, 0x30, 0x2d, + 0x7b, 0xf0, 0xcb, 0x1e, 0xfc, 0xa9, 0x1e, 0xd4, 0x33, 0x3c, 0xee, 0x63, + 0x45, 0xaa, 0x0f, 0xeb, 0xdb, 0x28, 0xf3, 0xf7, 0x87, 0xf5, 0x4c, 0xaa, + 0xab, 0x51, 0x7d, 0x4f, 0x46, 0xcd, 0x63, 0xfa, 0x5b, 0x39, 0x16, 0xff, + 0x80, 0x7c, 0xcb, 0x9e, 0x47, 0xf7, 0x0a, 0x41, 0x79, 0xc2, 0xe3, 0x7b, + 0x0b, 0xc5, 0x90, 0xa0, 0xeb, 0xf1, 0xf1, 0x10, 0x8e, 0xa3, 0x3c, 0xba, + 0x16, 0x29, 0xe3, 0x1c, 0x75, 0x19, 0xef, 0xbb, 0xf9, 0x06, 0x7a, 0x4e, + 0x4a, 0x79, 0xf3, 0xe9, 0xef, 0x05, 0x74, 0x99, 0xde, 0x2f, 0x3c, 0x05, + 0xdd, 0x33, 0xa7, 0x68, 0x0e, 0xdc, 0x2f, 0x6f, 0x7b, 0x67, 0xe4, 0xd0, + 0xe1, 0xf0, 0xee, 0xdd, 0x13, 0x0f, 0x0d, 0x6f, 0xa7, 0x47, 0x58, 0x90, + 0x3e, 0x6a, 0xe0, 0xe3, 0x8c, 0x7c, 0x3e, 0x42, 0x79, 0xdd, 0x4d, 0x4d, + 0x4d, 0xdd, 0x74, 0x3b, 0x58, 0xdb, 0x8e, 0x5f, 0x3e, 0x72, 0x7d, 0x13, + 0xfd, 0x02, 0x22, 0xdd, 0x74, 0xaf, 0x61, 0x2a, 0xf3, 0x3c, 0x87, 0xec, + 0xb3, 0x23, 0xf4, 0xaa, 0x26, 0x5c, 0xf5, 0xb3, 0x86, 0x43, 0xd7, 0xa4, + 0x5f, 0xa6, 0xeb, 0x7d, 0xc1, 0xe0, 0x7a, 0xdf, 0x57, 0x65, 0xdb, 0x8e, + 0xd3, 0xc7, 0x1d, 0xd0, 0x25, 0x0f, 0x6d, 0x7f, 0xda, 0xc1, 0xac, 0x2d, + 0xdd, 0xf4, 0x2e, 0x3e, 0xd5, 0x7f, 0x8c, 0xcd, 0x3c, 0xda, 0x34, 0x3a, + 0xda, 0x3d, 0xd1, 0xb2, 0xad, 0xa7, 0xa5, 0x9b, 0xde, 0xcb, 0xcc, 0x57, + 0xc1, 0xdc, 0x30, 0x3a, 0x72, 0xf8, 0xc5, 0xfd, 0xf4, 0x5d, 0x87, 0xd2, + 0x6b, 0x37, 0x5a, 0xbf, 0x0b, 0x65, 0x1b, 0x8e, 0x76, 0xd3, 0x7b, 0x32, + 0x41, 0xef, 0x11, 0x85, 0xf9, 0xcf, 0x76, 0xd3, 0x5d, 0x19, 0x3c, 0x56, + 0x9f, 0xe7, 0xe8, 0xe1, 0x9f, 0xd3, 0xe9, 0x4c, 0xa4, 0x37, 0x08, 0x5f, + 0xfe, 0xcf, 0x9b, 0xe8, 0x2b, 0x4e, 0xa4, 0x47, 0x8e, 0x6f, 0x08, 0xb7, + 0x3c, 0x24, 0x7c, 0xbe, 0x47, 0x36, 0xd0, 0xc7, 0x9c, 0x06, 0xdf, 0x8d, + 0x3e, 0x05, 0x7e, 0x17, 0x9d, 0x37, 0xa5, 0x5a, 0xc7, 0x6f, 0x3c, 0xda, + 0xfc, 0xce, 0x77, 0x1f, 0x7f, 0xe4, 0xf8, 0xbd, 0x0f, 0xb7, 0xd1, 0x43, + 0xb2, 0xa5, 0xa5, 0x9e, 0xa3, 0xf4, 0x8e, 0x4c, 0x56, 0x15, 0xed, 0xd3, + 0x67, 0xb8, 0x95, 0x0d, 0xd7, 0x1f, 0x6a, 0x3e, 0xd5, 0x1c, 0x6e, 0xa6, + 0x1f, 0xb8, 0x90, 0x3b, 0xbc, 0xe1, 0xee, 0x63, 0x0f, 0xef, 0x1f, 0x19, + 0x1d, 0x09, 0x3f, 0x7b, 0x82, 0x7e, 0x69, 0x72, 0x79, 0xb8, 0x7c, 0x42, + 0x14, 0x7a, 0x0e, 0xfd, 0x6b, 0xd8, 0x98, 0xfb, 0x5b, 0xfa, 0x99, 0x89, + 0x5e, 0x7e, 0xc4, 0x1f, 0xe7, 0x59, 0x7c, 0xe2, 0xd4, 0x43, 0xe1, 0x4f, + 0x1f, 0xa4, 0x07, 0x58, 0xd0, 0xb8, 0xdd, 0xec, 0x3a, 0x74, 0xe8, 0xb0, + 0xf1, 0xa7, 0xb7, 0xdd, 0x30, 0x72, 0x76, 0xdb, 0x68, 0xf8, 0x7a, 0xfa, + 0x1c, 0x46, 0x34, 0xb1, 0x61, 0x42, 0x2c, 0xcd, 0xff, 0x5e, 0x53, 0x39, + 0x3e, 0x31, 0x90, 0x66, 0x60, 0xf4, 0xd0, 0x5b, 0x8f, 0x4f, 0x18, 0x33, + 0x57, 0xd3, 0x53, 0xac, 0xd3, 0x71, 0x3a, 0xc7, 0x64, 0xc3, 0xc8, 0xcc, + 0xb3, 0xc7, 0x7f, 0x31, 0x72, 0xec, 0xe8, 0xdb, 0x1f, 0x99, 0xe9, 0xbe, + 0xbc, 0xa7, 0x6d, 0xf7, 0xdd, 0xc2, 0xe3, 0xa1, 0x8f, 0x72, 0x27, 0x1b, + 0x8e, 0x3d, 0x7b, 0xec, 0xc2, 0xa9, 0x87, 0xb6, 0x8f, 0xfc, 0xa2, 0x1b, + 0x65, 0x7d, 0x3d, 0xf4, 0x45, 0x59, 0xe1, 0xd1, 0x91, 0x0f, 0x61, 0x3c, + 0xc7, 0x44, 0x61, 0x51, 0xf7, 0xc6, 0x89, 0xf2, 0x2b, 0xe9, 0x2c, 0x8f, + 0x86, 0x9e, 0xe0, 0x32, 0xfa, 0x35, 0xa7, 0xdf, 0xba, 0xe1, 0xd8, 0x23, + 0xdd, 0x1b, 0xaf, 0xd9, 0x3f, 0xd2, 0x7d, 0xcf, 0xfe, 0x91, 0xb7, 0x1d, + 0xdf, 0x48, 0xa7, 0x1d, 0x38, 0xef, 0x3f, 0xdb, 0x44, 0x77, 0x60, 0x01, + 0x44, 0x41, 0x3e, 0xbd, 0xc3, 0x91, 0x5a, 0xda, 0x9d, 0x90, 0x0f, 0x1f, + 0xa6, 0x2f, 0xf0, 0x1c, 0xbf, 0xeb, 0xad, 0x1b, 0x46, 0x0e, 0x95, 0x6f, + 0xa6, 0x27, 0x90, 0x69, 0x3c, 0xae, 0xd2, 0x97, 0xb3, 0xd9, 0xb2, 0x2d, + 0x17, 0x69, 0xfb, 0xae, 0xd6, 0x94, 0x9f, 0x1b, 0x19, 0x92, 0xb6, 0x2b, + 0x9f, 0x41, 0xea, 0xfc, 0xf0, 0x20, 0xda, 0x75, 0x6a, 0x99, 0x32, 0x4d, + 0x89, 0xac, 0xef, 0x9a, 0x09, 0xfd, 0xd4, 0x50, 0xc8, 0xef, 0xb1, 0x58, + 0x27, 0x8b, 0xf5, 0xae, 0xcb, 0x90, 0x74, 0xb5, 0xcc, 0xd7, 0x6a, 0xfe, + 0x60, 0x4a, 0x2e, 0x57, 0xd7, 0xed, 0x91, 0x9f, 0xaa, 0x8f, 0xde, 0x54, + 0x7d, 0xa1, 0xfb, 0x57, 0x28, 0x92, 0x7b, 0xd4, 0x20, 0xd2, 0xcf, 0x73, + 0xd3, 0xdf, 0x5d, 0x53, 0xbc, 0x0c, 0xcd, 0xcb, 0x90, 0x3c, 0x95, 0x76, + 0xa5, 0xda, 0x72, 0x6b, 0xea, 0xd5, 0xb4, 0x40, 0xcb, 0x14, 0xe8, 0x76, + 0x99, 0x57, 0x48, 0xe9, 0x77, 0x84, 0x86, 0xa4, 0x2b, 0xc8, 0x7a, 0xa7, + 0xa7, 0xee, 0xc0, 0x4b, 0xe4, 0x18, 0x4c, 0x52, 0xef, 0xa1, 0x6b, 0xb5, + 0xdf, 0xe4, 0x77, 0xe2, 0xa6, 0xa6, 0x5e, 0xed, 0x6f, 0xbb, 0x74, 0x9d, + 0x2e, 0x9c, 0x02, 0x6e, 0xdd, 0xde, 0x46, 0xed, 0x43, 0x7a, 0x74, 0x59, + 0xaf, 0xd6, 0xdf, 0x4c, 0xa5, 0xe5, 0xa8, 0x6b, 0xc9, 0x51, 0x3b, 0x5c, + 0x47, 0xa2, 0x8e, 0xaa, 0x5b, 0xdb, 0xfb, 0x3a, 0x5b, 0x07, 0x3b, 0x7a, + 0x9b, 0x06, 0x07, 0x06, 0x3b, 0x9b, 0xd6, 0xf4, 0xb5, 0xb7, 0x37, 0xf5, + 0x5e, 0xd6, 0xd1, 0xd6, 0xb4, 0xb6, 0x7f, 0xb0, 0x7d, 0xcd, 0x60, 0xff, + 0x9a, 0xfe, 0xcb, 0x5a, 0x5b, 0x29, 0xb3, 0x7b, 0x72, 0x26, 0x1c, 0x09, + 0x27, 0x37, 0x52, 0x46, 0xb7, 0xa2, 0xc6, 0xc6, 0x2e, 0x32, 0x37, 0x76, + 0xd5, 0xed, 0xe2, 0x4f, 0xa4, 0x0b, 0xfb, 0x66, 0xe6, 0x42, 0xc9, 0x68, + 0x34, 0x39, 0xbd, 0x35, 0x3c, 0x15, 0xde, 0x1a, 0x8c, 0x04, 0xf7, 0x85, + 0xe2, 0xb4, 0x71, 0x31, 0xae, 0x3f, 0x14, 0x8f, 0x47, 0xe3, 0xeb, 0xfd, + 0x93, 0xd1, 0xb9, 0x99, 0x29, 0x7f, 0x24, 0x9a, 0xf4, 0xef, 0x0b, 0x25, + 0xfd, 0x29, 0x49, 0xff, 0xe8, 0xa0, 0x3f, 0x31, 0x19, 0x8c, 0x44, 0x50, + 0xbf, 0xff, 0xcd, 0xd7, 0x9f, 0x0a, 0xed, 0x0d, 0xce, 0xcd, 0xd8, 0xdb, + 0x09, 0x4e, 0x05, 0x63, 0x49, 0x34, 0x52, 0x32, 0x30, 0x37, 0x3b, 0x7b, + 0x38, 0xc5, 0xdf, 0x14, 0x4c, 0x26, 0xfb, 0x83, 0x33, 0x33, 0x7b, 0x82, + 0x93, 0xfb, 0x49, 0x0c, 0x93, 0x31, 0x3c, 0x4a, 0x8e, 0xe1, 0xd1, 0x51, + 0x2a, 0x1f, 0xde, 0xee, 0x1f, 0x3c, 0x34, 0x19, 0x8a, 0x25, 0xc3, 0xd1, + 0x88, 0xff, 0x86, 0xe9, 0xf0, 0x4c, 0xc8, 0x3f, 0x39, 0x13, 0x4d, 0x84, + 0x23, 0xfb, 0xfc, 0xb1, 0x68, 0x3c, 0x49, 0x2b, 0x87, 0xb7, 0xbf, 0x5e, + 0xf9, 0x2c, 0xd4, 0x83, 0x0a, 0x07, 0xc3, 0x93, 0x21, 0x12, 0x5b, 0xc8, + 0xdc, 0xb2, 0xb3, 0x7f, 0x90, 0xbc, 0x5b, 0xe6, 0x26, 0x43, 0xac, 0xf8, + 0x70, 0x24, 0x36, 0x97, 0xdc, 0xc1, 0x4d, 0xf8, 0x2c, 0xd6, 0xf6, 0xb9, + 0xa4, 0xc5, 0xcb, 0xb1, 0x78, 0x32, 0x57, 0x68, 0xe5, 0xc6, 0xe7, 0x62, + 0xdc, 0x6b, 0xf3, 0xf5, 0xc1, 0x83, 0x41, 0x42, 0x40, 0x68, 0x8c, 0x0e, + 0x93, 0x63, 0x74, 0x58, 0x7e, 0xa0, 0x07, 0x7c, 0xec, 0x06, 0x0f, 0xba, + 0x8f, 0xe2, 0xc3, 0x1c, 0x1d, 0xdd, 0x3d, 0x4a, 0xd5, 0xa3, 0xc1, 0xc8, + 0x54, 0x3c, 0x1a, 0x9e, 0x6a, 0xd9, 0x63, 0x8d, 0xb6, 0x25, 0x35, 0xee, + 0x5e, 0x35, 0x1d, 0x5d, 0x54, 0xf5, 0x46, 0x52, 0x03, 0x72, 0x0c, 0x5d, + 0x54, 0xf1, 0x46, 0x42, 0x3c, 0x85, 0x5d, 0x54, 0x77, 0x29, 0x11, 0x6b, + 0x96, 0xbb, 0xa8, 0xe5, 0x92, 0xa2, 0xd3, 0xc1, 0x78, 0x70, 0x12, 0xea, + 0x85, 0x13, 0xc9, 0xf0, 0x64, 0x17, 0x35, 0x5c, 0xaa, 0xc2, 0x40, 0x28, + 0x31, 0x19, 0x0f, 0xc7, 0x92, 0x51, 0x0c, 0xe8, 0x0d, 0x87, 0xad, 0x8d, + 0x66, 0x71, 0x75, 0x67, 0x42, 0x69, 0xc1, 0xd1, 0xd0, 0xb8, 0xb2, 0xba, + 0xc5, 0x67, 0x08, 0xa2, 0x5c, 0x9e, 0x1e, 0xd3, 0xeb, 0xb4, 0xc7, 0x42, + 0x43, 0xe1, 0x19, 0x0c, 0xa5, 0xba, 0x6f, 0x2e, 0x3c, 0x33, 0xc5, 0xed, + 0x2d, 0x36, 0x99, 0xf3, 0x44, 0xdf, 0x50, 0x64, 0x2c, 0x94, 0x80, 0x61, + 0x2f, 0x3e, 0x27, 0x5a, 0x64, 0x3c, 0x94, 0x4c, 0xc2, 0x0c, 0x13, 0xe9, + 0x2e, 0xdf, 0x60, 0x08, 0x96, 0x70, 0x17, 0x2d, 0x4b, 0x09, 0x4d, 0x46, + 0x23, 0xc9, 0x50, 0x24, 0xd9, 0xd2, 0xcf, 0xf4, 0x10, 0x3a, 0x2b, 0x4f, + 0x15, 0xcd, 0x86, 0xa6, 0xc2, 0xc1, 0x16, 0x36, 0xf0, 0x16, 0x36, 0x4b, + 0xcb, 0x40, 0x1a, 0xdf, 0x58, 0x60, 0x38, 0xb2, 0x37, 0x5a, 0xcd, 0x06, + 0xcd, 0x09, 0xbb, 0x3a, 0xaf, 0x2b, 0xdd, 0x45, 0x2b, 0xdf, 0x58, 0x68, + 0x3c, 0x19, 0x4c, 0xce, 0x41, 0xeb, 0xca, 0xd7, 0x13, 0x4b, 0x6d, 0x33, + 0xbb, 0xc1, 0x2d, 0x90, 0xd1, 0xe6, 0x50, 0xad, 0x9a, 0x4c, 0xaf, 0xe6, + 0x65, 0x97, 0xaa, 0xb0, 0x3d, 0xa2, 0xaa, 0x6c, 0x8f, 0x85, 0x22, 0xa1, + 0xa9, 0x51, 0xd8, 0x69, 0x48, 0xda, 0x8a, 0xff, 0x12, 0x15, 0xdf, 0x60, + 0xec, 0x69, 0x1f, 0x60, 0x5f, 0xff, 0x05, 0x42, 0x63, 0xa1, 0xc9, 0x50, + 0xf8, 0x20, 0xb7, 0x53, 0x92, 0x12, 0x89, 0x26, 0x5a, 0xe4, 0x42, 0x57, + 0xef, 0x1a, 0x1c, 0x1b, 0x1f, 0xde, 0xbe, 0xad, 0x8b, 0x0a, 0xe6, 0x97, + 0x45, 0xa6, 0x66, 0xb0, 0x44, 0x85, 0x76, 0xe6, 0xe6, 0x20, 0x33, 0xd1, + 0x4c, 0x91, 0x9d, 0xbb, 0x23, 0x18, 0x9f, 0x0c, 0xcd, 0xec, 0x9c, 0x0b, + 0x4f, 0x75, 0x91, 0x2f, 0x55, 0x30, 0x97, 0x0c, 0xcf, 0xb4, 0x8c, 0x46, + 0xf7, 0xd9, 0xdb, 0x95, 0xbc, 0x1d, 0xc1, 0x70, 0x7c, 0x51, 0x66, 0x37, + 0xad, 0x19, 0x9d, 0x8c, 0xce, 0xb6, 0xc4, 0x67, 0x13, 0x33, 0x2d, 0xd7, + 0xc3, 0x83, 0xb5, 0x2c, 0x70, 0x63, 0xd5, 0x8b, 0x79, 0xf2, 0x2e, 0x6a, + 0xbb, 0x44, 0xad, 0x8b, 0x3c, 0x68, 0x17, 0xad, 0x7e, 0x93, 0x55, 0xec, + 0xb3, 0xdb, 0xf8, 0x26, 0xeb, 0x28, 0xe9, 0xd1, 0x4b, 0x48, 0xa7, 0x4d, + 0x32, 0x65, 0x4d, 0xaf, 0x7b, 0xc2, 0x74, 0xd1, 0xc0, 0x5f, 0xdd, 0x5a, + 0x9a, 0xc3, 0xc6, 0x16, 0x08, 0x26, 0xf6, 0x5f, 0x7a, 0xa2, 0x2e, 0x6a, + 0xe5, 0xd2, 0x83, 0xb6, 0x06, 0xbc, 0x23, 0x98, 0x9c, 0xe6, 0x0d, 0xff, + 0x86, 0xd2, 0xbc, 0xed, 0xa6, 0x82, 0x33, 0x07, 0xc3, 0xfb, 0x5b, 0xe0, + 0x24, 0xa3, 0xd8, 0x8a, 0x38, 0x04, 0x5b, 0x06, 0x23, 0xfa, 0x00, 0xec, + 0x9f, 0x09, 0x26, 0xb0, 0x35, 0xcb, 0x16, 0x91, 0x19, 0x66, 0x9f, 0xaa, + 0xcb, 0x2b, 0x16, 0x29, 0xdf, 0x1a, 0x9a, 0xdd, 0xa3, 0x05, 0x42, 0x10, + 0x59, 0xb1, 0x88, 0xc8, 0x78, 0x78, 0x5f, 0x04, 0x7b, 0x3f, 0x1e, 0xe2, + 0x4d, 0x70, 0x71, 0x71, 0x60, 0x3a, 0x1e, 0xbd, 0x01, 0x55, 0x97, 0x8c, + 0xf2, 0x59, 0xd9, 0x12, 0x8e, 0xb6, 0xd8, 0x0e, 0xea, 0x2e, 0xf2, 0x2a, + 0xf6, 0x4c, 0x30, 0xb2, 0xaf, 0x45, 0xeb, 0x51, 0x60, 0x63, 0x0d, 0xc3, + 0xe3, 0xc9, 0xf9, 0xf2, 0xd9, 0x98, 0xdb, 0xf7, 0x5c, 0x1f, 0x9a, 0x4c, + 0xce, 0xe7, 0x8d, 0x27, 0xe3, 0x18, 0x69, 0xaa, 0x1b, 0xc9, 0x93, 0x5d, + 0x07, 0xf7, 0xf0, 0x6e, 0x5b, 0x61, 0x63, 0xc7, 0x43, 0x7b, 0x5b, 0xae, + 0x0c, 0x05, 0xf7, 0x8f, 0x85, 0xf6, 0x86, 0xe2, 0xa1, 0xc8, 0xe4, 0xa5, + 0x8a, 0xbb, 0xad, 0x46, 0xe5, 0x86, 0xea, 0x8d, 0xc7, 0x83, 0x87, 0xd9, + 0xc3, 0x74, 0x2d, 0xce, 0xee, 0xb6, 0xd4, 0x4a, 0xb3, 0xd3, 0x63, 0x92, + 0xbc, 0xcd, 0xc1, 0x04, 0x0e, 0xbe, 0xd8, 0xa2, 0xcc, 0xee, 0x8b, 0x98, + 0x38, 0x10, 0x2e, 0x96, 0x04, 0xb3, 0x1b, 0x1e, 0x24, 0xcd, 0x1c, 0xc6, + 0x39, 0x15, 0x94, 0xe7, 0xad, 0xc7, 0xc6, 0x55, 0x6a, 0x2e, 0xe4, 0x74, + 0x53, 0xbe, 0x8d, 0x23, 0xdb, 0xf7, 0xda, 0x18, 0x81, 0xf0, 0x2c, 0x4f, + 0xf8, 0x92, 0x85, 0x2c, 0x65, 0xea, 0xde, 0x8b, 0x6c, 0x99, 0x7a, 0x2f, + 0x62, 0x2d, 0x1e, 0x04, 0xda, 0xa3, 0xc4, 0xc4, 0x61, 0xb8, 0xe8, 0x59, + 0x7f, 0x22, 0x14, 0x97, 0x51, 0x99, 0xef, 0xe2, 0x5d, 0x45, 0x39, 0xf6, + 0x2d, 0x40, 0xae, 0xf1, 0x81, 0x91, 0x6b, 0x87, 0xb7, 0x05, 0x68, 0xa5, + 0xfd, 0x94, 0x6c, 0xee, 0xef, 0x1d, 0x1d, 0xed, 0xeb, 0xed, 0x1f, 0xb9, + 0x36, 0x70, 0xf5, 0x8e, 0xc1, 0x6b, 0xb7, 0xf6, 0x06, 0xfa, 0x37, 0x5f, + 0x3b, 0xba, 0x7d, 0x3c, 0x40, 0x62, 0x17, 0x19, 0xbb, 0x10, 0x8e, 0xed, + 0x42, 0x00, 0x69, 0xee, 0x1a, 0xde, 0x3d, 0x4c, 0x19, 0xbb, 0xb6, 0x20, + 0x40, 0xdb, 0x02, 0x36, 0xc2, 0xb2, 0x5d, 0x88, 0xd7, 0xcc, 0x5d, 0x1c, + 0xb0, 0x39, 0x77, 0x49, 0x2e, 0x38, 0xf2, 0x83, 0xa5, 0x47, 0x55, 0x21, + 0xd2, 0x4e, 0xfe, 0xdc, 0xa2, 0x08, 0x82, 0xbc, 0x5d, 0xbb, 0x49, 0x20, + 0xae, 0x43, 0x63, 0x06, 0x02, 0x3a, 0x63, 0xa2, 0x8f, 0x2a, 0x27, 0x2e, + 0x1d, 0x3d, 0x34, 0x4d, 0xfc, 0x55, 0xa7, 0x71, 0xf5, 0x9b, 0x10, 0x87, + 0x45, 0x4c, 0x2c, 0xb2, 0x21, 0xe6, 0x31, 0xad, 0x1d, 0xe1, 0x0e, 0x4e, + 0x4e, 0x86, 0x12, 0x89, 0xea, 0x56, 0x5c, 0x15, 0xb2, 0x55, 0x7a, 0x68, + 0x26, 0xb8, 0x2f, 0x41, 0x8e, 0xe0, 0xd4, 0x14, 0x38, 0xaa, 0x2f, 0x19, + 0xde, 0xba, 0x83, 0xb1, 0x98, 0x0e, 0x32, 0x28, 0x23, 0x98, 0x60, 0x63, + 0xa1, 0xac, 0xd4, 0xb8, 0xa8, 0x24, 0x95, 0x1c, 0x1d, 0x94, 0xde, 0x47, + 0xad, 0xde, 0xce, 0x9d, 0xc3, 0x03, 0xe4, 0xd9, 0xb3, 0x20, 0xa6, 0xa3, + 0xc2, 0x3d, 0xf6, 0x43, 0x45, 0xe9, 0x9e, 0xb0, 0xc9, 0x5d, 0xab, 0x23, + 0x72, 0xcf, 0x9e, 0xa4, 0x2e, 0xe4, 0xb3, 0x0a, 0x2a, 0x53, 0xc6, 0x9e, + 0x24, 0x7b, 0x69, 0x72, 0xee, 0xe1, 0x73, 0x94, 0x32, 0x30, 0x97, 0x38, + 0x07, 0xc9, 0x39, 0x39, 0x13, 0x0a, 0xc6, 0x99, 0x44, 0x13, 0x21, 0x72, + 0x21, 0x2a, 0x8a, 0x60, 0xd4, 0x94, 0xad, 0x13, 0xb2, 0x4a, 0x26, 0xc7, + 0x4a, 0xc1, 0x70, 0x24, 0x21, 0xd9, 0x32, 0x35, 0x12, 0x3a, 0x4c, 0x62, + 0x8a, 0x32, 0x55, 0x77, 0xc3, 0x18, 0xf2, 0x54, 0x6a, 0x1e, 0x13, 0xe4, + 0x9e, 0x0a, 0x27, 0xac, 0x96, 0x32, 0x42, 0x07, 0xe6, 0x82, 0x33, 0x09, + 0x6a, 0xde, 0x1b, 0xc4, 0xf5, 0x61, 0xca, 0x9f, 0x8c, 0xfa, 0x27, 0xe3, + 0xa1, 0x60, 0x32, 0xe4, 0xdf, 0x33, 0x37, 0xa3, 0xef, 0x2d, 0xaa, 0xae, + 0x7f, 0x6f, 0x3c, 0x3a, 0x8b, 0x3b, 0xcc, 0x54, 0x1c, 0xb3, 0x49, 0x99, + 0x7b, 0xc3, 0x91, 0xe0, 0x4c, 0xf8, 0xad, 0x21, 0xaa, 0x40, 0x6a, 0x2a, + 0x3d, 0xdc, 0xa1, 0x68, 0xdc, 0x16, 0xe1, 0x2b, 0xe1, 0x72, 0x16, 0xb1, + 0x8c, 0x7c, 0x31, 0x01, 0xe7, 0xde, 0x70, 0x1c, 0xf3, 0xee, 0xe6, 0x2e, + 0xd4, 0x1a, 0x92, 0x03, 0xdb, 0x87, 0xdc, 0xf8, 0xd0, 0xd7, 0x04, 0x9d, + 0x56, 0xf2, 0xd9, 0x9c, 0x9e, 0x99, 0xe1, 0x05, 0x4c, 0x50, 0x09, 0x67, + 0xd4, 0x8a, 0x2e, 0x0c, 0xb2, 0x69, 0x79, 0xba, 0xec, 0xe2, 0x4d, 0xbc, + 0x84, 0x0b, 0x63, 0xb1, 0x99, 0xf0, 0xa4, 0x74, 0xdb, 0x96, 0x15, 0x14, + 0x80, 0x7d, 0x91, 0x86, 0xc5, 0x76, 0xa6, 0x3d, 0xfc, 0x93, 0xad, 0x5c, + 0x1c, 0xb3, 0x53, 0x26, 0xd8, 0xd2, 0xb9, 0x93, 0x17, 0xa9, 0x01, 0x75, + 0x19, 0xb4, 0xc6, 0x52, 0x92, 0x66, 0x2d, 0xbc, 0x0e, 0x51, 0x96, 0x2c, + 0x93, 0x66, 0x92, 0x97, 0x4a, 0xea, 0x85, 0x4b, 0xe5, 0x13, 0xe4, 0x42, + 0x5a, 0x2e, 0x7f, 0x1d, 0x12, 0x9b, 0xe7, 0x66, 0x39, 0x2c, 0xc7, 0x05, + 0x13, 0x9e, 0x5f, 0x4d, 0xe0, 0xa2, 0xd3, 0x0c, 0x51, 0xd8, 0xad, 0x24, + 0x53, 0xb2, 0x05, 0x6e, 0x97, 0x2a, 0x91, 0xe0, 0xd3, 0xf5, 0xa2, 0x89, + 0xda, 0x16, 0x9c, 0x65, 0xe6, 0xf0, 0x40, 0x82, 0x6a, 0x2e, 0x96, 0x91, + 0x21, 0xd0, 0x45, 0x82, 0xb5, 0x17, 0x0b, 0xaa, 0xc0, 0xe7, 0x22, 0xc9, + 0x65, 0x90, 0xe4, 0xe2, 0x85, 0x6a, 0x62, 0x70, 0x4b, 0x75, 0x91, 0xaa, + 0xc3, 0x7b, 0x04, 0xc3, 0xd1, 0x2a, 0x73, 0x0b, 0xd2, 0x00, 0x78, 0xf5, + 0x65, 0x26, 0xd7, 0xca, 0xcc, 0xf1, 0xc1, 0x4d, 0x1e, 0x9d, 0x65, 0x1f, + 0xca, 0xd5, 0x06, 0xe4, 0x5a, 0x28, 0x5b, 0x91, 0xa2, 0xf1, 0x68, 0x2c, + 0x14, 0x4f, 0x86, 0xd1, 0x4f, 0x3e, 0xb2, 0x63, 0xa1, 0xd9, 0x68, 0x32, + 0xa4, 0x67, 0x9c, 0xeb, 0x8e, 0x4b, 0x3f, 0xad, 0x37, 0xba, 0xec, 0x32, + 0x70, 0x38, 0x16, 0xa2, 0xc2, 0x69, 0x19, 0xb0, 0xea, 0xf9, 0xc7, 0xfd, + 0x30, 0xb2, 0x2f, 0x34, 0x45, 0xb9, 0x8a, 0xab, 0x83, 0x62, 0x72, 0x4d, + 0x07, 0x13, 0xdb, 0xd8, 0x88, 0x32, 0x91, 0x98, 0xee, 0x8f, 0x4e, 0x41, + 0xd5, 0x70, 0xa2, 0x5f, 0x6d, 0x36, 0x88, 0xbb, 0xc2, 0x89, 0xc1, 0xd9, + 0x58, 0xf2, 0x30, 0x27, 0xe4, 0xfc, 0x71, 0x71, 0xfa, 0x36, 0x9e, 0x19, + 0xd6, 0x67, 0x1a, 0x65, 0x72, 0xcc, 0xb3, 0x39, 0x8a, 0x4d, 0x91, 0xb1, + 0x3f, 0x74, 0x18, 0xbe, 0x9f, 0x5c, 0xb3, 0xda, 0x6c, 0x4d, 0x76, 0x8e, + 0xe4, 0x9e, 0x4d, 0xcd, 0x0f, 0x79, 0x67, 0x2f, 0xb2, 0xed, 0xec, 0x59, + 0x9b, 0x03, 0x32, 0x23, 0x3c, 0x4d, 0x66, 0x84, 0x15, 0xf3, 0x45, 0x23, + 0x7d, 0xc1, 0xe4, 0xe4, 0x74, 0xfa, 0x22, 0x97, 0xa0, 0x22, 0x18, 0xff, + 0xbc, 0xfb, 0xae, 0x35, 0xba, 0xc2, 0x85, 0x05, 0x6c, 0x63, 0xb4, 0x64, + 0x21, 0xf7, 0xca, 0x38, 0xf4, 0x96, 0xad, 0xa8, 0x81, 0x62, 0x33, 0xf1, + 0xe6, 0x08, 0xa9, 0x66, 0xc8, 0x13, 0x8d, 0xa4, 0x6f, 0xc7, 0xb2, 0x05, + 0xaf, 0x9d, 0xa3, 0x6a, 0xe7, 0x46, 0xf5, 0x7d, 0x06, 0x56, 0x80, 0x9e, + 0xf3, 0xa2, 0xf3, 0xae, 0x37, 0xdc, 0xa7, 0x3d, 0x3f, 0x10, 0x9a, 0x09, + 0x1e, 0x06, 0x3b, 0xdf, 0x62, 0xf3, 0x2a, 0x1e, 0xb4, 0xcb, 0xa9, 0xdd, + 0x69, 0x0d, 0xc4, 0x15, 0x8d, 0x0c, 0xcd, 0xcc, 0x25, 0xa6, 0x29, 0x27, + 0x1a, 0xd9, 0x9a, 0x9c, 0xb3, 0xd8, 0xd0, 0x8c, 0xf5, 0x51, 0x26, 0x30, + 0x96, 0x48, 0x84, 0xa9, 0x98, 0x39, 0x33, 0x61, 0xde, 0x47, 0x52, 0xaf, + 0xfe, 0xe8, 0x6c, 0x0c, 0x7e, 0x10, 0xb2, 0xa8, 0x29, 0x4f, 0x38, 0xe9, + 0x27, 0xad, 0x9c, 0x9a, 0x41, 0xca, 0x40, 0x2e, 0x14, 0x91, 0xf3, 0xa5, + 0xed, 0x26, 0x31, 0xc0, 0x3e, 0x16, 0xd7, 0x24, 0xc8, 0x16, 0xc0, 0xde, + 0x22, 0x0b, 0x9c, 0x07, 0xb9, 0x99, 0xa9, 0xd3, 0xb9, 0x9c, 0x4e, 0x5b, + 0x41, 0x11, 0x67, 0xe7, 0x5d, 0x32, 0xae, 0x0c, 0x27, 0xa7, 0x61, 0xc7, + 0xc5, 0x56, 0x41, 0xfa, 0x2a, 0xa1, 0x4b, 0x7c, 0x56, 0x89, 0x8d, 0x97, + 0xc7, 0x3c, 0xdb, 0x63, 0x9d, 0x2c, 0xce, 0xab, 0x6d, 0x20, 0x93, 0xec, + 0x8c, 0xe1, 0x13, 0xa2, 0x37, 0xb0, 0xbb, 0x2a, 0x88, 0x61, 0x93, 0x2d, + 0xd4, 0xb1, 0x64, 0x11, 0xe6, 0x78, 0x32, 0x14, 0x0b, 0xdc, 0x10, 0xa5, + 0xa2, 0x79, 0x65, 0xe9, 0xcd, 0x4a, 0x59, 0x31, 0x79, 0xac, 0x4f, 0x85, + 0x0e, 0x51, 0x66, 0xcc, 0x0a, 0x65, 0x1c, 0x6c, 0xea, 0x4b, 0xe3, 0xa1, + 0x7d, 0x7c, 0x4d, 0x8d, 0xcf, 0xbf, 0xeb, 0x52, 0x46, 0x5c, 0x2e, 0x1e, + 0xb9, 0x15, 0x95, 0xca, 0x2e, 0x8b, 0xe3, 0x60, 0x0a, 0x25, 0x92, 0x69, + 0x8b, 0xda, 0x11, 0x0f, 0x47, 0xb1, 0x22, 0x87, 0xc9, 0x11, 0x9f, 0x8b, + 0x90, 0xcb, 0x7a, 0x22, 0x97, 0x99, 0x98, 0x9c, 0x0e, 0x4d, 0xe1, 0xac, + 0xa2, 0x8c, 0x44, 0x08, 0xa7, 0xda, 0x14, 0x99, 0x09, 0x5e, 0x8a, 0x12, + 0xfe, 0x54, 0x8f, 0xc1, 0xa6, 0x83, 0x53, 0xfe, 0xe1, 0xed, 0xfe, 0x90, + 0x15, 0x81, 0xa3, 0x4e, 0x48, 0x1d, 0x59, 0x94, 0x9f, 0x08, 0xa5, 0x6e, + 0x46, 0x72, 0x97, 0x67, 0x83, 0xc1, 0x0b, 0xbb, 0x95, 0xb7, 0x6e, 0x1e, + 0x67, 0xf4, 0x81, 0x8f, 0x1b, 0x29, 0x5a, 0xe6, 0x03, 0xcf, 0x99, 0x48, + 0x06, 0x79, 0x3a, 0x25, 0x61, 0x59, 0xca, 0x55, 0xc9, 0x64, 0x34, 0x26, + 0xb3, 0x66, 0x02, 0x29, 0x74, 0x62, 0xe5, 0x33, 0x92, 0xd3, 0x61, 0xc4, + 0x22, 0xe4, 0x4a, 0x46, 0x65, 0x6c, 0x4c, 0x99, 0xc9, 0xa8, 0x3e, 0xe5, + 0x96, 0xcc, 0x45, 0x16, 0x9b, 0xf8, 0x65, 0x0b, 0xd8, 0xb6, 0xe9, 0x2d, + 0x9e, 0x8b, 0xbc, 0xce, 0x34, 0x3a, 0x0f, 0x06, 0x21, 0x4f, 0x2e, 0x49, + 0xb6, 0xef, 0xa5, 0xf8, 0x4d, 0x37, 0x0d, 0x74, 0xbe, 0xad, 0x92, 0xcb, + 0x30, 0xdc, 0xca, 0xf5, 0x95, 0x58, 0x92, 0xca, 0xc6, 0x4a, 0xdc, 0xaa, + 0x62, 0xe1, 0x19, 0x79, 0xe2, 0x35, 0xcd, 0x62, 0x94, 0x28, 0x88, 0x87, + 0x10, 0x65, 0x24, 0x42, 0x28, 0x84, 0xd7, 0x6a, 0xc2, 0x7c, 0x4e, 0xee, + 0x4f, 0xcc, 0xcd, 0x26, 0x2a, 0xd7, 0xef, 0x45, 0x68, 0x10, 0x6a, 0xac, + 0x9c, 0x0d, 0x47, 0x9a, 0x82, 0xb1, 0x70, 0xe5, 0xfa, 0xb6, 0xb5, 0x8d, + 0x95, 0xb0, 0xe9, 0x04, 0xea, 0xa2, 0x5a, 0x7b, 0x73, 0x7b, 0xf3, 0xea, + 0xd6, 0x26, 0x84, 0x07, 0x0d, 0xc1, 0x68, 0x22, 0xd6, 0x51, 0xf9, 0x76, + 0x32, 0x9a, 0xc4, 0x29, 0xa3, 0xd4, 0x2c, 0x12, 0x45, 0xd7, 0x15, 0x35, + 0x16, 0x65, 0xaa, 0xac, 0xa3, 0xe8, 0x2d, 0x45, 0xe3, 0x45, 0x2e, 0xa3, + 0x1e, 0x99, 0xe2, 0x3a, 0xa3, 0xc1, 0xb8, 0x5d, 0x98, 0x59, 0xdf, 0x17, + 0x45, 0x6e, 0x99, 0xac, 0xce, 0xca, 0x40, 0xba, 0x48, 0x95, 0xd6, 0xa8, + 0x52, 0x03, 0x9c, 0xdc, 0x74, 0x72, 0xaf, 0x6a, 0xc7, 0x85, 0x66, 0xc7, + 0x8b, 0x1c, 0x45, 0x3d, 0x45, 0xad, 0x45, 0x55, 0x68, 0x4f, 0x32, 0x9d, + 0x45, 0x46, 0x8a, 0xd1, 0xcc, 0x0c, 0x51, 0xbc, 0xc6, 0xd2, 0xc2, 0x28, + 0xba, 0xaa, 0x68, 0xb3, 0x25, 0x68, 0x16, 0x4d, 0xa0, 0x76, 0x95, 0x3d, + 0x7b, 0x45, 0xd1, 0x50, 0xba, 0x99, 0xab, 0x74, 0x33, 0x59, 0x16, 0x63, + 0x02, 0x75, 0x77, 0x15, 0x0d, 0x80, 0x91, 0xd2, 0x78, 0xca, 0x68, 0xe4, + 0x32, 0xa3, 0x78, 0x55, 0xf1, 0x4a, 0xc5, 0x75, 0x83, 0xbb, 0x4f, 0x71, + 0x33, 0x8a, 0x6b, 0x8a, 0x2b, 0x8a, 0x6b, 0x8b, 0xab, 0x8a, 0x2b, 0x8b, + 0xab, 0xd3, 0x75, 0x96, 0xa4, 0x93, 0x4b, 0x85, 0x43, 0x64, 0x99, 0x25, + 0x0e, 0xc3, 0x30, 0x84, 0xb1, 0xe6, 0xc8, 0x11, 0xf3, 0xd1, 0x95, 0x1d, + 0xe2, 0xbe, 0x6a, 0x21, 0x9e, 0x03, 0xce, 0x03, 0xcf, 0xaf, 0x14, 0xe2, + 0xe4, 0x2a, 0x21, 0x1e, 0x04, 0xbe, 0x5d, 0x25, 0xc4, 0x7d, 0x35, 0x42, + 0xbc, 0xc0, 0x8f, 0xdf, 0x33, 0xae, 0xb8, 0x25, 0x93, 0xc4, 0x22, 0x20, + 0x43, 0xb8, 0xf3, 0x0d, 0x71, 0xd2, 0x7f, 0xe5, 0x2d, 0x47, 0xcc, 0x17, + 0x9a, 0xaf, 0x12, 0x47, 0x5a, 0x84, 0xb8, 0x13, 0x78, 0x14, 0x78, 0x1a, + 0x38, 0x07, 0xdc, 0xde, 0x2a, 0xc4, 0x3d, 0xc0, 0x63, 0xc0, 0x33, 0xc0, + 0xf3, 0xad, 0x64, 0x0a, 0xa7, 0x17, 0x4a, 0x08, 0xae, 0xba, 0x07, 0x55, + 0x1f, 0x6c, 0x9f, 0x14, 0x47, 0xda, 0xd0, 0xf3, 0x6a, 0x21, 0x5e, 0x82, + 0xc8, 0x6b, 0x48, 0x7f, 0xbd, 0x9d, 0x5c, 0x2e, 0xdf, 0x12, 0x25, 0xa6, + 0xff, 0x4f, 0x43, 0xf6, 0x99, 0x5e, 0xc3, 0xb8, 0xb3, 0x43, 0x18, 0xdf, + 0x5e, 0xef, 0x30, 0x4e, 0xae, 0x01, 0xdd, 0xe8, 0x30, 0x4e, 0xaf, 0x75, + 0x1b, 0xe7, 0x3b, 0xc3, 0xe6, 0x4b, 0x7d, 0x0e, 0xf1, 0x74, 0x0f, 0xba, + 0xec, 0x76, 0x88, 0xdb, 0x41, 0xef, 0xeb, 0x31, 0xc4, 0xf9, 0x6e, 0x21, + 0x9e, 0xea, 0x82, 0x3a, 0x83, 0x42, 0xbc, 0x02, 0x3c, 0xbe, 0x19, 0x2a, + 0x6c, 0x41, 0x7e, 0x14, 0xe8, 0x14, 0xe2, 0x4c, 0xa7, 0x21, 0xee, 0xbc, + 0x0c, 0x23, 0x46, 0xfe, 0x1c, 0x70, 0x62, 0x2b, 0x39, 0x04, 0x16, 0x92, + 0xff, 0xdd, 0x22, 0xd0, 0xe1, 0xa3, 0x3b, 0x6e, 0xc5, 0xfc, 0x6c, 0x43, + 0xad, 0xed, 0x06, 0xb9, 0xa8, 0xdc, 0x2d, 0xdc, 0xb7, 0x8b, 0x63, 0x47, + 0xcc, 0x9f, 0x8c, 0x71, 0xe9, 0xb9, 0x31, 0x71, 0xcc, 0x7f, 0x6c, 0x5c, + 0x64, 0x9d, 0xb8, 0x42, 0x64, 0x9d, 0xbf, 0x42, 0x64, 0x7e, 0x7d, 0x87, + 0x78, 0xa7, 0x41, 0xe4, 0x34, 0xb2, 0xac, 0x6f, 0x08, 0x0b, 0xda, 0x08, + 0xc1, 0xa7, 0xeb, 0xcc, 0x5b, 0x0d, 0xba, 0x5c, 0xbc, 0x52, 0x27, 0xcc, + 0x67, 0xeb, 0x31, 0x25, 0x75, 0x86, 0xb8, 0xbd, 0x4e, 0x88, 0xb3, 0x48, + 0x9f, 0x07, 0x1e, 0x6c, 0x80, 0x9e, 0xc0, 0xb9, 0x06, 0xf4, 0xef, 0xca, + 0x91, 0xf5, 0x86, 0x79, 0x5a, 0x1b, 0xb7, 0x88, 0xdb, 0x9b, 0x84, 0xf9, + 0x93, 0x26, 0xe8, 0xdb, 0x88, 0xa9, 0x05, 0x5e, 0x43, 0xfa, 0xa9, 0x66, + 0xcc, 0x51, 0xb3, 0x28, 0xb2, 0xde, 0xc9, 0xf0, 0x9b, 0x8c, 0x07, 0xfa, + 0x15, 0x7d, 0xb2, 0x5f, 0xbd, 0xff, 0xf8, 0x02, 0xe8, 0x97, 0x6d, 0xe9, + 0xef, 0xe8, 0xf4, 0x4f, 0x41, 0xcf, 0xda, 0xd2, 0xbf, 0xd7, 0xf5, 0x5e, + 0xd5, 0x34, 0x7f, 0x40, 0x51, 0xbf, 0xa6, 0x75, 0x9a, 0x76, 0x6a, 0xba, + 0x49, 0xd3, 0x5d, 0x9a, 0xee, 0x1b, 0x50, 0xef, 0x57, 0xb8, 0x8f, 0x43, + 0x48, 0xdf, 0x36, 0x90, 0xee, 0xf3, 0x3d, 0x5a, 0xe6, 0x03, 0x36, 0xde, + 0x63, 0xb6, 0xf4, 0xe7, 0x90, 0x7e, 0x7c, 0x28, 0xfd, 0xfe, 0xc9, 0x7a, + 0x2f, 0xf5, 0xe8, 0x90, 0xfa, 0xdb, 0x11, 0xf7, 0x81, 0x3e, 0x6d, 0xfd, + 0x00, 0x5b, 0xff, 0x7b, 0x6e, 0x41, 0xfe, 0xf9, 0x05, 0x79, 0xff, 0x26, + 0xf5, 0x8e, 0xc7, 0x9a, 0x1b, 0x7e, 0xbf, 0xc4, 0x7f, 0x50, 0x42, 0xfe, + 0xad, 0x84, 0x4d, 0xea, 0xfd, 0x52, 0xf1, 0x26, 0xf5, 0x1b, 0xee, 0x4c, + 0xd0, 0x30, 0xe8, 0xb9, 0x21, 0xf5, 0x7b, 0xff, 0x17, 0x86, 0xd4, 0xef, + 0xfb, 0xcf, 0x0f, 0xa9, 0xdf, 0x99, 0xbf, 0x04, 0xda, 0xba, 0x69, 0x7e, + 0xfb, 0xdd, 0x0b, 0xf2, 0x03, 0x9b, 0xd2, 0x6b, 0xc0, 0xff, 0x7a, 0x74, + 0x7b, 0x3b, 0x16, 0xf0, 0x47, 0x34, 0xdf, 0x4b, 0xf3, 0xf9, 0x4c, 0xad, + 0xbf, 0x13, 0x64, 0x50, 0xfa, 0x6f, 0x05, 0xf1, 0x9c, 0x5a, 0x7f, 0x2f, + 0x88, 0xc7, 0xc2, 0xdf, 0x51, 0xe0, 0x9f, 0x44, 0xf0, 0xfc, 0x58, 0x7f, + 0x37, 0x88, 0xdf, 0xbe, 0x59, 0x7f, 0x3b, 0x48, 0xf8, 0xd5, 0xdf, 0xc5, + 0xe0, 0xbf, 0x1f, 0xe4, 0xf0, 0xab, 0xef, 0x84, 0xf3, 0xef, 0xdb, 0x84, + 0x47, 0x7d, 0x5f, 0x95, 0x7f, 0x87, 0x67, 0xf8, 0x55, 0x5f, 0xfc, 0xf7, + 0x85, 0x4c, 0xbf, 0x9a, 0x1b, 0xb6, 0x1b, 0xfe, 0x11, 0x1c, 0xb7, 0xc3, + 0xbf, 0x0f, 0x74, 0xfa, 0x95, 0x4e, 0xfc, 0x3b, 0x41, 0x87, 0x47, 0xbd, + 0xa7, 0x3b, 0x8d, 0x74, 0x86, 0x96, 0xe1, 0xdf, 0x11, 0xf2, 0xcb, 0x4a, + 0x96, 0xe1, 0xbf, 0x71, 0xf4, 0x7f, 0x27, 0xec, 0x58, 0xd2, 0x1c, 0x49, + 0x00, 0x00 +}; + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + METHOD (getJuceAndroidMidiInputDeviceNameAndIDs, "getJuceAndroidMidiInputDeviceNameAndIDs", "()[Ljava/lang/String;") \ + METHOD (getJuceAndroidMidiOutputDeviceNameAndIDs, "getJuceAndroidMidiOutputDeviceNameAndIDs", "()[Ljava/lang/String;") \ + METHOD (openMidiInputPortWithID, "openMidiInputPortWithID", "(IJ)Lcom/rmsl/juce/JuceMidiSupport$JuceMidiPort;") \ + METHOD (openMidiOutputPortWithID, "openMidiOutputPortWithID", "(I)Lcom/rmsl/juce/JuceMidiSupport$JuceMidiPort;") + +DECLARE_JNI_CLASS_WITH_MIN_SDK (MidiDeviceManager, "com/rmsl/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 (getName, "getName", "()Ljava/lang/String;") + +DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiPort, "com/rmsl/juce/JuceMidiSupport$JuceMidiPort", 23) +#undef JNI_CLASS_MEMBERS + +//============================================================================== +class MidiInput::Pimpl +{ +public: + Pimpl (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))) + { + } + + ~Pimpl() + { + if (jobject d = javaMidiDevice.get()) + { + getEnv()->CallVoidMethod (d, JuceMidiPort.close); + javaMidiDevice.clear(); + } + } + + bool isOpen() const noexcept + { + return javaMidiDevice != nullptr; + } + + void start() + { + if (jobject d = javaMidiDevice.get()) + getEnv()->CallVoidMethod (d, JuceMidiPort.start); + } + + void stop() + { + if (jobject d = javaMidiDevice.get()) + getEnv()->CallVoidMethod (d, JuceMidiPort.stop); + + callback = nullptr; + } + + String getName() const noexcept + { + if (jobject d = javaMidiDevice.get()) + return juceString (LocalRef ((jstring) getEnv()->CallObjectMethod (d, JuceMidiPort.getName))); + + return {}; + } + + static void handleReceive (JNIEnv* env, Pimpl& myself, jbyteArray byteArray, jint offset, jint len, jlong timestamp) + { + jassert (byteArray != nullptr); + auto* data = env->GetByteArrayElements (byteArray, nullptr); + + std::vector buffer (static_cast (len)); + std::memcpy (buffer.data(), data + offset, static_cast (len)); + + myself.midiConcatenator.pushMidiData (buffer.data(), + len, + static_cast (timestamp) * 1.0e-9, + myself.juceMidiInput, + *myself.callback); + + env->ReleaseByteArrayElements (byteArray, data, 0); + } + +private: + MidiInput* juceMidiInput; + MidiInputCallback* callback; + MidiDataConcatenator midiConcatenator; + GlobalRef javaMidiDevice; +}; + +//============================================================================== +class MidiOutput::Pimpl +{ +public: + Pimpl (const LocalRef& midiDevice) + : javaMidiDevice (midiDevice) + { + } + + ~Pimpl() + { + if (jobject d = javaMidiDevice.get()) + { + getEnv()->CallVoidMethod (d, JuceMidiPort.close); + javaMidiDevice.clear(); + } + } + + void send (jbyteArray byteArray, jint offset, jint len) + { + if (jobject d = javaMidiDevice.get()) + getEnv()->CallVoidMethod (d, + JuceMidiPort.sendMidi, + 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; +}; + +//============================================================================== +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + CALLBACK (generatedCallback<&MidiInput::Pimpl::handleReceive>, "handleReceive", "(J[BIIJ)V" ) + +DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiInputPort, "com/rmsl/juce/JuceMidiSupport$JuceMidiInputPort", 23) +#undef JNI_CLASS_MEMBERS + +//============================================================================== +class AndroidMidiDeviceManager +{ +public: + AndroidMidiDeviceManager() = default; + + Array getDevices (bool input) + { + if (jobject dm = deviceManager.get()) + { + 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 {}; + } + + MidiInput::Pimpl* openMidiInputPortWithID (int deviceID, MidiInput* juceMidiInput, juce::MidiInputCallback* callback) + { + if (auto dm = deviceManager.get()) + { + auto androidMidiInput = std::make_unique (juceMidiInput, deviceID, callback, dm); + + if (androidMidiInput->isOpen()) + return androidMidiInput.release(); + + // Perhaps the device is already open + jassertfalse; + } + + return nullptr; + } + + MidiOutput::Pimpl* openMidiOutputPortWithID (int deviceID) + { + if (auto dm = deviceManager.get()) + { + if (auto javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithID, (jint) deviceID)) + return new MidiOutput::Pimpl (LocalRef (javaMidiPort)); + + // Perhaps the port is already open + jassertfalse; + } + + return nullptr; + } + +private: + static void handleDevicesChanged (JNIEnv*, jclass) + { + MidiDeviceListConnectionBroadcaster::get().notify(); + } + + #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + CALLBACK (handleDevicesChanged, "handleDevicesChanged", "()V" ) \ + STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$MidiDeviceManager;") \ + STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$BluetoothMidiManager;") + + DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/rmsl/juce/JuceMidiSupport", 23, javaMidiByteCode) + #undef JNI_CLASS_MEMBERS + + GlobalRef deviceManager { LocalRef (getEnv()->CallStaticObjectMethod (JuceMidiSupport, + JuceMidiSupport.getAndroidMidiDeviceManager, + getAppContext().get())) }; +}; + +//============================================================================== +Array MidiInput::getAvailableDevices() +{ + 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)) + { + midiInput->internal.reset (port); + midiInput->setName (port->getName()); + + return midiInput; + } + + return {}; +} + +StringArray MidiInput::getDevices() +{ + if (getAndroidSDKVersion() < 23) + return {}; + + StringArray deviceNames; + + for (auto& d : getAvailableDevices()) + deviceNames.add (d.name); + + return deviceNames; +} + +int MidiInput::getDefaultDeviceIndex() +{ + 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() = default; + +void MidiInput::start() +{ + if (auto* mi = internal.get()) + mi->start(); +} + +void MidiInput::stop() +{ + if (auto* mi = internal.get()) + 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; + + if (auto* port = manager.openMidiOutputPortWithID (deviceIdentifier.getIntValue())) + { + std::unique_ptr midiOutput (new MidiOutput ({}, deviceIdentifier)); + midiOutput->internal.reset (port); + midiOutput->setName (port->getName()); + + return midiOutput; + } + + return {}; +} + +StringArray MidiOutput::getDevices() +{ + if (getAndroidSDKVersion() < 23) + return {}; + + 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() +{ + stopBackgroundThread(); +} + +void MidiOutput::sendMessageNow (const MidiMessage& message) +{ + if (auto* androidMidi = internal.get()) + { + auto* env = getEnv(); + auto messageSize = message.getRawDataSize(); + + LocalRef messageContent (env->NewByteArray (messageSize)); + auto content = messageContent.get(); + + auto* rawBytes = env->GetByteArrayElements (content, nullptr); + std::memcpy (rawBytes, message.getRawData(), static_cast (messageSize)); + env->ReleaseByteArrayElements (content, rawBytes, 0); + + androidMidi->send (content, (jint) 0, (jint) messageSize); + } +} + +MidiDeviceListConnection MidiDeviceListConnection::make (std::function callback) +{ + auto& broadcaster = MidiDeviceListConnectionBroadcaster::get(); + return { &broadcaster, broadcaster.add (std::move (callback)) }; +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_linux.cpp similarity index 61% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_linux.cpp index 99ced6af..e0485028 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_linux.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -26,42 +26,32 @@ namespace juce #if JUCE_ALSA //============================================================================== -namespace +class AlsaClient { - -//============================================================================== -class AlsaClient : public ReferenceCountedObject -{ -public: - AlsaClient() + auto lowerBound (int portId) const { - jassert (instance == nullptr); - - snd_seq_open (&handle, "default", SND_SEQ_OPEN_DUPLEX, 0); - - if (handle != nullptr) - { - snd_seq_nonblock (handle, SND_SEQ_NONBLOCK); - snd_seq_set_client_name (handle, getAlsaMidiName().toRawUTF8()); - clientId = snd_seq_client_id (handle); + const auto comparator = [] (const auto& port, const auto& id) { return port->getPortId() < id; }; + return std::lower_bound (ports.begin(), ports.end(), portId, comparator); + } - // It's good idea to pre-allocate a good number of elements - ports.ensureStorageAllocated (32); - } + auto findPortIterator (int portId) const + { + const auto iter = lowerBound (portId); + return (iter == ports.end() || (*iter)->getPortId() != portId) ? ports.end() : iter; } +public: ~AlsaClient() { - jassert (instance != nullptr); - instance = nullptr; - - if (handle != nullptr) - snd_seq_close (handle); + inputThread.reset(); jassert (activeCallbacks.get() == 0); - if (inputThread) - inputThread->stopThread (3000); + if (handle != nullptr) + { + snd_seq_delete_simple_port (handle, announcementsIn); + snd_seq_close (handle); + } } static String getAlsaMidiName() @@ -76,15 +66,12 @@ class AlsaClient : public ReferenceCountedObject #endif } - using Ptr = ReferenceCountedObjectPtr; - //============================================================================== // represents an input or output port of the supplied AlsaClient struct Port { - Port (AlsaClient& c, bool forInput) noexcept - : client (c), isInput (forInput) - {} + explicit Port (bool forInput) noexcept + : isInput (forInput) {} ~Port() { @@ -95,21 +82,21 @@ class AlsaClient : public ReferenceCountedObject else snd_midi_event_free (midiParser); - snd_seq_delete_simple_port (client.get(), portId); + snd_seq_delete_simple_port (client->get(), portId); } } void connectWith (int sourceClient, int sourcePort) const noexcept { if (isInput) - snd_seq_connect_from (client.get(), portId, sourceClient, sourcePort); + snd_seq_connect_from (client->get(), portId, sourceClient, sourcePort); else - snd_seq_connect_to (client.get(), portId, sourceClient, sourcePort); + snd_seq_connect_to (client->get(), portId, sourceClient, sourcePort); } bool isValid() const noexcept { - return client.get() != nullptr && portId >= 0; + return client->get() != nullptr && portId >= 0; } void setupInput (MidiInput* input, MidiInputCallback* cb) @@ -127,15 +114,7 @@ class AlsaClient : public ReferenceCountedObject void enableCallback (bool enable) { - if (callbackEnabled != enable) - { - callbackEnabled = enable; - - if (enable) - client.registerCallback(); - else - client.unregisterCallback(); - } + callbackEnabled = enable; } bool sendMessageNow (const MidiMessage& message) @@ -153,7 +132,7 @@ class AlsaClient : public ReferenceCountedObject auto numBytes = (long) message.getRawDataSize(); auto* data = message.getRawData(); - auto seqHandle = client.get(); + auto seqHandle = client->get(); bool success = true; while (numBytes > 0) @@ -192,7 +171,7 @@ class AlsaClient : public ReferenceCountedObject void createPort (const String& name, bool enableSubscription) { - if (auto seqHandle = client.get()) + if (auto seqHandle = client->get()) { const unsigned int caps = isInput ? (SND_SEQ_PORT_CAP_WRITE | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_WRITE : 0)) @@ -207,15 +186,21 @@ class AlsaClient : public ReferenceCountedObject void handleIncomingMidiMessage (const MidiMessage& message) const { - callback->handleIncomingMidiMessage (midiInput, message); + if (callbackEnabled) + callback->handleIncomingMidiMessage (midiInput, message); } void handlePartialSysexMessage (const uint8* messageData, int numBytesSoFar, double timeStamp) { - callback->handlePartialSysexMessage (midiInput, messageData, numBytesSoFar, timeStamp); + if (callbackEnabled) + callback->handlePartialSysexMessage (midiInput, messageData, numBytesSoFar, timeStamp); } - AlsaClient& client; + int getPortId() const { return portId; } + const String& getPortName() const { return portName; } + + private: + const std::shared_ptr client = AlsaClient::getInstance(); MidiInputCallback* callback = nullptr; snd_midi_event_t* midiParser = nullptr; @@ -224,45 +209,36 @@ class AlsaClient : public ReferenceCountedObject String portName; int maxEventSize = 4096, portId = -1; - bool callbackEnabled = false, isInput = false; + std::atomic callbackEnabled { false }; + bool isInput = false; }; - static Ptr getInstance() - { - if (instance == nullptr) - instance = new AlsaClient(); - - return instance; - } - - void registerCallback() + static std::shared_ptr getInstance() { - if (inputThread == nullptr) - inputThread.reset (new MidiInputThread (*this)); + static std::weak_ptr ptr; - if (++activeCallbacks == 1) - inputThread->startThread(); - } + if (auto locked = ptr.lock()) + return locked; - void unregisterCallback() - { - jassert (activeCallbacks.get() > 0); - - if (--activeCallbacks == 0 && inputThread->isThreadRunning()) - inputThread->signalThreadShouldExit(); + std::shared_ptr result (new AlsaClient()); + ptr = result; + return result; } void handleIncomingMidiMessage (snd_seq_event* event, const MidiMessage& message) { - if (event->dest.port < ports.size() && ports[event->dest.port]->callbackEnabled) - ports[event->dest.port]->handleIncomingMidiMessage (message); + const ScopedLock sl (callbackLock); + + if (auto* port = findPort (event->dest.port)) + port->handleIncomingMidiMessage (message); } void handlePartialSysexMessage (snd_seq_event* event, const uint8* messageData, int numBytesSoFar, double timeStamp) { - if (event->dest.port < ports.size() - && ports[event->dest.port]->callbackEnabled) - ports[event->dest.port]->handlePartialSysexMessage (messageData, numBytesSoFar, timeStamp); + const ScopedLock sl (callbackLock); + + if (auto* port = findPort (event->dest.port)) + port->handlePartialSysexMessage (messageData, numBytesSoFar, timeStamp); } snd_seq_t* get() const noexcept { return handle; } @@ -270,40 +246,112 @@ class AlsaClient : public ReferenceCountedObject Port* createPort (const String& name, bool forInput, bool enableSubscription) { - auto port = new Port (*this, forInput); + const ScopedLock sl (callbackLock); + + auto port = new Port (forInput); port->createPort (name, enableSubscription); - ports.set (port->portId, port); - incReferenceCount(); + + const auto iter = lowerBound (port->getPortId()); + jassert (iter == ports.end() || port->getPortId() < (*iter)->getPortId()); + ports.insert (iter, rawToUniquePtr (port)); + return port; } void deletePort (Port* port) { - ports.set (port->portId, nullptr); - decReferenceCount(); + const ScopedLock sl (callbackLock); + + if (const auto iter = findPortIterator (port->getPortId()); iter != ports.end()) + ports.erase (iter); } private: + AlsaClient() + { + snd_seq_open (&handle, "default", SND_SEQ_OPEN_DUPLEX, 0); + + if (handle != nullptr) + { + snd_seq_nonblock (handle, SND_SEQ_NONBLOCK); + snd_seq_set_client_name (handle, getAlsaMidiName().toRawUTF8()); + clientId = snd_seq_client_id (handle); + + // It's good idea to pre-allocate a good number of elements + ports.reserve (32); + + announcementsIn = snd_seq_create_simple_port (handle, + TRANS ("announcements").toRawUTF8(), + SND_SEQ_PORT_CAP_WRITE, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); + snd_seq_connect_from (handle, announcementsIn, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE); + + inputThread.emplace (*this); + } + } + + Port* findPort (int portId) + { + if (const auto iter = findPortIterator (portId); iter != ports.end()) + return iter->get(); + + return nullptr; + } + snd_seq_t* handle = nullptr; int clientId = 0; - OwnedArray ports; + int announcementsIn = 0; + std::vector> ports; Atomic activeCallbacks; CriticalSection callbackLock; - static AlsaClient* instance; - //============================================================================== - class MidiInputThread : public Thread + class SequencerThread { public: - MidiInputThread (AlsaClient& c) - : Thread ("JUCE MIDI Input"), client (c) + explicit SequencerThread (AlsaClient& c) + : client (c) { - jassert (client.get() != nullptr); } - void run() override + ~SequencerThread() noexcept { + shouldStop = true; + thread.join(); + } + + private: + // If we directly call MidiDeviceListConnectionBroadcaster::get() from the background thread, + // there's a possibility that we'll deadlock in the following scenario: + // - The main thread calls MidiDeviceListConnectionBroadcaster::get() for the first time + // (e.g. to register a listener). The static MidiDeviceListConnectionBroadcaster singleton + // begins construction. During the constructor, an AlsaClient is created to iterate midi + // ins/outs. + // - The AlsaClient starts a new SequencerThread. If connections are updated, the + // SequencerThread may call MidiDeviceListConnectionBroadcaster::get().notify() + // while the MidiDeviceListConnectionBroadcaster singleton is still being created. + // - The SequencerThread blocks until the MidiDeviceListConnectionBroadcaster has been + // created on the main thread, but the MidiDeviceListConnectionBroadcaster's constructor + // can't complete until the AlsaClient's destructor has run, which in turn requires the + // SequencerThread to join. + class UpdateNotifier final : private AsyncUpdater + { + public: + ~UpdateNotifier() override { cancelPendingUpdate(); } + using AsyncUpdater::triggerAsyncUpdate; + + private: + void handleAsyncUpdate() override { MidiDeviceListConnectionBroadcaster::get().notify(); } + }; + + AlsaClient& client; + MidiDataConcatenator concatenator { 2048 }; + std::atomic shouldStop { false }; + UpdateNotifier notifier; + std::thread thread { [this] + { + Thread::setCurrentThreadName ("JUCE MIDI Input"); + auto seqHandle = client.get(); const int maxEventSize = 16 * 1024; @@ -311,17 +359,20 @@ class AlsaClient : public ReferenceCountedObject if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) { - auto numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); - HeapBlock pfd (numPfds); - snd_seq_poll_descriptors (seqHandle, pfd, (unsigned int) numPfds, POLLIN); + const ScopeGuard freeMidiEvent { [&] { snd_midi_event_free (midiParser); } }; + + const auto numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); + std::vector pfd (static_cast (numPfds)); + snd_seq_poll_descriptors (seqHandle, pfd.data(), (unsigned int) numPfds, POLLIN); - HeapBlock buffer (maxEventSize); + std::vector buffer (maxEventSize); - while (! threadShouldExit()) + while (! shouldStop) { - if (poll (pfd, (nfds_t) numPfds, 100) > 0) // there was a "500" here which is a bit long when we exit the program and have to wait for a timeout on this poll call + // This timeout shouldn't be too long, so that the program can exit in a timely manner + if (poll (pfd.data(), (nfds_t) numPfds, 100) > 0) { - if (threadShouldExit()) + if (shouldStop) break; do @@ -330,44 +381,60 @@ class AlsaClient : public ReferenceCountedObject if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) { + const ScopeGuard freeInputEvent { [&] { snd_seq_free_event (inputEvent); } }; + + constexpr int systemEvents[] + { + SND_SEQ_EVENT_CLIENT_CHANGE, + SND_SEQ_EVENT_CLIENT_START, + SND_SEQ_EVENT_CLIENT_EXIT, + SND_SEQ_EVENT_PORT_CHANGE, + SND_SEQ_EVENT_PORT_START, + SND_SEQ_EVENT_PORT_EXIT, + SND_SEQ_EVENT_PORT_SUBSCRIBED, + SND_SEQ_EVENT_PORT_UNSUBSCRIBED, + }; + + const auto foundEvent = std::find (std::begin (systemEvents), + std::end (systemEvents), + inputEvent->type); + + if (foundEvent != std::end (systemEvents)) + { + notifier.triggerAsyncUpdate(); + continue; + } + // xxx what about SYSEXes that are too big for the buffer? - auto numBytes = snd_midi_event_decode (midiParser, buffer, - maxEventSize, inputEvent); + const auto numBytes = snd_midi_event_decode (midiParser, + buffer.data(), + maxEventSize, + inputEvent); snd_midi_event_reset_decode (midiParser); - concatenator.pushMidiData (buffer, (int) numBytes, + concatenator.pushMidiData (buffer.data(), (int) numBytes, Time::getMillisecondCounter() * 0.001, inputEvent, client); - - snd_seq_free_event (inputEvent); } } while (snd_seq_event_input_pending (seqHandle, 0) > 0); } } - - snd_midi_event_free (midiParser); } - } - - private: - AlsaClient& client; - MidiDataConcatenator concatenator { 2048 }; + } }; }; - std::unique_ptr inputThread; + std::optional inputThread; }; -AlsaClient* AlsaClient::instance = nullptr; - //============================================================================== static String getFormattedPortIdentifier (int clientId, int portId) { return String (clientId) + "-" + String (portId); } -static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client, +static AlsaClient::Port* iterateMidiClient (AlsaClient& client, snd_seq_client_info_t* clientInfo, bool forInput, Array& devices, @@ -375,7 +442,7 @@ static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client, { AlsaClient::Port* port = nullptr; - auto seqHandle = client->get(); + auto seqHandle = client.get(); snd_seq_port_info_t* portInfo = nullptr; snd_seq_port_info_alloca (&portInfo); @@ -402,7 +469,7 @@ static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client, { if (portID != -1) { - port = client->createPort (portName, forInput, false); + port = client.createPort (portName, forInput, false); jassert (port->isValid()); port->connectWith (sourceClient, portID); break; @@ -440,8 +507,11 @@ static AlsaClient::Port* iterateMidiDevices (bool forInput, { if (snd_seq_query_next_client (seqHandle, clientInfo) == 0) { - port = iterateMidiClient (client, clientInfo, forInput, - devices, deviceIdentifierToOpen); + port = iterateMidiClient (*client, + clientInfo, + forInput, + devices, + deviceIdentifierToOpen); if (port != nullptr) break; @@ -453,9 +523,23 @@ static AlsaClient::Port* iterateMidiDevices (bool forInput, return port; } -} // namespace +struct AlsaPortPtr +{ + explicit AlsaPortPtr (AlsaClient::Port* p) + : ptr (p) {} + + virtual ~AlsaPortPtr() noexcept { AlsaClient::getInstance()->deletePort (ptr); } + + AlsaClient::Port* ptr = nullptr; +}; //============================================================================== +class MidiInput::Pimpl final : public AlsaPortPtr +{ +public: + using AlsaPortPtr::AlsaPortPtr; +}; + Array MidiInput::getAvailableDevices() { Array devices; @@ -482,10 +566,10 @@ std::unique_ptr MidiInput::openDevice (const String& deviceIdentifier jassert (port->isValid()); - std::unique_ptr midiInput (new MidiInput (port->portName, deviceIdentifier)); + std::unique_ptr midiInput (new MidiInput (port->getPortName(), deviceIdentifier)); port->setupInput (midiInput.get(), callback); - midiInput->internal = port; + midiInput->internal = std::make_unique (port); return midiInput; } @@ -498,10 +582,10 @@ std::unique_ptr MidiInput::createNewDevice (const String& deviceName, if (port == nullptr || ! port->isValid()) return {}; - std::unique_ptr midiInput (new MidiInput (deviceName, getFormattedPortIdentifier (client->getId(), port->portId))); + std::unique_ptr midiInput (new MidiInput (deviceName, getFormattedPortIdentifier (client->getId(), port->getPortId()))); port->setupInput (midiInput.get(), callback); - midiInput->internal = port; + midiInput->internal = std::make_unique (port); return midiInput; } @@ -536,20 +620,25 @@ MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier) MidiInput::~MidiInput() { stop(); - AlsaClient::getInstance()->deletePort (static_cast (internal)); } void MidiInput::start() { - static_cast (internal)->enableCallback (true); + internal->ptr->enableCallback (true); } void MidiInput::stop() { - static_cast (internal)->enableCallback (false); + internal->ptr->enableCallback (false); } //============================================================================== +class MidiOutput::Pimpl final : public AlsaPortPtr +{ +public: + using AlsaPortPtr::AlsaPortPtr; +}; + Array MidiOutput::getAvailableDevices() { Array devices; @@ -574,10 +663,10 @@ std::unique_ptr MidiOutput::openDevice (const String& deviceIdentifi if (port == nullptr || ! port->isValid()) return {}; - std::unique_ptr midiOutput (new MidiOutput (port->portName, deviceIdentifier)); + std::unique_ptr midiOutput (new MidiOutput (port->getPortName(), deviceIdentifier)); port->setupOutput(); - midiOutput->internal = port; + midiOutput->internal = std::make_unique (port); return midiOutput; } @@ -590,10 +679,10 @@ std::unique_ptr MidiOutput::createNewDevice (const String& deviceNam if (port == nullptr || ! port->isValid()) return {}; - std::unique_ptr midiOutput (new MidiOutput (deviceName, getFormattedPortIdentifier (client->getId(), port->portId))); + std::unique_ptr midiOutput (new MidiOutput (deviceName, getFormattedPortIdentifier (client->getId(), port->getPortId()))); port->setupOutput(); - midiOutput->internal = port; + midiOutput->internal = std::make_unique (port); return midiOutput; } @@ -623,17 +712,30 @@ std::unique_ptr MidiOutput::openDevice (int index) MidiOutput::~MidiOutput() { stopBackgroundThread(); - AlsaClient::getInstance()->deletePort (static_cast (internal)); } void MidiOutput::sendMessageNow (const MidiMessage& message) { - static_cast (internal)->sendMessageNow (message); + internal->ptr->sendMessageNow (message); +} + +MidiDeviceListConnection MidiDeviceListConnection::make (std::function cb) +{ + auto& broadcaster = MidiDeviceListConnectionBroadcaster::get(); + // We capture the AlsaClient instance here to ensure that it remains alive for at least as long + // as the MidiDeviceListConnection. This is necessary because system change messages will only + // be processed when the AlsaClient's SequencerThread is running. + return { &broadcaster, broadcaster.add ([fn = std::move (cb), client = AlsaClient::getInstance()] + { + NullCheckedInvocation::invoke (fn); + }) }; } //============================================================================== #else +class MidiInput::Pimpl {}; + // (These are just stub functions if ALSA is unavailable...) MidiInput::MidiInput (const String& deviceName, const String& deviceID) : deviceInfo (deviceName, deviceID) @@ -651,6 +753,8 @@ StringArray MidiInput::getDevices() int MidiInput::getDefaultDeviceIndex() { return 0;} std::unique_ptr MidiInput::openDevice (int, MidiInputCallback*) { return {}; } +class MidiOutput::Pimpl {}; + MidiOutput::~MidiOutput() {} void MidiOutput::sendMessageNow (const MidiMessage&) {} Array MidiOutput::getAvailableDevices() { return {}; } @@ -661,6 +765,12 @@ StringArray MidiOutput::getDevices() int MidiOutput::getDefaultDeviceIndex() { return 0;} std::unique_ptr MidiOutput::openDevice (int) { return {}; } +MidiDeviceListConnection MidiDeviceListConnection::make (std::function cb) +{ + auto& broadcaster = MidiDeviceListConnectionBroadcaster::get(); + return { &broadcaster, broadcaster.add (std::move (cb)) }; +} + #endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_windows.cpp similarity index 86% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_windows.cpp index c398268f..aa3648fb 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_windows.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -29,46 +29,81 @@ namespace juce { -struct MidiServiceType +template +class CheckedReference { - struct InputWrapper +public: + template + friend auto createCheckedReference (Ptr*); + + void clear() { - virtual ~InputWrapper() {} + std::lock_guard lock { mutex }; + ptr = nullptr; + } - virtual String getDeviceIdentifier() = 0; - virtual String getDeviceName() = 0; + template + void access (Callback&& callback) + { + std::lock_guard lock { mutex }; + callback (ptr); + } - virtual void start() = 0; - virtual void stop() = 0; - }; +private: + explicit CheckedReference (T* ptrIn) : ptr (ptrIn) {} - struct OutputWrapper - { - virtual ~OutputWrapper() {} + T* ptr; + std::mutex mutex; +}; - virtual String getDeviceIdentifier() = 0; - virtual String getDeviceName() = 0; +template +auto createCheckedReference (Ptr* ptrIn) +{ + return std::shared_ptr> { new CheckedReference (ptrIn) }; +} - virtual void sendMessageNow (const MidiMessage&) = 0; - }; +class MidiInput::Pimpl +{ +public: + virtual ~Pimpl() noexcept = default; + + virtual String getDeviceIdentifier() = 0; + virtual String getDeviceName() = 0; + + virtual void start() = 0; + virtual void stop() = 0; +}; + +class MidiOutput::Pimpl +{ +public: + virtual ~Pimpl() noexcept = default; - MidiServiceType() {} - virtual ~MidiServiceType() {} + virtual String getDeviceIdentifier() = 0; + virtual String getDeviceName() = 0; + + virtual void sendMessageNow (const MidiMessage&) = 0; +}; + +struct MidiServiceType +{ + MidiServiceType() = default; + virtual ~MidiServiceType() noexcept = default; virtual Array getAvailableDevices (bool) = 0; virtual MidiDeviceInfo getDefaultDevice (bool) = 0; - virtual InputWrapper* createInputWrapper (MidiInput&, const String&, MidiInputCallback&) = 0; - virtual OutputWrapper* createOutputWrapper (const String&) = 0; + virtual MidiInput::Pimpl* createInputWrapper (MidiInput&, const String&, MidiInputCallback&) = 0; + virtual MidiOutput::Pimpl* createOutputWrapper (const String&) = 0; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiServiceType) }; //============================================================================== -struct Win32MidiService : public MidiServiceType, - private Timer +struct Win32MidiService final : public MidiServiceType, + private Timer { - Win32MidiService() {} + Win32MidiService() = default; Array getAvailableDevices (bool isInput) override { @@ -82,12 +117,12 @@ struct Win32MidiService : public MidiServiceType, : Win32OutputWrapper::getDefaultDevice(); } - InputWrapper* createInputWrapper (MidiInput& input, const String& deviceIdentifier, MidiInputCallback& callback) override + MidiInput::Pimpl* createInputWrapper (MidiInput& input, const String& deviceIdentifier, MidiInputCallback& callback) override { return new Win32InputWrapper (*this, input, deviceIdentifier, callback); } - OutputWrapper* createOutputWrapper (const String& deviceIdentifier) override + MidiOutput::Pimpl* createOutputWrapper (const String& deviceIdentifier) override { return new Win32OutputWrapper (*this, deviceIdentifier); } @@ -96,7 +131,7 @@ struct Win32MidiService : public MidiServiceType, struct Win32InputWrapper; //============================================================================== - struct MidiInCollector : public ReferenceCountedObject + struct MidiInCollector final : public ReferenceCountedObject { MidiInCollector (Win32MidiService& s, MidiDeviceInfo d) : deviceInfo (d), midiService (s) @@ -107,7 +142,7 @@ struct Win32MidiService : public MidiServiceType, { stop(); - if (deviceHandle != 0) + if (deviceHandle != nullptr) { for (int count = 5; --count >= 0;) { @@ -181,7 +216,7 @@ struct Win32MidiService : public MidiServiceType, void start() { - if (deviceHandle != 0 && ! isStarted.load()) + if (deviceHandle != nullptr && ! isStarted.load()) { activeMidiCollectors.addIfNotAlreadyThere (this); @@ -230,7 +265,7 @@ struct Win32MidiService : public MidiServiceType, } MidiDeviceInfo deviceInfo; - HMIDIIN deviceHandle = 0; + HMIDIIN deviceHandle = nullptr; private: Win32MidiService& midiService; @@ -256,7 +291,7 @@ struct Win32MidiService : public MidiServiceType, struct MidiHeader { - MidiHeader() {} + MidiHeader() = default; void prepare (HMIDIIN device) { @@ -332,9 +367,11 @@ struct Win32MidiService : public MidiServiceType, }; //============================================================================== - template + template struct Win32MidiDeviceQuery { + virtual ~Win32MidiDeviceQuery() = default; + static Array getAvailableDevices() { StringArray deviceNames, deviceIDs; @@ -384,8 +421,8 @@ struct Win32MidiService : public MidiServiceType, } }; - struct Win32InputWrapper : public InputWrapper, - public Win32MidiDeviceQuery + struct Win32InputWrapper final : public MidiInput::Pimpl, + public Win32MidiDeviceQuery { Win32InputWrapper (Win32MidiService& parentService, MidiInput& midiInput, const String& deviceIdentifier, MidiInputCallback& c) : input (midiInput), callback (c) @@ -394,7 +431,7 @@ struct Win32MidiService : public MidiServiceType, collector->addClient (this); } - ~Win32InputWrapper() + ~Win32InputWrapper() override { collector->removeClient (this); } @@ -411,7 +448,7 @@ struct Win32MidiService : public MidiServiceType, if (d.identifier == deviceIdentifier) { - deviceID = i; + deviceID = (UINT) i; deviceName = d.name; break; } @@ -450,7 +487,7 @@ struct Win32MidiService : public MidiServiceType, for (UINT i = 0; i < midiInGetNumDevs(); ++i) { - MIDIINCAPS mc = { 0 }; + MIDIINCAPS mc = {}; if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) devices.add (mc); @@ -482,7 +519,7 @@ struct Win32MidiService : public MidiServiceType, }; //============================================================================== - struct MidiOutHandle : public ReferenceCountedObject + struct MidiOutHandle final : public ReferenceCountedObject { using Ptr = ReferenceCountedObjectPtr; @@ -508,8 +545,8 @@ struct Win32MidiService : public MidiServiceType, }; //============================================================================== - struct Win32OutputWrapper : public OutputWrapper, - public Win32MidiDeviceQuery + struct Win32OutputWrapper final : public MidiOutput::Pimpl, + public Win32MidiDeviceQuery { Win32OutputWrapper (Win32MidiService& p, const String& deviceIdentifier) : parent (p) @@ -524,7 +561,7 @@ struct Win32MidiService : public MidiServiceType, if (d.identifier == deviceIdentifier) { - deviceID = i; + deviceID = (UINT) i; deviceName = d.name; break; } @@ -552,7 +589,7 @@ struct Win32MidiService : public MidiServiceType, for (int i = 4; --i >= 0;) { - HMIDIOUT h = 0; + HMIDIOUT h = nullptr; auto res = midiOutOpen (&h, deviceID, 0, 0, CALLBACK_NULL); if (res == MMSYSERR_NOERROR) @@ -574,7 +611,7 @@ struct Win32MidiService : public MidiServiceType, { if (message.getRawDataSize() > 3 || message.isSysEx()) { - MIDIHDR h = { 0 }; + MIDIHDR h = {}; h.lpData = (char*) message.getRawData(); h.dwBytesRecorded = h.dwBufferLength = (DWORD) message.getRawDataSize(); @@ -606,7 +643,7 @@ struct Win32MidiService : public MidiServiceType, { for (int i = 0; i < 50; ++i) { - if (midiOutShortMsg (han->handle, *(unsigned int*) message.getRawData()) != MIDIERR_NOTREADY) + if (midiOutShortMsg (han->handle, *unalignedPointerCast (message.getRawData())) != MIDIERR_NOTREADY) break; Sleep (1); @@ -625,7 +662,7 @@ struct Win32MidiService : public MidiServiceType, for (UINT i = 0; i < midiOutGetNumDevs(); ++i) { - MIDIOUTCAPS mc = { 0 }; + MIDIOUTCAPS mc = {}; if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) devices.add (mc); @@ -672,7 +709,7 @@ struct Win32MidiService : public MidiServiceType, const ScopedLock sl (activeCollectorLock); for (int i = activeCollectors.size(); --i >= 0;) - if (activeCollectors.getObjectPointer(i)->getReferenceCount() == 1) + if (activeCollectors.getObjectPointer (i)->getReferenceCount() == 1) activeCollectors.remove (i); } @@ -710,7 +747,7 @@ using namespace ABI::Windows::Devices::Enumeration; using namespace ABI::Windows::Storage::Streams; //============================================================================== -struct WinRTMidiService : public MidiServiceType +struct WinRTMidiService final : public MidiServiceType { public: //============================================================================== @@ -763,12 +800,12 @@ struct WinRTMidiService : public MidiServiceType : outputDeviceWatcher->getDefaultDevice(); } - InputWrapper* createInputWrapper (MidiInput& input, const String& deviceIdentifier, MidiInputCallback& callback) override + MidiInput::Pimpl* createInputWrapper (MidiInput& input, const String& deviceIdentifier, MidiInputCallback& callback) override { return new WinRTInputWrapper (*this, input, deviceIdentifier, callback); } - OutputWrapper* createOutputWrapper (const String& deviceIdentifier) override + MidiOutput::Pimpl* createOutputWrapper (const String& deviceIdentifier) override { return new WinRTOutputWrapper (*this, deviceIdentifier); } @@ -780,9 +817,9 @@ struct WinRTMidiService : public MidiServiceType public: virtual ~DeviceCallbackHandler() {}; - virtual HRESULT addDevice (IDeviceInformation*) = 0; - virtual HRESULT removeDevice (IDeviceInformationUpdate*) = 0; - virtual HRESULT updateDevice (IDeviceInformationUpdate*) = 0; + JUCE_COMCALL addDevice (IDeviceInformation*) = 0; + JUCE_COMCALL removeDevice (IDeviceInformationUpdate*) = 0; + JUCE_COMCALL updateDevice (IDeviceInformationUpdate*) = 0; bool attach (HSTRING deviceSelector, DeviceInformationKind infoKind) { @@ -832,7 +869,7 @@ struct WinRTMidiService : public MidiServiceType } } - WinRTWrapper::ComPtr> iter; + ComSmartPtr> iter; auto hr = requestedProperties->QueryInterface (__uuidof (IIterable), (void**) iter.resetAndGetPointerAddress()); if (FAILED (hr)) @@ -889,7 +926,7 @@ struct WinRTMidiService : public MidiServiceType watcher = nullptr; } - template + template IInspectable* getValueFromDeviceInfo (String key, InfoType* info) { __FIMapView_2_HSTRING_IInspectable* properties; @@ -922,7 +959,7 @@ struct WinRTMidiService : public MidiServiceType String getGUIDFromInspectable (IInspectable& inspectable) { - WinRTWrapper::ComPtr> guidRef; + ComSmartPtr> guidRef; auto hr = inspectable.QueryInterface (__uuidof (IReference), (void**) guidRef.resetAndGetPointerAddress()); @@ -949,7 +986,7 @@ struct WinRTMidiService : public MidiServiceType bool getBoolFromInspectable (IInspectable& inspectable) { - WinRTWrapper::ComPtr> boolRef; + ComSmartPtr> boolRef; auto hr = inspectable.QueryInterface (__uuidof (IReference), (void**) boolRef.resetAndGetPointerAddress()); @@ -973,10 +1010,10 @@ struct WinRTMidiService : public MidiServiceType private: //============================================================================== - struct DeviceEnumerationThread : public Thread + struct DeviceEnumerationThread final : public Thread { DeviceEnumerationThread (DeviceCallbackHandler& h, - WinRTWrapper::ComPtr& w, + ComSmartPtr& w, EventRegistrationToken& added, EventRegistrationToken& removed, EventRegistrationToken& updated) @@ -990,19 +1027,19 @@ struct WinRTMidiService : public MidiServiceType watcher->add_Added ( Callback> ( - [handlerPtr](IDeviceWatcher*, IDeviceInformation* info) { return handlerPtr->addDevice (info); } + [handlerPtr] (IDeviceWatcher*, IDeviceInformation* info) { return handlerPtr->addDevice (info); } ).Get(), &deviceAddedToken); watcher->add_Removed ( Callback> ( - [handlerPtr](IDeviceWatcher*, IDeviceInformationUpdate* infoUpdate) { return handlerPtr->removeDevice (infoUpdate); } + [handlerPtr] (IDeviceWatcher*, IDeviceInformationUpdate* infoUpdate) { return handlerPtr->removeDevice (infoUpdate); } ).Get(), &deviceRemovedToken); watcher->add_Updated ( Callback> ( - [handlerPtr](IDeviceWatcher*, IDeviceInformationUpdate* infoUpdate) { return handlerPtr->updateDevice (infoUpdate); } + [handlerPtr] (IDeviceWatcher*, IDeviceInformationUpdate* infoUpdate) { return handlerPtr->updateDevice (infoUpdate); } ).Get(), &deviceUpdatedToken); @@ -1010,12 +1047,12 @@ struct WinRTMidiService : public MidiServiceType } DeviceCallbackHandler& handler; - WinRTWrapper::ComPtr& watcher; + ComSmartPtr& watcher; EventRegistrationToken& deviceAddedToken, deviceRemovedToken, deviceUpdatedToken; }; //============================================================================== - WinRTWrapper::ComPtr watcher; + ComSmartPtr watcher; EventRegistrationToken deviceAddedToken { 0 }, deviceRemovedToken { 0 }, @@ -1028,7 +1065,7 @@ struct WinRTMidiService : public MidiServiceType }; //============================================================================== - struct BLEDeviceWatcher final : private DeviceCallbackHandler + struct BLEDeviceWatcher final : private DeviceCallbackHandler { struct DeviceInfo { @@ -1118,7 +1155,7 @@ struct WinRTMidiService : public MidiServiceType if (devices.contains (removedDeviceId)) { auto& info = devices.getReference (removedDeviceId); - listeners.call ([&info](Listener& l) { l.bleDeviceDisconnected (info.containerID); }); + listeners.call ([&info] (Listener& l) { l.bleDeviceDisconnected (info.containerID); }); devices.remove (removedDeviceId); JUCE_WINRT_MIDI_LOG ("Removed BLE device: " << removedDeviceId); } @@ -1165,7 +1202,7 @@ struct WinRTMidiService : public MidiServiceType if (info.isConnected && ! isConnected) { JUCE_WINRT_MIDI_LOG ("BLE device connection status change: " << updatedDeviceId << " " << info.containerID << " " << (isConnected ? "connected" : "disconnected")); - listeners.call ([&info](Listener& l) { l.bleDeviceDisconnected (info.containerID); }); + listeners.call ([&info] (Listener& l) { l.bleDeviceDisconnected (info.containerID); }); } info.isConnected = isConnected; @@ -1218,9 +1255,9 @@ struct WinRTMidiService : public MidiServiceType //============================================================================== template - struct MidiIODeviceWatcher final : private DeviceCallbackHandler + struct MidiIODeviceWatcher final : private DeviceCallbackHandler { - MidiIODeviceWatcher (WinRTWrapper::ComPtr& comFactory) + MidiIODeviceWatcher (ComSmartPtr& comFactory) : factory (comFactory) { } @@ -1405,7 +1442,7 @@ struct WinRTMidiService : public MidiServiceType return {}; } - WinRTWrapper::ComPtr& factory; + ComSmartPtr& factory; Array connectedDevices; CriticalSection deviceChanges; @@ -1415,63 +1452,60 @@ struct WinRTMidiService : public MidiServiceType }; //============================================================================== - template - struct OpenMidiPortThread : public Thread + template + static void openMidiPortThread (String threadName, + String midiDeviceID, + ComSmartPtr& comFactory, + ComSmartPtr& comPort) { - OpenMidiPortThread (String threadName, String midiDeviceID, - WinRTWrapper::ComPtr& comFactory, - WinRTWrapper::ComPtr& comPort) - : Thread (threadName), - deviceID (midiDeviceID), - factory (comFactory), - port (comPort) + std::thread { [&] { - } + Thread::setCurrentThreadName (threadName); - ~OpenMidiPortThread() - { - stopThread (2000); - } - - void run() override - { - WinRTWrapper::ScopedHString hDeviceId (deviceID); - WinRTWrapper::ComPtr> asyncOp; - auto hr = factory->FromIdAsync (hDeviceId.get(), asyncOp.resetAndGetPointerAddress()); + const WinRTWrapper::ScopedHString hDeviceId { midiDeviceID }; + ComSmartPtr> asyncOp; + const auto hr = comFactory->FromIdAsync (hDeviceId.get(), asyncOp.resetAndGetPointerAddress()); if (FAILED (hr)) return; - hr = asyncOp->put_Completed (Callback> ( - [this] (IAsyncOperation* asyncOpPtr, AsyncStatus) - { - if (asyncOpPtr == nullptr) - return E_ABORT; + std::promise> promise; + auto future = promise.get_future(); + + auto callback = [p = std::move (promise)] (IAsyncOperation* asyncOpPtr, AsyncStatus) mutable + { + if (asyncOpPtr == nullptr) + { + p.set_value (nullptr); + return E_ABORT; + } - auto hr = asyncOpPtr->GetResults (port.resetAndGetPointerAddress()); + ComSmartPtr result; + const auto hr = asyncOpPtr->GetResults (result.resetAndGetPointerAddress()); - if (FAILED (hr)) - return hr; + if (FAILED (hr)) + { + p.set_value (nullptr); + return hr; + } - portOpened.signal(); - return S_OK; - } - ).Get()); + p.set_value (std::move (result)); + return S_OK; + }; - // We need to use a timeout here, rather than waiting indefinitely, as the - // WinRT API can occasionally hang! - portOpened.wait (2000); - } + const auto ir = asyncOp->put_Completed (Callback> (std::move (callback)).Get()); - const String deviceID; - WinRTWrapper::ComPtr& factory; - WinRTWrapper::ComPtr& port; - WaitableEvent portOpened { true }; - }; + if (FAILED (ir)) + return; + + if (future.wait_for (std::chrono::milliseconds (2000)) == std::future_status::ready) + comPort = future.get(); + } }.join(); + } //============================================================================== template - class WinRTIOWrapper : private BLEDeviceWatcher::Listener + class WinRTIOWrapper : private BLEDeviceWatcher::Listener { public: WinRTIOWrapper (BLEDeviceWatcher& bleWatcher, @@ -1550,12 +1584,12 @@ struct WinRTMidiService : public MidiServiceType BLEDeviceWatcher& bleDeviceWatcher; WinRTMIDIDeviceInfo deviceInfo; bool isBLEDevice = false; - WinRTWrapper::ComPtr midiPort; + ComSmartPtr midiPort; }; //============================================================================== - struct WinRTInputWrapper final : public InputWrapper, - private WinRTIOWrapper + struct WinRTInputWrapper final : public MidiInput::Pimpl, + private WinRTIOWrapper { WinRTInputWrapper (WinRTMidiService& service, MidiInput& input, const String& deviceIdentifier, MidiInputCallback& cb) @@ -1563,12 +1597,7 @@ struct WinRTMidiService : public MidiServiceType inputDevice (input), callback (cb) { - OpenMidiPortThread portThread ("Open WinRT MIDI input port", - deviceInfo.deviceID, - service.midiInFactory, - midiPort); - portThread.startThread(); - portThread.waitForThreadToExit (-1); + openMidiPortThread ("Open WinRT MIDI input port", deviceInfo.deviceID, service.midiInFactory, midiPort); if (midiPort == nullptr) { @@ -1580,7 +1609,18 @@ struct WinRTMidiService : public MidiServiceType auto hr = midiPort->add_MessageReceived ( Callback> ( - [this](IMidiInPort*, IMidiMessageReceivedEventArgs* args) { return midiInMessageReceived (args); } + [self = checkedReference] (IMidiInPort*, IMidiMessageReceivedEventArgs* args) + { + HRESULT hr = S_OK; + + self->access ([&hr, args] (auto* ptr) + { + if (ptr != nullptr) + hr = ptr->midiInMessageReceived (args); + }); + + return hr; + } ).Get(), &midiInMessageToken); @@ -1593,6 +1633,7 @@ struct WinRTMidiService : public MidiServiceType ~WinRTInputWrapper() { + checkedReference->clear(); disconnect(); } @@ -1635,19 +1676,19 @@ struct WinRTMidiService : public MidiServiceType if (! isStarted) return S_OK; - WinRTWrapper::ComPtr message; + ComSmartPtr message; auto hr = args->get_Message (message.resetAndGetPointerAddress()); if (FAILED (hr)) return hr; - WinRTWrapper::ComPtr buffer; + ComSmartPtr buffer; hr = message->get_RawData (buffer.resetAndGetPointerAddress()); if (FAILED (hr)) return hr; - WinRTWrapper::ComPtr bufferByteAccess; + ComSmartPtr bufferByteAccess; hr = buffer->QueryInterface (bufferByteAccess.resetAndGetPointerAddress()); if (FAILED (hr)) @@ -1704,22 +1745,19 @@ struct WinRTMidiService : public MidiServiceType double startTime = 0; bool isStarted = false; + std::shared_ptr> checkedReference = createCheckedReference (this); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WinRTInputWrapper); }; //============================================================================== - struct WinRTOutputWrapper final : public OutputWrapper, - private WinRTIOWrapper + struct WinRTOutputWrapper final : public MidiOutput::Pimpl, + private WinRTIOWrapper { WinRTOutputWrapper (WinRTMidiService& service, const String& deviceIdentifier) : WinRTIOWrapper (*service.bleDeviceWatcher, *service.outputDeviceWatcher, deviceIdentifier) { - OpenMidiPortThread portThread ("Open WinRT MIDI output port", - deviceInfo.deviceID, - service.midiOutFactory, - midiPort); - portThread.startThread(); - portThread.waitForThreadToExit (-1); + openMidiPortThread ("Open WinRT MIDI output port", deviceInfo.deviceID, service.midiOutFactory, midiPort); if (midiPort == nullptr) throw std::runtime_error ("Timed out waiting for midi output port creation"); @@ -1773,15 +1811,15 @@ struct WinRTMidiService : public MidiServiceType String getDeviceName() override { return deviceInfo.name; } //============================================================================== - WinRTWrapper::ComPtr buffer; - WinRTWrapper::ComPtr bufferByteAccess; + ComSmartPtr buffer; + ComSmartPtr bufferByteAccess; uint8_t* bufferData = nullptr; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WinRTOutputWrapper); }; - WinRTWrapper::ComPtr midiInFactory; - WinRTWrapper::ComPtr midiOutFactory; + ComSmartPtr midiInFactory; + ComSmartPtr midiOutFactory; std::unique_ptr> inputDeviceWatcher; std::unique_ptr> outputDeviceWatcher; @@ -1798,7 +1836,7 @@ struct WinRTMidiService : public MidiServiceType extern RTL_OSVERSIONINFOW getWindowsVersionInfo(); #endif -struct MidiService : public DeletedAtShutdown +struct MidiService final : public DeletedAtShutdown { MidiService() { @@ -1835,6 +1873,10 @@ struct MidiService : public DeletedAtShutdown private: std::unique_ptr internal; + DeviceChangeDetector detector { L"JuceMidiDeviceDetector_", [] + { + MidiDeviceListConnectionBroadcaster::get().notify(); + } }; }; JUCE_IMPLEMENT_SINGLETON (MidiService) @@ -1865,7 +1907,7 @@ std::unique_ptr MidiInput::openDevice (const String& deviceIdentifier return {}; std::unique_ptr in (new MidiInput ({}, deviceIdentifier)); - std::unique_ptr wrapper; + std::unique_ptr wrapper; try { @@ -1877,7 +1919,7 @@ std::unique_ptr MidiInput::openDevice (const String& deviceIdentifier } in->setName (wrapper->getDeviceName()); - in->internal = wrapper.release(); + in->internal = std::move (wrapper); return in; } @@ -1907,13 +1949,10 @@ MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier) { } -MidiInput::~MidiInput() -{ - delete static_cast (internal); -} +MidiInput::~MidiInput() = default; -void MidiInput::start() { static_cast (internal)->start(); } -void MidiInput::stop() { static_cast (internal)->stop(); } +void MidiInput::start() { internal->start(); } +void MidiInput::stop() { internal->stop(); } //============================================================================== Array MidiOutput::getAvailableDevices() @@ -1931,7 +1970,7 @@ std::unique_ptr MidiOutput::openDevice (const String& deviceIdentifi if (deviceIdentifier.isEmpty()) return {}; - std::unique_ptr wrapper; + std::unique_ptr wrapper; try { @@ -1945,7 +1984,7 @@ std::unique_ptr MidiOutput::openDevice (const String& deviceIdentifi std::unique_ptr out; out.reset (new MidiOutput (wrapper->getDeviceName(), deviceIdentifier)); - out->internal = wrapper.release(); + out->internal = std::move (wrapper); return out; } @@ -1973,12 +2012,17 @@ std::unique_ptr MidiOutput::openDevice (int index) MidiOutput::~MidiOutput() { stopBackgroundThread(); - delete static_cast (internal); } void MidiOutput::sendMessageNow (const MidiMessage& message) { - static_cast (internal)->sendMessageNow (message); + internal->sendMessageNow (message); +} + +MidiDeviceListConnection MidiDeviceListConnection::make (std::function cb) +{ + auto& broadcaster = MidiDeviceListConnectionBroadcaster::get(); + return { &broadcaster, broadcaster.add (std::move (cb)) }; } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Oboe_android.cpp similarity index 76% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_Oboe_android.cpp index ba9f4f70..f2036079 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Oboe_android.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2018 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -35,92 +35,84 @@ namespace juce template struct OboeAudioIODeviceBufferHelpers {}; -template<> +template <> struct OboeAudioIODeviceBufferHelpers { static oboe::AudioFormat oboeAudioFormat() { return oboe::AudioFormat::I16; } static constexpr int bitDepth() { return 16; } - static void referAudioBufferDirectlyToOboeIfPossible (int16*, AudioBuffer&, int) {} + static bool referAudioBufferDirectlyToOboeIfPossible (int16*, AudioBuffer&, int) { return false; } + + using NativeInt16 = AudioData::Format; + using NativeFloat32 = AudioData::Format; static void convertFromOboe (const int16* srcInterleaved, AudioBuffer& audioBuffer, int numSamples) { - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; + const auto numChannels = audioBuffer.getNumChannels(); - DstSampleType dstData (audioBuffer.getWritePointer (i)); - SrcSampleType srcData (srcInterleaved + i, audioBuffer.getNumChannels()); - dstData.convertSamples (srcData, numSamples); - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { reinterpret_cast (srcInterleaved), numChannels }, + AudioData::NonInterleavedDest { audioBuffer.getArrayOfWritePointers(), numChannels }, + numSamples); } static void convertToOboe (const AudioBuffer& audioBuffer, int16* dstInterleaved, int numSamples) { - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; + const auto numChannels = audioBuffer.getNumChannels(); - DstSampleType dstData (dstInterleaved + i, audioBuffer.getNumChannels()); - SrcSampleType srcData (audioBuffer.getReadPointer (i)); - dstData.convertSamples (srcData, numSamples); - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { audioBuffer.getArrayOfReadPointers(), numChannels }, + AudioData::InterleavedDest { reinterpret_cast (dstInterleaved), numChannels }, + numSamples); } }; -template<> +template <> struct OboeAudioIODeviceBufferHelpers { static oboe::AudioFormat oboeAudioFormat() { return oboe::AudioFormat::Float; } static constexpr int bitDepth() { return 32; } - static void referAudioBufferDirectlyToOboeIfPossible (float* nativeBuffer, AudioBuffer& audioBuffer, int numSamples) + static bool referAudioBufferDirectlyToOboeIfPossible (float* nativeBuffer, AudioBuffer& audioBuffer, int numSamples) { if (audioBuffer.getNumChannels() == 1) + { audioBuffer.setDataToReferTo (&nativeBuffer, 1, numSamples); + return true; + } + + return false; } + using Format = AudioData::Format; + static void convertFromOboe (const float* srcInterleaved, AudioBuffer& audioBuffer, int numSamples) { - // No need to convert, we instructed the buffer to point to the src data directly already - if (audioBuffer.getNumChannels() == 1) - { - jassert (audioBuffer.getWritePointer (0) == srcInterleaved); - return; - } + auto numChannels = audioBuffer.getNumChannels(); - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) + if (numChannels > 0) { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; + // No need to convert, we instructed the buffer to point to the src data directly already + jassert (audioBuffer.getWritePointer (0) != srcInterleaved); - DstSampleType dstData (audioBuffer.getWritePointer (i)); - SrcSampleType srcData (srcInterleaved + i, audioBuffer.getNumChannels()); - dstData.convertSamples (srcData, numSamples); + AudioData::deinterleaveSamples (AudioData::InterleavedSource { srcInterleaved, numChannels }, + AudioData::NonInterleavedDest { audioBuffer.getArrayOfWritePointers(), numChannels }, + numSamples); } } static void convertToOboe (const AudioBuffer& audioBuffer, float* dstInterleaved, int numSamples) { - // No need to convert, we instructed the buffer to point to the src data directly already - if (audioBuffer.getNumChannels() == 1) - { - jassert (audioBuffer.getReadPointer (0) == dstInterleaved); - return; - } + auto numChannels = audioBuffer.getNumChannels(); - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) + if (numChannels > 0) { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; + // No need to convert, we instructed the buffer to point to the src data directly already + jassert (audioBuffer.getReadPointer (0) != dstInterleaved); - DstSampleType dstData (dstInterleaved + i, audioBuffer.getNumChannels()); - SrcSampleType srcData (audioBuffer.getReadPointer (i)); - dstData.convertSamples (srcData, numSamples); + AudioData::interleaveSamples (AudioData::NonInterleavedSource { audioBuffer.getArrayOfReadPointers(), numChannels }, + AudioData::InterleavedDest { dstInterleaved, numChannels }, + numSamples); } } }; @@ -132,7 +124,7 @@ static String getOboeString (const Type& value) } //============================================================================== -class OboeAudioIODevice : public AudioIODevice +class OboeAudioIODevice final : public AudioIODevice { public: //============================================================================== @@ -151,11 +143,9 @@ class OboeAudioIODevice : public AudioIODevice supportedOutputSampleRates (supportedOutputSampleRatesToUse), maxNumOutputChannels (maxNumOutputChannelsToUse) { - // At least an input or an output has to be supported by the device! - jassert (inputDeviceId != -1 || outputDeviceId != -1); } - ~OboeAudioIODevice() + ~OboeAudioIODevice() override { close(); } @@ -197,16 +187,7 @@ class OboeAudioIODevice : public AudioIODevice Array getAvailableBufferSizes() override { - // we need to offer the lowest possible buffer size which - // is the native buffer size - const int defaultNumMultiples = 8; - const int nativeBufferSize = getNativeBufferSize(); - Array bufferSizes; - - for (int i = 1; i < defaultNumMultiples; ++i) - bufferSizes.add (i * nativeBufferSize); - - return bufferSizes; + return AndroidHighPerformanceAudioHelpers::getAvailableBufferSizes (getNativeBufferSize(), getAvailableSampleRates()); } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, @@ -215,8 +196,8 @@ class OboeAudioIODevice : public AudioIODevice close(); lastError.clear(); - sampleRate = (int) requestedSampleRate; + sampleRate = (int) (requestedSampleRate > 0 ? requestedSampleRate : AndroidHighPerformanceAudioHelpers::getNativeSampleRate()); actualBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; // The device may report no max, claiming "no limits". Pick sensible defaults. @@ -274,16 +255,12 @@ class OboeAudioIODevice : public AudioIODevice int getDefaultBufferSize() override { - // Only on a Pro-Audio device will we set the lowest possible buffer size - // by default. We need to be more conservative on other devices - // as they may be low-latency, but still have a crappy CPU. - return (isProAudioDevice() ? 1 : 6) - * getNativeBufferSize(); + return AndroidHighPerformanceAudioHelpers::getDefaultBufferSize (getNativeBufferSize(), getCurrentSampleRate()); } double getCurrentSampleRate() override { - return (sampleRate == 0.0 ? getNativeSampleRate() : sampleRate); + return (sampleRate == 0.0 ? AndroidHighPerformanceAudioHelpers::getNativeSampleRate() : sampleRate); } void start (AudioIODeviceCallback* newCallback) override @@ -375,10 +352,11 @@ class OboeAudioIODevice : public AudioIODevice { static const int standardRates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }; + Array rates (standardRates, numElementsInArray (standardRates)); // make sure the native sample rate is part of the list - int native = (int) getNativeSampleRate(); + int native = (int) AndroidHighPerformanceAudioHelpers::getNativeSampleRate(); if (native != 0 && ! rates.contains (native)) rates.add (native); @@ -386,6 +364,39 @@ class OboeAudioIODevice : public AudioIODevice return rates; } + static int getNativeBufferSize() + { + auto bufferSizeHint = AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint(); + + // providing a callback is required on some devices to get a FAST track, so we pass an + // empty one to the temp stream to get the best available buffer size + struct DummyCallback final : public oboe::AudioStreamCallback + { + oboe::DataCallbackResult onAudioReady (oboe::AudioStream*, void*, int32_t) override { return oboe::DataCallbackResult::Stop; } + }; + + DummyCallback callback; + + // NB: Exclusive mode could be rejected if a device is already opened in that mode, so to get + // reliable results, only use this function when a device is closed. + // We initially try to open a stream with a buffer size returned from + // android.media.property.OUTPUT_FRAMES_PER_BUFFER property, but then we verify the actual + // size after the stream is open. + OboeAudioIODevice::OboeStream tempStream (oboe::kUnspecified, + oboe::Direction::Output, + oboe::SharingMode::Exclusive, + 2, + getAndroidSDKVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16, + (int) AndroidHighPerformanceAudioHelpers::getNativeSampleRate(), + bufferSizeHint, + &callback); + + if (auto* nativeStream = tempStream.getNativeStream()) + return nativeStream->getFramesPerBurst(); + + return bufferSizeHint; + } + void setCallback (AudioIODeviceCallback* callbackToUse) { if (! running) @@ -412,13 +423,17 @@ class OboeAudioIODevice : public AudioIODevice } } - void process (const float** inputChannelData, int numInputChannels, - float** outputChannelData, int numOutputChannels, int32_t numFrames) + void process (const float* const* inputChannelData, int numInputChannels, + float* const* outputChannelData, int numOutputChannels, int32_t numFrames) { if (auto* cb = callback.exchange (nullptr)) { - cb->audioDeviceIOCallback (inputChannelData, numInputChannels, - outputChannelData, numOutputChannels, numFrames); + cb->audioDeviceIOCallbackWithContext (inputChannelData, + numInputChannels, + outputChannelData, + numOutputChannels, + numFrames, + {}); callback.set (cb); } else @@ -435,11 +450,11 @@ class OboeAudioIODevice : public AudioIODevice OboeStream (int deviceId, oboe::Direction direction, oboe::SharingMode sharingMode, int channelCount, oboe::AudioFormat format, - int32 sampleRate, int32 bufferSize, - oboe::AudioStreamCallback* callback = nullptr) + int32 sampleRateIn, int32 bufferSize, + oboe::AudioStreamCallback* callbackIn = nullptr) { open (deviceId, direction, sharingMode, channelCount, - format, sampleRate, bufferSize, callback); + format, sampleRateIn, bufferSize, callbackIn); } ~OboeStream() @@ -463,7 +478,7 @@ class OboeAudioIODevice : public AudioIODevice auto nextState = oboe::StreamState::Started; int64 timeoutNanos = 1000 * oboe::kNanosPerMillisecond; - auto startResult = stream->requestStart(); + [[maybe_unused]] auto startResult = stream->requestStart(); JUCE_OBOE_LOG ("Requested Oboe stream start with result: " + getOboeString (startResult)); startResult = stream->waitForStateChange (expectedState, &nextState, timeoutNanos); @@ -481,7 +496,7 @@ class OboeAudioIODevice : public AudioIODevice + "\nFramesPerCallback = " + String (stream->getFramesPerCallback()) + "\nBytesPerFrame = " + String (stream->getBytesPerFrame()) + "\nBytesPerSample = " + String (stream->getBytesPerSample()) - + "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency) + + "\nPerformanceMode = " + getOboeString (stream->getPerformanceMode()) + "\ngetDeviceId = " + String (stream->getDeviceId())); } } @@ -511,10 +526,10 @@ class OboeAudioIODevice : public AudioIODevice void open (int deviceId, oboe::Direction direction, oboe::SharingMode sharingMode, int channelCount, oboe::AudioFormat format, - int32 sampleRate, int32 bufferSize, - oboe::AudioStreamCallback* callback = nullptr) + int32 newSampleRate, int32 newBufferSize, + oboe::AudioStreamCallback* newCallback = nullptr) { - oboe::DefaultStreamValues::FramesPerBurst = getDefaultFramesPerBurst(); + oboe::DefaultStreamValues::FramesPerBurst = AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint(); oboe::AudioStreamBuilder builder; @@ -526,9 +541,18 @@ class OboeAudioIODevice : public AudioIODevice builder.setSharingMode (sharingMode); builder.setChannelCount (channelCount); builder.setFormat (format); - builder.setSampleRate (sampleRate); + builder.setSampleRate (newSampleRate); builder.setPerformanceMode (oboe::PerformanceMode::LowLatency); - builder.setCallback (callback); + + #if JUCE_USE_ANDROID_OBOE_STABILIZED_CALLBACK + if (newCallback != nullptr) + { + stabilizedCallback = std::make_unique (newCallback); + builder.setCallback (stabilizedCallback.get()); + } + #else + builder.setCallback (newCallback); + #endif JUCE_OBOE_LOG (String ("Preparing Oboe stream with params:") + "\nAAudio supported = " + String (int (builder.isAAudioSupported())) @@ -538,17 +562,17 @@ class OboeAudioIODevice : public AudioIODevice + "\nSharingMode = " + getOboeString (sharingMode) + "\nChannelCount = " + String (channelCount) + "\nFormat = " + getOboeString (format) - + "\nSampleRate = " + String (sampleRate) + + "\nSampleRate = " + String (newSampleRate) + "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency)); openResult = builder.openStream (&stream); JUCE_OBOE_LOG ("Building Oboe stream with result: " + getOboeString (openResult) + "\nStream state = " + (stream != nullptr ? getOboeString (stream->getState()) : String ("?"))); - if (stream != nullptr && bufferSize != 0) + if (stream != nullptr && newBufferSize != 0) { - JUCE_OBOE_LOG ("Setting the bufferSizeInFrames to " + String (bufferSize)); - stream->setBufferSizeInFrames (bufferSize); + JUCE_OBOE_LOG ("Setting the bufferSizeInFrames to " + String (newBufferSize)); + stream->setBufferSizeInFrames (newBufferSize); } JUCE_OBOE_LOG (String ("Stream details:") @@ -565,24 +589,27 @@ class OboeAudioIODevice : public AudioIODevice + "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?")) + "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?")) + "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?")) - + "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency)); + + "\nPerformanceMode = " + (stream != nullptr ? getOboeString (stream->getPerformanceMode()) : String ("?"))); } void close() { if (stream != nullptr) { - oboe::Result result = stream->close(); + [[maybe_unused]] oboe::Result result = stream->close(); JUCE_OBOE_LOG ("Requested Oboe stream close with result: " + getOboeString (result)); } } oboe::AudioStream* stream = nullptr; + #if JUCE_USE_ANDROID_OBOE_STABILIZED_CALLBACK + std::unique_ptr stabilizedCallback; + #endif oboe::Result openResult; }; //============================================================================== - class OboeSessionBase : protected oboe::AudioStreamCallback + class OboeSessionBase : protected oboe::AudioStreamCallback { public: static OboeSessionBase* create (OboeAudioIODevice& owner, @@ -665,16 +692,17 @@ class OboeAudioIODevice : public AudioIODevice } // Not strictly required as these should not change, but recommended by Google anyway - void checkStreamSetup (OboeStream* stream, int deviceId, int numChannels, int sampleRate, - int bufferSize, oboe::AudioFormat format) + void checkStreamSetup (OboeStream* stream, + [[maybe_unused]] int deviceId, + [[maybe_unused]] int numChannels, + [[maybe_unused]] int expectedSampleRate, + [[maybe_unused]] int expectedBufferSize, + oboe::AudioFormat format) { - if (auto* nativeStream = stream != nullptr ? stream->getNativeStream() : nullptr) + if ([[maybe_unused]] auto* nativeStream = stream != nullptr ? stream->getNativeStream() : nullptr) { - ignoreUnused (deviceId, numChannels, sampleRate, bufferSize); - ignoreUnused (streamFormat, bitDepth); - - jassert (numChannels == nativeStream->getChannelCount()); - jassert (sampleRate == 0 || sampleRate == nativeStream->getSampleRate()); + jassert (numChannels == 0 || numChannels == nativeStream->getChannelCount()); + jassert (expectedSampleRate == 0 || expectedSampleRate == nativeStream->getSampleRate()); jassert (format == nativeStream->getFormat()); } } @@ -702,15 +730,15 @@ class OboeAudioIODevice : public AudioIODevice //============================================================================== template - class OboeSessionImpl : public OboeSessionBase + class OboeSessionImpl final : public OboeSessionBase { public: OboeSessionImpl (OboeAudioIODevice& ownerToUse, - int inputDeviceId, int outputDeviceId, + int inputDeviceIdIn, int outputDeviceIdIn, int numInputChannelsToUse, int numOutputChannelsToUse, int sampleRateToUse, int bufferSizeToUse) : OboeSessionBase (ownerToUse, - inputDeviceId, outputDeviceId, + inputDeviceIdIn, outputDeviceIdIn, numInputChannelsToUse, numOutputChannelsToUse, sampleRateToUse, bufferSizeToUse, OboeAudioIODeviceBufferHelpers::oboeAudioFormat(), @@ -723,8 +751,6 @@ class OboeAudioIODevice : public AudioIODevice void start() override { - audioCallbackGuard.set (0); - if (inputStream != nullptr) inputStream->start(); @@ -736,13 +762,10 @@ class OboeAudioIODevice : public AudioIODevice void stop() override { - while (! audioCallbackGuard.compareAndSetBool (1, 0)) - Thread::sleep (1); + const SpinLock::ScopedLockType lock { audioCallbackMutex }; inputStream = nullptr; outputStream = nullptr; - - audioCallbackGuard.set (0); } int getOutputLatencyInSamples() override { return outputLatency; } @@ -754,13 +777,15 @@ class OboeAudioIODevice : public AudioIODevice if (stream == nullptr || ! openedOk()) return false; - auto result = stream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC, 0, 0); + auto result = stream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC, nullptr, nullptr); return result != oboe::Result::ErrorUnimplemented; } oboe::DataCallbackResult onAudioReady (oboe::AudioStream* stream, void* audioData, int32_t numFrames) override { - if (audioCallbackGuard.compareAndSetBool (1, 0)) + const SpinLock::ScopedTryLockType lock { audioCallbackMutex }; + + if (lock.isLocked()) { if (stream == nullptr) return oboe::DataCallbackResult::Stop; @@ -768,10 +793,10 @@ class OboeAudioIODevice : public AudioIODevice // only output stream should be the master stream receiving callbacks jassert (stream->getDirection() == oboe::Direction::Output && stream == outputStream->getNativeStream()); - //----------------- // Read input from Oboe - inputStreamSampleBuffer.clear(); - inputStreamNativeBuffer.calloc (static_cast (numInputChannels * bufferSize)); + const auto expandedBufferSize = jmax (inputStreamNativeBuffer.size(), + static_cast (numInputChannels * jmax (bufferSize, numFrames))); + inputStreamNativeBuffer.resize (expandedBufferSize); if (inputStream != nullptr) { @@ -784,15 +809,17 @@ class OboeAudioIODevice : public AudioIODevice return oboe::DataCallbackResult::Continue; } - auto result = inputStream->getNativeStream()->read (inputStreamNativeBuffer.getData(), numFrames, 0); + auto result = inputStream->getNativeStream()->read (inputStreamNativeBuffer.data(), numFrames, 0); if (result) { - OboeAudioIODeviceBufferHelpers::referAudioBufferDirectlyToOboeIfPossible (inputStreamNativeBuffer.get(), - inputStreamSampleBuffer, - result.value()); + auto referringDirectlyToOboeData = OboeAudioIODeviceBufferHelpers + ::referAudioBufferDirectlyToOboeIfPossible (inputStreamNativeBuffer.data(), + inputStreamSampleBuffer, + result.value()); - OboeAudioIODeviceBufferHelpers::convertFromOboe (inputStreamNativeBuffer.get(), inputStreamSampleBuffer, result.value()); + if (! referringDirectlyToOboeData) + OboeAudioIODeviceBufferHelpers::convertFromOboe (inputStreamNativeBuffer.data(), inputStreamSampleBuffer, result.value()); } else { @@ -803,38 +830,34 @@ class OboeAudioIODevice : public AudioIODevice inputLatency = getLatencyFor (*inputStream); } - //----------------- // Setup output buffer - outputStreamSampleBuffer.clear(); + auto referringDirectlyToOboeData = OboeAudioIODeviceBufferHelpers + ::referAudioBufferDirectlyToOboeIfPossible (static_cast (audioData), + outputStreamSampleBuffer, + numFrames); - OboeAudioIODeviceBufferHelpers::referAudioBufferDirectlyToOboeIfPossible (static_cast (audioData), - outputStreamSampleBuffer, - numFrames); + if (! referringDirectlyToOboeData) + outputStreamSampleBuffer.clear(); - //----------------- // Process // NB: the number of samples read from the input can potentially differ from numFrames. owner.process (inputStreamSampleBuffer.getArrayOfReadPointers(), numInputChannels, outputStreamSampleBuffer.getArrayOfWritePointers(), numOutputChannels, numFrames); - //----------------- // Write output to Oboe - OboeAudioIODeviceBufferHelpers::convertToOboe (outputStreamSampleBuffer, static_cast (audioData), numFrames); + if (! referringDirectlyToOboeData) + OboeAudioIODeviceBufferHelpers::convertToOboe (outputStreamSampleBuffer, static_cast (audioData), numFrames); if (isOutputLatencyDetectionSupported) outputLatency = getLatencyFor (*outputStream); - - audioCallbackGuard.set (0); } return oboe::DataCallbackResult::Continue; } - void printStreamDebugInfo (oboe::AudioStream* stream) + void printStreamDebugInfo ([[maybe_unused]] oboe::AudioStream* stream) { - ignoreUnused (stream); - JUCE_OBOE_LOG ("\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?")) + "\nDirection = " + (stream != nullptr ? getOboeString (stream->getDirection()) : String ("?")) + "\nSharingMode = " + (stream != nullptr ? getOboeString (stream->getSharingMode()) : String ("?")) @@ -847,7 +870,7 @@ class OboeAudioIODevice : public AudioIODevice + "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?")) + "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?")) + "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?")) - + "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency) + + "\nPerformanceMode = " + (stream != nullptr ? getOboeString (stream->getPerformanceMode()) : String ("?")) + "\ngetDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?"))); } @@ -874,9 +897,7 @@ class OboeAudioIODevice : public AudioIODevice const int64_t appFrameIndex = isOutput ? nativeStream.getFramesWritten() : nativeStream.getFramesRead(); // Assume that the next frame will be processed at the current time - using namespace std::chrono; - int64_t appFrameAppTime = getCurrentTimeNanos();//duration_cast (steady_clock::now().time_since_epoch()).count(); - int64_t appFrameAppTime2 = duration_cast (steady_clock::now().time_since_epoch()).count(); + int64_t appFrameAppTime = getCurrentTimeNanos(); // Calculate the number of frames between app and hardware int64_t frameIndexDelta = appFrameIndex - hardwareFrameIndex; @@ -901,7 +922,7 @@ class OboeAudioIODevice : public AudioIODevice return time.tv_sec * oboe::kNanosPerSecond + time.tv_nsec; } - void onErrorBeforeClose (oboe::AudioStream* stream, oboe::Result error) override + void onErrorBeforeClose (oboe::AudioStream* stream, [[maybe_unused]] oboe::Result error) override { // only output stream should be the master stream receiving callbacks jassert (stream->getDirection() == oboe::Direction::Output); @@ -919,16 +940,17 @@ class OboeAudioIODevice : public AudioIODevice if (error == oboe::Result::ErrorDisconnected) { - if (streamRestartGuard.compareAndSetBool (1, 0)) + const SpinLock::ScopedTryLockType streamRestartLock { streamRestartMutex }; + + if (streamRestartLock.isLocked()) { // Close, recreate, and start the stream, not much use in current one. // Use default device id, to let the OS pick the best ID (since our was disconnected). - while (! audioCallbackGuard.compareAndSetBool (1, 0)) - Thread::sleep (1); + const SpinLock::ScopedLockType audioCallbackLock { audioCallbackMutex }; outputStream = nullptr; - outputStream.reset (new OboeStream (-1, + outputStream.reset (new OboeStream (oboe::kUnspecified, oboe::Direction::Output, oboe::SharingMode::Exclusive, numOutputChannels, @@ -938,18 +960,14 @@ class OboeAudioIODevice : public AudioIODevice this)); outputStream->start(); - - audioCallbackGuard.set (0); - streamRestartGuard.set (0); } } } - HeapBlock inputStreamNativeBuffer; + std::vector inputStreamNativeBuffer; AudioBuffer inputStreamSampleBuffer, outputStreamSampleBuffer; - Atomic audioCallbackGuard { 0 }, - streamRestartGuard { 0 }; + SpinLock audioCallbackMutex, streamRestartMutex; bool isInputLatencyDetectionSupported = false; int inputLatency = -1; @@ -980,31 +998,6 @@ class OboeAudioIODevice : public AudioIODevice bool running = false; - //============================================================================== - static double getNativeSampleRate() - { - return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue(); - } - - static int getNativeBufferSize() - { - auto val = audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER").getIntValue(); - return val > 0 ? val : 512; - } - - static bool isProAudioDevice() - { - return androidHasSystemFeature ("android.hardware.audio.pro"); - } - - static int getDefaultFramesPerBurst() - { - // NB: this function only works for inbuilt speakers and headphones - auto framesPerBurstString = javaString (audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER")); - - return framesPerBurstString != 0 ? getEnv()->CallStaticIntMethod (JavaInteger, JavaInteger.parseInt, framesPerBurstString.get(), 10) : 192; - } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OboeAudioIODevice) }; @@ -1045,7 +1038,7 @@ OboeAudioIODevice::OboeSessionBase* OboeAudioIODevice::OboeSessionBase::create ( } //============================================================================== -class OboeAudioIODeviceType : public AudioIODeviceType +class OboeAudioIODeviceType final : public AudioIODeviceType { public: OboeAudioIODeviceType() @@ -1060,9 +1053,6 @@ class OboeAudioIODeviceType : public AudioIODeviceType StringArray getDeviceNames (bool wantInputNames) const override { - if (inputDevices.isEmpty() && outputDevices.isEmpty()) - return StringArray (OboeAudioIODevice::oboeTypeName); - StringArray names; for (auto& device : wantInputNames ? inputDevices : outputDevices) @@ -1071,36 +1061,8 @@ class OboeAudioIODeviceType : public AudioIODeviceType return names; } - int getDefaultDeviceIndex (bool forInput) const override + int getDefaultDeviceIndex (bool) const override { - // No need to create a stream when only one default device is created. - if (! supportsDevicesInfo()) - return 0; - - if (forInput && (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio))) - return 0; - - // Create stream with a default device ID and query the stream for its device ID - using OboeStream = OboeAudioIODevice::OboeStream; - - OboeStream tempStream (-1, - forInput ? oboe::Direction::Input : oboe::Direction::Output, - oboe::SharingMode::Shared, - forInput ? 1 : 2, - getAndroidSDKVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16, - (int) OboeAudioIODevice::getNativeSampleRate(), - OboeAudioIODevice::getNativeBufferSize(), - nullptr); - - if (auto* nativeStream = tempStream.getNativeStream()) - { - auto& devices = forInput ? inputDevices : outputDevices; - - for (int i = 0; i < devices.size(); ++i) - if (devices.getReference (i).id == nativeStream->getDeviceId()) - return i; - } - return 0; } @@ -1129,12 +1091,8 @@ class OboeAudioIODeviceType : public AudioIODeviceType auto outputDeviceInfo = getDeviceInfoForName (outputDeviceName, false); auto inputDeviceInfo = getDeviceInfoForName (inputDeviceName, true); - if (outputDeviceInfo.name.isEmpty() && inputDeviceInfo.name.isEmpty()) - { - // Invalid device name passed. It must be one of the names returned by getDeviceNames(). - jassertfalse; + if (outputDeviceInfo.id < 0 && inputDeviceInfo.id < 0) return nullptr; - } auto& name = outputDeviceInfo.name.isNotEmpty() ? outputDeviceInfo.name : inputDeviceInfo.name; @@ -1158,24 +1116,22 @@ class OboeAudioIODeviceType : public AudioIODeviceType private: void checkAvailableDevices() { - if (! supportsDevicesInfo()) - { - auto sampleRates = OboeAudioIODevice::getDefaultSampleRates(); + auto sampleRates = OboeAudioIODevice::getDefaultSampleRates(); - inputDevices .add ({ OboeAudioIODevice::oboeTypeName, -1, sampleRates, 1 }); - outputDevices.add ({ OboeAudioIODevice::oboeTypeName, -1, sampleRates, 2 }); + inputDevices .add ({ "System Default (Input)", oboe::kUnspecified, sampleRates, 1 }); + outputDevices.add ({ "System Default (Output)", oboe::kUnspecified, sampleRates, 2 }); + if (! supportsDevicesInfo()) return; - } auto* env = getEnv(); jclass audioManagerClass = env->FindClass ("android/media/AudioManager"); // We should be really entering here only if API supports it. - jassert (audioManagerClass != 0); + jassert (audioManagerClass != nullptr); - if (audioManagerClass == 0) + if (audioManagerClass == nullptr) return; auto audioManager = LocalRef (env->CallObjectMethod (getAppContext().get(), @@ -1200,7 +1156,7 @@ class OboeAudioIODeviceType : public AudioIODeviceType JUCE_OBOE_LOG ("-----InputDevices:"); - for (auto& device : inputDevices) + for ([[maybe_unused]] auto& device : inputDevices) { JUCE_OBOE_LOG ("name = " << device.name); JUCE_OBOE_LOG ("id = " << String (device.id)); @@ -1210,7 +1166,7 @@ class OboeAudioIODeviceType : public AudioIODeviceType JUCE_OBOE_LOG ("-----OutputDevices:"); - for (auto& device : outputDevices) + for ([[maybe_unused]] auto& device : outputDevices) { JUCE_OBOE_LOG ("name = " << device.name); JUCE_OBOE_LOG ("id = " << String (device.id)); @@ -1238,9 +1194,13 @@ class OboeAudioIODeviceType : public AudioIODeviceType jmethodID getChannelCountsMethod = env->GetMethodID (deviceClass, "getChannelCounts", "()[I"); jmethodID isSourceMethod = env->GetMethodID (deviceClass, "isSource", "()Z"); - auto name = juceString ((jstring) env->CallObjectMethod (device, getProductNameMethod)); - name << deviceTypeToString (env->CallIntMethod (device, getTypeMethod)); - int id = env->CallIntMethod (device, getIdMethod); + auto deviceTypeString = deviceTypeToString (env->CallIntMethod (device, getTypeMethod)); + + if (deviceTypeString.isEmpty()) // unknown device + return; + + auto name = juceString ((jstring) env->CallObjectMethod (device, getProductNameMethod)) + " " + deviceTypeString; + auto id = env->CallIntMethod (device, getIdMethod); auto jSampleRates = LocalRef ((jintArray) env->CallObjectMethod (device, getSampleRatesMethod)); auto sampleRates = jintArrayToJuceArray (jSampleRates); @@ -1249,40 +1209,48 @@ class OboeAudioIODeviceType : public AudioIODeviceType auto channelCounts = jintArrayToJuceArray (jChannelCounts); int numChannels = channelCounts.isEmpty() ? -1 : channelCounts.getLast(); - bool isInput = env->CallBooleanMethod (device, isSourceMethod); + auto isInput = env->CallBooleanMethod (device, isSourceMethod); auto& devices = isInput ? inputDevices : outputDevices; devices.add ({ name, id, sampleRates, numChannels }); } - static const char* deviceTypeToString (int type) + static String deviceTypeToString (int type) { switch (type) { - case 0: return ""; - case 1: return " built-in earphone speaker"; - case 2: return " built-in speaker"; - case 3: return " wired headset"; - case 4: return " wired headphones"; - case 5: return " line analog"; - case 6: return " line digital"; - case 7: return " Bluetooth device typically used for telephony"; - case 8: return " Bluetooth device supporting the A2DP profile"; - case 9: return " HDMI"; - case 10: return " HDMI audio return channel"; - case 11: return " USB device"; - case 12: return " USB accessory"; - case 13: return " DOCK"; - case 14: return " FM"; - case 15: return " built-in microphone"; - case 16: return " FM tuner"; - case 17: return " TV tuner"; - case 18: return " telephony"; - case 19: return " auxiliary line-level connectors"; - case 20: return " IP"; - case 21: return " BUS"; - case 22: return " USB headset"; - default: jassertfalse; return ""; // type not supported yet, needs to be added! + case 0: return {}; + case 1: return "built-in earphone speaker"; + case 2: return "built-in speaker"; + case 3: return "wired headset"; + case 4: return "wired headphones"; + case 5: return "line analog"; + case 6: return "line digital"; + case 7: return "Bluetooth device typically used for telephony"; + case 8: return "Bluetooth device supporting the A2DP profile"; + case 9: return "HDMI"; + case 10: return "HDMI audio return channel"; + case 11: return "USB device"; + case 12: return "USB accessory"; + case 13: return "DOCK"; + case 14: return "FM"; + case 15: return "built-in microphone"; + case 16: return "FM tuner"; + case 17: return "TV tuner"; + case 18: return "telephony"; + case 19: return "auxiliary line-level connectors"; + case 20: return "IP"; + case 21: return "BUS"; + case 22: return "USB headset"; + case 23: return "hearing aid"; + case 24: return "built-in speaker safe"; + case 25: return "remote submix"; + case 26: return "BLE headset"; + case 27: return "BLE speaker"; + case 28: return "echo reference"; + case 29: return "HDMI eARC"; + case 30: return "BLE broadcast"; + default: jassertfalse; return {}; // type not supported yet, needs to be added! } } @@ -1290,7 +1258,7 @@ class OboeAudioIODeviceType : public AudioIODeviceType { auto* env = getEnv(); - jint* jArrayElems = env->GetIntArrayElements (jArray, 0); + jint* jArrayElems = env->GetIntArrayElements (jArray, nullptr); int numElems = env->GetArrayLength (jArray); Array juceArray; @@ -1305,19 +1273,21 @@ class OboeAudioIODeviceType : public AudioIODeviceType struct DeviceInfo { String name; - int id; + int id = -1; Array sampleRates; int numChannels; }; DeviceInfo getDeviceInfoForName (const String& name, bool isInput) { - if (name.isEmpty()) - return {}; - - for (auto& device : isInput ? inputDevices : outputDevices) - if (device.name == name) - return device; + if (name.isNotEmpty()) + { + for (auto& device : isInput ? inputDevices : outputDevices) + { + if (device.name == name) + return device; + } + } return {}; } @@ -1329,28 +1299,21 @@ class OboeAudioIODeviceType : public AudioIODeviceType const char* const OboeAudioIODevice::oboeTypeName = "Android Oboe"; - -//============================================================================== bool isOboeAvailable() { return OboeAudioIODeviceType::isOboeAvailable(); } -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() -{ - return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr; -} - //============================================================================== -class OboeRealtimeThread : private oboe::AudioStreamCallback +class OboeRealtimeThread final : private oboe::AudioStreamCallback { using OboeStream = OboeAudioIODevice::OboeStream; public: OboeRealtimeThread() - : testStream (new OboeStream (-1, + : testStream (new OboeStream (oboe::kUnspecified, oboe::Direction::Output, oboe::SharingMode::Exclusive, 1, oboe::AudioFormat::Float, - (int) OboeAudioIODevice::getNativeSampleRate(), + (int) AndroidHighPerformanceAudioHelpers::getNativeSampleRate(), OboeAudioIODevice::getNativeBufferSize(), this)), formatUsed (oboe::AudioFormat::Float) @@ -1358,12 +1321,12 @@ class OboeRealtimeThread : private oboe::AudioStreamCallback // Fallback to I16 stream format if Float has not worked if (! testStream->openedOk()) { - testStream.reset (new OboeStream (-1, + testStream.reset (new OboeStream (oboe::kUnspecified, oboe::Direction::Output, oboe::SharingMode::Exclusive, 1, oboe::AudioFormat::I16, - (int) OboeAudioIODevice::getNativeSampleRate(), + (int) AndroidHighPerformanceAudioHelpers::getNativeSampleRate(), OboeAudioIODevice::getNativeBufferSize(), this)); @@ -1381,7 +1344,7 @@ class OboeRealtimeThread : private oboe::AudioStreamCallback return testStream != nullptr && testStream->openedOk(); } - pthread_t startThread (void*(*entry)(void*), void* userPtr) + pthread_t startThread (void*(*entry) (void*), void* userPtr) { pthread_mutex_lock (&threadReadyMutex); @@ -1411,7 +1374,7 @@ class OboeRealtimeThread : private oboe::AudioStreamCallback threadEntryProc (threadUserPtr); threadEntryProc = nullptr; - MessageManager::callAsync ([this] () { delete this; }); + MessageManager::callAsync ([this]() { delete this; }); return oboe::DataCallbackResult::Stop; } @@ -1419,17 +1382,15 @@ class OboeRealtimeThread : private oboe::AudioStreamCallback return oboe::DataCallbackResult::Continue; } - void onErrorBeforeClose (oboe::AudioStream*, oboe::Result error) override + void onErrorBeforeClose (oboe::AudioStream*, [[maybe_unused]] oboe::Result error) override { JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorBeforeClose(): " + getOboeString (error)); - ignoreUnused (error); jassertfalse; // Should never get here! } - void onErrorAfterClose (oboe::AudioStream*, oboe::Result error) override + void onErrorAfterClose (oboe::AudioStream*, [[maybe_unused]] oboe::Result error) override { JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorAfterClose(): " + getOboeString (error)); - ignoreUnused (error); jassertfalse; // Should never get here! } @@ -1446,16 +1407,23 @@ class OboeRealtimeThread : private oboe::AudioStreamCallback oboe::AudioFormat formatUsed; }; -pthread_t juce_createRealtimeAudioThread (void* (*entry) (void*), void* userPtr) +//============================================================================== +RealtimeThreadFactory getAndroidRealtimeThreadFactory() { - std::unique_ptr thread (new OboeRealtimeThread()); + return [] (void* (*entry) (void*), void* userPtr) -> pthread_t + { + auto thread = std::make_unique(); - if (! thread->isOk()) - return {}; + if (! thread->isOk()) + return {}; - auto threadID = thread->startThread (entry, userPtr); - thread.release(); // the thread will de-allocate itself - return threadID; + auto threadID = thread->startThread (entry, userPtr); + + // the thread will de-allocate itself + thread.release(); + + return threadID; + }; } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_OpenSL_android.cpp similarity index 82% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_OpenSL_android.cpp index dbc638a5..2a4920f0 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_OpenSL_android.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -41,7 +41,7 @@ DECLARE_JNI_CLASS (AndroidAudioManager, "android/media/AudioManager") #endif //============================================================================== -struct PCMDataFormatEx : SLDataFormat_PCM +struct PCMDataFormatEx final : SLDataFormat_PCM { SLuint32 representation; }; @@ -71,12 +71,11 @@ static void destroyObject (SLObjectType object) (*object)->Destroy (object); } -template <> -struct ContainerDeletePolicy +struct SLObjectItfFree { - static void destroy (SLObjectItf object) + void operator() (SLObjectItf obj) const noexcept { - destroyObject (object); + destroyObject (obj); } }; @@ -107,19 +106,19 @@ class SlObjectRef private: //============================================================================== - struct ControlBlock : ReferenceCountedObject + struct ControlBlock final : ReferenceCountedObject { ControlBlock() = default; ControlBlock (SLObjectItf o) : ptr (o) {} - std::unique_ptr ptr; + std::unique_ptr ptr; }; ReferenceCountedObjectPtr cb; }; template -class SlRef : public SlObjectRef +class SlRef final : public SlObjectRef { public: //============================================================================== @@ -195,31 +194,25 @@ struct BufferHelpers static void prepareCallbackBuffer (AudioBuffer&, int16*) {} + using LittleEndianInt16 = AudioData::Format; + using NativeFloat32 = AudioData::Format; + static void convertFromOpenSL (const int16* srcInterleaved, AudioBuffer& audioBuffer) { - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; + const auto numChannels = audioBuffer.getNumChannels(); - DstSampleType dstData (audioBuffer.getWritePointer (i)); - SrcSampleType srcData (srcInterleaved + i, audioBuffer.getNumChannels()); - dstData.convertSamples (srcData, audioBuffer.getNumSamples()); - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { reinterpret_cast (srcInterleaved), numChannels }, + AudioData::NonInterleavedDest { audioBuffer.getArrayOfWritePointers(), numChannels }, + audioBuffer.getNumSamples()); } static void convertToOpenSL (const AudioBuffer& audioBuffer, int16* dstInterleaved) { - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; + const auto numChannels = audioBuffer.getNumChannels(); - DstSampleType dstData (dstInterleaved + i, audioBuffer.getNumChannels()); - SrcSampleType srcData (audioBuffer.getReadPointer (i)); - - dstData.convertSamples (srcData, audioBuffer.getNumSamples()); - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { audioBuffer.getArrayOfReadPointers(), numChannels }, + AudioData::InterleavedDest { reinterpret_cast (dstInterleaved), numChannels }, + audioBuffer.getNumSamples()); } }; @@ -248,43 +241,37 @@ struct BufferHelpers audioBuffer.setDataToReferTo (&native, 1, audioBuffer.getNumSamples()); } + using LittleEndianFloat32 = AudioData::Format; + using NativeFloat32 = AudioData::Format; + static void convertFromOpenSL (const float* srcInterleaved, AudioBuffer& audioBuffer) { - if (audioBuffer.getNumChannels() == 1) + const auto numChannels = audioBuffer.getNumChannels(); + + if (numChannels == 1) { jassert (srcInterleaved == audioBuffer.getWritePointer (0)); return; } - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; - - DstSampleType dstData (audioBuffer.getWritePointer (i)); - SrcSampleType srcData (srcInterleaved + i, audioBuffer.getNumChannels()); - dstData.convertSamples (srcData, audioBuffer.getNumSamples()); - } + AudioData::deinterleaveSamples (AudioData::InterleavedSource { srcInterleaved, numChannels }, + AudioData::NonInterleavedDest { audioBuffer.getArrayOfWritePointers(), numChannels }, + audioBuffer.getNumSamples()); } static void convertToOpenSL (const AudioBuffer& audioBuffer, float* dstInterleaved) { - if (audioBuffer.getNumChannels() == 1) + const auto numChannels = audioBuffer.getNumChannels(); + + if (numChannels == 1) { jassert (dstInterleaved == audioBuffer.getReadPointer (0)); return; } - for (int i = 0; i < audioBuffer.getNumChannels(); ++i) - { - using DstSampleType = AudioData::Pointer; - using SrcSampleType = AudioData::Pointer; - - DstSampleType dstData (dstInterleaved + i, audioBuffer.getNumChannels()); - SrcSampleType srcData (audioBuffer.getReadPointer (i)); - - dstData.convertSamples (srcData, audioBuffer.getNumSamples()); - } + AudioData::interleaveSamples (AudioData::NonInterleavedSource { audioBuffer.getArrayOfReadPointers(), numChannels }, + AudioData::InterleavedDest { dstInterleaved, numChannels }, + audioBuffer.getNumSamples()); } }; @@ -325,7 +312,7 @@ OpenSLEngineHolder& getEngineHolder() class SLRealtimeThread; //============================================================================== -class OpenSLAudioIODevice : public AudioIODevice +class OpenSLAudioIODevice final : public AudioIODevice { public: //============================================================================== @@ -375,7 +362,7 @@ class OpenSLAudioIODevice : public AudioIODevice &audioRoutingJni); if (status == SL_RESULT_SUCCESS && audioRoutingJni != nullptr) - javaProxy = GlobalRef (LocalRef(getEnv()->NewLocalRef (audioRoutingJni))); + javaProxy = GlobalRef (LocalRef (getEnv()->NewLocalRef (audioRoutingJni))); } } @@ -404,7 +391,7 @@ class OpenSLAudioIODevice : public AudioIODevice } bool isBufferAvailable() const { return (numBlocksOut.get() < owner.numBuffers); } - T* getNextBuffer() { nextBlock.set((nextBlock.get() + 1) % owner.numBuffers); return getCurrentBuffer(); } + T* getNextBuffer() { nextBlock.set ((nextBlock.get() + 1) % owner.numBuffers); return getCurrentBuffer(); } T* getCurrentBuffer() { return nativeBuffer.get() + (static_cast (nextBlock.get()) * getBufferSizeInSamples()); } size_t getBufferSizeInSamples() const { return static_cast (owner.bufferSize * numChannels); } @@ -440,7 +427,7 @@ class OpenSLAudioIODevice : public AudioIODevice //============================================================================== template - struct OpenSLQueueRunnerPlayer : OpenSLQueueRunner, SLPlayItf_> + struct OpenSLQueueRunnerPlayer final : OpenSLQueueRunner, SLPlayItf_> { using Base = OpenSLQueueRunner, SLPlayItf_>; @@ -471,7 +458,7 @@ class OpenSLAudioIODevice : public AudioIODevice 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) + if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) { destroyObject (obj); return {}; @@ -485,7 +472,7 @@ class OpenSLAudioIODevice : public AudioIODevice }; template - struct OpenSLQueueRunnerRecorder : public OpenSLQueueRunner, SLRecordItf_> + struct OpenSLQueueRunnerRecorder final : public OpenSLQueueRunner, SLRecordItf_> { using Base = OpenSLQueueRunner, SLRecordItf_>; @@ -614,17 +601,17 @@ class OpenSLAudioIODevice : public AudioIODevice } } - void process (const float** inputChannelData, float** outputChannelData) + void process (const float* const* inputChannelData, float* const* outputChannelData) { if (auto* cb = callback.exchange (nullptr)) { - cb->audioDeviceIOCallback (inputChannelData, inputChannels, outputChannelData, outputChannels, bufferSize); + cb->audioDeviceIOCallbackWithContext (inputChannelData, inputChannels, outputChannelData, outputChannels, bufferSize, {}); callback.set (cb); } else { for (int i = 0; i < outputChannels; ++i) - zeromem (outputChannelData[i], sizeof(float) * static_cast (bufferSize)); + zeromem (outputChannelData[i], sizeof (float) * static_cast (bufferSize)); } } @@ -644,7 +631,7 @@ class OpenSLAudioIODevice : public AudioIODevice }; template - class OpenSLSessionT : public OpenSLSession + class OpenSLSessionT final : public OpenSLSession { public: OpenSLSessionT (int numInputChannels, int numOutputChannels, @@ -763,8 +750,8 @@ class OpenSLAudioIODevice : public AudioIODevice T* recorderBuffer = (inputChannels > 0 ? recorder->getNextBuffer() : nullptr); T* playerBuffer = (outputChannels > 0 ? player->getNextBuffer() : nullptr); - const float** inputChannelData = nullptr; - float** outputChannelData = nullptr; + const float* const* inputChannelData = nullptr; + float* const* outputChannelData = nullptr; if (recorderBuffer != nullptr) { @@ -852,10 +839,11 @@ class OpenSLAudioIODevice : public AudioIODevice static const double rates[] = { 8000.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0 }; + Array retval (rates, numElementsInArray (rates)); // make sure the native sample rate is part of the list - double native = getNativeSampleRate(); + double native = AndroidHighPerformanceAudioHelpers::getNativeSampleRate(); if (native != 0.0 && ! retval.contains (native)) retval.add (native); @@ -865,17 +853,8 @@ class OpenSLAudioIODevice : public AudioIODevice Array getAvailableBufferSizes() override { - // we need to offer the lowest possible buffer size which - // is the native buffer size - auto nativeBufferSize = getNativeBufferSize(); - auto minBuffersToQueue = getMinimumBuffersToEnqueue(); - auto maxBuffersToQueue = getMaximumBuffersToEnqueue(); - - Array retval; - for (int i = minBuffersToQueue; i <= maxBuffersToQueue; ++i) - retval.add (i * nativeBufferSize); - - return retval; + return AndroidHighPerformanceAudioHelpers::getAvailableBufferSizes (AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint(), + getAvailableSampleRates()); } String open (const BigInteger& inputChannels, @@ -887,15 +866,25 @@ class OpenSLAudioIODevice : public AudioIODevice lastError.clear(); - sampleRate = (int) (requestedSampleRate > 0 ? requestedSampleRate : getNativeSampleRate()); + sampleRate = (int) (requestedSampleRate > 0 ? requestedSampleRate : AndroidHighPerformanceAudioHelpers::getNativeSampleRate()); + auto preferredBufferSize = (bufferSize > 0) ? bufferSize : getDefaultBufferSize(); + + audioBuffersToEnqueue = [this, preferredBufferSize] + { + using namespace AndroidHighPerformanceAudioHelpers; + + auto nativeBufferSize = getNativeBufferSizeHint(); + + if (canUseHighPerformanceAudioPath (nativeBufferSize, preferredBufferSize, sampleRate)) + return preferredBufferSize / nativeBufferSize; + + + return 1; + }(); - auto totalPreferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; - auto nativeBufferSize = getNativeBufferSize(); - bool useHighPerformanceAudioPath = canUseHighPerformanceAudioPath (totalPreferredBufferSize, sampleRate); + actualBufferSize = preferredBufferSize / audioBuffersToEnqueue; - audioBuffersToEnqueue = useHighPerformanceAudioPath ? (totalPreferredBufferSize / nativeBufferSize) : 1; - actualBufferSize = totalPreferredBufferSize / audioBuffersToEnqueue; - jassert ((actualBufferSize * audioBuffersToEnqueue) == totalPreferredBufferSize); + jassert ((actualBufferSize * audioBuffersToEnqueue) == preferredBufferSize); activeOutputChans = outputChannels; activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); @@ -924,7 +913,7 @@ class OpenSLAudioIODevice : public AudioIODevice if (numInputChannels > 0 && numOutputChannels > 0 && RuntimePermissions::isGranted (RuntimePermissions::recordAudio)) { // New versions of the Android emulator do not seem to support audio input anymore on OS X - activeInputChans = BigInteger(0); + activeInputChans = BigInteger (0); numInputChannels = 0; session.reset (OpenSLSession::create (numInputChannels, numOutputChannels, @@ -934,8 +923,8 @@ class OpenSLAudioIODevice : public AudioIODevice DBG ("OpenSL: numInputChannels = " << numInputChannels << ", numOutputChannels = " << numOutputChannels - << ", nativeBufferSize = " << getNativeBufferSize() - << ", nativeSampleRate = " << getNativeSampleRate() + << ", nativeBufferSize = " << AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint() + << ", nativeSampleRate = " << AndroidHighPerformanceAudioHelpers::getNativeSampleRate() << ", actualBufferSize = " << actualBufferSize << ", audioBuffersToEnqueue = " << audioBuffersToEnqueue << ", sampleRate = " << sampleRate @@ -968,16 +957,13 @@ class OpenSLAudioIODevice : public AudioIODevice int getDefaultBufferSize() override { - auto defaultBufferLength = (hasLowLatencyAudioPath() ? defaultBufferSizeForLowLatencyDeviceMs - : defaultBufferSizeForStandardLatencyDeviceMs); - - auto defaultBuffersToEnqueue = buffersToQueueForBufferDuration (defaultBufferLength, getCurrentSampleRate()); - return defaultBuffersToEnqueue * getNativeBufferSize(); + return AndroidHighPerformanceAudioHelpers::getDefaultBufferSize (AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint(), + getCurrentSampleRate()); } double getCurrentSampleRate() override { - return (sampleRate == 0.0 ? getNativeSampleRate() : sampleRate); + return (sampleRate == 0.0 ? AndroidHighPerformanceAudioHelpers::getNativeSampleRate() : sampleRate); } void start (AudioIODeviceCallback* newCallback) override @@ -1048,91 +1034,6 @@ class OpenSLAudioIODevice : public AudioIODevice std::unique_ptr session; - enum - { - defaultBufferSizeForLowLatencyDeviceMs = 40, - defaultBufferSizeForStandardLatencyDeviceMs = 100 - }; - - static int getMinimumBuffersToEnqueue (double sampleRateToCheck = getNativeSampleRate()) - { - if (canUseHighPerformanceAudioPath (getNativeBufferSize(), (int) sampleRateToCheck)) - { - // see https://developer.android.com/ndk/guides/audio/opensl/opensl-prog-notes.html#sandp - // "For Android 4.2 (API level 17) and earlier, a buffer count of two or more is required - // for lower latency. Beginning with Android 4.3 (API level 18), a buffer count of one - // is sufficient for lower latency." - return (getAndroidSDKVersion() >= 18 ? 1 : 2); - } - - // we will not use the low-latency path so we can use the absolute minimum number of buffers - // to queue - return 1; - } - - int getMaximumBuffersToEnqueue() noexcept - { - constexpr auto maxBufferSizeMs = 200; - - auto availableSampleRates = getAvailableSampleRates(); - auto maximumSampleRate = findMaximum(availableSampleRates.getRawDataPointer(), availableSampleRates.size()); - - // ensure we don't return something crazy small - return jmax (8, buffersToQueueForBufferDuration (maxBufferSizeMs, maximumSampleRate)); - } - - static int buffersToQueueForBufferDuration (int bufferDurationInMs, double sampleRate) noexcept - { - auto maxBufferFrames = static_cast (std::ceil (bufferDurationInMs * sampleRate / 1000.0)); - auto maxNumBuffers = static_cast (std::ceil (static_cast (maxBufferFrames) - / static_cast (getNativeBufferSize()))); - - return jmax (getMinimumBuffersToEnqueue (sampleRate), maxNumBuffers); - } - - //============================================================================== - static double getNativeSampleRate() - { - return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue(); - } - - static int getNativeBufferSize() - { - const int val = audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER").getIntValue(); - return val > 0 ? val : 512; - } - - static bool isProAudioDevice() - { - return androidHasSystemFeature ("android.hardware.audio.pro") || isSapaSupported(); - } - - static bool hasLowLatencyAudioPath() - { - return androidHasSystemFeature ("android.hardware.audio.low_latency"); - } - - static bool canUseHighPerformanceAudioPath (int requestedBufferSize, int requestedSampleRate) - { - return ((requestedBufferSize % getNativeBufferSize()) == 0) - && (requestedSampleRate == getNativeSampleRate()) - && isProAudioDevice(); - } - - //============================================================================== - // Some minimum Sapa support to check if this device supports pro audio - static bool isSamsungDevice() - { - return SystemStats::getDeviceManufacturer().containsIgnoreCase ("SAMSUNG"); - } - - static bool isSapaSupported() - { - static bool supported = isSamsungDevice() && DynamicLibrary().open ("libapa_jni.so"); - - return supported; - } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice) }; @@ -1167,7 +1068,7 @@ OpenSLAudioIODevice::OpenSLSession* OpenSLAudioIODevice::OpenSLSession::create ( } //============================================================================== -class OpenSLAudioDeviceType : public AudioIODeviceType +class OpenSLAudioDeviceType final : public AudioIODeviceType { public: OpenSLAudioDeviceType() : AudioIODeviceType (OpenSLAudioIODevice::openSLTypeName) {} @@ -1207,11 +1108,6 @@ const char* const OpenSLAudioIODevice::openSLTypeName = "Android OpenSL"; //============================================================================== bool isOpenSLAvailable() { return OpenSLAudioDeviceType::isOpenSLAvailable(); } -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() -{ - return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr; -} - //============================================================================== class SLRealtimeThread { @@ -1263,7 +1159,7 @@ class SLRealtimeThread SLDataLocator_OutputMix outputMixLocator = {SL_DATALOCATOR_OUTPUTMIX, outputMix}; PCMDataFormatEx dataFormat; - BufferHelpers::initPCMDataFormat (dataFormat, 1, OpenSLAudioIODevice::getNativeSampleRate()); + BufferHelpers::initPCMDataFormat (dataFormat, 1, AndroidHighPerformanceAudioHelpers::getNativeSampleRate()); SLDataSource source = { &queueLocator, &dataFormat }; SLDataSink sink = { &outputMixLocator, nullptr }; @@ -1306,7 +1202,7 @@ class SLRealtimeThread } } - bool isOK() const { return queue != nullptr; } + bool isOk() const { return queue != nullptr; } pthread_t startThread (void* (*entry) (void*), void* userPtr) { @@ -1346,7 +1242,7 @@ class SLRealtimeThread threadEntryProc = nullptr; (*player)->SetPlayState (player, SL_PLAYSTATE_STOPPED); - MessageManager::callAsync ([this] () { delete this; }); + MessageManager::callAsync ([this]() { delete this; }); } } @@ -1365,7 +1261,7 @@ class SLRealtimeThread SlRef player; SlRef queue; - int bufferSize = OpenSLAudioIODevice::getNativeBufferSize(); + int bufferSize = AndroidHighPerformanceAudioHelpers::getNativeBufferSizeHint(); HeapBlock buffer { HeapBlock (static_cast (1 * bufferSize * numBuffers)) }; void* (*threadEntryProc) (void*) = nullptr; @@ -1376,19 +1272,23 @@ class SLRealtimeThread pthread_t threadID; }; -pthread_t juce_createRealtimeAudioThread (void* (*entry) (void*), void* userPtr) +//============================================================================== +RealtimeThreadFactory getAndroidRealtimeThreadFactory() { - std::unique_ptr thread (new SLRealtimeThread); + return [] (void* (*entry) (void*), void* userPtr) -> pthread_t + { + auto thread = std::make_unique(); - if (! thread->isOK()) - return 0; + if (! thread->isOk()) + return {}; - pthread_t threadID = thread->startThread (entry, userPtr); + auto threadID = thread->startThread (entry, userPtr); - // the thread will de-allocate itself - thread.release(); + // the thread will de-allocate itself + thread.release(); - return threadID; + return threadID; + }; } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_WASAPI_windows.cpp similarity index 60% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_WASAPI_windows.cpp index 188741e5..ed6623fd 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_WASAPI_windows.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,8 @@ namespace juce { +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + #ifndef JUCE_WASAPI_LOGGING #define JUCE_WASAPI_LOGGING 0 #endif @@ -31,9 +33,8 @@ namespace juce namespace WasapiClasses { -void logFailure (HRESULT hr) +static void logFailure ([[maybe_unused]] HRESULT hr) { - ignoreUnused (hr); jassert (hr != (HRESULT) 0x800401f0); // If you hit this, it means you're trying to call from // a thread which hasn't been initialised with CoInitialize(). @@ -87,7 +88,7 @@ void logFailure (HRESULT hr) #undef check -bool check (HRESULT hr) +static bool check (HRESULT hr) { logFailure (hr); return SUCCEEDED (hr); @@ -116,9 +117,6 @@ bool check (HRESULT hr) #define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71") #endif -#define JUCE_IUNKNOWNCLASS(name, guid) JUCE_COMCLASS(name, guid) : public IUnknown -#define JUCE_COMCALL virtual HRESULT STDMETHODCALLTYPE - enum EDataFlow { eRender = 0, @@ -208,6 +206,29 @@ enum AUDCLNT_SHAREMODE AUDCLNT_SHAREMODE_EXCLUSIVE }; +enum AUDIO_STREAM_CATEGORY +{ + AudioCategory_Other = 0, + AudioCategory_ForegroundOnlyMedia, + AudioCategory_BackgroundCapableMedia, + AudioCategory_Communications, + AudioCategory_Alerts, + AudioCategory_SoundEffects, + AudioCategory_GameEffects, + AudioCategory_GameMedia, + AudioCategory_GameChat, + AudioCategory_Speech, + AudioCategory_Movie, + AudioCategory_Media +}; + +struct AudioClientProperties +{ + UINT32 cbSize; + BOOL bIsOffload; + AUDIO_STREAM_CATEGORY eCategory; +}; + JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") { JUCE_COMCALL Initialize (AUDCLNT_SHAREMODE, DWORD, REFERENCE_TIME, REFERENCE_TIME, const WAVEFORMATEX*, LPCGUID) = 0; @@ -224,6 +245,20 @@ JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") JUCE_COMCALL GetService (REFIID, void**) = 0; }; +JUCE_COMCLASS (IAudioClient2, "726778CD-F60A-4eda-82DE-E47610CD78AA") : public IAudioClient +{ + JUCE_COMCALL IsOffloadCapable (AUDIO_STREAM_CATEGORY, BOOL*) = 0; + JUCE_COMCALL SetClientProperties (const AudioClientProperties*) = 0; + JUCE_COMCALL GetBufferSizeLimits (const WAVEFORMATEX*, BOOL, REFERENCE_TIME*, REFERENCE_TIME*) = 0; +}; + +JUCE_COMCLASS (IAudioClient3, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") : public IAudioClient2 +{ + JUCE_COMCALL GetSharedModeEnginePeriod (const WAVEFORMATEX*, UINT32*, UINT32*, UINT32*, UINT32*) = 0; + JUCE_COMCALL GetCurrentSharedModeEnginePeriod (WAVEFORMATEX**, UINT32*) = 0; + JUCE_COMCALL InitializeSharedAudioStream (DWORD, UINT32, const WAVEFORMATEX*, LPCGUID) = 0; +}; + JUCE_IUNKNOWNCLASS (IAudioCaptureClient, "C8ADBD64-E71E-48a0-A4DE-185C395CD317") { JUCE_COMCALL GetBuffer (BYTE**, UINT32*, DWORD*, UINT64*, UINT64*) = 0; @@ -300,15 +335,32 @@ JUCE_IUNKNOWNCLASS (IAudioSessionControl, "F4B1A599-7266-4319-A8CA-E70ACB11E8CD" JUCE_COMCALL UnregisterAudioSessionNotification (IAudioSessionEvents*) = 0; }; -#undef JUCE_COMCALL -#undef JUCE_COMCLASS -#undef JUCE_IUNKNOWNCLASS +} // namespace juce + +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL (juce::IPropertyStore, 0x886d8eeb, 0x8cf2, 0x4446, 0x8d, 0x02, 0xcd, 0xba, 0x1d, 0xbd, 0xcf, 0x99) +__CRT_UUID_DECL (juce::IMMDevice, 0xD666063F, 0x1587, 0x4E43, 0x81, 0xF1, 0xB9, 0x48, 0xE8, 0x07, 0x36, 0x3F) +__CRT_UUID_DECL (juce::IMMEndpoint, 0x1BE09788, 0x6894, 0x4089, 0x85, 0x86, 0x9A, 0x2A, 0x6C, 0x26, 0x5A, 0xC5) +__CRT_UUID_DECL (juce::IMMNotificationClient, 0x7991EEC9, 0x7E89, 0x4D85, 0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0) +__CRT_UUID_DECL (juce::IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6) +__CRT_UUID_DECL (juce::MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E) +__CRT_UUID_DECL (juce::IAudioClient, 0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2) +__CRT_UUID_DECL (juce::IAudioClient2, 0x726778CD, 0xF60A, 0x4eda, 0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA) +__CRT_UUID_DECL (juce::IAudioClient3, 0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2) +__CRT_UUID_DECL (juce::IAudioCaptureClient, 0xC8ADBD64, 0xE71E, 0x48a0, 0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17) +__CRT_UUID_DECL (juce::IAudioRenderClient, 0xF294ACFC, 0x3146, 0x4483, 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2) +__CRT_UUID_DECL (juce::IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A) +__CRT_UUID_DECL (juce::IAudioSessionEvents, 0x24918ACC, 0x64B3, 0x37C1, 0x8C, 0xA9, 0x74, 0xA6, 0x6E, 0x99, 0x57, 0xA8) +__CRT_UUID_DECL (juce::IAudioSessionControl, 0xF4B1A599, 0x7266, 0x4319, 0xA8, 0xCA, 0xE7, 0x0A, 0xCB, 0x11, 0xE8, 0xCD) +#endif //============================================================================== +namespace juce +{ namespace WasapiClasses { -String getDeviceID (IMMDevice* device) +static String getDeviceID (IMMDevice* device) { String s; WCHAR* deviceId = nullptr; @@ -322,87 +374,78 @@ String getDeviceID (IMMDevice* device) return s; } -EDataFlow getDataFlow (const ComSmartPtr& device) +static EDataFlow getDataFlow (const ComSmartPtr& device) { EDataFlow flow = eRender; - ComSmartPtr endPoint; - if (check (device.QueryInterface (endPoint))) - (void) check (endPoint->GetDataFlow (&flow)); + if (auto endpoint = device.getInterface()) + (void) check (endpoint->GetDataFlow (&flow)); return flow; } -int refTimeToSamples (const REFERENCE_TIME& t, double sampleRate) noexcept +static int refTimeToSamples (const REFERENCE_TIME& t, double sampleRate) noexcept { return roundToInt (sampleRate * ((double) t) * 0.0000001); } -REFERENCE_TIME samplesToRefTime (int numSamples, double sampleRate) noexcept +static REFERENCE_TIME samplesToRefTime (int numSamples, double sampleRate) noexcept { return (REFERENCE_TIME) ((numSamples * 10000.0 * 1000.0 / sampleRate) + 0.5); } -void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcept +static void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcept { memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE) : sizeof (WAVEFORMATEX)); } +static bool isExclusiveMode (WASAPIDeviceMode deviceMode) noexcept +{ + return deviceMode == WASAPIDeviceMode::exclusive; +} + +static bool isLowLatencyMode (WASAPIDeviceMode deviceMode) noexcept +{ + return deviceMode == WASAPIDeviceMode::sharedLowLatency; +} + +static bool supportsSampleRateConversion (WASAPIDeviceMode deviceMode) noexcept +{ + return deviceMode == WASAPIDeviceMode::shared; +} + //============================================================================== class WASAPIDeviceBase { public: - WASAPIDeviceBase (const ComSmartPtr& d, bool exclusiveMode, std::function&& cb) - : device (d), useExclusiveMode (exclusiveMode), reopenCallback (cb) + WASAPIDeviceBase (const ComSmartPtr& d, WASAPIDeviceMode mode) + : device (d), + deviceMode (mode) { clientEvent = CreateEvent (nullptr, false, false, nullptr); ComSmartPtr tempClient (createClient()); + if (tempClient == nullptr) return; - REFERENCE_TIME defaultPeriod, minPeriod; - if (! check (tempClient->GetDevicePeriod (&defaultPeriod, &minPeriod))) - return; + auto format = getClientMixFormat (tempClient); - WAVEFORMATEX* mixFormat = nullptr; - if (! check (tempClient->GetMixFormat (&mixFormat))) + if (! format) return; - WAVEFORMATEXTENSIBLE format; - copyWavFormat (format, mixFormat); - CoTaskMemFree (mixFormat); - - actualNumChannels = numChannels = format.Format.nChannels; - defaultSampleRate = format.Format.nSamplesPerSec; - minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate); - defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate); - mixFormatChannelMask = format.dwChannelMask; - + defaultNumChannels = maxNumChannels = format->Format.nChannels; + defaultSampleRate = format->Format.nSamplesPerSec; rates.addUsingDefaultSort (defaultSampleRate); + defaultFormatChannelMask = format->dwChannelMask; - if (useExclusiveMode - && findSupportedFormat (tempClient, defaultSampleRate, format.dwChannelMask, format)) - { - // Got a format that is supported by the device so we can ask what sample rates are supported (in whatever format) - } - - for (auto rate : { 8000, 11025, 16000, 22050, 32000, - 44100, 48000, 88200, 96000, 176400, - 192000, 352800, 384000, 705600, 768000 }) - { - if (rates.contains (rate)) - continue; + if (isExclusiveMode (deviceMode)) + if (auto optFormat = findSupportedFormat (tempClient, defaultNumChannels, defaultSampleRate)) + format = optFormat; - format.Format.nSamplesPerSec = (DWORD) rate; - format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8); - - if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE - : AUDCLNT_SHAREMODE_SHARED, - (WAVEFORMATEX*) &format, 0))) - if (! rates.contains (rate)) - rates.addUsingDefaultSort (rate); - } + querySupportedBufferSizes (*format, tempClient); + querySupportedSampleRates (*format, tempClient); + maxNumChannels = queryMaxNumChannels (tempClient); } virtual ~WASAPIDeviceBase() @@ -417,7 +460,7 @@ class WASAPIDeviceBase { sampleRate = newSampleRate; channels = newChannels; - channels.setRange (actualNumChannels, channels.getHighestBit() + 1 - actualNumChannels, false); + channels.setRange (maxNumChannels, channels.getHighestBit() + 1 - maxNumChannels, false); numChannels = channels.getHighestBit() + 1; if (numChannels == 0) @@ -429,7 +472,8 @@ class WASAPIDeviceBase && tryInitialisingWithBufferSize (bufferSizeSamples)) { sampleRateHasChanged = false; - shouldClose = false; + shouldShutdown = false; + channelMaps.clear(); for (int i = 0; i <= channels.getHighestBit(); ++i) @@ -468,54 +512,83 @@ class WASAPIDeviceBase sampleRateHasChanged = true; } - void deviceBecameInactive() + void deviceSessionBecameInactive() + { + isActive = false; + } + + void deviceSessionExpired() + { + shouldShutdown = true; + } + + void deviceSessionBecameActive() { - shouldClose = true; + isActive = true; + } + + std::optional getDefaultLayout() const + { + if (countNumberOfBits ((uint64) defaultFormatChannelMask) == defaultNumChannels) + return BigInteger ((int64) defaultFormatChannelMask); + + BigInteger integer; + integer.setRange (0, defaultNumChannels, true); + return integer; } //============================================================================== ComSmartPtr device; ComSmartPtr client; + + WASAPIDeviceMode deviceMode; + double sampleRate = 0, defaultSampleRate = 0; - int numChannels = 0, actualNumChannels = 0; + int numChannels = 0, actualNumChannels = 0, maxNumChannels = 0, defaultNumChannels = 0; int minBufferSize = 0, defaultBufferSize = 0, latencySamples = 0; - DWORD mixFormatChannelMask = 0; - const bool useExclusiveMode; + int lowLatencyBufferSizeMultiple = 0, lowLatencyMaxBufferSize = 0; + DWORD defaultFormatChannelMask = 0; Array rates; HANDLE clientEvent = {}; BigInteger channels; Array channelMaps; UINT32 actualBufferSize = 0; int bytesPerSample = 0, bytesPerFrame = 0; - bool sampleRateHasChanged = false, shouldClose = false; - std::function reopenCallback; + std::atomic sampleRateHasChanged { false }, shouldShutdown { false }, isActive { true }; virtual void updateFormat (bool isFloat) = 0; private: //============================================================================== - struct SessionEventCallback : public ComBaseClassHelper + struct SessionEventCallback final : public ComBaseClassHelper { SessionEventCallback (WASAPIDeviceBase& d) : owner (d) {} - JUCE_COMRESULT OnDisplayNameChanged (LPCWSTR, LPCGUID) { return S_OK; } - JUCE_COMRESULT OnIconPathChanged (LPCWSTR, LPCGUID) { return S_OK; } - JUCE_COMRESULT OnSimpleVolumeChanged (float, BOOL, LPCGUID) { return S_OK; } - JUCE_COMRESULT OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) { return S_OK; } - JUCE_COMRESULT OnGroupingParamChanged (LPCGUID, LPCGUID) { return S_OK; } + JUCE_COMRESULT OnDisplayNameChanged (LPCWSTR, LPCGUID) override { return S_OK; } + JUCE_COMRESULT OnIconPathChanged (LPCWSTR, LPCGUID) override { return S_OK; } + JUCE_COMRESULT OnSimpleVolumeChanged (float, BOOL, LPCGUID) override { return S_OK; } + JUCE_COMRESULT OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) override { return S_OK; } + JUCE_COMRESULT OnGroupingParamChanged (LPCGUID, LPCGUID) override { return S_OK; } - JUCE_COMRESULT OnStateChanged(AudioSessionState state) + JUCE_COMRESULT OnStateChanged (AudioSessionState state) override { - if (state == AudioSessionStateActive) - owner.reopenCallback(); - - if (state == AudioSessionStateInactive || state == AudioSessionStateExpired) - owner.deviceBecameInactive(); + switch (state) + { + case AudioSessionStateInactive: + owner.deviceSessionBecameInactive(); + break; + case AudioSessionStateExpired: + owner.deviceSessionExpired(); + break; + case AudioSessionStateActive: + owner.deviceSessionBecameActive(); + break; + } return S_OK; } - JUCE_COMRESULT OnSessionDisconnected (AudioSessionDisconnectReason reason) + JUCE_COMRESULT OnSessionDisconnected (AudioSessionDisconnectReason reason) override { if (reason == DisconnectReasonFormatChanged) owner.deviceSampleRateChanged(); @@ -565,6 +638,84 @@ class WASAPIDeviceBase return newClient; } + static std::optional getClientMixFormat (ComSmartPtr& client) + { + WAVEFORMATEX* mixFormat = nullptr; + + if (! check (client->GetMixFormat (&mixFormat))) + return {}; + + WAVEFORMATEXTENSIBLE format{}; + + copyWavFormat (format, mixFormat); + CoTaskMemFree (mixFormat); + + return format; + } + + //============================================================================== + void querySupportedBufferSizes (WAVEFORMATEXTENSIBLE format, ComSmartPtr& audioClient) + { + if (isLowLatencyMode (deviceMode)) + { + if (auto audioClient3 = audioClient.getInterface()) + { + UINT32 defaultPeriod = 0, fundamentalPeriod = 0, minPeriod = 0, maxPeriod = 0; + + if (check (audioClient3->GetSharedModeEnginePeriod ((WAVEFORMATEX*) &format, + &defaultPeriod, + &fundamentalPeriod, + &minPeriod, + &maxPeriod))) + { + minBufferSize = (int) minPeriod; + defaultBufferSize = (int) defaultPeriod; + lowLatencyMaxBufferSize = (int) maxPeriod; + lowLatencyBufferSizeMultiple = (int) fundamentalPeriod; + } + } + } + else + { + REFERENCE_TIME defaultPeriod, minPeriod; + + if (! check (audioClient->GetDevicePeriod (&defaultPeriod, &minPeriod))) + return; + + minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate); + defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate); + } + } + + void querySupportedSampleRates (WAVEFORMATEXTENSIBLE format, ComSmartPtr& audioClient) + { + for (auto rate : SampleRateHelpers::getAllSampleRates()) + { + if (rates.contains (rate)) + continue; + + format.Format.nSamplesPerSec = (DWORD) rate; + format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8); + + WAVEFORMATEX* nearestFormat = nullptr; + + if (SUCCEEDED (audioClient->IsFormatSupported (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE + : AUDCLNT_SHAREMODE_SHARED, + (WAVEFORMATEX*) &format, + isExclusiveMode (deviceMode) ? nullptr + : &nearestFormat))) + { + if (nearestFormat != nullptr) + rate = (double) nearestFormat->nSamplesPerSec; + + if (! rates.contains (rate)) + rates.addUsingDefaultSort (rate); + } + + CoTaskMemFree (nearestFormat); + } + } + struct AudioSampleFormat { bool useFloat; @@ -572,12 +723,25 @@ class WASAPIDeviceBase int bytesPerSampleContainer; }; - bool tryFormat (const AudioSampleFormat sampleFormat, IAudioClient* clientToUse, double newSampleRate, - DWORD newMixFormatChannelMask, WAVEFORMATEXTENSIBLE& format) const + static constexpr AudioSampleFormat formatsToTry[] = { + { true, 32, 4 }, + { false, 32, 4 }, + { false, 24, 4 }, + { false, 24, 3 }, + { false, 20, 4 }, + { false, 20, 3 }, + { false, 16, 2 } + }; + + static std::optional tryFormat (const AudioSampleFormat sampleFormat, IAudioClient* clientToUse, + WASAPIDeviceMode mode, int newNumChannels, double newSampleRate, + DWORD newMixFormatChannelMask) + { + WAVEFORMATEXTENSIBLE format; zerostruct (format); - if (numChannels <= 2 && sampleFormat.bitsPerSampleToTry <= 16) + if (newNumChannels <= 2 && sampleFormat.bitsPerSampleToTry <= 16) { format.Format.wFormatTag = WAVE_FORMAT_PCM; } @@ -588,7 +752,7 @@ class WASAPIDeviceBase } format.Format.nSamplesPerSec = (DWORD) newSampleRate; - format.Format.nChannels = (WORD) numChannels; + format.Format.nChannels = (WORD) newNumChannels; format.Format.wBitsPerSample = (WORD) (8 * sampleFormat.bytesPerSampleContainer); format.Samples.wValidBitsPerSample = (WORD) (sampleFormat.bitsPerSampleToTry); format.Format.nBlockAlign = (WORD) (format.Format.nChannels * format.Format.wBitsPerSample / 8); @@ -596,89 +760,159 @@ class WASAPIDeviceBase format.SubFormat = sampleFormat.useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; format.dwChannelMask = newMixFormatChannelMask; - WAVEFORMATEXTENSIBLE* nearestFormat = nullptr; + WAVEFORMATEX* nearestFormat = nullptr; - HRESULT hr = clientToUse->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE - : AUDCLNT_SHAREMODE_SHARED, + HRESULT hr = clientToUse->IsFormatSupported (isExclusiveMode (mode) ? AUDCLNT_SHAREMODE_EXCLUSIVE + : AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*) &format, - useExclusiveMode ? nullptr : (WAVEFORMATEX**) &nearestFormat); + isExclusiveMode (mode) ? nullptr + : &nearestFormat); logFailure (hr); - if (hr == S_FALSE && format.Format.nSamplesPerSec == nearestFormat->Format.nSamplesPerSec) + auto supportsSRC = supportsSampleRateConversion (mode); + + if (hr == S_FALSE + && nearestFormat != nullptr + && (format.Format.nSamplesPerSec == nearestFormat->nSamplesPerSec + || supportsSRC)) { - copyWavFormat (format, (const WAVEFORMATEX*) nearestFormat); + copyWavFormat (format, nearestFormat); + + if (supportsSRC) + { + format.Format.nSamplesPerSec = (DWORD) newSampleRate; + format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nBlockAlign); + } + hr = S_OK; } CoTaskMemFree (nearestFormat); - return check (hr); + + if (hr != S_OK) + return {}; + + return format; } - bool findSupportedFormat (IAudioClient* clientToUse, double newSampleRate, - DWORD newMixFormatChannelMask, WAVEFORMATEXTENSIBLE& format) const + std::optional findSupportedFormat (IAudioClient* clientToUse, int newNumChannels, double newSampleRate) const { - static const AudioSampleFormat formats[] = + for (auto ch = newNumChannels; ch <= maxNumChannels; ++ch) { - { true, 32, 4 }, - { false, 32, 4 }, - { false, 24, 4 }, - { false, 24, 3 }, - { false, 20, 4 }, - { false, 20, 3 }, - { false, 16, 2 } - }; + auto maskWithLowestNBitsSet = static_cast ((1 << ch) - 1); + auto mixFormatChannelMask = (ch == defaultNumChannels ? defaultFormatChannelMask : maskWithLowestNBitsSet); - for (int i = 0; i < numElementsInArray (formats); ++i) - if (tryFormat (formats[i], clientToUse, newSampleRate, newMixFormatChannelMask, format)) - return true; + for (auto const& sampleFormat: formatsToTry) + if (auto format = tryFormat (sampleFormat, clientToUse, deviceMode, ch, newSampleRate, mixFormatChannelMask)) + return format; + } + + return {}; + } + + int queryMaxNumChannels (IAudioClient* clientToUse) const + { + static constexpr auto maxNumChannelsToQuery = static_cast (AudioChannelSet::maxChannelsOfNamedLayout); + const auto fallbackNumChannels = defaultNumChannels; + + if (fallbackNumChannels >= maxNumChannelsToQuery) + return fallbackNumChannels; + + auto result = fallbackNumChannels; + + for (auto ch = maxNumChannelsToQuery; ch > result; --ch) + { + auto channelMask = static_cast ((1 << ch) - 1); + + for (auto rate : rates) + for (auto const& sampleFormat: formatsToTry) + if (auto format = tryFormat (sampleFormat, clientToUse, deviceMode, ch, rate, channelMask)) + result = jmax (static_cast (format->Format.nChannels), result); + } + + return result; + } + + DWORD getStreamFlags() const + { + DWORD streamFlags = 0x40000; /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/ + + if (supportsSampleRateConversion (deviceMode)) + streamFlags |= (0x80000000 /*AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM*/ + | 0x8000000); /*AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY*/ + + return streamFlags; + } + + bool initialiseLowLatencyClient (int bufferSizeSamples, WAVEFORMATEXTENSIBLE format) + { + if (auto audioClient3 = client.getInterface()) + return check (audioClient3->InitializeSharedAudioStream (getStreamFlags(), + (UINT32) bufferSizeSamples, + (WAVEFORMATEX*) &format, + nullptr)); return false; } - bool tryInitialisingWithBufferSize (int bufferSizeSamples) + bool initialiseStandardClient (int bufferSizeSamples, WAVEFORMATEXTENSIBLE format) { - WAVEFORMATEXTENSIBLE format; + REFERENCE_TIME defaultPeriod = 0, minPeriod = 0; + + check (client->GetDevicePeriod (&defaultPeriod, &minPeriod)); + + if (isExclusiveMode (deviceMode) && bufferSizeSamples > 0) + defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec)); - if (findSupportedFormat (client, sampleRate, mixFormatChannelMask, format)) + for (;;) { - REFERENCE_TIME defaultPeriod = 0, minPeriod = 0; + GUID session; + auto hr = client->Initialize (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE + : AUDCLNT_SHAREMODE_SHARED, + getStreamFlags(), + defaultPeriod, + isExclusiveMode (deviceMode) ? defaultPeriod : 0, + (WAVEFORMATEX*) &format, + &session); + + if (check (hr)) + return true; - check (client->GetDevicePeriod (&defaultPeriod, &minPeriod)); + // Handle the "alignment dance" : http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx (see Remarks) + if (hr != MAKE_HRESULT (1, 0x889, 0x19)) // AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED + break; - if (useExclusiveMode && bufferSizeSamples > 0) - defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec)); + UINT32 numFrames = 0; + if (! check (client->GetBufferSize (&numFrames))) + break; - for (;;) - { - GUID session; - HRESULT hr = client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, - 0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, - defaultPeriod, useExclusiveMode ? defaultPeriod : 0, (WAVEFORMATEX*) &format, &session); + // Recreate client + client = nullptr; + client = createClient(); - if (check (hr)) - { - actualNumChannels = format.Format.nChannels; - const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - bytesPerSample = format.Format.wBitsPerSample / 8; - bytesPerFrame = format.Format.nBlockAlign; + defaultPeriod = samplesToRefTime ((int) numFrames, format.Format.nSamplesPerSec); + } - updateFormat (isFloat); - return true; - } + return false; + } - // Handle the "alignment dance" : http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx (see Remarks) - if (hr != MAKE_HRESULT (1, 0x889, 0x19)) // AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED - break; + bool tryInitialisingWithBufferSize (int bufferSizeSamples) + { + if (auto format = findSupportedFormat (client, numChannels, sampleRate)) + { + auto isInitialised = isLowLatencyMode (deviceMode) ? initialiseLowLatencyClient (bufferSizeSamples, *format) + : initialiseStandardClient (bufferSizeSamples, *format); - UINT32 numFrames = 0; - if (! check (client->GetBufferSize (&numFrames))) - break; + if (isInitialised) + { + actualNumChannels = format->Format.nChannels; + const bool isFloat = format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + bytesPerSample = format->Format.wBitsPerSample / 8; + bytesPerFrame = format->Format.nBlockAlign; - // Recreate client - client = nullptr; - client = createClient(); + updateFormat (isFloat); - defaultPeriod = samplesToRefTime (numFrames, format.Format.nSamplesPerSec); + return true; } } @@ -689,15 +923,15 @@ class WASAPIDeviceBase }; //============================================================================== -class WASAPIInputDevice : public WASAPIDeviceBase +class WASAPIInputDevice final : public WASAPIDeviceBase { public: - WASAPIInputDevice (const ComSmartPtr& d, bool exclusiveMode, std::function&& reopenCallback) - : WASAPIDeviceBase (d, exclusiveMode, std::move (reopenCallback)) + WASAPIInputDevice (const ComSmartPtr& d, WASAPIDeviceMode mode) + : WASAPIDeviceBase (d, mode) { } - ~WASAPIInputDevice() + ~WASAPIInputDevice() override { close(); } @@ -714,11 +948,10 @@ class WASAPIInputDevice : public WASAPIDeviceBase closeClient(); captureClient = nullptr; reservoir.reset(); - reservoirReadPos = 0; - reservoirWritePos = 0; + queue = SingleThreadedAbstractFifo(); } - template + template void updateFormatWithType (SourceType*) noexcept { using NativeType = AudioData::Pointer; @@ -733,19 +966,20 @@ class WASAPIInputDevice : public WASAPIDeviceBase else updateFormatWithType ((AudioData::Int16*) nullptr); } - bool start (int userBufferSize) + bool start (int userBufferSizeIn) { - reservoirSize = actualBufferSize + userBufferSize; - reservoirMask = nextPowerOfTwo (reservoirSize) - 1; - reservoir.setSize ((reservoirMask + 1) * bytesPerFrame, true); - reservoirReadPos = 0; - reservoirWritePos = 0; + const auto reservoirSize = nextPowerOfTwo ((int) (actualBufferSize + (UINT32) userBufferSizeIn)); + + queue = SingleThreadedAbstractFifo (reservoirSize); + reservoir.setSize ((size_t) (queue.getSize() * bytesPerFrame), true); xruns = 0; if (! check (client->Start())) return false; purgeInputBuffers(); + isActive = true; + return true; } @@ -755,93 +989,80 @@ class WASAPIInputDevice : public WASAPIDeviceBase UINT32 numSamplesAvailable; DWORD flags; - while (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr) - != MAKE_HRESULT (0, 0x889, 0x1) /* AUDCLNT_S_BUFFER_EMPTY */) + while (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr) != MAKE_HRESULT (0, 0x889, 0x1) /* AUDCLNT_S_BUFFER_EMPTY */) captureClient->ReleaseBuffer (numSamplesAvailable); } - int getNumSamplesInReservoir() const noexcept { return reservoirWritePos.load() - reservoirReadPos.load(); } + int getNumSamplesInReservoir() const noexcept { return queue.getNumReadable(); } void handleDeviceBuffer() { if (numChannels <= 0) return; - uint8* inputData; - UINT32 numSamplesAvailable; - DWORD flags; + uint8* inputData = nullptr; + UINT32 numSamplesAvailable = 0; + DWORD flags = 0; while (check (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr)) && numSamplesAvailable > 0) { if ((flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) xruns++; - int samplesLeft = (int) numSamplesAvailable; + if (numSamplesAvailable > (UINT32) queue.getRemainingSpace()) + { + captureClient->ReleaseBuffer (0); + return; + } + + auto offset = 0; - while (samplesLeft > 0) + for (const auto& block : queue.write ((int) numSamplesAvailable)) { - auto localWrite = reservoirWritePos.load() & reservoirMask; - auto samplesToDo = jmin (samplesLeft, reservoirMask + 1 - localWrite); - auto samplesToDoBytes = samplesToDo * bytesPerFrame; + const auto samplesToDoBytes = block.getLength() * bytesPerFrame; - void* reservoirPtr = addBytesToPointer (reservoir.getData(), localWrite * bytesPerFrame); + auto* reservoirPtr = addBytesToPointer (reservoir.getData(), block.getStart() * bytesPerFrame); if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) != 0) - zeromem (reservoirPtr, samplesToDoBytes); + zeromem (reservoirPtr, (size_t) samplesToDoBytes); else - memcpy (reservoirPtr, inputData, samplesToDoBytes); + memcpy (reservoirPtr, inputData + offset * bytesPerFrame, (size_t) samplesToDoBytes); - reservoirWritePos += samplesToDo; - inputData += samplesToDoBytes; - samplesLeft -= samplesToDo; + offset += block.getLength(); } - if (getNumSamplesInReservoir() > reservoirSize) - reservoirReadPos = reservoirWritePos.load() - reservoirSize; - captureClient->ReleaseBuffer (numSamplesAvailable); } } - void copyBuffersFromReservoir (float** destBuffers, int numDestBuffers, int bufferSize) + void copyBuffersFromReservoir (float* const* destBuffers, const int numDestBuffers, const int bufferSize) { - if ((numChannels <= 0 && bufferSize == 0) || reservoir.getSize() == 0) + if ((numChannels <= 0 && bufferSize == 0) || reservoir.isEmpty()) return; - int offset = jmax (0, bufferSize - getNumSamplesInReservoir()); + auto offset = jmax (0, bufferSize - queue.getNumReadable()); if (offset > 0) - { for (int i = 0; i < numDestBuffers; ++i) - zeromem (destBuffers[i], offset * sizeof (float)); + zeromem (destBuffers[i], (size_t) offset * sizeof (float)); - bufferSize -= offset; - reservoirReadPos -= offset / 2; - } - - while (bufferSize > 0) + for (const auto& block : queue.read (jmin (queue.getNumReadable(), bufferSize))) { - auto localRead = reservoirReadPos.load() & reservoirMask; - auto samplesToDo = jmin (bufferSize, getNumSamplesInReservoir(), reservoirMask + 1 - localRead); - - if (samplesToDo <= 0) - break; - - auto reservoirOffset = localRead * bytesPerFrame; - - for (int i = 0; i < numDestBuffers; ++i) - converter->convertSamples (destBuffers[i] + offset, 0, addBytesToPointer (reservoir.getData(), reservoirOffset), channelMaps.getUnchecked(i), samplesToDo); - - bufferSize -= samplesToDo; - offset += samplesToDo; - reservoirReadPos += samplesToDo; + for (auto i = 0; i < numDestBuffers; ++i) + converter->convertSamples (destBuffers[i] + offset, + 0, + addBytesToPointer (reservoir.getData(), block.getStart() * bytesPerFrame), + channelMaps.getUnchecked (i), + block.getLength()); + + offset += block.getLength(); } } ComSmartPtr captureClient; MemoryBlock reservoir; - int reservoirSize, reservoirMask, xruns; - std::atomic reservoirReadPos, reservoirWritePos; + SingleThreadedAbstractFifo queue; + int xruns = 0; std::unique_ptr converter; @@ -850,15 +1071,15 @@ class WASAPIInputDevice : public WASAPIDeviceBase }; //============================================================================== -class WASAPIOutputDevice : public WASAPIDeviceBase +class WASAPIOutputDevice final : public WASAPIDeviceBase { public: - WASAPIOutputDevice (const ComSmartPtr& d, bool exclusiveMode, std::function&& reopenCallback) - : WASAPIDeviceBase (d, exclusiveMode, std::move (reopenCallback)) + WASAPIOutputDevice (const ComSmartPtr& d, WASAPIDeviceMode mode) + : WASAPIDeviceBase (d, mode) { } - ~WASAPIOutputDevice() + ~WASAPIOutputDevice() override { close(); } @@ -876,7 +1097,7 @@ class WASAPIOutputDevice : public WASAPIDeviceBase renderClient = nullptr; } - template + template void updateFormatWithType (DestType*) { using NativeType = AudioData::Pointer; @@ -896,10 +1117,15 @@ class WASAPIOutputDevice : public WASAPIDeviceBase auto samplesToDo = getNumSamplesAvailableToCopy(); uint8* outputData; - if (check (renderClient->GetBuffer (samplesToDo, &outputData))) - renderClient->ReleaseBuffer (samplesToDo, AUDCLNT_BUFFERFLAGS_SILENT); + if (check (renderClient->GetBuffer ((UINT32) samplesToDo, &outputData))) + renderClient->ReleaseBuffer ((UINT32) samplesToDo, AUDCLNT_BUFFERFLAGS_SILENT); + + if (! check (client->Start())) + return false; + + isActive = true; - return check (client->Start()); + return true; } int getNumSamplesAvailableToCopy() const @@ -907,18 +1133,18 @@ class WASAPIOutputDevice : public WASAPIDeviceBase if (numChannels <= 0) return 0; - if (! useExclusiveMode) + if (! isExclusiveMode (deviceMode)) { UINT32 padding = 0; if (check (client->GetCurrentPadding (&padding))) - return actualBufferSize - (int) padding; + return (int) actualBufferSize - (int) padding; } - return actualBufferSize; + return (int) actualBufferSize; } - void copyBuffers (const float** srcBuffers, int numSrcBuffers, int bufferSize, + void copyBuffers (const float* const* srcBuffers, int numSrcBuffers, int bufferSize, WASAPIInputDevice* inputDevice, Thread& thread) { if (numChannels <= 0) @@ -929,7 +1155,7 @@ class WASAPIOutputDevice : public WASAPIDeviceBase while (bufferSize > 0) { // This is needed in order not to drop any input data if the output device endpoint buffer was full - if ((! useExclusiveMode) && inputDevice != nullptr + if ((! isExclusiveMode (deviceMode)) && inputDevice != nullptr && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0) inputDevice->handleDeviceBuffer(); @@ -944,14 +1170,17 @@ class WASAPIOutputDevice : public WASAPIDeviceBase break; } - if (useExclusiveMode && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT) + if (isExclusiveMode (deviceMode) && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT) break; + const auto numChannelsToCopy = jmin (actualNumChannels, numSrcBuffers); + jassert (numChannelsToCopy <= channelMaps.size()); + uint8* outputData = nullptr; if (check (renderClient->GetBuffer ((UINT32) samplesToDo, &outputData))) { - for (int i = 0; i < numSrcBuffers; ++i) - converter->convertSamples (outputData, channelMaps.getUnchecked(i), srcBuffers[i] + offset, 0, samplesToDo); + for (int i = 0; i < numChannelsToCopy; ++i) + converter->convertSamples (outputData, channelMaps.getUnchecked (i), srcBuffers[i] + offset, 0, samplesToDo); renderClient->ReleaseBuffer ((UINT32) samplesToDo, 0); } @@ -969,25 +1198,25 @@ class WASAPIOutputDevice : public WASAPIDeviceBase }; //============================================================================== -class WASAPIAudioIODevice : public AudioIODevice, - public Thread, - private AsyncUpdater +class WASAPIAudioIODevice final : public AudioIODevice, + public Thread, + private AsyncUpdater { public: WASAPIAudioIODevice (const String& deviceName, - const String& typeName, + const String& typeNameIn, const String& outputDeviceID, const String& inputDeviceID, - bool exclusiveMode) - : AudioIODevice (deviceName, typeName), + WASAPIDeviceMode mode) + : AudioIODevice (deviceName, typeNameIn), Thread ("JUCE WASAPI"), outputDeviceId (outputDeviceID), inputDeviceId (inputDeviceID), - useExclusiveMode (exclusiveMode) + deviceMode (mode) { } - ~WASAPIAudioIODevice() + ~WASAPIAudioIODevice() override { cancelPendingUpdate(); close(); @@ -1002,21 +1231,48 @@ class WASAPIAudioIODevice : public AudioIODevice, { jassert (inputDevice != nullptr || outputDevice != nullptr); + sampleRates.clear(); + if (inputDevice != nullptr && outputDevice != nullptr) { defaultSampleRate = jmin (inputDevice->defaultSampleRate, outputDevice->defaultSampleRate); - minBufferSize = jmin (inputDevice->minBufferSize, outputDevice->minBufferSize); + minBufferSize = jmax (inputDevice->minBufferSize, outputDevice->minBufferSize); defaultBufferSize = jmax (inputDevice->defaultBufferSize, outputDevice->defaultBufferSize); - sampleRates = inputDevice->rates; - sampleRates.removeValuesNotIn (outputDevice->rates); + + if (isLowLatencyMode (deviceMode)) + { + lowLatencyMaxBufferSize = jmin (inputDevice->lowLatencyMaxBufferSize, outputDevice->lowLatencyMaxBufferSize); + lowLatencyBufferSizeMultiple = jmax (inputDevice->lowLatencyBufferSizeMultiple, outputDevice->lowLatencyBufferSizeMultiple); + } + + sampleRates.addArray (inputDevice->rates); + + if (supportsSampleRateConversion (deviceMode)) + { + for (auto r : outputDevice->rates) + if (! sampleRates.contains (r)) + sampleRates.addUsingDefaultSort (r); + } + else + { + sampleRates.removeValuesNotIn (outputDevice->rates); + } } else { - WASAPIDeviceBase* d = inputDevice != nullptr ? static_cast (inputDevice.get()) - : static_cast (outputDevice.get()); + auto* d = inputDevice != nullptr ? static_cast (inputDevice.get()) + : static_cast (outputDevice.get()); + defaultSampleRate = d->defaultSampleRate; minBufferSize = d->minBufferSize; defaultBufferSize = d->defaultBufferSize; + + if (isLowLatencyMode (deviceMode)) + { + lowLatencyMaxBufferSize = d->lowLatencyMaxBufferSize; + lowLatencyBufferSizeMultiple = d->lowLatencyBufferSizeMultiple; + } + sampleRates = d->rates; } @@ -1026,13 +1282,28 @@ class WASAPIAudioIODevice : public AudioIODevice, if (minBufferSize != defaultBufferSize) bufferSizes.addUsingDefaultSort (minBufferSize); - int n = 64; - for (int i = 0; i < 40; ++i) + if (isLowLatencyMode (deviceMode)) { - if (n >= minBufferSize && n <= 2048 && ! bufferSizes.contains (n)) - bufferSizes.addUsingDefaultSort (n); + auto size = minBufferSize; - n += (n < 512) ? 32 : (n < 1024 ? 64 : 128); + while (size < lowLatencyMaxBufferSize) + { + size += lowLatencyBufferSizeMultiple; + + if (! bufferSizes.contains (size)) + bufferSizes.addUsingDefaultSort (size); + } + } + else + { + int n = 64; + for (int i = 0; i < 40; ++i) + { + if (n >= minBufferSize && n <= 2048 && ! bufferSizes.contains (n)) + bufferSizes.addUsingDefaultSort (n); + + n += (n < 512) ? 32 : (n < 1024 ? 64 : 128); + } } return true; @@ -1046,7 +1317,7 @@ class WASAPIAudioIODevice : public AudioIODevice, StringArray outChannels; if (outputDevice != nullptr) - for (int i = 1; i <= outputDevice->actualNumChannels; ++i) + for (int i = 1; i <= outputDevice->maxNumChannels; ++i) outChannels.add ("Output channel " + String (i)); return outChannels; @@ -1057,7 +1328,7 @@ class WASAPIAudioIODevice : public AudioIODevice, StringArray inChannels; if (inputDevice != nullptr) - for (int i = 1; i <= inputDevice->actualNumChannels; ++i) + for (int i = 1; i <= inputDevice->maxNumChannels; ++i) inChannels.add ("Input channel " + String (i)); return inChannels; @@ -1077,6 +1348,16 @@ class WASAPIAudioIODevice : public AudioIODevice, String getLastError() override { return lastError; } int getXRunCount() const noexcept override { return inputDevice != nullptr ? inputDevice->xruns : -1; } + std::optional getDefaultOutputChannels() const override + { + return outputDevice != nullptr ? outputDevice->getDefaultLayout() : std::nullopt; + } + + std::optional getDefaultInputChannels() const override + { + return inputDevice != nullptr ? inputDevice->getDefaultLayout() : std::nullopt; + } + String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, int bufferSizeSamples) override { @@ -1085,7 +1366,7 @@ class WASAPIAudioIODevice : public AudioIODevice, if (sampleRates.size() == 0 && inputDevice != nullptr && outputDevice != nullptr) { - lastError = TRANS("The input and output devices don't share a common sample rate!"); + lastError = TRANS ("The input and output devices don't share a common sample rate!"); return lastError; } @@ -1096,37 +1377,38 @@ class WASAPIAudioIODevice : public AudioIODevice, if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels, bufferSizeSamples)) { - lastError = TRANS("Couldn't open the input device!"); + lastError = TRANS ("Couldn't open the input device!"); return lastError; } if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels, bufferSizeSamples)) { close(); - lastError = TRANS("Couldn't open the output device!"); + lastError = TRANS ("Couldn't open the output device!"); return lastError; } - if (useExclusiveMode) + if (isExclusiveMode (deviceMode)) { // This is to make sure that the callback uses actualBufferSize in case of exclusive mode if (inputDevice != nullptr && outputDevice != nullptr && inputDevice->actualBufferSize != outputDevice->actualBufferSize) { close(); - lastError = TRANS("Couldn't open the output device (buffer size mismatch)"); + lastError = TRANS ("Couldn't open the output device (buffer size mismatch)"); return lastError; } - currentBufferSizeSamples = outputDevice != nullptr ? outputDevice->actualBufferSize - : inputDevice->actualBufferSize; + currentBufferSizeSamples = (int) (outputDevice != nullptr ? outputDevice->actualBufferSize + : inputDevice->actualBufferSize); } if (inputDevice != nullptr) ResetEvent (inputDevice->clientEvent); if (outputDevice != nullptr) ResetEvent (outputDevice->clientEvent); - deviceBecameInactive = false; + shouldShutdown = false; + deviceSampleRateChanged = false; - startThread (8); + startThread (Priority::high); Thread::sleep (5); if (inputDevice != nullptr && inputDevice->client != nullptr) @@ -1136,7 +1418,7 @@ class WASAPIAudioIODevice : public AudioIODevice, if (! inputDevice->start (currentBufferSizeSamples)) { close(); - lastError = TRANS("Couldn't start the input device!"); + lastError = TRANS ("Couldn't start the input device!"); return lastError; } } @@ -1148,7 +1430,7 @@ class WASAPIAudioIODevice : public AudioIODevice, if (! outputDevice->start()) { close(); - lastError = TRANS("Couldn't start the output device!"); + lastError = TRANS ("Couldn't start the output device!"); return lastError; } } @@ -1217,7 +1499,7 @@ class WASAPIAudioIODevice : public AudioIODevice, JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadCharacteristicsW, avSetMmThreadCharacteristics, HANDLE, (LPCWSTR, LPDWORD)) JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, (HANDLE, AVRT_PRIORITY)) - if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0) + if (avSetMmThreadCharacteristics != nullptr && avSetMmThreadPriority != nullptr) { DWORD dummy = 0; @@ -1233,7 +1515,6 @@ class WASAPIAudioIODevice : public AudioIODevice, auto bufferSize = currentBufferSizeSamples; auto numInputBuffers = getActiveInputChannels().countNumberOfSetBits(); auto numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits(); - bool sampleRateHasChanged = false; AudioBuffer ins (jmax (1, numInputBuffers), bufferSize + 32); AudioBuffer outs (jmax (1, numOutputBuffers), bufferSize + 32); @@ -1244,14 +1525,23 @@ class WASAPIAudioIODevice : public AudioIODevice, while (! threadShouldExit()) { - if (outputDevice != nullptr && outputDevice->shouldClose) - deviceBecameInactive = true; - - if (inputDevice != nullptr && ! deviceBecameInactive) + if ((outputDevice != nullptr && outputDevice->shouldShutdown) + || (inputDevice != nullptr && inputDevice->shouldShutdown)) { - if (inputDevice->shouldClose) - deviceBecameInactive = true; + shouldShutdown = true; + triggerAsyncUpdate(); + + break; + } + + auto inputDeviceActive = (inputDevice != nullptr && inputDevice->isActive); + auto outputDeviceActive = (outputDevice != nullptr && outputDevice->isActive); + if (! inputDeviceActive && ! outputDeviceActive) + continue; + + if (inputDeviceActive) + { if (outputDevice == nullptr) { if (WaitForSingleObject (inputDevice->clientEvent, 1000) == WAIT_TIMEOUT) @@ -1264,7 +1554,7 @@ class WASAPIAudioIODevice : public AudioIODevice, } else { - if (useExclusiveMode && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0) + if (isExclusiveMode (deviceMode) && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0) inputDevice->handleDeviceBuffer(); } @@ -1272,39 +1562,40 @@ class WASAPIAudioIODevice : public AudioIODevice, if (inputDevice->sampleRateHasChanged) { - sampleRateHasChanged = true; - sampleRateChangedByOutput = false; + deviceSampleRateChanged = true; + triggerAsyncUpdate(); + + break; } } - if (! deviceBecameInactive) { const ScopedTryLock sl (startStopLock); if (sl.isLocked() && isStarted) - callback->audioDeviceIOCallback (const_cast (inputBuffers), numInputBuffers, - outputBuffers, numOutputBuffers, bufferSize); + callback->audioDeviceIOCallbackWithContext (inputBuffers, + numInputBuffers, + outputBuffers, + numOutputBuffers, + bufferSize, + {}); else outs.clear(); } - if (outputDevice != nullptr && ! deviceBecameInactive) + if (outputDeviceActive) { // Note that this function is handed the input device so it can check for the event and make sure // the input reservoir is filled up correctly even when bufferSize > device actualBufferSize - outputDevice->copyBuffers (const_cast (outputBuffers), numOutputBuffers, bufferSize, inputDevice.get(), *this); + outputDevice->copyBuffers (outputBuffers, numOutputBuffers, bufferSize, inputDevice.get(), *this); if (outputDevice->sampleRateHasChanged) { - sampleRateHasChanged = true; - sampleRateChangedByOutput = true; - } - } + deviceSampleRateChanged = true; + triggerAsyncUpdate(); - if (sampleRateHasChanged || deviceBecameInactive) - { - triggerAsyncUpdate(); - break; // Quit the thread... will restart it later! + break; + } } } } @@ -1317,9 +1608,10 @@ class WASAPIAudioIODevice : public AudioIODevice, // Device stats... std::unique_ptr inputDevice; std::unique_ptr outputDevice; - const bool useExclusiveMode; + WASAPIDeviceMode deviceMode; double defaultSampleRate = 0; int minBufferSize = 0, defaultBufferSize = 0; + int lowLatencyMaxBufferSize = 0, lowLatencyBufferSizeMultiple = 0; int latencyIn = 0, latencyOut = 0; Array sampleRates; Array bufferSizes; @@ -1332,7 +1624,7 @@ class WASAPIAudioIODevice : public AudioIODevice, AudioIODeviceCallback* callback = {}; CriticalSection startStopLock; - bool sampleRateChangedByOutput = false, deviceBecameInactive = false; + std::atomic shouldShutdown { false }, deviceSampleRateChanged { false }; BigInteger lastKnownInputChannels, lastKnownOutputChannels; @@ -1368,57 +1660,54 @@ class WASAPIAudioIODevice : public AudioIODevice, auto flow = getDataFlow (device); - auto deviceReopenCallback = [this] - { - if (deviceBecameInactive) - { - deviceBecameInactive = false; - MessageManager::callAsync ([this] { reopenDevices(); }); - } - }; - if (deviceId == inputDeviceId && flow == eCapture) - inputDevice.reset (new WASAPIInputDevice (device, useExclusiveMode, deviceReopenCallback)); + inputDevice.reset (new WASAPIInputDevice (device, deviceMode)); else if (deviceId == outputDeviceId && flow == eRender) - outputDevice.reset (new WASAPIOutputDevice (device, useExclusiveMode, deviceReopenCallback)); + outputDevice.reset (new WASAPIOutputDevice (device, deviceMode)); } return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk())) && (inputDeviceId.isEmpty() || (inputDevice != nullptr && inputDevice->isOk())); } - void reopenDevices() + //============================================================================== + void handleAsyncUpdate() override { - outputDevice = nullptr; - inputDevice = nullptr; + auto closeDevices = [this] + { + close(); - initialise(); + outputDevice = nullptr; + inputDevice = nullptr; + }; - open (lastKnownInputChannels, lastKnownOutputChannels, - getChangedSampleRate(), currentBufferSizeSamples); + if (shouldShutdown) + { + closeDevices(); + } + else if (deviceSampleRateChanged) + { + auto sampleRateChangedByInput = (inputDevice != nullptr && inputDevice->sampleRateHasChanged); - start (callback); - } + closeDevices(); + initialise(); - //============================================================================== - void handleAsyncUpdate() override - { - stop(); + auto changedSampleRate = [this, sampleRateChangedByInput]() + { + if (inputDevice != nullptr && sampleRateChangedByInput) + return inputDevice->defaultSampleRate; - // sample rate change - if (! deviceBecameInactive) - reopenDevices(); - } + if (outputDevice != nullptr && ! sampleRateChangedByInput) + return outputDevice->defaultSampleRate; - double getChangedSampleRate() const - { - if (outputDevice != nullptr && sampleRateChangedByOutput) - return outputDevice->defaultSampleRate; + return 0.0; + }(); - if (inputDevice != nullptr && ! sampleRateChangedByOutput) - return inputDevice->defaultSampleRate; + open (lastKnownInputChannels, lastKnownOutputChannels, + changedSampleRate, currentBufferSizeSamples); - return 0.0; + start (callback); + } } //============================================================================== @@ -1427,18 +1716,16 @@ class WASAPIAudioIODevice : public AudioIODevice, //============================================================================== -class WASAPIAudioIODeviceType : public AudioIODeviceType, - private DeviceChangeDetector +class WASAPIAudioIODeviceType final : public AudioIODeviceType { public: - WASAPIAudioIODeviceType (bool exclusive) - : AudioIODeviceType (exclusive ? "Windows Audio (Exclusive Mode)" : "Windows Audio"), - DeviceChangeDetector (L"Windows Audio"), - exclusiveMode (exclusive) + explicit WASAPIAudioIODeviceType (WASAPIDeviceMode mode) + : AudioIODeviceType (getDeviceTypename (mode)), + deviceMode (mode) { } - ~WASAPIAudioIODeviceType() + ~WASAPIAudioIODeviceType() override { if (notifyClient != nullptr) enumerator->UnregisterEndpointNotificationCallback (notifyClient); @@ -1448,22 +1735,15 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType, void scanForDevices() override { hasScanned = true; - - outputDeviceNames.clear(); - inputDeviceNames.clear(); - outputDeviceIds.clear(); - inputDeviceIds.clear(); - - scan (outputDeviceNames, inputDeviceNames, - outputDeviceIds, inputDeviceIds); + devices = scan(); } StringArray getDeviceNames (bool wantInputNames) const override { jassert (hasScanned); // need to call scanForDevices() before doing this - return wantInputNames ? inputDeviceNames - : outputDeviceNames; + return wantInputNames ? devices.inputDeviceNames + : devices.outputDeviceNames; } int getDefaultDeviceIndex (bool /*forInput*/) const override @@ -1477,8 +1757,8 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType, jassert (hasScanned); // need to call scanForDevices() before doing this if (auto d = dynamic_cast (device)) - return asInput ? inputDeviceIds.indexOf (d->inputDeviceId) - : outputDeviceIds.indexOf (d->outputDeviceId); + return asInput ? devices.inputDeviceIds .indexOf (d->inputDeviceId) + : devices.outputDeviceIds.indexOf (d->outputDeviceId); return -1; } @@ -1492,17 +1772,17 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType, std::unique_ptr device; - auto outputIndex = outputDeviceNames.indexOf (outputDeviceName); - auto inputIndex = inputDeviceNames.indexOf (inputDeviceName); + auto outputIndex = devices.outputDeviceNames.indexOf (outputDeviceName); + auto inputIndex = devices.inputDeviceNames .indexOf (inputDeviceName); if (outputIndex >= 0 || inputIndex >= 0) { device.reset (new WASAPIAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName : inputDeviceName, getTypeName(), - outputDeviceIds [outputIndex], - inputDeviceIds [inputIndex], - exclusiveMode)); + devices.outputDeviceIds[outputIndex], + devices.inputDeviceIds [inputIndex], + deviceMode)); if (! device->initialise()) device = nullptr; @@ -1512,26 +1792,40 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType, } //============================================================================== - StringArray outputDeviceNames, outputDeviceIds; - StringArray inputDeviceNames, inputDeviceIds; + struct Devices + { + StringArray outputDeviceNames, outputDeviceIds; + StringArray inputDeviceNames, inputDeviceIds; + + auto tie() const + { + return std::tie (outputDeviceNames, outputDeviceIds, inputDeviceNames, inputDeviceIds); + } + + bool operator== (const Devices& other) const { return tie() == other.tie(); } + bool operator!= (const Devices& other) const { return tie() != other.tie(); } + }; + + Devices devices; private: - const bool exclusiveMode; + DeviceChangeDetector deviceChangeDetector { L"Windows Audio", [this] { systemDeviceChanged(); } }; + WASAPIDeviceMode deviceMode; bool hasScanned = false; ComSmartPtr enumerator; //============================================================================== - class ChangeNotificationClient : public ComBaseClassHelper + class ChangeNotificationClient final : public ComBaseClassHelper { public: - ChangeNotificationClient (WASAPIAudioIODeviceType* d) - : ComBaseClassHelper (0), device (d) {} + explicit ChangeNotificationClient (WASAPIAudioIODeviceType* d) + : ComBaseClassHelper (0), device (d) {} - HRESULT STDMETHODCALLTYPE OnDeviceAdded (LPCWSTR) { return notify(); } - HRESULT STDMETHODCALLTYPE OnDeviceRemoved (LPCWSTR) { return notify(); } - HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) { return notify(); } - HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) { return notify(); } - HRESULT STDMETHODCALLTYPE OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); } + JUCE_COMRESULT OnDeviceAdded (LPCWSTR) override { return notify(); } + JUCE_COMRESULT OnDeviceRemoved (LPCWSTR) override { return notify(); } + JUCE_COMRESULT OnDeviceStateChanged (LPCWSTR, DWORD) override { return notify(); } + JUCE_COMRESULT OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) override { return notify(); } + JUCE_COMRESULT OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) override { return notify(); } private: WeakReference device; @@ -1539,7 +1833,7 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType, HRESULT notify() { if (device != nullptr) - device->triggerAsyncDeviceChangeCallback(); + device->deviceChangeDetector.triggerAsyncDeviceChangeCallback(); return S_OK; } @@ -1573,15 +1867,12 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType, } //============================================================================== - void scan (StringArray& outDeviceNames, - StringArray& inDeviceNames, - StringArray& outDeviceIds, - StringArray& inDeviceIds) + Devices scan() { if (enumerator == nullptr) { if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) - return; + return {}; notifyClient = new ChangeNotificationClient (this); enumerator->RegisterEndpointNotificationCallback (notifyClient); @@ -1595,7 +1886,9 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType, if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())) && check (deviceCollection->GetCount (&numDevices)))) - return; + return {}; + + Devices result; for (UINT32 i = 0; i < numDevices; ++i) { @@ -1635,40 +1928,44 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType, if (flow == eRender) { const int index = (deviceId == defaultRenderer) ? 0 : -1; - outDeviceIds.insert (index, deviceId); - outDeviceNames.insert (index, name); + result.outputDeviceIds.insert (index, deviceId); + result.outputDeviceNames.insert (index, name); } else if (flow == eCapture) { const int index = (deviceId == defaultCapture) ? 0 : -1; - inDeviceIds.insert (index, deviceId); - inDeviceNames.insert (index, name); + result.inputDeviceIds.insert (index, deviceId); + result.inputDeviceNames.insert (index, name); } } - inDeviceNames.appendNumbersToDuplicates (false, false); - outDeviceNames.appendNumbersToDuplicates (false, false); + result.inputDeviceNames .appendNumbersToDuplicates (false, false); + result.outputDeviceNames.appendNumbersToDuplicates (false, false); + + return result; } //============================================================================== - void systemDeviceChanged() override + void systemDeviceChanged() { - StringArray newOutNames, newInNames, newOutIds, newInIds; - scan (newOutNames, newInNames, newOutIds, newInIds); + const auto newDevices = scan(); - if (newOutNames != outputDeviceNames - || newInNames != inputDeviceNames - || newOutIds != outputDeviceIds - || newInIds != inputDeviceIds) + if (std::exchange (devices, newDevices) != newDevices) { hasScanned = true; - outputDeviceNames = newOutNames; - inputDeviceNames = newInNames; - outputDeviceIds = newOutIds; - inputDeviceIds = newInIds; + callDeviceChangeListeners(); } + } + + //============================================================================== + static String getDeviceTypename (WASAPIDeviceMode mode) + { + if (mode == WASAPIDeviceMode::shared) return "Windows Audio"; + if (mode == WASAPIDeviceMode::sharedLowLatency) return "Windows Audio (Low Latency Mode)"; + if (mode == WASAPIDeviceMode::exclusive) return "Windows Audio (Exclusive Mode)"; - callDeviceChangeListeners(); + jassertfalse; + return {}; } //============================================================================== @@ -1727,20 +2024,7 @@ struct MMDeviceMasterVolume JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MMDeviceMasterVolume) }; -} - -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode) -{ - #if ! JUCE_WASAPI_EXCLUSIVE - if (exclusiveMode) - return nullptr; - #endif - - return SystemStats::getOperatingSystemType() >= SystemStats::WinVista - ? new WasapiClasses::WASAPIAudioIODeviceType (exclusiveMode) - : nullptr; -} +} // namespace WasapiClasses //============================================================================== #define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1 @@ -1749,4 +2033,6 @@ bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return WasapiCla bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return WasapiClasses::MMDeviceMasterVolume().isMuted(); } bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return WasapiClasses::MMDeviceMasterVolume().setMuted (mute); } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp deleted file mode 100644 index 570eab22..00000000 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp +++ /dev/null @@ -1,707 +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 -{ - -//============================================================================== -// 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,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;") - -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 (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 (getName, "getName", "()Ljava/lang/String;") - -DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiPort, "com/roli/juce/JuceMidiSupport$JuceMidiPort", 23) -#undef JNI_CLASS_MEMBERS - -//============================================================================== -class AndroidMidiInput -{ -public: - 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))) - { - } - - ~AndroidMidiInput() - { - if (jobject d = javaMidiDevice.get()) - { - getEnv()->CallVoidMethod (d, JuceMidiPort.close); - javaMidiDevice.clear(); - } - } - - bool isOpen() const noexcept - { - return javaMidiDevice != nullptr; - } - - void start() - { - if (jobject d = javaMidiDevice.get()) - getEnv()->CallVoidMethod (d, JuceMidiPort.start); - } - - void stop() - { - if (jobject d = javaMidiDevice.get()) - getEnv()->CallVoidMethod (d, JuceMidiPort.stop); - - 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); - auto* data = env->GetByteArrayElements (byteArray, nullptr); - - HeapBlock buffer (static_cast (len)); - std::memcpy (buffer.get(), data + offset, static_cast (len)); - - midiConcatenator.pushMidiData (buffer.get(), - len, static_cast (timestamp) * 1.0e-9, - juceMidiInput, *callback); - - env->ReleaseByteArrayElements (byteArray, data, 0); - } - - static void handleReceive (JNIEnv*, jobject, jlong host, jbyteArray byteArray, - jint offset, jint len, jlong timestamp) - { - auto* myself = reinterpret_cast (host); - - myself->handleMidi (byteArray, offset, len, timestamp); - } - -private: - MidiInput* juceMidiInput; - MidiInputCallback* callback; - MidiDataConcatenator midiConcatenator; - GlobalRef javaMidiDevice; -}; - -//============================================================================== -class AndroidMidiOutput -{ -public: - AndroidMidiOutput (const LocalRef& midiDevice) - : javaMidiDevice (midiDevice) - { - } - - ~AndroidMidiOutput() - { - if (jobject d = javaMidiDevice.get()) - { - getEnv()->CallVoidMethod (d, JuceMidiPort.close); - javaMidiDevice.clear(); - } - } - - void send (jbyteArray byteArray, jint offset, jint len) - { - if (jobject d = javaMidiDevice.get()) - getEnv()->CallVoidMethod (d, - JuceMidiPort.sendMidi, - 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; -}; - -//============================================================================== -#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ - CALLBACK (AndroidMidiInput::handleReceive, "handleReceive", "(J[BIIJ)V" ) - -DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiInputPort, "com/roli/juce/JuceMidiSupport$JuceMidiInputPort", 23) -#undef JNI_CLASS_MEMBERS - -//============================================================================== -class AndroidMidiDeviceManager -{ -public: - AndroidMidiDeviceManager() - : deviceManager (LocalRef(getEnv()->CallStaticObjectMethod (JuceMidiSupport, - JuceMidiSupport.getAndroidMidiDeviceManager, - getAppContext().get()))) - { - } - - Array getDevices (bool input) - { - if (jobject dm = deviceManager.get()) - { - 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 {}; - } - - AndroidMidiInput* openMidiInputPortWithID (int deviceID, MidiInput* juceMidiInput, juce::MidiInputCallback* callback) - { - if (auto dm = deviceManager.get()) - { - std::unique_ptr androidMidiInput (new AndroidMidiInput (juceMidiInput, deviceID, callback, dm)); - - if (androidMidiInput->isOpen()) - return androidMidiInput.release(); - } - - return nullptr; - } - - AndroidMidiOutput* openMidiOutputPortWithID (int deviceID) - { - if (auto dm = deviceManager.get()) - if (auto javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithID, (jint) deviceID)) - return new AndroidMidiOutput (LocalRef(javaMidiPort)); - - return nullptr; - } - -private: - GlobalRef deviceManager; -}; - -//============================================================================== -Array MidiInput::getAvailableDevices() -{ - 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)) - { - midiInput->internal = port; - midiInput->setName (port->getName()); - - return midiInput; - } - - return {}; -} - -StringArray MidiInput::getDevices() -{ - if (getAndroidSDKVersion() < 23) - return {}; - - StringArray deviceNames; - - for (auto& d : getAvailableDevices()) - deviceNames.add (d.name); - - return deviceNames; -} - -int MidiInput::getDefaultDeviceIndex() -{ - 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; - - if (auto* port = manager.openMidiOutputPortWithID (deviceIdentifier.getIntValue())) - { - std::unique_ptr midiOutput (new MidiOutput ({}, deviceIdentifier)); - midiOutput->internal = port; - midiOutput->setName (port->getName()); - - return midiOutput; - } - - return {}; -} - -StringArray MidiOutput::getDevices() -{ - if (getAndroidSDKVersion() < 23) - return {}; - - 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() -{ - stopBackgroundThread(); - - delete reinterpret_cast (internal); -} - -void MidiOutput::sendMessageNow (const MidiMessage& message) -{ - if (auto* androidMidi = reinterpret_cast(internal)) - { - auto* env = getEnv(); - auto messageSize = message.getRawDataSize(); - - LocalRef messageContent (env->NewByteArray (messageSize)); - auto content = messageContent.get(); - - auto* rawBytes = env->GetByteArrayElements (content, nullptr); - std::memcpy (rawBytes, message.getRawData(), static_cast (messageSize)); - env->ReleaseByteArrayElements (content, rawBytes, 0); - - androidMidi->send (content, (jint) 0, (jint) messageSize); - } -} - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp deleted file mode 100644 index fd9645e1..00000000 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ /dev/null @@ -1,2250 +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 -{ - -#if JUCE_COREAUDIO_LOGGING_ENABLED - #define JUCE_COREAUDIOLOG(a) { String camsg ("CoreAudio: "); camsg << a; Logger::writeToLog (camsg); } -#else - #define JUCE_COREAUDIOLOG(a) -#endif - -#ifdef __clang__ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wnonnull" // avoid some spurious 10.11 SDK warnings -#endif - -//============================================================================== -struct SystemVol -{ - SystemVol (AudioObjectPropertySelector selector) noexcept - : outputDeviceID (kAudioObjectUnknown) - { - addr.mScope = kAudioObjectPropertyScopeGlobal; - addr.mElement = kAudioObjectPropertyElementMaster; - addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - - if (AudioObjectHasProperty (kAudioObjectSystemObject, &addr)) - { - UInt32 deviceIDSize = sizeof (outputDeviceID); - OSStatus status = AudioObjectGetPropertyData (kAudioObjectSystemObject, &addr, 0, nullptr, &deviceIDSize, &outputDeviceID); - - if (status == noErr) - { - addr.mElement = kAudioObjectPropertyElementMaster; - addr.mSelector = selector; - addr.mScope = kAudioDevicePropertyScopeOutput; - - if (! AudioObjectHasProperty (outputDeviceID, &addr)) - outputDeviceID = kAudioObjectUnknown; - } - } - } - - float getGain() const noexcept - { - Float32 gain = 0; - - if (outputDeviceID != kAudioObjectUnknown) - { - UInt32 size = sizeof (gain); - AudioObjectGetPropertyData (outputDeviceID, &addr, 0, nullptr, &size, &gain); - } - - return (float) gain; - } - - bool setGain (float gain) const noexcept - { - if (outputDeviceID != kAudioObjectUnknown && canSetVolume()) - { - Float32 newVolume = gain; - UInt32 size = sizeof (newVolume); - - return AudioObjectSetPropertyData (outputDeviceID, &addr, 0, nullptr, size, &newVolume) == noErr; - } - - return false; - } - - bool isMuted() const noexcept - { - UInt32 muted = 0; - - if (outputDeviceID != kAudioObjectUnknown) - { - UInt32 size = sizeof (muted); - AudioObjectGetPropertyData (outputDeviceID, &addr, 0, nullptr, &size, &muted); - } - - return muted != 0; - } - - bool setMuted (bool mute) const noexcept - { - if (outputDeviceID != kAudioObjectUnknown && canSetVolume()) - { - UInt32 newMute = mute ? 1 : 0; - UInt32 size = sizeof (newMute); - - return AudioObjectSetPropertyData (outputDeviceID, &addr, 0, nullptr, size, &newMute) == noErr; - } - - return false; - } - -private: - AudioDeviceID outputDeviceID; - AudioObjectPropertyAddress addr; - - bool canSetVolume() const noexcept - { - Boolean isSettable = NO; - return AudioObjectIsPropertySettable (outputDeviceID, &addr, &isSettable) == noErr && isSettable; - } -}; - -#ifdef __clang__ - #pragma clang diagnostic pop -#endif - -#define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1 -float JUCE_CALLTYPE SystemAudioVolume::getGain() { return SystemVol (kAudioHardwareServiceDeviceProperty_VirtualMasterVolume).getGain(); } -bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return SystemVol (kAudioHardwareServiceDeviceProperty_VirtualMasterVolume).setGain (gain); } -bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return SystemVol (kAudioDevicePropertyMute).isMuted(); } -bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return SystemVol (kAudioDevicePropertyMute).setMuted (mute); } - -//============================================================================== -struct CoreAudioClasses -{ - -class CoreAudioIODeviceType; -class CoreAudioIODevice; - -//============================================================================== -class CoreAudioInternal : private Timer -{ -public: - CoreAudioInternal (CoreAudioIODevice& d, AudioDeviceID id, bool input, bool output) - : owner (d), - deviceID (id), - isInputDevice (input), - isOutputDevice (output) - { - jassert (deviceID != 0); - - updateDetailsFromDevice(); - JUCE_COREAUDIOLOG ("Creating CoreAudioInternal\n" - << (isInputDevice ? (" inputDeviceId " + String (deviceID) + "\n") : "") - << (isOutputDevice ? (" outputDeviceId " + String (deviceID) + "\n") : "") - << getDeviceDetails().joinIntoString ("\n ")); - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioObjectPropertySelectorWildcard; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementWildcard; - - AudioObjectAddPropertyListener (deviceID, &pa, deviceListenerProc, this); - } - - ~CoreAudioInternal() override - { - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioObjectPropertySelectorWildcard; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementWildcard; - - AudioObjectRemovePropertyListener (deviceID, &pa, deviceListenerProc, this); - - stop (false); - } - - void allocateTempBuffers() - { - auto tempBufSize = bufferSize + 4; - audioBuffer.calloc ((numInputChans + numOutputChans) * tempBufSize); - - tempInputBuffers.calloc (numInputChans + 2); - tempOutputBuffers.calloc (numOutputChans + 2); - - int count = 0; - for (int i = 0; i < numInputChans; ++i) tempInputBuffers[i] = audioBuffer + count++ * tempBufSize; - for (int i = 0; i < numOutputChans; ++i) tempOutputBuffers[i] = audioBuffer + count++ * tempBufSize; - } - - struct CallbackDetailsForChannel - { - int streamNum; - int dataOffsetSamples; - int dataStrideSamples; - }; - - // returns the number of actual available channels - StringArray getChannelInfo (bool input, Array& newChannelInfo) const - { - StringArray newNames; - int chanNum = 0; - UInt32 size; - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyStreamConfiguration; - pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - pa.mElement = kAudioObjectPropertyElementMaster; - - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) - { - HeapBlock bufList; - bufList.calloc (size, 1); - - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList))) - { - const int numStreams = (int) bufList->mNumberBuffers; - - for (int i = 0; i < numStreams; ++i) - { - auto& b = bufList->mBuffers[i]; - - for (unsigned int j = 0; j < b.mNumberChannels; ++j) - { - String name; - NSString* nameNSString = nil; - size = sizeof (nameNSString); - - pa.mSelector = kAudioObjectPropertyElementName; - pa.mElement = (AudioObjectPropertyElement) chanNum + 1; - - if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &nameNSString) == noErr) - { - name = nsStringToJuce (nameNSString); - [nameNSString release]; - } - - if ((input ? activeInputChans : activeOutputChans) [chanNum]) - { - CallbackDetailsForChannel info = { i, (int) j, (int) b.mNumberChannels }; - newChannelInfo.add (info); - } - - if (name.isEmpty()) - name << (input ? "Input " : "Output ") << (chanNum + 1); - - newNames.add (name); - ++chanNum; - } - } - } - } - - return newNames; - } - - Array getSampleRatesFromDevice() const - { - Array newSampleRates; - - AudioObjectPropertyAddress pa; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; - pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; - UInt32 size = 0; - - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) - { - HeapBlock ranges; - ranges.calloc (size, 1); - - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) - { - for (auto r : { 8000, 11025, 16000, 22050, 32000, - 44100, 48000, 88200, 96000, 176400, - 192000, 352800, 384000, 705600, 768000 }) - { - auto rate = (double) r; - - for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) - { - if (rate >= ranges[j].mMinimum - 2 && rate <= ranges[j].mMaximum + 2) - { - newSampleRates.add (rate); - break; - } - } - } - } - } - - if (newSampleRates.isEmpty() && sampleRate > 0) - newSampleRates.add (sampleRate); - - return newSampleRates; - } - - Array getBufferSizesFromDevice() const - { - Array newBufferSizes; - - AudioObjectPropertyAddress pa; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; - pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange; - UInt32 size = 0; - - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) - { - HeapBlock ranges; - ranges.calloc (size, 1); - - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) - { - newBufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15); - - for (int i = 32; i <= 2048; i += 32) - { - for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) - { - if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) - { - newBufferSizes.addIfNotAlreadyThere (i); - break; - } - } - } - - if (bufferSize > 0) - newBufferSizes.addIfNotAlreadyThere (bufferSize); - } - } - - if (newBufferSizes.isEmpty() && bufferSize > 0) - newBufferSizes.add (bufferSize); - - return newBufferSizes; - } - - int getLatencyFromDevice (AudioObjectPropertyScope scope) const - { - UInt32 latency = 0; - UInt32 size = sizeof (latency); - AudioObjectPropertyAddress pa; - pa.mElement = kAudioObjectPropertyElementMaster; - pa.mSelector = kAudioDevicePropertyLatency; - pa.mScope = scope; - AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &latency); - - UInt32 safetyOffset = 0; - size = sizeof (safetyOffset); - pa.mSelector = kAudioDevicePropertySafetyOffset; - AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &safetyOffset); - - return (int) (latency + safetyOffset); - } - - int getBitDepthFromDevice (AudioObjectPropertyScope scope) const - { - AudioObjectPropertyAddress pa; - pa.mElement = kAudioObjectPropertyElementMaster; - pa.mSelector = kAudioStreamPropertyPhysicalFormat; - pa.mScope = scope; - - AudioStreamBasicDescription asbd; - UInt32 size = sizeof (asbd); - - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &asbd))) - return (int) asbd.mBitsPerChannel; - - return 0; - } - - int getFrameSizeFromDevice() const - { - AudioObjectPropertyAddress pa; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; - pa.mSelector = kAudioDevicePropertyBufferFrameSize; - - UInt32 framesPerBuf = (UInt32) bufferSize; - UInt32 size = sizeof (framesPerBuf); - AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf); - return (int) framesPerBuf; - } - - bool isDeviceAlive() const - { - AudioObjectPropertyAddress pa; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; - pa.mSelector = kAudioDevicePropertyDeviceIsAlive; - - UInt32 isAlive = 0; - UInt32 size = sizeof (isAlive); - return deviceID != 0 - && OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive)) - && isAlive != 0; - } - - bool updateDetailsFromDevice() - { - stopTimer(); - - if (! isDeviceAlive()) - return false; - - // this collects all the new details from the device without any locking, then - // locks + swaps them afterwards. - - auto newSampleRate = getNominalSampleRate(); - auto newBufferSize = getFrameSizeFromDevice(); - - auto newBufferSizes = getBufferSizesFromDevice(); - auto newSampleRates = getSampleRatesFromDevice(); - - auto newInputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeInput); - auto newOutputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeOutput); - - Array newInChans, newOutChans; - auto newInNames = isInputDevice ? getChannelInfo (true, newInChans) : StringArray(); - auto newOutNames = isOutputDevice ? getChannelInfo (false, newOutChans) : StringArray(); - - auto inputBitDepth = isInputDevice ? getBitDepthFromDevice (kAudioDevicePropertyScopeInput) : 0; - auto outputBitDepth = isOutputDevice ? getBitDepthFromDevice (kAudioDevicePropertyScopeOutput) : 0; - auto newBitDepth = jmax (inputBitDepth, outputBitDepth); - - { - const ScopedLock sl (callbackLock); - - bitDepth = newBitDepth > 0 ? newBitDepth : 32; - - if (newSampleRate > 0) - sampleRate = newSampleRate; - - inputLatency = newInputLatency; - outputLatency = newOutputLatency; - bufferSize = newBufferSize; - - sampleRates.swapWith (newSampleRates); - bufferSizes.swapWith (newBufferSizes); - - inChanNames.swapWith (newInNames); - outChanNames.swapWith (newOutNames); - - inputChannelInfo.swapWith (newInChans); - outputChannelInfo.swapWith (newOutChans); - - numInputChans = inputChannelInfo.size(); - numOutputChans = outputChannelInfo.size(); - - allocateTempBuffers(); - } - - return true; - } - - StringArray getDeviceDetails() - { - StringArray result; - - String availableSampleRates ("Available sample rates:"); - - for (auto& s : sampleRates) - availableSampleRates << " " << s; - - result.add (availableSampleRates); - result.add ("Sample rate: " + String (sampleRate)); - String availableBufferSizes ("Available buffer sizes:"); - - for (auto& b : bufferSizes) - availableBufferSizes << " " << b; - - result.add (availableBufferSizes); - result.add ("Buffer size: " + String (bufferSize)); - result.add ("Bit depth: " + String (bitDepth)); - result.add ("Input latency: " + String (inputLatency)); - result.add ("Output latency: " + String (outputLatency)); - result.add ("Input channel names: " + inChanNames.joinIntoString (" ")); - result.add ("Output channel names: " + outChanNames.joinIntoString (" ")); - - return result; - } - - //============================================================================== - StringArray getSources (bool input) - { - StringArray s; - HeapBlock types; - auto num = getAllDataSourcesForDevice (deviceID, types); - - for (int i = 0; i < num; ++i) - { - AudioValueTranslation avt; - char buffer[256]; - - avt.mInputData = &(types[i]); - avt.mInputDataSize = sizeof (UInt32); - avt.mOutputData = buffer; - avt.mOutputDataSize = 256; - - UInt32 transSize = sizeof (avt); - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyDataSourceNameForID; - pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - pa.mElement = kAudioObjectPropertyElementMaster; - - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &transSize, &avt))) - s.add (buffer); - } - - return s; - } - - int getCurrentSourceIndex (bool input) const - { - OSType currentSourceID = 0; - UInt32 size = sizeof (currentSourceID); - int result = -1; - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyDataSource; - pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - pa.mElement = kAudioObjectPropertyElementMaster; - - if (deviceID != 0) - { - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ¤tSourceID))) - { - HeapBlock types; - auto num = getAllDataSourcesForDevice (deviceID, types); - - for (int i = 0; i < num; ++i) - { - if (types[num] == currentSourceID) - { - result = i; - break; - } - } - } - } - - return result; - } - - void setCurrentSourceIndex (int index, bool input) - { - if (deviceID != 0) - { - HeapBlock types; - auto num = getAllDataSourcesForDevice (deviceID, types); - - if (isPositiveAndBelow (index, num)) - { - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyDataSource; - pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - pa.mElement = kAudioObjectPropertyElementMaster; - - OSType typeId = types[index]; - - OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (typeId), &typeId)); - } - } - } - - double getNominalSampleRate() const - { - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyNominalSampleRate; - pa.mScope = kAudioObjectPropertyScopeGlobal; - pa.mElement = kAudioObjectPropertyElementMaster; - Float64 sr = 0; - UInt32 size = (UInt32) sizeof (sr); - return OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr)) ? (double) sr : 0.0; - } - - bool setNominalSampleRate (double newSampleRate) const - { - if (std::abs (getNominalSampleRate() - newSampleRate) < 1.0) - return true; - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyNominalSampleRate; - pa.mScope = kAudioObjectPropertyScopeGlobal; - pa.mElement = kAudioObjectPropertyElementMaster; - Float64 sr = newSampleRate; - return OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (sr), &sr)); - } - - //============================================================================== - String reopen (const BigInteger& inputChannels, - const BigInteger& outputChannels, - double newSampleRate, int bufferSizeSamples) - { - String error; - callbacksAllowed = false; - stopTimer(); - - stop (false); - - updateDetailsFromDevice(); - - activeInputChans = inputChannels; - activeInputChans.setRange (inChanNames.size(), - activeInputChans.getHighestBit() + 1 - inChanNames.size(), - false); - - activeOutputChans = outputChannels; - activeOutputChans.setRange (outChanNames.size(), - activeOutputChans.getHighestBit() + 1 - outChanNames.size(), - false); - - numInputChans = activeInputChans.countNumberOfSetBits(); - numOutputChans = activeOutputChans.countNumberOfSetBits(); - - if (! setNominalSampleRate (newSampleRate)) - { - updateDetailsFromDevice(); - error = "Couldn't change sample rate"; - } - else - { - // change buffer size - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyBufferFrameSize; - pa.mScope = kAudioObjectPropertyScopeGlobal; - pa.mElement = kAudioObjectPropertyElementMaster; - UInt32 framesPerBuf = (UInt32) bufferSizeSamples; - - if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (framesPerBuf), &framesPerBuf))) - { - updateDetailsFromDevice(); - error = "Couldn't change buffer size"; - } - else - { - // Annoyingly, after changing the rate and buffer size, some devices fail to - // correctly report their new settings until some random time in the future, so - // after calling updateDetailsFromDevice, we need to manually bodge these values - // to make sure we're using the correct numbers.. - updateDetailsFromDevice(); - sampleRate = newSampleRate; - bufferSize = bufferSizeSamples; - - if (sampleRates.size() == 0) - error = "Device has no available sample-rates"; - else if (bufferSizes.size() == 0) - error = "Device has no available buffer-sizes"; - } - } - - callbacksAllowed = true; - return error; - } - - bool start() - { - if (! started) - { - callback = nullptr; - - if (deviceID != 0) - { - if (OK (AudioDeviceCreateIOProcID (deviceID, audioIOProc, this, &audioProcID))) - { - if (OK (AudioDeviceStart (deviceID, audioIOProc))) - { - started = true; - } - else - { - OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID)); - audioProcID = {}; - } - } - } - } - - return started; - } - - void setCallback (AudioIODeviceCallback* cb) - { - const ScopedLock sl (callbackLock); - callback = cb; - } - - void stop (bool leaveInterruptRunning) - { - { - const ScopedLock sl (callbackLock); - callback = nullptr; - } - - if (started && (deviceID != 0) && ! leaveInterruptRunning) - { - OK (AudioDeviceStop (deviceID, audioIOProc)); - OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID)); - audioProcID = {}; - - started = false; - - { const ScopedLock sl (callbackLock); } - - // wait until it's definitely stopped calling back.. - for (int i = 40; --i >= 0;) - { - Thread::sleep (50); - - UInt32 running = 0; - UInt32 size = sizeof (running); - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyDeviceIsRunning; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; - - OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &running)); - - if (running == 0) - break; - } - - const ScopedLock sl (callbackLock); - } - } - - double getSampleRate() const { return sampleRate; } - int getBufferSize() const { return bufferSize; } - - void audioCallback (const AudioBufferList* inInputData, - AudioBufferList* outOutputData) - { - const ScopedLock sl (callbackLock); - - if (callback != nullptr) - { - for (int i = numInputChans; --i >= 0;) - { - auto& info = inputChannelInfo.getReference(i); - auto dest = tempInputBuffers[i]; - auto src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; - auto stride = info.dataStrideSamples; - - if (stride != 0) // if this is zero, info is invalid - { - for (int j = bufferSize; --j >= 0;) - { - *dest++ = *src; - src += stride; - } - } - } - - callback->audioDeviceIOCallback (const_cast (tempInputBuffers.get()), - numInputChans, - tempOutputBuffers, - numOutputChans, - bufferSize); - - for (int i = numOutputChans; --i >= 0;) - { - auto& info = outputChannelInfo.getReference (i); - auto src = tempOutputBuffers[i]; - auto dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; - auto stride = info.dataStrideSamples; - - if (stride != 0) // if this is zero, info is invalid - { - for (int j = bufferSize; --j >= 0;) - { - *dest = *src++; - dest += stride; - } - } - } - } - else - { - for (UInt32 i = 0; i < outOutputData->mNumberBuffers; ++i) - zeromem (outOutputData->mBuffers[i].mData, - outOutputData->mBuffers[i].mDataByteSize); - } - } - - // called by callbacks - void deviceDetailsChanged() - { - if (callbacksAllowed.get() == 1) - startTimer (100); - } - - void timerCallback() override - { - JUCE_COREAUDIOLOG ("Device changed"); - - stopTimer(); - auto oldSampleRate = sampleRate; - auto oldBufferSize = bufferSize; - - if (! updateDetailsFromDevice()) - owner.stopInternal(); - else if ((oldBufferSize != bufferSize || oldSampleRate != sampleRate) && owner.shouldRestartDevice()) - owner.restart(); - } - - //============================================================================== - CoreAudioIODevice& owner; - int inputLatency = 0; - int outputLatency = 0; - int bitDepth = 32; - int xruns = 0; - BigInteger activeInputChans, activeOutputChans; - StringArray inChanNames, outChanNames; - Array sampleRates; - Array bufferSizes; - AudioIODeviceCallback* callback = nullptr; - AudioDeviceIOProcID audioProcID = {}; - -private: - CriticalSection callbackLock; - AudioDeviceID deviceID; - bool started = false; - double sampleRate = 0; - int bufferSize = 512; - HeapBlock audioBuffer; - int numInputChans = 0; - int numOutputChans = 0; - Atomic callbacksAllowed { 1 }; - const bool isInputDevice, isOutputDevice; - - Array inputChannelInfo, outputChannelInfo; - HeapBlock tempInputBuffers, tempOutputBuffers; - - //============================================================================== - static OSStatus audioIOProc (AudioDeviceID /*inDevice*/, - const AudioTimeStamp* /*inNow*/, - const AudioBufferList* inInputData, - const AudioTimeStamp* /*inInputTime*/, - AudioBufferList* outOutputData, - const AudioTimeStamp* /*inOutputTime*/, - void* device) - { - static_cast (device)->audioCallback (inInputData, outOutputData); - return noErr; - } - - static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, - const AudioObjectPropertyAddress* pa, void* inClientData) - { - auto intern = static_cast (inClientData); - - switch (pa->mSelector) - { - case kAudioDeviceProcessorOverload: - intern->xruns++; - break; - - case kAudioDevicePropertyBufferSize: - case kAudioDevicePropertyBufferFrameSize: - case kAudioDevicePropertyNominalSampleRate: - case kAudioDevicePropertyStreamFormat: - case kAudioDevicePropertyDeviceIsAlive: - case kAudioStreamPropertyPhysicalFormat: - intern->deviceDetailsChanged(); - break; - - case kAudioDevicePropertyDeviceHasChanged: - case kAudioObjectPropertyOwnedObjects: - intern->owner.restart(); - - if (intern->owner.deviceType != nullptr) - intern->owner.deviceType->triggerAsyncAudioDeviceListChange(); - break; - - case kAudioDevicePropertyBufferSizeRange: - case kAudioDevicePropertyVolumeScalar: - case kAudioDevicePropertyMute: - case kAudioDevicePropertyPlayThru: - case kAudioDevicePropertyDataSource: - case kAudioDevicePropertyDeviceIsRunning: - break; - } - - return noErr; - } - - //============================================================================== - static int getAllDataSourcesForDevice (AudioDeviceID deviceID, HeapBlock& types) - { - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyDataSources; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; - UInt32 size = 0; - - if (deviceID != 0 - && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr) - { - types.calloc (size, 1); - - if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, types) == noErr) - return size / (int) sizeof (OSType); - } - - return 0; - } - - bool OK (const OSStatus errorCode) const - { - if (errorCode == noErr) - return true; - - const String errorMessage ("CoreAudio error: " + String::toHexString ((int) errorCode)); - JUCE_COREAUDIOLOG (errorMessage); - - if (callback != nullptr) - callback->audioDeviceError (errorMessage); - - return false; - } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioInternal) -}; - - -//============================================================================== -class CoreAudioIODevice : public AudioIODevice, - private Timer -{ -public: - CoreAudioIODevice (CoreAudioIODeviceType* dt, - const String& deviceName, - AudioDeviceID inputDeviceId, int inputIndex_, - AudioDeviceID outputDeviceId, int outputIndex_) - : AudioIODevice (deviceName, "CoreAudio"), - deviceType (dt), - inputIndex (inputIndex_), - outputIndex (outputIndex_) - { - CoreAudioInternal* device = nullptr; - - if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) - { - jassert (inputDeviceId != 0); - device = new CoreAudioInternal (*this, inputDeviceId, true, outputDeviceId != 0); - } - else - { - device = new CoreAudioInternal (*this, outputDeviceId, false, true); - } - - jassert (device != nullptr); - internal.reset (device); - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioObjectPropertySelectorWildcard; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementWildcard; - - AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal.get()); - } - - ~CoreAudioIODevice() override - { - close(); - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioObjectPropertySelectorWildcard; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementWildcard; - - AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal.get()); - } - - StringArray getOutputChannelNames() override { return internal->outChanNames; } - StringArray getInputChannelNames() override { return internal->inChanNames; } - - bool isOpen() override { return isOpen_; } - - Array getAvailableSampleRates() override { return internal->sampleRates; } - Array getAvailableBufferSizes() override { return internal->bufferSizes; } - - double getCurrentSampleRate() override { return internal->getSampleRate(); } - int getCurrentBitDepth() override { return internal->bitDepth; } - int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); } - int getXRunCount() const noexcept override { return internal->xruns; } - - int getDefaultBufferSize() override - { - int best = 0; - - for (int i = 0; best < 512 && i < internal->bufferSizes.size(); ++i) - best = internal->bufferSizes.getUnchecked(i); - - if (best == 0) - best = 512; - - return best; - } - - String open (const BigInteger& inputChannels, - const BigInteger& outputChannels, - double sampleRate, int bufferSizeSamples) override - { - isOpen_ = true; - internal->xruns = 0; - - inputChannelsRequested = inputChannels; - outputChannelsRequested = outputChannels; - - if (bufferSizeSamples <= 0) - bufferSizeSamples = getDefaultBufferSize(); - - if (sampleRate <= 0) - sampleRate = internal->getNominalSampleRate(); - - lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); - JUCE_COREAUDIOLOG ("Opened: " << getName()); - - isOpen_ = lastError.isEmpty(); - - return lastError; - } - - void close() override - { - isOpen_ = false; - internal->stop (false); - } - - BigInteger getActiveOutputChannels() const override { return internal->activeOutputChans; } - BigInteger getActiveInputChannels() const override { return internal->activeInputChans; } - - int getOutputLatencyInSamples() override - { - // this seems like a good guess at getting the latency right - comparing - // this with a round-trip measurement, it gets it to within a few millisecs - // for the built-in mac soundcard - return internal->outputLatency; - } - - int getInputLatencyInSamples() override - { - return internal->inputLatency; - } - - void start (AudioIODeviceCallback* callback) override - { - if (! isStarted) - { - if (callback != nullptr) - callback->audioDeviceAboutToStart (this); - - isStarted = internal->start(); - - if (isStarted) - { - internal->setCallback (callback); - previousCallback = callback; - } - } - } - - void stop() override - { - restartDevice = false; - - if (isStarted) - { - auto lastCallback = internal->callback; - - isStarted = false; - internal->stop (true); - - if (lastCallback != nullptr) - lastCallback->audioDeviceStopped(); - } - } - - void stopInternal() - { - stop(); - restartDevice = true; - } - - bool isPlaying() override - { - if (internal->callback == nullptr) - isStarted = false; - - return isStarted; - } - - String getLastError() override - { - return lastError; - } - - void audioDeviceListChanged() - { - if (deviceType != nullptr) - deviceType->audioDeviceListChanged(); - } - - void restart() - { - if (deviceWrapperRestartCallback != nullptr) - { - deviceWrapperRestartCallback(); - } - else - { - { - const ScopedLock sl (closeLock); - - if (isStarted) - { - if (internal->callback != nullptr) - previousCallback = internal->callback; - - stopInternal(); - } - } - - startTimer (100); - } - } - - bool setCurrentSampleRate (double newSampleRate) - { - return internal->setNominalSampleRate (newSampleRate); - } - - void setDeviceWrapperRestartCallback (const std::function& cb) - { - deviceWrapperRestartCallback = cb; - } - - bool shouldRestartDevice() const noexcept { return restartDevice; } - - WeakReference deviceType; - int inputIndex, outputIndex; - -private: - std::unique_ptr internal; - bool isOpen_ = false, isStarted = false, restartDevice = true; - String lastError; - AudioIODeviceCallback* previousCallback = nullptr; - std::function deviceWrapperRestartCallback = nullptr; - BigInteger inputChannelsRequested, outputChannelsRequested; - CriticalSection closeLock; - - void timerCallback() override - { - stopTimer(); - - stopInternal(); - - internal->updateDetailsFromDevice(); - - open (inputChannelsRequested, outputChannelsRequested, - getCurrentSampleRate(), getCurrentBufferSizeSamples()); - start (previousCallback); - } - - static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) - { - switch (pa->mSelector) - { - case kAudioHardwarePropertyDevices: - static_cast (inClientData)->deviceDetailsChanged(); - break; - - case kAudioHardwarePropertyDefaultOutputDevice: - case kAudioHardwarePropertyDefaultInputDevice: - case kAudioHardwarePropertyDefaultSystemOutputDevice: - break; - } - - return noErr; - } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODevice) -}; - -//============================================================================== -class AudioIODeviceCombiner : public AudioIODevice, - private Thread, - private Timer -{ -public: - AudioIODeviceCombiner (const String& deviceName, CoreAudioIODeviceType* deviceType) - : AudioIODevice (deviceName, "CoreAudio"), - Thread (deviceName), - owner (deviceType) - { - } - - ~AudioIODeviceCombiner() override - { - close(); - devices.clear(); - } - - void addDevice (CoreAudioIODevice* device, bool useInputs, bool useOutputs) - { - jassert (device != nullptr); - jassert (! isOpen()); - jassert (! device->isOpen()); - devices.add (new DeviceWrapper (*this, device, useInputs, useOutputs)); - - if (currentSampleRate == 0) - currentSampleRate = device->getCurrentSampleRate(); - - if (currentBufferSize == 0) - currentBufferSize = device->getCurrentBufferSizeSamples(); - } - - Array getDevices() const - { - Array devs; - - for (auto* d : devices) - devs.add (d->device.get()); - - return devs; - } - - StringArray getOutputChannelNames() override - { - StringArray names; - - for (auto* d : devices) - names.addArray (d->getOutputChannelNames()); - - names.appendNumbersToDuplicates (false, true); - return names; - } - - StringArray getInputChannelNames() override - { - StringArray names; - - for (auto* d : devices) - names.addArray (d->getInputChannelNames()); - - names.appendNumbersToDuplicates (false, true); - return names; - } - - Array getAvailableSampleRates() override - { - Array commonRates; - bool first = true; - - for (auto* d : devices) - { - auto rates = d->device->getAvailableSampleRates(); - - if (first) - { - first = false; - commonRates = rates; - } - else - { - commonRates.removeValuesNotIn (rates); - } - } - - return commonRates; - } - - Array getAvailableBufferSizes() override - { - Array commonSizes; - bool first = true; - - for (auto* d : devices) - { - auto sizes = d->device->getAvailableBufferSizes(); - - if (first) - { - first = false; - commonSizes = sizes; - } - else - { - commonSizes.removeValuesNotIn (sizes); - } - } - - return commonSizes; - } - - bool isOpen() override { return active; } - bool isPlaying() override { return callback != nullptr; } - double getCurrentSampleRate() override { return currentSampleRate; } - int getCurrentBufferSizeSamples() override { return currentBufferSize; } - - int getCurrentBitDepth() override - { - int depth = 32; - - for (auto* d : devices) - depth = jmin (depth, d->device->getCurrentBitDepth()); - - return depth; - } - - int getDefaultBufferSize() override - { - int size = 0; - - for (auto* d : devices) - size = jmax (size, d->device->getDefaultBufferSize()); - - return size; - } - - String open (const BigInteger& inputChannels, - const BigInteger& outputChannels, - double sampleRate, int bufferSize) override - { - inputChannelsRequested = inputChannels; - outputChannelsRequested = outputChannels; - sampleRateRequested = sampleRate; - bufferSizeRequested = bufferSize; - - close(); - active = true; - - if (bufferSize <= 0) - bufferSize = getDefaultBufferSize(); - - if (sampleRate <= 0) - { - auto rates = getAvailableSampleRates(); - - for (int i = 0; i < rates.size() && sampleRate < 44100.0; ++i) - sampleRate = rates.getUnchecked(i); - } - - currentSampleRate = sampleRate; - currentBufferSize = bufferSize; - - const int fifoSize = bufferSize * 3 + 1; - int totalInputChanIndex = 0, totalOutputChanIndex = 0; - int chanIndex = 0; - - for (auto* d : devices) - { - BigInteger ins (inputChannels >> totalInputChanIndex); - BigInteger outs (outputChannels >> totalOutputChanIndex); - - int numIns = d->getInputChannelNames().size(); - int numOuts = d->getOutputChannelNames().size(); - - totalInputChanIndex += numIns; - totalOutputChanIndex += numOuts; - - String err = d->open (ins, outs, sampleRate, bufferSize, - chanIndex, fifoSize); - - if (err.isNotEmpty()) - { - close(); - lastError = err; - return err; - } - - chanIndex += d->numInputChans + d->numOutputChans; - } - - fifos.setSize (chanIndex, fifoSize); - fifoReadPointers = fifos.getArrayOfReadPointers(); - fifoWritePointers = fifos.getArrayOfWritePointers(); - fifos.clear(); - startThread (9); - threadInitialised.wait(); - - return {}; - } - - void close() override - { - stop(); - stopThread (10000); - fifos.clear(); - active = false; - - for (auto* d : devices) - d->close(); - } - - void restart (AudioIODeviceCallback* cb) - { - const ScopedLock sl (closeLock); - - close(); - - auto newSampleRate = sampleRateRequested; - auto newBufferSize = bufferSizeRequested; - - for (auto* d : devices) - { - auto deviceSampleRate = d->getCurrentSampleRate(); - - if (deviceSampleRate != sampleRateRequested) - { - if (! getAvailableSampleRates().contains (deviceSampleRate)) - return; - - for (auto* d2 : devices) - if (d2 != d) - d2->setCurrentSampleRate (deviceSampleRate); - - newSampleRate = deviceSampleRate; - break; - } - } - - for (auto* d : devices) - { - auto deviceBufferSize = d->getCurrentBufferSizeSamples(); - - if (deviceBufferSize != bufferSizeRequested) - { - if (! getAvailableBufferSizes().contains (deviceBufferSize)) - return; - - newBufferSize = deviceBufferSize; - break; - } - } - - open (inputChannelsRequested, outputChannelsRequested, - newSampleRate, newBufferSize); - - start (cb); - } - - void restartAsync() - { - { - const ScopedLock sl (closeLock); - - if (active) - { - if (callback != nullptr) - previousCallback = callback; - - close(); - } - } - - startTimer (100); - } - - BigInteger getActiveOutputChannels() const override - { - BigInteger chans; - int start = 0; - - for (auto* d : devices) - { - auto numChans = d->getOutputChannelNames().size(); - - if (numChans > 0) - { - chans |= (d->device->getActiveOutputChannels() << start); - start += numChans; - } - } - - return chans; - } - - BigInteger getActiveInputChannels() const override - { - BigInteger chans; - int start = 0; - - for (auto* d : devices) - { - auto numChans = d->getInputChannelNames().size(); - - if (numChans > 0) - { - chans |= (d->device->getActiveInputChannels() << start); - start += numChans; - } - } - - return chans; - } - - int getOutputLatencyInSamples() override - { - int lat = 0; - - for (auto* d : devices) - lat = jmax (lat, d->device->getOutputLatencyInSamples()); - - return lat + currentBufferSize * 2; - } - - int getInputLatencyInSamples() override - { - int lat = 0; - - for (auto* d : devices) - lat = jmax (lat, d->device->getInputLatencyInSamples()); - - return lat + currentBufferSize * 2; - } - - void start (AudioIODeviceCallback* newCallback) override - { - if (callback != newCallback) - { - stop(); - fifos.clear(); - - for (auto* d : devices) - d->start(); - - if (newCallback != nullptr) - newCallback->audioDeviceAboutToStart (this); - - const ScopedLock sl (callbackLock); - callback = newCallback; - previousCallback = callback; - } - } - - void stop() override { shutdown ({}); } - - String getLastError() override - { - return lastError; - } - -private: - WeakReference owner; - CriticalSection callbackLock; - AudioIODeviceCallback* callback = nullptr; - AudioIODeviceCallback* previousCallback = nullptr; - double currentSampleRate = 0; - int currentBufferSize = 0; - bool active = false; - String lastError; - AudioBuffer fifos; - const float** fifoReadPointers = nullptr; - float** fifoWritePointers = nullptr; - WaitableEvent threadInitialised; - CriticalSection closeLock; - - BigInteger inputChannelsRequested, outputChannelsRequested; - double sampleRateRequested = 44100; - int bufferSizeRequested = 512; - - void run() override - { - auto numSamples = currentBufferSize; - - AudioBuffer buffer (fifos.getNumChannels(), numSamples); - buffer.clear(); - - Array inputChans; - Array outputChans; - - for (auto* d : devices) - { - for (int j = 0; j < d->numInputChans; ++j) inputChans.add (buffer.getReadPointer (d->inputIndex + j)); - for (int j = 0; j < d->numOutputChans; ++j) outputChans.add (buffer.getWritePointer (d->outputIndex + j)); - } - - auto numInputChans = inputChans.size(); - auto numOutputChans = outputChans.size(); - - inputChans.add (nullptr); - outputChans.add (nullptr); - - auto blockSizeMs = jmax (1, (int) (1000 * numSamples / currentSampleRate)); - - jassert (numInputChans + numOutputChans == buffer.getNumChannels()); - - threadInitialised.signal(); - - while (! threadShouldExit()) - { - readInput (buffer, numSamples, blockSizeMs); - - bool didCallback = true; - - { - const ScopedLock sl (callbackLock); - - if (callback != nullptr) - callback->audioDeviceIOCallback ((const float**) inputChans.getRawDataPointer(), numInputChans, - outputChans.getRawDataPointer(), numOutputChans, numSamples); - else - didCallback = false; - } - - if (didCallback) - { - pushOutputData (buffer, numSamples, blockSizeMs); - } - else - { - for (int i = 0; i < numOutputChans; ++i) - FloatVectorOperations::clear (outputChans[i], numSamples); - - reset(); - } - } - } - - void timerCallback() override - { - stopTimer(); - - restart (previousCallback); - } - - void shutdown (const String& error) - { - AudioIODeviceCallback* lastCallback = nullptr; - - { - const ScopedLock sl (callbackLock); - std::swap (callback, lastCallback); - } - - for (auto* d : devices) - d->device->stopInternal(); - - if (lastCallback != nullptr) - { - if (error.isNotEmpty()) - lastCallback->audioDeviceError (error); - else - lastCallback->audioDeviceStopped(); - } - } - - void reset() - { - for (auto* d : devices) - d->reset(); - } - - void underrun() - { - } - - void readInput (AudioBuffer& buffer, const int numSamples, const int blockSizeMs) - { - for (auto* d : devices) - d->done = (d->numInputChans == 0); - - for (int tries = 5;;) - { - bool anyRemaining = false; - - for (auto* d : devices) - { - if (! d->done) - { - if (d->isInputReady (numSamples)) - { - d->readInput (buffer, numSamples); - d->done = true; - } - else - anyRemaining = true; - } - } - - if (! anyRemaining) - return; - - if (--tries == 0) - break; - - wait (blockSizeMs); - } - - for (auto* d : devices) - if (! d->done) - for (int i = 0; i < d->numInputChans; ++i) - buffer.clear (d->inputIndex + i, 0, numSamples); - } - - void pushOutputData (AudioBuffer& buffer, const int numSamples, const int blockSizeMs) - { - for (auto* d : devices) - d->done = (d->numOutputChans == 0); - - for (int tries = 5;;) - { - bool anyRemaining = false; - - for (auto* d : devices) - { - if (! d->done) - { - if (d->isOutputReady (numSamples)) - { - d->pushOutputData (buffer, numSamples); - d->done = true; - } - else - anyRemaining = true; - } - } - - if ((! anyRemaining) || --tries == 0) - return; - - wait (blockSizeMs); - } - } - - void handleAudioDeviceAboutToStart (AudioIODevice* device) - { - const ScopedLock sl (callbackLock); - - auto newSampleRate = device->getCurrentSampleRate(); - auto commonRates = getAvailableSampleRates(); - - if (! commonRates.contains (newSampleRate)) - { - commonRates.sort(); - - if (newSampleRate < commonRates.getFirst() || newSampleRate > commonRates.getLast()) - { - newSampleRate = jlimit (commonRates.getFirst(), commonRates.getLast(), newSampleRate); - } - else - { - for (auto it = commonRates.begin(); it < commonRates.end() - 1; ++it) - { - if (it[0] < newSampleRate && it[1] > newSampleRate) - { - newSampleRate = newSampleRate - it[0] < it[1] - newSampleRate ? it[0] : it[1]; - break; - } - } - } - } - - currentSampleRate = newSampleRate; - bool anySampleRateChanges = false; - - for (auto* d : devices) - { - if (d->getCurrentSampleRate() != currentSampleRate) - { - d->setCurrentSampleRate (currentSampleRate); - anySampleRateChanges = true; - } - } - - if (anySampleRateChanges && owner != nullptr) - owner->audioDeviceListChanged(); - - if (callback != nullptr) - callback->audioDeviceAboutToStart (device); - } - - void handleAudioDeviceStopped() { shutdown ({}); } - void handleAudioDeviceError (const String& errorMessage) { shutdown (errorMessage.isNotEmpty() ? errorMessage : String ("unknown")); } - - //============================================================================== - struct DeviceWrapper : private AudioIODeviceCallback - { - DeviceWrapper (AudioIODeviceCombiner& cd, CoreAudioIODevice* d, bool useIns, bool useOuts) - : owner (cd), device (d), - useInputs (useIns), useOutputs (useOuts) - { - d->setDeviceWrapperRestartCallback ([this] { owner.restartAsync(); }); - } - - ~DeviceWrapper() override - { - close(); - } - - String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double sampleRate, int bufferSize, int channelIndex, int fifoSize) - { - inputFifo.setTotalSize (fifoSize); - outputFifo.setTotalSize (fifoSize); - inputFifo.reset(); - outputFifo.reset(); - - auto err = device->open (useInputs ? inputChannels : BigInteger(), - useOutputs ? outputChannels : BigInteger(), - sampleRate, bufferSize); - - numInputChans = useInputs ? device->getActiveInputChannels().countNumberOfSetBits() : 0; - numOutputChans = useOutputs ? device->getActiveOutputChannels().countNumberOfSetBits() : 0; - - inputIndex = channelIndex; - outputIndex = channelIndex + numInputChans; - - return err; - } - - void close() - { - device->close(); - } - - void start() - { - reset(); - device->start (this); - } - - void reset() - { - inputFifo.reset(); - outputFifo.reset(); - } - - StringArray getOutputChannelNames() const { return useOutputs ? device->getOutputChannelNames() : StringArray(); } - StringArray getInputChannelNames() const { return useInputs ? device->getInputChannelNames() : StringArray(); } - - bool isInputReady (int numSamples) const noexcept - { - return numInputChans == 0 || inputFifo.getNumReady() >= numSamples; - } - - void readInput (AudioBuffer& destBuffer, int numSamples) - { - if (numInputChans == 0) - return; - - int start1, size1, start2, size2; - inputFifo.prepareToRead (numSamples, start1, size1, start2, size2); - - for (int i = 0; i < numInputChans; ++i) - { - auto index = inputIndex + i; - auto dest = destBuffer.getWritePointer (index); - auto src = owner.fifoReadPointers[index]; - - if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1); - if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2); - } - - inputFifo.finishedRead (size1 + size2); - } - - bool isOutputReady (int numSamples) const noexcept - { - return numOutputChans == 0 || outputFifo.getFreeSpace() >= numSamples; - } - - void pushOutputData (AudioBuffer& srcBuffer, int numSamples) - { - if (numOutputChans == 0) - return; - - int start1, size1, start2, size2; - outputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); - - for (int i = 0; i < numOutputChans; ++i) - { - auto index = outputIndex + i; - auto dest = owner.fifoWritePointers[index]; - auto src = srcBuffer.getReadPointer (index); - - if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1); - if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2); - } - - outputFifo.finishedWrite (size1 + size2); - } - - void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels, - float** outputChannelData, int numOutputChannels, - int numSamples) override - { - if (numInputChannels > 0) - { - int start1, size1, start2, size2; - inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); - - if (size1 + size2 < numSamples) - { - inputFifo.reset(); - inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); - } - - for (int i = 0; i < numInputChannels; ++i) - { - auto dest = owner.fifoWritePointers[inputIndex + i]; - auto src = inputChannelData[i]; - - if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1); - if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2); - } - - auto totalSize = size1 + size2; - inputFifo.finishedWrite (totalSize); - - if (numSamples > totalSize) - { - auto samplesRemaining = numSamples - totalSize; - - for (int i = 0; i < numInputChans; ++i) - FloatVectorOperations::clear (owner.fifoWritePointers[inputIndex + i] + totalSize, samplesRemaining); - - owner.underrun(); - } - } - - if (numOutputChannels > 0) - { - int start1, size1, start2, size2; - outputFifo.prepareToRead (numSamples, start1, size1, start2, size2); - - if (size1 + size2 < numSamples) - { - Thread::sleep (1); - outputFifo.prepareToRead (numSamples, start1, size1, start2, size2); - } - - for (int i = 0; i < numOutputChannels; ++i) - { - auto dest = outputChannelData[i]; - auto src = owner.fifoReadPointers[outputIndex + i]; - - if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1); - if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2); - } - - auto totalSize = size1 + size2; - outputFifo.finishedRead (totalSize); - - if (numSamples > totalSize) - { - auto samplesRemaining = numSamples - totalSize; - - for (int i = 0; i < numOutputChannels; ++i) - FloatVectorOperations::clear (outputChannelData[i] + totalSize, samplesRemaining); - - owner.underrun(); - } - } - - owner.notify(); - } - - double getCurrentSampleRate() { return device->getCurrentSampleRate(); } - bool setCurrentSampleRate (double newSampleRate) { return device->setCurrentSampleRate (newSampleRate); } - int getCurrentBufferSizeSamples() { return device->getCurrentBufferSizeSamples(); } - - void audioDeviceAboutToStart (AudioIODevice* d) override { owner.handleAudioDeviceAboutToStart (d); } - void audioDeviceStopped() override { owner.handleAudioDeviceStopped(); } - void audioDeviceError (const String& errorMessage) override { owner.handleAudioDeviceError (errorMessage); } - - AudioIODeviceCombiner& owner; - std::unique_ptr device; - int inputIndex = 0, numInputChans = 0, outputIndex = 0, numOutputChans = 0; - bool useInputs = false, useOutputs = false; - AbstractFifo inputFifo { 32 }, outputFifo { 32 }; - bool done = false; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DeviceWrapper) - }; - - OwnedArray devices; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioIODeviceCombiner) -}; - - -//============================================================================== -class CoreAudioIODeviceType : public AudioIODeviceType, - private AsyncUpdater -{ -public: - CoreAudioIODeviceType() : AudioIODeviceType ("CoreAudio") - { - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioHardwarePropertyDevices; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementWildcard; - - AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this); - } - - ~CoreAudioIODeviceType() override - { - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioHardwarePropertyDevices; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementWildcard; - - AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this); - } - - //============================================================================== - void scanForDevices() override - { - hasScanned = true; - - inputDeviceNames.clear(); - outputDeviceNames.clear(); - inputIds.clear(); - outputIds.clear(); - - UInt32 size; - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioHardwarePropertyDevices; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; - - if (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, nullptr, &size) == noErr) - { - HeapBlock devs; - devs.calloc (size, 1); - - if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, devs) == noErr) - { - auto num = (int) size / (int) sizeof (AudioDeviceID); - - for (int i = 0; i < num; ++i) - { - char name[1024]; - size = sizeof (name); - pa.mSelector = kAudioDevicePropertyDeviceName; - - if (AudioObjectGetPropertyData (devs[i], &pa, 0, nullptr, &size, name) == noErr) - { - auto nameString = String::fromUTF8 (name, (int) strlen (name)); - auto numIns = getNumChannels (devs[i], true); - auto numOuts = getNumChannels (devs[i], false); - - if (numIns > 0) - { - inputDeviceNames.add (nameString); - inputIds.add (devs[i]); - } - - if (numOuts > 0) - { - outputDeviceNames.add (nameString); - outputIds.add (devs[i]); - } - } - } - } - } - - inputDeviceNames.appendNumbersToDuplicates (false, true); - outputDeviceNames.appendNumbersToDuplicates (false, true); - } - - StringArray getDeviceNames (bool wantInputNames) const override - { - jassert (hasScanned); // need to call scanForDevices() before doing this - - return wantInputNames ? inputDeviceNames - : outputDeviceNames; - } - - int getDefaultDeviceIndex (bool forInput) const override - { - jassert (hasScanned); // need to call scanForDevices() before doing this - - AudioDeviceID deviceID; - UInt32 size = sizeof (deviceID); - - // if they're asking for any input channels at all, use the default input, so we - // get the built-in mic rather than the built-in output with no inputs.. - - AudioObjectPropertyAddress pa; - pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice - : kAudioHardwarePropertyDefaultOutputDevice; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; - - if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, &deviceID) == noErr) - { - if (forInput) - { - for (int i = inputIds.size(); --i >= 0;) - if (inputIds[i] == deviceID) - return i; - } - else - { - for (int i = outputIds.size(); --i >= 0;) - if (outputIds[i] == deviceID) - return i; - } - } - - return 0; - } - - int getIndexOfDevice (AudioIODevice* device, bool asInput) const override - { - jassert (hasScanned); // need to call scanForDevices() before doing this - - if (auto* d = dynamic_cast (device)) - return asInput ? d->inputIndex - : d->outputIndex; - - if (auto* d = dynamic_cast (device)) - { - for (auto* dev : d->getDevices()) - { - auto index = getIndexOfDevice (dev, asInput); - - if (index >= 0) - return index; - } - } - - return -1; - } - - bool hasSeparateInputsAndOutputs() const override { return true; } - - AudioIODevice* createDevice (const String& outputDeviceName, - const String& inputDeviceName) override - { - jassert (hasScanned); // need to call scanForDevices() before doing this - - auto inputIndex = inputDeviceNames.indexOf (inputDeviceName); - auto outputIndex = outputDeviceNames.indexOf (outputDeviceName); - - auto inputDeviceID = inputIds[inputIndex]; - auto outputDeviceID = outputIds[outputIndex]; - - if (inputDeviceID == 0 && outputDeviceID == 0) - return nullptr; - - auto combinedName = outputDeviceName.isEmpty() ? inputDeviceName - : outputDeviceName; - - if (inputDeviceID == outputDeviceID) - return new CoreAudioIODevice (this, combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex); - - std::unique_ptr in, out; - - if (inputDeviceID != 0) - in.reset (new CoreAudioIODevice (this, inputDeviceName, inputDeviceID, inputIndex, 0, -1)); - - if (outputDeviceID != 0) - out.reset (new CoreAudioIODevice (this, outputDeviceName, 0, -1, outputDeviceID, outputIndex)); - - if (in == nullptr) return out.release(); - if (out == nullptr) return in.release(); - - std::unique_ptr combo (new AudioIODeviceCombiner (combinedName, this)); - combo->addDevice (in.release(), true, false); - combo->addDevice (out.release(), false, true); - return combo.release(); - } - - void audioDeviceListChanged() - { - scanForDevices(); - callDeviceChangeListeners(); - } - - void triggerAsyncAudioDeviceListChange() - { - triggerAsyncUpdate(); - } - - //============================================================================== -private: - StringArray inputDeviceNames, outputDeviceNames; - Array inputIds, outputIds; - - bool hasScanned = false; - - static int getNumChannels (AudioDeviceID deviceID, bool input) - { - int total = 0; - UInt32 size; - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyStreamConfiguration; - pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - pa.mElement = kAudioObjectPropertyElementMaster; - - if (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr) - { - HeapBlock bufList; - bufList.calloc (size, 1); - - if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList) == noErr) - { - auto numStreams = (int) bufList->mNumberBuffers; - - for (int i = 0; i < numStreams; ++i) - total += bufList->mBuffers[i].mNumberChannels; - } - } - - return total; - } - - static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData) - { - static_cast (clientData)->triggerAsyncAudioDeviceListChange(); - return noErr; - } - - void handleAsyncUpdate() override - { - audioDeviceListChanged(); - } - - JUCE_DECLARE_WEAK_REFERENCEABLE (CoreAudioIODeviceType) - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType) -}; - -}; - -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() -{ - return new CoreAudioClasses::CoreAudioIODeviceType(); -} - -#undef JUCE_COREAUDIOLOG - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp deleted file mode 100644 index 67199a5b..00000000 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp +++ /dev/null @@ -1,736 +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 -{ - -#ifndef JUCE_LOG_COREMIDI_ERRORS - #define JUCE_LOG_COREMIDI_ERRORS 1 -#endif - -namespace CoreMidiHelpers -{ - //============================================================================== - static bool checkError (OSStatus err, int lineNum) - { - if (err == noErr) - return true; - - #if JUCE_LOG_COREMIDI_ERRORS - Logger::writeToLog ("CoreMIDI error: " + String (lineNum) + " - " + String::toHexString ((int) err)); - #endif - - ignoreUnused (lineNum); - return false; - } - - #undef CHECK_ERROR - #define CHECK_ERROR(a) CoreMidiHelpers::checkError (a, __LINE__) - - static MidiDeviceInfo getMidiObjectInfo (MIDIObjectRef entity) - { - MidiDeviceInfo info; - - { - ScopedCFString str; - - if (CHECK_ERROR (MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str.cfString))) - info.name = String::fromCFString (str.cfString); - } - - SInt32 objectID = 0; - - if (CHECK_ERROR (MIDIObjectGetIntegerProperty (entity, kMIDIPropertyUniqueID, &objectID))) - { - info.identifier = String (objectID); - } - else - { - ScopedCFString str; - - if (CHECK_ERROR (MIDIObjectGetStringProperty (entity, kMIDIPropertyUniqueID, &str.cfString))) - info.identifier = String::fromCFString (str.cfString); - } - - return info; - } - - static MidiDeviceInfo getEndpointInfo (MIDIEndpointRef endpoint, bool isExternal) - { - // 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 getMidiObjectInfo (endpoint); - - auto result = getMidiObjectInfo (endpoint); - - // 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 info = getMidiObjectInfo (device); - - 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 = info; - } - else if (! result.name.startsWithIgnoreCase (info.name)) - { - // prepend the device name and identifier to the entity's - result.name = (info.name + " " + result.name).trimEnd(); - result.identifier = info.identifier + " " + result.identifier; - } - } - } - - return result; - } - - static MidiDeviceInfo getConnectedEndpointInfo (MIDIEndpointRef endpoint) - { - MidiDeviceInfo result; - - // Does the endpoint have connections? - CFDataRef connections = nullptr; - int numConnections = 0; - - MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections); - - if (connections != nullptr) - { - numConnections = ((int) CFDataGetLength (connections)) / (int) sizeof (MIDIUniqueID); - - if (numConnections > 0) - { - auto* pid = reinterpret_cast (CFDataGetBytePtr (connections)); - - for (int i = 0; i < numConnections; ++i, ++pid) - { - auto id = (MIDIUniqueID) ByteOrder::swapIfLittleEndian ((uint32) *pid); - MIDIObjectRef connObject; - MIDIObjectType connObjectType; - auto err = MIDIObjectFindByUniqueID (id, &connObject, &connObjectType); - - if (err == noErr) - { - MidiDeviceInfo deviceInfo; - - if (connObjectType == kMIDIObjectType_ExternalSource - || connObjectType == kMIDIObjectType_ExternalDestination) - { - // Connected to an external device's endpoint (10.3 and later). - deviceInfo = getEndpointInfo (static_cast (connObject), true); - } - else - { - // Connected to an external device (10.2) (or something else, catch-all) - deviceInfo = getMidiObjectInfo (connObject); - } - - if (deviceInfo != MidiDeviceInfo()) - { - if (result.name.isNotEmpty()) result.name += ", "; - if (result.identifier.isNotEmpty()) result.identifier += ", "; - - result.name += deviceInfo.name; - result.identifier += deviceInfo.identifier; - } - } - } - } - - CFRelease (connections); - } - - // 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 int createUniqueIDForMidiPort (String deviceName, bool isInput) - { - 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, appBundlePath.cfString, kCFURLPOSIXPathStyle, true)) - { - auto bundleRef = CFBundleCreate (kCFAllocatorDefault, bundleURL); - CFRelease (bundleURL); - - if (bundleRef != nullptr) - { - if (auto bundleId = CFBundleGetIdentifier (bundleRef)) - uniqueID = String::fromCFString (bundleId); - - CFRelease (bundleRef); - } - } - #endif - - if (uniqueID.isEmpty()) - uniqueID = String (Random::getSystemRandom().nextInt (1024)); - - 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*) - { - // TODO.. Should pass-on this notification.. - } - - static String getGlobalMidiClientName() - { - if (auto* app = JUCEApplicationBase::getInstance()) - return app->getApplicationName(); - - return "JUCE"; - } - - static MIDIClientRef getGlobalMidiClient() - { - static MIDIClientRef globalMidiClient = 0; - - if (globalMidiClient == 0) - { - // Since OSX 10.6, the MIDIClientCreate function will only work - // correctly when called from the message thread! - JUCE_ASSERT_MESSAGE_THREAD - - enableSimulatorMidiSession(); - - ScopedCFString name (getGlobalMidiClientName()); - CHECK_ERROR (MIDIClientCreate (name.cfString, &globalSystemChangeCallback, nullptr, &globalMidiClient)); - } - - return globalMidiClient; - } - - 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. - JUCE_ASSERT_MESSAGE_THREAD - - if (getGlobalMidiClient() == 0) - { - jassertfalse; - return {}; - } - - enableSimulatorMidiSession(); - - Array devices; - auto numDevices = (forInput ? MIDIGetNumberOfSources() : MIDIGetNumberOfDestinations()); - - for (ItemCount i = 0; i < numDevices; ++i) - { - MidiDeviceInfo deviceInfo; - - if (auto dest = forInput ? MIDIGetSource (i) : MIDIGetDestination (i)) - deviceInfo = getConnectedEndpointInfo (dest); - - if (deviceInfo == MidiDeviceInfo()) - deviceInfo.name = deviceInfo.identifier = ""; - - devices.add (deviceInfo); - } - - return devices; - } - - //============================================================================== - class MidiPortAndEndpoint - { - public: - MidiPortAndEndpoint (MIDIPortRef p, MIDIEndpointRef ep) noexcept - : port (p), endpoint (ep) - { - } - - ~MidiPortAndEndpoint() noexcept - { - if (port != 0) - MIDIPortDispose (port); - - // 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); - else - MIDIReceived (endpoint, packets); - } - - MIDIPortRef port; - MIDIEndpointRef endpoint; - }; - - //============================================================================== - struct MidiPortAndCallback; - CriticalSection callbackLock; - Array activeCallbacks; - - struct MidiPortAndCallback - { - MidiPortAndCallback (MidiInputCallback& cb) : callback (cb) {} - - ~MidiPortAndCallback() - { - active = false; - - { - const ScopedLock sl (callbackLock); - activeCallbacks.removeFirstMatchingValue (this); - } - - if (portAndEndpoint != nullptr && portAndEndpoint->port != 0) - CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endpoint)); - } - - void handlePackets (const MIDIPacketList* pktlist) - { - auto time = Time::getMillisecondCounterHiRes() * 0.001; - - const ScopedLock sl (callbackLock); - - if (activeCallbacks.contains (this) && active) - { - auto* packet = &pktlist->packet[0]; - - for (unsigned int i = 0; i < pktlist->numPackets; ++i) - { - auto len = readUnalignedlength)> (&(packet->length)); - concatenator.pushMidiData (packet->data, (int) len, time, input, callback); - - packet = MIDIPacketNext (packet); - } - } - } - - MidiInput* input = nullptr; - std::unique_ptr portAndEndpoint; - std::atomic active { false }; - - private: - MidiInputCallback& callback; - MidiDataConcatenator concatenator { 2048 }; - }; - - static void midiInputProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* /*srcConnRefCon*/) - { - 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; - } -} - -//============================================================================== -Array MidiInput::getAvailableDevices() -{ - return CoreMidiHelpers::findDevices (true); -} - -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()) - { - for (auto& endpoint : getEndpoints (true)) - { - auto endpointInfo = getConnectedEndpointInfo (endpoint); - - if (deviceIdentifier == endpointInfo.identifier) - { - ScopedCFString cfName; - - if (CHECK_ERROR (MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &cfName.cfString))) - { - 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 {}; -} - -std::unique_ptr MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) -{ - using namespace CoreMidiHelpers; - jassert (callback != nullptr); - - if (auto client = getGlobalMidiClient()) - { - auto mpc = std::make_unique (*callback); - mpc->active = false; - - MIDIEndpointRef endpoint; - ScopedCFString name (deviceName); - - auto err = MIDIDestinationCreate (client, name.cfString, midiInputProc, mpc.get(), &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, 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 {}; -} - -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() -{ - stopBackgroundThread(); - - delete static_cast (internal); -} - -void MidiOutput::sendMessageNow (const MidiMessage& message) -{ - #if JUCE_IOS - const MIDITimeStamp timeStamp = mach_absolute_time(); - #else - const MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); - #endif - - HeapBlock allocatedPackets; - MIDIPacketList stackPacket; - auto* packetToSend = &stackPacket; - auto dataSize = (size_t) message.getRawDataSize(); - - if (message.isSysEx()) - { - const int maxPacketSize = 256; - int pos = 0, bytesLeft = (int) dataSize; - const int numPackets = (bytesLeft + maxPacketSize - 1) / maxPacketSize; - allocatedPackets.malloc ((size_t) (32 * (size_t) numPackets + dataSize), 1); - packetToSend = allocatedPackets; - packetToSend->numPackets = (UInt32) numPackets; - - auto* p = packetToSend->packet; - - for (int i = 0; i < numPackets; ++i) - { - p->timeStamp = timeStamp; - p->length = (UInt16) jmin (maxPacketSize, bytesLeft); - memcpy (p->data, message.getRawData() + pos, p->length); - pos += p->length; - bytesLeft -= p->length; - p = MIDIPacketNext (p); - } - } - else if (dataSize < 65536) // max packet size - { - auto stackCapacity = sizeof (stackPacket.packet->data); - - if (dataSize > stackCapacity) - { - allocatedPackets.malloc ((sizeof (MIDIPacketList) - stackCapacity) + dataSize, 1); - packetToSend = allocatedPackets; - } - - packetToSend->numPackets = 1; - auto& p = *(packetToSend->packet); - p.timeStamp = timeStamp; - p.length = (UInt16) dataSize; - memcpy (p.data, message.getRawData(), dataSize); - } - else - { - jassertfalse; // packet too large to send! - return; - } - - static_cast (internal)->send (packetToSend); -} - -#undef CHECK_ERROR - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/.clang-tidy b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/.clang-tidy new file mode 100644 index 00000000..857909fa --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/.clang-tidy @@ -0,0 +1,3 @@ +# We want to ignore this directory, but we need to enable at least a single +# check to avoid an error +Checks: -*,darwin-dispatch-once-nonstatic diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/CMakeLists.txt b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/CMakeLists.txt new file mode 100644 index 00000000..6f697da7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/CMakeLists.txt @@ -0,0 +1,105 @@ +cmake_minimum_required(VERSION 3.4.1) + +# Set the name of the project and store it in PROJECT_NAME. Also set the following variables: +# PROJECT_SOURCE_DIR (usually the root directory where Oboe has been cloned e.g.) +# PROJECT_BINARY_DIR (usually the containing project's binary directory, +# e.g. ${OBOE_HOME}/samples/RhythmGame/.externalNativeBuild/cmake/ndkExtractorDebug/x86/oboe-bin) +project(oboe) + +set (oboe_sources + src/aaudio/AAudioLoader.cpp + src/aaudio/AudioStreamAAudio.cpp + src/common/AdpfWrapper.cpp + src/common/AudioSourceCaller.cpp + src/common/AudioStream.cpp + src/common/AudioStreamBuilder.cpp + src/common/DataConversionFlowGraph.cpp + src/common/FilterAudioStream.cpp + src/common/FixedBlockAdapter.cpp + src/common/FixedBlockReader.cpp + src/common/FixedBlockWriter.cpp + src/common/LatencyTuner.cpp + src/common/OboeExtensions.cpp + src/common/SourceFloatCaller.cpp + src/common/SourceI16Caller.cpp + src/common/SourceI24Caller.cpp + src/common/SourceI32Caller.cpp + src/common/Utilities.cpp + src/common/QuirksManager.cpp + src/fifo/FifoBuffer.cpp + src/fifo/FifoController.cpp + src/fifo/FifoControllerBase.cpp + src/fifo/FifoControllerIndirect.cpp + src/flowgraph/FlowGraphNode.cpp + src/flowgraph/ChannelCountConverter.cpp + src/flowgraph/ClipToRange.cpp + src/flowgraph/Limiter.cpp + src/flowgraph/ManyToMultiConverter.cpp + src/flowgraph/MonoBlend.cpp + src/flowgraph/MonoToMultiConverter.cpp + src/flowgraph/MultiToManyConverter.cpp + src/flowgraph/MultiToMonoConverter.cpp + src/flowgraph/RampLinear.cpp + src/flowgraph/SampleRateConverter.cpp + src/flowgraph/SinkFloat.cpp + src/flowgraph/SinkI16.cpp + src/flowgraph/SinkI24.cpp + src/flowgraph/SinkI32.cpp + src/flowgraph/SourceFloat.cpp + src/flowgraph/SourceI16.cpp + src/flowgraph/SourceI24.cpp + src/flowgraph/SourceI32.cpp + src/flowgraph/resampler/IntegerRatio.cpp + src/flowgraph/resampler/LinearResampler.cpp + src/flowgraph/resampler/MultiChannelResampler.cpp + src/flowgraph/resampler/PolyphaseResampler.cpp + src/flowgraph/resampler/PolyphaseResamplerMono.cpp + src/flowgraph/resampler/PolyphaseResamplerStereo.cpp + src/flowgraph/resampler/SincResampler.cpp + src/flowgraph/resampler/SincResamplerStereo.cpp + src/opensles/AudioInputStreamOpenSLES.cpp + src/opensles/AudioOutputStreamOpenSLES.cpp + src/opensles/AudioStreamBuffered.cpp + src/opensles/AudioStreamOpenSLES.cpp + src/opensles/EngineOpenSLES.cpp + src/opensles/OpenSLESUtilities.cpp + src/opensles/OutputMixerOpenSLES.cpp + src/common/StabilizedCallback.cpp + src/common/Trace.cpp + src/common/Version.cpp + ) + +add_library(oboe ${oboe_sources}) + +# Specify directories which the compiler should look for headers +target_include_directories(oboe + PRIVATE src + PUBLIC include) + +# JUCE CHANGE STARTS HERE + +# This comment provided for Apache License compliance. We've removed the extra warnings flags and +# the `-Werror` option, to avoid cases where compilers produce unexpected errors and fail the build. +# We've also removed the explicit `-std=c++17` compile option, and replaced it with a more +# cmake-friendly way of specifying the language standard. + +target_compile_options(oboe + PRIVATE + "$<$:-Ofast>" + "$<$:-O3>") +target_compile_features(oboe PRIVATE cxx_std_17) + +# JUCE CHANGE ENDS HERE + +# Enable logging of D,V for debug builds +target_compile_definitions(oboe PUBLIC $<$:OBOE_ENABLE_LOGGING=1>) + +target_link_libraries(oboe PRIVATE log OpenSLES) + +# When installing oboe put the libraries in the lib/ folder e.g. lib/arm64-v8a +install(TARGETS oboe + LIBRARY DESTINATION lib/${ANDROID_ABI} + ARCHIVE DESTINATION lib/${ANDROID_ABI}) + +# Also install the headers +install(DIRECTORY include/oboe DESTINATION include) diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/LICENSE b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/README.md b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/README.md new file mode 100644 index 00000000..cff7332f --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/README.md @@ -0,0 +1,10 @@ +The files in this directory are reproduced from the official Oboe repository, which can be found at +github.com/google/oboe. + +These files are from tag 1.8.0 (987538b). + +We've included only those parts of the original repository which are required to build the Oboe +library. Documentation, samples, tests, and other non-library items have been omitted. + +Files in this directory and below are licensed under the terms of the license in the LICENSE file +which you can find in the same directory as this readme. diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStream.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStream.h new file mode 100644 index 00000000..261772a1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStream.h @@ -0,0 +1,706 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_STREAM_H_ +#define OBOE_STREAM_H_ + +#include +#include +#include +#include +#include "oboe/Definitions.h" +#include "oboe/ResultWithValue.h" +#include "oboe/AudioStreamBuilder.h" +#include "oboe/AudioStreamBase.h" + +/** WARNING - UNDER CONSTRUCTION - THIS API WILL CHANGE. */ + +namespace oboe { + +/** + * The default number of nanoseconds to wait for when performing state change operations on the + * stream, such as `start` and `stop`. + * + * @see oboe::AudioStream::start + */ +constexpr int64_t kDefaultTimeoutNanos = (2000 * kNanosPerMillisecond); + +/** + * Base class for Oboe C++ audio stream. + */ +class AudioStream : public AudioStreamBase { + friend class AudioStreamBuilder; // allow access to setWeakThis() and lockWeakThis() +public: + + AudioStream() {} + + /** + * Construct an `AudioStream` using the given `AudioStreamBuilder` + * + * @param builder containing all the stream's attributes + */ + explicit AudioStream(const AudioStreamBuilder &builder); + + virtual ~AudioStream() = default; + + /** + * Open a stream based on the current settings. + * + * Note that we do not recommend re-opening a stream that has been closed. + * TODO Should we prevent re-opening? + * + * @return + */ + virtual Result open() { + return Result::OK; // Called by subclasses. Might do more in the future. + } + + /** + * Free the audio resources associated with a stream created by AAudioStreamBuilder_openStream(). + * + * AAudioStream_close() should be called at some point after calling this function. + * + * After this call, the stream will be in AAUDIO_STREAM_STATE_CLOSING + * + * This function is useful if you want to release the audio resources immediately, but still allow + * queries to the stream to occur from other threads. This often happens if you are monitoring + * stream progress from a UI thread. + * + * NOTE: This function is only fully implemented for MMAP streams, which are low latency streams + * supported by some devices. On other "Legacy" streams some audio resources will still be in use + * and some callbacks may still be in process after this call. + * + * Available in AAudio since API level 30. Returns Result::ErrorUnimplemented otherwise. + * + * * @return either Result::OK or an error. + */ + virtual Result release() { + return Result::ErrorUnimplemented; + } + + /** + * Close the stream and deallocate any resources from the open() call. + */ + virtual Result close(); + + /** + * Start the stream. This will block until the stream has been started, an error occurs + * or `timeoutNanoseconds` has been reached. + */ + virtual Result start(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); + + /** + * Pause the stream. This will block until the stream has been paused, an error occurs + * or `timeoutNanoseconds` has been reached. + */ + virtual Result pause(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); + + /** + * Flush the stream. This will block until the stream has been flushed, an error occurs + * or `timeoutNanoseconds` has been reached. + */ + virtual Result flush(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); + + /** + * Stop the stream. This will block until the stream has been stopped, an error occurs + * or `timeoutNanoseconds` has been reached. + */ + virtual Result stop(int64_t timeoutNanoseconds = kDefaultTimeoutNanos); + + /* Asynchronous requests. + * Use waitForStateChange() if you need to wait for completion. + */ + + /** + * Start the stream asynchronously. Returns immediately (does not block). Equivalent to calling + * `start(0)`. + */ + virtual Result requestStart() = 0; + + /** + * Pause the stream asynchronously. Returns immediately (does not block). Equivalent to calling + * `pause(0)`. + */ + virtual Result requestPause() = 0; + + /** + * Flush the stream asynchronously. Returns immediately (does not block). Equivalent to calling + * `flush(0)`. + */ + virtual Result requestFlush() = 0; + + /** + * Stop the stream asynchronously. Returns immediately (does not block). Equivalent to calling + * `stop(0)`. + */ + virtual Result requestStop() = 0; + + /** + * Query the current state, eg. StreamState::Pausing + * + * @return state or a negative error. + */ + virtual StreamState getState() = 0; + + /** + * Wait until the stream's current state no longer matches the input state. + * The input state is passed to avoid race conditions caused by the state + * changing between calls. + * + * Note that generally applications do not need to call this. It is considered + * an advanced technique and is mostly used for testing. + * + *

+     * int64_t timeoutNanos = 500 * kNanosPerMillisecond; // arbitrary 1/2 second
+     * StreamState currentState = stream->getState();
+     * StreamState nextState = StreamState::Unknown;
+     * while (result == Result::OK && currentState != StreamState::Paused) {
+     *     result = stream->waitForStateChange(
+     *                                   currentState, &nextState, timeoutNanos);
+     *     currentState = nextState;
+     * }
+     * 
+ * + * If the state does not change within the timeout period then it will + * return ErrorTimeout. This is true even if timeoutNanoseconds is zero. + * + * @param inputState The state we want to change away from. + * @param nextState Pointer to a variable that will be set to the new state. + * @param timeoutNanoseconds The maximum time to wait in nanoseconds. + * @return Result::OK or a Result::Error. + */ + virtual Result waitForStateChange(StreamState inputState, + StreamState *nextState, + int64_t timeoutNanoseconds) = 0; + + /** + * This can be used to adjust the latency of the buffer by changing + * the threshold where blocking will occur. + * By combining this with getXRunCount(), the latency can be tuned + * at run-time for each device. + * + * This cannot be set higher than getBufferCapacity(). + * + * @param requestedFrames requested number of frames that can be filled without blocking + * @return the resulting buffer size in frames (obtained using value()) or an error (obtained + * using error()) + */ + virtual ResultWithValue setBufferSizeInFrames(int32_t /* requestedFrames */) { + return Result::ErrorUnimplemented; + } + + /** + * An XRun is an Underrun or an Overrun. + * During playing, an underrun will occur if the stream is not written in time + * and the system runs out of valid data. + * During recording, an overrun will occur if the stream is not read in time + * and there is no place to put the incoming data so it is discarded. + * + * An underrun or overrun can cause an audible "pop" or "glitch". + * + * @return a result which is either Result::OK with the xRun count as the value, or a + * Result::Error* code + */ + virtual ResultWithValue getXRunCount() { + return ResultWithValue(Result::ErrorUnimplemented); + } + + /** + * @return true if XRun counts are supported on the stream + */ + virtual bool isXRunCountSupported() const = 0; + + /** + * Query the number of frames that are read or written by the endpoint at one time. + * + * @return burst size + */ + int32_t getFramesPerBurst() const { + return mFramesPerBurst; + } + + /** + * Get the number of bytes in each audio frame. This is calculated using the channel count + * and the sample format. For example, a 2 channel floating point stream will have + * 2 * 4 = 8 bytes per frame. + * + * @return number of bytes in each audio frame. + */ + int32_t getBytesPerFrame() const { return mChannelCount * getBytesPerSample(); } + + /** + * Get the number of bytes per sample. This is calculated using the sample format. For example, + * a stream using 16-bit integer samples will have 2 bytes per sample. + * + * @return the number of bytes per sample. + */ + int32_t getBytesPerSample() const; + + /** + * The number of audio frames written into the stream. + * This monotonic counter will never get reset. + * + * @return the number of frames written so far + */ + virtual int64_t getFramesWritten(); + + /** + * The number of audio frames read from the stream. + * This monotonic counter will never get reset. + * + * @return the number of frames read so far + */ + virtual int64_t getFramesRead(); + + /** + * Calculate the latency of a stream based on getTimestamp(). + * + * Output latency is the time it takes for a given frame to travel from the + * app to some type of digital-to-analog converter. If the DAC is external, for example + * in a USB interface or a TV connected by HDMI, then there may be additional latency + * that the Android device is unaware of. + * + * Input latency is the time it takes to a given frame to travel from an analog-to-digital + * converter (ADC) to the app. + * + * Note that the latency of an OUTPUT stream will increase abruptly when you write data to it + * and then decrease slowly over time as the data is consumed. + * + * The latency of an INPUT stream will decrease abruptly when you read data from it + * and then increase slowly over time as more data arrives. + * + * The latency of an OUTPUT stream is generally higher than the INPUT latency + * because an app generally tries to keep the OUTPUT buffer full and the INPUT buffer empty. + * + * Note that due to issues in Android before R, we recommend NOT calling + * this method from a data callback. See this tech note for more details. + * https://github.com/google/oboe/wiki/TechNote_ReleaseBuffer + * + * @return a ResultWithValue which has a result of Result::OK and a value containing the latency + * in milliseconds, or a result of Result::Error*. + */ + virtual ResultWithValue calculateLatencyMillis() { + return ResultWithValue(Result::ErrorUnimplemented); + } + + /** + * Get the estimated time that the frame at `framePosition` entered or left the audio processing + * pipeline. + * + * This can be used to coordinate events and interactions with the external environment, and to + * estimate the latency of an audio stream. An example of usage can be found in the hello-oboe + * sample (search for "calculateCurrentOutputLatencyMillis"). + * + * The time is based on the implementation's best effort, using whatever knowledge is available + * to the system, but cannot account for any delay unknown to the implementation. + * + * Note that due to issues in Android before R, we recommend NOT calling + * this method from a data callback. See this tech note for more details. + * https://github.com/google/oboe/wiki/TechNote_ReleaseBuffer + * + * @deprecated since 1.0, use AudioStream::getTimestamp(clockid_t clockId) instead, which + * returns ResultWithValue + * @param clockId the type of clock to use e.g. CLOCK_MONOTONIC + * @param framePosition the frame number to query + * @param timeNanoseconds an output parameter which will contain the presentation timestamp + */ + virtual Result getTimestamp(clockid_t /* clockId */, + int64_t* /* framePosition */, + int64_t* /* timeNanoseconds */) { + return Result::ErrorUnimplemented; + } + + /** + * Get the estimated time that the frame at `framePosition` entered or left the audio processing + * pipeline. + * + * This can be used to coordinate events and interactions with the external environment, and to + * estimate the latency of an audio stream. An example of usage can be found in the hello-oboe + * sample (search for "calculateCurrentOutputLatencyMillis"). + * + * The time is based on the implementation's best effort, using whatever knowledge is available + * to the system, but cannot account for any delay unknown to the implementation. + * + * Note that due to issues in Android before R, we recommend NOT calling + * this method from a data callback. See this tech note for more details. + * https://github.com/google/oboe/wiki/TechNote_ReleaseBuffer + * + * See + * @param clockId the type of clock to use e.g. CLOCK_MONOTONIC + * @return a FrameTimestamp containing the position and time at which a particular audio frame + * entered or left the audio processing pipeline, or an error if the operation failed. + */ + virtual ResultWithValue getTimestamp(clockid_t /* clockId */); + + // ============== I/O =========================== + /** + * Write data from the supplied buffer into the stream. This method will block until the write + * is complete or it runs out of time. + * + * If `timeoutNanoseconds` is zero then this call will not wait. + * + * @param buffer The address of the first sample. + * @param numFrames Number of frames to write. Only complete frames will be written. + * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion. + * @return a ResultWithValue which has a result of Result::OK and a value containing the number + * of frames actually written, or result of Result::Error*. + */ + virtual ResultWithValue write(const void* /* buffer */, + int32_t /* numFrames */, + int64_t /* timeoutNanoseconds */ ) { + return ResultWithValue(Result::ErrorUnimplemented); + } + + /** + * Read data into the supplied buffer from the stream. This method will block until the read + * is complete or it runs out of time. + * + * If `timeoutNanoseconds` is zero then this call will not wait. + * + * @param buffer The address of the first sample. + * @param numFrames Number of frames to read. Only complete frames will be read. + * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion. + * @return a ResultWithValue which has a result of Result::OK and a value containing the number + * of frames actually read, or result of Result::Error*. + */ + virtual ResultWithValue read(void* /* buffer */, + int32_t /* numFrames */, + int64_t /* timeoutNanoseconds */) { + return ResultWithValue(Result::ErrorUnimplemented); + } + + /** + * Get the underlying audio API which the stream uses. + * + * @return the API that this stream uses. + */ + virtual AudioApi getAudioApi() const = 0; + + /** + * Returns true if the underlying audio API is AAudio. + * + * @return true if this stream is implemented using the AAudio API. + */ + bool usesAAudio() const { + return getAudioApi() == AudioApi::AAudio; + } + + /** + * Only for debugging. Do not use in production. + * If you need to call this method something is wrong. + * If you think you need it for production then please let us know + * so we can modify Oboe so that you don't need this. + * + * @return nullptr or a pointer to a stream from the system API + */ + virtual void *getUnderlyingStream() const { + return nullptr; + } + + /** + * Update mFramesWritten. + * For internal use only. + */ + virtual void updateFramesWritten() = 0; + + /** + * Update mFramesRead. + * For internal use only. + */ + virtual void updateFramesRead() = 0; + + /* + * Swap old callback for new callback. + * This not atomic. + * This should only be used internally. + * @param dataCallback + * @return previous dataCallback + */ + AudioStreamDataCallback *swapDataCallback(AudioStreamDataCallback *dataCallback) { + AudioStreamDataCallback *previousCallback = mDataCallback; + mDataCallback = dataCallback; + return previousCallback; + } + + /* + * Swap old callback for new callback. + * This not atomic. + * This should only be used internally. + * @param errorCallback + * @return previous errorCallback + */ + AudioStreamErrorCallback *swapErrorCallback(AudioStreamErrorCallback *errorCallback) { + AudioStreamErrorCallback *previousCallback = mErrorCallback; + mErrorCallback = errorCallback; + return previousCallback; + } + + /** + * @return number of frames of data currently in the buffer + */ + ResultWithValue getAvailableFrames(); + + /** + * Wait until the stream has a minimum amount of data available in its buffer. + * This can be used with an EXCLUSIVE MMAP input stream to avoid reading data too close to + * the DSP write position, which may cause glitches. + * + * Starting with Oboe 1.7.1, the numFrames will be clipped internally against the + * BufferCapacity minus BurstSize. This is to prevent trying to wait for more frames + * than could possibly be available. In this case, the return value may be less than numFrames. + * Note that there may still be glitching if numFrames is too high. + * + * @param numFrames requested minimum frames available + * @param timeoutNanoseconds + * @return number of frames available, ErrorTimeout + */ + ResultWithValue waitForAvailableFrames(int32_t numFrames, + int64_t timeoutNanoseconds); + + /** + * @return last result passed from an error callback + */ + virtual oboe::Result getLastErrorCallbackResult() const { + return mErrorCallbackResult; + } + + + int32_t getDelayBeforeCloseMillis() const { + return mDelayBeforeCloseMillis; + } + + /** + * Set the time to sleep before closing the internal stream. + * + * Sometimes a callback can occur shortly after a stream has been stopped and + * even after a close! If the stream has been closed then the callback + * might access memory that has been freed, which could cause a crash. + * This seems to be more likely in Android P or earlier. + * But it can also occur in later versions. By sleeping, we give time for + * the callback threads to finish. + * + * Note that this only has an effect when OboeGlobals::areWorkaroundsEnabled() is true. + * + * @param delayBeforeCloseMillis time to sleep before close. + */ + void setDelayBeforeCloseMillis(int32_t delayBeforeCloseMillis) { + mDelayBeforeCloseMillis = delayBeforeCloseMillis; + } + + /** + * Enable or disable a device specific CPU performance hint. + * Runtime benchmarks such as the callback duration may be used to + * speed up the CPU and improve real-time performance. + * + * Note that this feature is device specific and may not be implemented. + * Also the benefits may vary by device. + * + * The flag will be checked in the Oboe data callback. If it transitions from false to true + * then the PerformanceHint feature will be started. + * This only needs to be called once. + * + * You may want to enable this if you have a dynamically changing workload + * and you notice that you are getting underruns and glitches when your workload increases. + * This might happen, for example, if you suddenly go from playing one note to + * ten notes on a synthesizer. + * + * Try the CPU Load test in OboeTester if you would like to experiment with this interactively. + * + * On some devices, this may be implemented using the "ADPF" library. + * + * @param enabled true if you would like a performance boost + */ + void setPerformanceHintEnabled(bool enabled) { + mPerformanceHintEnabled = enabled; + } + + /** + * This only tells you if the feature has been requested. + * It does not tell you if the PerformanceHint feature is implemented or active on the device. + * + * @return true if set using setPerformanceHintEnabled(). + */ + bool isPerformanceHintEnabled() { + return mPerformanceHintEnabled; + } + +protected: + + /** + * This is used to detect more than one error callback from a stream. + * These were bugs in some versions of Android that caused multiple error callbacks. + * Internal bug b/63087953 + * + * Calling this sets an atomic true and returns the previous value. + * + * @return false on first call, true on subsequent calls + */ + bool wasErrorCallbackCalled() { + return mErrorCallbackCalled.exchange(true); + } + + /** + * Wait for a transition from one state to another. + * @return OK if the endingState was observed, or ErrorUnexpectedState + * if any state that was not the startingState or endingState was observed + * or ErrorTimeout. + */ + virtual Result waitForStateTransition(StreamState startingState, + StreamState endingState, + int64_t timeoutNanoseconds); + + /** + * Override this to provide a default for when the application did not specify a callback. + * + * @param audioData + * @param numFrames + * @return result + */ + virtual DataCallbackResult onDefaultCallback(void* /* audioData */, int /* numFrames */) { + return DataCallbackResult::Stop; + } + + /** + * Override this to provide your own behaviour for the audio callback + * + * @param audioData container array which audio frames will be written into or read from + * @param numFrames number of frames which were read/written + * @return the result of the callback: stop or continue + * + */ + DataCallbackResult fireDataCallback(void *audioData, int numFrames); + + /** + * @return true if callbacks may be called + */ + bool isDataCallbackEnabled() { + return mDataCallbackEnabled; + } + + /** + * This can be set false internally to prevent callbacks + * after DataCallbackResult::Stop has been returned. + */ + void setDataCallbackEnabled(bool enabled) { + mDataCallbackEnabled = enabled; + } + + /** + * This should only be called as a stream is being opened. + * Otherwise we might override setDelayBeforeCloseMillis(). + */ + void calculateDefaultDelayBeforeCloseMillis(); + + /** + * Try to avoid a race condition when closing. + */ + void sleepBeforeClose() { + if (mDelayBeforeCloseMillis > 0) { + usleep(mDelayBeforeCloseMillis * 1000); + } + } + + /** + * This may be called internally at the beginning of a callback. + */ + virtual void beginPerformanceHintInCallback() {} + + /** + * This may be called internally at the end of a callback. + * @param numFrames passed to the callback + */ + virtual void endPerformanceHintInCallback(int32_t numFrames) {} + + /** + * This will be called when the stream is closed just in case performance hints were enabled. + */ + virtual void closePerformanceHint() {} + + /* + * Set a weak_ptr to this stream from the shared_ptr so that we can + * later use a shared_ptr in the error callback. + */ + void setWeakThis(std::shared_ptr &sharedStream) { + mWeakThis = sharedStream; + } + + /* + * Make a shared_ptr that will prevent this stream from being deleted. + */ + std::shared_ptr lockWeakThis() { + return mWeakThis.lock(); + } + + std::weak_ptr mWeakThis; // weak pointer to this object + + /** + * Number of frames which have been written into the stream + * + * This is signed integer to match the counters in AAudio. + * At audio rates, the counter will overflow in about six million years. + */ + std::atomic mFramesWritten{}; + + /** + * Number of frames which have been read from the stream. + * + * This is signed integer to match the counters in AAudio. + * At audio rates, the counter will overflow in about six million years. + */ + std::atomic mFramesRead{}; + + std::mutex mLock; // for synchronizing start/stop/close + + oboe::Result mErrorCallbackResult = oboe::Result::OK; + + /** + * Number of frames which will be copied to/from the audio device in a single read/write + * operation + */ + int32_t mFramesPerBurst = kUnspecified; + + // Time to sleep in order to prevent a race condition with a callback after a close(). + // Two milliseconds may be enough but 10 msec is even safer. + static constexpr int kMinDelayBeforeCloseMillis = 10; + int32_t mDelayBeforeCloseMillis = kMinDelayBeforeCloseMillis; + +private: + + // Log the scheduler if it changes. + void checkScheduler(); + int mPreviousScheduler = -1; + + std::atomic mDataCallbackEnabled{false}; + std::atomic mErrorCallbackCalled{false}; + + std::atomic mPerformanceHintEnabled{false}; // set only by app +}; + +/** + * This struct is a stateless functor which closes an AudioStream prior to its deletion. + * This means it can be used to safely delete a smart pointer referring to an open stream. + */ + struct StreamDeleterFunctor { + void operator()(AudioStream *audioStream) { + if (audioStream) { + audioStream->close(); + } + delete audioStream; + } + }; +} // namespace oboe + +#endif /* OBOE_STREAM_H_ */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBase.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBase.h new file mode 100644 index 00000000..6222e448 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBase.h @@ -0,0 +1,343 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_STREAM_BASE_H_ +#define OBOE_STREAM_BASE_H_ + +#include +#include +#include "oboe/AudioStreamCallback.h" +#include "oboe/Definitions.h" + +namespace oboe { + +/** + * Base class containing parameters for audio streams and builders. + **/ +class AudioStreamBase { + +public: + + AudioStreamBase() {} + + virtual ~AudioStreamBase() = default; + + // This class only contains primitives so we can use default constructor and copy methods. + + /** + * Default copy constructor + */ + AudioStreamBase(const AudioStreamBase&) = default; + + /** + * Default assignment operator + */ + AudioStreamBase& operator=(const AudioStreamBase&) = default; + + /** + * @return number of channels, for example 2 for stereo, or kUnspecified + */ + int32_t getChannelCount() const { return mChannelCount; } + + /** + * @return Direction::Input or Direction::Output + */ + Direction getDirection() const { return mDirection; } + + /** + * @return sample rate for the stream or kUnspecified + */ + int32_t getSampleRate() const { return mSampleRate; } + + /** + * @deprecated use `getFramesPerDataCallback` instead. + */ + int32_t getFramesPerCallback() const { return getFramesPerDataCallback(); } + + /** + * @return the number of frames in each data callback or kUnspecified. + */ + int32_t getFramesPerDataCallback() const { return mFramesPerCallback; } + + /** + * @return the audio sample format (e.g. Float or I16) + */ + AudioFormat getFormat() const { return mFormat; } + + /** + * Query the maximum number of frames that can be filled without blocking. + * If the stream has been closed the last known value will be returned. + * + * @return buffer size + */ + virtual int32_t getBufferSizeInFrames() { return mBufferSizeInFrames; } + + /** + * @return capacityInFrames or kUnspecified + */ + virtual int32_t getBufferCapacityInFrames() const { return mBufferCapacityInFrames; } + + /** + * @return the sharing mode of the stream. + */ + SharingMode getSharingMode() const { return mSharingMode; } + + /** + * @return the performance mode of the stream. + */ + PerformanceMode getPerformanceMode() const { return mPerformanceMode; } + + /** + * @return the device ID of the stream. + */ + int32_t getDeviceId() const { return mDeviceId; } + + /** + * For internal use only. + * @return the data callback object for this stream, if set. + */ + AudioStreamDataCallback *getDataCallback() const { + return mDataCallback; + } + + /** + * For internal use only. + * @return the error callback object for this stream, if set. + */ + AudioStreamErrorCallback *getErrorCallback() const { + return mErrorCallback; + } + + /** + * @return true if a data callback was set for this stream + */ + bool isDataCallbackSpecified() const { + return mDataCallback != nullptr; + } + + /** + * Note that if the app does not set an error callback then a + * default one may be provided. + * @return true if an error callback was set for this stream + */ + bool isErrorCallbackSpecified() const { + return mErrorCallback != nullptr; + } + + /** + * @return the usage for this stream. + */ + Usage getUsage() const { return mUsage; } + + /** + * @return the stream's content type. + */ + ContentType getContentType() const { return mContentType; } + + /** + * @return the stream's input preset. + */ + InputPreset getInputPreset() const { return mInputPreset; } + + /** + * @return the stream's session ID allocation strategy (None or Allocate). + */ + SessionId getSessionId() const { return mSessionId; } + + /** + * @return whether the content of the stream is spatialized. + */ + bool isContentSpatialized() const { return mIsContentSpatialized; } + + /** + * @return the spatialization behavior for the stream. + */ + SpatializationBehavior getSpatializationBehavior() const { return mSpatializationBehavior; } + + /** + * Return the policy that determines whether the audio may or may not be captured + * by other apps or the system. + * + * See AudioStreamBuilder_setAllowedCapturePolicy(). + * + * Added in API level 29 to AAudio. + * + * @return the allowed capture policy, for example AllowedCapturePolicy::All + */ + AllowedCapturePolicy getAllowedCapturePolicy() const { return mAllowedCapturePolicy; } + + /** + * Return whether this input stream is marked as privacy sensitive. + * + * See AudioStreamBuilder_setPrivacySensitiveMode(). + * + * Added in API level 30 to AAudio. + * + * @return PrivacySensitiveMode::Enabled if privacy sensitive, + * PrivacySensitiveMode::Disabled if not privacy sensitive, and + * PrivacySensitiveMode::Unspecified if API is not supported. + */ + PrivacySensitiveMode getPrivacySensitiveMode() const { return mPrivacySensitiveMode; } + + /** + * @return true if Oboe can convert channel counts to achieve optimal results. + */ + bool isChannelConversionAllowed() const { + return mChannelConversionAllowed; + } + + /** + * @return true if Oboe can convert data formats to achieve optimal results. + */ + bool isFormatConversionAllowed() const { + return mFormatConversionAllowed; + } + + /** + * @return whether and how Oboe can convert sample rates to achieve optimal results. + */ + SampleRateConversionQuality getSampleRateConversionQuality() const { + return mSampleRateConversionQuality; + } + + /** + * @return the stream's channel mask. + */ + ChannelMask getChannelMask() const { + return mChannelMask; + } + + /** + * @return number of channels for the hardware, for example 2 for stereo, or kUnspecified. + */ + int32_t getHardwareChannelCount() const { return mHardwareChannelCount; } + + /** + * @return hardware sample rate for the stream or kUnspecified + */ + int32_t getHardwareSampleRate() const { return mHardwareSampleRate; } + + /** + * @return the audio sample format of the hardware (e.g. Float or I16) + */ + AudioFormat getHardwareFormat() const { return mHardwareFormat; } + +protected: + /** The callback which will be fired when new data is ready to be read/written. **/ + AudioStreamDataCallback *mDataCallback = nullptr; + std::shared_ptr mSharedDataCallback; + + /** The callback which will be fired when an error or a disconnect occurs. **/ + AudioStreamErrorCallback *mErrorCallback = nullptr; + std::shared_ptr mSharedErrorCallback; + + /** Number of audio frames which will be requested in each callback */ + int32_t mFramesPerCallback = kUnspecified; + /** Stream channel count */ + int32_t mChannelCount = kUnspecified; + /** Stream sample rate */ + int32_t mSampleRate = kUnspecified; + /** Stream audio device ID */ + int32_t mDeviceId = kUnspecified; + /** Stream buffer capacity specified as a number of audio frames */ + int32_t mBufferCapacityInFrames = kUnspecified; + /** Stream buffer size specified as a number of audio frames */ + int32_t mBufferSizeInFrames = kUnspecified; + /** Stream channel mask. Only active on Android 32+ */ + ChannelMask mChannelMask = ChannelMask::Unspecified; + + /** Stream sharing mode */ + SharingMode mSharingMode = SharingMode::Shared; + /** Format of audio frames */ + AudioFormat mFormat = AudioFormat::Unspecified; + /** Stream direction */ + Direction mDirection = Direction::Output; + /** Stream performance mode */ + PerformanceMode mPerformanceMode = PerformanceMode::None; + + /** Stream usage. Only active on Android 28+ */ + Usage mUsage = Usage::Media; + /** Stream content type. Only active on Android 28+ */ + ContentType mContentType = ContentType::Music; + /** Stream input preset. Only active on Android 28+ + * TODO InputPreset::Unspecified should be considered as a possible default alternative. + */ + InputPreset mInputPreset = InputPreset::VoiceRecognition; + /** Stream session ID allocation strategy. Only active on Android 28+ */ + SessionId mSessionId = SessionId::None; + + /** Allowed Capture Policy. Only active on Android 29+ */ + AllowedCapturePolicy mAllowedCapturePolicy = AllowedCapturePolicy::Unspecified; + + /** Privacy Sensitive Mode. Only active on Android 30+ */ + PrivacySensitiveMode mPrivacySensitiveMode = PrivacySensitiveMode::Unspecified; + + /** Control the name of the package creating the stream. Only active on Android 31+ */ + std::string mPackageName; + /** Control the attribution tag of the context creating the stream. Only active on Android 31+ */ + std::string mAttributionTag; + + /** Whether the content is already spatialized. Only used on Android 32+ */ + bool mIsContentSpatialized = false; + /** Spatialization Behavior. Only active on Android 32+ */ + SpatializationBehavior mSpatializationBehavior = SpatializationBehavior::Unspecified; + + /** Hardware channel count. Only specified on Android 34+ AAudio streams */ + int32_t mHardwareChannelCount = kUnspecified; + /** Hardware sample rate. Only specified on Android 34+ AAudio streams */ + int32_t mHardwareSampleRate = kUnspecified; + /** Hardware format. Only specified on Android 34+ AAudio streams */ + AudioFormat mHardwareFormat = AudioFormat::Unspecified; + + // Control whether Oboe can convert channel counts to achieve optimal results. + bool mChannelConversionAllowed = false; + // Control whether Oboe can convert data formats to achieve optimal results. + bool mFormatConversionAllowed = false; + // Control whether and how Oboe can convert sample rates to achieve optimal results. + SampleRateConversionQuality mSampleRateConversionQuality = SampleRateConversionQuality::None; + + /** Validate stream parameters that might not be checked in lower layers */ + virtual Result isValidConfig() { + switch (mFormat) { + case AudioFormat::Unspecified: + case AudioFormat::I16: + case AudioFormat::Float: + case AudioFormat::I24: + case AudioFormat::I32: + case AudioFormat::IEC61937: + break; + + default: + return Result::ErrorInvalidFormat; + } + + switch (mSampleRateConversionQuality) { + case SampleRateConversionQuality::None: + case SampleRateConversionQuality::Fastest: + case SampleRateConversionQuality::Low: + case SampleRateConversionQuality::Medium: + case SampleRateConversionQuality::High: + case SampleRateConversionQuality::Best: + return Result::OK; + default: + return Result::ErrorIllegalArgument; + } + } +}; + +} // namespace oboe + +#endif /* OBOE_STREAM_BASE_H_ */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h new file mode 100644 index 00000000..89e1447e --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamBuilder.h @@ -0,0 +1,670 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_STREAM_BUILDER_H_ +#define OBOE_STREAM_BUILDER_H_ + +#include "oboe/Definitions.h" +#include "oboe/AudioStreamBase.h" +#include "oboe/Utilities.h" +#include "ResultWithValue.h" + +namespace oboe { + + // This depends on AudioStream, so we use forward declaration, it will close and delete the stream + struct StreamDeleterFunctor; + using ManagedStream = std::unique_ptr; + +/** + * Factory class for an audio Stream. + */ +class AudioStreamBuilder : public AudioStreamBase { +public: + + AudioStreamBuilder() : AudioStreamBase() {} + + AudioStreamBuilder(const AudioStreamBase &audioStreamBase): AudioStreamBase(audioStreamBase) {} + + /** + * Request a specific number of channels. + * + * Default is kUnspecified. If the value is unspecified then + * the application should query for the actual value after the stream is opened. + * + * As the channel count here may be different from the corresponding channel count of + * provided channel mask used in setChannelMask(). The last called will be respected + * if this function and setChannelMask() are called. + */ + AudioStreamBuilder *setChannelCount(int channelCount) { + mChannelCount = channelCount; + mChannelMask = ChannelMask::Unspecified; + return this; + } + + /** + * Request a specific channel mask. + * + * Default is kUnspecified. If the value is unspecified then the application + * should query for the actual value after the stream is opened. + * + * As the corresponding channel count of provided channel mask here may be different + * from the channel count used in setChannelCount(). The last called will be respected + * if this function and setChannelCount() are called. + * + * As the setChannelMask API is available on Android 32+, this call will only take effects + * on Android 32+. + */ + AudioStreamBuilder *setChannelMask(ChannelMask channelMask) { + mChannelMask = channelMask; + mChannelCount = getChannelCountFromChannelMask(channelMask); + return this; + } + + /** + * Request the direction for a stream. The default is Direction::Output. + * + * @param direction Direction::Output or Direction::Input + */ + AudioStreamBuilder *setDirection(Direction direction) { + mDirection = direction; + return this; + } + + /** + * Request a specific sample rate in Hz. + * + * Default is kUnspecified. If the value is unspecified then + * the application should query for the actual value after the stream is opened. + * + * Technically, this should be called the "frame rate" or "frames per second", + * because it refers to the number of complete frames transferred per second. + * But it is traditionally called "sample rate". Se we use that term. + * + */ + AudioStreamBuilder *setSampleRate(int32_t sampleRate) { + mSampleRate = sampleRate; + return this; + } + + /** + * @deprecated use `setFramesPerDataCallback` instead. + */ + AudioStreamBuilder *setFramesPerCallback(int framesPerCallback) { + return setFramesPerDataCallback(framesPerCallback); + } + + /** + * Request a specific number of frames for the data callback. + * + * Default is kUnspecified. If the value is unspecified then + * the actual number may vary from callback to callback. + * + * If an application can handle a varying number of frames then we recommend + * leaving this unspecified. This allow the underlying API to optimize + * the callbacks. But if your application is, for example, doing FFTs or other block + * oriented operations, then call this function to get the sizes you need. + * + * Calling setFramesPerDataCallback() does not guarantee anything about timing. + * This just collects the data into a the number of frames that your app requires. + * We encourage leaving this unspecified in most cases. + * + * If this number is larger than the burst size, some bursts will not receive a callback. + * If this number is smaller than the burst size, there may be multiple callbacks in a single + * burst. + * + * @param framesPerCallback + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setFramesPerDataCallback(int framesPerCallback) { + mFramesPerCallback = framesPerCallback; + return this; + } + + /** + * Request a sample data format, for example Format::Float. + * + * Default is Format::Unspecified. If the value is unspecified then + * the application should query for the actual value after the stream is opened. + */ + AudioStreamBuilder *setFormat(AudioFormat format) { + mFormat = format; + return this; + } + + /** + * Set the requested buffer capacity in frames. + * BufferCapacityInFrames is the maximum possible BufferSizeInFrames. + * + * The final stream capacity may differ. For AAudio it should be at least this big. + * For OpenSL ES, it could be smaller. + * + * Default is kUnspecified. + * + * @param bufferCapacityInFrames the desired buffer capacity in frames or kUnspecified + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setBufferCapacityInFrames(int32_t bufferCapacityInFrames) { + mBufferCapacityInFrames = bufferCapacityInFrames; + return this; + } + + /** + * Get the audio API which will be requested when opening the stream. No guarantees that this is + * the API which will actually be used. Query the stream itself to find out the API which is + * being used. + * + * If you do not specify the API, then AAudio will be used if isAAudioRecommended() + * returns true. Otherwise OpenSL ES will be used. + * + * @return the requested audio API + */ + AudioApi getAudioApi() const { return mAudioApi; } + + /** + * If you leave this unspecified then Oboe will choose the best API + * for the device and SDK version at runtime. + * + * This should almost always be left unspecified, except for debugging purposes. + * Specifying AAudio will force Oboe to use AAudio on 8.0, which is extremely risky. + * Specifying OpenSLES should mainly be used to test legacy performance/functionality. + * + * If the caller requests AAudio and it is supported then AAudio will be used. + * + * @param audioApi Must be AudioApi::Unspecified, AudioApi::OpenSLES or AudioApi::AAudio. + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setAudioApi(AudioApi audioApi) { + mAudioApi = audioApi; + return this; + } + + /** + * Is the AAudio API supported on this device? + * + * AAudio was introduced in the Oreo 8.0 release. + * + * @return true if supported + */ + static bool isAAudioSupported(); + + /** + * Is the AAudio API recommended this device? + * + * AAudio may be supported but not recommended because of version specific issues. + * AAudio is not recommended for Android 8.0 or earlier versions. + * + * @return true if recommended + */ + static bool isAAudioRecommended(); + + /** + * Request a mode for sharing the device. + * The requested sharing mode may not be available. + * So the application should query for the actual mode after the stream is opened. + * + * @param sharingMode SharingMode::Shared or SharingMode::Exclusive + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setSharingMode(SharingMode sharingMode) { + mSharingMode = sharingMode; + return this; + } + + /** + * Request a performance level for the stream. + * This will determine the latency, the power consumption, and the level of + * protection from glitches. + * + * @param performanceMode for example, PerformanceMode::LowLatency + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setPerformanceMode(PerformanceMode performanceMode) { + mPerformanceMode = performanceMode; + return this; + } + + + /** + * Set the intended use case for an output stream. + * + * The system will use this information to optimize the behavior of the stream. + * This could, for example, affect how volume and focus is handled for the stream. + * The usage is ignored for input streams. + * + * The default, if you do not call this function, is Usage::Media. + * + * Added in API level 28. + * + * @param usage the desired usage, eg. Usage::Game + */ + AudioStreamBuilder *setUsage(Usage usage) { + mUsage = usage; + return this; + } + + /** + * Set the type of audio data that an output stream will carry. + * + * The system will use this information to optimize the behavior of the stream. + * This could, for example, affect whether a stream is paused when a notification occurs. + * The contentType is ignored for input streams. + * + * The default, if you do not call this function, is ContentType::Music. + * + * Added in API level 28. + * + * @param contentType the type of audio data, eg. ContentType::Speech + */ + AudioStreamBuilder *setContentType(ContentType contentType) { + mContentType = contentType; + return this; + } + + /** + * Set the input (capture) preset for the stream. + * + * The system will use this information to optimize the behavior of the stream. + * This could, for example, affect which microphones are used and how the + * recorded data is processed. + * + * The default, if you do not call this function, is InputPreset::VoiceRecognition. + * That is because VoiceRecognition is the preset with the lowest latency + * on many platforms. + * + * Added in API level 28. + * + * @param inputPreset the desired configuration for recording + */ + AudioStreamBuilder *setInputPreset(InputPreset inputPreset) { + mInputPreset = inputPreset; + return this; + } + + /** Set the requested session ID. + * + * The session ID can be used to associate a stream with effects processors. + * The effects are controlled using the Android AudioEffect Java API. + * + * The default, if you do not call this function, is SessionId::None. + * + * If set to SessionId::Allocate then a session ID will be allocated + * when the stream is opened. + * + * The allocated session ID can be obtained by calling AudioStream::getSessionId() + * and then used with this function when opening another stream. + * This allows effects to be shared between streams. + * + * Session IDs from Oboe can be used the Android Java APIs and vice versa. + * So a session ID from an Oboe stream can be passed to Java + * and effects applied using the Java AudioEffect API. + * + * Allocated session IDs will always be positive and nonzero. + * + * Added in API level 28. + * + * @param sessionId an allocated sessionID or SessionId::Allocate + */ + AudioStreamBuilder *setSessionId(SessionId sessionId) { + mSessionId = sessionId; + return this; + } + + /** + * Request a stream to a specific audio input/output device given an audio device ID. + * + * In most cases, the primary device will be the appropriate device to use, and the + * deviceId can be left kUnspecified. + * + * The ID could be obtained from the Java AudioManager. + * AudioManager.getDevices() returns an array of AudioDeviceInfo, + * which contains a getId() method. That ID can be passed to this function. + * + * It is possible that you may not get the device that you requested. + * So if it is important to you, you should call + * stream->getDeviceId() after the stream is opened to + * verify the actual ID. + * + * Note that when using OpenSL ES, this will be ignored and the created + * stream will have deviceId kUnspecified. + * + * @param deviceId device identifier or kUnspecified + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setDeviceId(int32_t deviceId) { + mDeviceId = deviceId; + return this; + } + + /** + * Specify whether this stream audio may or may not be captured by other apps or the system. + * + * The default is AllowedCapturePolicy::Unspecified which maps to AAUDIO_ALLOW_CAPTURE_BY_ALL. + * + * Note that an application can also set its global policy, in which case the most restrictive + * policy is always applied. See android.media.AudioAttributes.setAllowedCapturePolicy. + * + * Added in API level 29 to AAudio. + * + * @param inputPreset the desired level of opt-out from being captured. + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setAllowedCapturePolicy(AllowedCapturePolicy allowedCapturePolicy) { + mAllowedCapturePolicy = allowedCapturePolicy; + return this; + } + + /** Indicates whether this input stream must be marked as privacy sensitive or not. + * + * When PrivacySensitiveMode::Enabled, this input stream is privacy sensitive and any + * concurrent capture is not permitted. + * + * This is off (PrivacySensitiveMode::Disabled) by default except when the input preset is + * InputPreset::VoiceRecognition or InputPreset::Camcorder + * + * Always takes precedence over default from input preset when set explicitly. + * + * Only relevant if the stream direction is Direction::Input and AAudio is used. + * + * Added in API level 30 to AAudio. + * + * @param privacySensitive PrivacySensitiveMode::Enabled if capture from this stream must be + * marked as privacy sensitive, PrivacySensitiveMode::Disabled if stream should be marked as + * not sensitive. + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setPrivacySensitiveMode(PrivacySensitiveMode privacySensitiveMode) { + mPrivacySensitiveMode = privacySensitiveMode; + return this; + } + + /** + * Specifies whether the audio data of this output stream has already been processed for spatialization. + * + * If the stream has been processed for spatialization, setting this to true will prevent issues such as + * double-processing on platforms that will spatialize audio data. + * + * This is false by default. + * + * Available since API level 32. + * + * @param isContentSpatialized whether the content is already spatialized + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setIsContentSpatialized(bool isContentSpatialized) { + mIsContentSpatialized = isContentSpatialized; + return this; + } + + /** + * Sets the behavior affecting whether spatialization will be used. + * + * The AAudio system will use this information to select whether the stream will go through a + * spatializer effect or not when the effect is supported and enabled. + * + * This is SpatializationBehavior::Never by default. + * + * Available since API level 32. + * + * @param spatializationBehavior the desired spatialization behavior + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setSpatializationBehavior(SpatializationBehavior spatializationBehavior) { + mSpatializationBehavior = spatializationBehavior; + return this; + } + + /** + * Specifies an object to handle data related callbacks from the underlying API. + * + * Important: See AudioStreamCallback for restrictions on what may be called + * from the callback methods. + * + * We pass a shared_ptr so that the sharedDataCallback object cannot be deleted + * before the stream is deleted. + * + * @param sharedDataCallback + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setDataCallback(std::shared_ptr sharedDataCallback) { + // Use this raw pointer in the rest of the code to retain backwards compatibility. + mDataCallback = sharedDataCallback.get(); + // Hold a shared_ptr to protect the raw pointer for the lifetime of the stream. + mSharedDataCallback = sharedDataCallback; + return this; + } + + /** + * Pass a raw pointer to a data callback. This is not recommended because the dataCallback + * object might get deleted by the app while it is being used. + * + * @deprecated Call setDataCallback(std::shared_ptr) instead. + * @param dataCallback + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setDataCallback(AudioStreamDataCallback *dataCallback) { + mDataCallback = dataCallback; + mSharedDataCallback = nullptr; + return this; + } + + /** + * Specifies an object to handle error related callbacks from the underlying API. + * This can occur when a stream is disconnected because a headset is plugged in or unplugged. + * It can also occur if the audio service fails or if an exclusive stream is stolen by + * another stream. + * + * Important: See AudioStreamCallback for restrictions on what may be called + * from the callback methods. + * + * When an error callback occurs, the associated stream must be stopped and closed + * in a separate thread. + * + * We pass a shared_ptr so that the errorCallback object cannot be deleted before the stream is deleted. + * If the stream was created using a shared_ptr then the stream cannot be deleted before the + * error callback has finished running. + * + * @param sharedErrorCallback + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setErrorCallback(std::shared_ptr sharedErrorCallback) { + // Use this raw pointer in the rest of the code to retain backwards compatibility. + mErrorCallback = sharedErrorCallback.get(); + // Hold a shared_ptr to protect the raw pointer for the lifetime of the stream. + mSharedErrorCallback = sharedErrorCallback; + return this; + } + + /** + * Pass a raw pointer to an error callback. This is not recommended because the errorCallback + * object might get deleted by the app while it is being used. + * + * @deprecated Call setErrorCallback(std::shared_ptr) instead. + * @param errorCallback + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setErrorCallback(AudioStreamErrorCallback *errorCallback) { + mErrorCallback = errorCallback; + mSharedErrorCallback = nullptr; + return this; + } + + /** + * Specifies an object to handle data or error related callbacks from the underlying API. + * + * This is the equivalent of calling both setDataCallback() and setErrorCallback(). + * + * Important: See AudioStreamCallback for restrictions on what may be called + * from the callback methods. + * + * @deprecated Call setDataCallback(std::shared_ptr) and + * setErrorCallback(std::shared_ptr) instead. + * @param streamCallback + * @return pointer to the builder so calls can be chained + */ + AudioStreamBuilder *setCallback(AudioStreamCallback *streamCallback) { + // Use the same callback object for both, dual inheritance. + mDataCallback = streamCallback; + mErrorCallback = streamCallback; + return this; + } + + /** + * If true then Oboe might convert channel counts to achieve optimal results. + * On some versions of Android for example, stereo streams could not use a FAST track. + * So a mono stream might be used instead and duplicated to two channels. + * On some devices, mono streams might be broken, so a stereo stream might be opened + * and converted to mono. + * + * Default is false. + */ + AudioStreamBuilder *setChannelConversionAllowed(bool allowed) { + mChannelConversionAllowed = allowed; + return this; + } + + /** + * If true then Oboe might convert data formats to achieve optimal results. + * On some versions of Android, for example, a float stream could not get a + * low latency data path. So an I16 stream might be opened and converted to float. + * + * Default is false. + */ + AudioStreamBuilder *setFormatConversionAllowed(bool allowed) { + mFormatConversionAllowed = allowed; + return this; + } + + /** + * Specify the quality of the sample rate converter in Oboe. + * + * If set to None then Oboe will not do sample rate conversion. But the underlying APIs might + * still do sample rate conversion if you specify a sample rate. + * That can prevent you from getting a low latency stream. + * + * If you do the conversion in Oboe then you might still get a low latency stream. + * + * Default is SampleRateConversionQuality::None + */ + AudioStreamBuilder *setSampleRateConversionQuality(SampleRateConversionQuality quality) { + mSampleRateConversionQuality = quality; + return this; + } + + /** + * Declare the name of the package creating the stream. + * + * This is usually {@code Context#getPackageName()}. + * + * The default, if you do not call this function, is a random package in the calling uid. + * The vast majority of apps have only one package per calling UID. + * If an invalid package name is set, input streams may not be given permission to + * record when started. + * + * The package name is usually the applicationId in your app's build.gradle file. + * + * Available since API level 31. + * + * @param packageName packageName of the calling app. + */ + AudioStreamBuilder *setPackageName(std::string packageName) { + mPackageName = packageName; + return this; + } + + /** + * Declare the attribution tag of the context creating the stream. + * + * This is usually {@code Context#getAttributionTag()}. + * + * The default, if you do not call this function, is null. + * + * Available since API level 31. + * + * @param attributionTag attributionTag of the calling context. + */ + AudioStreamBuilder *setAttributionTag(std::string attributionTag) { + mAttributionTag = attributionTag; + return this; + } + + /** + * @return true if AAudio will be used based on the current settings. + */ + bool willUseAAudio() const { + return (mAudioApi == AudioApi::AAudio && isAAudioSupported()) + || (mAudioApi == AudioApi::Unspecified && isAAudioRecommended()); + } + + /** + * Create and open a stream object based on the current settings. + * + * The caller owns the pointer to the AudioStream object + * and must delete it when finished. + * + * @deprecated Use openStream(std::shared_ptr &stream) instead. + * @param stream pointer to a variable to receive the stream address + * @return OBOE_OK if successful or a negative error code + */ + Result openStream(AudioStream **stream); + + /** + * Create and open a stream object based on the current settings. + * + * The caller shares the pointer to the AudioStream object. + * The shared_ptr is used internally by Oboe to prevent the stream from being + * deleted while it is being used by callbacks. + * + * @param stream reference to a shared_ptr to receive the stream address + * @return OBOE_OK if successful or a negative error code + */ + Result openStream(std::shared_ptr &stream); + + /** + * Create and open a ManagedStream object based on the current builder state. + * + * The caller must create a unique ptr, and pass by reference so it can be + * modified to point to an opened stream. The caller owns the unique ptr, + * and it will be automatically closed and deleted when going out of scope. + * + * @deprecated Use openStream(std::shared_ptr &stream) instead. + * @param stream Reference to the ManagedStream (uniqueptr) used to keep track of stream + * @return OBOE_OK if successful or a negative error code. + */ + Result openManagedStream(ManagedStream &stream); + +private: + + /** + * @param other + * @return true if channels, format and sample rate match + */ + bool isCompatible(AudioStreamBase &other); + + /** + * Create an AudioStream object. The AudioStream must be opened before use. + * + * The caller owns the pointer. + * + * @return pointer to an AudioStream object or nullptr. + */ + oboe::AudioStream *build(); + + AudioApi mAudioApi = AudioApi::Unspecified; +}; + +} // namespace oboe + +#endif /* OBOE_STREAM_BUILDER_H_ */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamCallback.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamCallback.h new file mode 100644 index 00000000..17d28ba7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/AudioStreamCallback.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_STREAM_CALLBACK_H +#define OBOE_STREAM_CALLBACK_H + +#include "oboe/Definitions.h" + +namespace oboe { + +class AudioStream; + +/** + * AudioStreamDataCallback defines a callback interface for + * moving data to/from an audio stream using `onAudioReady` + * 2) being alerted when a stream has an error using `onError*` methods + * + * It is used with AudioStreamBuilder::setDataCallback(). + */ + +class AudioStreamDataCallback { +public: + virtual ~AudioStreamDataCallback() = default; + + /** + * A buffer is ready for processing. + * + * For an output stream, this function should render and write numFrames of data + * in the stream's current data format to the audioData buffer. + * + * For an input stream, this function should read and process numFrames of data + * from the audioData buffer. + * + * The audio data is passed through the buffer. So do NOT call read() or + * write() on the stream that is making the callback. + * + * Note that numFrames can vary unless AudioStreamBuilder::setFramesPerCallback() + * is called. + * + * Also note that this callback function should be considered a "real-time" function. + * It must not do anything that could cause an unbounded delay because that can cause the + * audio to glitch or pop. + * + * These are things the function should NOT do: + *
    + *
  • allocate memory using, for example, malloc() or new
  • + *
  • any file operations such as opening, closing, reading or writing
  • + *
  • any network operations such as streaming
  • + *
  • use any mutexes or other synchronization primitives
  • + *
  • sleep
  • + *
  • oboeStream->stop(), pause(), flush() or close()
  • + *
  • oboeStream->read()
  • + *
  • oboeStream->write()
  • + *
+ * + * The following are OK to call from the data callback: + *
    + *
  • oboeStream->get*()
  • + *
  • oboe::convertToText()
  • + *
  • oboeStream->setBufferSizeInFrames()
  • + *
+ * + * If you need to move data, eg. MIDI commands, in or out of the callback function then + * we recommend the use of non-blocking techniques such as an atomic FIFO. + * + * @param audioStream pointer to the associated stream + * @param audioData buffer containing input data or a place to put output data + * @param numFrames number of frames to be processed + * @return DataCallbackResult::Continue or DataCallbackResult::Stop + */ + virtual DataCallbackResult onAudioReady( + AudioStream *audioStream, + void *audioData, + int32_t numFrames) = 0; +}; + +/** + * AudioStreamErrorCallback defines a callback interface for + * being alerted when a stream has an error or is disconnected + * using `onError*` methods. + * + * Note: This callback is only fired when an AudioStreamCallback is set. + * If you use AudioStream::write() you have to evaluate the return codes of + * AudioStream::write() to notice errors in the stream. + * + * It is used with AudioStreamBuilder::setErrorCallback(). + */ +class AudioStreamErrorCallback { +public: + virtual ~AudioStreamErrorCallback() = default; + + /** + * This will be called before other `onError` methods when an error occurs on a stream, + * such as when the stream is disconnected. + * + * It can be used to override and customize the normal error processing. + * Use of this method is considered an advanced technique. + * It might, for example, be used if an app want to use a high level lock when + * closing and reopening a stream. + * Or it might be used when an app want to signal a management thread that handles + * all of the stream state. + * + * If this method returns false it indicates that the stream has *not been stopped and closed + * by the application. In this case it will be stopped by Oboe in the following way: + * onErrorBeforeClose() will be called, then the stream will be closed and onErrorAfterClose() + * will be closed. + * + * If this method returns true it indicates that the stream *has* been stopped and closed + * by the application and Oboe will not do this. + * In that case, the app MUST stop() and close() the stream. + * + * This method will be called on a thread created by Oboe. + * + * @param audioStream pointer to the associated stream + * @param error + * @return true if the stream has been stopped and closed, false if not + */ + virtual bool onError(AudioStream* /* audioStream */, Result /* error */) { + return false; + } + + /** + * This will be called when an error occurs on a stream, + * such as when the stream is disconnected, + * and if onError() returns false (indicating that the error has not already been handled). + * + * Note that this will be called on a thread created by Oboe. + * + * The underlying stream will already be stopped by Oboe but not yet closed. + * So the stream can be queried. + * + * Do not close or delete the stream in this method because it will be + * closed after this method returns. + * + * @param audioStream pointer to the associated stream + * @param error + */ + virtual void onErrorBeforeClose(AudioStream* /* audioStream */, Result /* error */) {} + + /** + * This will be called when an error occurs on a stream, + * such as when the stream is disconnected, + * and if onError() returns false (indicating that the error has not already been handled). + * + * The underlying AAudio or OpenSL ES stream will already be stopped AND closed by Oboe. + * So the underlying stream cannot be referenced. + * But you can still query most parameters. + * + * This callback could be used to reopen a new stream on another device. + * + * @param audioStream pointer to the associated stream + * @param error + */ + virtual void onErrorAfterClose(AudioStream* /* audioStream */, Result /* error */) {} + +}; + +/** + * AudioStreamCallback defines a callback interface for: + * + * 1) moving data to/from an audio stream using `onAudioReady` + * 2) being alerted when a stream has an error using `onError*` methods + * + * It is used with AudioStreamBuilder::setCallback(). + * + * It combines the interfaces defined by AudioStreamDataCallback and AudioStreamErrorCallback. + * This was the original callback object. We now recommend using the individual interfaces + * and using setDataCallback() and setErrorCallback(). + * + * @deprecated Use `AudioStreamDataCallback` and `AudioStreamErrorCallback` instead + */ +class AudioStreamCallback : public AudioStreamDataCallback, + public AudioStreamErrorCallback { +public: + virtual ~AudioStreamCallback() = default; +}; + +} // namespace oboe + +#endif //OBOE_STREAM_CALLBACK_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Definitions.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Definitions.h new file mode 100644 index 00000000..aaf4d640 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Definitions.h @@ -0,0 +1,897 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_DEFINITIONS_H +#define OBOE_DEFINITIONS_H + +#include +#include + +// Oboe needs to be able to build on old NDKs so we use hard coded constants. +// The correctness of these constants is verified in "aaudio/AAudioLoader.cpp". + +namespace oboe { + + /** + * Represents any attribute, property or value which hasn't been specified. + */ + constexpr int32_t kUnspecified = 0; + + // TODO: Investigate using std::chrono + /** + * The number of nanoseconds in a microsecond. 1,000. + */ + constexpr int64_t kNanosPerMicrosecond = 1000; + + /** + * The number of nanoseconds in a millisecond. 1,000,000. + */ + constexpr int64_t kNanosPerMillisecond = kNanosPerMicrosecond * 1000; + + /** + * The number of milliseconds in a second. 1,000. + */ + constexpr int64_t kMillisPerSecond = 1000; + + /** + * The number of nanoseconds in a second. 1,000,000,000. + */ + constexpr int64_t kNanosPerSecond = kNanosPerMillisecond * kMillisPerSecond; + + /** + * The state of the audio stream. + */ + enum class StreamState : int32_t { // aaudio_stream_state_t + Uninitialized = 0, // AAUDIO_STREAM_STATE_UNINITIALIZED, + Unknown = 1, // AAUDIO_STREAM_STATE_UNKNOWN, + Open = 2, // AAUDIO_STREAM_STATE_OPEN, + Starting = 3, // AAUDIO_STREAM_STATE_STARTING, + Started = 4, // AAUDIO_STREAM_STATE_STARTED, + Pausing = 5, // AAUDIO_STREAM_STATE_PAUSING, + Paused = 6, // AAUDIO_STREAM_STATE_PAUSED, + Flushing = 7, // AAUDIO_STREAM_STATE_FLUSHING, + Flushed = 8, // AAUDIO_STREAM_STATE_FLUSHED, + Stopping = 9, // AAUDIO_STREAM_STATE_STOPPING, + Stopped = 10, // AAUDIO_STREAM_STATE_STOPPED, + Closing = 11, // AAUDIO_STREAM_STATE_CLOSING, + Closed = 12, // AAUDIO_STREAM_STATE_CLOSED, + Disconnected = 13, // AAUDIO_STREAM_STATE_DISCONNECTED, + }; + + /** + * The direction of the stream. + */ + enum class Direction : int32_t { // aaudio_direction_t + + /** + * Used for playback. + */ + Output = 0, // AAUDIO_DIRECTION_OUTPUT, + + /** + * Used for recording. + */ + Input = 1, // AAUDIO_DIRECTION_INPUT, + }; + + /** + * The format of audio samples. + */ + enum class AudioFormat : int32_t { // aaudio_format_t + /** + * Invalid format. + */ + Invalid = -1, // AAUDIO_FORMAT_INVALID, + + /** + * Unspecified format. Format will be decided by Oboe. + * When calling getHardwareFormat(), this will be returned if + * the API is not supported. + */ + Unspecified = 0, // AAUDIO_FORMAT_UNSPECIFIED, + + /** + * Signed 16-bit integers. + */ + I16 = 1, // AAUDIO_FORMAT_PCM_I16, + + /** + * Single precision floating point. + * + * This is the recommended format for most applications. + * But note that the use of Float may prevent the opening of + * a low-latency input path on OpenSL ES or Legacy AAudio streams. + */ + Float = 2, // AAUDIO_FORMAT_PCM_FLOAT, + + /** + * Signed 24-bit integers, packed into 3 bytes. + * + * Note that the use of this format does not guarantee that + * the full precision will be provided. The underlying device may + * be using I16 format. + * + * Added in API 31 (S). + */ + I24 = 3, // AAUDIO_FORMAT_PCM_I24_PACKED + + /** + * Signed 32-bit integers. + * + * Note that the use of this format does not guarantee that + * the full precision will be provided. The underlying device may + * be using I16 format. + * + * Added in API 31 (S). + */ + I32 = 4, // AAUDIO_FORMAT_PCM_I32 + + /** + * This format is used for compressed audio wrapped in IEC61937 for HDMI + * or S/PDIF passthrough. + * + * Unlike PCM playback, the Android framework is not able to do format + * conversion for IEC61937. In that case, when IEC61937 is requested, sampling + * rate and channel count or channel mask must be specified. Otherwise, it may + * fail when opening the stream. Apps are able to get the correct configuration + * for the playback by calling AudioManager#getDevices(int). + * + * Available since API 34 (U). + */ + IEC61937 = 5, // AAUDIO_FORMAT_IEC61937 + }; + + /** + * The result of an audio callback. + */ + enum class DataCallbackResult : int32_t { // aaudio_data_callback_result_t + // Indicates to the caller that the callbacks should continue. + Continue = 0, // AAUDIO_CALLBACK_RESULT_CONTINUE, + + // Indicates to the caller that the callbacks should stop immediately. + Stop = 1, // AAUDIO_CALLBACK_RESULT_STOP, + }; + + /** + * The result of an operation. All except the `OK` result indicates that an error occurred. + * The `Result` can be converted into a human readable string using `convertToText`. + */ + enum class Result : int32_t { // aaudio_result_t + OK = 0, // AAUDIO_OK + ErrorBase = -900, // AAUDIO_ERROR_BASE, + ErrorDisconnected = -899, // AAUDIO_ERROR_DISCONNECTED, + ErrorIllegalArgument = -898, // AAUDIO_ERROR_ILLEGAL_ARGUMENT, + ErrorInternal = -896, // AAUDIO_ERROR_INTERNAL, + ErrorInvalidState = -895, // AAUDIO_ERROR_INVALID_STATE, + ErrorInvalidHandle = -892, // AAUDIO_ERROR_INVALID_HANDLE, + ErrorUnimplemented = -890, // AAUDIO_ERROR_UNIMPLEMENTED, + ErrorUnavailable = -889, // AAUDIO_ERROR_UNAVAILABLE, + ErrorNoFreeHandles = -888, // AAUDIO_ERROR_NO_FREE_HANDLES, + ErrorNoMemory = -887, // AAUDIO_ERROR_NO_MEMORY, + ErrorNull = -886, // AAUDIO_ERROR_NULL, + ErrorTimeout = -885, // AAUDIO_ERROR_TIMEOUT, + ErrorWouldBlock = -884, // AAUDIO_ERROR_WOULD_BLOCK, + ErrorInvalidFormat = -883, // AAUDIO_ERROR_INVALID_FORMAT, + ErrorOutOfRange = -882, // AAUDIO_ERROR_OUT_OF_RANGE, + ErrorNoService = -881, // AAUDIO_ERROR_NO_SERVICE, + ErrorInvalidRate = -880, // AAUDIO_ERROR_INVALID_RATE, + // Reserved for future AAudio result types + Reserved1, + Reserved2, + Reserved3, + Reserved4, + Reserved5, + Reserved6, + Reserved7, + Reserved8, + Reserved9, + Reserved10, + ErrorClosed = -869, + }; + + /** + * The sharing mode of the audio stream. + */ + enum class SharingMode : int32_t { // aaudio_sharing_mode_t + + /** + * This will be the only stream using a particular source or sink. + * This mode will provide the lowest possible latency. + * You should close EXCLUSIVE streams immediately when you are not using them. + * + * If you do not need the lowest possible latency then we recommend using Shared, + * which is the default. + */ + Exclusive = 0, // AAUDIO_SHARING_MODE_EXCLUSIVE, + + /** + * Multiple applications can share the same device. + * The data from output streams will be mixed by the audio service. + * The data for input streams will be distributed by the audio service. + * + * This will have higher latency than the EXCLUSIVE mode. + */ + Shared = 1, // AAUDIO_SHARING_MODE_SHARED, + }; + + /** + * The performance mode of the audio stream. + */ + enum class PerformanceMode : int32_t { // aaudio_performance_mode_t + + /** + * No particular performance needs. Default. + */ + None = 10, // AAUDIO_PERFORMANCE_MODE_NONE, + + /** + * Extending battery life is most important. + */ + PowerSaving = 11, // AAUDIO_PERFORMANCE_MODE_POWER_SAVING, + + /** + * Reducing latency is most important. + */ + LowLatency = 12, // AAUDIO_PERFORMANCE_MODE_LOW_LATENCY + }; + + /** + * The underlying audio API used by the audio stream. + */ + enum class AudioApi : int32_t { + /** + * Try to use AAudio. If not available then use OpenSL ES. + */ + Unspecified = kUnspecified, + + /** + * Use OpenSL ES. + * Note that OpenSL ES is deprecated in Android 13, API 30 and above. + */ + OpenSLES, + + /** + * Try to use AAudio. Fail if unavailable. + * AAudio was first supported in Android 8, API 26 and above. + * It is only recommended for API 27 and above. + */ + AAudio + }; + + /** + * Specifies the quality of the sample rate conversion performed by Oboe. + * Higher quality will require more CPU load. + * Higher quality conversion will probably be implemented using a sinc based resampler. + */ + enum class SampleRateConversionQuality : int32_t { + /** + * No conversion by Oboe. Underlying APIs may still do conversion. + */ + None, + /** + * Fastest conversion but may not sound great. + * This may be implemented using bilinear interpolation. + */ + Fastest, + /** + * Low quality conversion with 8 taps. + */ + Low, + /** + * Medium quality conversion with 16 taps. + */ + Medium, + /** + * High quality conversion with 32 taps. + */ + High, + /** + * Highest quality conversion, which may be expensive in terms of CPU. + */ + Best, + }; + + /** + * The Usage attribute expresses *why* you are playing a sound, what is this sound used for. + * This information is used by certain platforms or routing policies + * to make more refined volume or routing decisions. + * + * Note that these match the equivalent values in AudioAttributes in the Android Java API. + * + * This attribute only has an effect on Android API 28+. + */ + enum class Usage : int32_t { // aaudio_usage_t + /** + * Use this for streaming media, music performance, video, podcasts, etcetera. + */ + Media = 1, // AAUDIO_USAGE_MEDIA + + /** + * Use this for voice over IP, telephony, etcetera. + */ + VoiceCommunication = 2, // AAUDIO_USAGE_VOICE_COMMUNICATION + + /** + * Use this for sounds associated with telephony such as busy tones, DTMF, etcetera. + */ + VoiceCommunicationSignalling = 3, // AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING + + /** + * Use this to demand the users attention. + */ + Alarm = 4, // AAUDIO_USAGE_ALARM + + /** + * Use this for notifying the user when a message has arrived or some + * other background event has occured. + */ + Notification = 5, // AAUDIO_USAGE_NOTIFICATION + + /** + * Use this when the phone rings. + */ + NotificationRingtone = 6, // AAUDIO_USAGE_NOTIFICATION_RINGTONE + + /** + * Use this to attract the users attention when, for example, the battery is low. + */ + NotificationEvent = 10, // AAUDIO_USAGE_NOTIFICATION_EVENT + + /** + * Use this for screen readers, etcetera. + */ + AssistanceAccessibility = 11, // AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY + + /** + * Use this for driving or navigation directions. + */ + AssistanceNavigationGuidance = 12, // AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE + + /** + * Use this for user interface sounds, beeps, etcetera. + */ + AssistanceSonification = 13, // AAUDIO_USAGE_ASSISTANCE_SONIFICATION + + /** + * Use this for game audio and sound effects. + */ + Game = 14, // AAUDIO_USAGE_GAME + + /** + * Use this for audio responses to user queries, audio instructions or help utterances. + */ + Assistant = 16, // AAUDIO_USAGE_ASSISTANT + }; + + + /** + * The ContentType attribute describes *what* you are playing. + * It expresses the general category of the content. This information is optional. + * But in case it is known (for instance {@link Movie} for a + * movie streaming service or {@link Speech} for + * an audio book application) this information might be used by the audio framework to + * enforce audio focus. + * + * Note that these match the equivalent values in AudioAttributes in the Android Java API. + * + * This attribute only has an effect on Android API 28+. + */ + enum ContentType : int32_t { // aaudio_content_type_t + + /** + * Use this for spoken voice, audio books, etcetera. + */ + Speech = 1, // AAUDIO_CONTENT_TYPE_SPEECH + + /** + * Use this for pre-recorded or live music. + */ + Music = 2, // AAUDIO_CONTENT_TYPE_MUSIC + + /** + * Use this for a movie or video soundtrack. + */ + Movie = 3, // AAUDIO_CONTENT_TYPE_MOVIE + + /** + * Use this for sound is designed to accompany a user action, + * such as a click or beep sound made when the user presses a button. + */ + Sonification = 4, // AAUDIO_CONTENT_TYPE_SONIFICATION + }; + + /** + * Defines the audio source. + * An audio source defines both a default physical source of audio signal, and a recording + * configuration. + * + * Note that these match the equivalent values in MediaRecorder.AudioSource in the Android Java API. + * + * This attribute only has an effect on Android API 28+. + */ + enum InputPreset : int32_t { // aaudio_input_preset_t + /** + * Use this preset when other presets do not apply. + */ + Generic = 1, // AAUDIO_INPUT_PRESET_GENERIC + + /** + * Use this preset when recording video. + */ + Camcorder = 5, // AAUDIO_INPUT_PRESET_CAMCORDER + + /** + * Use this preset when doing speech recognition. + */ + VoiceRecognition = 6, // AAUDIO_INPUT_PRESET_VOICE_RECOGNITION + + /** + * Use this preset when doing telephony or voice messaging. + */ + VoiceCommunication = 7, // AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION + + /** + * Use this preset to obtain an input with no effects. + * Note that this input will not have automatic gain control + * so the recorded volume may be very low. + */ + Unprocessed = 9, // AAUDIO_INPUT_PRESET_UNPROCESSED + + /** + * Use this preset for capturing audio meant to be processed in real time + * and played back for live performance (e.g karaoke). + * The capture path will minimize latency and coupling with playback path. + */ + VoicePerformance = 10, // AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE + + }; + + /** + * This attribute can be used to allocate a session ID to the audio stream. + * + * This attribute only has an effect on Android API 28+. + */ + enum SessionId { + /** + * Do not allocate a session ID. + * Effects cannot be used with this stream. + * Default. + */ + None = -1, // AAUDIO_SESSION_ID_NONE + + /** + * Allocate a session ID that can be used to attach and control + * effects using the Java AudioEffects API. + * Note that the use of this flag may result in higher latency. + * + * Note that this matches the value of AudioManager.AUDIO_SESSION_ID_GENERATE. + */ + Allocate = 0, // AAUDIO_SESSION_ID_ALLOCATE + }; + + /** + * The channel count of the audio stream. The underlying type is `int32_t`. + * Use of this enum is convenient to avoid "magic" + * numbers when specifying the channel count. + * + * For example, you can write + * `builder.setChannelCount(ChannelCount::Stereo)` + * rather than `builder.setChannelCount(2)` + * + */ + enum ChannelCount : int32_t { + /** + * Audio channel count definition, use Mono or Stereo + */ + Unspecified = kUnspecified, + + /** + * Use this for mono audio + */ + Mono = 1, + + /** + * Use this for stereo audio. + */ + Stereo = 2, + }; + + /** + * The channel mask of the audio stream. The underlying type is `uint32_t`. + * Use of this enum is convenient. + * + * ChannelMask::Unspecified means this is not specified. + * The rest of the enums are channel position masks. + * Use the combinations of the channel position masks defined below instead of + * using those values directly. + * + * Channel masks are for input only, output only, or both input and output. + * These channel masks are different than those defined in AudioFormat.java. + * If an app gets a channel mask from Java API and wants to use it in Oboe, + * conversion should be done by the app. + */ + enum class ChannelMask : uint32_t { // aaudio_channel_mask_t + Unspecified = kUnspecified, + FrontLeft = 1 << 0, + FrontRight = 1 << 1, + FrontCenter = 1 << 2, + LowFrequency = 1 << 3, + BackLeft = 1 << 4, + BackRight = 1 << 5, + FrontLeftOfCenter = 1 << 6, + FrontRightOfCenter = 1 << 7, + BackCenter = 1 << 8, + SideLeft = 1 << 9, + SideRight = 1 << 10, + TopCenter = 1 << 11, + TopFrontLeft = 1 << 12, + TopFrontCenter = 1 << 13, + TopFrontRight = 1 << 14, + TopBackLeft = 1 << 15, + TopBackCenter = 1 << 16, + TopBackRight = 1 << 17, + TopSideLeft = 1 << 18, + TopSideRight = 1 << 19, + BottomFrontLeft = 1 << 20, + BottomFrontCenter = 1 << 21, + BottomFrontRight = 1 << 22, + LowFrequency2 = 1 << 23, + FrontWideLeft = 1 << 24, + FrontWideRight = 1 << 25, + + /** + * Supported for Input and Output + */ + Mono = FrontLeft, + + /** + * Supported for Input and Output + */ + Stereo = FrontLeft | + FrontRight, + + /** + * Supported for only Output + */ + CM2Point1 = FrontLeft | + FrontRight | + LowFrequency, + + /** + * Supported for only Output + */ + Tri = FrontLeft | + FrontRight | + FrontCenter, + + /** + * Supported for only Output + */ + TriBack = FrontLeft | + FrontRight | + BackCenter, + + /** + * Supported for only Output + */ + CM3Point1 = FrontLeft | + FrontRight | + FrontCenter | + LowFrequency, + + /** + * Supported for Input and Output + */ + CM2Point0Point2 = FrontLeft | + FrontRight | + TopSideLeft | + TopSideRight, + + /** + * Supported for Input and Output + */ + CM2Point1Point2 = CM2Point0Point2 | + LowFrequency, + + /** + * Supported for Input and Output + */ + CM3Point0Point2 = FrontLeft | + FrontRight | + FrontCenter | + TopSideLeft | + TopSideRight, + + /** + * Supported for Input and Output + */ + CM3Point1Point2 = CM3Point0Point2 | + LowFrequency, + + /** + * Supported for only Output + */ + Quad = FrontLeft | + FrontRight | + BackLeft | + BackRight, + + /** + * Supported for only Output + */ + QuadSide = FrontLeft | + FrontRight | + SideLeft | + SideRight, + + /** + * Supported for only Output + */ + Surround = FrontLeft | + FrontRight | + FrontCenter | + BackCenter, + + /** + * Supported for only Output + */ + Penta = Quad | + FrontCenter, + + /** + * Supported for Input and Output. aka 5Point1Back + */ + CM5Point1 = FrontLeft | + FrontRight | + FrontCenter | + LowFrequency | + BackLeft | + BackRight, + + /** + * Supported for only Output + */ + CM5Point1Side = FrontLeft | + FrontRight | + FrontCenter | + LowFrequency | + SideLeft | + SideRight, + + /** + * Supported for only Output + */ + CM6Point1 = FrontLeft | + FrontRight | + FrontCenter | + LowFrequency | + BackLeft | + BackRight | + BackCenter, + + /** + * Supported for only Output + */ + CM7Point1 = CM5Point1 | + SideLeft | + SideRight, + + /** + * Supported for only Output + */ + CM5Point1Point2 = CM5Point1 | + TopSideLeft | + TopSideRight, + + /** + * Supported for only Output + */ + CM5Point1Point4 = CM5Point1 | + TopFrontLeft | + TopFrontRight | + TopBackLeft | + TopBackRight, + + /** + * Supported for only Output + */ + CM7Point1Point2 = CM7Point1 | + TopSideLeft | + TopSideRight, + + /** + * Supported for only Output + */ + CM7Point1Point4 = CM7Point1 | + TopFrontLeft | + TopFrontRight | + TopBackLeft | + TopBackRight, + + /** + * Supported for only Output + */ + CM9Point1Point4 = CM7Point1Point4 | + FrontWideLeft | + FrontWideRight, + + /** + * Supported for only Output + */ + CM9Point1Point6 = CM9Point1Point4 | + TopSideLeft | + TopSideRight, + + /** + * Supported for only Input + */ + FrontBack = FrontCenter | + BackCenter, + }; + + /** + * The spatialization behavior of the audio stream. + */ + enum class SpatializationBehavior : int32_t { + + /** + * Constant indicating that the spatialization behavior is not specified. + */ + Unspecified = kUnspecified, + + /** + * Constant indicating the audio content associated with these attributes will follow the + * default platform behavior with regards to which content will be spatialized or not. + */ + Auto = 1, + + /** + * Constant indicating the audio content associated with these attributes should never + * be spatialized. + */ + Never = 2, + }; + + /** + * The PrivacySensitiveMode attribute determines whether an input stream can be shared + * with another privileged app, for example the Assistant. + * + * This allows to override the default behavior tied to the audio source (e.g + * InputPreset::VoiceCommunication is private by default but InputPreset::Unprocessed is not). + */ + enum class PrivacySensitiveMode : int32_t { + + /** + * When not explicitly requested, set privacy sensitive mode according to input preset: + * communication and camcorder captures are considered privacy sensitive by default. + */ + Unspecified = kUnspecified, + + /** + * Privacy sensitive mode disabled. + */ + Disabled = 1, + + /** + * Privacy sensitive mode enabled. + */ + Enabled = 2, + }; + + /** + * Specifies whether audio may or may not be captured by other apps or the system for an + * output stream. + * + * Note that these match the equivalent values in AudioAttributes in the Android Java API. + * + * Added in API level 29 for AAudio. + */ + enum class AllowedCapturePolicy : int32_t { + /** + * When not explicitly requested, set privacy sensitive mode according to the Usage. + * This should behave similarly to setting AllowedCapturePolicy::All. + */ + Unspecified = kUnspecified, + /** + * Indicates that the audio may be captured by any app. + * + * For privacy, the following Usages can not be recorded: VoiceCommunication*, + * Notification*, Assistance* and Assistant. + * + * On Android Q, only Usage::Game and Usage::Media may be captured. + * + * See ALLOW_CAPTURE_BY_ALL in the AudioAttributes Java API. + */ + All = 1, + /** + * Indicates that the audio may only be captured by system apps. + * + * System apps can capture for many purposes like accessibility, user guidance... + * but have strong restriction. See ALLOW_CAPTURE_BY_SYSTEM in the AudioAttributes Java API + * for what the system apps can do with the capture audio. + */ + System = 2, + /** + * Indicates that the audio may not be recorded by any app, even if it is a system app. + * + * It is encouraged to use AllowedCapturePolicy::System instead of this value as system apps + * provide significant and useful features for the user (eg. accessibility). + * See ALLOW_CAPTURE_BY_NONE in the AudioAttributes Java API + */ + None = 3, + }; + + /** + * On API 16 to 26 OpenSL ES will be used. When using OpenSL ES the optimal values for sampleRate and + * framesPerBurst are not known by the native code. + * On API 17+ these values should be obtained from the AudioManager using this code: + * + *

+     * // Note that this technique only works for built-in speakers and headphones.
+     * AudioManager myAudioMgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+     * String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
+     * int defaultSampleRate = Integer.parseInt(sampleRateStr);
+     * String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
+     * int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr);
+     * 
+ * + * It can then be passed down to Oboe through JNI. + * + * AAudio will get the optimal framesPerBurst from the HAL and will ignore this value. + */ + class DefaultStreamValues { + + public: + + /** The default sample rate to use when opening new audio streams */ + static int32_t SampleRate; + /** The default frames per burst to use when opening new audio streams */ + static int32_t FramesPerBurst; + /** The default channel count to use when opening new audio streams */ + static int32_t ChannelCount; + + }; + + /** + * The time at which the frame at `position` was presented + */ + struct FrameTimestamp { + int64_t position; // in frames + int64_t timestamp; // in nanoseconds + }; + + class OboeGlobals { + public: + + static bool areWorkaroundsEnabled() { + return mWorkaroundsEnabled; + } + + /** + * Disable this when writing tests to reproduce bugs in AAudio or OpenSL ES + * that have workarounds in Oboe. + * @param enabled + */ + static void setWorkaroundsEnabled(bool enabled) { + mWorkaroundsEnabled = enabled; + } + + private: + static bool mWorkaroundsEnabled; + }; +} // namespace oboe + +#endif // OBOE_DEFINITIONS_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/FifoBuffer.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/FifoBuffer.h new file mode 100644 index 00000000..68c1f055 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/FifoBuffer.h @@ -0,0 +1,164 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_FIFOPROCESSOR_H +#define OBOE_FIFOPROCESSOR_H + +#include +#include + +#include "oboe/Definitions.h" + +#include "oboe/FifoControllerBase.h" + +namespace oboe { + +class FifoBuffer { +public: + /** + * Construct a `FifoBuffer`. + * + * @param bytesPerFrame amount of bytes for one frame + * @param capacityInFrames the capacity of frames in fifo + */ + FifoBuffer(uint32_t bytesPerFrame, uint32_t capacityInFrames); + + /** + * Construct a `FifoBuffer`. + * To be used if the storage allocation is done outside of FifoBuffer. + * + * @param bytesPerFrame amount of bytes for one frame + * @param capacityInFrames capacity of frames in fifo + * @param readCounterAddress address of read counter + * @param writeCounterAddress address of write counter + * @param dataStorageAddress address of storage + */ + FifoBuffer(uint32_t bytesPerFrame, + uint32_t capacityInFrames, + std::atomic *readCounterAddress, + std::atomic *writeCounterAddress, + uint8_t *dataStorageAddress); + + ~FifoBuffer(); + + /** + * Convert a number of frames in bytes. + * + * @return number of bytes + */ + int32_t convertFramesToBytes(int32_t frames); + + /** + * Read framesToRead or, if not enough, then read as many as are available. + * + * @param destination + * @param framesToRead number of frames requested + * @return number of frames actually read + */ + int32_t read(void *destination, int32_t framesToRead); + + /** + * Write framesToWrite or, if too enough, then write as many as the fifo are not empty. + * + * @param destination + * @param framesToWrite number of frames requested + * @return number of frames actually write + */ + int32_t write(const void *source, int32_t framesToWrite); + + /** + * Get the buffer capacity in frames. + * + * @return number of frames + */ + uint32_t getBufferCapacityInFrames() const; + + /** + * Calls read(). If all of the frames cannot be read then the remainder of the buffer + * is set to zero. + * + * @param destination + * @param framesToRead number of frames requested + * @return number of frames actually read + */ + int32_t readNow(void *destination, int32_t numFrames); + + /** + * Get the number of frames in the fifo. + * + * @return number of frames actually in the buffer + */ + uint32_t getFullFramesAvailable() { + return mFifo->getFullFramesAvailable(); + } + + /** + * Get the amount of bytes per frame. + * + * @return number of bytes per frame + */ + uint32_t getBytesPerFrame() const { + return mBytesPerFrame; + } + + /** + * Get the position of read counter. + * + * @return position of read counter + */ + uint64_t getReadCounter() const { + return mFifo->getReadCounter(); + } + + /** + * Set the position of read counter. + * + * @param n position of read counter + */ + void setReadCounter(uint64_t n) { + mFifo->setReadCounter(n); + } + + /** + * Get the position of write counter. + * + * @return position of write counter + */ + uint64_t getWriteCounter() { + return mFifo->getWriteCounter(); + } + + /** + * Set the position of write counter. + * + * @param n position of write counter + */ + void setWriteCounter(uint64_t n) { + mFifo->setWriteCounter(n); + } + +private: + uint32_t mBytesPerFrame; + uint8_t* mStorage; + bool mStorageOwned; // did this object allocate the storage? + std::unique_ptr mFifo; + uint64_t mFramesReadCount; + uint64_t mFramesUnderrunCount; +}; + +} // namespace oboe + +#endif //OBOE_FIFOPROCESSOR_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/FifoControllerBase.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/FifoControllerBase.h new file mode 100644 index 00000000..6c12b75e --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/FifoControllerBase.h @@ -0,0 +1,112 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVEOBOE_FIFOCONTROLLERBASE_H +#define NATIVEOBOE_FIFOCONTROLLERBASE_H + +#include + +namespace oboe { + +/** + * Manage the read/write indices of a circular buffer. + * + * The caller is responsible for reading and writing the actual data. + * Note that the span of available frames may not be contiguous. They + * may wrap around from the end to the beginning of the buffer. In that + * case the data must be read or written in at least two blocks of frames. + * + */ + +class FifoControllerBase { + +public: + /** + * Construct a `FifoControllerBase`. + * + * @param totalFrames capacity of the circular buffer in frames + */ + FifoControllerBase(uint32_t totalFrames); + + virtual ~FifoControllerBase() = default; + + /** + * The frames available to read will be calculated from the read and write counters. + * The result will be clipped to the capacity of the buffer. + * If the buffer has underflowed then this will return zero. + * + * @return number of valid frames available to read. + */ + uint32_t getFullFramesAvailable() const; + + /** + * The index in a circular buffer of the next frame to read. + * + * @return read index position + */ + uint32_t getReadIndex() const; + + /** + * Advance read index from a number of frames. + * Equivalent of incrementReadCounter(numFrames). + * + * @param numFrames number of frames to advance the read index + */ + void advanceReadIndex(uint32_t numFrames); + + /** + * Get the number of frame that are not written yet. + * + * @return maximum number of frames that can be written without exceeding the threshold + */ + uint32_t getEmptyFramesAvailable() const; + + /** + * The index in a circular buffer of the next frame to write. + * + * @return index of the next frame to write + */ + uint32_t getWriteIndex() const; + + /** + * Advance write index from a number of frames. + * Equivalent of incrementWriteCounter(numFrames). + * + * @param numFrames number of frames to advance the write index + */ + void advanceWriteIndex(uint32_t numFrames); + + /** + * Get the frame capacity of the fifo. + * + * @return frame capacity + */ + uint32_t getFrameCapacity() const { return mTotalFrames; } + + virtual uint64_t getReadCounter() const = 0; + virtual void setReadCounter(uint64_t n) = 0; + virtual void incrementReadCounter(uint64_t n) = 0; + virtual uint64_t getWriteCounter() const = 0; + virtual void setWriteCounter(uint64_t n) = 0; + virtual void incrementWriteCounter(uint64_t n) = 0; + +private: + uint32_t mTotalFrames; +}; + +} // namespace oboe + +#endif //NATIVEOBOE_FIFOCONTROLLERBASE_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/FullDuplexStream.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/FullDuplexStream.h new file mode 100644 index 00000000..15d2489a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/FullDuplexStream.h @@ -0,0 +1,324 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_FULL_DUPLEX_STREAM_ +#define OBOE_FULL_DUPLEX_STREAM_ + +#include +#include "oboe/Definitions.h" +#include "oboe/AudioStream.h" +#include "oboe/AudioStreamCallback.h" + +namespace oboe { + +/** + * FullDuplexStream can be used to synchronize an input and output stream. + * + * For the builder of the output stream, call setDataCallback() with this object. + * + * When both streams are ready, onAudioReady() of the output stream will call onBothStreamsReady(). + * Callers must override onBothStreamsReady(). + * + * To ensure best results, open an output stream before the input stream. + * Call inputBuilder.setBufferCapacityInFrames(mOutputStream->getBufferCapacityInFrames() * 2). + * Also, call inputBuilder.setSampleRate(mOutputStream->getSampleRate()). + * + * Callers must call setInputStream() and setOutputStream(). + * Call start() to start both streams and stop() to stop both streams. + * Caller is responsible for closing both streams. + * + * Callers should handle error callbacks with setErrorCallback() for the output stream. + * When an error callback occurs for the output stream, Oboe will stop and close the output stream. + * The caller is responsible for stopping and closing the input stream. + * The caller should also reopen and restart both streams when the error callback is ErrorDisconnected. + * See the LiveEffect sample as an example of this. + * + */ +class FullDuplexStream : public AudioStreamDataCallback { +public: + FullDuplexStream() {} + virtual ~FullDuplexStream() = default; + + /** + * Sets the input stream. Calling this is mandatory. + * + * @param stream the output stream + */ + void setInputStream(AudioStream *stream) { + mInputStream = stream; + } + + /** + * Gets the input stream + * + * @return the input stream + */ + AudioStream *getInputStream() { + return mInputStream; + } + + /** + * Sets the output stream. Calling this is mandatory. + * + * @param stream the output stream + */ + void setOutputStream(AudioStream *stream) { + mOutputStream = stream; + } + + /** + * Gets the output stream + * + * @return the output stream + */ + AudioStream *getOutputStream() { + return mOutputStream; + } + + /** + * Attempts to start both streams. Please call setInputStream() and setOutputStream() before + * calling this function. + * + * @return result of the operation + */ + virtual Result start() { + mCountCallbacksToDrain = kNumCallbacksToDrain; + mCountInputBurstsCushion = mNumInputBurstsCushion; + mCountCallbacksToDiscard = kNumCallbacksToDiscard; + + // Determine maximum size that could possibly be called. + int32_t bufferSize = getOutputStream()->getBufferCapacityInFrames() + * getOutputStream()->getChannelCount(); + if (bufferSize > mBufferSize) { + mInputBuffer = std::make_unique(bufferSize); + mBufferSize = bufferSize; + } + + oboe::Result result = getInputStream()->requestStart(); + if (result != oboe::Result::OK) { + return result; + } + return getOutputStream()->requestStart(); + } + + /** + * Stops both streams. Returns Result::OK if neither stream had an error during close. + * + * @return result of the operation + */ + virtual Result stop() { + Result outputResult = Result::OK; + Result inputResult = Result::OK; + if (getOutputStream()) { + outputResult = mOutputStream->requestStop(); + } + if (getInputStream()) { + inputResult = mInputStream->requestStop(); + } + if (outputResult != Result::OK) { + return outputResult; + } else { + return inputResult; + } + } + + /** + * Reads input from the input stream. Callers should not call this directly as this is called + * in onAudioReady(). + * + * @param numFrames + * @return result of the operation + */ + virtual ResultWithValue readInput(int32_t numFrames) { + return getInputStream()->read(mInputBuffer.get(), numFrames, 0 /* timeout */); + } + + /** + * Called when data is available on both streams. + * Caller should override this method. + * numInputFrames and numOutputFrames may be zero. + * + * @param inputData buffer containing input data + * @param numInputFrames number of input frames + * @param outputData a place to put output data + * @param numOutputFrames number of output frames + * @return DataCallbackResult::Continue or DataCallbackResult::Stop + */ + virtual DataCallbackResult onBothStreamsReady( + const void *inputData, + int numInputFrames, + void *outputData, + int numOutputFrames + ) = 0; + + /** + * Called when the output stream is ready to process audio. + * This in return calls onBothStreamsReady() when data is available on both streams. + * Callers should call this function when the output stream is ready. + * Callers must override onBothStreamsReady(). + * + * @param audioStream pointer to the associated stream + * @param audioData a place to put output data + * @param numFrames number of frames to be processed + * @return DataCallbackResult::Continue or DataCallbackResult::Stop + * + */ + DataCallbackResult onAudioReady( + AudioStream *audioStream, + void *audioData, + int numFrames) { + DataCallbackResult callbackResult = DataCallbackResult::Continue; + int32_t actualFramesRead = 0; + + // Silence the output. + int32_t numBytes = numFrames * getOutputStream()->getBytesPerFrame(); + memset(audioData, 0 /* value */, numBytes); + + if (mCountCallbacksToDrain > 0) { + // Drain the input. + int32_t totalFramesRead = 0; + do { + ResultWithValue result = readInput(numFrames); + if (!result) { + // Ignore errors because input stream may not be started yet. + break; + } + actualFramesRead = result.value(); + totalFramesRead += actualFramesRead; + } while (actualFramesRead > 0); + // Only counts if we actually got some data. + if (totalFramesRead > 0) { + mCountCallbacksToDrain--; + } + + } else if (mCountInputBurstsCushion > 0) { + // Let the input fill up a bit so we are not so close to the write pointer. + mCountInputBurstsCushion--; + + } else if (mCountCallbacksToDiscard > 0) { + mCountCallbacksToDiscard--; + // Ignore. Allow the input to reach to equilibrium with the output. + ResultWithValue resultAvailable = getInputStream()->getAvailableFrames(); + if (!resultAvailable) { + callbackResult = DataCallbackResult::Stop; + } else { + int32_t framesAvailable = resultAvailable.value(); + if (framesAvailable >= mMinimumFramesBeforeRead) { + ResultWithValue resultRead = readInput(numFrames); + if (!resultRead) { + callbackResult = DataCallbackResult::Stop; + } + } + } + } else { + int32_t framesRead = 0; + ResultWithValue resultAvailable = getInputStream()->getAvailableFrames(); + if (!resultAvailable) { + callbackResult = DataCallbackResult::Stop; + } else { + int32_t framesAvailable = resultAvailable.value(); + if (framesAvailable >= mMinimumFramesBeforeRead) { + // Read data into input buffer. + ResultWithValue resultRead = readInput(numFrames); + if (!resultRead) { + callbackResult = DataCallbackResult::Stop; + } else { + framesRead = resultRead.value(); + } + } + } + + if (callbackResult == DataCallbackResult::Continue) { + callbackResult = onBothStreamsReady(mInputBuffer.get(), framesRead, + audioData, numFrames); + } + } + + if (callbackResult == DataCallbackResult::Stop) { + getInputStream()->requestStop(); + } + + return callbackResult; + } + + /** + * + * This is a cushion between the DSP and the application processor cursors to prevent collisions. + * Typically 0 for latency measurements or 1 for glitch tests. + * + * @param numBursts number of bursts to leave in the input buffer as a cushion + */ + void setNumInputBurstsCushion(int32_t numBursts) { + mNumInputBurstsCushion = numBursts; + } + + /** + * Get the number of bursts left in the input buffer as a cushion. + * + * @return number of bursts in the input buffer as a cushion + */ + int32_t getNumInputBurstsCushion() const { + return mNumInputBurstsCushion; + } + + /** + * Minimum number of frames in the input stream buffer before calling readInput(). + * + * @param numFrames number of bursts in the input buffer as a cushion + */ + void setMinimumFramesBeforeRead(int32_t numFrames) { + mMinimumFramesBeforeRead = numFrames; + } + + /** + * Gets the minimum number of frames in the input stream buffer before calling readInput(). + * + * @return minimum number of frames before reading + */ + int32_t getMinimumFramesBeforeRead() const { + return mMinimumFramesBeforeRead; + } + +private: + + // TODO add getters and setters + static constexpr int32_t kNumCallbacksToDrain = 20; + static constexpr int32_t kNumCallbacksToDiscard = 30; + + // let input fill back up, usually 0 or 1 + int32_t mNumInputBurstsCushion = 0; + int32_t mMinimumFramesBeforeRead = 0; + + // We want to reach a state where the input buffer is empty and + // the output buffer is full. + // These are used in order. + // Drain several callback so that input is empty. + int32_t mCountCallbacksToDrain = kNumCallbacksToDrain; + // Let the input fill back up slightly so we don't run dry. + int32_t mCountInputBurstsCushion = mNumInputBurstsCushion; + // Discard some callbacks so the input and output reach equilibrium. + int32_t mCountCallbacksToDiscard = kNumCallbacksToDiscard; + + AudioStream *mInputStream = nullptr; + AudioStream *mOutputStream = nullptr; + + int32_t mBufferSize = 0; + std::unique_ptr mInputBuffer; +}; + +} // namespace oboe + +#endif //OBOE_FULL_DUPLEX_STREAM_ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/LatencyTuner.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/LatencyTuner.h new file mode 100644 index 00000000..8b8404d9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/LatencyTuner.h @@ -0,0 +1,150 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_LATENCY_TUNER_ +#define OBOE_LATENCY_TUNER_ + +#include +#include +#include "oboe/Definitions.h" +#include "oboe/AudioStream.h" + +namespace oboe { + +/** + * LatencyTuner can be used to dynamically tune the latency of an output stream. + * It adjusts the stream's bufferSize by monitoring the number of underruns. + * + * This only affects the latency associated with the first level of buffering that is closest + * to the application. It does not affect low latency in the HAL, or touch latency in the UI. + * + * Call tune() right before returning from your data callback function if using callbacks. + * Call tune() right before calling write() if using blocking writes. + * + * If you want to see the ongoing results of this tuning process then call + * stream->getBufferSize() periodically. + * + */ +class LatencyTuner { +public: + + /** + * Construct a new LatencyTuner object which will act on the given audio stream + * + * @param stream the stream who's latency will be tuned + */ + explicit LatencyTuner(AudioStream &stream); + + /** + * Construct a new LatencyTuner object which will act on the given audio stream. + * + * @param stream the stream who's latency will be tuned + * @param the maximum buffer size which the tune() operation will set the buffer size to + */ + explicit LatencyTuner(AudioStream &stream, int32_t maximumBufferSize); + + /** + * Adjust the bufferSizeInFrames to optimize latency. + * It will start with a low latency and then raise it if an underrun occurs. + * + * Latency tuning is only supported for AAudio. + * + * @return OK or negative error, ErrorUnimplemented for OpenSL ES + */ + Result tune(); + + /** + * This may be called from another thread. Then tune() will call reset(), + * which will lower the latency to the minimum and then allow it to rise back up + * if there are glitches. + * + * This is typically called in response to a user decision to minimize latency. In other words, + * call this from a button handler. + */ + void requestReset(); + + /** + * @return true if the audio stream's buffer size is at the maximum value. If no maximum value + * was specified when constructing the LatencyTuner then the value of + * stream->getBufferCapacityInFrames is used + */ + bool isAtMaximumBufferSize(); + + /** + * Set the minimum bufferSize in frames that is used when the tuner is reset. + * You may wish to call requestReset() after calling this. + * @param bufferSize + */ + void setMinimumBufferSize(int32_t bufferSize) { + mMinimumBufferSize = bufferSize; + } + + int32_t getMinimumBufferSize() const { + return mMinimumBufferSize; + } + + /** + * Set the amount the bufferSize will be incremented while tuning. + * By default, this will be one burst. + * + * Note that AAudio will quantize the buffer size to a multiple of the burstSize. + * So the final buffer sizes may not be a multiple of this increment. + * + * @param sizeIncrement + */ + void setBufferSizeIncrement(int32_t sizeIncrement) { + mBufferSizeIncrement = sizeIncrement; + } + + int32_t getBufferSizeIncrement() const { + return mBufferSizeIncrement; + } + +private: + + /** + * Drop the latency down to the minimum and then let it rise back up. + * This is useful if a glitch caused the latency to increase and it hasn't gone back down. + * + * This should only be called in the same thread as tune(). + */ + void reset(); + + enum class State { + Idle, + Active, + AtMax, + Unsupported + } ; + + // arbitrary number of calls to wait before bumping up the latency + static constexpr int32_t kIdleCount = 8; + static constexpr int32_t kDefaultNumBursts = 2; + + AudioStream &mStream; + State mState = State::Idle; + int32_t mMaxBufferSize = 0; + int32_t mPreviousXRuns = 0; + int32_t mIdleCountDown = 0; + int32_t mMinimumBufferSize; + int32_t mBufferSizeIncrement; + std::atomic mLatencyTriggerRequests{0}; // TODO user atomic requester from AAudio + std::atomic mLatencyTriggerResponses{0}; +}; + +} // namespace oboe + +#endif // OBOE_LATENCY_TUNER_ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Oboe.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Oboe.h new file mode 100644 index 00000000..b9c948af --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Oboe.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_OBOE_H +#define OBOE_OBOE_H + +/** + * \mainpage API reference + * + * All documentation is found in the oboe namespace section + * + */ + +#include "oboe/Definitions.h" +#include "oboe/ResultWithValue.h" +#include "oboe/LatencyTuner.h" +#include "oboe/AudioStream.h" +#include "oboe/AudioStreamBase.h" +#include "oboe/AudioStreamBuilder.h" +#include "oboe/Utilities.h" +#include "oboe/Version.h" +#include "oboe/StabilizedCallback.h" +#include "oboe/FifoBuffer.h" +#include "oboe/OboeExtensions.h" +#include "oboe/FullDuplexStream.h" + +#endif //OBOE_OBOE_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/OboeExtensions.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/OboeExtensions.h new file mode 100644 index 00000000..3722e77e --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/OboeExtensions.h @@ -0,0 +1,64 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_EXTENSIONS_ +#define OBOE_EXTENSIONS_ + +#include + +#include "oboe/Definitions.h" +#include "oboe/AudioStream.h" + +namespace oboe { + +/** + * The definitions below are only for testing. + * They are not recommended for use in an application. + * They may change or be removed at any time. + */ +class OboeExtensions { +public: + + /** + * @returns true if the device supports AAudio MMAP + */ + static bool isMMapSupported(); + + /** + * @returns true if the AAudio MMAP data path can be selected + */ + static bool isMMapEnabled(); + + /** + * Controls whether the AAudio MMAP data path can be selected when opening a stream. + * It has no effect after the stream has been opened. + * It only affects the application that calls it. Other apps are not affected. + * + * @param enabled + * @return 0 or a negative error code + */ + static int32_t setMMapEnabled(bool enabled); + + /** + * @param oboeStream + * @return true if the AAudio MMAP data path is used on the stream + */ + static bool isMMapUsed(oboe::AudioStream *oboeStream); +}; + +} // namespace oboe + +#endif // OBOE_LATENCY_TUNER_ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/ResultWithValue.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/ResultWithValue.h new file mode 100644 index 00000000..fcb1fac1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/ResultWithValue.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_RESULT_WITH_VALUE_H +#define OBOE_RESULT_WITH_VALUE_H + +#include "oboe/Definitions.h" +#include +#include + +namespace oboe { + +/** + * A ResultWithValue can store both the result of an operation (either OK or an error) and a value. + * + * It has been designed for cases where the caller needs to know whether an operation succeeded and, + * if it did, a value which was obtained during the operation. + * + * For example, when reading from a stream the caller needs to know the result of the read operation + * and, if it was successful, how many frames were read. Note that ResultWithValue can be evaluated + * as a boolean so it's simple to check whether the result is OK. + * + * + * ResultWithValue resultOfRead = myStream.read(&buffer, numFrames, timeoutNanoseconds); + * + * if (resultOfRead) { + * LOGD("Frames read: %d", resultOfRead.value()); + * } else { + * LOGD("Error reading from stream: %s", resultOfRead.error()); + * } + * + */ +template +class ResultWithValue { +public: + + /** + * Construct a ResultWithValue containing an error result. + * + * @param error The error + */ + ResultWithValue(oboe::Result error) + : mValue{} + , mError(error) {} + + /** + * Construct a ResultWithValue containing an OK result and a value. + * + * @param value the value to store + */ + explicit ResultWithValue(T value) + : mValue(value) + , mError(oboe::Result::OK) {} + + /** + * Get the result. + * + * @return the result + */ + oboe::Result error() const { + return mError; + } + + /** + * Get the value + * @return + */ + T value() const { + return mValue; + } + + /** + * @return true if OK + */ + explicit operator bool() const { return mError == oboe::Result::OK; } + + /** + * Quick way to check for an error. + * + * The caller could write something like this: + * + * if (!result) { printf("Got error %s\n", convertToText(result.error())); } + * + * + * @return true if an error occurred + */ + bool operator !() const { return mError != oboe::Result::OK; } + + /** + * Implicitly convert to a Result. This enables easy comparison with Result values. Example: + * + * + * ResultWithValue result = openStream(); + * if (result == Result::ErrorNoMemory){ // tell user they're out of memory } + * + */ + operator Result() const { + return mError; + } + + /** + * Create a ResultWithValue from a number. If the number is positive the ResultWithValue will + * have a result of Result::OK and the value will contain the number. If the number is negative + * the result will be obtained from the negative number (numeric error codes can be found in + * AAudio.h) and the value will be null. + * + */ + static ResultWithValue createBasedOnSign(T numericResult){ + + // Ensure that the type is either an integer or float + static_assert(std::is_arithmetic::value, + "createBasedOnSign can only be called for numeric types (int or float)"); + + if (numericResult >= 0){ + return ResultWithValue(numericResult); + } else { + return ResultWithValue(static_cast(numericResult)); + } + } + +private: + const T mValue; + const oboe::Result mError; +}; + +/** + * If the result is `OK` then return the value, otherwise return a human-readable error message. + */ +template +std::ostream& operator<<(std::ostream &strm, const ResultWithValue &result) { + if (!result) { + strm << convertToText(result.error()); + } else { + strm << result.value(); + } + return strm; +} + +} // namespace oboe + + +#endif //OBOE_RESULT_WITH_VALUE_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/StabilizedCallback.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/StabilizedCallback.h new file mode 100644 index 00000000..244fdb70 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/StabilizedCallback.h @@ -0,0 +1,75 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_STABILIZEDCALLBACK_H +#define OBOE_STABILIZEDCALLBACK_H + +#include +#include "oboe/AudioStream.h" + +namespace oboe { + +class StabilizedCallback : public AudioStreamCallback { + +public: + explicit StabilizedCallback(AudioStreamCallback *callback); + + DataCallbackResult + onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override; + + void onErrorBeforeClose(AudioStream *oboeStream, Result error) override { + return mCallback->onErrorBeforeClose(oboeStream, error); + } + + void onErrorAfterClose(AudioStream *oboeStream, Result error) override { + + // Reset all fields now that the stream has been closed + mFrameCount = 0; + mEpochTimeNanos = 0; + mOpsPerNano = 1; + return mCallback->onErrorAfterClose(oboeStream, error); + } + +private: + + AudioStreamCallback *mCallback = nullptr; + int64_t mFrameCount = 0; + int64_t mEpochTimeNanos = 0; + double mOpsPerNano = 1; + + void generateLoad(int64_t durationNanos); +}; + +/** + * cpu_relax is an architecture specific method of telling the CPU that you don't want it to + * do much work. asm volatile keeps the compiler from optimising these instructions out. + */ +#if defined(__i386__) || defined(__x86_64__) +#define cpu_relax() asm volatile("rep; nop" ::: "memory"); + +#elif defined(__arm__) || defined(__mips__) || defined(__riscv) + #define cpu_relax() asm volatile("":::"memory") + +#elif defined(__aarch64__) +#define cpu_relax() asm volatile("yield" ::: "memory") + +#else +#error "cpu_relax is not defined for this architecture" +#endif + +} + +#endif //OBOE_STABILIZEDCALLBACK_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Utilities.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Utilities.h new file mode 100644 index 00000000..f0f41865 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Utilities.h @@ -0,0 +1,99 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_UTILITIES_H +#define OBOE_UTILITIES_H + +#include +#include +#include +#include "oboe/Definitions.h" + +namespace oboe { + +/** + * Convert an array of floats to an array of 16-bit integers. + * + * @param source the input array. + * @param destination the output array. + * @param numSamples the number of values to convert. + */ +void convertFloatToPcm16(const float *source, int16_t *destination, int32_t numSamples); + +/** + * Convert an array of 16-bit integers to an array of floats. + * + * @param source the input array. + * @param destination the output array. + * @param numSamples the number of values to convert. + */ +void convertPcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples); + +/** + * @return the size of a sample of the given format in bytes or 0 if format is invalid + */ +int32_t convertFormatToSizeInBytes(AudioFormat format); + +/** + * The text is the ASCII symbol corresponding to the supplied Oboe enum value, + * or an English message saying the value is unrecognized. + * This is intended for developers to use when debugging. + * It is not for displaying to users. + * + * @param input object to convert from. @see common/Utilities.cpp for concrete implementations + * @return text representation of an Oboe enum value. There is no need to call free on this. + */ +template +const char * convertToText(FromType input); + +/** + * @param name + * @return the value of a named system property in a string or empty string + */ +std::string getPropertyString(const char * name); + +/** + * @param name + * @param defaultValue + * @return integer value associated with a property or the default value + */ +int getPropertyInteger(const char * name, int defaultValue); + +/** + * Return the version of the SDK that is currently running. + * + * For example, on Android, this would return 27 for Oreo 8.1. + * If the version number cannot be determined then this will return -1. + * + * @return version number or -1 + */ +int getSdkVersion(); + +/** + * Returns whether a device is on a pre-release SDK that is at least the specified codename + * version. + * + * @param codename the code name to verify. + * @return boolean of whether the device is on a pre-release SDK and is at least the specified + * codename + */ +bool isAtLeastPreReleaseCodename(const std::string& codename); + +int getChannelCountFromChannelMask(ChannelMask channelMask); + +} // namespace oboe + +#endif //OBOE_UTILITIES_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Version.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Version.h new file mode 100644 index 00000000..4c77a675 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/include/oboe/Version.h @@ -0,0 +1,92 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_VERSIONINFO_H +#define OBOE_VERSIONINFO_H + +#include + +/** + * A note on use of preprocessor defines: + * + * This is one of the few times when it's suitable to use preprocessor defines rather than constexpr + * Why? Because C++11 requires a lot of boilerplate code to convert integers into compile-time + * string literals. The preprocessor, despite it's lack of type checking, is more suited to the task + * + * See: https://stackoverflow.com/questions/6713420/c-convert-integer-to-string-at-compile-time/26824971#26824971 + * + */ + +// Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description. +#define OBOE_VERSION_MAJOR 1 + +// Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description. +#define OBOE_VERSION_MINOR 8 + +// Type: 16-bit unsigned int. Min value: 0 Max value: 65535. See below for description. +#define OBOE_VERSION_PATCH 0 + +#define OBOE_STRINGIFY(x) #x +#define OBOE_TOSTRING(x) OBOE_STRINGIFY(x) + +// Type: String literal. See below for description. +#define OBOE_VERSION_TEXT \ + OBOE_TOSTRING(OBOE_VERSION_MAJOR) "." \ + OBOE_TOSTRING(OBOE_VERSION_MINOR) "." \ + OBOE_TOSTRING(OBOE_VERSION_PATCH) + +// Type: 32-bit unsigned int. See below for description. +#define OBOE_VERSION_NUMBER ((OBOE_VERSION_MAJOR << 24) | (OBOE_VERSION_MINOR << 16) | OBOE_VERSION_PATCH) + +namespace oboe { + +const char * getVersionText(); + +/** + * Oboe versioning object + */ +struct Version { + /** + * This is incremented when we make breaking API changes. Based loosely on https://semver.org/. + */ + static constexpr uint8_t Major = OBOE_VERSION_MAJOR; + + /** + * This is incremented when we add backwards compatible functionality. Or set to zero when MAJOR is + * incremented. + */ + static constexpr uint8_t Minor = OBOE_VERSION_MINOR; + + /** + * This is incremented when we make backwards compatible bug fixes. Or set to zero when MINOR is + * incremented. + */ + static constexpr uint16_t Patch = OBOE_VERSION_PATCH; + + /** + * Version string in the form MAJOR.MINOR.PATCH. + */ + static constexpr const char * Text = OBOE_VERSION_TEXT; + + /** + * Integer representation of the current Oboe library version. This will always increase when the + * version number changes so can be compared using integer comparison. + */ + static constexpr uint32_t Number = OBOE_VERSION_NUMBER; +}; + +} // namespace oboe +#endif //OBOE_VERSIONINFO_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioExtensions.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioExtensions.h new file mode 100644 index 00000000..3a9d782d --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioExtensions.h @@ -0,0 +1,180 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_AAUDIO_EXTENSIONS_H +#define OBOE_AAUDIO_EXTENSIONS_H + +#include +#include + +#include + +#include "common/OboeDebug.h" +#include "oboe/Oboe.h" +#include "AAudioLoader.h" + +namespace oboe { + +#define LIB_AAUDIO_NAME "libaaudio.so" +#define FUNCTION_IS_MMAP "AAudioStream_isMMapUsed" +#define FUNCTION_SET_MMAP_POLICY "AAudio_setMMapPolicy" +#define FUNCTION_GET_MMAP_POLICY "AAudio_getMMapPolicy" + +#define AAUDIO_ERROR_UNAVAILABLE static_cast(Result::ErrorUnavailable) + +typedef struct AAudioStreamStruct AAudioStream; + +/** + * Call some AAudio test routines that are not part of the normal API. + */ +class AAudioExtensions { +private: // Because it is a singleton. Call getInstance() instead. + AAudioExtensions() { + int32_t policy = getIntegerProperty("aaudio.mmap_policy", 0); + mMMapSupported = isPolicyEnabled(policy); + + policy = getIntegerProperty("aaudio.mmap_exclusive_policy", 0); + mMMapExclusiveSupported = isPolicyEnabled(policy); + } + +public: + static bool isPolicyEnabled(int32_t policy) { + return (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS); + } + + static AAudioExtensions &getInstance() { + static AAudioExtensions instance; + return instance; + } + + bool isMMapUsed(oboe::AudioStream *oboeStream) { + AAudioStream *aaudioStream = (AAudioStream *) oboeStream->getUnderlyingStream(); + return isMMapUsed(aaudioStream); + } + + bool isMMapUsed(AAudioStream *aaudioStream) { + if (loadSymbols()) return false; + if (mAAudioStream_isMMap == nullptr) return false; + return mAAudioStream_isMMap(aaudioStream); + } + + /** + * Controls whether the MMAP data path can be selected when opening a stream. + * It has no effect after the stream has been opened. + * It only affects the application that calls it. Other apps are not affected. + * + * @param enabled + * @return 0 or a negative error code + */ + int32_t setMMapEnabled(bool enabled) { + if (loadSymbols()) return AAUDIO_ERROR_UNAVAILABLE; + if (mAAudio_setMMapPolicy == nullptr) return false; + return mAAudio_setMMapPolicy(enabled ? AAUDIO_POLICY_AUTO : AAUDIO_POLICY_NEVER); + } + + bool isMMapEnabled() { + if (loadSymbols()) return false; + if (mAAudio_getMMapPolicy == nullptr) return false; + int32_t policy = mAAudio_getMMapPolicy(); + return isPolicyEnabled(policy); + } + + bool isMMapSupported() { + return mMMapSupported; + } + + bool isMMapExclusiveSupported() { + return mMMapExclusiveSupported; + } + +private: + + enum { + AAUDIO_POLICY_NEVER = 1, + AAUDIO_POLICY_AUTO, + AAUDIO_POLICY_ALWAYS + }; + typedef int32_t aaudio_policy_t; + + int getIntegerProperty(const char *name, int defaultValue) { + int result = defaultValue; + char valueText[PROP_VALUE_MAX] = {0}; + if (__system_property_get(name, valueText) != 0) { + result = atoi(valueText); + } + return result; + } + + /** + * Load the function pointers. + * This can be called multiple times. + * It should only be called from one thread. + * + * @return 0 if successful or negative error. + */ + aaudio_result_t loadSymbols() { + if (mAAudio_getMMapPolicy != nullptr) { + return 0; + } + + AAudioLoader *libLoader = AAudioLoader::getInstance(); + int openResult = libLoader->open(); + if (openResult != 0) { + LOGD("%s() could not open " LIB_AAUDIO_NAME, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + void *libHandle = AAudioLoader::getInstance()->getLibHandle(); + if (libHandle == nullptr) { + LOGE("%s() could not find " LIB_AAUDIO_NAME, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + mAAudioStream_isMMap = (bool (*)(AAudioStream *stream)) + dlsym(libHandle, FUNCTION_IS_MMAP); + if (mAAudioStream_isMMap == nullptr) { + LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy)) + dlsym(libHandle, FUNCTION_SET_MMAP_POLICY); + if (mAAudio_setMMapPolicy == nullptr) { + LOGI("%s() could not find " FUNCTION_SET_MMAP_POLICY, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + mAAudio_getMMapPolicy = (aaudio_policy_t (*)()) + dlsym(libHandle, FUNCTION_GET_MMAP_POLICY); + if (mAAudio_getMMapPolicy == nullptr) { + LOGI("%s() could not find " FUNCTION_GET_MMAP_POLICY, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + return 0; + } + + bool mMMapSupported = false; + bool mMMapExclusiveSupported = false; + + bool (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr; + int32_t (*mAAudio_setMMapPolicy)(aaudio_policy_t policy) = nullptr; + aaudio_policy_t (*mAAudio_getMMapPolicy)() = nullptr; +}; + +} // namespace oboe + +#endif //OBOE_AAUDIO_EXTENSIONS_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp new file mode 100644 index 00000000..213a6ef9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.cpp @@ -0,0 +1,506 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "common/OboeDebug.h" +#include "AAudioLoader.h" + +#define LIB_AAUDIO_NAME "libaaudio.so" + +namespace oboe { + +AAudioLoader::~AAudioLoader() { + // Issue 360: thread_local variables with non-trivial destructors + // will cause segfaults if the containing library is dlclose()ed on + // devices running M or newer, or devices before M when using a static STL. + // The simple workaround is to not call dlclose. + // https://github.com/android/ndk/wiki/Changelog-r22#known-issues + // + // The libaaudio and libaaudioclient do not use thread_local. + // But, to be safe, we should avoid dlclose() if possible. + // Because AAudioLoader is a static Singleton, we can safely skip + // calling dlclose() without causing a resource leak. + LOGI("%s() dlclose(%s) not called, OK", __func__, LIB_AAUDIO_NAME); +} + +AAudioLoader* AAudioLoader::getInstance() { + static AAudioLoader instance; + return &instance; +} + +int AAudioLoader::open() { + if (mLibHandle != nullptr) { + return 0; + } + + // Use RTLD_NOW to avoid the unpredictable behavior that RTLD_LAZY can cause. + // Also resolving all the links now will prevent a run-time penalty later. + mLibHandle = dlopen(LIB_AAUDIO_NAME, RTLD_NOW); + if (mLibHandle == nullptr) { + LOGI("AAudioLoader::open() could not find " LIB_AAUDIO_NAME); + return -1; // TODO review return code + } else { + LOGD("AAudioLoader(): dlopen(%s) returned %p", LIB_AAUDIO_NAME, mLibHandle); + } + + // Load all the function pointers. + createStreamBuilder = load_I_PPB("AAudio_createStreamBuilder"); + builder_openStream = load_I_PBPPS("AAudioStreamBuilder_openStream"); + + builder_setChannelCount = load_V_PBI("AAudioStreamBuilder_setChannelCount"); + if (builder_setChannelCount == nullptr) { + // Use old deprecated alias if needed. + builder_setChannelCount = load_V_PBI("AAudioStreamBuilder_setSamplesPerFrame"); + } + + builder_setBufferCapacityInFrames = load_V_PBI("AAudioStreamBuilder_setBufferCapacityInFrames"); + builder_setDeviceId = load_V_PBI("AAudioStreamBuilder_setDeviceId"); + builder_setDirection = load_V_PBI("AAudioStreamBuilder_setDirection"); + builder_setFormat = load_V_PBI("AAudioStreamBuilder_setFormat"); + builder_setFramesPerDataCallback = load_V_PBI("AAudioStreamBuilder_setFramesPerDataCallback"); + builder_setSharingMode = load_V_PBI("AAudioStreamBuilder_setSharingMode"); + builder_setPerformanceMode = load_V_PBI("AAudioStreamBuilder_setPerformanceMode"); + builder_setSampleRate = load_V_PBI("AAudioStreamBuilder_setSampleRate"); + + if (getSdkVersion() >= __ANDROID_API_P__){ + builder_setUsage = load_V_PBI("AAudioStreamBuilder_setUsage"); + builder_setContentType = load_V_PBI("AAudioStreamBuilder_setContentType"); + builder_setInputPreset = load_V_PBI("AAudioStreamBuilder_setInputPreset"); + builder_setSessionId = load_V_PBI("AAudioStreamBuilder_setSessionId"); + } + + if (getSdkVersion() >= __ANDROID_API_Q__){ + builder_setAllowedCapturePolicy = load_V_PBI("AAudioStreamBuilder_setAllowedCapturePolicy"); + } + + if (getSdkVersion() >= __ANDROID_API_R__){ + builder_setPrivacySensitive = load_V_PBO("AAudioStreamBuilder_setPrivacySensitive"); + } + + if (getSdkVersion() >= __ANDROID_API_S__){ + builder_setPackageName = load_V_PBCPH("AAudioStreamBuilder_setPackageName"); + builder_setAttributionTag = load_V_PBCPH("AAudioStreamBuilder_setAttributionTag"); + } + + if (getSdkVersion() >= __ANDROID_API_S_V2__) { + builder_setChannelMask = load_V_PBU("AAudioStreamBuilder_setChannelMask"); + builder_setIsContentSpatialized = load_V_PBO("AAudioStreamBuilder_setIsContentSpatialized"); + builder_setSpatializationBehavior = load_V_PBI("AAudioStreamBuilder_setSpatializationBehavior"); + } + + builder_delete = load_I_PB("AAudioStreamBuilder_delete"); + + + builder_setDataCallback = load_V_PBPDPV("AAudioStreamBuilder_setDataCallback"); + builder_setErrorCallback = load_V_PBPEPV("AAudioStreamBuilder_setErrorCallback"); + + stream_read = load_I_PSPVIL("AAudioStream_read"); + + stream_write = load_I_PSCPVIL("AAudioStream_write"); + + stream_waitForStateChange = load_I_PSTPTL("AAudioStream_waitForStateChange"); + + stream_getTimestamp = load_I_PSKPLPL("AAudioStream_getTimestamp"); + + stream_getChannelCount = load_I_PS("AAudioStream_getChannelCount"); + if (stream_getChannelCount == nullptr) { + // Use old alias if needed. + stream_getChannelCount = load_I_PS("AAudioStream_getSamplesPerFrame"); + } + + if (getSdkVersion() >= __ANDROID_API_R__) { + stream_release = load_I_PS("AAudioStream_release"); + } + + stream_close = load_I_PS("AAudioStream_close"); + + stream_getBufferSize = load_I_PS("AAudioStream_getBufferSizeInFrames"); + stream_getDeviceId = load_I_PS("AAudioStream_getDeviceId"); + stream_getBufferCapacity = load_I_PS("AAudioStream_getBufferCapacityInFrames"); + stream_getFormat = load_F_PS("AAudioStream_getFormat"); + stream_getFramesPerBurst = load_I_PS("AAudioStream_getFramesPerBurst"); + stream_getFramesRead = load_L_PS("AAudioStream_getFramesRead"); + stream_getFramesWritten = load_L_PS("AAudioStream_getFramesWritten"); + stream_getPerformanceMode = load_I_PS("AAudioStream_getPerformanceMode"); + stream_getSampleRate = load_I_PS("AAudioStream_getSampleRate"); + stream_getSharingMode = load_I_PS("AAudioStream_getSharingMode"); + stream_getState = load_I_PS("AAudioStream_getState"); + stream_getXRunCount = load_I_PS("AAudioStream_getXRunCount"); + + stream_requestStart = load_I_PS("AAudioStream_requestStart"); + stream_requestPause = load_I_PS("AAudioStream_requestPause"); + stream_requestFlush = load_I_PS("AAudioStream_requestFlush"); + stream_requestStop = load_I_PS("AAudioStream_requestStop"); + + stream_setBufferSize = load_I_PSI("AAudioStream_setBufferSizeInFrames"); + + convertResultToText = load_CPH_I("AAudio_convertResultToText"); + + if (getSdkVersion() >= __ANDROID_API_P__){ + stream_getUsage = load_I_PS("AAudioStream_getUsage"); + stream_getContentType = load_I_PS("AAudioStream_getContentType"); + stream_getInputPreset = load_I_PS("AAudioStream_getInputPreset"); + stream_getSessionId = load_I_PS("AAudioStream_getSessionId"); + } + + if (getSdkVersion() >= __ANDROID_API_Q__){ + stream_getAllowedCapturePolicy = load_I_PS("AAudioStream_getAllowedCapturePolicy"); + } + + if (getSdkVersion() >= __ANDROID_API_R__){ + stream_isPrivacySensitive = load_O_PS("AAudioStream_isPrivacySensitive"); + } + + if (getSdkVersion() >= __ANDROID_API_S_V2__) { + stream_getChannelMask = load_U_PS("AAudioStream_getChannelMask"); + stream_isContentSpatialized = load_O_PS("AAudioStream_isContentSpatialized"); + stream_getSpatializationBehavior = load_I_PS("AAudioStream_getSpatializationBehavior"); + } + + if (getSdkVersion() >= __ANDROID_API_U__) { + stream_getHardwareChannelCount = load_I_PS("AAudioStream_getHardwareChannelCount"); + stream_getHardwareSampleRate = load_I_PS("AAudioStream_getHardwareSampleRate"); + stream_getHardwareFormat = load_F_PS("AAudioStream_getHardwareFormat"); + } + + return 0; +} + +static void AAudioLoader_check(void *proc, const char *functionName) { + if (proc == nullptr) { + LOGW("AAudioLoader could not find %s", functionName); + } +} + +AAudioLoader::signature_I_PPB AAudioLoader::load_I_PPB(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_CPH_I AAudioLoader::load_CPH_I(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_V_PBI AAudioLoader::load_V_PBI(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_V_PBCPH AAudioLoader::load_V_PBCPH(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_V_PBPDPV AAudioLoader::load_V_PBPDPV(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_V_PBPEPV AAudioLoader::load_V_PBPEPV(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_I_PSI AAudioLoader::load_I_PSI(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_I_PS AAudioLoader::load_I_PS(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_L_PS AAudioLoader::load_L_PS(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_F_PS AAudioLoader::load_F_PS(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_O_PS AAudioLoader::load_O_PS(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_I_PB AAudioLoader::load_I_PB(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_I_PBPPS AAudioLoader::load_I_PBPPS(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_I_PSCPVIL AAudioLoader::load_I_PSCPVIL(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_I_PSPVIL AAudioLoader::load_I_PSPVIL(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_I_PSTPTL AAudioLoader::load_I_PSTPTL(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_I_PSKPLPL AAudioLoader::load_I_PSKPLPL(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_V_PBU AAudioLoader::load_V_PBU(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_U_PS AAudioLoader::load_U_PS(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +AAudioLoader::signature_V_PBO AAudioLoader::load_V_PBO(const char *functionName) { + void *proc = dlsym(mLibHandle, functionName); + AAudioLoader_check(proc, functionName); + return reinterpret_cast(proc); +} + +// Ensure that all AAudio primitive data types are int32_t +#define ASSERT_INT32(type) static_assert(std::is_same::value, \ +#type" must be int32_t") + +// Ensure that all AAudio primitive data types are uint32_t +#define ASSERT_UINT32(type) static_assert(std::is_same::value, \ +#type" must be uint32_t") + +#define ERRMSG "Oboe constants must match AAudio constants." + +// These asserts help verify that the Oboe definitions match the equivalent AAudio definitions. +// This code is in this .cpp file so it only gets tested once. +#ifdef AAUDIO_AAUDIO_H + + ASSERT_INT32(aaudio_stream_state_t); + ASSERT_INT32(aaudio_direction_t); + ASSERT_INT32(aaudio_format_t); + ASSERT_INT32(aaudio_data_callback_result_t); + ASSERT_INT32(aaudio_result_t); + ASSERT_INT32(aaudio_sharing_mode_t); + ASSERT_INT32(aaudio_performance_mode_t); + + static_assert((int32_t)StreamState::Uninitialized == AAUDIO_STREAM_STATE_UNINITIALIZED, ERRMSG); + static_assert((int32_t)StreamState::Unknown == AAUDIO_STREAM_STATE_UNKNOWN, ERRMSG); + static_assert((int32_t)StreamState::Open == AAUDIO_STREAM_STATE_OPEN, ERRMSG); + static_assert((int32_t)StreamState::Starting == AAUDIO_STREAM_STATE_STARTING, ERRMSG); + static_assert((int32_t)StreamState::Started == AAUDIO_STREAM_STATE_STARTED, ERRMSG); + static_assert((int32_t)StreamState::Pausing == AAUDIO_STREAM_STATE_PAUSING, ERRMSG); + static_assert((int32_t)StreamState::Paused == AAUDIO_STREAM_STATE_PAUSED, ERRMSG); + static_assert((int32_t)StreamState::Flushing == AAUDIO_STREAM_STATE_FLUSHING, ERRMSG); + static_assert((int32_t)StreamState::Flushed == AAUDIO_STREAM_STATE_FLUSHED, ERRMSG); + static_assert((int32_t)StreamState::Stopping == AAUDIO_STREAM_STATE_STOPPING, ERRMSG); + static_assert((int32_t)StreamState::Stopped == AAUDIO_STREAM_STATE_STOPPED, ERRMSG); + static_assert((int32_t)StreamState::Closing == AAUDIO_STREAM_STATE_CLOSING, ERRMSG); + static_assert((int32_t)StreamState::Closed == AAUDIO_STREAM_STATE_CLOSED, ERRMSG); + static_assert((int32_t)StreamState::Disconnected == AAUDIO_STREAM_STATE_DISCONNECTED, ERRMSG); + + static_assert((int32_t)Direction::Output == AAUDIO_DIRECTION_OUTPUT, ERRMSG); + static_assert((int32_t)Direction::Input == AAUDIO_DIRECTION_INPUT, ERRMSG); + + static_assert((int32_t)AudioFormat::Invalid == AAUDIO_FORMAT_INVALID, ERRMSG); + static_assert((int32_t)AudioFormat::Unspecified == AAUDIO_FORMAT_UNSPECIFIED, ERRMSG); + static_assert((int32_t)AudioFormat::I16 == AAUDIO_FORMAT_PCM_I16, ERRMSG); + static_assert((int32_t)AudioFormat::Float == AAUDIO_FORMAT_PCM_FLOAT, ERRMSG); + + static_assert((int32_t)DataCallbackResult::Continue == AAUDIO_CALLBACK_RESULT_CONTINUE, ERRMSG); + static_assert((int32_t)DataCallbackResult::Stop == AAUDIO_CALLBACK_RESULT_STOP, ERRMSG); + + static_assert((int32_t)Result::OK == AAUDIO_OK, ERRMSG); + static_assert((int32_t)Result::ErrorBase == AAUDIO_ERROR_BASE, ERRMSG); + static_assert((int32_t)Result::ErrorDisconnected == AAUDIO_ERROR_DISCONNECTED, ERRMSG); + static_assert((int32_t)Result::ErrorIllegalArgument == AAUDIO_ERROR_ILLEGAL_ARGUMENT, ERRMSG); + static_assert((int32_t)Result::ErrorInternal == AAUDIO_ERROR_INTERNAL, ERRMSG); + static_assert((int32_t)Result::ErrorInvalidState == AAUDIO_ERROR_INVALID_STATE, ERRMSG); + static_assert((int32_t)Result::ErrorInvalidHandle == AAUDIO_ERROR_INVALID_HANDLE, ERRMSG); + static_assert((int32_t)Result::ErrorUnimplemented == AAUDIO_ERROR_UNIMPLEMENTED, ERRMSG); + static_assert((int32_t)Result::ErrorUnavailable == AAUDIO_ERROR_UNAVAILABLE, ERRMSG); + static_assert((int32_t)Result::ErrorNoFreeHandles == AAUDIO_ERROR_NO_FREE_HANDLES, ERRMSG); + static_assert((int32_t)Result::ErrorNoMemory == AAUDIO_ERROR_NO_MEMORY, ERRMSG); + static_assert((int32_t)Result::ErrorNull == AAUDIO_ERROR_NULL, ERRMSG); + static_assert((int32_t)Result::ErrorTimeout == AAUDIO_ERROR_TIMEOUT, ERRMSG); + static_assert((int32_t)Result::ErrorWouldBlock == AAUDIO_ERROR_WOULD_BLOCK, ERRMSG); + static_assert((int32_t)Result::ErrorInvalidFormat == AAUDIO_ERROR_INVALID_FORMAT, ERRMSG); + static_assert((int32_t)Result::ErrorOutOfRange == AAUDIO_ERROR_OUT_OF_RANGE, ERRMSG); + static_assert((int32_t)Result::ErrorNoService == AAUDIO_ERROR_NO_SERVICE, ERRMSG); + static_assert((int32_t)Result::ErrorInvalidRate == AAUDIO_ERROR_INVALID_RATE, ERRMSG); + + static_assert((int32_t)SharingMode::Exclusive == AAUDIO_SHARING_MODE_EXCLUSIVE, ERRMSG); + static_assert((int32_t)SharingMode::Shared == AAUDIO_SHARING_MODE_SHARED, ERRMSG); + + static_assert((int32_t)PerformanceMode::None == AAUDIO_PERFORMANCE_MODE_NONE, ERRMSG); + static_assert((int32_t)PerformanceMode::PowerSaving + == AAUDIO_PERFORMANCE_MODE_POWER_SAVING, ERRMSG); + static_assert((int32_t)PerformanceMode::LowLatency + == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, ERRMSG); + +// The aaudio_ usage, content and input_preset types were added in NDK 17, +// which is the first version to support Android Pie (API 28). +#if __NDK_MAJOR__ >= 17 + + ASSERT_INT32(aaudio_usage_t); + ASSERT_INT32(aaudio_content_type_t); + ASSERT_INT32(aaudio_input_preset_t); + + static_assert((int32_t)Usage::Media == AAUDIO_USAGE_MEDIA, ERRMSG); + static_assert((int32_t)Usage::VoiceCommunication == AAUDIO_USAGE_VOICE_COMMUNICATION, ERRMSG); + static_assert((int32_t)Usage::VoiceCommunicationSignalling + == AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING, ERRMSG); + static_assert((int32_t)Usage::Alarm == AAUDIO_USAGE_ALARM, ERRMSG); + static_assert((int32_t)Usage::Notification == AAUDIO_USAGE_NOTIFICATION, ERRMSG); + static_assert((int32_t)Usage::NotificationRingtone == AAUDIO_USAGE_NOTIFICATION_RINGTONE, ERRMSG); + static_assert((int32_t)Usage::NotificationEvent == AAUDIO_USAGE_NOTIFICATION_EVENT, ERRMSG); + static_assert((int32_t)Usage::AssistanceAccessibility == AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY, ERRMSG); + static_assert((int32_t)Usage::AssistanceNavigationGuidance + == AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, ERRMSG); + static_assert((int32_t)Usage::AssistanceSonification == AAUDIO_USAGE_ASSISTANCE_SONIFICATION, ERRMSG); + static_assert((int32_t)Usage::Game == AAUDIO_USAGE_GAME, ERRMSG); + static_assert((int32_t)Usage::Assistant == AAUDIO_USAGE_ASSISTANT, ERRMSG); + + static_assert((int32_t)ContentType::Speech == AAUDIO_CONTENT_TYPE_SPEECH, ERRMSG); + static_assert((int32_t)ContentType::Music == AAUDIO_CONTENT_TYPE_MUSIC, ERRMSG); + static_assert((int32_t)ContentType::Movie == AAUDIO_CONTENT_TYPE_MOVIE, ERRMSG); + static_assert((int32_t)ContentType::Sonification == AAUDIO_CONTENT_TYPE_SONIFICATION, ERRMSG); + + static_assert((int32_t)InputPreset::Generic == AAUDIO_INPUT_PRESET_GENERIC, ERRMSG); + static_assert((int32_t)InputPreset::Camcorder == AAUDIO_INPUT_PRESET_CAMCORDER, ERRMSG); + static_assert((int32_t)InputPreset::VoiceRecognition == AAUDIO_INPUT_PRESET_VOICE_RECOGNITION, ERRMSG); + static_assert((int32_t)InputPreset::VoiceCommunication + == AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION, ERRMSG); + static_assert((int32_t)InputPreset::Unprocessed == AAUDIO_INPUT_PRESET_UNPROCESSED, ERRMSG); + + static_assert((int32_t)SessionId::None == AAUDIO_SESSION_ID_NONE, ERRMSG); + static_assert((int32_t)SessionId::Allocate == AAUDIO_SESSION_ID_ALLOCATE, ERRMSG); + +#endif // __NDK_MAJOR__ >= 17 + +// aaudio_allowed_capture_policy_t was added in NDK 20, +// which is the first version to support Android Q (API 29). +#if __NDK_MAJOR__ >= 20 + + ASSERT_INT32(aaudio_allowed_capture_policy_t); + + static_assert((int32_t)AllowedCapturePolicy::Unspecified == AAUDIO_UNSPECIFIED, ERRMSG); + static_assert((int32_t)AllowedCapturePolicy::All == AAUDIO_ALLOW_CAPTURE_BY_ALL, ERRMSG); + static_assert((int32_t)AllowedCapturePolicy::System == AAUDIO_ALLOW_CAPTURE_BY_SYSTEM, ERRMSG); + static_assert((int32_t)AllowedCapturePolicy::None == AAUDIO_ALLOW_CAPTURE_BY_NONE, ERRMSG); + +#endif // __NDK_MAJOR__ >= 20 + +// The aaudio channel masks and spatialization behavior were added in NDK 24, +// which is the first version to support Android SC_V2 (API 32). +#if __NDK_MAJOR__ >= 24 + + ASSERT_UINT32(aaudio_channel_mask_t); + + static_assert((uint32_t)ChannelMask::FrontLeft == AAUDIO_CHANNEL_FRONT_LEFT, ERRMSG); + static_assert((uint32_t)ChannelMask::FrontRight == AAUDIO_CHANNEL_FRONT_RIGHT, ERRMSG); + static_assert((uint32_t)ChannelMask::FrontCenter == AAUDIO_CHANNEL_FRONT_CENTER, ERRMSG); + static_assert((uint32_t)ChannelMask::LowFrequency == AAUDIO_CHANNEL_LOW_FREQUENCY, ERRMSG); + static_assert((uint32_t)ChannelMask::BackLeft == AAUDIO_CHANNEL_BACK_LEFT, ERRMSG); + static_assert((uint32_t)ChannelMask::BackRight == AAUDIO_CHANNEL_BACK_RIGHT, ERRMSG); + static_assert((uint32_t)ChannelMask::FrontLeftOfCenter == AAUDIO_CHANNEL_FRONT_LEFT_OF_CENTER, ERRMSG); + static_assert((uint32_t)ChannelMask::FrontRightOfCenter == AAUDIO_CHANNEL_FRONT_RIGHT_OF_CENTER, ERRMSG); + static_assert((uint32_t)ChannelMask::BackCenter == AAUDIO_CHANNEL_BACK_CENTER, ERRMSG); + static_assert((uint32_t)ChannelMask::SideLeft == AAUDIO_CHANNEL_SIDE_LEFT, ERRMSG); + static_assert((uint32_t)ChannelMask::SideRight == AAUDIO_CHANNEL_SIDE_RIGHT, ERRMSG); + static_assert((uint32_t)ChannelMask::TopCenter == AAUDIO_CHANNEL_TOP_CENTER, ERRMSG); + static_assert((uint32_t)ChannelMask::TopFrontLeft == AAUDIO_CHANNEL_TOP_FRONT_LEFT, ERRMSG); + static_assert((uint32_t)ChannelMask::TopFrontCenter == AAUDIO_CHANNEL_TOP_FRONT_CENTER, ERRMSG); + static_assert((uint32_t)ChannelMask::TopFrontRight == AAUDIO_CHANNEL_TOP_FRONT_RIGHT, ERRMSG); + static_assert((uint32_t)ChannelMask::TopBackLeft == AAUDIO_CHANNEL_TOP_BACK_LEFT, ERRMSG); + static_assert((uint32_t)ChannelMask::TopBackCenter == AAUDIO_CHANNEL_TOP_BACK_CENTER, ERRMSG); + static_assert((uint32_t)ChannelMask::TopBackRight == AAUDIO_CHANNEL_TOP_BACK_RIGHT, ERRMSG); + static_assert((uint32_t)ChannelMask::TopSideLeft == AAUDIO_CHANNEL_TOP_SIDE_LEFT, ERRMSG); + static_assert((uint32_t)ChannelMask::TopSideRight == AAUDIO_CHANNEL_TOP_SIDE_RIGHT, ERRMSG); + static_assert((uint32_t)ChannelMask::BottomFrontLeft == AAUDIO_CHANNEL_BOTTOM_FRONT_LEFT, ERRMSG); + static_assert((uint32_t)ChannelMask::BottomFrontCenter == AAUDIO_CHANNEL_BOTTOM_FRONT_CENTER, ERRMSG); + static_assert((uint32_t)ChannelMask::BottomFrontRight == AAUDIO_CHANNEL_BOTTOM_FRONT_RIGHT, ERRMSG); + static_assert((uint32_t)ChannelMask::LowFrequency2 == AAUDIO_CHANNEL_LOW_FREQUENCY_2, ERRMSG); + static_assert((uint32_t)ChannelMask::FrontWideLeft == AAUDIO_CHANNEL_FRONT_WIDE_LEFT, ERRMSG); + static_assert((uint32_t)ChannelMask::FrontWideRight == AAUDIO_CHANNEL_FRONT_WIDE_RIGHT, ERRMSG); + static_assert((uint32_t)ChannelMask::Mono == AAUDIO_CHANNEL_MONO, ERRMSG); + static_assert((uint32_t)ChannelMask::Stereo == AAUDIO_CHANNEL_STEREO, ERRMSG); + static_assert((uint32_t)ChannelMask::CM2Point1 == AAUDIO_CHANNEL_2POINT1, ERRMSG); + static_assert((uint32_t)ChannelMask::Tri == AAUDIO_CHANNEL_TRI, ERRMSG); + static_assert((uint32_t)ChannelMask::TriBack == AAUDIO_CHANNEL_TRI_BACK, ERRMSG); + static_assert((uint32_t)ChannelMask::CM3Point1 == AAUDIO_CHANNEL_3POINT1, ERRMSG); + static_assert((uint32_t)ChannelMask::CM2Point0Point2 == AAUDIO_CHANNEL_2POINT0POINT2, ERRMSG); + static_assert((uint32_t)ChannelMask::CM2Point1Point2 == AAUDIO_CHANNEL_2POINT1POINT2, ERRMSG); + static_assert((uint32_t)ChannelMask::CM3Point0Point2 == AAUDIO_CHANNEL_3POINT0POINT2, ERRMSG); + static_assert((uint32_t)ChannelMask::CM3Point1Point2 == AAUDIO_CHANNEL_3POINT1POINT2, ERRMSG); + static_assert((uint32_t)ChannelMask::Quad == AAUDIO_CHANNEL_QUAD, ERRMSG); + static_assert((uint32_t)ChannelMask::QuadSide == AAUDIO_CHANNEL_QUAD_SIDE, ERRMSG); + static_assert((uint32_t)ChannelMask::Surround == AAUDIO_CHANNEL_SURROUND, ERRMSG); + static_assert((uint32_t)ChannelMask::Penta == AAUDIO_CHANNEL_PENTA, ERRMSG); + static_assert((uint32_t)ChannelMask::CM5Point1 == AAUDIO_CHANNEL_5POINT1, ERRMSG); + static_assert((uint32_t)ChannelMask::CM5Point1Side == AAUDIO_CHANNEL_5POINT1_SIDE, ERRMSG); + static_assert((uint32_t)ChannelMask::CM6Point1 == AAUDIO_CHANNEL_6POINT1, ERRMSG); + static_assert((uint32_t)ChannelMask::CM7Point1 == AAUDIO_CHANNEL_7POINT1, ERRMSG); + static_assert((uint32_t)ChannelMask::CM5Point1Point2 == AAUDIO_CHANNEL_5POINT1POINT2, ERRMSG); + static_assert((uint32_t)ChannelMask::CM5Point1Point4 == AAUDIO_CHANNEL_5POINT1POINT4, ERRMSG); + static_assert((uint32_t)ChannelMask::CM7Point1Point2 == AAUDIO_CHANNEL_7POINT1POINT2, ERRMSG); + static_assert((uint32_t)ChannelMask::CM7Point1Point4 == AAUDIO_CHANNEL_7POINT1POINT4, ERRMSG); + static_assert((uint32_t)ChannelMask::CM9Point1Point4 == AAUDIO_CHANNEL_9POINT1POINT4, ERRMSG); + static_assert((uint32_t)ChannelMask::CM9Point1Point6 == AAUDIO_CHANNEL_9POINT1POINT6, ERRMSG); + static_assert((uint32_t)ChannelMask::FrontBack == AAUDIO_CHANNEL_FRONT_BACK, ERRMSG); + + ASSERT_INT32(aaudio_spatialization_behavior_t); + + static_assert((int32_t)SpatializationBehavior::Unspecified == AAUDIO_UNSPECIFIED, ERRMSG); + static_assert((int32_t)SpatializationBehavior::Auto == AAUDIO_SPATIALIZATION_BEHAVIOR_AUTO, ERRMSG); + static_assert((int32_t)SpatializationBehavior::Never == AAUDIO_SPATIALIZATION_BEHAVIOR_NEVER, ERRMSG); + +#endif + +#endif // AAUDIO_AAUDIO_H + +} // namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h new file mode 100644 index 00000000..2378464e --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AAudioLoader.h @@ -0,0 +1,299 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_AAUDIO_LOADER_H_ +#define OBOE_AAUDIO_LOADER_H_ + +#include +#include "oboe/Definitions.h" + +// If the NDK is before O then define this in your build +// so that AAudio.h will not be included. +#ifdef OBOE_NO_INCLUDE_AAUDIO + +// Define missing types from AAudio.h +typedef int32_t aaudio_stream_state_t; +typedef int32_t aaudio_direction_t; +typedef int32_t aaudio_format_t; +typedef int32_t aaudio_data_callback_result_t; +typedef int32_t aaudio_result_t; +typedef int32_t aaudio_sharing_mode_t; +typedef int32_t aaudio_performance_mode_t; + +typedef struct AAudioStreamStruct AAudioStream; +typedef struct AAudioStreamBuilderStruct AAudioStreamBuilder; + +typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)( + AAudioStream *stream, + void *userData, + void *audioData, + int32_t numFrames); + +typedef void (*AAudioStream_errorCallback)( + AAudioStream *stream, + void *userData, + aaudio_result_t error); + +// These were defined in P +typedef int32_t aaudio_usage_t; +typedef int32_t aaudio_content_type_t; +typedef int32_t aaudio_input_preset_t; +typedef int32_t aaudio_session_id_t; + +// There are a few definitions used by Oboe. +#define AAUDIO_OK static_cast(Result::OK) +#define AAUDIO_ERROR_TIMEOUT static_cast(Result::ErrorTimeout) +#define AAUDIO_STREAM_STATE_STARTING static_cast(StreamState::Starting) +#define AAUDIO_STREAM_STATE_STARTED static_cast(StreamState::Started) +#else +#include +#endif + +#ifndef __NDK_MAJOR__ +#define __NDK_MAJOR__ 0 +#endif + +#if __NDK_MAJOR__ < 24 +// Defined in SC_V2 +typedef uint32_t aaudio_channel_mask_t; +typedef int32_t aaudio_spatialization_behavior_t; +#endif + +#ifndef __ANDROID_API_Q__ +#define __ANDROID_API_Q__ 29 +#endif + +#ifndef __ANDROID_API_R__ +#define __ANDROID_API_R__ 30 +#endif + +#ifndef __ANDROID_API_S__ +#define __ANDROID_API_S__ 31 +#endif + +#ifndef __ANDROID_API_S_V2__ +#define __ANDROID_API_S_V2__ 32 +#endif + +#ifndef __ANDROID_API_U__ +#define __ANDROID_API_U__ 34 +#endif + +namespace oboe { + +/** + * The AAudio API was not available in early versions of Android. + * To avoid linker errors, we dynamically link with the functions by name using dlsym(). + * On older versions this linkage will safely fail. + */ +class AAudioLoader { + public: + // Use signatures for common functions. + // Key to letter abbreviations. + // S = Stream + // B = Builder + // I = int32_t + // L = int64_t + // T = sTate + // K = clocKid_t + // P = Pointer to following data type + // C = Const prefix + // H = cHar + // U = uint32_t + // O = bOol + + typedef int32_t (*signature_I_PPB)(AAudioStreamBuilder **builder); + + typedef const char * (*signature_CPH_I)(int32_t); + + typedef int32_t (*signature_I_PBPPS)(AAudioStreamBuilder *, + AAudioStream **stream); // AAudioStreamBuilder_open() + + typedef int32_t (*signature_I_PB)(AAudioStreamBuilder *); // AAudioStreamBuilder_delete() + // AAudioStreamBuilder_setSampleRate() + typedef void (*signature_V_PBI)(AAudioStreamBuilder *, int32_t); + + // AAudioStreamBuilder_setChannelMask() + typedef void (*signature_V_PBU)(AAudioStreamBuilder *, uint32_t); + + typedef void (*signature_V_PBCPH)(AAudioStreamBuilder *, const char *); + + // AAudioStreamBuilder_setPrivacySensitive + typedef void (*signature_V_PBO)(AAudioStreamBuilder *, bool); + + typedef int32_t (*signature_I_PS)(AAudioStream *); // AAudioStream_getSampleRate() + typedef int64_t (*signature_L_PS)(AAudioStream *); // AAudioStream_getFramesRead() + // AAudioStream_setBufferSizeInFrames() + typedef int32_t (*signature_I_PSI)(AAudioStream *, int32_t); + + typedef void (*signature_V_PBPDPV)(AAudioStreamBuilder *, + AAudioStream_dataCallback, + void *); + + typedef void (*signature_V_PBPEPV)(AAudioStreamBuilder *, + AAudioStream_errorCallback, + void *); + + typedef aaudio_format_t (*signature_F_PS)(AAudioStream *stream); + + typedef int32_t (*signature_I_PSPVIL)(AAudioStream *, void *, int32_t, int64_t); + typedef int32_t (*signature_I_PSCPVIL)(AAudioStream *, const void *, int32_t, int64_t); + + typedef int32_t (*signature_I_PSTPTL)(AAudioStream *, + aaudio_stream_state_t, + aaudio_stream_state_t *, + int64_t); + + typedef int32_t (*signature_I_PSKPLPL)(AAudioStream *, clockid_t, int64_t *, int64_t *); + + typedef bool (*signature_O_PS)(AAudioStream *); + + typedef uint32_t (*signature_U_PS)(AAudioStream *); + + static AAudioLoader* getInstance(); // singleton + + /** + * Open the AAudio shared library and load the function pointers. + * This can be called multiple times. + * It should only be called from one thread. + * + * The destructor will clean up after the open. + * + * @return 0 if successful or negative error. + */ + int open(); + + void *getLibHandle() const { return mLibHandle; } + + // Function pointers into the AAudio shared library. + signature_I_PPB createStreamBuilder = nullptr; + + signature_I_PBPPS builder_openStream = nullptr; + + signature_V_PBI builder_setBufferCapacityInFrames = nullptr; + signature_V_PBI builder_setChannelCount = nullptr; + signature_V_PBI builder_setDeviceId = nullptr; + signature_V_PBI builder_setDirection = nullptr; + signature_V_PBI builder_setFormat = nullptr; + signature_V_PBI builder_setFramesPerDataCallback = nullptr; + signature_V_PBI builder_setPerformanceMode = nullptr; + signature_V_PBI builder_setSampleRate = nullptr; + signature_V_PBI builder_setSharingMode = nullptr; + signature_V_PBU builder_setChannelMask = nullptr; + + signature_V_PBI builder_setUsage = nullptr; + signature_V_PBI builder_setContentType = nullptr; + signature_V_PBI builder_setInputPreset = nullptr; + signature_V_PBI builder_setSessionId = nullptr; + + signature_V_PBO builder_setPrivacySensitive = nullptr; + signature_V_PBI builder_setAllowedCapturePolicy = nullptr; + + signature_V_PBCPH builder_setPackageName = nullptr; + signature_V_PBCPH builder_setAttributionTag = nullptr; + + signature_V_PBO builder_setIsContentSpatialized = nullptr; + signature_V_PBI builder_setSpatializationBehavior = nullptr; + + signature_V_PBPDPV builder_setDataCallback = nullptr; + signature_V_PBPEPV builder_setErrorCallback = nullptr; + + signature_I_PB builder_delete = nullptr; + + signature_F_PS stream_getFormat = nullptr; + + signature_I_PSPVIL stream_read = nullptr; + signature_I_PSCPVIL stream_write = nullptr; + + signature_I_PSTPTL stream_waitForStateChange = nullptr; + + signature_I_PSKPLPL stream_getTimestamp = nullptr; + + signature_I_PS stream_release = nullptr; + signature_I_PS stream_close = nullptr; + + signature_I_PS stream_getChannelCount = nullptr; + signature_I_PS stream_getDeviceId = nullptr; + + signature_I_PS stream_getBufferSize = nullptr; + signature_I_PS stream_getBufferCapacity = nullptr; + signature_I_PS stream_getFramesPerBurst = nullptr; + signature_I_PS stream_getState = nullptr; + signature_I_PS stream_getPerformanceMode = nullptr; + signature_I_PS stream_getSampleRate = nullptr; + signature_I_PS stream_getSharingMode = nullptr; + signature_I_PS stream_getXRunCount = nullptr; + + signature_I_PSI stream_setBufferSize = nullptr; + signature_I_PS stream_requestStart = nullptr; + signature_I_PS stream_requestPause = nullptr; + signature_I_PS stream_requestFlush = nullptr; + signature_I_PS stream_requestStop = nullptr; + + signature_L_PS stream_getFramesRead = nullptr; + signature_L_PS stream_getFramesWritten = nullptr; + + signature_CPH_I convertResultToText = nullptr; + + signature_I_PS stream_getUsage = nullptr; + signature_I_PS stream_getContentType = nullptr; + signature_I_PS stream_getInputPreset = nullptr; + signature_I_PS stream_getSessionId = nullptr; + + signature_O_PS stream_isPrivacySensitive = nullptr; + signature_I_PS stream_getAllowedCapturePolicy = nullptr; + + signature_U_PS stream_getChannelMask = nullptr; + + signature_O_PS stream_isContentSpatialized = nullptr; + signature_I_PS stream_getSpatializationBehavior = nullptr; + + signature_I_PS stream_getHardwareChannelCount = nullptr; + signature_I_PS stream_getHardwareSampleRate = nullptr; + signature_F_PS stream_getHardwareFormat = nullptr; + + private: + AAudioLoader() {} + ~AAudioLoader(); + + // Load function pointers for specific signatures. + signature_I_PPB load_I_PPB(const char *name); + signature_CPH_I load_CPH_I(const char *name); + signature_V_PBI load_V_PBI(const char *name); + signature_V_PBCPH load_V_PBCPH(const char *name); + signature_V_PBPDPV load_V_PBPDPV(const char *name); + signature_V_PBPEPV load_V_PBPEPV(const char *name); + signature_I_PB load_I_PB(const char *name); + signature_I_PBPPS load_I_PBPPS(const char *name); + signature_I_PS load_I_PS(const char *name); + signature_L_PS load_L_PS(const char *name); + signature_F_PS load_F_PS(const char *name); + signature_O_PS load_O_PS(const char *name); + signature_I_PSI load_I_PSI(const char *name); + signature_I_PSPVIL load_I_PSPVIL(const char *name); + signature_I_PSCPVIL load_I_PSCPVIL(const char *name); + signature_I_PSTPTL load_I_PSTPTL(const char *name); + signature_I_PSKPLPL load_I_PSKPLPL(const char *name); + signature_V_PBU load_V_PBU(const char *name); + signature_U_PS load_U_PS(const char *name); + signature_V_PBO load_V_PBO(const char *name); + + void *mLibHandle = nullptr; +}; + +} // namespace oboe + +#endif //OBOE_AAUDIO_LOADER_H_ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp new file mode 100644 index 00000000..db7e658b --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.cpp @@ -0,0 +1,868 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "aaudio/AAudioLoader.h" +#include "aaudio/AudioStreamAAudio.h" +#include "common/AudioClock.h" +#include "common/OboeDebug.h" +#include "oboe/Utilities.h" +#include "AAudioExtensions.h" + +#ifdef __ANDROID__ +#include +#include + +#endif + +#ifndef OBOE_FIX_FORCE_STARTING_TO_STARTED +// Workaround state problems in AAudio +// TODO Which versions does this occur in? Verify fixed in Q. +#define OBOE_FIX_FORCE_STARTING_TO_STARTED 1 +#endif // OBOE_FIX_FORCE_STARTING_TO_STARTED + +using namespace oboe; +AAudioLoader *AudioStreamAAudio::mLibLoader = nullptr; + +// 'C' wrapper for the data callback method +static aaudio_data_callback_result_t oboe_aaudio_data_callback_proc( + AAudioStream *stream, + void *userData, + void *audioData, + int32_t numFrames) { + + AudioStreamAAudio *oboeStream = reinterpret_cast(userData); + if (oboeStream != nullptr) { + return static_cast( + oboeStream->callOnAudioReady(stream, audioData, numFrames)); + + } else { + return static_cast(DataCallbackResult::Stop); + } +} + +// This runs in its own thread. +// Only one of these threads will be launched from internalErrorCallback(). +// It calls app error callbacks from a static function in case the stream gets deleted. +static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream, + Result error) { + LOGD("%s(,%d) - entering >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", __func__, error); + AudioStreamErrorCallback *errorCallback = oboeStream->getErrorCallback(); + if (errorCallback == nullptr) return; // should be impossible + bool isErrorHandled = errorCallback->onError(oboeStream, error); + + if (!isErrorHandled) { + oboeStream->requestStop(); + errorCallback->onErrorBeforeClose(oboeStream, error); + oboeStream->close(); + // Warning, oboeStream may get deleted by this callback. + errorCallback->onErrorAfterClose(oboeStream, error); + } + LOGD("%s() - exiting <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", __func__); +} + +// This runs in its own thread. +// Only one of these threads will be launched from internalErrorCallback(). +// Prevents deletion of the stream if the app is using AudioStreamBuilder::openSharedStream() +static void oboe_aaudio_error_thread_proc_shared(std::shared_ptr sharedStream, + Result error) { + AudioStreamAAudio *oboeStream = reinterpret_cast(sharedStream.get()); + oboe_aaudio_error_thread_proc(oboeStream, error); +} + +namespace oboe { + +/* + * Create a stream that uses Oboe Audio API. + */ +AudioStreamAAudio::AudioStreamAAudio(const AudioStreamBuilder &builder) + : AudioStream(builder) + , mAAudioStream(nullptr) { + mCallbackThreadEnabled.store(false); + mLibLoader = AAudioLoader::getInstance(); +} + +bool AudioStreamAAudio::isSupported() { + mLibLoader = AAudioLoader::getInstance(); + int openResult = mLibLoader->open(); + return openResult == 0; +} + +// Static method for the error callback. +// We use a method so we can access protected methods on the stream. +// Launch a thread to handle the error. +// That other thread can safely stop, close and delete the stream. +void AudioStreamAAudio::internalErrorCallback( + AAudioStream *stream, + void *userData, + aaudio_result_t error) { + oboe::Result oboeResult = static_cast(error); + AudioStreamAAudio *oboeStream = reinterpret_cast(userData); + + // Coerce the error code if needed to workaround a regression in RQ1A that caused + // the wrong code to be passed when headsets plugged in. See b/173928197. + if (OboeGlobals::areWorkaroundsEnabled() + && getSdkVersion() == __ANDROID_API_R__ + && oboeResult == oboe::Result::ErrorTimeout) { + oboeResult = oboe::Result::ErrorDisconnected; + LOGD("%s() ErrorTimeout changed to ErrorDisconnected to fix b/173928197", __func__); + } + + oboeStream->mErrorCallbackResult = oboeResult; + + // Prevents deletion of the stream if the app is using AudioStreamBuilder::openStream(shared_ptr) + std::shared_ptr sharedStream = oboeStream->lockWeakThis(); + + // These checks should be enough because we assume that the stream close() + // will join() any active callback threads and will not allow new callbacks. + if (oboeStream->wasErrorCallbackCalled()) { // block extra error callbacks + LOGE("%s() multiple error callbacks called!", __func__); + } else if (stream != oboeStream->getUnderlyingStream()) { + LOGW("%s() stream already closed or closing", __func__); // might happen if there are bugs + } else if (sharedStream) { + // Handle error on a separate thread using shared pointer. + std::thread t(oboe_aaudio_error_thread_proc_shared, sharedStream, oboeResult); + t.detach(); + } else { + // Handle error on a separate thread. + std::thread t(oboe_aaudio_error_thread_proc, oboeStream, oboeResult); + t.detach(); + } +} + +void AudioStreamAAudio::beginPerformanceHintInCallback() { + if (isPerformanceHintEnabled()) { + if (!mAdpfOpenAttempted) { + int64_t targetDurationNanos = (mFramesPerBurst * 1e9) / getSampleRate(); + // This has to be called from the callback thread so we get the right TID. + int adpfResult = mAdpfWrapper.open(gettid(), targetDurationNanos); + if (adpfResult < 0) { + LOGW("WARNING ADPF not supported, %d\n", adpfResult); + } else { + LOGD("ADPF is now active\n"); + } + mAdpfOpenAttempted = true; + } + mAdpfWrapper.onBeginCallback(); + } else if (!isPerformanceHintEnabled() && mAdpfOpenAttempted) { + LOGD("ADPF closed\n"); + mAdpfWrapper.close(); + mAdpfOpenAttempted = false; + } +} + +void AudioStreamAAudio::endPerformanceHintInCallback(int32_t numFrames) { + if (mAdpfWrapper.isOpen()) { + // Scale the measured duration based on numFrames so it is normalized to a full burst. + double durationScaler = static_cast(mFramesPerBurst) / numFrames; + // Skip this callback if numFrames is very small. + // This can happen when buffers wrap around, particularly when doing sample rate conversion. + if (durationScaler < 2.0) { + mAdpfWrapper.onEndCallback(durationScaler); + } + } +} + +void AudioStreamAAudio::logUnsupportedAttributes() { + int sdkVersion = getSdkVersion(); + + // These attributes are not supported pre Android "P" + if (sdkVersion < __ANDROID_API_P__) { + if (mUsage != Usage::Media) { + LOGW("Usage [AudioStreamBuilder::setUsage()] " + "is not supported on AAudio streams running on pre-Android P versions."); + } + + if (mContentType != ContentType::Music) { + LOGW("ContentType [AudioStreamBuilder::setContentType()] " + "is not supported on AAudio streams running on pre-Android P versions."); + } + + if (mSessionId != SessionId::None) { + LOGW("SessionId [AudioStreamBuilder::setSessionId()] " + "is not supported on AAudio streams running on pre-Android P versions."); + } + } +} + +Result AudioStreamAAudio::open() { + Result result = Result::OK; + + if (mAAudioStream != nullptr) { + return Result::ErrorInvalidState; + } + + result = AudioStream::open(); + if (result != Result::OK) { + return result; + } + + AAudioStreamBuilder *aaudioBuilder; + result = static_cast(mLibLoader->createStreamBuilder(&aaudioBuilder)); + if (result != Result::OK) { + return result; + } + + // Do not set INPUT capacity below 4096 because that prevents us from getting a FAST track + // when using the Legacy data path. + // If the app requests > 4096 then we allow it but we are less likely to get LowLatency. + // See internal bug b/80308183 for more details. + // Fixed in Q but let's still clip the capacity because high input capacity + // does not increase latency. + int32_t capacity = mBufferCapacityInFrames; + constexpr int kCapacityRequiredForFastLegacyTrack = 4096; // matches value in AudioFinger + if (OboeGlobals::areWorkaroundsEnabled() + && mDirection == oboe::Direction::Input + && capacity != oboe::Unspecified + && capacity < kCapacityRequiredForFastLegacyTrack + && mPerformanceMode == oboe::PerformanceMode::LowLatency) { + capacity = kCapacityRequiredForFastLegacyTrack; + LOGD("AudioStreamAAudio.open() capacity changed from %d to %d for lower latency", + static_cast(mBufferCapacityInFrames), capacity); + } + mLibLoader->builder_setBufferCapacityInFrames(aaudioBuilder, capacity); + + if (mLibLoader->builder_setSessionId != nullptr) { + mLibLoader->builder_setSessionId(aaudioBuilder, + static_cast(mSessionId)); + // Output effects do not support PerformanceMode::LowLatency. + if (OboeGlobals::areWorkaroundsEnabled() + && mSessionId != SessionId::None + && mDirection == oboe::Direction::Output + && mPerformanceMode == PerformanceMode::LowLatency) { + mPerformanceMode = PerformanceMode::None; + LOGD("AudioStreamAAudio.open() performance mode changed to None when session " + "id is requested"); + } + } + + // Channel mask was added in SC_V2. Given the corresponding channel count of selected channel + // mask may be different from selected channel count, the last set value will be respected. + // If channel count is set after channel mask, the previously set channel mask will be cleared. + // If channel mask is set after channel count, the channel count will be automatically + // calculated from selected channel mask. In that case, only set channel mask when the API + // is available and the channel mask is specified. + if (mLibLoader->builder_setChannelMask != nullptr && mChannelMask != ChannelMask::Unspecified) { + mLibLoader->builder_setChannelMask(aaudioBuilder, + static_cast(mChannelMask)); + } else { + mLibLoader->builder_setChannelCount(aaudioBuilder, mChannelCount); + } + mLibLoader->builder_setDeviceId(aaudioBuilder, mDeviceId); + mLibLoader->builder_setDirection(aaudioBuilder, static_cast(mDirection)); + mLibLoader->builder_setFormat(aaudioBuilder, static_cast(mFormat)); + mLibLoader->builder_setSampleRate(aaudioBuilder, mSampleRate); + mLibLoader->builder_setSharingMode(aaudioBuilder, + static_cast(mSharingMode)); + mLibLoader->builder_setPerformanceMode(aaudioBuilder, + static_cast(mPerformanceMode)); + + // These were added in P so we have to check for the function pointer. + if (mLibLoader->builder_setUsage != nullptr) { + mLibLoader->builder_setUsage(aaudioBuilder, + static_cast(mUsage)); + } + + if (mLibLoader->builder_setContentType != nullptr) { + mLibLoader->builder_setContentType(aaudioBuilder, + static_cast(mContentType)); + } + + if (mLibLoader->builder_setInputPreset != nullptr) { + aaudio_input_preset_t inputPreset = mInputPreset; + if (getSdkVersion() <= __ANDROID_API_P__ && inputPreset == InputPreset::VoicePerformance) { + LOGD("InputPreset::VoicePerformance not supported before Q. Using VoiceRecognition."); + inputPreset = InputPreset::VoiceRecognition; // most similar preset + } + mLibLoader->builder_setInputPreset(aaudioBuilder, + static_cast(inputPreset)); + } + + // These were added in S so we have to check for the function pointer. + if (mLibLoader->builder_setPackageName != nullptr && !mPackageName.empty()) { + mLibLoader->builder_setPackageName(aaudioBuilder, + mPackageName.c_str()); + } + + if (mLibLoader->builder_setAttributionTag != nullptr && !mAttributionTag.empty()) { + mLibLoader->builder_setAttributionTag(aaudioBuilder, + mAttributionTag.c_str()); + } + + // This was added in Q so we have to check for the function pointer. + if (mLibLoader->builder_setAllowedCapturePolicy != nullptr && mDirection == oboe::Direction::Output) { + mLibLoader->builder_setAllowedCapturePolicy(aaudioBuilder, + static_cast(mAllowedCapturePolicy)); + } + + if (mLibLoader->builder_setPrivacySensitive != nullptr && mDirection == oboe::Direction::Input + && mPrivacySensitiveMode != PrivacySensitiveMode::Unspecified) { + mLibLoader->builder_setPrivacySensitive(aaudioBuilder, + mPrivacySensitiveMode == PrivacySensitiveMode::Enabled); + } + + if (mLibLoader->builder_setIsContentSpatialized != nullptr) { + mLibLoader->builder_setIsContentSpatialized(aaudioBuilder, mIsContentSpatialized); + } + + if (mLibLoader->builder_setSpatializationBehavior != nullptr) { + // Override Unspecified as Never to reduce latency. + if (mSpatializationBehavior == SpatializationBehavior::Unspecified) { + mSpatializationBehavior = SpatializationBehavior::Never; + } + mLibLoader->builder_setSpatializationBehavior(aaudioBuilder, + static_cast(mSpatializationBehavior)); + } else { + mSpatializationBehavior = SpatializationBehavior::Never; + } + + if (isDataCallbackSpecified()) { + mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this); + mLibLoader->builder_setFramesPerDataCallback(aaudioBuilder, getFramesPerDataCallback()); + + if (!isErrorCallbackSpecified()) { + // The app did not specify a callback so we should specify + // our own so the stream gets closed and stopped. + mErrorCallback = &mDefaultErrorCallback; + } + mLibLoader->builder_setErrorCallback(aaudioBuilder, internalErrorCallback, this); + } + // Else if the data callback is not being used then the write method will return an error + // and the app can stop and close the stream. + + // ============= OPEN THE STREAM ================ + { + AAudioStream *stream = nullptr; + result = static_cast(mLibLoader->builder_openStream(aaudioBuilder, &stream)); + mAAudioStream.store(stream); + } + if (result != Result::OK) { + // Warn developer because ErrorInternal is not very informative. + if (result == Result::ErrorInternal && mDirection == Direction::Input) { + LOGW("AudioStreamAAudio.open() may have failed due to lack of " + "audio recording permission."); + } + goto error2; + } + + // Query and cache the stream properties + mDeviceId = mLibLoader->stream_getDeviceId(mAAudioStream); + mChannelCount = mLibLoader->stream_getChannelCount(mAAudioStream); + mSampleRate = mLibLoader->stream_getSampleRate(mAAudioStream); + mFormat = static_cast(mLibLoader->stream_getFormat(mAAudioStream)); + mSharingMode = static_cast(mLibLoader->stream_getSharingMode(mAAudioStream)); + mPerformanceMode = static_cast( + mLibLoader->stream_getPerformanceMode(mAAudioStream)); + mBufferCapacityInFrames = mLibLoader->stream_getBufferCapacity(mAAudioStream); + mBufferSizeInFrames = mLibLoader->stream_getBufferSize(mAAudioStream); + mFramesPerBurst = mLibLoader->stream_getFramesPerBurst(mAAudioStream); + + // These were added in P so we have to check for the function pointer. + if (mLibLoader->stream_getUsage != nullptr) { + mUsage = static_cast(mLibLoader->stream_getUsage(mAAudioStream)); + } + if (mLibLoader->stream_getContentType != nullptr) { + mContentType = static_cast(mLibLoader->stream_getContentType(mAAudioStream)); + } + if (mLibLoader->stream_getInputPreset != nullptr) { + mInputPreset = static_cast(mLibLoader->stream_getInputPreset(mAAudioStream)); + } + if (mLibLoader->stream_getSessionId != nullptr) { + mSessionId = static_cast(mLibLoader->stream_getSessionId(mAAudioStream)); + } else { + mSessionId = SessionId::None; + } + + // This was added in Q so we have to check for the function pointer. + if (mLibLoader->stream_getAllowedCapturePolicy != nullptr && mDirection == oboe::Direction::Output) { + mAllowedCapturePolicy = static_cast(mLibLoader->stream_getAllowedCapturePolicy(mAAudioStream)); + } else { + mAllowedCapturePolicy = AllowedCapturePolicy::Unspecified; + } + + if (mLibLoader->stream_isPrivacySensitive != nullptr && mDirection == oboe::Direction::Input) { + bool isPrivacySensitive = mLibLoader->stream_isPrivacySensitive(mAAudioStream); + mPrivacySensitiveMode = isPrivacySensitive ? PrivacySensitiveMode::Enabled : + PrivacySensitiveMode::Disabled; + } else { + mPrivacySensitiveMode = PrivacySensitiveMode::Unspecified; + } + + if (mLibLoader->stream_getChannelMask != nullptr) { + mChannelMask = static_cast(mLibLoader->stream_getChannelMask(mAAudioStream)); + } + + if (mLibLoader->stream_isContentSpatialized != nullptr) { + mIsContentSpatialized = mLibLoader->stream_isContentSpatialized(mAAudioStream); + } + + if (mLibLoader->stream_getSpatializationBehavior != nullptr) { + mSpatializationBehavior = static_cast( + mLibLoader->stream_getSpatializationBehavior(mAAudioStream)); + } + + if (mLibLoader->stream_getHardwareChannelCount != nullptr) { + mHardwareChannelCount = mLibLoader->stream_getHardwareChannelCount(mAAudioStream); + } + if (mLibLoader->stream_getHardwareSampleRate != nullptr) { + mHardwareSampleRate = mLibLoader->stream_getHardwareSampleRate(mAAudioStream); + } + if (mLibLoader->stream_getHardwareFormat != nullptr) { + mHardwareFormat = static_cast(mLibLoader->stream_getHardwareFormat(mAAudioStream)); + } + + LOGD("AudioStreamAAudio.open() format=%d, sampleRate=%d, capacity = %d", + static_cast(mFormat), static_cast(mSampleRate), + static_cast(mBufferCapacityInFrames)); + + calculateDefaultDelayBeforeCloseMillis(); + +error2: + mLibLoader->builder_delete(aaudioBuilder); + if (static_cast(result) > 0) { + // Possibly due to b/267531411 + LOGW("AudioStreamAAudio.open: AAudioStream_Open() returned positive error = %d", + static_cast(result)); + if (OboeGlobals::areWorkaroundsEnabled()) { + result = Result::ErrorInternal; // Coerce to negative error. + } + } else { + LOGD("AudioStreamAAudio.open: AAudioStream_Open() returned %s = %d", + mLibLoader->convertResultToText(static_cast(result)), + static_cast(result)); + } + return result; +} + +Result AudioStreamAAudio::release() { + if (getSdkVersion() < __ANDROID_API_R__) { + return Result::ErrorUnimplemented; + } + + // AAudioStream_release() is buggy on Android R. + if (OboeGlobals::areWorkaroundsEnabled() && getSdkVersion() == __ANDROID_API_R__) { + LOGW("Skipping release() on Android R"); + return Result::ErrorUnimplemented; + } + + std::lock_guard lock(mLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + if (OboeGlobals::areWorkaroundsEnabled()) { + // Make sure we are really stopped. Do it under mLock + // so another thread cannot call requestStart() right before the close. + requestStop_l(stream); + } + return static_cast(mLibLoader->stream_release(stream)); + } else { + return Result::ErrorClosed; + } +} + +Result AudioStreamAAudio::close() { + // Prevent two threads from closing the stream at the same time and crashing. + // This could occur, for example, if an application called close() at the same + // time that an onError callback was being executed because of a disconnect. + std::lock_guard lock(mLock); + + AudioStream::close(); + + AAudioStream *stream = nullptr; + { + // Wait for any methods using mAAudioStream to finish. + std::unique_lock lock2(mAAudioStreamLock); + // Closing will delete *mAAudioStream so we need to null out the pointer atomically. + stream = mAAudioStream.exchange(nullptr); + } + if (stream != nullptr) { + if (OboeGlobals::areWorkaroundsEnabled()) { + // Make sure we are really stopped. Do it under mLock + // so another thread cannot call requestStart() right before the close. + requestStop_l(stream); + sleepBeforeClose(); + } + return static_cast(mLibLoader->stream_close(stream)); + } else { + return Result::ErrorClosed; + } +} + +static void oboe_stop_thread_proc(AudioStream *oboeStream) { + if (oboeStream != nullptr) { + oboeStream->requestStop(); + } +} + +void AudioStreamAAudio::launchStopThread() { + // Prevent multiple stop threads from being launched. + if (mStopThreadAllowed.exchange(false)) { + // Stop this stream on a separate thread + std::thread t(oboe_stop_thread_proc, this); + t.detach(); + } +} + +DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream * /*stream*/, + void *audioData, + int32_t numFrames) { + DataCallbackResult result = fireDataCallback(audioData, numFrames); + if (result == DataCallbackResult::Continue) { + return result; + } else { + if (result == DataCallbackResult::Stop) { + LOGD("Oboe callback returned DataCallbackResult::Stop"); + } else { + LOGE("Oboe callback returned unexpected value = %d", result); + } + + // Returning Stop caused various problems before S. See #1230 + if (OboeGlobals::areWorkaroundsEnabled() && getSdkVersion() <= __ANDROID_API_R__) { + launchStopThread(); + return DataCallbackResult::Continue; + } else { + return DataCallbackResult::Stop; // OK >= API_S + } + } +} + +Result AudioStreamAAudio::requestStart() { + std::lock_guard lock(mLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + // Avoid state machine errors in O_MR1. + if (getSdkVersion() <= __ANDROID_API_O_MR1__) { + StreamState state = static_cast(mLibLoader->stream_getState(stream)); + if (state == StreamState::Starting || state == StreamState::Started) { + // WARNING: On P, AAudio is returning ErrorInvalidState for Output and OK for Input. + return Result::OK; + } + } + if (isDataCallbackSpecified()) { + setDataCallbackEnabled(true); + } + mStopThreadAllowed = true; + closePerformanceHint(); + return static_cast(mLibLoader->stream_requestStart(stream)); + } else { + return Result::ErrorClosed; + } +} + +Result AudioStreamAAudio::requestPause() { + std::lock_guard lock(mLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + // Avoid state machine errors in O_MR1. + if (getSdkVersion() <= __ANDROID_API_O_MR1__) { + StreamState state = static_cast(mLibLoader->stream_getState(stream)); + if (state == StreamState::Pausing || state == StreamState::Paused) { + return Result::OK; + } + } + return static_cast(mLibLoader->stream_requestPause(stream)); + } else { + return Result::ErrorClosed; + } +} + +Result AudioStreamAAudio::requestFlush() { + std::lock_guard lock(mLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + // Avoid state machine errors in O_MR1. + if (getSdkVersion() <= __ANDROID_API_O_MR1__) { + StreamState state = static_cast(mLibLoader->stream_getState(stream)); + if (state == StreamState::Flushing || state == StreamState::Flushed) { + return Result::OK; + } + } + return static_cast(mLibLoader->stream_requestFlush(stream)); + } else { + return Result::ErrorClosed; + } +} + +Result AudioStreamAAudio::requestStop() { + std::lock_guard lock(mLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + return requestStop_l(stream); + } else { + return Result::ErrorClosed; + } +} + +// Call under mLock +Result AudioStreamAAudio::requestStop_l(AAudioStream *stream) { + // Avoid state machine errors in O_MR1. + if (getSdkVersion() <= __ANDROID_API_O_MR1__) { + StreamState state = static_cast(mLibLoader->stream_getState(stream)); + if (state == StreamState::Stopping || state == StreamState::Stopped) { + return Result::OK; + } + } + return static_cast(mLibLoader->stream_requestStop(stream)); +} + +ResultWithValue AudioStreamAAudio::write(const void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) { + std::shared_lock lock(mAAudioStreamLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + int32_t result = mLibLoader->stream_write(mAAudioStream, buffer, + numFrames, timeoutNanoseconds); + return ResultWithValue::createBasedOnSign(result); + } else { + return ResultWithValue(Result::ErrorClosed); + } +} + +ResultWithValue AudioStreamAAudio::read(void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) { + std::shared_lock lock(mAAudioStreamLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + int32_t result = mLibLoader->stream_read(mAAudioStream, buffer, + numFrames, timeoutNanoseconds); + return ResultWithValue::createBasedOnSign(result); + } else { + return ResultWithValue(Result::ErrorClosed); + } +} + + +// AAudioStream_waitForStateChange() can crash if it is waiting on a stream and that stream +// is closed from another thread. We do not want to lock the stream for the duration of the call. +// So we call AAudioStream_waitForStateChange() with a timeout of zero so that it will not block. +// Then we can do our own sleep with the lock unlocked. +Result AudioStreamAAudio::waitForStateChange(StreamState currentState, + StreamState *nextState, + int64_t timeoutNanoseconds) { + Result oboeResult = Result::ErrorTimeout; + int64_t sleepTimeNanos = 20 * kNanosPerMillisecond; // arbitrary + aaudio_stream_state_t currentAAudioState = static_cast(currentState); + + aaudio_result_t result = AAUDIO_OK; + int64_t timeLeftNanos = timeoutNanoseconds; + + mLock.lock(); + while (true) { + // Do we still have an AAudio stream? If not then stream must have been closed. + AAudioStream *stream = mAAudioStream.load(); + if (stream == nullptr) { + if (nextState != nullptr) { + *nextState = StreamState::Closed; + } + oboeResult = Result::ErrorClosed; + break; + } + + // Update and query state change with no blocking. + aaudio_stream_state_t aaudioNextState; + result = mLibLoader->stream_waitForStateChange( + mAAudioStream, + currentAAudioState, + &aaudioNextState, + 0); // timeout=0 for non-blocking + // AAudio will return AAUDIO_ERROR_TIMEOUT if timeout=0 and the state does not change. + if (result != AAUDIO_OK && result != AAUDIO_ERROR_TIMEOUT) { + oboeResult = static_cast(result); + break; + } +#if OBOE_FIX_FORCE_STARTING_TO_STARTED + if (OboeGlobals::areWorkaroundsEnabled() + && aaudioNextState == static_cast(StreamState::Starting)) { + aaudioNextState = static_cast(StreamState::Started); + } +#endif // OBOE_FIX_FORCE_STARTING_TO_STARTED + if (nextState != nullptr) { + *nextState = static_cast(aaudioNextState); + } + if (currentAAudioState != aaudioNextState) { // state changed? + oboeResult = Result::OK; + break; + } + + // Did we timeout or did user ask for non-blocking? + if (timeLeftNanos <= 0) { + break; + } + + // No change yet so sleep. + mLock.unlock(); // Don't sleep while locked. + if (sleepTimeNanos > timeLeftNanos) { + sleepTimeNanos = timeLeftNanos; // last little bit + } + AudioClock::sleepForNanos(sleepTimeNanos); + timeLeftNanos -= sleepTimeNanos; + mLock.lock(); + } + + mLock.unlock(); + return oboeResult; +} + +ResultWithValue AudioStreamAAudio::setBufferSizeInFrames(int32_t requestedFrames) { + int32_t adjustedFrames = requestedFrames; + if (adjustedFrames > mBufferCapacityInFrames) { + adjustedFrames = mBufferCapacityInFrames; + } + // This calls getBufferSize() so avoid recursive lock. + adjustedFrames = QuirksManager::getInstance().clipBufferSize(*this, adjustedFrames); + + std::shared_lock lock(mAAudioStreamLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + int32_t newBufferSize = mLibLoader->stream_setBufferSize(mAAudioStream, adjustedFrames); + // Cache the result if it's valid + if (newBufferSize > 0) mBufferSizeInFrames = newBufferSize; + return ResultWithValue::createBasedOnSign(newBufferSize); + } else { + return ResultWithValue(Result::ErrorClosed); + } +} + +StreamState AudioStreamAAudio::getState() { + std::shared_lock lock(mAAudioStreamLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + aaudio_stream_state_t aaudioState = mLibLoader->stream_getState(stream); +#if OBOE_FIX_FORCE_STARTING_TO_STARTED + if (OboeGlobals::areWorkaroundsEnabled() + && aaudioState == AAUDIO_STREAM_STATE_STARTING) { + aaudioState = AAUDIO_STREAM_STATE_STARTED; + } +#endif // OBOE_FIX_FORCE_STARTING_TO_STARTED + return static_cast(aaudioState); + } else { + return StreamState::Closed; + } +} + +int32_t AudioStreamAAudio::getBufferSizeInFrames() { + std::shared_lock lock(mAAudioStreamLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + mBufferSizeInFrames = mLibLoader->stream_getBufferSize(stream); + } + return mBufferSizeInFrames; +} + +void AudioStreamAAudio::updateFramesRead() { + std::shared_lock lock(mAAudioStreamLock); + AAudioStream *stream = mAAudioStream.load(); +// Set to 1 for debugging race condition #1180 with mAAudioStream. +// See also DEBUG_CLOSE_RACE in OboeTester. +// This was left in the code so that we could test the fix again easily in the future. +// We could not trigger the race condition without adding these get calls and the sleeps. +#define DEBUG_CLOSE_RACE 0 +#if DEBUG_CLOSE_RACE + // This is used when testing race conditions with close(). + // See DEBUG_CLOSE_RACE in OboeTester + AudioClock::sleepForNanos(400 * kNanosPerMillisecond); +#endif // DEBUG_CLOSE_RACE + if (stream != nullptr) { + mFramesRead = mLibLoader->stream_getFramesRead(stream); + } +} + +void AudioStreamAAudio::updateFramesWritten() { + std::shared_lock lock(mAAudioStreamLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + mFramesWritten = mLibLoader->stream_getFramesWritten(stream); + } +} + +ResultWithValue AudioStreamAAudio::getXRunCount() { + std::shared_lock lock(mAAudioStreamLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + return ResultWithValue::createBasedOnSign(mLibLoader->stream_getXRunCount(stream)); + } else { + return ResultWithValue(Result::ErrorNull); + } +} + +Result AudioStreamAAudio::getTimestamp(clockid_t clockId, + int64_t *framePosition, + int64_t *timeNanoseconds) { + if (getState() != StreamState::Started) { + return Result::ErrorInvalidState; + } + std::shared_lock lock(mAAudioStreamLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + return static_cast(mLibLoader->stream_getTimestamp(stream, clockId, + framePosition, timeNanoseconds)); + } else { + return Result::ErrorNull; + } +} + +ResultWithValue AudioStreamAAudio::calculateLatencyMillis() { + // Get the time that a known audio frame was presented. + int64_t hardwareFrameIndex; + int64_t hardwareFrameHardwareTime; + auto result = getTimestamp(CLOCK_MONOTONIC, + &hardwareFrameIndex, + &hardwareFrameHardwareTime); + if (result != oboe::Result::OK) { + return ResultWithValue(static_cast(result)); + } + + // Get counter closest to the app. + bool isOutput = (getDirection() == oboe::Direction::Output); + int64_t appFrameIndex = isOutput ? getFramesWritten() : getFramesRead(); + + // Assume that the next frame will be processed at the current time + using namespace std::chrono; + int64_t appFrameAppTime = + duration_cast(steady_clock::now().time_since_epoch()).count(); + + // Calculate the number of frames between app and hardware + int64_t frameIndexDelta = appFrameIndex - hardwareFrameIndex; + + // Calculate the time which the next frame will be or was presented + int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) / getSampleRate(); + int64_t appFrameHardwareTime = hardwareFrameHardwareTime + frameTimeDelta; + + // The current latency is the difference in time between when the current frame is at + // the app and when it is at the hardware. + double latencyNanos = static_cast(isOutput + ? (appFrameHardwareTime - appFrameAppTime) // hardware is later + : (appFrameAppTime - appFrameHardwareTime)); // hardware is earlier + double latencyMillis = latencyNanos / kNanosPerMillisecond; + + return ResultWithValue(latencyMillis); +} + +bool AudioStreamAAudio::isMMapUsed() { + std::shared_lock lock(mAAudioStreamLock); + AAudioStream *stream = mAAudioStream.load(); + if (stream != nullptr) { + return AAudioExtensions::getInstance().isMMapUsed(stream); + } else { + return false; + } +} + +} // namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.h new file mode 100644 index 00000000..2df4d857 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/aaudio/AudioStreamAAudio.h @@ -0,0 +1,152 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_STREAM_AAUDIO_H_ +#define OBOE_STREAM_AAUDIO_H_ + +#include +#include +#include +#include + +#include +#include "oboe/AudioStreamBuilder.h" +#include "oboe/AudioStream.h" +#include "oboe/Definitions.h" +#include "AAudioLoader.h" + +namespace oboe { + +/** + * Implementation of OboeStream that uses AAudio. + * + * Do not create this class directly. + * Use an OboeStreamBuilder to create one. + */ +class AudioStreamAAudio : public AudioStream { +public: + AudioStreamAAudio(); + explicit AudioStreamAAudio(const AudioStreamBuilder &builder); + + virtual ~AudioStreamAAudio() = default; + + /** + * + * @return true if AAudio is supported on this device. + */ + static bool isSupported(); + + // These functions override methods in AudioStream. + // See AudioStream for documentation. + Result open() override; + Result release() override; + Result close() override; + + Result requestStart() override; + Result requestPause() override; + Result requestFlush() override; + Result requestStop() override; + + ResultWithValue write(const void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) override; + + ResultWithValue read(void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) override; + + ResultWithValue setBufferSizeInFrames(int32_t requestedFrames) override; + int32_t getBufferSizeInFrames() override; + ResultWithValue getXRunCount() override; + bool isXRunCountSupported() const override { return true; } + + ResultWithValue calculateLatencyMillis() override; + + Result waitForStateChange(StreamState currentState, + StreamState *nextState, + int64_t timeoutNanoseconds) override; + + Result getTimestamp(clockid_t clockId, + int64_t *framePosition, + int64_t *timeNanoseconds) override; + + StreamState getState() override; + + AudioApi getAudioApi() const override { + return AudioApi::AAudio; + } + + DataCallbackResult callOnAudioReady(AAudioStream *stream, + void *audioData, + int32_t numFrames); + + bool isMMapUsed(); + + void closePerformanceHint() override { + mAdpfWrapper.close(); + mAdpfOpenAttempted = false; + } + +protected: + static void internalErrorCallback( + AAudioStream *stream, + void *userData, + aaudio_result_t error); + + void *getUnderlyingStream() const override { + return mAAudioStream.load(); + } + + void updateFramesRead() override; + void updateFramesWritten() override; + + void logUnsupportedAttributes(); + + void beginPerformanceHintInCallback() override; + + void endPerformanceHintInCallback(int32_t numFrames) override; + + // set by callback (or app when idle) + std::atomic mAdpfOpenAttempted{false}; + AdpfWrapper mAdpfWrapper; + +private: + // Must call under mLock. And stream must NOT be nullptr. + Result requestStop_l(AAudioStream *stream); + + /** + * Launch a thread that will stop the stream. + */ + void launchStopThread(); + +private: + + std::atomic mCallbackThreadEnabled; + std::atomic mStopThreadAllowed{false}; + + // pointer to the underlying 'C' AAudio stream, valid if open, null if closed + std::atomic mAAudioStream{nullptr}; + std::shared_mutex mAAudioStreamLock; // to protect mAAudioStream while closing + + static AAudioLoader *mLibLoader; + + // We may not use this but it is so small that it is not worth allocating dynamically. + AudioStreamErrorCallback mDefaultErrorCallback; +}; + +} // namespace oboe + +#endif // OBOE_STREAM_AAUDIO_H_ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AdpfWrapper.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AdpfWrapper.cpp new file mode 100644 index 00000000..05accdea --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AdpfWrapper.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "AdpfWrapper.h" +#include "AudioClock.h" +#include "OboeDebug.h" + +typedef APerformanceHintManager* (*APH_getManager)(); +typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*, + size_t, int64_t); +typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t); +typedef void (*APH_closeSession)(APerformanceHintSession* session); + +static bool gAPerformanceHintBindingInitialized = false; +static APH_getManager gAPH_getManagerFn = nullptr; +static APH_createSession gAPH_createSessionFn = nullptr; +static APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr; +static APH_closeSession gAPH_closeSessionFn = nullptr; + +static int loadAphFunctions() { + if (gAPerformanceHintBindingInitialized) return true; + + void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); + if (handle_ == nullptr) { + return -1000; + } + + gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager"); + if (gAPH_getManagerFn == nullptr) { + return -1001; + } + + gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession"); + if (gAPH_getManagerFn == nullptr) { + return -1002; + } + + gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym( + handle_, "APerformanceHint_reportActualWorkDuration"); + if (gAPH_getManagerFn == nullptr) { + return -1003; + } + + gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession"); + if (gAPH_getManagerFn == nullptr) { + return -1004; + } + + gAPerformanceHintBindingInitialized = true; + return 0; +} + +bool AdpfWrapper::sUseAlternativeHack = false; // TODO remove hack + +int AdpfWrapper::open(pid_t threadId, + int64_t targetDurationNanos) { + std::lock_guard lock(mLock); + int result = loadAphFunctions(); + if (result < 0) return result; + + // This is a singleton. + APerformanceHintManager* manager = gAPH_getManagerFn(); + + int32_t thread32 = threadId; + if (sUseAlternativeHack) { + // TODO Remove this hack when we finish experimenting with alternative algorithms. + // The A5 is an arbitrary signal to a hacked version of ADPF to try an alternative + // algorithm that is not based on PID. + targetDurationNanos = (targetDurationNanos & ~0xFF) | 0xA5; + } + mHintSession = gAPH_createSessionFn(manager, &thread32, 1 /* size */, targetDurationNanos); + if (mHintSession == nullptr) { + return -1; + } + return 0; +} + +void AdpfWrapper::reportActualDuration(int64_t actualDurationNanos) { + //LOGD("ADPF Oboe %s(dur=%lld)", __func__, (long long)actualDurationNanos); + std::lock_guard lock(mLock); + if (mHintSession != nullptr) { + gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos); + } +} + +void AdpfWrapper::close() { + std::lock_guard lock(mLock); + if (mHintSession != nullptr) { + gAPH_closeSessionFn(mHintSession); + mHintSession = nullptr; + } +} + +void AdpfWrapper::onBeginCallback() { + if (isOpen()) { + mBeginCallbackNanos = oboe::AudioClock::getNanoseconds(CLOCK_REALTIME); + } +} + +void AdpfWrapper::onEndCallback(double durationScaler) { + if (isOpen()) { + int64_t endCallbackNanos = oboe::AudioClock::getNanoseconds(CLOCK_REALTIME); + int64_t actualDurationNanos = endCallbackNanos - mBeginCallbackNanos; + int64_t scaledDurationNanos = static_cast(actualDurationNanos * durationScaler); + reportActualDuration(scaledDurationNanos); + } +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AdpfWrapper.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AdpfWrapper.h new file mode 100644 index 00000000..330ee3c6 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AdpfWrapper.h @@ -0,0 +1,85 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNTHMARK_ADPF_WRAPPER_H +#define SYNTHMARK_ADPF_WRAPPER_H + +#include +#include +#include +#include +#include +#include + +struct APerformanceHintManager; +struct APerformanceHintSession; + +typedef struct APerformanceHintManager APerformanceHintManager; +typedef struct APerformanceHintSession APerformanceHintSession; + +class AdpfWrapper { +public: + /** + * Create an ADPF session that can be used to boost performance. + * @param threadId + * @param targetDurationNanos - nominal period of isochronous task + * @return zero or negative error + */ + int open(pid_t threadId, + int64_t targetDurationNanos); + + bool isOpen() const { + return (mHintSession != nullptr); + } + + void close(); + + /** + * Call this at the beginning of the callback that you are measuring. + */ + void onBeginCallback(); + + /** + * Call this at the end of the callback that you are measuring. + * It is OK to skip this if you have a short callback. + */ + void onEndCallback(double durationScaler); + + /** + * For internal use only! + * This is a hack for communicating with experimental versions of ADPF. + * @param enabled + */ + static void setUseAlternative(bool enabled) { + sUseAlternativeHack = enabled; + } + + /** + * Report the measured duration of a callback. + * This is normally called by onEndCallback(). + * You may want to call this directly in order to give an advance hint of a jump in workload. + * @param actualDurationNanos + */ + void reportActualDuration(int64_t actualDurationNanos); + +private: + std::mutex mLock; + APerformanceHintSession* mHintSession = nullptr; + int64_t mBeginCallbackNanos = 0; + static bool sUseAlternativeHack; +}; + +#endif //SYNTHMARK_ADPF_WRAPPER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioClock.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioClock.h new file mode 100644 index 00000000..3fe20cb0 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioClock.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_AUDIO_CLOCK_H +#define OBOE_AUDIO_CLOCK_H + +#include +#include +#include "oboe/Definitions.h" + +namespace oboe { + +// TODO: Move this class into the public headers because it is useful when calculating stream latency +class AudioClock { +public: + static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) { + struct timespec time; + int result = clock_gettime(clockId, &time); + if (result < 0) { + return result; + } + return (time.tv_sec * kNanosPerSecond) + time.tv_nsec; + } + + /** + * Sleep until the specified time. + * + * @param nanoTime time to wake up + * @param clockId CLOCK_MONOTONIC is default + * @return 0 or a negative error, eg. -EINTR + */ + + static int sleepUntilNanoTime(int64_t nanoTime, clockid_t clockId = CLOCK_MONOTONIC) { + struct timespec time; + time.tv_sec = nanoTime / kNanosPerSecond; + time.tv_nsec = nanoTime - (time.tv_sec * kNanosPerSecond); + return 0 - clock_nanosleep(clockId, TIMER_ABSTIME, &time, NULL); + } + + /** + * Sleep for the specified number of nanoseconds in real-time. + * Return immediately with 0 if a negative nanoseconds is specified. + * + * @param nanoseconds time to sleep + * @param clockId CLOCK_REALTIME is default + * @return 0 or a negative error, eg. -EINTR + */ + + static int sleepForNanos(int64_t nanoseconds, clockid_t clockId = CLOCK_REALTIME) { + if (nanoseconds > 0) { + struct timespec time; + time.tv_sec = nanoseconds / kNanosPerSecond; + time.tv_nsec = nanoseconds - (time.tv_sec * kNanosPerSecond); + return 0 - clock_nanosleep(clockId, 0, &time, NULL); + } + return 0; + } +}; + +} // namespace oboe + +#endif //OBOE_AUDIO_CLOCK_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioSourceCaller.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioSourceCaller.cpp new file mode 100644 index 00000000..a19cf813 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioSourceCaller.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AudioSourceCaller.h" + +using namespace oboe; +using namespace flowgraph; + +int32_t AudioSourceCaller::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) { + AudioStreamDataCallback *callback = mStream->getDataCallback(); + int32_t result = 0; + int32_t numFrames = numBytes / mStream->getBytesPerFrame(); + if (callback != nullptr) { + DataCallbackResult callbackResult = callback->onAudioReady(mStream, buffer, numFrames); + // onAudioReady() does not return the number of bytes processed so we have to assume all. + result = (callbackResult == DataCallbackResult::Continue) + ? numBytes + : -1; + } else { + auto readResult = mStream->read(buffer, numFrames, mTimeoutNanos); + if (!readResult) return (int32_t) readResult.error(); + result = readResult.value() * mStream->getBytesPerFrame(); + } + return result; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioSourceCaller.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioSourceCaller.h new file mode 100644 index 00000000..d196d410 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioSourceCaller.h @@ -0,0 +1,83 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_AUDIO_SOURCE_CALLER_H +#define OBOE_AUDIO_SOURCE_CALLER_H + +#include "OboeDebug.h" +#include "oboe/Oboe.h" + +#include "flowgraph/FlowGraphNode.h" +#include "FixedBlockReader.h" + +namespace oboe { + +class AudioStreamCallback; +class AudioStream; + +/** + * For output streams that use a callback, call the application for more data. + * For input streams that do not use a callback, read from the stream. + */ +class AudioSourceCaller : public flowgraph::FlowGraphSource, public FixedBlockProcessor { +public: + AudioSourceCaller(int32_t channelCount, int32_t framesPerCallback, int32_t bytesPerSample) + : FlowGraphSource(channelCount) + , mBlockReader(*this) { + mBlockReader.open(channelCount * framesPerCallback * bytesPerSample); + } + + /** + * Set the stream to use as a source of data. + * @param stream + */ + void setStream(oboe::AudioStream *stream) { + mStream = stream; + } + + oboe::AudioStream *getStream() { + return mStream; + } + + /** + * Timeout value to use when calling audioStream->read(). + * @param timeoutNanos Zero for no timeout or time in nanoseconds. + */ + void setTimeoutNanos(int64_t timeoutNanos) { + mTimeoutNanos = timeoutNanos; + } + + int64_t getTimeoutNanos() const { + return mTimeoutNanos; + } + + /** + * Called internally for block size adaptation. + * @param buffer + * @param numBytes + * @return + */ + int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) override; + +protected: + oboe::AudioStream *mStream = nullptr; + int64_t mTimeoutNanos = 0; + + FixedBlockReader mBlockReader; +}; + +} +#endif //OBOE_AUDIO_SOURCE_CALLER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioStream.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioStream.cpp new file mode 100644 index 00000000..06c01118 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioStream.cpp @@ -0,0 +1,222 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include "OboeDebug.h" +#include "AudioClock.h" +#include + +namespace oboe { + +/* + * AudioStream + */ +AudioStream::AudioStream(const AudioStreamBuilder &builder) + : AudioStreamBase(builder) { +} + +Result AudioStream::close() { + closePerformanceHint(); + // Update local counters so they can be read after the close. + updateFramesWritten(); + updateFramesRead(); + return Result::OK; +} + +// Call this from fireDataCallback() if you want to monitor CPU scheduler. +void AudioStream::checkScheduler() { + int scheduler = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK; // for current thread + if (scheduler != mPreviousScheduler) { + LOGD("AudioStream::%s() scheduler = %s", __func__, + ((scheduler == SCHED_FIFO) ? "SCHED_FIFO" : + ((scheduler == SCHED_OTHER) ? "SCHED_OTHER" : + ((scheduler == SCHED_RR) ? "SCHED_RR" : "UNKNOWN"))) + ); + mPreviousScheduler = scheduler; + } +} + +DataCallbackResult AudioStream::fireDataCallback(void *audioData, int32_t numFrames) { + if (!isDataCallbackEnabled()) { + LOGW("AudioStream::%s() called with data callback disabled!", __func__); + return DataCallbackResult::Stop; // Should not be getting called + } + + beginPerformanceHintInCallback(); + + // Call the app to do the work. + DataCallbackResult result; + if (mDataCallback) { + result = mDataCallback->onAudioReady(this, audioData, numFrames); + } else { + result = onDefaultCallback(audioData, numFrames); + } + // On Oreo, we might get called after returning stop. + // So block that here. + setDataCallbackEnabled(result == DataCallbackResult::Continue); + + endPerformanceHintInCallback(numFrames); + + return result; +} + +Result AudioStream::waitForStateTransition(StreamState startingState, + StreamState endingState, + int64_t timeoutNanoseconds) +{ + StreamState state; + { + std::lock_guard lock(mLock); + state = getState(); + if (state == StreamState::Closed) { + return Result::ErrorClosed; + } else if (state == StreamState::Disconnected) { + return Result::ErrorDisconnected; + } + } + + StreamState nextState = state; + // TODO Should this be a while()?! + if (state == startingState && state != endingState) { + Result result = waitForStateChange(state, &nextState, timeoutNanoseconds); + if (result != Result::OK) { + return result; + } + } + + if (nextState != endingState) { + return Result::ErrorInvalidState; + } else { + return Result::OK; + } +} + +Result AudioStream::start(int64_t timeoutNanoseconds) +{ + Result result = requestStart(); + if (result != Result::OK) return result; + if (timeoutNanoseconds <= 0) return result; + return waitForStateTransition(StreamState::Starting, + StreamState::Started, timeoutNanoseconds); +} + +Result AudioStream::pause(int64_t timeoutNanoseconds) +{ + Result result = requestPause(); + if (result != Result::OK) return result; + if (timeoutNanoseconds <= 0) return result; + return waitForStateTransition(StreamState::Pausing, + StreamState::Paused, timeoutNanoseconds); +} + +Result AudioStream::flush(int64_t timeoutNanoseconds) +{ + Result result = requestFlush(); + if (result != Result::OK) return result; + if (timeoutNanoseconds <= 0) return result; + return waitForStateTransition(StreamState::Flushing, + StreamState::Flushed, timeoutNanoseconds); +} + +Result AudioStream::stop(int64_t timeoutNanoseconds) +{ + Result result = requestStop(); + if (result != Result::OK) return result; + if (timeoutNanoseconds <= 0) return result; + return waitForStateTransition(StreamState::Stopping, + StreamState::Stopped, timeoutNanoseconds); +} + +int32_t AudioStream::getBytesPerSample() const { + return convertFormatToSizeInBytes(mFormat); +} + +int64_t AudioStream::getFramesRead() { + updateFramesRead(); + return mFramesRead; +} + +int64_t AudioStream::getFramesWritten() { + updateFramesWritten(); + return mFramesWritten; +} + +ResultWithValue AudioStream::getAvailableFrames() { + int64_t readCounter = getFramesRead(); + if (readCounter < 0) return ResultWithValue::createBasedOnSign(readCounter); + int64_t writeCounter = getFramesWritten(); + if (writeCounter < 0) return ResultWithValue::createBasedOnSign(writeCounter); + int32_t framesAvailable = writeCounter - readCounter; + return ResultWithValue(framesAvailable); +} + +ResultWithValue AudioStream::waitForAvailableFrames(int32_t numFrames, + int64_t timeoutNanoseconds) { + if (numFrames == 0) return Result::OK; + if (numFrames < 0) return Result::ErrorOutOfRange; + + // Make sure we don't try to wait for more frames than the buffer can hold. + // Subtract framesPerBurst because this is often called from a callback + // and we don't want to be sleeping if the buffer is close to overflowing. + const int32_t maxAvailableFrames = getBufferCapacityInFrames() - getFramesPerBurst(); + numFrames = std::min(numFrames, maxAvailableFrames); + // The capacity should never be less than one burst. But clip to zero just in case. + numFrames = std::max(0, numFrames); + + int64_t framesAvailable = 0; + int64_t burstInNanos = getFramesPerBurst() * kNanosPerSecond / getSampleRate(); + bool ready = false; + int64_t deadline = AudioClock::getNanoseconds() + timeoutNanoseconds; + do { + ResultWithValue result = getAvailableFrames(); + if (!result) return result; + framesAvailable = result.value(); + ready = (framesAvailable >= numFrames); + if (!ready) { + int64_t now = AudioClock::getNanoseconds(); + if (now > deadline) break; + AudioClock::sleepForNanos(burstInNanos); + } + } while (!ready); + return (!ready) + ? ResultWithValue(Result::ErrorTimeout) + : ResultWithValue(framesAvailable); +} + +ResultWithValue AudioStream::getTimestamp(clockid_t clockId) { + FrameTimestamp frame; + Result result = getTimestamp(clockId, &frame.position, &frame.timestamp); + if (result == Result::OK){ + return ResultWithValue(frame); + } else { + return ResultWithValue(static_cast(result)); + } +} + +void AudioStream::calculateDefaultDelayBeforeCloseMillis() { + // Calculate delay time before close based on burst duration. + // Start with a burst duration then add 1 msec as a safety margin. + mDelayBeforeCloseMillis = std::max(kMinDelayBeforeCloseMillis, + 1 + ((mFramesPerBurst * 1000) / getSampleRate())); + LOGD("calculateDefaultDelayBeforeCloseMillis() default = %d", + static_cast(mDelayBeforeCloseMillis)); +} + +} // namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp new file mode 100644 index 00000000..5dbe38cc --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/AudioStreamBuilder.cpp @@ -0,0 +1,224 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + + +#include "aaudio/AAudioExtensions.h" +#include "aaudio/AudioStreamAAudio.h" +#include "FilterAudioStream.h" +#include "OboeDebug.h" +#include "oboe/Oboe.h" +#include "oboe/AudioStreamBuilder.h" +#include "opensles/AudioInputStreamOpenSLES.h" +#include "opensles/AudioOutputStreamOpenSLES.h" +#include "opensles/AudioStreamOpenSLES.h" +#include "QuirksManager.h" + +bool oboe::OboeGlobals::mWorkaroundsEnabled = true; + +namespace oboe { + +/** + * The following default values are used when oboe does not have any better way of determining the optimal values + * for an audio stream. This can happen when: + * + * - Client is creating a stream on API < 26 (OpenSLES) but has not supplied the optimal sample + * rate and/or frames per burst + * - Client is creating a stream on API 16 (OpenSLES) where AudioManager.PROPERTY_OUTPUT_* values + * are not available + */ +int32_t DefaultStreamValues::SampleRate = 48000; // Common rate for mobile audio and video +int32_t DefaultStreamValues::FramesPerBurst = 192; // 4 msec at 48000 Hz +int32_t DefaultStreamValues::ChannelCount = 2; // Stereo + +constexpr int kBufferSizeInBurstsForLowLatencyStreams = 2; + +#ifndef OBOE_ENABLE_AAUDIO +// Set OBOE_ENABLE_AAUDIO to 0 if you want to disable the AAudio API. +// This might be useful if you want to force all the unit tests to use OpenSL ES. +#define OBOE_ENABLE_AAUDIO 1 +#endif + +bool AudioStreamBuilder::isAAudioSupported() { + return AudioStreamAAudio::isSupported() && OBOE_ENABLE_AAUDIO; +} + +bool AudioStreamBuilder::isAAudioRecommended() { + // See https://github.com/google/oboe/issues/40, + // AAudio may not be stable on Android O, depending on how it is used. + // To be safe, use AAudio only on O_MR1 and above. + return (getSdkVersion() >= __ANDROID_API_O_MR1__) && isAAudioSupported(); +} + +AudioStream *AudioStreamBuilder::build() { + AudioStream *stream = nullptr; + if (isAAudioRecommended() && mAudioApi != AudioApi::OpenSLES) { + stream = new AudioStreamAAudio(*this); + } else if (isAAudioSupported() && mAudioApi == AudioApi::AAudio) { + stream = new AudioStreamAAudio(*this); + LOGE("Creating AAudio stream on 8.0 because it was specified. This is error prone."); + } else { + if (getDirection() == oboe::Direction::Output) { + stream = new AudioOutputStreamOpenSLES(*this); + } else if (getDirection() == oboe::Direction::Input) { + stream = new AudioInputStreamOpenSLES(*this); + } + } + return stream; +} + +bool AudioStreamBuilder::isCompatible(AudioStreamBase &other) { + return (getSampleRate() == oboe::Unspecified || getSampleRate() == other.getSampleRate()) + && (getFormat() == (AudioFormat)oboe::Unspecified || getFormat() == other.getFormat()) + && (getFramesPerDataCallback() == oboe::Unspecified || getFramesPerDataCallback() == other.getFramesPerDataCallback()) + && (getChannelCount() == oboe::Unspecified || getChannelCount() == other.getChannelCount()); +} + +Result AudioStreamBuilder::openStream(AudioStream **streamPP) { + auto result = isValidConfig(); + if (result != Result::OK) { + LOGW("%s() invalid config %d", __func__, result); + return result; + } + + LOGI("%s() %s -------- %s --------", + __func__, getDirection() == Direction::Input ? "INPUT" : "OUTPUT", getVersionText()); + + if (streamPP == nullptr) { + return Result::ErrorNull; + } + *streamPP = nullptr; + + AudioStream *streamP = nullptr; + + // Maybe make a FilterInputStream. + AudioStreamBuilder childBuilder(*this); + // Check need for conversion and modify childBuilder for optimal stream. + bool conversionNeeded = QuirksManager::getInstance().isConversionNeeded(*this, childBuilder); + // Do we need to make a child stream and convert. + if (conversionNeeded) { + AudioStream *tempStream; + result = childBuilder.openStream(&tempStream); + if (result != Result::OK) { + return result; + } + + if (isCompatible(*tempStream)) { + // The child stream would work as the requested stream so we can just use it directly. + *streamPP = tempStream; + return result; + } else { + AudioStreamBuilder parentBuilder = *this; + // Build a stream that is as close as possible to the childStream. + if (getFormat() == oboe::AudioFormat::Unspecified) { + parentBuilder.setFormat(tempStream->getFormat()); + } + if (getChannelCount() == oboe::Unspecified) { + parentBuilder.setChannelCount(tempStream->getChannelCount()); + } + if (getSampleRate() == oboe::Unspecified) { + parentBuilder.setSampleRate(tempStream->getSampleRate()); + } + if (getFramesPerDataCallback() == oboe::Unspecified) { + parentBuilder.setFramesPerCallback(tempStream->getFramesPerDataCallback()); + } + + // Use childStream in a FilterAudioStream. + LOGI("%s() create a FilterAudioStream for data conversion.", __func__); + FilterAudioStream *filterStream = new FilterAudioStream(parentBuilder, tempStream); + result = filterStream->configureFlowGraph(); + if (result != Result::OK) { + filterStream->close(); + delete filterStream; + // Just open streamP the old way. + } else { + streamP = static_cast(filterStream); + } + } + } + + if (streamP == nullptr) { + streamP = build(); + if (streamP == nullptr) { + return Result::ErrorNull; + } + } + + // If MMAP has a problem in this case then disable it temporarily. + bool wasMMapOriginallyEnabled = AAudioExtensions::getInstance().isMMapEnabled(); + bool wasMMapTemporarilyDisabled = false; + if (wasMMapOriginallyEnabled) { + bool isMMapSafe = QuirksManager::getInstance().isMMapSafe(childBuilder); + if (!isMMapSafe) { + AAudioExtensions::getInstance().setMMapEnabled(false); + wasMMapTemporarilyDisabled = true; + } + } + result = streamP->open(); + if (wasMMapTemporarilyDisabled) { + AAudioExtensions::getInstance().setMMapEnabled(wasMMapOriginallyEnabled); // restore original + } + if (result == Result::OK) { + + int32_t optimalBufferSize = -1; + // Use a reasonable default buffer size. + if (streamP->getDirection() == Direction::Input) { + // For input, small size does not improve latency because the stream is usually + // run close to empty. And a low size can result in XRuns so always use the maximum. + optimalBufferSize = streamP->getBufferCapacityInFrames(); + } else if (streamP->getPerformanceMode() == PerformanceMode::LowLatency + && streamP->getDirection() == Direction::Output) { // Output check is redundant. + optimalBufferSize = streamP->getFramesPerBurst() * + kBufferSizeInBurstsForLowLatencyStreams; + } + if (optimalBufferSize >= 0) { + auto setBufferResult = streamP->setBufferSizeInFrames(optimalBufferSize); + if (!setBufferResult) { + LOGW("Failed to setBufferSizeInFrames(%d). Error was %s", + optimalBufferSize, + convertToText(setBufferResult.error())); + } + } + + *streamPP = streamP; + } else { + delete streamP; + } + return result; +} + +Result AudioStreamBuilder::openManagedStream(oboe::ManagedStream &stream) { + stream.reset(); + AudioStream *streamptr; + auto result = openStream(&streamptr); + stream.reset(streamptr); + return result; +} + +Result AudioStreamBuilder::openStream(std::shared_ptr &sharedStream) { + sharedStream.reset(); + AudioStream *streamptr; + auto result = openStream(&streamptr); + if (result == Result::OK) { + sharedStream.reset(streamptr); + // Save a weak_ptr in the stream for use with callbacks. + streamptr->setWeakThis(sharedStream); + } + return result; +} + +} // namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.cpp new file mode 100644 index 00000000..374fffd1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "OboeDebug.h" +#include "DataConversionFlowGraph.h" +#include "SourceFloatCaller.h" +#include "SourceI16Caller.h" +#include "SourceI24Caller.h" +#include "SourceI32Caller.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace oboe; +using namespace flowgraph; +using namespace resampler; + +void DataConversionFlowGraph::setSource(const void *buffer, int32_t numFrames) { + mSource->setData(buffer, numFrames); +} + +static MultiChannelResampler::Quality convertOboeSRQualityToMCR(SampleRateConversionQuality quality) { + switch (quality) { + case SampleRateConversionQuality::Fastest: + return MultiChannelResampler::Quality::Fastest; + case SampleRateConversionQuality::Low: + return MultiChannelResampler::Quality::Low; + default: + case SampleRateConversionQuality::Medium: + return MultiChannelResampler::Quality::Medium; + case SampleRateConversionQuality::High: + return MultiChannelResampler::Quality::High; + case SampleRateConversionQuality::Best: + return MultiChannelResampler::Quality::Best; + } +} + +// Chain together multiple processors. +// Callback Output +// Use SourceCaller that calls original app callback from the flowgraph. +// The child callback from FilteredAudioStream read()s from the flowgraph. +// Callback Input +// Child callback from FilteredAudioStream writes()s to the flowgraph. +// The output of the flowgraph goes through a BlockWriter to the app callback. +// Blocking Write +// Write buffer is set on an AudioSource. +// Data is pulled through the graph and written to the child stream. +// Blocking Read +// Reads in a loop from the flowgraph Sink to fill the read buffer. +// A SourceCaller then does a blocking read from the child Stream. +// +Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream *sinkStream) { + + FlowGraphPortFloatOutput *lastOutput = nullptr; + + bool isOutput = sourceStream->getDirection() == Direction::Output; + bool isInput = !isOutput; + mFilterStream = isOutput ? sourceStream : sinkStream; + + AudioFormat sourceFormat = sourceStream->getFormat(); + int32_t sourceChannelCount = sourceStream->getChannelCount(); + int32_t sourceSampleRate = sourceStream->getSampleRate(); + int32_t sourceFramesPerCallback = sourceStream->getFramesPerDataCallback(); + + AudioFormat sinkFormat = sinkStream->getFormat(); + int32_t sinkChannelCount = sinkStream->getChannelCount(); + int32_t sinkSampleRate = sinkStream->getSampleRate(); + int32_t sinkFramesPerCallback = sinkStream->getFramesPerDataCallback(); + + LOGI("%s() flowgraph converts channels: %d to %d, format: %d to %d" + ", rate: %d to %d, cbsize: %d to %d, qual = %d", + __func__, + sourceChannelCount, sinkChannelCount, + sourceFormat, sinkFormat, + sourceSampleRate, sinkSampleRate, + sourceFramesPerCallback, sinkFramesPerCallback, + sourceStream->getSampleRateConversionQuality()); + + // Source + // IF OUTPUT and using a callback then call back to the app using a SourceCaller. + // OR IF INPUT and NOT using a callback then read from the child stream using a SourceCaller. + bool isDataCallbackSpecified = sourceStream->isDataCallbackSpecified(); + if ((isDataCallbackSpecified && isOutput) + || (!isDataCallbackSpecified && isInput)) { + int32_t actualSourceFramesPerCallback = (sourceFramesPerCallback == kUnspecified) + ? sourceStream->getFramesPerBurst() + : sourceFramesPerCallback; + switch (sourceFormat) { + case AudioFormat::Float: + mSourceCaller = std::make_unique(sourceChannelCount, + actualSourceFramesPerCallback); + break; + case AudioFormat::I16: + mSourceCaller = std::make_unique(sourceChannelCount, + actualSourceFramesPerCallback); + break; + case AudioFormat::I24: + mSourceCaller = std::make_unique(sourceChannelCount, + actualSourceFramesPerCallback); + break; + case AudioFormat::I32: + mSourceCaller = std::make_unique(sourceChannelCount, + actualSourceFramesPerCallback); + break; + default: + LOGE("%s() Unsupported source caller format = %d", __func__, sourceFormat); + return Result::ErrorIllegalArgument; + } + mSourceCaller->setStream(sourceStream); + lastOutput = &mSourceCaller->output; + } else { + // IF OUTPUT and NOT using a callback then write to the child stream using a BlockWriter. + // OR IF INPUT and using a callback then write to the app using a BlockWriter. + switch (sourceFormat) { + case AudioFormat::Float: + mSource = std::make_unique(sourceChannelCount); + break; + case AudioFormat::I16: + mSource = std::make_unique(sourceChannelCount); + break; + case AudioFormat::I24: + mSource = std::make_unique(sourceChannelCount); + break; + case AudioFormat::I32: + mSource = std::make_unique(sourceChannelCount); + break; + default: + LOGE("%s() Unsupported source format = %d", __func__, sourceFormat); + return Result::ErrorIllegalArgument; + } + if (isInput) { + int32_t actualSinkFramesPerCallback = (sinkFramesPerCallback == kUnspecified) + ? sinkStream->getFramesPerBurst() + : sinkFramesPerCallback; + // The BlockWriter is after the Sink so use the SinkStream size. + mBlockWriter.open(actualSinkFramesPerCallback * sinkStream->getBytesPerFrame()); + mAppBuffer = std::make_unique( + kDefaultBufferSize * sinkStream->getBytesPerFrame()); + } + lastOutput = &mSource->output; + } + + // If we are going to reduce the number of channels then do it before the + // sample rate converter. + if (sourceChannelCount > sinkChannelCount) { + if (sinkChannelCount == 1) { + mMultiToMonoConverter = std::make_unique(sourceChannelCount); + lastOutput->connect(&mMultiToMonoConverter->input); + lastOutput = &mMultiToMonoConverter->output; + } else { + mChannelCountConverter = std::make_unique( + sourceChannelCount, + sinkChannelCount); + lastOutput->connect(&mChannelCountConverter->input); + lastOutput = &mChannelCountConverter->output; + } + } + + // Sample Rate conversion + if (sourceSampleRate != sinkSampleRate) { + // Create a resampler to do the math. + mResampler.reset(MultiChannelResampler::make(lastOutput->getSamplesPerFrame(), + sourceSampleRate, + sinkSampleRate, + convertOboeSRQualityToMCR( + sourceStream->getSampleRateConversionQuality()))); + // Make a flowgraph node that uses the resampler. + mRateConverter = std::make_unique(lastOutput->getSamplesPerFrame(), + *mResampler.get()); + lastOutput->connect(&mRateConverter->input); + lastOutput = &mRateConverter->output; + } + + // Expand the number of channels if required. + if (sourceChannelCount < sinkChannelCount) { + if (sourceChannelCount == 1) { + mMonoToMultiConverter = std::make_unique(sinkChannelCount); + lastOutput->connect(&mMonoToMultiConverter->input); + lastOutput = &mMonoToMultiConverter->output; + } else { + mChannelCountConverter = std::make_unique( + sourceChannelCount, + sinkChannelCount); + lastOutput->connect(&mChannelCountConverter->input); + lastOutput = &mChannelCountConverter->output; + } + } + + // Sink + switch (sinkFormat) { + case AudioFormat::Float: + mSink = std::make_unique(sinkChannelCount); + break; + case AudioFormat::I16: + mSink = std::make_unique(sinkChannelCount); + break; + case AudioFormat::I24: + mSink = std::make_unique(sinkChannelCount); + break; + case AudioFormat::I32: + mSink = std::make_unique(sinkChannelCount); + break; + default: + LOGE("%s() Unsupported sink format = %d", __func__, sinkFormat); + return Result::ErrorIllegalArgument;; + } + lastOutput->connect(&mSink->input); + + return Result::OK; +} + +int32_t DataConversionFlowGraph::read(void *buffer, int32_t numFrames, int64_t timeoutNanos) { + if (mSourceCaller) { + mSourceCaller->setTimeoutNanos(timeoutNanos); + } + int32_t numRead = mSink->read(buffer, numFrames); + return numRead; +} + +// This is similar to pushing data through the flowgraph. +int32_t DataConversionFlowGraph::write(void *inputBuffer, int32_t numFrames) { + // Put the data from the input at the head of the flowgraph. + mSource->setData(inputBuffer, numFrames); + while (true) { + // Pull and read some data in app format into a small buffer. + int32_t framesRead = mSink->read(mAppBuffer.get(), flowgraph::kDefaultBufferSize); + if (framesRead <= 0) break; + // Write to a block adapter, which will call the destination whenever it has enough data. + int32_t bytesRead = mBlockWriter.write(mAppBuffer.get(), + framesRead * mFilterStream->getBytesPerFrame()); + if (bytesRead < 0) return bytesRead; // TODO review + } + return numFrames; +} + +int32_t DataConversionFlowGraph::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) { + int32_t numFrames = numBytes / mFilterStream->getBytesPerFrame(); + mCallbackResult = mFilterStream->getDataCallback()->onAudioReady(mFilterStream, buffer, numFrames); + // TODO handle STOP from callback, process data remaining in the block adapter + return numBytes; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.h new file mode 100644 index 00000000..0cde1f35 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/DataConversionFlowGraph.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_OBOE_FLOW_GRAPH_H +#define OBOE_OBOE_FLOW_GRAPH_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include "AudioSourceCaller.h" +#include "FixedBlockWriter.h" + +namespace oboe { + +class AudioStream; +class AudioSourceCaller; + +/** + * Convert PCM channels, format and sample rate for optimal latency. + */ +class DataConversionFlowGraph : public FixedBlockProcessor { +public: + + DataConversionFlowGraph() + : mBlockWriter(*this) {} + + void setSource(const void *buffer, int32_t numFrames); + + /** Connect several modules together to convert from source to sink. + * This should only be called once for each instance. + * + * @param sourceFormat + * @param sourceChannelCount + * @param sinkFormat + * @param sinkChannelCount + * @return + */ + oboe::Result configure(oboe::AudioStream *sourceStream, oboe::AudioStream *sinkStream); + + int32_t read(void *buffer, int32_t numFrames, int64_t timeoutNanos); + + int32_t write(void *buffer, int32_t numFrames); + + int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) override; + + DataCallbackResult getDataCallbackResult() { + return mCallbackResult; + } + +private: + std::unique_ptr mSource; + std::unique_ptr mSourceCaller; + std::unique_ptr mMonoToMultiConverter; + std::unique_ptr mMultiToMonoConverter; + std::unique_ptr mChannelCountConverter; + std::unique_ptr mResampler; + std::unique_ptr mRateConverter; + std::unique_ptr mSink; + + FixedBlockWriter mBlockWriter; + DataCallbackResult mCallbackResult = DataCallbackResult::Continue; + AudioStream *mFilterStream = nullptr; + std::unique_ptr mAppBuffer; +}; + +} +#endif //OBOE_OBOE_FLOW_GRAPH_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.cpp new file mode 100644 index 00000000..5d4e6934 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "OboeDebug.h" +#include "FilterAudioStream.h" + +using namespace oboe; +using namespace flowgraph; + +// Output callback uses FixedBlockReader::read() +// <= SourceFloatCaller::onProcess() +// <=== DataConversionFlowGraph::read() +// <== FilterAudioStream::onAudioReady() +// +// Output blocking uses no block adapter because AAudio can accept +// writes of any size. It uses DataConversionFlowGraph::read() <== FilterAudioStream::write() <= app +// +// Input callback uses FixedBlockWriter::write() +// <= DataConversionFlowGraph::write() +// <= FilterAudioStream::onAudioReady() +// +// Input blocking uses FixedBlockReader::read() // TODO may not need block adapter +// <= SourceFloatCaller::onProcess() +// <=== SinkFloat::read() +// <= DataConversionFlowGraph::read() +// <== FilterAudioStream::read() +// <= app + +Result FilterAudioStream::configureFlowGraph() { + mFlowGraph = std::make_unique(); + bool isOutput = getDirection() == Direction::Output; + + AudioStream *sourceStream = isOutput ? this : mChildStream.get(); + AudioStream *sinkStream = isOutput ? mChildStream.get() : this; + + mRateScaler = ((double) getSampleRate()) / mChildStream->getSampleRate(); + + return mFlowGraph->configure(sourceStream, sinkStream); +} + +// Put the data to be written at the source end of the flowgraph. +// Then read (pull) the data from the flowgraph and write it to the +// child stream. +ResultWithValue FilterAudioStream::write(const void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) { + int32_t framesWritten = 0; + mFlowGraph->setSource(buffer, numFrames); + while (true) { + int32_t numRead = mFlowGraph->read(mBlockingBuffer.get(), + getFramesPerBurst(), + timeoutNanoseconds); + if (numRead < 0) { + return ResultWithValue::createBasedOnSign(numRead); + } + if (numRead == 0) { + break; // finished processing the source buffer + } + auto writeResult = mChildStream->write(mBlockingBuffer.get(), + numRead, + timeoutNanoseconds); + if (!writeResult) { + return writeResult; + } + framesWritten += writeResult.value(); + } + return ResultWithValue::createBasedOnSign(framesWritten); +} + +// Read (pull) the data we want from the sink end of the flowgraph. +// The necessary data will be read from the child stream using a flowgraph callback. +ResultWithValue FilterAudioStream::read(void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) { + int32_t framesRead = mFlowGraph->read(buffer, numFrames, timeoutNanoseconds); + return ResultWithValue::createBasedOnSign(framesRead); +} + +DataCallbackResult FilterAudioStream::onAudioReady(AudioStream *oboeStream, + void *audioData, + int32_t numFrames) { + int32_t framesProcessed; + if (oboeStream->getDirection() == Direction::Output) { + framesProcessed = mFlowGraph->read(audioData, numFrames, 0 /* timeout */); + } else { + framesProcessed = mFlowGraph->write(audioData, numFrames); + } + return (framesProcessed < numFrames) + ? DataCallbackResult::Stop + : mFlowGraph->getDataCallbackResult(); +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.h new file mode 100644 index 00000000..18907499 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FilterAudioStream.h @@ -0,0 +1,227 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_FILTER_AUDIO_STREAM_H +#define OBOE_FILTER_AUDIO_STREAM_H + +#include +#include +#include "DataConversionFlowGraph.h" + +namespace oboe { + +/** + * An AudioStream that wraps another AudioStream and provides audio data conversion. + * Operations may include channel conversion, data format conversion and/or sample rate conversion. + */ +class FilterAudioStream : public AudioStream, AudioStreamCallback { +public: + + /** + * Construct an `AudioStream` using the given `AudioStreamBuilder` and a child AudioStream. + * + * This should only be called internally by AudioStreamBuilder. + * Ownership of childStream will be passed to this object. + * + * @param builder containing all the stream's attributes + */ + FilterAudioStream(const AudioStreamBuilder &builder, AudioStream *childStream) + : AudioStream(builder) + , mChildStream(childStream) { + // Intercept the callback if used. + if (builder.isErrorCallbackSpecified()) { + mErrorCallback = mChildStream->swapErrorCallback(this); + } + if (builder.isDataCallbackSpecified()) { + mDataCallback = mChildStream->swapDataCallback(this); + } else { + const int size = childStream->getFramesPerBurst() * childStream->getBytesPerFrame(); + mBlockingBuffer = std::make_unique(size); + } + + // Copy parameters that may not match builder. + mBufferCapacityInFrames = mChildStream->getBufferCapacityInFrames(); + mPerformanceMode = mChildStream->getPerformanceMode(); + mSharingMode = mChildStream->getSharingMode(); + mInputPreset = mChildStream->getInputPreset(); + mFramesPerBurst = mChildStream->getFramesPerBurst(); + mDeviceId = mChildStream->getDeviceId(); + mHardwareSampleRate = mChildStream->getHardwareSampleRate(); + mHardwareChannelCount = mChildStream->getHardwareChannelCount(); + mHardwareFormat = mChildStream->getHardwareFormat(); + } + + virtual ~FilterAudioStream() = default; + + AudioStream *getChildStream() const { + return mChildStream.get(); + } + + Result configureFlowGraph(); + + // Close child and parent. + Result close() override { + const Result result1 = mChildStream->close(); + const Result result2 = AudioStream::close(); + return (result1 != Result::OK ? result1 : result2); + } + + /** + * Start the stream asynchronously. Returns immediately (does not block). Equivalent to calling + * `start(0)`. + */ + Result requestStart() override { + return mChildStream->requestStart(); + } + + /** + * Pause the stream asynchronously. Returns immediately (does not block). Equivalent to calling + * `pause(0)`. + */ + Result requestPause() override { + return mChildStream->requestPause(); + } + + /** + * Flush the stream asynchronously. Returns immediately (does not block). Equivalent to calling + * `flush(0)`. + */ + Result requestFlush() override { + return mChildStream->requestFlush(); + } + + /** + * Stop the stream asynchronously. Returns immediately (does not block). Equivalent to calling + * `stop(0)`. + */ + Result requestStop() override { + return mChildStream->requestStop(); + } + + ResultWithValue read(void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) override; + + ResultWithValue write(const void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) override; + + StreamState getState() override { + return mChildStream->getState(); + } + + Result waitForStateChange( + StreamState inputState, + StreamState *nextState, + int64_t timeoutNanoseconds) override { + return mChildStream->waitForStateChange(inputState, nextState, timeoutNanoseconds); + } + + bool isXRunCountSupported() const override { + return mChildStream->isXRunCountSupported(); + } + + AudioApi getAudioApi() const override { + return mChildStream->getAudioApi(); + } + + void updateFramesWritten() override { + // TODO for output, just count local writes? + mFramesWritten = static_cast(mChildStream->getFramesWritten() * mRateScaler); + } + + void updateFramesRead() override { + // TODO for input, just count local reads? + mFramesRead = static_cast(mChildStream->getFramesRead() * mRateScaler); + } + + void *getUnderlyingStream() const override { + return mChildStream->getUnderlyingStream(); + } + + ResultWithValue setBufferSizeInFrames(int32_t requestedFrames) override { + return mChildStream->setBufferSizeInFrames(requestedFrames); + } + + int32_t getBufferSizeInFrames() override { + mBufferSizeInFrames = mChildStream->getBufferSizeInFrames(); + return mBufferSizeInFrames; + } + + ResultWithValue getXRunCount() override { + return mChildStream->getXRunCount(); + } + + ResultWithValue calculateLatencyMillis() override { + // This will automatically include the latency of the flowgraph? + return mChildStream->calculateLatencyMillis(); + } + + Result getTimestamp(clockid_t clockId, + int64_t *framePosition, + int64_t *timeNanoseconds) override { + int64_t childPosition = 0; + Result result = mChildStream->getTimestamp(clockId, &childPosition, timeNanoseconds); + // It is OK if framePosition is null. + if (framePosition) { + *framePosition = childPosition * mRateScaler; + } + return result; + } + + DataCallbackResult onAudioReady(AudioStream *oboeStream, + void *audioData, + int32_t numFrames) override; + + bool onError(AudioStream * /*audioStream*/, Result error) override { + if (mErrorCallback != nullptr) { + return mErrorCallback->onError(this, error); + } + return false; + } + + void onErrorBeforeClose(AudioStream * /*oboeStream*/, Result error) override { + if (mErrorCallback != nullptr) { + mErrorCallback->onErrorBeforeClose(this, error); + } + } + + void onErrorAfterClose(AudioStream * /*oboeStream*/, Result error) override { + // Close this parent stream because the callback will only close the child. + AudioStream::close(); + if (mErrorCallback != nullptr) { + mErrorCallback->onErrorAfterClose(this, error); + } + } + + /** + * @return last result passed from an error callback + */ + oboe::Result getLastErrorCallbackResult() const override { + return mChildStream->getLastErrorCallbackResult(); + } + +private: + + std::unique_ptr mChildStream; // this stream wraps the child stream + std::unique_ptr mFlowGraph; // for converting data + std::unique_ptr mBlockingBuffer; // temp buffer for write() + double mRateScaler = 1.0; // ratio parent/child sample rates +}; + +} // oboe + +#endif //OBOE_FILTER_AUDIO_STREAM_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockAdapter.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockAdapter.cpp new file mode 100644 index 00000000..e14c2caa --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockAdapter.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "FixedBlockAdapter.h" + +FixedBlockAdapter::~FixedBlockAdapter() { +} + +int32_t FixedBlockAdapter::open(int32_t bytesPerFixedBlock) +{ + mSize = bytesPerFixedBlock; + mStorage = std::make_unique(bytesPerFixedBlock); + mPosition = 0; + return 0; +} + +int32_t FixedBlockAdapter::close() +{ + mStorage.reset(nullptr); + mSize = 0; + mPosition = 0; + return 0; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockAdapter.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockAdapter.h new file mode 100644 index 00000000..76e961c9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockAdapter.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAUDIO_FIXED_BLOCK_ADAPTER_H +#define AAUDIO_FIXED_BLOCK_ADAPTER_H + +#include +#include +#include + +/** + * Interface for a class that needs fixed-size blocks. + */ +class FixedBlockProcessor { +public: + virtual ~FixedBlockProcessor() = default; + /** + * + * @param buffer Pointer to first byte of data. + * @param numBytes This will be a fixed size specified in FixedBlockAdapter::open(). + * @return Number of bytes processed or a negative error code. + */ + virtual int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) = 0; +}; + +/** + * Base class for a variable-to-fixed-size block adapter. + */ +class FixedBlockAdapter +{ +public: + FixedBlockAdapter(FixedBlockProcessor &fixedBlockProcessor) + : mFixedBlockProcessor(fixedBlockProcessor) {} + + virtual ~FixedBlockAdapter(); + + /** + * Allocate internal resources needed for buffering data. + */ + virtual int32_t open(int32_t bytesPerFixedBlock); + + /** + * Free internal resources. + */ + int32_t close(); + +protected: + FixedBlockProcessor &mFixedBlockProcessor; + std::unique_ptr mStorage; // Store data here while assembling buffers. + int32_t mSize = 0; // Size in bytes of the fixed size buffer. + int32_t mPosition = 0; // Offset of the last byte read or written. +}; + +#endif /* AAUDIO_FIXED_BLOCK_ADAPTER_H */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockReader.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockReader.cpp new file mode 100644 index 00000000..62e9664d --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockReader.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "FixedBlockAdapter.h" + +#include "FixedBlockReader.h" + + +FixedBlockReader::FixedBlockReader(FixedBlockProcessor &fixedBlockProcessor) + : FixedBlockAdapter(fixedBlockProcessor) { + mPosition = mSize; +} + +int32_t FixedBlockReader::open(int32_t bytesPerFixedBlock) { + int32_t result = FixedBlockAdapter::open(bytesPerFixedBlock); + mPosition = 0; + mValid = 0; + return result; +} + +int32_t FixedBlockReader::readFromStorage(uint8_t *buffer, int32_t numBytes) { + int32_t bytesToRead = numBytes; + int32_t dataAvailable = mValid - mPosition; + if (bytesToRead > dataAvailable) { + bytesToRead = dataAvailable; + } + memcpy(buffer, mStorage.get() + mPosition, bytesToRead); + mPosition += bytesToRead; + return bytesToRead; +} + +int32_t FixedBlockReader::read(uint8_t *buffer, int32_t numBytes) { + int32_t bytesRead; + int32_t bytesLeft = numBytes; + while(bytesLeft > 0) { + if (mPosition < mValid) { + // Use up bytes currently in storage. + bytesRead = readFromStorage(buffer, bytesLeft); + buffer += bytesRead; + bytesLeft -= bytesRead; + } else if (bytesLeft >= mSize) { + // Nothing in storage. Read through if enough for a complete block. + bytesRead = mFixedBlockProcessor.onProcessFixedBlock(buffer, mSize); + if (bytesRead < 0) return bytesRead; + buffer += bytesRead; + bytesLeft -= bytesRead; + } else { + // Just need a partial block so we have to reload storage. + bytesRead = mFixedBlockProcessor.onProcessFixedBlock(mStorage.get(), mSize); + if (bytesRead < 0) return bytesRead; + mPosition = 0; + mValid = bytesRead; + if (bytesRead == 0) break; + } + } + return numBytes - bytesLeft; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockReader.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockReader.h new file mode 100644 index 00000000..4cea9c8b --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockReader.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAUDIO_FIXED_BLOCK_READER_H +#define AAUDIO_FIXED_BLOCK_READER_H + +#include + +#include "FixedBlockAdapter.h" + +/** + * Read from a fixed-size block to a variable sized block. + * + * This can be used to convert a pull data flow from fixed sized buffers to variable sized buffers. + * An example would be an audio output callback that reads from the app. + */ +class FixedBlockReader : public FixedBlockAdapter +{ +public: + FixedBlockReader(FixedBlockProcessor &fixedBlockProcessor); + + virtual ~FixedBlockReader() = default; + + int32_t open(int32_t bytesPerFixedBlock) override; + + /** + * Read into a variable sized block. + * + * Note that if the fixed-sized blocks must be aligned, then the variable-sized blocks + * must have the same alignment. + * For example, if the fixed-size blocks must be a multiple of 8, then the variable-sized + * blocks must also be a multiple of 8. + * + * @param buffer + * @param numBytes + * @return Number of bytes read or a negative error code. + */ + int32_t read(uint8_t *buffer, int32_t numBytes); + +private: + int32_t readFromStorage(uint8_t *buffer, int32_t numBytes); + + int32_t mValid = 0; // Number of valid bytes in mStorage. +}; + + +#endif /* AAUDIO_FIXED_BLOCK_READER_H */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockWriter.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockWriter.cpp new file mode 100644 index 00000000..b3156097 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockWriter.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "FixedBlockAdapter.h" +#include "FixedBlockWriter.h" + +FixedBlockWriter::FixedBlockWriter(FixedBlockProcessor &fixedBlockProcessor) + : FixedBlockAdapter(fixedBlockProcessor) {} + + +int32_t FixedBlockWriter::writeToStorage(uint8_t *buffer, int32_t numBytes) { + int32_t bytesToStore = numBytes; + int32_t roomAvailable = mSize - mPosition; + if (bytesToStore > roomAvailable) { + bytesToStore = roomAvailable; + } + memcpy(mStorage.get() + mPosition, buffer, bytesToStore); + mPosition += bytesToStore; + return bytesToStore; +} + +int32_t FixedBlockWriter::write(uint8_t *buffer, int32_t numBytes) { + int32_t bytesLeft = numBytes; + + // If we already have data in storage then add to it. + if (mPosition > 0) { + int32_t bytesWritten = writeToStorage(buffer, bytesLeft); + buffer += bytesWritten; + bytesLeft -= bytesWritten; + // If storage full then flush it out + if (mPosition == mSize) { + bytesWritten = mFixedBlockProcessor.onProcessFixedBlock(mStorage.get(), mSize); + if (bytesWritten < 0) return bytesWritten; + mPosition = 0; + if (bytesWritten < mSize) { + // Only some of the data was written! This should not happen. + return -1; + } + } + } + + // Write through if enough for a complete block. + while(bytesLeft > mSize) { + int32_t bytesWritten = mFixedBlockProcessor.onProcessFixedBlock(buffer, mSize); + if (bytesWritten < 0) return bytesWritten; + buffer += bytesWritten; + bytesLeft -= bytesWritten; + } + + // Save any remaining partial blocks for next time. + if (bytesLeft > 0) { + int32_t bytesWritten = writeToStorage(buffer, bytesLeft); + bytesLeft -= bytesWritten; + } + + return numBytes - bytesLeft; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockWriter.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockWriter.h new file mode 100644 index 00000000..6dea6ca9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/FixedBlockWriter.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAUDIO_FIXED_BLOCK_WRITER_H +#define AAUDIO_FIXED_BLOCK_WRITER_H + +#include + +#include "FixedBlockAdapter.h" + +/** + * This can be used to convert a push data flow from variable sized buffers to fixed sized buffers. + * An example would be an audio input callback. + */ +class FixedBlockWriter : public FixedBlockAdapter +{ +public: + FixedBlockWriter(FixedBlockProcessor &fixedBlockProcessor); + + virtual ~FixedBlockWriter() = default; + + /** + * Write from a variable sized block. + * + * Note that if the fixed-sized blocks must be aligned, then the variable-sized blocks + * must have the same alignment. + * For example, if the fixed-size blocks must be a multiple of 8, then the variable-sized + * blocks must also be a multiple of 8. + * + * @param buffer + * @param numBytes + * @return Number of bytes written or a negative error code. + */ + int32_t write(uint8_t *buffer, int32_t numBytes); + +private: + + int32_t writeToStorage(uint8_t *buffer, int32_t numBytes); +}; + +#endif /* AAUDIO_FIXED_BLOCK_WRITER_H */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/LatencyTuner.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/LatencyTuner.cpp new file mode 100644 index 00000000..0ac29750 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/LatencyTuner.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "oboe/LatencyTuner.h" + +using namespace oboe; + +LatencyTuner::LatencyTuner(AudioStream &stream) + : LatencyTuner(stream, stream.getBufferCapacityInFrames()) { +} + +LatencyTuner::LatencyTuner(oboe::AudioStream &stream, int32_t maximumBufferSize) + : mStream(stream) + , mMaxBufferSize(maximumBufferSize) { + int32_t burstSize = stream.getFramesPerBurst(); + setMinimumBufferSize(kDefaultNumBursts * burstSize); + setBufferSizeIncrement(burstSize); + reset(); +} + +Result LatencyTuner::tune() { + if (mState == State::Unsupported) { + return Result::ErrorUnimplemented; + } + + Result result = Result::OK; + + // Process reset requests. + int32_t numRequests = mLatencyTriggerRequests.load(); + if (numRequests != mLatencyTriggerResponses.load()) { + mLatencyTriggerResponses.store(numRequests); + reset(); + } + + // Set state to Active if the idle countdown has reached zero. + if (mState == State::Idle && --mIdleCountDown <= 0) { + mState = State::Active; + } + + // When state is Active attempt to change the buffer size if the number of xRuns has increased. + if (mState == State::Active) { + + auto xRunCountResult = mStream.getXRunCount(); + if (xRunCountResult == Result::OK) { + if ((xRunCountResult.value() - mPreviousXRuns) > 0) { + mPreviousXRuns = xRunCountResult.value(); + int32_t oldBufferSize = mStream.getBufferSizeInFrames(); + int32_t requestedBufferSize = oldBufferSize + getBufferSizeIncrement(); + + // Do not request more than the maximum buffer size (which was either user-specified + // or was from stream->getBufferCapacityInFrames()) + if (requestedBufferSize > mMaxBufferSize) requestedBufferSize = mMaxBufferSize; + + // Note that this will not allocate more memory. It simply determines + // how much of the existing buffer capacity will be used. The size will be + // clipped to the bufferCapacity by AAudio. + auto setBufferResult = mStream.setBufferSizeInFrames(requestedBufferSize); + if (setBufferResult != Result::OK) { + result = setBufferResult; + mState = State::Unsupported; + } else if (setBufferResult.value() == oldBufferSize) { + mState = State::AtMax; + } + } + } else { + mState = State::Unsupported; + } + } + + if (mState == State::Unsupported) { + result = Result::ErrorUnimplemented; + } + + if (mState == State::AtMax) { + result = Result::OK; + } + return result; +} + +void LatencyTuner::requestReset() { + if (mState != State::Unsupported) { + mLatencyTriggerRequests++; + } +} + +void LatencyTuner::reset() { + mState = State::Idle; + mIdleCountDown = kIdleCount; + // Set to minimal latency + mStream.setBufferSizeInFrames(getMinimumBufferSize()); +} + +bool LatencyTuner::isAtMaximumBufferSize() { + return mState == State::AtMax; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/MonotonicCounter.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/MonotonicCounter.h new file mode 100644 index 00000000..00c979c5 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/MonotonicCounter.h @@ -0,0 +1,112 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMON_MONOTONIC_COUNTER_H +#define COMMON_MONOTONIC_COUNTER_H + +#include + +/** + * Maintain a 64-bit monotonic counter. + * Can be used to track a 32-bit counter that wraps or gets reset. + * + * Note that this is not atomic and has no interior locks. + * A caller will need to provide their own exterior locking + * if they need to use it from multiple threads. + */ +class MonotonicCounter { + +public: + MonotonicCounter() {} + virtual ~MonotonicCounter() {} + + /** + * @return current value of the counter + */ + int64_t get() const { + return mCounter64; + } + + /** + * set the current value of the counter + */ + void set(int64_t counter) { + mCounter64 = counter; + } + + /** + * Advance the counter if delta is positive. + * @return current value of the counter + */ + int64_t increment(int64_t delta) { + if (delta > 0) { + mCounter64 += delta; + } + return mCounter64; + } + + /** + * Advance the 64-bit counter if (current32 - previousCurrent32) > 0. + * This can be used to convert a 32-bit counter that may be wrapping into + * a monotonic 64-bit counter. + * + * This counter32 should NOT be allowed to advance by more than 0x7FFFFFFF between calls. + * Think of the wrapping counter like a sine wave. If the frequency of the signal + * is more than half the sampling rate (Nyquist rate) then you cannot measure it properly. + * If the counter wraps around every 24 hours then we should measure it with a period + * of less than 12 hours. + * + * @return current value of the 64-bit counter + */ + int64_t update32(int32_t counter32) { + int32_t delta = counter32 - mCounter32; + // protect against the mCounter64 going backwards + if (delta > 0) { + mCounter64 += delta; + mCounter32 = counter32; + } + return mCounter64; + } + + /** + * Reset the stored value of the 32-bit counter. + * This is used if your counter32 has been reset to zero. + */ + void reset32() { + mCounter32 = 0; + } + + /** + * Round 64-bit counter up to a multiple of the period. + * + * The period must be positive. + * + * @param period might be, for example, a buffer capacity + */ + void roundUp64(int32_t period) { + if (period > 0) { + int64_t numPeriods = (mCounter64 + period - 1) / period; + mCounter64 = numPeriods * period; + } + } + +private: + int64_t mCounter64 = 0; + int32_t mCounter32 = 0; +}; + + +#endif //COMMON_MONOTONIC_COUNTER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/OboeDebug.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/OboeDebug.h new file mode 100644 index 00000000..dc7434c8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/OboeDebug.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef OBOE_DEBUG_H +#define OBOE_DEBUG_H + +#include + +#ifndef MODULE_NAME +#define MODULE_NAME "OboeAudio" +#endif + +// Always log INFO and errors. +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, MODULE_NAME, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, MODULE_NAME, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, __VA_ARGS__) +#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, MODULE_NAME, __VA_ARGS__) + +#if OBOE_ENABLE_LOGGING +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, __VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, MODULE_NAME, __VA_ARGS__) +#else +#define LOGV(...) +#define LOGD(...) +#endif + +#endif //OBOE_DEBUG_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/OboeExtensions.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/OboeExtensions.cpp new file mode 100644 index 00000000..57731e89 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/OboeExtensions.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "oboe/OboeExtensions.h" +#include "aaudio/AAudioExtensions.h" + +using namespace oboe; + +bool OboeExtensions::isMMapSupported(){ + return AAudioExtensions::getInstance().isMMapSupported(); +} + +bool OboeExtensions::isMMapEnabled(){ + return AAudioExtensions::getInstance().isMMapEnabled(); +} + +int32_t OboeExtensions::setMMapEnabled(bool enabled){ + return AAudioExtensions::getInstance().setMMapEnabled(enabled); +} + +bool OboeExtensions::isMMapUsed(oboe::AudioStream *oboeStream){ + return AAudioExtensions::getInstance().isMMapUsed(oboeStream); +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp new file mode 100644 index 00000000..f9890be5 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.cpp @@ -0,0 +1,311 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "OboeDebug.h" +#include "QuirksManager.h" + +using namespace oboe; + +int32_t QuirksManager::DeviceQuirks::clipBufferSize(AudioStream &stream, + int32_t requestedSize) { + if (!OboeGlobals::areWorkaroundsEnabled()) { + return requestedSize; + } + int bottomMargin = kDefaultBottomMarginInBursts; + int topMargin = kDefaultTopMarginInBursts; + if (isMMapUsed(stream)) { + if (stream.getSharingMode() == SharingMode::Exclusive) { + bottomMargin = getExclusiveBottomMarginInBursts(); + topMargin = getExclusiveTopMarginInBursts(); + } + } else { + bottomMargin = kLegacyBottomMarginInBursts; + } + + int32_t burst = stream.getFramesPerBurst(); + int32_t minSize = bottomMargin * burst; + int32_t adjustedSize = requestedSize; + if (adjustedSize < minSize ) { + adjustedSize = minSize; + } else { + int32_t maxSize = stream.getBufferCapacityInFrames() - (topMargin * burst); + if (adjustedSize > maxSize ) { + adjustedSize = maxSize; + } + } + return adjustedSize; +} + +bool QuirksManager::DeviceQuirks::isAAudioMMapPossible(const AudioStreamBuilder &builder) const { + bool isSampleRateCompatible = + builder.getSampleRate() == oboe::Unspecified + || builder.getSampleRate() == kCommonNativeRate + || builder.getSampleRateConversionQuality() != SampleRateConversionQuality::None; + return builder.getPerformanceMode() == PerformanceMode::LowLatency + && isSampleRateCompatible + && builder.getChannelCount() <= kChannelCountStereo; +} + +bool QuirksManager::DeviceQuirks::shouldConvertFloatToI16ForOutputStreams() { + std::string productManufacturer = getPropertyString("ro.product.manufacturer"); + if (getSdkVersion() < __ANDROID_API_L__) { + return true; + } else if ((productManufacturer == "vivo") && (getSdkVersion() < __ANDROID_API_M__)) { + return true; + } + return false; +} + +/** + * This is for Samsung Exynos quirks. Samsung Mobile uses Qualcomm chips so + * the QualcommDeviceQuirks would apply. + */ +class SamsungExynosDeviceQuirks : public QuirksManager::DeviceQuirks { +public: + SamsungExynosDeviceQuirks() { + std::string chipname = getPropertyString("ro.hardware.chipname"); + isExynos9810 = (chipname == "exynos9810"); + isExynos990 = (chipname == "exynos990"); + isExynos850 = (chipname == "exynos850"); + + mBuildChangelist = getPropertyInteger("ro.build.changelist", 0); + } + + virtual ~SamsungExynosDeviceQuirks() = default; + + int32_t getExclusiveBottomMarginInBursts() const override { + return kBottomMargin; + } + + int32_t getExclusiveTopMarginInBursts() const override { + return kTopMargin; + } + + // See Oboe issues #824 and #1247 for more information. + bool isMonoMMapActuallyStereo() const override { + return isExynos9810 || isExynos850; // TODO We can make this version specific if it gets fixed. + } + + bool isAAudioMMapPossible(const AudioStreamBuilder &builder) const override { + return DeviceQuirks::isAAudioMMapPossible(builder) + // Samsung says they use Legacy for Camcorder + && builder.getInputPreset() != oboe::InputPreset::Camcorder; + } + + bool isMMapSafe(const AudioStreamBuilder &builder) override { + const bool isInput = builder.getDirection() == Direction::Input; + // This detects b/159066712 , S20 LSI has corrupt low latency audio recording + // and turns off MMAP. + // See also https://github.com/google/oboe/issues/892 + bool isRecordingCorrupted = isInput + && isExynos990 + && mBuildChangelist < 19350896; + + // Certain S9+ builds record silence when using MMAP and not using the VoiceCommunication + // preset. + // See https://github.com/google/oboe/issues/1110 + bool wouldRecordSilence = isInput + && isExynos9810 + && mBuildChangelist <= 18847185 + && (builder.getInputPreset() != InputPreset::VoiceCommunication); + + if (wouldRecordSilence){ + LOGI("QuirksManager::%s() Requested stream configuration would result in silence on " + "this device. Switching off MMAP.", __func__); + } + + return !isRecordingCorrupted && !wouldRecordSilence; + } + +private: + // Stay farther away from DSP position on Exynos devices. + static constexpr int32_t kBottomMargin = 2; + static constexpr int32_t kTopMargin = 1; + bool isExynos9810 = false; + bool isExynos990 = false; + bool isExynos850 = false; + int mBuildChangelist = 0; +}; + +class QualcommDeviceQuirks : public QuirksManager::DeviceQuirks { +public: + QualcommDeviceQuirks() { + std::string modelName = getPropertyString("ro.soc.model"); + isSM8150 = (modelName == "SDM8150"); + } + + virtual ~QualcommDeviceQuirks() = default; + + int32_t getExclusiveBottomMarginInBursts() const override { + return kBottomMargin; + } + + bool isMMapSafe(const AudioStreamBuilder &builder) override { + // See https://github.com/google/oboe/issues/1121#issuecomment-897957749 + bool isMMapBroken = false; + if (isSM8150 && (getSdkVersion() <= __ANDROID_API_P__)) { + LOGI("QuirksManager::%s() MMAP not actually supported on this chip." + " Switching off MMAP.", __func__); + isMMapBroken = true; + } + + return !isMMapBroken; + } + +private: + bool isSM8150 = false; + static constexpr int32_t kBottomMargin = 1; +}; + +QuirksManager::QuirksManager() { + std::string productManufacturer = getPropertyString("ro.product.manufacturer"); + if (productManufacturer == "samsung") { + std::string arch = getPropertyString("ro.arch"); + bool isExynos = (arch.rfind("exynos", 0) == 0); // starts with? + if (isExynos) { + mDeviceQuirks = std::make_unique(); + } + } + if (!mDeviceQuirks) { + std::string socManufacturer = getPropertyString("ro.soc.manufacturer"); + if (socManufacturer == "Qualcomm") { + // This may include Samsung Mobile devices. + mDeviceQuirks = std::make_unique(); + } else { + mDeviceQuirks = std::make_unique(); + } + } +} + +bool QuirksManager::isConversionNeeded( + const AudioStreamBuilder &builder, + AudioStreamBuilder &childBuilder) { + bool conversionNeeded = false; + const bool isLowLatency = builder.getPerformanceMode() == PerformanceMode::LowLatency; + const bool isInput = builder.getDirection() == Direction::Input; + const bool isFloat = builder.getFormat() == AudioFormat::Float; + const bool isIEC61937 = builder.getFormat() == AudioFormat::IEC61937; + + // There should be no conversion for IEC61937. Sample rates and channel counts must be set explicitly. + if (isIEC61937) { + LOGI("QuirksManager::%s() conversion not needed for IEC61937", __func__); + return false; + } + + // There are multiple bugs involving using callback with a specified callback size. + // Issue #778: O to Q had a problem with Legacy INPUT streams for FLOAT streams + // and a specified callback size. It would assert because of a bad buffer size. + // + // Issue #973: O to R had a problem with Legacy output streams using callback and a specified callback size. + // An AudioTrack stream could still be running when the AAudio FixedBlockReader was closed. + // Internally b/161914201#comment25 + // + // Issue #983: O to R would glitch if the framesPerCallback was too small. + // + // Most of these problems were related to Legacy stream. MMAP was OK. But we don't + // know if we will get an MMAP stream. So, to be safe, just do the conversion in Oboe. + if (OboeGlobals::areWorkaroundsEnabled() + && builder.willUseAAudio() + && builder.isDataCallbackSpecified() + && builder.getFramesPerDataCallback() != 0 + && getSdkVersion() <= __ANDROID_API_R__) { + LOGI("QuirksManager::%s() avoid setFramesPerCallback(n>0)", __func__); + childBuilder.setFramesPerCallback(oboe::Unspecified); + conversionNeeded = true; + } + + // If a SAMPLE RATE is specified for low latency, let the native code choose an optimal rate. + // This isn't really a workaround. It is an Oboe feature that is convenient to place here. + // TODO There may be a problem if the devices supports low latency + // at a higher rate than the default. + if (builder.getSampleRate() != oboe::Unspecified + && builder.getSampleRateConversionQuality() != SampleRateConversionQuality::None + && isLowLatency + ) { + childBuilder.setSampleRate(oboe::Unspecified); // native API decides the best sample rate + conversionNeeded = true; + } + + // Data Format + // OpenSL ES and AAudio before P do not support FAST path for FLOAT capture. + if (OboeGlobals::areWorkaroundsEnabled() + && isFloat + && isInput + && builder.isFormatConversionAllowed() + && isLowLatency + && (!builder.willUseAAudio() || (getSdkVersion() < __ANDROID_API_P__)) + ) { + childBuilder.setFormat(AudioFormat::I16); // needed for FAST track + conversionNeeded = true; + LOGI("QuirksManager::%s() forcing internal format to I16 for low latency", __func__); + } + + // Add quirk for float output when needed. + if (OboeGlobals::areWorkaroundsEnabled() + && isFloat + && !isInput + && builder.isFormatConversionAllowed() + && mDeviceQuirks->shouldConvertFloatToI16ForOutputStreams() + ) { + childBuilder.setFormat(AudioFormat::I16); + conversionNeeded = true; + LOGI("QuirksManager::%s() float was requested but not supported on pre-L devices " + "and some devices like Vivo devices may have issues on L devices, " + "creating an underlying I16 stream and using format conversion to provide a float " + "stream", __func__); + } + + // Channel Count conversions + if (OboeGlobals::areWorkaroundsEnabled() + && builder.isChannelConversionAllowed() + && builder.getChannelCount() == kChannelCountStereo + && isInput + && isLowLatency + && (!builder.willUseAAudio() && (getSdkVersion() == __ANDROID_API_O__)) + ) { + // Workaround for heap size regression in O. + // b/66967812 AudioRecord does not allow FAST track for stereo capture in O + childBuilder.setChannelCount(kChannelCountMono); + conversionNeeded = true; + LOGI("QuirksManager::%s() using mono internally for low latency on O", __func__); + } else if (OboeGlobals::areWorkaroundsEnabled() + && builder.getChannelCount() == kChannelCountMono + && isInput + && mDeviceQuirks->isMonoMMapActuallyStereo() + && builder.willUseAAudio() + // Note: we might use this workaround on a device that supports + // MMAP but will use Legacy for this stream. But this will only happen + // on devices that have the broken mono. + && mDeviceQuirks->isAAudioMMapPossible(builder) + ) { + // Workaround for mono actually running in stereo mode. + childBuilder.setChannelCount(kChannelCountStereo); // Use stereo and extract first channel. + conversionNeeded = true; + LOGI("QuirksManager::%s() using stereo internally to avoid broken mono", __func__); + } + // Note that MMAP does not support mono in 8.1. But that would only matter on Pixel 1 + // phones and they have almost all been updated to 9.0. + + return conversionNeeded; +} + +bool QuirksManager::isMMapSafe(AudioStreamBuilder &builder) { + if (!OboeGlobals::areWorkaroundsEnabled()) return true; + return mDeviceQuirks->isMMapSafe(builder); +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h new file mode 100644 index 00000000..c24e9749 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/QuirksManager.h @@ -0,0 +1,134 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_QUIRKS_MANAGER_H +#define OBOE_QUIRKS_MANAGER_H + +#include +#include +#include + +#ifndef __ANDROID_API_R__ +#define __ANDROID_API_R__ 30 +#endif + +namespace oboe { + +/** + * INTERNAL USE ONLY. + * + * Based on manufacturer, model and Android version number + * decide whether data conversion needs to occur. + * + * This also manages device and version specific workarounds. + */ + +class QuirksManager { +public: + + static QuirksManager &getInstance() { + static QuirksManager instance; // singleton + return instance; + } + + QuirksManager(); + virtual ~QuirksManager() = default; + + /** + * Do we need to do channel, format or rate conversion to provide a low latency + * stream for this builder? If so then provide a builder for the native child stream + * that will be used to get low latency. + * + * @param builder builder provided by application + * @param childBuilder modified builder appropriate for the underlying device + * @return true if conversion is needed + */ + bool isConversionNeeded(const AudioStreamBuilder &builder, AudioStreamBuilder &childBuilder); + + static bool isMMapUsed(AudioStream &stream) { + bool answer = false; + if (stream.getAudioApi() == AudioApi::AAudio) { + AudioStreamAAudio *streamAAudio = + reinterpret_cast(&stream); + answer = streamAAudio->isMMapUsed(); + } + return answer; + } + + virtual int32_t clipBufferSize(AudioStream &stream, int32_t bufferSize) { + return mDeviceQuirks->clipBufferSize(stream, bufferSize); + } + + class DeviceQuirks { + public: + virtual ~DeviceQuirks() = default; + + /** + * Restrict buffer size. This is mainly to avoid glitches caused by MMAP + * timestamp inaccuracies. + * @param stream + * @param requestedSize + * @return + */ + int32_t clipBufferSize(AudioStream &stream, int32_t requestedSize); + + // Exclusive MMAP streams can have glitches because they are using a timing + // model of the DSP to control IO instead of direct synchronization. + virtual int32_t getExclusiveBottomMarginInBursts() const { + return kDefaultBottomMarginInBursts; + } + + virtual int32_t getExclusiveTopMarginInBursts() const { + return kDefaultTopMarginInBursts; + } + + // On some devices, you can open a mono stream but it is actually running in stereo! + virtual bool isMonoMMapActuallyStereo() const { + return false; + } + + virtual bool isAAudioMMapPossible(const AudioStreamBuilder &builder) const; + + virtual bool isMMapSafe(const AudioStreamBuilder & /* builder */ ) { + return true; + } + + // On some devices, Float does not work so it should be converted to I16. + static bool shouldConvertFloatToI16ForOutputStreams(); + + static constexpr int32_t kDefaultBottomMarginInBursts = 0; + static constexpr int32_t kDefaultTopMarginInBursts = 0; + + // For Legacy streams, do not let the buffer go below one burst. + // b/129545119 | AAudio Legacy allows setBufferSizeInFrames too low + // Fixed in Q + static constexpr int32_t kLegacyBottomMarginInBursts = 1; + static constexpr int32_t kCommonNativeRate = 48000; // very typical native sample rate + }; + + bool isMMapSafe(AudioStreamBuilder &builder); + +private: + + static constexpr int32_t kChannelCountMono = 1; + static constexpr int32_t kChannelCountStereo = 2; + + std::unique_ptr mDeviceQuirks{}; + +}; + +} +#endif //OBOE_QUIRKS_MANAGER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/README.md b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/README.md new file mode 100644 index 00000000..d3974588 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/README.md @@ -0,0 +1,33 @@ +# Notes on Implementation + +## Latency from Resampling + +There are two components of the latency. The resampler itself, and a buffer that +is used to adapt the block sizes. + +1) The resampler is an FIR running at the target sample rate. So its latency is the number of taps. +From MultiChannelResampler.cpp, numTaps is + + Fastest: 2 + Low: 4 + Medium: 8 + High: 16 + Best: 32 + +For output, the device sampling rate is used, which is typically 48000.For input, the app sampling rate is used. + +2) There is a block size adapter that collects odd sized blocks into larger blocks of the correct size. + +The adapter contains one burst of frames, from getFramesPerBurst(). But if the app specifies a +particular size using setFramesPerCallback() then that size will be used. +Here is some pseudo-code to calculate the latency. + + latencyMillis = 0 + targetRate = isOutput ? deviceRate : applicationRate + // Add latency from FIR + latencyMillis += numTaps * 1000.0 / targetRate + // Add latency from block size adaptation + adapterSize = (callbackSize > 0) ? callbackSize : burstSize + if (isOutput && isCallbackUsed) latencyMillis += adapterSize * 1000.0 / deviceRate + else if (isInput && isCallbackUsed) latencyMillis += adapterSize * 1000.0 / applicationRate + else if (isInput && !isCallbackUsed) latencyMillis += adapterSize * 1000.0 / deviceRate diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceFloatCaller.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceFloatCaller.cpp new file mode 100644 index 00000000..631af853 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceFloatCaller.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "flowgraph/FlowGraphNode.h" +#include "SourceFloatCaller.h" + +using namespace oboe; +using namespace flowgraph; + +int32_t SourceFloatCaller::onProcess(int32_t numFrames) { + int32_t numBytes = mStream->getBytesPerFrame() * numFrames; + int32_t bytesRead = mBlockReader.read((uint8_t *) output.getBuffer(), numBytes); + int32_t framesRead = bytesRead / mStream->getBytesPerFrame(); + return framesRead; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceFloatCaller.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceFloatCaller.h new file mode 100644 index 00000000..85a15855 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceFloatCaller.h @@ -0,0 +1,44 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_SOURCE_FLOAT_CALLER_H +#define OBOE_SOURCE_FLOAT_CALLER_H + +#include +#include + +#include "flowgraph/FlowGraphNode.h" +#include "AudioSourceCaller.h" +#include "FixedBlockReader.h" + +namespace oboe { +/** + * AudioSource that uses callback to get more float data. + */ +class SourceFloatCaller : public AudioSourceCaller { +public: + SourceFloatCaller(int32_t channelCount, int32_t framesPerCallback) + : AudioSourceCaller(channelCount, framesPerCallback, (int32_t)sizeof(float)) {} + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceFloatCaller"; + } +}; + +} +#endif //OBOE_SOURCE_FLOAT_CALLER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI16Caller.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI16Caller.cpp new file mode 100644 index 00000000..2ab372b6 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI16Caller.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "flowgraph/FlowGraphNode.h" +#include "SourceI16Caller.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace oboe; +using namespace flowgraph; + +int32_t SourceI16Caller::onProcess(int32_t numFrames) { + int32_t numBytes = mStream->getBytesPerFrame() * numFrames; + int32_t bytesRead = mBlockReader.read((uint8_t *) mConversionBuffer.get(), numBytes); + int32_t framesRead = bytesRead / mStream->getBytesPerFrame(); + + float *floatData = output.getBuffer(); + const int16_t *shortData = mConversionBuffer.get(); + int32_t numSamples = framesRead * output.getSamplesPerFrame(); + +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_float_from_i16(floatData, shortData, numSamples); +#else + for (int i = 0; i < numSamples; i++) { + *floatData++ = *shortData++ * (1.0f / 32768); + } +#endif + + return framesRead; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI16Caller.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI16Caller.h new file mode 100644 index 00000000..02a57c22 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI16Caller.h @@ -0,0 +1,49 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_SOURCE_I16_CALLER_H +#define OBOE_SOURCE_I16_CALLER_H + +#include +#include + +#include "flowgraph/FlowGraphNode.h" +#include "AudioSourceCaller.h" +#include "FixedBlockReader.h" + +namespace oboe { +/** + * AudioSource that uses callback to get more data. + */ +class SourceI16Caller : public AudioSourceCaller { +public: + SourceI16Caller(int32_t channelCount, int32_t framesPerCallback) + : AudioSourceCaller(channelCount, framesPerCallback, sizeof(int16_t)) { + mConversionBuffer = std::make_unique(static_cast(channelCount) + * static_cast(output.getFramesPerBuffer())); + } + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI16Caller"; + } +private: + std::unique_ptr mConversionBuffer; +}; + +} +#endif //OBOE_SOURCE_I16_CALLER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.cpp new file mode 100644 index 00000000..2e44c3d2 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "flowgraph/FlowGraphNode.h" +#include "SourceI24Caller.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace oboe; +using namespace flowgraph; + +int32_t SourceI24Caller::onProcess(int32_t numFrames) { + int32_t numBytes = mStream->getBytesPerFrame() * numFrames; + int32_t bytesRead = mBlockReader.read((uint8_t *) mConversionBuffer.get(), numBytes); + int32_t framesRead = bytesRead / mStream->getBytesPerFrame(); + + float *floatData = output.getBuffer(); + const uint8_t *byteData = mConversionBuffer.get(); + int32_t numSamples = framesRead * output.getSamplesPerFrame(); + +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_float_from_p24(floatData, byteData, numSamples); +#else + static const float scale = 1. / (float)(1UL << 31); + for (int i = 0; i < numSamples; i++) { + // Assemble the data assuming Little Endian format. + int32_t pad = byteData[2]; + pad <<= 8; + pad |= byteData[1]; + pad <<= 8; + pad |= byteData[0]; + pad <<= 8; // Shift to 32 bit data so the sign is correct. + byteData += kBytesPerI24Packed; + *floatData++ = pad * scale; // scale to range -1.0 to 1.0 + } +#endif + + return framesRead; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.h new file mode 100644 index 00000000..3b5eab72 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI24Caller.h @@ -0,0 +1,53 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_SOURCE_I24_CALLER_H +#define OBOE_SOURCE_I24_CALLER_H + +#include +#include + +#include "flowgraph/FlowGraphNode.h" +#include "AudioSourceCaller.h" +#include "FixedBlockReader.h" + +namespace oboe { + +/** + * AudioSource that uses callback to get more data. + */ +class SourceI24Caller : public AudioSourceCaller { +public: + SourceI24Caller(int32_t channelCount, int32_t framesPerCallback) + : AudioSourceCaller(channelCount, framesPerCallback, kBytesPerI24Packed) { + mConversionBuffer = std::make_unique(static_cast(kBytesPerI24Packed) + * static_cast(channelCount) + * static_cast(output.getFramesPerBuffer())); + } + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI24Caller"; + } + +private: + std::unique_ptr mConversionBuffer; + static constexpr int kBytesPerI24Packed = 3; +}; + +} +#endif //OBOE_SOURCE_I16_CALLER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.cpp new file mode 100644 index 00000000..188bd47c --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "flowgraph/FlowGraphNode.h" +#include "SourceI32Caller.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace oboe; +using namespace flowgraph; + +int32_t SourceI32Caller::onProcess(int32_t numFrames) { + int32_t numBytes = mStream->getBytesPerFrame() * numFrames; + int32_t bytesRead = mBlockReader.read((uint8_t *) mConversionBuffer.get(), numBytes); + int32_t framesRead = bytesRead / mStream->getBytesPerFrame(); + + float *floatData = output.getBuffer(); + const int32_t *intData = mConversionBuffer.get(); + int32_t numSamples = framesRead * output.getSamplesPerFrame(); + +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_float_from_i32(floatData, shortData, numSamples); +#else + for (int i = 0; i < numSamples; i++) { + *floatData++ = *intData++ * kScale; + } +#endif + + return framesRead; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.h new file mode 100644 index 00000000..18c3343a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/SourceI32Caller.h @@ -0,0 +1,53 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_SOURCE_I32_CALLER_H +#define OBOE_SOURCE_I32_CALLER_H + +#include +#include +#include + +#include "flowgraph/FlowGraphNode.h" +#include "AudioSourceCaller.h" +#include "FixedBlockReader.h" + +namespace oboe { + +/** + * AudioSource that uses callback to get more data. + */ +class SourceI32Caller : public AudioSourceCaller { +public: + SourceI32Caller(int32_t channelCount, int32_t framesPerCallback) + : AudioSourceCaller(channelCount, framesPerCallback, sizeof(int32_t)) { + mConversionBuffer = std::make_unique(static_cast(channelCount) + * static_cast(output.getFramesPerBuffer())); + } + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI32Caller"; + } + +private: + std::unique_ptr mConversionBuffer; + static constexpr float kScale = 1.0 / (1UL << 31); +}; + +} +#endif //OBOE_SOURCE_I32_CALLER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/StabilizedCallback.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/StabilizedCallback.cpp new file mode 100644 index 00000000..a2ac5495 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/StabilizedCallback.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "oboe/StabilizedCallback.h" +#include "common/AudioClock.h" +#include "common/Trace.h" + +constexpr int32_t kLoadGenerationStepSizeNanos = 20000; +constexpr float kPercentageOfCallbackToUse = 0.8; + +using namespace oboe; + +StabilizedCallback::StabilizedCallback(AudioStreamCallback *callback) : mCallback(callback){ + Trace::initialize(); +} + +/** + * An audio callback which attempts to do work for a fixed amount of time. + * + * @param oboeStream + * @param audioData + * @param numFrames + * @return + */ +DataCallbackResult +StabilizedCallback::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) { + + int64_t startTimeNanos = AudioClock::getNanoseconds(); + + if (mFrameCount == 0){ + mEpochTimeNanos = startTimeNanos; + } + + int64_t durationSinceEpochNanos = startTimeNanos - mEpochTimeNanos; + + // In an ideal world the callback start time will be exactly the same as the duration of the + // frames already read/written into the stream. In reality the callback can start early + // or late. By finding the delta we can calculate the target duration for our stabilized + // callback. + int64_t idealStartTimeNanos = (mFrameCount * kNanosPerSecond) / oboeStream->getSampleRate(); + int64_t lateStartNanos = durationSinceEpochNanos - idealStartTimeNanos; + + if (lateStartNanos < 0){ + // This was an early start which indicates that our previous epoch was a late callback. + // Update our epoch to this more accurate time. + mEpochTimeNanos = startTimeNanos; + mFrameCount = 0; + } + + int64_t numFramesAsNanos = (numFrames * kNanosPerSecond) / oboeStream->getSampleRate(); + int64_t targetDurationNanos = static_cast( + (numFramesAsNanos * kPercentageOfCallbackToUse) - lateStartNanos); + + Trace::beginSection("Actual load"); + DataCallbackResult result = mCallback->onAudioReady(oboeStream, audioData, numFrames); + Trace::endSection(); + + int64_t executionDurationNanos = AudioClock::getNanoseconds() - startTimeNanos; + int64_t stabilizingLoadDurationNanos = targetDurationNanos - executionDurationNanos; + + Trace::beginSection("Stabilized load for %lldns", stabilizingLoadDurationNanos); + generateLoad(stabilizingLoadDurationNanos); + Trace::endSection(); + + // Wraparound: At 48000 frames per second mFrameCount wraparound will occur after 6m years, + // significantly longer than the average lifetime of an Android phone. + mFrameCount += numFrames; + return result; +} + +void StabilizedCallback::generateLoad(int64_t durationNanos) { + + int64_t currentTimeNanos = AudioClock::getNanoseconds(); + int64_t deadlineTimeNanos = currentTimeNanos + durationNanos; + + // opsPerStep gives us an estimated number of operations which need to be run to fully utilize + // the CPU for a fixed amount of time (specified by kLoadGenerationStepSizeNanos). + // After each step the opsPerStep value is re-calculated based on the actual time taken to + // execute those operations. + auto opsPerStep = (int)(mOpsPerNano * kLoadGenerationStepSizeNanos); + int64_t stepDurationNanos = 0; + int64_t previousTimeNanos = 0; + + while (currentTimeNanos <= deadlineTimeNanos){ + + for (int i = 0; i < opsPerStep; i++) cpu_relax(); + + previousTimeNanos = currentTimeNanos; + currentTimeNanos = AudioClock::getNanoseconds(); + stepDurationNanos = currentTimeNanos - previousTimeNanos; + + // Calculate exponential moving average to smooth out values, this acts as a low pass filter. + // @see https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average + static const float kFilterCoefficient = 0.1; + auto measuredOpsPerNano = (double) opsPerStep / stepDurationNanos; + mOpsPerNano = kFilterCoefficient * measuredOpsPerNano + (1.0 - kFilterCoefficient) * mOpsPerNano; + opsPerStep = (int) (mOpsPerNano * kLoadGenerationStepSizeNanos); + } +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Trace.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Trace.cpp new file mode 100644 index 00000000..f08f36dc --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Trace.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "Trace.h" +#include "OboeDebug.h" + +static char buffer[256]; + +// Tracing functions +static void *(*ATrace_beginSection)(const char *sectionName); + +static void *(*ATrace_endSection)(); + +typedef void *(*fp_ATrace_beginSection)(const char *sectionName); + +typedef void *(*fp_ATrace_endSection)(); + +bool Trace::mIsTracingSupported = false; + +void Trace::beginSection(const char *format, ...){ + + if (mIsTracingSupported) { + va_list va; + va_start(va, format); + vsprintf(buffer, format, va); + ATrace_beginSection(buffer); + va_end(va); + } else { + LOGE("Tracing is either not initialized (call Trace::initialize()) " + "or not supported on this device"); + } +} + +void Trace::endSection() { + + if (mIsTracingSupported) { + ATrace_endSection(); + } +} + +void Trace::initialize() { + + // Using dlsym allows us to use tracing on API 21+ without needing android/trace.h which wasn't + // published until API 23 + void *lib = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL); + if (lib == nullptr) { + LOGE("Could not open libandroid.so to dynamically load tracing symbols"); + } else { + ATrace_beginSection = + reinterpret_cast( + dlsym(lib, "ATrace_beginSection")); + ATrace_endSection = + reinterpret_cast( + dlsym(lib, "ATrace_endSection")); + + if (ATrace_beginSection != nullptr && ATrace_endSection != nullptr){ + mIsTracingSupported = true; + } + } +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Trace.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Trace.h new file mode 100644 index 00000000..dad6c007 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Trace.h @@ -0,0 +1,31 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_TRACE_H +#define OBOE_TRACE_H + +class Trace { + +public: + static void beginSection(const char *format, ...); + static void endSection(); + static void initialize(); + +private: + static bool mIsTracingSupported; +}; + +#endif //OBOE_TRACE_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Utilities.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Utilities.cpp new file mode 100644 index 00000000..f6718afc --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Utilities.cpp @@ -0,0 +1,333 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#ifdef __ANDROID__ +#include +#endif + +#include +#include "oboe/Definitions.h" +#include "oboe/Utilities.h" + +namespace oboe { + +constexpr float kScaleI16ToFloat = (1.0f / 32768.0f); + +void convertFloatToPcm16(const float *source, int16_t *destination, int32_t numSamples) { + for (int i = 0; i < numSamples; i++) { + float fval = source[i]; + fval += 1.0; // to avoid discontinuity at 0.0 caused by truncation + fval *= 32768.0f; + auto sample = static_cast(fval); + // clip to 16-bit range + if (sample < 0) sample = 0; + else if (sample > 0x0FFFF) sample = 0x0FFFF; + sample -= 32768; // center at zero + destination[i] = static_cast(sample); + } +} + +void convertPcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples) { + for (int i = 0; i < numSamples; i++) { + destination[i] = source[i] * kScaleI16ToFloat; + } +} + +int32_t convertFormatToSizeInBytes(AudioFormat format) { + int32_t size = 0; + switch (format) { + case AudioFormat::I16: + size = sizeof(int16_t); + break; + case AudioFormat::Float: + size = sizeof(float); + break; + case AudioFormat::I24: + size = 3; // packed 24-bit data + break; + case AudioFormat::I32: + size = sizeof(int32_t); + break; + case AudioFormat::IEC61937: + size = sizeof(int16_t); + break; + default: + break; + } + return size; +} + +template<> +const char *convertToText(Result returnCode) { + switch (returnCode) { + case Result::OK: return "OK"; + case Result::ErrorDisconnected: return "ErrorDisconnected"; + case Result::ErrorIllegalArgument: return "ErrorIllegalArgument"; + case Result::ErrorInternal: return "ErrorInternal"; + case Result::ErrorInvalidState: return "ErrorInvalidState"; + case Result::ErrorInvalidHandle: return "ErrorInvalidHandle"; + case Result::ErrorUnimplemented: return "ErrorUnimplemented"; + case Result::ErrorUnavailable: return "ErrorUnavailable"; + case Result::ErrorNoFreeHandles: return "ErrorNoFreeHandles"; + case Result::ErrorNoMemory: return "ErrorNoMemory"; + case Result::ErrorNull: return "ErrorNull"; + case Result::ErrorTimeout: return "ErrorTimeout"; + case Result::ErrorWouldBlock: return "ErrorWouldBlock"; + case Result::ErrorInvalidFormat: return "ErrorInvalidFormat"; + case Result::ErrorOutOfRange: return "ErrorOutOfRange"; + case Result::ErrorNoService: return "ErrorNoService"; + case Result::ErrorInvalidRate: return "ErrorInvalidRate"; + case Result::ErrorClosed: return "ErrorClosed"; + default: return "Unrecognized result"; + } +} + +template<> +const char *convertToText(AudioFormat format) { + switch (format) { + case AudioFormat::Invalid: return "Invalid"; + case AudioFormat::Unspecified: return "Unspecified"; + case AudioFormat::I16: return "I16"; + case AudioFormat::Float: return "Float"; + case AudioFormat::I24: return "I24"; + case AudioFormat::I32: return "I32"; + case AudioFormat::IEC61937: return "IEC61937"; + default: return "Unrecognized format"; + } +} + +template<> +const char *convertToText(PerformanceMode mode) { + switch (mode) { + case PerformanceMode::LowLatency: return "LowLatency"; + case PerformanceMode::None: return "None"; + case PerformanceMode::PowerSaving: return "PowerSaving"; + default: return "Unrecognized performance mode"; + } +} + +template<> +const char *convertToText(SharingMode mode) { + switch (mode) { + case SharingMode::Exclusive: return "Exclusive"; + case SharingMode::Shared: return "Shared"; + default: return "Unrecognized sharing mode"; + } +} + +template<> +const char *convertToText(DataCallbackResult result) { + switch (result) { + case DataCallbackResult::Continue: return "Continue"; + case DataCallbackResult::Stop: return "Stop"; + default: return "Unrecognized data callback result"; + } +} + +template<> +const char *convertToText(Direction direction) { + switch (direction) { + case Direction::Input: return "Input"; + case Direction::Output: return "Output"; + default: return "Unrecognized direction"; + } +} + +template<> +const char *convertToText(StreamState state) { + switch (state) { + case StreamState::Closed: return "Closed"; + case StreamState::Closing: return "Closing"; + case StreamState::Disconnected: return "Disconnected"; + case StreamState::Flushed: return "Flushed"; + case StreamState::Flushing: return "Flushing"; + case StreamState::Open: return "Open"; + case StreamState::Paused: return "Paused"; + case StreamState::Pausing: return "Pausing"; + case StreamState::Started: return "Started"; + case StreamState::Starting: return "Starting"; + case StreamState::Stopped: return "Stopped"; + case StreamState::Stopping: return "Stopping"; + case StreamState::Uninitialized: return "Uninitialized"; + case StreamState::Unknown: return "Unknown"; + default: return "Unrecognized stream state"; + } +} + +template<> +const char *convertToText(AudioApi audioApi) { + + switch (audioApi) { + case AudioApi::Unspecified: return "Unspecified"; + case AudioApi::OpenSLES: return "OpenSLES"; + case AudioApi::AAudio: return "AAudio"; + default: return "Unrecognized audio API"; + } +} + +template<> +const char *convertToText(AudioStream* stream) { + static std::string streamText; + std::stringstream s; + + s<<"StreamID: "<< static_cast(stream)<getDeviceId()<getDirection())<getAudioApi())<getBufferCapacityInFrames()<getBufferSizeInFrames()<getFramesPerBurst()<getFramesPerDataCallback()<getSampleRate()<getChannelCount()<getFormat())<getSharingMode())<getPerformanceMode()) + <getState())<getXRunCount()<getFramesRead()<getFramesWritten()< +const char *convertToText(Usage usage) { + + switch (usage) { + case Usage::Media: return "Media"; + case Usage::VoiceCommunication: return "VoiceCommunication"; + case Usage::VoiceCommunicationSignalling: return "VoiceCommunicationSignalling"; + case Usage::Alarm: return "Alarm"; + case Usage::Notification: return "Notification"; + case Usage::NotificationRingtone: return "NotificationRingtone"; + case Usage::NotificationEvent: return "NotificationEvent"; + case Usage::AssistanceAccessibility: return "AssistanceAccessibility"; + case Usage::AssistanceNavigationGuidance: return "AssistanceNavigationGuidance"; + case Usage::AssistanceSonification: return "AssistanceSonification"; + case Usage::Game: return "Game"; + case Usage::Assistant: return "Assistant"; + default: return "Unrecognized usage"; + } +} + +template<> +const char *convertToText(ContentType contentType) { + + switch (contentType) { + case ContentType::Speech: return "Speech"; + case ContentType::Music: return "Music"; + case ContentType::Movie: return "Movie"; + case ContentType::Sonification: return "Sonification"; + default: return "Unrecognized content type"; + } +} + +template<> +const char *convertToText(InputPreset inputPreset) { + + switch (inputPreset) { + case InputPreset::Generic: return "Generic"; + case InputPreset::Camcorder: return "Camcorder"; + case InputPreset::VoiceRecognition: return "VoiceRecognition"; + case InputPreset::VoiceCommunication: return "VoiceCommunication"; + case InputPreset::Unprocessed: return "Unprocessed"; + case InputPreset::VoicePerformance: return "VoicePerformance"; + default: return "Unrecognized input preset"; + } +} + +template<> +const char *convertToText(SessionId sessionId) { + + switch (sessionId) { + case SessionId::None: return "None"; + case SessionId::Allocate: return "Allocate"; + default: return "Unrecognized session id"; + } +} + +template<> +const char *convertToText(ChannelCount channelCount) { + + switch (channelCount) { + case ChannelCount::Unspecified: return "Unspecified"; + case ChannelCount::Mono: return "Mono"; + case ChannelCount::Stereo: return "Stereo"; + default: return "Unrecognized channel count"; + } +} + +std::string getPropertyString(const char * name) { + std::string result; +#ifdef __ANDROID__ + char valueText[PROP_VALUE_MAX] = {0}; + if (__system_property_get(name, valueText) != 0) { + result = valueText; + } +#else + (void) name; +#endif + return result; +} + +int getPropertyInteger(const char * name, int defaultValue) { + int result = defaultValue; +#ifdef __ANDROID__ + char valueText[PROP_VALUE_MAX] = {0}; + if (__system_property_get(name, valueText) != 0) { + result = atoi(valueText); + } +#else + (void) name; +#endif + return result; +} + +int getSdkVersion() { + static int sCachedSdkVersion = -1; +#ifdef __ANDROID__ + if (sCachedSdkVersion == -1) { + sCachedSdkVersion = getPropertyInteger("ro.build.version.sdk", -1); + } +#endif + return sCachedSdkVersion; +} + +bool isAtLeastPreReleaseCodename(const std::string& codename) { + std::string buildCodename = getPropertyString("ro.build.version.codename"); + // Special case "REL", which means the build is not a pre-release build. + if ("REL" == buildCodename) { + return false; + } + + // Otherwise lexically compare them. Return true if the build codename is equal to or + // greater than the requested codename. + return buildCodename.compare(codename) >= 0; +} + +int getChannelCountFromChannelMask(ChannelMask channelMask) { + return __builtin_popcount(static_cast(channelMask)); +} + +}// namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Version.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Version.cpp new file mode 100644 index 00000000..481bee76 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/common/Version.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "oboe/Version.h" + +namespace oboe { + + // This variable enables the version information to be read from the resulting binary e.g. + // by running `objdump -s --section=.data ` + // Please do not optimize or change in any way. + char kVersionText[] = "OboeVersion" OBOE_VERSION_TEXT; + + const char * getVersionText(){ + return kVersionText; + } +} // namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoBuffer.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoBuffer.cpp new file mode 100644 index 00000000..c33315d0 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoBuffer.cpp @@ -0,0 +1,178 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "oboe/FifoControllerBase.h" +#include "fifo/FifoController.h" +#include "fifo/FifoControllerIndirect.h" +#include "oboe/FifoBuffer.h" + +namespace oboe { + +FifoBuffer::FifoBuffer(uint32_t bytesPerFrame, uint32_t capacityInFrames) + : mBytesPerFrame(bytesPerFrame) + , mStorage(nullptr) + , mFramesReadCount(0) + , mFramesUnderrunCount(0) +{ + mFifo = std::make_unique(capacityInFrames); + // allocate buffer + int32_t bytesPerBuffer = bytesPerFrame * capacityInFrames; + mStorage = new uint8_t[bytesPerBuffer]; + mStorageOwned = true; +} + +FifoBuffer::FifoBuffer( uint32_t bytesPerFrame, + uint32_t capacityInFrames, + std::atomic *readCounterAddress, + std::atomic *writeCounterAddress, + uint8_t *dataStorageAddress + ) + : mBytesPerFrame(bytesPerFrame) + , mStorage(dataStorageAddress) + , mFramesReadCount(0) + , mFramesUnderrunCount(0) +{ + mFifo = std::make_unique(capacityInFrames, + readCounterAddress, + writeCounterAddress); + mStorage = dataStorageAddress; + mStorageOwned = false; +} + +FifoBuffer::~FifoBuffer() { + if (mStorageOwned) { + delete[] mStorage; + } +} + +int32_t FifoBuffer::convertFramesToBytes(int32_t frames) { + return frames * mBytesPerFrame; +} + +int32_t FifoBuffer::read(void *buffer, int32_t numFrames) { + if (numFrames <= 0) { + return 0; + } + // safe because numFrames is guaranteed positive + uint32_t framesToRead = static_cast(numFrames); + uint32_t framesAvailable = mFifo->getFullFramesAvailable(); + framesToRead = std::min(framesToRead, framesAvailable); + + uint32_t readIndex = mFifo->getReadIndex(); // ranges 0 to capacity + uint8_t *destination = reinterpret_cast(buffer); + uint8_t *source = &mStorage[convertFramesToBytes(readIndex)]; + if ((readIndex + framesToRead) > mFifo->getFrameCapacity()) { + // read in two parts, first part here is at the end of the mStorage buffer + int32_t frames1 = static_cast(mFifo->getFrameCapacity() - readIndex); + int32_t numBytes = convertFramesToBytes(frames1); + if (numBytes < 0) { + return static_cast(Result::ErrorOutOfRange); + } + memcpy(destination, source, static_cast(numBytes)); + destination += numBytes; + // read second part, which is at the beginning of mStorage + source = &mStorage[0]; + int32_t frames2 = static_cast(framesToRead - frames1); + numBytes = convertFramesToBytes(frames2); + if (numBytes < 0) { + return static_cast(Result::ErrorOutOfRange); + } + memcpy(destination, source, static_cast(numBytes)); + } else { + // just read in one shot + int32_t numBytes = convertFramesToBytes(framesToRead); + if (numBytes < 0) { + return static_cast(Result::ErrorOutOfRange); + } + memcpy(destination, source, static_cast(numBytes)); + } + mFifo->advanceReadIndex(framesToRead); + + return framesToRead; +} + +int32_t FifoBuffer::write(const void *buffer, int32_t numFrames) { + if (numFrames <= 0) { + return 0; + } + // Guaranteed positive. + uint32_t framesToWrite = static_cast(numFrames); + uint32_t framesAvailable = mFifo->getEmptyFramesAvailable(); + framesToWrite = std::min(framesToWrite, framesAvailable); + + uint32_t writeIndex = mFifo->getWriteIndex(); + int byteIndex = convertFramesToBytes(writeIndex); + const uint8_t *source = reinterpret_cast(buffer); + uint8_t *destination = &mStorage[byteIndex]; + if ((writeIndex + framesToWrite) > mFifo->getFrameCapacity()) { + // write in two parts, first part here + int32_t frames1 = static_cast(mFifo->getFrameCapacity() - writeIndex); + int32_t numBytes = convertFramesToBytes(frames1); + if (numBytes < 0) { + return static_cast(Result::ErrorOutOfRange); + } + memcpy(destination, source, static_cast(numBytes)); + // read second part + source += convertFramesToBytes(frames1); + destination = &mStorage[0]; + int frames2 = static_cast(framesToWrite - frames1); + numBytes = convertFramesToBytes(frames2); + if (numBytes < 0) { + return static_cast(Result::ErrorOutOfRange); + } + memcpy(destination, source, static_cast(numBytes)); + } else { + // just write in one shot + int32_t numBytes = convertFramesToBytes(framesToWrite); + if (numBytes < 0) { + return static_cast(Result::ErrorOutOfRange); + } + memcpy(destination, source, static_cast(numBytes)); + } + mFifo->advanceWriteIndex(framesToWrite); + + return framesToWrite; +} + +int32_t FifoBuffer::readNow(void *buffer, int32_t numFrames) { + int32_t framesRead = read(buffer, numFrames); + if (framesRead < 0) { + return framesRead; + } + int32_t framesLeft = numFrames - framesRead; + mFramesReadCount += framesRead; + mFramesUnderrunCount += framesLeft; + // Zero out any samples we could not set. + if (framesLeft > 0) { + uint8_t *destination = reinterpret_cast(buffer); + destination += convertFramesToBytes(framesRead); // point to first byte not set + int32_t bytesToZero = convertFramesToBytes(framesLeft); + memset(destination, 0, static_cast(bytesToZero)); + } + + return framesRead; +} + + +uint32_t FifoBuffer::getBufferCapacityInFrames() const { + return mFifo->getFrameCapacity(); +} + +} // namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoController.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoController.cpp new file mode 100644 index 00000000..aa0d5b7d --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoController.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "FifoController.h" + +namespace oboe { + +FifoController::FifoController(uint32_t numFrames) + : FifoControllerBase(numFrames) +{ + setReadCounter(0); + setWriteCounter(0); +} + +} // namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoController.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoController.h new file mode 100644 index 00000000..b3e2d079 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoController.h @@ -0,0 +1,62 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVEOBOE_FIFOCONTROLLER_H +#define NATIVEOBOE_FIFOCONTROLLER_H + +#include +#include + +#include "oboe/FifoControllerBase.h" + +namespace oboe { + +/** + * A FifoControllerBase with counters contained in the class. + */ +class FifoController : public FifoControllerBase +{ +public: + FifoController(uint32_t bufferSize); + virtual ~FifoController() = default; + + virtual uint64_t getReadCounter() const override { + return mReadCounter.load(std::memory_order_acquire); + } + virtual void setReadCounter(uint64_t n) override { + mReadCounter.store(n, std::memory_order_release); + } + virtual void incrementReadCounter(uint64_t n) override { + mReadCounter.fetch_add(n, std::memory_order_acq_rel); + } + virtual uint64_t getWriteCounter() const override { + return mWriteCounter.load(std::memory_order_acquire); + } + virtual void setWriteCounter(uint64_t n) override { + mWriteCounter.store(n, std::memory_order_release); + } + virtual void incrementWriteCounter(uint64_t n) override { + mWriteCounter.fetch_add(n, std::memory_order_acq_rel); + } + +private: + std::atomic mReadCounter{}; + std::atomic mWriteCounter{}; +}; + +} // namespace oboe + +#endif //NATIVEOBOE_FIFOCONTROLLER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoControllerBase.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoControllerBase.cpp new file mode 100644 index 00000000..dc65b87d --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoControllerBase.cpp @@ -0,0 +1,68 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "oboe/FifoControllerBase.h" + +namespace oboe { + +FifoControllerBase::FifoControllerBase(uint32_t capacityInFrames) + : mTotalFrames(capacityInFrames) +{ + // Avoid ridiculously large buffers and the arithmetic wraparound issues that can follow. + assert(capacityInFrames <= (UINT32_MAX / 4)); +} + +uint32_t FifoControllerBase::getFullFramesAvailable() const { + uint64_t writeCounter = getWriteCounter(); + uint64_t readCounter = getReadCounter(); + if (readCounter > writeCounter) { + return 0; + } + uint64_t delta = writeCounter - readCounter; + if (delta >= mTotalFrames) { + return mTotalFrames; + } + // delta is now guaranteed to fit within the range of a uint32_t + return static_cast(delta); +} + +uint32_t FifoControllerBase::getReadIndex() const { + // % works with non-power of two sizes + return static_cast(getReadCounter() % mTotalFrames); +} + +void FifoControllerBase::advanceReadIndex(uint32_t numFrames) { + incrementReadCounter(numFrames); +} + +uint32_t FifoControllerBase::getEmptyFramesAvailable() const { + return static_cast(mTotalFrames - getFullFramesAvailable()); +} + +uint32_t FifoControllerBase::getWriteIndex() const { + // % works with non-power of two sizes + return static_cast(getWriteCounter() % mTotalFrames); +} + +void FifoControllerBase::advanceWriteIndex(uint32_t numFrames) { + incrementWriteCounter(numFrames); +} + +} // namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoControllerIndirect.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoControllerIndirect.cpp new file mode 100644 index 00000000..78c43399 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoControllerIndirect.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "FifoControllerIndirect.h" + +namespace oboe { + +FifoControllerIndirect::FifoControllerIndirect(uint32_t numFrames, + std::atomic *readCounterAddress, + std::atomic *writeCounterAddress) + : FifoControllerBase(numFrames) + , mReadCounterAddress(readCounterAddress) + , mWriteCounterAddress(writeCounterAddress) +{ +} + +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoControllerIndirect.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoControllerIndirect.h new file mode 100644 index 00000000..c25c6074 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/fifo/FifoControllerIndirect.h @@ -0,0 +1,66 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVEOBOE_FIFOCONTROLLERINDIRECT_H +#define NATIVEOBOE_FIFOCONTROLLERINDIRECT_H + +#include +#include + +#include "oboe/FifoControllerBase.h" + +namespace oboe { + +/** + * A FifoControllerBase with counters external to the class. + */ +class FifoControllerIndirect : public FifoControllerBase { + +public: + FifoControllerIndirect(uint32_t bufferSize, + std::atomic *readCounterAddress, + std::atomic *writeCounterAddress); + virtual ~FifoControllerIndirect() = default; + + virtual uint64_t getReadCounter() const override { + return mReadCounterAddress->load(std::memory_order_acquire); + } + virtual void setReadCounter(uint64_t n) override { + mReadCounterAddress->store(n, std::memory_order_release); + } + virtual void incrementReadCounter(uint64_t n) override { + mReadCounterAddress->fetch_add(n, std::memory_order_acq_rel); + } + virtual uint64_t getWriteCounter() const override { + return mWriteCounterAddress->load(std::memory_order_acquire); + } + virtual void setWriteCounter(uint64_t n) override { + mWriteCounterAddress->store(n, std::memory_order_release); + } + virtual void incrementWriteCounter(uint64_t n) override { + mWriteCounterAddress->fetch_add(n, std::memory_order_acq_rel); + } + +private: + + std::atomic *mReadCounterAddress; + std::atomic *mWriteCounterAddress; + +}; + +} // namespace oboe + +#endif //NATIVEOBOE_FIFOCONTROLLERINDIRECT_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ChannelCountConverter.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ChannelCountConverter.cpp new file mode 100644 index 00000000..dc804277 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ChannelCountConverter.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "FlowGraphNode.h" +#include "ChannelCountConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +ChannelCountConverter::ChannelCountConverter( + int32_t inputChannelCount, + int32_t outputChannelCount) + : input(*this, inputChannelCount) + , output(*this, outputChannelCount) { +} + +ChannelCountConverter::~ChannelCountConverter() = default; + +int32_t ChannelCountConverter::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + int32_t inputChannelCount = input.getSamplesPerFrame(); + int32_t outputChannelCount = output.getSamplesPerFrame(); + for (int i = 0; i < numFrames; i++) { + int inputChannel = 0; + for (int outputChannel = 0; outputChannel < outputChannelCount; outputChannel++) { + // Copy input channels to output channels. + // Wrap if we run out of inputs. + // Discard if we run out of outputs. + outputBuffer[outputChannel] = inputBuffer[inputChannel]; + inputChannel = (inputChannel == inputChannelCount) + ? 0 : inputChannel + 1; + } + inputBuffer += inputChannelCount; + outputBuffer += outputChannelCount; + } + return numFrames; +} + diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ChannelCountConverter.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ChannelCountConverter.h new file mode 100644 index 00000000..858f4d4f --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ChannelCountConverter.h @@ -0,0 +1,52 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_CHANNEL_COUNT_CONVERTER_H +#define FLOWGRAPH_CHANNEL_COUNT_CONVERTER_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Change the number of number of channels without mixing. + * When increasing the channel count, duplicate input channels. + * When decreasing the channel count, drop input channels. + */ + class ChannelCountConverter : public FlowGraphNode { + public: + explicit ChannelCountConverter( + int32_t inputChannelCount, + int32_t outputChannelCount); + + virtual ~ChannelCountConverter(); + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "ChannelCountConverter"; + } + + FlowGraphPortFloatInput input; + FlowGraphPortFloatOutput output; + }; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_CHANNEL_COUNT_CONVERTER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ClipToRange.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ClipToRange.cpp new file mode 100644 index 00000000..c6ad0b0f --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ClipToRange.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "FlowGraphNode.h" +#include "ClipToRange.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +ClipToRange::ClipToRange(int32_t channelCount) + : FlowGraphFilter(channelCount) { +} + +int32_t ClipToRange::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + + int32_t numSamples = numFrames * output.getSamplesPerFrame(); + for (int32_t i = 0; i < numSamples; i++) { + *outputBuffer++ = std::min(mMaximum, std::max(mMinimum, *inputBuffer++)); + } + + return numFrames; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ClipToRange.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ClipToRange.h new file mode 100644 index 00000000..2fddeee7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ClipToRange.h @@ -0,0 +1,68 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_CLIP_TO_RANGE_H +#define FLOWGRAPH_CLIP_TO_RANGE_H + +#include +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data. +// It is designed to allow occasional transient peaks. +constexpr float kDefaultMaxHeadroom = 1.41253754f; +constexpr float kDefaultMinHeadroom = -kDefaultMaxHeadroom; + +class ClipToRange : public FlowGraphFilter { +public: + explicit ClipToRange(int32_t channelCount); + + virtual ~ClipToRange() = default; + + int32_t onProcess(int32_t numFrames) override; + + void setMinimum(float min) { + mMinimum = min; + } + + float getMinimum() const { + return mMinimum; + } + + void setMaximum(float min) { + mMaximum = min; + } + + float getMaximum() const { + return mMaximum; + } + + const char *getName() override { + return "ClipToRange"; + } + +private: + float mMinimum = kDefaultMinHeadroom; + float mMaximum = kDefaultMaxHeadroom; +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_CLIP_TO_RANGE_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowGraphNode.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowGraphNode.cpp new file mode 100644 index 00000000..012abe7e --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowGraphNode.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdio.h" +#include +#include +#include "FlowGraphNode.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +/***************************************************************************/ +int32_t FlowGraphNode::pullData(int32_t numFrames, int64_t callCount) { + int32_t frameCount = numFrames; + // Prevent recursion and multiple execution of nodes. + if (callCount > mLastCallCount) { + mLastCallCount = callCount; + if (mDataPulledAutomatically) { + // Pull from all the upstream nodes. + for (auto &port : mInputPorts) { + // TODO fix bug of leaving unused data in some ports if using multiple AudioSource + frameCount = port.get().pullData(callCount, frameCount); + } + } + if (frameCount > 0) { + frameCount = onProcess(frameCount); + } + mLastFrameCount = frameCount; + } else { + frameCount = mLastFrameCount; + } + return frameCount; +} + +void FlowGraphNode::pullReset() { + if (!mBlockRecursion) { + mBlockRecursion = true; // for cyclic graphs + // Pull reset from all the upstream nodes. + for (auto &port : mInputPorts) { + port.get().pullReset(); + } + mBlockRecursion = false; + reset(); + } +} + +void FlowGraphNode::reset() { + mLastFrameCount = 0; + mLastCallCount = kInitialCallCount; +} + +/***************************************************************************/ +FlowGraphPortFloat::FlowGraphPortFloat(FlowGraphNode &parent, + int32_t samplesPerFrame, + int32_t framesPerBuffer) + : FlowGraphPort(parent, samplesPerFrame) + , mFramesPerBuffer(framesPerBuffer) + , mBuffer(nullptr) { + size_t numFloats = static_cast(framesPerBuffer) * getSamplesPerFrame(); + mBuffer = std::make_unique(numFloats); +} + +/***************************************************************************/ +int32_t FlowGraphPortFloatOutput::pullData(int64_t callCount, int32_t numFrames) { + numFrames = std::min(getFramesPerBuffer(), numFrames); + return mContainingNode.pullData(numFrames, callCount); +} + +void FlowGraphPortFloatOutput::pullReset() { + mContainingNode.pullReset(); +} + +// These need to be in the .cpp file because of forward cross references. +void FlowGraphPortFloatOutput::connect(FlowGraphPortFloatInput *port) { + port->connect(this); +} + +void FlowGraphPortFloatOutput::disconnect(FlowGraphPortFloatInput *port) { + port->disconnect(this); +} + +/***************************************************************************/ +int32_t FlowGraphPortFloatInput::pullData(int64_t callCount, int32_t numFrames) { + return (mConnected == nullptr) + ? std::min(getFramesPerBuffer(), numFrames) + : mConnected->pullData(callCount, numFrames); +} +void FlowGraphPortFloatInput::pullReset() { + if (mConnected != nullptr) mConnected->pullReset(); +} + +float *FlowGraphPortFloatInput::getBuffer() { + if (mConnected == nullptr) { + return FlowGraphPortFloat::getBuffer(); // loaded using setValue() + } else { + return mConnected->getBuffer(); + } +} + +int32_t FlowGraphSink::pullData(int32_t numFrames) { + return FlowGraphNode::pullData(numFrames, getLastCallCount() + 1); +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowGraphNode.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowGraphNode.h new file mode 100644 index 00000000..2884c081 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowGraphNode.h @@ -0,0 +1,450 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * FlowGraph.h + * + * Processing node and ports that can be used in a simple data flow graph. + * This was designed to work with audio but could be used for other + * types of data. + */ + +#ifndef FLOWGRAPH_FLOW_GRAPH_NODE_H +#define FLOWGRAPH_FLOW_GRAPH_NODE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO Move these classes into separate files. +// TODO Review use of raw pointers for connect(). Maybe use smart pointers but need to avoid +// run-time deallocation in audio thread. + +// Set flags FLOWGRAPH_ANDROID_INTERNAL and FLOWGRAPH_OUTER_NAMESPACE based on whether compiler +// flag __ANDROID_NDK__ is defined. __ANDROID_NDK__ should be defined in oboe and not aaudio. + +#ifndef FLOWGRAPH_ANDROID_INTERNAL +#ifdef __ANDROID_NDK__ +#define FLOWGRAPH_ANDROID_INTERNAL 0 +#else +#define FLOWGRAPH_ANDROID_INTERNAL 1 +#endif // __ANDROID_NDK__ +#endif // FLOWGRAPH_ANDROID_INTERNAL + +#ifndef FLOWGRAPH_OUTER_NAMESPACE +#ifdef __ANDROID_NDK__ +#define FLOWGRAPH_OUTER_NAMESPACE oboe +#else +#define FLOWGRAPH_OUTER_NAMESPACE aaudio +#endif // __ANDROID_NDK__ +#endif // FLOWGRAPH_OUTER_NAMESPACE + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +// Default block size that can be overridden when the FlowGraphPortFloat is created. +// If it is too small then we will have too much overhead from switching between nodes. +// If it is too high then we will thrash the caches. +constexpr int kDefaultBufferSize = 8; // arbitrary + +class FlowGraphPort; +class FlowGraphPortFloatInput; + +/***************************************************************************/ +/** + * Base class for all nodes in the flowgraph. + */ +class FlowGraphNode { +public: + FlowGraphNode() = default; + virtual ~FlowGraphNode() = default; + + /** + * Read from the input ports, + * generate multiple frames of data then write the results to the output ports. + * + * @param numFrames maximum number of frames requested for processing + * @return number of frames actually processed + */ + virtual int32_t onProcess(int32_t numFrames) = 0; + + /** + * If the callCount is at or after the previous callCount then call + * pullData on all of the upstreamNodes. + * Then call onProcess(). + * This prevents infinite recursion in case of cyclic graphs. + * It also prevents nodes upstream from a branch from being executed twice. + * + * @param callCount + * @param numFrames + * @return number of frames valid + */ + int32_t pullData(int32_t numFrames, int64_t callCount); + + /** + * Recursively reset all the nodes in the graph, starting from a Sink. + * + * This must not be called at the same time as pullData! + */ + void pullReset(); + + /** + * Reset framePosition counters. + */ + virtual void reset(); + + void addInputPort(FlowGraphPort &port) { + mInputPorts.emplace_back(port); + } + + bool isDataPulledAutomatically() const { + return mDataPulledAutomatically; + } + + /** + * Set true if you want the data pulled through the graph automatically. + * This is the default. + * + * Set false if you want to pull the data from the input ports in the onProcess() method. + * You might do this, for example, in a sample rate converting node. + * + * @param automatic + */ + void setDataPulledAutomatically(bool automatic) { + mDataPulledAutomatically = automatic; + } + + virtual const char *getName() { + return "FlowGraph"; + } + + int64_t getLastCallCount() { + return mLastCallCount; + } + +protected: + + static constexpr int64_t kInitialCallCount = -1; + int64_t mLastCallCount = kInitialCallCount; + + std::vector> mInputPorts; + +private: + bool mDataPulledAutomatically = true; + bool mBlockRecursion = false; + int32_t mLastFrameCount = 0; + +}; + +/***************************************************************************/ +/** + * This is a connector that allows data to flow between modules. + * + * The ports are the primary means of interacting with a module. + * So they are generally declared as public. + * + */ +class FlowGraphPort { +public: + FlowGraphPort(FlowGraphNode &parent, int32_t samplesPerFrame) + : mContainingNode(parent) + , mSamplesPerFrame(samplesPerFrame) { + } + + virtual ~FlowGraphPort() = default; + + // Ports are often declared public. So let's make them non-copyable. + FlowGraphPort(const FlowGraphPort&) = delete; + FlowGraphPort& operator=(const FlowGraphPort&) = delete; + + int32_t getSamplesPerFrame() const { + return mSamplesPerFrame; + } + + virtual int32_t pullData(int64_t framePosition, int32_t numFrames) = 0; + + virtual void pullReset() {} + +protected: + FlowGraphNode &mContainingNode; + +private: + const int32_t mSamplesPerFrame = 1; +}; + +/***************************************************************************/ +/** + * This port contains a 32-bit float buffer that can contain several frames of data. + * Processing the data in a block improves performance. + * + * The size is framesPerBuffer * samplesPerFrame). + */ +class FlowGraphPortFloat : public FlowGraphPort { +public: + FlowGraphPortFloat(FlowGraphNode &parent, + int32_t samplesPerFrame, + int32_t framesPerBuffer = kDefaultBufferSize + ); + + virtual ~FlowGraphPortFloat() = default; + + int32_t getFramesPerBuffer() const { + return mFramesPerBuffer; + } + +protected: + + /** + * @return buffer internal to the port or from a connected port + */ + virtual float *getBuffer() { + return mBuffer.get(); + } + +private: + const int32_t mFramesPerBuffer = 1; + std::unique_ptr mBuffer; // allocated in constructor +}; + +/***************************************************************************/ +/** + * The results of a node's processing are stored in the buffers of the output ports. + */ +class FlowGraphPortFloatOutput : public FlowGraphPortFloat { +public: + FlowGraphPortFloatOutput(FlowGraphNode &parent, int32_t samplesPerFrame) + : FlowGraphPortFloat(parent, samplesPerFrame) { + } + + virtual ~FlowGraphPortFloatOutput() = default; + + using FlowGraphPortFloat::getBuffer; + + /** + * Connect to the input of another module. + * An input port can only have one connection. + * An output port can have multiple connections. + * If you connect a second output port to an input port + * then it overwrites the previous connection. + * + * This not thread safe. Do not modify the graph topology from another thread while running. + * Also do not delete a module while it is connected to another port if the graph is running. + */ + void connect(FlowGraphPortFloatInput *port); + + /** + * Disconnect from the input of another module. + * This not thread safe. + */ + void disconnect(FlowGraphPortFloatInput *port); + + /** + * Call the parent module's onProcess() method. + * That may pull data from its inputs and recursively + * process the entire graph. + * @return number of frames actually pulled + */ + int32_t pullData(int64_t framePosition, int32_t numFrames) override; + + + void pullReset() override; + +}; + +/***************************************************************************/ + +/** + * An input port for streaming audio data. + * You can set a value that will be used for processing. + * If you connect an output port to this port then its value will be used instead. + */ +class FlowGraphPortFloatInput : public FlowGraphPortFloat { +public: + FlowGraphPortFloatInput(FlowGraphNode &parent, int32_t samplesPerFrame) + : FlowGraphPortFloat(parent, samplesPerFrame) { + // Add to parent so it can pull data from each input. + parent.addInputPort(*this); + } + + virtual ~FlowGraphPortFloatInput() = default; + + /** + * If connected to an output port then this will return + * that output ports buffers. + * If not connected then it returns the input ports own buffer + * which can be loaded using setValue(). + */ + float *getBuffer() override; + + /** + * Write every value of the float buffer. + * This value will be ignored if an output port is connected + * to this port. + */ + void setValue(float value) { + int numFloats = kDefaultBufferSize * getSamplesPerFrame(); + float *buffer = getBuffer(); + for (int i = 0; i < numFloats; i++) { + *buffer++ = value; + } + } + + /** + * Connect to the output of another module. + * An input port can only have one connection. + * An output port can have multiple connections. + * This not thread safe. + */ + void connect(FlowGraphPortFloatOutput *port) { + assert(getSamplesPerFrame() == port->getSamplesPerFrame()); + mConnected = port; + } + + void disconnect(FlowGraphPortFloatOutput *port) { + assert(mConnected == port); + (void) port; + mConnected = nullptr; + } + + void disconnect() { + mConnected = nullptr; + } + + /** + * Pull data from any output port that is connected. + */ + int32_t pullData(int64_t framePosition, int32_t numFrames) override; + + void pullReset() override; + +private: + FlowGraphPortFloatOutput *mConnected = nullptr; +}; + +/***************************************************************************/ + +/** + * Base class for an edge node in a graph that has no upstream nodes. + * It outputs data but does not consume data. + * By default, it will read its data from an external buffer. + */ +class FlowGraphSource : public FlowGraphNode { +public: + explicit FlowGraphSource(int32_t channelCount) + : output(*this, channelCount) { + } + + virtual ~FlowGraphSource() = default; + + FlowGraphPortFloatOutput output; +}; + +/***************************************************************************/ + +/** + * Base class for an edge node in a graph that has no upstream nodes. + * It outputs data but does not consume data. + * By default, it will read its data from an external buffer. + */ +class FlowGraphSourceBuffered : public FlowGraphSource { +public: + explicit FlowGraphSourceBuffered(int32_t channelCount) + : FlowGraphSource(channelCount) {} + + virtual ~FlowGraphSourceBuffered() = default; + + /** + * Specify buffer that the node will read from. + * + * @param data TODO Consider using std::shared_ptr. + * @param numFrames + */ + void setData(const void *data, int32_t numFrames) { + mData = data; + mSizeInFrames = numFrames; + mFrameIndex = 0; + } + +protected: + const void *mData = nullptr; + int32_t mSizeInFrames = 0; // number of frames in mData + int32_t mFrameIndex = 0; // index of next frame to be processed +}; + +/***************************************************************************/ +/** + * Base class for an edge node in a graph that has no downstream nodes. + * It consumes data but does not output data. + * This graph will be executed when data is read() from this node + * by pulling data from upstream nodes. + */ +class FlowGraphSink : public FlowGraphNode { +public: + explicit FlowGraphSink(int32_t channelCount) + : input(*this, channelCount) { + } + + virtual ~FlowGraphSink() = default; + + FlowGraphPortFloatInput input; + + /** + * Do nothing. The work happens in the read() method. + * + * @param numFrames + * @return number of frames actually processed + */ + int32_t onProcess(int32_t numFrames) override { + return numFrames; + } + + virtual int32_t read(void *data, int32_t numFrames) = 0; + +protected: + /** + * Pull data through the graph using this nodes last callCount. + * @param numFrames + * @return + */ + int32_t pullData(int32_t numFrames); +}; + +/***************************************************************************/ +/** + * Base class for a node that has an input and an output with the same number of channels. + * This may include traditional filters, eg. FIR, but also include + * any processing node that converts input to output. + */ +class FlowGraphFilter : public FlowGraphNode { +public: + explicit FlowGraphFilter(int32_t channelCount) + : input(*this, channelCount) + , output(*this, channelCount) { + } + + virtual ~FlowGraphFilter() = default; + + FlowGraphPortFloatInput input; + FlowGraphPortFloatOutput output; +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif /* FLOWGRAPH_FLOW_GRAPH_NODE_H */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowgraphUtilities.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowgraphUtilities.h new file mode 100644 index 00000000..5e90588c --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/FlowgraphUtilities.h @@ -0,0 +1,55 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_UTILITIES_H +#define FLOWGRAPH_UTILITIES_H + +#include + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +class FlowgraphUtilities { +public: +// This was copied from audio_utils/primitives.h +/** + * Convert a single-precision floating point value to a Q0.31 integer value. + * Rounds to nearest, ties away from 0. + * + * Values outside the range [-1.0, 1.0) are properly clamped to -2147483648 and 2147483647, + * including -Inf and +Inf. NaN values are considered undefined, and behavior may change + * depending on hardware and future implementation of this function. + */ +static int32_t clamp32FromFloat(float f) +{ + static const float scale = (float)(1UL << 31); + static const float limpos = 1.; + static const float limneg = -1.; + + if (f <= limneg) { + return INT32_MIN; + } else if (f >= limpos) { + return INT32_MAX; + } + f *= scale; + /* integer conversion is through truncation (though int to float is not). + * ensure that we round to nearest, ties away from 0. + */ + return f > 0 ? f + 0.5 : f - 0.5; +} + +}; + +#endif // FLOWGRAPH_UTILITIES_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/Limiter.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/Limiter.cpp new file mode 100644 index 00000000..def905a7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/Limiter.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "FlowGraphNode.h" +#include "Limiter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +Limiter::Limiter(int32_t channelCount) + : FlowGraphFilter(channelCount) { +} + +int32_t Limiter::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + + int32_t numSamples = numFrames * output.getSamplesPerFrame(); + + // Cache the last valid output to reduce memory read/write + float lastValidOutput = mLastValidOutput; + + for (int32_t i = 0; i < numSamples; i++) { + // Use the previous output if the input is NaN + if (!isnan(*inputBuffer)) { + lastValidOutput = processFloat(*inputBuffer); + } + inputBuffer++; + *outputBuffer++ = lastValidOutput; + } + mLastValidOutput = lastValidOutput; + + return numFrames; +} + +float Limiter::processFloat(float in) +{ + float in_abs = fabsf(in); + if (in_abs <= 1) { + return in; + } + float out; + if (in_abs < kXWhenYis3Decibels) { + out = (kPolynomialSplineA * in_abs + kPolynomialSplineB) * in_abs + kPolynomialSplineC; + } else { + out = M_SQRT2; + } + if (in < 0) { + out = -out; + } + return out; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/Limiter.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/Limiter.h new file mode 100644 index 00000000..393a7bf3 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/Limiter.h @@ -0,0 +1,64 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_LIMITER_H +#define FLOWGRAPH_LIMITER_H + +#include +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +class Limiter : public FlowGraphFilter { +public: + explicit Limiter(int32_t channelCount); + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "Limiter"; + } + +private: + // These numbers are based on a polynomial spline for a quadratic solution Ax^2 + Bx + C + // The range is up to 3 dB, (10^(3/20)), to match AudioTrack for float data. + static constexpr float kPolynomialSplineA = -0.6035533905; // -(1+sqrt(2))/4 + static constexpr float kPolynomialSplineB = 2.2071067811; // (3+sqrt(2))/2 + static constexpr float kPolynomialSplineC = -0.6035533905; // -(1+sqrt(2))/4 + static constexpr float kXWhenYis3Decibels = 1.8284271247; // -1+2sqrt(2) + + /** + * Process an input based on the following: + * If between -1 and 1, return the input value. + * If above kXWhenYis3Decibels, return sqrt(2). + * If below -kXWhenYis3Decibels, return -sqrt(2). + * If between 1 and kXWhenYis3Decibels, use a quadratic spline (Ax^2 + Bx + C). + * If between -kXWhenYis3Decibels and -1, use the absolute value for the spline and flip it. + * The derivative of the spline is 1 at 1 and 0 at kXWhenYis3Decibels. + * This way, the graph is both continuous and differentiable. + */ + float processFloat(float in); + + // Use the previous valid output for NaN inputs + float mLastValidOutput = 0.0f; +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_LIMITER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ManyToMultiConverter.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ManyToMultiConverter.cpp new file mode 100644 index 00000000..4f973bc3 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ManyToMultiConverter.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ManyToMultiConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +ManyToMultiConverter::ManyToMultiConverter(int32_t channelCount) + : inputs(channelCount) + , output(*this, channelCount) { + for (int i = 0; i < channelCount; i++) { + inputs[i] = std::make_unique(*this, 1); + } +} + +int32_t ManyToMultiConverter::onProcess(int32_t numFrames) { + int32_t channelCount = output.getSamplesPerFrame(); + + for (int ch = 0; ch < channelCount; ch++) { + const float *inputBuffer = inputs[ch]->getBuffer(); + float *outputBuffer = output.getBuffer() + ch; + + for (int i = 0; i < numFrames; i++) { + // read one, write into the proper interleaved output channel + float sample = *inputBuffer++; + *outputBuffer = sample; + outputBuffer += channelCount; // advance to next multichannel frame + } + } + return numFrames; +} + diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ManyToMultiConverter.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ManyToMultiConverter.h new file mode 100644 index 00000000..50644cf9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/ManyToMultiConverter.h @@ -0,0 +1,53 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_MANY_TO_MULTI_CONVERTER_H +#define FLOWGRAPH_MANY_TO_MULTI_CONVERTER_H + +#include +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Combine multiple mono inputs into one interleaved multi-channel output. + */ +class ManyToMultiConverter : public flowgraph::FlowGraphNode { +public: + explicit ManyToMultiConverter(int32_t channelCount); + + virtual ~ManyToMultiConverter() = default; + + int32_t onProcess(int numFrames) override; + + void setEnabled(bool /*enabled*/) {} + + std::vector> inputs; + flowgraph::FlowGraphPortFloatOutput output; + + const char *getName() override { + return "ManyToMultiConverter"; + } + +private: +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_MANY_TO_MULTI_CONVERTER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoBlend.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoBlend.cpp new file mode 100644 index 00000000..4fd75e19 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoBlend.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "MonoBlend.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +MonoBlend::MonoBlend(int32_t channelCount) + : FlowGraphFilter(channelCount) + , mInvChannelCount(1. / channelCount) +{ +} + +int32_t MonoBlend::onProcess(int32_t numFrames) { + int32_t channelCount = output.getSamplesPerFrame(); + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + + for (size_t i = 0; i < numFrames; ++i) { + float accum = 0; + for (size_t j = 0; j < channelCount; ++j) { + accum += *inputBuffer++; + } + accum *= mInvChannelCount; + for (size_t j = 0; j < channelCount; ++j) { + *outputBuffer++ = accum; + } + } + + return numFrames; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoBlend.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoBlend.h new file mode 100644 index 00000000..f8d44ffb --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoBlend.h @@ -0,0 +1,48 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_MONO_BLEND_H +#define FLOWGRAPH_MONO_BLEND_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Combine data between multiple channels so each channel is an average + * of all channels. + */ +class MonoBlend : public FlowGraphFilter { +public: + explicit MonoBlend(int32_t channelCount); + + virtual ~MonoBlend() = default; + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "MonoBlend"; + } +private: + const float mInvChannelCount; +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_MONO_BLEND diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoToMultiConverter.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoToMultiConverter.cpp new file mode 100644 index 00000000..33eed523 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoToMultiConverter.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "FlowGraphNode.h" +#include "MonoToMultiConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +MonoToMultiConverter::MonoToMultiConverter(int32_t outputChannelCount) + : input(*this, 1) + , output(*this, outputChannelCount) { +} + +int32_t MonoToMultiConverter::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + int32_t channelCount = output.getSamplesPerFrame(); + for (int i = 0; i < numFrames; i++) { + // read one, write many + float sample = *inputBuffer++; + for (int channel = 0; channel < channelCount; channel++) { + *outputBuffer++ = sample; + } + } + return numFrames; +} + diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoToMultiConverter.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoToMultiConverter.h new file mode 100644 index 00000000..762edb0a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MonoToMultiConverter.h @@ -0,0 +1,49 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H +#define FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Convert a monophonic stream to a multi-channel interleaved stream + * with the same signal on each channel. + */ +class MonoToMultiConverter : public FlowGraphNode { +public: + explicit MonoToMultiConverter(int32_t outputChannelCount); + + virtual ~MonoToMultiConverter() = default; + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "MonoToMultiConverter"; + } + + FlowGraphPortFloatInput input; + FlowGraphPortFloatOutput output; +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToManyConverter.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToManyConverter.cpp new file mode 100644 index 00000000..5cdf594b --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToManyConverter.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "FlowGraphNode.h" +#include "MultiToManyConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +MultiToManyConverter::MultiToManyConverter(int32_t channelCount) + : outputs(channelCount) + , input(*this, channelCount) { + for (int i = 0; i < channelCount; i++) { + outputs[i] = std::make_unique(*this, 1); + } +} + +MultiToManyConverter::~MultiToManyConverter() = default; + +int32_t MultiToManyConverter::onProcess(int32_t numFrames) { + int32_t channelCount = input.getSamplesPerFrame(); + + for (int ch = 0; ch < channelCount; ch++) { + const float *inputBuffer = input.getBuffer() + ch; + float *outputBuffer = outputs[ch]->getBuffer(); + + for (int i = 0; i < numFrames; i++) { + *outputBuffer++ = *inputBuffer; + inputBuffer += channelCount; + } + } + + return numFrames; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToManyConverter.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToManyConverter.h new file mode 100644 index 00000000..0b222ac6 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToManyConverter.h @@ -0,0 +1,49 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H +#define FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Convert a multi-channel interleaved stream to multiple mono-channel + * outputs + */ + class MultiToManyConverter : public FlowGraphNode { + public: + explicit MultiToManyConverter(int32_t channelCount); + + virtual ~MultiToManyConverter(); + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "MultiToManyConverter"; + } + + std::vector> outputs; + flowgraph::FlowGraphPortFloatInput input; + }; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToMonoConverter.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToMonoConverter.cpp new file mode 100644 index 00000000..467f95ea --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToMonoConverter.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "FlowGraphNode.h" +#include "MultiToMonoConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +MultiToMonoConverter::MultiToMonoConverter(int32_t inputChannelCount) + : input(*this, inputChannelCount) + , output(*this, 1) { +} + +MultiToMonoConverter::~MultiToMonoConverter() = default; + +int32_t MultiToMonoConverter::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + int32_t channelCount = input.getSamplesPerFrame(); + for (int i = 0; i < numFrames; i++) { + // read first channel of multi stream, write many + *outputBuffer++ = *inputBuffer; + inputBuffer += channelCount; + } + return numFrames; +} + diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToMonoConverter.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToMonoConverter.h new file mode 100644 index 00000000..bf5b7b60 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/MultiToMonoConverter.h @@ -0,0 +1,49 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_MULTI_TO_MONO_CONVERTER_H +#define FLOWGRAPH_MULTI_TO_MONO_CONVERTER_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Convert a multi-channel interleaved stream to a monophonic stream + * by extracting channel[0]. + */ + class MultiToMonoConverter : public FlowGraphNode { + public: + explicit MultiToMonoConverter(int32_t inputChannelCount); + + virtual ~MultiToMonoConverter(); + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "MultiToMonoConverter"; + } + + FlowGraphPortFloatInput input; + FlowGraphPortFloatOutput output; + }; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_MULTI_TO_MONO_CONVERTER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/RampLinear.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/RampLinear.cpp new file mode 100644 index 00000000..80ac72ad --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/RampLinear.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "FlowGraphNode.h" +#include "RampLinear.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +RampLinear::RampLinear(int32_t channelCount) + : FlowGraphFilter(channelCount) { + mTarget.store(1.0f); +} + +void RampLinear::setLengthInFrames(int32_t frames) { + mLengthInFrames = frames; +} + +void RampLinear::setTarget(float target) { + mTarget.store(target); + // If the ramp has not been used then start immediately at this level. + if (mLastCallCount == kInitialCallCount) { + forceCurrent(target); + } +} + +float RampLinear::interpolateCurrent() { + return mLevelTo - (mRemaining * mScaler); +} + +int32_t RampLinear::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + int32_t channelCount = output.getSamplesPerFrame(); + + float target = getTarget(); + if (target != mLevelTo) { + // Start new ramp. Continue from previous level. + mLevelFrom = interpolateCurrent(); + mLevelTo = target; + mRemaining = mLengthInFrames; + mScaler = (mLevelTo - mLevelFrom) / mLengthInFrames; // for interpolation + } + + int32_t framesLeft = numFrames; + + if (mRemaining > 0) { // Ramping? This doesn't happen very often. + int32_t framesToRamp = std::min(framesLeft, mRemaining); + framesLeft -= framesToRamp; + while (framesToRamp > 0) { + float currentLevel = interpolateCurrent(); + for (int ch = 0; ch < channelCount; ch++) { + *outputBuffer++ = *inputBuffer++ * currentLevel; + } + mRemaining--; + framesToRamp--; + } + } + + // Process any frames after the ramp. + int32_t samplesLeft = framesLeft * channelCount; + for (int i = 0; i < samplesLeft; i++) { + *outputBuffer++ = *inputBuffer++ * mLevelTo; + } + + return numFrames; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/RampLinear.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/RampLinear.h new file mode 100644 index 00000000..3839d6ef --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/RampLinear.h @@ -0,0 +1,96 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_RAMP_LINEAR_H +#define FLOWGRAPH_RAMP_LINEAR_H + +#include +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * When the target is modified then the output will ramp smoothly + * between the original and the new target value. + * This can be used to smooth out control values and reduce pops. + * + * The target may be updated while a ramp is in progress, which will trigger + * a new ramp from the current value. + */ +class RampLinear : public FlowGraphFilter { +public: + explicit RampLinear(int32_t channelCount); + + virtual ~RampLinear() = default; + + int32_t onProcess(int32_t numFrames) override; + + /** + * This is used for the next ramp. + * Calling this does not affect a ramp that is in progress. + */ + void setLengthInFrames(int32_t frames); + + int32_t getLengthInFrames() const { + return mLengthInFrames; + } + + /** + * This may be safely called by another thread. + * @param target + */ + void setTarget(float target); + + float getTarget() const { + return mTarget.load(); + } + + /** + * Force the nextSegment to start from this level. + * + * WARNING: this can cause a discontinuity if called while the ramp is being used. + * Only call this when setting the initial ramp. + * + * @param level + */ + void forceCurrent(float level) { + mLevelFrom = level; + mLevelTo = level; + } + + const char *getName() override { + return "RampLinear"; + } + +private: + + float interpolateCurrent(); + + std::atomic mTarget; + + int32_t mLengthInFrames = 48000.0f / 100.0f ; // 10 msec at 48000 Hz; + int32_t mRemaining = 0; + float mScaler = 0.0f; + float mLevelFrom = 0.0f; + float mLevelTo = 0.0f; +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_RAMP_LINEAR_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SampleRateConverter.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SampleRateConverter.cpp new file mode 100644 index 00000000..a15fcb8c --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SampleRateConverter.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SampleRateConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +SampleRateConverter::SampleRateConverter(int32_t channelCount, + MultiChannelResampler &resampler) + : FlowGraphFilter(channelCount) + , mResampler(resampler) { + setDataPulledAutomatically(false); +} + +void SampleRateConverter::reset() { + FlowGraphNode::reset(); + mInputCursor = kInitialCallCount; +} + +// Return true if there is a sample available. +bool SampleRateConverter::isInputAvailable() { + // If we have consumed all of the input data then go out and get some more. + if (mInputCursor >= mNumValidInputFrames) { + mInputCallCount++; + mNumValidInputFrames = input.pullData(mInputCallCount, input.getFramesPerBuffer()); + mInputCursor = 0; + } + return (mInputCursor < mNumValidInputFrames); +} + +const float *SampleRateConverter::getNextInputFrame() { + const float *inputBuffer = input.getBuffer(); + return &inputBuffer[mInputCursor++ * input.getSamplesPerFrame()]; +} + +int32_t SampleRateConverter::onProcess(int32_t numFrames) { + float *outputBuffer = output.getBuffer(); + int32_t channelCount = output.getSamplesPerFrame(); + int framesLeft = numFrames; + while (framesLeft > 0) { + // Gather input samples as needed. + if(mResampler.isWriteNeeded()) { + if (isInputAvailable()) { + const float *frame = getNextInputFrame(); + mResampler.writeNextFrame(frame); + } else { + break; + } + } else { + // Output frame is interpolated from input samples. + mResampler.readNextFrame(outputBuffer); + outputBuffer += channelCount; + framesLeft--; + } + } + return numFrames - framesLeft; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SampleRateConverter.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SampleRateConverter.h new file mode 100644 index 00000000..f883e6ce --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SampleRateConverter.h @@ -0,0 +1,63 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SAMPLE_RATE_CONVERTER_H +#define FLOWGRAPH_SAMPLE_RATE_CONVERTER_H + +#include +#include + +#include "FlowGraphNode.h" +#include "resampler/MultiChannelResampler.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +class SampleRateConverter : public FlowGraphFilter { +public: + explicit SampleRateConverter(int32_t channelCount, + resampler::MultiChannelResampler &mResampler); + + virtual ~SampleRateConverter() = default; + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SampleRateConverter"; + } + + void reset() override; + +private: + + // Return true if there is a sample available. + bool isInputAvailable(); + + // This assumes data is available. Only call after calling isInputAvailable(). + const float *getNextInputFrame(); + + resampler::MultiChannelResampler &mResampler; + + int32_t mInputCursor = 0; // offset into the input port buffer + int32_t mNumValidInputFrames = 0; // number of valid frames currently in the input port buffer + // We need our own callCount for upstream calls because calls occur at a different rate. + // This means we cannot have cyclic graphs or merges that contain an SRC. + int64_t mInputCallCount = 0; + +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_SAMPLE_RATE_CONVERTER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.cpp new file mode 100644 index 00000000..940a66be --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "FlowGraphNode.h" +#include "SinkFloat.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +SinkFloat::SinkFloat(int32_t channelCount) + : FlowGraphSink(channelCount) { +} + +int32_t SinkFloat::read(void *data, int32_t numFrames) { + float *floatData = (float *) data; + const int32_t channelCount = input.getSamplesPerFrame(); + + int32_t framesLeft = numFrames; + while (framesLeft > 0) { + // Run the graph and pull data through the input port. + int32_t framesPulled = pullData(framesLeft); + if (framesPulled <= 0) { + break; + } + const float *signal = input.getBuffer(); + int32_t numSamples = framesPulled * channelCount; + memcpy(floatData, signal, numSamples * sizeof(float)); + floatData += numSamples; + framesLeft -= framesPulled; + } + return numFrames - framesLeft; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.h new file mode 100644 index 00000000..3be3f5d4 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkFloat.h @@ -0,0 +1,45 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef FLOWGRAPH_SINK_FLOAT_H +#define FLOWGRAPH_SINK_FLOAT_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * AudioSink that lets you read data as 32-bit floats. + */ +class SinkFloat : public FlowGraphSink { +public: + explicit SinkFloat(int32_t channelCount); + ~SinkFloat() override = default; + + int32_t read(void *data, int32_t numFrames) override; + + const char *getName() override { + return "SinkFloat"; + } +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_SINK_FLOAT_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI16.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI16.cpp new file mode 100644 index 00000000..690431ce --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI16.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "SinkI16.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +SinkI16::SinkI16(int32_t channelCount) + : FlowGraphSink(channelCount) {} + +int32_t SinkI16::read(void *data, int32_t numFrames) { + int16_t *shortData = (int16_t *) data; + const int32_t channelCount = input.getSamplesPerFrame(); + + int32_t framesLeft = numFrames; + while (framesLeft > 0) { + // Run the graph and pull data through the input port. + int32_t framesRead = pullData(framesLeft); + if (framesRead <= 0) { + break; + } + const float *signal = input.getBuffer(); + int32_t numSamples = framesRead * channelCount; +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_i16_from_float(shortData, signal, numSamples); + shortData += numSamples; + signal += numSamples; +#else + for (int i = 0; i < numSamples; i++) { + int32_t n = (int32_t) (*signal++ * 32768.0f); + *shortData++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip + } +#endif + framesLeft -= framesRead; + } + return numFrames - framesLeft; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI16.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI16.h new file mode 100644 index 00000000..bf124f55 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI16.h @@ -0,0 +1,43 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SINK_I16_H +#define FLOWGRAPH_SINK_I16_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * AudioSink that lets you read data as 16-bit signed integers. + */ +class SinkI16 : public FlowGraphSink { +public: + explicit SinkI16(int32_t channelCount); + + int32_t read(void *data, int32_t numFrames) override; + + const char *getName() override { + return "SinkI16"; + } +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_SINK_I16_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI24.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI24.cpp new file mode 100644 index 00000000..d4f68b68 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI24.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + + +#include "FlowGraphNode.h" +#include "SinkI24.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +SinkI24::SinkI24(int32_t channelCount) + : FlowGraphSink(channelCount) {} + +int32_t SinkI24::read(void *data, int32_t numFrames) { + uint8_t *byteData = (uint8_t *) data; + const int32_t channelCount = input.getSamplesPerFrame(); + + int32_t framesLeft = numFrames; + while (framesLeft > 0) { + // Run the graph and pull data through the input port. + int32_t framesRead = pullData(framesLeft); + if (framesRead <= 0) { + break; + } + const float *floatData = input.getBuffer(); + int32_t numSamples = framesRead * channelCount; +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_p24_from_float(byteData, floatData, numSamples); + static const int kBytesPerI24Packed = 3; + byteData += numSamples * kBytesPerI24Packed; + floatData += numSamples; +#else + const int32_t kI24PackedMax = 0x007FFFFF; + const int32_t kI24PackedMin = 0xFF800000; + for (int i = 0; i < numSamples; i++) { + int32_t n = (int32_t) (*floatData++ * 0x00800000); + n = std::min(kI24PackedMax, std::max(kI24PackedMin, n)); // clip + // Write as a packed 24-bit integer in Little Endian format. + *byteData++ = (uint8_t) n; + *byteData++ = (uint8_t) (n >> 8); + *byteData++ = (uint8_t) (n >> 16); + } +#endif + framesLeft -= framesRead; + } + return numFrames - framesLeft; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI24.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI24.h new file mode 100644 index 00000000..6b4135e9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI24.h @@ -0,0 +1,44 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SINK_I24_H +#define FLOWGRAPH_SINK_I24_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * AudioSink that lets you read data as packed 24-bit signed integers. + * The sample size is 3 bytes. + */ +class SinkI24 : public FlowGraphSink { +public: + explicit SinkI24(int32_t channelCount); + + int32_t read(void *data, int32_t numFrames) override; + + const char *getName() override { + return "SinkI24"; + } +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_SINK_I24_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.cpp new file mode 100644 index 00000000..b14b3d2a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FlowGraphNode.h" +#include "FlowgraphUtilities.h" +#include "SinkI32.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +SinkI32::SinkI32(int32_t channelCount) + : FlowGraphSink(channelCount) {} + +int32_t SinkI32::read(void *data, int32_t numFrames) { + int32_t *intData = (int32_t *) data; + const int32_t channelCount = input.getSamplesPerFrame(); + + int32_t framesLeft = numFrames; + while (framesLeft > 0) { + // Run the graph and pull data through the input port. + int32_t framesRead = pullData(framesLeft); + if (framesRead <= 0) { + break; + } + const float *signal = input.getBuffer(); + int32_t numSamples = framesRead * channelCount; +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_i32_from_float(intData, signal, numSamples); + intData += numSamples; + signal += numSamples; +#else + for (int i = 0; i < numSamples; i++) { + *intData++ = FlowgraphUtilities::clamp32FromFloat(*signal++); + } +#endif + framesLeft -= framesRead; + } + return numFrames - framesLeft; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.h new file mode 100644 index 00000000..35507ea1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SinkI32.h @@ -0,0 +1,40 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SINK_I32_H +#define FLOWGRAPH_SINK_I32_H + +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +class SinkI32 : public FlowGraphSink { +public: + explicit SinkI32(int32_t channelCount); + ~SinkI32() override = default; + + int32_t read(void *data, int32_t numFrames) override; + + const char *getName() override { + return "SinkI32"; + } +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_SINK_I32_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.cpp new file mode 100644 index 00000000..a0c88275 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "FlowGraphNode.h" +#include "SourceFloat.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +SourceFloat::SourceFloat(int32_t channelCount) + : FlowGraphSourceBuffered(channelCount) { +} + +int32_t SourceFloat::onProcess(int32_t numFrames) { + float *outputBuffer = output.getBuffer(); + const int32_t channelCount = output.getSamplesPerFrame(); + + const int32_t framesLeft = mSizeInFrames - mFrameIndex; + const int32_t framesToProcess = std::min(numFrames, framesLeft); + const int32_t numSamples = framesToProcess * channelCount; + + const float *floatBase = (float *) mData; + const float *floatData = &floatBase[mFrameIndex * channelCount]; + memcpy(outputBuffer, floatData, numSamples * sizeof(float)); + mFrameIndex += framesToProcess; + return framesToProcess; +} + diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.h new file mode 100644 index 00000000..78053e51 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceFloat.h @@ -0,0 +1,44 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SOURCE_FLOAT_H +#define FLOWGRAPH_SOURCE_FLOAT_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * AudioSource that reads a block of pre-defined float data. + */ +class SourceFloat : public FlowGraphSourceBuffered { +public: + explicit SourceFloat(int32_t channelCount); + ~SourceFloat() override = default; + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceFloat"; + } +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_SOURCE_FLOAT_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI16.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI16.cpp new file mode 100644 index 00000000..58c3b1e8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI16.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "FlowGraphNode.h" +#include "SourceI16.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +SourceI16::SourceI16(int32_t channelCount) + : FlowGraphSourceBuffered(channelCount) { +} + +int32_t SourceI16::onProcess(int32_t numFrames) { + float *floatData = output.getBuffer(); + int32_t channelCount = output.getSamplesPerFrame(); + + int32_t framesLeft = mSizeInFrames - mFrameIndex; + int32_t framesToProcess = std::min(numFrames, framesLeft); + int32_t numSamples = framesToProcess * channelCount; + + const int16_t *shortBase = static_cast(mData); + const int16_t *shortData = &shortBase[mFrameIndex * channelCount]; + +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_float_from_i16(floatData, shortData, numSamples); +#else + for (int i = 0; i < numSamples; i++) { + *floatData++ = *shortData++ * (1.0f / 32768); + } +#endif + + mFrameIndex += framesToProcess; + return framesToProcess; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI16.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI16.h new file mode 100644 index 00000000..923890cb --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI16.h @@ -0,0 +1,42 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SOURCE_I16_H +#define FLOWGRAPH_SOURCE_I16_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { +/** + * AudioSource that reads a block of pre-defined 16-bit integer data. + */ +class SourceI16 : public FlowGraphSourceBuffered { +public: + explicit SourceI16(int32_t channelCount); + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI16"; + } +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_SOURCE_I16_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI24.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI24.cpp new file mode 100644 index 00000000..cd4b7d90 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI24.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "FlowGraphNode.h" +#include "SourceI24.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +constexpr int kBytesPerI24Packed = 3; + +SourceI24::SourceI24(int32_t channelCount) + : FlowGraphSourceBuffered(channelCount) { +} + +int32_t SourceI24::onProcess(int32_t numFrames) { + float *floatData = output.getBuffer(); + int32_t channelCount = output.getSamplesPerFrame(); + + int32_t framesLeft = mSizeInFrames - mFrameIndex; + int32_t framesToProcess = std::min(numFrames, framesLeft); + int32_t numSamples = framesToProcess * channelCount; + + const uint8_t *byteBase = (uint8_t *) mData; + const uint8_t *byteData = &byteBase[mFrameIndex * channelCount * kBytesPerI24Packed]; + +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_float_from_p24(floatData, byteData, numSamples); +#else + static const float scale = 1. / (float)(1UL << 31); + for (int i = 0; i < numSamples; i++) { + // Assemble the data assuming Little Endian format. + int32_t pad = byteData[2]; + pad <<= 8; + pad |= byteData[1]; + pad <<= 8; + pad |= byteData[0]; + pad <<= 8; // Shift to 32 bit data so the sign is correct. + byteData += kBytesPerI24Packed; + *floatData++ = pad * scale; // scale to range -1.0 to 1.0 + } +#endif + + mFrameIndex += framesToProcess; + return framesToProcess; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI24.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI24.h new file mode 100644 index 00000000..fb66d4a4 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI24.h @@ -0,0 +1,43 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SOURCE_I24_H +#define FLOWGRAPH_SOURCE_I24_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * AudioSource that reads a block of pre-defined 24-bit packed integer data. + */ +class SourceI24 : public FlowGraphSourceBuffered { +public: + explicit SourceI24(int32_t channelCount); + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI24"; + } +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_SOURCE_I24_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.cpp new file mode 100644 index 00000000..b1c8f75a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "FlowGraphNode.h" +#include "SourceI32.h" + +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +SourceI32::SourceI32(int32_t channelCount) + : FlowGraphSourceBuffered(channelCount) { +} + +int32_t SourceI32::onProcess(int32_t numFrames) { + float *floatData = output.getBuffer(); + const int32_t channelCount = output.getSamplesPerFrame(); + + const int32_t framesLeft = mSizeInFrames - mFrameIndex; + const int32_t framesToProcess = std::min(numFrames, framesLeft); + const int32_t numSamples = framesToProcess * channelCount; + + const int32_t *intBase = static_cast(mData); + const int32_t *intData = &intBase[mFrameIndex * channelCount]; + +#if FLOWGRAPH_ANDROID_INTERNAL + memcpy_to_float_from_i32(floatData, intData, numSamples); +#else + for (int i = 0; i < numSamples; i++) { + *floatData++ = *intData++ * kScale; + } +#endif + + mFrameIndex += framesToProcess; + return framesToProcess; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.h new file mode 100644 index 00000000..71094690 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/SourceI32.h @@ -0,0 +1,42 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLOWGRAPH_SOURCE_I32_H +#define FLOWGRAPH_SOURCE_I32_H + +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +class SourceI32 : public FlowGraphSourceBuffered { +public: + explicit SourceI32(int32_t channelCount); + ~SourceI32() override = default; + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI32"; + } +private: + static constexpr float kScale = 1.0 / (1UL << 31); +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_SOURCE_I32_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/HyperbolicCosineWindow.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/HyperbolicCosineWindow.h new file mode 100644 index 00000000..76ec0e70 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/HyperbolicCosineWindow.h @@ -0,0 +1,71 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H +#define RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H + +#include + +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +/** + * Calculate a HyperbolicCosineWindow window centered at 0. + * This can be used in place of a Kaiser window. + * + * The code is based on an anonymous contribution by "a concerned citizen": + * https://dsp.stackexchange.com/questions/37714/kaiser-window-approximation + */ +class HyperbolicCosineWindow { +public: + HyperbolicCosineWindow() { + setStopBandAttenuation(60); + } + + /** + * @param attenuation typical values range from 30 to 90 dB + * @return beta + */ + double setStopBandAttenuation(double attenuation) { + double alpha = ((-325.1e-6 * attenuation + 0.1677) * attenuation) - 3.149; + setAlpha(alpha); + return alpha; + } + + void setAlpha(double alpha) { + mAlpha = alpha; + mInverseCoshAlpha = 1.0 / cosh(alpha); + } + + /** + * @param x ranges from -1.0 to +1.0 + */ + double operator()(double x) { + double x2 = x * x; + if (x2 >= 1.0) return 0.0; + double w = mAlpha * sqrt(1.0 - x2); + return cosh(w) * mInverseCoshAlpha; + } + +private: + double mAlpha = 0.0; + double mInverseCoshAlpha = 1.0; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.cpp new file mode 100644 index 00000000..39e9b245 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IntegerRatio.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +// Enough primes to cover the common sample rates. +static const int kPrimes[] = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, + 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, + 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199}; + +void IntegerRatio::reduce() { + for (int prime : kPrimes) { + if (mNumerator < prime || mDenominator < prime) { + break; + } + + // Find biggest prime factor for numerator. + while (true) { + int top = mNumerator / prime; + int bottom = mDenominator / prime; + if ((top >= 1) + && (bottom >= 1) + && (top * prime == mNumerator) // divided evenly? + && (bottom * prime == mDenominator)) { + mNumerator = top; + mDenominator = bottom; + } else { + break; + } + } + + } +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.h new file mode 100644 index 00000000..a6b524ce --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/IntegerRatio.h @@ -0,0 +1,54 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESAMPLER_INTEGER_RATIO_H +#define RESAMPLER_INTEGER_RATIO_H + +#include + +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +/** + * Represent the ratio of two integers. + */ +class IntegerRatio { +public: + IntegerRatio(int32_t numerator, int32_t denominator) + : mNumerator(numerator), mDenominator(denominator) {} + + /** + * Reduce by removing common prime factors. + */ + void reduce(); + + int32_t getNumerator() { + return mNumerator; + } + + int32_t getDenominator() { + return mDenominator; + } + +private: + int32_t mNumerator; + int32_t mDenominator; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_INTEGER_RATIO_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/KaiserWindow.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/KaiserWindow.h new file mode 100644 index 00000000..f99f9b4d --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/KaiserWindow.h @@ -0,0 +1,90 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESAMPLER_KAISER_WINDOW_H +#define RESAMPLER_KAISER_WINDOW_H + +#include + +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +/** + * Calculate a Kaiser window centered at 0. + */ +class KaiserWindow { +public: + KaiserWindow() { + setStopBandAttenuation(60); + } + + /** + * @param attenuation typical values range from 30 to 90 dB + * @return beta + */ + double setStopBandAttenuation(double attenuation) { + double beta = 0.0; + if (attenuation > 50) { + beta = 0.1102 * (attenuation - 8.7); + } else if (attenuation >= 21) { + double a21 = attenuation - 21; + beta = 0.5842 * pow(a21, 0.4) + (0.07886 * a21); + } + setBeta(beta); + return beta; + } + + void setBeta(double beta) { + mBeta = beta; + mInverseBesselBeta = 1.0 / bessel(beta); + } + + /** + * @param x ranges from -1.0 to +1.0 + */ + double operator()(double x) { + double x2 = x * x; + if (x2 >= 1.0) return 0.0; + double w = mBeta * sqrt(1.0 - x2); + return bessel(w) * mInverseBesselBeta; + } + + // Approximation of a + // modified zero order Bessel function of the first kind. + // Based on a discussion at: + // https://dsp.stackexchange.com/questions/37714/kaiser-window-approximation + static double bessel(double x) { + double y = cosh(0.970941817426052 * x); + y += cosh(0.8854560256532099 * x); + y += cosh(0.7485107481711011 * x); + y += cosh(0.5680647467311558 * x); + y += cosh(0.3546048870425356 * x); + y += cosh(0.120536680255323 * x); + y *= 2; + y += cosh(x); + y /= 13; + return y; + } + +private: + double mBeta = 0.0; + double mInverseBesselBeta = 1.0; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_KAISER_WINDOW_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/LinearResampler.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/LinearResampler.cpp new file mode 100644 index 00000000..cb4932a5 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/LinearResampler.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LinearResampler.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +LinearResampler::LinearResampler(const MultiChannelResampler::Builder &builder) + : MultiChannelResampler(builder) { + mPreviousFrame = std::make_unique(getChannelCount()); + mCurrentFrame = std::make_unique(getChannelCount()); +} + +void LinearResampler::writeFrame(const float *frame) { + memcpy(mPreviousFrame.get(), mCurrentFrame.get(), sizeof(float) * getChannelCount()); + memcpy(mCurrentFrame.get(), frame, sizeof(float) * getChannelCount()); +} + +void LinearResampler::readFrame(float *frame) { + float *previous = mPreviousFrame.get(); + float *current = mCurrentFrame.get(); + float phase = (float) getIntegerPhase() / mDenominator; + // iterate across samples in the frame + for (int channel = 0; channel < getChannelCount(); channel++) { + float f0 = *previous++; + float f1 = *current++; + *frame++ = f0 + (phase * (f1 - f0)); + } +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/LinearResampler.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/LinearResampler.h new file mode 100644 index 00000000..5434379c --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/LinearResampler.h @@ -0,0 +1,47 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESAMPLER_LINEAR_RESAMPLER_H +#define RESAMPLER_LINEAR_RESAMPLER_H + +#include +#include +#include + +#include "MultiChannelResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +/** + * Simple resampler that uses bi-linear interpolation. + */ +class LinearResampler : public MultiChannelResampler { +public: + explicit LinearResampler(const MultiChannelResampler::Builder &builder); + + void writeFrame(const float *frame) override; + + void readFrame(float *frame) override; + +private: + std::unique_ptr mPreviousFrame; + std::unique_ptr mCurrentFrame; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_LINEAR_RESAMPLER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/MultiChannelResampler.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/MultiChannelResampler.cpp new file mode 100644 index 00000000..611ddcd1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/MultiChannelResampler.cpp @@ -0,0 +1,171 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "IntegerRatio.h" +#include "LinearResampler.h" +#include "MultiChannelResampler.h" +#include "PolyphaseResampler.h" +#include "PolyphaseResamplerMono.h" +#include "PolyphaseResamplerStereo.h" +#include "SincResampler.h" +#include "SincResamplerStereo.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +MultiChannelResampler::MultiChannelResampler(const MultiChannelResampler::Builder &builder) + : mNumTaps(builder.getNumTaps()) + , mX(static_cast(builder.getChannelCount()) + * static_cast(builder.getNumTaps()) * 2) + , mSingleFrame(builder.getChannelCount()) + , mChannelCount(builder.getChannelCount()) + { + // Reduce sample rates to the smallest ratio. + // For example 44100/48000 would become 147/160. + IntegerRatio ratio(builder.getInputRate(), builder.getOutputRate()); + ratio.reduce(); + mNumerator = ratio.getNumerator(); + mDenominator = ratio.getDenominator(); + mIntegerPhase = mDenominator; // so we start with a write needed +} + +// static factory method +MultiChannelResampler *MultiChannelResampler::make(int32_t channelCount, + int32_t inputRate, + int32_t outputRate, + Quality quality) { + Builder builder; + builder.setInputRate(inputRate); + builder.setOutputRate(outputRate); + builder.setChannelCount(channelCount); + + switch (quality) { + case Quality::Fastest: + builder.setNumTaps(2); + break; + case Quality::Low: + builder.setNumTaps(4); + break; + case Quality::Medium: + default: + builder.setNumTaps(8); + break; + case Quality::High: + builder.setNumTaps(16); + break; + case Quality::Best: + builder.setNumTaps(32); + break; + } + + // Set the cutoff frequency so that we do not get aliasing when down-sampling. + if (inputRate > outputRate) { + builder.setNormalizedCutoff(kDefaultNormalizedCutoff); + } + return builder.build(); +} + +MultiChannelResampler *MultiChannelResampler::Builder::build() { + if (getNumTaps() == 2) { + // Note that this does not do low pass filteringh. + return new LinearResampler(*this); + } + IntegerRatio ratio(getInputRate(), getOutputRate()); + ratio.reduce(); + bool usePolyphase = (getNumTaps() * ratio.getDenominator()) <= kMaxCoefficients; + if (usePolyphase) { + if (getChannelCount() == 1) { + return new PolyphaseResamplerMono(*this); + } else if (getChannelCount() == 2) { + return new PolyphaseResamplerStereo(*this); + } else { + return new PolyphaseResampler(*this); + } + } else { + // Use less optimized resampler that uses a float phaseIncrement. + // TODO mono resampler + if (getChannelCount() == 2) { + return new SincResamplerStereo(*this); + } else { + return new SincResampler(*this); + } + } +} + +void MultiChannelResampler::writeFrame(const float *frame) { + // Move cursor before write so that cursor points to last written frame in read. + if (--mCursor < 0) { + mCursor = getNumTaps() - 1; + } + float *dest = &mX[static_cast(mCursor) * static_cast(getChannelCount())]; + int offset = getNumTaps() * getChannelCount(); + for (int channel = 0; channel < getChannelCount(); channel++) { + // Write twice so we avoid having to wrap when reading. + dest[channel] = dest[channel + offset] = frame[channel]; + } +} + +float MultiChannelResampler::sinc(float radians) { + if (abs(radians) < 1.0e-9) return 1.0f; // avoid divide by zero + return sinf(radians) / radians; // Sinc function +} + +// Generate coefficients in the order they will be used by readFrame(). +// This is more complicated but readFrame() is called repeatedly and should be optimized. +void MultiChannelResampler::generateCoefficients(int32_t inputRate, + int32_t outputRate, + int32_t numRows, + double phaseIncrement, + float normalizedCutoff) { + mCoefficients.resize(static_cast(getNumTaps()) * static_cast(numRows)); + int coefficientIndex = 0; + double phase = 0.0; // ranges from 0.0 to 1.0, fraction between samples + // Stretch the sinc function for low pass filtering. + const float cutoffScaler = (outputRate < inputRate) + ? (normalizedCutoff * (float)outputRate / inputRate) + : 1.0f; // Do not filter when upsampling. + const int numTapsHalf = getNumTaps() / 2; // numTaps must be even. + const float numTapsHalfInverse = 1.0f / numTapsHalf; + for (int i = 0; i < numRows; i++) { + float tapPhase = phase - numTapsHalf; + float gain = 0.0; // sum of raw coefficients + int gainCursor = coefficientIndex; + for (int tap = 0; tap < getNumTaps(); tap++) { + float radians = tapPhase * M_PI; + +#if MCR_USE_KAISER + float window = mKaiserWindow(tapPhase * numTapsHalfInverse); +#else + float window = mCoshWindow(static_cast(tapPhase) * numTapsHalfInverse); +#endif + float coefficient = sinc(radians * cutoffScaler) * window; + mCoefficients.at(coefficientIndex++) = coefficient; + gain += coefficient; + tapPhase += 1.0; + } + phase += phaseIncrement; + while (phase >= 1.0) { + phase -= 1.0; + } + + // Correct for gain variations. + float gainCorrection = 1.0 / gain; // normalize the gain + for (int tap = 0; tap < getNumTaps(); tap++) { + mCoefficients.at(gainCursor + tap) *= gainCorrection; + } + } +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/MultiChannelResampler.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/MultiChannelResampler.h new file mode 100644 index 00000000..9e47335a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/MultiChannelResampler.h @@ -0,0 +1,281 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESAMPLER_MULTICHANNEL_RESAMPLER_H +#define RESAMPLER_MULTICHANNEL_RESAMPLER_H + +#include +#include +#include +#include + +#ifndef MCR_USE_KAISER +// It appears from the spectrogram that the HyperbolicCosine window leads to fewer artifacts. +// And it is faster to calculate. +#define MCR_USE_KAISER 0 +#endif + +#if MCR_USE_KAISER +#include "KaiserWindow.h" +#else +#include "HyperbolicCosineWindow.h" +#endif + +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +class MultiChannelResampler { + +public: + + enum class Quality : int32_t { + Fastest, + Low, + Medium, + High, + Best, + }; + + class Builder { + public: + /** + * Construct an optimal resampler based on the specified parameters. + * @return address of a resampler + */ + MultiChannelResampler *build(); + + /** + * The number of taps in the resampling filter. + * More taps gives better quality but uses more CPU time. + * This typically ranges from 4 to 64. Default is 16. + * + * For polyphase filters, numTaps must be a multiple of four for loop unrolling. + * @param numTaps number of taps for the filter + * @return address of this builder for chaining calls + */ + Builder *setNumTaps(int32_t numTaps) { + mNumTaps = numTaps; + return this; + } + + /** + * Use 1 for mono, 2 for stereo, etc. Default is 1. + * + * @param channelCount number of channels + * @return address of this builder for chaining calls + */ + Builder *setChannelCount(int32_t channelCount) { + mChannelCount = channelCount; + return this; + } + + /** + * Default is 48000. + * + * @param inputRate sample rate of the input stream + * @return address of this builder for chaining calls + */ + Builder *setInputRate(int32_t inputRate) { + mInputRate = inputRate; + return this; + } + + /** + * Default is 48000. + * + * @param outputRate sample rate of the output stream + * @return address of this builder for chaining calls + */ + Builder *setOutputRate(int32_t outputRate) { + mOutputRate = outputRate; + return this; + } + + /** + * Set cutoff frequency relative to the Nyquist rate of the output sample rate. + * Set to 1.0 to match the Nyquist frequency. + * Set lower to reduce aliasing. + * Default is 0.70. + * + * Note that this value is ignored when upsampling, which is when + * the outputRate is higher than the inputRate. + * + * @param normalizedCutoff anti-aliasing filter cutoff + * @return address of this builder for chaining calls + */ + Builder *setNormalizedCutoff(float normalizedCutoff) { + mNormalizedCutoff = normalizedCutoff; + return this; + } + + int32_t getNumTaps() const { + return mNumTaps; + } + + int32_t getChannelCount() const { + return mChannelCount; + } + + int32_t getInputRate() const { + return mInputRate; + } + + int32_t getOutputRate() const { + return mOutputRate; + } + + float getNormalizedCutoff() const { + return mNormalizedCutoff; + } + + protected: + int32_t mChannelCount = 1; + int32_t mNumTaps = 16; + int32_t mInputRate = 48000; + int32_t mOutputRate = 48000; + float mNormalizedCutoff = kDefaultNormalizedCutoff; + }; + + virtual ~MultiChannelResampler() = default; + + /** + * Factory method for making a resampler that is optimal for the given inputs. + * + * @param channelCount number of channels, 2 for stereo + * @param inputRate sample rate of the input stream + * @param outputRate sample rate of the output stream + * @param quality higher quality sounds better but uses more CPU + * @return an optimal resampler + */ + static MultiChannelResampler *make(int32_t channelCount, + int32_t inputRate, + int32_t outputRate, + Quality quality); + + bool isWriteNeeded() const { + return mIntegerPhase >= mDenominator; + } + + /** + * Write a frame containing N samples. + * + * @param frame pointer to the first sample in a frame + */ + void writeNextFrame(const float *frame) { + writeFrame(frame); + advanceWrite(); + } + + /** + * Read a frame containing N samples. + * + * @param frame pointer to the first sample in a frame + */ + void readNextFrame(float *frame) { + readFrame(frame); + advanceRead(); + } + + int getNumTaps() const { + return mNumTaps; + } + + int getChannelCount() const { + return mChannelCount; + } + + static float hammingWindow(float radians, float spread); + + static float sinc(float radians); + +protected: + + explicit MultiChannelResampler(const MultiChannelResampler::Builder &builder); + + /** + * Write a frame containing N samples. + * Call advanceWrite() after calling this. + * @param frame pointer to the first sample in a frame + */ + virtual void writeFrame(const float *frame); + + /** + * Read a frame containing N samples using interpolation. + * Call advanceRead() after calling this. + * @param frame pointer to the first sample in a frame + */ + virtual void readFrame(float *frame) = 0; + + void advanceWrite() { + mIntegerPhase -= mDenominator; + } + + void advanceRead() { + mIntegerPhase += mNumerator; + } + + /** + * Generate the filter coefficients in optimal order. + * + * Note that normalizedCutoff is ignored when upsampling, which is when + * the outputRate is higher than the inputRate. + * + * @param inputRate sample rate of the input stream + * @param outputRate sample rate of the output stream + * @param numRows number of rows in the array that contain a set of tap coefficients + * @param phaseIncrement how much to increment the phase between rows + * @param normalizedCutoff filter cutoff frequency normalized to Nyquist rate of output + */ + void generateCoefficients(int32_t inputRate, + int32_t outputRate, + int32_t numRows, + double phaseIncrement, + float normalizedCutoff); + + + int32_t getIntegerPhase() { + return mIntegerPhase; + } + + static constexpr int kMaxCoefficients = 8 * 1024; + std::vector mCoefficients; + + const int mNumTaps; + int mCursor = 0; + std::vector mX; // delayed input values for the FIR + std::vector mSingleFrame; // one frame for temporary use + int32_t mIntegerPhase = 0; + int32_t mNumerator = 0; + int32_t mDenominator = 0; + + +private: + +#if MCR_USE_KAISER + KaiserWindow mKaiserWindow; +#else + HyperbolicCosineWindow mCoshWindow; +#endif + + static constexpr float kDefaultNormalizedCutoff = 0.70f; + + const int mChannelCount; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_MULTICHANNEL_RESAMPLER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResampler.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResampler.cpp new file mode 100644 index 00000000..c16273b7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResampler.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include // Do NOT delete. Needed for LLVM. See #1746 +#include +#include +#include "IntegerRatio.h" +#include "PolyphaseResampler.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +PolyphaseResampler::PolyphaseResampler(const MultiChannelResampler::Builder &builder) + : MultiChannelResampler(builder) + { + assert((getNumTaps() % 4) == 0); // Required for loop unrolling. + + int32_t inputRate = builder.getInputRate(); + int32_t outputRate = builder.getOutputRate(); + + int32_t numRows = mDenominator; + double phaseIncrement = (double) inputRate / (double) outputRate; + generateCoefficients(inputRate, outputRate, + numRows, phaseIncrement, + builder.getNormalizedCutoff()); +} + +void PolyphaseResampler::readFrame(float *frame) { + // Clear accumulator for mixing. + std::fill(mSingleFrame.begin(), mSingleFrame.end(), 0.0); + + // Multiply input times windowed sinc function. + float *coefficients = &mCoefficients[mCoefficientCursor]; + float *xFrame = &mX[static_cast(mCursor) * static_cast(getChannelCount())]; + for (int i = 0; i < mNumTaps; i++) { + float coefficient = *coefficients++; + for (int channel = 0; channel < getChannelCount(); channel++) { + mSingleFrame[channel] += *xFrame++ * coefficient; + } + } + + // Advance and wrap through coefficients. + mCoefficientCursor = (mCoefficientCursor + mNumTaps) % mCoefficients.size(); + + // Copy accumulator to output. + for (int channel = 0; channel < getChannelCount(); channel++) { + frame[channel] = mSingleFrame[channel]; + } +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResampler.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResampler.h new file mode 100644 index 00000000..3642fcec --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResampler.h @@ -0,0 +1,53 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESAMPLER_POLYPHASE_RESAMPLER_H +#define RESAMPLER_POLYPHASE_RESAMPLER_H + +#include +#include +#include +#include + +#include "MultiChannelResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { +/** + * Resampler that is optimized for a reduced ratio of sample rates. + * All of the coefficients for each possible phase value are pre-calculated. + */ +class PolyphaseResampler : public MultiChannelResampler { +public: + /** + * + * @param builder containing lots of parameters + */ + explicit PolyphaseResampler(const MultiChannelResampler::Builder &builder); + + virtual ~PolyphaseResampler() = default; + + void readFrame(float *frame) override; + +protected: + + int32_t mCoefficientCursor = 0; + +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_POLYPHASE_RESAMPLER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerMono.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerMono.cpp new file mode 100644 index 00000000..fdaf13ea --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerMono.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "PolyphaseResamplerMono.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +#define MONO 1 + +PolyphaseResamplerMono::PolyphaseResamplerMono(const MultiChannelResampler::Builder &builder) + : PolyphaseResampler(builder) { + assert(builder.getChannelCount() == MONO); +} + +void PolyphaseResamplerMono::writeFrame(const float *frame) { + // Move cursor before write so that cursor points to last written frame in read. + if (--mCursor < 0) { + mCursor = getNumTaps() - 1; + } + float *dest = &mX[mCursor * MONO]; + const int offset = mNumTaps * MONO; + // Write each channel twice so we avoid having to wrap when running the FIR. + const float sample = frame[0]; + // Put ordered writes together. + dest[0] = sample; + dest[offset] = sample; +} + +void PolyphaseResamplerMono::readFrame(float *frame) { + // Clear accumulator. + float sum = 0.0; + + // Multiply input times precomputed windowed sinc function. + const float *coefficients = &mCoefficients[mCoefficientCursor]; + float *xFrame = &mX[mCursor * MONO]; + const int numLoops = mNumTaps >> 2; // n/4 + for (int i = 0; i < numLoops; i++) { + // Manual loop unrolling, might get converted to SIMD. + sum += *xFrame++ * *coefficients++; + sum += *xFrame++ * *coefficients++; + sum += *xFrame++ * *coefficients++; + sum += *xFrame++ * *coefficients++; + } + + mCoefficientCursor = (mCoefficientCursor + mNumTaps) % mCoefficients.size(); + + // Copy accumulator to output. + frame[0] = sum; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerMono.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerMono.h new file mode 100644 index 00000000..fe020b54 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerMono.h @@ -0,0 +1,41 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESAMPLER_POLYPHASE_RESAMPLER_MONO_H +#define RESAMPLER_POLYPHASE_RESAMPLER_MONO_H + +#include +#include + +#include "PolyphaseResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +class PolyphaseResamplerMono : public PolyphaseResampler { +public: + explicit PolyphaseResamplerMono(const MultiChannelResampler::Builder &builder); + + virtual ~PolyphaseResamplerMono() = default; + + void writeFrame(const float *frame) override; + + void readFrame(float *frame) override; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_POLYPHASE_RESAMPLER_MONO_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerStereo.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerStereo.cpp new file mode 100644 index 00000000..b3818518 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerStereo.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "PolyphaseResamplerStereo.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +#define STEREO 2 + +PolyphaseResamplerStereo::PolyphaseResamplerStereo(const MultiChannelResampler::Builder &builder) + : PolyphaseResampler(builder) { + assert(builder.getChannelCount() == STEREO); +} + +void PolyphaseResamplerStereo::writeFrame(const float *frame) { + // Move cursor before write so that cursor points to last written frame in read. + if (--mCursor < 0) { + mCursor = getNumTaps() - 1; + } + float *dest = &mX[mCursor * STEREO]; + const int offset = mNumTaps * STEREO; + // Write each channel twice so we avoid having to wrap when running the FIR. + const float left = frame[0]; + const float right = frame[1]; + // Put ordered writes together. + dest[0] = left; + dest[1] = right; + dest[offset] = left; + dest[1 + offset] = right; +} + +void PolyphaseResamplerStereo::readFrame(float *frame) { + // Clear accumulators. + float left = 0.0; + float right = 0.0; + + // Multiply input times precomputed windowed sinc function. + const float *coefficients = &mCoefficients[mCoefficientCursor]; + float *xFrame = &mX[mCursor * STEREO]; + const int numLoops = mNumTaps >> 2; // n/4 + for (int i = 0; i < numLoops; i++) { + // Manual loop unrolling, might get converted to SIMD. + float coefficient = *coefficients++; + left += *xFrame++ * coefficient; + right += *xFrame++ * coefficient; + + coefficient = *coefficients++; // next tap + left += *xFrame++ * coefficient; + right += *xFrame++ * coefficient; + + coefficient = *coefficients++; // next tap + left += *xFrame++ * coefficient; + right += *xFrame++ * coefficient; + + coefficient = *coefficients++; // next tap + left += *xFrame++ * coefficient; + right += *xFrame++ * coefficient; + } + + mCoefficientCursor = (mCoefficientCursor + mNumTaps) % mCoefficients.size(); + + // Copy accumulators to output. + frame[0] = left; + frame[1] = right; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerStereo.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerStereo.h new file mode 100644 index 00000000..ee4cabaa --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/PolyphaseResamplerStereo.h @@ -0,0 +1,41 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESAMPLER_POLYPHASE_RESAMPLER_STEREO_H +#define RESAMPLER_POLYPHASE_RESAMPLER_STEREO_H + +#include +#include + +#include "PolyphaseResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +class PolyphaseResamplerStereo : public PolyphaseResampler { +public: + explicit PolyphaseResamplerStereo(const MultiChannelResampler::Builder &builder); + + virtual ~PolyphaseResamplerStereo() = default; + + void writeFrame(const float *frame) override; + + void readFrame(float *frame) override; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_POLYPHASE_RESAMPLER_STEREO_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/README.md b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/README.md new file mode 100644 index 00000000..356f06c7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/README.md @@ -0,0 +1,101 @@ +# Sample Rate Converter + +This folder contains a sample rate converter, or "resampler". + +The converter is based on a sinc function that has been windowed by a hyperbolic cosine. +We found this had fewer artifacts than the more traditional Kaiser window. + +## Building the Resampler + +It is part of [Oboe](https://github.com/google/oboe) but has no dependencies on Oboe. +So the contents of this folder can be used outside of Oboe. + +To build it for use outside of Oboe: + +1. Copy the "resampler" folder to a folder in your project that is in the include path. +2. Add all of the \*.cpp files in the resampler folder to your project IDE or Makefile. +3. In ResamplerDefinitions.h, define RESAMPLER_OUTER_NAMESPACE with your own project name. Alternatively, use -DRESAMPLER_OUTER_NAMESPACE=mynamespace when compiling to avoid modifying the resampler code. + +## Creating a Resampler + +Include the [main header](MultiChannelResampler.h) for the resampler. + + #include "resampler/MultiChannelResampler.h" + +Here is an example of creating a stereo resampler that will convert from 44100 to 48000 Hz. +Only do this once, when you open your stream. Then use the sample resampler to process multiple buffers. + + MultiChannelResampler *resampler = MultiChannelResampler::make( + 2, // channel count + 44100, // input sampleRate + 48000, // output sampleRate + MultiChannelResampler::Quality::Medium); // conversion quality + +Possible values for quality include { Fastest, Low, Medium, High, Best }. +Higher quality levels will sound better but consume more CPU because they have more taps in the filter. + +## Fractional Frame Counts + +Note that the number of output frames generated for a given number of input frames can vary. + +For example, suppose you are converting from 44100 Hz to 48000 Hz and using an input buffer with 960 frames. If you calculate the number of output frames you get: + + 960.0 * 48000 / 44100 = 1044.897959... + +You cannot generate a fractional number of frames. So the resampler will sometimes generate 1044 frames and sometimes 1045 frames. On average it will generate 1044.897959 frames. The resampler stores the fraction internally and keeps track of when to consume or generate a frame. + +You can either use a fixed number of input frames or a fixed number of output frames. The other frame count will vary. + +## Calling the Resampler with a fixed number of OUTPUT frames + +In this example, suppose we have a fixed number of output frames and a variable number of input frames. + +Assume you start with these variables and a method that returns the next input frame: + + float *outputBuffer; // multi-channel buffer to be filled + int numOutputFrames; // number of frames of output + +The resampler has a method isWriteNeeded() that tells you whether to write to or read from the resampler. + + int outputFramesLeft = numOutputFrames; + while (outputFramesLeft > 0) { + if(resampler->isWriteNeeded()) { + const float *frame = getNextInputFrame(); // you provide this + resampler->writeNextFrame(frame); + } else { + resampler->readNextFrame(outputBuffer); + outputBuffer += channelCount; + outputFramesLeft--; + } + } + +## Calling the Resampler with a fixed number of INPUT frames + +In this example, suppose we have a fixed number of input frames and a variable number of output frames. + +Assume you start with these variables: + + float *inputBuffer; // multi-channel buffer to be consumed + float *outputBuffer; // multi-channel buffer to be filled + int numInputFrames; // number of frames of input + int numOutputFrames = 0; + int channelCount; // 1 for mono, 2 for stereo + + int inputFramesLeft = numInputFrames; + while (inputFramesLeft > 0) { + if(resampler->isWriteNeeded()) { + resampler->writeNextFrame(inputBuffer); + inputBuffer += channelCount; + inputFramesLeft--; + } else { + resampler->readNextFrame(outputBuffer); + outputBuffer += channelCount; + numOutputFrames++; + } + } + +## Deleting the Resampler + +When you are done, you should delete the Resampler to avoid a memory leak. + + delete resampler; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/ResamplerDefinitions.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/ResamplerDefinitions.h new file mode 100644 index 00000000..c6791ec9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/ResamplerDefinitions.h @@ -0,0 +1,27 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Set flag RESAMPLER_OUTER_NAMESPACE based on whether compiler flag +// __ANDROID_NDK__ is defined. __ANDROID_NDK__ should be defined in oboe +// but not in android. + +#ifndef RESAMPLER_OUTER_NAMESPACE +#ifdef __ANDROID_NDK__ +#define RESAMPLER_OUTER_NAMESPACE oboe +#else +#define RESAMPLER_OUTER_NAMESPACE aaudio +#endif // __ANDROID_NDK__ +#endif // RESAMPLER_OUTER_NAMESPACE diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResampler.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResampler.cpp new file mode 100644 index 00000000..919f328a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResampler.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include // Do NOT delete. Needed for LLVM. See #1746 +#include +#include +#include "SincResampler.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +SincResampler::SincResampler(const MultiChannelResampler::Builder &builder) + : MultiChannelResampler(builder) + , mSingleFrame2(builder.getChannelCount()) { + assert((getNumTaps() % 4) == 0); // Required for loop unrolling. + mNumRows = kMaxCoefficients / getNumTaps(); // includes guard row + const int32_t numRowsNoGuard = mNumRows - 1; + mPhaseScaler = (double) numRowsNoGuard / mDenominator; + const double phaseIncrement = 1.0 / numRowsNoGuard; + generateCoefficients(builder.getInputRate(), + builder.getOutputRate(), + mNumRows, + phaseIncrement, + builder.getNormalizedCutoff()); +} + +void SincResampler::readFrame(float *frame) { + // Clear accumulator for mixing. + std::fill(mSingleFrame.begin(), mSingleFrame.end(), 0.0); + std::fill(mSingleFrame2.begin(), mSingleFrame2.end(), 0.0); + + // Determine indices into coefficients table. + const double tablePhase = getIntegerPhase() * mPhaseScaler; + const int indexLow = static_cast(floor(tablePhase)); + const int indexHigh = indexLow + 1; // OK because using a guard row. + assert (indexHigh < mNumRows); + float *coefficientsLow = &mCoefficients[static_cast(indexLow) + * static_cast(getNumTaps())]; + float *coefficientsHigh = &mCoefficients[static_cast(indexHigh) + * static_cast(getNumTaps())]; + + float *xFrame = &mX[static_cast(mCursor) * static_cast(getChannelCount())]; + for (int tap = 0; tap < mNumTaps; tap++) { + const float coefficientLow = *coefficientsLow++; + const float coefficientHigh = *coefficientsHigh++; + for (int channel = 0; channel < getChannelCount(); channel++) { + const float sample = *xFrame++; + mSingleFrame[channel] += sample * coefficientLow; + mSingleFrame2[channel] += sample * coefficientHigh; + } + } + + // Interpolate and copy to output. + const float fraction = tablePhase - indexLow; + for (int channel = 0; channel < getChannelCount(); channel++) { + const float low = mSingleFrame[channel]; + const float high = mSingleFrame2[channel]; + frame[channel] = low + (fraction * (high - low)); + } +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResampler.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResampler.h new file mode 100644 index 00000000..05ff0921 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResampler.h @@ -0,0 +1,50 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESAMPLER_SINC_RESAMPLER_H +#define RESAMPLER_SINC_RESAMPLER_H + +#include +#include +#include + +#include "MultiChannelResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +/** + * Resampler that can interpolate between coefficients. + * This can be used to support arbitrary ratios. + */ +class SincResampler : public MultiChannelResampler { +public: + explicit SincResampler(const MultiChannelResampler::Builder &builder); + + virtual ~SincResampler() = default; + + void readFrame(float *frame) override; + +protected: + + std::vector mSingleFrame2; // for interpolation + int32_t mNumRows = 0; + double mPhaseScaler = 1.0; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_SINC_RESAMPLER_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResamplerStereo.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResamplerStereo.cpp new file mode 100644 index 00000000..f9287baa --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResamplerStereo.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include // Do NOT delete. Needed for LLVM. See #1746 +#include +#include + +#include "SincResamplerStereo.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +#define STEREO 2 + +SincResamplerStereo::SincResamplerStereo(const MultiChannelResampler::Builder &builder) + : SincResampler(builder) { + assert(builder.getChannelCount() == STEREO); +} + +void SincResamplerStereo::writeFrame(const float *frame) { + // Move cursor before write so that cursor points to last written frame in read. + if (--mCursor < 0) { + mCursor = getNumTaps() - 1; + } + float *dest = &mX[mCursor * STEREO]; + const int offset = mNumTaps * STEREO; + // Write each channel twice so we avoid having to wrap when running the FIR. + const float left = frame[0]; + const float right = frame[1]; + // Put ordered writes together. + dest[0] = left; + dest[1] = right; + dest[offset] = left; + dest[1 + offset] = right; +} + +// Multiply input times windowed sinc function. +void SincResamplerStereo::readFrame(float *frame) { + // Clear accumulator for mixing. + std::fill(mSingleFrame.begin(), mSingleFrame.end(), 0.0); + std::fill(mSingleFrame2.begin(), mSingleFrame2.end(), 0.0); + + // Determine indices into coefficients table. + double tablePhase = getIntegerPhase() * mPhaseScaler; + int index1 = static_cast(floor(tablePhase)); + float *coefficients1 = &mCoefficients[static_cast(index1) + * static_cast(getNumTaps())]; + int index2 = (index1 + 1); + float *coefficients2 = &mCoefficients[static_cast(index2) + * static_cast(getNumTaps())]; + float *xFrame = &mX[static_cast(mCursor) * static_cast(getChannelCount())]; + for (int i = 0; i < mNumTaps; i++) { + float coefficient1 = *coefficients1++; + float coefficient2 = *coefficients2++; + for (int channel = 0; channel < getChannelCount(); channel++) { + float sample = *xFrame++; + mSingleFrame[channel] += sample * coefficient1; + mSingleFrame2[channel] += sample * coefficient2; + } + } + + // Interpolate and copy to output. + float fraction = tablePhase - index1; + for (int channel = 0; channel < getChannelCount(); channel++) { + float low = mSingleFrame[channel]; + float high = mSingleFrame2[channel]; + frame[channel] = low + (fraction * (high - low)); + } +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResamplerStereo.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResamplerStereo.h new file mode 100644 index 00000000..d5576d18 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/flowgraph/resampler/SincResamplerStereo.h @@ -0,0 +1,42 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RESAMPLER_SINC_RESAMPLER_STEREO_H +#define RESAMPLER_SINC_RESAMPLER_STEREO_H + +#include +#include + +#include "SincResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +class SincResamplerStereo : public SincResampler { +public: + explicit SincResamplerStereo(const MultiChannelResampler::Builder &builder); + + virtual ~SincResamplerStereo() = default; + + void writeFrame(const float *frame) override; + + void readFrame(float *frame) override; + +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_SINC_RESAMPLER_STEREO_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioInputStreamOpenSLES.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioInputStreamOpenSLES.cpp new file mode 100644 index 00000000..3653d964 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioInputStreamOpenSLES.cpp @@ -0,0 +1,360 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include "common/OboeDebug.h" +#include "oboe/AudioStreamBuilder.h" +#include "AudioInputStreamOpenSLES.h" +#include "AudioStreamOpenSLES.h" +#include "OpenSLESUtilities.h" + +using namespace oboe; + +static SLuint32 OpenSLES_convertInputPreset(InputPreset oboePreset) { + SLuint32 openslPreset = SL_ANDROID_RECORDING_PRESET_NONE; + switch(oboePreset) { + case InputPreset::Generic: + openslPreset = SL_ANDROID_RECORDING_PRESET_GENERIC; + break; + case InputPreset::Camcorder: + openslPreset = SL_ANDROID_RECORDING_PRESET_CAMCORDER; + break; + case InputPreset::VoiceRecognition: + case InputPreset::VoicePerformance: + openslPreset = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; + break; + case InputPreset::VoiceCommunication: + openslPreset = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; + break; + case InputPreset::Unprocessed: + openslPreset = SL_ANDROID_RECORDING_PRESET_UNPROCESSED; + break; + default: + break; + } + return openslPreset; +} + +AudioInputStreamOpenSLES::AudioInputStreamOpenSLES(const AudioStreamBuilder &builder) + : AudioStreamOpenSLES(builder) { +} + +AudioInputStreamOpenSLES::~AudioInputStreamOpenSLES() { +} + +// Calculate masks specific to INPUT streams. +SLuint32 AudioInputStreamOpenSLES::channelCountToChannelMask(int channelCount) const { + // Derived from internal sles_channel_in_mask_from_count(chanCount); + // in "frameworks/wilhelm/src/android/channels.cpp". + // Yes, it seems strange to use SPEAKER constants to describe inputs. + // But that is how OpenSL ES does it internally. + switch (channelCount) { + case 1: + return SL_SPEAKER_FRONT_LEFT; + case 2: + return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + default: + return channelCountToChannelMaskDefault(channelCount); + } +} + +Result AudioInputStreamOpenSLES::open() { + logUnsupportedAttributes(); + + SLAndroidConfigurationItf configItf = nullptr; + + if (getSdkVersion() < __ANDROID_API_M__ && mFormat == AudioFormat::Float){ + // TODO: Allow floating point format on API <23 using float->int16 converter + return Result::ErrorInvalidFormat; + } + + // If audio format is unspecified then choose a suitable default. + // API 23+: FLOAT + // API <23: INT16 + if (mFormat == AudioFormat::Unspecified){ + mFormat = (getSdkVersion() < __ANDROID_API_M__) ? + AudioFormat::I16 : AudioFormat::Float; + } + + Result oboeResult = AudioStreamOpenSLES::open(); + if (Result::OK != oboeResult) return oboeResult; + + SLuint32 bitsPerSample = static_cast(getBytesPerSample() * kBitsPerByte); + + // configure audio sink + mBufferQueueLength = calculateOptimalBufferQueueLength(); + SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { + SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType + static_cast(mBufferQueueLength)}; // numBuffers + + // Define the audio data format. + SLDataFormat_PCM format_pcm = { + SL_DATAFORMAT_PCM, // formatType + static_cast(mChannelCount), // numChannels + static_cast(mSampleRate * kMillisPerSecond), // milliSamplesPerSec + bitsPerSample, // bitsPerSample + bitsPerSample, // containerSize; + channelCountToChannelMask(mChannelCount), // channelMask + getDefaultByteOrder(), + }; + + SLDataSink audioSink = {&loc_bufq, &format_pcm}; + + /** + * API 23 (Marshmallow) introduced support for floating-point data representation and an + * extended data format type: SLAndroidDataFormat_PCM_EX for recording streams (playback streams + * got this in API 21). If running on API 23+ use this newer format type, creating it from our + * original format. + */ + SLAndroidDataFormat_PCM_EX format_pcm_ex; + if (getSdkVersion() >= __ANDROID_API_M__) { + SLuint32 representation = OpenSLES_ConvertFormatToRepresentation(getFormat()); + // Fill in the format structure. + format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, representation); + // Use in place of the previous format. + audioSink.pFormat = &format_pcm_ex; + } + + + // configure audio source + SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, + SL_IODEVICE_AUDIOINPUT, + SL_DEFAULTDEVICEID_AUDIOINPUT, + NULL}; + SLDataSource audioSrc = {&loc_dev, NULL}; + + SLresult result = EngineOpenSLES::getInstance().createAudioRecorder(&mObjectInterface, + &audioSrc, + &audioSink); + + if (SL_RESULT_SUCCESS != result) { + LOGE("createAudioRecorder() result:%s", getSLErrStr(result)); + goto error; + } + + // Configure the stream. + result = (*mObjectInterface)->GetInterface(mObjectInterface, + SL_IID_ANDROIDCONFIGURATION, + &configItf); + + if (SL_RESULT_SUCCESS != result) { + LOGW("%s() GetInterface(SL_IID_ANDROIDCONFIGURATION) failed with %s", + __func__, getSLErrStr(result)); + } else { + if (getInputPreset() == InputPreset::VoicePerformance) { + LOGD("OpenSL ES does not support InputPreset::VoicePerformance. Use VoiceRecognition."); + mInputPreset = InputPreset::VoiceRecognition; + } + SLuint32 presetValue = OpenSLES_convertInputPreset(getInputPreset()); + result = (*configItf)->SetConfiguration(configItf, + SL_ANDROID_KEY_RECORDING_PRESET, + &presetValue, + sizeof(SLuint32)); + if (SL_RESULT_SUCCESS != result + && presetValue != SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION) { + presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; + LOGD("Setting InputPreset %d failed. Using VoiceRecognition instead.", getInputPreset()); + mInputPreset = InputPreset::VoiceRecognition; + (*configItf)->SetConfiguration(configItf, + SL_ANDROID_KEY_RECORDING_PRESET, + &presetValue, + sizeof(SLuint32)); + } + + result = configurePerformanceMode(configItf); + if (SL_RESULT_SUCCESS != result) { + goto error; + } + } + + result = (*mObjectInterface)->Realize(mObjectInterface, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + LOGE("Realize recorder object result:%s", getSLErrStr(result)); + goto error; + } + + result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_RECORD, &mRecordInterface); + if (SL_RESULT_SUCCESS != result) { + LOGE("GetInterface RECORD result:%s", getSLErrStr(result)); + goto error; + } + + result = finishCommonOpen(configItf); + if (SL_RESULT_SUCCESS != result) { + goto error; + } + + setState(StreamState::Open); + return Result::OK; + +error: + close(); // Clean up various OpenSL objects and prevent resource leaks. + return Result::ErrorInternal; // TODO convert error from SLES to OBOE +} + +Result AudioInputStreamOpenSLES::close() { + LOGD("AudioInputStreamOpenSLES::%s()", __func__); + std::lock_guard lock(mLock); + Result result = Result::OK; + if (getState() == StreamState::Closed){ + result = Result::ErrorClosed; + } else { + (void) requestStop_l(); + if (OboeGlobals::areWorkaroundsEnabled()) { + sleepBeforeClose(); + } + // invalidate any interfaces + mRecordInterface = nullptr; + result = AudioStreamOpenSLES::close_l(); + } + return result; +} + +Result AudioInputStreamOpenSLES::setRecordState_l(SLuint32 newState) { + LOGD("AudioInputStreamOpenSLES::%s(%u)", __func__, newState); + Result result = Result::OK; + + if (mRecordInterface == nullptr) { + LOGW("AudioInputStreamOpenSLES::%s() mRecordInterface is null", __func__); + return Result::ErrorInvalidState; + } + SLresult slResult = (*mRecordInterface)->SetRecordState(mRecordInterface, newState); + //LOGD("AudioInputStreamOpenSLES::%s(%u) returned %u", __func__, newState, slResult); + if (SL_RESULT_SUCCESS != slResult) { + LOGE("AudioInputStreamOpenSLES::%s(%u) returned error %s", + __func__, newState, getSLErrStr(slResult)); + result = Result::ErrorInternal; // TODO review + } + return result; +} + +Result AudioInputStreamOpenSLES::requestStart() { + LOGD("AudioInputStreamOpenSLES(): %s() called", __func__); + std::lock_guard lock(mLock); + StreamState initialState = getState(); + switch (initialState) { + case StreamState::Starting: + case StreamState::Started: + return Result::OK; + case StreamState::Closed: + return Result::ErrorClosed; + default: + break; + } + + // We use a callback if the user requests one + // OR if we have an internal callback to fill the blocking IO buffer. + setDataCallbackEnabled(true); + + setState(StreamState::Starting); + + closePerformanceHint(); + + if (getBufferDepth(mSimpleBufferQueueInterface) == 0) { + // Enqueue the first buffer to start the streaming. + // This does not call the callback function. + enqueueCallbackBuffer(mSimpleBufferQueueInterface); + } + + Result result = setRecordState_l(SL_RECORDSTATE_RECORDING); + if (result == Result::OK) { + setState(StreamState::Started); + } else { + setState(initialState); + } + return result; +} + + +Result AudioInputStreamOpenSLES::requestPause() { + LOGW("AudioInputStreamOpenSLES::%s() is intentionally not implemented for input " + "streams", __func__); + return Result::ErrorUnimplemented; // Matches AAudio behavior. +} + +Result AudioInputStreamOpenSLES::requestFlush() { + LOGW("AudioInputStreamOpenSLES::%s() is intentionally not implemented for input " + "streams", __func__); + return Result::ErrorUnimplemented; // Matches AAudio behavior. +} + +Result AudioInputStreamOpenSLES::requestStop() { + LOGD("AudioInputStreamOpenSLES(): %s() called", __func__); + std::lock_guard lock(mLock); + return requestStop_l(); +} + +// Call under mLock +Result AudioInputStreamOpenSLES::requestStop_l() { + StreamState initialState = getState(); + switch (initialState) { + case StreamState::Stopping: + case StreamState::Stopped: + return Result::OK; + case StreamState::Uninitialized: + case StreamState::Closed: + return Result::ErrorClosed; + default: + break; + } + + setState(StreamState::Stopping); + + Result result = setRecordState_l(SL_RECORDSTATE_STOPPED); + if (result == Result::OK) { + mPositionMillis.reset32(); // OpenSL ES resets its millisecond position when stopped. + setState(StreamState::Stopped); + } else { + setState(initialState); + } + return result; +} + +void AudioInputStreamOpenSLES::updateFramesWritten() { + if (usingFIFO()) { + AudioStreamBuffered::updateFramesWritten(); + } else { + mFramesWritten = getFramesProcessedByServer(); + } +} + +Result AudioInputStreamOpenSLES::updateServiceFrameCounter() { + Result result = Result::OK; + // Avoid deadlock if another thread is trying to stop or close this stream + // and this is being called from a callback. + if (mLock.try_lock()) { + + if (mRecordInterface == nullptr) { + mLock.unlock(); + return Result::ErrorNull; + } + SLmillisecond msec = 0; + SLresult slResult = (*mRecordInterface)->GetPosition(mRecordInterface, &msec); + if (SL_RESULT_SUCCESS != slResult) { + LOGW("%s(): GetPosition() returned %s", __func__, getSLErrStr(slResult)); + // set result based on SLresult + result = Result::ErrorInternal; + } else { + mPositionMillis.update32(msec); + } + mLock.unlock(); + } + return result; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioInputStreamOpenSLES.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioInputStreamOpenSLES.h new file mode 100644 index 00000000..08e7a056 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioInputStreamOpenSLES.h @@ -0,0 +1,66 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUDIO_INPUT_STREAM_OPENSL_ES_H_ +#define AUDIO_INPUT_STREAM_OPENSL_ES_H_ + + +#include +#include + +#include "oboe/Oboe.h" +#include "AudioStreamOpenSLES.h" + +namespace oboe { + +/** + * INTERNAL USE ONLY + */ + +class AudioInputStreamOpenSLES : public AudioStreamOpenSLES { +public: + AudioInputStreamOpenSLES(); + explicit AudioInputStreamOpenSLES(const AudioStreamBuilder &builder); + + virtual ~AudioInputStreamOpenSLES(); + + Result open() override; + Result close() override; + + Result requestStart() override; + Result requestPause() override; + Result requestFlush() override; + Result requestStop() override; + +protected: + Result requestStop_l(); + + Result updateServiceFrameCounter() override; + + void updateFramesWritten() override; + +private: + + SLuint32 channelCountToChannelMask(int chanCount) const; + + Result setRecordState_l(SLuint32 newState); + + SLRecordItf mRecordInterface = nullptr; +}; + +} // namespace oboe + +#endif //AUDIO_INPUT_STREAM_OPENSL_ES_H_ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioOutputStreamOpenSLES.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioOutputStreamOpenSLES.cpp new file mode 100644 index 00000000..91f9882d --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioOutputStreamOpenSLES.cpp @@ -0,0 +1,462 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "common/OboeDebug.h" +#include "oboe/AudioStreamBuilder.h" +#include "AudioOutputStreamOpenSLES.h" +#include "AudioStreamOpenSLES.h" +#include "OpenSLESUtilities.h" +#include "OutputMixerOpenSLES.h" + +using namespace oboe; + +static SLuint32 OpenSLES_convertOutputUsage(Usage oboeUsage) { + SLuint32 openslStream = SL_ANDROID_STREAM_MEDIA; + switch(oboeUsage) { + case Usage::Media: + openslStream = SL_ANDROID_STREAM_MEDIA; + break; + case Usage::VoiceCommunication: + case Usage::VoiceCommunicationSignalling: + openslStream = SL_ANDROID_STREAM_VOICE; + break; + case Usage::Alarm: + openslStream = SL_ANDROID_STREAM_ALARM; + break; + case Usage::Notification: + case Usage::NotificationRingtone: + case Usage::NotificationEvent: + openslStream = SL_ANDROID_STREAM_NOTIFICATION; + break; + case Usage::AssistanceAccessibility: + case Usage::AssistanceNavigationGuidance: + case Usage::AssistanceSonification: + openslStream = SL_ANDROID_STREAM_SYSTEM; + break; + case Usage::Game: + openslStream = SL_ANDROID_STREAM_MEDIA; + break; + case Usage::Assistant: + default: + openslStream = SL_ANDROID_STREAM_SYSTEM; + break; + } + return openslStream; +} + +AudioOutputStreamOpenSLES::AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder) + : AudioStreamOpenSLES(builder) { +} + +// These will wind up in +constexpr int SL_ANDROID_SPEAKER_STEREO = (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); + +constexpr int SL_ANDROID_SPEAKER_QUAD = (SL_ANDROID_SPEAKER_STEREO + | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT); + +constexpr int SL_ANDROID_SPEAKER_5DOT1 = (SL_ANDROID_SPEAKER_QUAD + | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY); + +constexpr int SL_ANDROID_SPEAKER_7DOT1 = (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT + | SL_SPEAKER_SIDE_RIGHT); + +SLuint32 AudioOutputStreamOpenSLES::channelCountToChannelMask(int channelCount) const { + SLuint32 channelMask = 0; + + switch (channelCount) { + case 1: + channelMask = SL_SPEAKER_FRONT_CENTER; + break; + + case 2: + channelMask = SL_ANDROID_SPEAKER_STEREO; + break; + + case 4: // Quad + channelMask = SL_ANDROID_SPEAKER_QUAD; + break; + + case 6: // 5.1 + channelMask = SL_ANDROID_SPEAKER_5DOT1; + break; + + case 8: // 7.1 + channelMask = SL_ANDROID_SPEAKER_7DOT1; + break; + + default: + channelMask = channelCountToChannelMaskDefault(channelCount); + break; + } + return channelMask; +} + +Result AudioOutputStreamOpenSLES::open() { + logUnsupportedAttributes(); + + SLAndroidConfigurationItf configItf = nullptr; + + + if (getSdkVersion() < __ANDROID_API_L__ && mFormat == AudioFormat::Float){ + // TODO: Allow floating point format on API <21 using float->int16 converter + return Result::ErrorInvalidFormat; + } + + // If audio format is unspecified then choose a suitable default. + // API 21+: FLOAT + // API <21: INT16 + if (mFormat == AudioFormat::Unspecified){ + mFormat = (getSdkVersion() < __ANDROID_API_L__) ? + AudioFormat::I16 : AudioFormat::Float; + } + + Result oboeResult = AudioStreamOpenSLES::open(); + if (Result::OK != oboeResult) return oboeResult; + + SLresult result = OutputMixerOpenSL::getInstance().open(); + if (SL_RESULT_SUCCESS != result) { + AudioStreamOpenSLES::close(); + return Result::ErrorInternal; + } + + SLuint32 bitsPerSample = static_cast(getBytesPerSample() * kBitsPerByte); + + // configure audio source + mBufferQueueLength = calculateOptimalBufferQueueLength(); + SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { + SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType + static_cast(mBufferQueueLength)}; // numBuffers + + // Define the audio data format. + SLDataFormat_PCM format_pcm = { + SL_DATAFORMAT_PCM, // formatType + static_cast(mChannelCount), // numChannels + static_cast(mSampleRate * kMillisPerSecond), // milliSamplesPerSec + bitsPerSample, // bitsPerSample + bitsPerSample, // containerSize; + channelCountToChannelMask(mChannelCount), // channelMask + getDefaultByteOrder(), + }; + + SLDataSource audioSrc = {&loc_bufq, &format_pcm}; + + /** + * API 21 (Lollipop) introduced support for floating-point data representation and an extended + * data format type: SLAndroidDataFormat_PCM_EX. If running on API 21+ use this newer format + * type, creating it from our original format. + */ + SLAndroidDataFormat_PCM_EX format_pcm_ex; + if (getSdkVersion() >= __ANDROID_API_L__) { + SLuint32 representation = OpenSLES_ConvertFormatToRepresentation(getFormat()); + // Fill in the format structure. + format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, representation); + // Use in place of the previous format. + audioSrc.pFormat = &format_pcm_ex; + } + + result = OutputMixerOpenSL::getInstance().createAudioPlayer(&mObjectInterface, + &audioSrc); + if (SL_RESULT_SUCCESS != result) { + LOGE("createAudioPlayer() result:%s", getSLErrStr(result)); + goto error; + } + + // Configure the stream. + result = (*mObjectInterface)->GetInterface(mObjectInterface, + SL_IID_ANDROIDCONFIGURATION, + (void *)&configItf); + if (SL_RESULT_SUCCESS != result) { + LOGW("%s() GetInterface(SL_IID_ANDROIDCONFIGURATION) failed with %s", + __func__, getSLErrStr(result)); + } else { + result = configurePerformanceMode(configItf); + if (SL_RESULT_SUCCESS != result) { + goto error; + } + + SLuint32 presetValue = OpenSLES_convertOutputUsage(getUsage()); + result = (*configItf)->SetConfiguration(configItf, + SL_ANDROID_KEY_STREAM_TYPE, + &presetValue, + sizeof(presetValue)); + if (SL_RESULT_SUCCESS != result) { + goto error; + } + } + + result = (*mObjectInterface)->Realize(mObjectInterface, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + LOGE("Realize player object result:%s", getSLErrStr(result)); + goto error; + } + + result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_PLAY, &mPlayInterface); + if (SL_RESULT_SUCCESS != result) { + LOGE("GetInterface PLAY result:%s", getSLErrStr(result)); + goto error; + } + + result = finishCommonOpen(configItf); + if (SL_RESULT_SUCCESS != result) { + goto error; + } + + setState(StreamState::Open); + return Result::OK; + +error: + close(); // Clean up various OpenSL objects and prevent resource leaks. + return Result::ErrorInternal; // TODO convert error from SLES to OBOE +} + +Result AudioOutputStreamOpenSLES::onAfterDestroy() { + OutputMixerOpenSL::getInstance().close(); + return Result::OK; +} + +Result AudioOutputStreamOpenSLES::close() { + LOGD("AudioOutputStreamOpenSLES::%s()", __func__); + std::lock_guard lock(mLock); + Result result = Result::OK; + if (getState() == StreamState::Closed){ + result = Result::ErrorClosed; + } else { + (void) requestPause_l(); + if (OboeGlobals::areWorkaroundsEnabled()) { + sleepBeforeClose(); + } + // invalidate any interfaces + mPlayInterface = nullptr; + result = AudioStreamOpenSLES::close_l(); + } + return result; +} + +Result AudioOutputStreamOpenSLES::setPlayState_l(SLuint32 newState) { + + LOGD("AudioOutputStreamOpenSLES(): %s() called", __func__); + Result result = Result::OK; + + if (mPlayInterface == nullptr){ + LOGE("AudioOutputStreamOpenSLES::%s() mPlayInterface is null", __func__); + return Result::ErrorInvalidState; + } + + SLresult slResult = (*mPlayInterface)->SetPlayState(mPlayInterface, newState); + if (SL_RESULT_SUCCESS != slResult) { + LOGW("AudioOutputStreamOpenSLES(): %s() returned %s", __func__, getSLErrStr(slResult)); + result = Result::ErrorInternal; // TODO convert slResult to Result::Error + } + return result; +} + +Result AudioOutputStreamOpenSLES::requestStart() { + LOGD("AudioOutputStreamOpenSLES(): %s() called", __func__); + + mLock.lock(); + StreamState initialState = getState(); + switch (initialState) { + case StreamState::Starting: + case StreamState::Started: + mLock.unlock(); + return Result::OK; + case StreamState::Closed: + mLock.unlock(); + return Result::ErrorClosed; + default: + break; + } + + // We use a callback if the user requests one + // OR if we have an internal callback to read the blocking IO buffer. + setDataCallbackEnabled(true); + + setState(StreamState::Starting); + closePerformanceHint(); + + if (getBufferDepth(mSimpleBufferQueueInterface) == 0) { + // Enqueue the first buffer if needed to start the streaming. + // We may need to stop the current stream. + bool shouldStopStream = processBufferCallback(mSimpleBufferQueueInterface); + if (shouldStopStream) { + LOGD("Stopping the current stream."); + if (requestStop_l() != Result::OK) { + LOGW("Failed to flush the stream. Error %s", convertToText(flush())); + } + setState(initialState); + mLock.unlock(); + return Result::ErrorClosed; + } + } + + Result result = setPlayState_l(SL_PLAYSTATE_PLAYING); + if (result == Result::OK) { + setState(StreamState::Started); + mLock.unlock(); + } else { + setState(initialState); + mLock.unlock(); + } + return result; +} + +Result AudioOutputStreamOpenSLES::requestPause() { + LOGD("AudioOutputStreamOpenSLES(): %s() called", __func__); + std::lock_guard lock(mLock); + return requestPause_l(); +} + +// Call under mLock +Result AudioOutputStreamOpenSLES::requestPause_l() { + StreamState initialState = getState(); + switch (initialState) { + case StreamState::Pausing: + case StreamState::Paused: + return Result::OK; + case StreamState::Uninitialized: + case StreamState::Closed: + return Result::ErrorClosed; + default: + break; + } + + setState(StreamState::Pausing); + Result result = setPlayState_l(SL_PLAYSTATE_PAUSED); + if (result == Result::OK) { + // Note that OpenSL ES does NOT reset its millisecond position when OUTPUT is paused. + int64_t framesWritten = getFramesWritten(); + if (framesWritten >= 0) { + setFramesRead(framesWritten); + } + setState(StreamState::Paused); + } else { + setState(initialState); + } + return result; +} + +/** + * Flush/clear the queue buffers + */ +Result AudioOutputStreamOpenSLES::requestFlush() { + std::lock_guard lock(mLock); + return requestFlush_l(); +} + +Result AudioOutputStreamOpenSLES::requestFlush_l() { + LOGD("AudioOutputStreamOpenSLES(): %s() called", __func__); + if (getState() == StreamState::Closed) { + return Result::ErrorClosed; + } + + Result result = Result::OK; + if (mPlayInterface == nullptr || mSimpleBufferQueueInterface == nullptr) { + result = Result::ErrorInvalidState; + } else { + SLresult slResult = (*mSimpleBufferQueueInterface)->Clear(mSimpleBufferQueueInterface); + if (slResult != SL_RESULT_SUCCESS){ + LOGW("Failed to clear buffer queue. OpenSLES error: %d", result); + result = Result::ErrorInternal; + } + } + return result; +} + +Result AudioOutputStreamOpenSLES::requestStop() { + std::lock_guard lock(mLock); + return requestStop_l(); +} + +Result AudioOutputStreamOpenSLES::requestStop_l() { + LOGD("AudioOutputStreamOpenSLES(): %s() called", __func__); + + StreamState initialState = getState(); + switch (initialState) { + case StreamState::Stopping: + case StreamState::Stopped: + return Result::OK; + case StreamState::Uninitialized: + case StreamState::Closed: + return Result::ErrorClosed; + default: + break; + } + + setState(StreamState::Stopping); + + Result result = setPlayState_l(SL_PLAYSTATE_STOPPED); + if (result == Result::OK) { + + // Also clear the buffer queue so the old data won't be played if the stream is restarted. + // Call the _l function that expects to already be under a lock. + if (requestFlush_l() != Result::OK) { + LOGW("Failed to flush the stream. Error %s", convertToText(flush())); + } + + mPositionMillis.reset32(); // OpenSL ES resets its millisecond position when stopped. + int64_t framesWritten = getFramesWritten(); + if (framesWritten >= 0) { + setFramesRead(framesWritten); + } + setState(StreamState::Stopped); + } else { + setState(initialState); + } + return result; +} + +void AudioOutputStreamOpenSLES::setFramesRead(int64_t framesRead) { + int64_t millisWritten = framesRead * kMillisPerSecond / getSampleRate(); + mPositionMillis.set(millisWritten); +} + +void AudioOutputStreamOpenSLES::updateFramesRead() { + if (usingFIFO()) { + AudioStreamBuffered::updateFramesRead(); + } else { + mFramesRead = getFramesProcessedByServer(); + } +} + +Result AudioOutputStreamOpenSLES::updateServiceFrameCounter() { + Result result = Result::OK; + // Avoid deadlock if another thread is trying to stop or close this stream + // and this is being called from a callback. + if (mLock.try_lock()) { + + if (mPlayInterface == nullptr) { + mLock.unlock(); + return Result::ErrorNull; + } + SLmillisecond msec = 0; + SLresult slResult = (*mPlayInterface)->GetPosition(mPlayInterface, &msec); + if (SL_RESULT_SUCCESS != slResult) { + LOGW("%s(): GetPosition() returned %s", __func__, getSLErrStr(slResult)); + // set result based on SLresult + result = Result::ErrorInternal; + } else { + mPositionMillis.update32(msec); + } + mLock.unlock(); + } + return result; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioOutputStreamOpenSLES.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioOutputStreamOpenSLES.h new file mode 100644 index 00000000..fc57fd37 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioOutputStreamOpenSLES.h @@ -0,0 +1,80 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUDIO_OUTPUT_STREAM_OPENSL_ES_H_ +#define AUDIO_OUTPUT_STREAM_OPENSL_ES_H_ + + +#include +#include + +#include "oboe/Oboe.h" +#include "AudioStreamOpenSLES.h" + +namespace oboe { + +/** + * INTERNAL USE ONLY + */ +class AudioOutputStreamOpenSLES : public AudioStreamOpenSLES { +public: + AudioOutputStreamOpenSLES(); + explicit AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder); + + virtual ~AudioOutputStreamOpenSLES() = default; + + Result open() override; + Result close() override; + + Result requestStart() override; + Result requestPause() override; + Result requestFlush() override; + Result requestStop() override; + +protected: + Result requestPause_l(); + + void setFramesRead(int64_t framesRead); + + Result updateServiceFrameCounter() override; + + void updateFramesRead() override; + +private: + + SLuint32 channelCountToChannelMask(int chanCount) const; + + Result onAfterDestroy() override; + + Result requestFlush_l(); + + Result requestStop_l(); + + /** + * Set OpenSL ES PLAYSTATE. + * + * @param newState SL_PLAYSTATE_PAUSED, SL_PLAYSTATE_PLAYING, SL_PLAYSTATE_STOPPED + * @return + */ + Result setPlayState_l(SLuint32 newState); + + SLPlayItf mPlayInterface = nullptr; + +}; + +} // namespace oboe + +#endif //AUDIO_OUTPUT_STREAM_OPENSL_ES_H_ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamBuffered.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamBuffered.cpp new file mode 100644 index 00000000..9737b72b --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamBuffered.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "oboe/Oboe.h" + +#include "common/OboeDebug.h" +#include "opensles/AudioStreamBuffered.h" +#include "common/AudioClock.h" + +namespace oboe { + +constexpr int kDefaultBurstsPerBuffer = 16; // arbitrary, allows dynamic latency tuning +constexpr int kMinBurstsPerBuffer = 4; // arbitrary, allows dynamic latency tuning +constexpr int kMinFramesPerBuffer = 48 * 32; // arbitrary + +/* + * AudioStream with a FifoBuffer + */ +AudioStreamBuffered::AudioStreamBuffered(const AudioStreamBuilder &builder) + : AudioStream(builder) { +} + +void AudioStreamBuffered::allocateFifo() { + // If the caller does not provide a callback use our own internal + // callback that reads data from the FIFO. + if (usingFIFO()) { + // FIFO is configured with the same format and channels as the stream. + int32_t capacityFrames = getBufferCapacityInFrames(); + if (capacityFrames == oboe::kUnspecified) { + capacityFrames = getFramesPerBurst() * kDefaultBurstsPerBuffer; + } else { + int32_t minFramesPerBufferByBursts = getFramesPerBurst() * kMinBurstsPerBuffer; + if (capacityFrames <= minFramesPerBufferByBursts) { + capacityFrames = minFramesPerBufferByBursts; + } else { + capacityFrames = std::max(kMinFramesPerBuffer, capacityFrames); + // round up to nearest burst + int32_t numBursts = (capacityFrames + getFramesPerBurst() - 1) + / getFramesPerBurst(); + capacityFrames = numBursts * getFramesPerBurst(); + } + } + + mFifoBuffer = std::make_unique(getBytesPerFrame(), capacityFrames); + mBufferCapacityInFrames = capacityFrames; + mBufferSizeInFrames = mBufferCapacityInFrames; + } +} + +void AudioStreamBuffered::updateFramesWritten() { + if (mFifoBuffer) { + mFramesWritten = static_cast(mFifoBuffer->getWriteCounter()); + } // or else it will get updated by processBufferCallback() +} + +void AudioStreamBuffered::updateFramesRead() { + if (mFifoBuffer) { + mFramesRead = static_cast(mFifoBuffer->getReadCounter()); + } // or else it will get updated by processBufferCallback() +} + +// This is called by the OpenSL ES callback to read or write the back end of the FIFO. +DataCallbackResult AudioStreamBuffered::onDefaultCallback(void *audioData, int numFrames) { + int32_t framesTransferred = 0; + + if (getDirection() == oboe::Direction::Output) { + // Read from the FIFO and write to audioData, clear part of buffer if not enough data. + framesTransferred = mFifoBuffer->readNow(audioData, numFrames); + } else { + // Read from audioData and write to the FIFO + framesTransferred = mFifoBuffer->write(audioData, numFrames); // There is no writeNow() + } + + if (framesTransferred < numFrames) { + LOGD("AudioStreamBuffered::%s(): xrun! framesTransferred = %d, numFrames = %d", + __func__, framesTransferred, numFrames); + // TODO If we do not allow FIFO to wrap then our timestamps will drift when there is an XRun! + incrementXRunCount(); + } + markCallbackTime(static_cast(numFrames)); // so foreground knows how long to wait. + return DataCallbackResult::Continue; +} + +void AudioStreamBuffered::markCallbackTime(int32_t numFrames) { + mLastBackgroundSize = numFrames; + mBackgroundRanAtNanoseconds = AudioClock::getNanoseconds(); +} + +int64_t AudioStreamBuffered::predictNextCallbackTime() { + if (mBackgroundRanAtNanoseconds == 0) { + return 0; + } + int64_t nanosPerBuffer = (kNanosPerSecond * mLastBackgroundSize) / getSampleRate(); + const int64_t margin = 200 * kNanosPerMicrosecond; // arbitrary delay so we wake up just after + return mBackgroundRanAtNanoseconds + nanosPerBuffer + margin; +} + +// Common code for read/write. +// @return Result::OK with frames read/written, or Result::Error* +ResultWithValue AudioStreamBuffered::transfer( + void *readBuffer, + const void *writeBuffer, + int32_t numFrames, + int64_t timeoutNanoseconds) { + // Validate arguments. + if (readBuffer != nullptr && writeBuffer != nullptr) { + LOGE("AudioStreamBuffered::%s(): both buffers are not NULL", __func__); + return ResultWithValue(Result::ErrorInternal); + } + if (getDirection() == Direction::Input && readBuffer == nullptr) { + LOGE("AudioStreamBuffered::%s(): readBuffer is NULL", __func__); + return ResultWithValue(Result::ErrorNull); + } + if (getDirection() == Direction::Output && writeBuffer == nullptr) { + LOGE("AudioStreamBuffered::%s(): writeBuffer is NULL", __func__); + return ResultWithValue(Result::ErrorNull); + } + if (numFrames < 0) { + LOGE("AudioStreamBuffered::%s(): numFrames is negative", __func__); + return ResultWithValue(Result::ErrorOutOfRange); + } else if (numFrames == 0) { + return ResultWithValue(numFrames); + } + if (timeoutNanoseconds < 0) { + LOGE("AudioStreamBuffered::%s(): timeoutNanoseconds is negative", __func__); + return ResultWithValue(Result::ErrorOutOfRange); + } + + int32_t result = 0; + uint8_t *readData = reinterpret_cast(readBuffer); + const uint8_t *writeData = reinterpret_cast(writeBuffer); + int32_t framesLeft = numFrames; + int64_t timeToQuit = 0; + bool repeat = true; + + // Calculate when to timeout. + if (timeoutNanoseconds > 0) { + timeToQuit = AudioClock::getNanoseconds() + timeoutNanoseconds; + } + + // Loop until we get the data, or we have an error, or we timeout. + do { + // read or write + if (getDirection() == Direction::Input) { + result = mFifoBuffer->read(readData, framesLeft); + if (result > 0) { + readData += mFifoBuffer->convertFramesToBytes(result); + framesLeft -= result; + } + } else { + // between zero and capacity + uint32_t fullFrames = mFifoBuffer->getFullFramesAvailable(); + // Do not write above threshold size. + int32_t emptyFrames = getBufferSizeInFrames() - static_cast(fullFrames); + int32_t framesToWrite = std::max(0, std::min(framesLeft, emptyFrames)); + result = mFifoBuffer->write(writeData, framesToWrite); + if (result > 0) { + writeData += mFifoBuffer->convertFramesToBytes(result); + framesLeft -= result; + } + } + + // If we need more data then sleep and try again. + if (framesLeft > 0 && result >= 0 && timeoutNanoseconds > 0) { + int64_t timeNow = AudioClock::getNanoseconds(); + if (timeNow >= timeToQuit) { + LOGE("AudioStreamBuffered::%s(): TIMEOUT", __func__); + repeat = false; // TIMEOUT + } else { + // Figure out how long to sleep. + int64_t sleepForNanos; + int64_t wakeTimeNanos = predictNextCallbackTime(); + if (wakeTimeNanos <= 0) { + // No estimate available. Sleep for one burst. + sleepForNanos = (getFramesPerBurst() * kNanosPerSecond) / getSampleRate(); + } else { + // Don't sleep past timeout. + if (wakeTimeNanos > timeToQuit) { + wakeTimeNanos = timeToQuit; + } + sleepForNanos = wakeTimeNanos - timeNow; + // Avoid rapid loop with no sleep. + const int64_t minSleepTime = kNanosPerMillisecond; // arbitrary + if (sleepForNanos < minSleepTime) { + sleepForNanos = minSleepTime; + } + } + + AudioClock::sleepForNanos(sleepForNanos); + } + + } else { + repeat = false; + } + } while(repeat); + + if (result < 0) { + return ResultWithValue(static_cast(result)); + } else { + int32_t framesWritten = numFrames - framesLeft; + return ResultWithValue(framesWritten); + } +} + +// Write to the FIFO so the callback can read from it. +ResultWithValue AudioStreamBuffered::write(const void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) { + if (getState() == StreamState::Closed){ + return ResultWithValue(Result::ErrorClosed); + } + + if (getDirection() == Direction::Input) { + return ResultWithValue(Result::ErrorUnavailable); // TODO review, better error code? + } + Result result = updateServiceFrameCounter(); + if (result != Result::OK) return ResultWithValue(static_cast(result)); + return transfer(nullptr, buffer, numFrames, timeoutNanoseconds); +} + +// Read data from the FIFO that was written by the callback. +ResultWithValue AudioStreamBuffered::read(void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) { + if (getState() == StreamState::Closed){ + return ResultWithValue(Result::ErrorClosed); + } + + if (getDirection() == Direction::Output) { + return ResultWithValue(Result::ErrorUnavailable); // TODO review, better error code? + } + Result result = updateServiceFrameCounter(); + if (result != Result::OK) return ResultWithValue(static_cast(result)); + return transfer(buffer, nullptr, numFrames, timeoutNanoseconds); +} + +// Only supported when we are not using a callback. +ResultWithValue AudioStreamBuffered::setBufferSizeInFrames(int32_t requestedFrames) +{ + if (getState() == StreamState::Closed){ + return ResultWithValue(Result::ErrorClosed); + } + + if (!mFifoBuffer) { + return ResultWithValue(Result::ErrorUnimplemented); + } + + if (requestedFrames > mFifoBuffer->getBufferCapacityInFrames()) { + requestedFrames = mFifoBuffer->getBufferCapacityInFrames(); + } else if (requestedFrames < getFramesPerBurst()) { + requestedFrames = getFramesPerBurst(); + } + mBufferSizeInFrames = requestedFrames; + return ResultWithValue(requestedFrames); +} + +int32_t AudioStreamBuffered::getBufferCapacityInFrames() const { + if (mFifoBuffer) { + return mFifoBuffer->getBufferCapacityInFrames(); + } else { + return AudioStream::getBufferCapacityInFrames(); + } +} + +bool AudioStreamBuffered::isXRunCountSupported() const { + // XRun count is only supported if we're using blocking I/O (not callbacks) + return (!isDataCallbackSpecified()); +} + +} // namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamBuffered.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamBuffered.h new file mode 100644 index 00000000..3080ce61 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamBuffered.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_STREAM_BUFFERED_H +#define OBOE_STREAM_BUFFERED_H + +#include +#include +#include "common/OboeDebug.h" +#include "oboe/AudioStream.h" +#include "oboe/AudioStreamCallback.h" +#include "oboe/FifoBuffer.h" + +namespace oboe { + +// A stream that contains a FIFO buffer. +// This is used to implement blocking reads and writes. +class AudioStreamBuffered : public AudioStream { +public: + + AudioStreamBuffered(); + explicit AudioStreamBuffered(const AudioStreamBuilder &builder); + + void allocateFifo(); + + + ResultWithValue write(const void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) override; + + ResultWithValue read(void *buffer, + int32_t numFrames, + int64_t timeoutNanoseconds) override; + + ResultWithValue setBufferSizeInFrames(int32_t requestedFrames) override; + + int32_t getBufferCapacityInFrames() const override; + + ResultWithValue getXRunCount() override { + return ResultWithValue(mXRunCount); + } + + bool isXRunCountSupported() const override; + +protected: + + DataCallbackResult onDefaultCallback(void *audioData, int numFrames) override; + + // If there is no callback then we need a FIFO between the App and OpenSL ES. + bool usingFIFO() const { return !isDataCallbackSpecified(); } + + virtual Result updateServiceFrameCounter() = 0; + + void updateFramesRead() override; + void updateFramesWritten() override; + +private: + + int64_t predictNextCallbackTime(); + + void markCallbackTime(int32_t numFrames); + + // Read or write to the FIFO. + // Only pass one pointer and set the other to nullptr. + ResultWithValue transfer(void *readBuffer, + const void *writeBuffer, + int32_t numFrames, + int64_t timeoutNanoseconds); + + void incrementXRunCount() { + ++mXRunCount; + } + + std::unique_ptr mFifoBuffer{}; + + int64_t mBackgroundRanAtNanoseconds = 0; + int32_t mLastBackgroundSize = 0; + int32_t mXRunCount = 0; +}; + +} // namespace oboe + +#endif //OBOE_STREAM_BUFFERED_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.cpp new file mode 100644 index 00000000..9013d61c --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.cpp @@ -0,0 +1,499 @@ +/* Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include +#include +#include +#include + +#include "common/OboeDebug.h" +#include "oboe/AudioStreamBuilder.h" +#include "AudioStreamOpenSLES.h" +#include "OpenSLESUtilities.h" + +using namespace oboe; + +AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder) + : AudioStreamBuffered(builder) { + // OpenSL ES does not support device IDs. So overwrite value from builder. + mDeviceId = kUnspecified; + // OpenSL ES does not support session IDs. So overwrite value from builder. + mSessionId = SessionId::None; +} + +static constexpr int32_t kHighLatencyBufferSizeMillis = 20; // typical Android period +static constexpr SLuint32 kAudioChannelCountMax = 30; // TODO Why 30? +static constexpr SLuint32 SL_ANDROID_UNKNOWN_CHANNELMASK = 0; // Matches name used internally. + +SLuint32 AudioStreamOpenSLES::channelCountToChannelMaskDefault(int channelCount) const { + if (channelCount > kAudioChannelCountMax) { + return SL_ANDROID_UNKNOWN_CHANNELMASK; + } + + SLuint32 bitfield = (1 << channelCount) - 1; + + // Check for OS at run-time. + if(getSdkVersion() >= __ANDROID_API_N__) { + return SL_ANDROID_MAKE_INDEXED_CHANNEL_MASK(bitfield); + } + + // Indexed channels masks were added in N. + // For before N, the best we can do is use a positional channel mask. + return bitfield; +} + +static bool s_isLittleEndian() { + static uint32_t value = 1; + return (*reinterpret_cast(&value) == 1); // Does address point to LSB? +} + +SLuint32 AudioStreamOpenSLES::getDefaultByteOrder() { + return s_isLittleEndian() ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; +} + +Result AudioStreamOpenSLES::open() { + + LOGI("AudioStreamOpenSLES::open() chans=%d, rate=%d", mChannelCount, mSampleRate); + + // OpenSL ES only supports I16 and Float + if (mFormat != AudioFormat::I16 && mFormat != AudioFormat::Float) { + LOGW("%s() Android's OpenSL ES implementation only supports I16 and Float. Format: %d", + __func__, mFormat); + return Result::ErrorInvalidFormat; + } + + SLresult result = EngineOpenSLES::getInstance().open(); + if (SL_RESULT_SUCCESS != result) { + return Result::ErrorInternal; + } + + Result oboeResult = AudioStreamBuffered::open(); + if (oboeResult != Result::OK) { + EngineOpenSLES::getInstance().close(); + return oboeResult; + } + // Convert to defaults if UNSPECIFIED + if (mSampleRate == kUnspecified) { + mSampleRate = DefaultStreamValues::SampleRate; + } + if (mChannelCount == kUnspecified) { + mChannelCount = DefaultStreamValues::ChannelCount; + } + if (mContentType == kUnspecified) { + mContentType = ContentType::Music; + } + if (static_cast(mUsage) == kUnspecified) { + mUsage = Usage::Media; + } + + mSharingMode = SharingMode::Shared; + + return Result::OK; +} + + +SLresult AudioStreamOpenSLES::finishCommonOpen(SLAndroidConfigurationItf configItf) { + // Setting privacy sensitive mode and allowed capture policy are not supported for OpenSL ES. + mPrivacySensitiveMode = PrivacySensitiveMode::Unspecified; + mAllowedCapturePolicy = AllowedCapturePolicy::Unspecified; + + // Spatialization Behavior is not supported for OpenSL ES. + mSpatializationBehavior = SpatializationBehavior::Never; + + SLresult result = registerBufferQueueCallback(); + if (SL_RESULT_SUCCESS != result) { + return result; + } + + result = updateStreamParameters(configItf); + if (SL_RESULT_SUCCESS != result) { + return result; + } + + Result oboeResult = configureBufferSizes(mSampleRate); + if (Result::OK != oboeResult) { + return (SLresult) oboeResult; + } + + allocateFifo(); + + calculateDefaultDelayBeforeCloseMillis(); + + return SL_RESULT_SUCCESS; +} + +static int32_t roundUpDivideByN(int32_t x, int32_t n) { + return (x + n - 1) / n; +} + +int32_t AudioStreamOpenSLES::calculateOptimalBufferQueueLength() { + int32_t queueLength = kBufferQueueLengthDefault; + int32_t likelyFramesPerBurst = estimateNativeFramesPerBurst(); + int32_t minCapacity = mBufferCapacityInFrames; // specified by app or zero + // The buffer capacity needs to be at least twice the size of the requested callbackSize + // so that we can have double buffering. + minCapacity = std::max(minCapacity, kDoubleBufferCount * mFramesPerCallback); + if (minCapacity > 0) { + int32_t queueLengthFromCapacity = roundUpDivideByN(minCapacity, likelyFramesPerBurst); + queueLength = std::max(queueLength, queueLengthFromCapacity); + } + queueLength = std::min(queueLength, kBufferQueueLengthMax); // clip to max + // TODO Investigate the effect of queueLength on latency for normal streams. (not low latency) + return queueLength; +} + +/** + * The best information we have is if DefaultStreamValues::FramesPerBurst + * was set by the app based on AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER. + * Without that we just have to guess. + * @return + */ +int32_t AudioStreamOpenSLES::estimateNativeFramesPerBurst() { + int32_t framesPerBurst = DefaultStreamValues::FramesPerBurst; + LOGD("AudioStreamOpenSLES:%s() DefaultStreamValues::FramesPerBurst = %d", + __func__, DefaultStreamValues::FramesPerBurst); + framesPerBurst = std::max(framesPerBurst, 16); + // Calculate the size of a fixed duration high latency buffer based on sample rate. + // Estimate sample based on default options in order of priority. + int32_t sampleRate = 48000; + sampleRate = (DefaultStreamValues::SampleRate > 0) + ? DefaultStreamValues::SampleRate : sampleRate; + sampleRate = (mSampleRate > 0) ? mSampleRate : sampleRate; + int32_t framesPerHighLatencyBuffer = + (kHighLatencyBufferSizeMillis * sampleRate) / kMillisPerSecond; + // For high latency streams, use a larger buffer size. + // Performance Mode support was added in N_MR1 (7.1) + if (getSdkVersion() >= __ANDROID_API_N_MR1__ + && mPerformanceMode != PerformanceMode::LowLatency + && framesPerBurst < framesPerHighLatencyBuffer) { + // Find a multiple of framesPerBurst >= framesPerHighLatencyBuffer. + int32_t numBursts = roundUpDivideByN(framesPerHighLatencyBuffer, framesPerBurst); + framesPerBurst *= numBursts; + LOGD("AudioStreamOpenSLES:%s() NOT low latency, numBursts = %d, mSampleRate = %d, set framesPerBurst = %d", + __func__, numBursts, mSampleRate, framesPerBurst); + } + return framesPerBurst; +} + +Result AudioStreamOpenSLES::configureBufferSizes(int32_t sampleRate) { + LOGD("AudioStreamOpenSLES:%s(%d) initial mFramesPerBurst = %d, mFramesPerCallback = %d", + __func__, mSampleRate, mFramesPerBurst, mFramesPerCallback); + mFramesPerBurst = estimateNativeFramesPerBurst(); + mFramesPerCallback = (mFramesPerCallback > 0) ? mFramesPerCallback : mFramesPerBurst; + LOGD("AudioStreamOpenSLES:%s(%d) final mFramesPerBurst = %d, mFramesPerCallback = %d", + __func__, mSampleRate, mFramesPerBurst, mFramesPerCallback); + + mBytesPerCallback = mFramesPerCallback * getBytesPerFrame(); + if (mBytesPerCallback <= 0) { + LOGE("AudioStreamOpenSLES::open() bytesPerCallback < 0 = %d, bad format?", + mBytesPerCallback); + return Result::ErrorInvalidFormat; // causing bytesPerFrame == 0 + } + + for (int i = 0; i < mBufferQueueLength; ++i) { + mCallbackBuffer[i] = std::make_unique(mBytesPerCallback); + } + + if (!usingFIFO()) { + mBufferCapacityInFrames = mFramesPerBurst * mBufferQueueLength; + // Check for overflow. + if (mBufferCapacityInFrames <= 0) { + mBufferCapacityInFrames = 0; + LOGE("AudioStreamOpenSLES::open() numeric overflow because mFramesPerBurst = %d", + mFramesPerBurst); + return Result::ErrorOutOfRange; + } + mBufferSizeInFrames = mBufferCapacityInFrames; + } + + return Result::OK; +} + +SLuint32 AudioStreamOpenSLES::convertPerformanceMode(PerformanceMode oboeMode) const { + SLuint32 openslMode = SL_ANDROID_PERFORMANCE_NONE; + switch(oboeMode) { + case PerformanceMode::None: + openslMode = SL_ANDROID_PERFORMANCE_NONE; + break; + case PerformanceMode::LowLatency: + openslMode = (getSessionId() == SessionId::None) ? SL_ANDROID_PERFORMANCE_LATENCY : SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS; + break; + case PerformanceMode::PowerSaving: + openslMode = SL_ANDROID_PERFORMANCE_POWER_SAVING; + break; + default: + break; + } + return openslMode; +} + +PerformanceMode AudioStreamOpenSLES::convertPerformanceMode(SLuint32 openslMode) const { + PerformanceMode oboeMode = PerformanceMode::None; + switch(openslMode) { + case SL_ANDROID_PERFORMANCE_NONE: + oboeMode = PerformanceMode::None; + break; + case SL_ANDROID_PERFORMANCE_LATENCY: + case SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS: + oboeMode = PerformanceMode::LowLatency; + break; + case SL_ANDROID_PERFORMANCE_POWER_SAVING: + oboeMode = PerformanceMode::PowerSaving; + break; + default: + break; + } + return oboeMode; +} + +void AudioStreamOpenSLES::logUnsupportedAttributes() { + // Log unsupported attributes + // only report if changed from the default + + // Device ID + if (mDeviceId != kUnspecified) { + LOGW("Device ID [AudioStreamBuilder::setDeviceId()] " + "is not supported on OpenSLES streams."); + } + // Sharing Mode + if (mSharingMode != SharingMode::Shared) { + LOGW("SharingMode [AudioStreamBuilder::setSharingMode()] " + "is not supported on OpenSLES streams."); + } + // Performance Mode + int sdkVersion = getSdkVersion(); + if (mPerformanceMode != PerformanceMode::None && sdkVersion < __ANDROID_API_N_MR1__) { + LOGW("PerformanceMode [AudioStreamBuilder::setPerformanceMode()] " + "is not supported on OpenSLES streams running on pre-Android N-MR1 versions."); + } + // Content Type + if (mContentType != ContentType::Music) { + LOGW("ContentType [AudioStreamBuilder::setContentType()] " + "is not supported on OpenSLES streams."); + } + + // Session Id + if (mSessionId != SessionId::None) { + LOGW("SessionId [AudioStreamBuilder::setSessionId()] " + "is not supported on OpenSLES streams."); + } + + // Privacy Sensitive Mode + if (mPrivacySensitiveMode != PrivacySensitiveMode::Unspecified) { + LOGW("PrivacySensitiveMode [AudioStreamBuilder::setPrivacySensitiveMode()] " + "is not supported on OpenSLES streams."); + } + + // Spatialization Behavior + if (mSpatializationBehavior != SpatializationBehavior::Unspecified) { + LOGW("SpatializationBehavior [AudioStreamBuilder::setSpatializationBehavior()] " + "is not supported on OpenSLES streams."); + } + + // Allowed Capture Policy + if (mAllowedCapturePolicy != AllowedCapturePolicy::Unspecified) { + LOGW("AllowedCapturePolicy [AudioStreamBuilder::setAllowedCapturePolicy()] " + "is not supported on OpenSLES streams."); + } +} + +SLresult AudioStreamOpenSLES::configurePerformanceMode(SLAndroidConfigurationItf configItf) { + + if (configItf == nullptr) { + LOGW("%s() called with NULL configuration", __func__); + mPerformanceMode = PerformanceMode::None; + return SL_RESULT_INTERNAL_ERROR; + } + if (getSdkVersion() < __ANDROID_API_N_MR1__) { + LOGW("%s() not supported until N_MR1", __func__); + mPerformanceMode = PerformanceMode::None; + return SL_RESULT_SUCCESS; + } + + SLresult result = SL_RESULT_SUCCESS; + SLuint32 performanceMode = convertPerformanceMode(getPerformanceMode()); + result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE, + &performanceMode, sizeof(performanceMode)); + if (SL_RESULT_SUCCESS != result) { + LOGW("SetConfiguration(PERFORMANCE_MODE, SL %u) returned %s", + performanceMode, getSLErrStr(result)); + mPerformanceMode = PerformanceMode::None; + } + + return result; +} + +SLresult AudioStreamOpenSLES::updateStreamParameters(SLAndroidConfigurationItf configItf) { + SLresult result = SL_RESULT_SUCCESS; + if(getSdkVersion() >= __ANDROID_API_N_MR1__ && configItf != nullptr) { + SLuint32 performanceMode = 0; + SLuint32 performanceModeSize = sizeof(performanceMode); + result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE, + &performanceModeSize, &performanceMode); + // A bug in GetConfiguration() before P caused a wrong result code to be returned. + if (getSdkVersion() <= __ANDROID_API_O_MR1__) { + result = SL_RESULT_SUCCESS; // Ignore actual result before P. + } + + if (SL_RESULT_SUCCESS != result) { + LOGW("GetConfiguration(SL_ANDROID_KEY_PERFORMANCE_MODE) returned %d", result); + mPerformanceMode = PerformanceMode::None; // If we can't query it then assume None. + } else { + mPerformanceMode = convertPerformanceMode(performanceMode); // convert SL to Oboe mode + } + } else { + mPerformanceMode = PerformanceMode::None; // If we can't query it then assume None. + } + return result; +} + +// This is called under mLock. +Result AudioStreamOpenSLES::close_l() { + if (mState == StreamState::Closed) { + return Result::ErrorClosed; + } + + AudioStreamBuffered::close(); + + onBeforeDestroy(); + + if (mObjectInterface != nullptr) { + (*mObjectInterface)->Destroy(mObjectInterface); + mObjectInterface = nullptr; + } + + onAfterDestroy(); + + mSimpleBufferQueueInterface = nullptr; + EngineOpenSLES::getInstance().close(); + + setState(StreamState::Closed); + + return Result::OK; +} + +SLresult AudioStreamOpenSLES::enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq) { + SLresult result = (*bq)->Enqueue( + bq, mCallbackBuffer[mCallbackBufferIndex].get(), mBytesPerCallback); + mCallbackBufferIndex = (mCallbackBufferIndex + 1) % mBufferQueueLength; + return result; +} + +int32_t AudioStreamOpenSLES::getBufferDepth(SLAndroidSimpleBufferQueueItf bq) { + SLAndroidSimpleBufferQueueState queueState; + SLresult result = (*bq)->GetState(bq, &queueState); + return (result == SL_RESULT_SUCCESS) ? queueState.count : -1; +} + +bool AudioStreamOpenSLES::processBufferCallback(SLAndroidSimpleBufferQueueItf bq) { + bool shouldStopStream = false; + // Ask the app callback to process the buffer. + DataCallbackResult result = + fireDataCallback(mCallbackBuffer[mCallbackBufferIndex].get(), mFramesPerCallback); + if (result == DataCallbackResult::Continue) { + // Pass the buffer to OpenSLES. + SLresult enqueueResult = enqueueCallbackBuffer(bq); + if (enqueueResult != SL_RESULT_SUCCESS) { + LOGE("%s() returned %d", __func__, enqueueResult); + shouldStopStream = true; + } + // Update Oboe client position with frames handled by the callback. + if (getDirection() == Direction::Input) { + mFramesRead += mFramesPerCallback; + } else { + mFramesWritten += mFramesPerCallback; + } + } else if (result == DataCallbackResult::Stop) { + LOGD("Oboe callback returned Stop"); + shouldStopStream = true; + } else { + LOGW("Oboe callback returned unexpected value = %d", result); + shouldStopStream = true; + } + if (shouldStopStream) { + mCallbackBufferIndex = 0; + } + return shouldStopStream; +} + +// This callback handler is called every time a buffer has been processed by OpenSL ES. +static void bqCallbackGlue(SLAndroidSimpleBufferQueueItf bq, void *context) { + bool shouldStopStream = (reinterpret_cast(context)) + ->processBufferCallback(bq); + if (shouldStopStream) { + (reinterpret_cast(context))->requestStop(); + } +} + +SLresult AudioStreamOpenSLES::registerBufferQueueCallback() { + // The BufferQueue + SLresult result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &mSimpleBufferQueueInterface); + if (SL_RESULT_SUCCESS != result) { + LOGE("get buffer queue interface:%p result:%s", + mSimpleBufferQueueInterface, + getSLErrStr(result)); + } else { + // Register the BufferQueue callback + result = (*mSimpleBufferQueueInterface)->RegisterCallback(mSimpleBufferQueueInterface, + bqCallbackGlue, this); + if (SL_RESULT_SUCCESS != result) { + LOGE("RegisterCallback result:%s", getSLErrStr(result)); + } + } + return result; +} + +int64_t AudioStreamOpenSLES::getFramesProcessedByServer() { + updateServiceFrameCounter(); + int64_t millis64 = mPositionMillis.get(); + int64_t framesProcessed = millis64 * getSampleRate() / kMillisPerSecond; + return framesProcessed; +} + +Result AudioStreamOpenSLES::waitForStateChange(StreamState currentState, + StreamState *nextState, + int64_t timeoutNanoseconds) { + Result oboeResult = Result::ErrorTimeout; + int64_t sleepTimeNanos = 20 * kNanosPerMillisecond; // arbitrary + int64_t timeLeftNanos = timeoutNanoseconds; + + while (true) { + const StreamState state = getState(); // this does not require a lock + if (nextState != nullptr) { + *nextState = state; + } + if (currentState != state) { // state changed? + oboeResult = Result::OK; + break; + } + + // Did we timeout or did user ask for non-blocking? + if (timeLeftNanos <= 0) { + break; + } + + if (sleepTimeNanos > timeLeftNanos){ + sleepTimeNanos = timeLeftNanos; + } + AudioClock::sleepForNanos(sleepTimeNanos); + timeLeftNanos -= sleepTimeNanos; + } + + return oboeResult; +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.h new file mode 100644 index 00000000..0164b839 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/AudioStreamOpenSLES.h @@ -0,0 +1,148 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_AUDIO_STREAM_OPENSL_ES_H_ +#define OBOE_AUDIO_STREAM_OPENSL_ES_H_ + +#include + +#include +#include + +#include "oboe/Oboe.h" +#include "common/MonotonicCounter.h" +#include "opensles/AudioStreamBuffered.h" +#include "opensles/EngineOpenSLES.h" + +namespace oboe { + +constexpr int kBitsPerByte = 8; +constexpr int kBufferQueueLengthDefault = 2; // double buffered for callbacks +constexpr int kBufferQueueLengthMax = 8; // AudioFlinger won't use more than 8 + +/** + * INTERNAL USE ONLY + * + * A stream that wraps OpenSL ES. + * + * Do not instantiate this class directly. + * Use an OboeStreamBuilder to create one. + */ + +class AudioStreamOpenSLES : public AudioStreamBuffered { +public: + + AudioStreamOpenSLES(); + explicit AudioStreamOpenSLES(const AudioStreamBuilder &builder); + + virtual ~AudioStreamOpenSLES() = default; + + virtual Result open() override; + + /** + * Query the current state, eg. OBOE_STREAM_STATE_PAUSING + * + * @return state or a negative error. + */ + StreamState getState() override { return mState.load(); } + + AudioApi getAudioApi() const override { + return AudioApi::OpenSLES; + } + + /** + * Process next OpenSL ES buffer. + * Called by by OpenSL ES framework. + * + * This is public, but don't call it directly. + * + * @return whether the current stream should be stopped. + */ + bool processBufferCallback(SLAndroidSimpleBufferQueueItf bq); + + Result waitForStateChange(StreamState currentState, + StreamState *nextState, + int64_t timeoutNanoseconds) override; + +protected: + + /** + * Finish setting up the stream. Common for INPUT and OUTPUT. + * + * @param configItf + * @return SL_RESULT_SUCCESS if OK. + */ + SLresult finishCommonOpen(SLAndroidConfigurationItf configItf); + + // This must be called under mLock. + Result close_l(); + + SLuint32 channelCountToChannelMaskDefault(int channelCount) const; + + virtual Result onBeforeDestroy() { return Result::OK; } + virtual Result onAfterDestroy() { return Result::OK; } + + static SLuint32 getDefaultByteOrder(); + + int32_t getBufferDepth(SLAndroidSimpleBufferQueueItf bq); + + int32_t calculateOptimalBufferQueueLength(); + int32_t estimateNativeFramesPerBurst(); + + SLresult enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq); + + SLresult configurePerformanceMode(SLAndroidConfigurationItf configItf); + + PerformanceMode convertPerformanceMode(SLuint32 openslMode) const; + SLuint32 convertPerformanceMode(PerformanceMode oboeMode) const; + + void logUnsupportedAttributes(); + + /** + * Internal use only. + * Use this instead of directly setting the internal state variable. + */ + void setState(StreamState state) { + mState.store(state); + } + + int64_t getFramesProcessedByServer(); + + // OpenSLES stuff + SLObjectItf mObjectInterface = nullptr; + SLAndroidSimpleBufferQueueItf mSimpleBufferQueueInterface = nullptr; + int mBufferQueueLength = 0; + + int32_t mBytesPerCallback = oboe::kUnspecified; + MonotonicCounter mPositionMillis; // for tracking OpenSL ES service position + +private: + + constexpr static int kDoubleBufferCount = 2; + + SLresult registerBufferQueueCallback(); + SLresult updateStreamParameters(SLAndroidConfigurationItf configItf); + Result configureBufferSizes(int32_t sampleRate); + + std::unique_ptr mCallbackBuffer[kBufferQueueLengthMax]; + int mCallbackBufferIndex = 0; + std::atomic mState{StreamState::Uninitialized}; + +}; + +} // namespace oboe + +#endif // OBOE_AUDIO_STREAM_OPENSL_ES_H_ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/EngineOpenSLES.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/EngineOpenSLES.cpp new file mode 100644 index 00000000..e1007d10 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/EngineOpenSLES.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "common/OboeDebug.h" +#include "EngineOpenSLES.h" +#include "OpenSLESUtilities.h" + +using namespace oboe; + +// OpenSL ES is deprecated in SDK 30. +// So we use custom dynamic linking to access the library. +#define LIB_OPENSLES_NAME "libOpenSLES.so" +typedef SLresult (*prototype_slCreateEngine)( + SLObjectItf *pEngine, + SLuint32 numOptions, + const SLEngineOption *pEngineOptions, + SLuint32 numInterfaces, + const SLInterfaceID *pInterfaceIds, + const SLboolean *pInterfaceRequired +); +static prototype_slCreateEngine gFunction_slCreateEngine = nullptr; +static void *gLibOpenSlesLibraryHandle = nullptr; + +// Load the OpenSL ES library and the one primary entry point. +// @return true if linked OK +static bool linkOpenSLES() { + if (gLibOpenSlesLibraryHandle == nullptr && gFunction_slCreateEngine == nullptr) { + // Use RTLD_NOW to avoid the unpredictable behavior that RTLD_LAZY can cause. + // Also resolving all the links now will prevent a run-time penalty later. + gLibOpenSlesLibraryHandle = dlopen(LIB_OPENSLES_NAME, RTLD_NOW); + if (gLibOpenSlesLibraryHandle == nullptr) { + LOGE("linkOpenSLES() could not find " LIB_OPENSLES_NAME); + } else { + gFunction_slCreateEngine = (prototype_slCreateEngine) dlsym( + gLibOpenSlesLibraryHandle, + "slCreateEngine"); + LOGD("linkOpenSLES(): dlsym(%s) returned %p", "slCreateEngine", + gFunction_slCreateEngine); + } + } + return gFunction_slCreateEngine != nullptr; +} + +EngineOpenSLES &EngineOpenSLES::getInstance() { + static EngineOpenSLES sInstance; + return sInstance; +} + +SLresult EngineOpenSLES::open() { + std::lock_guard lock(mLock); + + SLresult result = SL_RESULT_SUCCESS; + if (mOpenCount++ == 0) { + // load the library and link to it + if (!linkOpenSLES()) { + result = SL_RESULT_FEATURE_UNSUPPORTED; + goto error; + }; + + // create engine + result = (*gFunction_slCreateEngine)(&mEngineObject, 0, NULL, 0, NULL, NULL); + if (SL_RESULT_SUCCESS != result) { + LOGE("EngineOpenSLES - slCreateEngine() result:%s", getSLErrStr(result)); + goto error; + } + + // realize the engine + result = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + LOGE("EngineOpenSLES - Realize() engine result:%s", getSLErrStr(result)); + goto error; + } + + // get the engine interface, which is needed in order to create other objects + result = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface); + if (SL_RESULT_SUCCESS != result) { + LOGE("EngineOpenSLES - GetInterface() engine result:%s", getSLErrStr(result)); + goto error; + } + } + + return result; + +error: + close(); + return result; +} + +void EngineOpenSLES::close() { + std::lock_guard lock(mLock); + if (--mOpenCount == 0) { + if (mEngineObject != nullptr) { + (*mEngineObject)->Destroy(mEngineObject); + mEngineObject = nullptr; + mEngineInterface = nullptr; + } + } +} + +SLresult EngineOpenSLES::createOutputMix(SLObjectItf *objectItf) { + return (*mEngineInterface)->CreateOutputMix(mEngineInterface, objectItf, 0, 0, 0); +} + +SLresult EngineOpenSLES::createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink) { + + const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION}; + const SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + + return (*mEngineInterface)->CreateAudioPlayer(mEngineInterface, objectItf, audioSource, + audioSink, + sizeof(ids) / sizeof(ids[0]), ids, reqs); +} + +SLresult EngineOpenSLES::createAudioRecorder(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink) { + + const SLInterfaceID ids[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; + const SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + + return (*mEngineInterface)->CreateAudioRecorder(mEngineInterface, objectItf, audioSource, + audioSink, + sizeof(ids) / sizeof(ids[0]), ids, reqs); +} + diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/EngineOpenSLES.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/EngineOpenSLES.h new file mode 100644 index 00000000..3d238a8c --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/EngineOpenSLES.h @@ -0,0 +1,65 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_ENGINE_OPENSLES_H +#define OBOE_ENGINE_OPENSLES_H + +#include +#include + +#include +#include + +namespace oboe { + +/** + * INTERNAL USE ONLY + */ +class EngineOpenSLES { +public: + static EngineOpenSLES &getInstance(); + + SLresult open(); + + void close(); + + SLresult createOutputMix(SLObjectItf *objectItf); + + SLresult createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink); + SLresult createAudioRecorder(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink); + +private: + // Make this a safe Singleton + EngineOpenSLES()= default; + ~EngineOpenSLES()= default; + EngineOpenSLES(const EngineOpenSLES&)= delete; + EngineOpenSLES& operator=(const EngineOpenSLES&)= delete; + + std::mutex mLock; + int32_t mOpenCount = 0; + + SLObjectItf mEngineObject = nullptr; + SLEngineItf mEngineInterface = nullptr; +}; + +} // namespace oboe + + +#endif //OBOE_ENGINE_OPENSLES_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OpenSLESUtilities.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OpenSLESUtilities.cpp new file mode 100644 index 00000000..534f641c --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OpenSLESUtilities.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OpenSLESUtilities.h" + +namespace oboe { + +/* + * OSLES Helpers + */ + +const char *getSLErrStr(SLresult code) { + switch (code) { + case SL_RESULT_SUCCESS: + return "SL_RESULT_SUCCESS"; + case SL_RESULT_PRECONDITIONS_VIOLATED: + return "SL_RESULT_PRECONDITIONS_VIOLATED"; + case SL_RESULT_PARAMETER_INVALID: + return "SL_RESULT_PARAMETER_INVALID"; + case SL_RESULT_MEMORY_FAILURE: + return "SL_RESULT_MEMORY_FAILURE"; + case SL_RESULT_RESOURCE_ERROR: + return "SL_RESULT_RESOURCE_ERROR"; + case SL_RESULT_RESOURCE_LOST: + return "SL_RESULT_RESOURCE_LOST"; + case SL_RESULT_IO_ERROR: + return "SL_RESULT_IO_ERROR"; + case SL_RESULT_BUFFER_INSUFFICIENT: + return "SL_RESULT_BUFFER_INSUFFICIENT"; + case SL_RESULT_CONTENT_CORRUPTED: + return "SL_RESULT_CONTENT_CORRUPTED"; + case SL_RESULT_CONTENT_UNSUPPORTED: + return "SL_RESULT_CONTENT_UNSUPPORTED"; + case SL_RESULT_CONTENT_NOT_FOUND: + return "SL_RESULT_CONTENT_NOT_FOUND"; + case SL_RESULT_PERMISSION_DENIED: + return "SL_RESULT_PERMISSION_DENIED"; + case SL_RESULT_FEATURE_UNSUPPORTED: + return "SL_RESULT_FEATURE_UNSUPPORTED"; + case SL_RESULT_INTERNAL_ERROR: + return "SL_RESULT_INTERNAL_ERROR"; + case SL_RESULT_UNKNOWN_ERROR: + return "SL_RESULT_UNKNOWN_ERROR"; + case SL_RESULT_OPERATION_ABORTED: + return "SL_RESULT_OPERATION_ABORTED"; + case SL_RESULT_CONTROL_LOST: + return "SL_RESULT_CONTROL_LOST"; + default: + return "Unknown SL error"; + } +} + +SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat( + SLDataFormat_PCM format, SLuint32 representation) { + SLAndroidDataFormat_PCM_EX format_pcm_ex; + format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX; + format_pcm_ex.numChannels = format.numChannels; + format_pcm_ex.sampleRate = format.samplesPerSec; + format_pcm_ex.bitsPerSample = format.bitsPerSample; + format_pcm_ex.containerSize = format.containerSize; + format_pcm_ex.channelMask = format.channelMask; + format_pcm_ex.endianness = format.endianness; + format_pcm_ex.representation = representation; + return format_pcm_ex; +} + +SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format) { + switch(format) { + case AudioFormat::I16: + return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; + case AudioFormat::Float: + return SL_ANDROID_PCM_REPRESENTATION_FLOAT; + case AudioFormat::I24: + case AudioFormat::I32: + case AudioFormat::IEC61937: + case AudioFormat::Invalid: + case AudioFormat::Unspecified: + default: + return 0; + } +} + +} // namespace oboe diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OpenSLESUtilities.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OpenSLESUtilities.h new file mode 100644 index 00000000..50c0e2da --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OpenSLESUtilities.h @@ -0,0 +1,44 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_OPENSLES_OPENSLESUTILITIES_H +#define OBOE_OPENSLES_OPENSLESUTILITIES_H + +#include +#include "oboe/Oboe.h" + +namespace oboe { + +const char *getSLErrStr(SLresult code); + +/** + * Creates an extended PCM format from the supplied format and data representation. This method + * should only be called on Android devices with API level 21+. API 21 introduced the + * SLAndroidDataFormat_PCM_EX object which allows audio samples to be represented using + * single precision floating-point. + * + * @param format + * @param representation + * @return the extended PCM format + */ +SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat(SLDataFormat_PCM format, + SLuint32 representation); + +SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format); + +} // namespace oboe + +#endif //OBOE_OPENSLES_OPENSLESUTILITIES_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OutputMixerOpenSLES.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OutputMixerOpenSLES.cpp new file mode 100644 index 00000000..e06f306b --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OutputMixerOpenSLES.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "common/OboeDebug.h" +#include "EngineOpenSLES.h" +#include "OpenSLESUtilities.h" +#include "OutputMixerOpenSLES.h" + +using namespace oboe; + +OutputMixerOpenSL &OutputMixerOpenSL::getInstance() { + static OutputMixerOpenSL sInstance; + return sInstance; +} + +SLresult OutputMixerOpenSL::open() { + std::lock_guard lock(mLock); + + SLresult result = SL_RESULT_SUCCESS; + if (mOpenCount++ == 0) { + // get the output mixer + result = EngineOpenSLES::getInstance().createOutputMix(&mOutputMixObject); + if (SL_RESULT_SUCCESS != result) { + LOGE("OutputMixerOpenSL() - createOutputMix() result:%s", getSLErrStr(result)); + goto error; + } + + // realize the output mix + result = (*mOutputMixObject)->Realize(mOutputMixObject, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + LOGE("OutputMixerOpenSL() - Realize() mOutputMixObject result:%s", getSLErrStr(result)); + goto error; + } + } + + return result; + +error: + close(); + return result; +} + +void OutputMixerOpenSL::close() { + std::lock_guard lock(mLock); + + if (--mOpenCount == 0) { + // destroy output mix object, and invalidate all associated interfaces + if (mOutputMixObject != nullptr) { + (*mOutputMixObject)->Destroy(mOutputMixObject); + mOutputMixObject = nullptr; + } + } +} + +SLresult OutputMixerOpenSL::createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource) { + SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, mOutputMixObject}; + SLDataSink audioSink = {&loc_outmix, NULL}; + return EngineOpenSLES::getInstance().createAudioPlayer(objectItf, audioSource, &audioSink); +} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OutputMixerOpenSLES.h b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OutputMixerOpenSLES.h new file mode 100644 index 00000000..813fd018 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/oboe/src/opensles/OutputMixerOpenSLES.h @@ -0,0 +1,58 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_OUTPUT_MIXER_OPENSLES_H +#define OBOE_OUTPUT_MIXER_OPENSLES_H + +#include +#include + +#include +#include + +namespace oboe { + +/** + * INTERNAL USE ONLY + */ + +class OutputMixerOpenSL { +public: + static OutputMixerOpenSL &getInstance(); + + SLresult open(); + + void close(); + + SLresult createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource); + +private: + // Make this a safe Singleton + OutputMixerOpenSL()= default; + ~OutputMixerOpenSL()= default; + OutputMixerOpenSL(const OutputMixerOpenSL&)= delete; + OutputMixerOpenSL& operator=(const OutputMixerOpenSL&)= delete; + + std::mutex mLock; + int32_t mOpenCount = 0; + + SLObjectItf mOutputMixObject = nullptr; +}; + +} // namespace oboe + +#endif //OBOE_OUTPUT_MIXER_OPENSLES_H diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp index 35687ec0..fa7a3aaf 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -56,11 +56,12 @@ void AudioSourcePlayer::setGain (const float newGain) noexcept gain = newGain; } -void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, - int totalNumInputChannels, - float** outputChannelData, - int totalNumOutputChannels, - int numSamples) +void AudioSourcePlayer::audioDeviceIOCallbackWithContext (const float* const* inputChannelData, + int totalNumInputChannels, + float* const* outputChannelData, + int totalNumOutputChannels, + int numSamples, + [[maybe_unused]] const AudioIODeviceCallbackContext& context) { // these should have been prepared by audioDeviceAboutToStart()... jassert (sampleRate > 0 && bufferSize > 0); diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h index f114a7bb..c77b5ad1 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -79,12 +79,13 @@ class JUCE_API AudioSourcePlayer : public AudioIODeviceCallback float getGain() const noexcept { return gain; } //============================================================================== - /** Implementation of the AudioIODeviceCallback method. */ - void audioDeviceIOCallback (const float** inputChannelData, - int totalNumInputChannels, - float** outputChannelData, - int totalNumOutputChannels, - int numSamples) override; + /** Implementation of the AudioIODeviceCallbackWithContext method. */ + void audioDeviceIOCallbackWithContext (const float* const* inputChannelData, + int totalNumInputChannels, + float* const* outputChannelData, + int totalNumOutputChannels, + int numSamples, + const AudioIODeviceCallbackContext& context) override; /** Implementation of the AudioIODeviceCallback method. */ void audioDeviceAboutToStart (AudioIODevice* device) override; diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp index 42ce09ba..d93ed1e3 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -45,9 +45,6 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, setSource (nullptr, 0, nullptr); // deselect and reselect to avoid releasing resources wrongly } - readAheadBufferSize = readAheadSize; - sourceSampleRate = sourceSampleRateToCorrectFor; - ResamplingAudioSource* newResamplerSource = nullptr; BufferingAudioSource* newBufferingSource = nullptr; PositionableAudioSource* newPositionableSource = nullptr; @@ -82,8 +79,8 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, if (isPrepared) { - if (newResamplerSource != nullptr && sourceSampleRate > 0 && sampleRate > 0) - newResamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); + if (newResamplerSource != nullptr && sourceSampleRateToCorrectFor > 0 && sampleRate > 0) + newResamplerSource->setResamplingRatio (sourceSampleRateToCorrectFor / sampleRate); newMasterSource->prepareToPlay (blockSize, sampleRate); } @@ -97,8 +94,9 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, bufferingSource = newBufferingSource; masterSource = newMasterSource; positionableSource = newPositionableSource; + readAheadBufferSize = readAheadSize; + sourceSampleRate = sourceSampleRateToCorrectFor; - inputStreamEOF = false; playing = false; } @@ -114,7 +112,6 @@ void AudioTransportSource::start() const ScopedLock sl (callbackLock); playing = true; stopped = false; - inputStreamEOF = false; } sendChangeMessage(); @@ -157,6 +154,12 @@ double AudioTransportSource::getLengthInSeconds() const return 0.0; } +bool AudioTransportSource::hasStreamFinished() const noexcept +{ + return positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 + && ! positionableSource->isLooping(); +} + void AudioTransportSource::setNextReadPosition (int64 newPosition) { if (positionableSource != nullptr) @@ -168,13 +171,13 @@ void AudioTransportSource::setNextReadPosition (int64 newPosition) if (resamplerSource != nullptr) resamplerSource->flushBuffers(); - - inputStreamEOF = false; } } int64 AudioTransportSource::getNextReadPosition() const { + const ScopedLock sl (callbackLock); + if (positionableSource != nullptr) { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; @@ -221,7 +224,6 @@ void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, double ne if (resamplerSource != nullptr && sourceSampleRate > 0) resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); - inputStreamEOF = false; isPrepared = true; } @@ -258,11 +260,9 @@ void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info info.buffer->clear (info.startSample + 256, info.numSamples - 256); } - if (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 - && ! positionableSource->isLooping()) + if (hasStreamFinished()) { playing = false; - inputStreamEOF = true; sendChangeMessage(); } diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h index c2ee74f6..c840c794 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -93,7 +93,7 @@ class JUCE_API AudioTransportSource : public PositionableAudioSource, */ void setPosition (double newPosition); - /** Returns the position that the next data block will be read from + /** Returns the position that the next data block will be read from. This is a time in seconds. */ double getCurrentPosition() const; @@ -102,7 +102,7 @@ class JUCE_API AudioTransportSource : public PositionableAudioSource, double getLengthInSeconds() const; /** Returns true if the player has stopped because its input stream ran out of data. */ - bool hasStreamFinished() const noexcept { return inputStreamEOF; } + bool hasStreamFinished() const noexcept; //============================================================================== /** Starts playing (if a source has been selected). @@ -170,7 +170,7 @@ class JUCE_API AudioTransportSource : public PositionableAudioSource, std::atomic playing { false }, stopped { true }; double sampleRate = 44100.0, sourceSampleRate = 0; int blockSize = 128, readAheadBufferSize = 0; - bool isPrepared = false, inputStreamEOF = false; + bool isPrepared = false; void releaseMasterResources(); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/Flac Licence.txt b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/Flac Licence.txt index fbd3e139..6ca17228 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/Flac Licence.txt +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/Flac Licence.txt @@ -5,8 +5,8 @@ I've incorporated FLAC directly into the JUCE codebase because it makes things much easier than having to make all your builds link correctly to the appropriate libraries on every different platform. -I've made minimal changes to the FLAC code - just tweaked a few include paths -to make it build smoothly, added some headers to allow you to turn off FLAC +I've made minimal changes to the FLAC code - just tweaked a few include paths +to make it build smoothly, added some headers to allow you to turn off FLAC compilation, and commented-out a couple of unused bits of code. ===================================================================== @@ -15,11 +15,13 @@ compilation, and commented-out a couple of unused bits of code. The following license is the BSD-style license that comes with the Flac distribution, and which applies just to the files I've included in this directory. For more info, and to get the rest of the -distribution, visit the Flac homepage: flac.sourceforge.net +distribution, visit the Flac homepage: https://xiph.org/flac/ ===================================================================== -Copyright (C) 2000,2001,2002,2003,2004,2005,2006 Josh Coalson +libFLAC - Free Lossless Audio Codec library +Copyright (C) 2000-2009 Josh Coalson +Copyright (C) 2011-2023 Xiph.Org Foundation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -32,7 +34,7 @@ notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -- Neither the name of the Xiph.org Foundation nor the names of its +- Neither the name of the Xiph.Org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/JUCE_CHANGES.txt b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/JUCE_CHANGES.txt new file mode 100644 index 00000000..805d9ba2 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/JUCE_CHANGES.txt @@ -0,0 +1,55 @@ +The files in this directory are from the FLAC 1.4.3 release downloaded in a tarball from +https://github.com/xiph/flac/releases/tag/1.4.3 + +They have been modified as little as possible, so that when they are included by +juce_FlacAudioFormat.cpp they compile without warnings and work correctly on the supported +platforms. + +Below I will refer to the contents of the tarball as RELEASE and to our repo as JUCE. + +The steps carried out when including the 1.4.3 version +* The contents of RELEASE/include/FLAC and RELEASE/include/share were copied to + JUCE/[..]/codecs/flac +* The contents of RELEASE/libFLAC were copied to JUCE/[..]/codecs/flac/libFLAC +* The includes in all these files were retargeted, to be relative to the file that does the + including, so that we don't have to rely on e.g. flac/libFLAC/include being among the include + directories, like it is when building FLAC with the official makefiles. All this retargeting is + done per-line with a Python script. All the rules used for the retargeting can be seen in the code + excerpt at [1]. +* Then I will do a compile/test/modify loop, until it works and compiles without warnings. The + changes made are mostly C to C++ conversions like adding explicit casts, or fixing warnings, like + eliminating always true branches that refer to preprocessor defines. +* Then I delete all library files, and only re-add those that are needed for successful compilation. + This delete step is propagated back to older commits, so that unneeded files are never added to + the GIT repo. + + +[1]: Code excerpt used for retargeting includes in the copied files +``` +def transform(path: Path, line: str) -> str: + if path.match("libFLAC/*"): + line = line.replace('#include "private', '#include "include/private') + line = line.replace('#include "protected', '#include "include/protected') + line = line.replace('#include "share/', '#include "../') + line = line.replace('#include "FLAC/', '#include "../') + + if path.match("libFLAC/include/private/*") or path.match( + "libFLAC/include/public/*" + ): + line = line.replace('#include "share/', '#include "../../../') + line = line.replace('#include "FLAC/', '#include "../../../') + + if path.match("*"): + line = line.replace('#include "share/', '#include "') + + if path.match("libFLAC/include/private/*"): + line = line.replace('#include "private/', '#include "') + + if path.match("libFLAC/include/protected/*"): + line = line.replace('#include "private/', '#include "../private/') + line = line.replace('#include "FLAC/', '#include "../../../') + + line = line.replace('#include "include/private/macros.h"', "") + + return line +``` diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/all.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/all.h index 2851cf59..966f6105 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/all.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/all.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,6 +33,10 @@ #ifndef FLAC__ALL_H #define FLAC__ALL_H +#ifndef FLAC__HAS_OGG + #define FLAC__HAS_OGG 0 +#endif + #include "export.h" #include "assert.h" @@ -52,7 +56,7 @@ * level idea of the structure and how to find the information you * need. As a prerequisite you should have at least a basic * knowledge of the FLAC format, documented - * here. + * here. * * \section c_api FLAC C API * @@ -64,7 +68,7 @@ * * By writing a little code and linking against libFLAC, it is * relatively easy to add FLAC support to another program. The - * library is licensed under Xiph's BSD license. + * library is licensed under Xiph's BSD license. * Complete source code of libFLAC as well as the command-line * encoder and plugins is available and is a useful source of * examples. @@ -97,7 +101,7 @@ * example /usr/include/FLAC++/...). * * libFLAC++ is also licensed under - * Xiph's BSD license. + * Xiph's BSD license. * * \section getting_started Getting Started * @@ -113,7 +117,7 @@ * functions through the links in top bar across this page. * * If you prefer a more hands-on approach, you can jump right to some - * example code. + * example code. * * \section porting_guide Porting Guide * @@ -147,7 +151,7 @@ * library. * * Also, there are several places in the libFLAC code with comments marked - * with "OPT:" where a #define can be changed to enable code that might be + * with "OPT:" where a \#define can be changed to enable code that might be * faster on a specific platform. Experimenting with these can yield faster * binaries. */ @@ -159,9 +163,9 @@ * the libraries to newer versions of FLAC. * * One simple facility for making porting easier that has been added - * in FLAC 1.1.3 is a set of \c #defines in \c export.h of each + * in FLAC 1.1.3 is a set of \#defines in \c export.h of each * library's includes (e.g. \c include/FLAC/export.h). The - * \c #defines mirror the libraries' + * \#defines mirror the libraries' * libtool version numbers, * e.g. in libFLAC there are \c FLAC_API_VERSION_CURRENT, * \c FLAC_API_VERSION_REVISION, and \c FLAC_API_VERSION_AGE. @@ -176,7 +180,7 @@ * #endif * \endcode * - * The the source will work for multiple versions and the legacy code can + * The source will work for multiple versions and the legacy code can * easily be removed when the transition is complete. * * Another available symbol is FLAC_API_SUPPORTS_OGG_FLAC (defined in @@ -321,7 +325,7 @@ * * The \a bytes parameter to FLAC__StreamDecoderReadCallback, * FLAC__StreamEncoderReadCallback, and FLAC__StreamEncoderWriteCallback - * is now \c size_t instead of \c unsigned. + * is now \c size_t instead of \c uint32_t. */ /** \defgroup porting_1_1_3_to_1_1_4 Porting from FLAC 1.1.3 to 1.1.4 @@ -357,6 +361,85 @@ * \c FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN */ +/** \defgroup porting_1_3_4_to_1_4_0 Porting from FLAC 1.3.4 to 1.4.0 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.3.4 to FLAC 1.4.0. + * + * \section porting_1_3_4_to_1_4_0_summary Summary + * + * Between FLAC 1.3.4 and FLAC 1.4.0, there have four breaking changes + * - the function get_client_data_from_decoder has been renamed to + * FLAC__get_decoder_client_data + * - some data types in the FLAC__Frame struct have changed + * - all functions resizing metadata blocks now return the object + * untouched if memory allocation fails, whereas previously the + * handling varied and was more or less undefined + * - all functions accepting a filename now take UTF-8 encoded filenames + * on Windows instead of filenames in the current codepage + * + * Furthermore, there have been the following additions + * - the functions FLAC__stream_encoder_set_limit_min_bitrate, + * FLAC__stream_encoder_get_limit_min_bitrate, + * FLAC::encoder::file::set_limit_min_bitrate() and + * FLAC::encoder::file::get_limit_min_bitrate() have been added + * - Added FLAC__STREAM_DECODER_ERROR_STATUS_BAD_METADATA to the + * FLAC__StreamDecoderErrorStatus enum + * + * \section porting_1_3_4_to_1_4_0_breaking Breaking changes + * + * The function \b get_client_data_from_decoder was added in FLAC 1.3.3 + * but did not follow the API naming convention and was not properly + * exported. The function is now renamed and properly integrated as + * FLAC__stream_decoder_get_client_data + * + * To accomodate encoding and decoding 32-bit int PCM, some data types + * in the \b FLAC__frame struct were changed. Specifically, warmup + * in both the FLAC__Subframe_Fixed struc and the FLAC__Subframe_LPC + * struct is changed from FLAC__int32 to FLAC__int64. Also, value + * in the FLAC__Subframe_Constant is changed from FLAC__int32 to + * FLAC__int64. Finally, in FLAC__Subframe_Verbatim struct data is + * changes from a FLAC__int32 array to a union containing a FLAC__int32 + * array and a FLAC__int64 array. Also, a new member is added, + * data_type, which clarifies whether the FLAC__int32 or FLAC__int64 + * array is in use. + * + * Furthermore, the following functions now return the object untouched + * if memory allocation fails, whereas previously the handling varied + * and was more or less undefined + * + * - FLAC__metadata_object_seektable_resize_points + * - FLAC__metadata_object_vorbiscomment_resize_comments + * - FLAC__metadata_object_cuesheet_track_resize_indices + * - FLAC__metadata_object_cuesheet_resize_tracks + * + * The last breaking change is that all API functions taking a filename + * as an argument now, on Windows, must be supplied with that filename + * in the UTF-8 character encoding instead of using the current code + * page. libFLAC internally translates these UTF-8 encoded filenames to + * an appropriate representation to use with _wfopen. On all other + * systems, filename is passed to fopen without any translation, as it + * in libFLAC 1.3.4 and earlier. + * + * \section porting_1_3_4_to_1_4_0_additions Additions + * + * To aid in creating properly streamable FLAC files, a set of functions + * was added to make it possible to enfore a minimum bitrate to files + * created through libFLAC's stream_encoder.h interface. With this + * function enabled the resulting FLAC files have a minimum bitrate of + * 1bit/sample independent of the number of channels, i.e. 48kbit/s for + * 48kHz. This can be beneficial for streaming, as very low bitrates for + * silent sections compressed with 'constant' subframes can result in a + * bitrate of 1kbit/s, creating problems with clients that aren't aware + * of this possibility and buffer too much data. + * + * Finally, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_METADATA was added to + * the FLAC__StreamDecoderErrorStatus enum to signal that the decoder + * encountered unreadable metadata. + * + */ + /** \defgroup flac FLAC C API * * The FLAC C API is the interface to libFLAC, a set of structures diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/alloc.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/alloc.h index 1b6b97c2..c9cc5601 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/alloc.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/alloc.h @@ -1,6 +1,6 @@ /* alloc - Convenience routines for safely allocating memory * Copyright (C) 2007-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -55,7 +55,7 @@ # ifndef SIZE_T_MAX # ifdef _MSC_VER # ifdef _WIN64 -# define SIZE_T_MAX 0xffffffffffffffffui64 +# define SIZE_T_MAX FLAC__U64L(0xffffffffffffffff) # else # define SIZE_T_MAX 0xffffffff # endif @@ -66,19 +66,58 @@ # define SIZE_MAX SIZE_T_MAX #endif +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +extern int alloc_check_threshold, alloc_check_counter; + +static inline int alloc_check() { + if(alloc_check_threshold == INT32_MAX) + return 0; + else if(alloc_check_counter++ == alloc_check_threshold) + return 1; + else + return 0; +} + +#endif + /* avoid malloc()ing 0 bytes, see: * https://www.securecoding.cert.org/confluence/display/seccode/MEM04-A.+Do+not+make+assumptions+about+the+result+of+allocating+0+bytes?focusedCommentId=5407003 */ + static inline void *safe_malloc_(size_t size) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* Fail if requested */ + if(alloc_check()) + return NULL; +#endif /* malloc(0) is undefined; FLAC src convention is to always allocate */ if(!size) size++; return malloc(size); } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +static inline void *malloc_(size_t size) +{ + /* Fail if requested */ + if(alloc_check()) + return NULL; + return malloc(size); +} +#else +#define malloc_ malloc +#endif + + + static inline void *safe_calloc_(size_t nmemb, size_t size) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* Fail if requested */ + if(alloc_check()) + return NULL; +#endif if(!nmemb || !size) return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ return calloc(nmemb, size); @@ -130,7 +169,7 @@ static inline void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size size1 *= size2; if(size1 > SIZE_MAX / size3) return 0; - return malloc(size1*size3); + return malloc_(size1*size3); } /* size1*size2 + size3 */ @@ -153,18 +192,64 @@ static inline void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size return 0; if(size1 > SIZE_MAX / size2) return 0; - return malloc(size1*size2); + return malloc_(size1*size2); +} + +static inline void *safe_realloc_(void *ptr, size_t size) +{ + void *oldptr; + void *newptr; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* Fail if requested */ + if(alloc_check() && size > 0) { + free(ptr); + return NULL; + } +#endif + oldptr = ptr; + newptr = realloc(ptr, size); + if(size > 0 && newptr == 0) + free(oldptr); + return newptr; } -static inline void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +static inline void *realloc_(void *ptr, size_t size) +{ + /* Fail if requested */ + if(alloc_check()) + return NULL; + return realloc(ptr, size); +} +#else +#define realloc_ realloc +#endif + + +static inline void *safe_realloc_nofree_add_2op_(void *ptr, size_t size1, size_t size2) { size2 += size1; if(size2 < size1) return 0; - return realloc(ptr, size2); + return realloc_(ptr, size2); } static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) +{ + size2 += size1; + if(size2 < size1) { + free(ptr); + return 0; + } + size3 += size2; + if(size3 < size2) { + free(ptr); + return 0; + } + return safe_realloc_(ptr, size3); +} + +static inline void *safe_realloc_nofree_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) { size2 += size1; if(size2 < size1) @@ -172,10 +257,10 @@ static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size3 += size2; if(size3 < size2) return 0; - return realloc(ptr, size3); + return realloc_(ptr, size3); } -static inline void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) +static inline void *safe_realloc_nofree_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) { size2 += size1; if(size2 < size1) @@ -186,16 +271,27 @@ static inline void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size4 += size3; if(size4 < size3) return 0; - return realloc(ptr, size4); + return realloc_(ptr, size4); } static inline void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) +{ + if(!size1 || !size2) + return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ + if(size1 > SIZE_MAX / size2) { + free(ptr); + return 0; + } + return safe_realloc_(ptr, size1*size2); +} + +static inline void *safe_realloc_nofree_mul_2op_(void *ptr, size_t size1, size_t size2) { if(!size1 || !size2) return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ if(size1 > SIZE_MAX / size2) return 0; - return realloc(ptr, size1*size2); + return realloc_(ptr, size1*size2); } /* size1 * (size2 + size3) */ @@ -204,9 +300,22 @@ static inline void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, if(!size1 || (!size2 && !size3)) return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ size2 += size3; - if(size2 < size3) + if(size2 < size3) { + free(ptr); return 0; + } return safe_realloc_mul_2op_(ptr, size1, size2); } +/* size1 * (size2 + size3) */ +static inline void *safe_realloc_nofree_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3) +{ + if(!size1 || (!size2 && !size3)) + return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ + size2 += size3; + if(size2 < size3) + return 0; + return safe_realloc_nofree_mul_2op_(ptr, size1, size2); +} + #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/assert.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/assert.h index c3c3c993..ee3ee080 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/assert.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/assert.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,16 +34,18 @@ #define FLAC__ASSERT_H /* we need this since some compilers (like MSVC) leave assert()s on release code (and we don't want to use their ASSERT) */ -#ifdef DEBUG -// JUCE: removed as JUCE already includes standard headers and including -// these in FlacNamespace will cause problems - -//#include +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FLAC__ASSERT(x) if(!(x)) __builtin_abort(); +#define FLAC__ASSERT_DECLARATION(x) x +#else +#ifndef NDEBUG +#include #define FLAC__ASSERT(x) assert(x) #define FLAC__ASSERT_DECLARATION(x) x #else #define FLAC__ASSERT(x) #define FLAC__ASSERT_DECLARATION(x) #endif +#endif #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/callback.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/callback.h index cd22b433..4babcd32 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/callback.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/callback.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2004-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,10 +34,7 @@ #define FLAC__CALLBACK_H #include "ordinals.h" - -// JUCE: removed as JUCE already includes this and including stdlib -// in FlacNamespace will cause problems -//#include +#include /* for size_t */ /** \file include/FLAC/callback.h * @@ -93,7 +90,9 @@ typedef void* FLAC__IOHandle; /** Signature for the read callback. * The signature and semantics match POSIX fread() implementations - * and can generally be used interchangeably. + * and can generally be used interchangeably. Note that the global + * variable errno from errno.h is read by some libFLAC functions to + * detect read errors. * * \param ptr The address of the read buffer. * \param size The size of the records to be read. @@ -168,15 +167,18 @@ typedef int (*FLAC__IOCallback_Close) (FLAC__IOHandle handle); * required may be set to NULL. * * If the seek requirement for an interface is optional, you can signify that - * a data sorce is not seekable by setting the \a seek field to \c NULL. + * a data source is not seekable by setting the \a seek field to \c NULL. + * + * See the detailed documentation for callbacks in the + * \link flac_callbacks callbacks \endlink module. */ typedef struct { - FLAC__IOCallback_Read read; - FLAC__IOCallback_Write write; - FLAC__IOCallback_Seek seek; - FLAC__IOCallback_Tell tell; - FLAC__IOCallback_Eof eof; - FLAC__IOCallback_Close close; + FLAC__IOCallback_Read read; /**< See FLAC__IOCallbacks */ + FLAC__IOCallback_Write write; /**< See FLAC__IOCallbacks */ + FLAC__IOCallback_Seek seek; /**< See FLAC__IOCallbacks */ + FLAC__IOCallback_Tell tell; /**< See FLAC__IOCallbacks */ + FLAC__IOCallback_Eof eof; /**< See FLAC__IOCallbacks */ + FLAC__IOCallback_Close close; /**< See FLAC__IOCallbacks */ } FLAC__IOCallbacks; /* \} */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/compat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/compat.h index 6c264914..6f5d58a0 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/compat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/compat.h @@ -1,5 +1,5 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2012-2014 Xiph.org Foundation + * Copyright (C) 2012-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,7 +29,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* This is the prefered location of all CPP hackery to make $random_compiler +/* This is the preferred location of all CPP hackery to make $random_compiler * work like something approaching a C99 (or maybe more accurately GNU99) * compiler. * @@ -39,8 +39,20 @@ #ifndef FLAC__SHARE__COMPAT_H #define FLAC__SHARE__COMPAT_H +#include +#include + +#if defined _WIN32 && !defined __CYGWIN__ +/* where MSVC puts unlink() */ +# include +#else +# include +#endif + #if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#include /* for off_t */ #define FLAC__off_t __int64 /* use this instead of off_t to fix the 2 GB limit */ +#define FLAC__OFF_T_MAX INT64_MAX #if !defined __MINGW32__ #define fseeko _fseeki64 #define ftello _ftelli64 @@ -52,6 +64,14 @@ #endif #else #define FLAC__off_t off_t +#define FLAC__OFF_T_MAX OFF_T_MAX +#endif + + + +#ifdef HAVE_INTTYPES_H +#define __STDC_FORMAT_MACROS +#include #endif #if defined(_MSC_VER) @@ -59,7 +79,7 @@ #define strtoull _strtoui64 #endif -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__cplusplus) #define inline __inline #endif @@ -74,7 +94,10 @@ #define FLAC__U64L(x) x##ULL -#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if defined _MSC_VER || defined __MINGW32__ +#define FLAC__STRCASECMP _stricmp +#define FLAC__STRNCASECMP _strnicmp +#elif defined __BORLANDC__ #define FLAC__STRCASECMP stricmp #define FLAC__STRNCASECMP strnicmp #else @@ -82,56 +105,105 @@ #define FLAC__STRNCASECMP strncasecmp #endif +#if defined _MSC_VER || defined __MINGW32__ || defined __EMX__ +#include /* for _setmode(), chmod() */ +#include /* for _O_BINARY */ +#else +#include /* for chown(), unlink() */ +#endif + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if defined __BORLANDC__ +#include /* for utime() */ +#else +#include /* for utime() */ +#endif +#else +#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) +#include +#else +#include /* some flavors of BSD (like OS X) require this to get time_t */ +#include /* for utime() */ +#endif +#endif + #if defined _MSC_VER -# if _MSC_VER >= 1600 +# if _MSC_VER >= 1800 +# include +# elif _MSC_VER >= 1600 /* Visual Studio 2010 has decent C99 support */ +# include # define PRIu64 "llu" # define PRId64 "lld" # define PRIx64 "llx" # else +# include # ifndef UINT32_MAX # define UINT32_MAX _UI32_MAX # endif - typedef unsigned __int64 uint64_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int8 uint8_t; - typedef __int64 int64_t; - typedef __int32 int32_t; - typedef __int16 int16_t; - typedef __int8 int8_t; # define PRIu64 "I64u" # define PRId64 "I64d" # define PRIx64 "I64x" # endif +# if defined(_USING_V110_SDK71_) && !defined(_DLL) +# pragma message("WARNING: This compile will NOT FUNCTION PROPERLY on Windows XP. See comments in include/share/compat.h for details") +#define FLAC__USE_FILELENGTHI64 +/* + ************************************************************************************* + * V110_SDK71, in MSVC 2017 also known as v141_xp, is a platform toolset that is supposed + * to target Windows XP. It turns out however that certain functions provided silently fail + * on Windows XP only, which makes debugging challenging. This only occurs when building with + * /MT. This problem has been reported to Microsoft, but there hasn't been a fix for years. See + * https://web.archive.org/web/20170327195018/https://connect.microsoft.com/VisualStudio/feedback/details/1557168/wstat64-returns-1-on-xp-always + * + * It is known that this problem affects the functions _wstat64 (used by flac_stat i.e. + * stat64_utf8) and _fstat64 (i.e. flac_fstat) and therefore affects both libFLAC in + * several places as well as the flac and metaflac command line tools + * + * As the extent of this problem is unknown and Microsoft seems unwilling to fix it, + * users of libFLAC building with Visual Studio are encouraged to not use the /MT compile + * switch when explicitly targeting Windows XP. When use of /MT is deemed necessary with + * this toolset, be sure to check whether your application works properly on Windows XP. + * It is also possible to build for Windows XP with MinGW instead. + ************************************************************************************* +*/ +# endif #endif /* defined _MSC_VER */ #ifdef _WIN32 /* All char* strings are in UTF-8 format. Added to support Unicode files on Windows */ -#include "win_utf8_io.h" +#if 0 +#include "win_utf8_io.h" #define flac_printf printf_utf8 #define flac_fprintf fprintf_utf8 #define flac_vfprintf vfprintf_utf8 +#endif + #define flac_fopen fopen_utf8 #define flac_chmod chmod_utf8 #define flac_utime utime_utf8 #define flac_unlink unlink_utf8 #define flac_rename rename_utf8 -#define flac_stat _stat64_utf8 +#define flac_stat stat64_utf8 #else #define flac_printf printf #define flac_fprintf fprintf #define flac_vfprintf vfprintf + #define flac_fopen fopen #define flac_chmod chmod -#define flac_utime utime #define flac_unlink unlink #define flac_rename rename #define flac_stat stat +#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) +#define flac_utime(a, b) utimensat (AT_FDCWD, a, *b, 0) +#else +#define flac_utime utime +#endif #endif #ifdef _WIN32 @@ -142,6 +214,10 @@ #define flac_fstat fstat #endif +#ifdef ANDROID +#include +#endif + #ifndef M_LN2 #define M_LN2 0.69314718055994530942 #endif @@ -153,7 +229,7 @@ * snprintf as well as Microsoft Visual Studio which has an non-standards * conformant snprint_s function. * - * This function wraps the MS version to behave more like the the ISO version. + * This function wraps the MS version to behave more like the ISO version. */ #ifdef __cplusplus extern "C" { diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/endswap.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/endswap.h index 5e0d6e7e..8687b9d7 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/endswap.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/endswap.h @@ -1,5 +1,5 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2012-2014 Xiph.org Foundation + * Copyright (C) 2012-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,10 +31,10 @@ /* It is assumed that this header will be included after "config.h". */ -#if HAVE_BSWAP32 /* GCC and Clang */ +#ifdef HAVE_BSWAP32 /* GCC and Clang */ /* GCC prior to 4.8 didn't provide bswap16 on x86_64 */ -#if ! HAVE_BSWAP16 +#ifndef HAVE_BSWAP16 static inline unsigned short __builtin_bswap16(unsigned short a) { return (a<<8)|(a>>8); @@ -43,30 +43,34 @@ static inline unsigned short __builtin_bswap16(unsigned short a) #define ENDSWAP_16(x) (__builtin_bswap16 (x)) #define ENDSWAP_32(x) (__builtin_bswap32 (x)) +#define ENDSWAP_64(x) (__builtin_bswap64 (x)) -#elif defined _MSC_VER /* Windows. Apparently in . */ +#elif defined _MSC_VER /* Windows */ + +#include #define ENDSWAP_16(x) (_byteswap_ushort (x)) #define ENDSWAP_32(x) (_byteswap_ulong (x)) +#define ENDSWAP_64(x) (_byteswap_uint64 (x)) #elif defined HAVE_BYTESWAP_H /* Linux */ -// JUCE: removed as JUCE already includes standard headers and including -// these in FlacNamespace will cause problems -//#include +#include #define ENDSWAP_16(x) (bswap_16 (x)) #define ENDSWAP_32(x) (bswap_32 (x)) +#define ENDSWAP_64(x) (bswap_64 (x)) #else #define ENDSWAP_16(x) ((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8)) #define ENDSWAP_32(x) ((((x) >> 24) & 0xFF) | (((x) >> 8) & 0xFF00) | (((x) & 0xFF00) << 8) | (((x) & 0xFF) << 24)) +#define ENDSWAP_64(x) ((ENDSWAP_32(((x) >> 32) & 0xFFFFFFFF)) | (ENDSWAP_32((x) & 0xFFFFFFFF) << 32)) #endif -/* Host to little-endian byte swapping. */ +/* Host to little-endian byte swapping (for MD5 calculation) */ #if CPU_IS_BIG_ENDIAN #define H2LE_16(x) ENDSWAP_16 (x) diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/export.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/export.h index 9cc9e137..d14728a5 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/export.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/export.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,7 +36,7 @@ /** \file include/FLAC/export.h * * \brief - * This module contains #defines and symbols for exporting function + * This module contains \#defines and symbols for exporting function * calls, and providing version information and compiled-in features. * * See the \link flac_export export \endlink module. @@ -46,25 +46,43 @@ * \ingroup flac * * \brief - * This module contains #defines and symbols for exporting function + * This module contains \#defines and symbols for exporting function * calls, and providing version information and compiled-in features. * - * If you are compiling with MSVC and will link to the static library - * (libFLAC.lib) you should define FLAC__NO_DLL in your project to - * make sure the symbols are exported properly. + * If you are compiling for Windows (with Visual Studio or MinGW for + * example) and will link to the static library (libFLAC++.lib) you + * should define FLAC__NO_DLL in your project to make sure the symbols + * are exported properly. * * \{ */ -#if defined(FLAC__NO_DLL) -#define FLAC_API +/** This \#define is used internally in libFLAC and its headers to make + * sure the correct symbols are exported when working with shared + * libraries. On Windows, this \#define is set to __declspec(dllexport) + * when compiling libFLAC into a library and to __declspec(dllimport) + * when the headers are used to link to that DLL. On non-Windows systems + * it is used to set symbol visibility. + * + * Because of this, the define FLAC__NO_DLL must be defined when linking + * to libFLAC statically or linking will fail. + */ +/* This has grown quite complicated. FLAC__NO_DLL is used by MSVC sln + * files and CMake, which build either static or shared. autotools can + * build static, shared or **both**. Therefore, DLL_EXPORT, which is set + * by libtool, must override FLAC__NO_DLL on building shared components + */ +#if defined(_WIN32) -#elif defined(_MSC_VER) +#if defined(FLAC__NO_DLL) && !(defined(DLL_EXPORT)) +#define FLAC_API +#else #ifdef FLAC_API_EXPORTS #define FLAC_API __declspec(dllexport) #else #define FLAC_API __declspec(dllimport) #endif +#endif #elif defined(FLAC__USE_VISIBILITY_ATTR) #define FLAC_API __attribute__ ((visibility ("default"))) @@ -74,12 +92,12 @@ #endif -/** These #defines will mirror the libtool-based library version number, see +/** These \#defines will mirror the libtool-based library version number, see * http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning */ -#define FLAC_API_VERSION_CURRENT 11 +#define FLAC_API_VERSION_CURRENT 13 #define FLAC_API_VERSION_REVISION 0 /**< see above */ -#define FLAC_API_VERSION_AGE 3 /**< see above */ +#define FLAC_API_VERSION_AGE 1 /**< see above */ #ifdef __cplusplus extern "C" { diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/format.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/format.h index bd1bf9da..ef7c8b21 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/format.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/format.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -60,7 +60,7 @@ extern "C" { * structures used by the rest of the interfaces. * * First, you should be familiar with the - * FLAC format. Many of the values here + * FLAC format. Many of the values here * follow directly from the specification. As a user of libFLAC, the * interesting parts really are the structures that describe the frame * header and metadata blocks. @@ -113,19 +113,16 @@ extern "C" { /** The maximum sample resolution permitted by libFLAC. * - * \warning * FLAC__MAX_BITS_PER_SAMPLE is the limit of the FLAC format. However, - * the reference encoder/decoder is currently limited to 24 bits because - * of prevalent 32-bit math, so make sure and use this value when - * appropriate. + * the reference encoder/decoder used to be limited to 24 bits. This + * value was used to signal that limit. */ -#define FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE (24u) +#define FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE (32u) /** The maximum sample rate permitted by the format. The value is - * ((2 ^ 16) - 1) * 10; see FLAC format - * as to why. + * ((2 ^ 20) - 1) */ -#define FLAC__MAX_SAMPLE_RATE (655350u) +#define FLAC__MAX_SAMPLE_RATE (1048575u) /** The maximum LPC order permitted by the format. */ #define FLAC__MAX_LPC_ORDER (32u) @@ -173,10 +170,10 @@ extern FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4]; /* = "fLaC" */ /** The 32-bit integer big-endian representation of the beginning of * a FLAC stream. */ -extern FLAC_API const unsigned FLAC__STREAM_SYNC; /* = 0x664C6143 */ +extern FLAC_API const uint32_t FLAC__STREAM_SYNC; /* = 0x664C6143 */ /** The length of the FLAC signature in bits. */ -extern FLAC_API const unsigned FLAC__STREAM_SYNC_LEN; /* = 32 bits */ +extern FLAC_API const uint32_t FLAC__STREAM_SYNC_LEN; /* = 32 bits */ /** The length of the FLAC signature in bytes. */ #define FLAC__STREAM_SYNC_LENGTH (4u) @@ -213,26 +210,26 @@ extern FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[]; */ typedef struct { - unsigned *parameters; + uint32_t *parameters; /**< The Rice parameters for each context. */ - unsigned *raw_bits; + uint32_t *raw_bits; /**< Widths for escape-coded partitions. Will be non-zero for escaped * partitions and zero for unescaped partitions. */ - unsigned capacity_by_order; + uint32_t capacity_by_order; /**< The capacity of the \a parameters and \a raw_bits arrays * specified as an order, i.e. the number of array elements * allocated is 2 ^ \a capacity_by_order. */ } FLAC__EntropyCodingMethod_PartitionedRiceContents; -/** Header for a Rice partitioned residual. (c.f. format specification) +/** Header for a Rice partitioned residual. (c.f. format specification) */ typedef struct { - unsigned order; + uint32_t order; /**< The partition order, i.e. # of contexts = 2 ^ \a order. */ const FLAC__EntropyCodingMethod_PartitionedRiceContents *contents; @@ -240,17 +237,17 @@ typedef struct { } FLAC__EntropyCodingMethod_PartitionedRice; -extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; /**< == 4 (bits) */ -extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; /**< == 4 (bits) */ -extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN; /**< == 5 (bits) */ -extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN; /**< == 5 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN; /**< == 5 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN; /**< == 5 (bits) */ -extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; /**< == (1<format specification) +/** Header for the entropy coding method. (c.f. format specification) */ typedef struct { FLAC__EntropyCodingMethodType type; @@ -259,7 +256,7 @@ typedef struct { } data; } FLAC__EntropyCodingMethod; -extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN; /**< == 2 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_TYPE_LEN; /**< == 2 (bits) */ /*****************************************************************************/ @@ -279,30 +276,40 @@ typedef enum { extern FLAC_API const char * const FLAC__SubframeTypeString[]; -/** CONSTANT subframe. (c.f. format specification) +/** CONSTANT subframe. (c.f. format specification) */ typedef struct { - FLAC__int32 value; /**< The constant signal value. */ + FLAC__int64 value; /**< The constant signal value. */ } FLAC__Subframe_Constant; +/** An enumeration of the possible verbatim subframe data types. */ +typedef enum { + FLAC__VERBATIM_SUBFRAME_DATA_TYPE_INT32, /**< verbatim subframe has 32-bit int */ + FLAC__VERBATIM_SUBFRAME_DATA_TYPE_INT64 /**< verbatim subframe has 64-bit int */ +} FLAC__VerbatimSubframeDataType; + -/** VERBATIM subframe. (c.f. format specification) +/** VERBATIM subframe. (c.f. format specification) */ typedef struct { - const FLAC__int32 *data; /**< A pointer to verbatim signal. */ + union { + const FLAC__int32 *int32; /**< A FLAC__int32 pointer to verbatim signal. */ + const FLAC__int64 *int64; /**< A FLAC__int64 pointer to verbatim signal. */ + } data; + FLAC__VerbatimSubframeDataType data_type; } FLAC__Subframe_Verbatim; -/** FIXED subframe. (c.f. format specification) +/** FIXED subframe. (c.f. format specification) */ typedef struct { FLAC__EntropyCodingMethod entropy_coding_method; /**< The residual coding method. */ - unsigned order; + uint32_t order; /**< The polynomial order. */ - FLAC__int32 warmup[FLAC__MAX_FIXED_ORDER]; + FLAC__int64 warmup[FLAC__MAX_FIXED_ORDER]; /**< Warmup samples to prime the predictor, length == order. */ const FLAC__int32 *residual; @@ -310,16 +317,16 @@ typedef struct { } FLAC__Subframe_Fixed; -/** LPC subframe. (c.f. format specification) +/** LPC subframe. (c.f. format specification) */ typedef struct { FLAC__EntropyCodingMethod entropy_coding_method; /**< The residual coding method. */ - unsigned order; + uint32_t order; /**< The FIR order. */ - unsigned qlp_coeff_precision; + uint32_t qlp_coeff_precision; /**< Quantized FIR filter coefficient precision in bits. */ int quantization_level; @@ -328,18 +335,18 @@ typedef struct { FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; /**< FIR filter coefficients. */ - FLAC__int32 warmup[FLAC__MAX_LPC_ORDER]; + FLAC__int64 warmup[FLAC__MAX_LPC_ORDER]; /**< Warmup samples to prime the predictor, length == order. */ const FLAC__int32 *residual; /**< The residual signal, length == (blocksize minus order) samples. */ } FLAC__Subframe_LPC; -extern FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN; /**< == 4 (bits) */ -extern FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN; /**< == 5 (bits) */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN; /**< == 5 (bits) */ -/** FLAC subframe structure. (c.f. format specification) +/** FLAC subframe structure. (c.f. format specification) */ typedef struct { FLAC__SubframeType type; @@ -349,7 +356,7 @@ typedef struct { FLAC__Subframe_LPC lpc; FLAC__Subframe_Verbatim verbatim; } data; - unsigned wasted_bits; + uint32_t wasted_bits; } FLAC__Subframe; /** == 1 (bit) @@ -359,14 +366,14 @@ typedef struct { * mandatory value of \c 0 but in the future may take on the value \c 0 or \c 1 * to mean something else. */ -extern FLAC_API const unsigned FLAC__SUBFRAME_ZERO_PAD_LEN; -extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LEN; /**< == 6 (bits) */ -extern FLAC_API const unsigned FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_ZERO_PAD_LEN; +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LEN; /**< == 6 (bits) */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN; /**< == 1 (bit) */ -extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK; /**< = 0x00 */ -extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK; /**< = 0x02 */ -extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK; /**< = 0x10 */ -extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK; /**< = 0x40 */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK; /**< = 0x00 */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK; /**< = 0x02 */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK; /**< = 0x10 */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK; /**< = 0x40 */ /*****************************************************************************/ @@ -406,22 +413,22 @@ typedef enum { extern FLAC_API const char * const FLAC__FrameNumberTypeString[]; -/** FLAC frame header structure. (c.f. format specification) +/** FLAC frame header structure. (c.f. format specification) */ typedef struct { - unsigned blocksize; + uint32_t blocksize; /**< The number of samples per subframe. */ - unsigned sample_rate; + uint32_t sample_rate; /**< The sample rate in Hz. */ - unsigned channels; + uint32_t channels; /**< The number of channels (== number of subframes). */ FLAC__ChannelAssignment channel_assignment; /**< The channel assignment for the frame. */ - unsigned bits_per_sample; + uint32_t bits_per_sample; /**< The sample resolution. */ FLAC__FrameNumberType number_type; @@ -443,19 +450,19 @@ typedef struct { */ } FLAC__FrameHeader; -extern FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC; /**< == 0x3ffe; the frame header sync code */ -extern FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN; /**< == 14 (bits) */ -extern FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN; /**< == 1 (bits) */ -extern FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN; /**< == 1 (bits) */ -extern FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN; /**< == 4 (bits) */ -extern FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN; /**< == 4 (bits) */ -extern FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN; /**< == 4 (bits) */ -extern FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN; /**< == 3 (bits) */ -extern FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN; /**< == 1 (bit) */ -extern FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN; /**< == 8 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC; /**< == 0x3ffe; the frame header sync code */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC_LEN; /**< == 14 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_RESERVED_LEN; /**< == 1 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN; /**< == 1 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCK_SIZE_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SAMPLE_RATE_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN; /**< == 3 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_ZERO_PAD_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_CRC_LEN; /**< == 8 (bits) */ -/** FLAC frame footer structure. (c.f. format specification) +/** FLAC frame footer structure. (c.f. format specification) */ typedef struct { FLAC__uint16 crc; @@ -465,10 +472,10 @@ typedef struct { */ } FLAC__FrameFooter; -extern FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN; /**< == 16 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_FOOTER_CRC_LEN; /**< == 16 (bits) */ -/** FLAC frame structure. (c.f. format specification) +/** FLAC frame structure. (c.f. format specification) */ typedef struct { FLAC__FrameHeader header; @@ -489,31 +496,31 @@ typedef struct { typedef enum { FLAC__METADATA_TYPE_STREAMINFO = 0, - /**< STREAMINFO block */ + /**< STREAMINFO block */ FLAC__METADATA_TYPE_PADDING = 1, - /**< PADDING block */ + /**< PADDING block */ FLAC__METADATA_TYPE_APPLICATION = 2, - /**< APPLICATION block */ + /**< APPLICATION block */ FLAC__METADATA_TYPE_SEEKTABLE = 3, - /**< SEEKTABLE block */ + /**< SEEKTABLE block */ FLAC__METADATA_TYPE_VORBIS_COMMENT = 4, - /**< VORBISCOMMENT block (a.k.a. FLAC tags) */ + /**< VORBISCOMMENT block (a.k.a. FLAC tags) */ FLAC__METADATA_TYPE_CUESHEET = 5, - /**< CUESHEET block */ + /**< CUESHEET block */ FLAC__METADATA_TYPE_PICTURE = 6, - /**< PICTURE block */ + /**< PICTURE block */ FLAC__METADATA_TYPE_UNDEFINED = 7, /**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */ - FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE, - /**< No type will ever be greater than this. There is not enough room in the protocol block. */ + FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE, + /**< No type will ever be greater than this. There is not enough room in the protocol block. */ } FLAC__MetadataType; /** Maps a FLAC__MetadataType to a C string. @@ -524,32 +531,32 @@ typedef enum { extern FLAC_API const char * const FLAC__MetadataTypeString[]; -/** FLAC STREAMINFO structure. (c.f. format specification) +/** FLAC STREAMINFO structure. (c.f. format specification) */ typedef struct { - unsigned min_blocksize, max_blocksize; - unsigned min_framesize, max_framesize; - unsigned sample_rate; - unsigned channels; - unsigned bits_per_sample; + uint32_t min_blocksize, max_blocksize; + uint32_t min_framesize, max_framesize; + uint32_t sample_rate; + uint32_t channels; + uint32_t bits_per_sample; FLAC__uint64 total_samples; FLAC__byte md5sum[16]; } FLAC__StreamMetadata_StreamInfo; -extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; /**< == 16 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; /**< == 16 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; /**< == 24 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; /**< == 24 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; /**< == 20 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; /**< == 3 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; /**< == 5 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; /**< == 36 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN; /**< == 128 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; /**< == 20 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; /**< == 3 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; /**< == 5 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; /**< == 36 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN; /**< == 128 (bits) */ /** The total stream length of the STREAMINFO block in bytes. */ #define FLAC__STREAM_METADATA_STREAMINFO_LENGTH (34u) -/** FLAC PADDING structure. (c.f. format specification) +/** FLAC PADDING structure. (c.f. format specification) */ typedef struct { int dummy; @@ -560,16 +567,16 @@ typedef struct { } FLAC__StreamMetadata_Padding; -/** FLAC APPLICATION structure. (c.f. format specification) +/** FLAC APPLICATION structure. (c.f. format specification) */ typedef struct { FLAC__byte id[4]; FLAC__byte *data; } FLAC__StreamMetadata_Application; -extern FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_APPLICATION_ID_LEN; /**< == 32 (bits) */ -/** SeekPoint structure used in SEEKTABLE blocks. (c.f. format specification) +/** SeekPoint structure used in SEEKTABLE blocks. (c.f. format specification) */ typedef struct { FLAC__uint64 sample_number; @@ -579,13 +586,13 @@ typedef struct { /**< The offset, in bytes, of the target frame with respect to * beginning of the first frame. */ - unsigned frame_samples; + uint32_t frame_samples; /**< The number of samples in the target frame. */ } FLAC__StreamMetadata_SeekPoint; -extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN; /**< == 64 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN; /**< == 64 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN; /**< == 16 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN; /**< == 16 (bits) */ /** The total stream length of a seek point in bytes. */ #define FLAC__STREAM_METADATA_SEEKPOINT_LENGTH (18u) @@ -597,7 +604,7 @@ extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN extern FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; -/** FLAC SEEKTABLE structure. (c.f. format specification) +/** FLAC SEEKTABLE structure. (c.f. format specification) * * \note From the format specification: * - The seek points must be sorted by ascending sample number. @@ -610,12 +617,12 @@ extern FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; * present in a stream. */ typedef struct { - unsigned num_points; + uint32_t num_points; FLAC__StreamMetadata_SeekPoint *points; } FLAC__StreamMetadata_SeekTable; -/** Vorbis comment entry structure used in VORBIS_COMMENT blocks. (c.f. format specification) +/** Vorbis comment entry structure used in VORBIS_COMMENT blocks. (c.f. format specification) * * For convenience, the APIs maintain a trailing NUL character at the end of * \a entry which is not counted toward \a length, i.e. @@ -626,10 +633,10 @@ typedef struct { FLAC__byte *entry; } FLAC__StreamMetadata_VorbisComment_Entry; -extern FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN; /**< == 32 (bits) */ -/** FLAC VORBIS_COMMENT structure. (c.f. format specification) +/** FLAC VORBIS_COMMENT structure. (c.f. format specification) */ typedef struct { FLAC__StreamMetadata_VorbisComment_Entry vendor_string; @@ -637,11 +644,11 @@ typedef struct { FLAC__StreamMetadata_VorbisComment_Entry *comments; } FLAC__StreamMetadata_VorbisComment; -extern FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN; /**< == 32 (bits) */ /** FLAC CUESHEET track index structure. (See the - * format specification for + * format specification for * the full description of each field.) */ typedef struct { @@ -654,13 +661,13 @@ typedef struct { /**< The index point number. */ } FLAC__StreamMetadata_CueSheet_Index; -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN; /**< == 64 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN; /**< == 8 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN; /**< == 3*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN; /**< == 3*8 (bits) */ /** FLAC CUESHEET track structure. (See the - * format specification for + * format specification for * the full description of each field.) */ typedef struct { @@ -673,10 +680,10 @@ typedef struct { char isrc[13]; /**< Track ISRC. This is a 12-digit alphanumeric code plus a trailing \c NUL byte */ - unsigned type:1; + uint32_t type:1; /**< The track type: 0 for audio, 1 for non-audio. */ - unsigned pre_emphasis:1; + uint32_t pre_emphasis:1; /**< The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis. */ FLAC__byte num_indices; @@ -687,17 +694,17 @@ typedef struct { } FLAC__StreamMetadata_CueSheet_Track; -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN; /**< == 64 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN; /**< == 8 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN; /**< == 12*8 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN; /**< == 1 (bit) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN; /**< == 1 (bit) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN; /**< == 6+13*8 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN; /**< == 8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN; /**< == 12*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN; /**< == 6+13*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN; /**< == 8 (bits) */ /** FLAC CUESHEET structure. (See the - * format specification + * format specification * for the full description of each field.) */ typedef struct { @@ -713,7 +720,7 @@ typedef struct { FLAC__bool is_cd; /**< \c true if CUESHEET corresponds to a Compact Disc, else \c false. */ - unsigned num_tracks; + uint32_t num_tracks; /**< The number of tracks. */ FLAC__StreamMetadata_CueSheet_Track *tracks; @@ -721,11 +728,11 @@ typedef struct { } FLAC__StreamMetadata_CueSheet; -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN; /**< == 128*8 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN; /**< == 64 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN; /**< == 1 (bit) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN; /**< == 7+258*8 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN; /**< == 8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN; /**< == 128*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN; /**< == 7+258*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN; /**< == 8 (bits) */ /** An enumeration of the PICTURE types (see FLAC__StreamMetadataPicture and id3 v2.4 APIC tag). */ @@ -763,7 +770,7 @@ typedef enum { extern FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[]; /** FLAC PICTURE structure. (See the - * format specification + * format specification * for the full description of each field.) */ typedef struct { @@ -810,14 +817,14 @@ typedef struct { } FLAC__StreamMetadata_Picture; -extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN; /**< == 32 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN; /**< == 32 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN; /**< == 32 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN; /**< == 32 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN; /**< == 32 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN; /**< == 32 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN; /**< == 32 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_TYPE_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_COLORS_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN; /**< == 32 (bits) */ /** Structure that is used when a metadata block of unknown type is loaded. @@ -829,9 +836,9 @@ typedef struct { } FLAC__StreamMetadata_Unknown; -/** FLAC metadata block structure. (c.f. format specification) +/** FLAC metadata block structure. (c.f. format specification) */ -typedef struct { +typedef struct FLAC__StreamMetadata { FLAC__MetadataType type; /**< The type of the metadata block; used determine which member of the * \a data union to dereference. If type >= FLAC__METADATA_TYPE_UNDEFINED @@ -840,7 +847,7 @@ typedef struct { FLAC__bool is_last; /**< \c true if this metadata block is the last, else \a false */ - unsigned length; + uint32_t length; /**< Length, in bytes, of the block data as it appears in the stream. */ union { @@ -857,9 +864,9 @@ typedef struct { * to use. */ } FLAC__StreamMetadata; -extern FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN; /**< == 1 (bit) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN; /**< == 7 (bits) */ -extern FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_IS_LAST_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_TYPE_LEN; /**< == 7 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bits) */ /** The total stream length of a metadata block header in bytes. */ #define FLAC__STREAM_METADATA_HEADER_LENGTH (4u) @@ -880,7 +887,7 @@ extern FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bit * \c true if the given sample rate conforms to the specification, else * \c false. */ -FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate); +FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(uint32_t sample_rate); /** Tests that a blocksize at the given sample rate is valid for the FLAC * subset. @@ -892,7 +899,7 @@ FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate); * \c true if the given blocksize conforms to the specification for the * subset at the given sample rate, else \c false. */ -FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(unsigned blocksize, unsigned sample_rate); +FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(uint32_t blocksize, uint32_t sample_rate); /** Tests that a sample rate is valid for the FLAC subset. The subset rules * for valid sample rates are slightly more complex since the rate has to @@ -903,7 +910,7 @@ FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(unsigned blocksize, unsigne * \c true if the given sample rate conforms to the specification for the * subset, else \c false. */ -FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate); +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(uint32_t sample_rate); /** Check a Vorbis comment entry name to see if it conforms to the Vorbis * comment specification. @@ -926,14 +933,14 @@ FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *n * * \param value A string to be checked. * \param length A the length of \a value in bytes. May be - * \c (unsigned)(-1) to indicate that \a value is a plain + * \c (uint32_t)(-1) to indicate that \a value is a plain * UTF-8 NUL-terminated string. * \assert * \code value != NULL \endcode * \retval FLAC__bool * \c false if entry name is illegal, else \c true. */ -FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length); +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, uint32_t length); /** Check a Vorbis comment entry to see if it conforms to the Vorbis * comment specification. @@ -950,7 +957,7 @@ FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__ * \retval FLAC__bool * \c false if entry name is illegal, else \c true. */ -FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length); +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, uint32_t length); /** Check a seek table to see if it conforms to the FLAC specification. * See the format specification for limits on the contents of the @@ -973,10 +980,10 @@ FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_S * \param seek_table A pointer to a seek table to be sorted. * \assert * \code seek_table != NULL \endcode - * \retval unsigned + * \retval uint32_t * The number of duplicate seek points converted into placeholders. */ -FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table); +FLAC_API uint32_t FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table); /** Check a cue sheet to see if it conforms to the FLAC specification. * See the format specification for limits on the contents of the diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c index bbfe8c14..c4c303c6 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -60,50 +60,14 @@ * silog2( 9) = 5 * silog2( 10) = 5 */ -unsigned FLAC__bitmath_silog2(int v) +uint32_t FLAC__bitmath_silog2(FLAC__int64 v) { - while(1) { - if(v == 0) { - return 0; - } - else if(v > 0) { - unsigned l = 0; - while(v) { - l++; - v >>= 1; - } - return l+1; - } - else if(v == -1) { - return 2; - } - else { - v++; - v = -v; - } - } -} + if(v == 0) + return 0; -unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v) -{ - while(1) { - if(v == 0) { - return 0; - } - else if(v > 0) { - unsigned l = 0; - while(v) { - l++; - v >>= 1; - } - return l+1; - } - else if(v == -1) { - return 2; - } - else { - v++; - v = -v; - } - } + if(v == -1) + return 2; + + v = (v < 0) ? (-(v+1)) : v; + return FLAC__bitmath_ilog2_wide(v)+2; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c index 0f0e1968..f8037078 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,22 +39,49 @@ #include "include/private/bitmath.h" #include "include/private/bitreader.h" #include "include/private/crc.h" +#include "include/private/cpu.h" + #include "../assert.h" #include "../compat.h" #include "../endswap.h" /* Things should be fastest when this matches the machine word size */ -/* WATCHOUT: if you change this you must also change the following #defines down to FLAC__clz_uint32 below to match */ -/* WATCHOUT: there are a few places where the code will not work unless uint32_t is >= 32 bits wide */ +/* WATCHOUT: if you change this you must also change the following #defines down to COUNT_ZERO_MSBS2 below to match */ +/* WATCHOUT: there are a few places where the code will not work unless brword is >= 32 bits wide */ /* also, some sections currently only have fast versions for 4 or 8 bytes per word */ -#define FLAC__BYTES_PER_WORD 4 /* sizeof uint32_t */ -#define FLAC__BITS_PER_WORD (8 * FLAC__BYTES_PER_WORD) + +#if (ENABLE_64_BIT_WORDS == 0) + +typedef FLAC__uint32 brword; +#define FLAC__BYTES_PER_WORD 4 /* sizeof brword */ +#define FLAC__BITS_PER_WORD 32 #define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) -/* SWAP_BE_WORD_TO_HOST swaps bytes in a uint32_t (which is always big-endian) if necessary to match host byte order */ +/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */ #if WORDS_BIGENDIAN #define SWAP_BE_WORD_TO_HOST(x) (x) #else #define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_32(x) +#endif +/* counts the # of zero MSBs in a word */ +#define COUNT_ZERO_MSBS(word) FLAC__clz_uint32(word) +#define COUNT_ZERO_MSBS2(word) FLAC__clz2_uint32(word) + +#else + +typedef FLAC__uint64 brword; +#define FLAC__BYTES_PER_WORD 8 /* sizeof brword */ +#define FLAC__BITS_PER_WORD 64 +#define FLAC__WORD_ALL_ONES ((FLAC__uint64)FLAC__U64L(0xffffffffffffffff)) +/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */ +#if WORDS_BIGENDIAN +#define SWAP_BE_WORD_TO_HOST(x) (x) +#else +#define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_64(x) +#endif +/* counts the # of zero MSBs in a word */ +#define COUNT_ZERO_MSBS(word) FLAC__clz_uint64(word) +#define COUNT_ZERO_MSBS2(word) FLAC__clz2_uint64(word) + #endif /* @@ -71,60 +98,79 @@ * also depends on the CPU cache size and other factors; some twiddling * may be necessary to squeeze out the best performance. */ -static const unsigned FLAC__BITREADER_DEFAULT_CAPACITY = 65536u / FLAC__BITS_PER_WORD; /* in words */ +static const uint32_t FLAC__BITREADER_DEFAULT_CAPACITY = 65536u / FLAC__BITS_PER_WORD; /* in words */ struct FLAC__BitReader { /* any partially-consumed word at the head will stay right-justified as bits are consumed from the left */ /* any incomplete word at the tail will be left-justified, and bytes from the read callback are added on the right */ - uint32_t *buffer; - unsigned capacity; /* in words */ - unsigned words; /* # of completed words in buffer */ - unsigned bytes; /* # of bytes in incomplete word at buffer[words] */ - unsigned consumed_words; /* #words ... */ - unsigned consumed_bits; /* ... + (#bits of head word) already consumed from the front of buffer */ - unsigned read_crc16; /* the running frame CRC */ - unsigned crc16_align; /* the number of bits in the current consumed word that should not be CRC'd */ + brword *buffer; + uint32_t capacity; /* in words */ + uint32_t words; /* # of completed words in buffer */ + uint32_t bytes; /* # of bytes in incomplete word at buffer[words] */ + uint32_t consumed_words; /* #words ... */ + uint32_t consumed_bits; /* ... + (#bits of head word) already consumed from the front of buffer */ + uint32_t read_crc16; /* the running frame CRC */ + uint32_t crc16_offset; /* the number of words in the current buffer that should not be CRC'd */ + uint32_t crc16_align; /* the number of bits in the current consumed word that should not be CRC'd */ + FLAC__bool read_limit_set; /* whether reads are limited */ + uint32_t read_limit; /* the remaining size of what can be read */ + uint32_t last_seen_framesync; /* the location of the last seen framesync, if it is in the buffer, in bits from front of buffer */ FLAC__BitReaderReadCallback read_callback; void *client_data; }; -static inline void crc16_update_word_(FLAC__BitReader *br, uint32_t word) +static inline void crc16_update_word_(FLAC__BitReader *br, brword word) { - unsigned crc = br->read_crc16; -#if FLAC__BYTES_PER_WORD == 4 - switch(br->crc16_align) { - case 0: crc = FLAC__CRC16_UPDATE((unsigned)(word >> 24), crc); - case 8: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 16) & 0xff), crc); - case 16: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 8) & 0xff), crc); - case 24: br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)(word & 0xff), crc); + uint32_t crc = br->read_crc16; + + for ( ; br->crc16_align < FLAC__BITS_PER_WORD ; br->crc16_align += 8) { + uint32_t shift = FLAC__BITS_PER_WORD - 8 - br->crc16_align ; + crc = FLAC__CRC16_UPDATE ((uint32_t) (shift < FLAC__BITS_PER_WORD ? (word >> shift) & 0xff : 0), crc); } + + br->read_crc16 = crc; + br->crc16_align = 0; +} + +static inline void crc16_update_block_(FLAC__BitReader *br) +{ + if(br->consumed_words > br->crc16_offset && br->crc16_align) + crc16_update_word_(br, br->buffer[br->crc16_offset++]); + + /* Prevent OOB read due to wrap-around. */ + if (br->consumed_words > br->crc16_offset) { +#if FLAC__BYTES_PER_WORD == 4 + br->read_crc16 = FLAC__crc16_update_words32(br->buffer + br->crc16_offset, br->consumed_words - br->crc16_offset, br->read_crc16); #elif FLAC__BYTES_PER_WORD == 8 - switch(br->crc16_align) { - case 0: crc = FLAC__CRC16_UPDATE((unsigned)(word >> 56), crc); - case 8: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 48) & 0xff), crc); - case 16: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 40) & 0xff), crc); - case 24: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 32) & 0xff), crc); - case 32: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 24) & 0xff), crc); - case 40: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 16) & 0xff), crc); - case 48: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 8) & 0xff), crc); - case 56: br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)(word & 0xff), crc); - } + br->read_crc16 = FLAC__crc16_update_words64(br->buffer + br->crc16_offset, br->consumed_words - br->crc16_offset, br->read_crc16); #else - for( ; br->crc16_align < FLAC__BITS_PER_WORD; br->crc16_align += 8) - crc = FLAC__CRC16_UPDATE((unsigned)((word >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), crc); - br->read_crc16 = crc; + unsigned i; + + for (i = br->crc16_offset; i < br->consumed_words; i++) + crc16_update_word_(br, br->buffer[i]); #endif - br->crc16_align = 0; + } + + br->crc16_offset = 0; } static FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) { - unsigned start, end; + uint32_t start, end; size_t bytes; FLAC__byte *target; +#if WORDS_BIGENDIAN +#else + brword preswap_backup; +#endif /* first shift the unconsumed buffer data toward the front as much as possible */ if(br->consumed_words > 0) { + /* invalidate last seen framesync */ + br->last_seen_framesync = (uint32_t) -1; + + crc16_update_block_(br); /* CRC consumed words */ + start = br->consumed_words; end = br->words + (br->bytes? 1:0); memmove(br->buffer, br->buffer+start, FLAC__BYTES_PER_WORD * (end - start)); @@ -141,9 +187,9 @@ static FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) return false; /* no space left, buffer is too small; see note for FLAC__BITREADER_DEFAULT_CAPACITY */ target = ((FLAC__byte*)(br->buffer+br->words)) + br->bytes; - /* before reading, if the existing reader looks like this (say uint32_t is 32 bits wide) + /* before reading, if the existing reader looks like this (say brword is 32 bits wide) * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 (partial tail word is left-justified) - * buffer[BE]: 11 22 33 44 55 ?? ?? ?? (shown layed out as bytes sequentially in memory) + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? (shown laid out as bytes sequentially in memory) * buffer[LE]: 44 33 22 11 ?? ?? ?? 55 (?? being don't-care) * ^^-------target, bytes=3 * on LE machines, have to byteswap the odd tail word so nothing is @@ -151,6 +197,7 @@ static FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) */ #if WORDS_BIGENDIAN #else + preswap_backup = br->buffer[br->words]; if(br->bytes) br->buffer[br->words] = SWAP_BE_WORD_TO_HOST(br->buffer[br->words]); #endif @@ -163,8 +210,16 @@ static FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) */ /* read in the data; note that the callback may return a smaller number of bytes */ - if(!br->read_callback(target, &bytes, br->client_data)) + if(!br->read_callback(target, &bytes, br->client_data)){ + /* Despite the read callback failing, the data in the target + * might be used later, when the buffer is rewound. Therefore + * we revert the swap that was just done */ +#if WORDS_BIGENDIAN +#else + br->buffer[br->words] = preswap_backup; +#endif return false; + } /* after reading bytes 66 77 88 99 AA BB CC DD EE FF from the client: * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF @@ -174,7 +229,7 @@ static FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) */ #if WORDS_BIGENDIAN #else - end = (br->words*FLAC__BYTES_PER_WORD + br->bytes + bytes + (FLAC__BYTES_PER_WORD-1)) / FLAC__BYTES_PER_WORD; + end = (br->words*FLAC__BYTES_PER_WORD + br->bytes + (uint32_t)bytes + (FLAC__BYTES_PER_WORD-1)) / FLAC__BYTES_PER_WORD; for(start = br->words; start < end; start++) br->buffer[start] = SWAP_BE_WORD_TO_HOST(br->buffer[start]); #endif @@ -185,7 +240,7 @@ static FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) * buffer[LE]: 44 33 22 11 88 77 66 55 CC BB AA 99 ?? FF EE DD * finally we'll update the reader values: */ - end = br->words*FLAC__BYTES_PER_WORD + br->bytes + bytes; + end = br->words*FLAC__BYTES_PER_WORD + br->bytes + (uint32_t)bytes; br->words = end / FLAC__BYTES_PER_WORD; br->bytes = end % FLAC__BYTES_PER_WORD; @@ -235,11 +290,14 @@ FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__BitReaderReadCallback br->words = br->bytes = 0; br->consumed_words = br->consumed_bits = 0; br->capacity = FLAC__BITREADER_DEFAULT_CAPACITY; - br->buffer = (uint32_t*) malloc(sizeof(uint32_t) * br->capacity); + br->buffer = (brword*) malloc(sizeof(brword) * br->capacity); if(br->buffer == 0) return false; br->read_callback = rcb; br->client_data = cd; + br->read_limit_set = false; + br->read_limit = (uint32_t) -1; + br->last_seen_framesync = (uint32_t) -1; return true; } @@ -256,42 +314,36 @@ void FLAC__bitreader_free(FLAC__BitReader *br) br->consumed_words = br->consumed_bits = 0; br->read_callback = 0; br->client_data = 0; + br->read_limit_set = false; + br->read_limit = (uint32_t) -1; + br->last_seen_framesync = (uint32_t) -1; } FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br) { br->words = br->bytes = 0; br->consumed_words = br->consumed_bits = 0; + br->read_limit_set = false; + br->read_limit = (uint32_t) -1; + br->last_seen_framesync = (uint32_t) -1; return true; } -void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out) +void FLAC__bitreader_set_framesync_location(FLAC__BitReader *br) { - unsigned i, j; - if(br == 0) { - fprintf(out, "bitreader is NULL\n"); + br->last_seen_framesync = br->consumed_words * FLAC__BYTES_PER_WORD + br->consumed_bits / 8; +} + +FLAC__bool FLAC__bitreader_rewind_to_after_last_seen_framesync(FLAC__BitReader *br) +{ + if(br->last_seen_framesync == (uint32_t)-1) { + br->consumed_words = br->consumed_bits = 0; + return false; } else { - fprintf(out, "bitreader: capacity=%u words=%u bytes=%u consumed: words=%u, bits=%u\n", br->capacity, br->words, br->bytes, br->consumed_words, br->consumed_bits); - - for(i = 0; i < br->words; i++) { - fprintf(out, "%08X: ", i); - for(j = 0; j < FLAC__BITS_PER_WORD; j++) - if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) - fprintf(out, "."); - else - fprintf(out, "%01u", br->buffer[i] & (1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0); - fprintf(out, "\n"); - } - if(br->bytes > 0) { - fprintf(out, "%08X: ", i); - for(j = 0; j < br->bytes*8; j++) - if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) - fprintf(out, "."); - else - fprintf(out, "%01u", br->buffer[i] & (1 << (br->bytes*8-j-1)) ? 1:0); - fprintf(out, "\n"); - } + br->consumed_words = (br->last_seen_framesync + 1) / FLAC__BYTES_PER_WORD; + br->consumed_bits = ((br->last_seen_framesync + 1) % FLAC__BYTES_PER_WORD) * 8; + return true; } } @@ -301,7 +353,8 @@ void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed) FLAC__ASSERT(0 != br->buffer); FLAC__ASSERT((br->consumed_bits & 7) == 0); - br->read_crc16 = (unsigned)seed; + br->read_crc16 = (uint32_t)seed; + br->crc16_offset = br->consumed_words; br->crc16_align = br->consumed_bits; } @@ -309,14 +362,18 @@ FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br) { FLAC__ASSERT(0 != br); FLAC__ASSERT(0 != br->buffer); + + /* CRC consumed words up to here */ + crc16_update_block_(br); + FLAC__ASSERT((br->consumed_bits & 7) == 0); FLAC__ASSERT(br->crc16_align <= br->consumed_bits); /* CRC any tail bytes in a partially-consumed word */ if(br->consumed_bits) { - const uint32_t tail = br->buffer[br->consumed_words]; + const brword tail = br->buffer[br->consumed_words]; for( ; br->crc16_align < br->consumed_bits; br->crc16_align += 8) - br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)((tail >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), br->read_crc16); + br->read_crc16 = FLAC__CRC16_UPDATE((uint32_t)((tail >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), br->read_crc16); } return br->read_crc16; } @@ -326,17 +383,39 @@ inline FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader return ((br->consumed_bits & 7) == 0); } -inline unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br) +inline uint32_t FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br) { return 8 - (br->consumed_bits & 7); } -inline unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br) +inline uint32_t FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br) { return (br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits; } -FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits) +void FLAC__bitreader_set_limit(FLAC__BitReader *br, uint32_t limit) +{ + br->read_limit = limit; + br->read_limit_set = true; +} + +void FLAC__bitreader_remove_limit(FLAC__BitReader *br) +{ + br->read_limit_set = false; + br->read_limit = (uint32_t) -1; +} + +uint32_t FLAC__bitreader_limit_remaining(FLAC__BitReader *br) +{ + FLAC__ASSERT(br->read_limit_set); + return br->read_limit; +} +void FLAC__bitreader_limit_invalidate(FLAC__BitReader *br) +{ + br->read_limit = (uint32_t) -1; +} + +FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, uint32_t bits) { FLAC__ASSERT(0 != br); FLAC__ASSERT(0 != br->buffer); @@ -353,6 +432,15 @@ FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *va return true; } + if(br->read_limit_set && br->read_limit < (uint32_t)-1){ + if(br->read_limit < bits) { + br->read_limit = (uint32_t) -1; + return false; + } + else + br->read_limit -= bits; + } + while((br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits < bits) { if(!bitreader_read_from_client_(br)) return false; @@ -361,35 +449,37 @@ FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *va /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ if(br->consumed_bits) { /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ - const unsigned n = FLAC__BITS_PER_WORD - br->consumed_bits; - const uint32_t word = br->buffer[br->consumed_words]; + const uint32_t n = FLAC__BITS_PER_WORD - br->consumed_bits; + const brword word = br->buffer[br->consumed_words]; + const brword mask = br->consumed_bits < FLAC__BITS_PER_WORD ? FLAC__WORD_ALL_ONES >> br->consumed_bits : 0; if(bits < n) { - *val = (word & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (n-bits); + uint32_t shift = n - bits; + *val = shift < FLAC__BITS_PER_WORD ? (FLAC__uint32)((word & mask) >> shift) : 0; /* The result has <= 32 non-zero bits */ br->consumed_bits += bits; return true; } - *val = word & (FLAC__WORD_ALL_ONES >> br->consumed_bits); + /* (FLAC__BITS_PER_WORD - br->consumed_bits <= bits) ==> (FLAC__WORD_ALL_ONES >> br->consumed_bits) has no more than 'bits' non-zero bits */ + *val = (FLAC__uint32)(word & mask); bits -= n; - crc16_update_word_(br, word); br->consumed_words++; br->consumed_bits = 0; if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ - *val <<= bits; - *val |= (br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits)); + uint32_t shift = FLAC__BITS_PER_WORD - bits; + *val = bits < 32 ? *val << bits : 0; + *val |= shift < FLAC__BITS_PER_WORD ? (FLAC__uint32)(br->buffer[br->consumed_words] >> shift) : 0; br->consumed_bits = bits; } return true; } - else { - const uint32_t word = br->buffer[br->consumed_words]; + else { /* br->consumed_bits == 0 */ + const brword word = br->buffer[br->consumed_words]; if(bits < FLAC__BITS_PER_WORD) { - *val = word >> (FLAC__BITS_PER_WORD-bits); + *val = (FLAC__uint32)(word >> (FLAC__BITS_PER_WORD-bits)); br->consumed_bits = bits; return true; } - /* at this point 'bits' must be == FLAC__BITS_PER_WORD; because of previous assertions, it can't be larger */ - *val = word; - crc16_update_word_(br, word); + /* at this point bits == FLAC__BITS_PER_WORD == 32; because of previous assertions, it can't be larger */ + *val = (FLAC__uint32)word; br->consumed_words++; return true; } @@ -403,30 +493,32 @@ FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *va if(br->consumed_bits) { /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ FLAC__ASSERT(br->consumed_bits + bits <= br->bytes*8); - *val = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (FLAC__BITS_PER_WORD-br->consumed_bits-bits); + *val = (FLAC__uint32)((br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (FLAC__BITS_PER_WORD-br->consumed_bits-bits)); br->consumed_bits += bits; return true; } else { - *val = br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits); + *val = (FLAC__uint32)(br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits)); br->consumed_bits += bits; return true; } } } -FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, unsigned bits) +FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, uint32_t bits) { + FLAC__uint32 uval, mask; /* OPT: inline raw uint32 code here, or make into a macro if possible in the .h file */ - if(!FLAC__bitreader_read_raw_uint32(br, (FLAC__uint32*)val, bits)) + if (bits < 1 || ! FLAC__bitreader_read_raw_uint32(br, &uval, bits)) return false; - /* sign-extend: */ - *val <<= (32-bits); - *val >>= (32-bits); + /* sign-extend *val assuming it is currently bits wide. */ + /* From: https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend */ + mask = bits >= 33 ? 0 : 1lu << (bits - 1); + *val = (uval ^ mask) - mask; return true; } -FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits) +FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, uint32_t bits) { FLAC__uint32 hi, lo; @@ -447,6 +539,19 @@ FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *va return true; } +FLAC__bool FLAC__bitreader_read_raw_int64(FLAC__BitReader *br, FLAC__int64 *val, uint32_t bits) +{ + FLAC__uint64 uval, mask; + /* OPT: inline raw uint64 code here, or make into a macro if possible in the .h file */ + if (bits < 1 || ! FLAC__bitreader_read_raw_uint64(br, &uval, bits)) + return false; + /* sign-extend *val assuming it is currently bits wide. */ + /* From: https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend */ + mask = bits >= 65 ? 0 : 1llu << (bits - 1); + *val = (uval ^ mask) - mask; + return true; +} + inline FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val) { FLAC__uint32 x8, x32 = 0; @@ -472,7 +577,7 @@ inline FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, return true; } -FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits) +FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, uint32_t bits) { /* * OPT: a faster implementation is possible but probably not that useful @@ -482,8 +587,8 @@ FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits) FLAC__ASSERT(0 != br->buffer); if(bits > 0) { - const unsigned n = br->consumed_bits & 7; - unsigned m; + const uint32_t n = br->consumed_bits & 7; + uint32_t m; FLAC__uint32 x; if(n != 0) { @@ -507,7 +612,7 @@ FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits) return true; } -FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, unsigned nvals) +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, uint32_t nvals) { FLAC__uint32 x; @@ -515,6 +620,13 @@ FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, u FLAC__ASSERT(0 != br->buffer); FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(br)); + if(br->read_limit_set && br->read_limit < (uint32_t)-1){ + if(br->read_limit < nvals*8){ + br->read_limit = (uint32_t) -1; + return false; + } + } + /* step 1: skip over partial head word to get word aligned */ while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) @@ -523,11 +635,14 @@ FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, u } if(0 == nvals) return true; + /* step 2: skip whole words in chunks */ while(nvals >= FLAC__BYTES_PER_WORD) { if(br->consumed_words < br->words) { br->consumed_words++; nvals -= FLAC__BYTES_PER_WORD; + if(br->read_limit_set) + br->read_limit -= FLAC__BITS_PER_WORD; } else if(!bitreader_read_from_client_(br)) return false; @@ -542,7 +657,7 @@ FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, u return true; } -FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, unsigned nvals) +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, uint32_t nvals) { FLAC__uint32 x; @@ -550,6 +665,13 @@ FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, F FLAC__ASSERT(0 != br->buffer); FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(br)); + if(br->read_limit_set && br->read_limit < (uint32_t)-1){ + if(br->read_limit < nvals*8){ + br->read_limit = (uint32_t) -1; + return false; + } + } + /* step 1: read from partial head word to get word aligned */ while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) @@ -562,7 +684,7 @@ FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, F /* step 2: read whole words in chunks */ while(nvals >= FLAC__BYTES_PER_WORD) { if(br->consumed_words < br->words) { - const uint32_t word = br->buffer[br->consumed_words++]; + const brword word = br->buffer[br->consumed_words++]; #if FLAC__BYTES_PER_WORD == 4 val[0] = (FLAC__byte)(word >> 24); val[1] = (FLAC__byte)(word >> 16); @@ -583,6 +705,8 @@ FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, F #endif val += FLAC__BYTES_PER_WORD; nvals -= FLAC__BYTES_PER_WORD; + if(br->read_limit_set) + br->read_limit -= FLAC__BITS_PER_WORD; } else if(!bitreader_read_from_client_(br)) return false; @@ -598,10 +722,10 @@ FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, F return true; } -FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val) +FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, uint32_t *val) #if 0 /* slow but readable version */ { - unsigned bit; + uint32_t bit; FLAC__ASSERT(0 != br); FLAC__ASSERT(0 != br->buffer); @@ -619,7 +743,7 @@ FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *va } #else { - unsigned i; + uint32_t i; FLAC__ASSERT(0 != br); FLAC__ASSERT(0 != br->buffer); @@ -627,14 +751,13 @@ FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *va *val = 0; while(1) { while(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ - uint32_t b = br->buffer[br->consumed_words] << br->consumed_bits; + brword b = br->consumed_bits < FLAC__BITS_PER_WORD ? br->buffer[br->consumed_words] << br->consumed_bits : 0; if(b) { - i = FLAC__clz_uint32(b); + i = COUNT_ZERO_MSBS(b); *val += i; i++; br->consumed_bits += i; if(br->consumed_bits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(br->consumed_bits == FLAC__BITS_PER_WORD) */ - crc16_update_word_(br, br->buffer[br->consumed_words]); br->consumed_words++; br->consumed_bits = 0; } @@ -642,7 +765,6 @@ FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *va } else { *val += FLAC__BITS_PER_WORD - br->consumed_bits; - crc16_update_word_(br, br->buffer[br->consumed_words]); br->consumed_words++; br->consumed_bits = 0; /* didn't find stop bit yet, have to keep going... */ @@ -656,10 +778,10 @@ FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *va * be zero. */ if(br->bytes*8 > br->consumed_bits) { - const unsigned end = br->bytes * 8; - uint32_t b = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << br->consumed_bits; + const uint32_t end = br->bytes * 8; + brword b = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << br->consumed_bits; if(b) { - i = FLAC__clz_uint32(b); + i = COUNT_ZERO_MSBS(b); *val += i; i++; br->consumed_bits += i; @@ -679,10 +801,11 @@ FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *va } #endif -FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsigned parameter) +#if 0 /* unused */ +FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, uint32_t parameter) { FLAC__uint32 lsbs = 0, msbs = 0; - unsigned uval; + uint32_t uval; FLAC__ASSERT(0 != br); FLAC__ASSERT(0 != br->buffer); @@ -705,152 +828,23 @@ FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsig return true; } +#endif /* this is by far the most heavily used reader call. it ain't pretty but it's fast */ -FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter) -{ - /* try and get br->consumed_words and br->consumed_bits into register; - * must remember to flush them back to *br before calling other - * bitreader functions that use them, and before returning */ - unsigned cwords, words, lsbs, msbs, x, y; - unsigned ucbits; /* keep track of the number of unconsumed bits in word */ - uint32_t b; - int *val, *end; - - FLAC__ASSERT(0 != br); - FLAC__ASSERT(0 != br->buffer); - /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ - FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); - FLAC__ASSERT(parameter < 32); - /* the above two asserts also guarantee that the binary part never straddles more than 2 words, so we don't have to loop to read it */ +FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], uint32_t nvals, uint32_t parameter) +#include "deduplication/bitreader_read_rice_signed_block.c" - val = vals; - end = vals + nvals; - - if(parameter == 0) { - while(val < end) { - /* read the unary MSBs and end bit */ - if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) - return false; - - *val++ = (int)(msbs >> 1) ^ -(int)(msbs & 1); - } - - return true; - } - - FLAC__ASSERT(parameter > 0); - - cwords = br->consumed_words; - words = br->words; - - /* if we've not consumed up to a partial tail word... */ - if(cwords >= words) { - x = 0; - goto process_tail; - } - - ucbits = FLAC__BITS_PER_WORD - br->consumed_bits; - b = br->buffer[cwords] << br->consumed_bits; /* keep unconsumed bits aligned to left */ - - while(val < end) { - /* read the unary MSBs and end bit */ - x = y = FLAC__clz2_uint32(b); - if(x == FLAC__BITS_PER_WORD) { - x = ucbits; - do { - /* didn't find stop bit yet, have to keep going... */ - crc16_update_word_(br, br->buffer[cwords++]); - if (cwords >= words) - goto incomplete_msbs; - b = br->buffer[cwords]; - y = FLAC__clz2_uint32(b); - x += y; - } while(y == FLAC__BITS_PER_WORD); - } - b <<= y; - b <<= 1; /* account for stop bit */ - ucbits = (ucbits - x - 1) % FLAC__BITS_PER_WORD; - msbs = x; - - /* read the binary LSBs */ - x = b >> (FLAC__BITS_PER_WORD - parameter); - if(parameter <= ucbits) { - ucbits -= parameter; - b <<= parameter; - } else { - /* there are still bits left to read, they will all be in the next word */ - crc16_update_word_(br, br->buffer[cwords++]); - if (cwords >= words) - goto incomplete_lsbs; - b = br->buffer[cwords]; - ucbits += FLAC__BITS_PER_WORD - parameter; - x |= b >> ucbits; - b <<= FLAC__BITS_PER_WORD - ucbits; - } - lsbs = x; - - /* compose the value */ - x = (msbs << parameter) | lsbs; - *val++ = (int)(x >> 1) ^ -(int)(x & 1); - - continue; - - /* at this point we've eaten up all the whole words */ -process_tail: - do { - if(0) { -incomplete_msbs: - br->consumed_bits = 0; - br->consumed_words = cwords; - } - - /* read the unary MSBs and end bit */ - if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) - return false; - msbs += x; - x = ucbits = 0; - - if(0) { -incomplete_lsbs: - br->consumed_bits = 0; - br->consumed_words = cwords; - } - - /* read the binary LSBs */ - if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter - ucbits)) - return false; - lsbs = x | lsbs; - - /* compose the value */ - x = (msbs << parameter) | lsbs; - *val++ = (int)(x >> 1) ^ -(int)(x & 1); - x = 0; - - cwords = br->consumed_words; - words = br->words; - ucbits = FLAC__BITS_PER_WORD - br->consumed_bits; - b = br->buffer[cwords] << br->consumed_bits; - } while(cwords >= words && val < end); - } - - if(ucbits == 0 && cwords < words) { - /* don't leave the head word with no unconsumed bits */ - crc16_update_word_(br, br->buffer[cwords++]); - ucbits = FLAC__BITS_PER_WORD; - } - - br->consumed_bits = FLAC__BITS_PER_WORD - ucbits; - br->consumed_words = cwords; - - return true; -} +#ifdef FLAC__BMI2_SUPPORTED +FLAC__SSE_TARGET("bmi2") +FLAC__bool FLAC__bitreader_read_rice_signed_block_bmi2(FLAC__BitReader *br, int vals[], uint32_t nvals, uint32_t parameter) +#include "deduplication/bitreader_read_rice_signed_block.c" +#endif #if 0 /* UNUSED */ -FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter) +FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, uint32_t parameter) { FLAC__uint32 lsbs = 0, msbs = 0; - unsigned bit, uval, k; + uint32_t bit, uval, k; FLAC__ASSERT(0 != br); FLAC__ASSERT(0 != br->buffer); @@ -870,7 +864,7 @@ FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, uns uval = (msbs << k) | lsbs; } else { - unsigned d = (1 << (k+1)) - parameter; + uint32_t d = (1 << (k+1)) - parameter; if(lsbs >= d) { if(!FLAC__bitreader_read_bit(br, &bit)) return false; @@ -882,7 +876,7 @@ FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, uns uval = msbs * parameter + lsbs; } - /* unfold unsigned to signed */ + /* unfold uint32_t to signed */ if(uval & 1) *val = -((int)(uval >> 1)) - 1; else @@ -891,10 +885,10 @@ FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, uns return true; } -FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter) +FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, uint32_t *val, uint32_t parameter) { FLAC__uint32 lsbs, msbs = 0; - unsigned bit, k; + uint32_t bit, k; FLAC__ASSERT(0 != br); FLAC__ASSERT(0 != br->buffer); @@ -914,7 +908,7 @@ FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *v *val = (msbs << k) | lsbs; } else { - unsigned d = (1 << (k+1)) - parameter; + uint32_t d = (1 << (k+1)) - parameter; if(lsbs >= d) { if(!FLAC__bitreader_read_bit(br, &bit)) return false; @@ -931,11 +925,11 @@ FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *v #endif /* UNUSED */ /* on return, if *val == 0xffffffff then the utf-8 sequence was invalid, but the return value will be true */ -FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen) +FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, uint32_t *rawlen) { FLAC__uint32 v = 0; FLAC__uint32 x; - unsigned i; + uint32_t i; if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) return false; @@ -986,11 +980,11 @@ FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *v } /* on return, if *val == 0xffffffffffffffff then the utf-8 sequence was invalid, but the return value will be true */ -FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen) +FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, uint32_t *rawlen) { FLAC__uint64 v = 0; FLAC__uint32 x; - unsigned i; + uint32_t i; if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) return false; @@ -1053,6 +1047,6 @@ FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *v * fix that we add extern declarations here. */ extern FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); -extern unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); -extern unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); +extern uint32_t FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); +extern uint32_t FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); extern FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c index 5009cd34..c69af330 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,6 +38,9 @@ #include #include "include/private/bitwriter.h" #include "include/private/crc.h" +#include "include/private/format.h" + +#include "include/private/stream_encoder.h" #include "../assert.h" #include "../alloc.h" #include "../compat.h" @@ -45,46 +48,69 @@ /* Things should be fastest when this matches the machine word size */ /* WATCHOUT: if you change this you must also change the following #defines down to SWAP_BE_WORD_TO_HOST below to match */ -/* WATCHOUT: there are a few places where the code will not work unless uint32_t is >= 32 bits wide */ -#define FLAC__BYTES_PER_WORD 4 -#define FLAC__BITS_PER_WORD (8 * FLAC__BYTES_PER_WORD) -#define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) -/* SWAP_BE_WORD_TO_HOST swaps bytes in a uint32_t (which is always big-endian) if necessary to match host byte order */ +/* WATCHOUT: there are a few places where the code will not work unless bwword is >= 32 bits wide */ + +#if (ENABLE_64_BIT_WORDS == 0) + +typedef FLAC__uint32 bwword; +typedef FLAC__uint64 FLAC__bwtemp; +#define FLAC__BYTES_PER_WORD 4 /* sizeof bwword */ +#define FLAC__BITS_PER_WORD 32 +#define FLAC__TEMP_BITS 64 +#define FLAC__HALF_TEMP_BITS 32 +/* SWAP_BE_WORD_TO_HOST swaps bytes in a bwword (which is always big-endian) if necessary to match host byte order */ #if WORDS_BIGENDIAN #define SWAP_BE_WORD_TO_HOST(x) (x) #else #define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_32(x) #endif +#else + +typedef FLAC__uint64 bwword; +typedef FLAC__uint64 FLAC__bwtemp; +#define FLAC__BYTES_PER_WORD 8 /* sizeof bwword */ +#define FLAC__BITS_PER_WORD 64 +#define FLAC__TEMP_BITS 64 +#define FLAC__HALF_TEMP_BITS 32 +/* SWAP_BE_WORD_TO_HOST swaps bytes in a bwword (which is always big-endian) if necessary to match host byte order */ +#if WORDS_BIGENDIAN +#define SWAP_BE_WORD_TO_HOST(x) (x) +#else +#define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_64(x) +#endif + +#endif + /* * The default capacity here doesn't matter too much. The buffer always grows * to hold whatever is written to it. Usually the encoder will stop adding at * a frame or metadata block, then write that out and clear the buffer for the * next one. */ -static const unsigned FLAC__BITWRITER_DEFAULT_CAPACITY = 32768u / sizeof(uint32_t); /* size in words */ +static const uint32_t FLAC__BITWRITER_DEFAULT_CAPACITY = 32768u / sizeof(bwword); /* size in words */ /* When growing, increment 4K at a time */ -static const unsigned FLAC__BITWRITER_DEFAULT_INCREMENT = 4096u / sizeof(uint32_t); /* size in words */ +static const uint32_t FLAC__BITWRITER_DEFAULT_INCREMENT = 4096u / sizeof(bwword); /* size in words */ #define FLAC__WORDS_TO_BITS(words) ((words) * FLAC__BITS_PER_WORD) #define FLAC__TOTAL_BITS(bw) (FLAC__WORDS_TO_BITS((bw)->words) + (bw)->bits) struct FLAC__BitWriter { - uint32_t *buffer; - uint32_t accum; /* accumulator; bits are right-justified; when full, accum is appended to buffer */ - unsigned capacity; /* capacity of buffer in words */ - unsigned words; /* # of complete words in buffer */ - unsigned bits; /* # of used bits in accum */ + bwword *buffer; + bwword accum; /* accumulator; bits are right-justified; when full, accum is appended to buffer */ + uint32_t capacity; /* capacity of buffer in words */ + uint32_t words; /* # of complete words in buffer */ + uint32_t bits; /* # of used bits in accum */ }; /* * WATCHOUT: The current implementation only grows the buffer. */ #ifndef __SUNPRO_C static #endif -FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) +FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, uint32_t bits_to_add) { - unsigned new_capacity; - uint32_t *new_buffer; + uint32_t new_capacity; + bwword *new_buffer; FLAC__ASSERT(0 != bw); FLAC__ASSERT(0 != bw->buffer); @@ -98,6 +124,13 @@ FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) if(bw->capacity >= new_capacity) return true; + if(new_capacity * sizeof(bwword) > (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) + /* Requested new capacity is larger than the largest possible metadata block, + * which is also larger than the largest sane framesize. That means something + * went very wrong somewhere and previous checks failed. + * To prevent chrashing, give up */ + return false; + /* round up capacity increase to the nearest FLAC__BITWRITER_DEFAULT_INCREMENT */ if((new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT) new_capacity += FLAC__BITWRITER_DEFAULT_INCREMENT - ((new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT); @@ -106,7 +139,7 @@ FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) FLAC__ASSERT(new_capacity > bw->capacity); FLAC__ASSERT(new_capacity >= bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD)); - new_buffer = (uint32_t*) safe_realloc_mul_2op_(bw->buffer, sizeof(uint32_t), /*times*/new_capacity); + new_buffer = (bwword*) safe_realloc_nofree_mul_2op_(bw->buffer, sizeof(bwword), /*times*/new_capacity); if(new_buffer == 0) return false; bw->buffer = new_buffer; @@ -148,7 +181,7 @@ FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw) bw->words = bw->bits = 0; bw->capacity = FLAC__BITWRITER_DEFAULT_CAPACITY; - bw->buffer = (uint32_t*) malloc(sizeof(uint32_t) * bw->capacity); + bw->buffer = (bwword*) malloc(sizeof(bwword) * bw->capacity); if(bw->buffer == 0) return false; @@ -171,30 +204,6 @@ void FLAC__bitwriter_clear(FLAC__BitWriter *bw) bw->words = bw->bits = 0; } -void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out) -{ - unsigned i, j; - if(bw == 0) { - fprintf(out, "bitwriter is NULL\n"); - } - else { - fprintf(out, "bitwriter: capacity=%u words=%u bits=%u total_bits=%u\n", bw->capacity, bw->words, bw->bits, FLAC__TOTAL_BITS(bw)); - - for(i = 0; i < bw->words; i++) { - fprintf(out, "%08X: ", i); - for(j = 0; j < FLAC__BITS_PER_WORD; j++) - fprintf(out, "%01u", bw->buffer[i] & (1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0); - fprintf(out, "\n"); - } - if(bw->bits > 0) { - fprintf(out, "%08X: ", i); - for(j = 0; j < bw->bits; j++) - fprintf(out, "%01u", bw->accum & (1 << (bw->bits-j-1)) ? 1:0); - fprintf(out, "\n"); - } - } -} - FLAC__bool FLAC__bitwriter_get_write_crc16(FLAC__BitWriter *bw, FLAC__uint16 *crc) { const FLAC__byte *buffer; @@ -230,7 +239,7 @@ FLAC__bool FLAC__bitwriter_is_byte_aligned(const FLAC__BitWriter *bw) return ((bw->bits & 7) == 0); } -unsigned FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw) +uint32_t FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw) { return FLAC__TOTAL_BITS(bw); } @@ -263,9 +272,9 @@ void FLAC__bitwriter_release_buffer(FLAC__BitWriter *bw) (void)bw; } -inline FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits) +inline FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, uint32_t bits) { - unsigned n; + uint32_t n; FLAC__ASSERT(0 != bw); FLAC__ASSERT(0 != bw->buffer); @@ -301,20 +310,24 @@ inline FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bit return true; } -inline FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits) +static inline FLAC__bool FLAC__bitwriter_write_raw_uint32_nocheck(FLAC__BitWriter *bw, FLAC__uint32 val, uint32_t bits) { - unsigned left; + uint32_t left; /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); - FLAC__ASSERT(0 != bw); - FLAC__ASSERT(0 != bw->buffer); + if(bw == 0 || bw->buffer == 0) + return false; + + if (bits > 32) + return false; - FLAC__ASSERT(bits <= 32); if(bits == 0) return true; + FLAC__ASSERT((bits == 32) || (val>>bits == 0)); + /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+bits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ if(bw->capacity <= bw->words + bits && !bitwriter_grow_(bw, bits)) return false; @@ -329,102 +342,124 @@ inline FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__ui bw->accum <<= left; bw->accum |= val >> (bw->bits = bits - left); bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); - bw->accum = val; + bw->accum = val; /* unused top bits can contain garbage */ } - else { - bw->accum = val; - bw->bits = 0; - bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(val); + else { /* at this point bits == FLAC__BITS_PER_WORD == 32 and bw->bits == 0 */ + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST((bwword)val); } return true; } -inline FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits) +inline FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, uint32_t bits) +{ + /* check that unused bits are unset */ + if((bits < 32) && (val>>bits != 0)) + return false; + + return FLAC__bitwriter_write_raw_uint32_nocheck(bw, val, bits); +} + +inline FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, uint32_t bits) { /* zero-out unused bits */ if(bits < 32) val &= (~(0xffffffff << bits)); - return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, bits); + return FLAC__bitwriter_write_raw_uint32_nocheck(bw, (FLAC__uint32)val, bits); } -inline FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits) +inline FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, uint32_t bits) { /* this could be a little faster but it's not used for much */ if(bits > 32) { return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)(val>>32), bits-32) && - FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, 32); + FLAC__bitwriter_write_raw_uint32_nocheck(bw, (FLAC__uint32)val, 32); } else return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, bits); } +inline FLAC__bool FLAC__bitwriter_write_raw_int64(FLAC__BitWriter *bw, FLAC__int64 val, uint32_t bits) +{ + FLAC__uint64 uval = val; + /* zero-out unused bits */ + if(bits < 64) + uval &= (~(UINT64_MAX << bits)); + return FLAC__bitwriter_write_raw_uint64(bw, uval, bits); +} + inline FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val) { /* this doesn't need to be that fast as currently it is only used for vorbis comments */ - if(!FLAC__bitwriter_write_raw_uint32(bw, val & 0xff, 8)) + if(!FLAC__bitwriter_write_raw_uint32_nocheck(bw, val & 0xff, 8)) return false; - if(!FLAC__bitwriter_write_raw_uint32(bw, (val>>8) & 0xff, 8)) + if(!FLAC__bitwriter_write_raw_uint32_nocheck(bw, (val>>8) & 0xff, 8)) return false; - if(!FLAC__bitwriter_write_raw_uint32(bw, (val>>16) & 0xff, 8)) + if(!FLAC__bitwriter_write_raw_uint32_nocheck(bw, (val>>16) & 0xff, 8)) return false; - if(!FLAC__bitwriter_write_raw_uint32(bw, val>>24, 8)) + if(!FLAC__bitwriter_write_raw_uint32_nocheck(bw, val>>24, 8)) return false; return true; } -inline FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals) +inline FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], uint32_t nvals) { - unsigned i; + uint32_t i; + + /* grow capacity upfront to prevent constant reallocation during writes */ + if(bw->capacity <= bw->words + nvals / (FLAC__BITS_PER_WORD / 8) + 1 && !bitwriter_grow_(bw, nvals * 8)) + return false; /* this could be faster but currently we don't need it to be since it's only used for writing metadata */ for(i = 0; i < nvals; i++) { - if(!FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)(vals[i]), 8)) + if(!FLAC__bitwriter_write_raw_uint32_nocheck(bw, (FLAC__uint32)(vals[i]), 8)) return false; } return true; } -FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, unsigned val) +FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, uint32_t val) { if(val < 32) - return FLAC__bitwriter_write_raw_uint32(bw, 1, ++val); + return FLAC__bitwriter_write_raw_uint32_nocheck(bw, 1, ++val); else return FLAC__bitwriter_write_zeroes(bw, val) && - FLAC__bitwriter_write_raw_uint32(bw, 1, 1); + FLAC__bitwriter_write_raw_uint32_nocheck(bw, 1, 1); } -unsigned FLAC__bitwriter_rice_bits(FLAC__int32 val, unsigned parameter) +#if 0 /* UNUSED */ +uint32_t FLAC__bitwriter_rice_bits(FLAC__int32 val, uint32_t parameter) { FLAC__uint32 uval; - FLAC__ASSERT(parameter < sizeof(unsigned)*8); + FLAC__ASSERT(parameter < 32); - /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ - uval = (val<<1) ^ (val>>31); + /* fold signed to uint32_t; actual formula is: negative(v)? -2v-1 : 2v */ + uval = val; + uval <<= 1; + uval ^= (val>>31); return 1 + parameter + (uval >> parameter); } -#if 0 /* UNUSED */ -unsigned FLAC__bitwriter_golomb_bits_signed(int val, unsigned parameter) +uint32_t FLAC__bitwriter_golomb_bits_signed(int val, uint32_t parameter) { - unsigned bits, msbs, uval; - unsigned k; + uint32_t bits, msbs, uval; + uint32_t k; FLAC__ASSERT(parameter > 0); - /* fold signed to unsigned */ + /* fold signed to uint32_t */ if(val < 0) - uval = (unsigned)(((-(++val)) << 1) + 1); + uval = (uint32_t)(((-(++val)) << 1) + 1); else - uval = (unsigned)(val << 1); + uval = (uint32_t)(val << 1); k = FLAC__bitmath_ilog2(parameter); if(parameter == 1u< 0); @@ -462,7 +497,7 @@ unsigned FLAC__bitwriter_golomb_bits_unsigned(unsigned uval, unsigned parameter) bits = 1 + k + msbs; } else { - unsigned q, r, d; + uint32_t q, r, d; d = (1 << (k+1)) - parameter; q = uval / parameter; @@ -474,19 +509,20 @@ unsigned FLAC__bitwriter_golomb_bits_unsigned(unsigned uval, unsigned parameter) } return bits; } -#endif /* UNUSED */ -FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, unsigned parameter) +FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, uint32_t parameter) { - unsigned total_bits, interesting_bits, msbs; + uint32_t total_bits, interesting_bits, msbs; FLAC__uint32 uval, pattern; FLAC__ASSERT(0 != bw); FLAC__ASSERT(0 != bw->buffer); - FLAC__ASSERT(parameter < 8*sizeof(uval)); + FLAC__ASSERT(parameter < 32); - /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ - uval = (val<<1) ^ (val>>31); + /* fold signed to uint32_t; actual formula is: negative(v)? -2v-1 : 2v */ + uval = val; + uval <<= 1; + uval ^= (val>>31); msbs = uval >> parameter; interesting_bits = 1 + parameter; @@ -501,117 +537,190 @@ FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 va FLAC__bitwriter_write_zeroes(bw, msbs) && /* write the unary MSBs */ FLAC__bitwriter_write_raw_uint32(bw, pattern, interesting_bits); /* write the unary end bit and binary LSBs */ } +#endif /* UNUSED */ + +#if (ENABLE_64_BIT_WORDS == 0) + +#define WIDE_ACCUM_TO_BW { \ + bw->accum = wide_accum >> FLAC__HALF_TEMP_BITS; \ + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); \ + wide_accum <<= FLAC__HALF_TEMP_BITS; \ + bitpointer += FLAC__HALF_TEMP_BITS; \ +} -FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FLAC__int32 *vals, unsigned nvals, unsigned parameter) +#else + +#define WIDE_ACCUM_TO_BW { \ + FLAC__ASSERT(bw->bits % FLAC__HALF_TEMP_BITS == 0); \ + if(bw->bits == 0) { \ + bw->accum = wide_accum >> FLAC__HALF_TEMP_BITS; \ + wide_accum <<= FLAC__HALF_TEMP_BITS; \ + bw->bits = FLAC__HALF_TEMP_BITS; \ + } \ + else { \ + bw->accum <<= FLAC__HALF_TEMP_BITS; \ + bw->accum += wide_accum >> FLAC__HALF_TEMP_BITS; \ + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); \ + wide_accum <<= FLAC__HALF_TEMP_BITS; \ + bw->bits = 0; \ + } \ + bitpointer += FLAC__HALF_TEMP_BITS; \ +} + +#endif + +FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FLAC__int32 *vals, uint32_t nvals, uint32_t parameter) { - const FLAC__uint32 mask1 = FLAC__WORD_ALL_ONES << parameter; /* we val|=mask1 to set the stop bit above it... */ - const FLAC__uint32 mask2 = FLAC__WORD_ALL_ONES >> (31-parameter); /* ...then mask off the bits above the stop bit with val&=mask2*/ + const FLAC__uint32 mask1 = (FLAC__uint32)0xffffffff << parameter; /* we val|=mask1 to set the stop bit above it... */ + const FLAC__uint32 mask2 = (FLAC__uint32)0xffffffff >> (31-parameter); /* ...then mask off the bits above the stop bit with val&=mask2 */ FLAC__uint32 uval; - unsigned left; - const unsigned lsbits = 1 + parameter; - unsigned msbits; + const uint32_t lsbits = 1 + parameter; + uint32_t msbits, total_bits; + FLAC__bwtemp wide_accum = 0; + FLAC__uint32 bitpointer = FLAC__TEMP_BITS; FLAC__ASSERT(0 != bw); FLAC__ASSERT(0 != bw->buffer); - FLAC__ASSERT(parameter < 8*sizeof(uint32_t)-1); + FLAC__ASSERT(parameter < 31); /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); +#if (ENABLE_64_BIT_WORDS == 0) + if(bw->bits > 0) { + bitpointer -= bw->bits; + wide_accum = (FLAC__bwtemp)(bw->accum) << bitpointer; + bw->bits = 0; + } +#else + if(bw->bits > 0 && bw->bits < FLAC__HALF_TEMP_BITS) { + bitpointer -= bw->bits; + wide_accum = bw->accum << bitpointer; + bw->bits = 0; + } + else if(bw->bits > FLAC__HALF_TEMP_BITS) { + bitpointer -= (bw->bits - FLAC__HALF_TEMP_BITS); + wide_accum = bw->accum << bitpointer; + bw->accum >>= (bw->bits - FLAC__HALF_TEMP_BITS); + bw->bits = FLAC__HALF_TEMP_BITS; + } +#endif + + /* Reserve one FLAC__TEMP_BITS per symbol, so checks for space are only necessary when very large symbols are encountered + * this might be considered wasteful, but is only at most 8kB more than necessary for a blocksize of 4096 */ + if(bw->capacity * FLAC__BITS_PER_WORD <= bw->words * FLAC__BITS_PER_WORD + nvals * FLAC__TEMP_BITS + bw->bits && !bitwriter_grow_(bw, nvals * FLAC__TEMP_BITS)) + return false; while(nvals) { - /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ - uval = (*vals<<1) ^ (*vals>>31); + /* fold signed to uint32_t; actual formula is: negative(v)? -2v-1 : 2v */ + uval = *vals; + uval <<= 1; + uval ^= (*vals>>31); msbits = uval >> parameter; + total_bits = lsbits + msbits; - if(bw->bits && bw->bits + msbits + lsbits < FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current uint32_t */ - /* ^^^ if bw->bits is 0 then we may have filled the buffer and have no free uint32_t to work in */ - bw->bits = bw->bits + msbits + lsbits; - uval |= mask1; /* set stop bit */ - uval &= mask2; /* mask off unused top bits */ - bw->accum <<= msbits + lsbits; - bw->accum |= uval; + uval |= mask1; /* set stop bit */ + uval &= mask2; /* mask off unused top bits */ + + + if(total_bits <= bitpointer) { + /* There is room enough to store the symbol whole at once */ + wide_accum |= (FLAC__bwtemp)(uval) << (bitpointer - total_bits); + bitpointer -= total_bits; + if(bitpointer <= FLAC__HALF_TEMP_BITS) { + /* A word is finished, copy the upper 32 bits of the wide_accum */ + WIDE_ACCUM_TO_BW + } } else { - /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+msbits+lsbits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ - /* OPT: pessimism may cause flurry of false calls to grow_ which eat up all savings before it */ - if(bw->capacity <= bw->words + bw->bits + msbits + 1/*lsbits always fit in 1 uint32_t*/ && !bitwriter_grow_(bw, msbits+lsbits)) - return false; - - if(msbits) { - /* first part gets to word alignment */ - if(bw->bits) { - left = FLAC__BITS_PER_WORD - bw->bits; - if(msbits < left) { - bw->accum <<= msbits; - bw->bits += msbits; - goto break1; - } - else { - bw->accum <<= left; - msbits -= left; - bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); - bw->bits = 0; - } - } - /* do whole words */ - while(msbits >= FLAC__BITS_PER_WORD) { - bw->buffer[bw->words++] = 0; - msbits -= FLAC__BITS_PER_WORD; - } - /* do any leftovers */ - if(msbits > 0) { - bw->accum = 0; - bw->bits = msbits; - } + /* The symbol needs to be split. This code isn't used often */ + /* First check for space in the bitwriter */ + if(total_bits > FLAC__TEMP_BITS) { + FLAC__uint32 oversize_in_bits = total_bits - FLAC__TEMP_BITS; + FLAC__uint32 capacity_needed = bw->words * FLAC__BITS_PER_WORD + bw->bits + nvals * FLAC__TEMP_BITS + oversize_in_bits; + if(bw->capacity * FLAC__BITS_PER_WORD <= capacity_needed && !bitwriter_grow_(bw, nvals * FLAC__TEMP_BITS + oversize_in_bits)) + return false; } -break1: - uval |= mask1; /* set stop bit */ - uval &= mask2; /* mask off unused top bits */ - - left = FLAC__BITS_PER_WORD - bw->bits; - if(lsbits < left) { - bw->accum <<= lsbits; - bw->accum |= uval; - bw->bits += lsbits; + if(msbits > bitpointer) { + /* We have a lot of 0 bits to write, first align with bitwriter word */ + msbits -= bitpointer - FLAC__HALF_TEMP_BITS; + bitpointer = FLAC__HALF_TEMP_BITS; + WIDE_ACCUM_TO_BW + while(msbits > bitpointer) { + /* As the accumulator is already zero, we only need to + * assign zeroes to the bitbuffer */ + WIDE_ACCUM_TO_BW + bitpointer -= FLAC__HALF_TEMP_BITS; + msbits -= FLAC__HALF_TEMP_BITS; + } + /* The remaining bits are zero, and the accumulator already is zero, + * so just subtract the number of bits from bitpointer. When storing, + * we can also just store 0 */ + bitpointer -= msbits; + if(bitpointer <= FLAC__HALF_TEMP_BITS) + WIDE_ACCUM_TO_BW } else { - /* if bw->bits == 0, left==FLAC__BITS_PER_WORD which will always - * be > lsbits (because of previous assertions) so it would have - * triggered the (lsbitsbits); - FLAC__ASSERT(left < FLAC__BITS_PER_WORD); - bw->accum <<= left; - bw->accum |= uval >> (bw->bits = lsbits - left); - bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); - bw->accum = uval; + bitpointer -= msbits; + if(bitpointer <= FLAC__HALF_TEMP_BITS) + WIDE_ACCUM_TO_BW } + /* The lsbs + stop bit always fit 32 bit, so this code mirrors the code above */ + wide_accum |= (FLAC__bwtemp)(uval) << (bitpointer - lsbits); + bitpointer -= lsbits; + if(bitpointer <= FLAC__HALF_TEMP_BITS) { + /* A word is finished, copy the upper 32 bits of the wide_accum */ + WIDE_ACCUM_TO_BW + } } vals++; nvals--; } + /* Now fixup remainder of wide_accum */ +#if (ENABLE_64_BIT_WORDS == 0) + if(bitpointer < FLAC__TEMP_BITS) { + bw->accum = wide_accum >> bitpointer; + bw->bits = FLAC__TEMP_BITS - bitpointer; + } +#else + if(bitpointer < FLAC__TEMP_BITS) { + if(bw->bits == 0) { + bw->accum = wide_accum >> bitpointer; + bw->bits = FLAC__TEMP_BITS - bitpointer; + } + else if (bw->bits == FLAC__HALF_TEMP_BITS) { + bw->accum <<= FLAC__TEMP_BITS - bitpointer; + bw->accum |= (wide_accum >> bitpointer); + bw->bits = FLAC__HALF_TEMP_BITS + FLAC__TEMP_BITS - bitpointer; + } + else { + FLAC__ASSERT(0); + } + } +#endif + + return true; } #if 0 /* UNUSED */ -FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, unsigned parameter) +FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, uint32_t parameter) { - unsigned total_bits, msbs, uval; - unsigned k; + uint32_t total_bits, msbs, uval; + uint32_t k; FLAC__ASSERT(0 != bw); FLAC__ASSERT(0 != bw->buffer); FLAC__ASSERT(parameter > 0); - /* fold signed to unsigned */ + /* fold signed to uint32_t */ if(val < 0) - uval = (unsigned)(((-(++val)) << 1) + 1); + uval = (uint32_t)(((-(++val)) << 1) + 1); else - uval = (unsigned)(val << 1); + uval = (uint32_t)(val << 1); k = FLAC__bitmath_ilog2(parameter); if(parameter == 1u<buffer); @@ -669,7 +778,7 @@ FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned u k = FLAC__bitmath_ilog2(parameter); if(parameter == 1u<buffer); - FLAC__ASSERT(!(val & 0x80000000)); /* this version only handles 31 bits */ + if((val & 0x80000000) != 0) /* this version only handles 31 bits */ + return false; if(val < 0x80) { - return FLAC__bitwriter_write_raw_uint32(bw, val, 8); + return FLAC__bitwriter_write_raw_uint32_nocheck(bw, val, 8); } else if(val < 0x800) { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xC0 | (val>>6), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xC0 | (val>>6), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (val&0x3F), 8); } else if(val < 0x10000) { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xE0 | (val>>12), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xE0 | (val>>12), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (val&0x3F), 8); } else if(val < 0x200000) { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF0 | (val>>18), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xF0 | (val>>18), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | ((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (val&0x3F), 8); } else if(val < 0x4000000) { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF8 | (val>>24), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>18)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xF8 | (val>>24), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | ((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | ((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (val&0x3F), 8); } else { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFC | (val>>30), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>24)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>18)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xFC | (val>>30), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | ((val>>24)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | ((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | ((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (val&0x3F), 8); } return ok; @@ -770,49 +880,50 @@ FLAC__bool FLAC__bitwriter_write_utf8_uint64(FLAC__BitWriter *bw, FLAC__uint64 v FLAC__ASSERT(0 != bw); FLAC__ASSERT(0 != bw->buffer); - FLAC__ASSERT(!(val & FLAC__U64L(0xFFFFFFF000000000))); /* this version only handles 36 bits */ + if((val & FLAC__U64L(0xFFFFFFF000000000)) != 0) /* this version only handles 36 bits */ + return false; if(val < 0x80) { - return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, 8); + return FLAC__bitwriter_write_raw_uint32_nocheck(bw, (FLAC__uint32)val, 8); } else if(val < 0x800) { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xC0 | (FLAC__uint32)(val>>6), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xC0 | (FLAC__uint32)(val>>6), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); } else if(val < 0x10000) { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xE0 | (FLAC__uint32)(val>>12), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xE0 | (FLAC__uint32)(val>>12), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); } else if(val < 0x200000) { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF0 | (FLAC__uint32)(val>>18), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xF0 | (FLAC__uint32)(val>>18), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); } else if(val < 0x4000000) { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF8 | (FLAC__uint32)(val>>24), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xF8 | (FLAC__uint32)(val>>24), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); } else if(val < 0x80000000) { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFC | (FLAC__uint32)(val>>30), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>24)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xFC | (FLAC__uint32)(val>>30), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>24)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); } else { - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFE, 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>30)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>24)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); - ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0xFE, 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>30)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>24)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32_nocheck(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); } return ok; @@ -835,8 +946,10 @@ FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw) * Unfortunately, the Microsoft VS compiler doesn't pick them up externally. To * fix that we add extern declarations here. */ -extern FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits); -extern FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits); -extern FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits); +extern FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, uint32_t bits); +extern FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, uint32_t bits); +extern FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, uint32_t bits); +extern FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, uint32_t bits); +extern FLAC__bool FLAC__bitwriter_write_raw_int64(FLAC__BitWriter *bw, FLAC__int64 val, uint32_t bits); extern FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val); -extern FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals); +extern FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], uint32_t nvals); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c index 27b80173..a5a9eedb 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,389 +35,77 @@ #endif #include "include/private/cpu.h" +#include "../compat.h" +#include +#include -#if 0 - #include - #include - #include -#endif - -#if defined FLAC__CPU_IA32 -# include - -static void disable_sse(FLAC__CPUInfo *info) -{ - info->ia32.sse = false; - info->ia32.sse2 = false; - info->ia32.sse3 = false; - info->ia32.ssse3 = false; - info->ia32.sse41 = false; - info->ia32.sse42 = false; -} - -static void disable_avx(FLAC__CPUInfo *info) -{ - info->ia32.avx = false; - info->ia32.avx2 = false; - info->ia32.fma = false; -} - -#elif defined FLAC__CPU_X86_64 - -static void disable_avx(FLAC__CPUInfo *info) -{ - info->x86.avx = false; - info->x86.avx2 = false; - info->x86.fma = false; -} +#if defined _MSC_VER +#include /* for __cpuid() and _xgetbv() */ +#elif defined __GNUC__ && defined HAVE_CPUID_H +#include /* for __get_cpuid() and __get_cpuid_max() */ #endif -#if defined (__NetBSD__) || defined(__OpenBSD__) -#include -#include -#include +#ifndef NDEBUG +#include +#define dfprintf fprintf +#else +/* This is bad practice, it should be a static void empty function */ +#define dfprintf(file, format, ...) #endif -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) -#include -#include +#if defined(HAVE_SYS_AUXV_H) +#include #endif -#if defined(__APPLE__) -/* how to get sysctlbyname()? */ -#endif +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN && !defined FLAC__NO_ASM -#ifdef FLAC__CPU_IA32 /* these are flags in EDX of CPUID AX=00000001 */ -static const unsigned FLAC__CPUINFO_IA32_CPUID_CMOV = 0x00008000; -static const unsigned FLAC__CPUINFO_IA32_CPUID_MMX = 0x00800000; -static const unsigned FLAC__CPUINFO_IA32_CPUID_FXSR = 0x01000000; -static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE = 0x02000000; -static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE2 = 0x04000000; -#endif +static const uint32_t FLAC__CPUINFO_X86_CPUID_CMOV = 0x00008000; +static const uint32_t FLAC__CPUINFO_X86_CPUID_MMX = 0x00800000; +static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE = 0x02000000; +static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE2 = 0x04000000; /* these are flags in ECX of CPUID AX=00000001 */ -static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001; -static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200; -static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE41 = 0x00080000; -static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE42 = 0x00100000; +static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE3 = 0x00000001; +static const uint32_t FLAC__CPUINFO_X86_CPUID_SSSE3 = 0x00000200; +static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE41 = 0x00080000; +static const uint32_t FLAC__CPUINFO_X86_CPUID_SSE42 = 0x00100000; +static const uint32_t FLAC__CPUINFO_X86_CPUID_OSXSAVE = 0x08000000; +static const uint32_t FLAC__CPUINFO_X86_CPUID_AVX = 0x10000000; +static const uint32_t FLAC__CPUINFO_X86_CPUID_FMA = 0x00001000; -#if defined FLAC__AVX_SUPPORTED -/* these are flags in ECX of CPUID AX=00000001 */ -static const unsigned FLAC__CPUINFO_IA32_CPUID_OSXSAVE = 0x08000000; -static const unsigned FLAC__CPUINFO_IA32_CPUID_AVX = 0x10000000; -static const unsigned FLAC__CPUINFO_IA32_CPUID_FMA = 0x00001000; /* these are flags in EBX of CPUID AX=00000007 */ -static const unsigned FLAC__CPUINFO_IA32_CPUID_AVX2 = 0x00000020; -#endif +static const uint32_t FLAC__CPUINFO_X86_CPUID_AVX2 = 0x00000020; +static const uint32_t FLAC__CPUINFO_X86_CPUID_BMI2 = 0x00000100; -/* - * Extra stuff needed for detection of OS support for SSE on IA-32 - */ -#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && (defined FLAC__HAS_NASM || defined FLAC__HAS_X86INTRIN) && !defined FLAC__NO_SSE_OS && !defined FLAC__SSE_OS -# if defined(__linux__) -/* - * If the OS doesn't support SSE, we will get here with a SIGILL. We - * modify the return address to jump over the offending SSE instruction - * and also the operation following it that indicates the instruction - * executed successfully. In this way we use no global variables and - * stay thread-safe. - * - * 3 + 3 + 6: - * 3 bytes for "xorps xmm0,xmm0" - * 3 bytes for estimate of how long the follwing "inc var" instruction is - * 6 bytes extra in case our estimate is wrong - * 12 bytes puts us in the NOP "landing zone" - */ -# include - static void sigill_handler_sse_os(int signal, siginfo_t *si, void *uc) - { - (void)signal, (void)si; - ((ucontext_t*)uc)->uc_mcontext.gregs[14/*REG_EIP*/] += 3 + 3 + 6; - } -# elif defined(_MSC_VER) -# include -# endif -#endif - - -void FLAC__cpu_info(FLAC__CPUInfo *info) +#if FLAC__AVX_SUPPORTED +static uint32_t +cpu_xgetbv_x86(void) { -/* - * IA32-specific - */ -#ifdef FLAC__CPU_IA32 - FLAC__bool ia32_fxsr = false; - FLAC__bool ia32_osxsave = false; - (void) ia32_fxsr; (void) ia32_osxsave; /* to avoid warnings about unused variables */ - memset(info, 0, sizeof(*info)); - info->type = FLAC__CPUINFO_TYPE_IA32; -#if !defined FLAC__NO_ASM && (defined FLAC__HAS_NASM || defined FLAC__HAS_X86INTRIN) - info->use_asm = true; /* we assume a minimum of 80386 with FLAC__CPU_IA32 */ -#ifdef FLAC__HAS_X86INTRIN - if(!FLAC__cpu_have_cpuid_x86()) - return; -#else - if(!FLAC__cpu_have_cpuid_asm_ia32()) - return; -#endif - { - /* http://www.sandpile.org/x86/cpuid.htm */ -#ifdef FLAC__HAS_X86INTRIN - FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx; - FLAC__cpu_info_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); -#else - FLAC__uint32 flags_ecx, flags_edx; - FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx); -#endif - info->ia32.cmov = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false; - info->ia32.mmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX )? true : false; - ia32_fxsr = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false; - info->ia32.sse = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE )? true : false; - info->ia32.sse2 = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false; - info->ia32.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false; - info->ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false; - info->ia32.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false; - info->ia32.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false; -#if defined FLAC__HAS_X86INTRIN && defined FLAC__AVX_SUPPORTED - ia32_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE)? true : false; - info->ia32.avx = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX )? true : false; - info->ia32.fma = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA )? true : false; - FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); - info->ia32.avx2 = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2 )? true : false; -#endif - } - -#ifdef DEBUG - fprintf(stderr, "CPU info (IA-32):\n"); - fprintf(stderr, " CMOV ....... %c\n", info->ia32.cmov ? 'Y' : 'n'); - fprintf(stderr, " MMX ........ %c\n", info->ia32.mmx ? 'Y' : 'n'); - fprintf(stderr, " SSE ........ %c\n", info->ia32.sse ? 'Y' : 'n'); - fprintf(stderr, " SSE2 ....... %c\n", info->ia32.sse2 ? 'Y' : 'n'); - fprintf(stderr, " SSE3 ....... %c\n", info->ia32.sse3 ? 'Y' : 'n'); - fprintf(stderr, " SSSE3 ...... %c\n", info->ia32.ssse3 ? 'Y' : 'n'); - fprintf(stderr, " SSE41 ...... %c\n", info->ia32.sse41 ? 'Y' : 'n'); - fprintf(stderr, " SSE42 ...... %c\n", info->ia32.sse42 ? 'Y' : 'n'); -# if defined FLAC__HAS_X86INTRIN && defined FLAC__AVX_SUPPORTED - fprintf(stderr, " AVX ........ %c\n", info->ia32.avx ? 'Y' : 'n'); - fprintf(stderr, " FMA ........ %c\n", info->ia32.fma ? 'Y' : 'n'); - fprintf(stderr, " AVX2 ....... %c\n", info->ia32.avx2 ? 'Y' : 'n'); -# endif -#endif - - /* - * now have to check for OS support of SSE instructions - */ - if(info->ia32.sse) { -#if defined FLAC__NO_SSE_OS - /* assume user knows better than us; turn it off */ - disable_sse(info); -#elif defined FLAC__SSE_OS - /* assume user knows better than us; leave as detected above */ -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__APPLE__) - int sse = 0; - size_t len; - /* at least one of these must work: */ - len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse); - len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse" , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */ - if(!sse) - disable_sse(info); -#elif defined(__NetBSD__) || defined (__OpenBSD__) -# if __NetBSD_Version__ >= 105250000 || (defined __OpenBSD__) - int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE }; - size_t len = sizeof(val); - if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) - disable_sse(info); - else { /* double-check SSE2 */ - mib[1] = CPU_SSE2; - len = sizeof(val); - if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) { - disable_sse(info); - info->ia32.sse = true; - } - } -# else - disable_sse(info); -# endif -#elif defined(__linux__) - int sse = 0; - struct sigaction sigill_save; - struct sigaction sigill_sse; - sigill_sse.sa_sigaction = sigill_handler_sse_os; - #ifdef __ANDROID__ - sigemptyset (&sigill_sse.sa_mask); - #else - __sigemptyset(&sigill_sse.sa_mask); - #endif - sigill_sse.sa_flags = SA_SIGINFO | SA_RESETHAND; /* SA_RESETHAND just in case our SIGILL return jump breaks, so we don't get stuck in a loop */ - if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save)) - { - /* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */ - /* see sigill_handler_sse_os() for an explanation of the following: */ - asm volatile ( - "xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */ - "incl %0\n\t" /* SIGILL handler will jump over this */ - /* landing zone */ - "nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */ - "nop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */ - "nop\n\t" - "nop" /* SIGILL jump lands here if "inc" is 1 byte */ - : "=r"(sse) - : "0"(sse) - ); - - sigaction(SIGILL, &sigill_save, NULL); - } - - if(!sse) - disable_sse(info); -#elif defined(_MSC_VER) - __try { - __asm { - xorps xmm0,xmm0 - } - } - __except(EXCEPTION_EXECUTE_HANDLER) { - if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION) - disable_sse(info); - } -#elif defined(__GNUC__) /* MinGW goes here */ - int sse = 0; - /* Based on the idea described in Agner Fog's manual "Optimizing subroutines in assembly language" */ - /* In theory, not guaranteed to detect lack of OS SSE support on some future Intel CPUs, but in practice works (see the aforementioned manual) */ - if (ia32_fxsr) { - struct { - FLAC__uint32 buff[128]; - } __attribute__((aligned(16))) fxsr; - FLAC__uint32 old_val, new_val; - - asm volatile ("fxsave %0" : "=m" (fxsr) : "m" (fxsr)); - old_val = fxsr.buff[50]; - fxsr.buff[50] ^= 0x0013c0de; /* change value in the buffer */ - asm volatile ("fxrstor %0" : "=m" (fxsr) : "m" (fxsr)); /* try to change SSE register */ - fxsr.buff[50] = old_val; /* restore old value in the buffer */ - asm volatile ("fxsave %0 " : "=m" (fxsr) : "m" (fxsr)); /* old value will be overwritten if SSE register was changed */ - new_val = fxsr.buff[50]; /* == old_val if FXRSTOR didn't change SSE register and (old_val ^ 0x0013c0de) otherwise */ - fxsr.buff[50] = old_val; /* again restore old value in the buffer */ - asm volatile ("fxrstor %0" : "=m" (fxsr) : "m" (fxsr)); /* restore old values of registers */ - - if ((old_val^new_val) == 0x0013c0de) - sse = 1; - } - if(!sse) - disable_sse(info); -#else - /* no way to test, disable to be safe */ - disable_sse(info); -#endif -#ifdef DEBUG - fprintf(stderr, " SSE OS sup . %c\n", info->ia32.sse ? 'Y' : 'n'); -#endif - } - else /* info->ia32.sse == false */ - disable_sse(info); - - /* - * now have to check for OS support of AVX instructions - */ - if(info->ia32.avx && ia32_osxsave) { - FLAC__uint32 ecr = FLAC__cpu_xgetbv_x86(); - if ((ecr & 0x6) != 0x6) - disable_avx(info); -#ifdef DEBUG - fprintf(stderr, " AVX OS sup . %c\n", info->ia32.avx ? 'Y' : 'n'); -#endif - } - else /* no OS AVX support*/ - disable_avx(info); -#else - info->use_asm = false; -#endif - -/* - * x86-64-specific - */ -#elif defined FLAC__CPU_X86_64 - FLAC__bool x86_osxsave = false; - (void) x86_osxsave; /* to avoid warnings about unused variables */ - memset(info, 0, sizeof(*info)); - info->type = FLAC__CPUINFO_TYPE_X86_64; -#if !defined FLAC__NO_ASM && defined FLAC__HAS_X86INTRIN - info->use_asm = true; - { - /* http://www.sandpile.org/x86/cpuid.htm */ - FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx; - FLAC__cpu_info_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); - info->x86.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false; - info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false; - info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41)? true : false; - info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42)? true : false; -#if defined FLAC__AVX_SUPPORTED - x86_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE)? true : false; - info->x86.avx = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX )? true : false; - info->x86.fma = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA )? true : false; - FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); - info->x86.avx2 = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2 )? true : false; -#endif - } -#ifdef DEBUG - fprintf(stderr, "CPU info (x86-64):\n"); - fprintf(stderr, " SSE3 ....... %c\n", info->x86.sse3 ? 'Y' : 'n'); - fprintf(stderr, " SSSE3 ...... %c\n", info->x86.ssse3 ? 'Y' : 'n'); - fprintf(stderr, " SSE41 ...... %c\n", info->x86.sse41 ? 'Y' : 'n'); - fprintf(stderr, " SSE42 ...... %c\n", info->x86.sse42 ? 'Y' : 'n'); -# if defined FLAC__AVX_SUPPORTED - fprintf(stderr, " AVX ........ %c\n", info->x86.avx ? 'Y' : 'n'); - fprintf(stderr, " FMA ........ %c\n", info->x86.fma ? 'Y' : 'n'); - fprintf(stderr, " AVX2 ....... %c\n", info->x86.avx2 ? 'Y' : 'n'); -# endif -#endif - - /* - * now have to check for OS support of AVX instructions - */ - if(info->x86.avx && x86_osxsave) { - FLAC__uint32 ecr = FLAC__cpu_xgetbv_x86(); - if ((ecr & 0x6) != 0x6) - disable_avx(info); -#ifdef DEBUG - fprintf(stderr, " AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n'); -#endif - } - else /* no OS AVX support*/ - disable_avx(info); -#else - info->use_asm = false; -#endif - -/* - * unknown CPU - */ +#if (defined _MSC_VER || defined __INTEL_COMPILER) && FLAC__AVX_SUPPORTED + return (uint32_t)_xgetbv(0); +#elif defined __GNUC__ + uint32_t lo, hi; + __asm__ volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0)); + return lo; #else - info->type = FLAC__CPUINFO_TYPE_UNKNOWN; - info->use_asm = false; + return 0; #endif } - -#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN - -#if defined _MSC_VER -#include /* for __cpuid() and _xgetbv() */ -#elif defined __GNUC__ && defined HAVE_CPUID_H -#include /* for __get_cpuid() and __get_cpuid_max() */ #endif -FLAC__uint32 FLAC__cpu_have_cpuid_x86(void) +static uint32_t +cpu_have_cpuid(void) { -#ifdef FLAC__CPU_X86_64 +#if defined FLAC__CPU_X86_64 || defined __i686__ || defined __SSE__ || (defined _M_IX86_FP && _M_IX86_FP > 0) + /* target CPU does have CPUID instruction */ return 1; -#else -# if defined _MSC_VER || defined __INTEL_COMPILER /* Do they support CPUs w/o CPUID support (or OSes that work on those CPUs)? */ +#elif defined __GNUC__ && defined HAVE_CPUID_H + if (__get_cpuid_max(0, 0) != 0) + return 1; + else + return 0; +#elif defined _MSC_VER FLAC__uint32 flags1, flags2; __asm { pushfd @@ -436,59 +124,138 @@ FLAC__uint32 FLAC__cpu_have_cpuid_x86(void) return 1; else return 0; -# elif defined __GNUC__ && defined HAVE_CPUID_H - if (__get_cpuid_max(0, 0) != 0) - return 1; - else - return 0; -# else +#else return 0; -# endif #endif } -void FLAC__cpu_info_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx) +static void +cpuinfo_x86([[maybe_unused]] FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx) { - (void) level; - -#if defined _MSC_VER || defined __INTEL_COMPILER +#if defined _MSC_VER int cpuinfo[4]; int ext = level & 0x80000000; __cpuid(cpuinfo, ext); - if((unsigned)cpuinfo[0] < level) { - *eax = *ebx = *ecx = *edx = 0; - return; - } -#if defined FLAC__AVX_SUPPORTED - __cpuidex(cpuinfo, level, 0); /* for AVX2 detection */ + if ((uint32_t)cpuinfo[0] >= level) { +#if FLAC__AVX_SUPPORTED + __cpuidex(cpuinfo, level, 0); /* for AVX2 detection */ #else - __cpuid(cpuinfo, level); /* some old compilers don't support __cpuidex */ + __cpuid(cpuinfo, level); /* some old compilers don't support __cpuidex */ #endif - *eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3]; + *eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3]; + return; + } #elif defined __GNUC__ && defined HAVE_CPUID_H FLAC__uint32 ext = level & 0x80000000; __cpuid(ext, *eax, *ebx, *ecx, *edx); - if (*eax < level) { - *eax = *ebx = *ecx = *edx = 0; + if (*eax >= level) { + __cpuid_count(level, 0, *eax, *ebx, *ecx, *edx); return; } - __cpuid_count(level, 0, *eax, *ebx, *ecx, *edx); -#else - *eax = *ebx = *ecx = *edx = 0; #endif + *eax = *ebx = *ecx = *edx = 0; } -FLAC__uint32 FLAC__cpu_xgetbv_x86(void) +#endif + +static void +x86_cpu_info (FLAC__CPUInfo *info) { -#if (defined _MSC_VER || defined __INTEL_COMPILER) && defined FLAC__AVX_SUPPORTED - return (FLAC__uint32)_xgetbv(0); -#elif defined __GNUC__ - FLAC__uint32 lo, hi; - asm volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0)); - return lo; +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN && !defined FLAC__NO_ASM + #if FLAC__AVX_SUPPORTED + FLAC__bool x86_osxsave = false; + #endif + FLAC__bool os_avx = false; + FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx; + + info->use_asm = true; /* we assume a minimum of 80386 */ + if (!cpu_have_cpuid()) + return; + + cpuinfo_x86(0, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + info->x86.intel = (flags_ebx == 0x756E6547 && flags_edx == 0x49656E69 && flags_ecx == 0x6C65746E) ? true : false; /* GenuineIntel */ + cpuinfo_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + + info->x86.cmov = (flags_edx & FLAC__CPUINFO_X86_CPUID_CMOV ) ? true : false; + info->x86.mmx = (flags_edx & FLAC__CPUINFO_X86_CPUID_MMX ) ? true : false; + info->x86.sse = (flags_edx & FLAC__CPUINFO_X86_CPUID_SSE ) ? true : false; + info->x86.sse2 = (flags_edx & FLAC__CPUINFO_X86_CPUID_SSE2 ) ? true : false; + info->x86.sse3 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE3 ) ? true : false; + info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSSE3) ? true : false; + info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE41) ? true : false; + info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_X86_CPUID_SSE42) ? true : false; + + #if (FLAC__AVX_SUPPORTED) + x86_osxsave = (flags_ecx & FLAC__CPUINFO_X86_CPUID_OSXSAVE) ? true : false; + info->x86.avx = (flags_ecx & FLAC__CPUINFO_X86_CPUID_AVX ) ? true : false; + info->x86.fma = (flags_ecx & FLAC__CPUINFO_X86_CPUID_FMA ) ? true : false; + cpuinfo_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + info->x86.avx2 = (flags_ebx & FLAC__CPUINFO_X86_CPUID_AVX2 ) ? true : false; + info->x86.bmi2 = (flags_ebx & FLAC__CPUINFO_X86_CPUID_BMI2 ) ? true : false; + #endif + +#if defined FLAC__CPU_IA32 + dfprintf(stderr, "CPU info (IA-32):\n", ""); #else - return 0; + dfprintf(stderr, "CPU info (x86-64):\n", ""); +#endif + dfprintf(stderr, " CMOV ....... %c\n", info->x86.cmov ? 'Y' : 'n'); + dfprintf(stderr, " MMX ........ %c\n", info->x86.mmx ? 'Y' : 'n'); + dfprintf(stderr, " SSE ........ %c\n", info->x86.sse ? 'Y' : 'n'); + dfprintf(stderr, " SSE2 ....... %c\n", info->x86.sse2 ? 'Y' : 'n'); + dfprintf(stderr, " SSE3 ....... %c\n", info->x86.sse3 ? 'Y' : 'n'); + dfprintf(stderr, " SSSE3 ...... %c\n", info->x86.ssse3 ? 'Y' : 'n'); + dfprintf(stderr, " SSE41 ...... %c\n", info->x86.sse41 ? 'Y' : 'n'); + dfprintf(stderr, " SSE42 ...... %c\n", info->x86.sse42 ? 'Y' : 'n'); + + if (FLAC__AVX_SUPPORTED) { + dfprintf(stderr, " AVX ........ %c\n", info->x86.avx ? 'Y' : 'n'); + dfprintf(stderr, " FMA ........ %c\n", info->x86.fma ? 'Y' : 'n'); + dfprintf(stderr, " AVX2 ....... %c\n", info->x86.avx2 ? 'Y' : 'n'); + dfprintf(stderr, " BMI2 ....... %c\n", info->x86.bmi2 ? 'Y' : 'n'); + } + + /* + * now have to check for OS support of AVX instructions + */ + #if FLAC__AVX_SUPPORTED + if (info->x86.avx && x86_osxsave && (cpu_xgetbv_x86() & 0x6) == 0x6) { + os_avx = true; + } + #endif + if (os_avx) { + dfprintf(stderr, " AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n'); + } + if (!os_avx) { + /* no OS AVX support */ + info->x86.avx = false; + info->x86.avx2 = false; + info->x86.fma = false; + } +#else + info->use_asm = false; #endif } -#endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */ +void FLAC__cpu_info (FLAC__CPUInfo *info) +{ + memset(info, 0, sizeof(*info)); + +#ifdef FLAC__CPU_IA32 + info->type = FLAC__CPUINFO_TYPE_IA32; +#elif defined FLAC__CPU_X86_64 + info->type = FLAC__CPUINFO_TYPE_X86_64; +#else + info->type = FLAC__CPUINFO_TYPE_UNKNOWN; +#endif + + switch (info->type) { + case FLAC__CPUINFO_TYPE_IA32: /* fallthrough */ + case FLAC__CPUINFO_TYPE_X86_64: + x86_cpu_info (info); + break; + default: + info->use_asm = false; + break; + } +} diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c index cb49c7b6..fd9b750b 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,7 +38,7 @@ /* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */ -FLAC__byte const FLAC__crc8_table[256] = { +FLAC__uint8 const FLAC__crc8_table[256] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, @@ -75,8 +75,8 @@ FLAC__byte const FLAC__crc8_table[256] = { /* CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 */ -unsigned const FLAC__crc16_table[256] = { - 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, +FLAC__uint16 const FLAC__crc16_table[8][256] = { + { 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, @@ -107,22 +107,263 @@ unsigned const FLAC__crc16_table[256] = { 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, - 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 -}; + 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 }, + { 0x0000, 0x8603, 0x8c03, 0x0a00, 0x9803, 0x1e00, 0x1400, 0x9203, + 0xb003, 0x3600, 0x3c00, 0xba03, 0x2800, 0xae03, 0xa403, 0x2200, + 0xe003, 0x6600, 0x6c00, 0xea03, 0x7800, 0xfe03, 0xf403, 0x7200, + 0x5000, 0xd603, 0xdc03, 0x5a00, 0xc803, 0x4e00, 0x4400, 0xc203, + 0x4003, 0xc600, 0xcc00, 0x4a03, 0xd800, 0x5e03, 0x5403, 0xd200, + 0xf000, 0x7603, 0x7c03, 0xfa00, 0x6803, 0xee00, 0xe400, 0x6203, + 0xa000, 0x2603, 0x2c03, 0xaa00, 0x3803, 0xbe00, 0xb400, 0x3203, + 0x1003, 0x9600, 0x9c00, 0x1a03, 0x8800, 0x0e03, 0x0403, 0x8200, + 0x8006, 0x0605, 0x0c05, 0x8a06, 0x1805, 0x9e06, 0x9406, 0x1205, + 0x3005, 0xb606, 0xbc06, 0x3a05, 0xa806, 0x2e05, 0x2405, 0xa206, + 0x6005, 0xe606, 0xec06, 0x6a05, 0xf806, 0x7e05, 0x7405, 0xf206, + 0xd006, 0x5605, 0x5c05, 0xda06, 0x4805, 0xce06, 0xc406, 0x4205, + 0xc005, 0x4606, 0x4c06, 0xca05, 0x5806, 0xde05, 0xd405, 0x5206, + 0x7006, 0xf605, 0xfc05, 0x7a06, 0xe805, 0x6e06, 0x6406, 0xe205, + 0x2006, 0xa605, 0xac05, 0x2a06, 0xb805, 0x3e06, 0x3406, 0xb205, + 0x9005, 0x1606, 0x1c06, 0x9a05, 0x0806, 0x8e05, 0x8405, 0x0206, + 0x8009, 0x060a, 0x0c0a, 0x8a09, 0x180a, 0x9e09, 0x9409, 0x120a, + 0x300a, 0xb609, 0xbc09, 0x3a0a, 0xa809, 0x2e0a, 0x240a, 0xa209, + 0x600a, 0xe609, 0xec09, 0x6a0a, 0xf809, 0x7e0a, 0x740a, 0xf209, + 0xd009, 0x560a, 0x5c0a, 0xda09, 0x480a, 0xce09, 0xc409, 0x420a, + 0xc00a, 0x4609, 0x4c09, 0xca0a, 0x5809, 0xde0a, 0xd40a, 0x5209, + 0x7009, 0xf60a, 0xfc0a, 0x7a09, 0xe80a, 0x6e09, 0x6409, 0xe20a, + 0x2009, 0xa60a, 0xac0a, 0x2a09, 0xb80a, 0x3e09, 0x3409, 0xb20a, + 0x900a, 0x1609, 0x1c09, 0x9a0a, 0x0809, 0x8e0a, 0x840a, 0x0209, + 0x000f, 0x860c, 0x8c0c, 0x0a0f, 0x980c, 0x1e0f, 0x140f, 0x920c, + 0xb00c, 0x360f, 0x3c0f, 0xba0c, 0x280f, 0xae0c, 0xa40c, 0x220f, + 0xe00c, 0x660f, 0x6c0f, 0xea0c, 0x780f, 0xfe0c, 0xf40c, 0x720f, + 0x500f, 0xd60c, 0xdc0c, 0x5a0f, 0xc80c, 0x4e0f, 0x440f, 0xc20c, + 0x400c, 0xc60f, 0xcc0f, 0x4a0c, 0xd80f, 0x5e0c, 0x540c, 0xd20f, + 0xf00f, 0x760c, 0x7c0c, 0xfa0f, 0x680c, 0xee0f, 0xe40f, 0x620c, + 0xa00f, 0x260c, 0x2c0c, 0xaa0f, 0x380c, 0xbe0f, 0xb40f, 0x320c, + 0x100c, 0x960f, 0x9c0f, 0x1a0c, 0x880f, 0x0e0c, 0x040c, 0x820f }, -void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc) -{ - *crc = FLAC__crc8_table[*crc ^ data]; -} + { 0x0000, 0x8017, 0x802b, 0x003c, 0x8053, 0x0044, 0x0078, 0x806f, + 0x80a3, 0x00b4, 0x0088, 0x809f, 0x00f0, 0x80e7, 0x80db, 0x00cc, + 0x8143, 0x0154, 0x0168, 0x817f, 0x0110, 0x8107, 0x813b, 0x012c, + 0x01e0, 0x81f7, 0x81cb, 0x01dc, 0x81b3, 0x01a4, 0x0198, 0x818f, + 0x8283, 0x0294, 0x02a8, 0x82bf, 0x02d0, 0x82c7, 0x82fb, 0x02ec, + 0x0220, 0x8237, 0x820b, 0x021c, 0x8273, 0x0264, 0x0258, 0x824f, + 0x03c0, 0x83d7, 0x83eb, 0x03fc, 0x8393, 0x0384, 0x03b8, 0x83af, + 0x8363, 0x0374, 0x0348, 0x835f, 0x0330, 0x8327, 0x831b, 0x030c, + 0x8503, 0x0514, 0x0528, 0x853f, 0x0550, 0x8547, 0x857b, 0x056c, + 0x05a0, 0x85b7, 0x858b, 0x059c, 0x85f3, 0x05e4, 0x05d8, 0x85cf, + 0x0440, 0x8457, 0x846b, 0x047c, 0x8413, 0x0404, 0x0438, 0x842f, + 0x84e3, 0x04f4, 0x04c8, 0x84df, 0x04b0, 0x84a7, 0x849b, 0x048c, + 0x0780, 0x8797, 0x87ab, 0x07bc, 0x87d3, 0x07c4, 0x07f8, 0x87ef, + 0x8723, 0x0734, 0x0708, 0x871f, 0x0770, 0x8767, 0x875b, 0x074c, + 0x86c3, 0x06d4, 0x06e8, 0x86ff, 0x0690, 0x8687, 0x86bb, 0x06ac, + 0x0660, 0x8677, 0x864b, 0x065c, 0x8633, 0x0624, 0x0618, 0x860f, + 0x8a03, 0x0a14, 0x0a28, 0x8a3f, 0x0a50, 0x8a47, 0x8a7b, 0x0a6c, + 0x0aa0, 0x8ab7, 0x8a8b, 0x0a9c, 0x8af3, 0x0ae4, 0x0ad8, 0x8acf, + 0x0b40, 0x8b57, 0x8b6b, 0x0b7c, 0x8b13, 0x0b04, 0x0b38, 0x8b2f, + 0x8be3, 0x0bf4, 0x0bc8, 0x8bdf, 0x0bb0, 0x8ba7, 0x8b9b, 0x0b8c, + 0x0880, 0x8897, 0x88ab, 0x08bc, 0x88d3, 0x08c4, 0x08f8, 0x88ef, + 0x8823, 0x0834, 0x0808, 0x881f, 0x0870, 0x8867, 0x885b, 0x084c, + 0x89c3, 0x09d4, 0x09e8, 0x89ff, 0x0990, 0x8987, 0x89bb, 0x09ac, + 0x0960, 0x8977, 0x894b, 0x095c, 0x8933, 0x0924, 0x0918, 0x890f, + 0x0f00, 0x8f17, 0x8f2b, 0x0f3c, 0x8f53, 0x0f44, 0x0f78, 0x8f6f, + 0x8fa3, 0x0fb4, 0x0f88, 0x8f9f, 0x0ff0, 0x8fe7, 0x8fdb, 0x0fcc, + 0x8e43, 0x0e54, 0x0e68, 0x8e7f, 0x0e10, 0x8e07, 0x8e3b, 0x0e2c, + 0x0ee0, 0x8ef7, 0x8ecb, 0x0edc, 0x8eb3, 0x0ea4, 0x0e98, 0x8e8f, + 0x8d83, 0x0d94, 0x0da8, 0x8dbf, 0x0dd0, 0x8dc7, 0x8dfb, 0x0dec, + 0x0d20, 0x8d37, 0x8d0b, 0x0d1c, 0x8d73, 0x0d64, 0x0d58, 0x8d4f, + 0x0cc0, 0x8cd7, 0x8ceb, 0x0cfc, 0x8c93, 0x0c84, 0x0cb8, 0x8caf, + 0x8c63, 0x0c74, 0x0c48, 0x8c5f, 0x0c30, 0x8c27, 0x8c1b, 0x0c0c }, + + { 0x0000, 0x9403, 0xa803, 0x3c00, 0xd003, 0x4400, 0x7800, 0xec03, + 0x2003, 0xb400, 0x8800, 0x1c03, 0xf000, 0x6403, 0x5803, 0xcc00, + 0x4006, 0xd405, 0xe805, 0x7c06, 0x9005, 0x0406, 0x3806, 0xac05, + 0x6005, 0xf406, 0xc806, 0x5c05, 0xb006, 0x2405, 0x1805, 0x8c06, + 0x800c, 0x140f, 0x280f, 0xbc0c, 0x500f, 0xc40c, 0xf80c, 0x6c0f, + 0xa00f, 0x340c, 0x080c, 0x9c0f, 0x700c, 0xe40f, 0xd80f, 0x4c0c, + 0xc00a, 0x5409, 0x6809, 0xfc0a, 0x1009, 0x840a, 0xb80a, 0x2c09, + 0xe009, 0x740a, 0x480a, 0xdc09, 0x300a, 0xa409, 0x9809, 0x0c0a, + 0x801d, 0x141e, 0x281e, 0xbc1d, 0x501e, 0xc41d, 0xf81d, 0x6c1e, + 0xa01e, 0x341d, 0x081d, 0x9c1e, 0x701d, 0xe41e, 0xd81e, 0x4c1d, + 0xc01b, 0x5418, 0x6818, 0xfc1b, 0x1018, 0x841b, 0xb81b, 0x2c18, + 0xe018, 0x741b, 0x481b, 0xdc18, 0x301b, 0xa418, 0x9818, 0x0c1b, + 0x0011, 0x9412, 0xa812, 0x3c11, 0xd012, 0x4411, 0x7811, 0xec12, + 0x2012, 0xb411, 0x8811, 0x1c12, 0xf011, 0x6412, 0x5812, 0xcc11, + 0x4017, 0xd414, 0xe814, 0x7c17, 0x9014, 0x0417, 0x3817, 0xac14, + 0x6014, 0xf417, 0xc817, 0x5c14, 0xb017, 0x2414, 0x1814, 0x8c17, + 0x803f, 0x143c, 0x283c, 0xbc3f, 0x503c, 0xc43f, 0xf83f, 0x6c3c, + 0xa03c, 0x343f, 0x083f, 0x9c3c, 0x703f, 0xe43c, 0xd83c, 0x4c3f, + 0xc039, 0x543a, 0x683a, 0xfc39, 0x103a, 0x8439, 0xb839, 0x2c3a, + 0xe03a, 0x7439, 0x4839, 0xdc3a, 0x3039, 0xa43a, 0x983a, 0x0c39, + 0x0033, 0x9430, 0xa830, 0x3c33, 0xd030, 0x4433, 0x7833, 0xec30, + 0x2030, 0xb433, 0x8833, 0x1c30, 0xf033, 0x6430, 0x5830, 0xcc33, + 0x4035, 0xd436, 0xe836, 0x7c35, 0x9036, 0x0435, 0x3835, 0xac36, + 0x6036, 0xf435, 0xc835, 0x5c36, 0xb035, 0x2436, 0x1836, 0x8c35, + 0x0022, 0x9421, 0xa821, 0x3c22, 0xd021, 0x4422, 0x7822, 0xec21, + 0x2021, 0xb422, 0x8822, 0x1c21, 0xf022, 0x6421, 0x5821, 0xcc22, + 0x4024, 0xd427, 0xe827, 0x7c24, 0x9027, 0x0424, 0x3824, 0xac27, + 0x6027, 0xf424, 0xc824, 0x5c27, 0xb024, 0x2427, 0x1827, 0x8c24, + 0x802e, 0x142d, 0x282d, 0xbc2e, 0x502d, 0xc42e, 0xf82e, 0x6c2d, + 0xa02d, 0x342e, 0x082e, 0x9c2d, 0x702e, 0xe42d, 0xd82d, 0x4c2e, + 0xc028, 0x542b, 0x682b, 0xfc28, 0x102b, 0x8428, 0xb828, 0x2c2b, + 0xe02b, 0x7428, 0x4828, 0xdc2b, 0x3028, 0xa42b, 0x982b, 0x0c28 }, + + { 0x0000, 0x807b, 0x80f3, 0x0088, 0x81e3, 0x0198, 0x0110, 0x816b, + 0x83c3, 0x03b8, 0x0330, 0x834b, 0x0220, 0x825b, 0x82d3, 0x02a8, + 0x8783, 0x07f8, 0x0770, 0x870b, 0x0660, 0x861b, 0x8693, 0x06e8, + 0x0440, 0x843b, 0x84b3, 0x04c8, 0x85a3, 0x05d8, 0x0550, 0x852b, + 0x8f03, 0x0f78, 0x0ff0, 0x8f8b, 0x0ee0, 0x8e9b, 0x8e13, 0x0e68, + 0x0cc0, 0x8cbb, 0x8c33, 0x0c48, 0x8d23, 0x0d58, 0x0dd0, 0x8dab, + 0x0880, 0x88fb, 0x8873, 0x0808, 0x8963, 0x0918, 0x0990, 0x89eb, + 0x8b43, 0x0b38, 0x0bb0, 0x8bcb, 0x0aa0, 0x8adb, 0x8a53, 0x0a28, + 0x9e03, 0x1e78, 0x1ef0, 0x9e8b, 0x1fe0, 0x9f9b, 0x9f13, 0x1f68, + 0x1dc0, 0x9dbb, 0x9d33, 0x1d48, 0x9c23, 0x1c58, 0x1cd0, 0x9cab, + 0x1980, 0x99fb, 0x9973, 0x1908, 0x9863, 0x1818, 0x1890, 0x98eb, + 0x9a43, 0x1a38, 0x1ab0, 0x9acb, 0x1ba0, 0x9bdb, 0x9b53, 0x1b28, + 0x1100, 0x917b, 0x91f3, 0x1188, 0x90e3, 0x1098, 0x1010, 0x906b, + 0x92c3, 0x12b8, 0x1230, 0x924b, 0x1320, 0x935b, 0x93d3, 0x13a8, + 0x9683, 0x16f8, 0x1670, 0x960b, 0x1760, 0x971b, 0x9793, 0x17e8, + 0x1540, 0x953b, 0x95b3, 0x15c8, 0x94a3, 0x14d8, 0x1450, 0x942b, + 0xbc03, 0x3c78, 0x3cf0, 0xbc8b, 0x3de0, 0xbd9b, 0xbd13, 0x3d68, + 0x3fc0, 0xbfbb, 0xbf33, 0x3f48, 0xbe23, 0x3e58, 0x3ed0, 0xbeab, + 0x3b80, 0xbbfb, 0xbb73, 0x3b08, 0xba63, 0x3a18, 0x3a90, 0xbaeb, + 0xb843, 0x3838, 0x38b0, 0xb8cb, 0x39a0, 0xb9db, 0xb953, 0x3928, + 0x3300, 0xb37b, 0xb3f3, 0x3388, 0xb2e3, 0x3298, 0x3210, 0xb26b, + 0xb0c3, 0x30b8, 0x3030, 0xb04b, 0x3120, 0xb15b, 0xb1d3, 0x31a8, + 0xb483, 0x34f8, 0x3470, 0xb40b, 0x3560, 0xb51b, 0xb593, 0x35e8, + 0x3740, 0xb73b, 0xb7b3, 0x37c8, 0xb6a3, 0x36d8, 0x3650, 0xb62b, + 0x2200, 0xa27b, 0xa2f3, 0x2288, 0xa3e3, 0x2398, 0x2310, 0xa36b, + 0xa1c3, 0x21b8, 0x2130, 0xa14b, 0x2020, 0xa05b, 0xa0d3, 0x20a8, + 0xa583, 0x25f8, 0x2570, 0xa50b, 0x2460, 0xa41b, 0xa493, 0x24e8, + 0x2640, 0xa63b, 0xa6b3, 0x26c8, 0xa7a3, 0x27d8, 0x2750, 0xa72b, + 0xad03, 0x2d78, 0x2df0, 0xad8b, 0x2ce0, 0xac9b, 0xac13, 0x2c68, + 0x2ec0, 0xaebb, 0xae33, 0x2e48, 0xaf23, 0x2f58, 0x2fd0, 0xafab, + 0x2a80, 0xaafb, 0xaa73, 0x2a08, 0xab63, 0x2b18, 0x2b90, 0xabeb, + 0xa943, 0x2938, 0x29b0, 0xa9cb, 0x28a0, 0xa8db, 0xa853, 0x2828 }, + + { 0x0000, 0xf803, 0x7003, 0x8800, 0xe006, 0x1805, 0x9005, 0x6806, + 0x4009, 0xb80a, 0x300a, 0xc809, 0xa00f, 0x580c, 0xd00c, 0x280f, + 0x8012, 0x7811, 0xf011, 0x0812, 0x6014, 0x9817, 0x1017, 0xe814, + 0xc01b, 0x3818, 0xb018, 0x481b, 0x201d, 0xd81e, 0x501e, 0xa81d, + 0x8021, 0x7822, 0xf022, 0x0821, 0x6027, 0x9824, 0x1024, 0xe827, + 0xc028, 0x382b, 0xb02b, 0x4828, 0x202e, 0xd82d, 0x502d, 0xa82e, + 0x0033, 0xf830, 0x7030, 0x8833, 0xe035, 0x1836, 0x9036, 0x6835, + 0x403a, 0xb839, 0x3039, 0xc83a, 0xa03c, 0x583f, 0xd03f, 0x283c, + 0x8047, 0x7844, 0xf044, 0x0847, 0x6041, 0x9842, 0x1042, 0xe841, + 0xc04e, 0x384d, 0xb04d, 0x484e, 0x2048, 0xd84b, 0x504b, 0xa848, + 0x0055, 0xf856, 0x7056, 0x8855, 0xe053, 0x1850, 0x9050, 0x6853, + 0x405c, 0xb85f, 0x305f, 0xc85c, 0xa05a, 0x5859, 0xd059, 0x285a, + 0x0066, 0xf865, 0x7065, 0x8866, 0xe060, 0x1863, 0x9063, 0x6860, + 0x406f, 0xb86c, 0x306c, 0xc86f, 0xa069, 0x586a, 0xd06a, 0x2869, + 0x8074, 0x7877, 0xf077, 0x0874, 0x6072, 0x9871, 0x1071, 0xe872, + 0xc07d, 0x387e, 0xb07e, 0x487d, 0x207b, 0xd878, 0x5078, 0xa87b, + 0x808b, 0x7888, 0xf088, 0x088b, 0x608d, 0x988e, 0x108e, 0xe88d, + 0xc082, 0x3881, 0xb081, 0x4882, 0x2084, 0xd887, 0x5087, 0xa884, + 0x0099, 0xf89a, 0x709a, 0x8899, 0xe09f, 0x189c, 0x909c, 0x689f, + 0x4090, 0xb893, 0x3093, 0xc890, 0xa096, 0x5895, 0xd095, 0x2896, + 0x00aa, 0xf8a9, 0x70a9, 0x88aa, 0xe0ac, 0x18af, 0x90af, 0x68ac, + 0x40a3, 0xb8a0, 0x30a0, 0xc8a3, 0xa0a5, 0x58a6, 0xd0a6, 0x28a5, + 0x80b8, 0x78bb, 0xf0bb, 0x08b8, 0x60be, 0x98bd, 0x10bd, 0xe8be, + 0xc0b1, 0x38b2, 0xb0b2, 0x48b1, 0x20b7, 0xd8b4, 0x50b4, 0xa8b7, + 0x00cc, 0xf8cf, 0x70cf, 0x88cc, 0xe0ca, 0x18c9, 0x90c9, 0x68ca, + 0x40c5, 0xb8c6, 0x30c6, 0xc8c5, 0xa0c3, 0x58c0, 0xd0c0, 0x28c3, + 0x80de, 0x78dd, 0xf0dd, 0x08de, 0x60d8, 0x98db, 0x10db, 0xe8d8, + 0xc0d7, 0x38d4, 0xb0d4, 0x48d7, 0x20d1, 0xd8d2, 0x50d2, 0xa8d1, + 0x80ed, 0x78ee, 0xf0ee, 0x08ed, 0x60eb, 0x98e8, 0x10e8, 0xe8eb, + 0xc0e4, 0x38e7, 0xb0e7, 0x48e4, 0x20e2, 0xd8e1, 0x50e1, 0xa8e2, + 0x00ff, 0xf8fc, 0x70fc, 0x88ff, 0xe0f9, 0x18fa, 0x90fa, 0x68f9, + 0x40f6, 0xb8f5, 0x30f5, 0xc8f6, 0xa0f0, 0x58f3, 0xd0f3, 0x28f0 }, + + { 0x0000, 0x8113, 0x8223, 0x0330, 0x8443, 0x0550, 0x0660, 0x8773, + 0x8883, 0x0990, 0x0aa0, 0x8bb3, 0x0cc0, 0x8dd3, 0x8ee3, 0x0ff0, + 0x9103, 0x1010, 0x1320, 0x9233, 0x1540, 0x9453, 0x9763, 0x1670, + 0x1980, 0x9893, 0x9ba3, 0x1ab0, 0x9dc3, 0x1cd0, 0x1fe0, 0x9ef3, + 0xa203, 0x2310, 0x2020, 0xa133, 0x2640, 0xa753, 0xa463, 0x2570, + 0x2a80, 0xab93, 0xa8a3, 0x29b0, 0xaec3, 0x2fd0, 0x2ce0, 0xadf3, + 0x3300, 0xb213, 0xb123, 0x3030, 0xb743, 0x3650, 0x3560, 0xb473, + 0xbb83, 0x3a90, 0x39a0, 0xb8b3, 0x3fc0, 0xbed3, 0xbde3, 0x3cf0, + 0xc403, 0x4510, 0x4620, 0xc733, 0x4040, 0xc153, 0xc263, 0x4370, + 0x4c80, 0xcd93, 0xcea3, 0x4fb0, 0xc8c3, 0x49d0, 0x4ae0, 0xcbf3, + 0x5500, 0xd413, 0xd723, 0x5630, 0xd143, 0x5050, 0x5360, 0xd273, + 0xdd83, 0x5c90, 0x5fa0, 0xdeb3, 0x59c0, 0xd8d3, 0xdbe3, 0x5af0, + 0x6600, 0xe713, 0xe423, 0x6530, 0xe243, 0x6350, 0x6060, 0xe173, + 0xee83, 0x6f90, 0x6ca0, 0xedb3, 0x6ac0, 0xebd3, 0xe8e3, 0x69f0, + 0xf703, 0x7610, 0x7520, 0xf433, 0x7340, 0xf253, 0xf163, 0x7070, + 0x7f80, 0xfe93, 0xfda3, 0x7cb0, 0xfbc3, 0x7ad0, 0x79e0, 0xf8f3, + 0x0803, 0x8910, 0x8a20, 0x0b33, 0x8c40, 0x0d53, 0x0e63, 0x8f70, + 0x8080, 0x0193, 0x02a3, 0x83b0, 0x04c3, 0x85d0, 0x86e0, 0x07f3, + 0x9900, 0x1813, 0x1b23, 0x9a30, 0x1d43, 0x9c50, 0x9f60, 0x1e73, + 0x1183, 0x9090, 0x93a0, 0x12b3, 0x95c0, 0x14d3, 0x17e3, 0x96f0, + 0xaa00, 0x2b13, 0x2823, 0xa930, 0x2e43, 0xaf50, 0xac60, 0x2d73, + 0x2283, 0xa390, 0xa0a0, 0x21b3, 0xa6c0, 0x27d3, 0x24e3, 0xa5f0, + 0x3b03, 0xba10, 0xb920, 0x3833, 0xbf40, 0x3e53, 0x3d63, 0xbc70, + 0xb380, 0x3293, 0x31a3, 0xb0b0, 0x37c3, 0xb6d0, 0xb5e0, 0x34f3, + 0xcc00, 0x4d13, 0x4e23, 0xcf30, 0x4843, 0xc950, 0xca60, 0x4b73, + 0x4483, 0xc590, 0xc6a0, 0x47b3, 0xc0c0, 0x41d3, 0x42e3, 0xc3f0, + 0x5d03, 0xdc10, 0xdf20, 0x5e33, 0xd940, 0x5853, 0x5b63, 0xda70, + 0xd580, 0x5493, 0x57a3, 0xd6b0, 0x51c3, 0xd0d0, 0xd3e0, 0x52f3, + 0x6e03, 0xef10, 0xec20, 0x6d33, 0xea40, 0x6b53, 0x6863, 0xe970, + 0xe680, 0x6793, 0x64a3, 0xe5b0, 0x62c3, 0xe3d0, 0xe0e0, 0x61f3, + 0xff00, 0x7e13, 0x7d23, 0xfc30, 0x7b43, 0xfa50, 0xf960, 0x7873, + 0x7783, 0xf690, 0xf5a0, 0x74b3, 0xf3c0, 0x72d3, 0x71e3, 0xf0f0 }, + + { 0x0000, 0x1006, 0x200c, 0x300a, 0x4018, 0x501e, 0x6014, 0x7012, + 0x8030, 0x9036, 0xa03c, 0xb03a, 0xc028, 0xd02e, 0xe024, 0xf022, + 0x8065, 0x9063, 0xa069, 0xb06f, 0xc07d, 0xd07b, 0xe071, 0xf077, + 0x0055, 0x1053, 0x2059, 0x305f, 0x404d, 0x504b, 0x6041, 0x7047, + 0x80cf, 0x90c9, 0xa0c3, 0xb0c5, 0xc0d7, 0xd0d1, 0xe0db, 0xf0dd, + 0x00ff, 0x10f9, 0x20f3, 0x30f5, 0x40e7, 0x50e1, 0x60eb, 0x70ed, + 0x00aa, 0x10ac, 0x20a6, 0x30a0, 0x40b2, 0x50b4, 0x60be, 0x70b8, + 0x809a, 0x909c, 0xa096, 0xb090, 0xc082, 0xd084, 0xe08e, 0xf088, + 0x819b, 0x919d, 0xa197, 0xb191, 0xc183, 0xd185, 0xe18f, 0xf189, + 0x01ab, 0x11ad, 0x21a7, 0x31a1, 0x41b3, 0x51b5, 0x61bf, 0x71b9, + 0x01fe, 0x11f8, 0x21f2, 0x31f4, 0x41e6, 0x51e0, 0x61ea, 0x71ec, + 0x81ce, 0x91c8, 0xa1c2, 0xb1c4, 0xc1d6, 0xd1d0, 0xe1da, 0xf1dc, + 0x0154, 0x1152, 0x2158, 0x315e, 0x414c, 0x514a, 0x6140, 0x7146, + 0x8164, 0x9162, 0xa168, 0xb16e, 0xc17c, 0xd17a, 0xe170, 0xf176, + 0x8131, 0x9137, 0xa13d, 0xb13b, 0xc129, 0xd12f, 0xe125, 0xf123, + 0x0101, 0x1107, 0x210d, 0x310b, 0x4119, 0x511f, 0x6115, 0x7113, + 0x8333, 0x9335, 0xa33f, 0xb339, 0xc32b, 0xd32d, 0xe327, 0xf321, + 0x0303, 0x1305, 0x230f, 0x3309, 0x431b, 0x531d, 0x6317, 0x7311, + 0x0356, 0x1350, 0x235a, 0x335c, 0x434e, 0x5348, 0x6342, 0x7344, + 0x8366, 0x9360, 0xa36a, 0xb36c, 0xc37e, 0xd378, 0xe372, 0xf374, + 0x03fc, 0x13fa, 0x23f0, 0x33f6, 0x43e4, 0x53e2, 0x63e8, 0x73ee, + 0x83cc, 0x93ca, 0xa3c0, 0xb3c6, 0xc3d4, 0xd3d2, 0xe3d8, 0xf3de, + 0x8399, 0x939f, 0xa395, 0xb393, 0xc381, 0xd387, 0xe38d, 0xf38b, + 0x03a9, 0x13af, 0x23a5, 0x33a3, 0x43b1, 0x53b7, 0x63bd, 0x73bb, + 0x02a8, 0x12ae, 0x22a4, 0x32a2, 0x42b0, 0x52b6, 0x62bc, 0x72ba, + 0x8298, 0x929e, 0xa294, 0xb292, 0xc280, 0xd286, 0xe28c, 0xf28a, + 0x82cd, 0x92cb, 0xa2c1, 0xb2c7, 0xc2d5, 0xd2d3, 0xe2d9, 0xf2df, + 0x02fd, 0x12fb, 0x22f1, 0x32f7, 0x42e5, 0x52e3, 0x62e9, 0x72ef, + 0x8267, 0x9261, 0xa26b, 0xb26d, 0xc27f, 0xd279, 0xe273, 0xf275, + 0x0257, 0x1251, 0x225b, 0x325d, 0x424f, 0x5249, 0x6243, 0x7245, + 0x0202, 0x1204, 0x220e, 0x3208, 0x421a, 0x521c, 0x6216, 0x7210, + 0x8232, 0x9234, 0xa23e, 0xb238, 0xc22a, 0xd22c, 0xe226, 0xf220 } +}; -void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc) +#if 0 +void FLAC__crc16_init_table(void) { - while(len--) - *crc = FLAC__crc8_table[*crc ^ *data++]; + int i, j; + FLAC__uint16 polynomial, crc; + polynomial = 0x8005; + + for(i = 0; i <= 0xFF; i++){ + crc = i << 8; + + for(j = 0; j < 8; j++) + crc = (crc << 1) ^ (crc & (1 << 15) ? polynomial : 0); + + FLAC__crc16_table[0][i] = crc; + } + + for(i = 0; i <= 0xFF; i++) + for(j = 1; j < 8; j++) + FLAC__crc16_table[j][i] = FLAC__crc16_table[0][FLAC__crc16_table[j - 1][i] >> 8] ^ (FLAC__crc16_table[j - 1][i] << 8); } +#endif -FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len) +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, uint32_t len) { FLAC__uint8 crc = 0; @@ -132,12 +373,64 @@ FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len) return crc; } -unsigned FLAC__crc16(const FLAC__byte *data, unsigned len) +FLAC__uint16 FLAC__crc16(const FLAC__byte *data, uint32_t len) { - unsigned crc = 0; + FLAC__uint16 crc = 0; + + while(len >= 8){ + crc ^= data[0] << 8 | data[1]; + + crc = FLAC__crc16_table[7][crc >> 8] ^ FLAC__crc16_table[6][crc & 0xFF] ^ + FLAC__crc16_table[5][data[2] ] ^ FLAC__crc16_table[4][data[3] ] ^ + FLAC__crc16_table[3][data[4] ] ^ FLAC__crc16_table[2][data[5] ] ^ + FLAC__crc16_table[1][data[6] ] ^ FLAC__crc16_table[0][data[7] ]; + + data += 8; + len -= 8; + } while(len--) - crc = ((crc<<8) ^ FLAC__crc16_table[(crc>>8) ^ *data++]) & 0xffff; + crc = (crc<<8) ^ FLAC__crc16_table[0][(crc>>8) ^ *data++]; + + return crc; +} + +FLAC__uint16 FLAC__crc16_update_words32(const FLAC__uint32 *words, uint32_t len, FLAC__uint16 crc) +{ + while (len >= 2) { + crc ^= words[0] >> 16; + + crc = FLAC__crc16_table[7][crc >> 8 ] ^ FLAC__crc16_table[6][crc & 0xFF ] ^ + FLAC__crc16_table[5][(words[0] >> 8) & 0xFF] ^ FLAC__crc16_table[4][ words[0] & 0xFF] ^ + FLAC__crc16_table[3][ words[1] >> 24 ] ^ FLAC__crc16_table[2][(words[1] >> 16) & 0xFF] ^ + FLAC__crc16_table[1][(words[1] >> 8) & 0xFF] ^ FLAC__crc16_table[0][ words[1] & 0xFF]; + + words += 2; + len -= 2; + } + + if (len) { + crc ^= words[0] >> 16; + + crc = FLAC__crc16_table[3][crc >> 8 ] ^ FLAC__crc16_table[2][crc & 0xFF ] ^ + FLAC__crc16_table[1][(words[0] >> 8) & 0xFF] ^ FLAC__crc16_table[0][words[0] & 0xFF]; + } + + return crc; +} + +FLAC__uint16 FLAC__crc16_update_words64(const FLAC__uint64 *words, uint32_t len, FLAC__uint16 crc) +{ + while (len--) { + crc ^= words[0] >> 48; + + crc = FLAC__crc16_table[7][crc >> 8 ] ^ FLAC__crc16_table[6][crc & 0xFF ] ^ + FLAC__crc16_table[5][(words[0] >> 40) & 0xFF] ^ FLAC__crc16_table[4][(words[0] >> 32) & 0xFF] ^ + FLAC__crc16_table[3][(words[0] >> 24) & 0xFF] ^ FLAC__crc16_table[2][(words[0] >> 16) & 0xFF] ^ + FLAC__crc16_table[1][(words[0] >> 8) & 0xFF] ^ FLAC__crc16_table[0][ words[0] & 0xFF]; + + words++; + } return crc; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/deduplication/bitreader_read_rice_signed_block.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/deduplication/bitreader_read_rice_signed_block.c new file mode 100644 index 00000000..75ed47f7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/deduplication/bitreader_read_rice_signed_block.c @@ -0,0 +1,143 @@ +{ + /* try and get br->consumed_words and br->consumed_bits into register; + * must remember to flush them back to *br before calling other + * bitreader functions that use them, and before returning */ + uint32_t cwords, words, lsbs, msbs, x, y, limit; + uint32_t ucbits; /* keep track of the number of unconsumed bits in word */ + brword b; + int *val, *end; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + FLAC__ASSERT(parameter < 32); + /* the above two asserts also guarantee that the binary part never straddles more than 2 words, so we don't have to loop to read it */ + + limit = UINT32_MAX >> parameter; /* Maximal msbs that can occur with residual bounded to int32_t */ + + val = vals; + end = vals + nvals; + + if(parameter == 0) { + while(val < end) { + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + /* Checking limit here would be overzealous: coding UINT32_MAX + * with parameter == 0 would take 4GiB */ + *val++ = (int)(msbs >> 1) ^ -(int)(msbs & 1); + } + + return true; + } + + FLAC__ASSERT(parameter > 0); + + cwords = br->consumed_words; + words = br->words; + + /* if we've not consumed up to a partial tail word... */ + if(cwords >= words) { + x = 0; + goto process_tail; + } + + ucbits = FLAC__BITS_PER_WORD - br->consumed_bits; + b = br->buffer[cwords] << br->consumed_bits; /* keep unconsumed bits aligned to left */ + + while(val < end) { + /* read the unary MSBs and end bit */ + x = y = COUNT_ZERO_MSBS2(b); + if(x == FLAC__BITS_PER_WORD) { + x = ucbits; + do { + /* didn't find stop bit yet, have to keep going... */ + cwords++; + if (cwords >= words) + goto incomplete_msbs; + b = br->buffer[cwords]; + y = COUNT_ZERO_MSBS2(b); + x += y; + } while(y == FLAC__BITS_PER_WORD); + } + b <<= y; + b <<= 1; /* account for stop bit */ + ucbits = (ucbits - x - 1) % FLAC__BITS_PER_WORD; + msbs = x; + + if(x > limit) + return false; + + /* read the binary LSBs */ + x = (FLAC__uint32)(b >> (FLAC__BITS_PER_WORD - parameter)); /* parameter < 32, so we can cast to 32-bit uint32_t */ + if(parameter <= ucbits) { + ucbits -= parameter; + b <<= parameter; + } else { + /* there are still bits left to read, they will all be in the next word */ + cwords++; + if (cwords >= words) + goto incomplete_lsbs; + b = br->buffer[cwords]; + ucbits += FLAC__BITS_PER_WORD - parameter; + x |= (FLAC__uint32)(b >> ucbits); + b <<= FLAC__BITS_PER_WORD - ucbits; + } + lsbs = x; + + /* compose the value */ + x = (msbs << parameter) | lsbs; + *val++ = (int)(x >> 1) ^ -(int)(x & 1); + + continue; + + /* at this point we've eaten up all the whole words */ +process_tail: + do { + if(0) { +incomplete_msbs: + br->consumed_bits = 0; + br->consumed_words = cwords; + } + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + msbs += x; + x = ucbits = 0; + + if(0) { +incomplete_lsbs: + br->consumed_bits = 0; + br->consumed_words = cwords; + } + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter - ucbits)) + return false; + lsbs = x | lsbs; + + /* compose the value */ + x = (msbs << parameter) | lsbs; + *val++ = (int)(x >> 1) ^ -(int)(x & 1); + x = 0; + + cwords = br->consumed_words; + words = br->words; + ucbits = FLAC__BITS_PER_WORD - br->consumed_bits; + b = cwords < br->capacity ? br->buffer[cwords] << br->consumed_bits : 0; + } while(cwords >= words && val < end); + } + + if(ucbits == 0 && cwords < words) { + /* don't leave the head word with no unconsumed bits */ + cwords++; + ucbits = FLAC__BITS_PER_WORD; + } + + br->consumed_bits = FLAC__BITS_PER_WORD - ucbits; + br->consumed_words = cwords; + + return true; +} diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/deduplication/lpc_compute_autocorrelation_intrin.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/deduplication/lpc_compute_autocorrelation_intrin.c new file mode 100644 index 00000000..76419db0 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/deduplication/lpc_compute_autocorrelation_intrin.c @@ -0,0 +1,14 @@ + int i, j; + (void) lag; + FLAC__ASSERT(lag <= MAX_LAG); + + for(i = 0; i < MAX_LAG; i++) + autoc[i] = 0.0; + + for(i = 0; i < MAX_LAG; i++) + for(j = 0; j <= i; j++) + autoc[j] += (double)data[i] * (double)data[i-j]; + + for(i = MAX_LAG; i < (int)data_len; i++) + for(j = 0; j < MAX_LAG; j++) + autoc[j] += (double)data[i] * (double)data[i-j]; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/deduplication/lpc_compute_autocorrelation_intrin_neon.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/deduplication/lpc_compute_autocorrelation_intrin_neon.c new file mode 100644 index 00000000..4df3aee9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/deduplication/lpc_compute_autocorrelation_intrin_neon.c @@ -0,0 +1,70 @@ + int i; + float64x2_t sum0 = vdupq_n_f64(0.0f); + float64x2_t sum1 = vdupq_n_f64(0.0f); + float64x2_t sum2 = vdupq_n_f64(0.0f); + float64x2_t sum3 = vdupq_n_f64(0.0f); + float64x2_t d0 = vdupq_n_f64(0.0f); + float64x2_t d1 = vdupq_n_f64(0.0f); + float64x2_t d2 = vdupq_n_f64(0.0f); + float64x2_t d3 = vdupq_n_f64(0.0f); +#if MAX_LAG > 8 + float64x2_t sum4 = vdupq_n_f64(0.0f); + float64x2_t d4 = vdupq_n_f64(0.0f); +#endif +#if MAX_LAG > 10 + float64x2_t sum5 = vdupq_n_f64(0.0f); + float64x2_t sum6 = vdupq_n_f64(0.0f); + float64x2_t d5 = vdupq_n_f64(0.0f); + float64x2_t d6 = vdupq_n_f64(0.0f); +#endif + float64x2_t d; + + (void)lag; + FLAC__ASSERT(lag <= MAX_LAG); + + // Loop backwards through samples from data_len to 0 + for (i = data_len - 1; i >= 0; i--) + { + d = vdupq_n_f64(data[i]); // Create vector with 2 entries data[i] + + // The next 6 lines of code right-shift the elements through the 7 vectors d0..d6. + // The 7th line adds the newly loaded element to d0. This works like a stack, where + // data[i] is pushed onto the stack every time and the 9th element falls off +#if MAX_LAG > 10 + d6 = vextq_f64(d5,d6,1); + d5 = vextq_f64(d4,d5,1); +#endif +#if MAX_LAG > 8 + d4 = vextq_f64(d3,d4,1); +#endif + d3 = vextq_f64(d2,d3,1); + d2 = vextq_f64(d1,d2,1); + d1 = vextq_f64(d0,d1,1); + d0 = vextq_f64(d,d0,1); + + // Fused multiply-add sum += d * d0..d6 + sum0 = vfmaq_f64(sum0, d, d0); + sum1 = vfmaq_f64(sum1, d, d1); + sum2 = vfmaq_f64(sum2, d, d2); + sum3 = vfmaq_f64(sum3, d, d3); +#if MAX_LAG > 8 + sum4 = vfmaq_f64(sum4, d, d4); +#endif +#if MAX_LAG > 10 + sum5 = vfmaq_f64(sum5, d, d5); + sum6 = vfmaq_f64(sum6, d, d6); +#endif + } + + // Store sum0..sum6 in autoc[0..14] + vst1q_f64(autoc, sum0); + vst1q_f64(autoc + 2, sum1); + vst1q_f64(autoc + 4, sum2); + vst1q_f64(autoc + 6, sum3); +#if MAX_LAG > 8 + vst1q_f64(autoc + 8, sum4); +#endif +#if MAX_LAG > 10 + vst1q_f64(autoc + 10, sum5); + vst1q_f64(autoc + 12, sum6); +#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c index d3576049..dfd9b7dd 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,12 +39,18 @@ #include "../compat.h" #include "include/private/bitmath.h" #include "include/private/fixed.h" + #include "../assert.h" #ifdef local_abs #undef local_abs #endif -#define local_abs(x) ((unsigned)((x)<0? -(x) : (x))) +#define local_abs(x) ((uint32_t)((x)<0? -(x) : (x))) + +#ifdef local_abs64 +#undef local_abs64 +#endif +#define local_abs64(x) ((uint64_t)((x)<0? -(x) : (x))) #ifdef FLAC__INTEGER_ONLY_LIBRARY /* rbps stands for residual bits per sample @@ -56,7 +62,7 @@ static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__uint32 n) { FLAC__uint32 rbps; - unsigned bits; /* the number of bits required to represent a number */ + uint32_t bits; /* the number of bits required to represent a number */ int fracbits; /* the number of bits of rbps that comprise the fractional part */ FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); @@ -104,7 +110,7 @@ static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__ } } - rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); + rbps = FLAC__fixedpoint_log2(rbps, fracbits, (uint32_t)(-1)); if(rbps == 0) return 0; @@ -135,7 +141,7 @@ static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__ static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, FLAC__uint32 n) { FLAC__uint32 rbps; - unsigned bits; /* the number of bits required to represent a number */ + uint32_t bits; /* the number of bits required to represent a number */ int fracbits; /* the number of bits of rbps that comprise the fractional part */ FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); @@ -183,7 +189,7 @@ static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, F } } - rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); + rbps = FLAC__fixedpoint_log2(rbps, fracbits, (uint32_t)(-1)); if(rbps == 0) return 0; @@ -213,19 +219,28 @@ static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, F #endif #ifndef FLAC__INTEGER_ONLY_LIBRARY -unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +uint32_t FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) #else -unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +uint32_t FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) #endif { + FLAC__uint32 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + uint32_t order; +#if 0 + /* This code has been around a long time, and was written when compilers weren't able + * to vectorize code. These days, compilers are better in optimizing the next block + * which is also much more readable + */ FLAC__int32 last_error_0 = data[-1]; FLAC__int32 last_error_1 = data[-1] - data[-2]; FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); FLAC__int32 error, save; - FLAC__uint32 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; - unsigned i, order; - + uint32_t i; + /* total_error_* are 64-bits to avoid overflow when encoding + * erratic signals when the bits-per-sample and blocksize are + * large. + */ for(i = 0; i < data_len; i++) { error = data[i] ; total_error_0 += local_abs(error); save = error; error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; @@ -233,14 +248,26 @@ unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned d error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; } +#else + int i; + for(i = 0; i < (int)data_len; i++) { + total_error_0 += local_abs(data[i]); + total_error_1 += local_abs(data[i] - data[i-1]); + total_error_2 += local_abs(data[i] - 2 * data[i-1] + data[i-2]); + total_error_3 += local_abs(data[i] - 3 * data[i-1] + 3 * data[i-2] - data[i-3]); + total_error_4 += local_abs(data[i] - 4 * data[i-1] + 6 * data[i-2] - 4 * data[i-3] + data[i-4]); + } +#endif - if(total_error_0 < flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) + + /* prefer lower order */ + if(total_error_0 <= flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) order = 0; - else if(total_error_1 < flac_min(flac_min(total_error_2, total_error_3), total_error_4)) + else if(total_error_1 <= flac_min(flac_min(total_error_2, total_error_3), total_error_4)) order = 1; - else if(total_error_2 < flac_min(total_error_3, total_error_4)) + else if(total_error_2 <= flac_min(total_error_3, total_error_4)) order = 2; - else if(total_error_3 < total_error_4) + else if(total_error_3 <= total_error_4) order = 3; else order = 4; @@ -254,11 +281,11 @@ unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned d FLAC__ASSERT(data_len > 0 || total_error_3 == 0); FLAC__ASSERT(data_len > 0 || total_error_4 == 0); #ifndef FLAC__INTEGER_ONLY_LIBRARY - residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (double)total_error_0 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (double)total_error_1 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (double)total_error_2 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (double)total_error_3 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (double)total_error_4 / (double)data_len) / M_LN2 : 0.0); #else residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_integerized(total_error_0, data_len) : 0; residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_integerized(total_error_1, data_len) : 0; @@ -271,38 +298,31 @@ unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned d } #ifndef FLAC__INTEGER_ONLY_LIBRARY -unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +uint32_t FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) #else -unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +uint32_t FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) #endif { - FLAC__int32 last_error_0 = data[-1]; - FLAC__int32 last_error_1 = data[-1] - data[-2]; - FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); - FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); - FLAC__int32 error, save; - /* total_error_* are 64-bits to avoid overflow when encoding - * erratic signals when the bits-per-sample and blocksize are - * large. - */ FLAC__uint64 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; - unsigned i, order; + uint32_t order; + int i; - for(i = 0; i < data_len; i++) { - error = data[i] ; total_error_0 += local_abs(error); save = error; - error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; - error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; - error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; - error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; + for(i = 0; i < (int)data_len; i++) { + total_error_0 += local_abs(data[i]); + total_error_1 += local_abs(data[i] - data[i-1]); + total_error_2 += local_abs(data[i] - 2 * data[i-1] + data[i-2]); + total_error_3 += local_abs(data[i] - 3 * data[i-1] + 3 * data[i-2] - data[i-3]); + total_error_4 += local_abs(data[i] - 4 * data[i-1] + 6 * data[i-2] - 4 * data[i-3] + data[i-4]); } - if(total_error_0 < flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) + /* prefer lower order */ + if(total_error_0 <= flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) order = 0; - else if(total_error_1 < flac_min(flac_min(total_error_2, total_error_3), total_error_4)) + else if(total_error_1 <= flac_min(flac_min(total_error_2, total_error_3), total_error_4)) order = 1; - else if(total_error_2 < flac_min(total_error_3, total_error_4)) + else if(total_error_2 <= flac_min(total_error_3, total_error_4)) order = 2; - else if(total_error_3 < total_error_4) + else if(total_error_3 <= total_error_4) order = 3; else order = 4; @@ -316,11 +336,11 @@ unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsig FLAC__ASSERT(data_len > 0 || total_error_3 == 0); FLAC__ASSERT(data_len > 0 || total_error_4 == 0); #ifndef FLAC__INTEGER_ONLY_LIBRARY - residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); - residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (double)total_error_0 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (double)total_error_1 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (double)total_error_2 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (double)total_error_3 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (double)total_error_4 / (double)data_len) / M_LN2 : 0.0); #else residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_wide_integerized(total_error_0, data_len) : 0; residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_wide_integerized(total_error_1, data_len) : 0; @@ -332,7 +352,122 @@ unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsig return order; } -void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]) +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#define CHECK_ORDER_IS_VALID(macro_order) \ +if(order_##macro_order##_is_valid && total_error_##macro_order < smallest_error) { \ + order = macro_order; \ + smallest_error = total_error_##macro_order ; \ + residual_bits_per_sample[ macro_order ] = (float)((total_error_0 > 0) ? log(M_LN2 * (double)total_error_0 / (double)data_len) / M_LN2 : 0.0); \ +} \ +else \ + residual_bits_per_sample[ macro_order ] = 34.0f; +#else +#define CHECK_ORDER_IS_VALID(macro_order) \ +if(order_##macro_order##_is_valid && total_error_##macro_order < smallest_error) { \ + order = macro_order; \ + smallest_error = total_error_##macro_order ; \ + residual_bits_per_sample[ macro_order ] = (total_error_##macro_order > 0) ? local__compute_rbps_wide_integerized(total_error_##macro_order, data_len) : 0; \ +} \ +else \ + residual_bits_per_sample[ macro_order ] = 34 * FLAC__FP_ONE; +#endif + + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +uint32_t FLAC__fixed_compute_best_predictor_limit_residual(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#else +uint32_t FLAC__fixed_compute_best_predictor_limit_residual(const FLAC__int32 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#endif +{ + FLAC__uint64 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0, smallest_error = UINT64_MAX; + FLAC__uint64 error_0, error_1, error_2, error_3, error_4; + FLAC__bool order_0_is_valid = true, order_1_is_valid = true, order_2_is_valid = true, order_3_is_valid = true, order_4_is_valid = true; + uint32_t order = 0; + int i; + + for(i = -4; i < (int)data_len; i++) { + error_0 = local_abs64((FLAC__int64)data[i]); + error_1 = (i > -4) ? local_abs64((FLAC__int64)data[i] - data[i-1]) : 0 ; + error_2 = (i > -3) ? local_abs64((FLAC__int64)data[i] - 2 * (FLAC__int64)data[i-1] + data[i-2]) : 0; + error_3 = (i > -2) ? local_abs64((FLAC__int64)data[i] - 3 * (FLAC__int64)data[i-1] + 3 * (FLAC__int64)data[i-2] - data[i-3]) : 0; + error_4 = (i > -1) ? local_abs64((FLAC__int64)data[i] - 4 * (FLAC__int64)data[i-1] + 6 * (FLAC__int64)data[i-2] - 4 * (FLAC__int64)data[i-3] + data[i-4]) : 0; + + total_error_0 += error_0; + total_error_1 += error_1; + total_error_2 += error_2; + total_error_3 += error_3; + total_error_4 += error_4; + + /* residual must not be INT32_MIN because abs(INT32_MIN) is undefined */ + if(error_0 > INT32_MAX) + order_0_is_valid = false; + if(error_1 > INT32_MAX) + order_1_is_valid = false; + if(error_2 > INT32_MAX) + order_2_is_valid = false; + if(error_3 > INT32_MAX) + order_3_is_valid = false; + if(error_4 > INT32_MAX) + order_4_is_valid = false; + } + + CHECK_ORDER_IS_VALID(0); + CHECK_ORDER_IS_VALID(1); + CHECK_ORDER_IS_VALID(2); + CHECK_ORDER_IS_VALID(3); + CHECK_ORDER_IS_VALID(4); + + return order; +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +uint32_t FLAC__fixed_compute_best_predictor_limit_residual_33bit(const FLAC__int64 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#else +uint32_t FLAC__fixed_compute_best_predictor_limit_residual_33bit(const FLAC__int64 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#endif +{ + FLAC__uint64 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0, smallest_error = UINT64_MAX; + FLAC__uint64 error_0, error_1, error_2, error_3, error_4; + FLAC__bool order_0_is_valid = true, order_1_is_valid = true, order_2_is_valid = true, order_3_is_valid = true, order_4_is_valid = true; + uint32_t order = 0; + int i; + + for(i = -4; i < (int)data_len; i++) { + error_0 = local_abs64(data[i]); + error_1 = (i > -4) ? local_abs64(data[i] - data[i-1]) : 0 ; + error_2 = (i > -3) ? local_abs64(data[i] - 2 * data[i-1] + data[i-2]) : 0; + error_3 = (i > -2) ? local_abs64(data[i] - 3 * data[i-1] + 3 * data[i-2] - data[i-3]) : 0; + error_4 = (i > -1) ? local_abs64(data[i] - 4 * data[i-1] + 6 * data[i-2] - 4 * data[i-3] + data[i-4]) : 0; + + total_error_0 += error_0; + total_error_1 += error_1; + total_error_2 += error_2; + total_error_3 += error_3; + total_error_4 += error_4; + + /* residual must not be INT32_MIN because abs(INT32_MIN) is undefined */ + if(error_0 > INT32_MAX) + order_0_is_valid = false; + if(error_1 > INT32_MAX) + order_1_is_valid = false; + if(error_2 > INT32_MAX) + order_2_is_valid = false; + if(error_3 > INT32_MAX) + order_3_is_valid = false; + if(error_4 > INT32_MAX) + order_4_is_valid = false; + } + + CHECK_ORDER_IS_VALID(0); + CHECK_ORDER_IS_VALID(1); + CHECK_ORDER_IS_VALID(2); + CHECK_ORDER_IS_VALID(3); + CHECK_ORDER_IS_VALID(4); + + return order; +} + +void FLAC__fixed_compute_residual(const FLAC__int32 data[], uint32_t data_len, uint32_t order, FLAC__int32 residual[]) { const int idata_len = (int)data_len; int i; @@ -348,34 +483,92 @@ void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, u break; case 2: for(i = 0; i < idata_len; i++) -#if 1 /* OPT: may be faster with some compilers on some systems */ - residual[i] = data[i] - (data[i-1] << 1) + data[i-2]; -#else residual[i] = data[i] - 2*data[i-1] + data[i-2]; -#endif break; case 3: for(i = 0; i < idata_len; i++) -#if 1 /* OPT: may be faster with some compilers on some systems */ - residual[i] = data[i] - (((data[i-1]-data[i-2])<<1) + (data[i-1]-data[i-2])) - data[i-3]; -#else residual[i] = data[i] - 3*data[i-1] + 3*data[i-2] - data[i-3]; -#endif break; case 4: for(i = 0; i < idata_len; i++) -#if 1 /* OPT: may be faster with some compilers on some systems */ - residual[i] = data[i] - ((data[i-1]+data[i-3])<<2) + ((data[i-2]<<2) + (data[i-2]<<1)) + data[i-4]; -#else residual[i] = data[i] - 4*data[i-1] + 6*data[i-2] - 4*data[i-3] + data[i-4]; -#endif break; default: FLAC__ASSERT(0); } } -void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]) +void FLAC__fixed_compute_residual_wide(const FLAC__int32 data[], uint32_t data_len, uint32_t order, FLAC__int32 residual[]) +{ + const int idata_len = (int)data_len; + int i; + + switch(order) { + case 0: + FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); + memcpy(residual, data, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + residual[i] = (FLAC__int64)data[i] - data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) + residual[i] = (FLAC__int64)data[i] - 2*(FLAC__int64)data[i-1] + data[i-2]; + break; + case 3: + for(i = 0; i < idata_len; i++) + residual[i] = (FLAC__int64)data[i] - 3*(FLAC__int64)data[i-1] + 3*(FLAC__int64)data[i-2] - data[i-3]; + break; + case 4: + for(i = 0; i < idata_len; i++) + residual[i] = (FLAC__int64)data[i] - 4*(FLAC__int64)data[i-1] + 6*(FLAC__int64)data[i-2] - 4*(FLAC__int64)data[i-3] + data[i-4]; + break; + default: + FLAC__ASSERT(0); + } +} + +void FLAC__fixed_compute_residual_wide_33bit(const FLAC__int64 data[], uint32_t data_len, uint32_t order, FLAC__int32 residual[]) +{ + const int idata_len = (int)data_len; + int i; + + switch(order) { + case 0: + for(i = 0; i < idata_len; i++) + residual[i] = data[i]; + break; + case 1: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - 2*data[i-1] + data[i-2]; + break; + case 3: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - 3*data[i-1] + 3*data[i-2] - data[i-3]; + break; + case 4: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - 4*data[i-1] + 6*data[i-2] - 4*data[i-3] + data[i-4]; + break; + default: + FLAC__ASSERT(0); + } +} + +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && !defined(FUZZING_BUILD_MODE_FLAC_SANITIZE_SIGNED_INTEGER_OVERFLOW) +/* The attribute below is to silence the undefined sanitizer of oss-fuzz. + * Because fuzzing feeds bogus predictors and residual samples to the + * decoder, having overflows in this section is unavoidable. Also, + * because the calculated values are audio path only, there is no + * potential for security problems */ +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], uint32_t data_len, uint32_t order, FLAC__int32 data[]) { int i, idata_len = (int)data_len; @@ -390,27 +583,83 @@ void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, break; case 2: for(i = 0; i < idata_len; i++) -#if 1 /* OPT: may be faster with some compilers on some systems */ - data[i] = residual[i] + (data[i-1]<<1) - data[i-2]; -#else data[i] = residual[i] + 2*data[i-1] - data[i-2]; -#endif break; case 3: for(i = 0; i < idata_len; i++) -#if 1 /* OPT: may be faster with some compilers on some systems */ - data[i] = residual[i] + (((data[i-1]-data[i-2])<<1) + (data[i-1]-data[i-2])) + data[i-3]; -#else data[i] = residual[i] + 3*data[i-1] - 3*data[i-2] + data[i-3]; -#endif break; case 4: for(i = 0; i < idata_len; i++) -#if 1 /* OPT: may be faster with some compilers on some systems */ - data[i] = residual[i] + ((data[i-1]+data[i-3])<<2) - ((data[i-2]<<2) + (data[i-2]<<1)) - data[i-4]; -#else data[i] = residual[i] + 4*data[i-1] - 6*data[i-2] + 4*data[i-3] - data[i-4]; + break; + default: + FLAC__ASSERT(0); + } +} + +void FLAC__fixed_restore_signal_wide(const FLAC__int32 residual[], uint32_t data_len, uint32_t order, FLAC__int32 data[]) +{ + int i, idata_len = (int)data_len; + + switch(order) { + case 0: + FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); + memcpy(data, residual, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + data[i] = (FLAC__int64)residual[i] + (FLAC__int64)data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) + data[i] = (FLAC__int64)residual[i] + 2*(FLAC__int64)data[i-1] - (FLAC__int64)data[i-2]; + break; + case 3: + for(i = 0; i < idata_len; i++) + data[i] = (FLAC__int64)residual[i] + 3*(FLAC__int64)data[i-1] - 3*(FLAC__int64)data[i-2] + (FLAC__int64)data[i-3]; + break; + case 4: + for(i = 0; i < idata_len; i++) + data[i] = (FLAC__int64)residual[i] + 4*(FLAC__int64)data[i-1] - 6*(FLAC__int64)data[i-2] + 4*(FLAC__int64)data[i-3] - (FLAC__int64)data[i-4]; + break; + default: + FLAC__ASSERT(0); + } +} + +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && !defined(FUZZING_BUILD_MODE_FLAC_SANITIZE_SIGNED_INTEGER_OVERFLOW) +/* The attribute below is to silence the undefined sanitizer of oss-fuzz. + * Because fuzzing feeds bogus predictors and residual samples to the + * decoder, having overflows in this section is unavoidable. Also, + * because the calculated values are audio path only, there is no + * potential for security problems */ +__attribute__((no_sanitize("signed-integer-overflow"))) #endif +void FLAC__fixed_restore_signal_wide_33bit(const FLAC__int32 residual[], uint32_t data_len, uint32_t order, FLAC__int64 data[]) +{ + int i, idata_len = (int)data_len; + + switch(order) { + case 0: + for(i = 0; i < idata_len; i++) + data[i] = residual[i]; + break; + case 1: + for(i = 0; i < idata_len; i++) + data[i] = (FLAC__int64)residual[i] + data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) + data[i] = (FLAC__int64)residual[i] + 2*data[i-1] - data[i-2]; + break; + case 3: + for(i = 0; i < idata_len; i++) + data[i] = (FLAC__int64)residual[i] + 3*data[i-1] - 3*data[i-2] + data[i-3]; + break; + case 4: + for(i = 0; i < idata_len; i++) + data[i] = (FLAC__int64)residual[i] + 4*data[i-1] - 6*data[i-2] + 4*data[i-3] - data[i-4]; break; default: FLAC__ASSERT(0); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/float.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/float.c index 4ddebf6e..0d02f344 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/float.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/float.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2004-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -266,7 +266,7 @@ static const FLAC__uint64 log2_lookup_wide[] = { }; #endif -FLAC__uint32 FLAC__fixedpoint_log2(FLAC__uint32 x, unsigned fracbits, unsigned precision) +FLAC__uint32 FLAC__fixedpoint_log2(FLAC__uint32 x, uint32_t fracbits, uint32_t precision) { const FLAC__uint32 ONE = (1u << fracbits); const FLAC__uint32 *table = log2_lookup[fracbits >> 2]; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/format.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/format.c index 7db77156..8f72d9c8 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/format.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/format.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,108 +39,119 @@ #include /* for memset() */ #include "../assert.h" #include "../format.h" +#include "../alloc.h" #include "../compat.h" #include "include/private/format.h" -/* VERSION should come from configure */ -FLAC_API const char *FLAC__VERSION_STRING = VERSION; -FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " VERSION " 20141125"; +#if (defined GIT_COMMIT_HASH && defined GIT_COMMIT_DATE) +# ifdef GIT_COMMIT_TAG +FLAC_API const char *FLAC__VERSION_STRING = GIT_COMMIT_TAG; +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " GIT_COMMIT_TAG " " GIT_COMMIT_DATE; +# else +FLAC_API const char *FLAC__VERSION_STRING = "git-" GIT_COMMIT_HASH " " GIT_COMMIT_DATE; +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC git-" GIT_COMMIT_HASH " " GIT_COMMIT_DATE; +# endif +#else +/* PACKAGE_VERSION should come from configure */ +FLAC_API const char *FLAC__VERSION_STRING = PACKAGE_VERSION; +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " PACKAGE_VERSION " 20230623"; +#endif FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' }; -FLAC_API const unsigned FLAC__STREAM_SYNC = 0x664C6143; -FLAC_API const unsigned FLAC__STREAM_SYNC_LEN = 32; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_SYNC = 0x664C6143; +FLAC_API const uint32_t FLAC__STREAM_SYNC_LEN = 32; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN = 32; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_APPLICATION_ID_LEN = 32; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__U64L(0xffffffffffffffff); -FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = 32; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = 32; /* bits */ - -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = 64; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = 8; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = 3*8; /* bits */ - -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = 64; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = 8; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = 12*8; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = 1; /* bit */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = 1; /* bit */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = 6+13*8; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = 8; /* bits */ - -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = 128*8; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = 64; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = 1; /* bit */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = 7+258*8; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = 8; /* bits */ - -FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = 32; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = 32; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = 32; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = 32; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = 32; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = 32; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = 32; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = 32; /* bits */ - -FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN = 1; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN = 7; /* bits */ -FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN = 24; /* bits */ - -FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC = 0x3ffe; -FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN = 14; /* bits */ -FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN = 1; /* bits */ -FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN = 1; /* bits */ -FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = 4; /* bits */ -FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = 4; /* bits */ -FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = 4; /* bits */ -FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = 3; /* bits */ -FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN = 1; /* bits */ -FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN = 8; /* bits */ - -FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN = 16; /* bits */ - -FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = 2; /* bits */ -FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = 4; /* bits */ -FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = 4; /* bits */ -FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN = 5; /* bits */ -FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = 5; /* bits */ - -FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = 15; /* == (1< FLAC__MAX_SAMPLE_RATE) { + if(sample_rate > FLAC__MAX_SAMPLE_RATE) { return false; } else return true; } -FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(unsigned blocksize, unsigned sample_rate) +FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(uint32_t blocksize, uint32_t sample_rate) { if(blocksize > 16384) return false; @@ -214,14 +225,12 @@ FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(unsigned blocksize, unsigne return true; } -FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate) +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(uint32_t sample_rate) { - if( - !FLAC__format_sample_rate_is_valid(sample_rate) || - ( - sample_rate >= (1u << 16) && - !(sample_rate % 1000 == 0 || sample_rate % 10 == 0) - ) + if( // sample rate is not subset if + !FLAC__format_sample_rate_is_valid(sample_rate) || // sample rate is invalid or + sample_rate >= ((1u << 16) * 10) || // sample rate is larger then or equal to 655360 or + (sample_rate >= (1u << 16) && sample_rate % 10 != 0) //sample rate is >= 65536 and not divisible by 10 ) { return false; } @@ -232,12 +241,15 @@ FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate) /* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table) { - unsigned i; + uint32_t i; FLAC__uint64 prev_sample_number = 0; FLAC__bool got_prev = false; FLAC__ASSERT(0 != seek_table); + if((FLAC__uint64)(seek_table->num_points) * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; + for(i = 0; i < seek_table->num_points; i++) { if(got_prev) { if( @@ -266,13 +278,16 @@ static int seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLA } /* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ -FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table) +FLAC_API uint32_t FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table) { - unsigned i, j; + uint32_t i, j; FLAC__bool first; FLAC__ASSERT(0 != seek_table); + if (seek_table->num_points == 0) + return 0; + /* sort the seekpoints */ qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_); @@ -304,7 +319,7 @@ FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *se * and a more clear explanation at the end of this section: * http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 */ -static unsigned utf8len_(const FLAC__byte *utf8) +static uint32_t utf8len_(const FLAC__byte *utf8) { FLAC__ASSERT(0 != utf8); if ((utf8[0] & 0x80) == 0) { @@ -354,11 +369,11 @@ FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *n return true; } -FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length) +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, uint32_t length) { - if(length == (unsigned)(-1)) { + if(length == (uint32_t)(-1)) { while(*value) { - unsigned n = utf8len_(value); + uint32_t n = utf8len_(value); if(n == 0) return false; value += n; @@ -367,7 +382,7 @@ FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__ else { const FLAC__byte *end = value + length; while(value < end) { - unsigned n = utf8len_(value); + uint32_t n = utf8len_(value); if(n == 0) return false; value += n; @@ -378,7 +393,7 @@ FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__ return true; } -FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length) +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, uint32_t length) { const FLAC__byte *s, *end; @@ -392,7 +407,7 @@ FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte * s++; /* skip '=' */ while(s < end) { - unsigned n = utf8len_(s); + uint32_t n = utf8len_(s); if(n == 0) return false; s += n; @@ -406,7 +421,7 @@ FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte * /* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation) { - unsigned i, j; + uint32_t i, j; if(check_cd_da_subset) { if(cue_sheet->lead_in < 2 * 44100) { @@ -496,7 +511,7 @@ FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Pic } for(b = picture->description; *b; ) { - unsigned n = utf8len_(b); + uint32_t n = utf8len_(b); if(n == 0) { if(violation) *violation = "description string must be valid UTF-8"; return false; @@ -510,7 +525,8 @@ FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Pic /* * These routines are private to libFLAC */ -unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order) +#if 0 /* UNUSED */ +uint32_t FLAC__format_get_max_rice_partition_order(uint32_t blocksize, uint32_t predictor_order) { return FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order( @@ -519,10 +535,11 @@ unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order ); } +#endif -unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned blocksize) +uint32_t FLAC__format_get_max_rice_partition_order_from_blocksize(uint32_t blocksize) { - unsigned max_rice_partition_order = 0; + uint32_t max_rice_partition_order = 0; while(!(blocksize & 1)) { max_rice_partition_order++; blocksize >>= 1; @@ -530,9 +547,9 @@ unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned block return flac_min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order); } -unsigned FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(unsigned limit, unsigned blocksize, unsigned predictor_order) +uint32_t FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(uint32_t limit, uint32_t blocksize, uint32_t predictor_order) { - unsigned max_rice_partition_order = limit; + uint32_t max_rice_partition_order = limit; while(max_rice_partition_order > 0 && (blocksize >> max_rice_partition_order) <= predictor_order) max_rice_partition_order--; @@ -565,20 +582,27 @@ void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__En FLAC__format_entropy_coding_method_partitioned_rice_contents_init(object); } -FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, unsigned max_partition_order) +#if defined(_MSC_VER) +// silence three MSVC warnings 'result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)' +#pragma warning ( disable : 4334 ) +#endif + +FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, uint32_t max_partition_order) { FLAC__ASSERT(0 != object); - FLAC__ASSERT(object->capacity_by_order > 0 || (0 == object->parameters && 0 == object->raw_bits)); - - if(object->capacity_by_order < max_partition_order) { - if(0 == (object->parameters = (unsigned int*) realloc(object->parameters, sizeof(unsigned)*(1 << max_partition_order)))) + if(object->capacity_by_order < max_partition_order || object->parameters == NULL || object->raw_bits == NULL) { + if(0 == (object->parameters = (uint32_t*) safe_realloc_(object->parameters, sizeof(uint32_t)*(1 << max_partition_order)))) return false; - if(0 == (object->raw_bits = (unsigned int*) realloc(object->raw_bits, sizeof(unsigned)*(1 << max_partition_order)))) + if(0 == (object->raw_bits = (uint32_t*) safe_realloc_(object->raw_bits, sizeof(uint32_t)*(1 << max_partition_order)))) return false; - memset(object->raw_bits, 0, sizeof(unsigned)*(1 << max_partition_order)); + memset(object->raw_bits, 0, sizeof(uint32_t)*(1 << max_partition_order)); object->capacity_by_order = max_partition_order; } return true; } + +#if defined(_MSC_VER) +#pragma warning ( default : 4334 ) +#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h deleted file mode 100644 index a4463d26..00000000 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h +++ /dev/null @@ -1,50 +0,0 @@ -/* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of the Xiph.org Foundation nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FLAC__PRIVATE__ALL_H -#define FLAC__PRIVATE__ALL_H - -#include "bitmath.h" -#include "bitreader.h" -#include "bitwriter.h" -#include "cpu.h" -#include "crc.h" -#include "fixed.h" -#include "float.h" -#include "format.h" -#include "lpc.h" -#include "md5.h" -#include "memory.h" -#include "metadata.h" -#include "stream_encoder_framing.h" - -#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h index 05db8b45..c2d8ee1e 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,69 +36,98 @@ #include "../../../ordinals.h" #include "../../../assert.h" -/* for CHAR_BIT */ -#include #include "../../../compat.h" -#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#if defined(_MSC_VER) #include /* for _BitScanReverse* */ #endif /* Will never be emitted for MSVC, GCC, Intel compilers */ -static inline unsigned int FLAC__clz_soft_uint32(unsigned int word) +static inline uint32_t FLAC__clz_soft_uint32(FLAC__uint32 word) { - static const unsigned char byte_to_unary_table[] = { - 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; - - return (word) > 0xffffff ? byte_to_unary_table[(word) >> 24] : - (word) > 0xffff ? byte_to_unary_table[(word) >> 16] + 8 : - (word) > 0xff ? byte_to_unary_table[(word) >> 8] + 16 : - byte_to_unary_table[(word)] + 24; + static const uint8_t byte_to_unary_table[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + return word > 0xffffff ? byte_to_unary_table[word >> 24] : + word > 0xffff ? byte_to_unary_table[word >> 16] + 8 : + word > 0xff ? byte_to_unary_table[word >> 8] + 16 : + byte_to_unary_table[word] + 24; } -static inline unsigned int FLAC__clz_uint32(FLAC__uint32 v) +static inline uint32_t FLAC__clz_uint32(FLAC__uint32 v) { /* Never used with input 0 */ - FLAC__ASSERT(v > 0); + FLAC__ASSERT(v > 0); #if defined(__INTEL_COMPILER) - return _bit_scan_reverse(v) ^ 31U; + return _bit_scan_reverse(v) ^ 31U; #elif defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) /* This will translate either to (bsr ^ 31U), clz , ctlz, cntlz, lzcnt depending on * -march= setting or to a software routine in exotic machines. */ - return __builtin_clz(v); -#elif defined(_MSC_VER) && (_MSC_VER >= 1400) - { - unsigned long idx; - _BitScanReverse(&idx, v); - return idx ^ 31U; - } + return __builtin_clz(v); +#elif defined(_MSC_VER) + { + uint32_t idx; + _BitScanReverse((unsigned long*) &idx, v); + return idx ^ 31U; + } +#else + return FLAC__clz_soft_uint32(v); +#endif +} + +/* Used when 64-bit bsr/clz is unavailable; can use 32-bit bsr/clz when possible */ +static inline uint32_t FLAC__clz_soft_uint64(FLAC__uint64 word) +{ + return (FLAC__uint32)(word>>32) ? FLAC__clz_uint32((FLAC__uint32)(word>>32)) : + FLAC__clz_uint32((FLAC__uint32)word) + 32; +} + +static inline uint32_t FLAC__clz_uint64(FLAC__uint64 v) +{ + /* Never used with input 0 */ + FLAC__ASSERT(v > 0); +#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) + return __builtin_clzll(v); +#elif (defined(__INTEL_COMPILER) || defined(_MSC_VER)) && (defined(_M_IA64) || defined(_M_X64)) + { + uint32_t idx; + _BitScanReverse64((unsigned long*) &idx, v); + return idx ^ 63U; + } #else - return FLAC__clz_soft_uint32(v); + return FLAC__clz_soft_uint64(v); #endif } -/* This one works with input 0 */ -static inline unsigned int FLAC__clz2_uint32(FLAC__uint32 v) +/* These two functions work with input 0 */ +static inline uint32_t FLAC__clz2_uint32(FLAC__uint32 v) +{ + if (!v) + return 32; + return FLAC__clz_uint32(v); +} + +static inline uint32_t FLAC__clz2_uint64(FLAC__uint64 v) { - if (!v) - return 32; - return FLAC__clz_uint32(v); + if (!v) + return 64; + return FLAC__clz_uint64(v); } /* An example of what FLAC__bitmath_ilog2() computes: @@ -124,63 +153,58 @@ static inline unsigned int FLAC__clz2_uint32(FLAC__uint32 v) * ilog2(18) = 4 */ -static inline unsigned FLAC__bitmath_ilog2(FLAC__uint32 v) +static inline uint32_t FLAC__bitmath_ilog2(FLAC__uint32 v) { - FLAC__ASSERT(v > 0); + FLAC__ASSERT(v > 0); #if defined(__INTEL_COMPILER) - return _bit_scan_reverse(v); -#elif defined(_MSC_VER) && (_MSC_VER >= 1400) - { - unsigned long idx; - _BitScanReverse(&idx, v); - return idx; - } + return _bit_scan_reverse(v); +#elif defined(_MSC_VER) + { + uint32_t idx; + _BitScanReverse((unsigned long*) &idx, v); + return idx; + } #else - return sizeof(FLAC__uint32) * CHAR_BIT - 1 - FLAC__clz_uint32(v); + return FLAC__clz_uint32(v) ^ 31U; #endif } - -#ifdef FLAC__INTEGER_ONLY_LIBRARY /* Unused otherwise */ - -static inline unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v) +static inline uint32_t FLAC__bitmath_ilog2_wide(FLAC__uint64 v) { - FLAC__ASSERT(v > 0); + FLAC__ASSERT(v > 0); #if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) - return sizeof(FLAC__uint64) * CHAR_BIT - 1 - __builtin_clzll(v); + return __builtin_clzll(v) ^ 63U; /* Sorry, only supported in x64/Itanium.. and both have fast FPU which makes integer-only encoder pointless */ -#elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && (defined(_M_IA64) || defined(_M_X64)) - { - unsigned long idx; - _BitScanReverse64(&idx, v); - return idx; - } +#elif (defined(__INTEL_COMPILER) || defined(_MSC_VER)) && (defined(_M_IA64) || defined(_M_X64)) + { + uint32_t idx; + _BitScanReverse64((unsigned long*) &idx, v); + return idx; + } #else /* Brain-damaged compilers will use the fastest possible way that is, - de Bruijn sequences (http://supertech.csail.mit.edu/papers/debruijn.pdf) - (C) Timothy B. Terriberry (tterribe@xiph.org) 2001-2009 CC0 (Public domain). + de Bruijn sequences (http://supertech.csail.mit.edu/papers/debruijn.pdf) + (C) Timothy B. Terriberry (tterribe@xiph.org) 2001-2009 CC0 (Public domain). */ - { - static const unsigned char DEBRUIJN_IDX64[64]={ - 0, 1, 2, 7, 3,13, 8,19, 4,25,14,28, 9,34,20,40, - 5,17,26,38,15,46,29,48,10,31,35,54,21,50,41,57, - 63, 6,12,18,24,27,33,39,16,37,45,47,30,53,49,56, - 62,11,23,32,36,44,52,55,61,22,43,51,60,42,59,58 - }; - v|= v>>1; - v|= v>>2; - v|= v>>4; - v|= v>>8; - v|= v>>16; - v|= v>>32; - v= (v>>1)+1; - return DEBRUIJN_IDX64[v*0x218A392CD3D5DBF>>58&0x3F]; - } + { + static const uint8_t DEBRUIJN_IDX64[64]={ + 0, 1, 2, 7, 3,13, 8,19, 4,25,14,28, 9,34,20,40, + 5,17,26,38,15,46,29,48,10,31,35,54,21,50,41,57, + 63, 6,12,18,24,27,33,39,16,37,45,47,30,53,49,56, + 62,11,23,32,36,44,52,55,61,22,43,51,60,42,59,58 + }; + v|= v>>1; + v|= v>>2; + v|= v>>4; + v|= v>>8; + v|= v>>16; + v|= v>>32; + v= (v>>1)+1; + return DEBRUIJN_IDX64[v*FLAC__U64L(0x218A392CD3D5DBF)>>58&0x3F]; + } #endif } -#endif -unsigned FLAC__bitmath_silog2(int v); -unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v); +uint32_t FLAC__bitmath_silog2(FLAC__int64 v); #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h index 2e9a4aab..fd68a706 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -53,7 +53,8 @@ void FLAC__bitreader_delete(FLAC__BitReader *br); FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__BitReaderReadCallback rcb, void *cd); void FLAC__bitreader_free(FLAC__BitReader *br); /* does not 'free(br)' */ FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br); -void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out); +void FLAC__bitreader_set_framesync_location(FLAC__BitReader *br); +FLAC__bool FLAC__bitreader_rewind_to_after_last_seen_framesync(FLAC__BitReader *br); /* * CRC functions @@ -65,27 +66,36 @@ FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br); * info functions */ FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); -unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); -unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); +uint32_t FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); +uint32_t FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); +void FLAC__bitreader_set_limit(FLAC__BitReader *br, uint32_t limit); +void FLAC__bitreader_remove_limit(FLAC__BitReader *br); +uint32_t FLAC__bitreader_limit_remaining(FLAC__BitReader *br); +void FLAC__bitreader_limit_invalidate(FLAC__BitReader *br); /* * read functions */ -FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits); -FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, unsigned bits); -FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, uint32_t bits); +FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, uint32_t bits); +FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, uint32_t bits); +FLAC__bool FLAC__bitreader_read_raw_int64(FLAC__BitReader *br, FLAC__int64 *val, uint32_t bits); FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); /*only for bits=32*/ -FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits); /* WATCHOUT: does not CRC the skipped data! */ /*@@@@ add to unit tests */ -FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ -FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ -FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val); -FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsigned parameter); -FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); +FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, uint32_t bits); /* WATCHOUT: does not CRC the skipped data! */ /*@@@@ add to unit tests */ +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, uint32_t nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, uint32_t nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, uint32_t *val); +FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, uint32_t parameter); +FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], uint32_t nvals, uint32_t parameter); +#ifdef FLAC__BMI2_SUPPORTED +FLAC__bool FLAC__bitreader_read_rice_signed_block_bmi2(FLAC__BitReader *br, int vals[], uint32_t nvals, uint32_t parameter); +#endif + #if 0 /* UNUSED */ -FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter); -FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter); +FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, uint32_t parameter); +FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, uint32_t *val, uint32_t parameter); #endif -FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen); -FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen); +FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, uint32_t *rawlen); +FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, uint32_t *rawlen); #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h index 706b64f8..49f6d053 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -50,7 +50,6 @@ void FLAC__bitwriter_delete(FLAC__BitWriter *bw); FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw); void FLAC__bitwriter_free(FLAC__BitWriter *bw); /* does not 'free(buffer)' */ void FLAC__bitwriter_clear(FLAC__BitWriter *bw); -void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out); /* * CRC functions @@ -64,7 +63,7 @@ FLAC__bool FLAC__bitwriter_get_write_crc8(FLAC__BitWriter *bw, FLAC__byte *crc); * info functions */ FLAC__bool FLAC__bitwriter_is_byte_aligned(const FLAC__BitWriter *bw); -unsigned FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw); /* can be called anytime, returns total # of bits unconsumed */ +uint32_t FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw); /* can be called anytime, returns total # of bits unconsumed */ /* * direct buffer access @@ -79,23 +78,24 @@ void FLAC__bitwriter_release_buffer(FLAC__BitWriter *bw); /* * write functions */ -FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits); -FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits); -FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits); -FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits); +FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, uint32_t bits); +FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, uint32_t bits); +FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, uint32_t bits); +FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, uint32_t bits); +FLAC__bool FLAC__bitwriter_write_raw_int64(FLAC__BitWriter *bw, FLAC__int64 val, uint32_t bits); FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val); /*only for bits=32*/ -FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals); -FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, unsigned val); -unsigned FLAC__bitwriter_rice_bits(FLAC__int32 val, unsigned parameter); +FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], uint32_t nvals); +FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, uint32_t val); #if 0 /* UNUSED */ -unsigned FLAC__bitwriter_golomb_bits_signed(int val, unsigned parameter); -unsigned FLAC__bitwriter_golomb_bits_unsigned(unsigned val, unsigned parameter); +uint32_t FLAC__bitwriter_rice_bits(FLAC__int32 val, uint32_t parameter); +uint32_t FLAC__bitwriter_golomb_bits_signed(int val, uint32_t parameter); +uint32_t FLAC__bitwriter_golomb_bits_unsigned(uint32_t val, uint32_t parameter); +FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, uint32_t parameter); #endif -FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, unsigned parameter); -FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FLAC__int32 *vals, unsigned nvals, unsigned parameter); +FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FLAC__int32 *vals, uint32_t nvals, uint32_t parameter); #if 0 /* UNUSED */ -FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, unsigned parameter); -FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned val, unsigned parameter); +FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, uint32_t parameter); +FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, uint32_t val, uint32_t parameter); #endif FLAC__bool FLAC__bitwriter_write_utf8_uint32(FLAC__BitWriter *bw, FLAC__uint32 val); FLAC__bool FLAC__bitwriter_write_utf8_uint64(FLAC__BitWriter *bw, FLAC__uint64 val); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h index 98dce0c3..1373d42e 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,14 +39,39 @@ #include #endif +#ifndef FLAC__CPU_X86_64 + +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define FLAC__CPU_X86_64 +#endif + +#endif + +#ifndef FLAC__CPU_IA32 + +#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86) +#define FLAC__CPU_IA32 +#endif + +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifndef FLAC__AVX_SUPPORTED +#define FLAC__AVX_SUPPORTED 0 +#endif + typedef enum { FLAC__CPUINFO_TYPE_IA32, FLAC__CPUINFO_TYPE_X86_64, FLAC__CPUINFO_TYPE_UNKNOWN } FLAC__CPUInfo_Type; -#if defined FLAC__CPU_IA32 typedef struct { + FLAC__bool intel; + FLAC__bool cmov; FLAC__bool mmx; FLAC__bool sse; @@ -59,41 +84,19 @@ typedef struct { FLAC__bool avx; FLAC__bool avx2; FLAC__bool fma; -} FLAC__CPUInfo_IA32; -#elif defined FLAC__CPU_X86_64 -typedef struct { - FLAC__bool sse3; - FLAC__bool ssse3; - FLAC__bool sse41; - FLAC__bool sse42; - FLAC__bool avx; - FLAC__bool avx2; - FLAC__bool fma; + FLAC__bool bmi2; } FLAC__CPUInfo_x86; -#endif typedef struct { FLAC__bool use_asm; FLAC__CPUInfo_Type type; -#if defined FLAC__CPU_IA32 - FLAC__CPUInfo_IA32 ia32; -#elif defined FLAC__CPU_X86_64 FLAC__CPUInfo_x86 x86; -#endif } FLAC__CPUInfo; void FLAC__cpu_info(FLAC__CPUInfo *info); -#ifndef FLAC__NO_ASM -# if defined FLAC__CPU_IA32 && defined FLAC__HAS_NASM FLAC__uint32 FLAC__cpu_have_cpuid_asm_ia32(void); -void FLAC__cpu_info_asm_ia32(FLAC__uint32 *flags_edx, FLAC__uint32 *flags_ecx); -# endif -# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN -FLAC__uint32 FLAC__cpu_have_cpuid_x86(void); -void FLAC__cpu_info_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx); -FLAC__uint32 FLAC__cpu_xgetbv_x86(void); -# endif -#endif + +void FLAC__cpu_info_asm_ia32(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx); #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h index 4d3de6fe..8ee1cf94 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,24 +39,22 @@ ** polynomial = x^8 + x^2 + x^1 + x^0 ** init = 0 */ -extern FLAC__byte const FLAC__crc8_table[256]; -#define FLAC__CRC8_UPDATE(data, crc) (crc) = FLAC__crc8_table[(crc) ^ (data)]; -void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc); -void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc); -FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len); +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, uint32_t len); /* 16 bit CRC generator, MSB shifted first ** polynomial = x^16 + x^15 + x^2 + x^0 ** init = 0 */ -extern unsigned const FLAC__crc16_table[256]; +extern FLAC__uint16 const FLAC__crc16_table[8][256]; -#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[((crc)>>8) ^ (data)]) +#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[0][((crc)>>8) ^ (data)]) /* this alternate may be faster on some systems/compilers */ #if 0 -#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) ^ FLAC__crc16_table[((crc)>>8) ^ (data)]) & 0xffff) +#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) ^ FLAC__crc16_table[0][((crc)>>8) ^ (data)]) & 0xffff) #endif -unsigned FLAC__crc16(const FLAC__byte *data, unsigned len); +FLAC__uint16 FLAC__crc16(const FLAC__byte *data, uint32_t len); +FLAC__uint16 FLAC__crc16_update_words32(const FLAC__uint32 *words, uint32_t len, FLAC__uint16 crc); +FLAC__uint16 FLAC__crc16_update_words64(const FLAC__uint64 *words, uint32_t len, FLAC__uint16 crc); #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h index 8b1ad931..aa6f7dfd 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -54,26 +54,32 @@ * OUT residual_bits_per_sample[0,FLAC__MAX_FIXED_ORDER] */ #ifndef FLAC__INTEGER_ONLY_LIBRARY -unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); -unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +uint32_t FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +uint32_t FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +uint32_t FLAC__fixed_compute_best_predictor_limit_residual(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +uint32_t FLAC__fixed_compute_best_predictor_limit_residual_33bit(const FLAC__int64 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); # ifndef FLAC__NO_ASM -# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN # ifdef FLAC__SSE2_SUPPORTED -unsigned FLAC__fixed_compute_best_predictor_intrin_sse2(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]); -unsigned FLAC__fixed_compute_best_predictor_wide_intrin_sse2(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]); +uint32_t FLAC__fixed_compute_best_predictor_intrin_sse2(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]); # endif # ifdef FLAC__SSSE3_SUPPORTED -unsigned FLAC__fixed_compute_best_predictor_intrin_ssse3(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); -unsigned FLAC__fixed_compute_best_predictor_wide_intrin_ssse3(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]); +uint32_t FLAC__fixed_compute_best_predictor_intrin_ssse3(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# endif +# ifdef FLAC__SSE4_2_SUPPORTED +uint32_t FLAC__fixed_compute_best_predictor_limit_residual_intrin_sse42(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# endif +# ifdef FLAC__AVX2_SUPPORTED +uint32_t FLAC__fixed_compute_best_predictor_wide_intrin_avx2(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +uint32_t FLAC__fixed_compute_best_predictor_limit_residual_intrin_avx2(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); # endif -# endif -# if defined FLAC__CPU_IA32 && defined FLAC__HAS_NASM -unsigned FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); # endif # endif #else -unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); -unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +uint32_t FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +uint32_t FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +uint32_t FLAC__fixed_compute_best_predictor_limit_residual(const FLAC__int32 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +uint32_t FLAC__fixed_compute_best_predictor_limit_residual_33bit(const FLAC__int64 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); #endif /* @@ -87,7 +93,9 @@ unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsig * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order * OUT residual[0,data_len-1] residual signal */ -void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]); +void FLAC__fixed_compute_residual(const FLAC__int32 data[], uint32_t data_len, uint32_t order, FLAC__int32 residual[]); +void FLAC__fixed_compute_residual_wide(const FLAC__int32 data[], uint32_t data_len, uint32_t order, FLAC__int32 residual[]); +void FLAC__fixed_compute_residual_wide_33bit(const FLAC__int64 data[], uint32_t data_len, uint32_t order, FLAC__int32 residual[]); /* * FLAC__fixed_restore_signal() @@ -102,6 +110,8 @@ void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, u * IN data[-order,-1] previously-reconstructed historical samples * OUT data[0,data_len-1] original signal */ -void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]); +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], uint32_t data_len, uint32_t order, FLAC__int32 data[]); +void FLAC__fixed_restore_signal_wide(const FLAC__int32 residual[], uint32_t data_len, uint32_t order, FLAC__int32 data[]); +void FLAC__fixed_restore_signal_wide_33bit(const FLAC__int32 residual[], uint32_t data_len, uint32_t order, FLAC__int64 data[]); #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h index 6c67af7d..e7ebb907 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2004-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,18 +40,15 @@ #include "../../../ordinals.h" /* - * These typedefs make it easier to ensure that integer versions of - * the library really only contain integer operations. All the code - * in libFLAC should use FLAC__float and FLAC__double in place of - * float and double, and be protected by checks of the macro + * All the code in libFLAC that uses float and double + * should be protected by checks of the macro * FLAC__INTEGER_ONLY_LIBRARY. * - * FLAC__real is the basic floating point type used in LPC analysis. */ #ifndef FLAC__INTEGER_ONLY_LIBRARY -typedef double FLAC__double; -typedef float FLAC__float; /* + * FLAC__real is the basic floating point type used in LPC analysis. + * * WATCHOUT: changing FLAC__real will change the signatures of many * functions that have assembly language equivalents and break them. */ @@ -84,14 +81,14 @@ extern const FLAC__fixedpoint FLAC__FP_E; * be < 32 and evenly divisible by 4 (0 is OK but not very precise). * * 'precision' roughly limits the number of iterations that are done; - * use (unsigned)(-1) for maximum precision. + * use (uint32_t)(-1) for maximum precision. * * If 'x' is less than one -- that is, x < (1< coefficients are all zero, which is bad. 'shift' is * unset. */ -int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift); +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], uint32_t order, uint32_t precision, FLAC__int32 qlp_coeff[], int *shift); /* * FLAC__lpc_compute_residual_from_qlp_coefficients() @@ -147,35 +152,38 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, * IN lp_quantization quantization of LP coefficients in bits * OUT residual[0,data_len-1] residual signal */ -void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); -void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +FLAC__bool FLAC__lpc_compute_residual_from_qlp_coefficients_limit_residual(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +FLAC__bool FLAC__lpc_compute_residual_from_qlp_coefficients_limit_residual_33bit(const FLAC__int64 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); #ifndef FLAC__NO_ASM -# ifdef FLAC__CPU_IA32 -# ifdef FLAC__HAS_NASM -void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); -void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); -void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_asm_ia32(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); -# endif -# endif -# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN +# ifdef FLAC__CPU_ARM64 +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_neon(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_neon(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +# endif + +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN # ifdef FLAC__SSE2_SUPPORTED -void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); -void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); # endif # ifdef FLAC__SSE4_1_SUPPORTED -void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse41(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); -void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_sse41(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse41(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_sse41(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); # endif # ifdef FLAC__AVX2_SUPPORTED -void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); -void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); -void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_avx2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_avx2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); # endif # endif #endif #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ +uint32_t FLAC__lpc_max_prediction_before_shift_bps(uint32_t subframe_bps, const FLAC__int32 qlp_coeff[], uint32_t order); +uint32_t FLAC__lpc_max_residual_bps(uint32_t subframe_bps, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization); + /* * FLAC__lpc_restore_signal() * -------------------------------------------------------------------- @@ -191,25 +199,9 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2(const FLA * IN data[-order,-1] previously-reconstructed historical samples * OUT data[0,data_len-1] original signal */ -void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); -void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); -#ifndef FLAC__NO_ASM -# ifdef FLAC__CPU_IA32 -# ifdef FLAC__HAS_NASM -void FLAC__lpc_restore_signal_asm_ia32(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); -void FLAC__lpc_restore_signal_asm_ia32_mmx(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); -void FLAC__lpc_restore_signal_wide_asm_ia32(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); -# endif /* FLAC__HAS_NASM */ -# endif /* FLAC__CPU_IA32 */ -# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN -# ifdef FLAC__SSE2_SUPPORTED -void FLAC__lpc_restore_signal_16_intrin_sse2(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); -# endif -# ifdef FLAC__SSE4_1_SUPPORTED -void FLAC__lpc_restore_signal_wide_intrin_sse41(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); -# endif -# endif -#endif /* FLAC__NO_ASM */ +void FLAC__lpc_restore_signal(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide_33bit(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int64 data[]); #ifndef FLAC__INTEGER_ONLY_LIBRARY @@ -223,8 +215,8 @@ void FLAC__lpc_restore_signal_wide_intrin_sse41(const FLAC__int32 residual[], un * IN total_samples > 0 # of samples in residual signal * RETURN expected bits per sample */ -FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample(FLAC__double lpc_error, unsigned total_samples); -FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(FLAC__double lpc_error, FLAC__double error_scale); +double FLAC__lpc_compute_expected_bits_per_residual_sample(double lpc_error, uint32_t total_samples); +double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(double lpc_error, double error_scale); /* * FLAC__lpc_compute_best_order() @@ -239,7 +231,7 @@ FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scal * (includes warmup sample size and quantized LP coefficient) * RETURN [1,max_order] best order */ -unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order); +uint32_t FLAC__lpc_compute_best_order(const double lpc_error[], uint32_t max_order, uint32_t total_samples, uint32_t overhead_bits_per_order); #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/md5.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/md5.h index 0af6ab55..2717536a 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/md5.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/md5.h @@ -45,6 +45,6 @@ typedef struct { void FLAC__MD5Init(FLAC__MD5Context *context); void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *context); -FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample); +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], uint32_t channels, uint32_t samples, uint32_t bytes_per_sample); #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h index de93c884..a9e0c2ef 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -48,8 +48,8 @@ void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address); FLAC__bool FLAC__memory_alloc_aligned_int32_array(size_t elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer); FLAC__bool FLAC__memory_alloc_aligned_uint32_array(size_t elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_int64_array(size_t elements, FLAC__int64 **unaligned_pointer, FLAC__int64 **aligned_pointer); FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer); -FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, unsigned **unaligned_pointer, unsigned **aligned_pointer); #ifndef FLAC__INTEGER_ONLY_LIBRARY FLAC__bool FLAC__memory_alloc_aligned_real_array(size_t elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer); #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder.h index 6a75d6de..7d62dd34 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -49,17 +49,17 @@ #ifdef FLAC__SSE2_SUPPORTED extern void FLAC__precompute_partition_info_sums_intrin_sse2(const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned residual_samples, unsigned predictor_order, unsigned min_partition_order, unsigned max_partition_order, unsigned bps); + uint32_t residual_samples, uint32_t predictor_order, uint32_t min_partition_order, uint32_t max_partition_order, uint32_t bps); #endif #ifdef FLAC__SSSE3_SUPPORTED extern void FLAC__precompute_partition_info_sums_intrin_ssse3(const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned residual_samples, unsigned predictor_order, unsigned min_partition_order, unsigned max_partition_order, unsigned bps); + uint32_t residual_samples, uint32_t predictor_order, uint32_t min_partition_order, uint32_t max_partition_order, uint32_t bps); #endif #ifdef FLAC__AVX2_SUPPORTED extern void FLAC__precompute_partition_info_sums_intrin_avx2(const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned residual_samples, unsigned predictor_order, unsigned min_partition_order, unsigned max_partition_order, unsigned bps); + uint32_t residual_samples, uint32_t predictor_order, uint32_t min_partition_order, uint32_t max_partition_order, uint32_t bps); #endif #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h index 0c4fd1f5..b042d88f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,11 +36,11 @@ #include "../../../format.h" #include "bitwriter.h" -FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw); +FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw, FLAC__bool update_vendor_string); FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWriter *bw); -FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); -FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); -FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); -FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, unsigned samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, uint32_t subframe_bps, uint32_t wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, uint32_t residual_samples, uint32_t subframe_bps, uint32_t wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, uint32_t residual_samples, uint32_t subframe_bps, uint32_t wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, uint32_t samples, uint32_t subframe_bps, uint32_t wasted_bits, FLAC__BitWriter *bw); #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h index c87bffd9..e40ada79 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2006-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h deleted file mode 100644 index 90912af5..00000000 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h +++ /dev/null @@ -1,39 +0,0 @@ -/* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of the Xiph.org Foundation nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FLAC__PROTECTED__ALL_H -#define FLAC__PROTECTED__ALL_H - -#include "stream_decoder.h" -#include "stream_encoder.h" - -#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h index 115c2554..43f79b09 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,11 +41,11 @@ typedef struct FLAC__StreamDecoderProtected { FLAC__StreamDecoderState state; FLAC__StreamDecoderInitStatus initstate; - unsigned channels; + uint32_t channels; FLAC__ChannelAssignment channel_assignment; - unsigned bits_per_sample; - unsigned sample_rate; /* in Hz */ - unsigned blocksize; /* in samples (per channel) */ + uint32_t bits_per_sample; + uint32_t sample_rate; /* in Hz */ + uint32_t blocksize; /* in samples (per channel) */ FLAC__bool md5_checking; /* if true, generate MD5 signature of decoded data and compare against signature in the STREAMINFO metadata block */ #if FLAC__HAS_OGG FLAC__OggDecoderAspect ogg_decoder_aspect; @@ -53,8 +53,8 @@ typedef struct FLAC__StreamDecoderProtected { } FLAC__StreamDecoderProtected; /* - * return the number of input bytes consumed + * Return the number of input bytes consumed */ -unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder); +uint32_t FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder); #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h index 5bb45831..00da24b1 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -61,6 +61,7 @@ typedef enum { FLAC__APODIZATION_TUKEY, FLAC__APODIZATION_PARTIAL_TUKEY, FLAC__APODIZATION_PUNCHOUT_TUKEY, + FLAC__APODIZATION_SUBDIVIDE_TUKEY, FLAC__APODIZATION_WELCH } FLAC__ApodizationFunction; @@ -78,6 +79,10 @@ typedef struct { FLAC__real start; FLAC__real end; } multiple_tukey; + struct { + FLAC__real p; + FLAC__int32 parts; + } subdivide_tukey; } parameters; } FLAC__ApodizationSpecification; @@ -90,25 +95,26 @@ typedef struct FLAC__StreamEncoderProtected { FLAC__bool do_md5; FLAC__bool do_mid_side_stereo; FLAC__bool loose_mid_side_stereo; - unsigned channels; - unsigned bits_per_sample; - unsigned sample_rate; - unsigned blocksize; + uint32_t channels; + uint32_t bits_per_sample; + uint32_t sample_rate; + uint32_t blocksize; #ifndef FLAC__INTEGER_ONLY_LIBRARY - unsigned num_apodizations; + uint32_t num_apodizations; FLAC__ApodizationSpecification apodizations[FLAC__MAX_APODIZATION_FUNCTIONS]; #endif - unsigned max_lpc_order; - unsigned qlp_coeff_precision; + uint32_t max_lpc_order; + uint32_t qlp_coeff_precision; FLAC__bool do_qlp_coeff_prec_search; FLAC__bool do_exhaustive_model_search; FLAC__bool do_escape_coding; - unsigned min_residual_partition_order; - unsigned max_residual_partition_order; - unsigned rice_parameter_search_dist; + uint32_t min_residual_partition_order; + uint32_t max_residual_partition_order; + uint32_t rice_parameter_search_dist; FLAC__uint64 total_samples_estimate; + FLAC__bool limit_min_bitrate; FLAC__StreamMetadata **metadata; - unsigned num_metadata_blocks; + uint32_t num_metadata_blocks; FLAC__uint64 streaminfo_offset, seektable_offset, audio_offset; #if FLAC__HAS_OGG FLAC__OggEncoderAspect ogg_encoder_aspect; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c index 3a17b255..c82c3198 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,13 +35,16 @@ #endif #include +#include #include "../assert.h" #include "../format.h" #include "../compat.h" #include "include/private/bitmath.h" #include "include/private/lpc.h" -#if defined DEBUG || defined FLAC__OVERFLOW_DETECT || defined FLAC__OVERFLOW_DETECT_VERBOSE + + +#if !defined(NDEBUG) || defined FLAC__OVERFLOW_DETECT || defined FLAC__OVERFLOW_DETECT_VERBOSE #include #endif @@ -50,32 +53,66 @@ #ifndef FLAC__INTEGER_ONLY_LIBRARY -#if !defined(HAVE_LROUND) -#if defined(_MSC_VER) +#if defined(_MSC_VER) && (_MSC_VER < 1800) #include -#define copysign _copysign -#elif defined(__GNUC__) -#define copysign __builtin_copysign -#endif static inline long int lround(double x) { - return (long)(x + copysign (0.5, x)); + return (long)(x + _copysign(0.5, x)); +} +#elif !defined(HAVE_LROUND) && defined(__GNUC__) +static inline long int lround(double x) { + return (long)(x + __builtin_copysign(0.5, x)); } /* If this fails, we are in the presence of a mid 90's compiler, move along... */ #endif -void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len) +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], uint32_t data_len) { - unsigned i; + uint32_t i; for(i = 0; i < data_len; i++) out[i] = in[i] * window[i]; } -void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +void FLAC__lpc_window_data_wide(const FLAC__int64 in[], const FLAC__real window[], FLAC__real out[], uint32_t data_len) +{ + uint32_t i; + for(i = 0; i < data_len; i++) + out[i] = in[i] * window[i]; +} + +void FLAC__lpc_window_data_partial(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], uint32_t data_len, uint32_t part_size, uint32_t data_shift) +{ + uint32_t i, j; + if((part_size + data_shift) < data_len){ + for(i = 0; i < part_size; i++) + out[i] = in[data_shift+i] * window[i]; + i = flac_min(i,data_len - part_size - data_shift); + for(j = data_len - part_size; j < data_len; i++, j++) + out[i] = in[data_shift+i] * window[j]; + if(i < data_len) + out[i] = 0.0f; + } +} + +void FLAC__lpc_window_data_partial_wide(const FLAC__int64 in[], const FLAC__real window[], FLAC__real out[], uint32_t data_len, uint32_t part_size, uint32_t data_shift) +{ + uint32_t i, j; + if((part_size + data_shift) < data_len){ + for(i = 0; i < part_size; i++) + out[i] = in[data_shift+i] * window[i]; + i = flac_min(i,data_len - part_size - data_shift); + for(j = data_len - part_size; j < data_len; i++, j++) + out[i] = in[data_shift+i] * window[j]; + if(i < data_len) + out[i] = 0.0f; + } +} + +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], uint32_t data_len, uint32_t lag, double autoc[]) { /* a readable, but slower, version */ #if 0 - FLAC__real d; - unsigned i; + double d; + uint32_t i; FLAC__ASSERT(lag > 0); FLAC__ASSERT(lag <= data_len); @@ -89,40 +126,57 @@ void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_le */ while(lag--) { for(i = lag, d = 0.0; i < data_len; i++) - d += data[i] * data[i - lag]; + d += data[i] * (double)data[i - lag]; autoc[lag] = d; } #endif + if (data_len < FLAC__MAX_LPC_ORDER || lag > 16) { + /* + * this version tends to run faster because of better data locality + * ('data_len' is usually much larger than 'lag') + */ + double d; + uint32_t sample, coeff; + const uint32_t limit = data_len - lag; + + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= data_len); - /* - * this version tends to run faster because of better data locality - * ('data_len' is usually much larger than 'lag') - */ - FLAC__real d; - unsigned sample, coeff; - const unsigned limit = data_len - lag; - - FLAC__ASSERT(lag > 0); - FLAC__ASSERT(lag <= data_len); - - for(coeff = 0; coeff < lag; coeff++) - autoc[coeff] = 0.0; - for(sample = 0; sample <= limit; sample++) { - d = data[sample]; for(coeff = 0; coeff < lag; coeff++) - autoc[coeff] += d * data[sample+coeff]; + autoc[coeff] = 0.0; + for(sample = 0; sample <= limit; sample++) { + d = data[sample]; + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } + for(; sample < data_len; sample++) { + d = data[sample]; + for(coeff = 0; coeff < data_len - sample; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } + } + else if(lag <= 8) { + #undef MAX_LAG + #define MAX_LAG 8 + #include "deduplication/lpc_compute_autocorrelation_intrin.c" + } + else if(lag <= 12) { + #undef MAX_LAG + #define MAX_LAG 12 + #include "deduplication/lpc_compute_autocorrelation_intrin.c" } - for(; sample < data_len; sample++) { - d = data[sample]; - for(coeff = 0; coeff < data_len - sample; coeff++) - autoc[coeff] += d * data[sample+coeff]; + else if(lag <= 16) { + #undef MAX_LAG + #define MAX_LAG 16 + #include "deduplication/lpc_compute_autocorrelation_intrin.c" } + } -void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], FLAC__double error[]) +void FLAC__lpc_compute_lp_coefficients(const double autoc[], uint32_t *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], double error[]) { - unsigned i, j; - FLAC__double r, err, lpc[FLAC__MAX_LPC_ORDER]; + uint32_t i, j; + double r, err, lpc[FLAC__MAX_LPC_ORDER]; FLAC__ASSERT(0 != max_order); FLAC__ASSERT(0 < *max_order); @@ -141,7 +195,7 @@ void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_o /* Update LPC coefficients and total error. */ lpc[i]=r; for(j = 0; j < (i>>1); j++) { - FLAC__double tmp = lpc[j]; + double tmp = lpc[j]; lpc[j] += r * lpc[i-1-j]; lpc[i-1-j] += r * tmp; } @@ -163,10 +217,10 @@ void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_o } } -int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift) +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], uint32_t order, uint32_t precision, FLAC__int32 qlp_coeff[], int *shift) { - unsigned i; - FLAC__double cmax; + uint32_t i; + double cmax; FLAC__int32 qmax, qmin; FLAC__ASSERT(precision > 0); @@ -181,7 +235,7 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, /* calc cmax = max( |lp_coeff[i]| ) */ cmax = 0.0; for(i = 0; i < order; i++) { - const FLAC__double d = fabs(lp_coeff[i]); + const double d = fabs(lp_coeff[i]); if(d > cmax) cmax = d; } @@ -206,7 +260,7 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, } if(*shift >= 0) { - FLAC__double error = 0.0; + double error = 0.0; FLAC__int32 q; for(i = 0; i < order; i++) { error += lp_coeff[i] * (1 << *shift); @@ -227,14 +281,14 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, } } /* negative shift is very rare but due to design flaw, negative shift is - * a NOP in the decoder, so it must be handled specially by scaling down - * coeffs + * not allowed in the decoder, so it must be handled specially by scaling + * down coeffs */ else { const int nshift = -(*shift); - FLAC__double error = 0.0; + double error = 0.0; FLAC__int32 q; -#ifdef DEBUG +#ifndef NDEBUG fprintf(stderr,"FLAC__lpc_quantize_coefficients: negative shift=%d order=%u cmax=%f\n", *shift, order, cmax); #endif for(i = 0; i < order; i++) { @@ -264,11 +318,11 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, #pragma warning ( disable : 4028 ) #endif -void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 * flac_restrict data, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict residual) +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 * flac_restrict data, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict residual) #if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) { FLAC__int64 sumo; - unsigned i, j; + uint32_t i, j; FLAC__int32 sum; const FLAC__int32 *history; @@ -287,6 +341,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 * flac_r for(j = 0; j < order; j++) { sum += qlp_coeff[j] * (*(--history)); sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); + if(sumo > 2147483647ll || sumo < -2147483648ll) fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%" PRId64 "\n",i,j,qlp_coeff[j],*history,sumo); } *(residual++) = *(data++) - (sum >> lp_quantization); @@ -485,25 +540,25 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 * flac_r for(i = 0; i < (int)data_len; i++) { sum = 0; switch(order) { - case 32: sum += qlp_coeff[31] * data[i-32]; - case 31: sum += qlp_coeff[30] * data[i-31]; - case 30: sum += qlp_coeff[29] * data[i-30]; - case 29: sum += qlp_coeff[28] * data[i-29]; - case 28: sum += qlp_coeff[27] * data[i-28]; - case 27: sum += qlp_coeff[26] * data[i-27]; - case 26: sum += qlp_coeff[25] * data[i-26]; - case 25: sum += qlp_coeff[24] * data[i-25]; - case 24: sum += qlp_coeff[23] * data[i-24]; - case 23: sum += qlp_coeff[22] * data[i-23]; - case 22: sum += qlp_coeff[21] * data[i-22]; - case 21: sum += qlp_coeff[20] * data[i-21]; - case 20: sum += qlp_coeff[19] * data[i-20]; - case 19: sum += qlp_coeff[18] * data[i-19]; - case 18: sum += qlp_coeff[17] * data[i-18]; - case 17: sum += qlp_coeff[16] * data[i-17]; - case 16: sum += qlp_coeff[15] * data[i-16]; - case 15: sum += qlp_coeff[14] * data[i-15]; - case 14: sum += qlp_coeff[13] * data[i-14]; + case 32: sum += qlp_coeff[31] * data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * data[i-14]; /* Falls through. */ case 13: sum += qlp_coeff[12] * data[i-13]; sum += qlp_coeff[11] * data[i-12]; sum += qlp_coeff[10] * data[i-11]; @@ -524,10 +579,10 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 * flac_r } #endif -void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * flac_restrict data, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict residual) +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * flac_restrict data, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict residual) #if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) { - unsigned i, j; + uint32_t i, j; FLAC__int64 sum; const FLAC__int32 *history; @@ -544,12 +599,8 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f history = data; for(j = 0; j < order; j++) sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); - if(FLAC__bitmath_silog2_wide(sum >> lp_quantization) > 32) { - fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%" PRId64 "\n", i, (sum >> lp_quantization)); - break; - } - if(FLAC__bitmath_silog2_wide((FLAC__int64)(*data) - (sum >> lp_quantization)) > 32) { - fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%" PRId64 ", residual=%" PRId64 "\n", i, *data, (long long)(sum >> lp_quantization), ((FLAC__int64)(*data) - (sum >> lp_quantization))); + if(FLAC__bitmath_silog2((FLAC__int64)(*data) - (sum >> lp_quantization)) > 32) { + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%" PRId64 ", residual=%" PRId64 "\n", i, *data, (int64_t)(sum >> lp_quantization), ((FLAC__int64)(*data) - (sum >> lp_quantization))); break; } *(residual++) = *(data++) - (FLAC__int32)(sum >> lp_quantization); @@ -586,7 +637,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } else { /* order == 11 */ @@ -603,7 +654,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } } @@ -621,7 +672,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } else { /* order == 9 */ @@ -636,7 +687,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } } @@ -654,7 +705,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } else { /* order == 7 */ @@ -667,7 +718,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } } @@ -681,7 +732,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } else { /* order == 5 */ @@ -692,7 +743,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } } @@ -706,7 +757,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } else { /* order == 3 */ @@ -715,7 +766,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } } @@ -725,12 +776,12 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum = 0; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } else { /* order == 1 */ for(i = 0; i < (int)data_len; i++) - residual[i] = data[i] - (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + residual[i] = data[i] - ((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); } } } @@ -739,25 +790,25 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f for(i = 0; i < (int)data_len; i++) { sum = 0; switch(order) { - case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; - case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; - case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; - case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; - case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; - case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; - case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; - case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; - case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; - case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; - case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; - case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; - case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; - case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; - case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; - case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; - case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; - case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; - case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; /* Falls through. */ case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; @@ -772,19 +823,160 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * f sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; } - residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + residual[i] = data[i] - (sum >> lp_quantization); } } } #endif +FLAC__bool FLAC__lpc_compute_residual_from_qlp_coefficients_limit_residual(const FLAC__int32 * flac_restrict data, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict residual) +{ + int i; + FLAC__int64 sum, residual_to_check; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; /* Falls through. */ + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; /* Falls through. */ + case 12: sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; /* Falls through. */ + case 11: sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; /* Falls through. */ + case 10: sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; /* Falls through. */ + case 9: sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; /* Falls through. */ + case 8: sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; /* Falls through. */ + case 7: sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; /* Falls through. */ + case 6: sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; /* Falls through. */ + case 5: sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; /* Falls through. */ + case 4: sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; /* Falls through. */ + case 3: sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; /* Falls through. */ + case 2: sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; /* Falls through. */ + case 1: sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + residual_to_check = data[i] - (sum >> lp_quantization); + /* residual must not be INT32_MIN because abs(INT32_MIN) is undefined */ + if(residual_to_check <= INT32_MIN || residual_to_check > INT32_MAX) + return false; + else + residual[i] = residual_to_check; + } + return true; +} + +FLAC__bool FLAC__lpc_compute_residual_from_qlp_coefficients_limit_residual_33bit(const FLAC__int64 * flac_restrict data, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict residual) +{ + int i; + FLAC__int64 sum, residual_to_check; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * data[i-14]; /* Falls through. */ + case 13: sum += qlp_coeff[12] * data[i-13]; /* Falls through. */ + case 12: sum += qlp_coeff[11] * data[i-12]; /* Falls through. */ + case 11: sum += qlp_coeff[10] * data[i-11]; /* Falls through. */ + case 10: sum += qlp_coeff[ 9] * data[i-10]; /* Falls through. */ + case 9: sum += qlp_coeff[ 8] * data[i- 9]; /* Falls through. */ + case 8: sum += qlp_coeff[ 7] * data[i- 8]; /* Falls through. */ + case 7: sum += qlp_coeff[ 6] * data[i- 7]; /* Falls through. */ + case 6: sum += qlp_coeff[ 5] * data[i- 6]; /* Falls through. */ + case 5: sum += qlp_coeff[ 4] * data[i- 5]; /* Falls through. */ + case 4: sum += qlp_coeff[ 3] * data[i- 4]; /* Falls through. */ + case 3: sum += qlp_coeff[ 2] * data[i- 3]; /* Falls through. */ + case 2: sum += qlp_coeff[ 1] * data[i- 2]; /* Falls through. */ + case 1: sum += qlp_coeff[ 0] * data[i- 1]; + } + residual_to_check = data[i] - (sum >> lp_quantization); + /* residual must not be INT32_MIN because abs(INT32_MIN) is undefined */ + if(residual_to_check <= INT32_MIN || residual_to_check > INT32_MAX) + return false; + else + residual[i] = residual_to_check; + } + return true; +} + #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ -void FLAC__lpc_restore_signal(const FLAC__int32 * flac_restrict residual, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict data) +uint32_t FLAC__lpc_max_prediction_before_shift_bps(uint32_t subframe_bps, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order) +{ + /* This used to be subframe_bps + qlp_coeff_precision + FLAC__bitmath_ilog2(order) + * but that treats both the samples as well as the predictor as unknown. The + * predictor is known however, so taking the log2 of the sum of the absolute values + * of all coefficients is a more accurate representation of the predictor */ + FLAC__int32 abs_sum_of_qlp_coeff = 0; + uint32_t i; + for(i = 0; i < order; i++) + abs_sum_of_qlp_coeff += abs(qlp_coeff[i]); + if(abs_sum_of_qlp_coeff == 0) + abs_sum_of_qlp_coeff = 1; + return subframe_bps + FLAC__bitmath_silog2(abs_sum_of_qlp_coeff); +} + + +uint32_t FLAC__lpc_max_residual_bps(uint32_t subframe_bps, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization) +{ + FLAC__int32 predictor_sum_bps = FLAC__lpc_max_prediction_before_shift_bps(subframe_bps, qlp_coeff, order) - lp_quantization; + if((int)subframe_bps > predictor_sum_bps) + return subframe_bps + 1; + else + return predictor_sum_bps + 1; +} + +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && !defined(FUZZING_BUILD_MODE_FLAC_SANITIZE_SIGNED_INTEGER_OVERFLOW) +/* The attribute below is to silence the undefined sanitizer of oss-fuzz. + * Because fuzzing feeds bogus predictors and residual samples to the + * decoder, having overflows in this section is unavoidable. Also, + * because the calculated values are audio path only, there is no + * potential for security problems */ +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +void FLAC__lpc_restore_signal(const FLAC__int32 * flac_restrict residual, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict data) #if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) { FLAC__int64 sumo; - unsigned i, j; + uint32_t i, j; FLAC__int32 sum; const FLAC__int32 *r = residual, *history; @@ -803,8 +995,10 @@ void FLAC__lpc_restore_signal(const FLAC__int32 * flac_restrict residual, unsign for(j = 0; j < order; j++) { sum += qlp_coeff[j] * (*(--history)); sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); +#ifdef FLAC__OVERFLOW_DETECT if(sumo > 2147483647ll || sumo < -2147483648ll) fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%" PRId64 "\n",i,j,qlp_coeff[j],*history,sumo); +#endif } *(data++) = *(r++) + (sum >> lp_quantization); } @@ -1002,25 +1196,25 @@ void FLAC__lpc_restore_signal(const FLAC__int32 * flac_restrict residual, unsign for(i = 0; i < (int)data_len; i++) { sum = 0; switch(order) { - case 32: sum += qlp_coeff[31] * data[i-32]; - case 31: sum += qlp_coeff[30] * data[i-31]; - case 30: sum += qlp_coeff[29] * data[i-30]; - case 29: sum += qlp_coeff[28] * data[i-29]; - case 28: sum += qlp_coeff[27] * data[i-28]; - case 27: sum += qlp_coeff[26] * data[i-27]; - case 26: sum += qlp_coeff[25] * data[i-26]; - case 25: sum += qlp_coeff[24] * data[i-25]; - case 24: sum += qlp_coeff[23] * data[i-24]; - case 23: sum += qlp_coeff[22] * data[i-23]; - case 22: sum += qlp_coeff[21] * data[i-22]; - case 21: sum += qlp_coeff[20] * data[i-21]; - case 20: sum += qlp_coeff[19] * data[i-20]; - case 19: sum += qlp_coeff[18] * data[i-19]; - case 18: sum += qlp_coeff[17] * data[i-18]; - case 17: sum += qlp_coeff[16] * data[i-17]; - case 16: sum += qlp_coeff[15] * data[i-16]; - case 15: sum += qlp_coeff[14] * data[i-15]; - case 14: sum += qlp_coeff[13] * data[i-14]; + case 32: sum += qlp_coeff[31] * data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * data[i-14]; /* Falls through. */ case 13: sum += qlp_coeff[12] * data[i-13]; sum += qlp_coeff[11] * data[i-12]; sum += qlp_coeff[10] * data[i-11]; @@ -1041,10 +1235,10 @@ void FLAC__lpc_restore_signal(const FLAC__int32 * flac_restrict residual, unsign } #endif -void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict data) +void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict data) #if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) { - unsigned i, j; + uint32_t i, j; FLAC__int64 sum; const FLAC__int32 *r = residual, *history; @@ -1061,15 +1255,13 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u history = data; for(j = 0; j < order; j++) sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); - if(FLAC__bitmath_silog2_wide(sum >> lp_quantization) > 32) { - fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%" PRId64 "\n", i, (sum >> lp_quantization)); - break; - } - if(FLAC__bitmath_silog2_wide((FLAC__int64)(*r) + (sum >> lp_quantization)) > 32) { +#ifdef FLAC__OVERFLOW_DETECT + if(FLAC__bitmath_silog2((FLAC__int64)(*r) + (sum >> lp_quantization)) > 32) { fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%" PRId64 ", data=%" PRId64 "\n", i, *r, (sum >> lp_quantization), ((FLAC__int64)(*r) + (sum >> lp_quantization))); break; } - *(data++) = *(r++) + (FLAC__int32)(sum >> lp_quantization); +#endif + *(data++) = (FLAC__int32)(*(r++) + (sum >> lp_quantization)); } } #else /* fully unrolled version for normal use */ @@ -1103,7 +1295,7 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } else { /* order == 11 */ @@ -1120,7 +1312,7 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } } @@ -1138,7 +1330,7 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } else { /* order == 9 */ @@ -1153,7 +1345,7 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } } @@ -1171,7 +1363,7 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } else { /* order == 7 */ @@ -1184,7 +1376,7 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } } @@ -1198,7 +1390,7 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } else { /* order == 5 */ @@ -1209,7 +1401,7 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } } @@ -1223,7 +1415,7 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } else { /* order == 3 */ @@ -1232,7 +1424,7 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } } @@ -1242,12 +1434,12 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum = 0; sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } else { /* order == 1 */ for(i = 0; i < (int)data_len; i++) - data[i] = residual[i] + (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + data[i] = (FLAC__int32)(residual[i] + ((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization)); } } } @@ -1256,25 +1448,25 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u for(i = 0; i < (int)data_len; i++) { sum = 0; switch(order) { - case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; - case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; - case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; - case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; - case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; - case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; - case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; - case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; - case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; - case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; - case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; - case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; - case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; - case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; - case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; - case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; - case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; - case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; - case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; /* Falls through. */ case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; @@ -1289,33 +1481,114 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, u sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; } - data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + data[i] = (FLAC__int32) (residual[i] + (sum >> lp_quantization)); } } } #endif +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && !defined(FUZZING_BUILD_MODE_FLAC_SANITIZE_SIGNED_INTEGER_OVERFLOW) +/* The attribute below is to silence the undefined sanitizer of oss-fuzz. + * Because fuzzing feeds bogus predictors and residual samples to the + * decoder, having overflows in this section is unavoidable. Also, + * because the calculated values are audio path only, there is no + * potential for security problems */ +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +void FLAC__lpc_restore_signal_wide_33bit(const FLAC__int32 * flac_restrict residual, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int64 * flac_restrict data) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + uint32_t i, j; + FLAC__int64 sum; + const FLAC__int32 *r = residual; + const FLAC__int64 *history; + + FLAC__ASSERT(order > 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); +#ifdef FLAC__OVERFLOW_DETECT + if(FLAC__bitmath_silog2((FLAC__int64)(*r) + (sum >> lp_quantization)) > 33) { + fprintf(stderr,"FLAC__lpc_restore_signal_33bit: OVERFLOW, i=%u, residual=%d, sum=%" PRId64 ", data=%" PRId64 "\n", i, *r, (sum >> lp_quantization), ((FLAC__int64)(*r) + (sum >> lp_quantization))); + break; + } +#endif + *(data++) = (FLAC__int64)(*(r++)) + (sum >> lp_quantization); + } +} +#else /* unrolled version for normal use */ +{ + int i; + FLAC__int64 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * data[i-14]; /* Falls through. */ + case 13: sum += qlp_coeff[12] * data[i-13]; /* Falls through. */ + case 12: sum += qlp_coeff[11] * data[i-12]; /* Falls through. */ + case 11: sum += qlp_coeff[10] * data[i-11]; /* Falls through. */ + case 10: sum += qlp_coeff[ 9] * data[i-10]; /* Falls through. */ + case 9: sum += qlp_coeff[ 8] * data[i- 9]; /* Falls through. */ + case 8: sum += qlp_coeff[ 7] * data[i- 8]; /* Falls through. */ + case 7: sum += qlp_coeff[ 6] * data[i- 7]; /* Falls through. */ + case 6: sum += qlp_coeff[ 5] * data[i- 6]; /* Falls through. */ + case 5: sum += qlp_coeff[ 4] * data[i- 5]; /* Falls through. */ + case 4: sum += qlp_coeff[ 3] * data[i- 4]; /* Falls through. */ + case 3: sum += qlp_coeff[ 2] * data[i- 3]; /* Falls through. */ + case 2: sum += qlp_coeff[ 1] * data[i- 2]; /* Falls through. */ + case 1: sum += qlp_coeff[ 0] * data[i- 1]; + } + data[i] = residual[i] + (sum >> lp_quantization); + } +} +#endif + #if defined(_MSC_VER) #pragma warning ( default : 4028 ) #endif #ifndef FLAC__INTEGER_ONLY_LIBRARY -FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample(FLAC__double lpc_error, unsigned total_samples) +double FLAC__lpc_compute_expected_bits_per_residual_sample(double lpc_error, uint32_t total_samples) { - FLAC__double error_scale; + double error_scale; FLAC__ASSERT(total_samples > 0); - error_scale = 0.5 * M_LN2 * M_LN2 / (FLAC__double)total_samples; + error_scale = 0.5 / (double)total_samples; return FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error, error_scale); } -FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(FLAC__double lpc_error, FLAC__double error_scale) +double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(double lpc_error, double error_scale) { if(lpc_error > 0.0) { - FLAC__double bps = (FLAC__double)0.5 * log(error_scale * lpc_error) / M_LN2; + double bps = (double)0.5 * log(error_scale * lpc_error) / M_LN2; if(bps >= 0.0) return bps; else @@ -1329,21 +1602,21 @@ FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scal } } -unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order) +uint32_t FLAC__lpc_compute_best_order(const double lpc_error[], uint32_t max_order, uint32_t total_samples, uint32_t overhead_bits_per_order) { - unsigned order, indx, best_index; /* 'index' the index into lpc_error; index==order-1 since lpc_error[0] is for order==1, lpc_error[1] is for order==2, etc */ - FLAC__double bits, best_bits, error_scale; + uint32_t order, indx, best_index; /* 'index' the index into lpc_error; index==order-1 since lpc_error[0] is for order==1, lpc_error[1] is for order==2, etc */ + double bits, best_bits, error_scale; FLAC__ASSERT(max_order > 0); FLAC__ASSERT(total_samples > 0); - error_scale = 0.5 * M_LN2 * M_LN2 / (FLAC__double)total_samples; + error_scale = 0.5 / (double)total_samples; best_index = 0; - best_bits = (unsigned)(-1); + best_bits = (uint32_t)(-1); for(indx = 0, order = 1; indx < max_order; indx++, order++) { - bits = FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error[indx], error_scale) * (FLAC__double)(total_samples - order) + (FLAC__double)(order * overhead_bits_per_order); + bits = FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error[indx], error_scale) * (double)(total_samples - order) + (double)(order * overhead_bits_per_order); if(bits < best_bits) { best_index = indx; best_bits = bits; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_intrin_neon.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_intrin_neon.c new file mode 100644 index 00000000..367885d7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_intrin_neon.c @@ -0,0 +1,1273 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2023 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "include/private/cpu.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#ifndef FLAC__NO_ASM +#if defined FLAC__CPU_ARM64 && FLAC__HAS_NEONINTRIN +#include "include/private/lpc.h" +#include "../assert.h" +#include "../format.h" + +#include + +#if FLAC__HAS_A64NEONINTRIN +void FLAC__lpc_compute_autocorrelation_intrin_neon_lag_14(const FLAC__real data[], uint32_t data_len, uint32_t lag, double autoc[]) +{ +#undef MAX_LAG +#define MAX_LAG 14 +#include "deduplication/lpc_compute_autocorrelation_intrin_neon.c" +} + +void FLAC__lpc_compute_autocorrelation_intrin_neon_lag_10(const FLAC__real data[], uint32_t data_len, uint32_t lag, double autoc[]) +{ +#undef MAX_LAG +#define MAX_LAG 10 +#include "deduplication/lpc_compute_autocorrelation_intrin_neon.c" +} + +void FLAC__lpc_compute_autocorrelation_intrin_neon_lag_8(const FLAC__real data[], uint32_t data_len, uint32_t lag, double autoc[]) +{ +#undef MAX_LAG +#define MAX_LAG 8 +#include "deduplication/lpc_compute_autocorrelation_intrin_neon.c" +} + +#endif /* ifdef FLAC__HAS_A64NEONINTRIN */ + + +#define MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_vec, lane) \ + summ_0 = vmulq_laneq_s32(tmp_vec[0], qlp_coeff_vec, lane); \ + summ_1 = vmulq_laneq_s32(tmp_vec[4], qlp_coeff_vec, lane); \ + summ_2 = vmulq_laneq_s32(tmp_vec[8], qlp_coeff_vec, lane); + + +#define MACC_32BIT_LOOP_UNROOL_3(tmp_vec_ind, qlp_coeff_vec, lane) \ + summ_0 = vmlaq_laneq_s32(summ_0,tmp_vec[tmp_vec_ind] ,qlp_coeff_vec, lane); \ + summ_1 = vmlaq_laneq_s32(summ_1,tmp_vec[tmp_vec_ind+4] ,qlp_coeff_vec, lane); \ + summ_2 = vmlaq_laneq_s32(summ_2,tmp_vec[tmp_vec_ind+8] ,qlp_coeff_vec, lane); + +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_neon(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]) +{ + int i; + FLAC__int32 sum; + int32x4_t tmp_vec[20]; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + // Using prologue reads is valid as encoder->private_->local_lpc_compute_residual_from_qlp_coefficients(signal+order,....) + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if (order == 12) { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], qlp_coeff[6], qlp_coeff[7]}; + int32x4_t qlp_coeff_2 = {qlp_coeff[8], qlp_coeff[9], qlp_coeff[10], qlp_coeff[11]}; + + tmp_vec[0] = vld1q_s32(data - 12); + tmp_vec[1] = vld1q_s32(data - 11); + tmp_vec[2] = vld1q_s32(data - 10); + tmp_vec[3] = vld1q_s32(data - 9); + tmp_vec[4] = vld1q_s32(data - 8); + tmp_vec[5] = vld1q_s32(data - 7); + tmp_vec[6] = vld1q_s32(data - 6); + tmp_vec[7] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + + tmp_vec[8] = vld1q_s32(data + i - 4); + tmp_vec[9] = vld1q_s32(data+i-3); + tmp_vec[10] = vld1q_s32(data+i-2); + tmp_vec[11] = vld1q_s32(data+i-1); + tmp_vec[12] = vld1q_s32(data+i); + tmp_vec[13] = vld1q_s32(data+i+1); + tmp_vec[14] = vld1q_s32(data+i+2); + tmp_vec[15] = vld1q_s32(data+i+3); + tmp_vec[16] = vld1q_s32(data + i + 4); + tmp_vec[17] = vld1q_s32(data + i + 5); + tmp_vec[18] = vld1q_s32(data + i + 6); + tmp_vec[19] = vld1q_s32(data + i + 7); + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_2, 3) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_2, 2) + MACC_32BIT_LOOP_UNROOL_3(2, qlp_coeff_2, 1) + MACC_32BIT_LOOP_UNROOL_3(3, qlp_coeff_2, 0) + MACC_32BIT_LOOP_UNROOL_3(4, qlp_coeff_1, 3) + MACC_32BIT_LOOP_UNROOL_3(5, qlp_coeff_1, 2) + MACC_32BIT_LOOP_UNROOL_3(6, qlp_coeff_1, 1) + MACC_32BIT_LOOP_UNROOL_3(7, qlp_coeff_1, 0) + MACC_32BIT_LOOP_UNROOL_3(8, qlp_coeff_0, 3) + MACC_32BIT_LOOP_UNROOL_3(9, qlp_coeff_0, 2) + MACC_32BIT_LOOP_UNROOL_3(10, qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(11, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + tmp_vec[3] = tmp_vec[15]; + tmp_vec[4] = tmp_vec[16]; + tmp_vec[5] = tmp_vec[17]; + tmp_vec[6] = tmp_vec[18]; + tmp_vec[7] = tmp_vec[19]; + } + } + + else { /* order == 11 */ + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], qlp_coeff[6], qlp_coeff[7]}; + int32x4_t qlp_coeff_2 = {qlp_coeff[8], qlp_coeff[9], qlp_coeff[10], 0}; + + tmp_vec[0] = vld1q_s32(data - 11); + tmp_vec[1] = vld1q_s32(data - 10); + tmp_vec[2] = vld1q_s32(data - 9); + tmp_vec[3] = vld1q_s32(data - 8); + tmp_vec[4] = vld1q_s32(data - 7); + tmp_vec[5] = vld1q_s32(data - 6); + tmp_vec[6] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + tmp_vec[7] = vld1q_s32(data + i - 4); + tmp_vec[8] = vld1q_s32(data + i - 3); + tmp_vec[9] = vld1q_s32(data + i - 2); + tmp_vec[10] = vld1q_s32(data + i - 1); + tmp_vec[11] = vld1q_s32(data + i - 0); + tmp_vec[12] = vld1q_s32(data + i + 1); + tmp_vec[13] = vld1q_s32(data + i + 2); + tmp_vec[14] = vld1q_s32(data + i + 3); + tmp_vec[15] = vld1q_s32(data + i + 4); + tmp_vec[16] = vld1q_s32(data + i + 5); + tmp_vec[17] = vld1q_s32(data + i + 6); + tmp_vec[18] = vld1q_s32(data + i + 7); + + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_2, 2) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_2, 1) + MACC_32BIT_LOOP_UNROOL_3(2, qlp_coeff_2, 0) + MACC_32BIT_LOOP_UNROOL_3(3, qlp_coeff_1, 3) + MACC_32BIT_LOOP_UNROOL_3(4, qlp_coeff_1, 2) + MACC_32BIT_LOOP_UNROOL_3(5, qlp_coeff_1, 1) + MACC_32BIT_LOOP_UNROOL_3(6, qlp_coeff_1, 0) + MACC_32BIT_LOOP_UNROOL_3(7, qlp_coeff_0, 3) + MACC_32BIT_LOOP_UNROOL_3(8, qlp_coeff_0, 2) + MACC_32BIT_LOOP_UNROOL_3(9, qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(10, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + tmp_vec[3] = tmp_vec[15]; + tmp_vec[4] = tmp_vec[16]; + tmp_vec[5] = tmp_vec[17]; + tmp_vec[6] = tmp_vec[18]; + } + } + } + else { + if(order == 10) { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], qlp_coeff[6], qlp_coeff[7]}; + int32x4_t qlp_coeff_2 = {qlp_coeff[8], qlp_coeff[9], 0, 0}; + + tmp_vec[0] = vld1q_s32(data - 10); + tmp_vec[1] = vld1q_s32(data - 9); + tmp_vec[2] = vld1q_s32(data - 8); + tmp_vec[3] = vld1q_s32(data - 7); + tmp_vec[4] = vld1q_s32(data - 6); + tmp_vec[5] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + tmp_vec[6] = vld1q_s32(data + i - 4); + tmp_vec[7] = vld1q_s32(data + i - 3); + tmp_vec[8] = vld1q_s32(data + i - 2); + tmp_vec[9] = vld1q_s32(data + i - 1); + tmp_vec[10] = vld1q_s32(data + i - 0); + tmp_vec[11] = vld1q_s32(data + i + 1); + tmp_vec[12] = vld1q_s32(data + i + 2); + tmp_vec[13] = vld1q_s32(data + i + 3); + tmp_vec[14] = vld1q_s32(data + i + 4); + tmp_vec[15] = vld1q_s32(data + i + 5); + tmp_vec[16] = vld1q_s32(data + i + 6); + tmp_vec[17] = vld1q_s32(data + i + 7); + + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_2, 1) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_2, 0) + MACC_32BIT_LOOP_UNROOL_3(2, qlp_coeff_1, 3) + MACC_32BIT_LOOP_UNROOL_3(3, qlp_coeff_1, 2) + MACC_32BIT_LOOP_UNROOL_3(4, qlp_coeff_1, 1) + MACC_32BIT_LOOP_UNROOL_3(5, qlp_coeff_1, 0) + MACC_32BIT_LOOP_UNROOL_3(6, qlp_coeff_0, 3) + MACC_32BIT_LOOP_UNROOL_3(7, qlp_coeff_0, 2) + MACC_32BIT_LOOP_UNROOL_3(8, qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(9, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + tmp_vec[3] = tmp_vec[15]; + tmp_vec[4] = tmp_vec[16]; + tmp_vec[5] = tmp_vec[17]; + } + } + else { /* order == 9 */ + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], qlp_coeff[6], qlp_coeff[7]}; + int32x4_t qlp_coeff_2 = {qlp_coeff[8], 0, 0, 0}; + + tmp_vec[0] = vld1q_s32(data - 9); + tmp_vec[1] = vld1q_s32(data - 8); + tmp_vec[2] = vld1q_s32(data - 7); + tmp_vec[3] = vld1q_s32(data - 6); + tmp_vec[4] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + tmp_vec[5] = vld1q_s32(data + i - 4); + tmp_vec[6] = vld1q_s32(data + i - 3); + tmp_vec[7] = vld1q_s32(data + i - 2); + tmp_vec[8] = vld1q_s32(data + i - 1); + tmp_vec[9] = vld1q_s32(data + i - 0); + tmp_vec[10] = vld1q_s32(data + i + 1); + tmp_vec[11] = vld1q_s32(data + i + 2); + tmp_vec[12] = vld1q_s32(data + i + 3); + tmp_vec[13] = vld1q_s32(data + i + 4); + tmp_vec[14] = vld1q_s32(data + i + 5); + tmp_vec[15] = vld1q_s32(data + i + 6); + tmp_vec[16] = vld1q_s32(data + i + 7); + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_2, 0) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_1, 3) + MACC_32BIT_LOOP_UNROOL_3(2, qlp_coeff_1, 2) + MACC_32BIT_LOOP_UNROOL_3(3, qlp_coeff_1, 1) + MACC_32BIT_LOOP_UNROOL_3(4, qlp_coeff_1, 0) + MACC_32BIT_LOOP_UNROOL_3(5, qlp_coeff_0, 3) + MACC_32BIT_LOOP_UNROOL_3(6, qlp_coeff_0, 2) + MACC_32BIT_LOOP_UNROOL_3(7, qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(8, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + tmp_vec[3] = tmp_vec[15]; + tmp_vec[4] = tmp_vec[16]; + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], qlp_coeff[6], qlp_coeff[7]}; + + tmp_vec[0] = vld1q_s32(data - 8); + tmp_vec[1] = vld1q_s32(data - 7); + tmp_vec[2] = vld1q_s32(data - 6); + tmp_vec[3] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + tmp_vec[4] = vld1q_s32(data + i - 4); + tmp_vec[5] = vld1q_s32(data + i - 3); + tmp_vec[6] = vld1q_s32(data + i - 2); + tmp_vec[7] = vld1q_s32(data + i - 1); + tmp_vec[8] = vld1q_s32(data + i - 0); + tmp_vec[9] = vld1q_s32(data + i + 1); + tmp_vec[10] = vld1q_s32(data + i + 2); + tmp_vec[11] = vld1q_s32(data + i + 3); + tmp_vec[12] = vld1q_s32(data + i + 4); + tmp_vec[13] = vld1q_s32(data + i + 5); + tmp_vec[14] = vld1q_s32(data + i + 6); + tmp_vec[15] = vld1q_s32(data + i + 7); + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_1, 3) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_1, 2) + MACC_32BIT_LOOP_UNROOL_3(2, qlp_coeff_1, 1) + MACC_32BIT_LOOP_UNROOL_3(3, qlp_coeff_1, 0) + MACC_32BIT_LOOP_UNROOL_3(4, qlp_coeff_0, 3) + MACC_32BIT_LOOP_UNROOL_3(5, qlp_coeff_0, 2) + MACC_32BIT_LOOP_UNROOL_3(6, qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(7, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + tmp_vec[3] = tmp_vec[15]; + } + } + else { /* order == 7 */ + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], qlp_coeff[6], 0}; + + tmp_vec[0] = vld1q_s32(data - 7); + tmp_vec[1] = vld1q_s32(data - 6); + tmp_vec[2] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + tmp_vec[3] = vld1q_s32(data + i - 4); + tmp_vec[4] = vld1q_s32(data + i - 3); + tmp_vec[5] = vld1q_s32(data + i - 2); + tmp_vec[6] = vld1q_s32(data + i - 1); + tmp_vec[7] = vld1q_s32(data + i - 0); + tmp_vec[8] = vld1q_s32(data + i + 1); + tmp_vec[9] = vld1q_s32(data + i + 2); + tmp_vec[10] = vld1q_s32(data + i + 3); + tmp_vec[11] = vld1q_s32(data + i + 4); + tmp_vec[12] = vld1q_s32(data + i + 5); + tmp_vec[13] = vld1q_s32(data + i + 6); + tmp_vec[14] = vld1q_s32(data + i + 7); + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_1, 2) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_1, 1) + MACC_32BIT_LOOP_UNROOL_3(2, qlp_coeff_1, 0) + MACC_32BIT_LOOP_UNROOL_3(3, qlp_coeff_0, 3) + MACC_32BIT_LOOP_UNROOL_3(4, qlp_coeff_0, 2) + MACC_32BIT_LOOP_UNROOL_3(5, qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(6, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + } + } + } + else { + if(order == 6) { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], 0, 0}; + + tmp_vec[0] = vld1q_s32(data - 6); + tmp_vec[1] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + tmp_vec[2] = vld1q_s32(data + i - 4); + tmp_vec[3] = vld1q_s32(data + i - 3); + tmp_vec[4] = vld1q_s32(data + i - 2); + tmp_vec[5] = vld1q_s32(data + i - 1); + tmp_vec[6] = vld1q_s32(data + i - 0); + tmp_vec[7] = vld1q_s32(data + i + 1); + tmp_vec[8] = vld1q_s32(data + i + 2); + tmp_vec[9] = vld1q_s32(data + i + 3); + tmp_vec[10] = vld1q_s32(data + i + 4); + tmp_vec[11] = vld1q_s32(data + i + 5); + tmp_vec[12] = vld1q_s32(data + i + 6); + tmp_vec[13] = vld1q_s32(data + i + 7); + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_1, 1) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_1, 0) + MACC_32BIT_LOOP_UNROOL_3(2, qlp_coeff_0, 3) + MACC_32BIT_LOOP_UNROOL_3(3, qlp_coeff_0, 2) + MACC_32BIT_LOOP_UNROOL_3(4, qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(5, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + } + } + else { /* order == 5 */ + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], 0, 0, 0}; + + tmp_vec[0] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + + tmp_vec[1] = vld1q_s32(data + i - 4); + tmp_vec[2] = vld1q_s32(data + i - 3); + tmp_vec[3] = vld1q_s32(data + i - 2); + tmp_vec[4] = vld1q_s32(data + i - 1); + tmp_vec[5] = vld1q_s32(data + i - 0); + tmp_vec[6] = vld1q_s32(data + i + 1); + tmp_vec[7] = vld1q_s32(data + i + 2); + tmp_vec[8] = vld1q_s32(data + i + 3); + tmp_vec[9] = vld1q_s32(data + i + 4); + tmp_vec[10] = vld1q_s32(data + i + 5); + tmp_vec[11] = vld1q_s32(data + i + 6); + tmp_vec[12] = vld1q_s32(data + i + 7); + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_1, 0) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_0, 3) + MACC_32BIT_LOOP_UNROOL_3(2, qlp_coeff_0, 2) + MACC_32BIT_LOOP_UNROOL_3(3, qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(4, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + + tmp_vec[0] = tmp_vec[12]; + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + tmp_vec[0] = vld1q_s32(data + i - 4); + tmp_vec[1] = vld1q_s32(data + i - 3); + tmp_vec[2] = vld1q_s32(data + i - 2); + tmp_vec[3] = vld1q_s32(data + i - 1); + tmp_vec[4] = vld1q_s32(data + i - 0); + tmp_vec[5] = vld1q_s32(data + i + 1); + tmp_vec[6] = vld1q_s32(data + i + 2); + tmp_vec[7] = vld1q_s32(data + i + 3); + tmp_vec[8] = vld1q_s32(data + i + 4); + tmp_vec[9] = vld1q_s32(data + i + 5); + tmp_vec[10] = vld1q_s32(data + i + 6); + tmp_vec[11] = vld1q_s32(data + i + 7); + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_0, 3) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_0, 2) + MACC_32BIT_LOOP_UNROOL_3(2, qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(3, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + } + } + else { /* order == 3 */ + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], 0}; + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + tmp_vec[0] = vld1q_s32(data + i - 3); + tmp_vec[1] = vld1q_s32(data + i - 2); + tmp_vec[2] = vld1q_s32(data + i - 1); + tmp_vec[4] = vld1q_s32(data + i + 1); + tmp_vec[5] = vld1q_s32(data + i + 2); + tmp_vec[6] = vld1q_s32(data + i + 3); + tmp_vec[8] = vld1q_s32(data + i + 5); + tmp_vec[9] = vld1q_s32(data + i + 6); + tmp_vec[10] = vld1q_s32(data + i + 7); + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_0, 2) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(2, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + } + } + } + else { + if(order == 2) { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], 0, 0}; + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + tmp_vec[0] = vld1q_s32(data + i - 2); + tmp_vec[1] = vld1q_s32(data + i - 1); + tmp_vec[4] = vld1q_s32(data + i + 2); + tmp_vec[5] = vld1q_s32(data + i + 3); + tmp_vec[8] = vld1q_s32(data + i + 6); + tmp_vec[9] = vld1q_s32(data + i + 7); + + MUL_32_BIT_LOOP_UNROOL_3(qlp_coeff_0, 1) + MACC_32BIT_LOOP_UNROOL_3(1, qlp_coeff_0, 0) + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + } + } + else { /* order == 1 */ + int32x4_t qlp_coeff_0 = vdupq_n_s32(qlp_coeff[0]); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int32x4_t summ_0, summ_1, summ_2; + tmp_vec[0] = vld1q_s32(data + i - 1); + tmp_vec[4] = vld1q_s32(data + i + 3); + tmp_vec[8] = vld1q_s32(data + i + 7); + + summ_0 = vmulq_s32(tmp_vec[0], qlp_coeff_0); + summ_1 = vmulq_s32(tmp_vec[4], qlp_coeff_0); + summ_2 = vmulq_s32(tmp_vec[8], qlp_coeff_0); + + vst1q_s32(residual+i + 0, vsubq_s32(vld1q_s32(data+i + 0) , vshlq_s32(summ_0,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 4, vsubq_s32(vld1q_s32(data+i + 4) , vshlq_s32(summ_1,vdupq_n_s32(-lp_quantization)))); + vst1q_s32(residual+i + 8, vsubq_s32(vld1q_s32(data+i + 8) , vshlq_s32(summ_2,vdupq_n_s32(-lp_quantization)))); + } + } + } + } + for(; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 12: sum += qlp_coeff[11] * data[i-12]; /* Falls through. */ + case 11: sum += qlp_coeff[10] * data[i-11]; /* Falls through. */ + case 10: sum += qlp_coeff[ 9] * data[i-10]; /* Falls through. */ + case 9: sum += qlp_coeff[ 8] * data[i- 9]; /* Falls through. */ + case 8: sum += qlp_coeff[ 7] * data[i- 8]; /* Falls through. */ + case 7: sum += qlp_coeff[ 6] * data[i- 7]; /* Falls through. */ + case 6: sum += qlp_coeff[ 5] * data[i- 6]; /* Falls through. */ + case 5: sum += qlp_coeff[ 4] * data[i- 5]; /* Falls through. */ + case 4: sum += qlp_coeff[ 3] * data[i- 4]; /* Falls through. */ + case 3: sum += qlp_coeff[ 2] * data[i- 3]; /* Falls through. */ + case 2: sum += qlp_coeff[ 1] * data[i- 2]; /* Falls through. */ + case 1: sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * data[i-14]; /* Falls through. */ + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } +} + + + +#define MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_vec, lane) \ + summ_l_0 = vmull_laneq_s32(vget_low_s32(tmp_vec[0]),qlp_coeff_vec, lane); \ + summ_h_0 = vmull_high_laneq_s32(tmp_vec[0], qlp_coeff_vec, lane);\ + summ_l_1 = vmull_laneq_s32(vget_low_s32(tmp_vec[4]),qlp_coeff_vec, lane); \ + summ_h_1 = vmull_high_laneq_s32(tmp_vec[4], qlp_coeff_vec, lane);\ + summ_l_2 = vmull_laneq_s32(vget_low_s32(tmp_vec[8]),qlp_coeff_vec, lane);\ + summ_h_2 = vmull_high_laneq_s32(tmp_vec[8], qlp_coeff_vec, lane); + + +#define MACC_64_BIT_LOOP_UNROOL_3(tmp_vec_ind, qlp_coeff_vec, lane) \ + summ_l_0 = vmlal_laneq_s32(summ_l_0,vget_low_s32(tmp_vec[tmp_vec_ind]),qlp_coeff_vec, lane); \ + summ_h_0 = vmlal_high_laneq_s32(summ_h_0, tmp_vec[tmp_vec_ind], qlp_coeff_vec, lane); \ + summ_l_1 = vmlal_laneq_s32(summ_l_1, vget_low_s32(tmp_vec[tmp_vec_ind+4]),qlp_coeff_vec, lane); \ + summ_h_1 = vmlal_high_laneq_s32(summ_h_1, tmp_vec[tmp_vec_ind+4], qlp_coeff_vec, lane); \ + summ_l_2 = vmlal_laneq_s32(summ_l_2, vget_low_s32(tmp_vec[tmp_vec_ind+8]),qlp_coeff_vec, lane);\ + summ_h_2 = vmlal_high_laneq_s32(summ_h_2,tmp_vec[tmp_vec_ind+8], qlp_coeff_vec, lane); + +#define SHIFT_SUMS_64BITS_AND_STORE_SUB() \ + res0 = vuzp1q_s32(vreinterpretq_s32_s64(vshlq_s64(summ_l_0,lp_quantization_vec)), vreinterpretq_s32_s64(vshlq_s64(summ_h_0,lp_quantization_vec))); \ + res1 = vuzp1q_s32(vreinterpretq_s32_s64(vshlq_s64(summ_l_1,lp_quantization_vec)), vreinterpretq_s32_s64(vshlq_s64(summ_h_1,lp_quantization_vec))); \ + res2 = vuzp1q_s32(vreinterpretq_s32_s64(vshlq_s64(summ_l_2,lp_quantization_vec)), vreinterpretq_s32_s64(vshlq_s64(summ_h_2,lp_quantization_vec))); \ + vst1q_s32(residual+i+0, vsubq_s32(vld1q_s32(data+i+0), res0));\ + vst1q_s32(residual+i+4, vsubq_s32(vld1q_s32(data+i+4), res1));\ + vst1q_s32(residual+i+8, vsubq_s32(vld1q_s32(data+i+8), res2)); + +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_neon(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]) { + int i; + FLAC__int64 sum; + + int32x4_t tmp_vec[20]; + int32x4_t res0, res1, res2; + int64x2_t lp_quantization_vec = vdupq_n_s64(-lp_quantization); + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + // Using prologue reads is valid as encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit(signal+order,....) + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4],qlp_coeff[5],qlp_coeff[6],qlp_coeff[7]}; + int32x4_t qlp_coeff_2 = {qlp_coeff[8],qlp_coeff[9],qlp_coeff[10],qlp_coeff[11]}; + + tmp_vec[0] = vld1q_s32(data - 12); + tmp_vec[1] = vld1q_s32(data - 11); + tmp_vec[2] = vld1q_s32(data - 10); + tmp_vec[3] = vld1q_s32(data - 9); + tmp_vec[4] = vld1q_s32(data - 8); + tmp_vec[5] = vld1q_s32(data - 7); + tmp_vec[6] = vld1q_s32(data - 6); + tmp_vec[7] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + + tmp_vec[8] = vld1q_s32(data+i-4); + tmp_vec[9] = vld1q_s32(data+i-3); + tmp_vec[10] = vld1q_s32(data+i-2); + tmp_vec[11] = vld1q_s32(data+i-1); + tmp_vec[12] = vld1q_s32(data+i); + tmp_vec[13] = vld1q_s32(data+i+1); + tmp_vec[14] = vld1q_s32(data+i+2); + tmp_vec[15] = vld1q_s32(data+i+3); + tmp_vec[16] = vld1q_s32(data + i + 4); + tmp_vec[17] = vld1q_s32(data + i + 5); + tmp_vec[18] = vld1q_s32(data + i + 6); + tmp_vec[19] = vld1q_s32(data + i + 7); + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_2, 3) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_2, 2) + MACC_64_BIT_LOOP_UNROOL_3(2, qlp_coeff_2, 1) + MACC_64_BIT_LOOP_UNROOL_3(3, qlp_coeff_2, 0) + MACC_64_BIT_LOOP_UNROOL_3(4, qlp_coeff_1, 3) + MACC_64_BIT_LOOP_UNROOL_3(5, qlp_coeff_1, 2) + MACC_64_BIT_LOOP_UNROOL_3(6, qlp_coeff_1, 1) + MACC_64_BIT_LOOP_UNROOL_3(7, qlp_coeff_1, 0) + MACC_64_BIT_LOOP_UNROOL_3(8, qlp_coeff_0, 3) + MACC_64_BIT_LOOP_UNROOL_3(9, qlp_coeff_0, 2) + MACC_64_BIT_LOOP_UNROOL_3(10,qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(11,qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + tmp_vec[3] = tmp_vec[15]; + tmp_vec[4] = tmp_vec[16]; + tmp_vec[5] = tmp_vec[17]; + tmp_vec[6] = tmp_vec[18]; + tmp_vec[7] = tmp_vec[19]; + } + } + else { /* order == 11 */ + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4],qlp_coeff[5],qlp_coeff[6],qlp_coeff[7]}; + int32x4_t qlp_coeff_2 = {qlp_coeff[8],qlp_coeff[9],qlp_coeff[10],0}; + + tmp_vec[0] = vld1q_s32(data - 11); + tmp_vec[1] = vld1q_s32(data - 10); + tmp_vec[2] = vld1q_s32(data - 9); + tmp_vec[3] = vld1q_s32(data - 8); + tmp_vec[4] = vld1q_s32(data - 7); + tmp_vec[5] = vld1q_s32(data - 6); + tmp_vec[6] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + + tmp_vec[7] = vld1q_s32(data+i-4); + tmp_vec[8] = vld1q_s32(data+i-3); + tmp_vec[9] = vld1q_s32(data+i-2); + tmp_vec[10] = vld1q_s32(data+i-1); + tmp_vec[11] = vld1q_s32(data+i); + tmp_vec[12] = vld1q_s32(data+i+1); + tmp_vec[13] = vld1q_s32(data+i+2); + tmp_vec[14] = vld1q_s32(data+i+3); + tmp_vec[15] = vld1q_s32(data + i + 4); + tmp_vec[16] = vld1q_s32(data + i + 5); + tmp_vec[17] = vld1q_s32(data + i + 6); + tmp_vec[18] = vld1q_s32(data + i + 7); + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_2, 2) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_2, 1) + MACC_64_BIT_LOOP_UNROOL_3(2, qlp_coeff_2, 0) + MACC_64_BIT_LOOP_UNROOL_3(3, qlp_coeff_1, 3) + MACC_64_BIT_LOOP_UNROOL_3(4, qlp_coeff_1, 2) + MACC_64_BIT_LOOP_UNROOL_3(5, qlp_coeff_1, 1) + MACC_64_BIT_LOOP_UNROOL_3(6, qlp_coeff_1, 0) + MACC_64_BIT_LOOP_UNROOL_3(7, qlp_coeff_0, 3) + MACC_64_BIT_LOOP_UNROOL_3(8, qlp_coeff_0, 2) + MACC_64_BIT_LOOP_UNROOL_3(9, qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(10,qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + tmp_vec[3] = tmp_vec[15]; + tmp_vec[4] = tmp_vec[16]; + tmp_vec[5] = tmp_vec[17]; + tmp_vec[6] = tmp_vec[18]; + } + } + } + else + { + if (order == 10) { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], qlp_coeff[6], qlp_coeff[7]}; + int32x4_t qlp_coeff_2 = {qlp_coeff[8], qlp_coeff[9], 0, 0}; + + tmp_vec[0] = vld1q_s32(data - 10); + tmp_vec[1] = vld1q_s32(data - 9); + tmp_vec[2] = vld1q_s32(data - 8); + tmp_vec[3] = vld1q_s32(data - 7); + tmp_vec[4] = vld1q_s32(data - 6); + tmp_vec[5] = vld1q_s32(data - 5); + + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + + tmp_vec[6] = vld1q_s32(data + i - 4); + tmp_vec[7] = vld1q_s32(data + i - 3); + tmp_vec[8] = vld1q_s32(data + i - 2); + tmp_vec[9] = vld1q_s32(data + i - 1); + tmp_vec[10] = vld1q_s32(data + i - 0); + tmp_vec[11] = vld1q_s32(data + i + 1); + tmp_vec[12] = vld1q_s32(data + i + 2); + tmp_vec[13] = vld1q_s32(data + i + 3); + tmp_vec[14] = vld1q_s32(data + i + 4); + tmp_vec[15] = vld1q_s32(data + i + 5); + tmp_vec[16] = vld1q_s32(data + i + 6); + tmp_vec[17] = vld1q_s32(data + i + 7); + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_2, 1) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_2, 0) + MACC_64_BIT_LOOP_UNROOL_3(2, qlp_coeff_1, 3) + MACC_64_BIT_LOOP_UNROOL_3(3, qlp_coeff_1, 2) + MACC_64_BIT_LOOP_UNROOL_3(4, qlp_coeff_1, 1) + MACC_64_BIT_LOOP_UNROOL_3(5, qlp_coeff_1, 0) + MACC_64_BIT_LOOP_UNROOL_3(6, qlp_coeff_0, 3) + MACC_64_BIT_LOOP_UNROOL_3(7, qlp_coeff_0, 2) + MACC_64_BIT_LOOP_UNROOL_3(8, qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(9, qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + tmp_vec[3] = tmp_vec[15]; + tmp_vec[4] = tmp_vec[16]; + tmp_vec[5] = tmp_vec[17]; + } + } + + else /* order == 9 */ { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], qlp_coeff[6], qlp_coeff[7]}; + int32x4_t qlp_coeff_2 = {qlp_coeff[8], 0, 0, 0}; + + tmp_vec[0] = vld1q_s32(data - 9); + tmp_vec[1] = vld1q_s32(data - 8); + tmp_vec[2] = vld1q_s32(data - 7); + tmp_vec[3] = vld1q_s32(data - 6); + tmp_vec[4] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + + tmp_vec[5] = vld1q_s32(data + i - 4); + tmp_vec[6] = vld1q_s32(data + i - 3); + tmp_vec[7] = vld1q_s32(data + i - 2); + tmp_vec[8] = vld1q_s32(data + i - 1); + tmp_vec[9] = vld1q_s32(data + i - 0); + tmp_vec[10] = vld1q_s32(data + i + 1); + tmp_vec[11] = vld1q_s32(data + i + 2); + tmp_vec[12] = vld1q_s32(data + i + 3); + tmp_vec[13] = vld1q_s32(data + i + 4); + tmp_vec[14] = vld1q_s32(data + i + 5); + tmp_vec[15] = vld1q_s32(data + i + 6); + tmp_vec[16] = vld1q_s32(data + i + 7); + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_2, 0) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_1, 3) + MACC_64_BIT_LOOP_UNROOL_3(2, qlp_coeff_1, 2) + MACC_64_BIT_LOOP_UNROOL_3(3, qlp_coeff_1, 1) + MACC_64_BIT_LOOP_UNROOL_3(4, qlp_coeff_1, 0) + MACC_64_BIT_LOOP_UNROOL_3(5, qlp_coeff_0, 3) + MACC_64_BIT_LOOP_UNROOL_3(6, qlp_coeff_0, 2) + MACC_64_BIT_LOOP_UNROOL_3(7, qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(8, qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + tmp_vec[3] = tmp_vec[15]; + tmp_vec[4] = tmp_vec[16]; + } + } + } + } + else if (order > 4) + { + if (order > 6) + { + if (order == 8) + { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], qlp_coeff[6], qlp_coeff[7]}; + + tmp_vec[0] = vld1q_s32(data - 8); + tmp_vec[1] = vld1q_s32(data - 7); + tmp_vec[2] = vld1q_s32(data - 6); + tmp_vec[3] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + + tmp_vec[4] = vld1q_s32(data + i - 4); + tmp_vec[5] = vld1q_s32(data + i - 3); + tmp_vec[6] = vld1q_s32(data + i - 2); + tmp_vec[7] = vld1q_s32(data + i - 1); + tmp_vec[8] = vld1q_s32(data + i - 0); + tmp_vec[9] = vld1q_s32(data + i + 1); + tmp_vec[10] = vld1q_s32(data + i + 2); + tmp_vec[11] = vld1q_s32(data + i + 3); + tmp_vec[12] = vld1q_s32(data + i + 4); + tmp_vec[13] = vld1q_s32(data + i + 5); + tmp_vec[14] = vld1q_s32(data + i + 6); + tmp_vec[15] = vld1q_s32(data + i + 7); + + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_1, 3) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_1, 2) + MACC_64_BIT_LOOP_UNROOL_3(2, qlp_coeff_1, 1) + MACC_64_BIT_LOOP_UNROOL_3(3, qlp_coeff_1, 0) + MACC_64_BIT_LOOP_UNROOL_3(4, qlp_coeff_0, 3) + MACC_64_BIT_LOOP_UNROOL_3(5, qlp_coeff_0, 2) + MACC_64_BIT_LOOP_UNROOL_3(6, qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(7, qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + tmp_vec[3] = tmp_vec[15]; + } + } + else /* order == 7 */ + { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], qlp_coeff[6], 0}; + + tmp_vec[0] = vld1q_s32(data - 7); + tmp_vec[1] = vld1q_s32(data - 6); + tmp_vec[2] = vld1q_s32(data - 5); + + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + tmp_vec[3] = vld1q_s32(data +i - 4); + tmp_vec[4] = vld1q_s32(data + i - 3); + tmp_vec[5] = vld1q_s32(data + i - 2); + tmp_vec[6] = vld1q_s32(data + i - 1); + tmp_vec[7] = vld1q_s32(data + i - 0); + tmp_vec[8] = vld1q_s32(data + i + 1); + tmp_vec[9] = vld1q_s32(data + i + 2); + tmp_vec[10] = vld1q_s32(data + i + 3); + tmp_vec[11] = vld1q_s32(data + i + 4); + tmp_vec[12] = vld1q_s32(data + i + 5); + tmp_vec[13] = vld1q_s32(data + i + 6); + tmp_vec[14] = vld1q_s32(data + i + 7); + + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_1, 2) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_1, 1) + MACC_64_BIT_LOOP_UNROOL_3(2, qlp_coeff_1, 0) + MACC_64_BIT_LOOP_UNROOL_3(3, qlp_coeff_0, 3) + MACC_64_BIT_LOOP_UNROOL_3(4, qlp_coeff_0, 2) + MACC_64_BIT_LOOP_UNROOL_3(5, qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(6, qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + tmp_vec[2] = tmp_vec[14]; + } + } + } + else + { + if (order == 6) { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], qlp_coeff[5], 0, 0}; + + tmp_vec[0] = vld1q_s32(data - 6); + tmp_vec[1] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + + tmp_vec[2] = vld1q_s32(data + i - 4); + tmp_vec[3] = vld1q_s32(data + i - 3); + tmp_vec[4] = vld1q_s32(data + i - 2); + tmp_vec[5] = vld1q_s32(data + i - 1); + tmp_vec[6] = vld1q_s32(data + i - 0); + tmp_vec[7] = vld1q_s32(data + i + 1); + tmp_vec[8] = vld1q_s32(data + i + 2); + tmp_vec[9] = vld1q_s32(data + i + 3); + tmp_vec[10] = vld1q_s32(data + i + 4); + tmp_vec[11] = vld1q_s32(data + i + 5); + tmp_vec[12] = vld1q_s32(data + i + 6); + tmp_vec[13] = vld1q_s32(data + i + 7); + + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_1, 1) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_1, 0) + MACC_64_BIT_LOOP_UNROOL_3(2, qlp_coeff_0, 3) + MACC_64_BIT_LOOP_UNROOL_3(3, qlp_coeff_0, 2) + MACC_64_BIT_LOOP_UNROOL_3(4, qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(5, qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + + tmp_vec[0] = tmp_vec[12]; + tmp_vec[1] = tmp_vec[13]; + } + } + + else + { /* order == 5 */ + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + int32x4_t qlp_coeff_1 = {qlp_coeff[4], 0, 0, 0}; + + tmp_vec[0] = vld1q_s32(data - 5); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + tmp_vec[1] = vld1q_s32(data + i - 4); + tmp_vec[2] = vld1q_s32(data + i - 3); + tmp_vec[3] = vld1q_s32(data + i - 2); + tmp_vec[4] = vld1q_s32(data + i - 1); + tmp_vec[5] = vld1q_s32(data + i - 0); + tmp_vec[6] = vld1q_s32(data + i + 1); + tmp_vec[7] = vld1q_s32(data + i + 2); + tmp_vec[8] = vld1q_s32(data + i + 3); + tmp_vec[9] = vld1q_s32(data + i + 4); + tmp_vec[10] = vld1q_s32(data + i + 5); + tmp_vec[11] = vld1q_s32(data + i + 6); + tmp_vec[12] = vld1q_s32(data + i + 7); + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_1, 0) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_0, 3) + MACC_64_BIT_LOOP_UNROOL_3(2, qlp_coeff_0, 2) + MACC_64_BIT_LOOP_UNROOL_3(3, qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(4, qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + + tmp_vec[0] = tmp_vec[12]; + } + } + } + } + else + { + if (order > 2) + { + if (order == 4) + { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], qlp_coeff[3]}; + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + tmp_vec[0] = vld1q_s32(data + i - 4); + tmp_vec[1] = vld1q_s32(data + i - 3); + tmp_vec[2] = vld1q_s32(data + i - 2); + tmp_vec[3] = vld1q_s32(data + i - 1); + tmp_vec[4] = vld1q_s32(data + i - 0); + tmp_vec[5] = vld1q_s32(data + i + 1); + tmp_vec[6] = vld1q_s32(data + i + 2); + tmp_vec[7] = vld1q_s32(data + i + 3); + tmp_vec[8] = vld1q_s32(data + i + 4); + tmp_vec[9] = vld1q_s32(data + i + 5); + tmp_vec[10] = vld1q_s32(data + i + 6); + tmp_vec[11] = vld1q_s32(data + i + 7); + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_0, 3) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_0, 2) + MACC_64_BIT_LOOP_UNROOL_3(2, qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(3, qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + } + } + else + { /* order == 3 */ + + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], qlp_coeff[2], 0}; + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + tmp_vec[0] = vld1q_s32(data + i - 3); + tmp_vec[1] = vld1q_s32(data + i - 2); + tmp_vec[2] = vld1q_s32(data + i - 1); + tmp_vec[4] = vld1q_s32(data + i + 1); + tmp_vec[5] = vld1q_s32(data + i + 2); + tmp_vec[6] = vld1q_s32(data + i + 3); + tmp_vec[8] = vld1q_s32(data + i + 5); + tmp_vec[9] = vld1q_s32(data + i + 6); + tmp_vec[10] = vld1q_s32(data + i + 7); + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_0, 2) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(2, qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + } + } + } + else + { + if (order == 2) + { + int32x4_t qlp_coeff_0 = {qlp_coeff[0], qlp_coeff[1], 0, 0}; + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + tmp_vec[0] = vld1q_s32(data + i - 2); + tmp_vec[1] = vld1q_s32(data + i - 1); + tmp_vec[4] = vld1q_s32(data + i + 2); + tmp_vec[5] = vld1q_s32(data + i + 3); + tmp_vec[8] = vld1q_s32(data + i + 6); + tmp_vec[9] = vld1q_s32(data + i + 7); + + MUL_64_BIT_LOOP_UNROOL_3(qlp_coeff_0, 1) + MACC_64_BIT_LOOP_UNROOL_3(1, qlp_coeff_0, 0) + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + } + } + + else + { /* order == 1 */ + + int32x2_t qlp_coeff_0_2 = vdup_n_s32(qlp_coeff[0]); + int32x4_t qlp_coeff_0_4 = vdupq_n_s32(qlp_coeff[0]); + + for (i = 0; i < (int)data_len - 11; i += 12) + { + int64x2_t summ_l_0, summ_h_0, summ_l_1, summ_h_1, summ_l_2, summ_h_2; + tmp_vec[0] = vld1q_s32(data + i - 1); + tmp_vec[4] = vld1q_s32(data + i + 3); + tmp_vec[8] = vld1q_s32(data + i + 7); + + summ_l_0 = vmull_s32(vget_low_s32(tmp_vec[0]), qlp_coeff_0_2); + summ_h_0 = vmull_high_s32(tmp_vec[0], qlp_coeff_0_4); + + summ_l_1 = vmull_s32(vget_low_s32(tmp_vec[4]), qlp_coeff_0_2); + summ_h_1 = vmull_high_s32(tmp_vec[4], qlp_coeff_0_4); + + summ_l_2 = vmull_s32(vget_low_s32(tmp_vec[8]), qlp_coeff_0_2); + summ_h_2 = vmull_high_s32(tmp_vec[8], qlp_coeff_0_4); + + SHIFT_SUMS_64BITS_AND_STORE_SUB() + } + } + } + } + for (; i < (int)data_len; i++) + { + sum = 0; + switch (order) + { + case 12: + sum += qlp_coeff[11] * (FLAC__int64)data[i - 12]; /* Falls through. */ + case 11: + sum += qlp_coeff[10] * (FLAC__int64)data[i - 11]; /* Falls through. */ + case 10: + sum += qlp_coeff[9] * (FLAC__int64)data[i - 10]; /* Falls through. */ + case 9: + sum += qlp_coeff[8] * (FLAC__int64)data[i - 9]; /* Falls through. */ + case 8: + sum += qlp_coeff[7] * (FLAC__int64)data[i - 8]; /* Falls through. */ + case 7: + sum += qlp_coeff[6] * (FLAC__int64)data[i - 7]; /* Falls through. */ + case 6: + sum += qlp_coeff[5] * (FLAC__int64)data[i - 6]; /* Falls through. */ + case 5: + sum += qlp_coeff[4] * (FLAC__int64)data[i - 5]; /* Falls through. */ + case 4: + sum += qlp_coeff[3] * (FLAC__int64)data[i - 4]; /* Falls through. */ + case 3: + sum += qlp_coeff[2] * (FLAC__int64)data[i - 3]; /* Falls through. */ + case 2: + sum += qlp_coeff[1] * (FLAC__int64)data[i - 2]; /* Falls through. */ + case 1: + sum += qlp_coeff[0] * (FLAC__int64)data[i - 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else + { /* order > 12 */ + for (i = 0; i < (int)data_len; i++) + { + sum = 0; + switch (order) + { + case 32: + sum += qlp_coeff[31] * (FLAC__int64)data[i - 32]; /* Falls through. */ + case 31: + sum += qlp_coeff[30] * (FLAC__int64)data[i - 31]; /* Falls through. */ + case 30: + sum += qlp_coeff[29] * (FLAC__int64)data[i - 30]; /* Falls through. */ + case 29: + sum += qlp_coeff[28] * (FLAC__int64)data[i - 29]; /* Falls through. */ + case 28: + sum += qlp_coeff[27] * (FLAC__int64)data[i - 28]; /* Falls through. */ + case 27: + sum += qlp_coeff[26] * (FLAC__int64)data[i - 27]; /* Falls through. */ + case 26: + sum += qlp_coeff[25] * (FLAC__int64)data[i - 26]; /* Falls through. */ + case 25: + sum += qlp_coeff[24] * (FLAC__int64)data[i - 25]; /* Falls through. */ + case 24: + sum += qlp_coeff[23] * (FLAC__int64)data[i - 24]; /* Falls through. */ + case 23: + sum += qlp_coeff[22] * (FLAC__int64)data[i - 23]; /* Falls through. */ + case 22: + sum += qlp_coeff[21] * (FLAC__int64)data[i - 22]; /* Falls through. */ + case 21: + sum += qlp_coeff[20] * (FLAC__int64)data[i - 21]; /* Falls through. */ + case 20: + sum += qlp_coeff[19] * (FLAC__int64)data[i - 20]; /* Falls through. */ + case 19: + sum += qlp_coeff[18] * (FLAC__int64)data[i - 19]; /* Falls through. */ + case 18: + sum += qlp_coeff[17] * (FLAC__int64)data[i - 18]; /* Falls through. */ + case 17: + sum += qlp_coeff[16] * (FLAC__int64)data[i - 17]; /* Falls through. */ + case 16: + sum += qlp_coeff[15] * (FLAC__int64)data[i - 16]; /* Falls through. */ + case 15: + sum += qlp_coeff[14] * (FLAC__int64)data[i - 15]; /* Falls through. */ + case 14: + sum += qlp_coeff[13] * (FLAC__int64)data[i - 14]; /* Falls through. */ + case 13: + sum += qlp_coeff[12] * (FLAC__int64)data[i - 13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i - 12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i - 11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i - 10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i - 9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i - 8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i - 7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i - 6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i - 5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i - 4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i - 3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i - 2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i - 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } + + return; +} + +#endif /* FLAC__CPU_ARM64 && FLAC__HAS_ARCH64INTRIN */ +#endif /* FLAC__NO_ASM */ +#endif /* FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c index a16be893..c46ed610 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c @@ -7,6 +7,7 @@ #include "include/private/md5.h" #include "../alloc.h" +#include "../compat.h" #include "../endswap.h" /* @@ -136,9 +137,9 @@ static void FLAC__MD5Transform(FLAC__uint32 buf[4], FLAC__uint32 const in[16]) #if WORDS_BIGENDIAN //@@@@@@ OPT: use bswap/intrinsics -static void byteSwap(FLAC__uint32 *buf, unsigned words) +static void byteSwap(FLAC__uint32 *buf, uint32_t words) { - FLAC__uint32 x; + register FLAC__uint32 x; do { x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); @@ -147,7 +148,7 @@ static void byteSwap(FLAC__uint32 *buf, unsigned words) } static void byteSwapX16(FLAC__uint32 *buf) { - FLAC__uint32 x; + register FLAC__uint32 x; x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); @@ -175,7 +176,7 @@ static void byteSwapX16(FLAC__uint32 *buf) * Update context to reflect the concatenation of another buffer full * of bytes. */ -static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, unsigned len) +static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, uint32_t len) { FLAC__uint32 t; @@ -224,7 +225,7 @@ void FLAC__MD5Init(FLAC__MD5Context *ctx) ctx->bytes[0] = 0; ctx->bytes[1] = 0; - ctx->internal_buf.p8= 0; + ctx->internal_buf.p8 = 0; ctx->capacity = 0; } @@ -262,7 +263,7 @@ void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) memcpy(digest, ctx->buf, 16); if (0 != ctx->internal_buf.p8) { free(ctx->internal_buf.p8); - ctx->internal_buf.p8= 0; + ctx->internal_buf.p8 = 0; ctx->capacity = 0; } memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ @@ -271,13 +272,13 @@ void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) /* * Convert the incoming audio signal to a byte stream */ -static void format_input_(FLAC__multibyte *mbuf, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +static void format_input_(FLAC__multibyte *mbuf, const FLAC__int32 * const signal[], uint32_t channels, uint32_t samples, uint32_t bytes_per_sample) { FLAC__byte *buf_ = mbuf->p8; FLAC__int16 *buf16 = mbuf->p16; FLAC__int32 *buf32 = mbuf->p32; FLAC__int32 a_word; - unsigned channel, sample; + uint32_t channel, sample; /* Storage in the output buffer, buf, is little endian. */ @@ -488,7 +489,7 @@ static void format_input_(FLAC__multibyte *mbuf, const FLAC__int32 * const signa /* * Convert the incoming audio signal to a byte stream and FLAC__MD5Update it. */ -FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], uint32_t channels, uint32_t samples, uint32_t bytes_per_sample) { const size_t bytes_needed = (size_t)channels * (size_t)samples * (size_t)bytes_per_sample; @@ -499,14 +500,12 @@ FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const return false; if (ctx->capacity < bytes_needed) { - FLAC__byte *tmp = (FLAC__byte*) realloc(ctx->internal_buf.p8, bytes_needed); - if (0 == tmp) { - free(ctx->internal_buf.p8); - if (0 == (ctx->internal_buf.p8= (FLAC__byte*) safe_malloc_(bytes_needed))) + if (0 == (ctx->internal_buf.p8 = (FLAC__byte*) safe_realloc_(ctx->internal_buf.p8, bytes_needed))) { + if (0 == (ctx->internal_buf.p8 = (FLAC__byte*) safe_malloc_(bytes_needed))) { + ctx->capacity = 0; return false; + } } - else - ctx->internal_buf.p8= tmp; ctx->capacity = bytes_needed; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c index b032a494..021cbb51 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,6 +40,7 @@ #include "include/private/memory.h" #include "../assert.h" +#include "../compat.h" #include "../alloc.h" void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) @@ -117,11 +118,11 @@ FLAC__bool FLAC__memory_alloc_aligned_uint32_array(size_t elements, FLAC__uint32 } } -FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer) +FLAC__bool FLAC__memory_alloc_aligned_int64_array(size_t elements, FLAC__int64 **unaligned_pointer, FLAC__int64 **aligned_pointer) { - FLAC__uint64 *pu; /* unaligned pointer */ + FLAC__int64 *pu; /* unaligned pointer */ union { /* union needed to comply with C99 pointer aliasing rules */ - FLAC__uint64 *pa; /* aligned pointer */ + FLAC__int64 *pa; /* aligned pointer */ void *pv; /* aligned pointer alias */ } u; @@ -133,7 +134,7 @@ FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ return false; - pu = (FLAC__uint64*) FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + pu = (FLAC__int64*) FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); if(0 == pu) { return false; } @@ -146,12 +147,12 @@ FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 } } -FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, unsigned **unaligned_pointer, unsigned **aligned_pointer) +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer) { - unsigned *pu; /* unaligned pointer */ + FLAC__uint64 *pu; /* unaligned pointer */ union { /* union needed to comply with C99 pointer aliasing rules */ - unsigned *pa; /* aligned pointer */ - void *pv; /* aligned pointer alias */ + FLAC__uint64 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ } u; FLAC__ASSERT(elements > 0); @@ -162,7 +163,7 @@ FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, unsigned * if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ return false; - pu = (unsigned int*) FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + pu = (FLAC__uint64*) FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); if(0 == pu) { return false; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c index a6af8d8f..bb0ea5dd 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,8 +37,8 @@ #include #include /* for malloc() */ #include /* for memset/memcpy() */ -#include /* for stat() */ #include /* for off_t */ +#include /* for stat() */ #include "../compat.h" #include "../assert.h" #include "../alloc.h" @@ -54,14 +54,9 @@ #include "include/private/memory.h" + /* technically this should be in an "export.c" but this is convenient enough */ -FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC = -#if FLAC__HAS_OGG - 1 -#else - 0 -#endif -; +FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC = FLAC__HAS_OGG; /*********************************************************************** @@ -80,26 +75,27 @@ static const FLAC__byte ID3V2_TAG_[3] = { 'I', 'D', '3' }; static void set_defaults_(FLAC__StreamDecoder *decoder); //static FILE *get_binary_stdin_(void); -static FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels); +static FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, uint32_t size, uint32_t channels, uint32_t bps); static FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id); static FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder); static FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder); -static FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); -static FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); -static FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, unsigned length); +static FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length); +static FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length); +static FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, uint32_t length); static FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj); static FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj); static FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder); static FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder); static FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode); static FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder); -static FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); -static FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); -static FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode); -static FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode); -static FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); -static FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigned predictor_order, unsigned partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended); +static FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode); +static FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, uint32_t predictor_order, uint32_t partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended); static FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder); +static void undo_channel_coding(FLAC__StreamDecoder *decoder); static FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data); #if FLAC__HAS_OGG static FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes); @@ -124,9 +120,7 @@ static FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLA ***********************************************************************/ typedef struct FLAC__StreamDecoderPrivate { -#if FLAC__HAS_OGG FLAC__bool is_ogg; -#endif FLAC__StreamDecoderReadCallback read_callback; FLAC__StreamDecoderSeekCallback seek_callback; FLAC__StreamDecoderTellCallback tell_callback; @@ -135,19 +129,15 @@ typedef struct FLAC__StreamDecoderPrivate { FLAC__StreamDecoderWriteCallback write_callback; FLAC__StreamDecoderMetadataCallback metadata_callback; FLAC__StreamDecoderErrorCallback error_callback; - /* generic 32-bit datapath: */ - void (*local_lpc_restore_signal)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); - /* generic 64-bit datapath: */ - void (*local_lpc_restore_signal_64bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); - /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit): */ - void (*local_lpc_restore_signal_16bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); void *client_data; FILE *file; /* only used if FLAC__stream_decoder_init_file()/FLAC__stream_decoder_init_file() called, else NULL */ FLAC__BitReader *input; FLAC__int32 *output[FLAC__MAX_CHANNELS]; FLAC__int32 *residual[FLAC__MAX_CHANNELS]; /* WATCHOUT: these are the aligned pointers; the real pointers that should be free()'d are residual_unaligned[] below */ + FLAC__int64 *side_subframe; + FLAC__bool side_subframe_in_use; FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents[FLAC__MAX_CHANNELS]; - unsigned output_capacity, output_channels; + uint32_t output_capacity, output_channels; FLAC__uint32 fixed_block_size, next_fixed_block_size; FLAC__uint64 samples_decoded; FLAC__bool has_stream_info, has_seek_table; @@ -169,13 +159,14 @@ typedef struct FLAC__StreamDecoderPrivate { FLAC__MD5Context md5context; FLAC__byte computed_md5sum[16]; /* this is the sum we computed from the decoded data */ /* (the rest of these are only used for seeking) */ - FLAC__Frame last_frame; /* holds the info of the last frame we seeked to */ + FLAC__Frame last_frame; /* holds the info of the last frame we decoded or seeked to */ + FLAC__bool last_frame_is_set; FLAC__uint64 first_frame_offset; /* hint to the seek routine of where in the stream the first audio frame starts */ + FLAC__uint64 last_seen_framesync; /* if tell callback works, the location of the last seen frame sync code, to rewind to if needed */ FLAC__uint64 target_sample; - unsigned unparseable_frame_count; /* used to tell whether we're decoding a future version of FLAC or just got a bad sync */ -#if FLAC__HAS_OGG + uint32_t unparseable_frame_count; /* used to tell whether we're decoding a future version of FLAC or just got a bad sync */ FLAC__bool got_a_frame; /* hack needed in Ogg FLAC seek routine to check when process_single() actually writes a frame */ -#endif + FLAC__bool (*local_bitreader_read_rice_signed_block)(FLAC__BitReader *br, int vals[], uint32_t nvals, uint32_t parameter); } FLAC__StreamDecoderPrivate; /*********************************************************************** @@ -239,7 +230,8 @@ FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[] = { "FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC", "FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER", "FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH", - "FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM" + "FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM", + "FLAC__STREAM_DECODER_ERROR_STATUS_BAD_METADATA" }; /*********************************************************************** @@ -250,7 +242,7 @@ FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[] = { FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) { FLAC__StreamDecoder *decoder; - unsigned i; + uint32_t i; FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */ @@ -294,6 +286,8 @@ FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; } + decoder->private_->side_subframe = 0; + decoder->private_->output_capacity = 0; decoder->private_->output_channels = 0; decoder->private_->has_seek_table = false; @@ -312,7 +306,7 @@ FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder) { - unsigned i; + uint32_t i; if (decoder == NULL) return ; @@ -361,10 +355,10 @@ static FLAC__StreamDecoderInitStatus init_stream_internal_( if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) return FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; -#if !FLAC__HAS_OGG + #if FLAC__HAS_OGG == 0 if(is_ogg) return FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER; -#endif + #endif if( 0 == read_callback || @@ -380,46 +374,12 @@ static FLAC__StreamDecoderInitStatus init_stream_internal_( return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE; #endif - /* - * get the CPU info and set the function pointers - */ FLAC__cpu_info(&decoder->private_->cpuinfo); - /* first default to the non-asm routines */ - decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal; - decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide; - decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal; - /* now override with asm where appropriate */ -#ifndef FLAC__NO_ASM - if(decoder->private_->cpuinfo.use_asm) { -#ifdef FLAC__CPU_IA32 - FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_IA32); -#ifdef FLAC__HAS_NASM - decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide_asm_ia32; /* OPT_IA32: was really necessary for GCC < 4.9 */ - if(decoder->private_->cpuinfo.ia32.mmx) { - decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; - decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32_mmx; - } - else { - decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; - decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32; - } -#endif -#ifdef FLAC__HAS_X86INTRIN -# if defined FLAC__SSE2_SUPPORTED && !defined FLAC__HAS_NASM /* OPT_SSE: not better than MMX asm */ - if(decoder->private_->cpuinfo.ia32.sse2) { - decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_16_intrin_sse2; - } -# endif -# if defined FLAC__SSE4_1_SUPPORTED - if(decoder->private_->cpuinfo.ia32.sse41) { - decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide_intrin_sse41; - } -# endif -#endif -#elif defined FLAC__CPU_X86_64 - FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_X86_64); - /* No useful SSE optimizations yet */ -#endif + decoder->private_->local_bitreader_read_rice_signed_block = FLAC__bitreader_read_rice_signed_block; + +#ifdef FLAC__BMI2_SUPPORTED + if (decoder->private_->cpuinfo.x86.bmi2) { + decoder->private_->local_bitreader_read_rice_signed_block = FLAC__bitreader_read_rice_signed_block_bmi2; } #endif @@ -642,7 +602,7 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) { FLAC__bool md5_failed = false; - unsigned i; + uint32_t i; FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->private_); @@ -656,18 +616,17 @@ FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) */ FLAC__MD5Final(decoder->private_->computed_md5sum, &decoder->private_->md5context); - if(decoder->private_->has_seek_table && 0 != decoder->private_->seek_table.data.seek_table.points) { - free(decoder->private_->seek_table.data.seek_table.points); - decoder->private_->seek_table.data.seek_table.points = 0; - decoder->private_->has_seek_table = false; - } + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + FLAC__bitreader_free(decoder->private_->input); for(i = 0; i < FLAC__MAX_CHANNELS; i++) { /* WATCHOUT: - * FLAC__lpc_restore_signal_asm_ia32_mmx() requires that the - * output arrays have a buffer of up to 3 zeroes in front - * (at negative indices) for alignment purposes; we use 4 - * to keep the data well-aligned. + * FLAC__lpc_restore_signal_asm_ia32_mmx() and ..._intrin_sseN() + * require that the output arrays have a buffer of up to 3 zeroes + * in front (at negative indices) for alignment purposes; + * we use 4 to keep the data well-aligned. */ if(0 != decoder->private_->output[i]) { free(decoder->private_->output[i]-4); @@ -678,6 +637,10 @@ FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; } } + if(0 != decoder->private_->side_subframe) { + free(decoder->private_->side_subframe); + decoder->private_->side_subframe = 0; + } decoder->private_->output_capacity = 0; decoder->private_->output_channels = 0; @@ -737,9 +700,9 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecode FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->private_); FLAC__ASSERT(0 != decoder->protected_); - FLAC__ASSERT((unsigned)type <= FLAC__MAX_METADATA_TYPE_CODE); + FLAC__ASSERT((uint32_t)type <= FLAC__MAX_METADATA_TYPE_CODE); /* double protection */ - if((unsigned)type > FLAC__MAX_METADATA_TYPE_CODE) + if((uint32_t)type > FLAC__MAX_METADATA_TYPE_CODE) return false; if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) return false; @@ -779,7 +742,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder) { - unsigned i; + uint32_t i; FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->private_); FLAC__ASSERT(0 != decoder->protected_); @@ -796,9 +759,9 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->private_); FLAC__ASSERT(0 != decoder->protected_); - FLAC__ASSERT((unsigned)type <= FLAC__MAX_METADATA_TYPE_CODE); + FLAC__ASSERT((uint32_t)type <= FLAC__MAX_METADATA_TYPE_CODE); /* double protection */ - if((unsigned)type > FLAC__MAX_METADATA_TYPE_CODE) + if((uint32_t)type > FLAC__MAX_METADATA_TYPE_CODE) return false; if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) return false; @@ -874,7 +837,7 @@ FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamD return decoder->private_->has_stream_info? decoder->private_->stream_info.data.stream_info.total_samples : 0; } -FLAC_API unsigned FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder) +FLAC_API uint32_t FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder) { FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->protected_); @@ -888,21 +851,21 @@ FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(con return decoder->protected_->channel_assignment; } -FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder) +FLAC_API uint32_t FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder) { FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->protected_); return decoder->protected_->bits_per_sample; } -FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder) +FLAC_API uint32_t FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder) { FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->protected_); return decoder->protected_->sample_rate; } -FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder) +FLAC_API uint32_t FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder) { FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->protected_); @@ -915,10 +878,9 @@ FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamD FLAC__ASSERT(0 != decoder->private_); FLAC__ASSERT(0 != position); -#if FLAC__HAS_OGG - if(decoder->private_->is_ogg) + if(FLAC__HAS_OGG && decoder->private_->is_ogg) return false; -#endif + if(0 == decoder->private_->tell_callback) return false; if(decoder->private_->tell_callback(decoder, position, decoder->private_->client_data) != FLAC__STREAM_DECODER_TELL_STATUS_OK) @@ -931,14 +893,24 @@ FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamD return true; } +FLAC_API const void *FLAC__stream_decoder_get_client_data(FLAC__StreamDecoder *decoder) +{ + return decoder->private_->client_data; +} + FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder) { FLAC__ASSERT(0 != decoder); FLAC__ASSERT(0 != decoder->private_); FLAC__ASSERT(0 != decoder->protected_); + if(!decoder->private_->internal_reset_hack && decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->samples_decoded = 0; decoder->private_->do_md5_checking = false; + decoder->private_->last_seen_framesync = 0; + decoder->private_->last_frame_is_set = false; #if FLAC__HAS_OGG if(decoder->private_->is_ogg) @@ -982,17 +954,15 @@ FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder) if(decoder->private_->seek_callback && decoder->private_->seek_callback(decoder, 0, decoder->private_->client_data) == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR) return false; /* seekable and seek fails, reset fails */ } - else - decoder->private_->internal_reset_hack = false; decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; decoder->private_->has_stream_info = false; - if(decoder->private_->has_seek_table && 0 != decoder->private_->seek_table.data.seek_table.points) { - free(decoder->private_->seek_table.data.seek_table.points); - decoder->private_->seek_table.data.seek_table.points = 0; - decoder->private_->has_seek_table = false; - } + + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; /* * This goes in reset() and not flush() because according to the spec, a @@ -1006,10 +976,19 @@ FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder) * FLAC__stream_decoder_finish() to make sure things are always cleaned up * properly. */ + if(!decoder->private_->internal_reset_hack) { + /* Only finish MD5 context when it has been initialized + * (i.e. when internal_reset_hack is not set) */ + FLAC__MD5Final(decoder->private_->computed_md5sum, &decoder->private_->md5context); + } + else + decoder->private_->internal_reset_hack = false; FLAC__MD5Init(&decoder->private_->md5context); decoder->private_->first_frame_offset = 0; decoder->private_->unparseable_frame_count = 0; + decoder->private_->last_seen_framesync = 0; + decoder->private_->last_frame_is_set = false; return true; } @@ -1045,7 +1024,6 @@ FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *dec case FLAC__STREAM_DECODER_ABORTED: return true; default: - FLAC__ASSERT(0); return false; } } @@ -1072,7 +1050,6 @@ FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__Str case FLAC__STREAM_DECODER_ABORTED: return true; default: - FLAC__ASSERT(0); return false; } } @@ -1106,7 +1083,6 @@ FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__Strea case FLAC__STREAM_DECODER_ABORTED: return true; default: - FLAC__ASSERT(0); return false; } } @@ -1137,7 +1113,6 @@ FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder * case FLAC__STREAM_DECODER_ABORTED: return true; default: - FLAC__ASSERT(0); return false; } } @@ -1216,7 +1191,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *deco * ***********************************************************************/ -unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder) +uint32_t FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder) { FLAC__ASSERT(0 != decoder); FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); @@ -1232,9 +1207,7 @@ unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecod void set_defaults_(FLAC__StreamDecoder *decoder) { -#if FLAC__HAS_OGG decoder->private_->is_ogg = false; -#endif decoder->private_->read_callback = 0; decoder->private_->seek_callback = 0; decoder->private_->tell_callback = 0; @@ -1268,9 +1241,6 @@ FILE *get_binary_stdin_(void) */ #if defined _MSC_VER || defined __MINGW32__ _setmode(_fileno(stdin), _O_BINARY); -#elif defined __CYGWIN__ - /* almost certainly not needed for any modern Cygwin, but let's be safe... */ - setmode(_fileno(stdin), _O_BINARY); #elif defined __EMX__ setmode(fileno(stdin), O_BINARY); #endif @@ -1279,12 +1249,13 @@ FILE *get_binary_stdin_(void) } #endif -FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels) +FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, uint32_t size, uint32_t channels, uint32_t bps) { - unsigned i; + uint32_t i; FLAC__int32 *tmp; - if(size <= decoder->private_->output_capacity && channels <= decoder->private_->output_channels) + if(size <= decoder->private_->output_capacity && channels <= decoder->private_->output_channels && + (bps < 32 || decoder->private_->side_subframe != 0)) return true; /* simply using realloc() is not practical because the number of channels may change mid-stream */ @@ -1300,12 +1271,17 @@ FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigne } } + if(0 != decoder->private_->side_subframe) { + free(decoder->private_->side_subframe); + decoder->private_->side_subframe = 0; + } + for(i = 0; i < channels; i++) { /* WATCHOUT: - * FLAC__lpc_restore_signal_asm_ia32_mmx() requires that the - * output arrays have a buffer of up to 3 zeroes in front - * (at negative indices) for alignment purposes; we use 4 - * to keep the data well-aligned. + * FLAC__lpc_restore_signal_asm_ia32_mmx() and ..._intrin_sseN() + * require that the output arrays have a buffer of up to 3 zeroes + * in front (at negative indices) for alignment purposes; + * we use 4 to keep the data well-aligned. */ tmp = (FLAC__int32*) safe_malloc_muladd2_(sizeof(FLAC__int32), /*times (*/size, /*+*/4/*)*/); if(tmp == 0) { @@ -1321,6 +1297,14 @@ FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigne } } + if(bps == 32) { + decoder->private_->side_subframe = (FLAC__int64*) safe_malloc_mul_2op_p(sizeof(FLAC__int64), /*times (*/size); + if(decoder->private_->side_subframe == NULL) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + } + decoder->private_->output_capacity = size; decoder->private_->output_channels = channels; @@ -1344,12 +1328,12 @@ FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id) FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) { FLAC__uint32 x; - unsigned i, id_; + uint32_t i, id; FLAC__bool first = true; FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); - for(i = id_ = 0; i < 4; ) { + for(i = id = 0; i < 4; ) { if(decoder->private_->cached) { x = (FLAC__uint32)decoder->private_->lookahead; decoder->private_->cached = false; @@ -1361,23 +1345,23 @@ FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) if(x == FLAC__STREAM_SYNC_STRING[i]) { first = true; i++; - id_ = 0; + id = 0; continue; } - if(id_ >= 3) + if(id >= 3) return false; - if(x == ID3V2_TAG_[id_]) { - id_++; + if(x == ID3V2_TAG_[id]) { + id++; i = 0; - if(id_ == 3) { + if(id == 3) { if(!skip_id3v2_tag_(decoder)) return false; /* skip_id3v2_tag_ sets the state for us */ } continue; } - id_ = 0; + id = 0; if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ decoder->private_->header_warmup[0] = (FLAC__byte)x; if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) @@ -1434,16 +1418,21 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) decoder->private_->metadata_callback(decoder, &decoder->private_->stream_info, decoder->private_->client_data); } else if(type == FLAC__METADATA_TYPE_SEEKTABLE) { - if(!read_metadata_seektable_(decoder, is_last, length)) - return false; + /* just in case we already have a seek table, and reading the next one fails: */ + decoder->private_->has_seek_table = false; + + if(length > 0) { + if(!read_metadata_seektable_(decoder, is_last, length)) + return false; - decoder->private_->has_seek_table = true; - if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_SEEKTABLE] && decoder->private_->metadata_callback) - decoder->private_->metadata_callback(decoder, &decoder->private_->seek_table, decoder->private_->client_data); + decoder->private_->has_seek_table = true; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_SEEKTABLE] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->seek_table, decoder->private_->client_data); + } } else { FLAC__bool skip_it = !decoder->private_->metadata_filter[type]; - unsigned real_length = length; + uint32_t real_length = length; FLAC__StreamMetadata block; memset(&block, 0, sizeof(block)); @@ -1472,6 +1461,7 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) } else { FLAC__bool ok = true; + FLAC__bitreader_set_limit(decoder->private_->input, real_length*8); switch(type) { case FLAC__METADATA_TYPE_PADDING: /* skip the padding bytes */ @@ -1520,6 +1510,16 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) block.data.unknown.data = 0; break; } + if(FLAC__bitreader_limit_remaining(decoder->private_->input) > 0) { + /* Content in metadata block didn't fit in block length + * We cannot know whether the length or the content was + * corrupt, so stop parsing metadata */ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_METADATA); + if(decoder->protected_->state == FLAC__STREAM_DECODER_READ_METADATA) + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + ok = false; + } + FLAC__bitreader_remove_limit(decoder->private_->input); if(ok && !decoder->private_->is_seeking && decoder->private_->metadata_callback) decoder->private_->metadata_callback(decoder, &block, decoder->private_->client_data); @@ -1542,7 +1542,7 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) free(block.data.vorbis_comment.comments); break; case FLAC__METADATA_TYPE_CUESHEET: - if(block.data.cue_sheet.num_tracks > 0) + if(block.data.cue_sheet.num_tracks > 0 && 0 != block.data.cue_sheet.tracks) for(i = 0; i < block.data.cue_sheet.num_tracks; i++) if(0 != block.data.cue_sheet.tracks[i].indices) free(block.data.cue_sheet.tracks[i].indices); @@ -1581,10 +1581,10 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) return true; } -FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length) +FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length) { FLAC__uint32 x; - unsigned bits, used_bits = 0; + uint32_t bits, used_bits = 0; FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); @@ -1645,6 +1645,8 @@ FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is /* skip the rest of the block */ FLAC__ASSERT(used_bits % 8 == 0); + if (length < (used_bits / 8)) + return false; /* read_callback_ sets the state for us */ length -= (used_bits / 8); if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) return false; /* read_callback_ sets the state for us */ @@ -1652,7 +1654,7 @@ FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is return true; } -FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length) +FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length) { FLAC__uint32 i, x; FLAC__uint64 xx; @@ -1663,6 +1665,11 @@ FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_ decoder->private_->seek_table.is_last = is_last; decoder->private_->seek_table.length = length; + if(length % FLAC__STREAM_METADATA_SEEKPOINT_LENGTH) { + FLAC__bitreader_limit_invalidate(decoder->private_->input); + return false; + } + decoder->private_->seek_table.data.seek_table.num_points = length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; /* use realloc since we may pass through here several times (e.g. after seeking) */ @@ -1684,17 +1691,13 @@ FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_ decoder->private_->seek_table.data.seek_table.points[i].frame_samples = x; } length -= (decoder->private_->seek_table.data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH); - /* if there is a partial point left, skip over it */ - if(length > 0) { - /*@@@ do a send_error_to_client_() here? there's an argument for either way */ - if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) - return false; /* read_callback_ sets the state for us */ - } + + FLAC__ASSERT(length == 0); return true; } -FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, unsigned length) +FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, uint32_t length) { FLAC__uint32 i; @@ -1706,24 +1709,20 @@ FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__Stre FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->vendor_string.length)) return false; /* read_callback_ sets the state for us */ - if (obj->vendor_string.length > 0) { - if (length < obj->vendor_string.length) { - obj->vendor_string.length = 0; - obj->vendor_string.entry = 0; - goto skip; - } - else - length -= obj->vendor_string.length; - if (0 == (obj->vendor_string.entry = (FLAC__byte*) safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) { - decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; - return false; - } - if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length)) - return false; /* read_callback_ sets the state for us */ - obj->vendor_string.entry[obj->vendor_string.length] = '\0'; + if (length < obj->vendor_string.length) { + obj->vendor_string.length = 0; + obj->vendor_string.entry = 0; + goto skip; } else - obj->vendor_string.entry = 0; + length -= obj->vendor_string.length; + if (0 == (obj->vendor_string.entry = (FLAC__byte*) safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + obj->vendor_string.entry[obj->vendor_string.length] = '\0'; /* read num comments */ FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN == 32); @@ -1731,12 +1730,22 @@ FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__Stre return false; /* read_callback_ sets the state for us */ /* read comments */ + if (obj->num_comments > 100000) { + /* Possibly malicious file. */ + obj->num_comments = 0; + return false; + } if (obj->num_comments > 0) { if (0 == (obj->comments = (FLAC__StreamMetadata_VorbisComment_Entry*) safe_malloc_mul_2op_p(obj->num_comments, /*times*/sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { + obj->num_comments = 0; decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } for (i = 0; i < obj->num_comments; i++) { + /* Initialize here just to make sure. */ + obj->comments[i].length = 0; + obj->comments[i].entry = 0; + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); if (length < 4) { obj->num_comments = i; @@ -1744,38 +1753,48 @@ FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__Stre } else length -= 4; - if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->comments[i].length)) + if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->comments[i].length)) { + obj->num_comments = i; return false; /* read_callback_ sets the state for us */ - if (obj->comments[i].length > 0) { - if (length < obj->comments[i].length) { - obj->comments[i].length = 0; - obj->comments[i].entry = 0; - obj->num_comments = i; - goto skip; - } - else - length -= obj->comments[i].length; - if (0 == (obj->comments[i].entry = (FLAC__byte*) safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) { - decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; - return false; - } - if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->comments[i].entry, obj->comments[i].length)) - return false; /* read_callback_ sets the state for us */ - obj->comments[i].entry[obj->comments[i].length] = '\0'; + } + if (length < obj->comments[i].length) { + obj->num_comments = i; + FLAC__bitreader_limit_invalidate(decoder->private_->input); + return false; } else - obj->comments[i].entry = 0; + length -= obj->comments[i].length; + if (0 == (obj->comments[i].entry = (FLAC__byte*) safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + obj->num_comments = i; + return false; + } + memset (obj->comments[i].entry, 0, obj->comments[i].length) ; + if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->comments[i].entry, obj->comments[i].length)) { + /* Current i-th entry is bad, so we delete it. */ + free (obj->comments[i].entry) ; + obj->comments[i].entry = NULL ; + obj->num_comments = i; + goto skip; + } + obj->comments[i].entry[obj->comments[i].length] = '\0'; } } - else - obj->comments = 0; + } + else { + FLAC__bitreader_limit_invalidate(decoder->private_->input); + return false; } skip: if (length > 0) { - /* This will only happen on files with invalid data in comments */ - if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) - return false; /* read_callback_ sets the state for us */ + /* length > 0 can only happen on files with invalid data in comments */ + if(obj->num_comments < 1) { + free(obj->comments); + obj->comments = NULL; + } + FLAC__bitreader_limit_invalidate(decoder->private_->input); + return false; } return true; @@ -1860,6 +1879,10 @@ FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMet } } } + else { /* obj->num_tracks == 0 */ + FLAC__bitreader_limit_invalidate(decoder->private_->input); + return false; + } return true; } @@ -1873,11 +1896,18 @@ FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMeta /* read type */ if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_TYPE_LEN)) return false; /* read_callback_ sets the state for us */ - obj->type = (FLAC__StreamMetadata_Picture_Type) x; + if(x < FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED) + obj->type = (FLAC__StreamMetadata_Picture_Type) x; + else + obj->type = FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER; /* read MIME type */ if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) return false; /* read_callback_ sets the state for us */ + if(FLAC__bitreader_limit_remaining(decoder->private_->input) < x){ + FLAC__bitreader_limit_invalidate(decoder->private_->input); + return false; + } if(0 == (obj->mime_type = (char*) safe_malloc_add_2op_(x, /*+*/1))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; @@ -1891,6 +1921,10 @@ FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMeta /* read description */ if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) return false; /* read_callback_ sets the state for us */ + if(FLAC__bitreader_limit_remaining(decoder->private_->input) < x){ + FLAC__bitreader_limit_invalidate(decoder->private_->input); + return false; + } if(0 == (obj->description = (FLAC__byte*) safe_malloc_add_2op_(x, /*+*/1))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; @@ -1920,6 +1954,10 @@ FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMeta /* read data */ if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &(obj->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) return false; /* read_callback_ sets the state for us */ + if(FLAC__bitreader_limit_remaining(decoder->private_->input) < obj->data_length){ + FLAC__bitreader_limit_invalidate(decoder->private_->input); + return false; + } if(0 == (obj->data = (FLAC__byte*) safe_malloc_(obj->data_length))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; @@ -1935,7 +1973,7 @@ FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMeta FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder) { FLAC__uint32 x; - unsigned i, skip; + uint32_t i, skip; /* skip the version and flags bytes */ if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 24)) @@ -1959,15 +1997,6 @@ FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder) FLAC__uint32 x; FLAC__bool first = true; - /* If we know the total number of samples in the stream, stop if we've read that many. */ - /* This will stop us, for example, from wasting time trying to sync on an ID3V1 tag. */ - if(FLAC__stream_decoder_get_total_samples(decoder) > 0) { - if(decoder->private_->samples_decoded >= FLAC__stream_decoder_get_total_samples(decoder)) { - decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; - return true; - } - } - /* make sure we're byte aligned */ if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) @@ -1997,6 +2026,12 @@ FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder) else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */ decoder->private_->header_warmup[1] = (FLAC__byte)x; decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + + /* Save location so we can rewind in case the frame turns + * out to be invalid after the header */ + FLAC__bitreader_set_framesync_location(decoder->private_->input); + if(!FLAC__stream_decoder_get_decode_position(decoder, &decoder->private_->last_seen_framesync)) + decoder->private_->last_seen_framesync = 0; return true; } } @@ -2011,13 +2046,13 @@ FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder) FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode) { - unsigned channel; - unsigned i; - FLAC__int32 mid, side; - unsigned frame_crc; /* the one we calculate from the input stream */ - FLAC__uint32 x; + uint32_t channel; + uint32_t i; + uint32_t frame_crc; /* the one we calculate from the input stream */ + FLAC__uint32 x{}; *got_a_frame = false; + decoder->private_->side_subframe_in_use = false; /* init the CRC */ frame_crc = 0; @@ -2029,13 +2064,13 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL return false; if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means we didn't sync on a valid header */ return true; - if(!allocate_output_(decoder, decoder->private_->frame.header.blocksize, decoder->private_->frame.header.channels)) + if(!allocate_output_(decoder, decoder->private_->frame.header.blocksize, decoder->private_->frame.header.channels, decoder->private_->frame.header.bits_per_sample)) return false; for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { /* * first figure the correct bits-per-sample of the subframe */ - unsigned bps = decoder->private_->frame.header.bits_per_sample; + uint32_t bps = decoder->private_->frame.header.bits_per_sample; switch(decoder->private_->frame.header.channel_assignment) { case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: /* no adjustment needed */ @@ -2061,93 +2096,184 @@ FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FL /* * now read it */ - if(!read_subframe_(decoder, channel, bps, do_full_decode)) - return false; - if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ - return true; + if(!read_subframe_(decoder, channel, bps, do_full_decode)){ + /* read_callback_ sets the state for us */ + if(decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM) + break; + else + return false; + } + if(decoder->protected_->state != FLAC__STREAM_DECODER_READ_FRAME) + break; } - if(!read_zero_padding_(decoder)) - return false; - if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption (i.e. "zero bits" were not all zeroes) */ - return true; + + if(decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM) + if(!read_zero_padding_(decoder)) + return false; /* * Read the frame CRC-16 from the footer and check */ - frame_crc = FLAC__bitreader_get_read_crc16(decoder->private_->input); - if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__FRAME_FOOTER_CRC_LEN)) - return false; /* read_callback_ sets the state for us */ - if(frame_crc == x) { + if(decoder->protected_->state == FLAC__STREAM_DECODER_READ_FRAME) { + frame_crc = FLAC__bitreader_get_read_crc16(decoder->private_->input); + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__FRAME_FOOTER_CRC_LEN)) { + /* read_callback_ sets the state for us */ + if(decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM) + return false; + } +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + } + if(decoder->protected_->state == FLAC__STREAM_DECODER_READ_FRAME && frame_crc == x) { +#endif if(do_full_decode) { /* Undo any special channel coding */ - switch(decoder->private_->frame.header.channel_assignment) { - case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: - /* do nothing */ - break; - case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: - FLAC__ASSERT(decoder->private_->frame.header.channels == 2); - for(i = 0; i < decoder->private_->frame.header.blocksize; i++) - decoder->private_->output[1][i] = decoder->private_->output[0][i] - decoder->private_->output[1][i]; - break; - case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: - FLAC__ASSERT(decoder->private_->frame.header.channels == 2); - for(i = 0; i < decoder->private_->frame.header.blocksize; i++) - decoder->private_->output[0][i] += decoder->private_->output[1][i]; - break; - case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: - FLAC__ASSERT(decoder->private_->frame.header.channels == 2); - for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { -#if 1 - mid = decoder->private_->output[0][i]; - side = decoder->private_->output[1][i]; - mid <<= 1; - mid |= (side & 1); /* i.e. if 'side' is odd... */ - decoder->private_->output[0][i] = (mid + side) >> 1; - decoder->private_->output[1][i] = (mid - side) >> 1; -#else - /* OPT: without 'side' temp variable */ - mid = (decoder->private_->output[0][i] << 1) | (decoder->private_->output[1][i] & 1); /* i.e. if 'side' is odd... */ - decoder->private_->output[0][i] = (mid + decoder->private_->output[1][i]) >> 1; - decoder->private_->output[1][i] = (mid - decoder->private_->output[1][i]) >> 1; -#endif + undo_channel_coding(decoder); + /* Check whether decoded data actually fits bps */ + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + int shift_bits = 32 - decoder->private_->frame.header.bits_per_sample; + /* Check whether shift_bits MSBs are 'empty' by shifting up and down */ + if((decoder->private_->output[channel][i] < (INT32_MIN >> shift_bits)) || + (decoder->private_->output[channel][i] > (INT32_MAX >> shift_bits))) { + /* Bad frame, emit error */ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + break; } - break; - default: - FLAC__ASSERT(0); - break; + } } } } - else { - /* Bad frame, emit error and zero the output signal */ +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + else if (decoder->protected_->state == FLAC__STREAM_DECODER_READ_FRAME) { + /* Bad frame, emit error */ send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH); - if(do_full_decode) { - for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { - memset(decoder->private_->output[channel], 0, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } +#endif + + /* Check whether frames are missing, if so, add silence to compensate */ + if(decoder->private_->last_frame_is_set && decoder->protected_->state == FLAC__STREAM_DECODER_READ_FRAME && !decoder->private_->is_seeking && do_full_decode) { + FLAC__ASSERT(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + if(decoder->private_->last_frame.header.number.sample_number + decoder->private_->last_frame.header.blocksize < decoder->private_->frame.header.number.sample_number) { + uint32_t padding_samples_needed = decoder->private_->frame.header.number.sample_number - (decoder->private_->last_frame.header.number.sample_number + decoder->private_->last_frame.header.blocksize); + + /* Do some extra validation to assure last frame an current frame + * header are both valid before adding silence inbetween + * Technically both frames could be valid with differing sample_rates, + * channels and bits_per_sample, but it is quite rare */ + if(decoder->private_->last_frame.header.sample_rate == decoder->private_->frame.header.sample_rate && + decoder->private_->last_frame.header.channels == decoder->private_->frame.header.channels && + decoder->private_->last_frame.header.bits_per_sample == decoder->private_->frame.header.bits_per_sample && + decoder->private_->last_frame.header.blocksize >= 16) { + FLAC__Frame empty_frame; + FLAC__int32 * empty_buffer[FLAC__MAX_CHANNELS] = {NULL}; + empty_frame.header = decoder->private_->last_frame.header; + empty_frame.footer.crc = 0; + for(i = 0; i < empty_frame.header.channels; i++) { + empty_buffer[i] = (FLAC__int32*) safe_calloc_(empty_frame.header.blocksize, sizeof(FLAC__int32)); + if(empty_buffer[i] == NULL) { + for(i = 0; i < empty_frame.header.channels; i++) + if(empty_buffer[i] != NULL) + free(empty_buffer[i]); + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + } + /* No repairs larger than 5 seconds or 50 frames are made, to not + * unexpectedly create enormous files when one of the headers was + * corrupt after all */ + if(padding_samples_needed > (5*empty_frame.header.sample_rate)) + padding_samples_needed = 5*empty_frame.header.sample_rate; + if(padding_samples_needed > (50*empty_frame.header.blocksize)) + padding_samples_needed = 50*empty_frame.header.blocksize; + while(padding_samples_needed){ + empty_frame.header.number.sample_number += empty_frame.header.blocksize; + if(padding_samples_needed < empty_frame.header.blocksize) + empty_frame.header.blocksize = padding_samples_needed; + padding_samples_needed -= empty_frame.header.blocksize; + decoder->protected_->blocksize = empty_frame.header.blocksize; + + FLAC__ASSERT(empty_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + decoder->private_->samples_decoded = empty_frame.header.number.sample_number + empty_frame.header.blocksize; + + for(channel = 0; channel < empty_frame.header.channels; channel++) { + empty_frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_CONSTANT; + empty_frame.subframes[channel].data.constant.value = 0; + empty_frame.subframes[channel].wasted_bits = 0; + } + + if(write_audio_frame_to_client_(decoder, &empty_frame, (const FLAC__int32 * const *)empty_buffer) != FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + for(i = 0; i < empty_frame.header.channels; i++) + if(empty_buffer[i] != NULL) + free(empty_buffer[i]); + return false; + } + } + for(i = 0; i < empty_frame.header.channels; i++) + if(empty_buffer[i] != NULL) + free(empty_buffer[i]); + } } } - *got_a_frame = true; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC || decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM) { + /* Got corruption, rewind if possible. Return value of seek + * isn't checked, if the seek fails the decoder will continue anyway */ + if(!FLAC__bitreader_rewind_to_after_last_seen_framesync(decoder->private_->input)){ +#ifndef NDEBUG + fprintf(stderr, "Rewinding, seeking necessary\n"); +#endif + if(decoder->private_->seek_callback && decoder->private_->last_seen_framesync){ + /* Last framesync isn't in bitreader anymore, rewind with seek if possible */ +#ifndef NDEBUG + FLAC__uint64 current_decode_position; + if(FLAC__stream_decoder_get_decode_position(decoder, ¤t_decode_position)) + fprintf(stderr, "Bitreader was %" PRIu64 " bytes short\n", current_decode_position-decoder->private_->last_seen_framesync); +#endif + if(decoder->private_->seek_callback(decoder, decoder->private_->last_seen_framesync, decoder->private_->client_data) == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__bitreader_clear(decoder->private_->input)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + } + } +#ifndef NDEBUG + else{ + fprintf(stderr, "Rewinding, seeking not necessary\n"); + } +#endif + } + else { + *got_a_frame = true; - /* we wait to update fixed_block_size until here, when we're sure we've got a proper frame and hence a correct blocksize */ - if(decoder->private_->next_fixed_block_size) - decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size; + /* we wait to update fixed_block_size until here, when we're sure we've got a proper frame and hence a correct blocksize */ + if(decoder->private_->next_fixed_block_size) + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size; - /* put the latest values into the public section of the decoder instance */ - decoder->protected_->channels = decoder->private_->frame.header.channels; - decoder->protected_->channel_assignment = decoder->private_->frame.header.channel_assignment; - decoder->protected_->bits_per_sample = decoder->private_->frame.header.bits_per_sample; - decoder->protected_->sample_rate = decoder->private_->frame.header.sample_rate; - decoder->protected_->blocksize = decoder->private_->frame.header.blocksize; + /* put the latest values into the public section of the decoder instance */ + decoder->protected_->channels = decoder->private_->frame.header.channels; + decoder->protected_->channel_assignment = decoder->private_->frame.header.channel_assignment; + decoder->protected_->bits_per_sample = decoder->private_->frame.header.bits_per_sample; + decoder->protected_->sample_rate = decoder->private_->frame.header.sample_rate; + decoder->protected_->blocksize = decoder->private_->frame.header.blocksize; - FLAC__ASSERT(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); - decoder->private_->samples_decoded = decoder->private_->frame.header.number.sample_number + decoder->private_->frame.header.blocksize; + FLAC__ASSERT(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + decoder->private_->samples_decoded = decoder->private_->frame.header.number.sample_number + decoder->private_->frame.header.blocksize; - /* write it */ - if(do_full_decode) { - if(write_audio_frame_to_client_(decoder, &decoder->private_->frame, (const FLAC__int32 * const *)decoder->private_->output) != FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE) - return false; + /* write it */ + if(do_full_decode) { + if(write_audio_frame_to_client_(decoder, &decoder->private_->frame, (const FLAC__int32 * const *)decoder->private_->output) != FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + } } decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; @@ -2158,9 +2284,9 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) { FLAC__uint32 x; FLAC__uint64 xx; - unsigned i, blocksize_hint = 0, sample_rate_hint = 0; + uint32_t i, blocksize_hint = 0, sample_rate_hint = 0; FLAC__byte crc8, raw_header[16]; /* MAGIC NUMBER based on the maximum frame header size, including CRC */ - unsigned raw_header_len; + uint32_t raw_header_len; FLAC__bool is_unparseable = false; FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); @@ -2295,7 +2421,7 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) FLAC__ASSERT(0); } - x = (unsigned)(raw_header[3] >> 4); + x = (uint32_t)(raw_header[3] >> 4); if(x & 8) { decoder->private_->frame.header.channels = 2; switch(x & 7) { @@ -2314,11 +2440,11 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) } } else { - decoder->private_->frame.header.channels = (unsigned)x + 1; + decoder->private_->frame.header.channels = (uint32_t)x + 1; decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; } - switch(x = (unsigned)(raw_header[3] & 0x0e) >> 1) { + switch(x = (uint32_t)(raw_header[3] & 0x0e) >> 1) { case 0: if(decoder->private_->has_stream_info) decoder->private_->frame.header.bits_per_sample = decoder->private_->stream_info.data.stream_info.bits_per_sample; @@ -2331,6 +2457,9 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) case 2: decoder->private_->frame.header.bits_per_sample = 12; break; + case 3: + is_unparseable = true; + break; case 4: decoder->private_->frame.header.bits_per_sample = 16; break; @@ -2340,18 +2469,19 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) case 6: decoder->private_->frame.header.bits_per_sample = 24; break; - case 3: case 7: - is_unparseable = true; + decoder->private_->frame.header.bits_per_sample = 32; break; default: FLAC__ASSERT(0); break; } +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* check to make sure that reserved bit is 0 */ if(raw_header[3] & 0x01) /* MAGIC NUMBER */ is_unparseable = true; +#endif /* read the frame's starting sample number (or frame number as the case may be) */ if( @@ -2397,6 +2527,14 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) x = (x << 8) | _x; } decoder->private_->frame.header.blocksize = x+1; + if(decoder->private_->frame.header.blocksize > 65535) { /* invalid blocksize (65536) specified */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + } if(sample_rate_hint) { @@ -2423,11 +2561,13 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) return false; /* read_callback_ sets the state for us */ crc8 = (FLAC__byte)x; +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if(FLAC__crc8(raw_header, raw_header_len) != crc8) { send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; return true; } +#endif /* calculate the sample number from the frame number if needed */ decoder->private_->next_fixed_block_size = 0; @@ -2463,11 +2603,11 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) return true; } -FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode) { FLAC__uint32 x; FLAC__bool wasted_bits; - unsigned i; + uint32_t i; if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) /* MAGIC NUMBER */ return false; /* read_callback_ sets the state for us */ @@ -2476,10 +2616,15 @@ FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsign x &= 0xfe; if(wasted_bits) { - unsigned u; + uint32_t u; if(!FLAC__bitreader_read_unary_unsigned(decoder->private_->input, &u)) return false; /* read_callback_ sets the state for us */ decoder->private_->frame.subframes[channel].wasted_bits = u+1; + if (decoder->private_->frame.subframes[channel].wasted_bits >= bps) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } bps -= decoder->private_->frame.subframes[channel].wasted_bits; } else @@ -2507,7 +2652,13 @@ FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsign return true; } else if(x <= 24) { - if(!read_subframe_fixed_(decoder, channel, bps, (x>>1)&7, do_full_decode)) + uint32_t predictor_order = (x>>1)&7; + if(decoder->private_->frame.header.blocksize <= predictor_order){ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + if(!read_subframe_fixed_(decoder, channel, bps, predictor_order, do_full_decode)) return false; if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ return true; @@ -2518,7 +2669,13 @@ FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsign return true; } else { - if(!read_subframe_lpc_(decoder, channel, bps, ((x>>1)&31)+1, do_full_decode)) + uint32_t predictor_order = ((x>>1)&31)+1; + if(decoder->private_->frame.header.blocksize <= predictor_order){ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + if(!read_subframe_lpc_(decoder, channel, bps, predictor_order, do_full_decode)) return false; if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ return true; @@ -2526,42 +2683,63 @@ FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsign if(wasted_bits && do_full_decode) { x = decoder->private_->frame.subframes[channel].wasted_bits; - for(i = 0; i < decoder->private_->frame.header.blocksize; i++) - decoder->private_->output[channel][i] <<= x; + if((bps + x) < 33) { + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + uint32_t val = decoder->private_->output[channel][i]; + decoder->private_->output[channel][i] = (val << x); + } + } + else { + /* When there are wasted bits, bps is never 33 and so + * side_subframe is never already in use */ + FLAC__ASSERT(!decoder->private_->side_subframe_in_use); + decoder->private_->side_subframe_in_use = true; + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + uint64_t val = decoder->private_->output[channel][i]; + decoder->private_->side_subframe[i] = (val << x); + } + } } return true; } -FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode) { FLAC__Subframe_Constant *subframe = &decoder->private_->frame.subframes[channel].data.constant; - FLAC__int32 x; - unsigned i; - FLAC__int32 *output = decoder->private_->output[channel]; + FLAC__int64 x; + uint32_t i; decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_CONSTANT; - if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) + if(!FLAC__bitreader_read_raw_int64(decoder->private_->input, &x, bps)) return false; /* read_callback_ sets the state for us */ subframe->value = x; /* decode the subframe */ if(do_full_decode) { - for(i = 0; i < decoder->private_->frame.header.blocksize; i++) - output[i] = x; + if(bps <= 32) { + FLAC__int32 *output = decoder->private_->output[channel]; + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + output[i] = x; + } else { + FLAC__int64 *output = decoder->private_->side_subframe; + decoder->private_->side_subframe_in_use = true; + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + output[i] = x; + } } return true; } -FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode) +FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode) { FLAC__Subframe_Fixed *subframe = &decoder->private_->frame.subframes[channel].data.fixed; - FLAC__int32 i32; + FLAC__int64 i64; FLAC__uint32 u32; - unsigned u; + uint32_t u; decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_FIXED; @@ -2570,9 +2748,9 @@ FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, /* read warm-up samples */ for(u = 0; u < order; u++) { - if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps)) + if(!FLAC__bitreader_read_raw_int64(decoder->private_->input, &i64, bps)) return false; /* read_callback_ sets the state for us */ - subframe->warmup[u] = i32; + subframe->warmup[u] = i64; } /* read entropy coding method info */ @@ -2584,6 +2762,12 @@ FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) return false; /* read_callback_ sets the state for us */ + if((decoder->private_->frame.header.blocksize >> u32 < order) || + (decoder->private_->frame.header.blocksize % (1 << u32) > 0)) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } subframe->entropy_coding_method.data.partitioned_rice.order = u32; subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; break; @@ -2606,19 +2790,32 @@ FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, /* decode the subframe */ if(do_full_decode) { - memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); - FLAC__fixed_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, order, decoder->private_->output[channel]+order); + if(bps < 33){ + uint32_t i; + for(i = 0; i < order; i++) + decoder->private_->output[channel][i] = subframe->warmup[i]; + if(bps+order <= 32) + FLAC__fixed_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, order, decoder->private_->output[channel]+order); + else + FLAC__fixed_restore_signal_wide(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, order, decoder->private_->output[channel]+order); + } + else { + decoder->private_->side_subframe_in_use = true; + memcpy(decoder->private_->side_subframe, subframe->warmup, sizeof(FLAC__int64) * order); + FLAC__fixed_restore_signal_wide_33bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, order, decoder->private_->side_subframe+order); + } } return true; } -FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode) +FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode) { FLAC__Subframe_LPC *subframe = &decoder->private_->frame.subframes[channel].data.lpc; FLAC__int32 i32; + FLAC__int64 i64; FLAC__uint32 u32; - unsigned u; + uint32_t u; decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_LPC; @@ -2627,9 +2824,9 @@ FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, un /* read warm-up samples */ for(u = 0; u < order; u++) { - if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps)) + if(!FLAC__bitreader_read_raw_int64(decoder->private_->input, &i64, bps)) return false; /* read_callback_ sets the state for us */ - subframe->warmup[u] = i32; + subframe->warmup[u] = i64; } /* read qlp coeff precision */ @@ -2645,6 +2842,11 @@ FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, un /* read qlp shift */ if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN)) return false; /* read_callback_ sets the state for us */ + if(i32 < 0) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } subframe->quantization_level = i32; /* read quantized lp coefficiencts */ @@ -2663,6 +2865,12 @@ FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, un case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) return false; /* read_callback_ sets the state for us */ + if((decoder->private_->frame.header.blocksize >> u32 < order) || + (decoder->private_->frame.header.blocksize % (1 << u32) > 0)) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } subframe->entropy_coding_method.data.partitioned_rice.order = u32; subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; break; @@ -2685,72 +2893,78 @@ FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, un /* decode the subframe */ if(do_full_decode) { - memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); - /*@@@@@@ technically not pessimistic enough, should be more like - if( (FLAC__uint64)order * ((((FLAC__uint64)1)<qlp_coeff_precision)-1) < (((FLAC__uint64)-1) << 32) ) - */ - if(bps + subframe->qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32) - if(bps <= 16 && subframe->qlp_coeff_precision <= 16) - decoder->private_->local_lpc_restore_signal_16bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + if(bps <= 32) { + uint32_t i; + for(i = 0; i < order; i++) + decoder->private_->output[channel][i] = subframe->warmup[i]; + if(FLAC__lpc_max_residual_bps(bps, subframe->qlp_coeff, order, subframe->quantization_level) <= 32 && + FLAC__lpc_max_prediction_before_shift_bps(bps, subframe->qlp_coeff, order) <= 32) + FLAC__lpc_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); else - decoder->private_->local_lpc_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); - else - decoder->private_->local_lpc_restore_signal_64bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + FLAC__lpc_restore_signal_wide(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + } + else { + decoder->private_->side_subframe_in_use = true; + memcpy(decoder->private_->side_subframe, subframe->warmup, sizeof(FLAC__int64) * order); + FLAC__lpc_restore_signal_wide_33bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->side_subframe+order); + } } return true; } -FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode) { FLAC__Subframe_Verbatim *subframe = &decoder->private_->frame.subframes[channel].data.verbatim; - FLAC__int32 x, *residual = decoder->private_->residual[channel]; - unsigned i; + uint32_t i; decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_VERBATIM; - subframe->data = residual; + if(bps < 33) { + FLAC__int32 x, *residual = decoder->private_->residual[channel]; - for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { - if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) - return false; /* read_callback_ sets the state for us */ - residual[i] = x; + subframe->data_type = FLAC__VERBATIM_SUBFRAME_DATA_TYPE_INT32; + subframe->data.int32 = residual; + + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + residual[i] = x; + } + + /* decode the subframe */ + if(do_full_decode) + memcpy(decoder->private_->output[channel], subframe->data.int32, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); } + else { + FLAC__int64 x, *side = decoder->private_->side_subframe; - /* decode the subframe */ - if(do_full_decode) - memcpy(decoder->private_->output[channel], subframe->data, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + subframe->data_type = FLAC__VERBATIM_SUBFRAME_DATA_TYPE_INT64; + subframe->data.int64 = side; + decoder->private_->side_subframe_in_use = true; + + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + if(!FLAC__bitreader_read_raw_int64(decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + side[i] = x; + } + } return true; } -FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigned predictor_order, unsigned partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended) +FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, uint32_t predictor_order, uint32_t partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended) { FLAC__uint32 rice_parameter; int i; - unsigned partition, sample, u; - const unsigned partitions = 1u << partition_order; - const unsigned partition_samples = partition_order > 0? decoder->private_->frame.header.blocksize >> partition_order : decoder->private_->frame.header.blocksize - predictor_order; - const unsigned plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; - const unsigned pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; - - /* sanity checks */ - if(partition_order == 0) { - if(decoder->private_->frame.header.blocksize < predictor_order) { - send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); - decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; - /* We have received a potentially malicious bit stream. All we can do is error out to avoid a heap overflow. */ - return false; - } - } - else { - if(partition_samples < predictor_order) { - send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); - decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; - /* We have received a potentially malicious bit stream. All we can do is error out to avoid a heap overflow. */ - return false; - } - } + uint32_t partition, sample, u; + const uint32_t partitions = 1u << partition_order; + const uint32_t partition_samples = decoder->private_->frame.header.blocksize >> partition_order; + const uint32_t plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; + const uint32_t pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + + /* invalid predictor and partition orders mush be handled in the callers */ + FLAC__ASSERT(partition_order > 0? partition_samples >= predictor_order : decoder->private_->frame.header.blocksize >= predictor_order); if(!FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, flac_max(6u, partition_order))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; @@ -2764,19 +2978,34 @@ FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigne partitioned_rice_contents->parameters[partition] = rice_parameter; if(rice_parameter < pesc) { partitioned_rice_contents->raw_bits[partition] = 0; - u = (partition_order == 0 || partition > 0)? partition_samples : partition_samples - predictor_order; - if(!FLAC__bitreader_read_rice_signed_block(decoder->private_->input, residual + sample, u, rice_parameter)) - return false; /* read_callback_ sets the state for us */ + u = (partition == 0) ? partition_samples - predictor_order : partition_samples; + if(!decoder->private_->local_bitreader_read_rice_signed_block(decoder->private_->input, residual + sample, u, rice_parameter)){ + if(decoder->protected_->state == FLAC__STREAM_DECODER_READ_FRAME) { + /* no error was set, read_callback_ didn't set it, so + * invalid rice symbol was found */ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else + return false; /* read_callback_ sets the state for us */ + } sample += u; } else { if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN)) return false; /* read_callback_ sets the state for us */ partitioned_rice_contents->raw_bits[partition] = rice_parameter; - for(u = (partition_order == 0 || partition > 0)? 0 : predictor_order; u < partition_samples; u++, sample++) { - if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i, rice_parameter)) - return false; /* read_callback_ sets the state for us */ - residual[sample] = i; + if(rice_parameter == 0) { + for(u = (partition == 0)? predictor_order : 0; u < partition_samples; u++, sample++) + residual[sample] = 0; + } + else{ + for(u = (partition == 0)? predictor_order : 0; u < partition_samples; u++, sample++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + residual[sample] = i; + } } } } @@ -2790,10 +3019,12 @@ FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder) FLAC__uint32 zero = 0; if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &zero, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) return false; /* read_callback_ sets the state for us */ +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if(zero != 0) { send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; } +#endif } return true; } @@ -2878,6 +3109,66 @@ FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data) */ } +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && !defined(FUZZING_BUILD_MODE_FLAC_SANITIZE_SIGNED_INTEGER_OVERFLOW) +/* The attribute below is to silence the undefined sanitizer of oss-fuzz. + * Because fuzzing feeds bogus predictors and residual samples to the + * decoder, having overflows in this section is unavoidable. Also, + * because the calculated values are audio path only, there is no + * potential for security problems */ +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +void undo_channel_coding(FLAC__StreamDecoder *decoder) { + uint32_t i; + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* do nothing */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + FLAC__ASSERT(decoder->private_->side_subframe_in_use != /* logical XOR */ (decoder->private_->frame.header.bits_per_sample < 32)); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + if(decoder->private_->side_subframe_in_use) + decoder->private_->output[1][i] = decoder->private_->output[0][i] - decoder->private_->side_subframe[i]; + else + decoder->private_->output[1][i] = decoder->private_->output[0][i] - decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + FLAC__ASSERT(decoder->private_->side_subframe_in_use != /* logical XOR */ (decoder->private_->frame.header.bits_per_sample < 32)); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + if(decoder->private_->side_subframe_in_use) + decoder->private_->output[0][i] = decoder->private_->output[1][i] + decoder->private_->side_subframe[i]; + else + decoder->private_->output[0][i] += decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + FLAC__ASSERT(decoder->private_->side_subframe_in_use != /* logical XOR */ (decoder->private_->frame.header.bits_per_sample < 32)); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + if(!decoder->private_->side_subframe_in_use){ + FLAC__int32 mid, side; + mid = decoder->private_->output[0][i]; + side = decoder->private_->output[1][i]; + mid = ((uint32_t) mid) << 1; + mid |= (side & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + side) >> 1; + decoder->private_->output[1][i] = (mid - side) >> 1; + } + else { /* bps == 32 */ + FLAC__int64 mid; + mid = ((uint64_t)decoder->private_->output[0][i]) << 1; + mid |= (decoder->private_->side_subframe[i] & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + decoder->private_->side_subframe[i]) >> 1; + decoder->private_->output[1][i] = (mid - decoder->private_->side_subframe[i]) >> 1; + } + } + break; + default: + FLAC__ASSERT(0); + break; + } +} + #if FLAC__HAS_OGG FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes) { @@ -2926,6 +3217,8 @@ FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) { + decoder->private_->last_frame = *frame; /* save the frame */ + decoder->private_->last_frame_is_set = true; if(decoder->private_->is_seeking) { FLAC__uint64 this_frame_sample = frame->header.number.sample_number; FLAC__uint64 next_frame_sample = this_frame_sample + (FLAC__uint64)frame->header.blocksize; @@ -2936,17 +3229,20 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder #if FLAC__HAS_OGG decoder->private_->got_a_frame = true; #endif - decoder->private_->last_frame = *frame; /* save the frame */ if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */ - unsigned delta = (unsigned)(target_sample - this_frame_sample); + uint32_t delta = (uint32_t)(target_sample - this_frame_sample); /* kick out of seek mode */ decoder->private_->is_seeking = false; /* shift out the samples before target_sample */ if(delta > 0) { - unsigned channel; + uint32_t channel; const FLAC__int32 *newbuffer[FLAC__MAX_CHANNELS]; - for(channel = 0; channel < frame->header.channels; channel++) + for(channel = 0; channel < frame->header.channels; channel++) { newbuffer[channel] = buffer[channel] + delta; + decoder->private_->last_frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_VERBATIM; + decoder->private_->last_frame.subframes[channel].data.verbatim.data_type = FLAC__VERBATIM_SUBFRAME_DATA_TYPE_INT32; + decoder->private_->last_frame.subframes[channel].data.verbatim.data.int32 = newbuffer[channel]; + } decoder->private_->last_frame.header.blocksize -= delta; decoder->private_->last_frame.header.number.sample_number += (FLAC__uint64)delta; /* write the relevant samples */ @@ -2989,16 +3285,16 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s FLAC__uint64 first_frame_offset = decoder->private_->first_frame_offset, lower_bound, upper_bound, lower_bound_sample, upper_bound_sample, this_frame_sample; FLAC__int64 pos = -1; int i; - unsigned approx_bytes_per_frame; - FLAC__bool first_seek = true; + uint32_t approx_bytes_per_frame; + FLAC__bool first_seek = true, seek_from_lower_bound = false; const FLAC__uint64 total_samples = FLAC__stream_decoder_get_total_samples(decoder); - const unsigned min_blocksize = decoder->private_->stream_info.data.stream_info.min_blocksize; - const unsigned max_blocksize = decoder->private_->stream_info.data.stream_info.max_blocksize; - const unsigned max_framesize = decoder->private_->stream_info.data.stream_info.max_framesize; - const unsigned min_framesize = decoder->private_->stream_info.data.stream_info.min_framesize; + const uint32_t min_blocksize = decoder->private_->stream_info.data.stream_info.min_blocksize; + const uint32_t max_blocksize = decoder->private_->stream_info.data.stream_info.max_blocksize; + const uint32_t max_framesize = decoder->private_->stream_info.data.stream_info.max_framesize; + const uint32_t min_framesize = decoder->private_->stream_info.data.stream_info.min_framesize; /* take these from the current frame in case they've changed mid-stream */ - unsigned channels = FLAC__stream_decoder_get_channels(decoder); - unsigned bps = FLAC__stream_decoder_get_bits_per_sample(decoder); + uint32_t channels = FLAC__stream_decoder_get_channels(decoder); + uint32_t bps = FLAC__stream_decoder_get_bits_per_sample(decoder); const FLAC__StreamMetadata_SeekTable *seek_table = decoder->private_->has_seek_table? &decoder->private_->seek_table.data.seek_table : 0; /* use values from stream info if we didn't decode a frame */ @@ -3017,7 +3313,7 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s * min_blocksize might be zero. */ else if(min_blocksize == max_blocksize && min_blocksize > 0) { - /* note there are no () around 'bps/8' to keep precision up since it's an integer calulation */ + /* note there are no () around 'bps/8' to keep precision up since it's an integer calculation */ approx_bytes_per_frame = min_blocksize * channels * bps/8 + 64; } else @@ -3025,22 +3321,36 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s /* * First, we set an upper and lower bound on where in the - * stream we will search. For now we assume the worst case - * scenario, which is our best guess at the beginning of - * the first frame and end of the stream. + * stream we will search. For now we take the current position + * as one bound and, depending on where the target position lies, + * the beginning of the first frame or the end of the stream as + * the other bound. */ lower_bound = first_frame_offset; lower_bound_sample = 0; upper_bound = stream_length; upper_bound_sample = total_samples > 0 ? total_samples : target_sample /*estimate it*/; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC && + decoder->private_->samples_decoded != 0) { + if(target_sample < decoder->private_->samples_decoded) { + if(FLAC__stream_decoder_get_decode_position(decoder, &upper_bound)) + upper_bound_sample = decoder->private_->samples_decoded; + } else { + if(FLAC__stream_decoder_get_decode_position(decoder, &lower_bound)) + lower_bound_sample = decoder->private_->samples_decoded; + } + } + /* * Now we refine the bounds if we have a seektable with * suitable points. Note that according to the spec they * must be ordered by ascending sample number. * * Note: to protect against invalid seek tables we will ignore points - * that have frame_samples==0 or sample_number>=total_samples + * that have frame_samples==0 or sample_number>=total_samples. Also, + * because math is limited to 64-bit ints, seekpoints with an offset + * larger than 2^63 (8 exbibyte) are rejected. */ if(seek_table) { FLAC__uint64 new_lower_bound = lower_bound; @@ -3101,20 +3411,34 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s decoder->private_->target_sample = target_sample; while(1) { + /* check whether decoder is still valid so bad state isn't overwritten + * with seek error */ + if(decoder->protected_->state == FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR || + decoder->protected_->state == FLAC__STREAM_DECODER_ABORTED) + return false; /* check if the bounds are still ok */ - if (lower_bound_sample >= upper_bound_sample || lower_bound > upper_bound) { + if (lower_bound_sample >= upper_bound_sample || + lower_bound > upper_bound || + upper_bound >= INT64_MAX) { decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; return false; } + if(seek_from_lower_bound) { + pos = lower_bound; + } + else { #ifndef FLAC__INTEGER_ONLY_LIBRARY - pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(target_sample - lower_bound_sample) / (FLAC__double)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(upper_bound - lower_bound)) - approx_bytes_per_frame; + pos = (FLAC__int64)lower_bound + (FLAC__int64)((double)(target_sample - lower_bound_sample) / (double)(upper_bound_sample - lower_bound_sample) * (double)(upper_bound - lower_bound)) - approx_bytes_per_frame; #else - /* a little less accurate: */ - if(upper_bound - lower_bound < 0xffffffff) - pos = (FLAC__int64)lower_bound + (FLAC__int64)(((target_sample - lower_bound_sample) * (upper_bound - lower_bound)) / (upper_bound_sample - lower_bound_sample)) - approx_bytes_per_frame; - else /* @@@ WATCHOUT, ~2TB limit */ - pos = (FLAC__int64)lower_bound + (FLAC__int64)((((target_sample - lower_bound_sample)>>8) * ((upper_bound - lower_bound)>>8)) / ((upper_bound_sample - lower_bound_sample)>>16)) - approx_bytes_per_frame; + /* a little less accurate: */ + if(upper_bound - lower_bound < 0xffffffff) + pos = (FLAC__int64)lower_bound + (FLAC__int64)(((target_sample - lower_bound_sample) * (upper_bound - lower_bound)) / (upper_bound_sample - lower_bound_sample)) - approx_bytes_per_frame; + else { /* @@@ WATCHOUT, ~2TB limit */ + FLAC__uint64 ratio = (1<<16) / (upper_bound_sample - lower_bound_sample); + pos = (FLAC__int64)lower_bound + (FLAC__int64)((((target_sample - lower_bound_sample)>>8) * ((upper_bound - lower_bound)>>8) * ratio)) - approx_bytes_per_frame; + } #endif + } if(pos >= (FLAC__int64)upper_bound) pos = (FLAC__int64)upper_bound - 1; if(pos < (FLAC__int64)lower_bound) @@ -3134,24 +3458,32 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s * FLAC__stream_decoder_process_single() to return false. */ decoder->private_->unparseable_frame_count = 0; - if(!FLAC__stream_decoder_process_single(decoder)) { - decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; - return false; + if(!FLAC__stream_decoder_process_single(decoder) || decoder->protected_->state == FLAC__STREAM_DECODER_ABORTED || 0 == decoder->private_->samples_decoded) { + /* No frame could be decoded */ + if(decoder->protected_->state != FLAC__STREAM_DECODER_ABORTED && decoder->private_->eof_callback(decoder, decoder->private_->client_data) && !seek_from_lower_bound){ + /* decoder has hit end of stream while processing corrupt + * frame. To remedy this, try decoding a frame at the lower + * bound so the seek after that hopefully ends up somewhere + * else */ + seek_from_lower_bound = true; + continue; + } + else { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } } + seek_from_lower_bound = false; + /* our write callback will change the state when it gets to the target frame */ /* actually, we could have got_a_frame if our decoder is at FLAC__STREAM_DECODER_END_OF_STREAM so we need to check for that also */ -#if 0 - /*@@@@@@ used to be the following; not clear if the check for end of stream is needed anymore */ - if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_SEEKING && decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM) - break; -#endif if(!decoder->private_->is_seeking) break; FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); this_frame_sample = decoder->private_->last_frame.header.number.sample_number; - if (0 == decoder->private_->samples_decoded || (this_frame_sample + decoder->private_->last_frame.header.blocksize >= upper_bound_sample && !first_seek)) { + if(this_frame_sample + decoder->private_->last_frame.header.blocksize >= upper_bound_sample && !first_seek) { if (pos == (FLAC__int64)lower_bound) { /* can't move back any more than the first frame, something is fatally wrong */ decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; @@ -3178,7 +3510,7 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; return false; } - approx_bytes_per_frame = (unsigned)(2 * (upper_bound - pos) / 3 + 16); + approx_bytes_per_frame = (uint32_t)(2 * (upper_bound - pos) / 3 + 16); } else { /* target_sample >= this_frame_sample + this frame's blocksize */ lower_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; @@ -3186,7 +3518,7 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; return false; } - approx_bytes_per_frame = (unsigned)(2 * (lower_bound - pos) / 3 + 16); + approx_bytes_per_frame = (uint32_t)(2 * (lower_bound - pos) / 3 + 16); } } @@ -3201,14 +3533,14 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint FLAC__uint64 this_frame_sample = (FLAC__uint64)0 - 1; FLAC__uint64 pos = 0; /* only initialized to avoid compiler warning */ FLAC__bool did_a_seek; - unsigned iteration = 0; + uint32_t iteration = 0; /* In the first iterations, we will calculate the target byte position * by the distance from the target sample to left_sample and * right_sample (let's call it "proportional search"). After that, we * will switch to binary search. */ - unsigned BINARY_SEARCH_AFTER_ITERATION = 2; + uint32_t BINARY_SEARCH_AFTER_ITERATION = 2; /* We will switch to a linear search once our current sample is less * than this number of samples ahead of the target sample @@ -3225,13 +3557,19 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint decoder->private_->target_sample = target_sample; for( ; ; iteration++) { + /* Do sanity checks on bounds */ + if(right_pos <= left_pos || right_pos - left_pos < 9) { + /* FLAC frame is at least 9 byte in size */ + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } if (iteration == 0 || this_frame_sample > target_sample || target_sample - this_frame_sample > LINEAR_SEARCH_WITHIN_SAMPLES) { if (iteration >= BINARY_SEARCH_AFTER_ITERATION) { pos = (right_pos + left_pos) / 2; } else { #ifndef FLAC__INTEGER_ONLY_LIBRARY - pos = (FLAC__uint64)((FLAC__double)(target_sample - left_sample) / (FLAC__double)(right_sample - left_sample) * (FLAC__double)(right_pos - left_pos)); + pos = (FLAC__uint64)((double)(target_sample - left_sample) / (double)(right_sample - left_sample) * (double)(right_pos - left_pos)); #else /* a little less accurate: */ if ((target_sample-left_sample <= 0xffffffff) && (right_pos-left_pos <= 0xffffffff)) @@ -3261,7 +3599,8 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint did_a_seek = false; decoder->private_->got_a_frame = false; - if(!FLAC__stream_decoder_process_single(decoder)) { + if(!FLAC__stream_decoder_process_single(decoder) || + decoder->protected_->state == FLAC__STREAM_DECODER_ABORTED) { decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; return false; } @@ -3308,7 +3647,7 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint } left_pos = pos; } - else if(this_frame_sample > target_sample) { + else { right_sample = this_frame_sample; /* sanity check to avoid infinite loop */ if (right_pos == pos) { @@ -3377,7 +3716,13 @@ FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder if(decoder->private_->file == stdin) return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; - else if(flac_fstat(fileno(decoder->private_->file), &filestats) != 0) + +#ifndef FLAC__USE_FILELENGTHI64 + if(flac_fstat(fileno(decoder->private_->file), &filestats) != 0) +#else + filestats.st_size = _filelengthi64(fileno(decoder->private_->file)); + if(filestats.st_size < 0) +#endif return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; else { *stream_length = (FLAC__uint64)filestats.st_size; @@ -3391,5 +3736,4 @@ FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_d return feof(decoder->private_->file)? true : false; } - #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c index a9510c6c..a706298c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,6 +39,11 @@ #include /* for malloc() */ #include /* for memcpy() */ #include /* for off_t */ +#ifdef _WIN32 +#include /* for GetFileType() */ +#include /* for _get_osfhandle() */ +#endif +#include "../compat.h" #include "../assert.h" #include "../stream_decoder.h" #include "include/protected/stream_encoder.h" @@ -51,6 +56,7 @@ #include "include/private/lpc.h" #include "include/private/md5.h" #include "include/private/memory.h" + #if FLAC__HAS_OGG #include "include/private/ogg_helper.h" #include "include/private/ogg_mapping.h" @@ -59,6 +65,7 @@ #include "include/private/stream_encoder_framing.h" #include "include/private/window.h" #include "../alloc.h" +#include "../private.h" /* Exact Rice codeword length calculation is off by default. The simple @@ -76,33 +83,42 @@ typedef struct { FLAC__int32 *data[FLAC__MAX_CHANNELS]; - unsigned size; /* of each data[] in samples */ - unsigned tail; + uint32_t size; /* of each data[] in samples */ + uint32_t tail; } verify_input_fifo; typedef struct { const FLAC__byte *data; - unsigned capacity; - unsigned bytes; + uint32_t capacity; + uint32_t bytes; } verify_output; +#ifndef FLAC__INTEGER_ONLY_LIBRARY +typedef struct { + uint32_t a, b, c; + FLAC__ApodizationSpecification * current_apodization; + double autoc_root[FLAC__MAX_LPC_ORDER+1]; + double autoc[FLAC__MAX_LPC_ORDER+1]; +} apply_apodization_state_struct; +#endif + typedef enum { ENCODER_IN_MAGIC = 0, ENCODER_IN_METADATA = 1, ENCODER_IN_AUDIO = 2 } EncoderStateHint; -static struct CompressionLevels { +static const struct CompressionLevels { FLAC__bool do_mid_side_stereo; FLAC__bool loose_mid_side_stereo; - unsigned max_lpc_order; - unsigned qlp_coeff_precision; + uint32_t max_lpc_order; + uint32_t qlp_coeff_precision; FLAC__bool do_qlp_coeff_prec_search; FLAC__bool do_escape_coding; FLAC__bool do_exhaustive_model_search; - unsigned min_residual_partition_order; - unsigned max_residual_partition_order; - unsigned rice_parameter_search_dist; + uint32_t min_residual_partition_order; + uint32_t max_residual_partition_order; + uint32_t rice_parameter_search_dist; const char *apodization; } compression_levels_[] = { { false, false, 0, 0, false, false, false, 0, 3, 0, "tukey(5e-1)" }, @@ -111,9 +127,9 @@ static struct CompressionLevels { { false, false, 6, 0, false, false, false, 0, 4, 0, "tukey(5e-1)" }, { true , true , 8, 0, false, false, false, 0, 4, 0, "tukey(5e-1)" }, { true , false, 8, 0, false, false, false, 0, 5, 0, "tukey(5e-1)" }, - { true , false, 8, 0, false, false, false, 0, 6, 0, "tukey(5e-1);partial_tukey(2)" }, - { true , false, 12, 0, false, false, false, 0, 6, 0, "tukey(5e-1);partial_tukey(2)" }, - { true , false, 12, 0, false, false, false, 0, 6, 0, "tukey(5e-1);partial_tukey(2);punchout_tukey(3)" } + { true , false, 8, 0, false, false, false, 0, 6, 0, "subdivide_tukey(2)" }, + { true , false, 12, 0, false, false, false, 0, 6, 0, "subdivide_tukey(2)" }, + { true , false, 12, 0, false, false, false, 0, 6, 0, "subdivide_tukey(3)" } /* here we use locale-independent 5e-1 instead of 0.5 or 0,5 */ }; @@ -126,130 +142,140 @@ static struct CompressionLevels { static void set_defaults_(FLAC__StreamEncoder *encoder); static void free_(FLAC__StreamEncoder *encoder); -static FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize); -static FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples, FLAC__bool is_last_block); -static FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, FLAC__bool is_last_block); +static FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, uint32_t new_blocksize); +static FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, uint32_t samples, FLAC__bool is_last_block); +static FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, FLAC__bool is_last_block); static void update_metadata_(const FLAC__StreamEncoder *encoder); #if FLAC__HAS_OGG static void update_ogg_metadata_(FLAC__StreamEncoder *encoder); #endif -static FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block, FLAC__bool is_last_block); -static FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block); +static FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_block); +static FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder); static FLAC__bool process_subframe_( FLAC__StreamEncoder *encoder, - unsigned min_partition_order, - unsigned max_partition_order, + uint32_t min_partition_order, + uint32_t max_partition_order, const FLAC__FrameHeader *frame_header, - unsigned subframe_bps, - const FLAC__int32 integer_signal[], + uint32_t subframe_bps, + const void *integer_signal, FLAC__Subframe *subframe[2], FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents[2], FLAC__int32 *residual[2], - unsigned *best_subframe, - unsigned *best_bits + uint32_t *best_subframe, + uint32_t *best_bits ); +#ifndef FLAC__INTEGER_ONLY_LIBRARY +static FLAC__bool apply_apodization_( + FLAC__StreamEncoder *encoder, + apply_apodization_state_struct *apply_apodization_state, + uint32_t blocksize, + double *lpc_error, + uint32_t *max_lpc_order_this_apodization, + uint32_t subframe_bps, + const void *integer_signal, + uint32_t *guess_lpc_order +); +#endif + static FLAC__bool add_subframe_( FLAC__StreamEncoder *encoder, - unsigned blocksize, - unsigned subframe_bps, + uint32_t blocksize, + uint32_t subframe_bps, const FLAC__Subframe *subframe, FLAC__BitWriter *frame ); -static unsigned evaluate_constant_subframe_( +static uint32_t evaluate_constant_subframe_( FLAC__StreamEncoder *encoder, - const FLAC__int32 signal, - unsigned blocksize, - unsigned subframe_bps, + const FLAC__int64 signal, + uint32_t blocksize, + uint32_t subframe_bps, FLAC__Subframe *subframe ); -static unsigned evaluate_fixed_subframe_( +static uint32_t evaluate_fixed_subframe_( FLAC__StreamEncoder *encoder, - const FLAC__int32 signal[], + const void *signal, FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned raw_bits_per_partition[], - unsigned blocksize, - unsigned subframe_bps, - unsigned order, - unsigned rice_parameter, - unsigned rice_parameter_limit, - unsigned min_partition_order, - unsigned max_partition_order, + uint32_t raw_bits_per_partition[], + uint32_t blocksize, + uint32_t subframe_bps, + uint32_t order, + uint32_t rice_parameter_limit, + uint32_t min_partition_order, + uint32_t max_partition_order, FLAC__bool do_escape_coding, - unsigned rice_parameter_search_dist, + uint32_t rice_parameter_search_dist, FLAC__Subframe *subframe, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents ); #ifndef FLAC__INTEGER_ONLY_LIBRARY -static unsigned evaluate_lpc_subframe_( +static uint32_t evaluate_lpc_subframe_( FLAC__StreamEncoder *encoder, - const FLAC__int32 signal[], + const void *signal, FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned raw_bits_per_partition[], + uint32_t raw_bits_per_partition[], const FLAC__real lp_coeff[], - unsigned blocksize, - unsigned subframe_bps, - unsigned order, - unsigned qlp_coeff_precision, - unsigned rice_parameter, - unsigned rice_parameter_limit, - unsigned min_partition_order, - unsigned max_partition_order, + uint32_t blocksize, + uint32_t subframe_bps, + uint32_t order, + uint32_t qlp_coeff_precision, + uint32_t rice_parameter_limit, + uint32_t min_partition_order, + uint32_t max_partition_order, FLAC__bool do_escape_coding, - unsigned rice_parameter_search_dist, + uint32_t rice_parameter_search_dist, FLAC__Subframe *subframe, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents ); #endif -static unsigned evaluate_verbatim_subframe_( +static uint32_t evaluate_verbatim_subframe_( FLAC__StreamEncoder *encoder, - const FLAC__int32 signal[], - unsigned blocksize, - unsigned subframe_bps, + const void *signal, + uint32_t blocksize, + uint32_t subframe_bps, FLAC__Subframe *subframe ); -static unsigned find_best_partition_order_( +static uint32_t find_best_partition_order_( struct FLAC__StreamEncoderPrivate *private_, const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned raw_bits_per_partition[], - unsigned residual_samples, - unsigned predictor_order, - unsigned rice_parameter, - unsigned rice_parameter_limit, - unsigned min_partition_order, - unsigned max_partition_order, - unsigned bps, + uint32_t raw_bits_per_partition[], + uint32_t residual_samples, + uint32_t predictor_order, + uint32_t rice_parameter_limit, + uint32_t min_partition_order, + uint32_t max_partition_order, + uint32_t bps, FLAC__bool do_escape_coding, - unsigned rice_parameter_search_dist, + uint32_t rice_parameter_search_dist, FLAC__EntropyCodingMethod *best_ecm ); static void precompute_partition_info_sums_( const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned residual_samples, - unsigned predictor_order, - unsigned min_partition_order, - unsigned max_partition_order, - unsigned bps + uint32_t residual_samples, + uint32_t predictor_order, + uint32_t min_partition_order, + uint32_t max_partition_order, + uint32_t bps ); static void precompute_partition_info_escapes_( const FLAC__int32 residual[], - unsigned raw_bits_per_partition[], - unsigned residual_samples, - unsigned predictor_order, - unsigned min_partition_order, - unsigned max_partition_order + uint32_t raw_bits_per_partition[], + uint32_t residual_samples, + uint32_t predictor_order, + uint32_t min_partition_order, + uint32_t max_partition_order ); static FLAC__bool set_partitioned_rice_( @@ -257,35 +283,35 @@ static FLAC__bool set_partitioned_rice_( const FLAC__int32 residual[], #endif const FLAC__uint64 abs_residual_partition_sums[], - const unsigned raw_bits_per_partition[], - const unsigned residual_samples, - const unsigned predictor_order, - const unsigned suggested_rice_parameter, - const unsigned rice_parameter_limit, - const unsigned rice_parameter_search_dist, - const unsigned partition_order, + const uint32_t raw_bits_per_partition[], + const uint32_t residual_samples, + const uint32_t predictor_order, + const uint32_t rice_parameter_limit, + const uint32_t rice_parameter_search_dist, + const uint32_t partition_order, const FLAC__bool search_for_escapes, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, - unsigned *bits + uint32_t *bits ); -static unsigned get_wasted_bits_(FLAC__int32 signal[], unsigned samples); +static uint32_t get_wasted_bits_(FLAC__int32 signal[], uint32_t samples); +static uint32_t get_wasted_bits_wide_(FLAC__int64 signal_wide[], FLAC__int32 signal[], uint32_t samples); /* verify-related routines: */ static void append_to_verify_fifo_( verify_input_fifo *fifo, const FLAC__int32 * const input[], - unsigned input_offset, - unsigned channels, - unsigned wide_samples + uint32_t input_offset, + uint32_t channels, + uint32_t wide_samples ); static void append_to_verify_fifo_interleaved_( verify_input_fifo *fifo, const FLAC__int32 input[], - unsigned input_offset, - unsigned channels, - unsigned wide_samples + uint32_t input_offset, + uint32_t channels, + uint32_t wide_samples ); static FLAC__StreamDecoderReadStatus verify_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); @@ -296,7 +322,7 @@ static void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__Str //static FLAC__StreamEncoderReadStatus file_read_callback_(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); //static FLAC__StreamEncoderSeekStatus file_seek_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); //static FLAC__StreamEncoderTellStatus file_tell_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); -//static FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); +//static FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, uint32_t current_frame, void *client_data); //static FILE *get_binary_stdout_(void); @@ -307,17 +333,18 @@ static void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__Str ***********************************************************************/ typedef struct FLAC__StreamEncoderPrivate { - unsigned input_capacity; /* current size (in samples) of the signal and residual buffers */ + uint32_t input_capacity; /* current size (in samples) of the signal and residual buffers */ FLAC__int32 *integer_signal[FLAC__MAX_CHANNELS]; /* the integer version of the input signal */ FLAC__int32 *integer_signal_mid_side[2]; /* the integer version of the mid-side input signal (stereo only) */ + FLAC__int64 *integer_signal_33bit_side; /* 33-bit side for 32-bit stereo decorrelation */ #ifndef FLAC__INTEGER_ONLY_LIBRARY FLAC__real *real_signal[FLAC__MAX_CHANNELS]; /* (@@@ currently unused) the floating-point version of the input signal */ FLAC__real *real_signal_mid_side[2]; /* (@@@ currently unused) the floating-point version of the mid-side input signal (stereo only) */ FLAC__real *window[FLAC__MAX_APODIZATION_FUNCTIONS]; /* the pre-computed floating-point window for each apodization function */ FLAC__real *windowed_signal; /* the integer_signal[] * current window[] */ #endif - unsigned subframe_bps[FLAC__MAX_CHANNELS]; /* the effective bits per sample of the input signal (stream bps - wasted bits) */ - unsigned subframe_bps_mid_side[2]; /* the effective bits per sample of the mid-side input signal (stream bps - wasted bits + 0/1) */ + uint32_t subframe_bps[FLAC__MAX_CHANNELS]; /* the effective bits per sample of the input signal (stream bps - wasted bits) */ + uint32_t subframe_bps_mid_side[2]; /* the effective bits per sample of the mid-side input signal (stream bps - wasted bits + 0/1) */ FLAC__int32 *residual_workspace[FLAC__MAX_CHANNELS][2]; /* each channel has a candidate and best workspace where the subframe residual signals will be stored */ FLAC__int32 *residual_workspace_mid_side[2][2]; FLAC__Subframe subframe_workspace[FLAC__MAX_CHANNELS][2]; @@ -328,45 +355,49 @@ typedef struct FLAC__StreamEncoderPrivate { FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents_workspace_mid_side[FLAC__MAX_CHANNELS][2]; FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents_workspace_ptr[FLAC__MAX_CHANNELS][2]; FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents_workspace_ptr_mid_side[FLAC__MAX_CHANNELS][2]; - unsigned best_subframe[FLAC__MAX_CHANNELS]; /* index (0 or 1) into 2nd dimension of the above workspaces */ - unsigned best_subframe_mid_side[2]; - unsigned best_subframe_bits[FLAC__MAX_CHANNELS]; /* size in bits of the best subframe for each channel */ - unsigned best_subframe_bits_mid_side[2]; + uint32_t best_subframe[FLAC__MAX_CHANNELS]; /* index (0 or 1) into 2nd dimension of the above workspaces */ + uint32_t best_subframe_mid_side[2]; + uint32_t best_subframe_bits[FLAC__MAX_CHANNELS]; /* size in bits of the best subframe for each channel */ + uint32_t best_subframe_bits_mid_side[2]; FLAC__uint64 *abs_residual_partition_sums; /* workspace where the sum of abs(candidate residual) for each partition is stored */ - unsigned *raw_bits_per_partition; /* workspace where the sum of silog2(candidate residual) for each partition is stored */ + uint32_t *raw_bits_per_partition; /* workspace where the sum of silog2(candidate residual) for each partition is stored */ FLAC__BitWriter *frame; /* the current frame being worked on */ - unsigned loose_mid_side_stereo_frames; /* rounded number of frames the encoder will use before trying both independent and mid/side frames again */ - unsigned loose_mid_side_stereo_frame_count; /* number of frames using the current channel assignment */ + uint32_t loose_mid_side_stereo_frames; /* rounded number of frames the encoder will use before trying both independent and mid/side frames again */ + uint32_t loose_mid_side_stereo_frame_count; /* number of frames using the current channel assignment */ FLAC__ChannelAssignment last_channel_assignment; FLAC__StreamMetadata streaminfo; /* scratchpad for STREAMINFO as it is built */ FLAC__StreamMetadata_SeekTable *seek_table; /* pointer into encoder->protected_->metadata_ where the seek table is */ - unsigned current_sample_number; - unsigned current_frame_number; + uint32_t current_sample_number; + uint32_t current_frame_number; FLAC__MD5Context md5context; FLAC__CPUInfo cpuinfo; - void (*local_precompute_partition_info_sums)(const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], unsigned residual_samples, unsigned predictor_order, unsigned min_partition_order, unsigned max_partition_order, unsigned bps); + void (*local_precompute_partition_info_sums)(const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], uint32_t residual_samples, uint32_t predictor_order, uint32_t min_partition_order, uint32_t max_partition_order, uint32_t bps); #ifndef FLAC__INTEGER_ONLY_LIBRARY - unsigned (*local_fixed_compute_best_predictor)(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); - unsigned (*local_fixed_compute_best_predictor_wide)(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); + uint32_t (*local_fixed_compute_best_predictor)(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); + uint32_t (*local_fixed_compute_best_predictor_wide)(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); + uint32_t (*local_fixed_compute_best_predictor_limit_residual)(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); #else - unsigned (*local_fixed_compute_best_predictor)(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); - unsigned (*local_fixed_compute_best_predictor_wide)(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); + uint32_t (*local_fixed_compute_best_predictor)(const FLAC__int32 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); + uint32_t (*local_fixed_compute_best_predictor_wide)(const FLAC__int32 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); + uint32_t (*local_fixed_compute_best_predictor_limit_residual)(const FLAC__int32 data[], uint32_t data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); #endif #ifndef FLAC__INTEGER_ONLY_LIBRARY - void (*local_lpc_compute_autocorrelation)(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); - void (*local_lpc_compute_residual_from_qlp_coefficients)(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); - void (*local_lpc_compute_residual_from_qlp_coefficients_64bit)(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); - void (*local_lpc_compute_residual_from_qlp_coefficients_16bit)(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); + void (*local_lpc_compute_autocorrelation)(const FLAC__real data[], uint32_t data_len, uint32_t lag, double autoc[]); + void (*local_lpc_compute_residual_from_qlp_coefficients)(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); + void (*local_lpc_compute_residual_from_qlp_coefficients_64bit)(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); + void (*local_lpc_compute_residual_from_qlp_coefficients_16bit)(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); #endif - FLAC__bool use_wide_by_block; /* use slow 64-bit versions of some functions because of the block size */ - FLAC__bool use_wide_by_partition; /* use slow 64-bit versions of some functions because of the min partition order and blocksize */ - FLAC__bool use_wide_by_order; /* use slow 64-bit versions of some functions because of the lpc order */ + FLAC__bool disable_mmx; + FLAC__bool disable_sse2; + FLAC__bool disable_ssse3; + FLAC__bool disable_sse41; + FLAC__bool disable_sse42; + FLAC__bool disable_avx2; + FLAC__bool disable_fma; FLAC__bool disable_constant_subframes; FLAC__bool disable_fixed_subframes; FLAC__bool disable_verbatim_subframes; -#if FLAC__HAS_OGG FLAC__bool is_ogg; -#endif FLAC__StreamEncoderReadCallback read_callback; /* currently only needed for Ogg FLAC */ FLAC__StreamEncoderSeekCallback seek_callback; FLAC__StreamEncoderTellCallback tell_callback; @@ -374,15 +405,16 @@ typedef struct FLAC__StreamEncoderPrivate { FLAC__StreamEncoderMetadataCallback metadata_callback; FLAC__StreamEncoderProgressCallback progress_callback; void *client_data; - unsigned first_seekpoint_to_check; + uint32_t first_seekpoint_to_check; FILE *file; /* only used when encoding to a file */ FLAC__uint64 bytes_written; FLAC__uint64 samples_written; - unsigned frames_written; - unsigned total_frames_estimate; + uint32_t frames_written; + uint32_t total_frames_estimate; /* unaligned (original) pointers to allocated data */ FLAC__int32 *integer_signal_unaligned[FLAC__MAX_CHANNELS]; FLAC__int32 *integer_signal_mid_side_unaligned[2]; + FLAC__int64 *integer_signal_33bit_side_unaligned; #ifndef FLAC__INTEGER_ONLY_LIBRARY FLAC__real *real_signal_unaligned[FLAC__MAX_CHANNELS]; /* (@@@ currently unused) */ FLAC__real *real_signal_mid_side_unaligned[2]; /* (@@@ currently unused) */ @@ -392,7 +424,7 @@ typedef struct FLAC__StreamEncoderPrivate { FLAC__int32 *residual_workspace_unaligned[FLAC__MAX_CHANNELS][2]; FLAC__int32 *residual_workspace_mid_side_unaligned[2][2]; FLAC__uint64 *abs_residual_partition_sums_unaligned; - unsigned *raw_bits_per_partition_unaligned; + uint32_t *raw_bits_per_partition_unaligned; /* * These fields have been moved here from private function local * declarations merely to save stack space during encoding. @@ -412,9 +444,9 @@ typedef struct FLAC__StreamEncoderPrivate { verify_output output; struct { FLAC__uint64 absolute_sample; - unsigned frame_number; - unsigned channel; - unsigned sample; + uint32_t frame_number; + uint32_t channel; + uint32_t sample; FLAC__int32 expected; FLAC__int32 got; } error_stats; @@ -492,7 +524,7 @@ FLAC_API const char * const FLAC__StreamEncoderTellStatusString[] = { * WATCHOUT: some parts of the code assert that OVERREAD_ == 1 and there's * not really any reason to change it. */ -static const unsigned OVERREAD_ = 1; +static const uint32_t OVERREAD_ = 1; /*********************************************************************** * @@ -502,7 +534,7 @@ static const unsigned OVERREAD_ = 1; FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void) { FLAC__StreamEncoder *encoder; - unsigned i; + uint32_t i; FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */ @@ -534,6 +566,8 @@ FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void) encoder->private_->file = 0; + encoder->protected_->state = FLAC__STREAM_ENCODER_UNINITIALIZED; + set_defaults_(encoder); encoder->private_->is_being_deleted = false; @@ -566,14 +600,12 @@ FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void) for(i = 0; i < 2; i++) FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_extra[i]); - encoder->protected_->state = FLAC__STREAM_ENCODER_UNINITIALIZED; - return encoder; } FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder) { - unsigned i; + uint32_t i; if (encoder == NULL) return ; @@ -623,7 +655,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( FLAC__bool is_ogg ) { - unsigned i; + uint32_t i; FLAC__bool metadata_has_seektable, metadata_has_vorbis_comment, metadata_picture_has_type1, metadata_picture_has_type2; FLAC__ASSERT(0 != encoder); @@ -631,10 +663,10 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED; -#if !FLAC__HAS_OGG + #if FLAC__HAS_OGG == 0 if(is_ogg) return FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER; -#endif + #endif if(0 == write_callback || (seek_callback && 0 == tell_callback)) return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS; @@ -649,10 +681,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( else if(!encoder->protected_->do_mid_side_stereo) encoder->protected_->loose_mid_side_stereo = false; - if(encoder->protected_->bits_per_sample >= 32) - encoder->protected_->do_mid_side_stereo = false; /* since we currenty do 32-bit math, the side channel would have 33 bps and overflow */ - - if(encoder->protected_->bits_per_sample < FLAC__MIN_BITS_PER_SAMPLE || encoder->protected_->bits_per_sample > FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE) + if(encoder->protected_->bits_per_sample < FLAC__MIN_BITS_PER_SAMPLE || encoder->protected_->bits_per_sample > FLAC__MAX_BITS_PER_SAMPLE) return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE; if(!FLAC__format_sample_rate_is_valid(encoder->protected_->sample_rate)) @@ -719,7 +748,8 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( encoder->protected_->bits_per_sample != 12 && encoder->protected_->bits_per_sample != 16 && encoder->protected_->bits_per_sample != 20 && - encoder->protected_->bits_per_sample != 24 + encoder->protected_->bits_per_sample != 24 && + encoder->protected_->bits_per_sample != 32 ) return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; if(encoder->protected_->max_residual_partition_order > FLAC__SUBSET_MAX_RICE_PARTITION_ORDER) @@ -741,9 +771,21 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( encoder->protected_->min_residual_partition_order = encoder->protected_->max_residual_partition_order; #if FLAC__HAS_OGG + /* drop any seektable for ogg */ + if(is_ogg && 0 != encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 0) { + uint32_t i1; + for(i1 = 0; i1 < encoder->protected_->num_metadata_blocks; i1++) { + if(0 != encoder->protected_->metadata[i1] && encoder->protected_->metadata[i1]->type == FLAC__METADATA_TYPE_SEEKTABLE) { + encoder->protected_->num_metadata_blocks--; + for( ; i1 < encoder->protected_->num_metadata_blocks; i1++) + encoder->protected_->metadata[i1] = encoder->protected_->metadata[i1+1]; + break; + } + } + } /* reorder metadata if necessary to ensure that any VORBIS_COMMENT is the first, according to the mapping spec */ if(is_ogg && 0 != encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 1) { - unsigned i1; + uint32_t i1; for(i1 = 1; i1 < encoder->protected_->num_metadata_blocks; i1++) { if(0 != encoder->protected_->metadata[i1] && encoder->protected_->metadata[i1]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { FLAC__StreamMetadata *vc = encoder->protected_->metadata[i1]; @@ -757,7 +799,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( #endif /* keep track of any SEEKTABLE block */ if(0 != encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 0) { - unsigned i2; + uint32_t i2; for(i2 = 0; i2 < encoder->protected_->num_metadata_blocks; i2++) { if(0 != encoder->protected_->metadata[i2] && encoder->protected_->metadata[i2]->type == FLAC__METADATA_TYPE_SEEKTABLE) { encoder->private_->seek_table = &encoder->protected_->metadata[i2]->data.seek_table; @@ -832,6 +874,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( encoder->private_->real_signal_mid_side_unaligned[i] = encoder->private_->real_signal_mid_side[i] = 0; #endif } + encoder->private_->integer_signal_33bit_side_unaligned = encoder->private_->integer_signal_33bit_side = 0; #ifndef FLAC__INTEGER_ONLY_LIBRARY for(i = 0; i < encoder->protected_->num_apodizations; i++) encoder->private_->window_unaligned[i] = encoder->private_->window[i] = 0; @@ -850,15 +893,15 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( encoder->private_->abs_residual_partition_sums_unaligned = encoder->private_->abs_residual_partition_sums = 0; encoder->private_->raw_bits_per_partition_unaligned = encoder->private_->raw_bits_per_partition = 0; #ifndef FLAC__INTEGER_ONLY_LIBRARY - encoder->private_->loose_mid_side_stereo_frames = (unsigned)((FLAC__double)encoder->protected_->sample_rate * 0.4 / (FLAC__double)encoder->protected_->blocksize + 0.5); + encoder->private_->loose_mid_side_stereo_frames = (uint32_t)((double)encoder->protected_->sample_rate * 0.4 / (double)encoder->protected_->blocksize + 0.5); #else /* 26214 is the approximate fixed-point equivalent to 0.4 (0.4 * 2^16) */ - /* sample rate can be up to 655350 Hz, and thus use 20 bits, so we do the multiply÷ by hand */ - FLAC__ASSERT(FLAC__MAX_SAMPLE_RATE <= 655350); + /* sample rate can be up to 1048575 Hz, and thus use 20 bits, so we do the multiply÷ by hand */ + FLAC__ASSERT(FLAC__MAX_SAMPLE_RATE <= 1048575); FLAC__ASSERT(FLAC__MAX_BLOCK_SIZE <= 65535); - FLAC__ASSERT(encoder->protected_->sample_rate <= 655350); + FLAC__ASSERT(encoder->protected_->sample_rate <= 1048575); FLAC__ASSERT(encoder->protected_->blocksize <= 65535); - encoder->private_->loose_mid_side_stereo_frames = (unsigned)FLAC__fixedpoint_trunc((((FLAC__uint64)(encoder->protected_->sample_rate) * (FLAC__uint64)(26214)) << 16) / (encoder->protected_->blocksize<<16) + FLAC__FP_ONE_HALF); + encoder->private_->loose_mid_side_stereo_frames = (uint32_t)FLAC__fixedpoint_trunc((((FLAC__uint64)(encoder->protected_->sample_rate) * (FLAC__uint64)(26214)) << 16) / (encoder->protected_->blocksize<<16) + FLAC__FP_ONE_HALF); #endif if(encoder->private_->loose_mid_side_stereo_frames == 0) encoder->private_->loose_mid_side_stereo_frames = 1; @@ -866,14 +909,26 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( encoder->private_->current_sample_number = 0; encoder->private_->current_frame_number = 0; - encoder->private_->use_wide_by_block = (encoder->protected_->bits_per_sample + FLAC__bitmath_ilog2(encoder->protected_->blocksize)+1 > 30); - encoder->private_->use_wide_by_order = (encoder->protected_->bits_per_sample + FLAC__bitmath_ilog2(flac_max(encoder->protected_->max_lpc_order, FLAC__MAX_FIXED_ORDER))+1 > 30); /*@@@ need to use this? */ - encoder->private_->use_wide_by_partition = (false); /*@@@ need to set this */ - /* * get the CPU info and set the function pointers */ FLAC__cpu_info(&encoder->private_->cpuinfo); + /* remove cpu info as requested by + * FLAC__stream_encoder_disable_instruction_set */ + if(encoder->private_->disable_mmx) + encoder->private_->cpuinfo.x86.mmx = false; + if(encoder->private_->disable_sse2) + encoder->private_->cpuinfo.x86.sse2 = false; + if(encoder->private_->disable_ssse3) + encoder->private_->cpuinfo.x86.ssse3 = false; + if(encoder->private_->disable_sse41) + encoder->private_->cpuinfo.x86.sse41 = false; + if(encoder->private_->disable_sse42) + encoder->private_->cpuinfo.x86.sse42 = false; + if(encoder->private_->disable_avx2) + encoder->private_->cpuinfo.x86.avx2 = false; + if(encoder->private_->disable_fma) + encoder->private_->cpuinfo.x86.fma = false; /* first default to the non-asm routines */ #ifndef FLAC__INTEGER_ONLY_LIBRARY encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation; @@ -881,6 +936,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( encoder->private_->local_precompute_partition_info_sums = precompute_partition_info_sums_; encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor; encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide; + encoder->private_->local_fixed_compute_best_predictor_limit_residual = FLAC__fixed_compute_best_predictor_limit_residual; #ifndef FLAC__INTEGER_ONLY_LIBRARY encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients; encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide; @@ -889,68 +945,47 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( /* now override with asm where appropriate */ #ifndef FLAC__INTEGER_ONLY_LIBRARY # ifndef FLAC__NO_ASM +#if defined FLAC__CPU_ARM64 && FLAC__HAS_NEONINTRIN +#if FLAC__HAS_A64NEONINTRIN + if(encoder->protected_->max_lpc_order < 8) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_neon_lag_8; + else if(encoder->protected_->max_lpc_order < 10) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_neon_lag_10; + else if(encoder->protected_->max_lpc_order < 14) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_neon_lag_14; + else + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation; +#endif + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_neon; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_neon; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_neon; +#endif /* defined FLAC__CPU_ARM64 && FLAC__HAS_NEONINTRIN */ + if(encoder->private_->cpuinfo.use_asm) { # ifdef FLAC__CPU_IA32 FLAC__ASSERT(encoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_IA32); -# ifdef FLAC__HAS_NASM - if(encoder->private_->cpuinfo.ia32.sse) { - if(encoder->protected_->max_lpc_order < 4) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4; - else if(encoder->protected_->max_lpc_order < 8) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8; - else if(encoder->protected_->max_lpc_order < 12) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12; - else if(encoder->protected_->max_lpc_order < 16) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_16; - else - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32; - } - else - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32; - - encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide_asm_ia32; /* OPT_IA32: was really necessary for GCC < 4.9 */ - if(encoder->private_->cpuinfo.ia32.mmx) { - encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; - encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx; - } - else { - encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; - encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; - } - - if(encoder->private_->cpuinfo.ia32.mmx && encoder->private_->cpuinfo.ia32.cmov) - encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov; -# endif /* FLAC__HAS_NASM */ -# ifdef FLAC__HAS_X86INTRIN -# if defined FLAC__SSE_SUPPORTED - if(encoder->private_->cpuinfo.ia32.sse) { - if(encoder->protected_->max_lpc_order < 4) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4; - else if(encoder->protected_->max_lpc_order < 8) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8; - else if(encoder->protected_->max_lpc_order < 12) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12; - else if(encoder->protected_->max_lpc_order < 16) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16; - else - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation; - } -# endif - +# if FLAC__HAS_X86INTRIN # ifdef FLAC__SSE2_SUPPORTED - if(encoder->private_->cpuinfo.ia32.sse2) { + if (encoder->private_->cpuinfo.x86.sse2) { + if(encoder->protected_->max_lpc_order < 8) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse2_lag_8; + else if(encoder->protected_->max_lpc_order < 10) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse2_lag_10; + else if(encoder->protected_->max_lpc_order < 14) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse2_lag_14; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse2; encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2; } # endif # ifdef FLAC__SSE4_1_SUPPORTED - if(encoder->private_->cpuinfo.ia32.sse41) { + if (encoder->private_->cpuinfo.x86.sse41) { encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse41; encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_sse41; } # endif # ifdef FLAC__AVX2_SUPPORTED - if(encoder->private_->cpuinfo.ia32.avx2) { + if (encoder->private_->cpuinfo.x86.avx2) { encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_avx2; encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_avx2; encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2; @@ -958,34 +993,41 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( # endif # ifdef FLAC__SSE2_SUPPORTED - if (encoder->private_->cpuinfo.ia32.sse2) { + if (encoder->private_->cpuinfo.x86.sse2) { encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_intrin_sse2; - encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide_intrin_sse2; } # endif # ifdef FLAC__SSSE3_SUPPORTED - if (encoder->private_->cpuinfo.ia32.ssse3) { + if (encoder->private_->cpuinfo.x86.ssse3) { encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_intrin_ssse3; - encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide_intrin_ssse3; + } +# endif +# ifdef FLAC__SSE4_2_SUPPORTED + if (encoder->private_->cpuinfo.x86.sse42) { + encoder->private_->local_fixed_compute_best_predictor_limit_residual = FLAC__fixed_compute_best_predictor_limit_residual_intrin_sse42; + } +# endif +# ifdef FLAC__AVX2_SUPPORTED + if (encoder->private_->cpuinfo.x86.avx2) { + encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide_intrin_avx2; + encoder->private_->local_fixed_compute_best_predictor_limit_residual = FLAC__fixed_compute_best_predictor_limit_residual_intrin_avx2; } # endif # endif /* FLAC__HAS_X86INTRIN */ # elif defined FLAC__CPU_X86_64 FLAC__ASSERT(encoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_X86_64); -# ifdef FLAC__HAS_X86INTRIN -# ifdef FLAC__SSE_SUPPORTED - if(encoder->protected_->max_lpc_order < 4) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4; - else if(encoder->protected_->max_lpc_order < 8) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8; - else if(encoder->protected_->max_lpc_order < 12) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12; - else if(encoder->protected_->max_lpc_order < 16) - encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16; -# endif - +# if FLAC__HAS_X86INTRIN # ifdef FLAC__SSE2_SUPPORTED - encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2; + if(encoder->private_->cpuinfo.x86.sse2) { /* For fuzzing */ + if(encoder->protected_->max_lpc_order < 8) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse2_lag_8; + else if(encoder->protected_->max_lpc_order < 10) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse2_lag_10; + else if(encoder->protected_->max_lpc_order < 14) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_sse2_lag_14; + + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2; + } # endif # ifdef FLAC__SSE4_1_SUPPORTED if(encoder->private_->cpuinfo.x86.sse41) { @@ -999,56 +1041,63 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2; } # endif +# ifdef FLAC__FMA_SUPPORTED + if(encoder->private_->cpuinfo.x86.fma) { + if(encoder->protected_->max_lpc_order < 8) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_fma_lag_8; + else if(encoder->protected_->max_lpc_order < 12) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_fma_lag_12; + else if(encoder->protected_->max_lpc_order < 16) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_intrin_fma_lag_16; + } +# endif + # ifdef FLAC__SSE2_SUPPORTED - encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_intrin_sse2; - encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide_intrin_sse2; + if(encoder->private_->cpuinfo.x86.sse2) { /* For fuzzing */ + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_intrin_sse2; + } # endif # ifdef FLAC__SSSE3_SUPPORTED if (encoder->private_->cpuinfo.x86.ssse3) { encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_intrin_ssse3; - encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide_intrin_ssse3; + } +# endif +# ifdef FLAC__SSE4_2_SUPPORTED + if (encoder->private_->cpuinfo.x86.sse42) { + encoder->private_->local_fixed_compute_best_predictor_limit_residual = FLAC__fixed_compute_best_predictor_limit_residual_intrin_sse42; + } +# endif +# ifdef FLAC__AVX2_SUPPORTED + if (encoder->private_->cpuinfo.x86.avx2) { + encoder->private_->local_fixed_compute_best_predictor_wide = FLAC__fixed_compute_best_predictor_wide_intrin_avx2; + encoder->private_->local_fixed_compute_best_predictor_limit_residual = FLAC__fixed_compute_best_predictor_limit_residual_intrin_avx2; } # endif # endif /* FLAC__HAS_X86INTRIN */ # endif /* FLAC__CPU_... */ } # endif /* !FLAC__NO_ASM */ + #endif /* !FLAC__INTEGER_ONLY_LIBRARY */ -#if !defined FLAC__NO_ASM && defined FLAC__HAS_X86INTRIN +#if !defined FLAC__NO_ASM && FLAC__HAS_X86INTRIN if(encoder->private_->cpuinfo.use_asm) { -# if defined FLAC__CPU_IA32 +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) # ifdef FLAC__SSE2_SUPPORTED - if(encoder->private_->cpuinfo.ia32.sse2) + if (encoder->private_->cpuinfo.x86.sse2) encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_sse2; # endif # ifdef FLAC__SSSE3_SUPPORTED - if(encoder->private_->cpuinfo.ia32.ssse3) - encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_ssse3; -# endif -# ifdef FLAC__AVX2_SUPPORTED - if(encoder->private_->cpuinfo.ia32.avx2) - encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_avx2; -# endif -# elif defined FLAC__CPU_X86_64 -# ifdef FLAC__SSE2_SUPPORTED - encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_sse2; -# endif -# ifdef FLAC__SSSE3_SUPPORTED - if(encoder->private_->cpuinfo.x86.ssse3) + if (encoder->private_->cpuinfo.x86.ssse3) encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_ssse3; # endif # ifdef FLAC__AVX2_SUPPORTED - if(encoder->private_->cpuinfo.x86.avx2) + if (encoder->private_->cpuinfo.x86.avx2) encoder->private_->local_precompute_partition_info_sums = FLAC__precompute_partition_info_sums_intrin_avx2; # endif # endif /* FLAC__CPU_... */ } #endif /* !FLAC__NO_ASM && FLAC__HAS_X86INTRIN */ - /* finally override based on wide-ness if necessary */ - if(encoder->private_->use_wide_by_block) { - encoder->private_->local_fixed_compute_best_predictor = encoder->private_->local_fixed_compute_best_predictor_wide; - } /* set state to OK; from here on, errors are fatal and we'll override the state then */ encoder->protected_->state = FLAC__STREAM_ENCODER_OK; @@ -1161,7 +1210,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( memset(encoder->private_->streaminfo.data.stream_info.md5sum, 0, 16); /* we don't know this yet; have to fill it in later */ if(encoder->protected_->do_md5) FLAC__MD5Init(&encoder->private_->md5context); - if(!FLAC__add_metadata_block(&encoder->private_->streaminfo, encoder->private_->frame)) { + if(!FLAC__add_metadata_block(&encoder->private_->streaminfo, encoder->private_->frame, true)) { encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; } @@ -1197,7 +1246,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( vorbis_comment.data.vorbis_comment.vendor_string.entry = 0; vorbis_comment.data.vorbis_comment.num_comments = 0; vorbis_comment.data.vorbis_comment.comments = 0; - if(!FLAC__add_metadata_block(&vorbis_comment, encoder->private_->frame)) { + if(!FLAC__add_metadata_block(&vorbis_comment, encoder->private_->frame, true)) { encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; } @@ -1212,7 +1261,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_( */ for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) { encoder->protected_->metadata[i]->is_last = (i == encoder->protected_->num_metadata_blocks - 1); - if(!FLAC__add_metadata_block(encoder->protected_->metadata[i], encoder->private_->frame)) { + if(!FLAC__add_metadata_block(encoder->protected_->metadata[i], encoder->private_->frame, true)) { encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; } @@ -1313,7 +1362,8 @@ static FLAC__StreamEncoderInitStatus init_FILE_internal_( * Windows can suffer quite badly from disk fragmentation. This can be * reduced significantly by setting the output buffer size to be 10MB. */ - setvbuf(file, NULL, _IOFBF, 10*1024*1024); + if(GetFileType((HANDLE)_get_osfhandle(_fileno(file))) == FILE_TYPE_DISK) + setvbuf(file, NULL, _IOFBF, 10*1024*1024); #endif encoder->private_->file = file; @@ -1338,10 +1388,10 @@ static FLAC__StreamEncoderInitStatus init_FILE_internal_( } { - unsigned blocksize = FLAC__stream_encoder_get_blocksize(encoder); + uint32_t blocksize = FLAC__stream_encoder_get_blocksize(encoder); FLAC__ASSERT(blocksize != 0); - encoder->private_->total_frames_estimate = (unsigned)((FLAC__stream_encoder_get_total_samples_estimate(encoder) + blocksize - 1) / blocksize); + encoder->private_->total_frames_estimate = (uint32_t)((FLAC__stream_encoder_get_total_samples_estimate(encoder) + blocksize - 1) / blocksize); } return init_status; @@ -1422,18 +1472,34 @@ FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder) { FLAC__bool error = false; - FLAC__ASSERT(0 != encoder); + if (encoder == NULL) + return false; + FLAC__ASSERT(0 != encoder->private_); FLAC__ASSERT(0 != encoder->protected_); - if(encoder->protected_->state == FLAC__STREAM_ENCODER_UNINITIALIZED) + if(encoder->protected_->state == FLAC__STREAM_ENCODER_UNINITIALIZED){ + if(encoder->protected_->metadata){ // True in case FLAC__stream_encoder_set_metadata was used but init failed + free(encoder->protected_->metadata); + encoder->protected_->metadata = 0; + encoder->protected_->num_metadata_blocks = 0; + } + if(0 != encoder->private_->file) { + if(encoder->private_->file != stdout) + fclose(encoder->private_->file); + encoder->private_->file = 0; + } return true; + } if(encoder->protected_->state == FLAC__STREAM_ENCODER_OK && !encoder->private_->is_being_deleted) { if(encoder->private_->current_sample_number != 0) { - const FLAC__bool is_fractional_block = encoder->protected_->blocksize != encoder->private_->current_sample_number; encoder->protected_->blocksize = encoder->private_->current_sample_number; - if(!process_frame_(encoder, is_fractional_block, /*is_last_block=*/true)) + if(!resize_buffers_(encoder, encoder->protected_->blocksize)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + if(!process_frame_(encoder, /*is_last_block=*/true)) error = true; } } @@ -1527,7 +1593,15 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncod return true; } -inline FLAC_API FLAC__bool FLAC__stream_encoder_set_do_md5(FLAC__StreamEncoder *encoder, FLAC__bool value) +/* + * The following routine was intended as debug routine and is not in the + * public headers, but SHOULD NOT CHANGE! It is known is is used in + * some non-audio projects needing every last bit of performance. + * See https://github.com/xiph/flac/issues/547 for details. These projects + * provide their own prototype, so changing the signature of this function + * would break building. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_md5(FLAC__StreamEncoder *encoder, FLAC__bool value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1538,7 +1612,7 @@ inline FLAC_API FLAC__bool FLAC__stream_encoder_set_do_md5(FLAC__StreamEncoder * return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, unsigned value) +FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1549,7 +1623,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encod return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, unsigned value) +FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1560,7 +1634,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, unsigned value) +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1571,7 +1645,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *en return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, unsigned value) +FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__bool ok = true; FLAC__ASSERT(0 != encoder); @@ -1604,7 +1678,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncod return ok; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, unsigned value) +FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1691,7 +1765,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *en encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_TUKEY; } } - else if(n>15 && 0 == strncmp("partial_tukey(" , specification, 14)) { + else if(n>15 && 0 == strncmp("partial_tukey(", specification, 14)) { FLAC__int32 tukey_parts = (FLAC__int32)strtod(specification+14, 0); const char *si_1 = strchr(specification, '/'); FLAC__real overlap = si_1?flac_min((FLAC__real)strtod(si_1+1, 0),0.99f):0.1f; @@ -1712,7 +1786,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *en } } } - else if(n>16 && 0 == strncmp("punchout_tukey(" , specification, 15)) { + else if(n>16 && 0 == strncmp("punchout_tukey(", specification, 15)) { FLAC__int32 tukey_parts = (FLAC__int32)strtod(specification+15, 0); const char *si_1 = strchr(specification, '/'); FLAC__real overlap = si_1?flac_min((FLAC__real)strtod(si_1+1, 0),0.99f):0.2f; @@ -1733,6 +1807,20 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *en } } } + else if(n>17 && 0 == strncmp("subdivide_tukey(", specification, 16)){ + FLAC__int32 parts = (FLAC__int32)strtod(specification+16, 0); + if(parts > 1){ + const char *si_1 = strchr(specification, '/'); + FLAC__real p = si_1?(FLAC__real)strtod(si_1+1, 0):5e-1; + if(p > 1) + p = 1; + else if(p < 0) + p = 0; + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.subdivide_tukey.parts = parts; + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.subdivide_tukey.p = p/parts; + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_SUBDIVIDE_TUKEY; + } + } else if(n==5 && 0 == strncmp("welch" , specification, n)) encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_WELCH; if (encoder->protected_->num_apodizations == 32) @@ -1751,7 +1839,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *en return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_max_lpc_order(FLAC__StreamEncoder *encoder, unsigned value) +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_lpc_order(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1762,7 +1850,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_max_lpc_order(FLAC__StreamEncoder * return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_qlp_coeff_precision(FLAC__StreamEncoder *encoder, unsigned value) +FLAC_API FLAC__bool FLAC__stream_encoder_set_qlp_coeff_precision(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1791,8 +1879,10 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_do_escape_coding(FLAC__StreamEncode FLAC__ASSERT(0 != encoder->protected_); if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) return false; -#if 0 - /*@@@ deprecated: */ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* was deprecated since FLAC 1.0.4 (24-Sep-2002), but is needed for + * full spec coverage, so this should be reenabled at some point. + * For now only enable while fuzzing */ encoder->protected_->do_escape_coding = value; #else (void)value; @@ -1811,7 +1901,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_do_exhaustive_model_search(FLAC__St return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value) +FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1822,7 +1912,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__ return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value) +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1833,7 +1923,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__ return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, unsigned value) +FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, uint32_t value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1856,11 +1946,12 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__Stream FLAC__ASSERT(0 != encoder->protected_); if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) return false; + value = flac_min(value, (FLAC__uint64)((FLAC__U64L(1) << FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN) - 1)); encoder->protected_->total_samples_estimate = value; return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks) +FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, uint32_t num_blocks) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1892,11 +1983,39 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encod return true; } +FLAC_API FLAC__bool FLAC__stream_encoder_set_limit_min_bitrate(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->limit_min_bitrate = value; + return true; +} + /* - * These three functions are not static, but not publically exposed in - * include/FLAC/ either. They are used by the test suite. + * These four functions are not static, but not publicly exposed in + * include/FLAC/ either. They are used by the test suite and in fuzzing */ -inline FLAC_API FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) +FLAC_API FLAC__bool FLAC__stream_encoder_disable_instruction_set(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->private_->disable_mmx = value & 1; + encoder->private_->disable_sse2 = value & 2; + encoder->private_->disable_ssse3 = value & 4; + encoder->private_->disable_sse41 = value & 8; + encoder->private_->disable_avx2 = value & 16; + encoder->private_->disable_fma = value & 32; + encoder->private_->disable_sse42 = value & 64; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1907,7 +2026,7 @@ inline FLAC_API FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC_ return true; } -inline FLAC_API FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) +FLAC_API FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1918,7 +2037,7 @@ inline FLAC_API FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__St return true; } -inline FLAC_API FLAC__bool FLAC__stream_encoder_disable_verbatim_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) +FLAC_API FLAC__bool FLAC__stream_encoder_disable_verbatim_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1959,7 +2078,7 @@ FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__ return FLAC__stream_decoder_get_resolved_state_string(encoder->private_->verify.decoder); } -FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, unsigned *frame_number, unsigned *channel, unsigned *sample, FLAC__int32 *expected, FLAC__int32 *got) +FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, uint32_t *frame_number, uint32_t *channel, uint32_t *sample, FLAC__int32 *expected, FLAC__int32 *got) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -1994,7 +2113,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_get_streamable_subset(const FLAC__Strea return encoder->protected_->streamable_subset; } -inline FLAC_API FLAC__bool FLAC__stream_encoder_get_do_md5(const FLAC__StreamEncoder *encoder) +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_md5(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -2002,7 +2121,7 @@ inline FLAC_API FLAC__bool FLAC__stream_encoder_get_do_md5(const FLAC__StreamEnc return encoder->protected_->do_md5; } -FLAC_API unsigned FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *encoder) +FLAC_API uint32_t FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -2010,7 +2129,7 @@ FLAC_API unsigned FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *e return encoder->protected_->channels; } -FLAC_API unsigned FLAC__stream_encoder_get_bits_per_sample(const FLAC__StreamEncoder *encoder) +FLAC_API uint32_t FLAC__stream_encoder_get_bits_per_sample(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -2018,7 +2137,7 @@ FLAC_API unsigned FLAC__stream_encoder_get_bits_per_sample(const FLAC__StreamEnc return encoder->protected_->bits_per_sample; } -FLAC_API unsigned FLAC__stream_encoder_get_sample_rate(const FLAC__StreamEncoder *encoder) +FLAC_API uint32_t FLAC__stream_encoder_get_sample_rate(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -2026,7 +2145,7 @@ FLAC_API unsigned FLAC__stream_encoder_get_sample_rate(const FLAC__StreamEncoder return encoder->protected_->sample_rate; } -FLAC_API unsigned FLAC__stream_encoder_get_blocksize(const FLAC__StreamEncoder *encoder) +FLAC_API uint32_t FLAC__stream_encoder_get_blocksize(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -2050,7 +2169,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_get_loose_mid_side_stereo(const FLAC__S return encoder->protected_->loose_mid_side_stereo; } -FLAC_API unsigned FLAC__stream_encoder_get_max_lpc_order(const FLAC__StreamEncoder *encoder) +FLAC_API uint32_t FLAC__stream_encoder_get_max_lpc_order(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -2058,7 +2177,7 @@ FLAC_API unsigned FLAC__stream_encoder_get_max_lpc_order(const FLAC__StreamEncod return encoder->protected_->max_lpc_order; } -FLAC_API unsigned FLAC__stream_encoder_get_qlp_coeff_precision(const FLAC__StreamEncoder *encoder) +FLAC_API uint32_t FLAC__stream_encoder_get_qlp_coeff_precision(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -2090,7 +2209,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_get_do_exhaustive_model_search(const FL return encoder->protected_->do_exhaustive_model_search; } -FLAC_API unsigned FLAC__stream_encoder_get_min_residual_partition_order(const FLAC__StreamEncoder *encoder) +FLAC_API uint32_t FLAC__stream_encoder_get_min_residual_partition_order(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -2098,7 +2217,7 @@ FLAC_API unsigned FLAC__stream_encoder_get_min_residual_partition_order(const FL return encoder->protected_->min_residual_partition_order; } -FLAC_API unsigned FLAC__stream_encoder_get_max_residual_partition_order(const FLAC__StreamEncoder *encoder) +FLAC_API uint32_t FLAC__stream_encoder_get_max_residual_partition_order(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -2106,7 +2225,7 @@ FLAC_API unsigned FLAC__stream_encoder_get_max_residual_partition_order(const FL return encoder->protected_->max_residual_partition_order; } -FLAC_API unsigned FLAC__stream_encoder_get_rice_parameter_search_dist(const FLAC__StreamEncoder *encoder) +FLAC_API uint32_t FLAC__stream_encoder_get_rice_parameter_search_dist(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); @@ -2122,51 +2241,58 @@ FLAC_API FLAC__uint64 FLAC__stream_encoder_get_total_samples_estimate(const FLAC return encoder->protected_->total_samples_estimate; } -FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], unsigned samples) +FLAC_API FLAC__bool FLAC__stream_encoder_get_limit_min_bitrate(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->limit_min_bitrate; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], uint32_t samples) { - unsigned i, j = 0, channel; - const unsigned channels = encoder->protected_->channels, blocksize = encoder->protected_->blocksize; + uint32_t i, j = 0, k = 0, channel; + const uint32_t channels = encoder->protected_->channels, blocksize = encoder->protected_->blocksize; + const FLAC__int32 sample_max = INT32_MAX >> (32 - encoder->protected_->bits_per_sample); + const FLAC__int32 sample_min = INT32_MIN >> (32 - encoder->protected_->bits_per_sample); FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); FLAC__ASSERT(0 != encoder->protected_); - FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); + + if(encoder->protected_->state != FLAC__STREAM_ENCODER_OK) + return false; do { - const unsigned n = flac_min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j); + const uint32_t n = flac_min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j); if(encoder->protected_->verify) append_to_verify_fifo_(&encoder->private_->verify.input_fifo, buffer, j, channels, n); - for(channel = 0; channel < channels; channel++) - memcpy(&encoder->private_->integer_signal[channel][encoder->private_->current_sample_number], &buffer[channel][j], sizeof(buffer[channel][0]) * n); - - if(encoder->protected_->do_mid_side_stereo) { - FLAC__ASSERT(channels == 2); - /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ - for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { - encoder->private_->integer_signal_mid_side[1][i] = buffer[0][j] - buffer[1][j]; - encoder->private_->integer_signal_mid_side[0][i] = (buffer[0][j] + buffer[1][j]) >> 1; /* NOTE: not the same as 'mid = (buffer[0][j] + buffer[1][j]) / 2' ! */ + for(channel = 0; channel < channels; channel++) { + if (buffer[channel] == NULL) { + return false; + } + for(i = encoder->private_->current_sample_number, k = j; i <= blocksize && k < samples; i++, k++) { + if(buffer[channel][k] < sample_min || buffer[channel][k] > sample_max){ + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return false; + } } + memcpy(&encoder->private_->integer_signal[channel][encoder->private_->current_sample_number], &buffer[channel][j], sizeof(buffer[channel][0]) * n); } - else - j += n; - + j += n; encoder->private_->current_sample_number += n; /* we only process if we have a full block + 1 extra sample; final block is always handled by FLAC__stream_encoder_finish() */ if(encoder->private_->current_sample_number > blocksize) { FLAC__ASSERT(encoder->private_->current_sample_number == blocksize+OVERREAD_); FLAC__ASSERT(OVERREAD_ == 1); /* assert we only overread 1 sample which simplifies the rest of the code below */ - if(!process_frame_(encoder, /*is_fractional_block=*/false, /*is_last_block=*/false)) + if(!process_frame_(encoder, /*is_last_block=*/false)) return false; /* move unprocessed overread samples to beginnings of arrays */ for(channel = 0; channel < channels; channel++) encoder->private_->integer_signal[channel][0] = encoder->private_->integer_signal[channel][blocksize]; - if(encoder->protected_->do_mid_side_stereo) { - encoder->private_->integer_signal_mid_side[0][0] = encoder->private_->integer_signal_mid_side[0][blocksize]; - encoder->private_->integer_signal_mid_side[1][0] = encoder->private_->integer_signal_mid_side[1][blocksize]; - } encoder->private_->current_sample_number = 1; } } while(j < samples); @@ -2174,84 +2300,48 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, c return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], unsigned samples) +FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], uint32_t samples) { - unsigned i, j, k, channel; - FLAC__int32 x, mid, side; - const unsigned channels = encoder->protected_->channels, blocksize = encoder->protected_->blocksize; + uint32_t i, j, k, channel; + const uint32_t channels = encoder->protected_->channels, blocksize = encoder->protected_->blocksize; + const FLAC__int32 sample_max = INT32_MAX >> (32 - encoder->protected_->bits_per_sample); + const FLAC__int32 sample_min = INT32_MIN >> (32 - encoder->protected_->bits_per_sample); FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); FLAC__ASSERT(0 != encoder->protected_); - FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); - j = k = 0; - /* - * we have several flavors of the same basic loop, optimized for - * different conditions: - */ - if(encoder->protected_->do_mid_side_stereo && channels == 2) { - /* - * stereo coding: unroll channel loop - */ - do { - if(encoder->protected_->verify) - append_to_verify_fifo_interleaved_(&encoder->private_->verify.input_fifo, buffer, j, channels, flac_min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j)); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_OK) + return false; - /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ - for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { - encoder->private_->integer_signal[0][i] = mid = side = buffer[k++]; - x = buffer[k++]; - encoder->private_->integer_signal[1][i] = x; - mid += x; - side -= x; - mid >>= 1; /* NOTE: not the same as 'mid = (left + right) / 2' ! */ - encoder->private_->integer_signal_mid_side[1][i] = side; - encoder->private_->integer_signal_mid_side[0][i] = mid; - } - encoder->private_->current_sample_number = i; - /* we only process if we have a full block + 1 extra sample; final block is always handled by FLAC__stream_encoder_finish() */ - if(i > blocksize) { - if(!process_frame_(encoder, /*is_fractional_block=*/false, /*is_last_block=*/false)) - return false; - /* move unprocessed overread samples to beginnings of arrays */ - FLAC__ASSERT(i == blocksize+OVERREAD_); - FLAC__ASSERT(OVERREAD_ == 1); /* assert we only overread 1 sample which simplifies the rest of the code below */ - encoder->private_->integer_signal[0][0] = encoder->private_->integer_signal[0][blocksize]; - encoder->private_->integer_signal[1][0] = encoder->private_->integer_signal[1][blocksize]; - encoder->private_->integer_signal_mid_side[0][0] = encoder->private_->integer_signal_mid_side[0][blocksize]; - encoder->private_->integer_signal_mid_side[1][0] = encoder->private_->integer_signal_mid_side[1][blocksize]; - encoder->private_->current_sample_number = 1; - } - } while(j < samples); - } - else { - /* - * independent channel coding: buffer each channel in inner loop - */ - do { - if(encoder->protected_->verify) - append_to_verify_fifo_interleaved_(&encoder->private_->verify.input_fifo, buffer, j, channels, flac_min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j)); + j = k = 0; + do { + if(encoder->protected_->verify) + append_to_verify_fifo_interleaved_(&encoder->private_->verify.input_fifo, buffer, j, channels, flac_min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j)); /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ - for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { - for(channel = 0; channel < channels; channel++) - encoder->private_->integer_signal[channel][i] = buffer[k++]; - } - encoder->private_->current_sample_number = i; - /* we only process if we have a full block + 1 extra sample; final block is always handled by FLAC__stream_encoder_finish() */ - if(i > blocksize) { - if(!process_frame_(encoder, /*is_fractional_block=*/false, /*is_last_block=*/false)) + for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { + for(channel = 0; channel < channels; channel++){ + if(buffer[k] < sample_min || buffer[k] > sample_max){ + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; return false; - /* move unprocessed overread samples to beginnings of arrays */ - FLAC__ASSERT(i == blocksize+OVERREAD_); - FLAC__ASSERT(OVERREAD_ == 1); /* assert we only overread 1 sample which simplifies the rest of the code below */ - for(channel = 0; channel < channels; channel++) - encoder->private_->integer_signal[channel][0] = encoder->private_->integer_signal[channel][blocksize]; - encoder->private_->current_sample_number = 1; + } + encoder->private_->integer_signal[channel][i] = buffer[k++]; } - } while(j < samples); - } + } + encoder->private_->current_sample_number = i; + /* we only process if we have a full block + 1 extra sample; final block is always handled by FLAC__stream_encoder_finish() */ + if(i > blocksize) { + if(!process_frame_(encoder, /*is_last_block=*/false)) + return false; + /* move unprocessed overread samples to beginnings of arrays */ + FLAC__ASSERT(i == blocksize+OVERREAD_); + FLAC__ASSERT(OVERREAD_ == 1); /* assert we only overread 1 sample which simplifies the rest of the code below */ + for(channel = 0; channel < channels; channel++) + encoder->private_->integer_signal[channel][0] = encoder->private_->integer_signal[channel][blocksize]; + encoder->private_->current_sample_number = 1; + } + } while(j < samples); return true; } @@ -2293,16 +2383,21 @@ void set_defaults_(FLAC__StreamEncoder *encoder) encoder->protected_->max_residual_partition_order = 0; encoder->protected_->rice_parameter_search_dist = 0; encoder->protected_->total_samples_estimate = 0; + encoder->protected_->limit_min_bitrate = false; encoder->protected_->metadata = 0; encoder->protected_->num_metadata_blocks = 0; encoder->private_->seek_table = 0; + encoder->private_->disable_mmx = false; + encoder->private_->disable_sse2 = false; + encoder->private_->disable_ssse3 = false; + encoder->private_->disable_sse41 = false; + encoder->private_->disable_sse42 = false; + encoder->private_->disable_avx2 = false; encoder->private_->disable_constant_subframes = false; encoder->private_->disable_fixed_subframes = false; encoder->private_->disable_verbatim_subframes = false; -#if FLAC__HAS_OGG encoder->private_->is_ogg = false; -#endif encoder->private_->read_callback = 0; encoder->private_->write_callback = 0; encoder->private_->seek_callback = 0; @@ -2320,7 +2415,7 @@ void set_defaults_(FLAC__StreamEncoder *encoder) void free_(FLAC__StreamEncoder *encoder) { - unsigned i, channel; + uint32_t i, channel; FLAC__ASSERT(0 != encoder); if(encoder->protected_->metadata) { @@ -2352,6 +2447,10 @@ void free_(FLAC__StreamEncoder *encoder) } #endif } + if(0 != encoder->private_->integer_signal_33bit_side_unaligned){ + free(encoder->private_->integer_signal_33bit_side_unaligned); + encoder->private_->integer_signal_33bit_side_unaligned = 0; + } #ifndef FLAC__INTEGER_ONLY_LIBRARY for(i = 0; i < encoder->protected_->num_apodizations; i++) { if(0 != encoder->private_->window_unaligned[i]) { @@ -2399,76 +2498,96 @@ void free_(FLAC__StreamEncoder *encoder) FLAC__bitwriter_free(encoder->private_->frame); } -FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize) +FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, uint32_t new_blocksize) { FLAC__bool ok; - unsigned i, channel; + uint32_t i, channel; FLAC__ASSERT(new_blocksize > 0); FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); - FLAC__ASSERT(encoder->private_->current_sample_number == 0); - - /* To avoid excessive malloc'ing, we only grow the buffer; no shrinking. */ - if(new_blocksize <= encoder->private_->input_capacity) - return true; ok = true; - /* WATCHOUT: FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx() and ..._intrin_sse2() - * require that the input arrays (in our case the integer signals) - * have a buffer of up to 3 zeroes in front (at negative indices) for - * alignment purposes; we use 4 in front to keep the data well-aligned. - */ + /* To avoid excessive malloc'ing, we only grow the buffer; no shrinking. */ + if(new_blocksize > encoder->private_->input_capacity) { - for(i = 0; ok && i < encoder->protected_->channels; i++) { - ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize+4+OVERREAD_, &encoder->private_->integer_signal_unaligned[i], &encoder->private_->integer_signal[i]); - memset(encoder->private_->integer_signal[i], 0, sizeof(FLAC__int32)*4); - encoder->private_->integer_signal[i] += 4; -#ifndef FLAC__INTEGER_ONLY_LIBRARY -#if 0 /* @@@ currently unused */ - if(encoder->protected_->max_lpc_order > 0) - ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize+OVERREAD_, &encoder->private_->real_signal_unaligned[i], &encoder->private_->real_signal[i]); -#endif -#endif - } - for(i = 0; ok && i < 2; i++) { - ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize+4+OVERREAD_, &encoder->private_->integer_signal_mid_side_unaligned[i], &encoder->private_->integer_signal_mid_side[i]); - memset(encoder->private_->integer_signal_mid_side[i], 0, sizeof(FLAC__int32)*4); - encoder->private_->integer_signal_mid_side[i] += 4; -#ifndef FLAC__INTEGER_ONLY_LIBRARY -#if 0 /* @@@ currently unused */ - if(encoder->protected_->max_lpc_order > 0) - ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize+OVERREAD_, &encoder->private_->real_signal_mid_side_unaligned[i], &encoder->private_->real_signal_mid_side[i]); -#endif -#endif - } + /* WATCHOUT: FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx() and ..._intrin_sse2() + * require that the input arrays (in our case the integer signals) + * have a buffer of up to 3 zeroes in front (at negative indices) for + * alignment purposes; we use 4 in front to keep the data well-aligned. + */ + + for(i = 0; ok && i < encoder->protected_->channels; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize+4+OVERREAD_, &encoder->private_->integer_signal_unaligned[i], &encoder->private_->integer_signal[i]); + if(ok) { + memset(encoder->private_->integer_signal[i], 0, sizeof(FLAC__int32)*4); + encoder->private_->integer_signal[i] += 4; + } + } + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize+4+OVERREAD_, &encoder->private_->integer_signal_mid_side_unaligned[i], &encoder->private_->integer_signal_mid_side[i]); + if(ok) { + memset(encoder->private_->integer_signal_mid_side[i], 0, sizeof(FLAC__int32)*4); + encoder->private_->integer_signal_mid_side[i] += 4; + } + } + ok = ok && FLAC__memory_alloc_aligned_int64_array(new_blocksize+4+OVERREAD_, &encoder->private_->integer_signal_33bit_side_unaligned, &encoder->private_->integer_signal_33bit_side); #ifndef FLAC__INTEGER_ONLY_LIBRARY - if(ok && encoder->protected_->max_lpc_order > 0) { - for(i = 0; ok && i < encoder->protected_->num_apodizations; i++) - ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize, &encoder->private_->window_unaligned[i], &encoder->private_->window[i]); - ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize, &encoder->private_->windowed_signal_unaligned, &encoder->private_->windowed_signal); - } + if(ok && encoder->protected_->max_lpc_order > 0) { + for(i = 0; ok && i < encoder->protected_->num_apodizations; i++) + ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize, &encoder->private_->window_unaligned[i], &encoder->private_->window[i]); + ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize, &encoder->private_->windowed_signal_unaligned, &encoder->private_->windowed_signal); + } #endif - for(channel = 0; ok && channel < encoder->protected_->channels; channel++) { - for(i = 0; ok && i < 2; i++) { - ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize, &encoder->private_->residual_workspace_unaligned[channel][i], &encoder->private_->residual_workspace[channel][i]); + for(channel = 0; ok && channel < encoder->protected_->channels; channel++) { + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize, &encoder->private_->residual_workspace_unaligned[channel][i], &encoder->private_->residual_workspace[channel][i]); + } } - } - for(channel = 0; ok && channel < 2; channel++) { + + + for(channel = 0; ok && channel < encoder->protected_->channels; channel++) { + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(&encoder->private_->partitioned_rice_contents_workspace[channel][i], encoder->protected_->max_residual_partition_order); + ok = ok && FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(&encoder->private_->partitioned_rice_contents_workspace[channel][i], encoder->protected_->max_residual_partition_order); + } + } + + for(channel = 0; ok && channel < 2; channel++) { + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize, &encoder->private_->residual_workspace_mid_side_unaligned[channel][i], &encoder->private_->residual_workspace_mid_side[channel][i]); + } + } + + for(channel = 0; ok && channel < 2; channel++) { + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(&encoder->private_->partitioned_rice_contents_workspace_mid_side[channel][i], encoder->protected_->max_residual_partition_order); + } + } + for(i = 0; ok && i < 2; i++) { - ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize, &encoder->private_->residual_workspace_mid_side_unaligned[channel][i], &encoder->private_->residual_workspace_mid_side[channel][i]); + ok = ok && FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(&encoder->private_->partitioned_rice_contents_extra[i], encoder->protected_->max_residual_partition_order); } + + + /* the *2 is an approximation to the series 1 + 1/2 + 1/4 + ... that sums tree occupies in a flat array */ + /*@@@ new_blocksize*2 is too pessimistic, but to fix, we need smarter logic because a smaller new_blocksize can actually increase the # of partitions; would require moving this out into a separate function, then checking its capacity against the need of the current blocksize&min/max_partition_order (and maybe predictor order) */ + ok = ok && FLAC__memory_alloc_aligned_uint64_array(new_blocksize * 2, &encoder->private_->abs_residual_partition_sums_unaligned, &encoder->private_->abs_residual_partition_sums); + if(encoder->protected_->do_escape_coding) + ok = ok && FLAC__memory_alloc_aligned_uint32_array(new_blocksize * 2, &encoder->private_->raw_bits_per_partition_unaligned, &encoder->private_->raw_bits_per_partition); +} + if(ok) + encoder->private_->input_capacity = new_blocksize; + else { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return ok; } - /* the *2 is an approximation to the series 1 + 1/2 + 1/4 + ... that sums tree occupies in a flat array */ - /*@@@ new_blocksize*2 is too pessimistic, but to fix, we need smarter logic because a smaller new_blocksize can actually increase the # of partitions; would require moving this out into a separate function, then checking its capacity against the need of the current blocksize&min/max_partition_order (and maybe predictor order) */ - ok = ok && FLAC__memory_alloc_aligned_uint64_array(new_blocksize * 2, &encoder->private_->abs_residual_partition_sums_unaligned, &encoder->private_->abs_residual_partition_sums); - if(encoder->protected_->do_escape_coding) - ok = ok && FLAC__memory_alloc_aligned_unsigned_array(new_blocksize * 2, &encoder->private_->raw_bits_per_partition_unaligned, &encoder->private_->raw_bits_per_partition); + /* now adjust the windows if the blocksize has changed */ #ifndef FLAC__INTEGER_ONLY_LIBRARY - if(ok && new_blocksize != encoder->private_->input_capacity && encoder->protected_->max_lpc_order > 0) { - for(i = 0; ok && i < encoder->protected_->num_apodizations; i++) { + if(encoder->protected_->max_lpc_order > 0 && new_blocksize > 1) { + for(i = 0; i < encoder->protected_->num_apodizations; i++) { switch(encoder->protected_->apodizations[i].type) { case FLAC__APODIZATION_BARTLETT: FLAC__window_bartlett(encoder->private_->window[i], new_blocksize); @@ -2518,6 +2637,9 @@ FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize) case FLAC__APODIZATION_PUNCHOUT_TUKEY: FLAC__window_punchout_tukey(encoder->private_->window[i], new_blocksize, encoder->protected_->apodizations[i].parameters.multiple_tukey.p, encoder->protected_->apodizations[i].parameters.multiple_tukey.start, encoder->protected_->apodizations[i].parameters.multiple_tukey.end); break; + case FLAC__APODIZATION_SUBDIVIDE_TUKEY: + FLAC__window_tukey(encoder->private_->window[i], new_blocksize, encoder->protected_->apodizations[i].parameters.tukey.p); + break; case FLAC__APODIZATION_WELCH: FLAC__window_welch(encoder->private_->window[i], new_blocksize); break; @@ -2529,17 +2651,17 @@ FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize) } } } + if (new_blocksize <= FLAC__MAX_LPC_ORDER) { + /* intrinsics autocorrelation routines do not all handle cases in which lag might be + * larger than data_len. Lag is one larger than the LPC order */ + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation; + } #endif - if(ok) - encoder->private_->input_capacity = new_blocksize; - else - encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; - - return ok; + return true; } -FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples, FLAC__bool is_last_block) +FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, uint32_t samples, FLAC__bool is_last_block) { const FLAC__byte *buffer; size_t bytes; @@ -2558,7 +2680,10 @@ FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples, FLAC encoder->private_->verify.needs_magic_hack = true; } else { - if(!FLAC__stream_decoder_process_single(encoder->private_->verify.decoder)) { + if(!FLAC__stream_decoder_process_single(encoder->private_->verify.decoder) + || (!is_last_block + && (FLAC__stream_encoder_get_verify_decoder_state(encoder) == FLAC__STREAM_DECODER_END_OF_STREAM)) + || encoder->protected_->state == FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR /* Happens when error callback was used */) { FLAC__bitwriter_release_buffer(encoder->private_->frame); FLAC__bitwriter_clear(encoder->private_->frame); if(encoder->protected_->state != FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA) @@ -2579,14 +2704,14 @@ FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples, FLAC FLAC__bitwriter_clear(encoder->private_->frame); if(samples > 0) { - encoder->private_->streaminfo.data.stream_info.min_framesize = flac_min(bytes, (size_t) encoder->private_->streaminfo.data.stream_info.min_framesize); - encoder->private_->streaminfo.data.stream_info.max_framesize = flac_max(bytes, (size_t) encoder->private_->streaminfo.data.stream_info.max_framesize); + encoder->private_->streaminfo.data.stream_info.min_framesize = flac_min((uint32_t) bytes, encoder->private_->streaminfo.data.stream_info.min_framesize); + encoder->private_->streaminfo.data.stream_info.max_framesize = flac_max((uint32_t) bytes, encoder->private_->streaminfo.data.stream_info.max_framesize); } return true; } -FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, FLAC__bool is_last_block) +FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, FLAC__bool is_last_block) { FLAC__StreamEncoderWriteStatus status; FLAC__uint64 output_position = 0; @@ -2618,11 +2743,11 @@ FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const * frame yet) */ if(0 != encoder->private_->seek_table && encoder->protected_->audio_offset > 0 && encoder->private_->seek_table->num_points > 0) { - const unsigned blocksize = FLAC__stream_encoder_get_blocksize(encoder); + const uint32_t blocksize = FLAC__stream_encoder_get_blocksize(encoder); const FLAC__uint64 frame_first_sample = encoder->private_->samples_written; const FLAC__uint64 frame_last_sample = frame_first_sample + (FLAC__uint64)blocksize - 1; FLAC__uint64 test_sample; - unsigned i; + uint32_t i; for(i = encoder->private_->first_seekpoint_to_check; i < encoder->private_->seek_table->num_points; i++) { test_sample = encoder->private_->seek_table->points[i].sample_number; if(test_sample > frame_last_sample) { @@ -2682,12 +2807,12 @@ FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const /* Gets called when the encoding process has finished so that we can update the STREAMINFO and SEEKTABLE blocks. */ void update_metadata_(const FLAC__StreamEncoder *encoder) { - FLAC__byte b[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH]; + FLAC__byte b[flac_max(6u, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)]; const FLAC__StreamMetadata *metadata = &encoder->private_->streaminfo; - const FLAC__uint64 samples = metadata->data.stream_info.total_samples; - const unsigned min_framesize = metadata->data.stream_info.min_framesize; - const unsigned max_framesize = metadata->data.stream_info.max_framesize; - const unsigned bps = metadata->data.stream_info.bits_per_sample; + FLAC__uint64 samples = metadata->data.stream_info.total_samples; + const uint32_t min_framesize = metadata->data.stream_info.min_framesize; + const uint32_t max_framesize = metadata->data.stream_info.max_framesize; + const uint32_t bps = metadata->data.stream_info.bits_per_sample; FLAC__StreamEncoderSeekStatus seek_status; FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); @@ -2701,7 +2826,7 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) * Write MD5 signature */ { - const unsigned md5_offset = + const uint32_t md5_offset = FLAC__STREAM_METADATA_HEADER_LENGTH + ( FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + @@ -2729,7 +2854,7 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) * Write total samples */ { - const unsigned total_samples_byte_offset = + const uint32_t total_samples_byte_offset = FLAC__STREAM_METADATA_HEADER_LENGTH + ( FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + @@ -2741,6 +2866,8 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN - 4 ) / 8; + if(samples > (FLAC__U64L(1) << FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) + samples = 0; b[0] = ((FLAC__byte)(bps-1) << 4) | (FLAC__byte)((samples >> 32) & 0x0F); b[1] = (FLAC__byte)((samples >> 24) & 0xFF); @@ -2762,7 +2889,7 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) * Write min/max framesize */ { - const unsigned min_framesize_offset = + const uint32_t min_framesize_offset = FLAC__STREAM_METADATA_HEADER_LENGTH + ( FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + @@ -2790,7 +2917,7 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) * Write seektable */ if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) { - unsigned i; + uint32_t i; FLAC__format_seektable_sort(encoder->private_->seek_table); @@ -2804,7 +2931,7 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) for(i = 0; i < encoder->private_->seek_table->num_points; i++) { FLAC__uint64 xx; - unsigned x; + uint32_t x; xx = encoder->private_->seek_table->points[i].sample_number; b[7] = (FLAC__byte)xx; xx >>= 8; b[6] = (FLAC__byte)xx; xx >>= 8; @@ -2813,7 +2940,7 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) b[3] = (FLAC__byte)xx; xx >>= 8; b[2] = (FLAC__byte)xx; xx >>= 8; b[1] = (FLAC__byte)xx; xx >>= 8; - b[0] = (FLAC__byte)xx; //xx >>= 8; + b[0] = (FLAC__byte)xx; xx >>= 8; xx = encoder->private_->seek_table->points[i].stream_offset; b[15] = (FLAC__byte)xx; xx >>= 8; b[14] = (FLAC__byte)xx; xx >>= 8; @@ -2822,10 +2949,10 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) b[11] = (FLAC__byte)xx; xx >>= 8; b[10] = (FLAC__byte)xx; xx >>= 8; b[9] = (FLAC__byte)xx; xx >>= 8; - b[8] = (FLAC__byte)xx; //xx >>= 8; + b[8] = (FLAC__byte)xx; xx >>= 8; x = encoder->private_->seek_table->points[i].frame_samples; b[17] = (FLAC__byte)x; x >>= 8; - b[16] = (FLAC__byte)x; //x >>= 8; + b[16] = (FLAC__byte)x; x >>= 8; if(encoder->private_->write_callback(encoder, b, 18, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; return; @@ -2839,7 +2966,7 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) void update_ogg_metadata_(FLAC__StreamEncoder *encoder) { /* the # of bytes in the 1st packet that precede the STREAMINFO */ - static const unsigned FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH = + static const uint32_t FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH = FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH + FLAC__OGG_MAPPING_MAGIC_LENGTH + FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH + @@ -2850,8 +2977,8 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) FLAC__byte b[flac_max(6u, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)]; const FLAC__StreamMetadata *metadata = &encoder->private_->streaminfo; const FLAC__uint64 samples = metadata->data.stream_info.total_samples; - const unsigned min_framesize = metadata->data.stream_info.min_framesize; - const unsigned max_framesize = metadata->data.stream_info.max_framesize; + const uint32_t min_framesize = metadata->data.stream_info.min_framesize; + const uint32_t max_framesize = metadata->data.stream_info.max_framesize; ogg_page page; FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); @@ -2881,7 +3008,7 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) * Write MD5 signature */ { - const unsigned md5_offset = + const uint32_t md5_offset = FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH + FLAC__STREAM_METADATA_HEADER_LENGTH + ( @@ -2895,7 +3022,7 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN ) / 8; - if(md5_offset + 16 > (unsigned)page.body_len) { + if(md5_offset + 16 > (uint32_t)page.body_len) { encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; simple_ogg_page__clear(&page); return; @@ -2907,7 +3034,7 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) * Write total samples */ { - const unsigned total_samples_byte_offset = + const uint32_t total_samples_byte_offset = FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH + FLAC__STREAM_METADATA_HEADER_LENGTH + ( @@ -2921,7 +3048,7 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) - 4 ) / 8; - if(total_samples_byte_offset + 5 > (unsigned)page.body_len) { + if(total_samples_byte_offset + 5 > (uint32_t)page.body_len) { encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; simple_ogg_page__clear(&page); return; @@ -2939,7 +3066,7 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) * Write min/max framesize */ { - const unsigned min_framesize_offset = + const uint32_t min_framesize_offset = FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH + FLAC__STREAM_METADATA_HEADER_LENGTH + ( @@ -2947,7 +3074,7 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN ) / 8; - if(min_framesize_offset + 6 > (unsigned)page.body_len) { + if(min_framesize_offset + 6 > (uint32_t)page.body_len) { encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; simple_ogg_page__clear(&page); return; @@ -2965,67 +3092,10 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) return; /* state already set */ } simple_ogg_page__clear(&page); - - /* - * Write seektable - */ - if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) { - unsigned i; - FLAC__byte *p; - - FLAC__format_seektable_sort(encoder->private_->seek_table); - - FLAC__ASSERT(FLAC__format_seektable_is_legal(encoder->private_->seek_table)); - - simple_ogg_page__init(&page); - if(!simple_ogg_page__get_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data)) { - simple_ogg_page__clear(&page); - return; /* state already set */ - } - - if((FLAC__STREAM_METADATA_HEADER_LENGTH + 18*encoder->private_->seek_table->num_points) != (unsigned)page.body_len) { - encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; - simple_ogg_page__clear(&page); - return; - } - - for(i = 0, p = page.body + FLAC__STREAM_METADATA_HEADER_LENGTH; i < encoder->private_->seek_table->num_points; i++, p += 18) { - FLAC__uint64 xx; - unsigned x; - xx = encoder->private_->seek_table->points[i].sample_number; - b[7] = (FLAC__byte)xx; xx >>= 8; - b[6] = (FLAC__byte)xx; xx >>= 8; - b[5] = (FLAC__byte)xx; xx >>= 8; - b[4] = (FLAC__byte)xx; xx >>= 8; - b[3] = (FLAC__byte)xx; xx >>= 8; - b[2] = (FLAC__byte)xx; xx >>= 8; - b[1] = (FLAC__byte)xx; xx >>= 8; - b[0] = (FLAC__byte)xx; xx >>= 8; - xx = encoder->private_->seek_table->points[i].stream_offset; - b[15] = (FLAC__byte)xx; xx >>= 8; - b[14] = (FLAC__byte)xx; xx >>= 8; - b[13] = (FLAC__byte)xx; xx >>= 8; - b[12] = (FLAC__byte)xx; xx >>= 8; - b[11] = (FLAC__byte)xx; xx >>= 8; - b[10] = (FLAC__byte)xx; xx >>= 8; - b[9] = (FLAC__byte)xx; xx >>= 8; - b[8] = (FLAC__byte)xx; xx >>= 8; - x = encoder->private_->seek_table->points[i].frame_samples; - b[17] = (FLAC__byte)x; x >>= 8; - b[16] = (FLAC__byte)x; x >>= 8; - memcpy(p, b, 18); - } - - if(!simple_ogg_page__set_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->write_callback, encoder->private_->client_data)) { - simple_ogg_page__clear(&page); - return; /* state already set */ - } - simple_ogg_page__clear(&page); - } } #endif -FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block, FLAC__bool is_last_block) +FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_block) { FLAC__uint16 crc; FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); @@ -3041,7 +3111,7 @@ FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional /* * Process the frame header and subframes into the frame bitbuffer */ - if(!process_subframes_(encoder, is_fractional_block)) { + if(!process_subframes_(encoder)) { /* the above function sets the state for us in case of an error */ return false; } @@ -3084,22 +3154,18 @@ FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional return true; } -FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block) +FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder) { FLAC__FrameHeader frame_header; - unsigned channel, min_partition_order = encoder->protected_->min_residual_partition_order, max_partition_order; - FLAC__bool do_independent, do_mid_side; + uint32_t channel, min_partition_order = encoder->protected_->min_residual_partition_order, max_partition_order; + FLAC__bool do_independent, do_mid_side, backup_disable_constant_subframes = encoder->private_->disable_constant_subframes, all_subframes_constant = true; /* * Calculate the min,max Rice partition orders */ - if(is_fractional_block) { - max_partition_order = 0; - } - else { - max_partition_order = FLAC__format_get_max_rice_partition_order_from_blocksize(encoder->protected_->blocksize); - max_partition_order = flac_min(max_partition_order, encoder->protected_->max_residual_partition_order); - } + + max_partition_order = FLAC__format_get_max_rice_partition_order_from_blocksize(encoder->protected_->blocksize); + max_partition_order = flac_min(max_partition_order, encoder->protected_->max_residual_partition_order); min_partition_order = flac_min(min_partition_order, max_partition_order); /* @@ -3139,12 +3205,34 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti FLAC__ASSERT(do_independent || do_mid_side); + /* + * Prepare mid-side signals if applicable + */ + if(do_mid_side) { + uint32_t i; + FLAC__ASSERT(encoder->protected_->channels == 2); + if(encoder->protected_->bits_per_sample < 32) + for(i = 0; i < encoder->protected_->blocksize; i++) { + encoder->private_->integer_signal_mid_side[1][i] = encoder->private_->integer_signal[0][i] - encoder->private_->integer_signal[1][i]; + encoder->private_->integer_signal_mid_side[0][i] = (encoder->private_->integer_signal[0][i] + encoder->private_->integer_signal[1][i]) >> 1; /* NOTE: not the same as 'mid = (signal[0][j] + signal[1][j]) / 2' ! */ + } + else + for(i = 0; i <= encoder->protected_->blocksize; i++) { + encoder->private_->integer_signal_33bit_side[i] = (FLAC__int64)encoder->private_->integer_signal[0][i] - (FLAC__int64)encoder->private_->integer_signal[1][i]; + encoder->private_->integer_signal_mid_side[0][i] = ((FLAC__int64)encoder->private_->integer_signal[0][i] + (FLAC__int64)encoder->private_->integer_signal[1][i]) >> 1; /* NOTE: not the same as 'mid = (signal[0][j] + signal[1][j]) / 2' ! */ + } + } + + /* * Check for wasted bits; set effective bps for each subframe */ if(do_independent) { for(channel = 0; channel < encoder->protected_->channels; channel++) { - const unsigned w = get_wasted_bits_(encoder->private_->integer_signal[channel], encoder->protected_->blocksize); + uint32_t w = get_wasted_bits_(encoder->private_->integer_signal[channel], encoder->protected_->blocksize); + if (w > encoder->protected_->bits_per_sample) { + w = encoder->protected_->bits_per_sample; + } encoder->private_->subframe_workspace[channel][0].wasted_bits = encoder->private_->subframe_workspace[channel][1].wasted_bits = w; encoder->private_->subframe_bps[channel] = encoder->protected_->bits_per_sample - w; } @@ -3152,7 +3240,15 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti if(do_mid_side) { FLAC__ASSERT(encoder->protected_->channels == 2); for(channel = 0; channel < 2; channel++) { - const unsigned w = get_wasted_bits_(encoder->private_->integer_signal_mid_side[channel], encoder->protected_->blocksize); + uint32_t w; + if(encoder->protected_->bits_per_sample < 32 || channel == 0) + w = get_wasted_bits_(encoder->private_->integer_signal_mid_side[channel], encoder->protected_->blocksize); + else + w = get_wasted_bits_wide_(encoder->private_->integer_signal_33bit_side, encoder->private_->integer_signal_mid_side[channel], encoder->protected_->blocksize); + + if (w > encoder->protected_->bits_per_sample) { + w = encoder->protected_->bits_per_sample; + } encoder->private_->subframe_workspace_mid_side[channel][0].wasted_bits = encoder->private_->subframe_workspace_mid_side[channel][1].wasted_bits = w; encoder->private_->subframe_bps_mid_side[channel] = encoder->protected_->bits_per_sample - w + (channel==0? 0:1); } @@ -3163,6 +3259,12 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti */ if(do_independent) { for(channel = 0; channel < encoder->protected_->channels; channel++) { + if(encoder->protected_->limit_min_bitrate && all_subframes_constant && (channel + 1) == encoder->protected_->channels){ + /* This frame contains only constant subframes at this point. + * To prevent the frame from becoming too small, make sure + * the last subframe isn't constant */ + encoder->private_->disable_constant_subframes = true; + } if(! process_subframe_( encoder, @@ -3179,6 +3281,8 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti ) ) return false; + if(encoder->private_->subframe_workspace[channel][encoder->private_->best_subframe[channel]].type != FLAC__SUBFRAME_TYPE_CONSTANT) + all_subframes_constant = false; } } @@ -3189,6 +3293,11 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti FLAC__ASSERT(encoder->protected_->channels == 2); for(channel = 0; channel < 2; channel++) { + void *integer_signal_; + if(encoder->private_->subframe_bps_mid_side[channel] <= 32) + integer_signal_ = encoder->private_->integer_signal_mid_side[channel]; + else + integer_signal_ = encoder->private_->integer_signal_33bit_side; if(! process_subframe_( encoder, @@ -3196,7 +3305,7 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti max_partition_order, &frame_header, encoder->private_->subframe_bps_mid_side[channel], - encoder->private_->integer_signal_mid_side[channel], + integer_signal_, encoder->private_->subframe_workspace_ptr_mid_side[channel], encoder->private_->partitioned_rice_contents_workspace_ptr_mid_side[channel], encoder->private_->residual_workspace_mid_side[channel], @@ -3212,7 +3321,7 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti * Compose the frame bitbuffer */ if(do_mid_side) { - unsigned left_bps = 0, right_bps = 0; /* initialized only to prevent superfluous compiler warning */ + uint32_t left_bps = 0, right_bps = 0; /* initialized only to prevent superfluous compiler warning */ FLAC__Subframe *left_subframe = 0, *right_subframe = 0; /* initialized only to prevent superfluous compiler warning */ FLAC__ChannelAssignment channel_assignment; @@ -3222,8 +3331,8 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti channel_assignment = (encoder->private_->last_channel_assignment == FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT? FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT : FLAC__CHANNEL_ASSIGNMENT_MID_SIDE); } else { - unsigned bits[4]; /* WATCHOUT - indexed by FLAC__ChannelAssignment */ - unsigned min_bits; + uint32_t bits[4]; /* WATCHOUT - indexed by FLAC__ChannelAssignment */ + uint32_t min_bits; int ca; FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT == 0); @@ -3240,7 +3349,11 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; min_bits = bits[channel_assignment]; - for(ca = 1; ca <= 3; ca++) { + + /* When doing loose mid-side stereo, ignore left-side + * and right-side options */ + ca = encoder->protected_->loose_mid_side_stereo ? 3 : 1; + for( ; ca <= 3; ca++) { if(bits[ca] < min_bits) { min_bits = bits[ca]; channel_assignment = (FLAC__ChannelAssignment)ca; @@ -3324,55 +3437,75 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti } encoder->private_->last_channel_assignment = frame_header.channel_assignment; + encoder->private_->disable_constant_subframes = backup_disable_constant_subframes; return true; } FLAC__bool process_subframe_( FLAC__StreamEncoder *encoder, - unsigned min_partition_order, - unsigned max_partition_order, + uint32_t min_partition_order, + uint32_t max_partition_order, const FLAC__FrameHeader *frame_header, - unsigned subframe_bps, - const FLAC__int32 integer_signal[], + uint32_t subframe_bps, + const void *integer_signal, FLAC__Subframe *subframe[2], FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents[2], FLAC__int32 *residual[2], - unsigned *best_subframe, - unsigned *best_bits + uint32_t *best_subframe, + uint32_t *best_bits ) { #ifndef FLAC__INTEGER_ONLY_LIBRARY - FLAC__float fixed_residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]; + float fixed_residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]; #else FLAC__fixedpoint fixed_residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]; #endif #ifndef FLAC__INTEGER_ONLY_LIBRARY - FLAC__double lpc_residual_bits_per_sample; - FLAC__real autoc[FLAC__MAX_LPC_ORDER+1]; /* WATCHOUT: the size is important even though encoder->protected_->max_lpc_order might be less; some asm and x86 intrinsic routines need all the space */ - FLAC__double lpc_error[FLAC__MAX_LPC_ORDER]; - unsigned min_lpc_order, max_lpc_order, lpc_order; - unsigned min_qlp_coeff_precision, max_qlp_coeff_precision, qlp_coeff_precision; + double lpc_residual_bits_per_sample; + apply_apodization_state_struct apply_apodization_state; + double lpc_error[FLAC__MAX_LPC_ORDER]; + uint32_t min_lpc_order, max_lpc_order, lpc_order, guess_lpc_order; + uint32_t min_qlp_coeff_precision, max_qlp_coeff_precision, qlp_coeff_precision; #endif - unsigned min_fixed_order, max_fixed_order, guess_fixed_order, fixed_order; - unsigned rice_parameter; - unsigned _candidate_bits, _best_bits; - unsigned _best_subframe; + uint32_t min_fixed_order, max_fixed_order, guess_fixed_order, fixed_order; + uint32_t _candidate_bits, _best_bits; + uint32_t _best_subframe; /* only use RICE2 partitions if stream bps > 16 */ - const unsigned rice_parameter_limit = FLAC__stream_encoder_get_bits_per_sample(encoder) > 16? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + const uint32_t rice_parameter_limit = FLAC__stream_encoder_get_bits_per_sample(encoder) > 16? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; FLAC__ASSERT(frame_header->blocksize > 0); /* verbatim subframe is the baseline against which we measure other compressed subframes */ _best_subframe = 0; if(encoder->private_->disable_verbatim_subframes && frame_header->blocksize >= FLAC__MAX_FIXED_ORDER) - _best_bits = UINT_MAX; + _best_bits = UINT32_MAX; else _best_bits = evaluate_verbatim_subframe_(encoder, integer_signal, frame_header->blocksize, subframe_bps, subframe[_best_subframe]); + *best_bits = _best_bits; + + if(frame_header->blocksize > FLAC__MAX_FIXED_ORDER) { + uint32_t signal_is_constant = false; + /* The next formula determines when to use a 64-bit accumulator + * for the error of a fixed predictor, and when a 32-bit one. As + * the error of a 4th order predictor for a given sample is the + * sum of 17 sample values (1+4+6+4+1) and there are blocksize - + * order error values to be summed, the maximum total error is + * maximum_sample_value * (blocksize - order) * 17. As ilog2(x) + * calculates floor(2log(x)), the result must be 31 or lower + */ + if(subframe_bps < 28){ + if(subframe_bps + FLAC__bitmath_ilog2((frame_header->blocksize-FLAC__MAX_FIXED_ORDER)*17) < 32) + guess_fixed_order = encoder->private_->local_fixed_compute_best_predictor(((FLAC__int32 *)integer_signal)+FLAC__MAX_FIXED_ORDER, frame_header->blocksize-FLAC__MAX_FIXED_ORDER, fixed_residual_bits_per_sample); + else + guess_fixed_order = encoder->private_->local_fixed_compute_best_predictor_wide(((FLAC__int32 *)integer_signal)+FLAC__MAX_FIXED_ORDER, frame_header->blocksize-FLAC__MAX_FIXED_ORDER, fixed_residual_bits_per_sample); + } + else + if(subframe_bps <= 32) + guess_fixed_order = encoder->private_->local_fixed_compute_best_predictor_limit_residual(((FLAC__int32 *)integer_signal+FLAC__MAX_FIXED_ORDER),frame_header->blocksize-FLAC__MAX_FIXED_ORDER, fixed_residual_bits_per_sample); + else + guess_fixed_order = FLAC__fixed_compute_best_predictor_limit_residual_33bit(((FLAC__int64 *)integer_signal+FLAC__MAX_FIXED_ORDER),frame_header->blocksize-FLAC__MAX_FIXED_ORDER, fixed_residual_bits_per_sample); - if(frame_header->blocksize >= FLAC__MAX_FIXED_ORDER) { - unsigned signal_is_constant = false; - guess_fixed_order = encoder->private_->local_fixed_compute_best_predictor(integer_signal+FLAC__MAX_FIXED_ORDER, frame_header->blocksize-FLAC__MAX_FIXED_ORDER, fixed_residual_bits_per_sample); /* check for constant subframe */ if( !encoder->private_->disable_constant_subframes && @@ -3383,17 +3516,33 @@ FLAC__bool process_subframe_( #endif ) { /* the above means it's possible all samples are the same value; now double-check it: */ - unsigned i; + uint32_t i; signal_is_constant = true; - for(i = 1; i < frame_header->blocksize; i++) { - if(integer_signal[0] != integer_signal[i]) { - signal_is_constant = false; - break; + if(subframe_bps <= 32){ + const FLAC__int32 *integer_signal_ = (FLAC__int32*) integer_signal; + for(i = 1; i < frame_header->blocksize; i++) { + if(integer_signal_[0] != integer_signal_[i]) { + signal_is_constant = false; + break; + } + } + } + else { + const FLAC__int64 *integer_signal_ = (FLAC__int64*) integer_signal; + for(i = 1; i < frame_header->blocksize; i++) { + if(integer_signal_[0] != integer_signal_[i]) { + signal_is_constant = false; + break; + } } } } if(signal_is_constant) { - _candidate_bits = evaluate_constant_subframe_(encoder, integer_signal[0], frame_header->blocksize, subframe_bps, subframe[!_best_subframe]); + if(subframe_bps <= 32) + _candidate_bits = evaluate_constant_subframe_(encoder, ((FLAC__int32 *)integer_signal)[0], frame_header->blocksize, subframe_bps, subframe[!_best_subframe]); + else + _candidate_bits = evaluate_constant_subframe_(encoder, ((FLAC__int64 *)integer_signal)[0], frame_header->blocksize, subframe_bps, subframe[!_best_subframe]); + if(_candidate_bits < _best_bits) { _best_subframe = !_best_subframe; _best_bits = _candidate_bits; @@ -3413,21 +3562,12 @@ FLAC__bool process_subframe_( max_fixed_order = frame_header->blocksize - 1; for(fixed_order = min_fixed_order; fixed_order <= max_fixed_order; fixed_order++) { #ifndef FLAC__INTEGER_ONLY_LIBRARY - if(fixed_residual_bits_per_sample[fixed_order] >= (FLAC__float)subframe_bps) + if(fixed_residual_bits_per_sample[fixed_order] >= (float)subframe_bps) continue; /* don't even try */ - rice_parameter = (fixed_residual_bits_per_sample[fixed_order] > 0.0)? (unsigned)(fixed_residual_bits_per_sample[fixed_order]+0.5) : 0; /* 0.5 is for rounding */ #else if(FLAC__fixedpoint_trunc(fixed_residual_bits_per_sample[fixed_order]) >= (int)subframe_bps) continue; /* don't even try */ - rice_parameter = (fixed_residual_bits_per_sample[fixed_order] > FLAC__FP_ZERO)? (unsigned)FLAC__fixedpoint_trunc(fixed_residual_bits_per_sample[fixed_order]+FLAC__FP_ONE_HALF) : 0; /* 0.5 is for rounding */ #endif - rice_parameter++; /* to account for the signed->unsigned conversion during rice coding */ - if(rice_parameter >= rice_parameter_limit) { -#ifdef DEBUG_VERBOSE - fprintf(stderr, "clipping rice_parameter (%u -> %u) @0\n", rice_parameter, rice_parameter_limit - 1); -#endif - rice_parameter = rice_parameter_limit - 1; - } _candidate_bits = evaluate_fixed_subframe_( encoder, @@ -3438,7 +3578,6 @@ FLAC__bool process_subframe_( frame_header->blocksize, subframe_bps, fixed_order, - rice_parameter, rice_parameter_limit, min_partition_order, max_partition_order, @@ -3462,84 +3601,68 @@ FLAC__bool process_subframe_( else max_lpc_order = encoder->protected_->max_lpc_order; if(max_lpc_order > 0) { - unsigned a; - for (a = 0; a < encoder->protected_->num_apodizations; a++) { - FLAC__lpc_window_data(integer_signal, encoder->private_->window[a], encoder->private_->windowed_signal, frame_header->blocksize); - encoder->private_->local_lpc_compute_autocorrelation(encoder->private_->windowed_signal, frame_header->blocksize, max_lpc_order+1, autoc); - /* if autoc[0] == 0.0, the signal is constant and we usually won't get here, but it can happen */ - if(autoc[0] != 0.0) { - FLAC__lpc_compute_lp_coefficients(autoc, &max_lpc_order, encoder->private_->lp_coeff, lpc_error); - if(encoder->protected_->do_exhaustive_model_search) { - min_lpc_order = 1; + apply_apodization_state.a = 0; + apply_apodization_state.b = 1; + apply_apodization_state.c = 0; + while (apply_apodization_state.a < encoder->protected_->num_apodizations) { + uint32_t max_lpc_order_this_apodization = max_lpc_order; + + if(!apply_apodization_(encoder, &apply_apodization_state, + frame_header->blocksize, lpc_error, + &max_lpc_order_this_apodization, + subframe_bps, integer_signal, + &guess_lpc_order)) + /* If apply_apodization_ fails, try next apodization */ + continue; + + if(encoder->protected_->do_exhaustive_model_search) { + min_lpc_order = 1; + } + else { + min_lpc_order = max_lpc_order_this_apodization = guess_lpc_order; + } + for(lpc_order = min_lpc_order; lpc_order <= max_lpc_order_this_apodization; lpc_order++) { + lpc_residual_bits_per_sample = FLAC__lpc_compute_expected_bits_per_residual_sample(lpc_error[lpc_order-1], frame_header->blocksize-lpc_order); + if(lpc_residual_bits_per_sample >= (double)subframe_bps) + continue; /* don't even try */ + if(encoder->protected_->do_qlp_coeff_prec_search) { + min_qlp_coeff_precision = FLAC__MIN_QLP_COEFF_PRECISION; + /* try to keep qlp coeff precision such that only 32-bit math is required for decode of <=16bps(+1bps for side channel) streams */ + if(subframe_bps <= 17) { + max_qlp_coeff_precision = flac_min(32 - subframe_bps - FLAC__bitmath_ilog2(lpc_order), FLAC__MAX_QLP_COEFF_PRECISION); + max_qlp_coeff_precision = flac_max(max_qlp_coeff_precision, min_qlp_coeff_precision); + } + else + max_qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION; } else { - const unsigned guess_lpc_order = - FLAC__lpc_compute_best_order( - lpc_error, - max_lpc_order, + min_qlp_coeff_precision = max_qlp_coeff_precision = encoder->protected_->qlp_coeff_precision; + } + for(qlp_coeff_precision = min_qlp_coeff_precision; qlp_coeff_precision <= max_qlp_coeff_precision; qlp_coeff_precision++) { + _candidate_bits = + evaluate_lpc_subframe_( + encoder, + integer_signal, + residual[!_best_subframe], + encoder->private_->abs_residual_partition_sums, + encoder->private_->raw_bits_per_partition, + encoder->private_->lp_coeff[lpc_order-1], frame_header->blocksize, - subframe_bps + ( - encoder->protected_->do_qlp_coeff_prec_search? - FLAC__MIN_QLP_COEFF_PRECISION : /* have to guess; use the min possible size to avoid accidentally favoring lower orders */ - encoder->protected_->qlp_coeff_precision - ) + subframe_bps, + lpc_order, + qlp_coeff_precision, + rice_parameter_limit, + min_partition_order, + max_partition_order, + encoder->protected_->do_escape_coding, + encoder->protected_->rice_parameter_search_dist, + subframe[!_best_subframe], + partitioned_rice_contents[!_best_subframe] ); - min_lpc_order = max_lpc_order = guess_lpc_order; - } - if(max_lpc_order >= frame_header->blocksize) - max_lpc_order = frame_header->blocksize - 1; - for(lpc_order = min_lpc_order; lpc_order <= max_lpc_order; lpc_order++) { - lpc_residual_bits_per_sample = FLAC__lpc_compute_expected_bits_per_residual_sample(lpc_error[lpc_order-1], frame_header->blocksize-lpc_order); - if(lpc_residual_bits_per_sample >= (FLAC__double)subframe_bps) - continue; /* don't even try */ - rice_parameter = (lpc_residual_bits_per_sample > 0.0)? (unsigned)(lpc_residual_bits_per_sample+0.5) : 0; /* 0.5 is for rounding */ - rice_parameter++; /* to account for the signed->unsigned conversion during rice coding */ - if(rice_parameter >= rice_parameter_limit) { -#ifdef DEBUG_VERBOSE - fprintf(stderr, "clipping rice_parameter (%u -> %u) @1\n", rice_parameter, rice_parameter_limit - 1); -#endif - rice_parameter = rice_parameter_limit - 1; - } - if(encoder->protected_->do_qlp_coeff_prec_search) { - min_qlp_coeff_precision = FLAC__MIN_QLP_COEFF_PRECISION; - /* try to keep qlp coeff precision such that only 32-bit math is required for decode of <=16bps streams */ - if(subframe_bps <= 16) { - max_qlp_coeff_precision = flac_min(32 - subframe_bps - FLAC__bitmath_ilog2(lpc_order), FLAC__MAX_QLP_COEFF_PRECISION); - max_qlp_coeff_precision = flac_max(max_qlp_coeff_precision, min_qlp_coeff_precision); - } - else - max_qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION; - } - else { - min_qlp_coeff_precision = max_qlp_coeff_precision = encoder->protected_->qlp_coeff_precision; - } - for(qlp_coeff_precision = min_qlp_coeff_precision; qlp_coeff_precision <= max_qlp_coeff_precision; qlp_coeff_precision++) { - _candidate_bits = - evaluate_lpc_subframe_( - encoder, - integer_signal, - residual[!_best_subframe], - encoder->private_->abs_residual_partition_sums, - encoder->private_->raw_bits_per_partition, - encoder->private_->lp_coeff[lpc_order-1], - frame_header->blocksize, - subframe_bps, - lpc_order, - qlp_coeff_precision, - rice_parameter, - rice_parameter_limit, - min_partition_order, - max_partition_order, - encoder->protected_->do_escape_coding, - encoder->protected_->rice_parameter_search_dist, - subframe[!_best_subframe], - partitioned_rice_contents[!_best_subframe] - ); - if(_candidate_bits > 0) { /* if == 0, there was a problem quantizing the lpcoeffs */ - if(_candidate_bits < _best_bits) { - _best_subframe = !_best_subframe; - _best_bits = _candidate_bits; - } + if(_candidate_bits > 0) { /* if == 0, there was a problem quantizing the lpcoeffs */ + if(_candidate_bits < _best_bits) { + _best_subframe = !_best_subframe; + _best_bits = _candidate_bits; } } } @@ -3552,7 +3675,7 @@ FLAC__bool process_subframe_( } /* under rare circumstances this can happen when all but lpc subframe types are disabled: */ - if(_best_bits == UINT_MAX) { + if(_best_bits == UINT32_MAX) { FLAC__ASSERT(_best_subframe == 0); _best_bits = evaluate_verbatim_subframe_(encoder, integer_signal, frame_header->blocksize, subframe_bps, subframe[_best_subframe]); } @@ -3563,10 +3686,112 @@ FLAC__bool process_subframe_( return true; } +#ifndef FLAC__INTEGER_ONLY_LIBRARY +static inline void set_next_subdivide_tukey(FLAC__int32 parts, uint32_t * apodizations, uint32_t * current_depth, uint32_t * current_part){ + // current_part is interleaved: even are partial, odd are punchout + if(*current_depth == 2){ + // For depth 2, we only do partial, no punchout as that is almost redundant + if(*current_part == 0){ + *current_part = 2; + }else{ /* *current_path == 2 */ + *current_part = 0; + (*current_depth)++; + } + }else if((*current_part) < (2*(*current_depth)-1)){ + (*current_part)++; + }else{ /* (*current_part) >= (2*(*current_depth)-1) */ + *current_part = 0; + (*current_depth)++; + } + + /* Now check if we are done with this SUBDIVIDE_TUKEY apodization */ + if(*current_depth > (uint32_t) parts){ + (*apodizations)++; + *current_depth = 1; + *current_part = 0; + } +} + +FLAC__bool apply_apodization_(FLAC__StreamEncoder *encoder, + apply_apodization_state_struct *apply_apodization_state, + uint32_t blocksize, + double *lpc_error, + uint32_t *max_lpc_order_this_apodization, + uint32_t subframe_bps, + const void *integer_signal, + uint32_t *guess_lpc_order) +{ + apply_apodization_state->current_apodization = &encoder->protected_->apodizations[apply_apodization_state->a]; + + if(apply_apodization_state->b == 1) { + /* window full subblock */ + if(subframe_bps <= 32) + FLAC__lpc_window_data((const FLAC__int32*) integer_signal, encoder->private_->window[apply_apodization_state->a], encoder->private_->windowed_signal, blocksize); + else + FLAC__lpc_window_data_wide((const FLAC__int64*) integer_signal, encoder->private_->window[apply_apodization_state->a], encoder->private_->windowed_signal, blocksize); + encoder->private_->local_lpc_compute_autocorrelation(encoder->private_->windowed_signal, blocksize, (*max_lpc_order_this_apodization)+1, apply_apodization_state->autoc); + if(apply_apodization_state->current_apodization->type == FLAC__APODIZATION_SUBDIVIDE_TUKEY){ + uint32_t i; + for(i = 0; i < *max_lpc_order_this_apodization; i++) + memcpy(apply_apodization_state->autoc_root, apply_apodization_state->autoc, *max_lpc_order_this_apodization*sizeof(apply_apodization_state->autoc[0])); + + (apply_apodization_state->b)++; + }else{ + (apply_apodization_state->a)++; + } + } + else { + /* window part of subblock */ + if(blocksize/apply_apodization_state->b <= FLAC__MAX_LPC_ORDER) { + /* intrinsics autocorrelation routines do not all handle cases in which lag might be + * larger than data_len, and some routines round lag up to the nearest multiple of 4 + * As little gain is expected from using LPC on part of a signal as small as 32 samples + * and to enable widening this rounding up to larger values in the future, windowing + * parts smaller than or equal to FLAC__MAX_LPC_ORDER (which is 32) samples is not supported */ + set_next_subdivide_tukey(apply_apodization_state->current_apodization->parameters.subdivide_tukey.parts, &apply_apodization_state->a, &apply_apodization_state->b, &apply_apodization_state->c); + return false; + } + if(!(apply_apodization_state->c % 2)) { + /* on even c, evaluate the (c/2)th partial window of size blocksize/b */ + if(subframe_bps <= 32) + FLAC__lpc_window_data_partial((const FLAC__int32*) integer_signal, encoder->private_->window[apply_apodization_state->a], encoder->private_->windowed_signal, blocksize, blocksize/apply_apodization_state->b/2, (apply_apodization_state->c/2*blocksize)/apply_apodization_state->b); + else + FLAC__lpc_window_data_partial_wide((const FLAC__int64*) integer_signal, encoder->private_->window[apply_apodization_state->a], encoder->private_->windowed_signal, blocksize, blocksize/apply_apodization_state->b/2, (apply_apodization_state->c/2*blocksize)/apply_apodization_state->b); + encoder->private_->local_lpc_compute_autocorrelation(encoder->private_->windowed_signal, blocksize/apply_apodization_state->b, (*max_lpc_order_this_apodization)+1, apply_apodization_state->autoc); + } + else { + /* on uneven c, evaluate the root window (over the whole block) minus the previous partial window + * similar to tukey_punchout apodization but more efficient */ + uint32_t i; + for(i = 0; i < *max_lpc_order_this_apodization; i++) + apply_apodization_state->autoc[i] = apply_apodization_state->autoc_root[i] - apply_apodization_state->autoc[i]; + } + /* Next function sets a, b and c appropriate for next iteration */ + set_next_subdivide_tukey(apply_apodization_state->current_apodization->parameters.subdivide_tukey.parts, &apply_apodization_state->a, &apply_apodization_state->b, &apply_apodization_state->c); + } + + if(apply_apodization_state->autoc[0] == 0.0) /* Signal seems to be constant, so we can't do lp. Constant detection is probably disabled */ + return false; + FLAC__lpc_compute_lp_coefficients(apply_apodization_state->autoc, max_lpc_order_this_apodization, encoder->private_->lp_coeff, lpc_error); + *guess_lpc_order = + FLAC__lpc_compute_best_order( + lpc_error, + *max_lpc_order_this_apodization, + blocksize, + subframe_bps + ( + encoder->protected_->do_qlp_coeff_prec_search? + FLAC__MIN_QLP_COEFF_PRECISION : /* have to guess; use the min possible size to avoid accidentally favoring lower orders */ + encoder->protected_->qlp_coeff_precision + ) + ); + return true; +} +#endif + FLAC__bool add_subframe_( FLAC__StreamEncoder *encoder, - unsigned blocksize, - unsigned subframe_bps, + uint32_t blocksize, + uint32_t subframe_bps, const FLAC__Subframe *subframe, FLAC__BitWriter *frame ) @@ -3607,10 +3832,10 @@ FLAC__bool add_subframe_( #if SPOTCHECK_ESTIMATE static void spotcheck_subframe_estimate_( FLAC__StreamEncoder *encoder, - unsigned blocksize, - unsigned subframe_bps, + uint32_t blocksize, + uint32_t subframe_bps, const FLAC__Subframe *subframe, - unsigned estimate + uint32_t estimate ) { FLAC__bool ret; @@ -3626,7 +3851,7 @@ static void spotcheck_subframe_estimate_( ret = add_subframe_(encoder, blocksize, subframe_bps, subframe, frame); FLAC__ASSERT(ret); { - const unsigned actual = FLAC__bitwriter_get_input_bits_unconsumed(frame); + const uint32_t actual = FLAC__bitwriter_get_input_bits_unconsumed(frame); if(estimate != actual) fprintf(stderr, "EST: bad, frame#%u sub#%%d type=%8s est=%u, actual=%u, delta=%d\n", encoder->private_->current_frame_number, FLAC__SubframeTypeString[subframe->type], estimate, actual, (int)actual-(int)estimate); } @@ -3634,15 +3859,15 @@ static void spotcheck_subframe_estimate_( } #endif -unsigned evaluate_constant_subframe_( +uint32_t evaluate_constant_subframe_( FLAC__StreamEncoder *encoder, - const FLAC__int32 signal, - unsigned blocksize, - unsigned subframe_bps, + const FLAC__int64 signal, + uint32_t blocksize, + uint32_t subframe_bps, FLAC__Subframe *subframe ) { - unsigned estimate; + uint32_t estimate; subframe->type = FLAC__SUBFRAME_TYPE_CONSTANT; subframe->data.constant.value = signal; @@ -3657,29 +3882,33 @@ unsigned evaluate_constant_subframe_( return estimate; } -unsigned evaluate_fixed_subframe_( +uint32_t evaluate_fixed_subframe_( FLAC__StreamEncoder *encoder, - const FLAC__int32 signal[], + const void *signal, FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned raw_bits_per_partition[], - unsigned blocksize, - unsigned subframe_bps, - unsigned order, - unsigned rice_parameter, - unsigned rice_parameter_limit, - unsigned min_partition_order, - unsigned max_partition_order, + uint32_t raw_bits_per_partition[], + uint32_t blocksize, + uint32_t subframe_bps, + uint32_t order, + uint32_t rice_parameter_limit, + uint32_t min_partition_order, + uint32_t max_partition_order, FLAC__bool do_escape_coding, - unsigned rice_parameter_search_dist, + uint32_t rice_parameter_search_dist, FLAC__Subframe *subframe, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents ) { - unsigned i, residual_bits, estimate; - const unsigned residual_samples = blocksize - order; + uint32_t i, residual_bits, estimate; + const uint32_t residual_samples = blocksize - order; - FLAC__fixed_compute_residual(signal+order, residual_samples, order, residual); + if((subframe_bps + order) <= 32) + FLAC__fixed_compute_residual(((FLAC__int32 *)signal)+order, residual_samples, order, residual); + else if(subframe_bps <= 32) + FLAC__fixed_compute_residual_wide(((FLAC__int32 *)signal)+order, residual_samples, order, residual); + else + FLAC__fixed_compute_residual_wide_33bit(((FLAC__int64 *)signal)+order, residual_samples, order, residual); subframe->type = FLAC__SUBFRAME_TYPE_FIXED; @@ -3695,7 +3924,6 @@ unsigned evaluate_fixed_subframe_( raw_bits_per_partition, residual_samples, order, - rice_parameter, rice_parameter_limit, min_partition_order, max_partition_order, @@ -3706,10 +3934,18 @@ unsigned evaluate_fixed_subframe_( ); subframe->data.fixed.order = order; - for(i = 0; i < order; i++) - subframe->data.fixed.warmup[i] = signal[i]; + if(subframe_bps <= 32) + for(i = 0; i < order; i++) + subframe->data.fixed.warmup[i] = ((FLAC__int32 *)signal)[i]; + else + for(i = 0; i < order; i++) + subframe->data.fixed.warmup[i] = ((FLAC__int64 *)signal)[i]; - estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + (order * subframe_bps) + residual_bits; + estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + (order * subframe_bps); + if(residual_bits < UINT32_MAX - estimate) // To make sure estimate doesn't overflow + estimate += residual_bits; + else + estimate = UINT32_MAX; #if SPOTCHECK_ESTIMATE spotcheck_subframe_estimate_(encoder, blocksize, subframe_bps, subframe, estimate); @@ -3719,34 +3955,33 @@ unsigned evaluate_fixed_subframe_( } #ifndef FLAC__INTEGER_ONLY_LIBRARY -unsigned evaluate_lpc_subframe_( +uint32_t evaluate_lpc_subframe_( FLAC__StreamEncoder *encoder, - const FLAC__int32 signal[], + const void *signal, FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned raw_bits_per_partition[], + uint32_t raw_bits_per_partition[], const FLAC__real lp_coeff[], - unsigned blocksize, - unsigned subframe_bps, - unsigned order, - unsigned qlp_coeff_precision, - unsigned rice_parameter, - unsigned rice_parameter_limit, - unsigned min_partition_order, - unsigned max_partition_order, + uint32_t blocksize, + uint32_t subframe_bps, + uint32_t order, + uint32_t qlp_coeff_precision, + uint32_t rice_parameter_limit, + uint32_t min_partition_order, + uint32_t max_partition_order, FLAC__bool do_escape_coding, - unsigned rice_parameter_search_dist, + uint32_t rice_parameter_search_dist, FLAC__Subframe *subframe, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents ) { FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; /* WATCHOUT: the size is important; some x86 intrinsic routines need more than lpc order elements */ - unsigned i, residual_bits, estimate; + uint32_t i, residual_bits, estimate; int quantization, ret; - const unsigned residual_samples = blocksize - order; + const uint32_t residual_samples = blocksize - order; - /* try to keep qlp coeff precision such that only 32-bit math is required for decode of <=16bps streams */ - if(subframe_bps <= 16) { + /* try to keep qlp coeff precision such that only 32-bit math is required for decode of <=16bps(+1bps for side channel) streams */ + if(subframe_bps <= 17) { FLAC__ASSERT(order > 0); FLAC__ASSERT(order <= FLAC__MAX_LPC_ORDER); qlp_coeff_precision = flac_min(qlp_coeff_precision, 32 - subframe_bps - FLAC__bitmath_ilog2(order)); @@ -3756,13 +3991,23 @@ unsigned evaluate_lpc_subframe_( if(ret != 0) return 0; /* this is a hack to indicate to the caller that we can't do lp at this order on this subframe */ - if(subframe_bps + qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32) - if(subframe_bps <= 16 && qlp_coeff_precision <= 16) - encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit(signal+order, residual_samples, qlp_coeff, order, quantization, residual); + if(FLAC__lpc_max_residual_bps(subframe_bps, qlp_coeff, order, quantization) > 32) { + if(subframe_bps <= 32){ + if(!FLAC__lpc_compute_residual_from_qlp_coefficients_limit_residual(((FLAC__int32 *)signal)+order, residual_samples, qlp_coeff, order, quantization, residual)) + return 0; + } else - encoder->private_->local_lpc_compute_residual_from_qlp_coefficients(signal+order, residual_samples, qlp_coeff, order, quantization, residual); + if(!FLAC__lpc_compute_residual_from_qlp_coefficients_limit_residual_33bit(((FLAC__int64 *)signal)+order, residual_samples, qlp_coeff, order, quantization, residual)) + return 0; + } else - encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit(signal+order, residual_samples, qlp_coeff, order, quantization, residual); + if(FLAC__lpc_max_prediction_before_shift_bps(subframe_bps, qlp_coeff, order) <= 32) + if(subframe_bps <= 16 && qlp_coeff_precision <= 16) + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit(((FLAC__int32 *)signal)+order, residual_samples, qlp_coeff, order, quantization, residual); + else + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients(((FLAC__int32 *)signal)+order, residual_samples, qlp_coeff, order, quantization, residual); + else + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit(((FLAC__int32 *)signal)+order, residual_samples, qlp_coeff, order, quantization, residual); subframe->type = FLAC__SUBFRAME_TYPE_LPC; @@ -3778,7 +4023,6 @@ unsigned evaluate_lpc_subframe_( raw_bits_per_partition, residual_samples, order, - rice_parameter, rice_parameter_limit, min_partition_order, max_partition_order, @@ -3792,10 +4036,19 @@ unsigned evaluate_lpc_subframe_( subframe->data.lpc.qlp_coeff_precision = qlp_coeff_precision; subframe->data.lpc.quantization_level = quantization; memcpy(subframe->data.lpc.qlp_coeff, qlp_coeff, sizeof(FLAC__int32)*FLAC__MAX_LPC_ORDER); - for(i = 0; i < order; i++) - subframe->data.lpc.warmup[i] = signal[i]; + if(subframe_bps <= 32) + for(i = 0; i < order; i++) + subframe->data.lpc.warmup[i] = ((FLAC__int32 *)signal)[i]; + else + for(i = 0; i < order; i++) + subframe->data.lpc.warmup[i] = ((FLAC__int64 *)signal)[i]; - estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN + FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN + (order * (qlp_coeff_precision + subframe_bps)) + residual_bits; + + estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN + FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN + (order * (qlp_coeff_precision + subframe_bps)); + if(residual_bits < UINT32_MAX - estimate) // To make sure estimate doesn't overflow + estimate += residual_bits; + else + estimate = UINT32_MAX; #if SPOTCHECK_ESTIMATE spotcheck_subframe_estimate_(encoder, blocksize, subframe_bps, subframe, estimate); @@ -3805,19 +4058,26 @@ unsigned evaluate_lpc_subframe_( } #endif -unsigned evaluate_verbatim_subframe_( +uint32_t evaluate_verbatim_subframe_( FLAC__StreamEncoder *encoder, - const FLAC__int32 signal[], - unsigned blocksize, - unsigned subframe_bps, + const void *signal, + uint32_t blocksize, + uint32_t subframe_bps, FLAC__Subframe *subframe ) { - unsigned estimate; + uint32_t estimate; subframe->type = FLAC__SUBFRAME_TYPE_VERBATIM; - subframe->data.verbatim.data = signal; + if(subframe_bps <= 32){ + subframe->data.verbatim.data_type = FLAC__VERBATIM_SUBFRAME_DATA_TYPE_INT32; + subframe->data.verbatim.data.int32 = (const FLAC__int32*) signal; + } + else { + subframe->data.verbatim.data_type = FLAC__VERBATIM_SUBFRAME_DATA_TYPE_INT64; + subframe->data.verbatim.data.int64 = (const FLAC__int64*) signal; + } estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + (blocksize * subframe_bps); @@ -3830,27 +4090,26 @@ unsigned evaluate_verbatim_subframe_( return estimate; } -unsigned find_best_partition_order_( +uint32_t find_best_partition_order_( FLAC__StreamEncoderPrivate *private_, const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned raw_bits_per_partition[], - unsigned residual_samples, - unsigned predictor_order, - unsigned rice_parameter, - unsigned rice_parameter_limit, - unsigned min_partition_order, - unsigned max_partition_order, - unsigned bps, + uint32_t raw_bits_per_partition[], + uint32_t residual_samples, + uint32_t predictor_order, + uint32_t rice_parameter_limit, + uint32_t min_partition_order, + uint32_t max_partition_order, + uint32_t bps, FLAC__bool do_escape_coding, - unsigned rice_parameter_search_dist, + uint32_t rice_parameter_search_dist, FLAC__EntropyCodingMethod *best_ecm ) { - unsigned residual_bits, best_residual_bits = 0; - unsigned best_parameters_index = 0; - unsigned best_partition_order = 0; - const unsigned blocksize = residual_samples + predictor_order; + uint32_t residual_bits, best_residual_bits = 0; + uint32_t best_parameters_index = 0; + uint32_t best_partition_order = 0; + const uint32_t blocksize = residual_samples + predictor_order; max_partition_order = FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(max_partition_order, blocksize, predictor_order); min_partition_order = flac_min(min_partition_order, max_partition_order); @@ -3862,7 +4121,7 @@ unsigned find_best_partition_order_( { int partition_order; - unsigned sum; + uint32_t sum; for(partition_order = (int)max_partition_order, sum = 0; partition_order >= (int)min_partition_order; partition_order--) { if(! @@ -3874,10 +4133,9 @@ unsigned find_best_partition_order_( raw_bits_per_partition+sum, residual_samples, predictor_order, - rice_parameter, rice_parameter_limit, rice_parameter_search_dist, - (unsigned)partition_order, + (uint32_t)partition_order, do_escape_coding, &private_->partitioned_rice_contents_extra[!best_parameters_index], &residual_bits @@ -3904,13 +4162,12 @@ unsigned find_best_partition_order_( * knowledge; it is const to the outside world. */ FLAC__EntropyCodingMethod_PartitionedRiceContents* prc = (FLAC__EntropyCodingMethod_PartitionedRiceContents*)best_ecm->data.partitioned_rice.contents; - unsigned partition; + uint32_t partition; /* save best parameters and raw_bits */ - FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(prc, flac_max(6u, best_partition_order)); - memcpy(prc->parameters, private_->partitioned_rice_contents_extra[best_parameters_index].parameters, sizeof(unsigned)*(1<<(best_partition_order))); + memcpy(prc->parameters, private_->partitioned_rice_contents_extra[best_parameters_index].parameters, (uint32_t)sizeof(uint32_t)*(1<<(best_partition_order))); if(do_escape_coding) - memcpy(prc->raw_bits, private_->partitioned_rice_contents_extra[best_parameters_index].raw_bits, sizeof(unsigned)*(1<<(best_partition_order))); + memcpy(prc->raw_bits, private_->partitioned_rice_contents_extra[best_parameters_index].raw_bits, (uint32_t)sizeof(uint32_t)*(1<<(best_partition_order))); /* * Now need to check if the type should be changed to * FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 based on the @@ -3930,53 +4187,49 @@ unsigned find_best_partition_order_( void precompute_partition_info_sums_( const FLAC__int32 residual[], FLAC__uint64 abs_residual_partition_sums[], - unsigned residual_samples, - unsigned predictor_order, - unsigned min_partition_order, - unsigned max_partition_order, - unsigned bps + uint32_t residual_samples, + uint32_t predictor_order, + uint32_t min_partition_order, + uint32_t max_partition_order, + uint32_t bps ) { - const unsigned default_partition_samples = (residual_samples + predictor_order) >> max_partition_order; - unsigned partitions = 1u << max_partition_order; + const uint32_t default_partition_samples = (residual_samples + predictor_order) >> max_partition_order; + uint32_t partitions = 1u << max_partition_order; FLAC__ASSERT(default_partition_samples > predictor_order); /* first do max_partition_order */ { - unsigned partition, residual_sample, end = (unsigned)(-(int)predictor_order); - /* WATCHOUT: "+ bps + FLAC__MAX_EXTRA_RESIDUAL_BPS" is the maximum - * assumed size of the average residual magnitude */ - if(FLAC__bitmath_ilog2(default_partition_samples) + bps + FLAC__MAX_EXTRA_RESIDUAL_BPS < 32) { - FLAC__uint32 abs_residual_partition_sum; - + const uint32_t threshold = 32 - FLAC__bitmath_ilog2(default_partition_samples); + uint32_t partition, residual_sample, end = (uint32_t)(-(int)predictor_order); + /* WATCHOUT: "bps + FLAC__MAX_EXTRA_RESIDUAL_BPS" is the maximum assumed size of the average residual magnitude */ + if(bps + FLAC__MAX_EXTRA_RESIDUAL_BPS < threshold) { for(partition = residual_sample = 0; partition < partitions; partition++) { + FLAC__uint32 abs_residual_partition_sum = 0; end += default_partition_samples; - abs_residual_partition_sum = 0; for( ; residual_sample < end; residual_sample++) abs_residual_partition_sum += abs(residual[residual_sample]); /* abs(INT_MIN) is undefined, but if the residual is INT_MIN we have bigger problems */ abs_residual_partition_sums[partition] = abs_residual_partition_sum; } } else { /* have to pessimistically use 64 bits for accumulator */ - FLAC__uint64 abs_residual_partition_sum; - for(partition = residual_sample = 0; partition < partitions; partition++) { + FLAC__uint64 abs_residual_partition_sum64 = 0; end += default_partition_samples; - abs_residual_partition_sum = 0; for( ; residual_sample < end; residual_sample++) - abs_residual_partition_sum += abs(residual[residual_sample]); /* abs(INT_MIN) is undefined, but if the residual is INT_MIN we have bigger problems */ - abs_residual_partition_sums[partition] = abs_residual_partition_sum; + abs_residual_partition_sum64 += abs(residual[residual_sample]); /* abs(INT_MIN) is undefined, but if the residual is INT_MIN we have bigger problems */ + abs_residual_partition_sums[partition] = abs_residual_partition_sum64; } } } /* now merge partitions for lower orders */ { - unsigned from_partition = 0, to_partition = partitions; + uint32_t from_partition = 0, to_partition = partitions; int partition_order; for(partition_order = (int)max_partition_order - 1; partition_order >= (int)min_partition_order; partition_order--) { - unsigned i; + uint32_t i; partitions >>= 1; for(i = 0; i < partitions; i++) { abs_residual_partition_sums[to_partition++] = @@ -3990,24 +4243,24 @@ void precompute_partition_info_sums_( void precompute_partition_info_escapes_( const FLAC__int32 residual[], - unsigned raw_bits_per_partition[], - unsigned residual_samples, - unsigned predictor_order, - unsigned min_partition_order, - unsigned max_partition_order + uint32_t raw_bits_per_partition[], + uint32_t residual_samples, + uint32_t predictor_order, + uint32_t min_partition_order, + uint32_t max_partition_order ) { int partition_order; - unsigned from_partition, to_partition = 0; - const unsigned blocksize = residual_samples + predictor_order; + uint32_t from_partition, to_partition = 0; + const uint32_t blocksize = residual_samples + predictor_order; /* first do max_partition_order */ for(partition_order = (int)max_partition_order; partition_order >= 0; partition_order--) { FLAC__int32 r; FLAC__uint32 rmax; - unsigned partition, partition_sample, partition_samples, residual_sample; - const unsigned partitions = 1u << partition_order; - const unsigned default_partition_samples = blocksize >> partition_order; + uint32_t partition, partition_sample, partition_samples, residual_sample; + const uint32_t partitions = 1u << partition_order; + const uint32_t default_partition_samples = blocksize >> partition_order; FLAC__ASSERT(default_partition_samples > predictor_order); @@ -4033,9 +4286,9 @@ void precompute_partition_info_escapes_( /* now merge partitions for lower orders */ for(from_partition = 0, --partition_order; partition_order >= (int)min_partition_order; partition_order--) { - unsigned m; - unsigned i; - const unsigned partitions = 1u << partition_order; + uint32_t m; + uint32_t i; + const uint32_t partitions = 1u << partition_order; for(i = 0; i < partitions; i++) { m = raw_bits_per_partition[from_partition]; from_partition++; @@ -4047,36 +4300,37 @@ void precompute_partition_info_escapes_( } #ifdef EXACT_RICE_BITS_CALCULATION -static inline unsigned count_rice_bits_in_partition_( - const unsigned rice_parameter, - const unsigned partition_samples, +static inline uint32_t count_rice_bits_in_partition_( + const uint32_t rice_parameter, + const uint32_t partition_samples, const FLAC__int32 *residual ) { - unsigned i, partition_bits = + uint32_t i; + uint64_t partition_bits = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN + /* actually could end up being FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN but err on side of 16bps */ (1+rice_parameter) * partition_samples /* 1 for unary stop bit + rice_parameter for the binary portion */ ; for(i = 0; i < partition_samples; i++) partition_bits += ( (FLAC__uint32)((residual[i]<<1)^(residual[i]>>31)) >> rice_parameter ); - return partition_bits; + return (uint32_t)(flac_min(partition_bits,UINT32_MAX)); // To make sure the return value doesn't overflow } #else -static inline unsigned count_rice_bits_in_partition_( - const unsigned rice_parameter, - const unsigned partition_samples, +static inline uint32_t count_rice_bits_in_partition_( + const uint32_t rice_parameter, + const uint32_t partition_samples, const FLAC__uint64 abs_residual_partition_sum ) { - return - FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN + /* actually could end up being FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN but err on side of 16bps */ + return (uint32_t)(flac_min( // To make sure the return value doesn't overflow + (uint32_t) (FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN + /* actually could end up being FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN but err on side of 16bps */ (1+rice_parameter) * partition_samples + /* 1 for unary stop bit + rice_parameter for the binary portion */ ( rice_parameter? - (unsigned)(abs_residual_partition_sum >> (rice_parameter-1)) /* rice_parameter-1 because the real coder sign-folds instead of using a sign bit */ - : (unsigned)(abs_residual_partition_sum << 1) /* can't shift by negative number, so reverse */ + (abs_residual_partition_sum >> (rice_parameter-1)) /* rice_parameter-1 because the real coder sign-folds instead of using a sign bit */ + : (abs_residual_partition_sum << 1) /* can't shift by negative number, so reverse */ ) - - (partition_samples >> 1) + - (partition_samples >> 1)),UINT32_MAX)); /* -(partition_samples>>1) to subtract out extra contributions to the abs_residual_partition_sum. * The actual number of bits used is closer to the sum(for all i in the partition) of abs(residual[i])>>(rice_parameter-1) * By using the abs_residual_partition sum, we also add in bits in the LSBs that would normally be shifted out. @@ -4092,62 +4346,100 @@ FLAC__bool set_partitioned_rice_( const FLAC__int32 residual[], #endif const FLAC__uint64 abs_residual_partition_sums[], - const unsigned raw_bits_per_partition[], - const unsigned residual_samples, - const unsigned predictor_order, - const unsigned suggested_rice_parameter, - const unsigned rice_parameter_limit, - const unsigned rice_parameter_search_dist, - const unsigned partition_order, + const uint32_t raw_bits_per_partition[], + const uint32_t residual_samples, + const uint32_t predictor_order, + const uint32_t rice_parameter_limit, + const uint32_t rice_parameter_search_dist, + const uint32_t partition_order, const FLAC__bool search_for_escapes, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, - unsigned *bits + uint32_t *bits ) { - unsigned rice_parameter, partition_bits; - unsigned best_partition_bits, best_rice_parameter = 0; - unsigned bits_ = FLAC__ENTROPY_CODING_METHOD_TYPE_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; - unsigned *parameters, *raw_bits; + uint32_t rice_parameter, partition_bits; + uint32_t best_partition_bits, best_rice_parameter = 0; + uint32_t bits_ = FLAC__ENTROPY_CODING_METHOD_TYPE_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; + uint32_t *parameters, *raw_bits; + uint32_t partition, residual_sample; + uint32_t partition_samples, partition_samples_base; + uint32_t partition_samples_fixed_point_divisor, partition_samples_fixed_point_divisor_base; + const uint32_t partitions = 1u << partition_order; + FLAC__uint64 mean; #ifdef ENABLE_RICE_PARAMETER_SEARCH - unsigned min_rice_parameter, max_rice_parameter; + uint32_t min_rice_parameter, max_rice_parameter; #else (void)rice_parameter_search_dist; #endif - FLAC__ASSERT(suggested_rice_parameter < FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER); FLAC__ASSERT(rice_parameter_limit <= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER); - FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, flac_max(6u, partition_order)); parameters = partitioned_rice_contents->parameters; raw_bits = partitioned_rice_contents->raw_bits; - if(partition_order == 0) { - best_partition_bits = (unsigned)(-1); + partition_samples_base = (residual_samples+predictor_order) >> partition_order; + + /* Integer division is slow. To speed up things, precalculate a fixed point + * divisor, as all partitions except the first are the same size. 18 bits + * are taken because maximum block size is 65535, max partition size for + * partitions other than 0 is 32767 (15 bit), max abs residual is 2^31, + * which leaves 18 bit */ + partition_samples_fixed_point_divisor_base = 0x40000 / partition_samples_base; + + for(partition = residual_sample = 0; partition < partitions; partition++) { + partition_samples = partition_samples_base; + if(partition > 0) { + partition_samples_fixed_point_divisor = partition_samples_fixed_point_divisor_base; + } + else { + if(partition_samples <= predictor_order) + return false; + else + partition_samples -= predictor_order; + partition_samples_fixed_point_divisor = 0x40000 / partition_samples; + } + mean = abs_residual_partition_sums[partition]; + /* 'mean' is not a good name for the variable, it is + * actually the sum of magnitudes of all residual values + * in the partition, so the actual mean is + * mean/partition_samples + */ + if(mean < 2 || (((mean - 1)*partition_samples_fixed_point_divisor)>>18) == 0) + rice_parameter = 0; + else + rice_parameter = FLAC__bitmath_ilog2_wide(((mean - 1)*partition_samples_fixed_point_divisor)>>18) + 1; + + if(rice_parameter >= rice_parameter_limit) { +#ifndef NDEBUG + fprintf(stderr, "clipping rice_parameter (%u -> %u) @6\n", rice_parameter, rice_parameter_limit - 1); +#endif + rice_parameter = rice_parameter_limit - 1; + } + + best_partition_bits = UINT32_MAX; #ifdef ENABLE_RICE_PARAMETER_SEARCH if(rice_parameter_search_dist) { - if(suggested_rice_parameter < rice_parameter_search_dist) + if(rice_parameter < rice_parameter_search_dist) min_rice_parameter = 0; else - min_rice_parameter = suggested_rice_parameter - rice_parameter_search_dist; - max_rice_parameter = suggested_rice_parameter + rice_parameter_search_dist; + min_rice_parameter = rice_parameter - rice_parameter_search_dist; + max_rice_parameter = rice_parameter + rice_parameter_search_dist; if(max_rice_parameter >= rice_parameter_limit) { -#ifdef DEBUG_VERBOSE - fprintf(stderr, "clipping rice_parameter (%u -> %u) @5\n", max_rice_parameter, rice_parameter_limit - 1); +#ifndef NDEBUG + fprintf(stderr, "clipping rice_parameter (%u -> %u) @7\n", max_rice_parameter, rice_parameter_limit - 1); #endif max_rice_parameter = rice_parameter_limit - 1; } } else - min_rice_parameter = max_rice_parameter = suggested_rice_parameter; + min_rice_parameter = max_rice_parameter = rice_parameter; for(rice_parameter = min_rice_parameter; rice_parameter <= max_rice_parameter; rice_parameter++) { -#else - rice_parameter = suggested_rice_parameter; #endif #ifdef EXACT_RICE_BITS_CALCULATION - partition_bits = count_rice_bits_in_partition_(rice_parameter, residual_samples, residual); + partition_bits = count_rice_bits_in_partition_(rice_parameter, partition_samples, residual+residual_sample); #else - partition_bits = count_rice_bits_in_partition_(rice_parameter, residual_samples, abs_residual_partition_sums[0]); + partition_bits = count_rice_bits_in_partition_(rice_parameter, partition_samples, abs_residual_partition_sums[partition]); #endif if(partition_bits < best_partition_bits) { best_rice_parameter = rice_parameter; @@ -4157,131 +4449,30 @@ FLAC__bool set_partitioned_rice_( } #endif if(search_for_escapes) { - partition_bits = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN + raw_bits_per_partition[0] * residual_samples; - if(partition_bits <= best_partition_bits) { - raw_bits[0] = raw_bits_per_partition[0]; + partition_bits = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN + raw_bits_per_partition[partition] * partition_samples; + if(partition_bits <= best_partition_bits && raw_bits_per_partition[partition] < 32) { + raw_bits[partition] = raw_bits_per_partition[partition]; best_rice_parameter = 0; /* will be converted to appropriate escape parameter later */ best_partition_bits = partition_bits; } else - raw_bits[0] = 0; + raw_bits[partition] = 0; } - parameters[0] = best_rice_parameter; - bits_ += best_partition_bits; - } - else { - unsigned partition, residual_sample; - unsigned partition_samples; - FLAC__uint64 mean, k; - const unsigned partitions = 1u << partition_order; - for(partition = residual_sample = 0; partition < partitions; partition++) { - partition_samples = (residual_samples+predictor_order) >> partition_order; - if(partition == 0) { - if(partition_samples <= predictor_order) - return false; - else - partition_samples -= predictor_order; - } - mean = abs_residual_partition_sums[partition]; - /* we are basically calculating the size in bits of the - * average residual magnitude in the partition: - * rice_parameter = floor(log2(mean/partition_samples)) - * 'mean' is not a good name for the variable, it is - * actually the sum of magnitudes of all residual values - * in the partition, so the actual mean is - * mean/partition_samples - */ -#if 0 /* old simple code */ - for(rice_parameter = 0, k = partition_samples; k < mean; rice_parameter++, k <<= 1) - ; -#else -#if defined FLAC__CPU_X86_64 /* and other 64-bit arch, too */ - if(mean <= 0x80000000/512) { /* 512: more or less optimal for both 16- and 24-bit input */ -#else - if(mean <= 0x80000000/8) { /* 32-bit arch: use 32-bit math if possible */ -#endif - FLAC__uint32 k2, mean2 = (FLAC__uint32) mean; - rice_parameter = 0; k2 = partition_samples; - while(k2*8 < mean2) { /* requires: mean <= (2^31)/8 */ - rice_parameter += 4; k2 <<= 4; /* tuned for 16-bit input */ - } - while(k2 < mean2) { /* requires: mean <= 2^31 */ - rice_parameter++; k2 <<= 1; - } - } - else { - rice_parameter = 0; k = partition_samples; - if(mean <= FLAC__U64L(0x8000000000000000)/128) /* usually mean is _much_ smaller than this value */ - while(k*128 < mean) { /* requires: mean <= (2^63)/128 */ - rice_parameter += 8; k <<= 8; /* tuned for 24-bit input */ - } - while(k < mean) { /* requires: mean <= 2^63 */ - rice_parameter++; k <<= 1; - } - } -#endif - if(rice_parameter >= rice_parameter_limit) { -#ifdef DEBUG_VERBOSE - fprintf(stderr, "clipping rice_parameter (%u -> %u) @6\n", rice_parameter, rice_parameter_limit - 1); -#endif - rice_parameter = rice_parameter_limit - 1; - } - - best_partition_bits = (unsigned)(-1); -#ifdef ENABLE_RICE_PARAMETER_SEARCH - if(rice_parameter_search_dist) { - if(rice_parameter < rice_parameter_search_dist) - min_rice_parameter = 0; - else - min_rice_parameter = rice_parameter - rice_parameter_search_dist; - max_rice_parameter = rice_parameter + rice_parameter_search_dist; - if(max_rice_parameter >= rice_parameter_limit) { -#ifdef DEBUG_VERBOSE - fprintf(stderr, "clipping rice_parameter (%u -> %u) @7\n", max_rice_parameter, rice_parameter_limit - 1); -#endif - max_rice_parameter = rice_parameter_limit - 1; - } - } - else - min_rice_parameter = max_rice_parameter = rice_parameter; - - for(rice_parameter = min_rice_parameter; rice_parameter <= max_rice_parameter; rice_parameter++) { -#endif -#ifdef EXACT_RICE_BITS_CALCULATION - partition_bits = count_rice_bits_in_partition_(rice_parameter, partition_samples, residual+residual_sample); -#else - partition_bits = count_rice_bits_in_partition_(rice_parameter, partition_samples, abs_residual_partition_sums[partition]); -#endif - if(partition_bits < best_partition_bits) { - best_rice_parameter = rice_parameter; - best_partition_bits = partition_bits; - } -#ifdef ENABLE_RICE_PARAMETER_SEARCH - } -#endif - if(search_for_escapes) { - partition_bits = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN + raw_bits_per_partition[partition] * partition_samples; - if(partition_bits <= best_partition_bits) { - raw_bits[partition] = raw_bits_per_partition[partition]; - best_rice_parameter = 0; /* will be converted to appropriate escape parameter later */ - best_partition_bits = partition_bits; - } - else - raw_bits[partition] = 0; - } - parameters[partition] = best_rice_parameter; + parameters[partition] = best_rice_parameter; + if(best_partition_bits < UINT32_MAX - bits_) // To make sure _bits doesn't overflow bits_ += best_partition_bits; - residual_sample += partition_samples; - } + else + bits_ = UINT32_MAX; + residual_sample += partition_samples; } *bits = bits_; return true; } -unsigned get_wasted_bits_(FLAC__int32 signal[], unsigned samples) +uint32_t get_wasted_bits_(FLAC__int32 signal[], uint32_t samples) { - unsigned i, shift; + uint32_t i, shift; FLAC__int32 x = 0; for(i = 0; i < samples && !(x&1); i++) @@ -4303,9 +4494,34 @@ unsigned get_wasted_bits_(FLAC__int32 signal[], unsigned samples) return shift; } -void append_to_verify_fifo_(verify_input_fifo *fifo, const FLAC__int32 * const input[], unsigned input_offset, unsigned channels, unsigned wide_samples) +uint32_t get_wasted_bits_wide_(FLAC__int64 signal_wide[], FLAC__int32 signal[], uint32_t samples) { - unsigned channel; + uint32_t i, shift; + FLAC__int64 x = 0; + + for(i = 0; i < samples && !(x&1); i++) + x |= signal_wide[i]; + + if(x == 0) { + shift = 1; + } + else { + for(shift = 0; !(x&1); shift++) + x >>= 1; + } + + if(shift > 0) { + for(i = 0; i < samples; i++) + signal[i] = (FLAC__int32)(signal_wide[i] >> shift); + } + + return shift; +} + + +void append_to_verify_fifo_(verify_input_fifo *fifo, const FLAC__int32 * const input[], uint32_t input_offset, uint32_t channels, uint32_t wide_samples) +{ + uint32_t channel; for(channel = 0; channel < channels; channel++) memcpy(&fifo->data[channel][fifo->tail], &input[channel][input_offset], sizeof(FLAC__int32) * wide_samples); @@ -4315,11 +4531,11 @@ void append_to_verify_fifo_(verify_input_fifo *fifo, const FLAC__int32 * const i FLAC__ASSERT(fifo->tail <= fifo->size); } -void append_to_verify_fifo_interleaved_(verify_input_fifo *fifo, const FLAC__int32 input[], unsigned input_offset, unsigned channels, unsigned wide_samples) +void append_to_verify_fifo_interleaved_(verify_input_fifo *fifo, const FLAC__int32 input[], uint32_t input_offset, uint32_t channels, uint32_t wide_samples) { - unsigned channel; - unsigned sample, wide_sample; - unsigned tail = fifo->tail; + uint32_t channel; + uint32_t sample, wide_sample; + uint32_t tail = fifo->tail; sample = input_offset * channels; for(wide_sample = 0; wide_sample < wide_samples; wide_sample++) { @@ -4366,16 +4582,21 @@ FLAC__StreamDecoderReadStatus verify_read_callback_(const FLAC__StreamDecoder *d FLAC__StreamDecoderWriteStatus verify_write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) { FLAC__StreamEncoder *encoder = (FLAC__StreamEncoder *)client_data; - unsigned channel; - const unsigned channels = frame->header.channels; - const unsigned blocksize = frame->header.blocksize; - const unsigned bytes_per_block = sizeof(FLAC__int32) * blocksize; + uint32_t channel; + const uint32_t channels = frame->header.channels; + const uint32_t blocksize = frame->header.blocksize; + const uint32_t bytes_per_block = sizeof(FLAC__int32) * blocksize; (void)decoder; + if(encoder->protected_->state == FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR) { + /* This is set when verify_error_callback_ was called */ + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + for(channel = 0; channel < channels; channel++) { if(0 != memcmp(buffer[channel], encoder->private_->verify.input_fifo.data[channel], bytes_per_block)) { - unsigned i, sample = 0; + uint32_t i, sample = 0; FLAC__int32 expect = 0, got = 0; for(i = 0; i < blocksize; i++) { @@ -4389,7 +4610,7 @@ FLAC__StreamDecoderWriteStatus verify_write_callback_(const FLAC__StreamDecoder FLAC__ASSERT(i < blocksize); FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); encoder->private_->verify.error_stats.absolute_sample = frame->header.number.sample_number + sample; - encoder->private_->verify.error_stats.frame_number = (unsigned)(frame->header.number.sample_number / blocksize); + encoder->private_->verify.error_stats.frame_number = (uint32_t)(frame->header.number.sample_number / blocksize); encoder->private_->verify.error_stats.channel = channel; encoder->private_->verify.error_stats.sample = sample; encoder->private_->verify.error_stats.expected = expect; @@ -4472,7 +4693,7 @@ static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *st #define local__fwrite fwrite #endif -FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) +FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, uint32_t current_frame, void *client_data) { (void)client_data, (void)current_frame; @@ -4514,14 +4735,10 @@ FILE *get_binary_stdout_(void) */ #if defined _MSC_VER || defined __MINGW32__ _setmode(_fileno(stdout), _O_BINARY); -#elif defined __CYGWIN__ - /* almost certainly not needed for any modern Cygwin, but let's be safe... */ - setmode(_fileno(stdout), _O_BINARY); #elif defined __EMX__ setmode(fileno(stdout), O_BINARY); #endif return stdout; } - #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c index 8d6953ac..2fda19b6 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,14 +39,18 @@ #include "include/private/stream_encoder_framing.h" #include "include/private/crc.h" #include "../assert.h" +#include "../compat.h" static FLAC__bool add_entropy_coding_method_(FLAC__BitWriter *bw, const FLAC__EntropyCodingMethod *method); -static FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const unsigned residual_samples, const unsigned predictor_order, const unsigned rice_parameters[], const unsigned raw_bits[], const unsigned partition_order, const FLAC__bool is_extended); +static FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const uint32_t residual_samples, const uint32_t predictor_order, const uint32_t rice_parameters[], const uint32_t raw_bits[], const uint32_t partition_order, const FLAC__bool is_extended); -FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw) +FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw, FLAC__bool update_vendor_string) { - unsigned i, j; - const unsigned vendor_string_length = (unsigned)strlen(FLAC__VENDOR_STRING); + uint32_t i, j, metadata_length; + const uint32_t vendor_string_length = (uint32_t)strlen(FLAC__VENDOR_STRING); + const uint32_t start_bits = FLAC__bitwriter_get_input_bits_unconsumed(bw); + + FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(bw)); if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->is_last, FLAC__STREAM_METADATA_IS_LAST_LEN)) return false; @@ -57,14 +61,17 @@ FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__ /* * First, for VORBIS_COMMENTs, adjust the length to reflect our vendor string */ - i = metadata->length; - if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + metadata_length = metadata->length; + if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT && update_vendor_string) { FLAC__ASSERT(metadata->data.vorbis_comment.vendor_string.length == 0 || 0 != metadata->data.vorbis_comment.vendor_string.entry); - i -= metadata->data.vorbis_comment.vendor_string.length; - i += vendor_string_length; + metadata_length -= metadata->data.vorbis_comment.vendor_string.length; + metadata_length += vendor_string_length; } - FLAC__ASSERT(i < (1u << FLAC__STREAM_METADATA_LENGTH_LEN)); - if(!FLAC__bitwriter_write_raw_uint32(bw, i, FLAC__STREAM_METADATA_LENGTH_LEN)) + FLAC__ASSERT(metadata_length < (1u << FLAC__STREAM_METADATA_LENGTH_LEN)); + /* double protection */ + if(metadata_length >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata_length, FLAC__STREAM_METADATA_LENGTH_LEN)) return false; switch(metadata->type) { @@ -92,8 +99,13 @@ FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__ FLAC__ASSERT(metadata->data.stream_info.bits_per_sample <= (1u << FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)); if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.bits_per_sample-1, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) return false; - if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) - return false; + if(metadata->data.stream_info.total_samples >= (FLAC__U64L(1) << FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)){ + if(!FLAC__bitwriter_write_raw_uint64(bw, 0, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) + return false; + }else{ + if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) + return false; + } if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.stream_info.md5sum, 16)) return false; break; @@ -118,10 +130,18 @@ FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__ } break; case FLAC__METADATA_TYPE_VORBIS_COMMENT: - if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, vendor_string_length)) - return false; - if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)FLAC__VENDOR_STRING, vendor_string_length)) - return false; + if(update_vendor_string) { + if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, vendor_string_length)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)FLAC__VENDOR_STRING, vendor_string_length)) + return false; + } + else { + if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, metadata->data.vorbis_comment.vendor_string.length)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.vorbis_comment.vendor_string.entry, metadata->data.vorbis_comment.vendor_string.length)) + return false; + } if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, metadata->data.vorbis_comment.num_comments)) return false; for(i = 0; i < metadata->data.vorbis_comment.num_comments; i++) { @@ -208,13 +228,23 @@ FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__ break; } + /* Now check whether metadata block length was correct */ + { + uint32_t length_in_bits = FLAC__bitwriter_get_input_bits_unconsumed(bw); + if(length_in_bits < start_bits) + return false; + length_in_bits -= start_bits; + if(length_in_bits % 8 != 0 || length_in_bits != (metadata_length*8+32)) + return false; + } + FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(bw)); return true; } FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWriter *bw) { - unsigned u, blocksize_hint, sample_rate_hint; + uint32_t u, blocksize_hint, sample_rate_hint; FLAC__byte crc; FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(bw)); @@ -273,7 +303,7 @@ FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWrit default: if(header->sample_rate <= 255000 && header->sample_rate % 1000 == 0) sample_rate_hint = u = 12; - else if(header->sample_rate % 10 == 0) + else if(header->sample_rate <= 655350 && header->sample_rate % 10 == 0) sample_rate_hint = u = 14; else if(header->sample_rate <= 0xffff) sample_rate_hint = u = 13; @@ -314,6 +344,7 @@ FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWrit case 16: u = 4; break; case 20: u = 5; break; case 24: u = 6; break; + case 32: u = 7; break; default: u = 0; break; } if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN)) @@ -359,22 +390,22 @@ FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWrit return true; } -FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, uint32_t subframe_bps, uint32_t wasted_bits, FLAC__BitWriter *bw) { FLAC__bool ok; ok = FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN) && (wasted_bits? FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1) : true) && - FLAC__bitwriter_write_raw_int32(bw, subframe->value, subframe_bps) + FLAC__bitwriter_write_raw_int64(bw, subframe->value, subframe_bps) ; return ok; } -FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, uint32_t residual_samples, uint32_t subframe_bps, uint32_t wasted_bits, FLAC__BitWriter *bw) { - unsigned i; + uint32_t i; if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK | (subframe->order<<1) | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) return false; @@ -383,7 +414,7 @@ FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsign return false; for(i = 0; i < subframe->order; i++) - if(!FLAC__bitwriter_write_raw_int32(bw, subframe->warmup[i], subframe_bps)) + if(!FLAC__bitwriter_write_raw_int64(bw, subframe->warmup[i], subframe_bps)) return false; if(!add_entropy_coding_method_(bw, &subframe->entropy_coding_method)) @@ -410,9 +441,9 @@ FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsign return true; } -FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, uint32_t residual_samples, uint32_t subframe_bps, uint32_t wasted_bits, FLAC__BitWriter *bw) { - unsigned i; + uint32_t i; if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK | ((subframe->order-1)<<1) | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) return false; @@ -421,7 +452,7 @@ FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned r return false; for(i = 0; i < subframe->order; i++) - if(!FLAC__bitwriter_write_raw_int32(bw, subframe->warmup[i], subframe_bps)) + if(!FLAC__bitwriter_write_raw_int64(bw, subframe->warmup[i], subframe_bps)) return false; if(!FLAC__bitwriter_write_raw_uint32(bw, subframe->qlp_coeff_precision-1, FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN)) @@ -456,10 +487,9 @@ FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned r return true; } -FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, unsigned samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, uint32_t samples, uint32_t subframe_bps, uint32_t wasted_bits, FLAC__BitWriter *bw) { - unsigned i; - const FLAC__int32 *signal = subframe->data; + uint32_t i; if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) return false; @@ -467,9 +497,24 @@ FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, if(!FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1)) return false; - for(i = 0; i < samples; i++) - if(!FLAC__bitwriter_write_raw_int32(bw, signal[i], subframe_bps)) - return false; + if(subframe->data_type == FLAC__VERBATIM_SUBFRAME_DATA_TYPE_INT32) { + const FLAC__int32 *signal = subframe->data.int32; + + FLAC__ASSERT(subframe_bps < 33); + + for(i = 0; i < samples; i++) + if(!FLAC__bitwriter_write_raw_int32(bw, signal[i], subframe_bps)) + return false; + } + else { + const FLAC__int64 *signal = subframe->data.int64; + + FLAC__ASSERT(subframe_bps == 33); + + for(i = 0; i < samples; i++) + if(!FLAC__bitwriter_write_raw_int64(bw, (FLAC__int64)signal[i], subframe_bps)) + return false; + } return true; } @@ -490,13 +535,13 @@ FLAC__bool add_entropy_coding_method_(FLAC__BitWriter *bw, const FLAC__EntropyCo return true; } -FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const unsigned residual_samples, const unsigned predictor_order, const unsigned rice_parameters[], const unsigned raw_bits[], const unsigned partition_order, const FLAC__bool is_extended) +FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const uint32_t residual_samples, const uint32_t predictor_order, const uint32_t rice_parameters[], const uint32_t raw_bits[], const uint32_t partition_order, const FLAC__bool is_extended) { - const unsigned plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; - const unsigned pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + const uint32_t plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; + const uint32_t pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; if(partition_order == 0) { - unsigned i; + uint32_t i; if(raw_bits[0] == 0) { if(!FLAC__bitwriter_write_raw_uint32(bw, rice_parameters[0], plen)) @@ -518,9 +563,9 @@ FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 return true; } else { - unsigned i, j, k = 0, k_last = 0; - unsigned partition_samples; - const unsigned default_partition_samples = (residual_samples+predictor_order) >> partition_order; + uint32_t i, j, k = 0, k_last = 0; + uint32_t partition_samples; + const uint32_t default_partition_samples = (residual_samples+predictor_order) >> partition_order; for(i = 0; i < (1u< +#include "../compat.h" #include "../assert.h" #include "../format.h" #include "include/private/window.h" #ifndef FLAC__INTEGER_ONLY_LIBRARY +#if defined(_MSC_VER) +// silence 25 MSVC warnings 'conversion from 'double' to 'float', possible loss of data' +#pragma warning ( disable : 4244 ) +#endif void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L) { @@ -67,7 +72,7 @@ void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L) FLAC__int32 n; for (n = 0; n < L; n++) - window[n] = (FLAC__real)(0.62f - 0.48f * fabs((float)n/(float)N-0.5f) - 0.38f * cos(2.0f * M_PI * ((float)n/(float)N))); + window[n] = (FLAC__real)(0.62f - 0.48f * fabsf((float)n/(float)N-0.5f) - 0.38f * cosf(2.0f * M_PI * ((float)n/(float)N))); } void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L) @@ -76,7 +81,7 @@ void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L) FLAC__int32 n; for (n = 0; n < L; n++) - window[n] = (FLAC__real)(0.42f - 0.5f * cos(2.0f * M_PI * n / N) + 0.08f * cos(4.0f * M_PI * n / N)); + window[n] = (FLAC__real)(0.42f - 0.5f * cosf(2.0f * M_PI * n / N) + 0.08f * cosf(4.0f * M_PI * n / N)); } /* 4-term -92dB side-lobe */ @@ -86,7 +91,7 @@ void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 n; for (n = 0; n <= N; n++) - window[n] = (FLAC__real)(0.35875f - 0.48829f * cos(2.0f * M_PI * n / N) + 0.14128f * cos(4.0f * M_PI * n / N) - 0.01168f * cos(6.0f * M_PI * n / N)); + window[n] = (FLAC__real)(0.35875f - 0.48829f * cosf(2.0f * M_PI * n / N) + 0.14128f * cosf(4.0f * M_PI * n / N) - 0.01168f * cosf(6.0f * M_PI * n / N)); } void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L) @@ -108,7 +113,7 @@ void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L) FLAC__int32 n; for (n = 0; n < L; n++) - window[n] = (FLAC__real)(1.0f - 1.93f * cos(2.0f * M_PI * n / N) + 1.29f * cos(4.0f * M_PI * n / N) - 0.388f * cos(6.0f * M_PI * n / N) + 0.0322f * cos(8.0f * M_PI * n / N)); + window[n] = (FLAC__real)(0.21557895f - 0.41663158f * cosf(2.0f * M_PI * n / N) + 0.277263158f * cosf(4.0f * M_PI * n / N) - 0.083578947f * cosf(6.0f * M_PI * n / N) + 0.006947368f * cosf(8.0f * M_PI * n / N)); } void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev) @@ -117,9 +122,15 @@ void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__rea const double N2 = (double)N / 2.; FLAC__int32 n; - for (n = 0; n <= N; n++) { - const double k = ((double)n - N2) / (stddev * N2); - window[n] = (FLAC__real)exp(-0.5f * k * k); + if(!(stddev > 0.0f && stddev <= 0.5f)) + /* stddev is not between 0 and 0.5, might be NaN. + * Default to 0.5 */ + FLAC__window_gauss(window, L, 0.25f); + else { + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / (stddev * N2); + window[n] = (FLAC__real)exp(-0.5f * k * k); + } } } @@ -129,7 +140,7 @@ void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L) FLAC__int32 n; for (n = 0; n < L; n++) - window[n] = (FLAC__real)(0.54f - 0.46f * cos(2.0f * M_PI * n / N)); + window[n] = (FLAC__real)(0.54f - 0.46f * cosf(2.0f * M_PI * n / N)); } void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L) @@ -138,7 +149,7 @@ void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L) FLAC__int32 n; for (n = 0; n < L; n++) - window[n] = (FLAC__real)(0.5f - 0.5f * cos(2.0f * M_PI * n / N)); + window[n] = (FLAC__real)(0.5f - 0.5f * cosf(2.0f * M_PI * n / N)); } void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L) @@ -147,7 +158,7 @@ void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L) FLAC__int32 n; for (n = 0; n < L; n++) - window[n] = (FLAC__real)(0.402f - 0.498f * cos(2.0f * M_PI * n / N) + 0.098f * cos(4.0f * M_PI * n / N) - 0.001f * cos(6.0f * M_PI * n / N)); + window[n] = (FLAC__real)(0.402f - 0.498f * cosf(2.0f * M_PI * n / N) + 0.098f * cosf(4.0f * M_PI * n / N) - 0.001f * cosf(6.0f * M_PI * n / N)); } void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L) @@ -156,7 +167,7 @@ void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L) FLAC__int32 n; for (n = 0; n < L; n++) - window[n] = (FLAC__real)(0.3635819f - 0.4891775f*cos(2.0f*M_PI*n/N) + 0.1365995f*cos(4.0f*M_PI*n/N) - 0.0106411f*cos(6.0f*M_PI*n/N)); + window[n] = (FLAC__real)(0.3635819f - 0.4891775f*cosf(2.0f*M_PI*n/N) + 0.1365995f*cosf(4.0f*M_PI*n/N) - 0.0106411f*cosf(6.0f*M_PI*n/N)); } void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L) @@ -191,6 +202,10 @@ void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__rea FLAC__window_rectangle(window, L); else if (p >= 1.0) FLAC__window_hann(window, L); + else if (!(p > 0.0f && p < 1.0f)) + /* p is not between 0 and 1, probably NaN. + * Default to 0.5 */ + FLAC__window_tukey(window, L, 0.5f); else { const FLAC__int32 Np = (FLAC__int32)(p / 2.0f * L) - 1; FLAC__int32 n; @@ -199,8 +214,8 @@ void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__rea /* ...replace ends with hann */ if (Np > 0) { for (n = 0; n <= Np; n++) { - window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * n / Np)); - window[L-Np-1+n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * (n+Np) / Np)); + window[n] = (FLAC__real)(0.5f - 0.5f * cosf(M_PI * n / Np)); + window[L-Np-1+n] = (FLAC__real)(0.5f - 0.5f * cosf(M_PI * (n+Np) / Np)); } } } @@ -217,6 +232,10 @@ void FLAC__window_partial_tukey(FLAC__real *window, const FLAC__int32 L, const F FLAC__window_partial_tukey(window, L, 0.05f, start, end); else if (p >= 1.0f) FLAC__window_partial_tukey(window, L, 0.95f, start, end); + else if (!(p > 0.0f && p < 1.0f)) + /* p is not between 0 and 1, probably NaN. + * Default to 0.5 */ + FLAC__window_partial_tukey(window, L, 0.5f, start, end); else { Np = (FLAC__int32)(p / 2.0f * N); @@ -224,11 +243,11 @@ void FLAC__window_partial_tukey(FLAC__real *window, const FLAC__int32 L, const F for (n = 0; n < start_n && n < L; n++) window[n] = 0.0f; for (i = 1; n < (start_n+Np) && n < L; n++, i++) - window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Np)); + window[n] = (FLAC__real)(0.5f - 0.5f * cosf(M_PI * i / Np)); for (; n < (end_n-Np) && n < L; n++) window[n] = 1.0f; for (i = Np; n < end_n && n < L; n++, i--) - window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Np)); + window[n] = (FLAC__real)(0.5f - 0.5f * cosf(M_PI * i / Np)); for (; n < L; n++) window[n] = 0.0f; } @@ -244,25 +263,29 @@ void FLAC__window_punchout_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__window_punchout_tukey(window, L, 0.05f, start, end); else if (p >= 1.0f) FLAC__window_punchout_tukey(window, L, 0.95f, start, end); + else if (!(p > 0.0f && p < 1.0f)) + /* p is not between 0 and 1, probably NaN. + * Default to 0.5 */ + FLAC__window_punchout_tukey(window, L, 0.5f, start, end); else { Ns = (FLAC__int32)(p / 2.0f * start_n); Ne = (FLAC__int32)(p / 2.0f * (L - end_n)); for (n = 0, i = 1; n < Ns && n < L; n++, i++) - window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ns)); + window[n] = (FLAC__real)(0.5f - 0.5f * cosf(M_PI * i / Ns)); for (; n < start_n-Ns && n < L; n++) window[n] = 1.0f; for (i = Ns; n < start_n && n < L; n++, i--) - window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ns)); + window[n] = (FLAC__real)(0.5f - 0.5f * cosf(M_PI * i / Ns)); for (; n < end_n && n < L; n++) window[n] = 0.0f; for (i = 1; n < end_n+Ne && n < L; n++, i++) - window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ne)); + window[n] = (FLAC__real)(0.5f - 0.5f * cosf(M_PI * i / Ne)); for (; n < L - (Ne) && n < L; n++) window[n] = 1.0f; for (i = Ne; n < L; n++, i--) - window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ne)); + window[n] = (FLAC__real)(0.5f - 0.5f * cosf(M_PI * i / Ne)); } } @@ -278,4 +301,8 @@ void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L) } } +#if defined(_MSC_VER) +#pragma warning ( default : 4244 ) +#endif + #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/metadata.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/metadata.h index ac944772..4747a5f3 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/metadata.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/metadata.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,6 +33,7 @@ #ifndef FLAC__METADATA_H #define FLAC__METADATA_H +#include /* for off_t */ #include "export.h" #include "callback.h" #include "format.h" @@ -92,7 +93,7 @@ * Efficient means the whole file is rewritten at most one time, and only * when necessary. Level 1 is not efficient only in the case that you * cause more than one metadata block to grow or shrink beyond what can - * be accomodated by padding. In this case you should probably use level + * be accommodated by padding. In this case you should probably use level * 2, which allows you to edit all the metadata for a file in memory and * write it out all at once. * @@ -133,6 +134,11 @@ extern "C" { * STREAMINFO, VORBIS_COMMENT, CUESHEET, and PICTURE blocks, requiring * only a filename. * + * On Windows, filename must be a UTF-8 encoded filename, which libFLAC + * internally translates to an appropriate representation to use with + * _wfopen. On all other systems, filename is passed to fopen without + * any translation. + * * They try to skip any ID3v2 tag at the head of the file. * * \{ @@ -216,13 +222,13 @@ FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__Stre * matched exactly. Use \c NULL to mean "any * description". * \param max_width The maximum width in pixels desired. Use - * \c (unsigned)(-1) to mean "any width". + * \c (uint32_t)(-1) to mean "any width". * \param max_height The maximum height in pixels desired. Use - * \c (unsigned)(-1) to mean "any height". + * \c (uint32_t)(-1) to mean "any height". * \param max_depth The maximum color depth in bits-per-pixel desired. - * Use \c (unsigned)(-1) to mean "any depth". + * Use \c (uint32_t)(-1) to mean "any depth". * \param max_colors The maximum number of colors desired. Use - * \c (unsigned)(-1) to mean "any number of colors". + * \c (uint32_t)(-1) to mean "any number of colors". * \assert * \code filename != NULL \endcode * \code picture != NULL \endcode @@ -233,7 +239,7 @@ FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__Stre * error, a file decoder error, or the file contained no PICTURE * block, and \a *picture will be set to \c NULL. */ -FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, unsigned max_width, unsigned max_height, unsigned max_depth, unsigned max_colors); +FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, uint32_t max_width, uint32_t max_height, uint32_t max_depth, uint32_t max_colors); /* \} */ @@ -386,6 +392,11 @@ FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_stat /** Initialize the iterator to point to the first metadata block in the * given FLAC file. * + * On Windows, filename must be a UTF-8 encoded filename, which libFLAC + * internally translates to an appropriate representation to use with + * _wfopen. On all other systems, filename is passed to fopen without + * any translation. + * * \param iterator A pointer to an existing iterator. * \param filename The path to the FLAC file. * \param read_only If \c true, the FLAC file will be opened @@ -496,13 +507,13 @@ FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const * \code iterator != NULL \endcode * \a iterator has been successfully initialized with * FLAC__metadata_simple_iterator_init() - * \retval unsigned + * \retval uint32_t * The length of the metadata block at the current iterator position. * The is same length as that in the - * metadata block header, + * metadata block header, * i.e. the length of the metadata body that follows the header. */ -FLAC_API unsigned FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator); +FLAC_API uint32_t FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator); /** Get the application ID of the \c APPLICATION block at the current * position. This avoids reading the actual block data which can save @@ -666,7 +677,7 @@ FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_S * * - Create a new chain using FLAC__metadata_chain_new(). A chain is a * linked list of FLAC metadata blocks. - * - Read all metadata into the the chain from a FLAC file using + * - Read all metadata into the chain from a FLAC file using * FLAC__metadata_chain_read() or FLAC__metadata_chain_read_ogg() and * check the status. * - Optionally, consolidate the padding using @@ -818,6 +829,11 @@ FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain); FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain); /** Read all metadata from a FLAC file into the chain. + * + * On Windows, filename must be a UTF-8 encoded filename, which libFLAC + * internally translates to an appropriate representation to use with + * _wfopen. On all other systems, filename is passed to fopen without + * any translation. * * \param chain A pointer to an existing chain. * \param filename The path to the FLAC file to read. @@ -832,6 +848,11 @@ FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_C FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename); /** Read all metadata from an Ogg FLAC file into the chain. + * + * On Windows, filename must be a UTF-8 encoded filename, which libFLAC + * internally translates to an appropriate representation to use with + * _wfopen. On all other systems, filename is passed to fopen without + * any translation. * * \note Ogg FLAC metadata data writing is not supported yet and * FLAC__metadata_chain_write() will fail. @@ -1372,12 +1393,13 @@ FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *b * \retval FLAC__bool * \c false if \a copy is \c true and malloc() fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy); +FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, uint32_t length, FLAC__bool copy); /** Resize the seekpoint array. * * If the size shrinks, elements will truncated; if it grows, new placeholder - * points will be added to the end. + * points will be added to the end. If this function returns false, the + * object is left untouched. * * \param object A pointer to an existing SEEKTABLE object. * \param new_num_points The desired length of the array; may be \c 0. @@ -1389,7 +1411,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetad * \retval FLAC__bool * \c false if memory allocation error, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points); +FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, uint32_t new_num_points); /** Set a seekpoint in a seektable. * @@ -1401,7 +1423,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMe * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode * \code object->data.seek_table.num_points > point_num \endcode */ -FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point); +FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, uint32_t point_num, FLAC__StreamMetadata_SeekPoint point); /** Insert a seekpoint into a seektable. * @@ -1415,7 +1437,7 @@ FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *ob * \retval FLAC__bool * \c false if memory allocation error, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point); +FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, uint32_t point_num, FLAC__StreamMetadata_SeekPoint point); /** Delete a seekpoint from a seektable. * @@ -1428,7 +1450,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMet * \retval FLAC__bool * \c false if memory allocation error, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num); +FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, uint32_t point_num); /** Check a seektable to see if it conforms to the FLAC specification. * See the format specification for limits on the contents of the @@ -1458,7 +1480,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamM * \retval FLAC__bool * \c false if memory allocation fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num); +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, uint32_t num); /** Append a specific seek point template to the end of a seek table. * @@ -1493,7 +1515,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__ * \retval FLAC__bool * \c false if memory allocation fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num); +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], uint32_t num); /** Append a set of evenly-spaced seek point templates to the end of a * seek table. @@ -1515,7 +1537,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC_ * \retval FLAC__bool * \c false if memory allocation fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples); +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, uint32_t num, FLAC__uint64 total_samples); /** Append a set of evenly-spaced seek point templates to the end of a * seek table. @@ -1543,7 +1565,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_point * \retval FLAC__bool * \c false if memory allocation fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples); +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, uint32_t samples, FLAC__uint64 total_samples); /** Sort a seek table's seek points according to the format specification, * removing duplicates. @@ -1590,7 +1612,8 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__ /** Resize the comment array. * * If the size shrinks, elements will truncated; if it grows, new empty - * fields will be added to the end. + * fields will be added to the end. If this function returns false, the + * object is left untouched. * * \param object A pointer to an existing VORBIS_COMMENT object. * \param new_num_comments The desired length of the array; may be \c 0. @@ -1602,7 +1625,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__ * \retval FLAC__bool * \c false if memory allocation fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments); +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, uint32_t new_num_comments); /** Sets a comment in a VORBIS_COMMENT block. * @@ -1629,7 +1652,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__St * \c false if memory allocation fails or \a entry does not comply with the * Vorbis comment specification, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, uint32_t comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); /** Insert a comment in a VORBIS_COMMENT block at the given index. * @@ -1659,7 +1682,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__Stream * \c false if memory allocation fails or \a entry does not comply with the * Vorbis comment specification, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, uint32_t comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); /** Appends a comment to a VORBIS_COMMENT block. * @@ -1691,7 +1714,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__Str * For convenience, a trailing NUL is added to the entry if it doesn't have * one already. * - * Depending on the the value of \a all, either all or just the first comment + * Depending on the value of \a all, either all or just the first comment * whose field name(s) match the given entry's name will be replaced by the * given entry. If no comments match, \a entry will simply be appended. * @@ -1732,7 +1755,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__St * \retval FLAC__bool * \c false if realloc() fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num); +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, uint32_t comment_num); /** Creates a Vorbis comment entry from NUL-terminated name and value strings. * @@ -1788,7 +1811,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair * \retval FLAC__bool * \c true if the field names match, else \c false */ -FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length); +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, uint32_t field_name_length); /** Find a Vorbis comment with the given field name. * @@ -1807,7 +1830,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC * The offset in the comment array of the first comment whose field * name matches \a field_name, or \c -1 if no match was found. */ -FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name); +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, uint32_t offset, const char *field_name); /** Remove first Vorbis comment matching the given field name. * @@ -1870,7 +1893,8 @@ FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_C /** Resize a track's index point array. * * If the size shrinks, elements will truncated; if it grows, new blank - * indices will be added to the end. + * indices will be added to the end. If this function returns false, the + * track object is left untouched. * * \param object A pointer to an existing CUESHEET object. * \param track_num The index of the track to modify. NOTE: this is not @@ -1885,7 +1909,7 @@ FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_C * \retval FLAC__bool * \c false if memory allocation error, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices); +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t new_num_indices); /** Insert an index point in a CUESHEET track at the given index. * @@ -1908,7 +1932,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__St * \retval FLAC__bool * \c false if realloc() fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index); +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num, FLAC__StreamMetadata_CueSheet_Index index); /** Insert a blank index point in a CUESHEET track at the given index. * @@ -1932,7 +1956,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__Stre * \retval FLAC__bool * \c false if realloc() fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num); /** Delete an index point in a CUESHEET track at the given index. * @@ -1951,12 +1975,13 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC * \retval FLAC__bool * \c false if realloc() fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num); /** Resize the track array. * * If the size shrinks, elements will truncated; if it grows, new blank - * tracks will be added to the end. + * tracks will be added to the end. If this function returns false, the + * object is left untouched. * * \param object A pointer to an existing CUESHEET object. * \param new_num_tracks The desired length of the array; may be \c 0. @@ -1968,7 +1993,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__Stre * \retval FLAC__bool * \c false if memory allocation error, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks); +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, uint32_t new_num_tracks); /** Sets a track in a CUESHEET block. * @@ -1990,7 +2015,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMet * \retval FLAC__bool * \c false if \a copy is \c true and malloc() fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, uint32_t track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); /** Insert a track in a CUESHEET block at the given index. * @@ -2013,7 +2038,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadat * \retval FLAC__bool * \c false if \a copy is \c true and malloc() fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, uint32_t track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); /** Insert a blank track in a CUESHEET block at the given index. * @@ -2032,7 +2057,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMeta * \retval FLAC__bool * \c false if \a copy is \c true and malloc() fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num); +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, uint32_t track_num); /** Delete a track in a CUESHEET block at the given index. * @@ -2047,7 +2072,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__Stre * \retval FLAC__bool * \c false if realloc() fails, else \c true. */ -FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num); +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, uint32_t track_num); /** Check a cue sheet to see if it conforms to the FLAC specification. * See the format specification for limits on the contents of the @@ -2172,6 +2197,34 @@ FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata */ FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation); + +/** Get the raw (binary) representation of a FLAC__StreamMetadata objeect. + * After use, free() the returned buffer. The length of the buffer is + * the length of the input metadata object plus 4 bytes for the header. + * + * \param object A pointer to metadata block to be converted. + * \assert + * \code object != NULL \endcode + * \retval FLAC__byte* + * \c NULL if there was an error, else a pointer to a buffer holding + * the requested data. + */ +FLAC_API FLAC__byte * FLAC__metadata_object_get_raw(const FLAC__StreamMetadata *object); + + +/** Turn a raw (binary) representation into a FLAC__StreamMetadata objeect. + * The returned object must be deleted with FLAC__metadata_object_delete() + * after use. + * + * \param buffer A pointer to a buffer containing a binary representation + * to be converted to a FLAC__StreamMetadata object + * \param length The length of the supplied buffer + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error, else a pointer to a FLAC__StreamMetadata + * holding the requested data. + */ + +FLAC_API FLAC__StreamMetadata * FLAC__metadata_object_set_raw(FLAC__byte *buffer, FLAC__uint32 length); /* \} */ #ifdef __cplusplus diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/ordinals.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/ordinals.h index b1e1acfc..d61aac57 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/ordinals.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/ordinals.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,27 +33,10 @@ #ifndef FLAC__ORDINALS_H #define FLAC__ORDINALS_H -#if defined(_MSC_VER) && _MSC_VER < 1600 - -/* Microsoft Visual Studio earlier than the 2010 version did not provide - * the 1999 ISO C Standard header file . - */ - -typedef __int8 FLAC__int8; -typedef unsigned __int8 FLAC__uint8; - -typedef __int16 FLAC__int16; -typedef __int32 FLAC__int32; -typedef __int64 FLAC__int64; -typedef unsigned __int16 FLAC__uint16; -typedef unsigned __int32 FLAC__uint32; -typedef unsigned __int64 FLAC__uint64; - -#else - -/* For MSVC 2010 and everything else which provides . */ +/* This of course assumes C99 headers */ #include +#include typedef int8_t FLAC__int8; typedef uint8_t FLAC__uint8; @@ -65,22 +48,8 @@ typedef uint16_t FLAC__uint16; typedef uint32_t FLAC__uint32; typedef uint64_t FLAC__uint64; -#endif - typedef int FLAC__bool; typedef FLAC__uint8 FLAC__byte; - -#ifdef true -#undef true -#endif -#ifdef false -#undef false -#endif -#ifndef __cplusplus -#define true 1 -#define false 0 -#endif - #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/private.h similarity index 53% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/flac/private.h index bf8e40f5..5340d401 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/private.h @@ -1,6 +1,5 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2002-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2013-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,17 +29,26 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FLAC__PRIVATE__METADATA_H -#define FLAC__PRIVATE__METADATA_H +#ifndef FLAC__SHARE__PRIVATE_H +#define FLAC__SHARE__PRIVATE_H -#include "../../../metadata.h" - -/* WATCHOUT: all malloc()ed data in the block is free()ed; this may not - * be a consistent state (e.g. PICTURE) or equivalent to the initial - * state after FLAC__metadata_object_new() +/* + * Unpublished debug routines from libFLAC. This should not be used from any + * client code other than code shipped with the FLAC sources. */ -void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object); - -void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object); +FLAC_API FLAC__bool FLAC__stream_encoder_disable_instruction_set(FLAC__StreamEncoder *encoder, int value); +FLAC_API FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value); +FLAC_API FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value); +FLAC_API FLAC__bool FLAC__stream_encoder_disable_verbatim_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value); +/* + * The following two routines were intended as debug routines and are not + * in the public headers, but SHOULD NOT CHANGE! It is known they are used + * in some non-audio projects needing every last bit of performance. + * See https://github.com/xiph/flac/issues/547 for details. These projects + * provide their own prototypes, so changing the signature of these + * functions would break building. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_md5(FLAC__StreamEncoder *encoder, FLAC__bool value); +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_md5(const FLAC__StreamEncoder *encoder); -#endif +#endif /* FLAC__SHARE__PRIVATE_H */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_decoder.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_decoder.h index fee97abc..2272bcac 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_decoder.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_decoder.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,6 +33,7 @@ #ifndef FLAC__STREAM_DECODER_H #define FLAC__STREAM_DECODER_H +#include /* for FILE */ #include "export.h" #include "format.h" @@ -227,7 +228,7 @@ typedef enum { */ FLAC__STREAM_DECODER_ABORTED, - /**< The decoder was aborted by the read callback. */ + /**< The decoder was aborted by the read or write callback. */ FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR, /**< An error occurred allocating memory. The decoder is in an invalid @@ -421,7 +422,11 @@ extern FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[]; * could be because the decoder encountered a valid frame made by a future * version of the encoder which it cannot parse, or because of a false * sync making it appear as though an encountered frame was generated by - * a future encoder. + * a future encoder. \c FLAC__STREAM_DECODER_ERROR_STATUS_BAD_METADATA is + * caused by finding data that doesn't fit a metadata block (too large + * or too small) or finding inconsistencies in the metadata, for example + * a PICTURE block with an image that exceeds the size of the metadata + * block. */ typedef enum { @@ -434,9 +439,12 @@ typedef enum { FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH, /**< The frame's data did not match the CRC in the footer. */ - FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM, /**< The decoder encountered reserved fields in use in the stream. */ + FLAC__STREAM_DECODER_ERROR_STATUS_BAD_METADATA + /**< The decoder encountered a corrupted metadata block. */ + } FLAC__StreamDecoderErrorStatus; /** Maps a FLAC__StreamDecoderErrorStatus to a C string. @@ -673,7 +681,7 @@ typedef FLAC__bool (*FLAC__StreamDecoderEofCallback)(const FLAC__StreamDecoder * * samples of length \a frame->header.blocksize. * Channels will be ordered according to the FLAC * specification; see the documentation for the - * frame header. + * frame header. * \param client_data The callee's client data set through * FLAC__stream_decoder_init_*(). * \retval FLAC__StreamDecoderWriteStatus @@ -919,7 +927,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDeco * \param decoder A decoder instance to query. * \assert * \code decoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See above. */ FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder); @@ -931,10 +939,10 @@ FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamD * \param decoder A decoder instance to query. * \assert * \code decoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See above. */ -FLAC_API unsigned FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder); +FLAC_API uint32_t FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder); /** Get the current channel assignment in the stream being decoded. * Will only be valid after decoding has started and will contain the @@ -955,10 +963,10 @@ FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(con * \param decoder A decoder instance to query. * \assert * \code decoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See above. */ -FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder); +FLAC_API uint32_t FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder); /** Get the current sample rate in Hz of the stream being decoded. * Will only be valid after decoding has started and will contain the @@ -967,10 +975,10 @@ FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDec * \param decoder A decoder instance to query. * \assert * \code decoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See above. */ -FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder); +FLAC_API uint32_t FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder); /** Get the current blocksize of the stream being decoded. * Will only be valid after decoding has started and will contain the @@ -979,10 +987,10 @@ FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder * \param decoder A decoder instance to query. * \assert * \code decoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See above. */ -FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder); +FLAC_API uint32_t FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder); /** Returns the decoder's current read position within the stream. * The position is the byte offset from the start of the stream. @@ -1005,6 +1013,16 @@ FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder * */ FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position); +/** Return client_data from decoder. + * The data pointed to by the pointer should not be modified. + * + * \param decoder A decoder instance. + * \retval const void * + * The callee's client data set through FLAC__stream_decoder_init_*(). + * Do not modify the contents. + */ +FLAC_API const void *FLAC__stream_decoder_get_client_data(FLAC__StreamDecoder *decoder); + /** Initialize the decoder instance to decode native FLAC streams. * * This flavor of initialization sets up the decoder to decode from a @@ -1183,7 +1201,7 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( * Unless \a file is \c stdin, it will be closed * when FLAC__stream_decoder_finish() is called. * Note however that seeking will not work when - * decoding from \c stdout since it is not seekable. + * decoding from \c stdin since it is not seekable. * \param write_callback See FLAC__StreamDecoderWriteCallback. This * pointer must not be \c NULL. * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This @@ -1233,7 +1251,7 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( * Unless \a file is \c stdin, it will be closed * when FLAC__stream_decoder_finish() is called. * Note however that seeking will not work when - * decoding from \c stdout since it is not seekable. + * decoding from \c stdin since it is not seekable. * \param write_callback See FLAC__StreamDecoderWriteCallback. This * pointer must not be \c NULL. * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This @@ -1262,11 +1280,15 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( /** Initialize the decoder instance to decode native FLAC files. * * This flavor of initialization sets up the decoder to decode from a plain - * native FLAC file. If POSIX fopen() semantics are not sufficient, (for - * example, with Unicode filenames on Windows), you must use - * FLAC__stream_decoder_init_FILE(), or FLAC__stream_decoder_init_stream() + * native FLAC file. If POSIX fopen() semantics are not sufficient, you must + * use FLAC__stream_decoder_init_FILE(), or FLAC__stream_decoder_init_stream() * and provide callbacks for the I/O. * + * On Windows, filename must be a UTF-8 encoded filename, which libFLAC + * internally translates to an appropriate representation to use with + * _wfopen. On all other systems, filename is passed to fopen without + * any translation. + * * This function should be called after FLAC__stream_decoder_new() and * FLAC__stream_decoder_set_*() but before any of the * FLAC__stream_decoder_process_*() functions. Will set and return the @@ -1304,11 +1326,15 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( /** Initialize the decoder instance to decode Ogg FLAC files. * * This flavor of initialization sets up the decoder to decode from a plain - * Ogg FLAC file. If POSIX fopen() semantics are not sufficient, (for - * example, with Unicode filenames on Windows), you must use + * Ogg FLAC file. If POSIX fopen() semantics are not sufficient, you must use * FLAC__stream_decoder_init_ogg_FILE(), or FLAC__stream_decoder_init_ogg_stream() * and provide callbacks for the I/O. * + * On Windows, filename must be a UTF-8 encoded filename, which libFLAC + * internally translates to an appropriate representation to use with + * _wfopen. On all other systems, filename is passed to fopen without + * any translation. + * * This function should be called after FLAC__stream_decoder_new() and * FLAC__stream_decoder_set_*() but before any of the * FLAC__stream_decoder_process_*() functions. Will set and return the @@ -1402,8 +1428,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder); * and is not seekable (i.e. no seek callback was provided or the seek * callback returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED), it * is the duty of the client to start feeding data from the beginning of - * the stream on the next FLAC__stream_decoder_process() or - * FLAC__stream_decoder_process_interleaved() call. + * the stream on the next FLAC__stream_decoder_process_*() call. * * \param decoder A decoder instance. * \assert diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_encoder.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_encoder.h index 599936d4..a0d02639 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_encoder.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_encoder.h @@ -1,6 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2000-2009 Josh Coalson - * Copyright (C) 2011-2014 Xiph.Org Foundation + * Copyright (C) 2011-2023 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,6 +33,7 @@ #ifndef FLAC__STREAM_ENCODER_H #define FLAC__STREAM_ENCODER_H +#include /* for FILE */ #include "export.h" #include "format.h" #include "stream_decoder.h" @@ -128,8 +129,8 @@ extern "C" { * Unlike the decoders, the stream encoder has many options that can * affect the speed and compression ratio. When setting these parameters * you should have some basic knowledge of the format (see the - * user-level documentation - * or the formal description). The + * user-level documentation + * or the formal description). The * FLAC__stream_encoder_set_*() functions themselves do not validate the * values as many are interdependent. The FLAC__stream_encoder_init_*() * functions will do this, so make sure to pay attention to the state @@ -310,8 +311,7 @@ typedef enum { FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE, /**< The encoder has an invalid setting for bits-per-sample. - * FLAC supports 4-32 bps but the reference encoder currently supports - * only up to 24 bps. + * FLAC supports 4-32 bps. */ FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE, @@ -330,7 +330,7 @@ typedef enum { /**< The specified block size is less than the maximum LPC order. */ FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE, - /**< The encoder is bound to the Subset but other settings violate it. */ + /**< The encoder is bound to the Subset but other settings violate it. */ FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA, /**< The metadata input to the encoder is invalid, in one of the following ways: @@ -553,7 +553,7 @@ typedef FLAC__StreamEncoderReadStatus (*FLAC__StreamEncoderReadCallback)(const F * \retval FLAC__StreamEncoderWriteStatus * The callee's return status. */ -typedef FLAC__StreamEncoderWriteStatus (*FLAC__StreamEncoderWriteCallback)(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); +typedef FLAC__StreamEncoderWriteStatus (*FLAC__StreamEncoderWriteCallback)(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, uint32_t current_frame, void *client_data); /** Signature for the seek callback. * @@ -674,7 +674,7 @@ typedef void (*FLAC__StreamEncoderMetadataCallback)(const FLAC__StreamEncoder *e * \param client_data The callee's client data set through * FLAC__stream_encoder_init_*(). */ -typedef void (*FLAC__StreamEncoderProgressCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data); +typedef void (*FLAC__StreamEncoderProgressCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, uint32_t frames_written, uint32_t total_frames_estimate, void *client_data); /*********************************************************************** @@ -742,7 +742,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_ogg_serial_number(FLAC__StreamEncod */ FLAC_API FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder, FLAC__bool value); -/** Set the Subset flag. If \c true, +/** Set the Subset flag. If \c true, * the encoder will comply with the Subset and will check the * settings during FLAC__stream_encoder_init_*() to see if all settings * comply. If \c false, the settings may take advantage of the full @@ -770,7 +770,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncod * \retval FLAC__bool * \c false if the encoder is already initialized, else \c true. */ -FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, unsigned value); +FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, uint32_t value); /** Set the sample resolution of the input to be encoded. * @@ -786,7 +786,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encod * \retval FLAC__bool * \c false if the encoder is already initialized, else \c true. */ -FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, unsigned value); +FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, uint32_t value); /** Set the sample rate (in Hz) of the input to be encoded. * @@ -798,7 +798,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder * \retval FLAC__bool * \c false if the encoder is already initialized, else \c true. */ -FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, unsigned value); +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, uint32_t value); /** Set the compression level * @@ -842,15 +842,15 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *en * max residual partition order * rice parameter search dist * - * 0 false false tukey(0.5) 0 0 false false false 0 3 0 - * 1 true true tukey(0.5) 0 0 false false false 0 3 0 - * 2 true false tukey(0.5) 0 0 false false false 0 3 0 - * 3 false false tukey(0.5) 6 0 false false false 0 4 0 - * 4 true true tukey(0.5) 8 0 false false false 0 4 0 - * 5 true false tukey(0.5) 8 0 false false false 0 5 0 - * 6 true false tukey(0.5);partial_tukey(2) 8 0 false false false 0 6 0 - * 7 true false tukey(0.5);partial_tukey(2) 12 0 false false false 0 6 0 - * 8 true false tukey(0.5);partial_tukey(2);punchout_tukey(3) 12 0 false false false 0 6 0 + * 0 false false tukey(0.5) 0 0 false false false 0 3 0 + * 1 true true tukey(0.5) 0 0 false false false 0 3 0 + * 2 true false tukey(0.5) 0 0 false false false 0 3 0 + * 3 false false tukey(0.5) 6 0 false false false 0 4 0 + * 4 true true tukey(0.5) 8 0 false false false 0 4 0 + * 5 true false tukey(0.5) 8 0 false false false 0 5 0 + * 6 true false subdivide_tukey(2) 8 0 false false false 0 6 0 + * 7 true false subdivide_tukey(2) 12 0 false false false 0 6 0 + * 8 true false subdivide_tukey(3) 12 0 false false false 0 6 0 * * * \default \c 5 @@ -861,7 +861,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *en * \retval FLAC__bool * \c false if the encoder is already initialized, else \c true. */ -FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, unsigned value); +FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, uint32_t value); /** Set the blocksize to use while encoding. * @@ -876,13 +876,13 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncod * \retval FLAC__bool * \c false if the encoder is already initialized, else \c true. */ -FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, unsigned value); +FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, uint32_t value); /** Set to \c true to enable mid-side encoding on stereo input. The * number of channels must be 2 for this to have any effect. Set to * \c false to use only independent channel coding. * - * \default \c false + * \default \c true * \param encoder An encoder instance to set. * \param value Flag value (see above). * \assert @@ -920,7 +920,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamE * \c blackman, \c blackman_harris_4term_92db, \c connes, \c flattop, * \c gauss(STDDEV), \c hamming, \c hann, \c kaiser_bessel, \c nuttall, * \c rectangle, \c triangle, \c tukey(P), \c partial_tukey(n[/ov[/P]]), - * \c punchout_tukey(n[/ov[/P]]), \c welch. + * \c punchout_tukey(n[/ov[/P]]), \c subdivide_tukey(n[/P]), \c welch. * * For \c gauss(STDDEV), STDDEV specifies the standard deviation * (0 65535 if encoding to Ogg FLAC, else \c true. */ -FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks); +FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, uint32_t num_blocks); + +/** Set to \c true to make the encoder not output frames which contain + * only constant subframes. This is beneficial for streaming + * applications: very small frames can cause problems with buffering + * as bitrates can drop as low 1kbit/s for CDDA audio encoded within + * subset. The minimum bitrate for a FLAC file encoded with this + * function used is raised to 1bit/sample (i.e. 48kbit/s for 48kHz + * material). + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_limit_min_bitrate(FLAC__StreamEncoder *encoder, FLAC__bool value); /** Get the current encoder state. * @@ -1253,7 +1283,7 @@ FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__ * \assert * \code encoder != NULL \endcode */ -FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, unsigned *frame_number, unsigned *channel, unsigned *sample, FLAC__int32 *expected, FLAC__int32 *got); +FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, uint32_t *frame_number, uint32_t *channel, uint32_t *sample, FLAC__int32 *expected, FLAC__int32 *got); /** Get the "verify" flag. * @@ -1265,7 +1295,7 @@ FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__St */ FLAC_API FLAC__bool FLAC__stream_encoder_get_verify(const FLAC__StreamEncoder *encoder); -/** Get the Subset flag. * * \param encoder An encoder instance to query. * \assert @@ -1280,40 +1310,40 @@ FLAC_API FLAC__bool FLAC__stream_encoder_get_streamable_subset(const FLAC__Strea * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See FLAC__stream_encoder_set_channels(). */ -FLAC_API unsigned FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *encoder); +FLAC_API uint32_t FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *encoder); /** Get the input sample resolution setting. * * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See FLAC__stream_encoder_set_bits_per_sample(). */ -FLAC_API unsigned FLAC__stream_encoder_get_bits_per_sample(const FLAC__StreamEncoder *encoder); +FLAC_API uint32_t FLAC__stream_encoder_get_bits_per_sample(const FLAC__StreamEncoder *encoder); /** Get the input sample rate setting. * * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See FLAC__stream_encoder_set_sample_rate(). */ -FLAC_API unsigned FLAC__stream_encoder_get_sample_rate(const FLAC__StreamEncoder *encoder); +FLAC_API uint32_t FLAC__stream_encoder_get_sample_rate(const FLAC__StreamEncoder *encoder); /** Get the blocksize setting. * * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See FLAC__stream_encoder_set_blocksize(). */ -FLAC_API unsigned FLAC__stream_encoder_get_blocksize(const FLAC__StreamEncoder *encoder); +FLAC_API uint32_t FLAC__stream_encoder_get_blocksize(const FLAC__StreamEncoder *encoder); /** Get the "mid/side stereo coding" flag. * @@ -1340,20 +1370,20 @@ FLAC_API FLAC__bool FLAC__stream_encoder_get_loose_mid_side_stereo(const FLAC__S * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See FLAC__stream_encoder_set_max_lpc_order(). */ -FLAC_API unsigned FLAC__stream_encoder_get_max_lpc_order(const FLAC__StreamEncoder *encoder); +FLAC_API uint32_t FLAC__stream_encoder_get_max_lpc_order(const FLAC__StreamEncoder *encoder); /** Get the quantized linear predictor coefficient precision setting. * * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See FLAC__stream_encoder_set_qlp_coeff_precision(). */ -FLAC_API unsigned FLAC__stream_encoder_get_qlp_coeff_precision(const FLAC__StreamEncoder *encoder); +FLAC_API uint32_t FLAC__stream_encoder_get_qlp_coeff_precision(const FLAC__StreamEncoder *encoder); /** Get the qlp coefficient precision search flag. * @@ -1390,30 +1420,30 @@ FLAC_API FLAC__bool FLAC__stream_encoder_get_do_exhaustive_model_search(const FL * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See FLAC__stream_encoder_set_min_residual_partition_order(). */ -FLAC_API unsigned FLAC__stream_encoder_get_min_residual_partition_order(const FLAC__StreamEncoder *encoder); +FLAC_API uint32_t FLAC__stream_encoder_get_min_residual_partition_order(const FLAC__StreamEncoder *encoder); /** Get maximum residual partition order setting. * * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See FLAC__stream_encoder_set_max_residual_partition_order(). */ -FLAC_API unsigned FLAC__stream_encoder_get_max_residual_partition_order(const FLAC__StreamEncoder *encoder); +FLAC_API uint32_t FLAC__stream_encoder_get_max_residual_partition_order(const FLAC__StreamEncoder *encoder); /** Get the Rice parameter search distance setting. * * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval unsigned + * \retval uint32_t * See FLAC__stream_encoder_set_rice_parameter_search_dist(). */ -FLAC_API unsigned FLAC__stream_encoder_get_rice_parameter_search_dist(const FLAC__StreamEncoder *encoder); +FLAC_API uint32_t FLAC__stream_encoder_get_rice_parameter_search_dist(const FLAC__StreamEncoder *encoder); /** Get the previously set estimate of the total samples to be encoded. * The encoder merely mimics back the value given to @@ -1428,6 +1458,16 @@ FLAC_API unsigned FLAC__stream_encoder_get_rice_parameter_search_dist(const FLAC */ FLAC_API FLAC__uint64 FLAC__stream_encoder_get_total_samples_estimate(const FLAC__StreamEncoder *encoder); +/** Get the "limit_min_bitrate" flag. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * See FLAC__stream_encoder_set_limit_min_bitrate(). + */ +FLAC_API FLAC__bool FLAC__stream_encoder_get_limit_min_bitrate(const FLAC__StreamEncoder *encoder); + /** Initialize the encoder instance to encode native FLAC streams. * * This flavor of initialization sets up the encoder to encode to a @@ -1632,11 +1672,15 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_FILE(FLAC__ /** Initialize the encoder instance to encode native FLAC files. * * This flavor of initialization sets up the encoder to encode to a plain - * FLAC file. If POSIX fopen() semantics are not sufficient (for example, - * with Unicode filenames on Windows), you must use + * FLAC file. If POSIX fopen() semantics are not sufficient you must use * FLAC__stream_encoder_init_FILE(), or FLAC__stream_encoder_init_stream() * and provide callbacks for the I/O. * + * On Windows, filename must be a UTF-8 encoded filename, which libFLAC + * internally translates to an appropriate representation to use with + * _wfopen. On all other systems, filename is passed to fopen without + * any translation. + * * This function should be called after FLAC__stream_encoder_new() and * FLAC__stream_encoder_set_*() but before FLAC__stream_encoder_process() * or FLAC__stream_encoder_process_interleaved(). @@ -1664,11 +1708,15 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_file(FLAC__Stre /** Initialize the encoder instance to encode Ogg FLAC files. * * This flavor of initialization sets up the encoder to encode to a plain - * Ogg FLAC file. If POSIX fopen() semantics are not sufficient (for example, - * with Unicode filenames on Windows), you must use + * Ogg FLAC file. If POSIX fopen() semantics are not sufficient, you must use * FLAC__stream_encoder_init_ogg_FILE(), or FLAC__stream_encoder_init_ogg_stream() * and provide callbacks for the I/O. * + * On Windows, filename must be a UTF-8 encoded filename, which libFLAC + * internally translates to an appropriate representation to use with + * _wfopen. On all other systems, filename is passed to fopen without + * any translation. + * * This function should be called after FLAC__stream_encoder_new() and * FLAC__stream_encoder_set_*() but before FLAC__stream_encoder_process() * or FLAC__stream_encoder_process_interleaved(). @@ -1733,7 +1781,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder); * * For applications where channel order is important, channels must * follow the order as described in the - * frame header. + * frame header. * * \param encoder An initialized encoder instance in the OK state. * \param buffer An array of pointers to each channel's signal. @@ -1746,7 +1794,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder); * encoder state with FLAC__stream_encoder_get_state() to see what * went wrong. */ -FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], unsigned samples); +FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], uint32_t samples); /** Submit data for encoding. * This version allows you to supply the input data where the channels @@ -1762,7 +1810,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, c * * For applications where channel order is important, channels must * follow the order as described in the - * frame header. + * frame header. * * \param encoder An initialized encoder instance in the OK state. * \param buffer An array of channel-interleaved data (see above). @@ -1778,7 +1826,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, c * encoder state with FLAC__stream_encoder_get_state() to see what * went wrong. */ -FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], unsigned samples); +FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], uint32_t samples); /* \} */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/win_utf8_io.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/win_utf8_io.h deleted file mode 100644 index cfaa647d..00000000 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/win_utf8_io.h +++ /dev/null @@ -1,64 +0,0 @@ -/* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2013-2014 Xiph.Org Foundation - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of the Xiph.org Foundation nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifdef _WIN32 - -#ifndef flac__win_utf8_io_h -#define flac__win_utf8_io_h - -#ifdef __cplusplus -extern "C" { -#endif - -int get_utf8_argv(int *argc, char ***argv); - -int printf_utf8(const char *format, ...); -int fprintf_utf8(FILE *stream, const char *format, ...); -int vfprintf_utf8(FILE *stream, const char *format, va_list argptr); - -FILE *fopen_utf8(const char *filename, const char *mode); -int stat_utf8(const char *path, struct stat *buffer); -int _stat64_utf8(const char *path, struct __stat64 *buffer); -int chmod_utf8(const char *filename, int pmode); -int utime_utf8(const char *filename, struct utimbuf *times); -int unlink_utf8(const char *filename); -int rename_utf8(const char *oldname, const char *newname); -size_t strlen_utf8(const char *str); -int win_get_console_width(void); -int print_console(FILE *stream, const wchar_t *text, size_t len); -HANDLE WINAPI CreateFile_utf8(const char *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif -#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp index a6567d00..c67a9913 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -68,25 +67,25 @@ namespace AiffFileHelpers Loop sustainLoop; Loop releaseLoop; - void copyTo (StringPairArray& values) const + void copyTo (std::map& values) const { - values.set ("MidiUnityNote", String (baseNote)); - values.set ("Detune", String (detune)); - - values.set ("LowNote", String (lowNote)); - values.set ("HighNote", String (highNote)); - values.set ("LowVelocity", String (lowVelocity)); - values.set ("HighVelocity", String (highVelocity)); - - values.set ("Gain", String ((int16) ByteOrder::swapIfLittleEndian ((uint16) gain))); - - values.set ("NumSampleLoops", String (2)); // always 2 with AIFF, WAV can have more - values.set ("Loop0Type", String (ByteOrder::swapIfLittleEndian (sustainLoop.type))); - values.set ("Loop0StartIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.startIdentifier))); - values.set ("Loop0EndIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.endIdentifier))); - values.set ("Loop1Type", String (ByteOrder::swapIfLittleEndian (releaseLoop.type))); - values.set ("Loop1StartIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.startIdentifier))); - values.set ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier))); + values.emplace ("MidiUnityNote", String (baseNote)); + values.emplace ("Detune", String (detune)); + + values.emplace ("LowNote", String (lowNote)); + values.emplace ("HighNote", String (highNote)); + values.emplace ("LowVelocity", String (lowVelocity)); + values.emplace ("HighVelocity", String (highVelocity)); + + values.emplace ("Gain", String ((int16) ByteOrder::swapIfLittleEndian ((uint16) gain))); + + values.emplace ("NumSampleLoops", String (2)); // always 2 with AIFF, WAV can have more + values.emplace ("Loop0Type", String (ByteOrder::swapIfLittleEndian (sustainLoop.type))); + values.emplace ("Loop0StartIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.startIdentifier))); + values.emplace ("Loop0EndIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.endIdentifier))); + values.emplace ("Loop1Type", String (ByteOrder::swapIfLittleEndian (releaseLoop.type))); + values.emplace ("Loop1StartIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.startIdentifier))); + values.emplace ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier))); } static uint16 getValue16 (const StringPairArray& values, const char* name, const char* def) @@ -150,7 +149,7 @@ namespace AiffFileHelpers input.read (unknown, sizeof (unknown)); } - void addToMetadata (StringPairArray& metadata) const + void addToMetadata (std::map& metadata) const { const bool rootNoteSet = rootNote != 0; @@ -158,29 +157,32 @@ namespace AiffFileHelpers setBoolFlag (metadata, AiffAudioFormat::appleRootSet, rootNoteSet); if (rootNoteSet) - metadata.set (AiffAudioFormat::appleRootNote, String (rootNote)); + metadata.emplace (AiffAudioFormat::appleRootNote, String (rootNote)); - metadata.set (AiffAudioFormat::appleBeats, String (numBeats)); - metadata.set (AiffAudioFormat::appleDenominator, String (timeSigDen)); - metadata.set (AiffAudioFormat::appleNumerator, String (timeSigNum)); + metadata.emplace (AiffAudioFormat::appleBeats, String (numBeats)); + metadata.emplace (AiffAudioFormat::appleDenominator, String (timeSigDen)); + metadata.emplace (AiffAudioFormat::appleNumerator, String (timeSigNum)); const char* keyString = nullptr; switch (key) { - case minor: keyString = "minor"; break; - case major: keyString = "major"; break; - case neither: keyString = "neither"; break; - case both: keyString = "both"; break; + case minor: keyString = "minor"; break; + case major: keyString = "major"; break; + case neither: keyString = "neither"; break; + case both: keyString = "both"; break; + default: break; } if (keyString != nullptr) - metadata.set (AiffAudioFormat::appleKey, keyString); + metadata.emplace (AiffAudioFormat::appleKey, keyString); } - void setBoolFlag (StringPairArray& values, const char* name, bool shouldBeSet) const + void setBoolFlag (std::map& values, + const char* name, + bool shouldBeSet) const { - values.set (name, shouldBeSet ? "1" : "0"); + values.emplace (name, shouldBeSet ? "1" : "0"); } uint32 flags; @@ -334,7 +336,7 @@ namespace AiffFileHelpers out.writeIntBigEndian (offset); auto labelLength = jmin ((size_t) 254, label.getNumBytesAsUTF8()); // seems to need null terminator even though it's a pstring - out.writeByte ((char) labelLength + 1); + out.writeByte (static_cast (labelLength + 1)); out.write (label.toUTF8(), labelLength); out.writeByte (0); @@ -367,7 +369,7 @@ namespace AiffFileHelpers auto comment = values.getValue (prefix + "Text", String()); auto commentLength = jmin (comment.getNumBytesAsUTF8(), (size_t) 65534); - out.writeShortBigEndian ((short) commentLength + 1); + out.writeShortBigEndian (static_cast (commentLength + 1)); out.write (comment.toUTF8(), commentLength); out.writeByte (0); @@ -380,7 +382,7 @@ namespace AiffFileHelpers } //============================================================================== -class AiffAudioFormatReader : public AudioFormatReader +class AiffAudioFormatReader final : public AudioFormatReader { public: AiffAudioFormatReader (InputStream* in) @@ -388,6 +390,17 @@ class AiffAudioFormatReader : public AudioFormatReader { using namespace AiffFileHelpers; + std::map metadataValuesMap; + + for (int i = 0; i != metadataValues.size(); ++i) + { + metadataValuesMap.emplace (metadataValues.getAllKeys().getReference (i), + metadataValues.getAllValues().getReference (i)); + } + + // If this fails, there were duplicate keys in the metadata + jassert ((size_t) metadataValuesMap.size() == (size_t) metadataValues.size()); + if (input->readInt() == chunkName ("FORM")) { auto len = input->readIntBigEndian(); @@ -479,8 +492,8 @@ class AiffAudioFormatReader : public AudioFormatReader auto numCues = (uint16) input->readShortBigEndian(); // these two are always the same for AIFF-read files - metadataValues.set ("NumCuePoints", String (numCues)); - metadataValues.set ("NumCueLabels", String (numCues)); + metadataValuesMap.emplace ("NumCuePoints", String (numCues)); + metadataValuesMap.emplace ("NumCueLabels", String (numCues)); for (uint16 i = 0; i < numCues; ++i) { @@ -497,18 +510,18 @@ class AiffAudioFormatReader : public AudioFormatReader input->readByte(); auto prefixCue = "Cue" + String (i); - metadataValues.set (prefixCue + "Identifier", String (identifier)); - metadataValues.set (prefixCue + "Offset", String (offset)); + metadataValuesMap.emplace (prefixCue + "Identifier", String (identifier)); + metadataValuesMap.emplace (prefixCue + "Offset", String (offset)); auto prefixLabel = "CueLabel" + String (i); - metadataValues.set (prefixLabel + "Identifier", String (identifier)); - metadataValues.set (prefixLabel + "Text", textBlock.toString()); + metadataValuesMap.emplace (prefixLabel + "Identifier", String (identifier)); + metadataValuesMap.emplace (prefixLabel + "Text", textBlock.toString()); } } else if (type == chunkName ("COMT")) { auto numNotes = (uint16) input->readShortBigEndian(); - metadataValues.set ("NumCueNotes", String (numNotes)); + metadataValuesMap.emplace ("NumCueNotes", String (numNotes)); for (uint16 i = 0; i < numNotes; ++i) { @@ -520,9 +533,9 @@ class AiffAudioFormatReader : public AudioFormatReader input->readIntoMemoryBlock (textBlock, stringLength + (stringLength & 1)); auto prefix = "CueNote" + String (i); - metadataValues.set (prefix + "TimeStamp", String (timestamp)); - metadataValues.set (prefix + "Identifier", String (identifier)); - metadataValues.set (prefix + "Text", textBlock.toString()); + metadataValuesMap.emplace (prefix + "TimeStamp", String (timestamp)); + metadataValuesMap.emplace (prefix + "Identifier", String (identifier)); + metadataValuesMap.emplace (prefix + "Text", textBlock.toString()); } } else if (type == chunkName ("INST")) @@ -530,16 +543,16 @@ class AiffAudioFormatReader : public AudioFormatReader HeapBlock inst; inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1); input->read (inst, (int) length); - inst->copyTo (metadataValues); + inst->copyTo (metadataValuesMap); } else if (type == chunkName ("basc")) { - AiffFileHelpers::BASCChunk (*input).addToMetadata (metadataValues); + AiffFileHelpers::BASCChunk (*input).addToMetadata (metadataValuesMap); } else if (type == chunkName ("cate")) { - metadataValues.set (AiffAudioFormat::appleTag, - AiffFileHelpers::CATEChunk::read (*input, length)); + metadataValuesMap.emplace (AiffAudioFormat::appleTag, + AiffFileHelpers::CATEChunk::read (*input, length)); } else if ((hasGotVer && hasGotData && hasGotType) || chunkEnd < input->getPosition() @@ -553,12 +566,14 @@ class AiffAudioFormatReader : public AudioFormatReader } } - if (metadataValues.size() > 0) - metadataValues.set ("MetaDataSource", "AIFF"); + if (metadataValuesMap.size() > 0) + metadataValuesMap.emplace ("MetaDataSource", "AIFF"); + + metadataValues.addMap (metadataValuesMap); } //============================================================================== - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, @@ -625,7 +640,7 @@ class AiffAudioFormatReader : public AudioFormatReader }; //============================================================================== -class AiffAudioFormatWriter : public AudioFormatWriter +class AiffAudioFormatWriter final : public AudioFormatWriter { public: AiffAudioFormatWriter (OutputStream* out, double rate, @@ -706,16 +721,15 @@ class AiffAudioFormatWriter : public AudioFormatWriter { using namespace AiffFileHelpers; - const bool couldSeekOk = output->setPosition (headerPosition); - ignoreUnused (couldSeekOk); + [[maybe_unused]] const bool couldSeekOk = output->setPosition (headerPosition); // if this fails, you've given it an output stream that can't seek! It needs // to be able to seek back to write the header jassert (couldSeekOk); - auto headerLen = (int) (54 + (markChunk.getSize() > 0 ? markChunk.getSize() + 8 : 0) - + (comtChunk.getSize() > 0 ? comtChunk.getSize() + 8 : 0) - + (instChunk.getSize() > 0 ? instChunk.getSize() + 8 : 0)); + auto headerLen = (int) (54 + (markChunk.isEmpty() ? 0 : markChunk.getSize() + 8) + + (comtChunk.isEmpty() ? 0 : comtChunk.getSize() + 8) + + (instChunk.isEmpty() ? 0 : instChunk.getSize() + 8)); auto audioBytes = (int) (lengthInSamples * ((bitsPerSample * numChannels) / 8)); audioBytes += (audioBytes & 1); @@ -771,21 +785,21 @@ class AiffAudioFormatWriter : public AudioFormatWriter output->write (sampleRateBytes, 10); - if (markChunk.getSize() > 0) + if (! markChunk.isEmpty()) { output->writeInt (chunkName ("MARK")); output->writeIntBigEndian ((int) markChunk.getSize()); *output << markChunk; } - if (comtChunk.getSize() > 0) + if (! comtChunk.isEmpty()) { output->writeInt (chunkName ("COMT")); output->writeIntBigEndian ((int) comtChunk.getSize()); *output << comtChunk; } - if (instChunk.getSize() > 0) + if (! instChunk.isEmpty()) { output->writeInt (chunkName ("INST")); output->writeIntBigEndian ((int) instChunk.getSize()); @@ -804,7 +818,7 @@ class AiffAudioFormatWriter : public AudioFormatWriter }; //============================================================================== -class MemoryMappedAiffReader : public MemoryMappedAudioFormatReader +class MemoryMappedAiffReader final : public MemoryMappedAudioFormatReader { public: MemoryMappedAiffReader (const File& f, const AiffAudioFormatReader& reader) @@ -814,12 +828,15 @@ class MemoryMappedAiffReader : public MemoryMappedAudioFormatReader { } - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, lengthInSamples); + if (numSamples <= 0) + return true; + if (map == nullptr || ! mappedSection.contains (Range (startSampleInFile, startSampleInFile + numSamples))) { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. @@ -975,7 +992,7 @@ AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, MemoryMappedAudioFormatReader* AiffAudioFormat::createMemoryMappedReader (const File& file) { - return createMemoryMappedReader (file.createInputStream()); + return createMemoryMappedReader (file.createInputStream().release()); } MemoryMappedAudioFormatReader* AiffAudioFormat::createMemoryMappedReader (FileInputStream* fin) diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h index e4b2fa0f..bae68b74 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -89,7 +88,7 @@ class JUCE_API AiffAudioFormat : public AudioFormat using AudioFormat::createWriterFor; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AiffAudioFormat) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormat) }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp index 8be193c4..ef8ef2d2 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -26,7 +25,8 @@ #if JUCE_MAC || JUCE_IOS -#include "../../juce_audio_basics/native/juce_mac_CoreAudioLayouts.h" +#include +#include namespace juce { @@ -36,23 +36,68 @@ namespace { const char* const coreAudioFormatName = "CoreAudio supported file"; - StringArray findFileExtensionsForCoreAudioCodecs() + StringArray getStringInfo (AudioFilePropertyID property, UInt32 size, void* data) { + CFObjectHolder extensions; + UInt32 sizeOfArray = sizeof (extensions.object); + + const auto err = AudioFileGetGlobalInfo (property, + size, + data, + &sizeOfArray, + &extensions.object); + + if (err != noErr) + return {}; + + const auto numValues = CFArrayGetCount (extensions.object); + StringArray extensionsArray; - CFArrayRef extensions = nullptr; - UInt32 sizeOfArray = sizeof (extensions); - if (AudioFileGetGlobalInfo (kAudioFileGlobalInfo_AllExtensions, 0, nullptr, &sizeOfArray, &extensions) == noErr) - { - auto numValues = CFArrayGetCount (extensions); + for (CFIndex i = 0; i < numValues; ++i) + extensionsArray.add ("." + String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (extensions.object, i))); - for (CFIndex i = 0; i < numValues; ++i) - extensionsArray.add ("." + String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (extensions, i))); + return extensionsArray; + } + + StringArray findFileExtensionsForCoreAudioCodec (AudioFileTypeID type) + { + return getStringInfo (kAudioFileGlobalInfo_ExtensionsForType, sizeof (AudioFileTypeID), &type); + } - CFRelease (extensions); + StringArray findFileExtensionsForCoreAudioCodecs() + { + return getStringInfo (kAudioFileGlobalInfo_AllExtensions, 0, nullptr); + } + + static AudioFileTypeID toAudioFileTypeID (CoreAudioFormat::StreamKind kind) + { + using StreamKind = CoreAudioFormat::StreamKind; + + switch (kind) + { + case StreamKind::kAiff: return kAudioFileAIFFType; + case StreamKind::kAifc: return kAudioFileAIFCType; + case StreamKind::kWave: return kAudioFileWAVEType; + case StreamKind::kSoundDesigner2: return kAudioFileSoundDesigner2Type; + case StreamKind::kNext: return kAudioFileNextType; + case StreamKind::kMp3: return kAudioFileMP3Type; + case StreamKind::kMp2: return kAudioFileMP2Type; + case StreamKind::kMp1: return kAudioFileMP1Type; + case StreamKind::kAc3: return kAudioFileAC3Type; + case StreamKind::kAacAdts: return kAudioFileAAC_ADTSType; + case StreamKind::kMpeg4: return kAudioFileMPEG4Type; + case StreamKind::kM4a: return kAudioFileM4AType; + case StreamKind::kM4b: return kAudioFileM4BType; + case StreamKind::kCaf: return kAudioFileCAFType; + case StreamKind::k3gp: return kAudioFile3GPType; + case StreamKind::k3gp2: return kAudioFile3GP2Type; + case StreamKind::kAmr: return kAudioFileAMRType; + + case StreamKind::kNone: break; } - return extensionsArray; + return {}; } } @@ -222,7 +267,7 @@ struct CoreAudioFormatMetatdata for (int i = 0; i < numTimeSigEvents; ++i) { int numerator, denominator; - timeSigEvents.getEventPointer(i)->message.getTimeSignatureInfo (numerator, denominator); + timeSigEvents.getEventPointer (i)->message.getTimeSignatureInfo (numerator, denominator); String timeSigString; timeSigString << numerator << '/' << denominator; @@ -339,10 +384,13 @@ struct CoreAudioFormatMetatdata }; //============================================================================== -class CoreAudioReader : public AudioFormatReader +class CoreAudioReader final : public AudioFormatReader { public: - CoreAudioReader (InputStream* inp) : AudioFormatReader (inp, coreAudioFormatName) + using StreamKind = CoreAudioFormat::StreamKind; + + CoreAudioReader (InputStream* inp, StreamKind streamKind) + : AudioFormatReader (inp, coreAudioFormatName) { usesFloatingPointData = true; bitsPerSample = 32; @@ -355,7 +403,7 @@ class CoreAudioReader : public AudioFormatReader nullptr, // write needs to be null to avoid permissions errors &getSizeCallback, nullptr, // setSize needs to be null to avoid permissions errors - 0, // AudioFileTypeID inFileTypeHint + toAudioFileTypeID (streamKind), &audioFileID); if (status == noErr) { @@ -454,7 +502,7 @@ class CoreAudioReader : public AudioFormatReader } //============================================================================== - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, @@ -563,11 +611,18 @@ class CoreAudioReader : public AudioFormatReader //============================================================================== CoreAudioFormat::CoreAudioFormat() - : AudioFormat (coreAudioFormatName, findFileExtensionsForCoreAudioCodecs()) + : AudioFormat (coreAudioFormatName, findFileExtensionsForCoreAudioCodecs()), + streamKind (StreamKind::kNone) +{ +} + +CoreAudioFormat::CoreAudioFormat (StreamKind kind) + : AudioFormat (coreAudioFormatName, findFileExtensionsForCoreAudioCodec (toAudioFileTypeID (kind))), + streamKind (kind) { } -CoreAudioFormat::~CoreAudioFormat() {} +CoreAudioFormat::~CoreAudioFormat() = default; Array CoreAudioFormat::getPossibleSampleRates() { return {}; } Array CoreAudioFormat::getPossibleBitDepths() { return {}; } @@ -579,7 +634,7 @@ bool CoreAudioFormat::canDoMono() { return true; } AudioFormatReader* CoreAudioFormat::createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails) { - std::unique_ptr r (new CoreAudioReader (sourceStream)); + std::unique_ptr r (new CoreAudioReader (sourceStream, streamKind)); if (r->ok) return r.release(); @@ -609,7 +664,7 @@ AudioFormatWriter* CoreAudioFormat::createWriterFor (OutputStream*, #define DEFINE_CHANNEL_LAYOUT_DFL_ENTRY(x) CoreAudioChannelLayoutTag { x, #x, AudioChannelSet() } #define DEFINE_CHANNEL_LAYOUT_TAG_ENTRY(x, y) CoreAudioChannelLayoutTag { x, #x, y } -class CoreAudioLayoutsUnitTest : public UnitTest +class CoreAudioLayoutsUnitTest final : public UnitTest { public: CoreAudioLayoutsUnitTest() diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h index 1e84bc8e..04ccb835 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -43,10 +42,38 @@ namespace juce class JUCE_API CoreAudioFormat : public AudioFormat { public: + /** File type hints. */ + enum class StreamKind + { + kNone, + kAiff, + kAifc, + kWave, + kSoundDesigner2, + kNext, + kMp3, + kMp2, + kMp1, + kAc3, + kAacAdts, + kMpeg4, + kM4a, + kM4b, + kCaf, + k3gp, + k3gp2, + kAmr, + }; + //============================================================================== /** Creates a format object. */ CoreAudioFormat(); + /** Creates a format object and provides a hint as to the format of data + to be read or written. + */ + explicit CoreAudioFormat (StreamKind); + /** Destructor. */ ~CoreAudioFormat() override; @@ -79,6 +106,8 @@ class JUCE_API CoreAudioFormat : public AudioFormat using AudioFormat::createWriterFor; private: + StreamKind streamKind = StreamKind::kNone; + 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 a3168721..6535b700 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -93,14 +92,13 @@ namespace FlacNamespace { #if JUCE_INCLUDE_FLAC_CODE || ! defined (JUCE_INCLUDE_FLAC_CODE) - #undef VERSION - #define VERSION "1.3.1" + #undef PACKAGE_VERSION + #define PACKAGE_VERSION "1.4.3" #define FLAC__NO_DLL 1 - #if JUCE_MSVC - #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312 4505 4365 4005 4334 181 111) - #else + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312 4505 4365 4005 4334 181 111 6340 6308 6297 6001 6320) + #if ! JUCE_MSVC #define HAVE_LROUND 1 #endif @@ -112,22 +110,17 @@ namespace FlacNamespace #define SIZE_MAX 0xffffffff #endif - #if JUCE_CLANG - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wconversion" - #pragma clang diagnostic ignored "-Wshadow" - #pragma clang diagnostic ignored "-Wdeprecated-register" - #if __has_warning("-Wzero-as-null-pointer-constant") - #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" - #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 + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wconversion", + "-Wdeprecated-register", + "-Wfloat-equal", + "-Wimplicit-fallthrough", + "-Wlanguage-extension-token", + "-Wredundant-decls", + "-Wshadow", + "-Wsign-conversion", + "-Wswitch-default", + "-Wswitch-enum", + "-Wzero-as-null-pointer-constant") #if JUCE_INTEL #if JUCE_32BIT @@ -139,11 +132,26 @@ namespace FlacNamespace #define FLAC__HAS_X86INTRIN 1 #endif - #undef __STDC_LIMIT_MACROS - #define __STDC_LIMIT_MACROS 1 + #if JUCE_ARM && JUCE_64BIT + #define FLAC__CPU_ARM64 1 + + #if JUCE_USE_ARM_NEON + #define FLAC__HAS_NEONINTRIN 1 + #define FLAC__HAS_A64NEONINTRIN 1 + #endif + #endif + #define flac_max jmax #define flac_min jmin - #undef DEBUG // (some flac code dumps debug trace if the app defines this macro) + + #pragma push_macro ("DEBUG") + #pragma push_macro ("NDEBUG") + #undef DEBUG // (some flac code dumps debug trace if the app defines this macro) + + #ifndef NDEBUG + #define NDEBUG // (some flac code prints cpu info if this isn't defined) + #endif + #include "flac/all.h" #include "flac/libFLAC/bitmath.c" #include "flac/libFLAC/bitreader.c" @@ -154,24 +162,25 @@ namespace FlacNamespace #include "flac/libFLAC/float.c" #include "flac/libFLAC/format.c" #include "flac/libFLAC/lpc_flac.c" + #include "flac/libFLAC/lpc_intrin_neon.c" #include "flac/libFLAC/md5.c" #include "flac/libFLAC/memory.c" #include "flac/libFLAC/stream_decoder.c" #include "flac/libFLAC/stream_encoder.c" #include "flac/libFLAC/stream_encoder_framing.c" #include "flac/libFLAC/window_flac.c" - #undef VERSION + + #pragma pop_macro ("DEBUG") + #pragma pop_macro ("NDEBUG") + + #undef PACKAGE_VERSION + + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_WARNINGS_MSVC + #else #include #endif - - #if JUCE_CLANG - #pragma clang diagnostic pop - #endif - - #if JUCE_GCC - #pragma GCC diagnostic pop - #endif } #undef max @@ -180,9 +189,11 @@ namespace FlacNamespace //============================================================================== static const char* const flacFormatName = "FLAC file"; +template +auto emptyRange (Item item) { return Range::emptyRange (item); } //============================================================================== -class FlacReader : public AudioFormatReader +class FlacReader final : public AudioFormatReader { public: FlacReader (InputStream* in) : AudioFormatReader (in, flacFormatName) @@ -230,66 +241,62 @@ class FlacReader : public AudioFormatReader reservoir.setSize ((int) numChannels, 2 * (int) info.max_blocksize, false, false, true); } - // returns the number of samples read - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { if (! ok) return false; - while (numSamples > 0) - { - if (startSampleInFile >= reservoirStart - && startSampleInFile < reservoirStart + samplesInReservoir) - { - auto num = (int) jmin ((int64) numSamples, - reservoirStart + samplesInReservoir - startSampleInFile); - - jassert (num > 0); + const auto getBufferedRange = [this] { return bufferedRange; }; - for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) - if (destSamples[i] != nullptr) - memcpy (destSamples[i] + startOffsetInDestBuffer, - reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), - (size_t) num * sizeof (int)); + const auto readFromReservoir = [this, &destSamples, &numDestChannels, &startOffsetInDestBuffer, &startSampleInFile] (const Range rangeToRead) + { + const auto bufferIndices = rangeToRead - bufferedRange.getStart(); + const auto writePos = (int64) startOffsetInDestBuffer + (rangeToRead.getStart() - startSampleInFile); - startOffsetInDestBuffer += num; - startSampleInFile += num; - numSamples -= num; - } - else + for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) { - if (startSampleInFile >= lengthInSamples) - { - samplesInReservoir = 0; - } - else if (startSampleInFile < reservoirStart - || startSampleInFile > reservoirStart + jmax (samplesInReservoir, 511)) - { - // had some problems with flac crashing if the read pos is aligned more - // accurately than this. Probably fixed in newer versions of the library, though. - reservoirStart = (int) (startSampleInFile & ~511); - samplesInReservoir = 0; - FLAC__stream_decoder_seek_absolute (decoder, (FlacNamespace::FLAC__uint64) reservoirStart); - } - else + if (destSamples[i] != nullptr) { - reservoirStart += samplesInReservoir; - samplesInReservoir = 0; - FLAC__stream_decoder_process_single (decoder); + memcpy (destSamples[i] + writePos, + reservoir.getReadPointer (i) + bufferIndices.getStart(), + (size_t) bufferIndices.getLength() * sizeof (int)); } - - if (samplesInReservoir == 0) - break; } - } + }; - if (numSamples > 0) + const auto fillReservoir = [this] (const int64 requestedStart) { + if (requestedStart >= lengthInSamples) + { + bufferedRange = emptyRange (requestedStart); + return; + } + + if (requestedStart < bufferedRange.getStart() + || jmax (bufferedRange.getEnd(), bufferedRange.getStart() + (int64) 511) < requestedStart) + { + // had some problems with flac crashing if the read pos is aligned more + // accurately than this. Probably fixed in newer versions of the library, though. + bufferedRange = emptyRange (requestedStart & ~511); + FLAC__stream_decoder_seek_absolute (decoder, (FlacNamespace::FLAC__uint64) bufferedRange.getStart()); + return; + } + + bufferedRange = emptyRange (bufferedRange.getEnd()); + FLAC__stream_decoder_process_single (decoder); + }; + + const auto remainingSamples = Reservoir::doBufferedRead (Range { startSampleInFile, startSampleInFile + numSamples }, + getBufferedRange, + readFromReservoir, + fillReservoir); + + if (! remainingSamples.isEmpty()) for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) - zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int)); - } + zeromem (destSamples[i] + startOffsetInDestBuffer + (remainingSamples.getStart() - startSampleInFile), + (size_t) remainingSamples.getLength() * sizeof (int)); return true; } @@ -317,14 +324,14 @@ class FlacReader : public AudioFormatReader if (src != nullptr) { - auto* dest = reinterpret_cast (reservoir.getWritePointer(i)); + auto* dest = reinterpret_cast (reservoir.getWritePointer (i)); for (int j = 0; j < numSamples; ++j) dest[j] = src[j] << bitsToShift; } } - samplesInReservoir = numSamples; + bufferedRange.setLength (numSamples); } } @@ -381,7 +388,7 @@ class FlacReader : public AudioFormatReader private: FlacNamespace::FLAC__StreamDecoder* decoder; AudioBuffer reservoir; - int reservoirStart = 0, samplesInReservoir = 0; + Range bufferedRange; bool ok = false, scanningForLength = false; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacReader) @@ -389,7 +396,7 @@ class FlacReader : public AudioFormatReader //============================================================================== -class FlacWriter : public AudioFormatWriter +class FlacWriter final : public AudioFormatWriter { public: FlacWriter (OutputStream* out, double rate, uint32 numChans, uint32 bits, int qualityOptionIndex) @@ -500,8 +507,7 @@ class FlacWriter : public AudioFormatWriter packUint32 ((FLAC__uint32) info.total_samples, buffer + 14, 4); memcpy (buffer + 18, info.md5sum, 16); - const bool seekOk = output->setPosition (streamStartPos + 4); - ignoreUnused (seekOk); + [[maybe_unused]] const bool seekOk = output->setPosition (streamStartPos + 4); // if this fails, you've given it an output stream that can't seek! It needs // to be able to seek back to write the header diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h index 68bf7cde..851c90b5 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -27,7 +26,7 @@ namespace juce { -#if JUCE_USE_FLAC || defined (DOXYGEN) +#if JUCE_USE_FLAC || DOXYGEN //============================================================================== /** diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp index ee395c93..1184a21b 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -29,23 +28,23 @@ namespace juce #if JUCE_USE_LAME_AUDIO_FORMAT -class LAMEEncoderAudioFormat::Writer : public AudioFormatWriter +class LAMEEncoderAudioFormat::Writer final : public AudioFormatWriter { public: Writer (OutputStream* destStream, const String& formatName, const File& appFile, int vbr, int cbr, - double sampleRate, unsigned int numberOfChannels, - int bitsPerSample, const StringPairArray& metadata) - : AudioFormatWriter (destStream, formatName, sampleRate, - numberOfChannels, (unsigned int) bitsPerSample), + double sampleRateIn, unsigned int numberOfChannels, + int bitsPerSampleIn, const StringPairArray& metadata) + : AudioFormatWriter (destStream, formatName, sampleRateIn, + numberOfChannels, (unsigned int) bitsPerSampleIn), vbrLevel (vbr), cbrBitrate (cbr) { WavAudioFormat wavFormat; - if (auto* out = tempWav.getFile().createOutputStream()) + if (auto out = tempWav.getFile().createOutputStream()) { - writer.reset (wavFormat.createWriterFor (out, sampleRate, numChannels, - bitsPerSample, metadata, 0)); + writer.reset (wavFormat.createWriterFor (out.release(), sampleRateIn, numChannels, + bitsPerSampleIn, metadata, 0)); args.add (appFile.getFullPathName()); @@ -113,8 +112,8 @@ class LAMEEncoderAudioFormat::Writer : public AudioFormatWriter if (cp.start (processArgs)) { - auto childOutput = cp.readAllProcessOutput(); - DBG (childOutput); ignoreUnused (childOutput); + [[maybe_unused]] auto childOutput = cp.readAllProcessOutput(); + DBG (childOutput); cp.waitForProcessToFinish (10000); return tempMP3.getFile().getSize() > 0; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h index 935b71db..2129a5a1 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -27,7 +26,7 @@ namespace juce { -#if JUCE_USE_LAME_AUDIO_FORMAT || defined (DOXYGEN) +#if JUCE_USE_LAME_AUDIO_FORMAT || DOXYGEN //============================================================================== /** diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp index cafa8d90..56e73b79 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -30,8 +29,8 @@ namespace juce /* IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile this MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, - you are agreeing that ROLI Ltd. is in no way responsible for any patent, copyright, - or other legal issues that you may suffer as a result. + you are agreeing that Raw Material Software Limited is in no way responsible for any patent, + copyright, or other legal issues that you may suffer as a result. The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party intellectual property. If you wish to use it, please seek your own independent advice about the @@ -43,9 +42,12 @@ namespace juce namespace MP3Decoder { -struct AllocationTable { int16 bits, d; }; +struct AllocationTable +{ + int16 bits, d; +}; -const struct AllocationTable allocTable0[] = +constexpr AllocationTable allocTable0[] = { {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {16, -32767}, @@ -67,7 +69,7 @@ const struct AllocationTable allocTable0[] = {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767} }; -const struct AllocationTable allocTable1[] = +constexpr AllocationTable allocTable1[] = { {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {16, -32767}, {4, 0}, {5, 3}, {3, -3}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {16, -32767}, @@ -90,7 +92,7 @@ const struct AllocationTable allocTable1[] = {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767}, {2, 0}, {5, 3}, {7, 5}, {16, -32767} }; -const struct AllocationTable allocTable2[] = +constexpr AllocationTable allocTable2[] = { {4, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {4, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, @@ -99,7 +101,7 @@ const struct AllocationTable allocTable2[] = {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63} }; -const struct AllocationTable allocTable3[] = +constexpr AllocationTable allocTable3[] = { {4, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, {4, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {15, -16383}, @@ -110,7 +112,7 @@ const struct AllocationTable allocTable3[] = {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {3, 0}, {5, 3}, {7, 5}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63} }; -const struct AllocationTable allocTable4[] = +constexpr AllocationTable allocTable4[] = { {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {4, 0}, {5, 3}, {7, 5}, {3, -3}, {10, 9}, {4, -7}, {5, -15}, {6, -31}, {7, -63}, {8, -127}, {9, -255}, {10, -511}, {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, @@ -135,7 +137,7 @@ struct BandInfoStruct int16 shortDiff[13]; }; -const BandInfoStruct bandInfo[9] = +constexpr BandInfoStruct bandInfo[9] = { { {0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, 74, 90, 110, 134, 162, 196, 238, 288, 342, 418, 576}, {4, 4, 4, 4, 4, 4, 6, 6, 8, 8, 10, 12, 16, 20, 24, 28, 34, 42, 50, 54, 76, 158}, @@ -183,7 +185,7 @@ const BandInfoStruct bandInfo[9] = {8, 8, 8, 12, 16, 20, 24, 28, 36, 2, 2, 2, 26} } }; -const double decodeWindow[] = +constexpr double decodeWindow[] = { 0.000000000, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000030518, -0.000030518, -0.000030518, -0.000030518, -0.000045776, -0.000045776, -0.000061035, -0.000061035, -0.000076294, @@ -220,32 +222,32 @@ const double decodeWindow[] = 1.144989014 }; -const int16 huffmanTab0[] = { 0 }; -const int16 huffmanTab1[] = { -5,-3,-1,17,1,16,0 }; -const int16 huffmanTab2[] = { -15,-11,-9,-5,-3,-1,34,2,18,-1,33,32,17,-1,1,16,0 }; -const int16 huffmanTab3[] = { -13,-11,-9,-5,-3,-1,34,2,18,-1,33,32,16,17,-1,1,0 }; -const int16 huffmanTab5[] = { -29,-25,-23,-15,-7,-5,-3,-1,51,35,50,49,-3,-1,19,3,-1,48,34,-3,-1,18,33,-1,2,32,17,-1,1,16,0 }; -const int16 huffmanTab6[] = { -25,-19,-13,-9,-5,-3,-1,51,3,35,-1,50,48,-1,19,49,-3,-1,34,2,18,-3,-1,33,32,1,-1,17,-1,16,0 }; +constexpr int16 huffmanTab0[] = { 0 }; +constexpr int16 huffmanTab1[] = { -5,-3,-1,17,1,16,0 }; +constexpr int16 huffmanTab2[] = { -15,-11,-9,-5,-3,-1,34,2,18,-1,33,32,17,-1,1,16,0 }; +constexpr int16 huffmanTab3[] = { -13,-11,-9,-5,-3,-1,34,2,18,-1,33,32,16,17,-1,1,0 }; +constexpr int16 huffmanTab5[] = { -29,-25,-23,-15,-7,-5,-3,-1,51,35,50,49,-3,-1,19,3,-1,48,34,-3,-1,18,33,-1,2,32,17,-1,1,16,0 }; +constexpr int16 huffmanTab6[] = { -25,-19,-13,-9,-5,-3,-1,51,3,35,-1,50,48,-1,19,49,-3,-1,34,2,18,-3,-1,33,32,1,-1,17,-1,16,0 }; -const int16 huffmanTab7[] = +constexpr int16 huffmanTab7[] = { -69,-65,-57,-39,-29,-17,-11,-7,-3,-1,85,69,-1,84,83,-1,53,68,-3,-1,37,82,21,-5,-1,81,-1,5,52,-1,80,-1,67,51, -5,-3,-1,36,66,20,-1,65,64,-11,-7,-3,-1,4,35,-1,50,3,-1,19,49,-3,-1,48,34,18,-5,-1,33,-1,2,32,17,-1,1,16,0 }; -const int16 huffmanTab8[] = +constexpr int16 huffmanTab8[] = { -65,-63,-59,-45,-31,-19,-13,-7,-5,-3,-1,85,84,69,83,-3,-1,53,68,37,-3,-1,82,5,21,-5,-1,81,-1,52,67,-3,-1,80, 51,36,-5,-3,-1,66,20,65,-3,-1,4,64,-1,35,50,-9,-7,-3,-1,19,49,-1,3,48,34,-1,2,32,-1,18,33,17,-3,-1,1,16,0 }; -const int16 huffmanTab9[] = +constexpr int16 huffmanTab9[] = { -63,-53,-41,-29,-19,-11,-5,-3,-1,85,69,53,-1,83,-1,84,5,-3,-1,68,37,-1,82,21,-3,-1,81,52,-1,67,-1,80,4,-7,-3, -1,36,66,-1,51,64,-1,20,65,-5,-3,-1,35,50,19,-1,49,-1,3,48,-5,-3,-1,34,2,18,-1,33,32,-3,-1,17,1,-1,16,0 }; -const int16 huffmanTab10[] = +constexpr int16 huffmanTab10[] = { -125,-121,-111,-83,-55,-35,-21,-13,-7,-3,-1,119,103,-1,118,87,-3,-1,117,102,71,-3,-1,116,86,-1,101,55,-9,-3, -1,115,70,-3,-1,85,84,99,-1,39,114,-11,-5,-3,-1,100,7,112,-1,98,-1,69,53,-5,-1,6,-1,83,68,23,-17,-5,-1,113, @@ -253,7 +255,7 @@ const int16 huffmanTab10[] = 65,-3,-1,64,35,-1,50,3,-3,-1,19,49,-1,48,34,-7,-3,-1,18,33,-1,2,32,17,-1,1,16,0 }; -const int16 huffmanTab11[] = +constexpr int16 huffmanTab11[] = { -121,-113,-89,-59,-43,-27,-17,-7,-3,-1,119,103,-1,118,117,-3,-1,102,71,-1,116,-1,87,85,-5,-3,-1,86,101,55, -1,115,70,-9,-7,-3,-1,69,84,-1,53,83,39,-1,114,-1,100,7,-5,-1,113,-1,23,112,-3,-1,54,99,-1,96,-1,68,37,-13, @@ -261,7 +263,7 @@ const int16 huffmanTab11[] = -1,4,64,-1,35,50,-1,19,49,-5,-3,-1,3,48,34,33,-5,-1,18,-1,2,32,17,-3,-1,1,16,0 }; -const int16 huffmanTab12[] = +constexpr int16 huffmanTab12[] = { -115,-99,-73,-45,-27,-17,-9,-5,-3,-1,119,103,118,-1,87,117,-3,-1,102,71,-1,116,101,-3,-1,86,55,-3,-1,115, 85,39,-7,-3,-1,114,70,-1,100,23,-5,-1,113,-1,7,112,-1,54,99,-13,-9,-3,-1,69,84,-1,68,-1,6,5,-1,38,98,-5, @@ -269,7 +271,7 @@ const int16 huffmanTab12[] = 35,50,-11,-7,-5,-3,-1,64,3,48,19,-1,49,34,-1,18,33,-7,-5,-3,-1,2,32,0,17,-1,1,16 }; -const int16 huffmanTab13[] = +constexpr int16 huffmanTab13[] = { -509,-503,-475,-405,-333,-265,-205,-153,-115,-83,-53,-35,-21,-13,-9,-7,-5,-3,-1,254,252,253,237,255,-1,239,223, -3,-1,238,207,-1,222,191,-9,-3,-1,251,206,-1,220,-1,175,233,-1,236,221,-9,-5,-3,-1,250,205,190,-1,235,159,-3, @@ -289,7 +291,7 @@ const int16 huffmanTab13[] = -5,-1,65,-1,4,64,-3,-1,35,50,19,-3,-1,49,3,-1,48,34,-3,-1,18,33,-1,2,32,-3,-1,17,1,16,0 }; -const int16 huffmanTab15[] = +constexpr int16 huffmanTab15[] = { -495,-445,-355,-263,-183,-115,-77,-43,-27,-13,-7,-3,-1,255,239,-1,254,223,-1,238,-1,253,207,-7,-3,-1,252,222,-1, 237,191,-1,251,-1,206,236,-7,-3,-1,221,175,-1,250,190,-3,-1,235,205,-1,220,159,-15,-7,-3,-1,249,234,-1,189,219, @@ -309,7 +311,7 @@ const int16 huffmanTab15[] = 65,-1,20,4,-9,-3,-1,35,50,-3,-1,64,3,19,-3,-1,49,48,34,-9,-7,-3,-1,18,33,-1,2,32,17,-3,-1,1,16,0 }; -const int16 huffmanTab16[] = +constexpr int16 huffmanTab16[] = { -509,-503,-461,-323,-103,-37,-27,-15,-7,-3,-1,239,254,-1,223,253,-3,-1,207,252,-1,191,251,-5,-1,175,-1,250,159, -3,-1,249,248,143,-7,-3,-1,127,247,-1,111,246,255,-9,-5,-3,-1,95,245,79,-1,244,243,-53,-1,240,-1,63,-29,-19, @@ -329,7 +331,7 @@ const int16 huffmanTab16[] = 36,-3,-1,66,51,20,-5,-1,65,-1,4,64,-1,35,50,-3,-1,19,49,-3,-1,3,48,34,-3,-1,18,33,-1,2,32,-3,-1,17,1,16,0 }; -const int16 huffmanTab24[] = +constexpr int16 huffmanTab24[] = { -451,-117,-43,-25,-15,-7,-3,-1,239,254,-1,223,253,-3,-1,207,252,-1,191,251,-5,-1,250,-1,175,159,-1,249,248,-9, -5,-3,-1,143,127,247,-1,111,246,-3,-1,95,245,-1,79,244,-71,-7,-3,-1,63,243,-1,47,242,-5,-1,241,-1,31,240,-25,-9, @@ -354,7 +356,7 @@ struct BitsToTableMap const int16* table; }; -const BitsToTableMap huffmanTables1[] = +constexpr BitsToTableMap huffmanTables1[] = { { 0, huffmanTab0 }, { 0, huffmanTab1 }, { 0, huffmanTab2 }, { 0, huffmanTab3 }, { 0, huffmanTab0 }, { 0, huffmanTab5 }, { 0, huffmanTab6 }, { 0, huffmanTab7 }, @@ -366,10 +368,10 @@ const BitsToTableMap huffmanTables1[] = { 8, huffmanTab24 }, { 9, huffmanTab24 }, { 11, huffmanTab24 }, { 13, huffmanTab24 } }; -const int16 huffmanTabC0[] = { -29,-21,-13,-7,-3,-1,11,15,-1,13,14,-3,-1,7,5,9,-3,-1,6,3,-1,10,12,-3,-1,2,1,-1,4,8,0 }; -const int16 huffmanTabC1[] = { -15,-7,-3,-1,15,14,-1,13,12,-3,-1,11,10,-1,9,8,-7,-3,-1,7,6,-1,5,4,-3,-1,3,2,-1,1,0 }; +constexpr int16 huffmanTabC0[] = { -29,-21,-13,-7,-3,-1,11,15,-1,13,14,-3,-1,7,5,9,-3,-1,6,3,-1,10,12,-3,-1,2,1,-1,4,8,0 }; +constexpr int16 huffmanTabC1[] = { -15,-7,-3,-1,15,14,-1,13,12,-3,-1,11,10,-1,9,8,-7,-3,-1,7,6,-1,5,4,-3,-1,3,2,-1,1,0 }; -const BitsToTableMap huffmanTables2[] = { { 0, huffmanTabC0 }, { 0, huffmanTabC1 } }; +constexpr BitsToTableMap huffmanTables2[] = { { 0, huffmanTabC0 }, { 0, huffmanTabC1 } }; //============================================================================== struct VBRTagData @@ -386,7 +388,7 @@ struct VBRTagData const int sampleRateIndex = (data[2] >> 2) & 3; const int mode = (data[3] >> 6) & 3; - static const short bitRates[3][16] = + static constexpr short bitRates[3][16] = { { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // MPEG2 { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, // MPEG1 @@ -469,7 +471,7 @@ struct MP3Frame void selectLayer2Table() { - static const int translate[3][2][16] = + static constexpr int translate[3][2][16] = { { { 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 0 }, { 0, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 } }, { { 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, @@ -477,7 +479,7 @@ struct MP3Frame }; static const AllocationTable* const tables[] = { allocTable0, allocTable1, allocTable2, allocTable3, allocTable4 }; - static const int8 limits[] = { 27, 30, 8, 12, 30 }; + static constexpr int8 limits[] = { 27, 30, 8, 12, 30 }; const int index = lsf ? 4 : translate[sampleRateIndex][2 - numChannels][bitrateIndex]; layer2SubBandLimit = limits[index]; @@ -490,7 +492,9 @@ struct MP3Frame return frequencies[sampleRateIndex]; } - void decodeHeader (const uint32 header) + enum class ParseSuccessful { no, yes }; + + ParseSuccessful decodeHeader (const uint32 header) { jassert (((header >> 10) & 3) != 3); @@ -503,13 +507,13 @@ struct MP3Frame padding = (header >> 9) & 1; mode = (header >> 6) & 3; modeExt = (header >> 4) & 3; - //extension = (header >> 8) & 1; + //extension = (header >> 8) & 1; //copyright = (header >> 3) & 1; //original = (header >> 2) & 1; //emphasis = header & 3; numChannels = (mode == 3) ? 1 : 2; - static const int frameSizes[2][3][16] = + static constexpr int frameSizes[2][3][16] = { { { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 }, { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 }, @@ -525,17 +529,18 @@ struct MP3Frame jassertfalse; // This means the file is using "free format". Apparently very few decoders // support this mode, and this one certainly doesn't handle it correctly! frameSize = 0; + return ParseSuccessful::no; } - else + + switch (layer) { - switch (layer) - { - case 1: frameSize = (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() + padding) * 4) - 4; break; - case 2: frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() + (padding - 4); break; - case 3: frameSize = (bitrateIndex == 0) ? 0 : ((frameSizes[lsf][2][bitrateIndex] * 144000) / (getFrequency() << lsf) + (padding - 4)); break; - default: break; - } + case 1: frameSize = (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() + padding) * 4) - 4; break; + case 2: frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() + (padding - 4); break; + case 3: frameSize = (bitrateIndex == 0) ? 0 : ((frameSizes[lsf][2][bitrateIndex] * 144000) / (getFrequency() << lsf) + (padding - 4)); break; + default: break; } + + return ParseSuccessful::yes; } int layer, frameSize, numChannels, single; @@ -569,7 +574,7 @@ struct Constants default: break; } - static const uint8 dummy[] = { 0, 0, 0 }; + static constexpr uint8 dummy[] = { 0, 0, 0 }; return dummy; } @@ -643,7 +648,7 @@ struct Constants { 21, 1, 22, 23, 0, 24, 25, 2, 26 } }; - static const int tableLengths[] = { 3, 5, 9 }; + static constexpr int tableLengths[] = { 3, 5, 9 }; static uint8* tables[] = { group3tab, group5tab, group9tab }; for (int i = 0; i < 3; ++i) @@ -663,7 +668,7 @@ struct Constants for (int k = 0; k < 27; ++k) { - static const double multipliers[] = + static constexpr double multipliers[] = { 0, -2.0 / 3.0, 2.0 / 3.0, 2.0 / 7.0, 2.0 / 15.0, 2.0 / 31.0, 2.0 / 63.0, 2.0 / 127.0, 2.0 / 255.0, 2.0 / 511.0, 2.0 / 1023.0, 2.0 / 2047.0, 2.0 / 4095.0, 2.0 / 8191.0, 2.0 / 16383.0, 2.0 / 32767.0, 2.0 / 65535.0, @@ -688,7 +693,7 @@ struct Constants for (i = 0; i < 8; ++i) { - static double Ci[] = { -0.6, -0.535, -0.33, -0.185, -0.095, -0.041, -0.0142, -0.0037 }; + static constexpr double Ci[] = { -0.6, -0.535, -0.33, -0.185, -0.095, -0.041, -0.0142, -0.0037 }; const double sq = sqrt (1.0 + Ci[i] * Ci[i]); antiAliasingCs[i] = (float) (1.0 / sq); antiAliasingCa[i] = (float) (Ci[i] / sq); @@ -716,7 +721,7 @@ struct Constants for (j = 0; j < 4; ++j) { - static const int len[4] = { 36, 36, 12, 36 }; + static constexpr int len[4] = { 36, 36, 12, 36 }; for (i = 0; i < len[j]; i += 2) win1[j][i] = win[j][i]; for (i = 1; i < len[j]; i += 2) win1[j][i] = -win[j][i]; } @@ -1069,11 +1074,11 @@ struct Layer3SideInfo namespace DCT { enum { subBandLimit = 32 }; - static const float cos6_1 = 0.866025388f; - static const float cos6_2 = 0.5f; - static const float cos9[] = { 1.0f, 0.98480773f, 0.939692616f, 0.866025388f, 0.766044438f, 0.642787635f, 0.5f, 0.342020154f, 0.173648179f }; - static const float cos36[] = { 0.501909912f, 0.517638087f, 0.551688969f, 0.610387266f, 0.707106769f, 0.871723413f, 1.18310082f, 1.93185163f, 5.73685646f }; - static const float cos12[] = { 0.517638087f, 0.707106769f, 1.93185163f }; + static constexpr float cos6_1 = 0.866025388f; + static constexpr float cos6_2 = 0.5f; + static constexpr float cos9[] = { 1.0f, 0.98480773f, 0.939692616f, 0.866025388f, 0.766044438f, 0.642787635f, 0.5f, 0.342020154f, 0.173648179f }; + static constexpr float cos36[] = { 0.501909912f, 0.517638087f, 0.551688969f, 0.610387266f, 0.707106769f, 0.871723413f, 1.18310082f, 1.93185163f, 5.73685646f }; + static constexpr float cos12[] = { 0.517638087f, 0.707106769f, 1.93185163f }; inline void dct36_0 (int v, float* ts, float* out1, float* out2, const float* wintab, float sum0, float sum1) noexcept { @@ -1428,7 +1433,11 @@ struct MP3Stream lastFrameSize += nextFrameOffset; } - frame.decodeHeader ((uint32) stream.readIntBigEndian()); + const auto successful = frame.decodeHeader ((uint32) stream.readIntBigEndian()); + + if (successful == MP3Frame::ParseSuccessful::no) + return -1; + headerParsed = true; frameSize = frame.frameSize; isFreeFormat = (frameSize == 0); @@ -1562,7 +1571,8 @@ struct MP3Stream } frameIndex = jmin (frameIndex & ~(storedStartPosInterval - 1), - frameStreamPositions.size() * storedStartPosInterval - 1); + (frameStreamPositions.size() - 1) * storedStartPosInterval); + stream.setPosition (frameStreamPositions.getUnchecked (frameIndex / storedStartPosInterval)); currentFrameIndex = frameIndex; reset(); @@ -1878,6 +1888,9 @@ struct MP3Stream *in0++ = *in1++; } break; + + default: + break; } } @@ -1962,8 +1975,8 @@ struct MP3Stream { const uint8 n0 = si.allocation[i][0]; const uint8 n1 = si.allocation[i][1]; - fraction[0][i] = n0 > 0 ? (float) ((-(1 << n0) + getBitsUint16 (n0 + 1) + 1) * constants.muls[n0 + 1][si.scaleFactor[i][0]]) : 0; - fraction[1][i] = n1 > 0 ? (float) ((-(1 << n1) + getBitsUint16 (n1 + 1) + 1) * constants.muls[n1 + 1][si.scaleFactor[i][1]]) : 0; + fraction[0][i] = n0 > 0 ? ((float) (-(1 << n0) + getBitsUint16 (n0 + 1) + 1) * constants.muls[n0 + 1][si.scaleFactor[i][0]]) : 0.0f; + fraction[1][i] = n1 > 0 ? ((float) (-(1 << n1) + getBitsUint16 (n1 + 1) + 1) * constants.muls[n1 + 1][si.scaleFactor[i][1]]) : 0.0f; } for (i = jsbound; i < 32; ++i) @@ -1973,8 +1986,8 @@ struct MP3Stream if (n > 0) { const uint32 w = ((uint32) -(1 << n) + getBitsUint16 (n + 1) + 1); - fraction[0][i] = (float) (w * constants.muls[n + 1][si.scaleFactor[i][0]]); - fraction[1][i] = (float) (w * constants.muls[n + 1][si.scaleFactor[i][1]]); + fraction[0][i] = ((float) w * constants.muls[n + 1][si.scaleFactor[i][0]]); + fraction[1][i] = ((float) w * constants.muls[n + 1][si.scaleFactor[i][1]]); } else fraction[0][i] = fraction[1][i] = 0; @@ -1988,7 +2001,7 @@ struct MP3Stream const uint8 j = si.scaleFactor[i][0]; if (n > 0) - fraction[0][i] = (float) ((-(1 << n) + getBitsUint16 (n + 1) + 1) * constants.muls[n + 1][j]); + fraction[0][i] = ((float) (-(1 << n) + getBitsUint16 (n + 1) + 1) * constants.muls[n + 1][j]); else fraction[0][i] = 0; } @@ -1998,8 +2011,8 @@ struct MP3Stream void layer2Step1 (SideInfoLayer2& si) noexcept { zerostruct (si); - const int sblimit = frame.layer2SubBandLimit; - const int jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : frame.layer2SubBandLimit; + const auto sblimit = frame.layer2SubBandLimit; + const auto jsbound = (frame.mode == 1 ? jmin ((frame.modeExt << 2) + 4, sblimit) : sblimit); auto* allocTable = frame.allocationTable; uint8 scfsi[32][2]; @@ -2082,7 +2095,8 @@ struct MP3Stream void layer2Step2 (SideInfoLayer2& si, const int gr, float fraction[2][4][32]) noexcept { auto* allocTable = frame.allocationTable; - const int jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : frame.layer2SubBandLimit; + auto sblimit = frame.layer2SubBandLimit; + const auto jsbound = (frame.mode == 1 ? jmin ((frame.modeExt << 2) + 4, sblimit) : sblimit); for (int i = 0; i < jsbound; ++i) { @@ -2395,6 +2409,7 @@ struct MP3Stream return numBits; } + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6385) int getLayer3ScaleFactors2 (int* scf, Layer3SideInfo::Info& granule, const bool iStereo) noexcept { static const uint8 scaleTable[3][6][4] = @@ -2446,6 +2461,7 @@ struct MP3Stream return numBits; } + JUCE_END_IGNORE_WARNINGS_MSVC bool layer3DequantizeSample (float xr[32][18], int* scf, Layer3SideInfo::Info& granule, int sampleRate, int part2bits) noexcept { @@ -2912,7 +2928,7 @@ struct MP3Stream sum += window[12] * b0[12]; sum += window[14] * b0[14]; *out++ = sum; b0 -= 16; window -= 32; - window += bo1 << 1; + window += (ptrdiff_t) bo1 << 1; } for (int j = 15; j != 0; --j, b0 -= 16, window -= 32) @@ -2938,7 +2954,7 @@ struct MP3Stream static const char* const mp3FormatName = "MP3 file"; //============================================================================== -class MP3Reader : public AudioFormatReader +class MP3Reader final : public AudioFormatReader { public: MP3Reader (InputStream* const in) @@ -2959,10 +2975,14 @@ class MP3Reader : public AudioFormatReader } } - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { - jassert (destSamples != nullptr); + if (destSamples == nullptr) + { + jassertfalse; + return false; + } if (currentPosition != startSampleInFile) { @@ -3013,7 +3033,7 @@ class MP3Reader : public AudioFormatReader } const int numToCopy = jmin (decodedEnd - decodedStart, numSamples); - float* const* const dst = reinterpret_cast (destSamples); + float* const* const dst = reinterpret_cast (destSamples); memcpy (dst[0] + startOffsetInDestBuffer, decoded0 + decodedStart, (size_t) numToCopy * sizeof (float)); if (numDestChannels > 1 && dst[1] != nullptr) @@ -3106,7 +3126,7 @@ class MP3Reader : public AudioFormatReader const int bytesPerFrame = stream.frame.frameSize + 4; if (bytesPerFrame == 417 || bytesPerFrame == 418) - numFrames = roundToInt ((streamSize - streamStartPos) / 417.95918); // more accurate for 128k + numFrames = roundToInt ((double) (streamSize - streamStartPos) / 417.95918); // more accurate for 128k else numFrames = (streamSize - streamStartPos) / bytesPerFrame; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h index 7a847396..ce621376 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -35,8 +34,8 @@ namespace juce IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile the MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, - you are agreeing that ROLI Ltd. is in no way responsible for any patent, copyright, - or other legal issues that you may suffer as a result. + you are agreeing that Raw Material Software Limited is in no way responsible for any patent, + copyright, or other legal issues that you may suffer as a result. The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party intellectual property. If you wish to use it, please seek your own independent advice about the diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp index 86a7068b..aebee866 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -36,24 +35,24 @@ namespace juce namespace OggVorbisNamespace { #if JUCE_INCLUDE_OGGVORBIS_CODE || ! defined (JUCE_INCLUDE_OGGVORBIS_CODE) - #if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706 4995 4365 4456 4457 4459) - #elif JUCE_CLANG - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wconversion" - #pragma clang diagnostic ignored "-Wshadow" - #pragma clang diagnostic ignored "-Wdeprecated-register" - #if __has_warning("-Wzero-as-null-pointer-constant") - #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" - #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 + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706 4995 4365 4456 4457 4459 6297 6011 6001 6308 6255 6386 6385 6246 6387 6263 6262 28182) + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-align", + "-Wconversion", + "-Wdeprecated-declarations", + "-Wdeprecated-register", + "-Wfloat-conversion", + "-Wfloat-equal", + "-Wmaybe-uninitialized", + "-Wmisleading-indentation", + "-Wmissing-prototypes", + "-Wredundant-decls", + "-Wshadow", + "-Wsign-conversion", + "-Wswitch-default", + "-Wswitch-enum", + "-Wzero-as-null-pointer-constant") + JUCE_BEGIN_NO_SANITIZE ("undefined") #include "oggvorbis/vorbisenc.h" #include "oggvorbis/codec.h" @@ -61,35 +60,31 @@ namespace OggVorbisNamespace #include "oggvorbis/bitwise.c" #include "oggvorbis/framing.c" - #include "oggvorbis/libvorbis-1.3.2/lib/analysis.c" - #include "oggvorbis/libvorbis-1.3.2/lib/bitrate.c" - #include "oggvorbis/libvorbis-1.3.2/lib/block.c" - #include "oggvorbis/libvorbis-1.3.2/lib/codebook.c" - #include "oggvorbis/libvorbis-1.3.2/lib/envelope.c" - #include "oggvorbis/libvorbis-1.3.2/lib/floor0.c" - #include "oggvorbis/libvorbis-1.3.2/lib/floor1.c" - #include "oggvorbis/libvorbis-1.3.2/lib/info.c" - #include "oggvorbis/libvorbis-1.3.2/lib/lpc.c" - #include "oggvorbis/libvorbis-1.3.2/lib/lsp.c" - #include "oggvorbis/libvorbis-1.3.2/lib/mapping0.c" - #include "oggvorbis/libvorbis-1.3.2/lib/mdct.c" - #include "oggvorbis/libvorbis-1.3.2/lib/psy.c" - #include "oggvorbis/libvorbis-1.3.2/lib/registry.c" - #include "oggvorbis/libvorbis-1.3.2/lib/res0.c" - #include "oggvorbis/libvorbis-1.3.2/lib/sharedbook.c" - #include "oggvorbis/libvorbis-1.3.2/lib/smallft.c" - #include "oggvorbis/libvorbis-1.3.2/lib/synthesis.c" - #include "oggvorbis/libvorbis-1.3.2/lib/vorbisenc.c" - #include "oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c" - #include "oggvorbis/libvorbis-1.3.2/lib/window.c" - - #if JUCE_MSVC - #pragma warning (pop) - #elif JUCE_CLANG - #pragma clang diagnostic pop - #elif JUCE_GCC - #pragma GCC diagnostic pop - #endif + #include "oggvorbis/libvorbis-1.3.7/lib/analysis.c" + #include "oggvorbis/libvorbis-1.3.7/lib/bitrate.c" + #include "oggvorbis/libvorbis-1.3.7/lib/block.c" + #include "oggvorbis/libvorbis-1.3.7/lib/codebook.c" + #include "oggvorbis/libvorbis-1.3.7/lib/envelope.c" + #include "oggvorbis/libvorbis-1.3.7/lib/floor0.c" + #include "oggvorbis/libvorbis-1.3.7/lib/floor1.c" + #include "oggvorbis/libvorbis-1.3.7/lib/info.c" + #include "oggvorbis/libvorbis-1.3.7/lib/lpc.c" + #include "oggvorbis/libvorbis-1.3.7/lib/lsp.c" + #include "oggvorbis/libvorbis-1.3.7/lib/mapping0.c" + #include "oggvorbis/libvorbis-1.3.7/lib/mdct.c" + #include "oggvorbis/libvorbis-1.3.7/lib/psy.c" + #include "oggvorbis/libvorbis-1.3.7/lib/registry.c" + #include "oggvorbis/libvorbis-1.3.7/lib/res0.c" + #include "oggvorbis/libvorbis-1.3.7/lib/sharedbook.c" + #include "oggvorbis/libvorbis-1.3.7/lib/smallft.c" + #include "oggvorbis/libvorbis-1.3.7/lib/synthesis.c" + #include "oggvorbis/libvorbis-1.3.7/lib/vorbisenc.c" + #include "oggvorbis/libvorbis-1.3.7/lib/vorbisfile.c" + #include "oggvorbis/libvorbis-1.3.7/lib/window.c" + + JUCE_END_NO_SANITIZE + JUCE_END_IGNORE_WARNINGS_MSVC + JUCE_END_IGNORE_WARNINGS_GCC_LIKE #else #include #include @@ -114,7 +109,7 @@ const char* const OggVorbisAudioFormat::id3trackNumber = "id3trackNumber"; //============================================================================== -class OggReader : public AudioFormatReader +class OggReader final : public AudioFormatReader { public: OggReader (InputStream* inp) : AudioFormatReader (inp, oggFormatName) @@ -146,7 +141,7 @@ class OggReader : public AudioFormatReader lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1); numChannels = (unsigned int) info->channels; bitsPerSample = 16; - sampleRate = info->rate; + sampleRate = (double) info->rate; reservoir.setSize ((int) numChannels, (int) jmin (lengthInSamples, (int64) 4096)); } @@ -164,75 +159,66 @@ class OggReader : public AudioFormatReader } //============================================================================== - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { - while (numSamples > 0) + const auto getBufferedRange = [this] { return bufferedRange; }; + + const auto readFromReservoir = [this, &destSamples, &numDestChannels, &startOffsetInDestBuffer, &startSampleInFile] (const Range rangeToRead) { - auto numAvailable = (int) (reservoirStart + samplesInReservoir - startSampleInFile); + const auto bufferIndices = rangeToRead - bufferedRange.getStart(); + const auto writePos = (int64) startOffsetInDestBuffer + (rangeToRead.getStart() - startSampleInFile); - if (startSampleInFile >= reservoirStart && numAvailable > 0) - { - // got a few samples overlapping, so use them before seeking.. + for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) + if (destSamples[i] != nullptr) + memcpy (destSamples[i] + writePos, + reservoir.getReadPointer (i) + bufferIndices.getStart(), + (size_t) bufferIndices.getLength() * sizeof (float)); + }; - auto numToUse = jmin (numSamples, numAvailable); + const auto fillReservoir = [this] (int64 requestedStart) + { + const auto newStart = jmax ((int64) 0, requestedStart); + bufferedRange = Range { newStart, newStart + reservoir.getNumSamples() }; - for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) - if (destSamples[i] != nullptr) - memcpy (destSamples[i] + startOffsetInDestBuffer, - reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), - (size_t) numToUse * sizeof (float)); + if (bufferedRange.getStart() != ov_pcm_tell (&ovFile)) + ov_pcm_seek (&ovFile, bufferedRange.getStart()); - startSampleInFile += numToUse; - numSamples -= numToUse; - startOffsetInDestBuffer += numToUse; + int bitStream = 0; + int offset = 0; + int numToRead = (int) bufferedRange.getLength(); - if (numSamples == 0) - break; - } - - if (startSampleInFile < reservoirStart - || startSampleInFile + numSamples > reservoirStart + samplesInReservoir) + while (numToRead > 0) { - // buffer miss, so refill the reservoir - reservoirStart = jmax (0, (int) startSampleInFile); - samplesInReservoir = reservoir.getNumSamples(); - - if (reservoirStart != (int) ov_pcm_tell (&ovFile)) - ov_pcm_seek (&ovFile, reservoirStart); - - int bitStream = 0; - int offset = 0; - int numToRead = samplesInReservoir; + float** dataIn = nullptr; + auto samps = static_cast (ov_read_float (&ovFile, &dataIn, numToRead, &bitStream)); - while (numToRead > 0) - { - float** dataIn = nullptr; - auto samps = ov_read_float (&ovFile, &dataIn, numToRead, &bitStream); + if (samps <= 0) + break; - if (samps <= 0) - break; + jassert (samps <= numToRead); - jassert (samps <= numToRead); + for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;) + memcpy (reservoir.getWritePointer (i, offset), dataIn[i], (size_t) samps * sizeof (float)); - for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;) - memcpy (reservoir.getWritePointer (i, offset), dataIn[i], (size_t) samps * sizeof (float)); + numToRead -= samps; + offset += samps; + } - numToRead -= samps; - offset += samps; - } + if (numToRead > 0) + reservoir.clear (offset, numToRead); + }; - if (numToRead > 0) - reservoir.clear (offset, numToRead); - } - } + const auto remainingSamples = Reservoir::doBufferedRead (Range { startSampleInFile, startSampleInFile + numSamples }, + getBufferedRange, + readFromReservoir, + fillReservoir); - if (numSamples > 0) - { + if (! remainingSamples.isEmpty()) for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) - zeromem (destSamples[i] + startOffsetInDestBuffer, (size_t) numSamples * sizeof (int)); - } + zeromem (destSamples[i] + startOffsetInDestBuffer + (remainingSamples.getStart() - startSampleInFile), + (size_t) remainingSamples.getLength() * sizeof (int)); return true; } @@ -270,13 +256,13 @@ class OggReader : public AudioFormatReader OggVorbisNamespace::OggVorbis_File ovFile; OggVorbisNamespace::ov_callbacks callbacks; AudioBuffer reservoir; - int reservoirStart = 0, samplesInReservoir = 0; + Range bufferedRange; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader) }; //============================================================================== -class OggWriter : public AudioFormatWriter +class OggWriter final : public AudioFormatWriter { public: OggWriter (OutputStream* out, double rate, @@ -287,7 +273,7 @@ class OggWriter : public AudioFormatWriter vorbis_info_init (&vi); if (vorbis_encode_init_vbr (&vi, (int) numChans, (int) rate, - jlimit (0.0f, 1.0f, qualityIndex * 0.1f)) == 0) + jlimit (0.0f, 1.0f, (float) qualityIndex * 0.1f)) == 0) { vorbis_comment_init (&vc); @@ -490,12 +476,12 @@ StringArray OggVorbisAudioFormat::getQualityOptions() int OggVorbisAudioFormat::estimateOggFileQuality (const File& source) { - if (auto* in = source.createInputStream()) + if (auto in = source.createInputStream()) { - if (auto r = std::unique_ptr (createReaderFor (in, true))) + if (auto r = std::unique_ptr (createReaderFor (in.release(), true))) { - auto lengthSecs = r->lengthInSamples / r->sampleRate; - auto approxBitsPerSecond = (int) (source.getSize() * 8 / lengthSecs); + auto lengthSecs = (double) r->lengthInSamples / r->sampleRate; + auto approxBitsPerSecond = (int) ((double) source.getSize() * 8 / lengthSecs); auto qualities = getQualityOptions(); int bestIndex = 0; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h index 993204a5..840be9ee 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -27,7 +26,7 @@ namespace juce { -#if JUCE_USE_OGGVORBIS || defined (DOXYGEN) +#if JUCE_USE_OGGVORBIS || DOXYGEN //============================================================================== /** diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 7f2b26f7..2d8e7f75 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -27,6 +26,24 @@ namespace juce { +using StringMap = std::unordered_map; + +static auto toMap (const StringPairArray& array) +{ + StringMap result; + + for (auto i = 0; i < array.size(); ++i) + result[array.getAllKeys()[i]] = array.getAllValues()[i]; + + return result; +} + +static auto getValueWithDefault (const StringMap& m, const String& key, const String& fallback = {}) +{ + const auto iter = m.find (key); + return iter != m.cend() ? iter->second : fallback; +} + static const char* const wavFormatName = "WAV file"; //============================================================================== @@ -151,14 +168,100 @@ const char* const WavAudioFormat::riffInfoWatermarkURL = "IWMU"; const char* const WavAudioFormat::riffInfoWrittenBy = "IWRI"; const char* const WavAudioFormat::riffInfoYear = "YEAR"; -const char* const WavAudioFormat::ISRC = "ISRC"; -const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info"; +const char* const WavAudioFormat::aswgContentType = "contentType"; +const char* const WavAudioFormat::aswgProject = "project"; +const char* const WavAudioFormat::aswgOriginator = "originator"; +const char* const WavAudioFormat::aswgOriginatorStudio = "originatorStudio"; +const char* const WavAudioFormat::aswgNotes = "notes"; +const char* const WavAudioFormat::aswgSession = "session"; +const char* const WavAudioFormat::aswgState = "state"; +const char* const WavAudioFormat::aswgEditor = "editor"; +const char* const WavAudioFormat::aswgMixer = "mixer"; +const char* const WavAudioFormat::aswgFxChainName = "fxChainName"; +const char* const WavAudioFormat::aswgChannelConfig = "channelConfig"; +const char* const WavAudioFormat::aswgAmbisonicFormat = "ambisonicFormat"; +const char* const WavAudioFormat::aswgAmbisonicChnOrder = "ambisonicChnOrder"; +const char* const WavAudioFormat::aswgAmbisonicNorm = "ambisonicNorm"; +const char* const WavAudioFormat::aswgMicType = "micType"; +const char* const WavAudioFormat::aswgMicConfig = "micConfig"; +const char* const WavAudioFormat::aswgMicDistance = "micDistance"; +const char* const WavAudioFormat::aswgRecordingLoc = "recordingLoc"; +const char* const WavAudioFormat::aswgIsDesigned = "isDesigned"; +const char* const WavAudioFormat::aswgRecEngineer = "recEngineer"; +const char* const WavAudioFormat::aswgRecStudio = "recStudio"; +const char* const WavAudioFormat::aswgImpulseLocation = "impulseLocation"; +const char* const WavAudioFormat::aswgCategory = "category"; +const char* const WavAudioFormat::aswgSubCategory = "subCategory"; +const char* const WavAudioFormat::aswgCatId = "catId"; +const char* const WavAudioFormat::aswgUserCategory = "userCategory"; +const char* const WavAudioFormat::aswgUserData = "userData"; +const char* const WavAudioFormat::aswgVendorCategory = "vendorCategory"; +const char* const WavAudioFormat::aswgFxName = "fxName"; +const char* const WavAudioFormat::aswgLibrary = "library"; +const char* const WavAudioFormat::aswgCreatorId = "creatorId"; +const char* const WavAudioFormat::aswgSourceId = "sourceId"; +const char* const WavAudioFormat::aswgRmsPower = "rmsPower"; +const char* const WavAudioFormat::aswgLoudness = "loudness"; +const char* const WavAudioFormat::aswgLoudnessRange = "loudnessRange"; +const char* const WavAudioFormat::aswgMaxPeak = "maxPeak"; +const char* const WavAudioFormat::aswgSpecDensity = "specDensity"; +const char* const WavAudioFormat::aswgZeroCrossRate = "zeroCrossRate"; +const char* const WavAudioFormat::aswgPapr = "papr"; +const char* const WavAudioFormat::aswgText = "text"; +const char* const WavAudioFormat::aswgEfforts = "efforts"; +const char* const WavAudioFormat::aswgEffortType = "effortType"; +const char* const WavAudioFormat::aswgProjection = "projection"; +const char* const WavAudioFormat::aswgLanguage = "language"; +const char* const WavAudioFormat::aswgTimingRestriction = "timingRestriction"; +const char* const WavAudioFormat::aswgCharacterName = "characterName"; +const char* const WavAudioFormat::aswgCharacterGender = "characterGender"; +const char* const WavAudioFormat::aswgCharacterAge = "characterAge"; +const char* const WavAudioFormat::aswgCharacterRole = "characterRole"; +const char* const WavAudioFormat::aswgActorName = "actorName"; +const char* const WavAudioFormat::aswgActorGender = "actorGender"; +const char* const WavAudioFormat::aswgDirector = "director"; +const char* const WavAudioFormat::aswgDirection = "direction"; +const char* const WavAudioFormat::aswgFxUsed = "fxUsed"; +const char* const WavAudioFormat::aswgUsageRights = "usageRights"; +const char* const WavAudioFormat::aswgIsUnion = "isUnion"; +const char* const WavAudioFormat::aswgAccent = "accent"; +const char* const WavAudioFormat::aswgEmotion = "emotion"; +const char* const WavAudioFormat::aswgComposor = "composor"; +const char* const WavAudioFormat::aswgArtist = "artist"; +const char* const WavAudioFormat::aswgSongTitle = "songTitle"; +const char* const WavAudioFormat::aswgGenre = "genre"; +const char* const WavAudioFormat::aswgSubGenre = "subGenre"; +const char* const WavAudioFormat::aswgProducer = "producer"; +const char* const WavAudioFormat::aswgMusicSup = "musicSup"; +const char* const WavAudioFormat::aswgInstrument = "instrument"; +const char* const WavAudioFormat::aswgMusicPublisher = "musicPublisher"; +const char* const WavAudioFormat::aswgRightsOwner = "rightsOwner"; +const char* const WavAudioFormat::aswgIsSource = "isSource"; +const char* const WavAudioFormat::aswgIsLoop = "isLoop"; +const char* const WavAudioFormat::aswgIntensity = "intensity"; +const char* const WavAudioFormat::aswgIsFinal = "isFinal"; +const char* const WavAudioFormat::aswgOrderRef = "orderRef"; +const char* const WavAudioFormat::aswgIsOst = "isOst"; +const char* const WavAudioFormat::aswgIsCinematic = "isCinematic"; +const char* const WavAudioFormat::aswgIsLicensed = "isLicensed"; +const char* const WavAudioFormat::aswgIsDiegetic = "isDiegetic"; +const char* const WavAudioFormat::aswgMusicVersion = "musicVersion"; +const char* const WavAudioFormat::aswgIsrcId = "isrcId"; +const char* const WavAudioFormat::aswgTempo = "tempo"; +const char* const WavAudioFormat::aswgTimeSig = "timeSig"; +const char* const WavAudioFormat::aswgInKey = "inKey"; +const char* const WavAudioFormat::aswgBillingCode = "billingCode"; +const char* const WavAudioFormat::aswgVersion = "IXML_VERSION"; + +const char* const WavAudioFormat::ISRC = "ISRC"; +const char* const WavAudioFormat::internationalStandardRecordingCode = "international standard recording code"; +const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info"; //============================================================================== namespace WavFileHelpers { - JUCE_CONSTEXPR inline int chunkName (const char* name) noexcept { return (int) ByteOrder::littleEndianInt (name); } - JUCE_CONSTEXPR inline size_t roundUpSize (size_t sz) noexcept { return (sz + 3) & ~3u; } + constexpr inline int chunkName (const char* name) noexcept { return (int) ByteOrder::littleEndianInt (name); } + constexpr inline size_t roundUpSize (size_t sz) noexcept { return (sz + 3) & ~3u; } #if JUCE_MSVC #pragma pack (push, 1) @@ -178,43 +281,42 @@ namespace WavFileHelpers uint8 reserved[190]; char codingHistory[1]; - void copyTo (StringPairArray& values, const int totalSize) const + void copyTo (StringMap& values, const int totalSize) const { - values.set (WavAudioFormat::bwavDescription, String::fromUTF8 (description, sizeof (description))); - values.set (WavAudioFormat::bwavOriginator, String::fromUTF8 (originator, sizeof (originator))); - values.set (WavAudioFormat::bwavOriginatorRef, String::fromUTF8 (originatorRef, sizeof (originatorRef))); - values.set (WavAudioFormat::bwavOriginationDate, String::fromUTF8 (originationDate, sizeof (originationDate))); - values.set (WavAudioFormat::bwavOriginationTime, String::fromUTF8 (originationTime, sizeof (originationTime))); + values[WavAudioFormat::bwavDescription] = String::fromUTF8 (description, sizeof (description)); + values[WavAudioFormat::bwavOriginator] = String::fromUTF8 (originator, sizeof (originator)); + values[WavAudioFormat::bwavOriginatorRef] = String::fromUTF8 (originatorRef, sizeof (originatorRef)); + values[WavAudioFormat::bwavOriginationDate] = String::fromUTF8 (originationDate, sizeof (originationDate)); + values[WavAudioFormat::bwavOriginationTime] = String::fromUTF8 (originationTime, sizeof (originationTime)); auto timeLow = ByteOrder::swapIfBigEndian (timeRefLow); auto timeHigh = ByteOrder::swapIfBigEndian (timeRefHigh); auto time = (((int64) timeHigh) << 32) + timeLow; - values.set (WavAudioFormat::bwavTimeReference, String (time)); - values.set (WavAudioFormat::bwavCodingHistory, - String::fromUTF8 (codingHistory, totalSize - (int) offsetof (BWAVChunk, codingHistory))); + values[WavAudioFormat::bwavTimeReference] = String (time); + values[WavAudioFormat::bwavCodingHistory] = String::fromUTF8 (codingHistory, totalSize - (int) offsetof (BWAVChunk, codingHistory)); } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { - MemoryBlock data (roundUpSize (sizeof (BWAVChunk) + values[WavAudioFormat::bwavCodingHistory].getNumBytesAsUTF8())); + MemoryBlock data (roundUpSize (sizeof (BWAVChunk) + getValueWithDefault (values, WavAudioFormat::bwavCodingHistory).getNumBytesAsUTF8())); data.fillWith (0); auto* b = (BWAVChunk*) data.getData(); // Allow these calls to overwrite an extra byte at the end, which is fine as long - // as they get called in the right order.. - values[WavAudioFormat::bwavDescription] .copyToUTF8 (b->description, 257); - values[WavAudioFormat::bwavOriginator] .copyToUTF8 (b->originator, 33); - values[WavAudioFormat::bwavOriginatorRef] .copyToUTF8 (b->originatorRef, 33); - values[WavAudioFormat::bwavOriginationDate].copyToUTF8 (b->originationDate, 11); - values[WavAudioFormat::bwavOriginationTime].copyToUTF8 (b->originationTime, 9); - - auto time = values[WavAudioFormat::bwavTimeReference].getLargeIntValue(); + // as they get called in the right order. + getValueWithDefault (values, WavAudioFormat::bwavDescription) .copyToUTF8 (b->description, 257); + getValueWithDefault (values, WavAudioFormat::bwavOriginator) .copyToUTF8 (b->originator, 33); + getValueWithDefault (values, WavAudioFormat::bwavOriginatorRef) .copyToUTF8 (b->originatorRef, 33); + getValueWithDefault (values, WavAudioFormat::bwavOriginationDate).copyToUTF8 (b->originationDate, 11); + getValueWithDefault (values, WavAudioFormat::bwavOriginationTime).copyToUTF8 (b->originationTime, 9); + + auto time = getValueWithDefault (values, WavAudioFormat::bwavTimeReference).getLargeIntValue(); b->timeRefLow = ByteOrder::swapIfBigEndian ((uint32) (time & 0xffffffff)); b->timeRefHigh = ByteOrder::swapIfBigEndian ((uint32) (time >> 32)); - values[WavAudioFormat::bwavCodingHistory].copyToUTF8 (b->codingHistory, 0x7fffffff); + getValueWithDefault (values, WavAudioFormat::bwavCodingHistory).copyToUTF8 (b->codingHistory, 0x7fffffff); if (b->description[0] != 0 || b->originator[0] != 0 @@ -271,17 +373,17 @@ namespace WavFileHelpers SampleLoop loops[1]; template - static void setValue (StringPairArray& values, NameType name, uint32 val) + static void setValue (StringMap& values, NameType name, uint32 val) { - values.set (name, String (ByteOrder::swapIfBigEndian (val))); + values[name] = String (ByteOrder::swapIfBigEndian (val)); } - static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val) + static void setValue (StringMap& values, int prefix, const char* name, uint32 val) { setValue (values, "Loop" + String (prefix) + name, val); } - void copyTo (StringPairArray& values, const int totalSize) const + void copyTo (StringMap& values, const int totalSize) const { setValue (values, "Manufacturer", manufacturer); setValue (values, "Product", product); @@ -308,20 +410,20 @@ namespace WavFileHelpers } template - static uint32 getValue (const StringPairArray& values, NameType name, const char* def) + static uint32 getValue (const StringMap& values, NameType name, const char* def) { - return ByteOrder::swapIfBigEndian ((uint32) values.getValue (name, def).getIntValue()); + return ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, name, def).getIntValue()); } - static uint32 getValue (const StringPairArray& values, int prefix, const char* name, const char* def) + static uint32 getValue (const StringMap& values, int prefix, const char* name, const char* def) { return getValue (values, "Loop" + String (prefix) + name, def); } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { MemoryBlock data; - auto numLoops = jmin (64, values.getValue ("NumSampleLoops", "0").getIntValue()); + auto numLoops = jmin (64, getValueWithDefault (values, "NumSampleLoops", "0").getIntValue()); data.setSize (roundUpSize (sizeof (SMPLChunk) + (size_t) (jmax (0, numLoops - 1)) * sizeof (SampleLoop)), true); @@ -363,12 +465,12 @@ namespace WavFileHelpers int8 lowVelocity; int8 highVelocity; - static void setValue (StringPairArray& values, const char* name, int val) + static void setValue (StringMap& values, const char* name, int val) { - values.set (name, String (val)); + values[name] = String (val); } - void copyTo (StringPairArray& values) const + void copyTo (StringMap& values) const { setValue (values, "MidiUnityNote", baseNote); setValue (values, "Detune", detune); @@ -379,17 +481,17 @@ namespace WavFileHelpers setValue (values, "HighVelocity", highVelocity); } - static int8 getValue (const StringPairArray& values, const char* name, const char* def) + static int8 getValue (const StringMap& values, const char* name, const char* def) { - return (int8) values.getValue (name, def).getIntValue(); + return (int8) getValueWithDefault (values, name, def).getIntValue(); } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { MemoryBlock data; - auto& keys = values.getAllKeys(); - if (keys.contains ("LowNote", true) && keys.contains ("HighNote", true)) + if ( values.find ("LowNote") != values.cend() + && values.find ("HighNote") != values.cend()) { data.setSize (8, true); auto* inst = static_cast (data.getData()); @@ -423,14 +525,14 @@ namespace WavFileHelpers uint32 numCues; Cue cues[1]; - static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val) + static void setValue (StringMap& values, int prefix, const char* name, uint32 val) { - values.set ("Cue" + String (prefix) + name, String (ByteOrder::swapIfBigEndian (val))); + values["Cue" + String (prefix) + name] = String (ByteOrder::swapIfBigEndian (val)); } - void copyTo (StringPairArray& values, const int totalSize) const + void copyTo (StringMap& values, const int totalSize) const { - values.set ("NumCuePoints", String (ByteOrder::swapIfBigEndian (numCues))); + values["NumCuePoints"] = String (ByteOrder::swapIfBigEndian (numCues)); for (int i = 0; i < (int) numCues; ++i) { @@ -446,10 +548,10 @@ namespace WavFileHelpers } } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { MemoryBlock data; - const int numCues = values.getValue ("NumCuePoints", "0").getIntValue(); + const int numCues = getValueWithDefault (values, "NumCuePoints", "0").getIntValue(); if (numCues > 0) { @@ -469,23 +571,23 @@ namespace WavFileHelpers for (int i = 0; i < numCues; ++i) { auto prefix = "Cue" + String (i); - auto identifier = (uint32) values.getValue (prefix + "Identifier", "0").getIntValue(); + auto identifier = (uint32) getValueWithDefault (values, prefix + "Identifier", "0").getIntValue(); #if JUCE_DEBUG jassert (! identifiers.contains (identifier)); identifiers.add (identifier); #endif - auto order = values.getValue (prefix + "Order", String (nextOrder)).getIntValue(); + auto order = getValueWithDefault (values, prefix + "Order", String (nextOrder)).getIntValue(); nextOrder = jmax (nextOrder, order) + 1; auto& cue = c->cues[i]; cue.identifier = ByteOrder::swapIfBigEndian ((uint32) identifier); cue.order = ByteOrder::swapIfBigEndian ((uint32) order); - cue.chunkID = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkID", dataChunkID).getIntValue()); - cue.chunkStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkStart", "0").getIntValue()); - cue.blockStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "BlockStart", "0").getIntValue()); - cue.offset = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Offset", "0").getIntValue()); + cue.chunkID = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "ChunkID", dataChunkID).getIntValue()); + cue.chunkStart = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "ChunkStart", "0").getIntValue()); + cue.blockStart = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "BlockStart", "0").getIntValue()); + cue.offset = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, prefix + "Offset", "0").getIntValue()); } } @@ -497,20 +599,20 @@ namespace WavFileHelpers //============================================================================== namespace ListChunk { - static int getValue (const StringPairArray& values, const String& name) + static int getValue (const StringMap& values, const String& name) { - return values.getValue (name, "0").getIntValue(); + return getValueWithDefault (values, name, "0").getIntValue(); } - static int getValue (const StringPairArray& values, const String& prefix, const char* name) + static int getValue (const StringMap& values, const String& prefix, const char* name) { return getValue (values, prefix + name); } - static void appendLabelOrNoteChunk (const StringPairArray& values, const String& prefix, + static void appendLabelOrNoteChunk (const StringMap& values, const String& prefix, const int chunkType, MemoryOutputStream& out) { - auto label = values.getValue (prefix + "Text", prefix); + auto label = getValueWithDefault (values, prefix + "Text", prefix); auto labelLength = (int) label.getNumBytesAsUTF8() + 1; auto chunkLength = 4 + labelLength + (labelLength & 1); @@ -523,9 +625,9 @@ namespace WavFileHelpers out.writeByte (0); } - static void appendExtraChunk (const StringPairArray& values, const String& prefix, MemoryOutputStream& out) + static void appendExtraChunk (const StringMap& values, const String& prefix, MemoryOutputStream& out) { - auto text = values.getValue (prefix + "Text", prefix); + auto text = getValueWithDefault (values, prefix + "Text", prefix); auto textLength = (int) text.getNumBytesAsUTF8() + 1; // include null terminator auto chunkLength = textLength + 20 + (textLength & 1); @@ -545,7 +647,7 @@ namespace WavFileHelpers out.writeByte (0); } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { auto numCueLabels = getValue (values, "NumCueLabels"); auto numCueNotes = getValue (values, "NumCueNotes"); @@ -669,7 +771,7 @@ namespace WavFileHelpers return true; } - static void addToMetadata (StringPairArray& values, InputStream& input, int64 chunkEnd) + static void addToMetadata (StringMap& values, InputStream& input, int64 chunkEnd) { while (input.getPosition() < chunkEnd) { @@ -689,8 +791,8 @@ namespace WavFileHelpers { MemoryBlock mb; input.readIntoMemoryBlock (mb, (ssize_t) infoLength); - values.set (type, String::createStringFromData ((const char*) mb.getData(), - (int) mb.getSize())); + values[type] = String::createStringFromData ((const char*) mb.getData(), + (int) mb.getSize()); break; } } @@ -698,9 +800,9 @@ namespace WavFileHelpers } } - static bool writeValue (const StringPairArray& values, MemoryOutputStream& out, const char* paramName) + static bool writeValue (const StringMap& values, MemoryOutputStream& out, const char* paramName) { - auto value = values.getValue (paramName, {}); + auto value = getValueWithDefault (values, paramName, {}); if (value.isEmpty()) return false; @@ -718,7 +820,7 @@ namespace WavFileHelpers return true; } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { MemoryOutputStream out; out.writeInt (chunkName ("INFO")); @@ -742,7 +844,7 @@ namespace WavFileHelpers input.read (this, (int) jmin (sizeof (*this), length)); } - AcidChunk (const StringPairArray& values) + AcidChunk (const StringMap& values) { zerostruct (*this); @@ -752,18 +854,20 @@ namespace WavFileHelpers | getFlagIfPresent (values, WavAudioFormat::acidDiskBased, 0x08) | getFlagIfPresent (values, WavAudioFormat::acidizerFlag, 0x10); - if (values[WavAudioFormat::acidRootSet].getIntValue() != 0) - rootNote = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidRootNote].getIntValue()); + if (getValueWithDefault (values, WavAudioFormat::acidRootSet).getIntValue() != 0) + rootNote = ByteOrder::swapIfBigEndian ((uint16) getValueWithDefault (values, WavAudioFormat::acidRootNote).getIntValue()); + + numBeats = ByteOrder::swapIfBigEndian ((uint32) getValueWithDefault (values, WavAudioFormat::acidBeats).getIntValue()); + meterDenominator = ByteOrder::swapIfBigEndian ((uint16) getValueWithDefault (values, WavAudioFormat::acidDenominator).getIntValue()); + meterNumerator = ByteOrder::swapIfBigEndian ((uint16) getValueWithDefault (values, WavAudioFormat::acidNumerator).getIntValue()); - numBeats = ByteOrder::swapIfBigEndian ((uint32) values[WavAudioFormat::acidBeats].getIntValue()); - meterDenominator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidDenominator].getIntValue()); - meterNumerator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidNumerator].getIntValue()); + const auto iter = values.find (WavAudioFormat::acidTempo); - if (values.containsKey (WavAudioFormat::acidTempo)) - tempo = swapFloatByteOrder (values[WavAudioFormat::acidTempo].getFloatValue()); + if (iter != values.cend()) + tempo = swapFloatByteOrder (iter->second.getFloatValue()); } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { return AcidChunk (values).toMemoryBlock(); } @@ -774,7 +878,7 @@ namespace WavFileHelpers ? MemoryBlock (this, sizeof (*this)) : MemoryBlock(); } - void addToMetadata (StringPairArray& values) const + void addToMetadata (StringMap& values) const { setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01); setBoolFlag (values, WavAudioFormat::acidRootSet, 0x02); @@ -783,22 +887,22 @@ namespace WavFileHelpers setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10); if (flags & 0x02) // root note set - values.set (WavAudioFormat::acidRootNote, String (ByteOrder::swapIfBigEndian (rootNote))); + values[WavAudioFormat::acidRootNote] = String (ByteOrder::swapIfBigEndian (rootNote)); - values.set (WavAudioFormat::acidBeats, String (ByteOrder::swapIfBigEndian (numBeats))); - values.set (WavAudioFormat::acidDenominator, String (ByteOrder::swapIfBigEndian (meterDenominator))); - values.set (WavAudioFormat::acidNumerator, String (ByteOrder::swapIfBigEndian (meterNumerator))); - values.set (WavAudioFormat::acidTempo, String (swapFloatByteOrder (tempo))); + values[WavAudioFormat::acidBeats] = String (ByteOrder::swapIfBigEndian (numBeats)); + values[WavAudioFormat::acidDenominator] = String (ByteOrder::swapIfBigEndian (meterDenominator)); + values[WavAudioFormat::acidNumerator] = String (ByteOrder::swapIfBigEndian (meterNumerator)); + values[WavAudioFormat::acidTempo] = String (swapFloatByteOrder (tempo)); } - void setBoolFlag (StringPairArray& values, const char* name, uint32 mask) const + void setBoolFlag (StringMap& values, const char* name, uint32 mask) const { - values.set (name, (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0"); + values[name] = (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0"; } - static uint32 getFlagIfPresent (const StringPairArray& values, const char* name, uint32 flag) + static uint32 getFlagIfPresent (const StringMap& values, const char* name, uint32 flag) { - return values[name].getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0; + return getValueWithDefault (values, name).getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0; } static float swapFloatByteOrder (const float x) noexcept @@ -827,10 +931,10 @@ namespace WavFileHelpers //============================================================================== struct TracktionChunk { - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { MemoryOutputStream out; - auto s = values[WavAudioFormat::tracktionLoopInfo]; + auto s = getValueWithDefault (values, WavAudioFormat::tracktionLoopInfo); if (s.isNotEmpty()) { @@ -844,10 +948,161 @@ namespace WavFileHelpers } }; + //============================================================================== + namespace IXMLChunk + { + static const std::unordered_set aswgMetadataKeys + { + WavAudioFormat::aswgContentType, + WavAudioFormat::aswgProject, + WavAudioFormat::aswgOriginator, + WavAudioFormat::aswgOriginatorStudio, + WavAudioFormat::aswgNotes, + WavAudioFormat::aswgSession, + WavAudioFormat::aswgState, + WavAudioFormat::aswgEditor, + WavAudioFormat::aswgMixer, + WavAudioFormat::aswgFxChainName, + WavAudioFormat::aswgChannelConfig, + WavAudioFormat::aswgAmbisonicFormat, + WavAudioFormat::aswgAmbisonicChnOrder, + WavAudioFormat::aswgAmbisonicNorm, + WavAudioFormat::aswgMicType, + WavAudioFormat::aswgMicConfig, + WavAudioFormat::aswgMicDistance, + WavAudioFormat::aswgRecordingLoc, + WavAudioFormat::aswgIsDesigned, + WavAudioFormat::aswgRecEngineer, + WavAudioFormat::aswgRecStudio, + WavAudioFormat::aswgImpulseLocation, + WavAudioFormat::aswgCategory, + WavAudioFormat::aswgSubCategory, + WavAudioFormat::aswgCatId, + WavAudioFormat::aswgUserCategory, + WavAudioFormat::aswgUserData, + WavAudioFormat::aswgVendorCategory, + WavAudioFormat::aswgFxName, + WavAudioFormat::aswgLibrary, + WavAudioFormat::aswgCreatorId, + WavAudioFormat::aswgSourceId, + WavAudioFormat::aswgRmsPower, + WavAudioFormat::aswgLoudness, + WavAudioFormat::aswgLoudnessRange, + WavAudioFormat::aswgMaxPeak, + WavAudioFormat::aswgSpecDensity, + WavAudioFormat::aswgZeroCrossRate, + WavAudioFormat::aswgPapr, + WavAudioFormat::aswgText, + WavAudioFormat::aswgEfforts, + WavAudioFormat::aswgEffortType, + WavAudioFormat::aswgProjection, + WavAudioFormat::aswgLanguage, + WavAudioFormat::aswgTimingRestriction, + WavAudioFormat::aswgCharacterName, + WavAudioFormat::aswgCharacterGender, + WavAudioFormat::aswgCharacterAge, + WavAudioFormat::aswgCharacterRole, + WavAudioFormat::aswgActorName, + WavAudioFormat::aswgActorGender, + WavAudioFormat::aswgDirector, + WavAudioFormat::aswgDirection, + WavAudioFormat::aswgFxUsed, + WavAudioFormat::aswgUsageRights, + WavAudioFormat::aswgIsUnion, + WavAudioFormat::aswgAccent, + WavAudioFormat::aswgEmotion, + WavAudioFormat::aswgComposor, + WavAudioFormat::aswgArtist, + WavAudioFormat::aswgSongTitle, + WavAudioFormat::aswgGenre, + WavAudioFormat::aswgSubGenre, + WavAudioFormat::aswgProducer, + WavAudioFormat::aswgMusicSup, + WavAudioFormat::aswgInstrument, + WavAudioFormat::aswgMusicPublisher, + WavAudioFormat::aswgRightsOwner, + WavAudioFormat::aswgIsSource, + WavAudioFormat::aswgIsLoop, + WavAudioFormat::aswgIntensity, + WavAudioFormat::aswgIsFinal, + WavAudioFormat::aswgOrderRef, + WavAudioFormat::aswgIsOst, + WavAudioFormat::aswgIsCinematic, + WavAudioFormat::aswgIsLicensed, + WavAudioFormat::aswgIsDiegetic, + WavAudioFormat::aswgMusicVersion, + WavAudioFormat::aswgIsrcId, + WavAudioFormat::aswgTempo, + WavAudioFormat::aswgTimeSig, + WavAudioFormat::aswgInKey, + WavAudioFormat::aswgBillingCode + }; + + static void addToMetadata (StringMap& destValues, const String& source) + { + if (auto xml = parseXML (source)) + { + if (xml->hasTagName ("BWFXML")) + { + if (const auto* entry = xml->getChildByName (WavAudioFormat::aswgVersion)) + destValues[WavAudioFormat::aswgVersion] = entry->getAllSubText(); + + if (const auto* aswgElement = xml->getChildByName ("ASWG")) + { + for (const auto* entry : aswgElement->getChildIterator()) + { + const auto& tag = entry->getTagName(); + + if (aswgMetadataKeys.find (tag) != aswgMetadataKeys.end()) + destValues[tag] = entry->getAllSubText(); + } + } + } + } + } + + static MemoryBlock createFrom (const StringMap& values) + { + auto createTextElement = [] (const StringRef& key, const StringRef& value) + { + auto* elem = new XmlElement (key); + elem->addTextElement (value); + return elem; + }; + + std::unique_ptr aswgElement; + + for (const auto& pair : values) + { + if (aswgMetadataKeys.find (pair.first) != aswgMetadataKeys.end()) + { + if (aswgElement == nullptr) + aswgElement = std::make_unique ("ASWG"); + + aswgElement->addChildElement (createTextElement (pair.first, pair.second)); + } + } + + MemoryOutputStream outputStream; + + if (aswgElement != nullptr) + { + XmlElement xml ("BWFXML"); + auto aswgVersion = getValueWithDefault (values, WavAudioFormat::aswgVersion, "3.01"); + xml.addChildElement (createTextElement (WavAudioFormat::aswgVersion, aswgVersion)); + xml.addChildElement (aswgElement.release()); + xml.writeTo (outputStream); + outputStream.writeRepeatedByte (0, outputStream.getDataSize()); + } + + return outputStream.getMemoryBlock(); + } + } + //============================================================================== namespace AXMLChunk { - static void addToMetadata (StringPairArray& destValues, const String& source) + static void addToMetadata (StringMap& destValues, const String& source) { if (auto xml = parseXML (source)) { @@ -862,7 +1117,12 @@ namespace WavFileHelpers auto ISRCCode = xml4->getAllSubText().fromFirstOccurrenceOf ("ISRC:", false, true); if (ISRCCode.isNotEmpty()) - destValues.set (WavAudioFormat::ISRC, ISRCCode); + { + // We set ISRC here for backwards compatibility. + // If the INFO 'source' field is set in the info chunk, then the + // value for this key will be overwritten later. + destValues[WavAudioFormat::riffInfoSource] = destValues[WavAudioFormat::internationalStandardRecordingCode] = ISRCCode; + } } } } @@ -870,13 +1130,26 @@ namespace WavFileHelpers } } - static MemoryBlock createFrom (const StringPairArray& values) + static MemoryBlock createFrom (const StringMap& values) { - auto ISRC = values.getValue (WavAudioFormat::ISRC, {}); + // Use the new ISRC key if it is present, but fall back to the + // INFO 'source' value for backwards compatibility. + auto ISRC = getValueWithDefault (values, + WavAudioFormat::internationalStandardRecordingCode, + getValueWithDefault (values, WavAudioFormat::riffInfoSource)); + MemoryOutputStream xml; if (ISRC.isNotEmpty()) { + // If you are trying to set the ISRC, make sure that you are using + // WavAudioFormat::internationalStandardRecordingCode as the metadata key, + // and that the value is 12 characters long. If you are trying to set the + // 'source' field in the INFO chunk, set the + // WavAudioFormat::internationalStandardRecordingCode metadata field to the + // empty string to silence this assertion. + jassert (ISRC.length() == 12); + xml << "" "" @@ -931,7 +1204,7 @@ namespace WavFileHelpers } //============================================================================== -class WavAudioFormatReader : public AudioFormatReader +class WavAudioFormatReader final : public AudioFormatReader { public: WavAudioFormatReader (InputStream* in) : AudioFormatReader (in, wavFormatName) @@ -942,6 +1215,8 @@ class WavAudioFormatReader : public AudioFormatReader int cueLabelIndex = 0; int cueRegionIndex = 0; + StringMap dict; + auto streamStartPos = input->getPosition(); auto firstChunkType = input->readInt(); @@ -994,10 +1269,12 @@ class WavAudioFormatReader : public AudioFormatReader input->skipNextBytes (2); bitsPerSample = (unsigned int) (int) input->readShort(); - if (bitsPerSample > 64) + if (bitsPerSample > 64 && (int) sampleRate != 0) { bytesPerFrame = bytesPerSec / (int) sampleRate; - bitsPerSample = 8 * (unsigned int) bytesPerFrame / numChannels; + + if (numChannels != 0) + bitsPerSample = 8 * (unsigned int) bytesPerFrame / numChannels; } else { @@ -1018,7 +1295,7 @@ class WavAudioFormatReader : public AudioFormatReader { input->skipNextBytes (4); // skip over size and bitsPerSample auto channelMask = input->readInt(); - metadataValues.set ("ChannelMask", String (channelMask)); + dict["ChannelMask"] = String (channelMask); channelLayout = getChannelLayoutFromMask (channelMask, numChannels); ExtensibleWavSubFormat subFormat; @@ -1073,34 +1350,40 @@ class WavAudioFormatReader : public AudioFormatReader HeapBlock bwav; bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); input->read (bwav, (int) length); - bwav->copyTo (metadataValues, (int) length); + bwav->copyTo (dict, (int) length); } else if (chunkType == chunkName ("smpl")) { HeapBlock smpl; smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1); input->read (smpl, (int) length); - smpl->copyTo (metadataValues, (int) length); + smpl->copyTo (dict, (int) length); } else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which... { HeapBlock inst; inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1); input->read (inst, (int) length); - inst->copyTo (metadataValues); + inst->copyTo (dict); } else if (chunkType == chunkName ("cue ")) { HeapBlock cue; cue.calloc (jmax ((size_t) length + 1, sizeof (CueChunk)), 1); input->read (cue, (int) length); - cue->copyTo (metadataValues, (int) length); + cue->copyTo (dict, (int) length); } else if (chunkType == chunkName ("axml")) { MemoryBlock axml; input->readIntoMemoryBlock (axml, (ssize_t) length); - AXMLChunk::addToMetadata (metadataValues, axml.toString()); + AXMLChunk::addToMetadata (dict, axml.toString()); + } + else if (chunkType == chunkName ("iXML")) + { + MemoryBlock ixml; + input->readIntoMemoryBlock (ixml, (ssize_t) length); + IXMLChunk::addToMetadata (dict, ixml.toString()); } else if (chunkType == chunkName ("LIST")) { @@ -1108,7 +1391,7 @@ class WavAudioFormatReader : public AudioFormatReader if (subChunkType == chunkName ("info") || subChunkType == chunkName ("INFO")) { - ListInfoChunk::addToMetadata (metadataValues, *input, chunkEnd); + ListInfoChunk::addToMetadata (dict, *input, chunkEnd); } else if (subChunkType == chunkName ("adtl")) { @@ -1133,8 +1416,8 @@ class WavAudioFormatReader : public AudioFormatReader MemoryBlock textBlock; input->readIntoMemoryBlock (textBlock, stringLength); - metadataValues.set (prefix + "Identifier", String (identifier)); - metadataValues.set (prefix + "Text", textBlock.toString()); + dict[prefix + "Identifier"] = String (identifier); + dict[prefix + "Text"] = textBlock.toString(); } else if (adtlChunkType == chunkName ("ltxt")) { @@ -1151,14 +1434,14 @@ class WavAudioFormatReader : public AudioFormatReader MemoryBlock textBlock; input->readIntoMemoryBlock (textBlock, (int) stringLength); - metadataValues.set (prefix + "Identifier", String (identifier)); - metadataValues.set (prefix + "SampleLength", String (sampleLength)); - metadataValues.set (prefix + "Purpose", String (purpose)); - metadataValues.set (prefix + "Country", String (country)); - metadataValues.set (prefix + "Language", String (language)); - metadataValues.set (prefix + "Dialect", String (dialect)); - metadataValues.set (prefix + "CodePage", String (codePage)); - metadataValues.set (prefix + "Text", textBlock.toString()); + dict[prefix + "Identifier"] = String (identifier); + dict[prefix + "SampleLength"] = String (sampleLength); + dict[prefix + "Purpose"] = String (purpose); + dict[prefix + "Country"] = String (country); + dict[prefix + "Language"] = String (language); + dict[prefix + "Dialect"] = String (dialect); + dict[prefix + "CodePage"] = String (codePage); + dict[prefix + "Text"] = textBlock.toString(); } input->setPosition (adtlChunkEnd); @@ -1167,13 +1450,13 @@ class WavAudioFormatReader : public AudioFormatReader } else if (chunkType == chunkName ("acid")) { - AcidChunk (*input, length).addToMetadata (metadataValues); + AcidChunk (*input, length).addToMetadata (dict); } else if (chunkType == chunkName ("Trkn")) { MemoryBlock tracktion; input->readIntoMemoryBlock (tracktion, (ssize_t) length); - metadataValues.set (WavAudioFormat::tracktionLoopInfo, tracktion.toString()); + dict[WavAudioFormat::tracktionLoopInfo] = tracktion.toString(); } else if (chunkEnd <= input->getPosition()) { @@ -1184,14 +1467,16 @@ class WavAudioFormatReader : public AudioFormatReader } } - if (cueLabelIndex > 0) metadataValues.set ("NumCueLabels", String (cueLabelIndex)); - if (cueNoteIndex > 0) metadataValues.set ("NumCueNotes", String (cueNoteIndex)); - if (cueRegionIndex > 0) metadataValues.set ("NumCueRegions", String (cueRegionIndex)); - if (metadataValues.size() > 0) metadataValues.set ("MetaDataSource", "WAV"); + if (cueLabelIndex > 0) dict["NumCueLabels"] = String (cueLabelIndex); + if (cueNoteIndex > 0) dict["NumCueNotes"] = String (cueNoteIndex); + if (cueRegionIndex > 0) dict["NumCueRegions"] = String (cueRegionIndex); + if (dict.size() > 0) dict["MetaDataSource"] = "WAV"; + + metadataValues.addUnorderedMap (dict); } //============================================================================== - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, @@ -1294,7 +1579,7 @@ class WavAudioFormatReader : public AudioFormatReader }; //============================================================================== -class WavAudioFormatWriter : public AudioFormatWriter +class WavAudioFormatWriter final : public AudioFormatWriter { public: WavAudioFormatWriter (OutputStream* const out, const double rate, @@ -1311,15 +1596,18 @@ class WavAudioFormatWriter : public AudioFormatWriter // key should be removed (or set to "WAV") once this has been done jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF"); - bwavChunk = BWAVChunk::createFrom (metadataValues); - axmlChunk = AXMLChunk::createFrom (metadataValues); - smplChunk = SMPLChunk::createFrom (metadataValues); - instChunk = InstChunk::createFrom (metadataValues); - cueChunk = CueChunk ::createFrom (metadataValues); - listChunk = ListChunk::createFrom (metadataValues); - listInfoChunk = ListInfoChunk::createFrom (metadataValues); - acidChunk = AcidChunk::createFrom (metadataValues); - trckChunk = TracktionChunk::createFrom (metadataValues); + const auto map = toMap (metadataValues); + + bwavChunk = BWAVChunk::createFrom (map); + ixmlChunk = IXMLChunk::createFrom (map); + axmlChunk = AXMLChunk::createFrom (map); + smplChunk = SMPLChunk::createFrom (map); + instChunk = InstChunk::createFrom (map); + cueChunk = CueChunk ::createFrom (map); + listChunk = ListChunk::createFrom (map); + listInfoChunk = ListInfoChunk::createFrom (map); + acidChunk = AcidChunk::createFrom (map); + trckChunk = TracktionChunk::createFrom (map); } headerPosition = out->getPosition(); @@ -1382,7 +1670,7 @@ class WavAudioFormatWriter : public AudioFormatWriter } private: - MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk; + MemoryBlock tempBlock, bwavChunk, ixmlChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk; uint64 lengthInSamples = 0, bytesWritten = 0; int64 headerPosition = 0; bool writeFailed = false; @@ -1412,6 +1700,7 @@ class WavAudioFormatWriter : public AudioFormatWriter int64 riffChunkSize = (int64) (4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */ + 8 + audioDataSize + (audioDataSize & 1) + chunkSize (bwavChunk) + + chunkSize (ixmlChunk) + chunkSize (axmlChunk) + chunkSize (smplChunk) + chunkSize (instChunk) @@ -1475,7 +1764,7 @@ class WavAudioFormatWriter : public AudioFormatWriter output->writeShort ((short) numChannels); output->writeInt ((int) sampleRate); - output->writeInt ((int) (bytesPerFrame * sampleRate)); // nAvgBytesPerSec + output->writeInt ((int) ((double) bytesPerFrame * sampleRate)); // nAvgBytesPerSec output->writeShort ((short) bytesPerFrame); // nBlockAlign output->writeShort ((short) bitsPerSample); // wBitsPerSample @@ -1494,6 +1783,7 @@ class WavAudioFormatWriter : public AudioFormatWriter } writeChunk (bwavChunk, chunkName ("bext")); + writeChunk (ixmlChunk, chunkName ("iXML")); writeChunk (axmlChunk, chunkName ("axml")); writeChunk (smplChunk, chunkName ("smpl")); writeChunk (instChunk, chunkName ("inst"), 7); @@ -1508,7 +1798,7 @@ class WavAudioFormatWriter : public AudioFormatWriter usesFloatingPointData = (bitsPerSample == 32); } - static size_t chunkSize (const MemoryBlock& data) noexcept { return data.getSize() > 0 ? (8 + data.getSize()) : 0; } + static size_t chunkSize (const MemoryBlock& data) noexcept { return data.isEmpty() ? 0 : (8 + data.getSize()); } void writeChunkHeader (int chunkType, int size) const { @@ -1518,7 +1808,7 @@ class WavAudioFormatWriter : public AudioFormatWriter void writeChunk (const MemoryBlock& data, int chunkType, int size = 0) const { - if (data.getSize() > 0) + if (! data.isEmpty()) { writeChunkHeader (chunkType, size != 0 ? size : (int) data.getSize()); *output << data; @@ -1553,7 +1843,7 @@ class WavAudioFormatWriter : public AudioFormatWriter }; //============================================================================== -class MemoryMappedWavReader : public MemoryMappedAudioFormatReader +class MemoryMappedWavReader final : public MemoryMappedAudioFormatReader { public: MemoryMappedWavReader (const File& wavFile, const WavAudioFormatReader& reader) @@ -1562,12 +1852,15 @@ class MemoryMappedWavReader : public MemoryMappedAudioFormatReader { } - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, lengthInSamples); + if (numSamples <= 0) + return true; + if (map == nullptr || ! mappedSection.contains (Range (startSampleInFile, startSampleInFile + numSamples))) { jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. @@ -1703,7 +1996,7 @@ AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream, b MemoryMappedAudioFormatReader* WavAudioFormat::createMemoryMappedReader (const File& file) { - return createMemoryMappedReader (file.createInputStream()); + return createMemoryMappedReader (file.createInputStream().release()); } MemoryMappedAudioFormatReader* WavAudioFormat::createMemoryMappedReader (FileInputStream* fin) @@ -1748,7 +2041,7 @@ namespace WavFileHelpers TemporaryFile tempFile (file); WavAudioFormat wav; - std::unique_ptr reader (wav.createReaderFor (file.createInputStream(), true)); + std::unique_ptr reader (wav.createReaderFor (file.createInputStream().release(), true)); if (reader != nullptr) { @@ -1781,7 +2074,7 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai { using namespace WavFileHelpers; - std::unique_ptr reader (static_cast (createReaderFor (wavFile.createInputStream(), true))); + std::unique_ptr reader (static_cast (createReaderFor (wavFile.createInputStream().release(), true))); if (reader != nullptr) { @@ -1791,7 +2084,7 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai if (bwavSize > 0) { - auto chunk = BWAVChunk::createFrom (newMetadata); + auto chunk = BWAVChunk::createFrom (toMap (newMetadata)); if (chunk.getSize() <= (size_t) bwavSize) { @@ -1823,7 +2116,7 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai //============================================================================== #if JUCE_UNIT_TESTS -struct WaveAudioFormatTests : public UnitTest +struct WaveAudioFormatTests final : public UnitTest { WaveAudioFormatTests() : UnitTest ("Wave audio format tests", UnitTestCategories::audio) @@ -1833,69 +2126,212 @@ struct WaveAudioFormatTests : public UnitTest { beginTest ("Setting up metadata"); - StringPairArray metadataValues = WavAudioFormat::createBWAVMetadata ("description", - "originator", - "originatorRef", - Time::getCurrentTime(), - numTestAudioBufferSamples, - "codingHistory"); + auto metadataValues = toMap (WavAudioFormat::createBWAVMetadata ("description", + "originator", + "originatorRef", + Time::getCurrentTime(), + numTestAudioBufferSamples, + "codingHistory")); for (int i = numElementsInArray (WavFileHelpers::ListInfoChunk::types); --i >= 0;) - metadataValues.set (WavFileHelpers::ListInfoChunk::types[i], - WavFileHelpers::ListInfoChunk::types[i]); + metadataValues[WavFileHelpers::ListInfoChunk::types[i]] = WavFileHelpers::ListInfoChunk::types[i]; + + metadataValues[WavAudioFormat::internationalStandardRecordingCode] = WavAudioFormat::internationalStandardRecordingCode; if (metadataValues.size() > 0) - metadataValues.set ("MetaDataSource", "WAV"); + metadataValues["MetaDataSource"] = "WAV"; - metadataValues.addArray (createDefaultSMPLMetadata()); + const auto smplMetadata = createDefaultSMPLMetadata(); + metadataValues.insert (smplMetadata.cbegin(), smplMetadata.cend()); WavAudioFormat format; MemoryBlock memoryBlock; + StringPairArray metadataArray; + metadataArray.addUnorderedMap (metadataValues); + { - beginTest ("Creating a basic wave writer"); + beginTest ("Metadata can be written and read"); - std::unique_ptr writer (format.createWriterFor (new MemoryOutputStream (memoryBlock, false), - 44100.0, numTestAudioBufferChannels, - 32, metadataValues, 0)); - expect (writer != nullptr); + const auto newMetadata = getMetadataAfterReading (format, writeToBlock (format, metadataArray)); + expect (newMetadata == metadataArray, "Somehow, the metadata is different!"); + } - AudioBuffer buffer (numTestAudioBufferChannels, numTestAudioBufferSamples); - buffer.clear(); + { + beginTest ("Files containing a riff info source and an empty ISRC associate the source with the riffInfoSource key"); + StringPairArray meta; + meta.addMap ({ { WavAudioFormat::riffInfoSource, "customsource" }, + { WavAudioFormat::internationalStandardRecordingCode, "" } }); + const auto mb = writeToBlock (format, meta); + checkPatternsPresent (mb, { "INFOISRC" }); + checkPatternsNotPresent (mb, { "ISRC:", "writeFromAudioSampleBuffer (buffer, 0, numTestAudioBufferSamples)); + { + beginTest ("Files containing a riff info source and no ISRC associate the source with both keys " + "for backwards compatibility"); + StringPairArray meta; + meta.addMap ({ { WavAudioFormat::riffInfoSource, "customsource" } }); + const auto mb = writeToBlock (format, meta); + checkPatternsPresent (mb, { "INFOISRC", "ISRC:customsource", " reader (format.createReaderFor (new MemoryInputStream (memoryBlock, false), false)); - expect (reader != nullptr); - expect (reader->metadataValues == metadataValues, "Somehow, the metadata is different!"); + { + beginTest ("Files containing an ISRC and a riff info source associate the values with the appropriate keys"); + StringPairArray meta; + meta.addMap ({ { WavAudioFormat::riffInfoSource, "source" } }); + meta.addMap ({ { WavAudioFormat::internationalStandardRecordingCode, "UUVVVXXYYYYY" } }); + const auto mb = writeToBlock (format, meta); + checkPatternsPresent (mb, { "INFOISRC", "ISRC:UUVVVXXYYYYY", ""); + + { + auto writer = rawToUniquePtr (WavAudioFormat().createWriterFor (new MemoryOutputStream (block, false), 48000, 1, 32, meta, 0)); + expect (writer != nullptr); + } + + expect ([&] + { + auto input = std::make_unique (block, false); + + while (! input->isExhausted()) + { + char chunkType[4] {}; + auto pos = input->getPosition(); + + input->read (chunkType, 4); + + if (memcmp (chunkType, "iXML", 4) == 0) + { + auto length = (uint32) input->readInt(); + + MemoryBlock xmlBlock; + input->readIntoMemoryBlock (xmlBlock, (ssize_t) length); + + return parseXML (xmlBlock.toString()) != nullptr; + } + + input->setPosition (pos + 1); + } + + return false; + }()); + + { + auto reader = rawToUniquePtr (WavAudioFormat().createReaderFor (new MemoryInputStream (block, false), true)); + expect (reader != nullptr); + + for (const auto& key : meta.getAllKeys()) + { + const auto oldValue = meta.getValue (key, "!"); + const auto newValue = reader->metadataValues.getValue (key, ""); + expectEquals (oldValue, newValue); + } + + expect (reader->metadataValues.getValue (WavAudioFormat::aswgVersion, "") == "3.01"); + } } } private: + MemoryBlock writeToBlock (WavAudioFormat& format, StringPairArray meta) + { + MemoryBlock mb; + + { + // The destructor of the writer will modify the block, so make sure that we've + // destroyed the writer before returning the block! + auto writer = rawToUniquePtr (format.createWriterFor (new MemoryOutputStream (mb, false), + 44100.0, + numTestAudioBufferChannels, + 16, + meta, + 0)); + expect (writer != nullptr); + AudioBuffer buffer (numTestAudioBufferChannels, numTestAudioBufferSamples); + expect (writer->writeFromAudioSampleBuffer (buffer, 0, numTestAudioBufferSamples)); + } + + return mb; + } + + StringPairArray getMetadataAfterReading (WavAudioFormat& format, const MemoryBlock& mb) + { + auto reader = rawToUniquePtr (format.createReaderFor (new MemoryInputStream (mb, false), true)); + expect (reader != nullptr); + return reader->metadataValues; + } + + template + void checkPatterns (const MemoryBlock& mb, const std::vector& patterns, Fn&& fn) + { + for (const auto& pattern : patterns) + { + const auto begin = static_cast (mb.getData()); + const auto end = begin + mb.getSize(); + expect (fn (std::search (begin, end, pattern.begin(), pattern.end()), end)); + } + } + + void checkPatternsPresent (const MemoryBlock& mb, const std::vector& patterns) + { + checkPatterns (mb, patterns, std::not_equal_to<>{}); + } + + void checkPatternsNotPresent (const MemoryBlock& mb, const std::vector& patterns) + { + checkPatterns (mb, patterns, std::equal_to<>{}); + } + enum { numTestAudioBufferChannels = 2, numTestAudioBufferSamples = 256 }; - StringPairArray createDefaultSMPLMetadata() const + static StringMap createDefaultSMPLMetadata() { - StringPairArray m; - - m.set ("Manufacturer", "0"); - m.set ("Product", "0"); - m.set ("SamplePeriod", "0"); - m.set ("MidiUnityNote", "60"); - m.set ("MidiPitchFraction", "0"); - m.set ("SmpteFormat", "0"); - m.set ("SmpteOffset", "0"); - m.set ("NumSampleLoops", "0"); - m.set ("SamplerData", "0"); + StringMap m; + + m["Manufacturer"] = "0"; + m["Product"] = "0"; + m["SamplePeriod"] = "0"; + m["MidiUnityNote"] = "60"; + m["MidiPitchFraction"] = "0"; + m["SmpteFormat"] = "0"; + m["SmpteOffset"] = "0"; + m["NumSampleLoops"] = "0"; + m["SamplerData"] = "0"; return m; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h index 5e417d77..3c636a64 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -176,10 +175,102 @@ class JUCE_API WavAudioFormat : public AudioFormat static const char* const riffInfoWrittenBy; /**< Metadata property name used in INFO chunks. */ static const char* const riffInfoYear; /**< Metadata property name used in INFO chunks. */ + //============================================================================== + // ASWG chunk properties: + + static const char* const aswgContentType; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgProject; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgOriginator; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgOriginatorStudio; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgNotes; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgSession; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgState; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgEditor; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgMixer; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgFxChainName; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgChannelConfig; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgAmbisonicFormat; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgAmbisonicChnOrder; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgAmbisonicNorm; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgMicType; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgMicConfig; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgMicDistance; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgRecordingLoc; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIsDesigned; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgRecEngineer; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgRecStudio; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgImpulseLocation; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgCategory; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgSubCategory; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgCatId; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgUserCategory; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgUserData; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgVendorCategory; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgFxName; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgLibrary; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgCreatorId; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgSourceId; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgRmsPower; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgLoudness; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgLoudnessRange; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgMaxPeak; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgSpecDensity; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgZeroCrossRate; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgPapr; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgText; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgEfforts; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgEffortType; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgProjection; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgLanguage; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgTimingRestriction; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgCharacterName; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgCharacterGender; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgCharacterAge; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgCharacterRole; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgActorName; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgActorGender; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgDirector; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgDirection; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgFxUsed; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgUsageRights; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIsUnion; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgAccent; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgEmotion; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgComposor; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgArtist; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgSongTitle; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgGenre; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgSubGenre; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgProducer; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgMusicSup; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgInstrument; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgMusicPublisher; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgRightsOwner; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIsSource; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIsLoop; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIntensity; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIsFinal; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgOrderRef; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIsOst; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIsCinematic; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIsLicensed; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIsDiegetic; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgMusicVersion; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgIsrcId; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgTempo; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgTimeSig; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgInKey; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgBillingCode; /**< Metadata property name used in ASWG/iXML chunks. */ + static const char* const aswgVersion; /**< Metadata property name used in ASWG/iXML chunks. */ + //============================================================================== /** Metadata property name used when reading an ISRC code from an AXML chunk. */ + [[deprecated ("This string is identical to riffInfoSource, making it impossible to differentiate between the two")]] static const char* const ISRC; + /** Metadata property name used when reading and writing ISRC codes to/from AXML chunks. */ + static const char* const internationalStandardRecordingCode; + /** Metadata property name used when reading a WAV file with a Tracktion chunk. */ static const char* const tracktionLoopInfo; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp index e0cb1e05..d9ef4a6c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -30,23 +29,23 @@ namespace juce namespace WindowsMediaCodec { -class JuceIStream : public ComBaseClassHelper +class JuceIStream final : public ComBaseClassHelper { public: JuceIStream (InputStream& in) noexcept - : ComBaseClassHelper (0), source (in) + : ComBaseClassHelper (0), source (in) { } - JUCE_COMRESULT Commit (DWORD) { return S_OK; } - JUCE_COMRESULT Write (const void*, ULONG, ULONG*) { return E_NOTIMPL; } - JUCE_COMRESULT Clone (IStream**) { return E_NOTIMPL; } - JUCE_COMRESULT SetSize (ULARGE_INTEGER) { return E_NOTIMPL; } - JUCE_COMRESULT Revert() { return E_NOTIMPL; } - JUCE_COMRESULT LockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; } - JUCE_COMRESULT UnlockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; } + JUCE_COMRESULT Commit (DWORD) override { return S_OK; } + JUCE_COMRESULT Write (const void*, ULONG, ULONG*) override { return E_NOTIMPL; } + JUCE_COMRESULT Clone (IStream**) override { return E_NOTIMPL; } + JUCE_COMRESULT SetSize (ULARGE_INTEGER) override { return E_NOTIMPL; } + JUCE_COMRESULT Revert() override { return E_NOTIMPL; } + JUCE_COMRESULT LockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD) override { return E_NOTIMPL; } + JUCE_COMRESULT UnlockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD) override { return E_NOTIMPL; } - JUCE_COMRESULT Read (void* dest, ULONG numBytes, ULONG* bytesRead) + JUCE_COMRESULT Read (void* dest, ULONG numBytes, ULONG* bytesRead) override { auto numRead = source.read (dest, (size_t) numBytes); @@ -56,7 +55,7 @@ class JuceIStream : public ComBaseClassHelper return (numRead == (int) numBytes) ? S_OK : S_FALSE; } - JUCE_COMRESULT Seek (LARGE_INTEGER position, DWORD origin, ULARGE_INTEGER* resultPosition) + JUCE_COMRESULT Seek (LARGE_INTEGER position, DWORD origin, ULARGE_INTEGER* resultPosition) override { auto newPos = (int64) position.QuadPart; @@ -75,16 +74,16 @@ class JuceIStream : public ComBaseClassHelper } if (resultPosition != nullptr) - resultPosition->QuadPart = newPos; + resultPosition->QuadPart = (ULONGLONG) newPos; return source.setPosition (newPos) ? S_OK : E_NOTIMPL; } JUCE_COMRESULT CopyTo (IStream* destStream, ULARGE_INTEGER numBytesToDo, - ULARGE_INTEGER* bytesRead, ULARGE_INTEGER* bytesWritten) + ULARGE_INTEGER* bytesRead, ULARGE_INTEGER* bytesWritten) override { uint64 totalCopied = 0; - int64 numBytes = numBytesToDo.QuadPart; + auto numBytes = (int64) numBytesToDo.QuadPart; while (numBytes > 0 && ! source.isExhausted()) { @@ -96,8 +95,8 @@ class JuceIStream : public ComBaseClassHelper if (numRead <= 0) break; - destStream->Write (buffer, numRead, nullptr); - totalCopied += numRead; + destStream->Write (buffer, (ULONG) numRead, nullptr); + totalCopied += (ULONG) numRead; } if (bytesRead != nullptr) bytesRead->QuadPart = totalCopied; @@ -106,14 +105,14 @@ class JuceIStream : public ComBaseClassHelper return S_OK; } - JUCE_COMRESULT Stat (STATSTG* stat, DWORD) + JUCE_COMRESULT Stat (STATSTG* stat, DWORD) override { if (stat == nullptr) return STG_E_INVALIDPOINTER; zerostruct (*stat); stat->type = STGTY_STREAM; - stat->cbSize.QuadPart = jmax ((int64) 0, source.getTotalLength()); + stat->cbSize.QuadPart = (ULONGLONG) jmax ((int64) 0, source.getTotalLength()); return S_OK; } @@ -125,10 +124,10 @@ class JuceIStream : public ComBaseClassHelper //============================================================================== static const char* wmFormatName = "Windows Media"; -static const char* const extensions[] = { ".mp3", ".wmv", ".asf", ".wm", ".wma", 0 }; +static const char* const extensions[] = { ".mp3", ".wmv", ".asf", ".wm", ".wma", nullptr }; //============================================================================== -class WMAudioReader : public AudioFormatReader +class WMAudioReader final : public AudioFormatReader { public: WMAudioReader (InputStream* const input_) @@ -158,13 +157,13 @@ class WMAudioReader : public AudioFormatReader } } - ~WMAudioReader() + ~WMAudioReader() override { if (wmSyncReader != nullptr) wmSyncReader->Close(); } - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override { if (sampleRate <= 0) @@ -175,7 +174,7 @@ class WMAudioReader : public AudioFormatReader clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, lengthInSamples); - const int stride = numChannels * sizeof (int16); + const auto stride = (int) (numChannels * sizeof (int16)); while (numSamples > 0) { @@ -204,20 +203,20 @@ class WMAudioReader : public AudioFormatReader return false; if (hasJumped) - bufferedRange.setStart ((int64) ((sampleTime * (int64) sampleRate) / 10000000)); + bufferedRange.setStart ((int64) ((sampleTime * (QWORD) sampleRate) / 10000000)); else bufferedRange.setStart (bufferedRange.getEnd()); // (because the positions returned often aren't contiguous) - bufferedRange.setLength ((int64) (dataLength / stride)); + bufferedRange.setLength ((int64) dataLength / (int64) stride); - buffer.ensureSize ((int) dataLength); + buffer.ensureSize ((size_t) dataLength); memcpy (buffer.getData(), rawData, (size_t) dataLength); } else if (hr == NS_E_NO_MORE_SAMPLES) { bufferedRange.setStart (startSampleInFile); bufferedRange.setLength (256); - buffer.ensureSize (256 * stride); + buffer.ensureSize (256 * (size_t) stride); buffer.fillWith (0); } else @@ -232,6 +231,7 @@ class WMAudioReader : public AudioFormatReader for (int i = 0; i < numDestChannels; ++i) { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (28182) jassert (destSamples[i] != nullptr); auto srcChan = jmin (i, (int) numChannels - 1); @@ -240,9 +240,10 @@ class WMAudioReader : public AudioFormatReader for (int j = 0; j < numToDo; ++j) { - dst[j] = ((uint32) *src) << 16; + dst[j] = (int) (((uint32) *src) << 16); src += numChannels; } + JUCE_END_IGNORE_WARNINGS_MSVC } startSampleInFile += numToDo; @@ -261,40 +262,31 @@ class WMAudioReader : public AudioFormatReader void checkCoInitialiseCalled() { - CoInitialize (0); + [[maybe_unused]] const auto result = CoInitialize (nullptr); } void scanFileForDetails() { - ComSmartPtr wmHeaderInfo; - HRESULT hr = wmSyncReader.QueryInterface (wmHeaderInfo); - - if (SUCCEEDED (hr)) + if (auto wmHeaderInfo = wmSyncReader.getInterface()) { QWORD lengthInNanoseconds = 0; WORD lengthOfLength = sizeof (lengthInNanoseconds); WORD streamNum = 0; WMT_ATTR_DATATYPE wmAttrDataType; - hr = wmHeaderInfo->GetAttributeByName (&streamNum, L"Duration", &wmAttrDataType, - (BYTE*) &lengthInNanoseconds, &lengthOfLength); - - ComSmartPtr wmProfile; - hr = wmSyncReader.QueryInterface (wmProfile); + wmHeaderInfo->GetAttributeByName (&streamNum, L"Duration", &wmAttrDataType, + (BYTE*) &lengthInNanoseconds, &lengthOfLength); - if (SUCCEEDED (hr)) + if (auto wmProfile = wmSyncReader.getInterface()) { ComSmartPtr wmStreamConfig; - hr = wmProfile->GetStream (0, wmStreamConfig.resetAndGetPointerAddress()); + auto hr = wmProfile->GetStream (0, wmStreamConfig.resetAndGetPointerAddress()); if (SUCCEEDED (hr)) { - ComSmartPtr wmMediaProperties; - hr = wmStreamConfig.QueryInterface (wmMediaProperties); - - if (SUCCEEDED (hr)) + if (auto wmMediaProperties = wmStreamConfig.getInterface()) { DWORD sizeMediaType; - hr = wmMediaProperties->GetMediaType (0, &sizeMediaType); + hr = wmMediaProperties->GetMediaType (nullptr, &sizeMediaType); HeapBlock mediaType; mediaType.malloc (sizeMediaType, 1); @@ -307,7 +299,7 @@ class WMAudioReader : public AudioFormatReader sampleRate = inputFormat->nSamplesPerSec; numChannels = inputFormat->nChannels; bitsPerSample = inputFormat->wBitsPerSample != 0 ? inputFormat->wBitsPerSample : 16; - lengthInSamples = (lengthInNanoseconds * (int) sampleRate) / 10000000; + lengthInSamples = (lengthInNanoseconds * (QWORD) sampleRate) / 10000000; } } } @@ -327,7 +319,7 @@ WindowsMediaAudioFormat::WindowsMediaAudioFormat() { } -WindowsMediaAudioFormat::~WindowsMediaAudioFormat() {} +WindowsMediaAudioFormat::~WindowsMediaAudioFormat() = default; Array WindowsMediaAudioFormat::getPossibleSampleRates() { return {}; } Array WindowsMediaAudioFormat::getPossibleBitDepths() { return {}; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h index 6c422bfd..6b140eae 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -40,7 +39,7 @@ class WindowsMediaAudioFormat : public AudioFormat public: //============================================================================== WindowsMediaAudioFormat(); - ~WindowsMediaAudioFormat(); + ~WindowsMediaAudioFormat() override; //============================================================================== Array getPossibleSampleRates() override; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/Ogg Vorbis Licence.txt b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/Ogg Vorbis Licence.txt index 4297870b..0fab923d 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/Ogg Vorbis Licence.txt +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/Ogg Vorbis Licence.txt @@ -1,23 +1,25 @@ ===================================================================== -I've incorporated Ogg-Vorbis directly into the JUCE codebase because it makes +Ogg-Vorbis is incorporated directly into the JUCE codebase because it makes things much easier than having to make all your builds link correctly to the appropriate libraries on every different platform. -I've made minimal changes to the Ogg-Vorbis code - just tweaked a few include -paths to make it build smoothly, and added some headers to allow you to exclude -it from the build. +There are minimal changes to the Ogg-Vorbis code - a few include paths have +been changed to make it build smoothly, and some headers have been added to +allow you to exclude it from the build. Any significant code modifications +are marked clearly by "JUCE CHANGE STARTS HERE" and "JUCE CHANGE ENDS HERE" +comments. ===================================================================== -The following license is the BSD-style license that comes with the -Ogg-Vorbis distribution, and which applies just to the header files I've -included in this directory. For more info, and to get the rest of the -distribution, visit the Ogg-Vorbis homepage: www.vorbis.com +The following license is the BSD-style license that comes with the Ogg-Vorbis +distribution, and which applies just to the header files included in this +directory. For more info, and to get the rest of the distribution, visit the +Ogg-Vorbis homepage: https://xiph.org/vorbis ===================================================================== -Copyright (c) 2002-2004 Xiph.org Foundation +Copyright (c) 2002-2020 Xiph.org Foundation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/bitwise.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/bitwise.c index 35e20ca8..6cb8b33d 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/bitwise.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/bitwise.c @@ -1,17 +1,16 @@ /******************************************************************** * * - * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * THIS FILE IS PART OF THE Ogg CONTAINER SOURCE CODE. * * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2014 * * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** function: packing variable sized words into an octet stream - last mod: $Id: bitwise.c,v 1.1 2007/06/07 17:48:18 jules_rms Exp $ ********************************************************************/ @@ -24,6 +23,7 @@ #include #include +#include #include "ogg.h" #define BUFFER_INCREMENT 256 @@ -42,7 +42,7 @@ static const unsigned int mask8B[]= void oggpack_writeinit(oggpack_buffer *b){ memset(b,0,sizeof(*b)); - b->ptr=b->buffer=(unsigned char*) _ogg_malloc(BUFFER_INCREMENT); + b->ptr=b->buffer=(unsigned char*)_ogg_malloc(BUFFER_INCREMENT); b->buffer[0]='\0'; b->storage=BUFFER_INCREMENT; } @@ -51,28 +51,47 @@ void oggpackB_writeinit(oggpack_buffer *b){ oggpack_writeinit(b); } +int oggpack_writecheck(oggpack_buffer *b){ + if(!b->ptr || !b->storage)return -1; + return 0; +} + +int oggpackB_writecheck(oggpack_buffer *b){ + return oggpack_writecheck(b); +} + void oggpack_writetrunc(oggpack_buffer *b,long bits){ long bytes=bits>>3; - bits-=bytes*8; - b->ptr=b->buffer+bytes; - b->endbit=bits; - b->endbyte=bytes; - *b->ptr&=mask[bits]; + if(b->ptr){ + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask[bits]; + } } void oggpackB_writetrunc(oggpack_buffer *b,long bits){ long bytes=bits>>3; - bits-=bytes*8; - b->ptr=b->buffer+bytes; - b->endbit=bits; - b->endbyte=bytes; - *b->ptr&=mask8B[bits]; + if(b->ptr){ + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask8B[bits]; + } } /* Takes only up to 32 bits. */ void oggpack_write(oggpack_buffer *b,unsigned long value,int bits){ - if(b->endbyte+4>=b->storage){ - b->buffer=(unsigned char*) _ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + if(bits<0 || bits>32) goto err; + if(b->endbyte>=b->storage-4){ + void *ret; + if(!b->ptr)return; + if(b->storage>LONG_MAX-BUFFER_INCREMENT) goto err; + ret=_ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + if(!ret) goto err; + b->buffer=(unsigned char*)ret; b->storage+=BUFFER_INCREMENT; b->ptr=b->buffer+b->endbyte; } @@ -87,13 +106,13 @@ void oggpack_write(oggpack_buffer *b,unsigned long value,int bits){ if(bits>=16){ b->ptr[2]=(unsigned char)(value>>(16-b->endbit)); if(bits>=24){ - b->ptr[3]=(unsigned char)(value>>(24-b->endbit)); - if(bits>=32){ - if(b->endbit) - b->ptr[4]=(unsigned char)(value>>(32-b->endbit)); - else - b->ptr[4]=0; - } + b->ptr[3]=(unsigned char)(value>>(24-b->endbit)); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=(unsigned char)(value>>(32-b->endbit)); + else + b->ptr[4]=0; + } } } } @@ -101,12 +120,21 @@ void oggpack_write(oggpack_buffer *b,unsigned long value,int bits){ b->endbyte+=bits/8; b->ptr+=bits/8; b->endbit=bits&7; + return; + err: + oggpack_writeclear(b); } /* Takes only up to 32 bits. */ void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits){ - if(b->endbyte+4>=b->storage){ - b->buffer=(unsigned char*) _ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + if(bits<0 || bits>32) goto err; + if(b->endbyte>=b->storage-4){ + void *ret; + if(!b->ptr)return; + if(b->storage>LONG_MAX-BUFFER_INCREMENT) goto err; + ret=_ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + if(!ret) goto err; + b->buffer=(unsigned char*)ret; b->storage+=BUFFER_INCREMENT; b->ptr=b->buffer+b->endbyte; } @@ -121,13 +149,13 @@ void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits){ if(bits>=16){ b->ptr[2]=(unsigned char)(value>>(8+b->endbit)); if(bits>=24){ - b->ptr[3]=(unsigned char)(value>>(b->endbit)); - if(bits>=32){ - if(b->endbit) - b->ptr[4]=(unsigned char)(value<<(8-b->endbit)); - else - b->ptr[4]=0; - } + b->ptr[3]=(unsigned char)(value>>(b->endbit)); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=(unsigned char)(value<<(8-b->endbit)); + else + b->ptr[4]=0; + } } } } @@ -135,6 +163,9 @@ void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits){ b->endbyte+=bits/8; b->ptr+=bits/8; b->endbit=bits&7; + return; + err: + oggpack_writeclear(b); } void oggpack_writealign(oggpack_buffer *b){ @@ -150,17 +181,31 @@ void oggpackB_writealign(oggpack_buffer *b){ } static void oggpack_writecopy_helper(oggpack_buffer *b, - void *source, - long bits, - void (*w)(oggpack_buffer *, - unsigned long, - int), - int msb){ + void *source, + long bits, + void (*w)(oggpack_buffer *, + unsigned long, + int), + int msb){ unsigned char *ptr=(unsigned char *)source; long bytes=bits/8; + long pbytes=(b->endbit+bits)/8; bits-=bytes*8; + /* expand storage up-front */ + if(b->endbyte+pbytes>=b->storage){ + void *ret; + if(!b->ptr) goto err; + if(b->storage>b->endbyte+pbytes+BUFFER_INCREMENT) goto err; + b->storage=b->endbyte+pbytes+BUFFER_INCREMENT; + ret=_ogg_realloc(b->buffer,b->storage); + if(!ret) goto err; + b->buffer=(unsigned char*)ret; + b->ptr=b->buffer+b->endbyte; + } + + /* copy whole octets */ if(b->endbit){ int i; /* unaligned copy. Do it the hard way. */ @@ -168,24 +213,22 @@ static void oggpack_writecopy_helper(oggpack_buffer *b, w(b,(unsigned long)(ptr[i]),8); }else{ /* aligned block copy */ - if(b->endbyte+bytes+1>=b->storage){ - b->storage=b->endbyte+bytes+BUFFER_INCREMENT; - b->buffer=(unsigned char*) _ogg_realloc(b->buffer,b->storage); - b->ptr=b->buffer+b->endbyte; - } - memmove(b->ptr,source,bytes); b->ptr+=bytes; b->endbyte+=bytes; *b->ptr=0; - } + + /* copy trailing bits */ if(bits){ if(msb) w(b,(unsigned long)(ptr[bytes]>>(8-bits)),bits); else w(b,(unsigned long)(ptr[bytes]),bits); } + return; + err: + oggpack_writeclear(b); } void oggpack_writecopy(oggpack_buffer *b,void *source,long bits){ @@ -197,6 +240,7 @@ void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits){ } void oggpack_reset(oggpack_buffer *b){ + if(!b->ptr)return; b->ptr=b->buffer; b->buffer[0]=0; b->endbit=b->endbyte=0; @@ -207,7 +251,7 @@ void oggpackB_reset(oggpack_buffer *b){ } void oggpack_writeclear(oggpack_buffer *b){ - _ogg_free(b->buffer); + if(b->buffer)_ogg_free(b->buffer); memset(b,0,sizeof(*b)); } @@ -228,13 +272,18 @@ void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ /* Read in bits without advancing the bitptr; bits <= 32 */ long oggpack_look(oggpack_buffer *b,int bits){ unsigned long ret; - unsigned long m=mask[bits]; + unsigned long m; + if(bits<0 || bits>32) return -1; + m=mask[bits]; bits+=b->endbit; - if(b->endbyte+4>=b->storage){ + if(b->endbyte >= b->storage-4){ /* not the main path */ - if(b->endbyte*8+bits>b->storage*8)return(-1); + if(b->endbyte > b->storage-((bits+7)>>3)) return -1; + /* special case to avoid reading b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting */ + else if(!bits)return(0L); } ret=b->ptr[0]>>b->endbit; @@ -243,9 +292,9 @@ long oggpack_look(oggpack_buffer *b,int bits){ if(bits>16){ ret|=b->ptr[2]<<(16-b->endbit); if(bits>24){ - ret|=b->ptr[3]<<(24-b->endbit); - if(bits>32 && b->endbit) - ret|=b->ptr[4]<<(32-b->endbit); + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]<<(32-b->endbit); } } } @@ -257,11 +306,15 @@ long oggpackB_look(oggpack_buffer *b,int bits){ unsigned long ret; int m=32-bits; + if(m<0 || m>32) return -1; bits+=b->endbit; - if(b->endbyte+4>=b->storage){ + if(b->endbyte >= b->storage-4){ /* not the main path */ - if(b->endbyte*8+bits>b->storage*8)return(-1); + if(b->endbyte > b->storage-((bits+7)>>3)) return -1; + /* special case to avoid reading b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting */ + else if(!bits)return(0L); } ret=b->ptr[0]<<(24+b->endbit); @@ -270,9 +323,9 @@ long oggpackB_look(oggpack_buffer *b,int bits){ if(bits>16){ ret|=b->ptr[2]<<(8+b->endbit); if(bits>24){ - ret|=b->ptr[3]<<(b->endbit); - if(bits>32 && b->endbit) - ret|=b->ptr[4]>>(8-b->endbit); + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); } } } @@ -291,9 +344,18 @@ long oggpackB_look1(oggpack_buffer *b){ void oggpack_adv(oggpack_buffer *b,int bits){ bits+=b->endbit; + + if(b->endbyte > b->storage-((bits+7)>>3)) goto overflow; + b->ptr+=bits/8; b->endbyte+=bits/8; b->endbit=bits&7; + return; + + overflow: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; } void oggpackB_adv(oggpack_buffer *b,int bits){ @@ -315,37 +377,45 @@ void oggpackB_adv1(oggpack_buffer *b){ /* bits <= 32 */ long oggpack_read(oggpack_buffer *b,int bits){ long ret; - unsigned long m=mask[bits]; + unsigned long m; + if(bits<0 || bits>32) goto err; + m=mask[bits]; bits+=b->endbit; - if(b->endbyte+4>=b->storage){ + if(b->endbyte >= b->storage-4){ /* not the main path */ - ret=-1L; - if(b->endbyte*8+bits>b->storage*8)goto overflow; + if(b->endbyte > b->storage-((bits+7)>>3)) goto overflow; + /* special case to avoid reading b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting */ + else if(!bits)return(0L); } ret=b->ptr[0]>>b->endbit; if(bits>8){ ret|=b->ptr[1]<<(8-b->endbit); if(bits>16){ - ret|=((unsigned long) b->ptr[2]) << (16 - b->endbit); + ret|=b->ptr[2]<<(16-b->endbit); if(bits>24){ - ret |= ((unsigned long) b->ptr[3]) << (24 - b->endbit); - if(bits>32 && b->endbit){ - ret |= ((unsigned long) b->ptr[4]) << (32 - b->endbit); - } + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit){ + ret|=b->ptr[4]<<(32-b->endbit); + } } } } ret&=m; - - overflow: - b->ptr+=bits/8; b->endbyte+=bits/8; b->endbit=bits&7; - return(ret); + return ret; + + overflow: + err: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; + return -1L; } /* bits <= 32 */ @@ -353,12 +423,15 @@ long oggpackB_read(oggpack_buffer *b,int bits){ long ret; long m=32-bits; + if(m<0 || m>32) goto err; bits+=b->endbit; if(b->endbyte+4>=b->storage){ /* not the main path */ - ret=-1L; - if(b->endbyte*8+bits>b->storage*8)goto overflow; + if(b->endbyte > b->storage-((bits+7)>>3)) goto overflow; + /* special case to avoid reading b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting */ + else if(!bits)return(0L); } ret=b->ptr[0]<<(24+b->endbit); @@ -367,64 +440,67 @@ long oggpackB_read(oggpack_buffer *b,int bits){ if(bits>16){ ret|=b->ptr[2]<<(8+b->endbit); if(bits>24){ - ret|=b->ptr[3]<<(b->endbit); - if(bits>32 && b->endbit) - ret|=b->ptr[4]>>(8-b->endbit); + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); } } } ret=((ret&0xffffffffUL)>>(m>>1))>>((m+1)>>1); - overflow: - b->ptr+=bits/8; b->endbyte+=bits/8; b->endbit=bits&7; - return(ret); + return ret; + + overflow: + err: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; + return -1L; } long oggpack_read1(oggpack_buffer *b){ long ret; - if(b->endbyte>=b->storage){ - /* not the main path */ - ret=-1L; - goto overflow; - } - + if(b->endbyte >= b->storage) goto overflow; ret=(b->ptr[0]>>b->endbit)&1; - overflow: - b->endbit++; if(b->endbit>7){ b->endbit=0; b->ptr++; b->endbyte++; } - return(ret); + return ret; + + overflow: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; + return -1L; } long oggpackB_read1(oggpack_buffer *b){ long ret; - if(b->endbyte>=b->storage){ - /* not the main path */ - ret=-1L; - goto overflow; - } - + if(b->endbyte >= b->storage) goto overflow; ret=(b->ptr[0]>>(7-b->endbit))&1; - overflow: - b->endbit++; if(b->endbit>7){ b->endbit=0; b->ptr++; b->endbyte++; } - return(ret); + return ret; + + overflow: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; + return -1L; } long oggpack_bytes(oggpack_buffer *b){ @@ -497,10 +573,10 @@ void cliptest(unsigned long *b,int vals,int bits,int *comp,int compsize){ report("looked at incorrect value!\n"); if(tbit==1) if(oggpack_look1(&r)!=(b[i]&mask[tbit])) - report("looked at single bit incorrect value!\n"); + report("looked at single bit incorrect value!\n"); if(tbit==1){ if(oggpack_read1(&r)!=(b[i]&mask[tbit])) - report("read incorrect single bit value!\n"); + report("read incorrect single bit value!\n"); }else{ if(oggpack_read(&r,tbit)!=(b[i]&mask[tbit])) report("read incorrect value!\n"); @@ -532,10 +608,10 @@ void cliptestB(unsigned long *b,int vals,int bits,int *comp,int compsize){ report("looked at incorrect value!\n"); if(tbit==1) if(oggpackB_look1(&r)!=(b[i]&mask[tbit])) - report("looked at single bit incorrect value!\n"); + report("looked at single bit incorrect value!\n"); if(tbit==1){ if(oggpackB_read1(&r)!=(b[i]&mask[tbit])) - report("read incorrect single bit value!\n"); + report("read incorrect single bit value!\n"); }else{ if(oggpackB_read(&r,tbit)!=(b[i]&mask[tbit])) report("read incorrect value!\n"); @@ -544,9 +620,190 @@ void cliptestB(unsigned long *b,int vals,int bits,int *comp,int compsize){ if(oggpackB_bytes(&r)!=bytes)report("leftover bytes after read!\n"); } +void copytest(int prefill, int copy){ + oggpack_buffer source_write; + oggpack_buffer dest_write; + oggpack_buffer source_read; + oggpack_buffer dest_read; + unsigned char *source; + unsigned char *dest; + long source_bytes,dest_bytes; + int i; + + oggpack_writeinit(&source_write); + oggpack_writeinit(&dest_write); + + for(i=0;i<(prefill+copy+7)/8;i++) + oggpack_write(&source_write,(i^0x5a)&0xff,8); + source=oggpack_get_buffer(&source_write); + source_bytes=oggpack_bytes(&source_write); + + /* prefill */ + oggpack_writecopy(&dest_write,source,prefill); + + /* check buffers; verify end byte masking */ + dest=oggpack_get_buffer(&dest_write); + dest_bytes=oggpack_bytes(&dest_write); + if(dest_bytes!=(prefill+7)/8){ + fprintf(stderr,"wrong number of bytes after prefill! %ld!=%d\n",dest_bytes,(prefill+7)/8); + exit(1); + } + oggpack_readinit(&source_read,source,source_bytes); + oggpack_readinit(&dest_read,dest,dest_bytes); + + for(i=0;i +#include #include #include "ogg.h" /* A complete description of Ogg framing exists in docs/framing.html */ -int ogg_page_version(ogg_page *og){ +int ogg_page_version(const ogg_page *og){ return((int)(og->header[4])); } -int ogg_page_continued(ogg_page *og){ +int ogg_page_continued(const ogg_page *og){ return((int)(og->header[5]&0x01)); } -int ogg_page_bos(ogg_page *og){ +int ogg_page_bos(const ogg_page *og){ return((int)(og->header[5]&0x02)); } -int ogg_page_eos(ogg_page *og){ +int ogg_page_eos(const ogg_page *og){ return((int)(og->header[5]&0x04)); } -ogg_int64_t ogg_page_granulepos(ogg_page *og){ +ogg_int64_t ogg_page_granulepos(const ogg_page *og){ unsigned char *page=og->header; - ogg_int64_t granulepos=page[13]&(0xff); + ogg_uint64_t granulepos=page[13]&(0xff); granulepos= (granulepos<<8)|(page[12]&0xff); granulepos= (granulepos<<8)|(page[11]&0xff); granulepos= (granulepos<<8)|(page[10]&0xff); @@ -52,21 +56,21 @@ ogg_int64_t ogg_page_granulepos(ogg_page *og){ granulepos= (granulepos<<8)|(page[8]&0xff); granulepos= (granulepos<<8)|(page[7]&0xff); granulepos= (granulepos<<8)|(page[6]&0xff); - return(granulepos); + return((ogg_int64_t)granulepos); } -int ogg_page_serialno(ogg_page *og){ - return(og->header[14] | - (og->header[15]<<8) | - (og->header[16]<<16) | - (og->header[17]<<24)); +int ogg_page_serialno(const ogg_page *og){ + return((int)((ogg_uint32_t)og->header[14]) | + ((ogg_uint32_t)og->header[15]<<8) | + ((ogg_uint32_t)og->header[16]<<16) | + ((ogg_uint32_t)og->header[17]<<24)); } -long ogg_page_pageno(ogg_page *og){ - return(og->header[18] | - (og->header[19]<<8) | - (og->header[20]<<16) | - (og->header[21]<<24)); +long ogg_page_pageno(const ogg_page *og){ + return((long)((ogg_uint32_t)og->header[18]) | + ((ogg_uint32_t)og->header[19]<<8) | + ((ogg_uint32_t)og->header[20]<<16) | + ((ogg_uint32_t)og->header[21]<<24)); } @@ -76,19 +80,19 @@ long ogg_page_pageno(ogg_page *og){ page, it's counted */ /* NOTE: -If a page consists of a packet begun on a previous page, and a new -packet begun (but not completed) on this page, the return will be: - ogg_page_packets(page) ==1, - ogg_page_continued(page) !=0 - -If a page happens to be a single packet that was begun on a -previous page, and spans to the next page (in the case of a three or -more page packet), the return will be: - ogg_page_packets(page) ==0, - ogg_page_continued(page) !=0 + If a page consists of a packet begun on a previous page, and a new + packet begun (but not completed) on this page, the return will be: + ogg_page_packets(page) ==1, + ogg_page_continued(page) !=0 + + If a page happens to be a single packet that was begun on a + previous page, and spans to the next page (in the case of a three or + more page packet), the return will be: + ogg_page_packets(page) ==0, + ogg_page_continued(page) !=0 */ -int ogg_page_packets(ogg_page *og){ +int ogg_page_packets(const ogg_page *og){ int i,n=og->header[26],count=0; for(i=0;iheader[27+i]<255)count++; @@ -98,90 +102,31 @@ int ogg_page_packets(ogg_page *og){ #if 0 /* helper to initialize lookup for direct-table CRC (illustrative; we - use the static init below) */ - -static ogg_uint32_t _ogg_crc_entry(unsigned long index){ - int i; - unsigned long r; - - r = index << 24; - for (i=0; i<8; i++) - if (r & 0x80000000UL) - r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator - polynomial, although we use an - unreflected alg and an init/final - of 0, not 0xffffffff */ - else - r<<=1; - return (r & 0xffffffffUL); + use the static init in crctable.h) */ + +static void _ogg_crc_init(){ + int i, j; + ogg_uint32_t polynomial, crc; + polynomial = 0x04c11db7; /* The same as the ethernet generator + polynomial, although we use an + unreflected alg and an init/final + of 0, not 0xffffffff */ + for (i = 0; i <= 0xFF; i++){ + crc = i << 24; + + for (j = 0; j < 8; j++) + crc = (crc << 1) ^ (crc & (1 << 31) ? polynomial : 0); + + crc_lookup[0][i] = crc; + } + + for (i = 0; i <= 0xFF; i++) + for (j = 1; j < 8; j++) + crc_lookup[j][i] = crc_lookup[0][(crc_lookup[j - 1][i] >> 24) & 0xFF] ^ (crc_lookup[j - 1][i] << 8); } #endif -static const ogg_uint32_t crc_lookup[256]={ - 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, - 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, - 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, - 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, - 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, - 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, - 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, - 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, - 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, - 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, - 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, - 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, - 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, - 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, - 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, - 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, - 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, - 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, - 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, - 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, - 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, - 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, - 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, - 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, - 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, - 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, - 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, - 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, - 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, - 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, - 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, - 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, - 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, - 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, - 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, - 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, - 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, - 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, - 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, - 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, - 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, - 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, - 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, - 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, - 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, - 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, - 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, - 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, - 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, - 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, - 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, - 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, - 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, - 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, - 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, - 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, - 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, - 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, - 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, - 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, - 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, - 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, - 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, - 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; +#include "crctable.h" /* init the encode/decode logical stream state */ @@ -189,11 +134,16 @@ int ogg_stream_init(ogg_stream_state *os,int serialno){ if(os){ memset(os,0,sizeof(*os)); os->body_storage=16*1024; - os->body_data=(unsigned char*) _ogg_malloc(os->body_storage*sizeof(*os->body_data)); - os->lacing_storage=1024; + + os->body_data= (unsigned char*) _ogg_malloc(os->body_storage*sizeof(*os->body_data)); os->lacing_vals=(int*) _ogg_malloc(os->lacing_storage*sizeof(*os->lacing_vals)); - os->granule_vals=(ogg_int64_t*) _ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals)); + os->granule_vals= (ogg_int64_t*) _ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals)); + + if(!os->body_data || !os->lacing_vals || !os->granule_vals){ + ogg_stream_clear(os); + return -1; + } os->serialno=serialno; @@ -202,6 +152,12 @@ int ogg_stream_init(ogg_stream_state *os,int serialno){ return(-1); } +/* async/delayed error detection for the ogg_stream_state */ +int ogg_stream_check(ogg_stream_state *os){ + if(!os || !os->body_data) return -1; + return 0; +} + /* _clear does not free os, only the non-flat storage within */ int ogg_stream_clear(ogg_stream_state *os){ if(os){ @@ -225,29 +181,80 @@ int ogg_stream_destroy(ogg_stream_state *os){ /* Helpers for ogg_stream_encode; this keeps the structure and what's happening fairly clear */ -static void _os_body_expand(ogg_stream_state *os,int needed){ - if(os->body_storage<=os->body_fill+needed){ - os->body_storage+=(needed+1024); - os->body_data=(unsigned char*) _ogg_realloc(os->body_data,os->body_storage*sizeof(*os->body_data)); +static int _os_body_expand(ogg_stream_state *os,long needed){ + if(os->body_storage-needed<=os->body_fill){ + long body_storage; + void *ret; + if(os->body_storage>LONG_MAX-needed){ + ogg_stream_clear(os); + return -1; + } + body_storage=os->body_storage+needed; + if(body_storagebody_data,body_storage*sizeof(*os->body_data)); + if(!ret){ + ogg_stream_clear(os); + return -1; + } + os->body_storage=body_storage; + os->body_data=(unsigned char*)ret; } + return 0; } -static void _os_lacing_expand(ogg_stream_state *os,int needed){ - if(os->lacing_storage<=os->lacing_fill+needed){ - os->lacing_storage+=(needed+32); - os->lacing_vals=(int*)_ogg_realloc(os->lacing_vals,os->lacing_storage*sizeof(*os->lacing_vals)); - os->granule_vals=(ogg_int64_t*)_ogg_realloc(os->granule_vals,os->lacing_storage*sizeof(*os->granule_vals)); +static int _os_lacing_expand(ogg_stream_state *os,long needed){ + if(os->lacing_storage-needed<=os->lacing_fill){ + long lacing_storage; + void *ret; + if(os->lacing_storage>LONG_MAX-needed){ + ogg_stream_clear(os); + return -1; + } + lacing_storage=os->lacing_storage+needed; + if(lacing_storagelacing_vals,lacing_storage*sizeof(*os->lacing_vals)); + if(!ret){ + ogg_stream_clear(os); + return -1; + } + os->lacing_vals=(int*)ret; + ret=_ogg_realloc(os->granule_vals,lacing_storage* + sizeof(*os->granule_vals)); + if(!ret){ + ogg_stream_clear(os); + return -1; + } + os->granule_vals=(ogg_int64_t*)ret; + os->lacing_storage=lacing_storage; } + return 0; } /* checksum the page */ /* Direct table CRC; note that this will be faster in the future if we - perform the checksum silmultaneously with other copies */ + perform the checksum simultaneously with other copies */ + +static ogg_uint32_t _os_update_crc(ogg_uint32_t crc, unsigned char *buffer, int size){ + while (size>=8){ + crc^=((ogg_uint32_t)buffer[0]<<24)|((ogg_uint32_t)buffer[1]<<16)|((ogg_uint32_t)buffer[2]<<8)|((ogg_uint32_t)buffer[3]); + + crc=crc_lookup[7][ crc>>24 ]^crc_lookup[6][(crc>>16)&0xFF]^ + crc_lookup[5][(crc>> 8)&0xFF]^crc_lookup[4][ crc &0xFF]^ + crc_lookup[3][buffer[4] ]^crc_lookup[2][buffer[5] ]^ + crc_lookup[1][buffer[6] ]^crc_lookup[0][buffer[7] ]; + + buffer+=8; + size-=8; + } + + while (size--) + crc=(crc<<8)^crc_lookup[0][((crc >> 24)&0xff)^*buffer++]; + return crc; +} void ogg_page_checksum_set(ogg_page *og){ if(og){ ogg_uint32_t crc_reg=0; - int i; /* safety; needed for API behavior, but not framing code */ og->header[22]=0; @@ -255,10 +262,8 @@ void ogg_page_checksum_set(ogg_page *og){ og->header[24]=0; og->header[25]=0; - for(i=0;iheader_len;i++) - crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]]; - for(i=0;ibody_len;i++) - crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]]; + crc_reg=_os_update_crc(crc_reg,og->header,og->header_len); + crc_reg=_os_update_crc(crc_reg,og->body,og->body_len); og->header[22]=(unsigned char)(crc_reg&0xff); og->header[23]=(unsigned char)((crc_reg>>8)&0xff); @@ -268,8 +273,21 @@ void ogg_page_checksum_set(ogg_page *og){ } /* submit data to the internal buffer of the framing engine */ -int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ - int lacing_vals=op->bytes/255+1,i; +int ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov, int count, + long e_o_s, ogg_int64_t granulepos){ + + long bytes = 0, lacing_vals; + int i; + + if(ogg_stream_check(os)) return -1; + if(!iov) return 0; + + for (i = 0; i < count; ++i){ + if(iov[i].iov_len>LONG_MAX) return -1; + if(bytes>LONG_MAX-(long)iov[i].iov_len) return -1; + bytes += (long)iov[i].iov_len; + } + lacing_vals=bytes/255+1; if(os->body_returned){ /* advance packet data according to the body_returned pointer. We @@ -279,29 +297,31 @@ int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ os->body_fill-=os->body_returned; if(os->body_fill) memmove(os->body_data,os->body_data+os->body_returned, - os->body_fill); + os->body_fill); os->body_returned=0; } /* make sure we have the buffer storage */ - _os_body_expand(os,op->bytes); - _os_lacing_expand(os,lacing_vals); + if(_os_body_expand(os,bytes) || _os_lacing_expand(os,lacing_vals)) + return -1; /* Copy in the submitted packet. Yes, the copy is a waste; this is the liability of overly clean abstraction for the time being. It will actually be fairly easy to eliminate the extra copy in the future */ - memcpy(os->body_data+os->body_fill,op->packet,op->bytes); - os->body_fill+=op->bytes; + for (i = 0; i < count; ++i) { + memcpy(os->body_data+os->body_fill, iov[i].iov_base, iov[i].iov_len); + os->body_fill += (int)iov[i].iov_len; + } /* Store lacing vals for this packet */ for(i=0;ilacing_vals[os->lacing_fill+i]=255; os->granule_vals[os->lacing_fill+i]=os->granulepos; } - os->lacing_vals[os->lacing_fill+i]=(op->bytes)%255; - os->granulepos=os->granule_vals[os->lacing_fill+i]=op->granulepos; + os->lacing_vals[os->lacing_fill+i]=bytes%255; + os->granulepos=os->granule_vals[os->lacing_fill+i]=granulepos; /* flag the first segment as the beginning of the packet */ os->lacing_vals[os->lacing_fill]|= 0x100; @@ -311,26 +331,22 @@ int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ /* for the sake of completeness */ os->packetno++; - if(op->e_o_s)os->e_o_s=1; + if(e_o_s)os->e_o_s=1; return(0); } -/* This will flush remaining packets into a page (returning nonzero), - even if there is not enough data to trigger a flush normally - (undersized page). If there are no packets or partial packets to - flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will - try to flush a normal sized page like ogg_stream_pageout; a call to - ogg_stream_flush does not guarantee that all packets have flushed. - Only a return value of 0 from ogg_stream_flush indicates all packet - data is flushed into pages. - - since ogg_stream_flush will flush the last page in a stream even if - it's undersized, you almost certainly want to use ogg_stream_pageout - (and *not* ogg_stream_flush) unless you specifically need to flush - an page regardless of size in the middle of a stream. */ +int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ + ogg_iovec_t iov; + iov.iov_base = op->packet; + iov.iov_len = op->bytes; + return ogg_stream_iovecin(os, &iov, 1, op->e_o_s, op->granulepos); +} -int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ +/* Conditionally flush a page; force==0 will only flush nominal-size + pages, force==1 forces us to flush a page regardless of page size + so long as there's any data available at all. */ +static int ogg_stream_flush_i(ogg_stream_state *os,ogg_page *og, int force, int nfill){ int i; int vals=0; int maxvals=(os->lacing_fill>255?255:os->lacing_fill); @@ -338,7 +354,8 @@ int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ long acc=0; ogg_int64_t granule_pos=-1; - if(maxvals==0)return(0); + if(ogg_stream_check(os)) return(0); + if(maxvals==0) return(0); /* construct a page */ /* decide how many segments to include */ @@ -349,19 +366,40 @@ int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ granule_pos=0; for(vals=0;valslacing_vals[vals]&0x0ff)<255){ - vals++; - break; + vals++; + break; } } }else{ + + /* The extra packets_done, packet_just_done logic here attempts to do two things: + 1) Don't unnecessarily span pages. + 2) Unless necessary, don't flush pages if there are less than four packets on + them; this expands page size to reduce unnecessary overhead if incoming packets + are large. + These are not necessary behaviors, just 'always better than naive flushing' + without requiring an application to explicitly request a specific optimized + behavior. We'll want an explicit behavior setup pathway eventually as well. */ + + int packets_done=0; + int packet_just_done=0; for(vals=0;vals4096)break; + if(acc>nfill && packet_just_done>=4){ + force=1; + break; + } acc+=os->lacing_vals[vals]&0x0ff; - if((os->lacing_vals[vals]&0xff)<255) + if((os->lacing_vals[vals]&0xff)<255){ granule_pos=os->granule_vals[vals]; + packet_just_done=++packets_done; + }else + packet_just_done=0; } + if(vals==255)force=1; } + if(!force) return(0); + /* construct the header in temp storage */ memcpy(os->header,"OggS",4); @@ -395,10 +433,10 @@ int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ /* 32 bits of page counter (we have both counter and page header because this val can roll over) */ if(os->pageno==-1)os->pageno=0; /* because someone called - stream_reset; this would be a - strange thing to do in an - encode stream, but it has - plausible uses */ + stream_reset; this would be a + strange thing to do in an + encode stream, but it has + plausible uses */ { long pageno=os->pageno++; for(i=18;i<22;i++){ @@ -439,26 +477,64 @@ int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ return(1); } +/* This will flush remaining packets into a page (returning nonzero), + even if there is not enough data to trigger a flush normally + (undersized page). If there are no packets or partial packets to + flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will + try to flush a normal sized page like ogg_stream_pageout; a call to + ogg_stream_flush does not guarantee that all packets have flushed. + Only a return value of 0 from ogg_stream_flush indicates all packet + data is flushed into pages. + + since ogg_stream_flush will flush the last page in a stream even if + it's undersized, you almost certainly want to use ogg_stream_pageout + (and *not* ogg_stream_flush) unless you specifically need to flush + a page regardless of size in the middle of a stream. */ + +int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ + return ogg_stream_flush_i(os,og,1,4096); +} + +/* Like the above, but an argument is provided to adjust the nominal + page size for applications which are smart enough to provide their + own delay based flushing */ + +int ogg_stream_flush_fill(ogg_stream_state *os,ogg_page *og, int nfill){ + return ogg_stream_flush_i(os,og,1,nfill); +} /* This constructs pages from buffered packet segments. The pointers returned are to static buffers; do not free. The returned buffers are good only until the next call (using the same ogg_stream_state) */ int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ + int force=0; + if(ogg_stream_check(os)) return 0; if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ - os->body_fill-os->body_returned > 4096 ||/* 'page nominal size' case */ - os->lacing_fill>=255 || /* 'segment table full' case */ - (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */ + (os->lacing_fill&&!os->b_o_s)) /* 'initial header page' case */ + force=1; - return(ogg_stream_flush(os,og)); - } + return(ogg_stream_flush_i(os,og,force,4096)); +} - /* not enough data to construct a page and not end of stream */ - return(0); +/* Like the above, but an argument is provided to adjust the nominal +page size for applications which are smart enough to provide their +own delay based flushing */ + +int ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill){ + int force=0; + if(ogg_stream_check(os)) return 0; + + if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ + (os->lacing_fill&&!os->b_o_s)) /* 'initial header page' case */ + force=1; + + return(ogg_stream_flush_i(os,og,force,nfill)); } int ogg_stream_eos(ogg_stream_state *os){ + if(ogg_stream_check(os)) return 1; return os->e_o_s; } @@ -480,6 +556,7 @@ int ogg_stream_eos(ogg_stream_state *os){ /* initialize the struct to a known state */ int ogg_sync_init(ogg_sync_state *oy){ if(oy){ + oy->storage = -1; /* used as a readiness flag */ memset(oy,0,sizeof(*oy)); } return(0); @@ -489,7 +566,7 @@ int ogg_sync_init(ogg_sync_state *oy){ int ogg_sync_clear(ogg_sync_state *oy){ if(oy){ if(oy->data)_ogg_free(oy->data); - ogg_sync_init(oy); + memset(oy,0,sizeof(*oy)); } return(0); } @@ -502,7 +579,13 @@ int ogg_sync_destroy(ogg_sync_state *oy){ return(0); } +int ogg_sync_check(ogg_sync_state *oy){ + if(oy->storage<0) return -1; + return 0; +} + char *ogg_sync_buffer(ogg_sync_state *oy, long size){ + if(ogg_sync_check(oy)) return NULL; /* first, clear out any space that has been previously returned */ if(oy->returned){ @@ -515,11 +598,17 @@ char *ogg_sync_buffer(ogg_sync_state *oy, long size){ if(size>oy->storage-oy->fill){ /* We need to extend the internal buffer */ long newsize=size+oy->fill+4096; /* an extra page to be nice */ + void *ret; if(oy->data) - oy->data=(unsigned char*) _ogg_realloc(oy->data,newsize); + ret=_ogg_realloc(oy->data,newsize); else - oy->data=(unsigned char*) _ogg_malloc(newsize); + ret=_ogg_malloc(newsize); + if(!ret){ + ogg_sync_clear(oy); + return NULL; + } + oy->data=(unsigned char*)ret; oy->storage=newsize; } @@ -528,7 +617,8 @@ char *ogg_sync_buffer(ogg_sync_state *oy, long size){ } int ogg_sync_wrote(ogg_sync_state *oy, long bytes){ - if(oy->fill+bytes>oy->storage)return(-1); + if(ogg_sync_check(oy))return -1; + if(oy->fill+bytes>oy->storage)return -1; oy->fill+=bytes; return(0); } @@ -548,6 +638,8 @@ long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ unsigned char *next; long bytes=oy->fill-oy->returned; + if(ogg_sync_check(oy))return 0; + if(oy->headerbytes==0){ int headerbytes,i; if(bytes<27)return(0); /* not enough for a header */ @@ -586,20 +678,19 @@ long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ /* Compare */ if(memcmp(chksum,page+22,4)){ /* D'oh. Mismatch! Corrupt page (or miscapture and not a page - at all) */ + at all) */ /* replace the computed checksum with the one actually read in */ memcpy(page+22,chksum,4); +#ifndef DISABLE_CRC /* Bad checksum. Lose sync */ goto sync_fail; +#endif } } /* yes, have a whole page all ready to go */ { - unsigned char *page=oy->data+oy->returned; - long bytes; - if(og){ og->header=page; og->header_len=oy->headerbytes; @@ -624,12 +715,12 @@ long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ if(!next) next=oy->data+oy->fill; - oy->returned=next-oy->data; - return(-(next-page)); + oy->returned=(int)(next-oy->data); + return((long)-(next-page)); } /* sync the stream and get a page. Keep trying until we find a page. - Supress 'sync errors' after reporting the first. + Suppress 'sync errors' after reporting the first. return values: -1) recapture (hole in data) @@ -641,6 +732,8 @@ long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ + if(ogg_sync_check(oy))return 0; + /* all we need to do is verify a page at the head of the stream buffer. If it doesn't verify, we look for the next potential frame */ @@ -685,6 +778,8 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ long pageno=ogg_page_pageno(og); int segments=header[26]; + if(ogg_stream_check(os)) return -1; + /* clean up 'returned data' */ { long lr=os->lacing_returned; @@ -694,17 +789,17 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ if(br){ os->body_fill-=br; if(os->body_fill) - memmove(os->body_data,os->body_data+br,os->body_fill); + memmove(os->body_data,os->body_data+br,os->body_fill); os->body_returned=0; } if(lr){ /* segment table */ if(os->lacing_fill-lr){ - memmove(os->lacing_vals,os->lacing_vals+lr, - (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); - memmove(os->granule_vals,os->granule_vals+lr, - (os->lacing_fill-lr)*sizeof(*os->granule_vals)); + memmove(os->lacing_vals,os->lacing_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->granule_vals)); } os->lacing_fill-=lr; os->lacing_packet-=lr; @@ -716,7 +811,7 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ if(serialno!=os->serialno)return(-1); if(version>0)return(-1); - _os_lacing_expand(os,segments+1); + if(_os_lacing_expand(os,segments+1)) return -1; /* are we in sequence? */ if(pageno!=os->pageno){ @@ -738,22 +833,23 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ some segments */ if(continued){ if(os->lacing_fill<1 || + (os->lacing_vals[os->lacing_fill-1]&0xff)<255 || os->lacing_vals[os->lacing_fill-1]==0x400){ bos=0; for(;segptrbody_data+os->body_fill,body,bodysize); os->body_fill+=bodysize; } @@ -766,8 +862,8 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ os->granule_vals[os->lacing_fill]=-1; if(bos){ - os->lacing_vals[os->lacing_fill]|=0x100; - bos=0; + os->lacing_vals[os->lacing_fill]|=0x100; + bos=0; } if(val<255)saved=os->lacing_fill; @@ -798,6 +894,8 @@ int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ /* clear things to an initial state. Good to call, eg, before seeking */ int ogg_sync_reset(ogg_sync_state *oy){ + if(ogg_sync_check(oy))return -1; + oy->fill=0; oy->returned=0; oy->unsynced=0; @@ -807,6 +905,8 @@ int ogg_sync_reset(ogg_sync_state *oy){ } int ogg_stream_reset(ogg_stream_state *os){ + if(ogg_stream_check(os)) return -1; + os->body_fill=0; os->body_returned=0; @@ -826,6 +926,7 @@ int ogg_stream_reset(ogg_stream_state *os){ } int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ + if(ogg_stream_check(os)) return -1; ogg_stream_reset(os); os->serialno=serialno; return(0); @@ -856,7 +957,7 @@ static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ /* Gather the whole packet. We'll have no holes or a partial packet */ { int size=os->lacing_vals[ptr]&0xff; - int bytes=size; + long bytes=size; int eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */ int bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */ @@ -886,10 +987,12 @@ static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ } int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ + if(ogg_stream_check(os)) return 0; return _packetout(os,op,1); } int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ + if(ogg_stream_check(os)) return 0; return _packetout(os,op,0); } @@ -904,17 +1007,17 @@ void ogg_packet_clear(ogg_packet *op) { ogg_stream_state os_en, os_de; ogg_sync_state oy; -void checkpacket(ogg_packet *op,int len, int no, int pos){ +void checkpacket(ogg_packet *op,long len, int no, long pos){ long j; static int sequence=0; static int lastno=0; if(op->bytes!=len){ - fprintf(stderr,"incorrect packet length!\n"); + fprintf(stderr,"incorrect packet length (%ld != %ld)!\n",op->bytes,len); exit(1); } if(op->granulepos!=pos){ - fprintf(stderr,"incorrect packet position!\n"); + fprintf(stderr,"incorrect packet granpos (%ld != %ld)!\n",(long)op->granulepos,pos); exit(1); } @@ -930,7 +1033,7 @@ void checkpacket(ogg_packet *op,int len, int no, int pos){ lastno=no; if(op->packetno!=sequence){ fprintf(stderr,"incorrect packet sequence %ld != %d\n", - (long)(op->packetno),sequence); + (long)(op->packetno),sequence); exit(1); } @@ -938,7 +1041,7 @@ void checkpacket(ogg_packet *op,int len, int no, int pos){ for(j=0;jbytes;j++) if(op->packet[j]!=((j+no)&0xff)){ fprintf(stderr,"body data mismatch (1) at pos %ld: %x!=%lx!\n\n", - j,op->packet[j],(j+no)&0xff); + j,op->packet[j],(j+no)&0xff); exit(1); } } @@ -949,7 +1052,7 @@ void check_page(unsigned char *data,const int *header,ogg_page *og){ for(j=0;jbody_len;j++) if(og->body[j]!=data[j]){ fprintf(stderr,"body data mismatch (2) at pos %ld: %x!=%x!\n\n", - j,data[j],og->body[j]); + j,data[j],og->body[j]); exit(1); } @@ -958,14 +1061,14 @@ void check_page(unsigned char *data,const int *header,ogg_page *og){ if(og->header[j]!=header[j]){ fprintf(stderr,"header content mismatch at pos %ld:\n",j); for(j=0;jheader[j]); + fprintf(stderr," (%ld)%02x:%02x",j,header[j],og->header[j]); fprintf(stderr,"\n"); exit(1); } } if(og->header_len!=header[26]+27){ fprintf(stderr,"header length incorrect! (%ld!=%d)\n", - og->header_len,header[26]+27); + og->header_len,header[26]+27); exit(1); } } @@ -974,21 +1077,21 @@ void print_header(ogg_page *og){ int j; fprintf(stderr,"\nHEADER:\n"); fprintf(stderr," capture: %c %c %c %c version: %d flags: %x\n", - og->header[0],og->header[1],og->header[2],og->header[3], - (int)og->header[4],(int)og->header[5]); + og->header[0],og->header[1],og->header[2],og->header[3], + (int)og->header[4],(int)og->header[5]); fprintf(stderr," granulepos: %d serialno: %d pageno: %ld\n", - (og->header[9]<<24)|(og->header[8]<<16)| - (og->header[7]<<8)|og->header[6], - (og->header[17]<<24)|(og->header[16]<<16)| - (og->header[15]<<8)|og->header[14], - ((long)(og->header[21])<<24)|(og->header[20]<<16)| - (og->header[19]<<8)|og->header[18]); + (og->header[9]<<24)|(og->header[8]<<16)| + (og->header[7]<<8)|og->header[6], + (og->header[17]<<24)|(og->header[16]<<16)| + (og->header[15]<<8)|og->header[14], + ((long)(og->header[21])<<24)|(og->header[20]<<16)| + (og->header[19]<<8)|og->header[18]); fprintf(stderr," checksum: %02x:%02x:%02x:%02x\n segments: %d (", - (int)og->header[22],(int)og->header[23], - (int)og->header[24],(int)og->header[25], - (int)og->header[26]); + (int)og->header[22],(int)og->header[23], + (int)og->header[24],(int)og->header[25], + (int)og->header[26]); for(j=27;jheader_len;j++) fprintf(stderr,"%d ",(int)og->header[j]); @@ -1017,192 +1120,367 @@ void error(void){ /* 17 only */ const int head1_0[] = {0x4f,0x67,0x67,0x53,0,0x06, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,0,0,0,0, - 0x15,0xed,0xec,0x91, - 1, - 17}; + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x15,0xed,0xec,0x91, + 1, + 17}; /* 17, 254, 255, 256, 500, 510, 600 byte, pad */ const int head1_1[] = {0x4f,0x67,0x67,0x53,0,0x02, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,0,0,0,0, - 0x59,0x10,0x6c,0x2c, - 1, - 17}; + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x59,0x10,0x6c,0x2c, + 1, + 17}; const int head2_1[] = {0x4f,0x67,0x67,0x53,0,0x04, - 0x07,0x18,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,1,0,0,0, - 0x89,0x33,0x85,0xce, - 13, - 254,255,0,255,1,255,245,255,255,0, - 255,255,90}; + 0x07,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x89,0x33,0x85,0xce, + 13, + 254,255,0,255,1,255,245,255,255,0, + 255,255,90}; /* nil packets; beginning,middle,end */ const int head1_2[] = {0x4f,0x67,0x67,0x53,0,0x02, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,0,0,0,0, - 0xff,0x7b,0x23,0x17, - 1, - 0}; + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; const int head2_2[] = {0x4f,0x67,0x67,0x53,0,0x04, - 0x07,0x28,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,1,0,0,0, - 0x5c,0x3f,0x66,0xcb, - 17, - 17,254,255,0,0,255,1,0,255,245,255,255,0, - 255,255,90,0}; + 0x07,0x28,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x5c,0x3f,0x66,0xcb, + 17, + 17,254,255,0,0,255,1,0,255,245,255,255,0, + 255,255,90,0}; /* large initial packet */ const int head1_3[] = {0x4f,0x67,0x67,0x53,0,0x02, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,0,0,0,0, - 0x01,0x27,0x31,0xaa, - 18, - 255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,10}; + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x01,0x27,0x31,0xaa, + 18, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,10}; const int head2_3[] = {0x4f,0x67,0x67,0x53,0,0x04, - 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,1,0,0,0, - 0x7f,0x4e,0x8a,0xd2, - 4, - 255,4,255,0}; + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x7f,0x4e,0x8a,0xd2, + 4, + 255,4,255,0}; /* continuing packet test */ const int head1_4[] = {0x4f,0x67,0x67,0x53,0,0x02, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,0,0,0,0, - 0xff,0x7b,0x23,0x17, - 1, - 0}; + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; const int head2_4[] = {0x4f,0x67,0x67,0x53,0,0x00, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0x01,0x02,0x03,0x04,1,0,0,0, - 0x54,0x05,0x51,0xc8, - 17, - 255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255}; + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xf8,0x3c,0x19,0x79, + 255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255}; const int head3_4[] = {0x4f,0x67,0x67,0x53,0,0x05, - 0x07,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,2,0,0,0, - 0xc8,0xc3,0xcb,0xed, - 5, - 10,255,4,255,0}; - + 0x07,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x38,0xe6,0xb6,0x28, + 6, + 255,220,255,4,255,0}; + + +/* spill expansion test */ +const int head1_4b[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_4b[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xce,0x8f,0x17,0x1a, + 23, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,10,255,4,255,0,0}; + + +const int head3_4b[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x14,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x9b,0xb2,0x50,0xa1, + 1, + 0}; /* page with the 255 segment limit */ const int head1_5[] = {0x4f,0x67,0x67,0x53,0,0x02, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,0,0,0,0, - 0xff,0x7b,0x23,0x17, - 1, - 0}; + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; const int head2_5[] = {0x4f,0x67,0x67,0x53,0,0x00, - 0x07,0xfc,0x03,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,1,0,0,0, - 0xed,0x2a,0x2e,0xa7, - 255, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10}; + 0x07,0xfc,0x03,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xed,0x2a,0x2e,0xa7, + 255, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10}; const int head3_5[] = {0x4f,0x67,0x67,0x53,0,0x04, - 0x07,0x00,0x04,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,2,0,0,0, - 0x6c,0x3b,0x82,0x3d, - 1, - 50}; + 0x07,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x6c,0x3b,0x82,0x3d, + 1, + 50}; /* packet that overspans over an entire page */ const int head1_6[] = {0x4f,0x67,0x67,0x53,0,0x02, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,0,0,0,0, - 0xff,0x7b,0x23,0x17, - 1, - 0}; + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; const int head2_6[] = {0x4f,0x67,0x67,0x53,0,0x00, - 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,1,0,0,0, - 0x3c,0xd9,0x4d,0x3f, - 17, - 100,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255}; + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x68,0x22,0x7c,0x3d, + 255, + 100, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255}; const int head3_6[] = {0x4f,0x67,0x67,0x53,0,0x01, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0x01,0x02,0x03,0x04,2,0,0,0, - 0x01,0xd2,0xe5,0xe5, - 17, - 255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255}; + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xf4,0x87,0xba,0xf3, + 255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255}; const int head4_6[] = {0x4f,0x67,0x67,0x53,0,0x05, - 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,3,0,0,0, - 0xef,0xdd,0x88,0xde, - 7, - 255,255,75,255,4,255,0}; + 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,3,0,0,0, + 0xf7,0x2f,0x6c,0x60, + 5, + 254,255,4,255,0}; /* packet that overspans over an entire page */ const int head1_7[] = {0x4f,0x67,0x67,0x53,0,0x02, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,0,0,0,0, - 0xff,0x7b,0x23,0x17, - 1, - 0}; + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; const int head2_7[] = {0x4f,0x67,0x67,0x53,0,0x00, - 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,1,0,0,0, - 0x3c,0xd9,0x4d,0x3f, - 17, - 100,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255}; + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x68,0x22,0x7c,0x3d, + 255, + 100, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255}; const int head3_7[] = {0x4f,0x67,0x67,0x53,0,0x05, - 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x02,0x03,0x04,2,0,0,0, - 0xd4,0xe0,0x60,0xe5, - 1,0}; + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xd4,0xe0,0x60,0xe5, + 1, + 0}; + +int compare_packet(const ogg_packet *op1, const ogg_packet *op2){ + if(op1->packet!=op2->packet){ + fprintf(stderr,"op1->packet != op2->packet\n"); + return(1); + } + if(op1->bytes!=op2->bytes){ + fprintf(stderr,"op1->bytes != op2->bytes\n"); + return(1); + } + if(op1->b_o_s!=op2->b_o_s){ + fprintf(stderr,"op1->b_o_s != op2->b_o_s\n"); + return(1); + } + if(op1->e_o_s!=op2->e_o_s){ + fprintf(stderr,"op1->e_o_s != op2->e_o_s\n"); + return(1); + } + if(op1->granulepos!=op2->granulepos){ + fprintf(stderr,"op1->granulepos != op2->granulepos\n"); + return(1); + } + if(op1->packetno!=op2->packetno){ + fprintf(stderr,"op1->packetno != op2->packetno\n"); + return(1); + } + return(0); +} void test_pack(const int *pl, const int **headers, int byteskip, - int pageskip, int packetskip){ + int pageskip, int packetskip){ unsigned char *data=_ogg_malloc(1024*1024); /* for scripted test cases only */ long inptr=0; long outptr=0; @@ -1246,109 +1524,109 @@ void test_pack(const int *pl, const int **headers, int byteskip, ogg_page og; while(ogg_stream_pageout(&os_en,&og)){ - /* We have a page. Check it carefully */ - - fprintf(stderr,"%ld, ",pageno); - - if(headers[pageno]==NULL){ - fprintf(stderr,"coded too many pages!\n"); - exit(1); - } - - check_page(data+outptr,headers[pageno],&og); - - outptr+=og.body_len; - pageno++; - if(pageskip){ - bosflag=1; - pageskip--; - deptr+=og.body_len; - } - - /* have a complete page; submit it to sync/decode */ - - { - ogg_page og_de; - ogg_packet op_de,op_de2; - char *buf=ogg_sync_buffer(&oy,og.header_len+og.body_len); - char *next=buf; - byteskipcount+=og.header_len; - if(byteskipcount>byteskip){ - memcpy(next,og.header,byteskipcount-byteskip); - next+=byteskipcount-byteskip; - byteskipcount=byteskip; - } - - byteskipcount+=og.body_len; - if(byteskipcount>byteskip){ - memcpy(next,og.body,byteskipcount-byteskip); - next+=byteskipcount-byteskip; - byteskipcount=byteskip; - } - - ogg_sync_wrote(&oy,next-buf); - - while(1){ - int ret=ogg_sync_pageout(&oy,&og_de); - if(ret==0)break; - if(ret<0)continue; - /* got a page. Happy happy. Verify that it's good. */ - - fprintf(stderr,"(%ld), ",pageout); - - check_page(data+deptr,headers[pageout],&og_de); - deptr+=og_de.body_len; - pageout++; - - /* submit it to deconstitution */ - ogg_stream_pagein(&os_de,&og_de); - - /* packets out? */ - while(ogg_stream_packetpeek(&os_de,&op_de2)>0){ - ogg_stream_packetpeek(&os_de,NULL); - ogg_stream_packetout(&os_de,&op_de); /* just catching them all */ - - /* verify peek and out match */ - if(memcmp(&op_de,&op_de2,sizeof(op_de))){ - fprintf(stderr,"packetout != packetpeek! pos=%ld\n", - depacket); - exit(1); - } - - /* verify the packet! */ - /* check data */ - if(memcmp(data+depacket,op_de.packet,op_de.bytes)){ - fprintf(stderr,"packet data mismatch in decode! pos=%ld\n", - depacket); - exit(1); - } - /* check bos flag */ - if(bosflag==0 && op_de.b_o_s==0){ - fprintf(stderr,"b_o_s flag not set on packet!\n"); - exit(1); - } - if(bosflag && op_de.b_o_s){ - fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); - exit(1); - } - bosflag=1; - depacket+=op_de.bytes; - - /* check eos flag */ - if(eosflag){ - fprintf(stderr,"Multiple decoded packets with eos flag!\n"); - exit(1); - } - - if(op_de.e_o_s)eosflag=1; - - /* check granulepos flag */ - if(op_de.granulepos!=-1){ - fprintf(stderr," granule:%ld ",(long)op_de.granulepos); - } - } - } - } + /* We have a page. Check it carefully */ + + fprintf(stderr,"%ld, ",pageno); + + if(headers[pageno]==NULL){ + fprintf(stderr,"coded too many pages!\n"); + exit(1); + } + + check_page(data+outptr,headers[pageno],&og); + + outptr+=og.body_len; + pageno++; + if(pageskip){ + bosflag=1; + pageskip--; + deptr+=og.body_len; + } + + /* have a complete page; submit it to sync/decode */ + + { + ogg_page og_de; + ogg_packet op_de,op_de2; + char *buf=ogg_sync_buffer(&oy,og.header_len+og.body_len); + char *next=buf; + byteskipcount+=og.header_len; + if(byteskipcount>byteskip){ + memcpy(next,og.header,byteskipcount-byteskip); + next+=byteskipcount-byteskip; + byteskipcount=byteskip; + } + + byteskipcount+=og.body_len; + if(byteskipcount>byteskip){ + memcpy(next,og.body,byteskipcount-byteskip); + next+=byteskipcount-byteskip; + byteskipcount=byteskip; + } + + ogg_sync_wrote(&oy,next-buf); + + while(1){ + int ret=ogg_sync_pageout(&oy,&og_de); + if(ret==0)break; + if(ret<0)continue; + /* got a page. Happy happy. Verify that it's good. */ + + fprintf(stderr,"(%d), ",pageout); + + check_page(data+deptr,headers[pageout],&og_de); + deptr+=og_de.body_len; + pageout++; + + /* submit it to deconstitution */ + ogg_stream_pagein(&os_de,&og_de); + + /* packets out? */ + while(ogg_stream_packetpeek(&os_de,&op_de2)>0){ + ogg_stream_packetpeek(&os_de,NULL); + ogg_stream_packetout(&os_de,&op_de); /* just catching them all */ + + /* verify peek and out match */ + if(compare_packet(&op_de,&op_de2)){ + fprintf(stderr,"packetout != packetpeek! pos=%ld\n", + depacket); + exit(1); + } + + /* verify the packet! */ + /* check data */ + if(memcmp(data+depacket,op_de.packet,op_de.bytes)){ + fprintf(stderr,"packet data mismatch in decode! pos=%ld\n", + depacket); + exit(1); + } + /* check bos flag */ + if(bosflag==0 && op_de.b_o_s==0){ + fprintf(stderr,"b_o_s flag not set on packet!\n"); + exit(1); + } + if(bosflag && op_de.b_o_s){ + fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); + exit(1); + } + bosflag=1; + depacket+=op_de.bytes; + + /* check eos flag */ + if(eosflag){ + fprintf(stderr,"Multiple decoded packets with eos flag!\n"); + exit(1); + } + + if(op_de.e_o_s)eosflag=1; + + /* check granulepos flag */ + if(op_de.granulepos!=-1){ + fprintf(stderr," granule:%ld ",(long)op_de.granulepos); + } + } + } + } } } } @@ -1426,49 +1704,59 @@ int main(void){ } { - /* continuing packet test */ - const int packets[]={0,4345,259,255,-1}; + /* continuing packet test; with page spill expansion, we have to + overflow the lacing table. */ + const int packets[]={0,65500,259,255,-1}; const int *headret[]={head1_4,head2_4,head3_4,NULL}; fprintf(stderr,"testing single packet page span... "); test_pack(packets,headret,0,0,0); } + { + /* spill expand packet test */ + const int packets[]={0,4345,259,255,0,0,-1}; + const int *headret[]={head1_4b,head2_4b,head3_4b,NULL}; + + fprintf(stderr,"testing page spill expansion... "); + test_pack(packets,headret,0,0,0); + } + /* page with the 255 segment limit */ { const int packets[]={0,10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,10, - 10,10,10,10,10,10,10,50,-1}; + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,50,-1}; const int *headret[]={head1_5,head2_5,head3_5,NULL}; fprintf(stderr,"testing max packet segments... "); @@ -1477,26 +1765,30 @@ int main(void){ { /* packet that overspans over an entire page */ - const int packets[]={0,100,9000,259,255,-1}; + const int packets[]={0,100,130049,259,255,-1}; const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; fprintf(stderr,"testing very large packets... "); test_pack(packets,headret,0,0,0); } +#ifndef DISABLE_CRC { /* test for the libogg 1.1.1 resync in large continuation bug found by Josh Coalson) */ - const int packets[]={0,100,9000,259,255,-1}; + const int packets[]={0,100,130049,259,255,-1}; const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; fprintf(stderr,"testing continuation resync in very large packets... "); test_pack(packets,headret,100,2,3); } +#else + fprintf(stderr,"Skipping continuation resync test due to --disable-crc\n"); +#endif { /* term only page. why not? */ - const int packets[]={0,100,4080,-1}; + const int packets[]={0,100,64770,-1}; const int *headret[]={head1_7,head2_7,head3_7,NULL}; fprintf(stderr,"testing zero data page (1 nil packet)... "); @@ -1508,7 +1800,7 @@ int main(void){ { /* build a bunch of pages for testing */ unsigned char *data=_ogg_malloc(1024*1024); - int pl[]={0,100,4079,2956,2057,76,34,912,0,234,1000,1000,1000,300,-1}; + int pl[]={0, 1,1,98,4079, 1,1,2954,2057, 76,34,912,0,234,1000,1000, 1000,300,-1}; int inptr=0,i,j; ogg_page og[5]; @@ -1532,8 +1824,8 @@ int main(void){ /* retrieve finished pages */ for(i=0;i<5;i++){ if(ogg_stream_pageout(&os_en,&og[i])==0){ - fprintf(stderr,"Too few pages output building sync tests!\n"); - exit(1); + fprintf(stderr,"Too few pages output building sync tests!\n"); + exit(1); } copy_page(&og[i]); } @@ -1548,11 +1840,11 @@ int main(void){ ogg_sync_reset(&oy); ogg_stream_reset(&os_de); for(i=0;i<5;i++){ - memcpy(ogg_sync_buffer(&oy,og[i].header_len),og[i].header, - og[i].header_len); - ogg_sync_wrote(&oy,og[i].header_len); - memcpy(ogg_sync_buffer(&oy,og[i].body_len),og[i].body,og[i].body_len); - ogg_sync_wrote(&oy,og[i].body_len); + memcpy(ogg_sync_buffer(&oy,og[i].header_len),og[i].header, + og[i].header_len); + ogg_sync_wrote(&oy,og[i].header_len); + memcpy(ogg_sync_buffer(&oy,og[i].body_len),og[i].body,og[i].body_len); + ogg_sync_wrote(&oy,og[i].body_len); } ogg_sync_pageout(&oy,&temp); @@ -1569,17 +1861,21 @@ int main(void){ if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,0,0,0); if(ogg_stream_packetout(&os_de,&test)!=1)error(); - checkpacket(&test,100,1,-1); + checkpacket(&test,1,1,-1); + if(ogg_stream_packetout(&os_de,&test)!=1)error(); + checkpacket(&test,1,2,-1); + if(ogg_stream_packetout(&os_de,&test)!=1)error(); + checkpacket(&test,98,3,-1); if(ogg_stream_packetout(&os_de,&test)!=1)error(); - checkpacket(&test,4079,2,3000); + checkpacket(&test,4079,4,5000); if(ogg_stream_packetout(&os_de,&test)!=-1){ - fprintf(stderr,"Error: loss of page did not return error\n"); - exit(1); + fprintf(stderr,"Error: loss of page did not return error\n"); + exit(1); } if(ogg_stream_packetout(&os_de,&test)!=1)error(); - checkpacket(&test,76,5,-1); + checkpacket(&test,76,9,-1); if(ogg_stream_packetout(&os_de,&test)!=1)error(); - checkpacket(&test,34,6,-1); + checkpacket(&test,34,10,-1); fprintf(stderr,"ok.\n"); } @@ -1593,11 +1889,11 @@ int main(void){ ogg_sync_reset(&oy); ogg_stream_reset(&os_de); for(i=0;i<5;i++){ - memcpy(ogg_sync_buffer(&oy,og[i].header_len),og[i].header, - og[i].header_len); - ogg_sync_wrote(&oy,og[i].header_len); - memcpy(ogg_sync_buffer(&oy,og[i].body_len),og[i].body,og[i].body_len); - ogg_sync_wrote(&oy,og[i].body_len); + memcpy(ogg_sync_buffer(&oy,og[i].header_len),og[i].header, + og[i].header_len); + ogg_sync_wrote(&oy,og[i].header_len); + memcpy(ogg_sync_buffer(&oy,og[i].body_len),og[i].body,og[i].body_len); + ogg_sync_wrote(&oy,og[i].body_len); } ogg_sync_pageout(&oy,&temp); @@ -1616,17 +1912,27 @@ int main(void){ if(ogg_stream_packetout(&os_de,&test)!=1)error(); checkpacket(&test,0,0,0); if(ogg_stream_packetout(&os_de,&test)!=1)error(); - checkpacket(&test,100,1,-1); + checkpacket(&test,1,1,-1); if(ogg_stream_packetout(&os_de,&test)!=1)error(); - checkpacket(&test,4079,2,3000); + checkpacket(&test,1,2,-1); if(ogg_stream_packetout(&os_de,&test)!=1)error(); - checkpacket(&test,2956,3,4000); + checkpacket(&test,98,3,-1); + if(ogg_stream_packetout(&os_de,&test)!=1)error(); + checkpacket(&test,4079,4,5000); + if(ogg_stream_packetout(&os_de,&test)!=1)error(); + checkpacket(&test,1,5,-1); + if(ogg_stream_packetout(&os_de,&test)!=1)error(); + checkpacket(&test,1,6,-1); + if(ogg_stream_packetout(&os_de,&test)!=1)error(); + checkpacket(&test,2954,7,-1); + if(ogg_stream_packetout(&os_de,&test)!=1)error(); + checkpacket(&test,2057,8,9000); if(ogg_stream_packetout(&os_de,&test)!=-1){ - fprintf(stderr,"Error: loss of page did not return error\n"); - exit(1); + fprintf(stderr,"Error: loss of page did not return error\n"); + exit(1); } if(ogg_stream_packetout(&os_de,&test)!=1)error(); - checkpacket(&test,300,13,14000); + checkpacket(&test,300,17,18000); fprintf(stderr,"ok.\n"); } @@ -1637,26 +1943,26 @@ int main(void){ fprintf(stderr,"Testing sync on partial inputs... "); ogg_sync_reset(&oy); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, - 3); + 3); ogg_sync_wrote(&oy,3); if(ogg_sync_pageout(&oy,&og_de)>0)error(); /* Test fractional page inputs: incomplete fixed header */ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+3, - 20); + 20); ogg_sync_wrote(&oy,20); if(ogg_sync_pageout(&oy,&og_de)>0)error(); /* Test fractional page inputs: incomplete header */ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+23, - 5); + 5); ogg_sync_wrote(&oy,5); if(ogg_sync_pageout(&oy,&og_de)>0)error(); /* Test fractional page inputs: incomplete body */ memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+28, - og[1].header_len-28); + og[1].header_len-28); ogg_sync_wrote(&oy,og[1].header_len-28); if(ogg_sync_pageout(&oy,&og_de)>0)error(); @@ -1665,7 +1971,7 @@ int main(void){ if(ogg_sync_pageout(&oy,&og_de)>0)error(); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body+1000, - og[1].body_len-1000); + og[1].body_len-1000); ogg_sync_wrote(&oy,og[1].body_len-1000); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); @@ -1679,24 +1985,24 @@ int main(void){ ogg_sync_reset(&oy); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, - og[1].header_len); + og[1].header_len); ogg_sync_wrote(&oy,og[1].header_len); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, - og[1].body_len); + og[1].body_len); ogg_sync_wrote(&oy,og[1].body_len); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, - 20); + 20); ogg_sync_wrote(&oy,20); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); if(ogg_sync_pageout(&oy,&og_de)>0)error(); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+20, - og[1].header_len-20); + og[1].header_len-20); ogg_sync_wrote(&oy,og[1].header_len-20); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, - og[1].body_len); + og[1].body_len); ogg_sync_wrote(&oy,og[1].body_len); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); @@ -1711,35 +2017,36 @@ int main(void){ /* 'garbage' */ memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, - og[1].body_len); + og[1].body_len); ogg_sync_wrote(&oy,og[1].body_len); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, - og[1].header_len); + og[1].header_len); ogg_sync_wrote(&oy,og[1].header_len); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, - og[1].body_len); + og[1].body_len); ogg_sync_wrote(&oy,og[1].body_len); memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, - 20); + 20); ogg_sync_wrote(&oy,20); if(ogg_sync_pageout(&oy,&og_de)>0)error(); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); if(ogg_sync_pageout(&oy,&og_de)>0)error(); memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header+20, - og[2].header_len-20); + og[2].header_len-20); ogg_sync_wrote(&oy,og[2].header_len-20); memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, - og[2].body_len); + og[2].body_len); ogg_sync_wrote(&oy,og[2].body_len); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); fprintf(stderr,"ok.\n"); } +#ifndef DISABLE_CRC /* Test recapture: page + garbage + page */ { ogg_page og_de; @@ -1747,33 +2054,33 @@ int main(void){ ogg_sync_reset(&oy); memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, - og[1].header_len); + og[1].header_len); ogg_sync_wrote(&oy,og[1].header_len); memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, - og[1].body_len); + og[1].body_len); ogg_sync_wrote(&oy,og[1].body_len); memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, - og[2].header_len); + og[2].header_len); ogg_sync_wrote(&oy,og[2].header_len); memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, - og[2].header_len); + og[2].header_len); ogg_sync_wrote(&oy,og[2].header_len); if(ogg_sync_pageout(&oy,&og_de)<=0)error(); memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, - og[2].body_len-5); + og[2].body_len-5); ogg_sync_wrote(&oy,og[2].body_len-5); memcpy(ogg_sync_buffer(&oy,og[3].header_len),og[3].header, - og[3].header_len); + og[3].header_len); ogg_sync_wrote(&oy,og[3].header_len); memcpy(ogg_sync_buffer(&oy,og[3].body_len),og[3].body, - og[3].body_len); + og[3].body_len); ogg_sync_wrote(&oy,og[3].body_len); if(ogg_sync_pageout(&oy,&og_de)>0)error(); @@ -1781,14 +2088,20 @@ int main(void){ fprintf(stderr,"ok.\n"); } +#else + fprintf(stderr,"Skipping recapture test due to --disable-crc\n"); +#endif /* Free page data that was previously copied */ { for(i=0;i<5;i++){ - free_page(&og[i]); + free_page(&og[i]); } } } + ogg_sync_clear(&oy); + ogg_stream_clear(&os_en); + ogg_stream_clear(&os_de); return(0); } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/README b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/README deleted file mode 100644 index 3e969e0c..00000000 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/README +++ /dev/null @@ -1,134 +0,0 @@ -******************************************************************** -* * -* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * -* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * -* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * -* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * -* * -* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * -* by the Xiph.org Foundation, http://www.xiph.org/ * -* * -******************************************************************** - -Vorbis is a general purpose audio and music encoding format -contemporary to MPEG-4's AAC and TwinVQ, the next generation beyond -MPEG audio layer 3. Unlike the MPEG sponsored formats (and other -proprietary formats such as RealAudio G2 and Windows' flavor of the -month), the Vorbis CODEC specification belongs to the public domain. -All the technical details are published and documented, and any -software entity may make full use of the format without license -fee, royalty or patent concerns. - -This package contains: - -* libvorbis, a BSD-style license software implementation of - the Vorbis specification by the Xiph.Org Foundation - (http://www.xiph.org/) - -* libvorbisfile, a BSD-style license convenience library - built on Vorbis designed to simplify common uses - -* libvorbisenc, a BSD-style license library that provides a simple, - programmatic encoding setup interface - -* example code making use of libogg, libvorbis, libvorbisfile and - libvorbisenc - -WHAT'S HERE: - -This source distribution includes libvorbis and an example -encoder/player to demonstrate use of libvorbis as well as -documentation on the Ogg Vorbis audio coding format. - -You'll need libogg (distributed separately) to compile this library. -A more comprehensive set of utilities is available in the vorbis-tools -package. - -Directory: - -./lib The source for the libraries, a BSD-license implementation - of the public domain Ogg Vorbis audio encoding format. - -./include Library API headers - -./debian Rules/spec files for building Debian .deb packages - -./doc Vorbis documentation - -./examples Example code illustrating programmatic use of libvorbis, - libvorbisfile and libvorbisenc - -./mac Codewarrior project files and build tweaks for MacOS. - -./macosx Project files for MacOS X. - -./win32 Win32 projects files and build automation - -./vq Internal utilities for training/building new LSP/residue - and auxiliary codebooks. - -CONTACT: - -The Ogg homepage is located at 'http://www.xiph.org/ogg/'. -Vorbis's homepage is located at 'http://www.xiph.org/vorbis/'. -Up to date technical documents, contact information, source code and -pre-built utilities may be found there. - -The user website for Ogg Vorbis software and audio is http://vorbis.com/ - -BUILDING FROM TRUNK: - -Development source is under subversion revision control at -https://svn.xiph.org/trunk/vorbis/. You will also need the -newest versions of autoconf, automake, libtool and pkg-config in -order to compile Vorbis from development source. A configure script -is provided for you in the source tarball distributions. - - [update or checkout latest source] - ./autogen.sh - make - -and as root if desired: - - make install - -This will install the Vorbis libraries (static and shared) into -/usr/local/lib, includes into /usr/local/include and API manpages -(once we write some) into /usr/local/man. - -Documentation building requires xsltproc and pdfxmltex. - -BUILDING FROM TARBALL DISTRIBUTIONS: - - ./configure - make - -and optionally (as root): - make install - -BUILDING RPMS: - -after normal configuring: - - make dist - rpm -ta libvorbis-.tar.gz - -BUILDING ON MACOS 9: - -Vorbis on MacOS 9 is built using Metroworks CodeWarrior. To build it, -first verify that the Ogg libraries are already built following the -instructions in the Ogg module README. Open vorbis/mac/libvorbis.mcp, -switch to the "Targets" pane, select everything, and make the project. -Do the same thing to build libvorbisenc.mcp, and libvorbisfile.mcp (in -that order). In vorbis/mac/Output you will now have both debug and final -versions of Vorbis shared libraries to link your projects against. - -To build a project using Ogg Vorbis, add access paths to your -CodeWarrior project for the ogg/include, ogg/mac/Output, -vorbis/include, and vorbis/mac/Output folders. Be sure that -"interpret DOS and Unix paths" is turned on in your project; it can -be found in the "access paths" pane in your project settings. Now -simply add the shared libraries you need to your project (OggLib and -VorbisLib at least) and #include "ogg/ogg.h" and "vorbis/codec.h" -wherever you need to access Ogg and Vorbis functionality. - diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/AUTHORS b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/AUTHORS similarity index 100% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/AUTHORS rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/AUTHORS diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/CHANGES b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/CHANGES similarity index 69% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/CHANGES rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/CHANGES index e7d5dd30..c4a0addf 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/CHANGES +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/CHANGES @@ -1,3 +1,61 @@ +libvorbis 1.3.7 (2020-07-04) -- "Xiph.Org libVorbis I 20200704 (Reducing Environment)" + +* Fix CVE-2018-10393 - out-of-bounds read encoding very low sample rates. +* Fix CVE-2017-14160 - out-of-bounds read encoding very low sample rates. +* Fix handling invalid bytes per sample arguments. +* Fix handling invalid channel count arguments. +* Fix invalid free on seek failure. +* Fix negative shift reading blocksize. +* Fix accepting unreasonable float32 values. +* Fix tag comparison depending on locale. +* Fix unnecessarily linking libm. +* Fix memory leak in test_sharedbook. +* Update Visual Studio projects for ogg library filename change. +* Distribute CMake build files with the source package. +* Remove unnecessary configure --target switch. +* Add gitlab CI support. +* Add OSS-Fuzz support. +* Build system and integration updates. + +libvorbis 1.3.6 (2018-03-16) -- "Xiph.Org libVorbis I 20180316 (Now 100% fewer shells)" + +* Fix CVE-2018-5146 - out-of-bounds write on codebook decoding. +* Fix CVE-2017-14632 - free() on unitialized data +* Fix CVE-2017-14633 - out-of-bounds read +* Fix bitrate metadata parsing. +* Fix out-of-bounds read in codebook parsing. +* Fix residue vector size in Vorbis I spec. +* Appveyor support +* Travis CI support +* Add secondary CMake build system. +* Build system fixes + +libvorbis 1.3.5 (2015-03-03) -- "Xiph.Org libVorbis I 20150105 (⛄⛄⛄⛄)" + +* Tolerate single-entry codebooks. +* Fix decoder crash with invalid input. +* Fix encoder crash with non-positive sample rates. +# Fix issues in vorbisfile's seek bisection code. +* Spec errata. +* Reject multiple headers of the same type. +* Various build fixes and code cleanup. + +libvorbis 1.3.4 (2014-01-22) -- "Xiph.Org libVorbis I 20140122 (Turpakäräjiin)" + +* Reduce codebook footprint in library code. +* Various build and documentation fixes. + +libvorbis 1.3.3 (2012-02-03) -- "Xiph.Org libVorbis I 20120203 (Omnipresent)" + +* vorbis: additional proofing against invalid/malicious + streams in decode (see SVN for details). +* vorbis: fix a memory leak in vorbis_commentheader_out(). +* updates, corrections and clarifications in the Vorbis I specification + document +* win32: fixed project configuration which referenced two CRT versions + in output binaries. +* build warning fixes + libvorbis 1.3.2 (2010-11-01) -- "Xiph.Org libVorbis I 20101101 (Schaufenugget)" * vorbis: additional proofing against invalid/malicious diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/COPYING b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/COPYING similarity index 97% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/COPYING rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/COPYING index 28de72a9..fb456a87 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/COPYING +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/COPYING @@ -1,4 +1,4 @@ -Copyright (c) 2002-2008 Xiph.org Foundation +Copyright (c) 2002-2020 Xiph.org Foundation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/README.md b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/README.md new file mode 100644 index 00000000..30c88d39 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/README.md @@ -0,0 +1,147 @@ +# Vorbis + +[![GitLab Build Status](https://gitlab.xiph.org/xiph/vorbis/badges/master/pipeline.svg)](https://gitlab.xiph.org/xiph/vorbis/-/pipelines) +[![Travis Build Status](https://travis-ci.org/xiph/vorbis.svg?branch=master)](https://travis-ci.org/xiph/vorbis) +[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/github/xiph/vorbis?branch=master&svg=true)](https://ci.appveyor.com/project/rillian/vorbis) + +Vorbis is a general purpose audio and music encoding format +contemporary to MPEG-4's AAC and TwinVQ, the next generation beyond +MPEG audio layer 3. Unlike the MPEG sponsored formats (and other +proprietary formats such as RealAudio G2 and Windows' flavor of the +month), the Vorbis CODEC specification belongs to the public domain. +All the technical details are published and documented, and any +software entity may make full use of the format without license +fee, royalty or patent concerns. + +This package contains: + +- libvorbis, a BSD-style license software implementation of + the Vorbis specification by the Xiph.Org Foundation + (https://xiph.org/) + +- libvorbisfile, a BSD-style license convenience library + built on Vorbis designed to simplify common uses + +- libvorbisenc, a BSD-style license library that provides a simple, + programmatic encoding setup interface + +- example code making use of libogg, libvorbis, libvorbisfile and + libvorbisenc + +## What's here ## + +This source distribution includes libvorbis and an example +encoder/player to demonstrate use of libvorbis as well as +documentation on the Ogg Vorbis audio coding format. + +You'll need libogg (distributed separately) to compile this library. +A more comprehensive set of utilities is available in the vorbis-tools +package. + +Directory: + +- `lib` The source for the libraries, a BSD-license implementation of the public domain Ogg Vorbis audio encoding format. + +- `include` Library API headers + +- `debian` Rules/spec files for building Debian .deb packages + +- `doc` Vorbis documentation + +- `examples` Example code illustrating programmatic use of libvorbis, libvorbisfile and libvorbisenc + +- `macosx` Project files for MacOS X. + +- `win32` Win32 projects files and build automation + +- `vq` Internal utilities for training/building new LSP/residue and auxiliary codebooks. + +## Contact ## + +The Ogg homepage is located at 'https://xiph.org/ogg/'. +Vorbis's homepage is located at 'https://xiph.org/vorbis/'. +Up to date technical documents, contact information, source code and +pre-built utilities may be found there. + +## Building ## + +#### Building from master #### + +Development source is under git revision control at +https://gitlab.xiph.org/xiph/vorbis.git. You will also need the +newest versions of autoconf, automake, libtool and pkg-config in +order to compile Vorbis from development source. A configure script +is provided for you in the source tarball distributions. + + ./autogen.sh + ./configure + make + +and as root if desired: + + make install + +This will install the Vorbis libraries (static and shared) into +/usr/local/lib, includes into /usr/local/include and API manpages +(once we write some) into /usr/local/man. + +Documentation building requires xsltproc and pdfxmltex. + +#### Building from tarball distributions #### + + ./configure + make + +and optionally (as root): + + make install + +#### Building RPM packages #### + +after normal configuring: + + make dist + rpm -ta libvorbis-.tar.gz + +## Building with CMake ## + +Ogg supports building using [CMake](https://cmake.org/). CMake is a meta build system that generates native projects for each platform. +To generate projects just run cmake replacing `YOUR-PROJECT-GENERATOR` with a proper generator from a list [here](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html): + + cmake -G YOUR-PROJECT-GENERATOR . + +Note that by default cmake generates projects that will build static libraries. +To generate projects that will build dynamic library use `BUILD_SHARED_LIBS` option like this: + + cmake -G YOUR-PROJECT-GENERATOR -DBUILD_SHARED_LIBS=1 . + +After projects are generated use them as usual + +#### Building on Windows #### + +Use proper generator for your Visual Studio version like: + + cmake -G "Visual Studio 12 2013" . + +#### Building on Mac OS X #### + +Use Xcode generator. To build framework run: + + cmake -G Xcode -DBUILD_FRAMEWORK=1 . + +#### Building on Linux #### + +Use Makefile generator which is default one. + + cmake . + make + +## License ## + +THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. +USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS +GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE +IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. + +THE OggVorbis SOURCE CODE IS COPYRIGHT (C) 1994-2020 +by the Xiph.Org Foundation https://xiph.org/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/analysis.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/analysis.c similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/analysis.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/analysis.c index b4f032e1..617329b0 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/analysis.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/analysis.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: single-block PCM analysis mode dispatch - last mod: $Id: analysis.c 16226 2009-07-08 06:43:49Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/backends.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/backends.h similarity index 97% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/backends.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/backends.h index c264f39a..01cb7f52 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/backends.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/backends.h @@ -6,13 +6,12 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: libvorbis backend and mapping structures; needed for static mode headers - last mod: $Id: backends.h 16962 2010-03-11 07:30:34Z xiphmont $ ********************************************************************/ @@ -93,7 +92,7 @@ typedef struct{ void (*free_info) (vorbis_info_residue *); void (*free_look) (vorbis_look_residue *); long **(*classx) (struct vorbis_block *,vorbis_look_residue *, - int **,int *,int); + int **,int *,int); int (*forward) (oggpack_buffer *,struct vorbis_block *, vorbis_look_residue *, int **,int *,int,long **,int); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/bitrate.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/bitrate.c similarity index 98% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/bitrate.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/bitrate.c index 70fc2440..9525fbcd 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/bitrate.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/bitrate.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: bitrate tracking and management - last mod: $Id: bitrate.c 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/bitrate.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/bitrate.h similarity index 93% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/bitrate.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/bitrate.h index 40e08d39..72cbd092 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/bitrate.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/bitrate.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: bitrate tracking and management - last mod: $Id: bitrate.h 13293 2007-07-24 00:09:47Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/block.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/block.c similarity index 97% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/block.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/block.c index bc597437..0a3873bf 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/block.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/block.c @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: PCM data vector blocking, windowing and dis/reassembly - last mod: $Id: block.c 17561 2010-10-23 10:34:24Z xiphmont $ Handle windowing, overlap-add, etc of the PCM vectors. This is made more amusing by Vorbis' current two allowed block sizes. @@ -173,14 +172,19 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ private_state *b=NULL; int hs; - if(ci==NULL) return 1; + if(ci==NULL|| + ci->modes<=0|| + ci->blocksizes[0]<64|| + ci->blocksizes[1]blocksizes[0]){ + return 1; + } hs=ci->halfrate_flag; memset(v,0,sizeof(*v)); b=(private_state*) (v->backend_state=(private_state*)_ogg_calloc(1,sizeof(*b))); v->vi=vi; - b->modebits=ilog2(ci->modes); + b->modebits=ov_ilog(ci->modes-1); b->transform[0]=(vorbis_look_transform**)_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[0])); b->transform[1]=(vorbis_look_transform**)_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[1])); @@ -193,8 +197,14 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ mdct_init((mdct_lookup*)b->transform[1][0],ci->blocksizes[1]>>hs); /* Vorbis I uses only window type 0 */ - b->window[0]=ilog2(ci->blocksizes[0])-6; - b->window[1]=ilog2(ci->blocksizes[1])-6; + /* note that the correct computation below is technically: + b->window[0]=ov_ilog(ci->blocksizes[0]-1)-6; + b->window[1]=ov_ilog(ci->blocksizes[1]-1)-6; + but since blocksizes are always powers of two, + the below is equivalent. + */ + b->window[0]=ov_ilog(ci->blocksizes[0])-7; + b->window[1]=ov_ilog(ci->blocksizes[1])-7; if(encp){ /* encode/decode differ here */ @@ -404,7 +414,7 @@ float **vorbis_analysis_buffer(vorbis_dsp_state *v, int vals){ static void _preextrapolate_helper(vorbis_dsp_state *v){ int i; - int order=32; + int order=16; float *lpc=(float*)alloca(order*sizeof(*lpc)); float *work=(float*)alloca(v->pcm_current*sizeof(*work)); long j; @@ -1022,7 +1032,7 @@ int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ } -static float *vorbis_window(vorbis_dsp_state *v,int W){ +const float *vorbis_window(vorbis_dsp_state *v,int W){ vorbis_info *vi=v->vi; codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; int hs=ci->halfrate_flag; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/coupled/res_books_51.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/coupled/res_books_51.h similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/coupled/res_books_51.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/coupled/res_books_51.h index 87b4b281..eb569c6f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/coupled/res_books_51.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/coupled/res_books_51.h @@ -1,3 +1,19 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * + * by the Xiph.Org Foundation https://xiph.org/ * + * * + ******************************************************************** + * + * function: static codebooks for 5.1 surround + * + ********************************************************************/ + static const long _vq_quantlist__44p0_l0_0[] = { 6, 5, @@ -14,7 +30,7 @@ static const long _vq_quantlist__44p0_l0_0[] = { 12, }; -static const long _vq_lengthlist__44p0_l0_0[] = { +static const char _vq_lengthlist__44p0_l0_0[] = { 1, 3, 4, 7, 7, 8, 8, 9, 9, 9,10,10,10, 5, 6, 5, 8, 7, 9, 8, 9, 9,10, 9,11,10, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, 8, 9, 8,10, 9,10, 9,10, 9, @@ -30,7 +46,7 @@ static const long _vq_lengthlist__44p0_l0_0[] = { static const static_codebook _44p0_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44p0_l0_0, + (char *)_vq_lengthlist__44p0_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44p0_l0_0, 0 @@ -44,14 +60,14 @@ static const long _vq_quantlist__44p0_l0_1[] = { 4, }; -static const long _vq_lengthlist__44p0_l0_1[] = { +static const char _vq_lengthlist__44p0_l0_1[] = { 1, 4, 4, 6, 6, 5, 5, 5, 7, 5, 5, 5, 5, 6, 7, 7, 6, 7, 7, 7, 6, 7, 7, 7, 7, }; static const static_codebook _44p0_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44p0_l0_1, + (char *)_vq_lengthlist__44p0_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p0_l0_1, 0 @@ -63,31 +79,31 @@ static const long _vq_quantlist__44p0_l1_0[] = { 2, }; -static const long _vq_lengthlist__44p0_l1_0[] = { +static const char _vq_lengthlist__44p0_l1_0[] = { 1, 4, 4, 4, 4, 4, 4, 4, 4, }; static const static_codebook _44p0_l1_0 = { 2, 9, - (long *)_vq_lengthlist__44p0_l1_0, + (char *)_vq_lengthlist__44p0_l1_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p0_l1_0, 0 }; -static const long _huff_lengthlist__44p0_lfe[] = { +static const char _huff_lengthlist__44p0_lfe[] = { 1, 3, 2, 3, }; static const static_codebook _huff_book__44p0_lfe = { 2, 4, - (long *)_huff_lengthlist__44p0_lfe, + (char *)_huff_lengthlist__44p0_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44p0_long[] = { +static const char _huff_lengthlist__44p0_long[] = { 2, 3, 6, 7,10,14,16, 3, 2, 5, 7,11,14,17, 6, 5, 5, 7,10,12,14, 7, 7, 6, 6, 7, 9,13,10,11, 9, 6, 6, 9,11,15,15,13,10, 9,10,12,18,18,16,14,12,13, @@ -96,7 +112,7 @@ static const long _huff_lengthlist__44p0_long[] = { static const static_codebook _huff_book__44p0_long = { 2, 49, - (long *)_huff_lengthlist__44p0_long, + (char *)_huff_lengthlist__44p0_long, 0, 0, 0, 0, 0, NULL, 0 @@ -108,7 +124,7 @@ static const long _vq_quantlist__44p0_p1_0[] = { 2, }; -static const long _vq_lengthlist__44p0_p1_0[] = { +static const char _vq_lengthlist__44p0_p1_0[] = { 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -129,7 +145,7 @@ static const long _vq_lengthlist__44p0_p1_0[] = { static const static_codebook _44p0_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44p0_p1_0, + (char *)_vq_lengthlist__44p0_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p0_p1_0, 0 @@ -141,7 +157,7 @@ static const long _vq_quantlist__44p0_p2_0[] = { 2, }; -static const long _vq_lengthlist__44p0_p2_0[] = { +static const char _vq_lengthlist__44p0_p2_0[] = { 1, 5, 5, 0, 7, 7, 0, 8, 8, 0, 9, 9, 0,12,12, 0, 8, 8, 0, 9, 9, 0,12,12, 0, 8, 8, 0, 6, 6, 0,11, 11, 0,12,12, 0,12,12, 0,15,15, 0,11,11, 0,12,12, @@ -162,7 +178,7 @@ static const long _vq_lengthlist__44p0_p2_0[] = { static const static_codebook _44p0_p2_0 = { 5, 243, - (long *)_vq_lengthlist__44p0_p2_0, + (char *)_vq_lengthlist__44p0_p2_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44p0_p2_0, 0 @@ -174,7 +190,7 @@ static const long _vq_quantlist__44p0_p2_1[] = { 2, }; -static const long _vq_lengthlist__44p0_p2_1[] = { +static const char _vq_lengthlist__44p0_p2_1[] = { 1, 3, 3, 0, 9, 9, 0, 9, 9, 0,10,10, 0, 9, 9, 0, 10,10, 0,10,10, 0, 9, 9, 0,10,10, 0, 7, 7, 0, 7, 7, 0, 6, 6, 0, 8, 8, 0, 7, 7, 0, 8, 8, 0, 8, 9, @@ -195,7 +211,7 @@ static const long _vq_lengthlist__44p0_p2_1[] = { static const static_codebook _44p0_p2_1 = { 5, 243, - (long *)_vq_lengthlist__44p0_p2_1, + (char *)_vq_lengthlist__44p0_p2_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p0_p2_1, 0 @@ -207,7 +223,7 @@ static const long _vq_quantlist__44p0_p3_0[] = { 2, }; -static const long _vq_lengthlist__44p0_p3_0[] = { +static const char _vq_lengthlist__44p0_p3_0[] = { 1, 6, 6, 7, 8, 8, 7, 8, 8, 7, 9, 9,10,12,11, 9, 8, 8, 7, 9, 9,11,12,12, 9, 9, 9, 6, 7, 7,10,11, 11,10,11,11,10,11,11,13,13,14,12,12,12,11,11,11, @@ -228,7 +244,7 @@ static const long _vq_lengthlist__44p0_p3_0[] = { static const static_codebook _44p0_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44p0_p3_0, + (char *)_vq_lengthlist__44p0_p3_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44p0_p3_0, 0 @@ -242,7 +258,7 @@ static const long _vq_quantlist__44p0_p3_1[] = { 4, }; -static const long _vq_lengthlist__44p0_p3_1[] = { +static const char _vq_lengthlist__44p0_p3_1[] = { 2, 4, 4, 8, 8,10,12,12,11,11, 9,11,11,12,13,11, 12,12,11,11,11,12,12,12,12,10,13,12,13,13,11,12, 12,13,13,11,12,12,13,13,11,12,13,13,13,11,13,13, @@ -443,7 +459,7 @@ static const long _vq_lengthlist__44p0_p3_1[] = { static const static_codebook _44p0_p3_1 = { 5, 3125, - (long *)_vq_lengthlist__44p0_p3_1, + (char *)_vq_lengthlist__44p0_p3_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p0_p3_1, 0 @@ -457,7 +473,7 @@ static const long _vq_quantlist__44p0_p4_0[] = { 4, }; -static const long _vq_lengthlist__44p0_p4_0[] = { +static const char _vq_lengthlist__44p0_p4_0[] = { 2, 6, 6,14,14, 6, 8, 8,14,14, 7, 7, 7,14,14, 0, 13,13,15,16, 0,13,13,15,15, 7, 8, 8,15,15, 9,10, 10,16,16, 9, 8, 8,14,15, 0,13,13,17,17, 0,13,13, @@ -658,7 +674,7 @@ static const long _vq_lengthlist__44p0_p4_0[] = { static const static_codebook _44p0_p4_0 = { 5, 3125, - (long *)_vq_lengthlist__44p0_p4_0, + (char *)_vq_lengthlist__44p0_p4_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44p0_p4_0, 0 @@ -674,13 +690,13 @@ static const long _vq_quantlist__44p0_p4_1[] = { 6, }; -static const long _vq_lengthlist__44p0_p4_1[] = { +static const char _vq_lengthlist__44p0_p4_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44p0_p4_1 = { 1, 7, - (long *)_vq_lengthlist__44p0_p4_1, + (char *)_vq_lengthlist__44p0_p4_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44p0_p4_1, 0 @@ -692,7 +708,7 @@ static const long _vq_quantlist__44p0_p5_0[] = { 2, }; -static const long _vq_lengthlist__44p0_p5_0[] = { +static const char _vq_lengthlist__44p0_p5_0[] = { 1, 6, 6, 6, 8, 8, 7, 8, 8, 7, 9, 8,10,11,11, 9, 8, 8, 7, 8, 8,11,11,11, 9, 8, 8, 6, 7, 7,10,10, 10,10,10,10,10,10,10,14,13,13,12,11,11,10,10,10, @@ -713,7 +729,7 @@ static const long _vq_lengthlist__44p0_p5_0[] = { static const static_codebook _44p0_p5_0 = { 5, 243, - (long *)_vq_lengthlist__44p0_p5_0, + (char *)_vq_lengthlist__44p0_p5_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44p0_p5_0, 0 @@ -725,7 +741,7 @@ static const long _vq_quantlist__44p0_p5_1[] = { 2, }; -static const long _vq_lengthlist__44p0_p5_1[] = { +static const char _vq_lengthlist__44p0_p5_1[] = { 2, 7, 7, 7, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 9, 8, 7, 7, 8, 8, 8, 9, 9, 9, 9, 7, 7, 6, 6, 6, 9, 7, 7, 9, 7, 7, 9, 8, 8,10, 8, 8,10, 8, 8,10, 8, 8, @@ -746,7 +762,7 @@ static const long _vq_lengthlist__44p0_p5_1[] = { static const static_codebook _44p0_p5_1 = { 5, 243, - (long *)_vq_lengthlist__44p0_p5_1, + (char *)_vq_lengthlist__44p0_p5_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44p0_p5_1, 0 @@ -758,7 +774,7 @@ static const long _vq_quantlist__44p0_p6_0[] = { 2, }; -static const long _vq_lengthlist__44p0_p6_0[] = { +static const char _vq_lengthlist__44p0_p6_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -779,7 +795,7 @@ static const long _vq_lengthlist__44p0_p6_0[] = { static const static_codebook _44p0_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44p0_p6_0, + (char *)_vq_lengthlist__44p0_p6_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p0_p6_0, 0 @@ -813,14 +829,14 @@ static const long _vq_quantlist__44p0_p6_1[] = { 24, }; -static const long _vq_lengthlist__44p0_p6_1[] = { +static const char _vq_lengthlist__44p0_p6_1[] = { 1, 3, 2, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, 11,12,12,12,14,14,14,15,15, }; static const static_codebook _44p0_p6_1 = { 1, 25, - (long *)_vq_lengthlist__44p0_p6_1, + (char *)_vq_lengthlist__44p0_p6_1, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44p0_p6_1, 0 @@ -854,20 +870,20 @@ static const long _vq_quantlist__44p0_p6_2[] = { 24, }; -static const long _vq_lengthlist__44p0_p6_2[] = { +static const char _vq_lengthlist__44p0_p6_2[] = { 3, 4, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p0_p6_2 = { 1, 25, - (long *)_vq_lengthlist__44p0_p6_2, + (char *)_vq_lengthlist__44p0_p6_2, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44p0_p6_2, 0 }; -static const long _huff_lengthlist__44p0_short[] = { +static const char _huff_lengthlist__44p0_short[] = { 3, 3, 7, 8,10,13,16, 3, 2, 5, 7, 9,13,16, 6, 4, 4, 6,10,14,15, 7, 5, 5, 7,10,13,14, 9, 8, 9, 9, 9,11,13,12,11,12, 9, 7, 8,11,14,12,10, 6, 5, 7, @@ -876,7 +892,7 @@ static const long _huff_lengthlist__44p0_short[] = { static const static_codebook _huff_book__44p0_short = { 2, 49, - (long *)_huff_lengthlist__44p0_short, + (char *)_huff_lengthlist__44p0_short, 0, 0, 0, 0, 0, NULL, 0 @@ -898,7 +914,7 @@ static const long _vq_quantlist__44p1_l0_0[] = { 12, }; -static const long _vq_lengthlist__44p1_l0_0[] = { +static const char _vq_lengthlist__44p1_l0_0[] = { 1, 4, 4, 7, 7, 8, 8, 9, 9,10,10,11,11, 4, 6, 5, 8, 6, 9, 8,10, 9,10,10,11,10, 5, 5, 6, 6, 8, 8, 9, 9,10,10,10,10,11, 7, 8, 8, 9, 8,10, 9,10, 9, @@ -914,7 +930,7 @@ static const long _vq_lengthlist__44p1_l0_0[] = { static const static_codebook _44p1_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44p1_l0_0, + (char *)_vq_lengthlist__44p1_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44p1_l0_0, 0 @@ -928,14 +944,14 @@ static const long _vq_quantlist__44p1_l0_1[] = { 4, }; -static const long _vq_lengthlist__44p1_l0_1[] = { +static const char _vq_lengthlist__44p1_l0_1[] = { 1, 4, 4, 6, 6, 5, 5, 5, 6, 6, 5, 6, 5, 6, 6, 6, 6, 7, 7, 7, 6, 7, 6, 7, 7, }; static const static_codebook _44p1_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44p1_l0_1, + (char *)_vq_lengthlist__44p1_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p1_l0_1, 0 @@ -947,31 +963,31 @@ static const long _vq_quantlist__44p1_l1_0[] = { 2, }; -static const long _vq_lengthlist__44p1_l1_0[] = { +static const char _vq_lengthlist__44p1_l1_0[] = { 1, 4, 4, 4, 4, 4, 4, 4, 4, }; static const static_codebook _44p1_l1_0 = { 2, 9, - (long *)_vq_lengthlist__44p1_l1_0, + (char *)_vq_lengthlist__44p1_l1_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p1_l1_0, 0 }; -static const long _huff_lengthlist__44p1_lfe[] = { +static const char _huff_lengthlist__44p1_lfe[] = { 1, 3, 2, 3, }; static const static_codebook _huff_book__44p1_lfe = { 2, 4, - (long *)_huff_lengthlist__44p1_lfe, + (char *)_huff_lengthlist__44p1_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44p1_long[] = { +static const char _huff_lengthlist__44p1_long[] = { 3, 3, 7, 7, 9,13,16, 3, 2, 4, 6,10,13,17, 7, 4, 4, 6, 9,12,14, 7, 6, 6, 5, 7, 9,12,10,10, 9, 6, 6, 9,12,14,14,13, 9, 8,10,11,18,18,15,13,11,10, @@ -980,7 +996,7 @@ static const long _huff_lengthlist__44p1_long[] = { static const static_codebook _huff_book__44p1_long = { 2, 49, - (long *)_huff_lengthlist__44p1_long, + (char *)_huff_lengthlist__44p1_long, 0, 0, 0, 0, 0, NULL, 0 @@ -992,7 +1008,7 @@ static const long _vq_quantlist__44p1_p1_0[] = { 2, }; -static const long _vq_lengthlist__44p1_p1_0[] = { +static const char _vq_lengthlist__44p1_p1_0[] = { 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1013,7 +1029,7 @@ static const long _vq_lengthlist__44p1_p1_0[] = { static const static_codebook _44p1_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44p1_p1_0, + (char *)_vq_lengthlist__44p1_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p1_p1_0, 0 @@ -1025,7 +1041,7 @@ static const long _vq_quantlist__44p1_p2_0[] = { 2, }; -static const long _vq_lengthlist__44p1_p2_0[] = { +static const char _vq_lengthlist__44p1_p2_0[] = { 1, 4, 4, 0, 7, 7, 0, 8, 8, 0, 9, 9, 0,12,12, 0, 8, 8, 0, 9, 9, 0,12,12, 0, 8, 8, 0, 6, 6, 0,11, 11, 0,11,11, 0,12,12, 0,14,14, 0,11,11, 0,12,12, @@ -1046,7 +1062,7 @@ static const long _vq_lengthlist__44p1_p2_0[] = { static const static_codebook _44p1_p2_0 = { 5, 243, - (long *)_vq_lengthlist__44p1_p2_0, + (char *)_vq_lengthlist__44p1_p2_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44p1_p2_0, 0 @@ -1058,7 +1074,7 @@ static const long _vq_quantlist__44p1_p2_1[] = { 2, }; -static const long _vq_lengthlist__44p1_p2_1[] = { +static const char _vq_lengthlist__44p1_p2_1[] = { 1, 3, 3, 0, 8, 8, 0, 8, 8, 0,10,10, 0, 9, 9, 0, 10,10, 0,10,10, 0, 9, 9, 0,10,10, 0, 7, 7, 0, 7, 7, 0, 7, 7, 0, 8, 8, 0, 8, 8, 0, 8, 8, 0, 9, 9, @@ -1079,7 +1095,7 @@ static const long _vq_lengthlist__44p1_p2_1[] = { static const static_codebook _44p1_p2_1 = { 5, 243, - (long *)_vq_lengthlist__44p1_p2_1, + (char *)_vq_lengthlist__44p1_p2_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p1_p2_1, 0 @@ -1091,7 +1107,7 @@ static const long _vq_quantlist__44p1_p3_0[] = { 2, }; -static const long _vq_lengthlist__44p1_p3_0[] = { +static const char _vq_lengthlist__44p1_p3_0[] = { 1, 6, 6, 6, 7, 7, 7, 8, 8, 7, 8, 8,10,11,11, 9, 8, 8, 7, 9, 9,11,12,12, 9, 8, 8, 6, 7, 7, 9,11, 11,10,11,11,10,11,11,13,13,13,11,12,12,10,11,11, @@ -1112,7 +1128,7 @@ static const long _vq_lengthlist__44p1_p3_0[] = { static const static_codebook _44p1_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44p1_p3_0, + (char *)_vq_lengthlist__44p1_p3_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44p1_p3_0, 0 @@ -1126,7 +1142,7 @@ static const long _vq_quantlist__44p1_p3_1[] = { 4, }; -static const long _vq_lengthlist__44p1_p3_1[] = { +static const char _vq_lengthlist__44p1_p3_1[] = { 2, 3, 4, 7, 7,10,12,12,12,12,10,11,11,13,13,11, 12,12,11,11,12,12,12,12,12,11,13,13,13,13,12,12, 12,13,14,12,13,13,13,13,12,13,13,13,13,12,13,13, @@ -1327,7 +1343,7 @@ static const long _vq_lengthlist__44p1_p3_1[] = { static const static_codebook _44p1_p3_1 = { 5, 3125, - (long *)_vq_lengthlist__44p1_p3_1, + (char *)_vq_lengthlist__44p1_p3_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p1_p3_1, 0 @@ -1341,7 +1357,7 @@ static const long _vq_quantlist__44p1_p4_0[] = { 4, }; -static const long _vq_lengthlist__44p1_p4_0[] = { +static const char _vq_lengthlist__44p1_p4_0[] = { 2, 6, 6,14,14, 6, 7, 7,14,14, 7, 7, 7,14,14, 0, 13,13,16,16, 0,13,13,15,14, 7, 8, 8,15,15, 9,10, 10,16,16, 9, 8, 8,15,15, 0,13,13,17,16, 0,13,13, @@ -1542,7 +1558,7 @@ static const long _vq_lengthlist__44p1_p4_0[] = { static const static_codebook _44p1_p4_0 = { 5, 3125, - (long *)_vq_lengthlist__44p1_p4_0, + (char *)_vq_lengthlist__44p1_p4_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44p1_p4_0, 0 @@ -1558,13 +1574,13 @@ static const long _vq_quantlist__44p1_p4_1[] = { 6, }; -static const long _vq_lengthlist__44p1_p4_1[] = { +static const char _vq_lengthlist__44p1_p4_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44p1_p4_1 = { 1, 7, - (long *)_vq_lengthlist__44p1_p4_1, + (char *)_vq_lengthlist__44p1_p4_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44p1_p4_1, 0 @@ -1576,7 +1592,7 @@ static const long _vq_quantlist__44p1_p5_0[] = { 2, }; -static const long _vq_lengthlist__44p1_p5_0[] = { +static const char _vq_lengthlist__44p1_p5_0[] = { 1, 6, 6, 7, 8, 8, 7, 8, 8, 7, 9, 8,10,11,11, 9, 8, 8, 7, 8, 8,11,11,11, 9, 8, 8, 6, 7, 7,10,10, 10,10,10,10,10,10,10,14,13,13,12,11,11,10,10,10, @@ -1597,7 +1613,7 @@ static const long _vq_lengthlist__44p1_p5_0[] = { static const static_codebook _44p1_p5_0 = { 5, 243, - (long *)_vq_lengthlist__44p1_p5_0, + (char *)_vq_lengthlist__44p1_p5_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44p1_p5_0, 0 @@ -1609,7 +1625,7 @@ static const long _vq_quantlist__44p1_p5_1[] = { 2, }; -static const long _vq_lengthlist__44p1_p5_1[] = { +static const char _vq_lengthlist__44p1_p5_1[] = { 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 8, 8, 8, 7, 7, 8, 8, 8, 9, 8, 8, 9, 7, 7, 6, 6, 6, 9, 8, 7, 9, 7, 7, 9, 8, 8,10, 8, 8,10, 8, 8,10, 8, 8, @@ -1630,7 +1646,7 @@ static const long _vq_lengthlist__44p1_p5_1[] = { static const static_codebook _44p1_p5_1 = { 5, 243, - (long *)_vq_lengthlist__44p1_p5_1, + (char *)_vq_lengthlist__44p1_p5_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44p1_p5_1, 0 @@ -1642,7 +1658,7 @@ static const long _vq_quantlist__44p1_p6_0[] = { 2, }; -static const long _vq_lengthlist__44p1_p6_0[] = { +static const char _vq_lengthlist__44p1_p6_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -1663,7 +1679,7 @@ static const long _vq_lengthlist__44p1_p6_0[] = { static const static_codebook _44p1_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44p1_p6_0, + (char *)_vq_lengthlist__44p1_p6_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p1_p6_0, 0 @@ -1697,14 +1713,14 @@ static const long _vq_quantlist__44p1_p6_1[] = { 24, }; -static const long _vq_lengthlist__44p1_p6_1[] = { +static const char _vq_lengthlist__44p1_p6_1[] = { 1, 3, 2, 5, 4, 7, 7, 8, 8, 9, 9,10,10,11,11,12, 12,13,13,13,14,16,16,16,16, }; static const static_codebook _44p1_p6_1 = { 1, 25, - (long *)_vq_lengthlist__44p1_p6_1, + (char *)_vq_lengthlist__44p1_p6_1, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44p1_p6_1, 0 @@ -1738,20 +1754,20 @@ static const long _vq_quantlist__44p1_p6_2[] = { 24, }; -static const long _vq_lengthlist__44p1_p6_2[] = { +static const char _vq_lengthlist__44p1_p6_2[] = { 3, 4, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p1_p6_2 = { 1, 25, - (long *)_vq_lengthlist__44p1_p6_2, + (char *)_vq_lengthlist__44p1_p6_2, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44p1_p6_2, 0 }; -static const long _huff_lengthlist__44p1_short[] = { +static const char _huff_lengthlist__44p1_short[] = { 4, 5, 7, 8,10,13,14, 4, 2, 4, 6, 8,11,12, 7, 4, 3, 5, 8,12,14, 8, 5, 4, 4, 8,12,12, 9, 7, 7, 7, 9,10,11,13,11,11, 9, 7, 8,10,13,11,10, 6, 5, 7, @@ -1760,7 +1776,7 @@ static const long _huff_lengthlist__44p1_short[] = { static const static_codebook _huff_book__44p1_short = { 2, 49, - (long *)_huff_lengthlist__44p1_short, + (char *)_huff_lengthlist__44p1_short, 0, 0, 0, 0, 0, NULL, 0 @@ -1782,7 +1798,7 @@ static const long _vq_quantlist__44p2_l0_0[] = { 12, }; -static const long _vq_lengthlist__44p2_l0_0[] = { +static const char _vq_lengthlist__44p2_l0_0[] = { 1, 4, 4, 7, 7, 8, 8, 9, 9,10,10,11,11, 4, 6, 5, 8, 7, 9, 8,10, 9,11,10,11,11, 4, 5, 6, 7, 8, 8, 9, 9,10,10,10,10,11, 8, 9, 8,10, 8,10, 9,11,10, @@ -1798,7 +1814,7 @@ static const long _vq_lengthlist__44p2_l0_0[] = { static const static_codebook _44p2_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44p2_l0_0, + (char *)_vq_lengthlist__44p2_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44p2_l0_0, 0 @@ -1812,14 +1828,14 @@ static const long _vq_quantlist__44p2_l0_1[] = { 4, }; -static const long _vq_lengthlist__44p2_l0_1[] = { +static const char _vq_lengthlist__44p2_l0_1[] = { 2, 4, 4, 5, 5, 4, 5, 5, 6, 5, 4, 5, 5, 5, 6, 5, 5, 6, 6, 6, 5, 6, 5, 6, 6, }; static const static_codebook _44p2_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44p2_l0_1, + (char *)_vq_lengthlist__44p2_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p2_l0_1, 0 @@ -1831,31 +1847,31 @@ static const long _vq_quantlist__44p2_l1_0[] = { 2, }; -static const long _vq_lengthlist__44p2_l1_0[] = { +static const char _vq_lengthlist__44p2_l1_0[] = { 1, 4, 4, 4, 4, 4, 4, 4, 4, }; static const static_codebook _44p2_l1_0 = { 2, 9, - (long *)_vq_lengthlist__44p2_l1_0, + (char *)_vq_lengthlist__44p2_l1_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p2_l1_0, 0 }; -static const long _huff_lengthlist__44p2_lfe[] = { +static const char _huff_lengthlist__44p2_lfe[] = { 1, 3, 2, 3, }; static const static_codebook _huff_book__44p2_lfe = { 2, 4, - (long *)_huff_lengthlist__44p2_lfe, + (char *)_huff_lengthlist__44p2_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44p2_long[] = { +static const char _huff_lengthlist__44p2_long[] = { 3, 4, 9, 8, 8,10,13,16, 4, 2, 9, 5, 7,10,14,18, 9, 7, 6, 5, 7, 9,12,16, 7, 5, 5, 3, 5, 8,11,13, 8, 7, 7, 5, 5, 7, 9,11,10,10, 9, 8, 6, 6, 8,10, @@ -1864,7 +1880,7 @@ static const long _huff_lengthlist__44p2_long[] = { static const static_codebook _huff_book__44p2_long = { 2, 64, - (long *)_huff_lengthlist__44p2_long, + (char *)_huff_lengthlist__44p2_long, 0, 0, 0, 0, 0, NULL, 0 @@ -1876,7 +1892,7 @@ static const long _vq_quantlist__44p2_p1_0[] = { 2, }; -static const long _vq_lengthlist__44p2_p1_0[] = { +static const char _vq_lengthlist__44p2_p1_0[] = { 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1897,7 +1913,7 @@ static const long _vq_lengthlist__44p2_p1_0[] = { static const static_codebook _44p2_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44p2_p1_0, + (char *)_vq_lengthlist__44p2_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p2_p1_0, 0 @@ -1911,7 +1927,7 @@ static const long _vq_quantlist__44p2_p2_0[] = { 4, }; -static const long _vq_lengthlist__44p2_p2_0[] = { +static const char _vq_lengthlist__44p2_p2_0[] = { 1, 4, 4, 0, 0, 0, 8, 8, 0, 0, 0, 9, 9, 0, 0, 0, 10,10, 0, 0, 0, 0, 0, 0, 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0,11,11, 0, 0, 0, 0, 0, @@ -2112,7 +2128,7 @@ static const long _vq_lengthlist__44p2_p2_0[] = { static const static_codebook _44p2_p2_0 = { 5, 3125, - (long *)_vq_lengthlist__44p2_p2_0, + (char *)_vq_lengthlist__44p2_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p2_p2_0, 0 @@ -2124,7 +2140,7 @@ static const long _vq_quantlist__44p2_p3_0[] = { 2, }; -static const long _vq_lengthlist__44p2_p3_0[] = { +static const char _vq_lengthlist__44p2_p3_0[] = { 1, 5, 5, 6, 7, 7, 0, 8, 8, 6, 9, 9, 8,11,11, 0, 8, 8, 0, 9, 9, 0,12,12, 0, 8, 8, 5, 7, 7, 7,10, 10, 0,12,12, 8,11,11, 9,12,12, 0,11,12, 0,12,12, @@ -2145,7 +2161,7 @@ static const long _vq_lengthlist__44p2_p3_0[] = { static const static_codebook _44p2_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44p2_p3_0, + (char *)_vq_lengthlist__44p2_p3_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44p2_p3_0, 0 @@ -2157,7 +2173,7 @@ static const long _vq_quantlist__44p2_p3_1[] = { 2, }; -static const long _vq_lengthlist__44p2_p3_1[] = { +static const char _vq_lengthlist__44p2_p3_1[] = { 2, 3, 3, 0, 8, 8, 0, 8, 8, 0, 9, 9, 0, 9, 9, 0, 9, 9, 0, 9, 9, 0, 9, 9, 0, 8, 8, 0, 6, 6, 0, 7, 7, 0, 7, 7, 0, 8, 8, 0, 8, 8, 0, 8, 8, 0, 8, 8, @@ -2178,7 +2194,7 @@ static const long _vq_lengthlist__44p2_p3_1[] = { static const static_codebook _44p2_p3_1 = { 5, 243, - (long *)_vq_lengthlist__44p2_p3_1, + (char *)_vq_lengthlist__44p2_p3_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p2_p3_1, 0 @@ -2190,7 +2206,7 @@ static const long _vq_quantlist__44p2_p4_0[] = { 2, }; -static const long _vq_lengthlist__44p2_p4_0[] = { +static const char _vq_lengthlist__44p2_p4_0[] = { 1, 6, 6, 6, 7, 7, 7, 8, 8, 7, 8, 8,10,11,11, 9, 8, 8, 7, 8, 8,11,11,11, 9, 8, 8, 6, 7, 7, 9,11, 11, 9,11,11,10,11,11,12,13,13,11,12,12,10,11,11, @@ -2211,7 +2227,7 @@ static const long _vq_lengthlist__44p2_p4_0[] = { static const static_codebook _44p2_p4_0 = { 5, 243, - (long *)_vq_lengthlist__44p2_p4_0, + (char *)_vq_lengthlist__44p2_p4_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44p2_p4_0, 0 @@ -2225,7 +2241,7 @@ static const long _vq_quantlist__44p2_p4_1[] = { 4, }; -static const long _vq_lengthlist__44p2_p4_1[] = { +static const char _vq_lengthlist__44p2_p4_1[] = { 3, 4, 4, 8, 8,11, 9, 9,12,12,11,10,10,12,12,12, 10,10,11,11,12,12,12,12,12,12,11,11,13,13,12,12, 12,13,13,12,10,10,12,12,12,11,11,13,13,12,13,13, @@ -2426,7 +2442,7 @@ static const long _vq_lengthlist__44p2_p4_1[] = { static const static_codebook _44p2_p4_1 = { 5, 3125, - (long *)_vq_lengthlist__44p2_p4_1, + (char *)_vq_lengthlist__44p2_p4_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p2_p4_1, 0 @@ -2440,7 +2456,7 @@ static const long _vq_quantlist__44p2_p5_0[] = { 4, }; -static const long _vq_lengthlist__44p2_p5_0[] = { +static const char _vq_lengthlist__44p2_p5_0[] = { 2, 6, 6,14,14, 6, 7, 7,14,14, 7, 7, 7,15,15, 0, 13,13,16,16, 0,13,13,15,15, 7, 8, 8,15,15, 9,10, 10,17,16, 9, 8, 8,15,15, 0,13,13,18,17, 0,13,13, @@ -2641,7 +2657,7 @@ static const long _vq_lengthlist__44p2_p5_0[] = { static const static_codebook _44p2_p5_0 = { 5, 3125, - (long *)_vq_lengthlist__44p2_p5_0, + (char *)_vq_lengthlist__44p2_p5_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44p2_p5_0, 0 @@ -2657,13 +2673,13 @@ static const long _vq_quantlist__44p2_p5_1[] = { 6, }; -static const long _vq_lengthlist__44p2_p5_1[] = { +static const char _vq_lengthlist__44p2_p5_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44p2_p5_1 = { 1, 7, - (long *)_vq_lengthlist__44p2_p5_1, + (char *)_vq_lengthlist__44p2_p5_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44p2_p5_1, 0 @@ -2675,7 +2691,7 @@ static const long _vq_quantlist__44p2_p6_0[] = { 2, }; -static const long _vq_lengthlist__44p2_p6_0[] = { +static const char _vq_lengthlist__44p2_p6_0[] = { 1, 7, 7, 7, 8, 8, 7, 8, 8, 7, 9, 9,10,11,11, 9, 8, 8, 7, 8, 9,11,11,11, 9, 8, 8, 6, 7, 7,10,10, 10,10,10,10,10,10,10,14,14,14,12,11,11,10,11,11, @@ -2696,7 +2712,7 @@ static const long _vq_lengthlist__44p2_p6_0[] = { static const static_codebook _44p2_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44p2_p6_0, + (char *)_vq_lengthlist__44p2_p6_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44p2_p6_0, 0 @@ -2708,7 +2724,7 @@ static const long _vq_quantlist__44p2_p6_1[] = { 2, }; -static const long _vq_lengthlist__44p2_p6_1[] = { +static const char _vq_lengthlist__44p2_p6_1[] = { 2, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, 8, 7, 7, 8, 8, 8, 9, 9, 9, 9, 8, 8, 6, 7, 7, 9, 8, 8, 9, 7, 7, 9, 8, 8,10, 8, 8,10, 8, 8,10, 8, 8, @@ -2729,7 +2745,7 @@ static const long _vq_lengthlist__44p2_p6_1[] = { static const static_codebook _44p2_p6_1 = { 5, 243, - (long *)_vq_lengthlist__44p2_p6_1, + (char *)_vq_lengthlist__44p2_p6_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44p2_p6_1, 0 @@ -2741,7 +2757,7 @@ static const long _vq_quantlist__44p2_p7_0[] = { 2, }; -static const long _vq_lengthlist__44p2_p7_0[] = { +static const char _vq_lengthlist__44p2_p7_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -2762,7 +2778,7 @@ static const long _vq_lengthlist__44p2_p7_0[] = { static const static_codebook _44p2_p7_0 = { 5, 243, - (long *)_vq_lengthlist__44p2_p7_0, + (char *)_vq_lengthlist__44p2_p7_0, 1, -513979392, 1633504256, 2, 0, (long *)_vq_quantlist__44p2_p7_0, 0 @@ -2774,7 +2790,7 @@ static const long _vq_quantlist__44p2_p7_1[] = { 2, }; -static const long _vq_lengthlist__44p2_p7_1[] = { +static const char _vq_lengthlist__44p2_p7_1[] = { 1, 9, 9, 6, 9, 9, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -2795,7 +2811,7 @@ static const long _vq_lengthlist__44p2_p7_1[] = { static const static_codebook _44p2_p7_1 = { 5, 243, - (long *)_vq_lengthlist__44p2_p7_1, + (char *)_vq_lengthlist__44p2_p7_1, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p2_p7_1, 0 @@ -2829,14 +2845,14 @@ static const long _vq_quantlist__44p2_p7_2[] = { 24, }; -static const long _vq_lengthlist__44p2_p7_2[] = { +static const char _vq_lengthlist__44p2_p7_2[] = { 1, 3, 2, 5, 4, 7, 7, 8, 8, 9, 9,10,10,11,11,12, 12,13,13,14,14,15,15,15,15, }; static const static_codebook _44p2_p7_2 = { 1, 25, - (long *)_vq_lengthlist__44p2_p7_2, + (char *)_vq_lengthlist__44p2_p7_2, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44p2_p7_2, 0 @@ -2870,20 +2886,20 @@ static const long _vq_quantlist__44p2_p7_3[] = { 24, }; -static const long _vq_lengthlist__44p2_p7_3[] = { +static const char _vq_lengthlist__44p2_p7_3[] = { 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p2_p7_3 = { 1, 25, - (long *)_vq_lengthlist__44p2_p7_3, + (char *)_vq_lengthlist__44p2_p7_3, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44p2_p7_3, 0 }; -static const long _huff_lengthlist__44p2_short[] = { +static const char _huff_lengthlist__44p2_short[] = { 4, 4,12, 9, 8,12,15,17, 4, 2,11, 6, 5, 9,13,15, 11, 7, 8, 7, 7,10,14,13, 8, 5, 7, 5, 5, 8,12,12, 8, 4, 7, 4, 3, 6,11,12,11, 8, 9, 7, 6, 8,11,12, @@ -2892,7 +2908,7 @@ static const long _huff_lengthlist__44p2_short[] = { static const static_codebook _huff_book__44p2_short = { 2, 64, - (long *)_huff_lengthlist__44p2_short, + (char *)_huff_lengthlist__44p2_short, 0, 0, 0, 0, 0, NULL, 0 @@ -2914,7 +2930,7 @@ static const long _vq_quantlist__44p3_l0_0[] = { 12, }; -static const long _vq_lengthlist__44p3_l0_0[] = { +static const char _vq_lengthlist__44p3_l0_0[] = { 1, 4, 4, 8, 8, 8, 8, 9, 9,10,10,10,10, 4, 6, 5, 8, 7, 9, 9, 9, 9,10, 9,11, 9, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9,10, 9,10, 8, 9, 8, 9, 8,10, 9,11, 9, @@ -2930,7 +2946,7 @@ static const long _vq_lengthlist__44p3_l0_0[] = { static const static_codebook _44p3_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44p3_l0_0, + (char *)_vq_lengthlist__44p3_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44p3_l0_0, 0 @@ -2944,14 +2960,14 @@ static const long _vq_quantlist__44p3_l0_1[] = { 4, }; -static const long _vq_lengthlist__44p3_l0_1[] = { +static const char _vq_lengthlist__44p3_l0_1[] = { 3, 4, 4, 5, 5, 4, 4, 5, 5, 5, 4, 5, 4, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, }; static const static_codebook _44p3_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44p3_l0_1, + (char *)_vq_lengthlist__44p3_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p3_l0_1, 0 @@ -2963,31 +2979,31 @@ static const long _vq_quantlist__44p3_l1_0[] = { 2, }; -static const long _vq_lengthlist__44p3_l1_0[] = { +static const char _vq_lengthlist__44p3_l1_0[] = { 1, 4, 4, 4, 4, 4, 4, 4, 4, }; static const static_codebook _44p3_l1_0 = { 2, 9, - (long *)_vq_lengthlist__44p3_l1_0, + (char *)_vq_lengthlist__44p3_l1_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p3_l1_0, 0 }; -static const long _huff_lengthlist__44p3_lfe[] = { +static const char _huff_lengthlist__44p3_lfe[] = { 1, 3, 2, 3, }; static const static_codebook _huff_book__44p3_lfe = { 2, 4, - (long *)_huff_lengthlist__44p3_lfe, + (char *)_huff_lengthlist__44p3_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44p3_long[] = { +static const char _huff_lengthlist__44p3_long[] = { 3, 4,13, 9, 9,12,15,17, 4, 2,18, 5, 7,10,14,18, 11, 8, 6, 5, 6, 8,11,14, 8, 5, 5, 3, 5, 8,11,13, 9, 6, 7, 5, 5, 7, 9,10,11,10, 9, 8, 6, 6, 8,10, @@ -2996,7 +3012,7 @@ static const long _huff_lengthlist__44p3_long[] = { static const static_codebook _huff_book__44p3_long = { 2, 64, - (long *)_huff_lengthlist__44p3_long, + (char *)_huff_lengthlist__44p3_long, 0, 0, 0, 0, 0, NULL, 0 @@ -3008,7 +3024,7 @@ static const long _vq_quantlist__44p3_p1_0[] = { 2, }; -static const long _vq_lengthlist__44p3_p1_0[] = { +static const char _vq_lengthlist__44p3_p1_0[] = { 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3029,7 +3045,7 @@ static const long _vq_lengthlist__44p3_p1_0[] = { static const static_codebook _44p3_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44p3_p1_0, + (char *)_vq_lengthlist__44p3_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p3_p1_0, 0 @@ -3043,7 +3059,7 @@ static const long _vq_quantlist__44p3_p2_0[] = { 4, }; -static const long _vq_lengthlist__44p3_p2_0[] = { +static const char _vq_lengthlist__44p3_p2_0[] = { 3, 7, 7, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 11,11, 0, 0, 0, 0, 0, 0, 0, 0,10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0,10,11, 0, 0, 0, 0, 0, @@ -3244,7 +3260,7 @@ static const long _vq_lengthlist__44p3_p2_0[] = { static const static_codebook _44p3_p2_0 = { 5, 3125, - (long *)_vq_lengthlist__44p3_p2_0, + (char *)_vq_lengthlist__44p3_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p3_p2_0, 0 @@ -3256,7 +3272,7 @@ static const long _vq_quantlist__44p3_p3_0[] = { 2, }; -static const long _vq_lengthlist__44p3_p3_0[] = { +static const char _vq_lengthlist__44p3_p3_0[] = { 1, 5, 5, 5, 8, 8, 0, 8, 8, 6, 9, 9, 8,10,10, 0, 8, 8, 0, 9, 9, 0,12,12, 0, 8, 8, 4, 7, 7, 6,10, 10, 0,12,12, 7,11,11, 9,12,12, 0,12,12, 0,13,13, @@ -3277,7 +3293,7 @@ static const long _vq_lengthlist__44p3_p3_0[] = { static const static_codebook _44p3_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44p3_p3_0, + (char *)_vq_lengthlist__44p3_p3_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44p3_p3_0, 0 @@ -3289,7 +3305,7 @@ static const long _vq_quantlist__44p3_p3_1[] = { 2, }; -static const long _vq_lengthlist__44p3_p3_1[] = { +static const char _vq_lengthlist__44p3_p3_1[] = { 3, 4, 4, 0, 8, 8, 0, 8, 8, 0, 9, 9, 0,10,10, 0, 8, 8, 0, 9, 9, 0,10,10, 0, 8, 8, 0, 7, 7, 0, 8, 8, 0, 8, 8, 0, 8, 8, 0, 8, 8, 0, 8, 8, 0, 8, 8, @@ -3310,7 +3326,7 @@ static const long _vq_lengthlist__44p3_p3_1[] = { static const static_codebook _44p3_p3_1 = { 5, 243, - (long *)_vq_lengthlist__44p3_p3_1, + (char *)_vq_lengthlist__44p3_p3_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p3_p3_1, 0 @@ -3322,7 +3338,7 @@ static const long _vq_quantlist__44p3_p4_0[] = { 2, }; -static const long _vq_lengthlist__44p3_p4_0[] = { +static const char _vq_lengthlist__44p3_p4_0[] = { 1, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8,10,11,11, 9, 8, 8, 8, 8, 8,11,11,11,10, 8, 8, 5, 7, 7, 9,11, 11,10,11,11,10,11,11,12,13,14,11,12,12,10,11,11, @@ -3343,7 +3359,7 @@ static const long _vq_lengthlist__44p3_p4_0[] = { static const static_codebook _44p3_p4_0 = { 5, 243, - (long *)_vq_lengthlist__44p3_p4_0, + (char *)_vq_lengthlist__44p3_p4_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44p3_p4_0, 0 @@ -3357,7 +3373,7 @@ static const long _vq_quantlist__44p3_p4_1[] = { 4, }; -static const long _vq_lengthlist__44p3_p4_1[] = { +static const char _vq_lengthlist__44p3_p4_1[] = { 3, 4, 5, 8, 8,12,10,10,12,12,12,10,10,12,12,13, 11,11,12,12,13,12,12,12,12,13,10,10,13,13,13,13, 13,13,13,13,10,10,13,13,13,11,11,13,13,14,13,13, @@ -3558,7 +3574,7 @@ static const long _vq_lengthlist__44p3_p4_1[] = { static const static_codebook _44p3_p4_1 = { 5, 3125, - (long *)_vq_lengthlist__44p3_p4_1, + (char *)_vq_lengthlist__44p3_p4_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p3_p4_1, 0 @@ -3572,7 +3588,7 @@ static const long _vq_quantlist__44p3_p5_0[] = { 4, }; -static const long _vq_lengthlist__44p3_p5_0[] = { +static const char _vq_lengthlist__44p3_p5_0[] = { 2, 6, 6,14,14, 6, 7, 7,14,14, 7, 7, 7,15,15, 0, 12,12,15,15, 0,13,13,15,15, 7, 8, 8,15,15,10,10, 10,16,16, 9, 8, 8,15,15, 0,13,13,18,17, 0,13,13, @@ -3773,7 +3789,7 @@ static const long _vq_lengthlist__44p3_p5_0[] = { static const static_codebook _44p3_p5_0 = { 5, 3125, - (long *)_vq_lengthlist__44p3_p5_0, + (char *)_vq_lengthlist__44p3_p5_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44p3_p5_0, 0 @@ -3789,13 +3805,13 @@ static const long _vq_quantlist__44p3_p5_1[] = { 6, }; -static const long _vq_lengthlist__44p3_p5_1[] = { +static const char _vq_lengthlist__44p3_p5_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44p3_p5_1 = { 1, 7, - (long *)_vq_lengthlist__44p3_p5_1, + (char *)_vq_lengthlist__44p3_p5_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44p3_p5_1, 0 @@ -3807,7 +3823,7 @@ static const long _vq_quantlist__44p3_p6_0[] = { 2, }; -static const long _vq_lengthlist__44p3_p6_0[] = { +static const char _vq_lengthlist__44p3_p6_0[] = { 1, 6, 6, 7, 7, 7, 7, 8, 8, 7, 9, 9,11,11,11, 9, 8, 8, 8, 9, 9,12,11,11, 9, 8, 8, 6, 7, 7,10,11, 10,10,10,10,11,11,10,14,13,14,12,11,11,11,11,11, @@ -3828,7 +3844,7 @@ static const long _vq_lengthlist__44p3_p6_0[] = { static const static_codebook _44p3_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44p3_p6_0, + (char *)_vq_lengthlist__44p3_p6_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44p3_p6_0, 0 @@ -3840,7 +3856,7 @@ static const long _vq_quantlist__44p3_p6_1[] = { 2, }; -static const long _vq_lengthlist__44p3_p6_1[] = { +static const char _vq_lengthlist__44p3_p6_1[] = { 2, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 7, 7, 8, 8, 8, 9, 9, 9, 9, 7, 8, 6, 7, 7, 8, 8, 8, 8, 8, 8, 9, 8, 8,10, 9, 9,10, 8, 8,10, 8, 8, @@ -3861,7 +3877,7 @@ static const long _vq_lengthlist__44p3_p6_1[] = { static const static_codebook _44p3_p6_1 = { 5, 243, - (long *)_vq_lengthlist__44p3_p6_1, + (char *)_vq_lengthlist__44p3_p6_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44p3_p6_1, 0 @@ -3873,7 +3889,7 @@ static const long _vq_quantlist__44p3_p7_0[] = { 2, }; -static const long _vq_lengthlist__44p3_p7_0[] = { +static const char _vq_lengthlist__44p3_p7_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -3894,7 +3910,7 @@ static const long _vq_lengthlist__44p3_p7_0[] = { static const static_codebook _44p3_p7_0 = { 5, 243, - (long *)_vq_lengthlist__44p3_p7_0, + (char *)_vq_lengthlist__44p3_p7_0, 1, -513979392, 1633504256, 2, 0, (long *)_vq_quantlist__44p3_p7_0, 0 @@ -3906,7 +3922,7 @@ static const long _vq_quantlist__44p3_p7_1[] = { 2, }; -static const long _vq_lengthlist__44p3_p7_1[] = { +static const char _vq_lengthlist__44p3_p7_1[] = { 1, 9, 9, 6, 9, 9, 5, 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -3927,7 +3943,7 @@ static const long _vq_lengthlist__44p3_p7_1[] = { static const static_codebook _44p3_p7_1 = { 5, 243, - (long *)_vq_lengthlist__44p3_p7_1, + (char *)_vq_lengthlist__44p3_p7_1, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p3_p7_1, 0 @@ -3961,14 +3977,14 @@ static const long _vq_quantlist__44p3_p7_2[] = { 24, }; -static const long _vq_lengthlist__44p3_p7_2[] = { +static const char _vq_lengthlist__44p3_p7_2[] = { 1, 3, 2, 5, 4, 7, 7, 8, 8, 9, 9,10,10,11,11,12, 12,13,13,14,14,15,15,15,15, }; static const static_codebook _44p3_p7_2 = { 1, 25, - (long *)_vq_lengthlist__44p3_p7_2, + (char *)_vq_lengthlist__44p3_p7_2, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44p3_p7_2, 0 @@ -4002,20 +4018,20 @@ static const long _vq_quantlist__44p3_p7_3[] = { 24, }; -static const long _vq_lengthlist__44p3_p7_3[] = { +static const char _vq_lengthlist__44p3_p7_3[] = { 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p3_p7_3 = { 1, 25, - (long *)_vq_lengthlist__44p3_p7_3, + (char *)_vq_lengthlist__44p3_p7_3, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44p3_p7_3, 0 }; -static const long _huff_lengthlist__44p3_short[] = { +static const char _huff_lengthlist__44p3_short[] = { 4, 5,16, 9, 9,12,17,18, 4, 2,18, 6, 5, 9,13,15, 10, 7, 7, 6, 7, 9,13,13, 8, 5, 6, 5, 5, 7,11,12, 8, 4, 7, 4, 3, 6,10,12,11, 8, 9, 7, 6, 8,11,12, @@ -4024,7 +4040,7 @@ static const long _huff_lengthlist__44p3_short[] = { static const static_codebook _huff_book__44p3_short = { 2, 64, - (long *)_huff_lengthlist__44p3_short, + (char *)_huff_lengthlist__44p3_short, 0, 0, 0, 0, 0, NULL, 0 @@ -4046,7 +4062,7 @@ static const long _vq_quantlist__44p4_l0_0[] = { 12, }; -static const long _vq_lengthlist__44p4_l0_0[] = { +static const char _vq_lengthlist__44p4_l0_0[] = { 1, 4, 4, 8, 8, 9, 8, 9, 9,10,10,10,10, 4, 6, 5, 8, 7, 9, 9, 9, 9,10, 9,10,10, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9,10, 9,10, 8, 9, 8, 9, 8,10, 9,11, 9, @@ -4062,7 +4078,7 @@ static const long _vq_lengthlist__44p4_l0_0[] = { static const static_codebook _44p4_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44p4_l0_0, + (char *)_vq_lengthlist__44p4_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44p4_l0_0, 0 @@ -4076,14 +4092,14 @@ static const long _vq_quantlist__44p4_l0_1[] = { 4, }; -static const long _vq_lengthlist__44p4_l0_1[] = { +static const char _vq_lengthlist__44p4_l0_1[] = { 3, 4, 4, 5, 5, 4, 4, 5, 5, 5, 4, 5, 4, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, }; static const static_codebook _44p4_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44p4_l0_1, + (char *)_vq_lengthlist__44p4_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p4_l0_1, 0 @@ -4095,31 +4111,31 @@ static const long _vq_quantlist__44p4_l1_0[] = { 2, }; -static const long _vq_lengthlist__44p4_l1_0[] = { +static const char _vq_lengthlist__44p4_l1_0[] = { 1, 4, 4, 4, 4, 4, 4, 4, 4, }; static const static_codebook _44p4_l1_0 = { 2, 9, - (long *)_vq_lengthlist__44p4_l1_0, + (char *)_vq_lengthlist__44p4_l1_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p4_l1_0, 0 }; -static const long _huff_lengthlist__44p4_lfe[] = { +static const char _huff_lengthlist__44p4_lfe[] = { 1, 3, 2, 3, }; static const static_codebook _huff_book__44p4_lfe = { 2, 4, - (long *)_huff_lengthlist__44p4_lfe, + (char *)_huff_lengthlist__44p4_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44p4_long[] = { +static const char _huff_lengthlist__44p4_long[] = { 3, 5,13, 9, 9,12,16,18, 4, 2,20, 6, 7,10,15,20, 10, 7, 5, 5, 6, 8,10,13, 8, 5, 5, 3, 5, 7,10,11, 9, 7, 6, 5, 5, 7, 9, 9,11,10, 8, 7, 6, 6, 8, 8, @@ -4128,7 +4144,7 @@ static const long _huff_lengthlist__44p4_long[] = { static const static_codebook _huff_book__44p4_long = { 2, 64, - (long *)_huff_lengthlist__44p4_long, + (char *)_huff_lengthlist__44p4_long, 0, 0, 0, 0, 0, NULL, 0 @@ -4140,7 +4156,7 @@ static const long _vq_quantlist__44p4_p1_0[] = { 2, }; -static const long _vq_lengthlist__44p4_p1_0[] = { +static const char _vq_lengthlist__44p4_p1_0[] = { 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -4161,7 +4177,7 @@ static const long _vq_lengthlist__44p4_p1_0[] = { static const static_codebook _44p4_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44p4_p1_0, + (char *)_vq_lengthlist__44p4_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p4_p1_0, 0 @@ -4175,7 +4191,7 @@ static const long _vq_quantlist__44p4_p2_0[] = { 4, }; -static const long _vq_lengthlist__44p4_p2_0[] = { +static const char _vq_lengthlist__44p4_p2_0[] = { 3, 9, 9, 0, 0, 0, 8, 8, 0, 0, 0, 9, 9, 0, 0, 0, 12,12, 0, 0, 0, 0, 0, 0, 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0,11,11, 0, 0, 0, 0, 0, @@ -4376,7 +4392,7 @@ static const long _vq_lengthlist__44p4_p2_0[] = { static const static_codebook _44p4_p2_0 = { 5, 3125, - (long *)_vq_lengthlist__44p4_p2_0, + (char *)_vq_lengthlist__44p4_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p4_p2_0, 0 @@ -4388,7 +4404,7 @@ static const long _vq_quantlist__44p4_p3_0[] = { 2, }; -static const long _vq_lengthlist__44p4_p3_0[] = { +static const char _vq_lengthlist__44p4_p3_0[] = { 1, 6, 6, 5, 7, 8, 0, 8, 8, 6, 9, 9, 7,10,10, 0, 8, 8, 0, 9, 9, 0,12,12, 0, 8, 8, 4, 7, 7, 6,10, 10, 0,12,12, 7,11,11, 8,12,12, 0,12,12, 0,13,12, @@ -4409,7 +4425,7 @@ static const long _vq_lengthlist__44p4_p3_0[] = { static const static_codebook _44p4_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44p4_p3_0, + (char *)_vq_lengthlist__44p4_p3_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44p4_p3_0, 0 @@ -4421,7 +4437,7 @@ static const long _vq_quantlist__44p4_p3_1[] = { 2, }; -static const long _vq_lengthlist__44p4_p3_1[] = { +static const char _vq_lengthlist__44p4_p3_1[] = { 3, 5, 5, 0, 8, 8, 0, 8, 8, 0, 9, 9, 0,10,10, 0, 8, 8, 0, 8, 8, 0,10,10, 0, 8, 8, 0, 7, 7, 0, 8, 8, 0, 7, 7, 0, 8, 8, 0, 8, 8, 0, 8, 8, 0, 8, 8, @@ -4442,7 +4458,7 @@ static const long _vq_lengthlist__44p4_p3_1[] = { static const static_codebook _44p4_p3_1 = { 5, 243, - (long *)_vq_lengthlist__44p4_p3_1, + (char *)_vq_lengthlist__44p4_p3_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p4_p3_1, 0 @@ -4454,7 +4470,7 @@ static const long _vq_quantlist__44p4_p4_0[] = { 2, }; -static const long _vq_lengthlist__44p4_p4_0[] = { +static const char _vq_lengthlist__44p4_p4_0[] = { 1, 6, 6, 6, 7, 7, 7, 8, 8, 7, 8, 8,10,11,11, 9, 8, 8, 8, 8, 8,11,11,12, 9, 8, 8, 5, 7, 7, 9,11, 11,10,11,11,10,11,11,12,14,14,11,12,12,10,12,12, @@ -4475,7 +4491,7 @@ static const long _vq_lengthlist__44p4_p4_0[] = { static const static_codebook _44p4_p4_0 = { 5, 243, - (long *)_vq_lengthlist__44p4_p4_0, + (char *)_vq_lengthlist__44p4_p4_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44p4_p4_0, 0 @@ -4489,7 +4505,7 @@ static const long _vq_quantlist__44p4_p4_1[] = { 4, }; -static const long _vq_lengthlist__44p4_p4_1[] = { +static const char _vq_lengthlist__44p4_p4_1[] = { 4, 5, 5, 9, 9,12, 9, 9,12,12,12,10,10,13,13,13, 11,11,12,12,13,13,13,12,12,13,10,10,13,13,13,13, 13,13,13,13,10,10,13,12,13,11,11,13,13,13,14,14, @@ -4690,7 +4706,7 @@ static const long _vq_lengthlist__44p4_p4_1[] = { static const static_codebook _44p4_p4_1 = { 5, 3125, - (long *)_vq_lengthlist__44p4_p4_1, + (char *)_vq_lengthlist__44p4_p4_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p4_p4_1, 0 @@ -4704,7 +4720,7 @@ static const long _vq_quantlist__44p4_p5_0[] = { 4, }; -static const long _vq_lengthlist__44p4_p5_0[] = { +static const char _vq_lengthlist__44p4_p5_0[] = { 1, 7, 6,15,15, 7, 8, 8,15,15, 8, 8, 8,15,15, 0, 13,13,16,16, 0,14,14,16,16, 7, 9, 9,16,16,10,11, 11,17,17,10, 8, 8,15,16, 0,14,14,18,18, 0,14,14, @@ -4905,7 +4921,7 @@ static const long _vq_lengthlist__44p4_p5_0[] = { static const static_codebook _44p4_p5_0 = { 5, 3125, - (long *)_vq_lengthlist__44p4_p5_0, + (char *)_vq_lengthlist__44p4_p5_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44p4_p5_0, 0 @@ -4921,13 +4937,13 @@ static const long _vq_quantlist__44p4_p5_1[] = { 6, }; -static const long _vq_lengthlist__44p4_p5_1[] = { +static const char _vq_lengthlist__44p4_p5_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44p4_p5_1 = { 1, 7, - (long *)_vq_lengthlist__44p4_p5_1, + (char *)_vq_lengthlist__44p4_p5_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44p4_p5_1, 0 @@ -4939,7 +4955,7 @@ static const long _vq_quantlist__44p4_p6_0[] = { 2, }; -static const long _vq_lengthlist__44p4_p6_0[] = { +static const char _vq_lengthlist__44p4_p6_0[] = { 1, 7, 7, 7, 8, 8, 7, 8, 8, 7, 9, 9,11,11,11, 9, 8, 8, 8, 9, 9,12,11,12, 9, 8, 8, 6, 7, 7,10,11, 11,10,10,10,11,11,11,14,14,14,12,11,12,11,11,11, @@ -4960,7 +4976,7 @@ static const long _vq_lengthlist__44p4_p6_0[] = { static const static_codebook _44p4_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44p4_p6_0, + (char *)_vq_lengthlist__44p4_p6_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44p4_p6_0, 0 @@ -4972,7 +4988,7 @@ static const long _vq_quantlist__44p4_p6_1[] = { 2, }; -static const long _vq_lengthlist__44p4_p6_1[] = { +static const char _vq_lengthlist__44p4_p6_1[] = { 2, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 7, 7, 8, 8, 8, 9, 9, 9, 9, 8, 8, 6, 7, 7, 8, 8, 8, 8, 8, 8, 9, 8, 8, 9, 8, 9, 9, 8, 8,10, 8, 8, @@ -4993,7 +5009,7 @@ static const long _vq_lengthlist__44p4_p6_1[] = { static const static_codebook _44p4_p6_1 = { 5, 243, - (long *)_vq_lengthlist__44p4_p6_1, + (char *)_vq_lengthlist__44p4_p6_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44p4_p6_1, 0 @@ -5005,7 +5021,7 @@ static const long _vq_quantlist__44p4_p7_0[] = { 2, }; -static const long _vq_lengthlist__44p4_p7_0[] = { +static const char _vq_lengthlist__44p4_p7_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -5026,7 +5042,7 @@ static const long _vq_lengthlist__44p4_p7_0[] = { static const static_codebook _44p4_p7_0 = { 5, 243, - (long *)_vq_lengthlist__44p4_p7_0, + (char *)_vq_lengthlist__44p4_p7_0, 1, -513979392, 1633504256, 2, 0, (long *)_vq_quantlist__44p4_p7_0, 0 @@ -5038,7 +5054,7 @@ static const long _vq_quantlist__44p4_p7_1[] = { 2, }; -static const long _vq_lengthlist__44p4_p7_1[] = { +static const char _vq_lengthlist__44p4_p7_1[] = { 1, 9, 9, 7, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -5059,7 +5075,7 @@ static const long _vq_lengthlist__44p4_p7_1[] = { static const static_codebook _44p4_p7_1 = { 5, 243, - (long *)_vq_lengthlist__44p4_p7_1, + (char *)_vq_lengthlist__44p4_p7_1, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p4_p7_1, 0 @@ -5093,14 +5109,14 @@ static const long _vq_quantlist__44p4_p7_2[] = { 24, }; -static const long _vq_lengthlist__44p4_p7_2[] = { +static const char _vq_lengthlist__44p4_p7_2[] = { 1, 3, 2, 5, 4, 7, 7, 8, 8, 9, 9,10,10,11,11,12, 12,13,13,14,14,15,15,15,15, }; static const static_codebook _44p4_p7_2 = { 1, 25, - (long *)_vq_lengthlist__44p4_p7_2, + (char *)_vq_lengthlist__44p4_p7_2, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44p4_p7_2, 0 @@ -5134,20 +5150,20 @@ static const long _vq_quantlist__44p4_p7_3[] = { 24, }; -static const long _vq_lengthlist__44p4_p7_3[] = { +static const char _vq_lengthlist__44p4_p7_3[] = { 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p4_p7_3 = { 1, 25, - (long *)_vq_lengthlist__44p4_p7_3, + (char *)_vq_lengthlist__44p4_p7_3, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44p4_p7_3, 0 }; -static const long _huff_lengthlist__44p4_short[] = { +static const char _huff_lengthlist__44p4_short[] = { 3, 5,16, 9, 9,13,18,21, 4, 2,21, 6, 6,10,15,21, 16,19, 6, 5, 7,10,13,16, 8, 6, 5, 4, 4, 8,13,16, 8, 5, 6, 4, 4, 7,12,15,13,10, 9, 7, 7, 9,13,16, @@ -5156,7 +5172,7 @@ static const long _huff_lengthlist__44p4_short[] = { static const static_codebook _huff_book__44p4_short = { 2, 64, - (long *)_huff_lengthlist__44p4_short, + (char *)_huff_lengthlist__44p4_short, 0, 0, 0, 0, 0, NULL, 0 @@ -5178,7 +5194,7 @@ static const long _vq_quantlist__44p5_l0_0[] = { 12, }; -static const long _vq_lengthlist__44p5_l0_0[] = { +static const char _vq_lengthlist__44p5_l0_0[] = { 1, 4, 4, 8, 8,10,10,10,10, 9, 8,11,11, 4, 6, 5, 8, 6,10,10,10,10,10, 9,10, 9, 4, 5, 6, 6, 9,10, 10,10,10, 9,10, 9,10, 8, 9, 8, 9, 8, 9, 9,10, 9, @@ -5194,7 +5210,7 @@ static const long _vq_lengthlist__44p5_l0_0[] = { static const static_codebook _44p5_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44p5_l0_0, + (char *)_vq_lengthlist__44p5_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44p5_l0_0, 0 @@ -5208,14 +5224,14 @@ static const long _vq_quantlist__44p5_l0_1[] = { 4, }; -static const long _vq_lengthlist__44p5_l0_1[] = { +static const char _vq_lengthlist__44p5_l0_1[] = { 4, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p5_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44p5_l0_1, + (char *)_vq_lengthlist__44p5_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p5_l0_1, 0 @@ -5227,31 +5243,31 @@ static const long _vq_quantlist__44p5_l1_0[] = { 2, }; -static const long _vq_lengthlist__44p5_l1_0[] = { +static const char _vq_lengthlist__44p5_l1_0[] = { 1, 4, 4, 4, 4, 4, 4, 4, 4, }; static const static_codebook _44p5_l1_0 = { 2, 9, - (long *)_vq_lengthlist__44p5_l1_0, + (char *)_vq_lengthlist__44p5_l1_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p5_l1_0, 0 }; -static const long _huff_lengthlist__44p5_lfe[] = { +static const char _huff_lengthlist__44p5_lfe[] = { 1, 3, 2, 3, }; static const static_codebook _huff_book__44p5_lfe = { 2, 4, - (long *)_huff_lengthlist__44p5_lfe, + (char *)_huff_lengthlist__44p5_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44p5_long[] = { +static const char _huff_lengthlist__44p5_long[] = { 3, 7,12,14,14,16,18,19, 6, 2, 4, 6, 8, 9,12,14, 12, 3, 3, 5, 7, 8,11,13,13, 6, 4, 5, 7, 8,10,11, 14, 8, 7, 7, 7, 7, 9,10,15, 9, 8, 7, 7, 6, 8, 9, @@ -5260,7 +5276,7 @@ static const long _huff_lengthlist__44p5_long[] = { static const static_codebook _huff_book__44p5_long = { 2, 64, - (long *)_huff_lengthlist__44p5_long, + (char *)_huff_lengthlist__44p5_long, 0, 0, 0, 0, 0, NULL, 0 @@ -5272,7 +5288,7 @@ static const long _vq_quantlist__44p5_p1_0[] = { 2, }; -static const long _vq_lengthlist__44p5_p1_0[] = { +static const char _vq_lengthlist__44p5_p1_0[] = { 2, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 8, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 8, 5, 7, 8, 8, 9, 10, 8, 9,10, 8, 9,10, 9,10,12,10,11,11, 8,10,10, @@ -5293,7 +5309,7 @@ static const long _vq_lengthlist__44p5_p1_0[] = { static const static_codebook _44p5_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44p5_p1_0, + (char *)_vq_lengthlist__44p5_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p5_p1_0, 0 @@ -5307,7 +5323,7 @@ static const long _vq_quantlist__44p5_p2_0[] = { 4, }; -static const long _vq_lengthlist__44p5_p2_0[] = { +static const char _vq_lengthlist__44p5_p2_0[] = { 4, 6, 6, 9, 9, 6, 7, 8,10,10, 6, 8, 7,10,10, 8, 10,10,12,13, 8,10,10,13,12, 6, 7, 8,10,10, 7, 8, 9,10,11, 8, 9, 9,11,11,10,10,11,12,14,10,11,11, @@ -5508,7 +5524,7 @@ static const long _vq_lengthlist__44p5_p2_0[] = { static const static_codebook _44p5_p2_0 = { 5, 3125, - (long *)_vq_lengthlist__44p5_p2_0, + (char *)_vq_lengthlist__44p5_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p5_p2_0, 0 @@ -5520,7 +5536,7 @@ static const long _vq_quantlist__44p5_p3_0[] = { 2, }; -static const long _vq_lengthlist__44p5_p3_0[] = { +static const char _vq_lengthlist__44p5_p3_0[] = { 1, 5, 6, 5, 7, 8, 5, 8, 7, 5, 7, 8, 7, 8,10, 8, 10,10, 5, 8, 7, 8,10,10, 7,10, 8, 6, 8, 9, 8,10, 11, 9,10,10, 9,10,11,10,11,12,11,12,12, 9,11,10, @@ -5541,7 +5557,7 @@ static const long _vq_lengthlist__44p5_p3_0[] = { static const static_codebook _44p5_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44p5_p3_0, + (char *)_vq_lengthlist__44p5_p3_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44p5_p3_0, 0 @@ -5553,7 +5569,7 @@ static const long _vq_quantlist__44p5_p3_1[] = { 2, }; -static const long _vq_lengthlist__44p5_p3_1[] = { +static const char _vq_lengthlist__44p5_p3_1[] = { 5, 6, 6, 6, 7, 7, 6, 7, 7, 6, 7, 7, 7, 7, 8, 7, 8, 8, 6, 7, 7, 7, 8, 8, 7, 8, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 9, 9, 8, 8, 8, @@ -5574,7 +5590,7 @@ static const long _vq_lengthlist__44p5_p3_1[] = { static const static_codebook _44p5_p3_1 = { 5, 243, - (long *)_vq_lengthlist__44p5_p3_1, + (char *)_vq_lengthlist__44p5_p3_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p5_p3_1, 0 @@ -5586,7 +5602,7 @@ static const long _vq_quantlist__44p5_p4_0[] = { 2, }; -static const long _vq_lengthlist__44p5_p4_0[] = { +static const char _vq_lengthlist__44p5_p4_0[] = { 1, 5, 5, 5, 7, 9, 5, 9, 7, 5, 7, 8, 7, 7,10, 9, 10,10, 5, 8, 7, 9,10,10, 7,10, 7, 6, 8, 9, 9,10, 12, 9,11,11, 9,10,11,11,11,13,12,13,13, 9,11,11, @@ -5607,7 +5623,7 @@ static const long _vq_lengthlist__44p5_p4_0[] = { static const static_codebook _44p5_p4_0 = { 5, 243, - (long *)_vq_lengthlist__44p5_p4_0, + (char *)_vq_lengthlist__44p5_p4_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44p5_p4_0, 0 @@ -5621,7 +5637,7 @@ static const long _vq_quantlist__44p5_p4_1[] = { 4, }; -static const long _vq_lengthlist__44p5_p4_1[] = { +static const char _vq_lengthlist__44p5_p4_1[] = { 5, 7, 7,10,10, 7, 8, 9,10,11, 7, 9, 8,11,10, 9, 10,10,11,11, 9,10,10,11,11, 7, 9, 9,10,10, 8, 9, 10,10,11, 9,10,10,11,11,10,10,11,11,11,10,11,11, @@ -5822,7 +5838,7 @@ static const long _vq_lengthlist__44p5_p4_1[] = { static const static_codebook _44p5_p4_1 = { 5, 3125, - (long *)_vq_lengthlist__44p5_p4_1, + (char *)_vq_lengthlist__44p5_p4_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p5_p4_1, 0 @@ -5836,7 +5852,7 @@ static const long _vq_quantlist__44p5_p5_0[] = { 4, }; -static const long _vq_lengthlist__44p5_p5_0[] = { +static const char _vq_lengthlist__44p5_p5_0[] = { 1, 6, 6,10,10, 6, 7, 9,11,13, 5, 9, 7,13,11, 8, 11,12,13,15, 8,12,11,15,13, 6, 7, 8,11,11, 7, 8, 10,11,13, 9,10,10,13,13,11,11,13,12,16,12,13,13, @@ -6037,7 +6053,7 @@ static const long _vq_lengthlist__44p5_p5_0[] = { static const static_codebook _44p5_p5_0 = { 5, 3125, - (long *)_vq_lengthlist__44p5_p5_0, + (char *)_vq_lengthlist__44p5_p5_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44p5_p5_0, 0 @@ -6053,13 +6069,13 @@ static const long _vq_quantlist__44p5_p5_1[] = { 6, }; -static const long _vq_lengthlist__44p5_p5_1[] = { +static const char _vq_lengthlist__44p5_p5_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44p5_p5_1 = { 1, 7, - (long *)_vq_lengthlist__44p5_p5_1, + (char *)_vq_lengthlist__44p5_p5_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44p5_p5_1, 0 @@ -6071,7 +6087,7 @@ static const long _vq_quantlist__44p5_p6_0[] = { 2, }; -static const long _vq_lengthlist__44p5_p6_0[] = { +static const char _vq_lengthlist__44p5_p6_0[] = { 1, 5, 5, 5, 7, 9, 5, 9, 7, 5, 7, 8, 7, 7,10, 9, 9,10, 5, 8, 7, 9,10, 9, 7,10, 7, 6, 9, 9, 9,10, 12,10,12,11, 9,10,11,11,10,13,12,12,13,10,11,11, @@ -6092,7 +6108,7 @@ static const long _vq_lengthlist__44p5_p6_0[] = { static const static_codebook _44p5_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44p5_p6_0, + (char *)_vq_lengthlist__44p5_p6_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44p5_p6_0, 0 @@ -6104,7 +6120,7 @@ static const long _vq_quantlist__44p5_p6_1[] = { 2, }; -static const long _vq_lengthlist__44p5_p6_1[] = { +static const char _vq_lengthlist__44p5_p6_1[] = { 2, 6, 6, 5, 7, 8, 5, 8, 7, 6, 7, 7, 7, 7, 8, 8, 8, 8, 6, 7, 7, 7, 8, 8, 7, 8, 7, 6, 8, 8, 8, 9, 10, 8, 9, 9, 8, 9, 9, 9, 9,10,10,10,10, 8, 9, 9, @@ -6125,7 +6141,7 @@ static const long _vq_lengthlist__44p5_p6_1[] = { static const static_codebook _44p5_p6_1 = { 5, 243, - (long *)_vq_lengthlist__44p5_p6_1, + (char *)_vq_lengthlist__44p5_p6_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44p5_p6_1, 0 @@ -6137,7 +6153,7 @@ static const long _vq_quantlist__44p5_p7_0[] = { 2, }; -static const long _vq_lengthlist__44p5_p7_0[] = { +static const char _vq_lengthlist__44p5_p7_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -6158,7 +6174,7 @@ static const long _vq_lengthlist__44p5_p7_0[] = { static const static_codebook _44p5_p7_0 = { 5, 243, - (long *)_vq_lengthlist__44p5_p7_0, + (char *)_vq_lengthlist__44p5_p7_0, 1, -513979392, 1633504256, 2, 0, (long *)_vq_quantlist__44p5_p7_0, 0 @@ -6170,7 +6186,7 @@ static const long _vq_quantlist__44p5_p7_1[] = { 2, }; -static const long _vq_lengthlist__44p5_p7_1[] = { +static const char _vq_lengthlist__44p5_p7_1[] = { 1, 7, 7, 6, 9, 9, 7, 9, 9, 6, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -6191,7 +6207,7 @@ static const long _vq_lengthlist__44p5_p7_1[] = { static const static_codebook _44p5_p7_1 = { 5, 243, - (long *)_vq_lengthlist__44p5_p7_1, + (char *)_vq_lengthlist__44p5_p7_1, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p5_p7_1, 0 @@ -6225,14 +6241,14 @@ static const long _vq_quantlist__44p5_p7_2[] = { 24, }; -static const long _vq_lengthlist__44p5_p7_2[] = { +static const char _vq_lengthlist__44p5_p7_2[] = { 1, 2, 3, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, 11,12,12,13,13,14,14,14,14, }; static const static_codebook _44p5_p7_2 = { 1, 25, - (long *)_vq_lengthlist__44p5_p7_2, + (char *)_vq_lengthlist__44p5_p7_2, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44p5_p7_2, 0 @@ -6266,20 +6282,20 @@ static const long _vq_quantlist__44p5_p7_3[] = { 24, }; -static const long _vq_lengthlist__44p5_p7_3[] = { +static const char _vq_lengthlist__44p5_p7_3[] = { 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p5_p7_3 = { 1, 25, - (long *)_vq_lengthlist__44p5_p7_3, + (char *)_vq_lengthlist__44p5_p7_3, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44p5_p7_3, 0 }; -static const long _huff_lengthlist__44p5_short[] = { +static const char _huff_lengthlist__44p5_short[] = { 4, 7,12,14,15,18,20,20, 5, 3, 4, 6, 9,11,15,19, 9, 4, 3, 4, 7, 9,13,18,11, 6, 3, 3, 5, 8,13,19, 14, 9, 6, 5, 7,10,16,20,16,11, 9, 8,10,10,14,16, @@ -6288,7 +6304,7 @@ static const long _huff_lengthlist__44p5_short[] = { static const static_codebook _huff_book__44p5_short = { 2, 64, - (long *)_huff_lengthlist__44p5_short, + (char *)_huff_lengthlist__44p5_short, 0, 0, 0, 0, 0, NULL, 0 @@ -6310,7 +6326,7 @@ static const long _vq_quantlist__44p6_l0_0[] = { 12, }; -static const long _vq_lengthlist__44p6_l0_0[] = { +static const char _vq_lengthlist__44p6_l0_0[] = { 1, 4, 4, 7, 7,10,10,12,12,12,12,13,12, 5, 5, 5, 8, 6,11, 9,12,12,13,12,12,12, 4, 5, 5, 6, 8, 9, 11,12,12,13,12,12,12, 7, 7, 8, 9, 9,11, 8,12, 9, @@ -6326,7 +6342,7 @@ static const long _vq_lengthlist__44p6_l0_0[] = { static const static_codebook _44p6_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44p6_l0_0, + (char *)_vq_lengthlist__44p6_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44p6_l0_0, 0 @@ -6340,14 +6356,14 @@ static const long _vq_quantlist__44p6_l0_1[] = { 4, }; -static const long _vq_lengthlist__44p6_l0_1[] = { +static const char _vq_lengthlist__44p6_l0_1[] = { 4, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 4, }; static const static_codebook _44p6_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44p6_l0_1, + (char *)_vq_lengthlist__44p6_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p6_l0_1, 0 @@ -6359,31 +6375,31 @@ static const long _vq_quantlist__44p6_l1_0[] = { 2, }; -static const long _vq_lengthlist__44p6_l1_0[] = { +static const char _vq_lengthlist__44p6_l1_0[] = { 1, 3, 2, 5, 5, 6, 6, 6, 6, }; static const static_codebook _44p6_l1_0 = { 2, 9, - (long *)_vq_lengthlist__44p6_l1_0, + (char *)_vq_lengthlist__44p6_l1_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p6_l1_0, 0 }; -static const long _huff_lengthlist__44p6_lfe[] = { +static const char _huff_lengthlist__44p6_lfe[] = { 2, 3, 1, 3, }; static const static_codebook _huff_book__44p6_lfe = { 2, 4, - (long *)_huff_lengthlist__44p6_lfe, + (char *)_huff_lengthlist__44p6_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44p6_long[] = { +static const char _huff_lengthlist__44p6_long[] = { 2, 7,13,15,16,17,19,20, 6, 3, 4, 7, 9,10,12,15, 13, 4, 3, 4, 7, 8,11,13,14, 7, 4, 4, 6, 7,10,11, 16, 9, 7, 6, 7, 8, 9,10,16, 9, 8, 7, 7, 6, 8, 8, @@ -6392,7 +6408,7 @@ static const long _huff_lengthlist__44p6_long[] = { static const static_codebook _huff_book__44p6_long = { 2, 64, - (long *)_huff_lengthlist__44p6_long, + (char *)_huff_lengthlist__44p6_long, 0, 0, 0, 0, 0, NULL, 0 @@ -6404,7 +6420,7 @@ static const long _vq_quantlist__44p6_p1_0[] = { 2, }; -static const long _vq_lengthlist__44p6_p1_0[] = { +static const char _vq_lengthlist__44p6_p1_0[] = { 2, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 8, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 8, 5, 7, 8, 8, 9, 10, 8, 9, 9, 8, 9,10, 9,10,12,10,11,11, 8, 9,10, @@ -6425,7 +6441,7 @@ static const long _vq_lengthlist__44p6_p1_0[] = { static const static_codebook _44p6_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44p6_p1_0, + (char *)_vq_lengthlist__44p6_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p6_p1_0, 0 @@ -6439,7 +6455,7 @@ static const long _vq_quantlist__44p6_p2_0[] = { 4, }; -static const long _vq_lengthlist__44p6_p2_0[] = { +static const char _vq_lengthlist__44p6_p2_0[] = { 4, 6, 6, 9, 9, 6, 7, 8,10,10, 6, 8, 7,10,10, 8, 10,10,12,13, 8,10,10,13,12, 6, 8, 8,10,10, 7, 8, 9,10,11, 8, 9, 9,11,11,10,10,11,12,13,10,11,11, @@ -6640,7 +6656,7 @@ static const long _vq_lengthlist__44p6_p2_0[] = { static const static_codebook _44p6_p2_0 = { 5, 3125, - (long *)_vq_lengthlist__44p6_p2_0, + (char *)_vq_lengthlist__44p6_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p6_p2_0, 0 @@ -6652,7 +6668,7 @@ static const long _vq_quantlist__44p6_p3_0[] = { 2, }; -static const long _vq_lengthlist__44p6_p3_0[] = { +static const char _vq_lengthlist__44p6_p3_0[] = { 1, 5, 5, 5, 7, 8, 5, 8, 7, 5, 7, 8, 8, 8,10, 8, 10,10, 5, 8, 7, 8,10,10, 8,10, 8, 6, 8, 9, 8,10, 12, 9,11,11, 9,10,11,11,11,13,12,13,13, 9,11,11, @@ -6673,7 +6689,7 @@ static const long _vq_lengthlist__44p6_p3_0[] = { static const static_codebook _44p6_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44p6_p3_0, + (char *)_vq_lengthlist__44p6_p3_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44p6_p3_0, 0 @@ -6685,7 +6701,7 @@ static const long _vq_quantlist__44p6_p3_1[] = { 2, }; -static const long _vq_lengthlist__44p6_p3_1[] = { +static const char _vq_lengthlist__44p6_p3_1[] = { 5, 7, 7, 6, 7, 7, 6, 7, 7, 6, 7, 7, 7, 8, 8, 7, 8, 8, 6, 7, 7, 7, 8, 8, 7, 8, 8, 7, 7, 8, 7, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 9, 8, 9, 9, 8, 8, 8, @@ -6706,7 +6722,7 @@ static const long _vq_lengthlist__44p6_p3_1[] = { static const static_codebook _44p6_p3_1 = { 5, 243, - (long *)_vq_lengthlist__44p6_p3_1, + (char *)_vq_lengthlist__44p6_p3_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p6_p3_1, 0 @@ -6718,7 +6734,7 @@ static const long _vq_quantlist__44p6_p4_0[] = { 2, }; -static const long _vq_lengthlist__44p6_p4_0[] = { +static const char _vq_lengthlist__44p6_p4_0[] = { 2, 5, 5, 5, 7, 8, 5, 8, 7, 5, 7, 7, 7, 7, 9, 7, 9, 9, 5, 7, 7, 8, 9, 9, 7, 9, 7, 6, 8, 8, 8, 9, 10, 8, 9, 9, 8, 9,10, 9, 9,11,10,11,11, 8, 9, 9, @@ -6739,7 +6755,7 @@ static const long _vq_lengthlist__44p6_p4_0[] = { static const static_codebook _44p6_p4_0 = { 5, 243, - (long *)_vq_lengthlist__44p6_p4_0, + (char *)_vq_lengthlist__44p6_p4_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44p6_p4_0, 0 @@ -6753,7 +6769,7 @@ static const long _vq_quantlist__44p6_p4_1[] = { 4, }; -static const long _vq_lengthlist__44p6_p4_1[] = { +static const char _vq_lengthlist__44p6_p4_1[] = { 6, 8, 8,10,10, 8, 9, 9,10,11, 8,10, 9,11,10, 9, 10,10,11,11, 9,10,10,11,11, 8, 9, 9,10,10, 9, 9, 10,11,11,10,10,10,11,11,10,11,11,11,11,10,11,11, @@ -6954,7 +6970,7 @@ static const long _vq_lengthlist__44p6_p4_1[] = { static const static_codebook _44p6_p4_1 = { 5, 3125, - (long *)_vq_lengthlist__44p6_p4_1, + (char *)_vq_lengthlist__44p6_p4_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p6_p4_1, 0 @@ -6968,7 +6984,7 @@ static const long _vq_quantlist__44p6_p5_0[] = { 4, }; -static const long _vq_lengthlist__44p6_p5_0[] = { +static const char _vq_lengthlist__44p6_p5_0[] = { 2, 6, 6,10,10, 5, 7, 8,11,12, 5, 8, 7,12,11, 9, 11,11,13,15, 9,11,11,15,13, 6, 7, 8,11,11, 7, 7, 9,11,13, 8, 9, 9,13,12,11,11,12,12,15,11,12,12, @@ -7169,7 +7185,7 @@ static const long _vq_lengthlist__44p6_p5_0[] = { static const static_codebook _44p6_p5_0 = { 5, 3125, - (long *)_vq_lengthlist__44p6_p5_0, + (char *)_vq_lengthlist__44p6_p5_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44p6_p5_0, 0 @@ -7185,13 +7201,13 @@ static const long _vq_quantlist__44p6_p5_1[] = { 6, }; -static const long _vq_lengthlist__44p6_p5_1[] = { +static const char _vq_lengthlist__44p6_p5_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44p6_p5_1 = { 1, 7, - (long *)_vq_lengthlist__44p6_p5_1, + (char *)_vq_lengthlist__44p6_p5_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44p6_p5_1, 0 @@ -7203,7 +7219,7 @@ static const long _vq_quantlist__44p6_p6_0[] = { 2, }; -static const long _vq_lengthlist__44p6_p6_0[] = { +static const char _vq_lengthlist__44p6_p6_0[] = { 1, 5, 5, 5, 7, 9, 5, 9, 7, 5, 7, 8, 7, 7,10, 9, 10,10, 5, 8, 7, 9,10,10, 7,10, 7, 6, 9, 9, 9,10, 12, 9,11,11, 9,10,11,11,11,13,12,13,13, 9,11,11, @@ -7224,7 +7240,7 @@ static const long _vq_lengthlist__44p6_p6_0[] = { static const static_codebook _44p6_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44p6_p6_0, + (char *)_vq_lengthlist__44p6_p6_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44p6_p6_0, 0 @@ -7236,7 +7252,7 @@ static const long _vq_quantlist__44p6_p6_1[] = { 2, }; -static const long _vq_lengthlist__44p6_p6_1[] = { +static const char _vq_lengthlist__44p6_p6_1[] = { 2, 6, 6, 6, 7, 8, 6, 8, 7, 6, 7, 7, 7, 7, 8, 7, 8, 8, 6, 7, 7, 7, 8, 8, 7, 8, 7, 6, 8, 8, 8, 9, 9, 8, 9, 9, 8, 9, 9, 9, 9,10, 9,10,10, 8, 9, 9, @@ -7257,7 +7273,7 @@ static const long _vq_lengthlist__44p6_p6_1[] = { static const static_codebook _44p6_p6_1 = { 5, 243, - (long *)_vq_lengthlist__44p6_p6_1, + (char *)_vq_lengthlist__44p6_p6_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44p6_p6_1, 0 @@ -7269,7 +7285,7 @@ static const long _vq_quantlist__44p6_p7_0[] = { 2, }; -static const long _vq_lengthlist__44p6_p7_0[] = { +static const char _vq_lengthlist__44p6_p7_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -7290,7 +7306,7 @@ static const long _vq_lengthlist__44p6_p7_0[] = { static const static_codebook _44p6_p7_0 = { 5, 243, - (long *)_vq_lengthlist__44p6_p7_0, + (char *)_vq_lengthlist__44p6_p7_0, 1, -513979392, 1633504256, 2, 0, (long *)_vq_quantlist__44p6_p7_0, 0 @@ -7302,7 +7318,7 @@ static const long _vq_quantlist__44p6_p7_1[] = { 2, }; -static const long _vq_lengthlist__44p6_p7_1[] = { +static const char _vq_lengthlist__44p6_p7_1[] = { 1, 4, 5, 5,10,10, 5,10,10, 5,10,10,10,10,10,10, 10,10, 5,10,10,10,10,10,10,10,10, 7,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, @@ -7323,7 +7339,7 @@ static const long _vq_lengthlist__44p6_p7_1[] = { static const static_codebook _44p6_p7_1 = { 5, 243, - (long *)_vq_lengthlist__44p6_p7_1, + (char *)_vq_lengthlist__44p6_p7_1, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p6_p7_1, 0 @@ -7357,14 +7373,14 @@ static const long _vq_quantlist__44p6_p7_2[] = { 24, }; -static const long _vq_lengthlist__44p6_p7_2[] = { +static const char _vq_lengthlist__44p6_p7_2[] = { 1, 2, 3, 4, 5, 7, 7, 8, 8, 9, 9,10,10,11,11,12, 12,13,13,14,14,15,15,15,15, }; static const static_codebook _44p6_p7_2 = { 1, 25, - (long *)_vq_lengthlist__44p6_p7_2, + (char *)_vq_lengthlist__44p6_p7_2, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44p6_p7_2, 0 @@ -7398,20 +7414,20 @@ static const long _vq_quantlist__44p6_p7_3[] = { 24, }; -static const long _vq_lengthlist__44p6_p7_3[] = { +static const char _vq_lengthlist__44p6_p7_3[] = { 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p6_p7_3 = { 1, 25, - (long *)_vq_lengthlist__44p6_p7_3, + (char *)_vq_lengthlist__44p6_p7_3, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44p6_p7_3, 0 }; -static const long _huff_lengthlist__44p6_short[] = { +static const char _huff_lengthlist__44p6_short[] = { 2, 8,13,15,16,18,21,22, 5, 4, 6, 8,10,12,17,21, 9, 5, 5, 6, 8,11,15,19,11, 6, 5, 5, 6, 7,12,14, 14, 8, 7, 5, 4, 4, 9,11,16,11, 9, 7, 4, 3, 7,10, @@ -7420,7 +7436,7 @@ static const long _huff_lengthlist__44p6_short[] = { static const static_codebook _huff_book__44p6_short = { 2, 64, - (long *)_huff_lengthlist__44p6_short, + (char *)_huff_lengthlist__44p6_short, 0, 0, 0, 0, 0, NULL, 0 @@ -7442,7 +7458,7 @@ static const long _vq_quantlist__44p7_l0_0[] = { 12, }; -static const long _vq_lengthlist__44p7_l0_0[] = { +static const char _vq_lengthlist__44p7_l0_0[] = { 2, 4, 4, 7, 7, 8, 8,10,10,11,11,12,12, 4, 5, 5, 7, 7, 9, 9,11, 9,12,11,12,12, 4, 5, 5, 7, 7, 9, 9, 9,10,10,11,12,12, 7, 7, 7, 7, 8, 9, 8,11, 5, @@ -7458,7 +7474,7 @@ static const long _vq_lengthlist__44p7_l0_0[] = { static const static_codebook _44p7_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44p7_l0_0, + (char *)_vq_lengthlist__44p7_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44p7_l0_0, 0 @@ -7472,14 +7488,14 @@ static const long _vq_quantlist__44p7_l0_1[] = { 4, }; -static const long _vq_lengthlist__44p7_l0_1[] = { +static const char _vq_lengthlist__44p7_l0_1[] = { 4, 4, 4, 5, 5, 4, 4, 5, 5, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p7_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44p7_l0_1, + (char *)_vq_lengthlist__44p7_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p7_l0_1, 0 @@ -7493,32 +7509,32 @@ static const long _vq_quantlist__44p7_l1_0[] = { 108, }; -static const long _vq_lengthlist__44p7_l1_0[] = { +static const char _vq_lengthlist__44p7_l1_0[] = { 1, 2, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, }; static const static_codebook _44p7_l1_0 = { 2, 25, - (long *)_vq_lengthlist__44p7_l1_0, + (char *)_vq_lengthlist__44p7_l1_0, 1, -514516992, 1620639744, 7, 0, (long *)_vq_quantlist__44p7_l1_0, 0 }; -static const long _huff_lengthlist__44p7_lfe[] = { +static const char _huff_lengthlist__44p7_lfe[] = { 2, 3, 1, 3, }; static const static_codebook _huff_book__44p7_lfe = { 2, 4, - (long *)_huff_lengthlist__44p7_lfe, + (char *)_huff_lengthlist__44p7_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44p7_long[] = { +static const char _huff_lengthlist__44p7_long[] = { 2, 7,14,16,17,17,18,20, 6, 3, 5, 8,10,11,13,15, 13, 5, 3, 5, 8, 9,11,12,15, 7, 4, 3, 5, 7, 9,11, 16,10, 7, 5, 6, 7, 9,10,17,11, 8, 7, 7, 6, 8, 8, @@ -7527,7 +7543,7 @@ static const long _huff_lengthlist__44p7_long[] = { static const static_codebook _huff_book__44p7_long = { 2, 64, - (long *)_huff_lengthlist__44p7_long, + (char *)_huff_lengthlist__44p7_long, 0, 0, 0, 0, 0, NULL, 0 @@ -7539,7 +7555,7 @@ static const long _vq_quantlist__44p7_p1_0[] = { 2, }; -static const long _vq_lengthlist__44p7_p1_0[] = { +static const char _vq_lengthlist__44p7_p1_0[] = { 2, 5, 5, 4, 7, 7, 4, 7, 7, 5, 7, 7, 7, 8, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 8, 6, 7, 8, 8, 9, 10, 8, 9,10, 8, 9,10,10,10,12,10,11,11, 8,10,10, @@ -7560,7 +7576,7 @@ static const long _vq_lengthlist__44p7_p1_0[] = { static const static_codebook _44p7_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44p7_p1_0, + (char *)_vq_lengthlist__44p7_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p7_p1_0, 0 @@ -7574,7 +7590,7 @@ static const long _vq_quantlist__44p7_p2_0[] = { 4, }; -static const long _vq_lengthlist__44p7_p2_0[] = { +static const char _vq_lengthlist__44p7_p2_0[] = { 4, 6, 6, 9, 9, 6, 8, 8,10,10, 6, 8, 8,10,10, 8, 10,10,12,13, 8,10,10,13,12, 6, 8, 8,10,10, 8, 8, 9,10,11, 8, 9, 9,11,11,10,10,11,12,13,10,11,11, @@ -7775,7 +7791,7 @@ static const long _vq_lengthlist__44p7_p2_0[] = { static const static_codebook _44p7_p2_0 = { 5, 3125, - (long *)_vq_lengthlist__44p7_p2_0, + (char *)_vq_lengthlist__44p7_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p7_p2_0, 0 @@ -7787,7 +7803,7 @@ static const long _vq_quantlist__44p7_p3_0[] = { 2, }; -static const long _vq_lengthlist__44p7_p3_0[] = { +static const char _vq_lengthlist__44p7_p3_0[] = { 2, 5, 5, 4, 7, 7, 4, 7, 7, 5, 7, 8, 7, 8,10, 8, 9, 9, 5, 7, 7, 8, 9, 9, 7,10, 8, 5, 7, 8, 8, 9, 10, 8,10,10, 8, 9,10,10,10,12,10,12,12, 8,10,10, @@ -7808,7 +7824,7 @@ static const long _vq_lengthlist__44p7_p3_0[] = { static const static_codebook _44p7_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44p7_p3_0, + (char *)_vq_lengthlist__44p7_p3_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44p7_p3_0, 0 @@ -7820,7 +7836,7 @@ static const long _vq_quantlist__44p7_p3_1[] = { 2, }; -static const long _vq_lengthlist__44p7_p3_1[] = { +static const char _vq_lengthlist__44p7_p3_1[] = { 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 8, 7, 8, 8, 7, 8, 7, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 8, 9, 8, 8, 8, @@ -7841,7 +7857,7 @@ static const long _vq_lengthlist__44p7_p3_1[] = { static const static_codebook _44p7_p3_1 = { 5, 243, - (long *)_vq_lengthlist__44p7_p3_1, + (char *)_vq_lengthlist__44p7_p3_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p7_p3_1, 0 @@ -7853,7 +7869,7 @@ static const long _vq_quantlist__44p7_p4_0[] = { 2, }; -static const long _vq_lengthlist__44p7_p4_0[] = { +static const char _vq_lengthlist__44p7_p4_0[] = { 1, 5, 5, 5, 7, 8, 5, 8, 7, 5, 7, 8, 7, 8,10, 8, 10,10, 5, 8, 7, 8,10,10, 7,10, 8, 6, 8, 9, 9,10, 12, 9,11,11, 9,10,11,11,11,13,11,13,13, 9,11,11, @@ -7874,7 +7890,7 @@ static const long _vq_lengthlist__44p7_p4_0[] = { static const static_codebook _44p7_p4_0 = { 5, 243, - (long *)_vq_lengthlist__44p7_p4_0, + (char *)_vq_lengthlist__44p7_p4_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44p7_p4_0, 0 @@ -7888,7 +7904,7 @@ static const long _vq_quantlist__44p7_p4_1[] = { 4, }; -static const long _vq_lengthlist__44p7_p4_1[] = { +static const char _vq_lengthlist__44p7_p4_1[] = { 7, 8, 8,10,10, 8, 9, 9,10,11, 8, 9, 9,10,10, 9, 10,10,11,11, 9,10,10,11,11, 8, 9, 9,10,10, 9, 9, 10,11,11, 9,10,10,11,11,10,10,11,11,11,10,11,11, @@ -8089,7 +8105,7 @@ static const long _vq_lengthlist__44p7_p4_1[] = { static const static_codebook _44p7_p4_1 = { 5, 3125, - (long *)_vq_lengthlist__44p7_p4_1, + (char *)_vq_lengthlist__44p7_p4_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p7_p4_1, 0 @@ -8103,7 +8119,7 @@ static const long _vq_quantlist__44p7_p5_0[] = { 4, }; -static const long _vq_lengthlist__44p7_p5_0[] = { +static const char _vq_lengthlist__44p7_p5_0[] = { 2, 6, 6, 9, 9, 5, 7, 8,10,11, 5, 8, 7,11,10, 8, 10,11,12,13, 8,11,10,13,12, 6, 7, 8,10,11, 7, 8, 10,10,12, 8, 9, 9,12,11,10,10,12,11,14,10,11,12, @@ -8304,7 +8320,7 @@ static const long _vq_lengthlist__44p7_p5_0[] = { static const static_codebook _44p7_p5_0 = { 5, 3125, - (long *)_vq_lengthlist__44p7_p5_0, + (char *)_vq_lengthlist__44p7_p5_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44p7_p5_0, 0 @@ -8320,13 +8336,13 @@ static const long _vq_quantlist__44p7_p5_1[] = { 6, }; -static const long _vq_lengthlist__44p7_p5_1[] = { +static const char _vq_lengthlist__44p7_p5_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44p7_p5_1 = { 1, 7, - (long *)_vq_lengthlist__44p7_p5_1, + (char *)_vq_lengthlist__44p7_p5_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44p7_p5_1, 0 @@ -8338,7 +8354,7 @@ static const long _vq_quantlist__44p7_p6_0[] = { 2, }; -static const long _vq_lengthlist__44p7_p6_0[] = { +static const char _vq_lengthlist__44p7_p6_0[] = { 2, 5, 6, 5, 7, 8, 5, 8, 7, 5, 7, 7, 7, 7, 9, 8, 9, 9, 5, 7, 7, 8, 9, 9, 7, 9, 7, 6, 8, 8, 8, 9, 10, 8, 9, 9, 8, 9,10, 9, 9,11,10,10,11, 8,10, 9, @@ -8359,7 +8375,7 @@ static const long _vq_lengthlist__44p7_p6_0[] = { static const static_codebook _44p7_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44p7_p6_0, + (char *)_vq_lengthlist__44p7_p6_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44p7_p6_0, 0 @@ -8371,7 +8387,7 @@ static const long _vq_quantlist__44p7_p6_1[] = { 2, }; -static const long _vq_lengthlist__44p7_p6_1[] = { +static const char _vq_lengthlist__44p7_p6_1[] = { 4, 7, 7, 6, 7, 8, 6, 8, 7, 7, 7, 8, 7, 7, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 7, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 9, 9, 8, 8, 8, @@ -8392,7 +8408,7 @@ static const long _vq_lengthlist__44p7_p6_1[] = { static const static_codebook _44p7_p6_1 = { 5, 243, - (long *)_vq_lengthlist__44p7_p6_1, + (char *)_vq_lengthlist__44p7_p6_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44p7_p6_1, 0 @@ -8404,7 +8420,7 @@ static const long _vq_quantlist__44p7_p7_0[] = { 2, }; -static const long _vq_lengthlist__44p7_p7_0[] = { +static const char _vq_lengthlist__44p7_p7_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -8425,7 +8441,7 @@ static const long _vq_lengthlist__44p7_p7_0[] = { static const static_codebook _44p7_p7_0 = { 5, 243, - (long *)_vq_lengthlist__44p7_p7_0, + (char *)_vq_lengthlist__44p7_p7_0, 1, -513979392, 1633504256, 2, 0, (long *)_vq_quantlist__44p7_p7_0, 0 @@ -8437,7 +8453,7 @@ static const long _vq_quantlist__44p7_p7_1[] = { 2, }; -static const long _vq_lengthlist__44p7_p7_1[] = { +static const char _vq_lengthlist__44p7_p7_1[] = { 1, 5, 5, 4,10,10, 5,10,10, 5,10,10,10,10,10,10, 10,10, 5,10,10,10,10,10, 9,10,10, 6,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, @@ -8458,7 +8474,7 @@ static const long _vq_lengthlist__44p7_p7_1[] = { static const static_codebook _44p7_p7_1 = { 5, 243, - (long *)_vq_lengthlist__44p7_p7_1, + (char *)_vq_lengthlist__44p7_p7_1, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44p7_p7_1, 0 @@ -8492,14 +8508,14 @@ static const long _vq_quantlist__44p7_p7_2[] = { 24, }; -static const long _vq_lengthlist__44p7_p7_2[] = { +static const char _vq_lengthlist__44p7_p7_2[] = { 1, 3, 2, 4, 5, 7, 7, 8, 8, 9, 9,10,10,11,11,12, 12,13,13,14,14,15,15,15,15, }; static const static_codebook _44p7_p7_2 = { 1, 25, - (long *)_vq_lengthlist__44p7_p7_2, + (char *)_vq_lengthlist__44p7_p7_2, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44p7_p7_2, 0 @@ -8533,20 +8549,20 @@ static const long _vq_quantlist__44p7_p7_3[] = { 24, }; -static const long _vq_lengthlist__44p7_p7_3[] = { +static const char _vq_lengthlist__44p7_p7_3[] = { 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p7_p7_3 = { 1, 25, - (long *)_vq_lengthlist__44p7_p7_3, + (char *)_vq_lengthlist__44p7_p7_3, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44p7_p7_3, 0 }; -static const long _huff_lengthlist__44p7_short[] = { +static const char _huff_lengthlist__44p7_short[] = { 3, 9,14,16,17,19,22,22, 5, 4, 6, 9,11,13,17,20, 9, 5, 5, 6, 9,11,15,19,11, 7, 5, 5, 7, 9,13,17, 14, 9, 7, 6, 6, 7,11,14,16,11, 9, 7, 6, 4, 4, 8, @@ -8555,7 +8571,7 @@ static const long _huff_lengthlist__44p7_short[] = { static const static_codebook _huff_book__44p7_short = { 2, 64, - (long *)_huff_lengthlist__44p7_short, + (char *)_huff_lengthlist__44p7_short, 0, 0, 0, 0, 0, NULL, 0 @@ -8577,7 +8593,7 @@ static const long _vq_quantlist__44p8_l0_0[] = { 12, }; -static const long _vq_lengthlist__44p8_l0_0[] = { +static const char _vq_lengthlist__44p8_l0_0[] = { 2, 4, 4, 7, 7, 8, 8,10,10,11,11,12,12, 4, 5, 5, 7, 7, 9, 9,10, 9,12,10,12,12, 4, 5, 5, 7, 7, 9, 9, 9,10,10,12,12,12, 7, 7, 7, 7, 8, 9, 8,11, 5, @@ -8593,7 +8609,7 @@ static const long _vq_lengthlist__44p8_l0_0[] = { static const static_codebook _44p8_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44p8_l0_0, + (char *)_vq_lengthlist__44p8_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44p8_l0_0, 0 @@ -8607,14 +8623,14 @@ static const long _vq_quantlist__44p8_l0_1[] = { 4, }; -static const long _vq_lengthlist__44p8_l0_1[] = { +static const char _vq_lengthlist__44p8_l0_1[] = { 4, 4, 4, 5, 5, 4, 4, 5, 5, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p8_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44p8_l0_1, + (char *)_vq_lengthlist__44p8_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p8_l0_1, 0 @@ -8628,32 +8644,32 @@ static const long _vq_quantlist__44p8_l1_0[] = { 108, }; -static const long _vq_lengthlist__44p8_l1_0[] = { +static const char _vq_lengthlist__44p8_l1_0[] = { 1, 2, 3, 6, 7, 7, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, }; static const static_codebook _44p8_l1_0 = { 2, 25, - (long *)_vq_lengthlist__44p8_l1_0, + (char *)_vq_lengthlist__44p8_l1_0, 1, -514516992, 1620639744, 7, 0, (long *)_vq_quantlist__44p8_l1_0, 0 }; -static const long _huff_lengthlist__44p8_lfe[] = { +static const char _huff_lengthlist__44p8_lfe[] = { 2, 3, 1, 3, }; static const static_codebook _huff_book__44p8_lfe = { 2, 4, - (long *)_huff_lengthlist__44p8_lfe, + (char *)_huff_lengthlist__44p8_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44p8_long[] = { +static const char _huff_lengthlist__44p8_long[] = { 2, 7,14,16,17,18,20,21, 7, 4, 6, 8,11,12,14,16, 13, 5, 4, 4, 8, 9,11,13,15, 8, 4, 3, 5, 7, 9,10, 17,11, 8, 4, 4, 6, 9, 9,17,11, 9, 7, 6, 5, 7, 8, @@ -8662,7 +8678,7 @@ static const long _huff_lengthlist__44p8_long[] = { static const static_codebook _huff_book__44p8_long = { 2, 64, - (long *)_huff_lengthlist__44p8_long, + (char *)_huff_lengthlist__44p8_long, 0, 0, 0, 0, 0, NULL, 0 @@ -8674,7 +8690,7 @@ static const long _vq_quantlist__44p8_p1_0[] = { 2, }; -static const long _vq_lengthlist__44p8_p1_0[] = { +static const char _vq_lengthlist__44p8_p1_0[] = { 2, 5, 5, 4, 7, 7, 4, 7, 7, 5, 7, 7, 7, 8, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 8, 6, 7, 8, 8, 9, 10, 8, 9,10, 8, 9,10,10,10,12,10,11,12, 8,10,10, @@ -8695,7 +8711,7 @@ static const long _vq_lengthlist__44p8_p1_0[] = { static const static_codebook _44p8_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44p8_p1_0, + (char *)_vq_lengthlist__44p8_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p8_p1_0, 0 @@ -8709,7 +8725,7 @@ static const long _vq_quantlist__44p8_p2_0[] = { 4, }; -static const long _vq_lengthlist__44p8_p2_0[] = { +static const char _vq_lengthlist__44p8_p2_0[] = { 4, 6, 6, 9, 9, 6, 8, 8,10,10, 6, 8, 8,10,10, 8, 9,10,12,12, 8,10, 9,12,12, 6, 8, 8,10,10, 8, 8, 9,10,11, 8, 9, 9,11,11, 9,10,11,12,13,10,11,11, @@ -8910,7 +8926,7 @@ static const long _vq_lengthlist__44p8_p2_0[] = { static const static_codebook _44p8_p2_0 = { 5, 3125, - (long *)_vq_lengthlist__44p8_p2_0, + (char *)_vq_lengthlist__44p8_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p8_p2_0, 0 @@ -8922,7 +8938,7 @@ static const long _vq_quantlist__44p8_p3_0[] = { 2, }; -static const long _vq_lengthlist__44p8_p3_0[] = { +static const char _vq_lengthlist__44p8_p3_0[] = { 2, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 8, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 8, 5, 7, 8, 7, 9, 10, 8, 9, 9, 8, 9,10, 9,10,12,10,11,11, 8,10, 9, @@ -8943,7 +8959,7 @@ static const long _vq_lengthlist__44p8_p3_0[] = { static const static_codebook _44p8_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44p8_p3_0, + (char *)_vq_lengthlist__44p8_p3_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44p8_p3_0, 0 @@ -8955,7 +8971,7 @@ static const long _vq_quantlist__44p8_p3_1[] = { 2, }; -static const long _vq_lengthlist__44p8_p3_1[] = { +static const char _vq_lengthlist__44p8_p3_1[] = { 6, 7, 7, 7, 7, 8, 7, 8, 7, 7, 7, 8, 7, 8, 8, 8, 8, 8, 7, 8, 7, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, @@ -8976,7 +8992,7 @@ static const long _vq_lengthlist__44p8_p3_1[] = { static const static_codebook _44p8_p3_1 = { 5, 243, - (long *)_vq_lengthlist__44p8_p3_1, + (char *)_vq_lengthlist__44p8_p3_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p8_p3_1, 0 @@ -8988,7 +9004,7 @@ static const long _vq_quantlist__44p8_p4_0[] = { 2, }; -static const long _vq_lengthlist__44p8_p4_0[] = { +static const char _vq_lengthlist__44p8_p4_0[] = { 2, 5, 5, 4, 7, 8, 4, 8, 7, 5, 7, 8, 7, 7,10, 8, 9, 9, 5, 7, 7, 8, 9, 9, 7,10, 7, 5, 7, 8, 8, 9, 11, 8,10,10, 8, 9,10,10,10,12,11,12,12, 8,10,10, @@ -9009,7 +9025,7 @@ static const long _vq_lengthlist__44p8_p4_0[] = { static const static_codebook _44p8_p4_0 = { 5, 243, - (long *)_vq_lengthlist__44p8_p4_0, + (char *)_vq_lengthlist__44p8_p4_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44p8_p4_0, 0 @@ -9023,7 +9039,7 @@ static const long _vq_quantlist__44p8_p4_1[] = { 4, }; -static const long _vq_lengthlist__44p8_p4_1[] = { +static const char _vq_lengthlist__44p8_p4_1[] = { 7, 9, 9,10,10, 9,10,10,10,11, 9,10,10,11,10, 9, 10,10,11,11, 9,10,10,11,11, 9,10,10,11,11,10,10, 10,11,11,10,10,10,11,11,10,11,11,11,11,10,11,11, @@ -9224,7 +9240,7 @@ static const long _vq_lengthlist__44p8_p4_1[] = { static const static_codebook _44p8_p4_1 = { 5, 3125, - (long *)_vq_lengthlist__44p8_p4_1, + (char *)_vq_lengthlist__44p8_p4_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p8_p4_1, 0 @@ -9238,7 +9254,7 @@ static const long _vq_quantlist__44p8_p5_0[] = { 4, }; -static const long _vq_lengthlist__44p8_p5_0[] = { +static const char _vq_lengthlist__44p8_p5_0[] = { 2, 6, 6, 9, 9, 5, 7, 8,10,11, 5, 8, 7,11,10, 8, 10,11,12,13, 8,11,10,13,12, 6, 7, 8,10,11, 7, 8, 10,10,12, 8, 9, 9,12,12,10,10,12,12,14,10,12,12, @@ -9439,7 +9455,7 @@ static const long _vq_lengthlist__44p8_p5_0[] = { static const static_codebook _44p8_p5_0 = { 5, 3125, - (long *)_vq_lengthlist__44p8_p5_0, + (char *)_vq_lengthlist__44p8_p5_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44p8_p5_0, 0 @@ -9455,13 +9471,13 @@ static const long _vq_quantlist__44p8_p5_1[] = { 6, }; -static const long _vq_lengthlist__44p8_p5_1[] = { +static const char _vq_lengthlist__44p8_p5_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44p8_p5_1 = { 1, 7, - (long *)_vq_lengthlist__44p8_p5_1, + (char *)_vq_lengthlist__44p8_p5_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44p8_p5_1, 0 @@ -9473,7 +9489,7 @@ static const long _vq_quantlist__44p8_p6_0[] = { 2, }; -static const long _vq_lengthlist__44p8_p6_0[] = { +static const char _vq_lengthlist__44p8_p6_0[] = { 2, 6, 6, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 7, 9, 7, 9, 9, 6, 7, 7, 8, 9, 9, 7, 9, 7, 6, 8, 8, 8, 9, 10, 8, 9, 9, 8, 9,10, 9, 9,10,10,10,10, 8, 9, 9, @@ -9494,7 +9510,7 @@ static const long _vq_lengthlist__44p8_p6_0[] = { static const static_codebook _44p8_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44p8_p6_0, + (char *)_vq_lengthlist__44p8_p6_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44p8_p6_0, 0 @@ -9506,7 +9522,7 @@ static const long _vq_quantlist__44p8_p6_1[] = { 2, }; -static const long _vq_lengthlist__44p8_p6_1[] = { +static const char _vq_lengthlist__44p8_p6_1[] = { 4, 7, 7, 7, 7, 8, 7, 8, 7, 7, 7, 8, 7, 8, 8, 8, 8, 8, 7, 8, 7, 8, 8, 8, 7, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 8, 9, 8, 8, 8, @@ -9527,7 +9543,7 @@ static const long _vq_lengthlist__44p8_p6_1[] = { static const static_codebook _44p8_p6_1 = { 5, 243, - (long *)_vq_lengthlist__44p8_p6_1, + (char *)_vq_lengthlist__44p8_p6_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44p8_p6_1, 0 @@ -9539,7 +9555,7 @@ static const long _vq_quantlist__44p8_p7_0[] = { 2, }; -static const long _vq_lengthlist__44p8_p7_0[] = { +static const char _vq_lengthlist__44p8_p7_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -9560,7 +9576,7 @@ static const long _vq_lengthlist__44p8_p7_0[] = { static const static_codebook _44p8_p7_0 = { 5, 243, - (long *)_vq_lengthlist__44p8_p7_0, + (char *)_vq_lengthlist__44p8_p7_0, 1, -512202240, 1635281408, 2, 0, (long *)_vq_quantlist__44p8_p7_0, 0 @@ -9574,7 +9590,7 @@ static const long _vq_quantlist__44p8_p7_1[] = { 4, }; -static const long _vq_lengthlist__44p8_p7_1[] = { +static const char _vq_lengthlist__44p8_p7_1[] = { 1, 7, 7,12,12, 5,11,12,12,12, 5,12,11,12,12,12, 12,12,12,12,12,13,13,13,13, 7,11,11,13,13,13,12, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, @@ -9775,7 +9791,7 @@ static const long _vq_lengthlist__44p8_p7_1[] = { static const static_codebook _44p8_p7_1 = { 5, 3125, - (long *)_vq_lengthlist__44p8_p7_1, + (char *)_vq_lengthlist__44p8_p7_1, 1, -514619392, 1630767104, 3, 0, (long *)_vq_quantlist__44p8_p7_1, 0 @@ -9809,14 +9825,14 @@ static const long _vq_quantlist__44p8_p7_2[] = { 24, }; -static const long _vq_lengthlist__44p8_p7_2[] = { +static const char _vq_lengthlist__44p8_p7_2[] = { 1, 3, 2, 4, 5, 7, 7, 8, 8, 9, 9,10,10,11,11,12, 12,13,13,14,14,15,15,15,15, }; static const static_codebook _44p8_p7_2 = { 1, 25, - (long *)_vq_lengthlist__44p8_p7_2, + (char *)_vq_lengthlist__44p8_p7_2, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44p8_p7_2, 0 @@ -9850,20 +9866,20 @@ static const long _vq_quantlist__44p8_p7_3[] = { 24, }; -static const long _vq_lengthlist__44p8_p7_3[] = { +static const char _vq_lengthlist__44p8_p7_3[] = { 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p8_p7_3 = { 1, 25, - (long *)_vq_lengthlist__44p8_p7_3, + (char *)_vq_lengthlist__44p8_p7_3, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44p8_p7_3, 0 }; -static const long _huff_lengthlist__44p8_short[] = { +static const char _huff_lengthlist__44p8_short[] = { 3, 9,15,17,20,21,22,23, 5, 5, 7, 9,11,13,17,20, 9, 5, 5, 6, 8,10,15,18,11, 7, 5, 4, 6, 9,13,17, 14, 9, 7, 5, 6, 7,10,14,17,10, 8, 6, 6, 4, 5, 8, @@ -9872,7 +9888,7 @@ static const long _huff_lengthlist__44p8_short[] = { static const static_codebook _huff_book__44p8_short = { 2, 64, - (long *)_huff_lengthlist__44p8_short, + (char *)_huff_lengthlist__44p8_short, 0, 0, 0, 0, 0, NULL, 0 @@ -9894,7 +9910,7 @@ static const long _vq_quantlist__44p9_l0_0[] = { 12, }; -static const long _vq_lengthlist__44p9_l0_0[] = { +static const char _vq_lengthlist__44p9_l0_0[] = { 2, 5, 5, 7, 6, 8, 8, 9, 9,10,10,11,11, 4, 5, 5, 6, 7, 8, 8, 9, 9,10,10,11,10, 4, 5, 5, 7, 6, 8, 8, 9, 9,10,10,10,10, 6, 6, 7, 6, 7, 8, 8, 9, 9, @@ -9910,7 +9926,7 @@ static const long _vq_lengthlist__44p9_l0_0[] = { static const static_codebook _44p9_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44p9_l0_0, + (char *)_vq_lengthlist__44p9_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44p9_l0_0, 0 @@ -9924,14 +9940,14 @@ static const long _vq_quantlist__44p9_l0_1[] = { 4, }; -static const long _vq_lengthlist__44p9_l0_1[] = { +static const char _vq_lengthlist__44p9_l0_1[] = { 4, 4, 4, 5, 5, 4, 4, 5, 5, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p9_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44p9_l0_1, + (char *)_vq_lengthlist__44p9_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p9_l0_1, 0 @@ -9945,38 +9961,38 @@ static const long _vq_quantlist__44p9_l1_0[] = { 4, }; -static const long _vq_lengthlist__44p9_l1_0[] = { +static const char _vq_lengthlist__44p9_l1_0[] = { 1, 2, 3, 5, 9, 9, 4, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10, }; static const static_codebook _44p9_l1_0 = { 2, 25, - (long *)_vq_lengthlist__44p9_l1_0, + (char *)_vq_lengthlist__44p9_l1_0, 1, -514619392, 1630767104, 3, 0, (long *)_vq_quantlist__44p9_l1_0, 0 }; -static const long _huff_lengthlist__44p9_lfe[] = { +static const char _huff_lengthlist__44p9_lfe[] = { 1, 1, }; static const static_codebook _huff_book__44p9_lfe = { 1, 2, - (long *)_huff_lengthlist__44p9_lfe, + (char *)_huff_lengthlist__44p9_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44p9_long[] = { +static const char _huff_lengthlist__44p9_long[] = { 3, 3, 3, 3, 3, 3, 3, 3, }; static const static_codebook _huff_book__44p9_long = { 1, 8, - (long *)_huff_lengthlist__44p9_long, + (char *)_huff_lengthlist__44p9_long, 0, 0, 0, 0, 0, NULL, 0 @@ -9988,7 +10004,7 @@ static const long _vq_quantlist__44p9_p1_0[] = { 2, }; -static const long _vq_lengthlist__44p9_p1_0[] = { +static const char _vq_lengthlist__44p9_p1_0[] = { 1, 5, 5, 4, 8, 8, 4, 8, 8, 5, 7, 8, 8, 9,10, 8, 10,10, 5, 8, 7, 8,10,10, 8,10, 9, 7, 9, 9, 9,11, 11, 9,11,11, 9,11,11,11,12,13,11,13,13, 9,11,11, @@ -10009,7 +10025,7 @@ static const long _vq_lengthlist__44p9_p1_0[] = { static const static_codebook _44p9_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44p9_p1_0, + (char *)_vq_lengthlist__44p9_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p9_p1_0, 0 @@ -10023,7 +10039,7 @@ static const long _vq_quantlist__44p9_p2_0[] = { 4, }; -static const long _vq_lengthlist__44p9_p2_0[] = { +static const char _vq_lengthlist__44p9_p2_0[] = { 4, 6, 6, 8, 8, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 6, 8, 8,11,11, 6, 8, 8,11,11, 6, 7, 7, 9, 9, 7, 8, 9,10,11, 7, 9, 9,11,10, 8, 9,10,12,12, 8,10,10, @@ -10224,7 +10240,7 @@ static const long _vq_lengthlist__44p9_p2_0[] = { static const static_codebook _44p9_p2_0 = { 5, 3125, - (long *)_vq_lengthlist__44p9_p2_0, + (char *)_vq_lengthlist__44p9_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p9_p2_0, 0 @@ -10236,7 +10252,7 @@ static const long _vq_quantlist__44p9_p3_0[] = { 2, }; -static const long _vq_lengthlist__44p9_p3_0[] = { +static const char _vq_lengthlist__44p9_p3_0[] = { 2, 5, 4, 4, 7, 7, 4, 7, 6, 5, 6, 7, 7, 8, 9, 7, 9, 9, 5, 7, 6, 7, 9, 9, 7, 9, 8, 6, 8, 8, 8,10, 10, 8,10,10, 8, 9,10,10,11,12,10,12,12, 8,10,10, @@ -10257,7 +10273,7 @@ static const long _vq_lengthlist__44p9_p3_0[] = { static const static_codebook _44p9_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44p9_p3_0, + (char *)_vq_lengthlist__44p9_p3_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44p9_p3_0, 0 @@ -10269,7 +10285,7 @@ static const long _vq_quantlist__44p9_p3_1[] = { 2, }; -static const long _vq_lengthlist__44p9_p3_1[] = { +static const char _vq_lengthlist__44p9_p3_1[] = { 4, 6, 6, 6, 7, 7, 6, 7, 7, 6, 7, 7, 7, 7, 8, 7, 7, 8, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 8, 9, 9, 8, 8, 8, @@ -10290,7 +10306,7 @@ static const long _vq_lengthlist__44p9_p3_1[] = { static const static_codebook _44p9_p3_1 = { 5, 243, - (long *)_vq_lengthlist__44p9_p3_1, + (char *)_vq_lengthlist__44p9_p3_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44p9_p3_1, 0 @@ -10302,7 +10318,7 @@ static const long _vq_quantlist__44p9_p4_0[] = { 2, }; -static const long _vq_lengthlist__44p9_p4_0[] = { +static const char _vq_lengthlist__44p9_p4_0[] = { 2, 5, 5, 4, 7, 7, 4, 7, 6, 5, 7, 7, 7, 8, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 8, 6, 7, 8, 8, 9, 10, 8,10,10, 8, 9,10,10,11,12,10,11,12, 8,10,10, @@ -10323,7 +10339,7 @@ static const long _vq_lengthlist__44p9_p4_0[] = { static const static_codebook _44p9_p4_0 = { 5, 243, - (long *)_vq_lengthlist__44p9_p4_0, + (char *)_vq_lengthlist__44p9_p4_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44p9_p4_0, 0 @@ -10337,7 +10353,7 @@ static const long _vq_quantlist__44p9_p4_1[] = { 4, }; -static const long _vq_lengthlist__44p9_p4_1[] = { +static const char _vq_lengthlist__44p9_p4_1[] = { 6, 8, 8,10, 9, 8, 9, 9,10,10, 8, 9, 9,10,10, 8, 10,10,10,10, 8,10,10,10,10, 9, 9, 9,10,10, 9,10, 10,10,11, 9,10,10,11,11,10,10,10,11,11,10,10,10, @@ -10538,7 +10554,7 @@ static const long _vq_lengthlist__44p9_p4_1[] = { static const static_codebook _44p9_p4_1 = { 5, 3125, - (long *)_vq_lengthlist__44p9_p4_1, + (char *)_vq_lengthlist__44p9_p4_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44p9_p4_1, 0 @@ -10552,7 +10568,7 @@ static const long _vq_quantlist__44p9_p5_0[] = { 4, }; -static const long _vq_lengthlist__44p9_p5_0[] = { +static const char _vq_lengthlist__44p9_p5_0[] = { 4, 6, 6, 9, 9, 6, 7, 8,10,11, 6, 8, 7,10,10, 8, 10,10,12,12, 8,10,10,12,12, 6, 7, 8,10,10, 7, 8, 9,10,11, 8, 9, 9,11,11,10,10,11,12,13,10,11,11, @@ -10753,7 +10769,7 @@ static const long _vq_lengthlist__44p9_p5_0[] = { static const static_codebook _44p9_p5_0 = { 5, 3125, - (long *)_vq_lengthlist__44p9_p5_0, + (char *)_vq_lengthlist__44p9_p5_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44p9_p5_0, 0 @@ -10769,13 +10785,13 @@ static const long _vq_quantlist__44p9_p5_1[] = { 6, }; -static const long _vq_lengthlist__44p9_p5_1[] = { +static const char _vq_lengthlist__44p9_p5_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44p9_p5_1 = { 1, 7, - (long *)_vq_lengthlist__44p9_p5_1, + (char *)_vq_lengthlist__44p9_p5_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44p9_p5_1, 0 @@ -10787,7 +10803,7 @@ static const long _vq_quantlist__44p9_p6_0[] = { 2, }; -static const long _vq_lengthlist__44p9_p6_0[] = { +static const char _vq_lengthlist__44p9_p6_0[] = { 2, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 8, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 8, 5, 7, 8, 8, 9, 10, 8, 9,10, 8, 9,10,10,10,12,10,11,11, 8,10,10, @@ -10808,7 +10824,7 @@ static const long _vq_lengthlist__44p9_p6_0[] = { static const static_codebook _44p9_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44p9_p6_0, + (char *)_vq_lengthlist__44p9_p6_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44p9_p6_0, 0 @@ -10820,7 +10836,7 @@ static const long _vq_quantlist__44p9_p6_1[] = { 2, }; -static const long _vq_lengthlist__44p9_p6_1[] = { +static const char _vq_lengthlist__44p9_p6_1[] = { 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 8, 7, 8, 8, 7, 8, 7, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 8, 9, 8, 8, 8, @@ -10841,7 +10857,7 @@ static const long _vq_lengthlist__44p9_p6_1[] = { static const static_codebook _44p9_p6_1 = { 5, 243, - (long *)_vq_lengthlist__44p9_p6_1, + (char *)_vq_lengthlist__44p9_p6_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44p9_p6_1, 0 @@ -10855,7 +10871,7 @@ static const long _vq_quantlist__44p9_p7_0[] = { 4, }; -static const long _vq_lengthlist__44p9_p7_0[] = { +static const char _vq_lengthlist__44p9_p7_0[] = { 1,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, @@ -11056,7 +11072,7 @@ static const long _vq_lengthlist__44p9_p7_0[] = { static const static_codebook _44p9_p7_0 = { 5, 3125, - (long *)_vq_lengthlist__44p9_p7_0, + (char *)_vq_lengthlist__44p9_p7_0, 1, -510105088, 1635281408, 3, 0, (long *)_vq_quantlist__44p9_p7_0, 0 @@ -11070,7 +11086,7 @@ static const long _vq_quantlist__44p9_p7_1[] = { 4, }; -static const long _vq_lengthlist__44p9_p7_1[] = { +static const char _vq_lengthlist__44p9_p7_1[] = { 1, 4, 4,16,16, 4, 9,11,15,16, 4,12, 8,16,16,12, 16,16,16,16,13,16,16,16,16, 5, 8,10,16,16, 9, 9, 14,15,16,12,14,14,16,16,16,16,16,16,16,16,16,16, @@ -11271,7 +11287,7 @@ static const long _vq_lengthlist__44p9_p7_1[] = { static const static_codebook _44p9_p7_1 = { 5, 3125, - (long *)_vq_lengthlist__44p9_p7_1, + (char *)_vq_lengthlist__44p9_p7_1, 1, -514619392, 1630767104, 3, 0, (long *)_vq_quantlist__44p9_p7_1, 0 @@ -11305,14 +11321,14 @@ static const long _vq_quantlist__44p9_p7_2[] = { 24, }; -static const long _vq_lengthlist__44p9_p7_2[] = { +static const char _vq_lengthlist__44p9_p7_2[] = { 1, 3, 2, 5, 4, 7, 7, 8, 8, 9,10,10,10,11,11,11, 12,12,12,13,13,13,13,13,13, }; static const static_codebook _44p9_p7_2 = { 1, 25, - (long *)_vq_lengthlist__44p9_p7_2, + (char *)_vq_lengthlist__44p9_p7_2, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44p9_p7_2, 0 @@ -11346,26 +11362,26 @@ static const long _vq_quantlist__44p9_p7_3[] = { 24, }; -static const long _vq_lengthlist__44p9_p7_3[] = { +static const char _vq_lengthlist__44p9_p7_3[] = { 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44p9_p7_3 = { 1, 25, - (long *)_vq_lengthlist__44p9_p7_3, + (char *)_vq_lengthlist__44p9_p7_3, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44p9_p7_3, 0 }; -static const long _huff_lengthlist__44p9_short[] = { +static const char _huff_lengthlist__44p9_short[] = { 3, 3, 3, 3, 3, 3, 3, 3, }; static const static_codebook _huff_book__44p9_short = { 1, 8, - (long *)_huff_lengthlist__44p9_short, + (char *)_huff_lengthlist__44p9_short, 0, 0, 0, 0, 0, NULL, 0 @@ -11387,7 +11403,7 @@ static const long _vq_quantlist__44pn1_l0_0[] = { 12, }; -static const long _vq_lengthlist__44pn1_l0_0[] = { +static const char _vq_lengthlist__44pn1_l0_0[] = { 1, 3, 3, 8, 8,10,10,10,10,10,10,10,10, 5, 7, 5, 9, 8,10,10,10,10,11,10,11,10, 5, 5, 7, 8, 9,10, 10,11,10,10,11,10,11,10,10,10,11,11,11,11,11,11, @@ -11403,7 +11419,7 @@ static const long _vq_lengthlist__44pn1_l0_0[] = { static const static_codebook _44pn1_l0_0 = { 2, 169, - (long *)_vq_lengthlist__44pn1_l0_0, + (char *)_vq_lengthlist__44pn1_l0_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44pn1_l0_0, 0 @@ -11417,14 +11433,14 @@ static const long _vq_quantlist__44pn1_l0_1[] = { 4, }; -static const long _vq_lengthlist__44pn1_l0_1[] = { +static const char _vq_lengthlist__44pn1_l0_1[] = { 1, 4, 4, 7, 7, 4, 5, 6, 7, 7, 4, 6, 5, 7, 7, 7, 6, 7, 6, 7, 7, 7, 6, 7, 6, }; static const static_codebook _44pn1_l0_1 = { 2, 25, - (long *)_vq_lengthlist__44pn1_l0_1, + (char *)_vq_lengthlist__44pn1_l0_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44pn1_l0_1, 0 @@ -11436,31 +11452,31 @@ static const long _vq_quantlist__44pn1_l1_0[] = { 2, }; -static const long _vq_lengthlist__44pn1_l1_0[] = { +static const char _vq_lengthlist__44pn1_l1_0[] = { 1, 4, 4, 4, 4, 4, 4, 4, 4, }; static const static_codebook _44pn1_l1_0 = { 2, 9, - (long *)_vq_lengthlist__44pn1_l1_0, + (char *)_vq_lengthlist__44pn1_l1_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44pn1_l1_0, 0 }; -static const long _huff_lengthlist__44pn1_lfe[] = { +static const char _huff_lengthlist__44pn1_lfe[] = { 1, 3, 2, 3, }; static const static_codebook _huff_book__44pn1_lfe = { 2, 4, - (long *)_huff_lengthlist__44pn1_lfe, + (char *)_huff_lengthlist__44pn1_lfe, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44pn1_long[] = { +static const char _huff_lengthlist__44pn1_long[] = { 2, 3, 6, 7, 9,13,17, 3, 2, 5, 7, 9,13,17, 6, 5, 5, 6, 9,12,16, 7, 7, 6, 6, 7,10,13,10,10, 9, 7, 6,10,13,13,13,12,10,10,11,15,17,17,17,14,14,15, @@ -11469,7 +11485,7 @@ static const long _huff_lengthlist__44pn1_long[] = { static const static_codebook _huff_book__44pn1_long = { 2, 49, - (long *)_huff_lengthlist__44pn1_long, + (char *)_huff_lengthlist__44pn1_long, 0, 0, 0, 0, 0, NULL, 0 @@ -11481,7 +11497,7 @@ static const long _vq_quantlist__44pn1_p1_0[] = { 2, }; -static const long _vq_lengthlist__44pn1_p1_0[] = { +static const char _vq_lengthlist__44pn1_p1_0[] = { 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -11502,7 +11518,7 @@ static const long _vq_lengthlist__44pn1_p1_0[] = { static const static_codebook _44pn1_p1_0 = { 5, 243, - (long *)_vq_lengthlist__44pn1_p1_0, + (char *)_vq_lengthlist__44pn1_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44pn1_p1_0, 0 @@ -11514,7 +11530,7 @@ static const long _vq_quantlist__44pn1_p2_0[] = { 2, }; -static const long _vq_lengthlist__44pn1_p2_0[] = { +static const char _vq_lengthlist__44pn1_p2_0[] = { 1, 5, 5, 0, 7, 7, 0, 8, 8, 0, 9, 9, 0,12,12, 0, 8, 8, 0, 9, 9, 0,13,13, 0, 8, 8, 0, 6, 6, 0,11, 11, 0,12,12, 0,12,12, 0,14,14, 0,11,12, 0,12,12, @@ -11535,7 +11551,7 @@ static const long _vq_lengthlist__44pn1_p2_0[] = { static const static_codebook _44pn1_p2_0 = { 5, 243, - (long *)_vq_lengthlist__44pn1_p2_0, + (char *)_vq_lengthlist__44pn1_p2_0, 1, -533200896, 1614282752, 2, 0, (long *)_vq_quantlist__44pn1_p2_0, 0 @@ -11547,7 +11563,7 @@ static const long _vq_quantlist__44pn1_p2_1[] = { 2, }; -static const long _vq_lengthlist__44pn1_p2_1[] = { +static const char _vq_lengthlist__44pn1_p2_1[] = { 1, 3, 3, 0, 9, 9, 0, 9, 9, 0,10,10, 0, 9, 9, 0, 10,10, 0,10,10, 0,10,10, 0,10,10, 0, 7, 7, 0, 7, 7, 0, 6, 6, 0, 8, 8, 0, 7, 7, 0, 8, 8, 0, 8, 8, @@ -11568,7 +11584,7 @@ static const long _vq_lengthlist__44pn1_p2_1[] = { static const static_codebook _44pn1_p2_1 = { 5, 243, - (long *)_vq_lengthlist__44pn1_p2_1, + (char *)_vq_lengthlist__44pn1_p2_1, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44pn1_p2_1, 0 @@ -11580,7 +11596,7 @@ static const long _vq_quantlist__44pn1_p3_0[] = { 2, }; -static const long _vq_lengthlist__44pn1_p3_0[] = { +static const char _vq_lengthlist__44pn1_p3_0[] = { 1, 6, 6, 6, 8, 8, 6, 8, 8, 7, 9, 9,10,11,11, 8, 8, 8, 7, 9, 9,11,12,12, 9, 9, 9, 6, 7, 7,10,11, 11,10,11,11,10,11,11,13,13,13,12,12,12,10,12,11, @@ -11601,7 +11617,7 @@ static const long _vq_lengthlist__44pn1_p3_0[] = { static const static_codebook _44pn1_p3_0 = { 5, 243, - (long *)_vq_lengthlist__44pn1_p3_0, + (char *)_vq_lengthlist__44pn1_p3_0, 1, -531365888, 1616117760, 2, 0, (long *)_vq_quantlist__44pn1_p3_0, 0 @@ -11615,7 +11631,7 @@ static const long _vq_quantlist__44pn1_p3_1[] = { 4, }; -static const long _vq_lengthlist__44pn1_p3_1[] = { +static const char _vq_lengthlist__44pn1_p3_1[] = { 2, 3, 4, 9, 9,10,12,12,12,11,10,12,12,13,12,11, 13,12,11,11,11,12,12,12,11,11,13,13,13,13,11,12, 12,14,14,12,13,13,13,13,11,13,13,13,13,11,13,13, @@ -11816,7 +11832,7 @@ static const long _vq_lengthlist__44pn1_p3_1[] = { static const static_codebook _44pn1_p3_1 = { 5, 3125, - (long *)_vq_lengthlist__44pn1_p3_1, + (char *)_vq_lengthlist__44pn1_p3_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44pn1_p3_1, 0 @@ -11830,7 +11846,7 @@ static const long _vq_quantlist__44pn1_p4_0[] = { 4, }; -static const long _vq_lengthlist__44pn1_p4_0[] = { +static const char _vq_lengthlist__44pn1_p4_0[] = { 1, 7, 7,14,14, 6, 8, 8,15,16, 7, 8, 8,16,15, 0, 14,14,17,17, 0,14,14,16,16, 7, 9, 9,16,16,10,11, 11,17,18, 9, 8, 8,16,16, 0,14,14,19,19, 0,14,14, @@ -12031,7 +12047,7 @@ static const long _vq_lengthlist__44pn1_p4_0[] = { static const static_codebook _44pn1_p4_0 = { 5, 3125, - (long *)_vq_lengthlist__44pn1_p4_0, + (char *)_vq_lengthlist__44pn1_p4_0, 1, -528744448, 1616642048, 3, 0, (long *)_vq_quantlist__44pn1_p4_0, 0 @@ -12047,13 +12063,13 @@ static const long _vq_quantlist__44pn1_p4_1[] = { 6, }; -static const long _vq_lengthlist__44pn1_p4_1[] = { +static const char _vq_lengthlist__44pn1_p4_1[] = { 2, 3, 3, 3, 3, 3, 3, }; static const static_codebook _44pn1_p4_1 = { 1, 7, - (long *)_vq_lengthlist__44pn1_p4_1, + (char *)_vq_lengthlist__44pn1_p4_1, 1, -533200896, 1611661312, 3, 0, (long *)_vq_quantlist__44pn1_p4_1, 0 @@ -12065,7 +12081,7 @@ static const long _vq_quantlist__44pn1_p5_0[] = { 2, }; -static const long _vq_lengthlist__44pn1_p5_0[] = { +static const char _vq_lengthlist__44pn1_p5_0[] = { 1, 7, 7, 6, 8, 8, 7, 8, 8, 7, 9, 9,11,11,11, 9, 8, 8, 7, 9, 9,11,12,11, 9, 9, 9, 6, 7, 7,10,11, 11,10,10,10,10,11,11,15,14,14,12,12,12,11,11,11, @@ -12086,7 +12102,7 @@ static const long _vq_lengthlist__44pn1_p5_0[] = { static const static_codebook _44pn1_p5_0 = { 5, 243, - (long *)_vq_lengthlist__44pn1_p5_0, + (char *)_vq_lengthlist__44pn1_p5_0, 1, -527106048, 1620377600, 2, 0, (long *)_vq_quantlist__44pn1_p5_0, 0 @@ -12098,7 +12114,7 @@ static const long _vq_quantlist__44pn1_p5_1[] = { 2, }; -static const long _vq_lengthlist__44pn1_p5_1[] = { +static const char _vq_lengthlist__44pn1_p5_1[] = { 2, 6, 7, 6, 8, 8, 7, 7, 8, 7, 8, 8, 9, 9, 9, 8, 7, 7, 8, 8, 8, 9, 9, 9, 9, 8, 8, 6, 6, 6, 9, 7, 7, 9, 7, 7, 9, 8, 8,10, 8, 8,10, 8, 8,10, 8, 8, @@ -12119,7 +12135,7 @@ static const long _vq_lengthlist__44pn1_p5_1[] = { static const static_codebook _44pn1_p5_1 = { 5, 243, - (long *)_vq_lengthlist__44pn1_p5_1, + (char *)_vq_lengthlist__44pn1_p5_1, 1, -530841600, 1616642048, 2, 0, (long *)_vq_quantlist__44pn1_p5_1, 0 @@ -12131,7 +12147,7 @@ static const long _vq_quantlist__44pn1_p6_0[] = { 2, }; -static const long _vq_lengthlist__44pn1_p6_0[] = { +static const char _vq_lengthlist__44pn1_p6_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -12152,7 +12168,7 @@ static const long _vq_lengthlist__44pn1_p6_0[] = { static const static_codebook _44pn1_p6_0 = { 5, 243, - (long *)_vq_lengthlist__44pn1_p6_0, + (char *)_vq_lengthlist__44pn1_p6_0, 1, -516716544, 1630767104, 2, 0, (long *)_vq_quantlist__44pn1_p6_0, 0 @@ -12186,14 +12202,14 @@ static const long _vq_quantlist__44pn1_p6_1[] = { 24, }; -static const long _vq_lengthlist__44pn1_p6_1[] = { +static const char _vq_lengthlist__44pn1_p6_1[] = { 1, 3, 2, 5, 4, 7, 7, 8, 8, 9, 9,10,10,11,11,12, 12,13,13,14,14,15,15,15,15, }; static const static_codebook _44pn1_p6_1 = { 1, 25, - (long *)_vq_lengthlist__44pn1_p6_1, + (char *)_vq_lengthlist__44pn1_p6_1, 1, -518864896, 1620639744, 5, 0, (long *)_vq_quantlist__44pn1_p6_1, 0 @@ -12227,20 +12243,20 @@ static const long _vq_quantlist__44pn1_p6_2[] = { 24, }; -static const long _vq_lengthlist__44pn1_p6_2[] = { +static const char _vq_lengthlist__44pn1_p6_2[] = { 3, 5, 4, 5, 4, 5, 4, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44pn1_p6_2 = { 1, 25, - (long *)_vq_lengthlist__44pn1_p6_2, + (char *)_vq_lengthlist__44pn1_p6_2, 1, -529006592, 1611661312, 5, 0, (long *)_vq_quantlist__44pn1_p6_2, 0 }; -static const long _huff_lengthlist__44pn1_short[] = { +static const char _huff_lengthlist__44pn1_short[] = { 4, 3, 7, 9,12,16,16, 3, 2, 5, 7,11,14,15, 7, 4, 5, 6, 9,12,15, 8, 5, 5, 5, 8,10,14, 9, 7, 6, 6, 8,10,12,12,10,10, 7, 6, 8,10,15,12,10, 6, 4, 7, @@ -12249,8 +12265,9 @@ static const long _huff_lengthlist__44pn1_short[] = { static const static_codebook _huff_book__44pn1_short = { 2, 49, - (long *)_huff_lengthlist__44pn1_short, + (char *)_huff_lengthlist__44pn1_short, 0, 0, 0, 0, 0, NULL, 0 }; + diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/coupled/res_books_stereo.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/coupled/res_books_stereo.h similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/coupled/res_books_stereo.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/coupled/res_books_stereo.h index c53966d7..5cc0e481 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/coupled/res_books_stereo.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/coupled/res_books_stereo.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: static codebooks autogenerated by huff/huffbuld - last modified: $Id: res_books_stereo.h 17025 2010-03-25 04:56:56Z xiphmont $ ********************************************************************/ @@ -23,7 +22,7 @@ static const long _vq_quantlist__16c0_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__16c0_s_p1_0[] = { +static const char _vq_lengthlist__16c0_s_p1_0[] = { 1, 4, 4, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -439,7 +438,7 @@ static const long _vq_lengthlist__16c0_s_p1_0[] = { static const static_codebook _16c0_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__16c0_s_p1_0, + (char *)_vq_lengthlist__16c0_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__16c0_s_p1_0, 0 @@ -453,7 +452,7 @@ static const long _vq_quantlist__16c0_s_p3_0[] = { 4, }; -static const long _vq_lengthlist__16c0_s_p3_0[] = { +static const char _vq_lengthlist__16c0_s_p3_0[] = { 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -498,7 +497,7 @@ static const long _vq_lengthlist__16c0_s_p3_0[] = { static const static_codebook _16c0_s_p3_0 = { 4, 625, - (long *)_vq_lengthlist__16c0_s_p3_0, + (char *)_vq_lengthlist__16c0_s_p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16c0_s_p3_0, 0 @@ -516,7 +515,7 @@ static const long _vq_quantlist__16c0_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__16c0_s_p4_0[] = { +static const char _vq_lengthlist__16c0_s_p4_0[] = { 1, 3, 2, 7, 8, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, @@ -527,7 +526,7 @@ static const long _vq_lengthlist__16c0_s_p4_0[] = { static const static_codebook _16c0_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__16c0_s_p4_0, + (char *)_vq_lengthlist__16c0_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__16c0_s_p4_0, 0 @@ -545,7 +544,7 @@ static const long _vq_quantlist__16c0_s_p5_0[] = { 8, }; -static const long _vq_lengthlist__16c0_s_p5_0[] = { +static const char _vq_lengthlist__16c0_s_p5_0[] = { 1, 3, 3, 6, 6, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, 8, 8, 0, 0, 0, 7, 7, 7, 7, 8, 8, 0, 0, 0, 7, 7, 8, 8, 9, 9, 0, 0, 0, 7, 7, 8, 8, 9, 9, 0, 0, 0, @@ -556,7 +555,7 @@ static const long _vq_lengthlist__16c0_s_p5_0[] = { static const static_codebook _16c0_s_p5_0 = { 2, 81, - (long *)_vq_lengthlist__16c0_s_p5_0, + (char *)_vq_lengthlist__16c0_s_p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__16c0_s_p5_0, 0 @@ -582,7 +581,7 @@ static const long _vq_quantlist__16c0_s_p6_0[] = { 16, }; -static const long _vq_lengthlist__16c0_s_p6_0[] = { +static const char _vq_lengthlist__16c0_s_p6_0[] = { 1, 3, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,11, 11,11, 0, 0, 0, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11, @@ -606,7 +605,7 @@ static const long _vq_lengthlist__16c0_s_p6_0[] = { static const static_codebook _16c0_s_p6_0 = { 2, 289, - (long *)_vq_lengthlist__16c0_s_p6_0, + (char *)_vq_lengthlist__16c0_s_p6_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__16c0_s_p6_0, 0 @@ -618,7 +617,7 @@ static const long _vq_quantlist__16c0_s_p7_0[] = { 2, }; -static const long _vq_lengthlist__16c0_s_p7_0[] = { +static const char _vq_lengthlist__16c0_s_p7_0[] = { 1, 4, 4, 6, 6, 6, 7, 6, 6, 4, 7, 7,11,10,10,11, 11,10, 4, 7, 7,10,10,10,11,10,10, 6,10,10,11,11, 11,11,11,10, 6, 9, 9,11,12,12,11, 9, 9, 6, 9,10, @@ -629,7 +628,7 @@ static const long _vq_lengthlist__16c0_s_p7_0[] = { static const static_codebook _16c0_s_p7_0 = { 4, 81, - (long *)_vq_lengthlist__16c0_s_p7_0, + (char *)_vq_lengthlist__16c0_s_p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__16c0_s_p7_0, 0 @@ -649,7 +648,7 @@ static const long _vq_quantlist__16c0_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__16c0_s_p7_1[] = { +static const char _vq_lengthlist__16c0_s_p7_1[] = { 1, 3, 4, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 9, 9, 9,10,10,10, 6, 7, 8, 8, 8, 8, 9, 8,10,10,10, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, 7, @@ -662,7 +661,7 @@ static const long _vq_lengthlist__16c0_s_p7_1[] = { static const static_codebook _16c0_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__16c0_s_p7_1, + (char *)_vq_lengthlist__16c0_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__16c0_s_p7_1, 0 @@ -684,7 +683,7 @@ static const long _vq_quantlist__16c0_s_p8_0[] = { 12, }; -static const long _vq_lengthlist__16c0_s_p8_0[] = { +static const char _vq_lengthlist__16c0_s_p8_0[] = { 1, 4, 4, 7, 7, 7, 7, 7, 6, 8, 8,10,10, 6, 5, 6, 8, 8, 8, 8, 8, 8, 8, 9,10,10, 7, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8,10,10, 0, 8, 8, 8, 8, 9, 8, 9, 9, @@ -700,7 +699,7 @@ static const long _vq_lengthlist__16c0_s_p8_0[] = { static const static_codebook _16c0_s_p8_0 = { 2, 169, - (long *)_vq_lengthlist__16c0_s_p8_0, + (char *)_vq_lengthlist__16c0_s_p8_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__16c0_s_p8_0, 0 @@ -714,14 +713,14 @@ static const long _vq_quantlist__16c0_s_p8_1[] = { 4, }; -static const long _vq_lengthlist__16c0_s_p8_1[] = { +static const char _vq_lengthlist__16c0_s_p8_1[] = { 1, 4, 3, 5, 5, 7, 7, 7, 6, 6, 7, 7, 7, 5, 5, 7, 7, 7, 6, 6, 7, 7, 7, 6, 6, }; static const static_codebook _16c0_s_p8_1 = { 2, 25, - (long *)_vq_lengthlist__16c0_s_p8_1, + (char *)_vq_lengthlist__16c0_s_p8_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16c0_s_p8_1, 0 @@ -733,7 +732,7 @@ static const long _vq_quantlist__16c0_s_p9_0[] = { 2, }; -static const long _vq_lengthlist__16c0_s_p9_0[] = { +static const char _vq_lengthlist__16c0_s_p9_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -744,7 +743,7 @@ static const long _vq_lengthlist__16c0_s_p9_0[] = { static const static_codebook _16c0_s_p9_0 = { 4, 81, - (long *)_vq_lengthlist__16c0_s_p9_0, + (char *)_vq_lengthlist__16c0_s_p9_0, 1, -518803456, 1628680192, 2, 0, (long *)_vq_quantlist__16c0_s_p9_0, 0 @@ -768,7 +767,7 @@ static const long _vq_quantlist__16c0_s_p9_1[] = { 14, }; -static const long _vq_lengthlist__16c0_s_p9_1[] = { +static const char _vq_lengthlist__16c0_s_p9_1[] = { 1, 5, 5, 5, 5, 9,11,11,10,10,10,10,10,10,10, 7, 6, 6, 6, 6,10,10,10,10,10,10,10,10,10,10, 7, 6, 6, 6, 6,10, 9,10,10,10,10,10,10,10,10,10, 7, 7, @@ -788,7 +787,7 @@ static const long _vq_lengthlist__16c0_s_p9_1[] = { static const static_codebook _16c0_s_p9_1 = { 2, 225, - (long *)_vq_lengthlist__16c0_s_p9_1, + (char *)_vq_lengthlist__16c0_s_p9_1, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__16c0_s_p9_1, 0 @@ -818,7 +817,7 @@ static const long _vq_quantlist__16c0_s_p9_2[] = { 20, }; -static const long _vq_lengthlist__16c0_s_p9_2[] = { +static const char _vq_lengthlist__16c0_s_p9_2[] = { 1, 5, 5, 7, 8, 8, 7, 9, 9, 9,12,12,11,12,12,10, 10,11,12,12,12,11,12,12, 8, 9, 8, 7, 9,10,10,11, 11,10,11,12,10,12,10,12,12,12,11,12,11, 9, 8, 8, @@ -851,13 +850,13 @@ static const long _vq_lengthlist__16c0_s_p9_2[] = { static const static_codebook _16c0_s_p9_2 = { 2, 441, - (long *)_vq_lengthlist__16c0_s_p9_2, + (char *)_vq_lengthlist__16c0_s_p9_2, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__16c0_s_p9_2, 0 }; -static const long _huff_lengthlist__16c0_s_single[] = { +static const char _huff_lengthlist__16c0_s_single[] = { 3, 4,19, 7, 9, 7, 8,11, 9,12, 4, 1,19, 6, 7, 7, 8,10,11,13,18,18,18,18,18,18,18,18,18,18, 8, 6, 18, 8, 9, 9,11,12,14,18, 9, 6,18, 9, 7, 8, 9,11, @@ -869,13 +868,13 @@ static const long _huff_lengthlist__16c0_s_single[] = { static const static_codebook _huff_book__16c0_s_single = { 2, 100, - (long *)_huff_lengthlist__16c0_s_single, + (char *)_huff_lengthlist__16c0_s_single, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__16c1_s_long[] = { +static const char _huff_lengthlist__16c1_s_long[] = { 2, 5,20, 7,10, 7, 8,10,11,11, 4, 2,20, 5, 8, 6, 7, 9,10,10,20,20,20,20,19,19,19,19,19,19, 7, 5, 19, 6,10, 7, 9,11,13,17,11, 8,19,10, 7, 7, 8,10, @@ -887,7 +886,7 @@ static const long _huff_lengthlist__16c1_s_long[] = { static const static_codebook _huff_book__16c1_s_long = { 2, 100, - (long *)_huff_lengthlist__16c1_s_long, + (char *)_huff_lengthlist__16c1_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -899,7 +898,7 @@ static const long _vq_quantlist__16c1_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__16c1_s_p1_0[] = { +static const char _vq_lengthlist__16c1_s_p1_0[] = { 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1315,7 +1314,7 @@ static const long _vq_lengthlist__16c1_s_p1_0[] = { static const static_codebook _16c1_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__16c1_s_p1_0, + (char *)_vq_lengthlist__16c1_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__16c1_s_p1_0, 0 @@ -1329,7 +1328,7 @@ static const long _vq_quantlist__16c1_s_p3_0[] = { 4, }; -static const long _vq_lengthlist__16c1_s_p3_0[] = { +static const char _vq_lengthlist__16c1_s_p3_0[] = { 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1374,7 +1373,7 @@ static const long _vq_lengthlist__16c1_s_p3_0[] = { static const static_codebook _16c1_s_p3_0 = { 4, 625, - (long *)_vq_lengthlist__16c1_s_p3_0, + (char *)_vq_lengthlist__16c1_s_p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16c1_s_p3_0, 0 @@ -1392,7 +1391,7 @@ static const long _vq_quantlist__16c1_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__16c1_s_p4_0[] = { +static const char _vq_lengthlist__16c1_s_p4_0[] = { 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, @@ -1403,7 +1402,7 @@ static const long _vq_lengthlist__16c1_s_p4_0[] = { static const static_codebook _16c1_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__16c1_s_p4_0, + (char *)_vq_lengthlist__16c1_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__16c1_s_p4_0, 0 @@ -1421,7 +1420,7 @@ static const long _vq_quantlist__16c1_s_p5_0[] = { 8, }; -static const long _vq_lengthlist__16c1_s_p5_0[] = { +static const char _vq_lengthlist__16c1_s_p5_0[] = { 1, 3, 3, 5, 5, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 8, 8, 8, 8, 9, 9, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, @@ -1432,7 +1431,7 @@ static const long _vq_lengthlist__16c1_s_p5_0[] = { static const static_codebook _16c1_s_p5_0 = { 2, 81, - (long *)_vq_lengthlist__16c1_s_p5_0, + (char *)_vq_lengthlist__16c1_s_p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__16c1_s_p5_0, 0 @@ -1458,7 +1457,7 @@ static const long _vq_quantlist__16c1_s_p6_0[] = { 16, }; -static const long _vq_lengthlist__16c1_s_p6_0[] = { +static const char _vq_lengthlist__16c1_s_p6_0[] = { 1, 3, 3, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,11,12, 12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, 12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, @@ -1482,7 +1481,7 @@ static const long _vq_lengthlist__16c1_s_p6_0[] = { static const static_codebook _16c1_s_p6_0 = { 2, 289, - (long *)_vq_lengthlist__16c1_s_p6_0, + (char *)_vq_lengthlist__16c1_s_p6_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__16c1_s_p6_0, 0 @@ -1494,7 +1493,7 @@ static const long _vq_quantlist__16c1_s_p7_0[] = { 2, }; -static const long _vq_lengthlist__16c1_s_p7_0[] = { +static const char _vq_lengthlist__16c1_s_p7_0[] = { 1, 4, 4, 6, 6, 6, 7, 6, 6, 4, 7, 7,10, 9,10,10, 10, 9, 4, 7, 7,10,10,10,11,10,10, 6,10,10,11,11, 11,11,10,10, 6,10, 9,11,11,11,11,10,10, 6,10,10, @@ -1505,7 +1504,7 @@ static const long _vq_lengthlist__16c1_s_p7_0[] = { static const static_codebook _16c1_s_p7_0 = { 4, 81, - (long *)_vq_lengthlist__16c1_s_p7_0, + (char *)_vq_lengthlist__16c1_s_p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__16c1_s_p7_0, 0 @@ -1525,7 +1524,7 @@ static const long _vq_quantlist__16c1_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__16c1_s_p7_1[] = { +static const char _vq_lengthlist__16c1_s_p7_1[] = { 2, 3, 3, 5, 6, 7, 7, 7, 7, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, @@ -1538,7 +1537,7 @@ static const long _vq_lengthlist__16c1_s_p7_1[] = { static const static_codebook _16c1_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__16c1_s_p7_1, + (char *)_vq_lengthlist__16c1_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__16c1_s_p7_1, 0 @@ -1560,7 +1559,7 @@ static const long _vq_quantlist__16c1_s_p8_0[] = { 12, }; -static const long _vq_lengthlist__16c1_s_p8_0[] = { +static const char _vq_lengthlist__16c1_s_p8_0[] = { 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 6, 5, 5, 7, 8, 8, 9, 8, 8, 9, 9,10,11, 6, 5, 5, 8, 8, 9, 9, 8, 8, 9,10,10,11, 0, 8, 8, 8, 9, 9, 9, 9, 9, @@ -1576,7 +1575,7 @@ static const long _vq_lengthlist__16c1_s_p8_0[] = { static const static_codebook _16c1_s_p8_0 = { 2, 169, - (long *)_vq_lengthlist__16c1_s_p8_0, + (char *)_vq_lengthlist__16c1_s_p8_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__16c1_s_p8_0, 0 @@ -1590,14 +1589,14 @@ static const long _vq_quantlist__16c1_s_p8_1[] = { 4, }; -static const long _vq_lengthlist__16c1_s_p8_1[] = { +static const char _vq_lengthlist__16c1_s_p8_1[] = { 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _16c1_s_p8_1 = { 2, 25, - (long *)_vq_lengthlist__16c1_s_p8_1, + (char *)_vq_lengthlist__16c1_s_p8_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16c1_s_p8_1, 0 @@ -1619,7 +1618,7 @@ static const long _vq_quantlist__16c1_s_p9_0[] = { 12, }; -static const long _vq_lengthlist__16c1_s_p9_0[] = { +static const char _vq_lengthlist__16c1_s_p9_0[] = { 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -1635,7 +1634,7 @@ static const long _vq_lengthlist__16c1_s_p9_0[] = { static const static_codebook _16c1_s_p9_0 = { 2, 169, - (long *)_vq_lengthlist__16c1_s_p9_0, + (char *)_vq_lengthlist__16c1_s_p9_0, 1, -513964032, 1628680192, 4, 0, (long *)_vq_quantlist__16c1_s_p9_0, 0 @@ -1659,7 +1658,7 @@ static const long _vq_quantlist__16c1_s_p9_1[] = { 14, }; -static const long _vq_lengthlist__16c1_s_p9_1[] = { +static const char _vq_lengthlist__16c1_s_p9_1[] = { 1, 4, 4, 4, 4, 8, 8,12,13,14,14,14,14,14,14, 6, 6, 6, 6, 6,10, 9,14,14,14,14,14,14,14,14, 7, 6, 5, 6, 6,10, 9,12,13,13,13,13,13,13,13,13, 7, 7, @@ -1679,7 +1678,7 @@ static const long _vq_lengthlist__16c1_s_p9_1[] = { static const static_codebook _16c1_s_p9_1 = { 2, 225, - (long *)_vq_lengthlist__16c1_s_p9_1, + (char *)_vq_lengthlist__16c1_s_p9_1, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__16c1_s_p9_1, 0 @@ -1709,7 +1708,7 @@ static const long _vq_quantlist__16c1_s_p9_2[] = { 20, }; -static const long _vq_lengthlist__16c1_s_p9_2[] = { +static const char _vq_lengthlist__16c1_s_p9_2[] = { 1, 4, 4, 6, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9,10, 10,10, 9,10,10,11,12,12, 8, 8, 8, 8, 9, 9, 9, 9, 10,10,10,10,10,11,11,10,12,11,11,13,11, 7, 7, 8, @@ -1742,13 +1741,13 @@ static const long _vq_lengthlist__16c1_s_p9_2[] = { static const static_codebook _16c1_s_p9_2 = { 2, 441, - (long *)_vq_lengthlist__16c1_s_p9_2, + (char *)_vq_lengthlist__16c1_s_p9_2, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__16c1_s_p9_2, 0 }; -static const long _huff_lengthlist__16c1_s_short[] = { +static const char _huff_lengthlist__16c1_s_short[] = { 5, 6,17, 8,12, 9,10,10,12,13, 5, 2,17, 4, 9, 5, 7, 8,11,13,16,16,16,16,16,16,16,16,16,16, 6, 4, 16, 5,10, 5, 7,10,14,16,13, 9,16,11, 8, 7, 8, 9, @@ -1760,13 +1759,13 @@ static const long _huff_lengthlist__16c1_s_short[] = { static const static_codebook _huff_book__16c1_s_short = { 2, 100, - (long *)_huff_lengthlist__16c1_s_short, + (char *)_huff_lengthlist__16c1_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__16c2_s_long[] = { +static const char _huff_lengthlist__16c2_s_long[] = { 4, 7, 9, 9, 9, 8, 9,10,13,16, 5, 4, 5, 6, 7, 7, 8, 9,12,16, 6, 5, 5, 5, 7, 7, 9,10,12,15, 7, 6, 5, 4, 5, 6, 8, 9,10,13, 8, 7, 7, 5, 5, 5, 7, 9, @@ -1778,7 +1777,7 @@ static const long _huff_lengthlist__16c2_s_long[] = { static const static_codebook _huff_book__16c2_s_long = { 2, 100, - (long *)_huff_lengthlist__16c2_s_long, + (char *)_huff_lengthlist__16c2_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -1790,7 +1789,7 @@ static const long _vq_quantlist__16c2_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__16c2_s_p1_0[] = { +static const char _vq_lengthlist__16c2_s_p1_0[] = { 1, 3, 3, 0, 0, 0, 0, 0, 0, 4, 5, 5, 0, 0, 0, 0, 0, 0, 4, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1801,7 +1800,7 @@ static const long _vq_lengthlist__16c2_s_p1_0[] = { static const static_codebook _16c2_s_p1_0 = { 4, 81, - (long *)_vq_lengthlist__16c2_s_p1_0, + (char *)_vq_lengthlist__16c2_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__16c2_s_p1_0, 0 @@ -1815,7 +1814,7 @@ static const long _vq_quantlist__16c2_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__16c2_s_p2_0[] = { +static const char _vq_lengthlist__16c2_s_p2_0[] = { 2, 4, 4, 7, 7, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, 4, 4, 4, 8, 7, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 9, 9, 0, 0, 0, @@ -1860,7 +1859,7 @@ static const long _vq_lengthlist__16c2_s_p2_0[] = { static const static_codebook _16c2_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__16c2_s_p2_0, + (char *)_vq_lengthlist__16c2_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16c2_s_p2_0, 0 @@ -1878,7 +1877,7 @@ static const long _vq_quantlist__16c2_s_p3_0[] = { 8, }; -static const long _vq_lengthlist__16c2_s_p3_0[] = { +static const char _vq_lengthlist__16c2_s_p3_0[] = { 1, 3, 3, 5, 5, 7, 7, 8, 8, 0, 0, 0, 6, 6, 8, 8, 9, 9, 0, 0, 0, 6, 6, 8, 8, 9, 9, 0, 0, 0, 7, 7, 8, 9,10,10, 0, 0, 0, 7, 7, 9, 9,10,10, 0, 0, 0, @@ -1889,7 +1888,7 @@ static const long _vq_lengthlist__16c2_s_p3_0[] = { static const static_codebook _16c2_s_p3_0 = { 2, 81, - (long *)_vq_lengthlist__16c2_s_p3_0, + (char *)_vq_lengthlist__16c2_s_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__16c2_s_p3_0, 0 @@ -1915,7 +1914,7 @@ static const long _vq_quantlist__16c2_s_p4_0[] = { 16, }; -static const long _vq_lengthlist__16c2_s_p4_0[] = { +static const char _vq_lengthlist__16c2_s_p4_0[] = { 2, 3, 3, 5, 5, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 0, 0, 0, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 11,10, 0, 0, 0, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10, @@ -1939,7 +1938,7 @@ static const long _vq_lengthlist__16c2_s_p4_0[] = { static const static_codebook _16c2_s_p4_0 = { 2, 289, - (long *)_vq_lengthlist__16c2_s_p4_0, + (char *)_vq_lengthlist__16c2_s_p4_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__16c2_s_p4_0, 0 @@ -1951,7 +1950,7 @@ static const long _vq_quantlist__16c2_s_p5_0[] = { 2, }; -static const long _vq_lengthlist__16c2_s_p5_0[] = { +static const char _vq_lengthlist__16c2_s_p5_0[] = { 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 6, 6,10,11,10,10, 10,11, 4, 6, 6,10,10,11,10,11,10, 5,10,10, 9,12, 11,10,12,12, 7,10,10,12,12,12,12,13,13, 7,11,10, @@ -1962,7 +1961,7 @@ static const long _vq_lengthlist__16c2_s_p5_0[] = { static const static_codebook _16c2_s_p5_0 = { 4, 81, - (long *)_vq_lengthlist__16c2_s_p5_0, + (char *)_vq_lengthlist__16c2_s_p5_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__16c2_s_p5_0, 0 @@ -1982,7 +1981,7 @@ static const long _vq_quantlist__16c2_s_p5_1[] = { 10, }; -static const long _vq_lengthlist__16c2_s_p5_1[] = { +static const char _vq_lengthlist__16c2_s_p5_1[] = { 2, 3, 3, 6, 6, 6, 6, 7, 7, 7, 7,11,10,10, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, 8,11,11,11, 7, 7, 8, 8, 8, 8, 9, 9,11,11,11, 6, @@ -1995,7 +1994,7 @@ static const long _vq_lengthlist__16c2_s_p5_1[] = { static const static_codebook _16c2_s_p5_1 = { 2, 121, - (long *)_vq_lengthlist__16c2_s_p5_1, + (char *)_vq_lengthlist__16c2_s_p5_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__16c2_s_p5_1, 0 @@ -2017,7 +2016,7 @@ static const long _vq_quantlist__16c2_s_p6_0[] = { 12, }; -static const long _vq_lengthlist__16c2_s_p6_0[] = { +static const char _vq_lengthlist__16c2_s_p6_0[] = { 1, 4, 4, 6, 6, 8, 7, 8, 8, 9, 9,10,10, 5, 5, 5, 7, 7, 9, 9, 9, 9,11,11,12,12, 6, 5, 5, 7, 7, 9, 9,10, 9,11,11,12,12, 0, 7, 7, 7, 7, 9, 9,10,10, @@ -2033,7 +2032,7 @@ static const long _vq_lengthlist__16c2_s_p6_0[] = { static const static_codebook _16c2_s_p6_0 = { 2, 169, - (long *)_vq_lengthlist__16c2_s_p6_0, + (char *)_vq_lengthlist__16c2_s_p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__16c2_s_p6_0, 0 @@ -2047,14 +2046,14 @@ static const long _vq_quantlist__16c2_s_p6_1[] = { 4, }; -static const long _vq_lengthlist__16c2_s_p6_1[] = { +static const char _vq_lengthlist__16c2_s_p6_1[] = { 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _16c2_s_p6_1 = { 2, 25, - (long *)_vq_lengthlist__16c2_s_p6_1, + (char *)_vq_lengthlist__16c2_s_p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16c2_s_p6_1, 0 @@ -2076,7 +2075,7 @@ static const long _vq_quantlist__16c2_s_p7_0[] = { 12, }; -static const long _vq_lengthlist__16c2_s_p7_0[] = { +static const char _vq_lengthlist__16c2_s_p7_0[] = { 1, 4, 4, 7, 7, 8, 8, 8, 8,10, 9,10,10, 5, 5, 5, 7, 7, 9, 9,10,10,11,10,12,11, 6, 5, 5, 7, 7, 9, 9,10,10,11,11,12,12,20, 7, 7, 7, 7, 9, 9,10,10, @@ -2092,7 +2091,7 @@ static const long _vq_lengthlist__16c2_s_p7_0[] = { static const static_codebook _16c2_s_p7_0 = { 2, 169, - (long *)_vq_lengthlist__16c2_s_p7_0, + (char *)_vq_lengthlist__16c2_s_p7_0, 1, -523206656, 1618345984, 4, 0, (long *)_vq_quantlist__16c2_s_p7_0, 0 @@ -2112,7 +2111,7 @@ static const long _vq_quantlist__16c2_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__16c2_s_p7_1[] = { +static const char _vq_lengthlist__16c2_s_p7_1[] = { 2, 4, 4, 6, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 6, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 7, @@ -2125,7 +2124,7 @@ static const long _vq_lengthlist__16c2_s_p7_1[] = { static const static_codebook _16c2_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__16c2_s_p7_1, + (char *)_vq_lengthlist__16c2_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__16c2_s_p7_1, 0 @@ -2149,7 +2148,7 @@ static const long _vq_quantlist__16c2_s_p8_0[] = { 14, }; -static const long _vq_lengthlist__16c2_s_p8_0[] = { +static const char _vq_lengthlist__16c2_s_p8_0[] = { 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9,10,10, 6, 6, 6, 8, 8, 9, 9, 8, 8, 9, 9,10,10,11,11, 6, 5, 5, 8, 7, 9, 9, 8, 8, 9, 9,10,10,11,11,20, 8, 8, @@ -2169,7 +2168,7 @@ static const long _vq_lengthlist__16c2_s_p8_0[] = { static const static_codebook _16c2_s_p8_0 = { 2, 225, - (long *)_vq_lengthlist__16c2_s_p8_0, + (char *)_vq_lengthlist__16c2_s_p8_0, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__16c2_s_p8_0, 0 @@ -2199,7 +2198,7 @@ static const long _vq_quantlist__16c2_s_p8_1[] = { 20, }; -static const long _vq_lengthlist__16c2_s_p8_1[] = { +static const char _vq_lengthlist__16c2_s_p8_1[] = { 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,11,11,11, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,11,10, 7, 7, 8, @@ -2232,7 +2231,7 @@ static const long _vq_lengthlist__16c2_s_p8_1[] = { static const static_codebook _16c2_s_p8_1 = { 2, 441, - (long *)_vq_lengthlist__16c2_s_p8_1, + (char *)_vq_lengthlist__16c2_s_p8_1, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__16c2_s_p8_1, 0 @@ -2258,7 +2257,7 @@ static const long _vq_quantlist__16c2_s_p9_0[] = { 16, }; -static const long _vq_lengthlist__16c2_s_p9_0[] = { +static const char _vq_lengthlist__16c2_s_p9_0[] = { 1, 4, 3,10, 8,10,10,10,10,10,10,10,10,10,10,10, 10, 6,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 10,10, 6,10, 9,10,10,10,10,10,10,10,10,10,10,10, @@ -2282,7 +2281,7 @@ static const long _vq_lengthlist__16c2_s_p9_0[] = { static const static_codebook _16c2_s_p9_0 = { 2, 289, - (long *)_vq_lengthlist__16c2_s_p9_0, + (char *)_vq_lengthlist__16c2_s_p9_0, 1, -509798400, 1631393792, 5, 0, (long *)_vq_quantlist__16c2_s_p9_0, 0 @@ -2310,7 +2309,7 @@ static const long _vq_quantlist__16c2_s_p9_1[] = { 18, }; -static const long _vq_lengthlist__16c2_s_p9_1[] = { +static const char _vq_lengthlist__16c2_s_p9_1[] = { 1, 4, 4, 7, 7, 7, 7, 7, 7, 8, 8,10, 9,11,10,13, 11,14,13, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8,11, 9, 13,11,14,12,14,13, 5, 6, 6, 8, 8, 8, 8, 8, 8, 9, @@ -2338,7 +2337,7 @@ static const long _vq_lengthlist__16c2_s_p9_1[] = { static const static_codebook _16c2_s_p9_1 = { 2, 361, - (long *)_vq_lengthlist__16c2_s_p9_1, + (char *)_vq_lengthlist__16c2_s_p9_1, 1, -518287360, 1622704128, 5, 0, (long *)_vq_quantlist__16c2_s_p9_1, 0 @@ -2396,7 +2395,7 @@ static const long _vq_quantlist__16c2_s_p9_2[] = { 48, }; -static const long _vq_lengthlist__16c2_s_p9_2[] = { +static const char _vq_lengthlist__16c2_s_p9_2[] = { 2, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -2405,13 +2404,13 @@ static const long _vq_lengthlist__16c2_s_p9_2[] = { static const static_codebook _16c2_s_p9_2 = { 1, 49, - (long *)_vq_lengthlist__16c2_s_p9_2, + (char *)_vq_lengthlist__16c2_s_p9_2, 1, -526909440, 1611661312, 6, 0, (long *)_vq_quantlist__16c2_s_p9_2, 0 }; -static const long _huff_lengthlist__16c2_s_short[] = { +static const char _huff_lengthlist__16c2_s_short[] = { 7,10,12,11,12,13,15,16,18,15,10, 8, 8, 8, 9,10, 12,13,14,17,10, 7, 7, 7, 7, 8,10,12,15,18,10, 7, 7, 5, 5, 6, 8,10,13,15,10, 7, 6, 5, 4, 4, 6, 9, @@ -2423,7 +2422,7 @@ static const long _huff_lengthlist__16c2_s_short[] = { static const static_codebook _huff_book__16c2_s_short = { 2, 100, - (long *)_huff_lengthlist__16c2_s_short, + (char *)_huff_lengthlist__16c2_s_short, 0, 0, 0, 0, 0, NULL, 0 @@ -2435,7 +2434,7 @@ static const long _vq_quantlist__8c0_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__8c0_s_p1_0[] = { +static const char _vq_lengthlist__8c0_s_p1_0[] = { 1, 5, 4, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2851,7 +2850,7 @@ static const long _vq_lengthlist__8c0_s_p1_0[] = { static const static_codebook _8c0_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__8c0_s_p1_0, + (char *)_vq_lengthlist__8c0_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__8c0_s_p1_0, 0 @@ -2865,7 +2864,7 @@ static const long _vq_quantlist__8c0_s_p3_0[] = { 4, }; -static const long _vq_lengthlist__8c0_s_p3_0[] = { +static const char _vq_lengthlist__8c0_s_p3_0[] = { 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2910,7 +2909,7 @@ static const long _vq_lengthlist__8c0_s_p3_0[] = { static const static_codebook _8c0_s_p3_0 = { 4, 625, - (long *)_vq_lengthlist__8c0_s_p3_0, + (char *)_vq_lengthlist__8c0_s_p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__8c0_s_p3_0, 0 @@ -2928,7 +2927,7 @@ static const long _vq_quantlist__8c0_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__8c0_s_p4_0[] = { +static const char _vq_lengthlist__8c0_s_p4_0[] = { 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, @@ -2939,7 +2938,7 @@ static const long _vq_lengthlist__8c0_s_p4_0[] = { static const static_codebook _8c0_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__8c0_s_p4_0, + (char *)_vq_lengthlist__8c0_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__8c0_s_p4_0, 0 @@ -2957,7 +2956,7 @@ static const long _vq_quantlist__8c0_s_p5_0[] = { 8, }; -static const long _vq_lengthlist__8c0_s_p5_0[] = { +static const char _vq_lengthlist__8c0_s_p5_0[] = { 1, 3, 3, 5, 5, 7, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, 8, 8, 0, 0, 0, 7, 7, 7, 7, 8, 9, 0, 0, 0, 8, 8, 8, 8, 9, 9, 0, 0, 0, 8, 8, 8, 8, 9, 9, 0, 0, 0, @@ -2968,7 +2967,7 @@ static const long _vq_lengthlist__8c0_s_p5_0[] = { static const static_codebook _8c0_s_p5_0 = { 2, 81, - (long *)_vq_lengthlist__8c0_s_p5_0, + (char *)_vq_lengthlist__8c0_s_p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__8c0_s_p5_0, 0 @@ -2994,7 +2993,7 @@ static const long _vq_quantlist__8c0_s_p6_0[] = { 16, }; -static const long _vq_lengthlist__8c0_s_p6_0[] = { +static const char _vq_lengthlist__8c0_s_p6_0[] = { 1, 3, 3, 6, 6, 8, 8, 9, 9, 8, 8,10, 9,10,10,11, 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, 11,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, @@ -3018,7 +3017,7 @@ static const long _vq_lengthlist__8c0_s_p6_0[] = { static const static_codebook _8c0_s_p6_0 = { 2, 289, - (long *)_vq_lengthlist__8c0_s_p6_0, + (char *)_vq_lengthlist__8c0_s_p6_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__8c0_s_p6_0, 0 @@ -3030,7 +3029,7 @@ static const long _vq_quantlist__8c0_s_p7_0[] = { 2, }; -static const long _vq_lengthlist__8c0_s_p7_0[] = { +static const char _vq_lengthlist__8c0_s_p7_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,11, 9,10,12, 9,10, 4, 7, 7,10,10,10,11, 9, 9, 6,11,10,11,11, 12,11,11,11, 6,10,10,11,11,12,11,10,10, 6, 9,10, @@ -3041,7 +3040,7 @@ static const long _vq_lengthlist__8c0_s_p7_0[] = { static const static_codebook _8c0_s_p7_0 = { 4, 81, - (long *)_vq_lengthlist__8c0_s_p7_0, + (char *)_vq_lengthlist__8c0_s_p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__8c0_s_p7_0, 0 @@ -3061,7 +3060,7 @@ static const long _vq_quantlist__8c0_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__8c0_s_p7_1[] = { +static const char _vq_lengthlist__8c0_s_p7_1[] = { 1, 3, 3, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10, 7, 7, 8, 8, 9, 9, 9, 9,10,10, 9, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, 8, 8, 9, 9, 9, 9, 9, 9,10,10,10, 8, @@ -3074,7 +3073,7 @@ static const long _vq_lengthlist__8c0_s_p7_1[] = { static const static_codebook _8c0_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__8c0_s_p7_1, + (char *)_vq_lengthlist__8c0_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__8c0_s_p7_1, 0 @@ -3096,7 +3095,7 @@ static const long _vq_quantlist__8c0_s_p8_0[] = { 12, }; -static const long _vq_lengthlist__8c0_s_p8_0[] = { +static const char _vq_lengthlist__8c0_s_p8_0[] = { 1, 4, 4, 7, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 6, 6, 7, 7, 8, 8, 7, 7, 8, 9,10,10, 7, 6, 6, 7, 7, 8, 7, 7, 7, 9, 9,10,12, 0, 8, 8, 8, 8, 8, 9, 8, 8, @@ -3112,7 +3111,7 @@ static const long _vq_lengthlist__8c0_s_p8_0[] = { static const static_codebook _8c0_s_p8_0 = { 2, 169, - (long *)_vq_lengthlist__8c0_s_p8_0, + (char *)_vq_lengthlist__8c0_s_p8_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__8c0_s_p8_0, 0 @@ -3126,14 +3125,14 @@ static const long _vq_quantlist__8c0_s_p8_1[] = { 4, }; -static const long _vq_lengthlist__8c0_s_p8_1[] = { +static const char _vq_lengthlist__8c0_s_p8_1[] = { 1, 3, 4, 5, 5, 7, 6, 6, 6, 5, 7, 7, 7, 6, 6, 7, 7, 7, 6, 6, 7, 7, 7, 6, 6, }; static const static_codebook _8c0_s_p8_1 = { 2, 25, - (long *)_vq_lengthlist__8c0_s_p8_1, + (char *)_vq_lengthlist__8c0_s_p8_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__8c0_s_p8_1, 0 @@ -3145,7 +3144,7 @@ static const long _vq_quantlist__8c0_s_p9_0[] = { 2, }; -static const long _vq_lengthlist__8c0_s_p9_0[] = { +static const char _vq_lengthlist__8c0_s_p9_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -3156,7 +3155,7 @@ static const long _vq_lengthlist__8c0_s_p9_0[] = { static const static_codebook _8c0_s_p9_0 = { 4, 81, - (long *)_vq_lengthlist__8c0_s_p9_0, + (char *)_vq_lengthlist__8c0_s_p9_0, 1, -518803456, 1628680192, 2, 0, (long *)_vq_quantlist__8c0_s_p9_0, 0 @@ -3180,7 +3179,7 @@ static const long _vq_quantlist__8c0_s_p9_1[] = { 14, }; -static const long _vq_lengthlist__8c0_s_p9_1[] = { +static const char _vq_lengthlist__8c0_s_p9_1[] = { 1, 4, 4, 5, 5,10, 8,11,11,11,11,11,11,11,11, 6, 6, 6, 7, 6,11,10,11,11,11,11,11,11,11,11, 7, 5, 6, 6, 6, 8, 7,11,11,11,11,11,11,11,11,11, 7, 8, @@ -3200,7 +3199,7 @@ static const long _vq_lengthlist__8c0_s_p9_1[] = { static const static_codebook _8c0_s_p9_1 = { 2, 225, - (long *)_vq_lengthlist__8c0_s_p9_1, + (char *)_vq_lengthlist__8c0_s_p9_1, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__8c0_s_p9_1, 0 @@ -3230,7 +3229,7 @@ static const long _vq_quantlist__8c0_s_p9_2[] = { 20, }; -static const long _vq_lengthlist__8c0_s_p9_2[] = { +static const char _vq_lengthlist__8c0_s_p9_2[] = { 1, 5, 5, 7, 7, 8, 7, 8, 8,10,10, 9, 9,10,10,10, 11,11,10,12,11,12,12,12, 9, 8, 8, 8, 8, 8, 9,10, 10,10,10,11,11,11,10,11,11,12,12,11,12, 8, 8, 7, @@ -3263,13 +3262,13 @@ static const long _vq_lengthlist__8c0_s_p9_2[] = { static const static_codebook _8c0_s_p9_2 = { 2, 441, - (long *)_vq_lengthlist__8c0_s_p9_2, + (char *)_vq_lengthlist__8c0_s_p9_2, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__8c0_s_p9_2, 0 }; -static const long _huff_lengthlist__8c0_s_single[] = { +static const char _huff_lengthlist__8c0_s_single[] = { 4, 5,18, 7,10, 6, 7, 8, 9,10, 5, 2,18, 5, 7, 5, 6, 7, 8,11,17,17,17,17,17,17,17,17,17,17, 7, 4, 17, 6, 9, 6, 8,10,12,15,11, 7,17, 9, 6, 6, 7, 9, @@ -3281,7 +3280,7 @@ static const long _huff_lengthlist__8c0_s_single[] = { static const static_codebook _huff_book__8c0_s_single = { 2, 100, - (long *)_huff_lengthlist__8c0_s_single, + (char *)_huff_lengthlist__8c0_s_single, 0, 0, 0, 0, 0, NULL, 0 @@ -3293,7 +3292,7 @@ static const long _vq_quantlist__8c1_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__8c1_s_p1_0[] = { +static const char _vq_lengthlist__8c1_s_p1_0[] = { 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3709,7 +3708,7 @@ static const long _vq_lengthlist__8c1_s_p1_0[] = { static const static_codebook _8c1_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__8c1_s_p1_0, + (char *)_vq_lengthlist__8c1_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__8c1_s_p1_0, 0 @@ -3723,7 +3722,7 @@ static const long _vq_quantlist__8c1_s_p3_0[] = { 4, }; -static const long _vq_lengthlist__8c1_s_p3_0[] = { +static const char _vq_lengthlist__8c1_s_p3_0[] = { 2, 4, 4, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3768,7 +3767,7 @@ static const long _vq_lengthlist__8c1_s_p3_0[] = { static const static_codebook _8c1_s_p3_0 = { 4, 625, - (long *)_vq_lengthlist__8c1_s_p3_0, + (char *)_vq_lengthlist__8c1_s_p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__8c1_s_p3_0, 0 @@ -3786,7 +3785,7 @@ static const long _vq_quantlist__8c1_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__8c1_s_p4_0[] = { +static const char _vq_lengthlist__8c1_s_p4_0[] = { 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, @@ -3797,7 +3796,7 @@ static const long _vq_lengthlist__8c1_s_p4_0[] = { static const static_codebook _8c1_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__8c1_s_p4_0, + (char *)_vq_lengthlist__8c1_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__8c1_s_p4_0, 0 @@ -3815,7 +3814,7 @@ static const long _vq_quantlist__8c1_s_p5_0[] = { 8, }; -static const long _vq_lengthlist__8c1_s_p5_0[] = { +static const char _vq_lengthlist__8c1_s_p5_0[] = { 1, 3, 3, 4, 5, 6, 6, 8, 8, 0, 0, 0, 8, 8, 7, 7, 9, 9, 0, 0, 0, 8, 8, 7, 7, 9, 9, 0, 0, 0, 9,10, 8, 8, 9, 9, 0, 0, 0,10,10, 8, 8, 9, 9, 0, 0, 0, @@ -3826,7 +3825,7 @@ static const long _vq_lengthlist__8c1_s_p5_0[] = { static const static_codebook _8c1_s_p5_0 = { 2, 81, - (long *)_vq_lengthlist__8c1_s_p5_0, + (char *)_vq_lengthlist__8c1_s_p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__8c1_s_p5_0, 0 @@ -3852,7 +3851,7 @@ static const long _vq_quantlist__8c1_s_p6_0[] = { 16, }; -static const long _vq_lengthlist__8c1_s_p6_0[] = { +static const char _vq_lengthlist__8c1_s_p6_0[] = { 1, 3, 3, 5, 5, 8, 8, 8, 8, 9, 9,10,10,11,11,11, 11, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11,11, 12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, @@ -3876,7 +3875,7 @@ static const long _vq_lengthlist__8c1_s_p6_0[] = { static const static_codebook _8c1_s_p6_0 = { 2, 289, - (long *)_vq_lengthlist__8c1_s_p6_0, + (char *)_vq_lengthlist__8c1_s_p6_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__8c1_s_p6_0, 0 @@ -3888,7 +3887,7 @@ static const long _vq_quantlist__8c1_s_p7_0[] = { 2, }; -static const long _vq_lengthlist__8c1_s_p7_0[] = { +static const char _vq_lengthlist__8c1_s_p7_0[] = { 1, 4, 4, 6, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,10, 9, 9, 5, 7, 7,10, 9, 9,10, 9, 9, 6,10,10,10,10, 10,11,10,10, 6, 9, 9,10, 9,10,11,10,10, 6, 9, 9, @@ -3899,7 +3898,7 @@ static const long _vq_lengthlist__8c1_s_p7_0[] = { static const static_codebook _8c1_s_p7_0 = { 4, 81, - (long *)_vq_lengthlist__8c1_s_p7_0, + (char *)_vq_lengthlist__8c1_s_p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__8c1_s_p7_0, 0 @@ -3919,7 +3918,7 @@ static const long _vq_quantlist__8c1_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__8c1_s_p7_1[] = { +static const char _vq_lengthlist__8c1_s_p7_1[] = { 2, 3, 3, 5, 5, 7, 7, 7, 7, 7, 7,10,10, 9, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, @@ -3932,7 +3931,7 @@ static const long _vq_lengthlist__8c1_s_p7_1[] = { static const static_codebook _8c1_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__8c1_s_p7_1, + (char *)_vq_lengthlist__8c1_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__8c1_s_p7_1, 0 @@ -3954,7 +3953,7 @@ static const long _vq_quantlist__8c1_s_p8_0[] = { 12, }; -static const long _vq_lengthlist__8c1_s_p8_0[] = { +static const char _vq_lengthlist__8c1_s_p8_0[] = { 1, 4, 4, 6, 6, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 5, 7, 7, 8, 8, 8, 8, 9,10,11,11, 7, 5, 5, 7, 7, 8, 8, 9, 9,10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -3970,7 +3969,7 @@ static const long _vq_lengthlist__8c1_s_p8_0[] = { static const static_codebook _8c1_s_p8_0 = { 2, 169, - (long *)_vq_lengthlist__8c1_s_p8_0, + (char *)_vq_lengthlist__8c1_s_p8_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__8c1_s_p8_0, 0 @@ -3984,14 +3983,14 @@ static const long _vq_quantlist__8c1_s_p8_1[] = { 4, }; -static const long _vq_lengthlist__8c1_s_p8_1[] = { +static const char _vq_lengthlist__8c1_s_p8_1[] = { 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _8c1_s_p8_1 = { 2, 25, - (long *)_vq_lengthlist__8c1_s_p8_1, + (char *)_vq_lengthlist__8c1_s_p8_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__8c1_s_p8_1, 0 @@ -4013,7 +4012,7 @@ static const long _vq_quantlist__8c1_s_p9_0[] = { 12, }; -static const long _vq_lengthlist__8c1_s_p9_0[] = { +static const char _vq_lengthlist__8c1_s_p9_0[] = { 1, 3, 3,10,10,10,10,10,10,10,10,10,10, 5, 6, 6, 10,10,10,10,10,10,10,10,10,10, 6, 7, 8,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, @@ -4029,7 +4028,7 @@ static const long _vq_lengthlist__8c1_s_p9_0[] = { static const static_codebook _8c1_s_p9_0 = { 2, 169, - (long *)_vq_lengthlist__8c1_s_p9_0, + (char *)_vq_lengthlist__8c1_s_p9_0, 1, -513964032, 1628680192, 4, 0, (long *)_vq_quantlist__8c1_s_p9_0, 0 @@ -4053,7 +4052,7 @@ static const long _vq_quantlist__8c1_s_p9_1[] = { 14, }; -static const long _vq_lengthlist__8c1_s_p9_1[] = { +static const char _vq_lengthlist__8c1_s_p9_1[] = { 1, 4, 4, 5, 5, 7, 7, 9, 9,11,11,12,12,13,13, 6, 5, 5, 6, 6, 9, 9,10,10,12,12,12,13,15,14, 6, 5, 5, 7, 7, 9, 9,10,10,12,12,12,13,14,13,17, 7, 7, @@ -4073,7 +4072,7 @@ static const long _vq_lengthlist__8c1_s_p9_1[] = { static const static_codebook _8c1_s_p9_1 = { 2, 225, - (long *)_vq_lengthlist__8c1_s_p9_1, + (char *)_vq_lengthlist__8c1_s_p9_1, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__8c1_s_p9_1, 0 @@ -4103,7 +4102,7 @@ static const long _vq_quantlist__8c1_s_p9_2[] = { 20, }; -static const long _vq_lengthlist__8c1_s_p9_2[] = { +static const char _vq_lengthlist__8c1_s_p9_2[] = { 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9,11,11,12, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,10,10,10,10,10,11,11,11, 7, 7, 7, @@ -4136,13 +4135,13 @@ static const long _vq_lengthlist__8c1_s_p9_2[] = { static const static_codebook _8c1_s_p9_2 = { 2, 441, - (long *)_vq_lengthlist__8c1_s_p9_2, + (char *)_vq_lengthlist__8c1_s_p9_2, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__8c1_s_p9_2, 0 }; -static const long _huff_lengthlist__8c1_s_single[] = { +static const char _huff_lengthlist__8c1_s_single[] = { 4, 6,18, 8,11, 8, 8, 9, 9,10, 4, 4,18, 5, 9, 5, 6, 7, 8,10,18,18,18,18,17,17,17,17,17,17, 7, 5, 17, 6,11, 6, 7, 8, 9,12,12, 9,17,12, 8, 8, 9,10, @@ -4154,13 +4153,13 @@ static const long _huff_lengthlist__8c1_s_single[] = { static const static_codebook _huff_book__8c1_s_single = { 2, 100, - (long *)_huff_lengthlist__8c1_s_single, + (char *)_huff_lengthlist__8c1_s_single, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c2_s_long[] = { +static const char _huff_lengthlist__44c2_s_long[] = { 6, 6,12,10,10,10, 9,10,12,12, 6, 1,10, 5, 6, 6, 7, 9,11,14,12, 9, 8,11, 7, 8, 9,11,13,15,10, 5, 12, 7, 8, 7, 9,12,14,15,10, 6, 7, 8, 5, 6, 7, 9, @@ -4172,7 +4171,7 @@ static const long _huff_lengthlist__44c2_s_long[] = { static const static_codebook _huff_book__44c2_s_long = { 2, 100, - (long *)_huff_lengthlist__44c2_s_long, + (char *)_huff_lengthlist__44c2_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -4184,7 +4183,7 @@ static const long _vq_quantlist__44c2_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c2_s_p1_0[] = { +static const char _vq_lengthlist__44c2_s_p1_0[] = { 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 0, 0, 0, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -4600,7 +4599,7 @@ static const long _vq_lengthlist__44c2_s_p1_0[] = { static const static_codebook _44c2_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__44c2_s_p1_0, + (char *)_vq_lengthlist__44c2_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c2_s_p1_0, 0 @@ -4614,7 +4613,7 @@ static const long _vq_quantlist__44c2_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c2_s_p2_0[] = { +static const char _vq_lengthlist__44c2_s_p2_0[] = { 1, 4, 4, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, @@ -4659,7 +4658,7 @@ static const long _vq_lengthlist__44c2_s_p2_0[] = { static const static_codebook _44c2_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c2_s_p2_0, + (char *)_vq_lengthlist__44c2_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c2_s_p2_0, 0 @@ -4673,7 +4672,7 @@ static const long _vq_quantlist__44c2_s_p3_0[] = { 4, }; -static const long _vq_lengthlist__44c2_s_p3_0[] = { +static const char _vq_lengthlist__44c2_s_p3_0[] = { 2, 4, 3, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -4718,7 +4717,7 @@ static const long _vq_lengthlist__44c2_s_p3_0[] = { static const static_codebook _44c2_s_p3_0 = { 4, 625, - (long *)_vq_lengthlist__44c2_s_p3_0, + (char *)_vq_lengthlist__44c2_s_p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c2_s_p3_0, 0 @@ -4736,7 +4735,7 @@ static const long _vq_quantlist__44c2_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__44c2_s_p4_0[] = { +static const char _vq_lengthlist__44c2_s_p4_0[] = { 1, 3, 3, 6, 6, 0, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 7, 7, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, @@ -4747,7 +4746,7 @@ static const long _vq_lengthlist__44c2_s_p4_0[] = { static const static_codebook _44c2_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__44c2_s_p4_0, + (char *)_vq_lengthlist__44c2_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c2_s_p4_0, 0 @@ -4765,7 +4764,7 @@ static const long _vq_quantlist__44c2_s_p5_0[] = { 8, }; -static const long _vq_lengthlist__44c2_s_p5_0[] = { +static const char _vq_lengthlist__44c2_s_p5_0[] = { 1, 3, 3, 6, 6, 7, 7, 9, 9, 0, 7, 7, 7, 7, 7, 7, 9, 9, 0, 7, 7, 7, 7, 7, 7, 9, 9, 0, 8, 8, 7, 7, 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, @@ -4776,7 +4775,7 @@ static const long _vq_lengthlist__44c2_s_p5_0[] = { static const static_codebook _44c2_s_p5_0 = { 2, 81, - (long *)_vq_lengthlist__44c2_s_p5_0, + (char *)_vq_lengthlist__44c2_s_p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c2_s_p5_0, 0 @@ -4802,7 +4801,7 @@ static const long _vq_quantlist__44c2_s_p6_0[] = { 16, }; -static const long _vq_lengthlist__44c2_s_p6_0[] = { +static const char _vq_lengthlist__44c2_s_p6_0[] = { 1, 4, 3, 6, 6, 8, 8, 9, 9, 9, 9, 9, 9,10,10,11, 11, 0, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, 12,11, 0, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, @@ -4826,7 +4825,7 @@ static const long _vq_lengthlist__44c2_s_p6_0[] = { static const static_codebook _44c2_s_p6_0 = { 2, 289, - (long *)_vq_lengthlist__44c2_s_p6_0, + (char *)_vq_lengthlist__44c2_s_p6_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c2_s_p6_0, 0 @@ -4838,7 +4837,7 @@ static const long _vq_quantlist__44c2_s_p7_0[] = { 2, }; -static const long _vq_lengthlist__44c2_s_p7_0[] = { +static const char _vq_lengthlist__44c2_s_p7_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, 9, 9, 4, 7, 7,10, 9, 9,10, 9, 9, 7,10,10,11,10, 11,11,10,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, @@ -4849,7 +4848,7 @@ static const long _vq_lengthlist__44c2_s_p7_0[] = { static const static_codebook _44c2_s_p7_0 = { 4, 81, - (long *)_vq_lengthlist__44c2_s_p7_0, + (char *)_vq_lengthlist__44c2_s_p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c2_s_p7_0, 0 @@ -4869,7 +4868,7 @@ static const long _vq_quantlist__44c2_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__44c2_s_p7_1[] = { +static const char _vq_lengthlist__44c2_s_p7_1[] = { 2, 3, 4, 6, 6, 7, 7, 7, 7, 7, 7, 9, 7, 7, 6, 6, 7, 7, 8, 8, 8, 8, 9, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8,10, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, @@ -4882,7 +4881,7 @@ static const long _vq_lengthlist__44c2_s_p7_1[] = { static const static_codebook _44c2_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__44c2_s_p7_1, + (char *)_vq_lengthlist__44c2_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c2_s_p7_1, 0 @@ -4904,7 +4903,7 @@ static const long _vq_quantlist__44c2_s_p8_0[] = { 12, }; -static const long _vq_lengthlist__44c2_s_p8_0[] = { +static const char _vq_lengthlist__44c2_s_p8_0[] = { 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 6, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 6, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -4920,7 +4919,7 @@ static const long _vq_lengthlist__44c2_s_p8_0[] = { static const static_codebook _44c2_s_p8_0 = { 2, 169, - (long *)_vq_lengthlist__44c2_s_p8_0, + (char *)_vq_lengthlist__44c2_s_p8_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c2_s_p8_0, 0 @@ -4934,14 +4933,14 @@ static const long _vq_quantlist__44c2_s_p8_1[] = { 4, }; -static const long _vq_lengthlist__44c2_s_p8_1[] = { +static const char _vq_lengthlist__44c2_s_p8_1[] = { 2, 4, 4, 5, 4, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c2_s_p8_1 = { 2, 25, - (long *)_vq_lengthlist__44c2_s_p8_1, + (char *)_vq_lengthlist__44c2_s_p8_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c2_s_p8_1, 0 @@ -4963,7 +4962,7 @@ static const long _vq_quantlist__44c2_s_p9_0[] = { 12, }; -static const long _vq_lengthlist__44c2_s_p9_0[] = { +static const char _vq_lengthlist__44c2_s_p9_0[] = { 1, 5, 4,12,12,12,12,12,12,12,12,12,12, 4, 9, 8, 11,11,11,11,11,11,11,11,11,11, 2, 8, 7,11,11,11, 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, @@ -4979,7 +4978,7 @@ static const long _vq_lengthlist__44c2_s_p9_0[] = { static const static_codebook _44c2_s_p9_0 = { 2, 169, - (long *)_vq_lengthlist__44c2_s_p9_0, + (char *)_vq_lengthlist__44c2_s_p9_0, 1, -514541568, 1627103232, 4, 0, (long *)_vq_quantlist__44c2_s_p9_0, 0 @@ -5001,7 +5000,7 @@ static const long _vq_quantlist__44c2_s_p9_1[] = { 12, }; -static const long _vq_lengthlist__44c2_s_p9_1[] = { +static const char _vq_lengthlist__44c2_s_p9_1[] = { 1, 4, 4, 6, 6, 7, 6, 8, 8,10, 9,10,10, 6, 5, 5, 7, 7, 8, 7,10, 9,11,11,12,13, 6, 5, 5, 7, 7, 8, 8,10,10,11,11,13,13,18, 8, 8, 8, 8, 9, 9,10,10, @@ -5017,7 +5016,7 @@ static const long _vq_lengthlist__44c2_s_p9_1[] = { static const static_codebook _44c2_s_p9_1 = { 2, 169, - (long *)_vq_lengthlist__44c2_s_p9_1, + (char *)_vq_lengthlist__44c2_s_p9_1, 1, -522616832, 1620115456, 4, 0, (long *)_vq_quantlist__44c2_s_p9_1, 0 @@ -5043,7 +5042,7 @@ static const long _vq_quantlist__44c2_s_p9_2[] = { 16, }; -static const long _vq_lengthlist__44c2_s_p9_2[] = { +static const char _vq_lengthlist__44c2_s_p9_2[] = { 2, 4, 4, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,10, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, @@ -5067,13 +5066,13 @@ static const long _vq_lengthlist__44c2_s_p9_2[] = { static const static_codebook _44c2_s_p9_2 = { 2, 289, - (long *)_vq_lengthlist__44c2_s_p9_2, + (char *)_vq_lengthlist__44c2_s_p9_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c2_s_p9_2, 0 }; -static const long _huff_lengthlist__44c2_s_short[] = { +static const char _huff_lengthlist__44c2_s_short[] = { 11, 9,13,12,12,11,12,12,13,15, 8, 2,11, 4, 8, 5, 7,10,12,15,13, 7,10, 9, 8, 8,10,13,17,17,11, 4, 12, 5, 9, 5, 8,11,14,16,12, 6, 8, 7, 6, 6, 8,11, @@ -5085,13 +5084,13 @@ static const long _huff_lengthlist__44c2_s_short[] = { static const static_codebook _huff_book__44c2_s_short = { 2, 100, - (long *)_huff_lengthlist__44c2_s_short, + (char *)_huff_lengthlist__44c2_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c3_s_long[] = { +static const char _huff_lengthlist__44c3_s_long[] = { 5, 6,11,11,11,11,10,10,12,11, 5, 2,11, 5, 6, 6, 7, 9,11,13,13,10, 7,11, 6, 7, 8, 9,10,12,11, 5, 11, 6, 8, 7, 9,11,14,15,11, 6, 6, 8, 4, 5, 7, 8, @@ -5103,7 +5102,7 @@ static const long _huff_lengthlist__44c3_s_long[] = { static const static_codebook _huff_book__44c3_s_long = { 2, 100, - (long *)_huff_lengthlist__44c3_s_long, + (char *)_huff_lengthlist__44c3_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -5115,7 +5114,7 @@ static const long _vq_quantlist__44c3_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c3_s_p1_0[] = { +static const char _vq_lengthlist__44c3_s_p1_0[] = { 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -5531,7 +5530,7 @@ static const long _vq_lengthlist__44c3_s_p1_0[] = { static const static_codebook _44c3_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__44c3_s_p1_0, + (char *)_vq_lengthlist__44c3_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c3_s_p1_0, 0 @@ -5545,7 +5544,7 @@ static const long _vq_quantlist__44c3_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c3_s_p2_0[] = { +static const char _vq_lengthlist__44c3_s_p2_0[] = { 2, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0,10,10, 0, 0, 0, 0, 0, @@ -5590,7 +5589,7 @@ static const long _vq_lengthlist__44c3_s_p2_0[] = { static const static_codebook _44c3_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c3_s_p2_0, + (char *)_vq_lengthlist__44c3_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c3_s_p2_0, 0 @@ -5604,7 +5603,7 @@ static const long _vq_quantlist__44c3_s_p3_0[] = { 4, }; -static const long _vq_lengthlist__44c3_s_p3_0[] = { +static const char _vq_lengthlist__44c3_s_p3_0[] = { 2, 4, 3, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -5649,7 +5648,7 @@ static const long _vq_lengthlist__44c3_s_p3_0[] = { static const static_codebook _44c3_s_p3_0 = { 4, 625, - (long *)_vq_lengthlist__44c3_s_p3_0, + (char *)_vq_lengthlist__44c3_s_p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c3_s_p3_0, 0 @@ -5667,7 +5666,7 @@ static const long _vq_quantlist__44c3_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__44c3_s_p4_0[] = { +static const char _vq_lengthlist__44c3_s_p4_0[] = { 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 5, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, @@ -5678,7 +5677,7 @@ static const long _vq_lengthlist__44c3_s_p4_0[] = { static const static_codebook _44c3_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__44c3_s_p4_0, + (char *)_vq_lengthlist__44c3_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c3_s_p4_0, 0 @@ -5696,7 +5695,7 @@ static const long _vq_quantlist__44c3_s_p5_0[] = { 8, }; -static const long _vq_lengthlist__44c3_s_p5_0[] = { +static const char _vq_lengthlist__44c3_s_p5_0[] = { 1, 3, 4, 6, 6, 7, 7, 9, 9, 0, 5, 5, 7, 7, 7, 8, 9, 9, 0, 5, 5, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, @@ -5707,7 +5706,7 @@ static const long _vq_lengthlist__44c3_s_p5_0[] = { static const static_codebook _44c3_s_p5_0 = { 2, 81, - (long *)_vq_lengthlist__44c3_s_p5_0, + (char *)_vq_lengthlist__44c3_s_p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c3_s_p5_0, 0 @@ -5733,7 +5732,7 @@ static const long _vq_quantlist__44c3_s_p6_0[] = { 16, }; -static const long _vq_lengthlist__44c3_s_p6_0[] = { +static const char _vq_lengthlist__44c3_s_p6_0[] = { 2, 3, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, 10, 0, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,10, 11,11, 0, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, @@ -5757,7 +5756,7 @@ static const long _vq_lengthlist__44c3_s_p6_0[] = { static const static_codebook _44c3_s_p6_0 = { 2, 289, - (long *)_vq_lengthlist__44c3_s_p6_0, + (char *)_vq_lengthlist__44c3_s_p6_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c3_s_p6_0, 0 @@ -5769,7 +5768,7 @@ static const long _vq_quantlist__44c3_s_p7_0[] = { 2, }; -static const long _vq_lengthlist__44c3_s_p7_0[] = { +static const char _vq_lengthlist__44c3_s_p7_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, 10,12,11,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, @@ -5780,7 +5779,7 @@ static const long _vq_lengthlist__44c3_s_p7_0[] = { static const static_codebook _44c3_s_p7_0 = { 4, 81, - (long *)_vq_lengthlist__44c3_s_p7_0, + (char *)_vq_lengthlist__44c3_s_p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c3_s_p7_0, 0 @@ -5800,7 +5799,7 @@ static const long _vq_quantlist__44c3_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__44c3_s_p7_1[] = { +static const char _vq_lengthlist__44c3_s_p7_1[] = { 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, @@ -5813,7 +5812,7 @@ static const long _vq_lengthlist__44c3_s_p7_1[] = { static const static_codebook _44c3_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__44c3_s_p7_1, + (char *)_vq_lengthlist__44c3_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c3_s_p7_1, 0 @@ -5835,7 +5834,7 @@ static const long _vq_quantlist__44c3_s_p8_0[] = { 12, }; -static const long _vq_lengthlist__44c3_s_p8_0[] = { +static const char _vq_lengthlist__44c3_s_p8_0[] = { 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,11,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -5851,7 +5850,7 @@ static const long _vq_lengthlist__44c3_s_p8_0[] = { static const static_codebook _44c3_s_p8_0 = { 2, 169, - (long *)_vq_lengthlist__44c3_s_p8_0, + (char *)_vq_lengthlist__44c3_s_p8_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c3_s_p8_0, 0 @@ -5865,14 +5864,14 @@ static const long _vq_quantlist__44c3_s_p8_1[] = { 4, }; -static const long _vq_lengthlist__44c3_s_p8_1[] = { +static const char _vq_lengthlist__44c3_s_p8_1[] = { 2, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 4, 5, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c3_s_p8_1 = { 2, 25, - (long *)_vq_lengthlist__44c3_s_p8_1, + (char *)_vq_lengthlist__44c3_s_p8_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c3_s_p8_1, 0 @@ -5894,7 +5893,7 @@ static const long _vq_quantlist__44c3_s_p9_0[] = { 12, }; -static const long _vq_lengthlist__44c3_s_p9_0[] = { +static const char _vq_lengthlist__44c3_s_p9_0[] = { 1, 4, 4,12,12,12,12,12,12,12,12,12,12, 4, 9, 8, 12,12,12,12,12,12,12,12,12,12, 2, 9, 7,12,12,12, 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, @@ -5910,7 +5909,7 @@ static const long _vq_lengthlist__44c3_s_p9_0[] = { static const static_codebook _44c3_s_p9_0 = { 2, 169, - (long *)_vq_lengthlist__44c3_s_p9_0, + (char *)_vq_lengthlist__44c3_s_p9_0, 1, -514332672, 1627381760, 4, 0, (long *)_vq_quantlist__44c3_s_p9_0, 0 @@ -5934,7 +5933,7 @@ static const long _vq_quantlist__44c3_s_p9_1[] = { 14, }; -static const long _vq_lengthlist__44c3_s_p9_1[] = { +static const char _vq_lengthlist__44c3_s_p9_1[] = { 1, 4, 4, 6, 6, 7, 7, 8, 7, 9, 9,10,10,10,10, 6, 5, 5, 7, 7, 8, 8,10, 8,11,10,12,12,13,13, 6, 5, 5, 7, 7, 8, 8,10, 9,11,11,12,12,13,12,18, 8, 8, @@ -5954,7 +5953,7 @@ static const long _vq_lengthlist__44c3_s_p9_1[] = { static const static_codebook _44c3_s_p9_1 = { 2, 225, - (long *)_vq_lengthlist__44c3_s_p9_1, + (char *)_vq_lengthlist__44c3_s_p9_1, 1, -522338304, 1620115456, 4, 0, (long *)_vq_quantlist__44c3_s_p9_1, 0 @@ -5980,7 +5979,7 @@ static const long _vq_quantlist__44c3_s_p9_2[] = { 16, }; -static const long _vq_lengthlist__44c3_s_p9_2[] = { +static const char _vq_lengthlist__44c3_s_p9_2[] = { 2, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, @@ -6004,13 +6003,13 @@ static const long _vq_lengthlist__44c3_s_p9_2[] = { static const static_codebook _44c3_s_p9_2 = { 2, 289, - (long *)_vq_lengthlist__44c3_s_p9_2, + (char *)_vq_lengthlist__44c3_s_p9_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c3_s_p9_2, 0 }; -static const long _huff_lengthlist__44c3_s_short[] = { +static const char _huff_lengthlist__44c3_s_short[] = { 10, 9,13,11,14,10,12,13,13,14, 7, 2,12, 5,10, 5, 7,10,12,14,12, 6, 9, 8, 7, 7, 9,11,13,16,10, 4, 12, 5,10, 6, 8,12,14,16,12, 6, 8, 7, 6, 5, 7,11, @@ -6022,13 +6021,13 @@ static const long _huff_lengthlist__44c3_s_short[] = { static const static_codebook _huff_book__44c3_s_short = { 2, 100, - (long *)_huff_lengthlist__44c3_s_short, + (char *)_huff_lengthlist__44c3_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c4_s_long[] = { +static const char _huff_lengthlist__44c4_s_long[] = { 4, 7,11,11,11,11,10,11,12,11, 5, 2,11, 5, 6, 6, 7, 9,11,12,11, 9, 6,10, 6, 7, 8, 9,10,11,11, 5, 11, 7, 8, 8, 9,11,13,14,11, 6, 5, 8, 4, 5, 7, 8, @@ -6040,7 +6039,7 @@ static const long _huff_lengthlist__44c4_s_long[] = { static const static_codebook _huff_book__44c4_s_long = { 2, 100, - (long *)_huff_lengthlist__44c4_s_long, + (char *)_huff_lengthlist__44c4_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -6052,7 +6051,7 @@ static const long _vq_quantlist__44c4_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c4_s_p1_0[] = { +static const char _vq_lengthlist__44c4_s_p1_0[] = { 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 0, 0, 0, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6468,7 +6467,7 @@ static const long _vq_lengthlist__44c4_s_p1_0[] = { static const static_codebook _44c4_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__44c4_s_p1_0, + (char *)_vq_lengthlist__44c4_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c4_s_p1_0, 0 @@ -6482,7 +6481,7 @@ static const long _vq_quantlist__44c4_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c4_s_p2_0[] = { +static const char _vq_lengthlist__44c4_s_p2_0[] = { 2, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0,10,10, 0, 0, 0, 0, 0, @@ -6527,7 +6526,7 @@ static const long _vq_lengthlist__44c4_s_p2_0[] = { static const static_codebook _44c4_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c4_s_p2_0, + (char *)_vq_lengthlist__44c4_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c4_s_p2_0, 0 @@ -6541,7 +6540,7 @@ static const long _vq_quantlist__44c4_s_p3_0[] = { 4, }; -static const long _vq_lengthlist__44c4_s_p3_0[] = { +static const char _vq_lengthlist__44c4_s_p3_0[] = { 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6586,7 +6585,7 @@ static const long _vq_lengthlist__44c4_s_p3_0[] = { static const static_codebook _44c4_s_p3_0 = { 4, 625, - (long *)_vq_lengthlist__44c4_s_p3_0, + (char *)_vq_lengthlist__44c4_s_p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c4_s_p3_0, 0 @@ -6604,7 +6603,7 @@ static const long _vq_quantlist__44c4_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__44c4_s_p4_0[] = { +static const char _vq_lengthlist__44c4_s_p4_0[] = { 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 5, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, @@ -6615,7 +6614,7 @@ static const long _vq_lengthlist__44c4_s_p4_0[] = { static const static_codebook _44c4_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__44c4_s_p4_0, + (char *)_vq_lengthlist__44c4_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c4_s_p4_0, 0 @@ -6633,7 +6632,7 @@ static const long _vq_quantlist__44c4_s_p5_0[] = { 8, }; -static const long _vq_lengthlist__44c4_s_p5_0[] = { +static const char _vq_lengthlist__44c4_s_p5_0[] = { 2, 3, 3, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 4, 5, 6, 6, 7, 7, 9, 9, 0, 6, 6, 7, 7, 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10, 9, 0, 0, 0, @@ -6644,7 +6643,7 @@ static const long _vq_lengthlist__44c4_s_p5_0[] = { static const static_codebook _44c4_s_p5_0 = { 2, 81, - (long *)_vq_lengthlist__44c4_s_p5_0, + (char *)_vq_lengthlist__44c4_s_p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c4_s_p5_0, 0 @@ -6670,7 +6669,7 @@ static const long _vq_quantlist__44c4_s_p6_0[] = { 16, }; -static const long _vq_lengthlist__44c4_s_p6_0[] = { +static const char _vq_lengthlist__44c4_s_p6_0[] = { 2, 4, 4, 6, 6, 8, 8, 9, 9, 8, 8, 9, 9,10,10,11, 11, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,11, 11,11, 0, 4, 4, 7, 6, 8, 8, 9, 9, 9, 9,10,10,11, @@ -6694,7 +6693,7 @@ static const long _vq_lengthlist__44c4_s_p6_0[] = { static const static_codebook _44c4_s_p6_0 = { 2, 289, - (long *)_vq_lengthlist__44c4_s_p6_0, + (char *)_vq_lengthlist__44c4_s_p6_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c4_s_p6_0, 0 @@ -6706,7 +6705,7 @@ static const long _vq_quantlist__44c4_s_p7_0[] = { 2, }; -static const long _vq_lengthlist__44c4_s_p7_0[] = { +static const char _vq_lengthlist__44c4_s_p7_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, 10,11,11,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, @@ -6717,7 +6716,7 @@ static const long _vq_lengthlist__44c4_s_p7_0[] = { static const static_codebook _44c4_s_p7_0 = { 4, 81, - (long *)_vq_lengthlist__44c4_s_p7_0, + (char *)_vq_lengthlist__44c4_s_p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c4_s_p7_0, 0 @@ -6737,7 +6736,7 @@ static const long _vq_quantlist__44c4_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__44c4_s_p7_1[] = { +static const char _vq_lengthlist__44c4_s_p7_1[] = { 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, @@ -6750,7 +6749,7 @@ static const long _vq_lengthlist__44c4_s_p7_1[] = { static const static_codebook _44c4_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__44c4_s_p7_1, + (char *)_vq_lengthlist__44c4_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c4_s_p7_1, 0 @@ -6772,7 +6771,7 @@ static const long _vq_quantlist__44c4_s_p8_0[] = { 12, }; -static const long _vq_lengthlist__44c4_s_p8_0[] = { +static const char _vq_lengthlist__44c4_s_p8_0[] = { 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, 7, 7, 8, 8, 8, 8, 9,10,11,11, 7, 5, 5, 7, 7, 8, 8, 9, 9,10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -6788,7 +6787,7 @@ static const long _vq_lengthlist__44c4_s_p8_0[] = { static const static_codebook _44c4_s_p8_0 = { 2, 169, - (long *)_vq_lengthlist__44c4_s_p8_0, + (char *)_vq_lengthlist__44c4_s_p8_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c4_s_p8_0, 0 @@ -6802,14 +6801,14 @@ static const long _vq_quantlist__44c4_s_p8_1[] = { 4, }; -static const long _vq_lengthlist__44c4_s_p8_1[] = { +static const char _vq_lengthlist__44c4_s_p8_1[] = { 2, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 5, 4, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c4_s_p8_1 = { 2, 25, - (long *)_vq_lengthlist__44c4_s_p8_1, + (char *)_vq_lengthlist__44c4_s_p8_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c4_s_p8_1, 0 @@ -6831,7 +6830,7 @@ static const long _vq_quantlist__44c4_s_p9_0[] = { 12, }; -static const long _vq_lengthlist__44c4_s_p9_0[] = { +static const char _vq_lengthlist__44c4_s_p9_0[] = { 1, 3, 3,12,12,12,12,12,12,12,12,12,12, 4, 7, 7, 12,12,12,12,12,12,12,12,12,12, 3, 8, 8,12,12,12, 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, @@ -6847,7 +6846,7 @@ static const long _vq_lengthlist__44c4_s_p9_0[] = { static const static_codebook _44c4_s_p9_0 = { 2, 169, - (long *)_vq_lengthlist__44c4_s_p9_0, + (char *)_vq_lengthlist__44c4_s_p9_0, 1, -513964032, 1628680192, 4, 0, (long *)_vq_quantlist__44c4_s_p9_0, 0 @@ -6871,7 +6870,7 @@ static const long _vq_quantlist__44c4_s_p9_1[] = { 14, }; -static const long _vq_lengthlist__44c4_s_p9_1[] = { +static const char _vq_lengthlist__44c4_s_p9_1[] = { 1, 4, 4, 5, 5, 7, 7, 9, 8,10, 9,10,10,10,10, 6, 5, 5, 7, 7, 9, 8,10, 9,11,10,12,12,13,13, 6, 5, 5, 7, 7, 9, 9,10,10,11,11,12,12,12,13,19, 8, 8, @@ -6891,7 +6890,7 @@ static const long _vq_lengthlist__44c4_s_p9_1[] = { static const static_codebook _44c4_s_p9_1 = { 2, 225, - (long *)_vq_lengthlist__44c4_s_p9_1, + (char *)_vq_lengthlist__44c4_s_p9_1, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__44c4_s_p9_1, 0 @@ -6921,7 +6920,7 @@ static const long _vq_quantlist__44c4_s_p9_2[] = { 20, }; -static const long _vq_lengthlist__44c4_s_p9_2[] = { +static const char _vq_lengthlist__44c4_s_p9_2[] = { 2, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9,11, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,11, 6, 6, 7, 7, 8, @@ -6954,13 +6953,13 @@ static const long _vq_lengthlist__44c4_s_p9_2[] = { static const static_codebook _44c4_s_p9_2 = { 2, 441, - (long *)_vq_lengthlist__44c4_s_p9_2, + (char *)_vq_lengthlist__44c4_s_p9_2, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__44c4_s_p9_2, 0 }; -static const long _huff_lengthlist__44c4_s_short[] = { +static const char _huff_lengthlist__44c4_s_short[] = { 4, 7,14,10,15,10,12,15,16,15, 4, 2,11, 5,10, 6, 8,11,14,14,14,10, 7,11, 6, 8,10,11,13,15, 9, 4, 11, 5, 9, 6, 9,12,14,15,14, 9, 6, 9, 4, 5, 7,10, @@ -6972,13 +6971,13 @@ static const long _huff_lengthlist__44c4_s_short[] = { static const static_codebook _huff_book__44c4_s_short = { 2, 100, - (long *)_huff_lengthlist__44c4_s_short, + (char *)_huff_lengthlist__44c4_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c5_s_long[] = { +static const char _huff_lengthlist__44c5_s_long[] = { 3, 8, 9,13,10,12,12,12,12,12, 6, 4, 6, 8, 6, 8, 10,10,11,12, 8, 5, 4,10, 4, 7, 8, 9,10,11,13, 8, 10, 8, 9, 9,11,12,13,14,10, 6, 4, 9, 3, 5, 6, 8, @@ -6990,7 +6989,7 @@ static const long _huff_lengthlist__44c5_s_long[] = { static const static_codebook _huff_book__44c5_s_long = { 2, 100, - (long *)_huff_lengthlist__44c5_s_long, + (char *)_huff_lengthlist__44c5_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -7002,7 +7001,7 @@ static const long _vq_quantlist__44c5_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c5_s_p1_0[] = { +static const char _vq_lengthlist__44c5_s_p1_0[] = { 2, 4, 4, 0, 0, 0, 0, 0, 0, 4, 7, 7, 0, 0, 0, 0, 0, 0, 4, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7418,7 +7417,7 @@ static const long _vq_lengthlist__44c5_s_p1_0[] = { static const static_codebook _44c5_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__44c5_s_p1_0, + (char *)_vq_lengthlist__44c5_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c5_s_p1_0, 0 @@ -7432,7 +7431,7 @@ static const long _vq_quantlist__44c5_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c5_s_p2_0[] = { +static const char _vq_lengthlist__44c5_s_p2_0[] = { 2, 4, 4, 0, 0, 0, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 8, 7, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 0, 0, 0, 8, 8, 0, 0, 0, 8, 7, 0, 0, 0,10,10, 0, 0, 0, 0, 0, @@ -7477,7 +7476,7 @@ static const long _vq_lengthlist__44c5_s_p2_0[] = { static const static_codebook _44c5_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c5_s_p2_0, + (char *)_vq_lengthlist__44c5_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c5_s_p2_0, 0 @@ -7491,7 +7490,7 @@ static const long _vq_quantlist__44c5_s_p3_0[] = { 4, }; -static const long _vq_lengthlist__44c5_s_p3_0[] = { +static const char _vq_lengthlist__44c5_s_p3_0[] = { 2, 4, 3, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7536,7 +7535,7 @@ static const long _vq_lengthlist__44c5_s_p3_0[] = { static const static_codebook _44c5_s_p3_0 = { 4, 625, - (long *)_vq_lengthlist__44c5_s_p3_0, + (char *)_vq_lengthlist__44c5_s_p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c5_s_p3_0, 0 @@ -7554,7 +7553,7 @@ static const long _vq_quantlist__44c5_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__44c5_s_p4_0[] = { +static const char _vq_lengthlist__44c5_s_p4_0[] = { 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 5, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, @@ -7565,7 +7564,7 @@ static const long _vq_lengthlist__44c5_s_p4_0[] = { static const static_codebook _44c5_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__44c5_s_p4_0, + (char *)_vq_lengthlist__44c5_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c5_s_p4_0, 0 @@ -7583,7 +7582,7 @@ static const long _vq_quantlist__44c5_s_p5_0[] = { 8, }; -static const long _vq_lengthlist__44c5_s_p5_0[] = { +static const char _vq_lengthlist__44c5_s_p5_0[] = { 2, 4, 3, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 6, 6, 7, 7, 7, 7, 9, 9, 0, 0, 0, 7, 6, 7, 7, 9, 9, 0, 0, 0, @@ -7594,7 +7593,7 @@ static const long _vq_lengthlist__44c5_s_p5_0[] = { static const static_codebook _44c5_s_p5_0 = { 2, 81, - (long *)_vq_lengthlist__44c5_s_p5_0, + (char *)_vq_lengthlist__44c5_s_p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c5_s_p5_0, 0 @@ -7620,7 +7619,7 @@ static const long _vq_quantlist__44c5_s_p6_0[] = { 16, }; -static const long _vq_lengthlist__44c5_s_p6_0[] = { +static const char _vq_lengthlist__44c5_s_p6_0[] = { 2, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10,10,11, 11, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,11, 12,12, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11, @@ -7644,7 +7643,7 @@ static const long _vq_lengthlist__44c5_s_p6_0[] = { static const static_codebook _44c5_s_p6_0 = { 2, 289, - (long *)_vq_lengthlist__44c5_s_p6_0, + (char *)_vq_lengthlist__44c5_s_p6_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c5_s_p6_0, 0 @@ -7656,7 +7655,7 @@ static const long _vq_quantlist__44c5_s_p7_0[] = { 2, }; -static const long _vq_lengthlist__44c5_s_p7_0[] = { +static const char _vq_lengthlist__44c5_s_p7_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, 10,11,11,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, @@ -7667,7 +7666,7 @@ static const long _vq_lengthlist__44c5_s_p7_0[] = { static const static_codebook _44c5_s_p7_0 = { 4, 81, - (long *)_vq_lengthlist__44c5_s_p7_0, + (char *)_vq_lengthlist__44c5_s_p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c5_s_p7_0, 0 @@ -7687,7 +7686,7 @@ static const long _vq_quantlist__44c5_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__44c5_s_p7_1[] = { +static const char _vq_lengthlist__44c5_s_p7_1[] = { 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, @@ -7700,7 +7699,7 @@ static const long _vq_lengthlist__44c5_s_p7_1[] = { static const static_codebook _44c5_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__44c5_s_p7_1, + (char *)_vq_lengthlist__44c5_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c5_s_p7_1, 0 @@ -7722,7 +7721,7 @@ static const long _vq_quantlist__44c5_s_p8_0[] = { 12, }; -static const long _vq_lengthlist__44c5_s_p8_0[] = { +static const char _vq_lengthlist__44c5_s_p8_0[] = { 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, 7, 7, 8, 8, 8, 9,10,10,10,10, 7, 5, 5, 7, 7, 8, 8, 9, 9,10,10,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -7738,7 +7737,7 @@ static const long _vq_lengthlist__44c5_s_p8_0[] = { static const static_codebook _44c5_s_p8_0 = { 2, 169, - (long *)_vq_lengthlist__44c5_s_p8_0, + (char *)_vq_lengthlist__44c5_s_p8_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c5_s_p8_0, 0 @@ -7752,14 +7751,14 @@ static const long _vq_quantlist__44c5_s_p8_1[] = { 4, }; -static const long _vq_lengthlist__44c5_s_p8_1[] = { +static const char _vq_lengthlist__44c5_s_p8_1[] = { 2, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 4, 5, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c5_s_p8_1 = { 2, 25, - (long *)_vq_lengthlist__44c5_s_p8_1, + (char *)_vq_lengthlist__44c5_s_p8_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c5_s_p8_1, 0 @@ -7783,7 +7782,7 @@ static const long _vq_quantlist__44c5_s_p9_0[] = { 14, }; -static const long _vq_lengthlist__44c5_s_p9_0[] = { +static const char _vq_lengthlist__44c5_s_p9_0[] = { 1, 3, 3,13,13,13,13,13,13,13,13,13,13,13,13, 4, 7, 7,13,13,13,13,13,13,13,13,13,13,13,13, 3, 8, 6,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, @@ -7803,7 +7802,7 @@ static const long _vq_lengthlist__44c5_s_p9_0[] = { static const static_codebook _44c5_s_p9_0 = { 2, 225, - (long *)_vq_lengthlist__44c5_s_p9_0, + (char *)_vq_lengthlist__44c5_s_p9_0, 1, -512522752, 1628852224, 4, 0, (long *)_vq_quantlist__44c5_s_p9_0, 0 @@ -7829,7 +7828,7 @@ static const long _vq_quantlist__44c5_s_p9_1[] = { 16, }; -static const long _vq_lengthlist__44c5_s_p9_1[] = { +static const char _vq_lengthlist__44c5_s_p9_1[] = { 1, 4, 4, 5, 5, 7, 7, 9, 8,10, 9,10,10,11,10,11, 11, 6, 5, 5, 7, 7, 8, 9,10,10,11,10,12,11,12,11, 13,12, 6, 5, 5, 7, 7, 9, 9,10,10,11,11,12,12,13, @@ -7853,7 +7852,7 @@ static const long _vq_lengthlist__44c5_s_p9_1[] = { static const static_codebook _44c5_s_p9_1 = { 2, 289, - (long *)_vq_lengthlist__44c5_s_p9_1, + (char *)_vq_lengthlist__44c5_s_p9_1, 1, -520814592, 1620377600, 5, 0, (long *)_vq_quantlist__44c5_s_p9_1, 0 @@ -7883,7 +7882,7 @@ static const long _vq_quantlist__44c5_s_p9_2[] = { 20, }; -static const long _vq_lengthlist__44c5_s_p9_2[] = { +static const char _vq_lengthlist__44c5_s_p9_2[] = { 3, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9,11, 5, 6, 7, 7, 8, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, 5, 5, 7, 7, 7, @@ -7916,13 +7915,13 @@ static const long _vq_lengthlist__44c5_s_p9_2[] = { static const static_codebook _44c5_s_p9_2 = { 2, 441, - (long *)_vq_lengthlist__44c5_s_p9_2, + (char *)_vq_lengthlist__44c5_s_p9_2, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__44c5_s_p9_2, 0 }; -static const long _huff_lengthlist__44c5_s_short[] = { +static const char _huff_lengthlist__44c5_s_short[] = { 5, 8,10,14,11,11,12,16,15,17, 5, 5, 7, 9, 7, 8, 10,13,17,17, 7, 5, 5,10, 5, 7, 8,11,13,15,10, 8, 10, 8, 8, 8,11,15,18,18, 8, 5, 5, 8, 3, 4, 6,10, @@ -7934,13 +7933,13 @@ static const long _huff_lengthlist__44c5_s_short[] = { static const static_codebook _huff_book__44c5_s_short = { 2, 100, - (long *)_huff_lengthlist__44c5_s_short, + (char *)_huff_lengthlist__44c5_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c6_s_long[] = { +static const char _huff_lengthlist__44c6_s_long[] = { 3, 8,11,13,14,14,13,13,16,14, 6, 3, 4, 7, 9, 9, 10,11,14,13,10, 4, 3, 5, 7, 7, 9,10,13,15,12, 7, 4, 4, 6, 6, 8,10,13,15,12, 8, 6, 6, 6, 6, 8,10, @@ -7952,7 +7951,7 @@ static const long _huff_lengthlist__44c6_s_long[] = { static const static_codebook _huff_book__44c6_s_long = { 2, 100, - (long *)_huff_lengthlist__44c6_s_long, + (char *)_huff_lengthlist__44c6_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -7964,7 +7963,7 @@ static const long _vq_quantlist__44c6_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c6_s_p1_0[] = { +static const char _vq_lengthlist__44c6_s_p1_0[] = { 1, 5, 5, 0, 5, 5, 0, 5, 5, 5, 8, 7, 0, 9, 9, 0, 9, 8, 5, 7, 8, 0, 9, 9, 0, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 9, 8, 0, 8, 8, 0, 8, 8, 5, 8, 9, @@ -7974,7 +7973,7 @@ static const long _vq_lengthlist__44c6_s_p1_0[] = { }; static const static_codebook _44c6_s_p1_0 = { 4, 81, - (long *)_vq_lengthlist__44c6_s_p1_0, + (char *)_vq_lengthlist__44c6_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c6_s_p1_0, 0 @@ -7988,7 +7987,7 @@ static const long _vq_quantlist__44c6_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c6_s_p2_0[] = { +static const char _vq_lengthlist__44c6_s_p2_0[] = { 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 7, 7, 9, 9, 0, 0, 0, 9, 9, 5, 7, 7, 9, 9, 0, 8, 8,10,10, 0, 8, 7,10, 9, 0,10,10,11,11, 0, 0, 0, @@ -8033,7 +8032,7 @@ static const long _vq_lengthlist__44c6_s_p2_0[] = { static const static_codebook _44c6_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c6_s_p2_0, + (char *)_vq_lengthlist__44c6_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c6_s_p2_0, 0 @@ -8051,7 +8050,7 @@ static const long _vq_quantlist__44c6_s_p3_0[] = { 8, }; -static const long _vq_lengthlist__44c6_s_p3_0[] = { +static const char _vq_lengthlist__44c6_s_p3_0[] = { 2, 3, 4, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, 9,10, 0, 4, 4, 6, 6, 7, 7,10, 9, 0, 5, 5, 7, 7, 8, 8,10,10, 0, 0, 0, 7, 6, 8, 8,10,10, 0, 0, 0, @@ -8062,7 +8061,7 @@ static const long _vq_lengthlist__44c6_s_p3_0[] = { static const static_codebook _44c6_s_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44c6_s_p3_0, + (char *)_vq_lengthlist__44c6_s_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c6_s_p3_0, 0 @@ -8088,7 +8087,7 @@ static const long _vq_quantlist__44c6_s_p4_0[] = { 16, }; -static const long _vq_lengthlist__44c6_s_p4_0[] = { +static const char _vq_lengthlist__44c6_s_p4_0[] = { 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9,10,10, 10, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10,10, 11,11, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10, @@ -8112,7 +8111,7 @@ static const long _vq_lengthlist__44c6_s_p4_0[] = { static const static_codebook _44c6_s_p4_0 = { 2, 289, - (long *)_vq_lengthlist__44c6_s_p4_0, + (char *)_vq_lengthlist__44c6_s_p4_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c6_s_p4_0, 0 @@ -8124,7 +8123,7 @@ static const long _vq_quantlist__44c6_s_p5_0[] = { 2, }; -static const long _vq_lengthlist__44c6_s_p5_0[] = { +static const char _vq_lengthlist__44c6_s_p5_0[] = { 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 6, 6, 9, 9,10,10, 10, 9, 4, 6, 6, 9,10, 9,10, 9,10, 6, 9, 9,10,12, 11,10,11,11, 7,10, 9,11,12,12,12,12,12, 7,10,10, @@ -8135,7 +8134,7 @@ static const long _vq_lengthlist__44c6_s_p5_0[] = { static const static_codebook _44c6_s_p5_0 = { 4, 81, - (long *)_vq_lengthlist__44c6_s_p5_0, + (char *)_vq_lengthlist__44c6_s_p5_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c6_s_p5_0, 0 @@ -8155,7 +8154,7 @@ static const long _vq_quantlist__44c6_s_p5_1[] = { 10, }; -static const long _vq_lengthlist__44c6_s_p5_1[] = { +static const char _vq_lengthlist__44c6_s_p5_1[] = { 3, 5, 4, 6, 6, 7, 7, 8, 8, 8, 8,11, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,11, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,11, 6, 6, 6, 6, 8, 8, 8, 8, 9, 9,11,11,11, 6, @@ -8168,7 +8167,7 @@ static const long _vq_lengthlist__44c6_s_p5_1[] = { static const static_codebook _44c6_s_p5_1 = { 2, 121, - (long *)_vq_lengthlist__44c6_s_p5_1, + (char *)_vq_lengthlist__44c6_s_p5_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c6_s_p5_1, 0 @@ -8190,7 +8189,7 @@ static const long _vq_quantlist__44c6_s_p6_0[] = { 12, }; -static const long _vq_lengthlist__44c6_s_p6_0[] = { +static const char _vq_lengthlist__44c6_s_p6_0[] = { 1, 4, 4, 6, 6, 8, 8, 8, 8,10, 9,10,10, 6, 5, 5, 7, 7, 9, 9, 9, 9,10,10,11,11, 6, 5, 5, 7, 7, 9, 9,10, 9,11,10,11,11, 0, 6, 6, 7, 7, 9, 9,10,10, @@ -8206,7 +8205,7 @@ static const long _vq_lengthlist__44c6_s_p6_0[] = { static const static_codebook _44c6_s_p6_0 = { 2, 169, - (long *)_vq_lengthlist__44c6_s_p6_0, + (char *)_vq_lengthlist__44c6_s_p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c6_s_p6_0, 0 @@ -8220,14 +8219,14 @@ static const long _vq_quantlist__44c6_s_p6_1[] = { 4, }; -static const long _vq_lengthlist__44c6_s_p6_1[] = { +static const char _vq_lengthlist__44c6_s_p6_1[] = { 3, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c6_s_p6_1 = { 2, 25, - (long *)_vq_lengthlist__44c6_s_p6_1, + (char *)_vq_lengthlist__44c6_s_p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c6_s_p6_1, 0 @@ -8249,7 +8248,7 @@ static const long _vq_quantlist__44c6_s_p7_0[] = { 12, }; -static const long _vq_lengthlist__44c6_s_p7_0[] = { +static const char _vq_lengthlist__44c6_s_p7_0[] = { 1, 4, 4, 6, 6, 8, 8, 8, 8,10,10,11,10, 6, 5, 5, 7, 7, 8, 8, 9, 9,10,10,12,11, 6, 5, 5, 7, 7, 8, 8, 9, 9,10,10,12,11,21, 7, 7, 7, 7, 9, 9,10,10, @@ -8265,7 +8264,7 @@ static const long _vq_lengthlist__44c6_s_p7_0[] = { static const static_codebook _44c6_s_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44c6_s_p7_0, + (char *)_vq_lengthlist__44c6_s_p7_0, 1, -523206656, 1618345984, 4, 0, (long *)_vq_quantlist__44c6_s_p7_0, 0 @@ -8285,7 +8284,7 @@ static const long _vq_quantlist__44c6_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__44c6_s_p7_1[] = { +static const char _vq_lengthlist__44c6_s_p7_1[] = { 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 9, 5, 5, 6, 6, 7, 7, 7, 7, 8, 7, 8, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 9, 6, 6, 7, 7, 7, 7, 8, 7, 7, 8, 9, 9, 9, 7, @@ -8298,7 +8297,7 @@ static const long _vq_lengthlist__44c6_s_p7_1[] = { static const static_codebook _44c6_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__44c6_s_p7_1, + (char *)_vq_lengthlist__44c6_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c6_s_p7_1, 0 @@ -8322,7 +8321,7 @@ static const long _vq_quantlist__44c6_s_p8_0[] = { 14, }; -static const long _vq_lengthlist__44c6_s_p8_0[] = { +static const char _vq_lengthlist__44c6_s_p8_0[] = { 1, 4, 4, 7, 7, 8, 8, 7, 7, 8, 7, 9, 8,10, 9, 6, 5, 5, 8, 8, 9, 9, 8, 8, 9, 9,11,10,11,10, 6, 5, 5, 8, 8, 9, 9, 8, 8, 9, 9,10,10,11,11,18, 8, 8, @@ -8342,7 +8341,7 @@ static const long _vq_lengthlist__44c6_s_p8_0[] = { static const static_codebook _44c6_s_p8_0 = { 2, 225, - (long *)_vq_lengthlist__44c6_s_p8_0, + (char *)_vq_lengthlist__44c6_s_p8_0, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__44c6_s_p8_0, 0 @@ -8372,7 +8371,7 @@ static const long _vq_quantlist__44c6_s_p8_1[] = { 20, }; -static const long _vq_lengthlist__44c6_s_p8_1[] = { +static const char _vq_lengthlist__44c6_s_p8_1[] = { 3, 5, 5, 6, 6, 7, 7, 7, 7, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, @@ -8405,7 +8404,7 @@ static const long _vq_lengthlist__44c6_s_p8_1[] = { static const static_codebook _44c6_s_p8_1 = { 2, 441, - (long *)_vq_lengthlist__44c6_s_p8_1, + (char *)_vq_lengthlist__44c6_s_p8_1, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__44c6_s_p8_1, 0 @@ -8427,7 +8426,7 @@ static const long _vq_quantlist__44c6_s_p9_0[] = { 12, }; -static const long _vq_lengthlist__44c6_s_p9_0[] = { +static const char _vq_lengthlist__44c6_s_p9_0[] = { 1, 3, 3,11,11,11,11,11,11,11,11,11,11, 4, 7, 7, 11,11,11,11,11,11,11,11,11,11, 5, 8, 9,11,11,11, 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, @@ -8443,7 +8442,7 @@ static const long _vq_lengthlist__44c6_s_p9_0[] = { static const static_codebook _44c6_s_p9_0 = { 2, 169, - (long *)_vq_lengthlist__44c6_s_p9_0, + (char *)_vq_lengthlist__44c6_s_p9_0, 1, -511845376, 1630791680, 4, 0, (long *)_vq_quantlist__44c6_s_p9_0, 0 @@ -8465,7 +8464,7 @@ static const long _vq_quantlist__44c6_s_p9_1[] = { 12, }; -static const long _vq_lengthlist__44c6_s_p9_1[] = { +static const char _vq_lengthlist__44c6_s_p9_1[] = { 1, 4, 4, 7, 7, 7, 7, 7, 6, 8, 8, 8, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8,10,10, 5, 6, 6, 8, 8, 9, 9, 8, 8,10,10,10,10,16, 9, 9, 9, 9, 9, 9, 9, 8, @@ -8481,7 +8480,7 @@ static const long _vq_lengthlist__44c6_s_p9_1[] = { static const static_codebook _44c6_s_p9_1 = { 2, 169, - (long *)_vq_lengthlist__44c6_s_p9_1, + (char *)_vq_lengthlist__44c6_s_p9_1, 1, -518889472, 1622704128, 4, 0, (long *)_vq_quantlist__44c6_s_p9_1, 0 @@ -8539,7 +8538,7 @@ static const long _vq_quantlist__44c6_s_p9_2[] = { 48, }; -static const long _vq_lengthlist__44c6_s_p9_2[] = { +static const char _vq_lengthlist__44c6_s_p9_2[] = { 2, 4, 3, 4, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -8548,13 +8547,13 @@ static const long _vq_lengthlist__44c6_s_p9_2[] = { static const static_codebook _44c6_s_p9_2 = { 1, 49, - (long *)_vq_lengthlist__44c6_s_p9_2, + (char *)_vq_lengthlist__44c6_s_p9_2, 1, -526909440, 1611661312, 6, 0, (long *)_vq_quantlist__44c6_s_p9_2, 0 }; -static const long _huff_lengthlist__44c6_s_short[] = { +static const char _huff_lengthlist__44c6_s_short[] = { 3, 9,11,11,13,14,19,17,17,19, 5, 4, 5, 8,10,10, 13,16,18,19, 7, 4, 4, 5, 8, 9,12,14,17,19, 8, 6, 5, 5, 7, 7,10,13,16,18,10, 8, 7, 6, 5, 5, 8,11, @@ -8566,13 +8565,13 @@ static const long _huff_lengthlist__44c6_s_short[] = { static const static_codebook _huff_book__44c6_s_short = { 2, 100, - (long *)_huff_lengthlist__44c6_s_short, + (char *)_huff_lengthlist__44c6_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c7_s_long[] = { +static const char _huff_lengthlist__44c7_s_long[] = { 3, 8,11,13,15,14,14,13,15,14, 6, 4, 5, 7, 9,10, 11,11,14,13,10, 4, 3, 5, 7, 8, 9,10,13,13,12, 7, 4, 4, 5, 6, 8, 9,12,14,13, 9, 6, 5, 5, 6, 8, 9, @@ -8584,7 +8583,7 @@ static const long _huff_lengthlist__44c7_s_long[] = { static const static_codebook _huff_book__44c7_s_long = { 2, 100, - (long *)_huff_lengthlist__44c7_s_long, + (char *)_huff_lengthlist__44c7_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -8596,7 +8595,7 @@ static const long _vq_quantlist__44c7_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c7_s_p1_0[] = { +static const char _vq_lengthlist__44c7_s_p1_0[] = { 1, 5, 5, 0, 5, 5, 0, 5, 5, 5, 8, 7, 0, 9, 9, 0, 9, 8, 5, 7, 8, 0, 9, 9, 0, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 9, 9, 0, 8, 8, 0, 8, 8, 5, 8, 9, @@ -8607,7 +8606,7 @@ static const long _vq_lengthlist__44c7_s_p1_0[] = { static const static_codebook _44c7_s_p1_0 = { 4, 81, - (long *)_vq_lengthlist__44c7_s_p1_0, + (char *)_vq_lengthlist__44c7_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c7_s_p1_0, 0 @@ -8621,7 +8620,7 @@ static const long _vq_quantlist__44c7_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c7_s_p2_0[] = { +static const char _vq_lengthlist__44c7_s_p2_0[] = { 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 7, 7, 9, 9, 0, 0, 0, 9, 9, 5, 7, 7, 9, 9, 0, 8, 8,10,10, 0, 8, 7,10, 9, 0,10,10,11,11, 0, 0, 0, @@ -8666,7 +8665,7 @@ static const long _vq_lengthlist__44c7_s_p2_0[] = { static const static_codebook _44c7_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c7_s_p2_0, + (char *)_vq_lengthlist__44c7_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c7_s_p2_0, 0 @@ -8684,7 +8683,7 @@ static const long _vq_quantlist__44c7_s_p3_0[] = { 8, }; -static const long _vq_lengthlist__44c7_s_p3_0[] = { +static const char _vq_lengthlist__44c7_s_p3_0[] = { 2, 4, 4, 5, 5, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 5, 5, 6, 6, 8, 8,10,10, 0, 0, 0, 6, 6, 8, 8,10,10, 0, 0, 0, @@ -8695,7 +8694,7 @@ static const long _vq_lengthlist__44c7_s_p3_0[] = { static const static_codebook _44c7_s_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44c7_s_p3_0, + (char *)_vq_lengthlist__44c7_s_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c7_s_p3_0, 0 @@ -8721,7 +8720,7 @@ static const long _vq_quantlist__44c7_s_p4_0[] = { 16, }; -static const long _vq_lengthlist__44c7_s_p4_0[] = { +static const char _vq_lengthlist__44c7_s_p4_0[] = { 3, 4, 4, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, 11, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11, 12,12, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, @@ -8745,7 +8744,7 @@ static const long _vq_lengthlist__44c7_s_p4_0[] = { static const static_codebook _44c7_s_p4_0 = { 2, 289, - (long *)_vq_lengthlist__44c7_s_p4_0, + (char *)_vq_lengthlist__44c7_s_p4_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c7_s_p4_0, 0 @@ -8757,7 +8756,7 @@ static const long _vq_quantlist__44c7_s_p5_0[] = { 2, }; -static const long _vq_lengthlist__44c7_s_p5_0[] = { +static const char _vq_lengthlist__44c7_s_p5_0[] = { 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 6, 7,10,10,10,10, 10, 9, 4, 6, 6,10,10,10,10, 9,10, 5,10,10, 9,11, 12,10,11,12, 7,10,10,11,12,12,12,12,12, 7,10,10, @@ -8768,7 +8767,7 @@ static const long _vq_lengthlist__44c7_s_p5_0[] = { static const static_codebook _44c7_s_p5_0 = { 4, 81, - (long *)_vq_lengthlist__44c7_s_p5_0, + (char *)_vq_lengthlist__44c7_s_p5_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c7_s_p5_0, 0 @@ -8788,7 +8787,7 @@ static const long _vq_quantlist__44c7_s_p5_1[] = { 10, }; -static const long _vq_lengthlist__44c7_s_p5_1[] = { +static const char _vq_lengthlist__44c7_s_p5_1[] = { 3, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,11, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,11, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,12, 5, 5, 6, 6, 7, 7, 9, 9, 9, 9,12,12,12, 6, @@ -8801,7 +8800,7 @@ static const long _vq_lengthlist__44c7_s_p5_1[] = { static const static_codebook _44c7_s_p5_1 = { 2, 121, - (long *)_vq_lengthlist__44c7_s_p5_1, + (char *)_vq_lengthlist__44c7_s_p5_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c7_s_p5_1, 0 @@ -8823,7 +8822,7 @@ static const long _vq_quantlist__44c7_s_p6_0[] = { 12, }; -static const long _vq_lengthlist__44c7_s_p6_0[] = { +static const char _vq_lengthlist__44c7_s_p6_0[] = { 1, 4, 4, 6, 6, 7, 7, 8, 7, 9, 8,10,10, 6, 5, 5, 7, 7, 8, 8, 9, 9, 9,10,11,11, 7, 5, 5, 7, 7, 8, 8, 9, 9,10,10,11,11, 0, 7, 7, 7, 7, 9, 8, 9, 9, @@ -8839,7 +8838,7 @@ static const long _vq_lengthlist__44c7_s_p6_0[] = { static const static_codebook _44c7_s_p6_0 = { 2, 169, - (long *)_vq_lengthlist__44c7_s_p6_0, + (char *)_vq_lengthlist__44c7_s_p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c7_s_p6_0, 0 @@ -8853,14 +8852,14 @@ static const long _vq_quantlist__44c7_s_p6_1[] = { 4, }; -static const long _vq_lengthlist__44c7_s_p6_1[] = { +static const char _vq_lengthlist__44c7_s_p6_1[] = { 3, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c7_s_p6_1 = { 2, 25, - (long *)_vq_lengthlist__44c7_s_p6_1, + (char *)_vq_lengthlist__44c7_s_p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c7_s_p6_1, 0 @@ -8882,7 +8881,7 @@ static const long _vq_quantlist__44c7_s_p7_0[] = { 12, }; -static const long _vq_lengthlist__44c7_s_p7_0[] = { +static const char _vq_lengthlist__44c7_s_p7_0[] = { 1, 4, 4, 6, 6, 7, 8, 9, 9,10,10,12,11, 6, 5, 5, 7, 7, 8, 8, 9,10,11,11,12,12, 7, 5, 5, 7, 7, 8, 8,10,10,11,11,12,12,20, 7, 7, 7, 7, 8, 9,10,10, @@ -8898,7 +8897,7 @@ static const long _vq_lengthlist__44c7_s_p7_0[] = { static const static_codebook _44c7_s_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44c7_s_p7_0, + (char *)_vq_lengthlist__44c7_s_p7_0, 1, -523206656, 1618345984, 4, 0, (long *)_vq_quantlist__44c7_s_p7_0, 0 @@ -8918,7 +8917,7 @@ static const long _vq_quantlist__44c7_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__44c7_s_p7_1[] = { +static const char _vq_lengthlist__44c7_s_p7_1[] = { 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 8, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, @@ -8931,7 +8930,7 @@ static const long _vq_lengthlist__44c7_s_p7_1[] = { static const static_codebook _44c7_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__44c7_s_p7_1, + (char *)_vq_lengthlist__44c7_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c7_s_p7_1, 0 @@ -8955,7 +8954,7 @@ static const long _vq_quantlist__44c7_s_p8_0[] = { 14, }; -static const long _vq_lengthlist__44c7_s_p8_0[] = { +static const char _vq_lengthlist__44c7_s_p8_0[] = { 1, 4, 4, 7, 7, 8, 8, 8, 7, 9, 8, 9, 9,10,10, 6, 5, 5, 7, 7, 9, 9, 8, 8,10, 9,11,10,12,11, 6, 5, 5, 8, 7, 9, 9, 8, 8,10,10,11,11,12,11,19, 8, 8, @@ -8975,7 +8974,7 @@ static const long _vq_lengthlist__44c7_s_p8_0[] = { static const static_codebook _44c7_s_p8_0 = { 2, 225, - (long *)_vq_lengthlist__44c7_s_p8_0, + (char *)_vq_lengthlist__44c7_s_p8_0, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__44c7_s_p8_0, 0 @@ -9005,7 +9004,7 @@ static const long _vq_quantlist__44c7_s_p8_1[] = { 20, }; -static const long _vq_lengthlist__44c7_s_p8_1[] = { +static const char _vq_lengthlist__44c7_s_p8_1[] = { 3, 5, 5, 7, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, @@ -9038,7 +9037,7 @@ static const long _vq_lengthlist__44c7_s_p8_1[] = { static const static_codebook _44c7_s_p8_1 = { 2, 441, - (long *)_vq_lengthlist__44c7_s_p8_1, + (char *)_vq_lengthlist__44c7_s_p8_1, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__44c7_s_p8_1, 0 @@ -9060,7 +9059,7 @@ static const long _vq_quantlist__44c7_s_p9_0[] = { 12, }; -static const long _vq_lengthlist__44c7_s_p9_0[] = { +static const char _vq_lengthlist__44c7_s_p9_0[] = { 1, 3, 3,11,11,11,11,11,11,11,11,11,11, 4, 6, 6, 11,11,11,11,11,11,11,11,11,11, 4, 7, 7,11,11,11, 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, @@ -9076,7 +9075,7 @@ static const long _vq_lengthlist__44c7_s_p9_0[] = { static const static_codebook _44c7_s_p9_0 = { 2, 169, - (long *)_vq_lengthlist__44c7_s_p9_0, + (char *)_vq_lengthlist__44c7_s_p9_0, 1, -511845376, 1630791680, 4, 0, (long *)_vq_quantlist__44c7_s_p9_0, 0 @@ -9098,7 +9097,7 @@ static const long _vq_quantlist__44c7_s_p9_1[] = { 12, }; -static const long _vq_lengthlist__44c7_s_p9_1[] = { +static const char _vq_lengthlist__44c7_s_p9_1[] = { 1, 4, 4, 7, 7, 7, 7, 7, 6, 8, 8, 8, 8, 6, 6, 6, 8, 8, 9, 8, 8, 7, 9, 8,11,10, 5, 6, 6, 8, 8, 9, 8, 8, 8,10, 9,11,11,16, 8, 8, 9, 8, 9, 9, 9, 8, @@ -9114,7 +9113,7 @@ static const long _vq_lengthlist__44c7_s_p9_1[] = { static const static_codebook _44c7_s_p9_1 = { 2, 169, - (long *)_vq_lengthlist__44c7_s_p9_1, + (char *)_vq_lengthlist__44c7_s_p9_1, 1, -518889472, 1622704128, 4, 0, (long *)_vq_quantlist__44c7_s_p9_1, 0 @@ -9172,7 +9171,7 @@ static const long _vq_quantlist__44c7_s_p9_2[] = { 48, }; -static const long _vq_lengthlist__44c7_s_p9_2[] = { +static const char _vq_lengthlist__44c7_s_p9_2[] = { 2, 4, 3, 4, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -9181,13 +9180,13 @@ static const long _vq_lengthlist__44c7_s_p9_2[] = { static const static_codebook _44c7_s_p9_2 = { 1, 49, - (long *)_vq_lengthlist__44c7_s_p9_2, + (char *)_vq_lengthlist__44c7_s_p9_2, 1, -526909440, 1611661312, 6, 0, (long *)_vq_quantlist__44c7_s_p9_2, 0 }; -static const long _huff_lengthlist__44c7_s_short[] = { +static const char _huff_lengthlist__44c7_s_short[] = { 4,11,12,14,15,15,17,17,18,18, 5, 6, 6, 8, 9,10, 13,17,18,19, 7, 5, 4, 6, 8, 9,11,15,19,19, 8, 6, 5, 5, 6, 7,11,14,16,17, 9, 7, 7, 6, 7, 7,10,13, @@ -9199,13 +9198,13 @@ static const long _huff_lengthlist__44c7_s_short[] = { static const static_codebook _huff_book__44c7_s_short = { 2, 100, - (long *)_huff_lengthlist__44c7_s_short, + (char *)_huff_lengthlist__44c7_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c8_s_long[] = { +static const char _huff_lengthlist__44c8_s_long[] = { 3, 8,12,13,14,14,14,13,14,14, 6, 4, 5, 8,10,10, 11,11,14,13, 9, 5, 4, 5, 7, 8, 9,10,13,13,12, 7, 5, 4, 5, 6, 8, 9,12,13,13, 9, 6, 5, 5, 5, 7, 9, @@ -9217,7 +9216,7 @@ static const long _huff_lengthlist__44c8_s_long[] = { static const static_codebook _huff_book__44c8_s_long = { 2, 100, - (long *)_huff_lengthlist__44c8_s_long, + (char *)_huff_lengthlist__44c8_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -9229,7 +9228,7 @@ static const long _vq_quantlist__44c8_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c8_s_p1_0[] = { +static const char _vq_lengthlist__44c8_s_p1_0[] = { 1, 5, 5, 0, 5, 5, 0, 5, 5, 5, 7, 7, 0, 9, 8, 0, 9, 8, 6, 7, 7, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 9, 8, 0, 8, 8, 0, 8, 8, 5, 8, 9, @@ -9240,7 +9239,7 @@ static const long _vq_lengthlist__44c8_s_p1_0[] = { static const static_codebook _44c8_s_p1_0 = { 4, 81, - (long *)_vq_lengthlist__44c8_s_p1_0, + (char *)_vq_lengthlist__44c8_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c8_s_p1_0, 0 @@ -9254,7 +9253,7 @@ static const long _vq_quantlist__44c8_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c8_s_p2_0[] = { +static const char _vq_lengthlist__44c8_s_p2_0[] = { 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 7, 7, 9, 9, 0, 0, 0, 9, 9, 5, 7, 7, 9, 9, 0, 8, 7,10, 9, 0, 8, 7,10, 9, 0,10,10,11,11, 0, 0, 0, @@ -9299,7 +9298,7 @@ static const long _vq_lengthlist__44c8_s_p2_0[] = { static const static_codebook _44c8_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c8_s_p2_0, + (char *)_vq_lengthlist__44c8_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c8_s_p2_0, 0 @@ -9317,7 +9316,7 @@ static const long _vq_quantlist__44c8_s_p3_0[] = { 8, }; -static const long _vq_lengthlist__44c8_s_p3_0[] = { +static const char _vq_lengthlist__44c8_s_p3_0[] = { 2, 4, 4, 5, 5, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 5, 5, 6, 6, 8, 8,10,10, 0, 0, 0, 6, 6, 8, 8,10,10, 0, 0, 0, @@ -9328,7 +9327,7 @@ static const long _vq_lengthlist__44c8_s_p3_0[] = { static const static_codebook _44c8_s_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44c8_s_p3_0, + (char *)_vq_lengthlist__44c8_s_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c8_s_p3_0, 0 @@ -9354,7 +9353,7 @@ static const long _vq_quantlist__44c8_s_p4_0[] = { 16, }; -static const long _vq_lengthlist__44c8_s_p4_0[] = { +static const char _vq_lengthlist__44c8_s_p4_0[] = { 3, 4, 4, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, 11, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 8,10,10,11,11, 11,11, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, @@ -9378,7 +9377,7 @@ static const long _vq_lengthlist__44c8_s_p4_0[] = { static const static_codebook _44c8_s_p4_0 = { 2, 289, - (long *)_vq_lengthlist__44c8_s_p4_0, + (char *)_vq_lengthlist__44c8_s_p4_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c8_s_p4_0, 0 @@ -9390,7 +9389,7 @@ static const long _vq_quantlist__44c8_s_p5_0[] = { 2, }; -static const long _vq_lengthlist__44c8_s_p5_0[] = { +static const char _vq_lengthlist__44c8_s_p5_0[] = { 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 7, 6,10,10,10,10, 10,10, 4, 6, 6,10,10,10,10, 9,10, 5,10,10, 9,11, 11,10,11,11, 7,10,10,11,12,12,12,12,12, 7,10,10, @@ -9401,7 +9400,7 @@ static const long _vq_lengthlist__44c8_s_p5_0[] = { static const static_codebook _44c8_s_p5_0 = { 4, 81, - (long *)_vq_lengthlist__44c8_s_p5_0, + (char *)_vq_lengthlist__44c8_s_p5_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c8_s_p5_0, 0 @@ -9421,7 +9420,7 @@ static const long _vq_quantlist__44c8_s_p5_1[] = { 10, }; -static const long _vq_lengthlist__44c8_s_p5_1[] = { +static const char _vq_lengthlist__44c8_s_p5_1[] = { 3, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,11, 4, 5, 6, 6, 7, 7, 8, 8, 8, 8,11, 5, 5, 6, 6, 7, 7, 8, 8, 8, 9,12, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,12,12,12, 6, @@ -9434,7 +9433,7 @@ static const long _vq_lengthlist__44c8_s_p5_1[] = { static const static_codebook _44c8_s_p5_1 = { 2, 121, - (long *)_vq_lengthlist__44c8_s_p5_1, + (char *)_vq_lengthlist__44c8_s_p5_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c8_s_p5_1, 0 @@ -9456,7 +9455,7 @@ static const long _vq_quantlist__44c8_s_p6_0[] = { 12, }; -static const long _vq_lengthlist__44c8_s_p6_0[] = { +static const char _vq_lengthlist__44c8_s_p6_0[] = { 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, 7, 7, 8, 8, 9, 9,10,10,11,11, 6, 5, 5, 7, 7, 8, 8, 9, 9,10,10,11,11, 0, 7, 7, 7, 7, 9, 9,10,10, @@ -9472,7 +9471,7 @@ static const long _vq_lengthlist__44c8_s_p6_0[] = { static const static_codebook _44c8_s_p6_0 = { 2, 169, - (long *)_vq_lengthlist__44c8_s_p6_0, + (char *)_vq_lengthlist__44c8_s_p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c8_s_p6_0, 0 @@ -9486,14 +9485,14 @@ static const long _vq_quantlist__44c8_s_p6_1[] = { 4, }; -static const long _vq_lengthlist__44c8_s_p6_1[] = { +static const char _vq_lengthlist__44c8_s_p6_1[] = { 3, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c8_s_p6_1 = { 2, 25, - (long *)_vq_lengthlist__44c8_s_p6_1, + (char *)_vq_lengthlist__44c8_s_p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c8_s_p6_1, 0 @@ -9515,7 +9514,7 @@ static const long _vq_quantlist__44c8_s_p7_0[] = { 12, }; -static const long _vq_lengthlist__44c8_s_p7_0[] = { +static const char _vq_lengthlist__44c8_s_p7_0[] = { 1, 4, 4, 6, 6, 8, 7, 9, 9,10,10,12,12, 6, 5, 5, 7, 7, 8, 8,10,10,11,11,12,12, 7, 5, 5, 7, 7, 8, 8,10,10,11,11,12,12,21, 7, 7, 7, 7, 8, 9,10,10, @@ -9531,7 +9530,7 @@ static const long _vq_lengthlist__44c8_s_p7_0[] = { static const static_codebook _44c8_s_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44c8_s_p7_0, + (char *)_vq_lengthlist__44c8_s_p7_0, 1, -523206656, 1618345984, 4, 0, (long *)_vq_quantlist__44c8_s_p7_0, 0 @@ -9551,7 +9550,7 @@ static const long _vq_quantlist__44c8_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__44c8_s_p7_1[] = { +static const char _vq_lengthlist__44c8_s_p7_1[] = { 4, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, @@ -9564,7 +9563,7 @@ static const long _vq_lengthlist__44c8_s_p7_1[] = { static const static_codebook _44c8_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__44c8_s_p7_1, + (char *)_vq_lengthlist__44c8_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c8_s_p7_1, 0 @@ -9588,7 +9587,7 @@ static const long _vq_quantlist__44c8_s_p8_0[] = { 14, }; -static const long _vq_lengthlist__44c8_s_p8_0[] = { +static const char _vq_lengthlist__44c8_s_p8_0[] = { 1, 4, 4, 7, 6, 8, 8, 8, 7, 9, 8,10,10,11,10, 6, 5, 5, 7, 7, 9, 9, 8, 8,10,10,11,11,12,11, 6, 5, 5, 7, 7, 9, 9, 9, 9,10,10,11,11,12,12,20, 8, 8, @@ -9608,7 +9607,7 @@ static const long _vq_lengthlist__44c8_s_p8_0[] = { static const static_codebook _44c8_s_p8_0 = { 2, 225, - (long *)_vq_lengthlist__44c8_s_p8_0, + (char *)_vq_lengthlist__44c8_s_p8_0, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__44c8_s_p8_0, 0 @@ -9638,7 +9637,7 @@ static const long _vq_quantlist__44c8_s_p8_1[] = { 20, }; -static const long _vq_lengthlist__44c8_s_p8_1[] = { +static const char _vq_lengthlist__44c8_s_p8_1[] = { 4, 5, 5, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, @@ -9671,7 +9670,7 @@ static const long _vq_lengthlist__44c8_s_p8_1[] = { static const static_codebook _44c8_s_p8_1 = { 2, 441, - (long *)_vq_lengthlist__44c8_s_p8_1, + (char *)_vq_lengthlist__44c8_s_p8_1, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__44c8_s_p8_1, 0 @@ -9697,7 +9696,7 @@ static const long _vq_quantlist__44c8_s_p9_0[] = { 16, }; -static const long _vq_lengthlist__44c8_s_p9_0[] = { +static const char _vq_lengthlist__44c8_s_p9_0[] = { 1, 4, 3,11,11,11,11,11,11,11,11,11,11,11,11,11, 11, 4, 7, 7,11,11,11,11,11,11,11,11,11,11,11,11, 11,11, 4, 8,11,11,11,11,11,11,11,11,11,11,11,11, @@ -9721,7 +9720,7 @@ static const long _vq_lengthlist__44c8_s_p9_0[] = { static const static_codebook _44c8_s_p9_0 = { 2, 289, - (long *)_vq_lengthlist__44c8_s_p9_0, + (char *)_vq_lengthlist__44c8_s_p9_0, 1, -509798400, 1631393792, 5, 0, (long *)_vq_quantlist__44c8_s_p9_0, 0 @@ -9749,7 +9748,7 @@ static const long _vq_quantlist__44c8_s_p9_1[] = { 18, }; -static const long _vq_lengthlist__44c8_s_p9_1[] = { +static const char _vq_lengthlist__44c8_s_p9_1[] = { 1, 4, 4, 7, 6, 7, 7, 7, 7, 8, 8, 9, 9,10,10,10, 10,11,11, 6, 6, 6, 8, 8, 9, 8, 8, 7,10, 8,11,10, 12,11,12,12,13,13, 5, 5, 6, 8, 8, 9, 9, 8, 8,10, @@ -9777,7 +9776,7 @@ static const long _vq_lengthlist__44c8_s_p9_1[] = { static const static_codebook _44c8_s_p9_1 = { 2, 361, - (long *)_vq_lengthlist__44c8_s_p9_1, + (char *)_vq_lengthlist__44c8_s_p9_1, 1, -518287360, 1622704128, 5, 0, (long *)_vq_quantlist__44c8_s_p9_1, 0 @@ -9835,7 +9834,7 @@ static const long _vq_quantlist__44c8_s_p9_2[] = { 48, }; -static const long _vq_lengthlist__44c8_s_p9_2[] = { +static const char _vq_lengthlist__44c8_s_p9_2[] = { 2, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -9844,13 +9843,13 @@ static const long _vq_lengthlist__44c8_s_p9_2[] = { static const static_codebook _44c8_s_p9_2 = { 1, 49, - (long *)_vq_lengthlist__44c8_s_p9_2, + (char *)_vq_lengthlist__44c8_s_p9_2, 1, -526909440, 1611661312, 6, 0, (long *)_vq_quantlist__44c8_s_p9_2, 0 }; -static const long _huff_lengthlist__44c8_s_short[] = { +static const char _huff_lengthlist__44c8_s_short[] = { 4,11,13,14,15,15,18,17,19,17, 5, 6, 8, 9,10,10, 12,15,19,19, 6, 6, 6, 6, 8, 8,11,14,18,19, 8, 6, 5, 4, 6, 7,10,13,16,17, 9, 7, 6, 5, 6, 7, 9,12, @@ -9862,13 +9861,13 @@ static const long _huff_lengthlist__44c8_s_short[] = { static const static_codebook _huff_book__44c8_s_short = { 2, 100, - (long *)_huff_lengthlist__44c8_s_short, + (char *)_huff_lengthlist__44c8_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c9_s_long[] = { +static const char _huff_lengthlist__44c9_s_long[] = { 3, 8,12,14,15,15,15,13,15,15, 6, 5, 8,10,12,12, 13,12,14,13,10, 6, 5, 6, 8, 9,11,11,13,13,13, 8, 5, 4, 5, 6, 8,10,11,13,14,10, 7, 5, 4, 5, 7, 9, @@ -9880,7 +9879,7 @@ static const long _huff_lengthlist__44c9_s_long[] = { static const static_codebook _huff_book__44c9_s_long = { 2, 100, - (long *)_huff_lengthlist__44c9_s_long, + (char *)_huff_lengthlist__44c9_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -9892,7 +9891,7 @@ static const long _vq_quantlist__44c9_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c9_s_p1_0[] = { +static const char _vq_lengthlist__44c9_s_p1_0[] = { 1, 5, 5, 0, 5, 5, 0, 5, 5, 6, 8, 8, 0, 9, 8, 0, 9, 8, 6, 8, 8, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 8, 0, 7, 7, 0, 8, 8, 5, 8, 8, @@ -9903,7 +9902,7 @@ static const long _vq_lengthlist__44c9_s_p1_0[] = { static const static_codebook _44c9_s_p1_0 = { 4, 81, - (long *)_vq_lengthlist__44c9_s_p1_0, + (char *)_vq_lengthlist__44c9_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c9_s_p1_0, 0 @@ -9917,7 +9916,7 @@ static const long _vq_quantlist__44c9_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c9_s_p2_0[] = { +static const char _vq_lengthlist__44c9_s_p2_0[] = { 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 7, 7, 9, 9, 0, 0, 0, 9, 9, 6, 7, 7, 9, 8, 0, 8, 8, 9, 9, 0, 8, 7, 9, 9, 0, 9,10,10,10, 0, 0, 0, @@ -9962,7 +9961,7 @@ static const long _vq_lengthlist__44c9_s_p2_0[] = { static const static_codebook _44c9_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c9_s_p2_0, + (char *)_vq_lengthlist__44c9_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c9_s_p2_0, 0 @@ -9980,7 +9979,7 @@ static const long _vq_quantlist__44c9_s_p3_0[] = { 8, }; -static const long _vq_lengthlist__44c9_s_p3_0[] = { +static const char _vq_lengthlist__44c9_s_p3_0[] = { 3, 4, 4, 5, 5, 6, 6, 8, 8, 0, 4, 4, 5, 5, 6, 7, 8, 8, 0, 4, 4, 5, 5, 7, 7, 8, 8, 0, 5, 5, 6, 6, 7, 7, 9, 9, 0, 0, 0, 6, 6, 7, 7, 9, 9, 0, 0, 0, @@ -9991,7 +9990,7 @@ static const long _vq_lengthlist__44c9_s_p3_0[] = { static const static_codebook _44c9_s_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44c9_s_p3_0, + (char *)_vq_lengthlist__44c9_s_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c9_s_p3_0, 0 @@ -10017,7 +10016,7 @@ static const long _vq_quantlist__44c9_s_p4_0[] = { 16, }; -static const long _vq_lengthlist__44c9_s_p4_0[] = { +static const char _vq_lengthlist__44c9_s_p4_0[] = { 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,10, 10, 0, 5, 4, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 11,11, 0, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10, @@ -10041,7 +10040,7 @@ static const long _vq_lengthlist__44c9_s_p4_0[] = { static const static_codebook _44c9_s_p4_0 = { 2, 289, - (long *)_vq_lengthlist__44c9_s_p4_0, + (char *)_vq_lengthlist__44c9_s_p4_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c9_s_p4_0, 0 @@ -10053,7 +10052,7 @@ static const long _vq_quantlist__44c9_s_p5_0[] = { 2, }; -static const long _vq_lengthlist__44c9_s_p5_0[] = { +static const char _vq_lengthlist__44c9_s_p5_0[] = { 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 7, 6, 9,10,10,10, 10, 9, 4, 6, 7, 9,10,10,10, 9,10, 5, 9, 9, 9,11, 11,10,11,11, 7,10, 9,11,12,11,12,12,12, 7, 9,10, @@ -10064,7 +10063,7 @@ static const long _vq_lengthlist__44c9_s_p5_0[] = { static const static_codebook _44c9_s_p5_0 = { 4, 81, - (long *)_vq_lengthlist__44c9_s_p5_0, + (char *)_vq_lengthlist__44c9_s_p5_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c9_s_p5_0, 0 @@ -10084,7 +10083,7 @@ static const long _vq_quantlist__44c9_s_p5_1[] = { 10, }; -static const long _vq_lengthlist__44c9_s_p5_1[] = { +static const char _vq_lengthlist__44c9_s_p5_1[] = { 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7,11, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8,11, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8,11, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,11,11,11, 6, @@ -10097,7 +10096,7 @@ static const long _vq_lengthlist__44c9_s_p5_1[] = { static const static_codebook _44c9_s_p5_1 = { 2, 121, - (long *)_vq_lengthlist__44c9_s_p5_1, + (char *)_vq_lengthlist__44c9_s_p5_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c9_s_p5_1, 0 @@ -10119,7 +10118,7 @@ static const long _vq_quantlist__44c9_s_p6_0[] = { 12, }; -static const long _vq_lengthlist__44c9_s_p6_0[] = { +static const char _vq_lengthlist__44c9_s_p6_0[] = { 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 5, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10, 6, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10, 0, 6, 6, 7, 7, 8, 8, 9, 9, @@ -10135,7 +10134,7 @@ static const long _vq_lengthlist__44c9_s_p6_0[] = { static const static_codebook _44c9_s_p6_0 = { 2, 169, - (long *)_vq_lengthlist__44c9_s_p6_0, + (char *)_vq_lengthlist__44c9_s_p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c9_s_p6_0, 0 @@ -10149,14 +10148,14 @@ static const long _vq_quantlist__44c9_s_p6_1[] = { 4, }; -static const long _vq_lengthlist__44c9_s_p6_1[] = { +static const char _vq_lengthlist__44c9_s_p6_1[] = { 4, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44c9_s_p6_1 = { 2, 25, - (long *)_vq_lengthlist__44c9_s_p6_1, + (char *)_vq_lengthlist__44c9_s_p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c9_s_p6_1, 0 @@ -10178,7 +10177,7 @@ static const long _vq_quantlist__44c9_s_p7_0[] = { 12, }; -static const long _vq_lengthlist__44c9_s_p7_0[] = { +static const char _vq_lengthlist__44c9_s_p7_0[] = { 2, 4, 4, 6, 6, 7, 7, 8, 8,10,10,11,11, 6, 4, 4, 6, 6, 8, 8, 9, 9,10,10,12,12, 6, 4, 5, 6, 6, 8, 8, 9, 9,10,10,12,12,20, 6, 6, 6, 6, 8, 8, 9,10, @@ -10194,7 +10193,7 @@ static const long _vq_lengthlist__44c9_s_p7_0[] = { static const static_codebook _44c9_s_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44c9_s_p7_0, + (char *)_vq_lengthlist__44c9_s_p7_0, 1, -523206656, 1618345984, 4, 0, (long *)_vq_quantlist__44c9_s_p7_0, 0 @@ -10214,7 +10213,7 @@ static const long _vq_quantlist__44c9_s_p7_1[] = { 10, }; -static const long _vq_lengthlist__44c9_s_p7_1[] = { +static const char _vq_lengthlist__44c9_s_p7_1[] = { 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 6, @@ -10227,7 +10226,7 @@ static const long _vq_lengthlist__44c9_s_p7_1[] = { static const static_codebook _44c9_s_p7_1 = { 2, 121, - (long *)_vq_lengthlist__44c9_s_p7_1, + (char *)_vq_lengthlist__44c9_s_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c9_s_p7_1, 0 @@ -10251,7 +10250,7 @@ static const long _vq_quantlist__44c9_s_p8_0[] = { 14, }; -static const long _vq_lengthlist__44c9_s_p8_0[] = { +static const char _vq_lengthlist__44c9_s_p8_0[] = { 1, 4, 4, 7, 6, 8, 8, 8, 8, 9, 9,10,10,11,10, 6, 5, 5, 7, 7, 9, 9, 8, 9,10,10,11,11,12,12, 6, 5, 5, 7, 7, 9, 9, 9, 9,10,10,11,11,12,12,21, 7, 8, @@ -10271,7 +10270,7 @@ static const long _vq_lengthlist__44c9_s_p8_0[] = { static const static_codebook _44c9_s_p8_0 = { 2, 225, - (long *)_vq_lengthlist__44c9_s_p8_0, + (char *)_vq_lengthlist__44c9_s_p8_0, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__44c9_s_p8_0, 0 @@ -10301,7 +10300,7 @@ static const long _vq_quantlist__44c9_s_p8_1[] = { 20, }; -static const long _vq_lengthlist__44c9_s_p8_1[] = { +static const char _vq_lengthlist__44c9_s_p8_1[] = { 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, @@ -10334,7 +10333,7 @@ static const long _vq_lengthlist__44c9_s_p8_1[] = { static const static_codebook _44c9_s_p8_1 = { 2, 441, - (long *)_vq_lengthlist__44c9_s_p8_1, + (char *)_vq_lengthlist__44c9_s_p8_1, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__44c9_s_p8_1, 0 @@ -10362,7 +10361,7 @@ static const long _vq_quantlist__44c9_s_p9_0[] = { 18, }; -static const long _vq_lengthlist__44c9_s_p9_0[] = { +static const char _vq_lengthlist__44c9_s_p9_0[] = { 1, 4, 3,12,12,12,12,12,12,12,12,12,12,12,12,12, 12,12,12, 4, 5, 6,12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12, 4, 6, 6,12,12,12,12,12,12,12, @@ -10390,7 +10389,7 @@ static const long _vq_lengthlist__44c9_s_p9_0[] = { static const static_codebook _44c9_s_p9_0 = { 2, 361, - (long *)_vq_lengthlist__44c9_s_p9_0, + (char *)_vq_lengthlist__44c9_s_p9_0, 1, -508535424, 1631393792, 5, 0, (long *)_vq_quantlist__44c9_s_p9_0, 0 @@ -10418,7 +10417,7 @@ static const long _vq_quantlist__44c9_s_p9_1[] = { 18, }; -static const long _vq_lengthlist__44c9_s_p9_1[] = { +static const char _vq_lengthlist__44c9_s_p9_1[] = { 1, 4, 4, 7, 7, 7, 7, 8, 7, 9, 8, 9, 9,10,10,11, 11,11,11, 6, 5, 5, 8, 8, 9, 9, 9, 8,10, 9,11,10, 12,12,13,12,13,13, 5, 5, 5, 8, 8, 9, 9, 9, 9,10, @@ -10446,7 +10445,7 @@ static const long _vq_lengthlist__44c9_s_p9_1[] = { static const static_codebook _44c9_s_p9_1 = { 2, 361, - (long *)_vq_lengthlist__44c9_s_p9_1, + (char *)_vq_lengthlist__44c9_s_p9_1, 1, -518287360, 1622704128, 5, 0, (long *)_vq_quantlist__44c9_s_p9_1, 0 @@ -10504,7 +10503,7 @@ static const long _vq_quantlist__44c9_s_p9_2[] = { 48, }; -static const long _vq_lengthlist__44c9_s_p9_2[] = { +static const char _vq_lengthlist__44c9_s_p9_2[] = { 2, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -10513,13 +10512,13 @@ static const long _vq_lengthlist__44c9_s_p9_2[] = { static const static_codebook _44c9_s_p9_2 = { 1, 49, - (long *)_vq_lengthlist__44c9_s_p9_2, + (char *)_vq_lengthlist__44c9_s_p9_2, 1, -526909440, 1611661312, 6, 0, (long *)_vq_quantlist__44c9_s_p9_2, 0 }; -static const long _huff_lengthlist__44c9_s_short[] = { +static const char _huff_lengthlist__44c9_s_short[] = { 5,13,18,16,17,17,19,18,19,19, 5, 7,10,11,12,12, 13,16,17,18, 6, 6, 7, 7, 9, 9,10,14,17,19, 8, 7, 6, 5, 6, 7, 9,12,19,17, 8, 7, 7, 6, 5, 6, 8,11, @@ -10531,13 +10530,13 @@ static const long _huff_lengthlist__44c9_s_short[] = { static const static_codebook _huff_book__44c9_s_short = { 2, 100, - (long *)_huff_lengthlist__44c9_s_short, + (char *)_huff_lengthlist__44c9_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c0_s_long[] = { +static const char _huff_lengthlist__44c0_s_long[] = { 5, 4, 8, 9, 8, 9,10,12,15, 4, 1, 5, 5, 6, 8,11, 12,12, 8, 5, 8, 9, 9,11,13,12,12, 9, 5, 8, 5, 7, 9,12,13,13, 8, 6, 8, 7, 7, 9,11,11,11, 9, 7, 9, @@ -10548,7 +10547,7 @@ static const long _huff_lengthlist__44c0_s_long[] = { static const static_codebook _huff_book__44c0_s_long = { 2, 81, - (long *)_huff_lengthlist__44c0_s_long, + (char *)_huff_lengthlist__44c0_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -10560,7 +10559,7 @@ static const long _vq_quantlist__44c0_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c0_s_p1_0[] = { +static const char _vq_lengthlist__44c0_s_p1_0[] = { 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -10976,7 +10975,7 @@ static const long _vq_lengthlist__44c0_s_p1_0[] = { static const static_codebook _44c0_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__44c0_s_p1_0, + (char *)_vq_lengthlist__44c0_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c0_s_p1_0, 0 @@ -10990,7 +10989,7 @@ static const long _vq_quantlist__44c0_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c0_s_p2_0[] = { +static const char _vq_lengthlist__44c0_s_p2_0[] = { 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -11035,7 +11034,7 @@ static const long _vq_lengthlist__44c0_s_p2_0[] = { static const static_codebook _44c0_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c0_s_p2_0, + (char *)_vq_lengthlist__44c0_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c0_s_p2_0, 0 @@ -11053,7 +11052,7 @@ static const long _vq_quantlist__44c0_s_p3_0[] = { 8, }; -static const long _vq_lengthlist__44c0_s_p3_0[] = { +static const char _vq_lengthlist__44c0_s_p3_0[] = { 1, 3, 2, 8, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, @@ -11064,7 +11063,7 @@ static const long _vq_lengthlist__44c0_s_p3_0[] = { static const static_codebook _44c0_s_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44c0_s_p3_0, + (char *)_vq_lengthlist__44c0_s_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c0_s_p3_0, 0 @@ -11082,7 +11081,7 @@ static const long _vq_quantlist__44c0_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__44c0_s_p4_0[] = { +static const char _vq_lengthlist__44c0_s_p4_0[] = { 1, 3, 3, 6, 6, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 7, 7, 7, 8, 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, @@ -11093,7 +11092,7 @@ static const long _vq_lengthlist__44c0_s_p4_0[] = { static const static_codebook _44c0_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__44c0_s_p4_0, + (char *)_vq_lengthlist__44c0_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c0_s_p4_0, 0 @@ -11119,7 +11118,7 @@ static const long _vq_quantlist__44c0_s_p5_0[] = { 16, }; -static const long _vq_lengthlist__44c0_s_p5_0[] = { +static const char _vq_lengthlist__44c0_s_p5_0[] = { 1, 4, 3, 6, 6, 8, 7, 8, 8, 8, 8, 9, 9,10,10,11, 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9, 9,10,10,10, 11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, @@ -11143,7 +11142,7 @@ static const long _vq_lengthlist__44c0_s_p5_0[] = { static const static_codebook _44c0_s_p5_0 = { 2, 289, - (long *)_vq_lengthlist__44c0_s_p5_0, + (char *)_vq_lengthlist__44c0_s_p5_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c0_s_p5_0, 0 @@ -11155,7 +11154,7 @@ static const long _vq_quantlist__44c0_s_p6_0[] = { 2, }; -static const long _vq_lengthlist__44c0_s_p6_0[] = { +static const char _vq_lengthlist__44c0_s_p6_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,10, 9, 9, 4, 6, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, 11,12,10,11, 6, 9, 9,11,10,11,11,10,10, 6, 9, 9, @@ -11166,7 +11165,7 @@ static const long _vq_lengthlist__44c0_s_p6_0[] = { static const static_codebook _44c0_s_p6_0 = { 4, 81, - (long *)_vq_lengthlist__44c0_s_p6_0, + (char *)_vq_lengthlist__44c0_s_p6_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c0_s_p6_0, 0 @@ -11186,7 +11185,7 @@ static const long _vq_quantlist__44c0_s_p6_1[] = { 10, }; -static const long _vq_lengthlist__44c0_s_p6_1[] = { +static const char _vq_lengthlist__44c0_s_p6_1[] = { 2, 3, 3, 6, 6, 7, 7, 7, 7, 7, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, @@ -11199,7 +11198,7 @@ static const long _vq_lengthlist__44c0_s_p6_1[] = { static const static_codebook _44c0_s_p6_1 = { 2, 121, - (long *)_vq_lengthlist__44c0_s_p6_1, + (char *)_vq_lengthlist__44c0_s_p6_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c0_s_p6_1, 0 @@ -11221,7 +11220,7 @@ static const long _vq_quantlist__44c0_s_p7_0[] = { 12, }; -static const long _vq_lengthlist__44c0_s_p7_0[] = { +static const char _vq_lengthlist__44c0_s_p7_0[] = { 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -11237,7 +11236,7 @@ static const long _vq_lengthlist__44c0_s_p7_0[] = { static const static_codebook _44c0_s_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44c0_s_p7_0, + (char *)_vq_lengthlist__44c0_s_p7_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c0_s_p7_0, 0 @@ -11251,14 +11250,14 @@ static const long _vq_quantlist__44c0_s_p7_1[] = { 4, }; -static const long _vq_lengthlist__44c0_s_p7_1[] = { +static const char _vq_lengthlist__44c0_s_p7_1[] = { 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c0_s_p7_1 = { 2, 25, - (long *)_vq_lengthlist__44c0_s_p7_1, + (char *)_vq_lengthlist__44c0_s_p7_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c0_s_p7_1, 0 @@ -11272,7 +11271,7 @@ static const long _vq_quantlist__44c0_s_p8_0[] = { 4, }; -static const long _vq_lengthlist__44c0_s_p8_0[] = { +static const char _vq_lengthlist__44c0_s_p8_0[] = { 1, 5, 5,10,10, 6, 9, 8,10,10, 6,10, 9,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, @@ -11317,7 +11316,7 @@ static const long _vq_lengthlist__44c0_s_p8_0[] = { static const static_codebook _44c0_s_p8_0 = { 4, 625, - (long *)_vq_lengthlist__44c0_s_p8_0, + (char *)_vq_lengthlist__44c0_s_p8_0, 1, -518283264, 1627103232, 3, 0, (long *)_vq_quantlist__44c0_s_p8_0, 0 @@ -11339,7 +11338,7 @@ static const long _vq_quantlist__44c0_s_p8_1[] = { 12, }; -static const long _vq_lengthlist__44c0_s_p8_1[] = { +static const char _vq_lengthlist__44c0_s_p8_1[] = { 1, 4, 4, 6, 6, 7, 7, 9, 9,11,12,13,12, 6, 5, 5, 7, 7, 8, 8,10, 9,12,12,12,12, 6, 5, 5, 7, 7, 8, 8,10, 9,12,11,11,13,16, 7, 7, 8, 8, 9, 9,10,10, @@ -11355,7 +11354,7 @@ static const long _vq_lengthlist__44c0_s_p8_1[] = { static const static_codebook _44c0_s_p8_1 = { 2, 169, - (long *)_vq_lengthlist__44c0_s_p8_1, + (char *)_vq_lengthlist__44c0_s_p8_1, 1, -522616832, 1620115456, 4, 0, (long *)_vq_quantlist__44c0_s_p8_1, 0 @@ -11381,7 +11380,7 @@ static const long _vq_quantlist__44c0_s_p8_2[] = { 16, }; -static const long _vq_lengthlist__44c0_s_p8_2[] = { +static const char _vq_lengthlist__44c0_s_p8_2[] = { 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8,10,10,10, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, @@ -11405,13 +11404,13 @@ static const long _vq_lengthlist__44c0_s_p8_2[] = { static const static_codebook _44c0_s_p8_2 = { 2, 289, - (long *)_vq_lengthlist__44c0_s_p8_2, + (char *)_vq_lengthlist__44c0_s_p8_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c0_s_p8_2, 0 }; -static const long _huff_lengthlist__44c0_s_short[] = { +static const char _huff_lengthlist__44c0_s_short[] = { 9, 8,12,11,12,13,14,14,16, 6, 1, 5, 6, 6, 9,12, 14,17, 9, 4, 5, 9, 7, 9,13,15,16, 8, 5, 8, 6, 8, 10,13,17,17, 9, 6, 7, 7, 8, 9,13,15,17,11, 8, 9, @@ -11422,13 +11421,13 @@ static const long _huff_lengthlist__44c0_s_short[] = { static const static_codebook _huff_book__44c0_s_short = { 2, 81, - (long *)_huff_lengthlist__44c0_s_short, + (char *)_huff_lengthlist__44c0_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c0_sm_long[] = { +static const char _huff_lengthlist__44c0_sm_long[] = { 5, 4, 9,10, 9,10,11,12,13, 4, 1, 5, 7, 7, 9,11, 12,14, 8, 5, 7, 9, 8,10,13,13,13,10, 7, 9, 4, 6, 7,10,12,14, 9, 6, 7, 6, 6, 7,10,12,12, 9, 8, 9, @@ -11439,7 +11438,7 @@ static const long _huff_lengthlist__44c0_sm_long[] = { static const static_codebook _huff_book__44c0_sm_long = { 2, 81, - (long *)_huff_lengthlist__44c0_sm_long, + (char *)_huff_lengthlist__44c0_sm_long, 0, 0, 0, 0, 0, NULL, 0 @@ -11451,7 +11450,7 @@ static const long _vq_quantlist__44c0_sm_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c0_sm_p1_0[] = { +static const char _vq_lengthlist__44c0_sm_p1_0[] = { 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -11867,7 +11866,7 @@ static const long _vq_lengthlist__44c0_sm_p1_0[] = { static const static_codebook _44c0_sm_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__44c0_sm_p1_0, + (char *)_vq_lengthlist__44c0_sm_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c0_sm_p1_0, 0 @@ -11881,7 +11880,7 @@ static const long _vq_quantlist__44c0_sm_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c0_sm_p2_0[] = { +static const char _vq_lengthlist__44c0_sm_p2_0[] = { 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -11926,7 +11925,7 @@ static const long _vq_lengthlist__44c0_sm_p2_0[] = { static const static_codebook _44c0_sm_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c0_sm_p2_0, + (char *)_vq_lengthlist__44c0_sm_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c0_sm_p2_0, 0 @@ -11944,7 +11943,7 @@ static const long _vq_quantlist__44c0_sm_p3_0[] = { 8, }; -static const long _vq_lengthlist__44c0_sm_p3_0[] = { +static const char _vq_lengthlist__44c0_sm_p3_0[] = { 1, 3, 3, 7, 7, 0, 0, 0, 0, 0, 5, 4, 7, 7, 0, 0, 0, 0, 0, 5, 5, 7, 7, 0, 0, 0, 0, 0, 6, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, @@ -11955,7 +11954,7 @@ static const long _vq_lengthlist__44c0_sm_p3_0[] = { static const static_codebook _44c0_sm_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44c0_sm_p3_0, + (char *)_vq_lengthlist__44c0_sm_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c0_sm_p3_0, 0 @@ -11973,7 +11972,7 @@ static const long _vq_quantlist__44c0_sm_p4_0[] = { 8, }; -static const long _vq_lengthlist__44c0_sm_p4_0[] = { +static const char _vq_lengthlist__44c0_sm_p4_0[] = { 1, 4, 3, 6, 6, 7, 7, 9, 9, 0, 5, 5, 7, 7, 8, 7, 9, 9, 0, 5, 5, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, @@ -11984,7 +11983,7 @@ static const long _vq_lengthlist__44c0_sm_p4_0[] = { static const static_codebook _44c0_sm_p4_0 = { 2, 81, - (long *)_vq_lengthlist__44c0_sm_p4_0, + (char *)_vq_lengthlist__44c0_sm_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c0_sm_p4_0, 0 @@ -12010,7 +12009,7 @@ static const long _vq_quantlist__44c0_sm_p5_0[] = { 16, }; -static const long _vq_lengthlist__44c0_sm_p5_0[] = { +static const char _vq_lengthlist__44c0_sm_p5_0[] = { 1, 4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 9, 9,10,10,11, 11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,11, 11,11, 0, 5, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, @@ -12034,7 +12033,7 @@ static const long _vq_lengthlist__44c0_sm_p5_0[] = { static const static_codebook _44c0_sm_p5_0 = { 2, 289, - (long *)_vq_lengthlist__44c0_sm_p5_0, + (char *)_vq_lengthlist__44c0_sm_p5_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c0_sm_p5_0, 0 @@ -12046,7 +12045,7 @@ static const long _vq_quantlist__44c0_sm_p6_0[] = { 2, }; -static const long _vq_lengthlist__44c0_sm_p6_0[] = { +static const char _vq_lengthlist__44c0_sm_p6_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,10,11, 11,11,10,10, 6, 9, 9,11,11,10,11,10,10, 6, 9, 9, @@ -12057,7 +12056,7 @@ static const long _vq_lengthlist__44c0_sm_p6_0[] = { static const static_codebook _44c0_sm_p6_0 = { 4, 81, - (long *)_vq_lengthlist__44c0_sm_p6_0, + (char *)_vq_lengthlist__44c0_sm_p6_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c0_sm_p6_0, 0 @@ -12077,7 +12076,7 @@ static const long _vq_quantlist__44c0_sm_p6_1[] = { 10, }; -static const long _vq_lengthlist__44c0_sm_p6_1[] = { +static const char _vq_lengthlist__44c0_sm_p6_1[] = { 2, 4, 4, 6, 6, 7, 7, 7, 7, 7, 8, 9, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, @@ -12090,7 +12089,7 @@ static const long _vq_lengthlist__44c0_sm_p6_1[] = { static const static_codebook _44c0_sm_p6_1 = { 2, 121, - (long *)_vq_lengthlist__44c0_sm_p6_1, + (char *)_vq_lengthlist__44c0_sm_p6_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c0_sm_p6_1, 0 @@ -12112,7 +12111,7 @@ static const long _vq_quantlist__44c0_sm_p7_0[] = { 12, }; -static const long _vq_lengthlist__44c0_sm_p7_0[] = { +static const char _vq_lengthlist__44c0_sm_p7_0[] = { 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 6, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -12128,7 +12127,7 @@ static const long _vq_lengthlist__44c0_sm_p7_0[] = { static const static_codebook _44c0_sm_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44c0_sm_p7_0, + (char *)_vq_lengthlist__44c0_sm_p7_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c0_sm_p7_0, 0 @@ -12142,14 +12141,14 @@ static const long _vq_quantlist__44c0_sm_p7_1[] = { 4, }; -static const long _vq_lengthlist__44c0_sm_p7_1[] = { +static const char _vq_lengthlist__44c0_sm_p7_1[] = { 2, 4, 4, 4, 4, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c0_sm_p7_1 = { 2, 25, - (long *)_vq_lengthlist__44c0_sm_p7_1, + (char *)_vq_lengthlist__44c0_sm_p7_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c0_sm_p7_1, 0 @@ -12167,7 +12166,7 @@ static const long _vq_quantlist__44c0_sm_p8_0[] = { 8, }; -static const long _vq_lengthlist__44c0_sm_p8_0[] = { +static const char _vq_lengthlist__44c0_sm_p8_0[] = { 1, 3, 3,11,11,11,11,11,11, 3, 7, 6,11,11,11,11, 11,11, 4, 8, 7,11,11,11,11,11,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, @@ -12178,7 +12177,7 @@ static const long _vq_lengthlist__44c0_sm_p8_0[] = { static const static_codebook _44c0_sm_p8_0 = { 2, 81, - (long *)_vq_lengthlist__44c0_sm_p8_0, + (char *)_vq_lengthlist__44c0_sm_p8_0, 1, -516186112, 1627103232, 4, 0, (long *)_vq_quantlist__44c0_sm_p8_0, 0 @@ -12200,7 +12199,7 @@ static const long _vq_quantlist__44c0_sm_p8_1[] = { 12, }; -static const long _vq_lengthlist__44c0_sm_p8_1[] = { +static const char _vq_lengthlist__44c0_sm_p8_1[] = { 1, 4, 4, 6, 6, 7, 7, 9, 9,10,11,12,12, 6, 5, 5, 7, 7, 8, 8,10,10,12,11,12,12, 6, 5, 5, 7, 7, 8, 8,10,10,12,11,12,12,17, 7, 7, 8, 8, 9, 9,10,10, @@ -12216,7 +12215,7 @@ static const long _vq_lengthlist__44c0_sm_p8_1[] = { static const static_codebook _44c0_sm_p8_1 = { 2, 169, - (long *)_vq_lengthlist__44c0_sm_p8_1, + (char *)_vq_lengthlist__44c0_sm_p8_1, 1, -522616832, 1620115456, 4, 0, (long *)_vq_quantlist__44c0_sm_p8_1, 0 @@ -12242,7 +12241,7 @@ static const long _vq_quantlist__44c0_sm_p8_2[] = { 16, }; -static const long _vq_lengthlist__44c0_sm_p8_2[] = { +static const char _vq_lengthlist__44c0_sm_p8_2[] = { 2, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, @@ -12266,13 +12265,13 @@ static const long _vq_lengthlist__44c0_sm_p8_2[] = { static const static_codebook _44c0_sm_p8_2 = { 2, 289, - (long *)_vq_lengthlist__44c0_sm_p8_2, + (char *)_vq_lengthlist__44c0_sm_p8_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c0_sm_p8_2, 0 }; -static const long _huff_lengthlist__44c0_sm_short[] = { +static const char _huff_lengthlist__44c0_sm_short[] = { 6, 6,12,13,13,14,16,17,17, 4, 2, 5, 8, 7, 9,12, 15,15, 9, 4, 5, 9, 7, 9,12,16,18,11, 6, 7, 4, 6, 8,11,14,18,10, 5, 6, 5, 5, 7,10,14,17,10, 5, 7, @@ -12283,13 +12282,13 @@ static const long _huff_lengthlist__44c0_sm_short[] = { static const static_codebook _huff_book__44c0_sm_short = { 2, 81, - (long *)_huff_lengthlist__44c0_sm_short, + (char *)_huff_lengthlist__44c0_sm_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c1_s_long[] = { +static const char _huff_lengthlist__44c1_s_long[] = { 5, 5, 9,10, 9, 9,10,11,12, 5, 1, 5, 6, 6, 7,10, 12,14, 9, 5, 6, 8, 8,10,12,14,14,10, 5, 8, 5, 6, 8,11,13,14, 9, 5, 7, 6, 6, 8,10,12,11, 9, 7, 9, @@ -12300,7 +12299,7 @@ static const long _huff_lengthlist__44c1_s_long[] = { static const static_codebook _huff_book__44c1_s_long = { 2, 81, - (long *)_huff_lengthlist__44c1_s_long, + (char *)_huff_lengthlist__44c1_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -12312,7 +12311,7 @@ static const long _vq_quantlist__44c1_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c1_s_p1_0[] = { +static const char _vq_lengthlist__44c1_s_p1_0[] = { 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 7, 6, 0, 0, 0, 0, 0, 0, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -12728,7 +12727,7 @@ static const long _vq_lengthlist__44c1_s_p1_0[] = { static const static_codebook _44c1_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__44c1_s_p1_0, + (char *)_vq_lengthlist__44c1_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c1_s_p1_0, 0 @@ -12742,7 +12741,7 @@ static const long _vq_quantlist__44c1_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c1_s_p2_0[] = { +static const char _vq_lengthlist__44c1_s_p2_0[] = { 2, 3, 4, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -12787,7 +12786,7 @@ static const long _vq_lengthlist__44c1_s_p2_0[] = { static const static_codebook _44c1_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c1_s_p2_0, + (char *)_vq_lengthlist__44c1_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c1_s_p2_0, 0 @@ -12805,7 +12804,7 @@ static const long _vq_quantlist__44c1_s_p3_0[] = { 8, }; -static const long _vq_lengthlist__44c1_s_p3_0[] = { +static const char _vq_lengthlist__44c1_s_p3_0[] = { 1, 3, 2, 7, 7, 0, 0, 0, 0, 0,13,13, 6, 6, 0, 0, 0, 0, 0,12, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, @@ -12816,7 +12815,7 @@ static const long _vq_lengthlist__44c1_s_p3_0[] = { static const static_codebook _44c1_s_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44c1_s_p3_0, + (char *)_vq_lengthlist__44c1_s_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c1_s_p3_0, 0 @@ -12834,7 +12833,7 @@ static const long _vq_quantlist__44c1_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__44c1_s_p4_0[] = { +static const char _vq_lengthlist__44c1_s_p4_0[] = { 1, 3, 3, 6, 5, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, @@ -12845,7 +12844,7 @@ static const long _vq_lengthlist__44c1_s_p4_0[] = { static const static_codebook _44c1_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__44c1_s_p4_0, + (char *)_vq_lengthlist__44c1_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c1_s_p4_0, 0 @@ -12871,7 +12870,7 @@ static const long _vq_quantlist__44c1_s_p5_0[] = { 16, }; -static const long _vq_lengthlist__44c1_s_p5_0[] = { +static const char _vq_lengthlist__44c1_s_p5_0[] = { 1, 4, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,10, 11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, @@ -12895,7 +12894,7 @@ static const long _vq_lengthlist__44c1_s_p5_0[] = { static const static_codebook _44c1_s_p5_0 = { 2, 289, - (long *)_vq_lengthlist__44c1_s_p5_0, + (char *)_vq_lengthlist__44c1_s_p5_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c1_s_p5_0, 0 @@ -12907,7 +12906,7 @@ static const long _vq_quantlist__44c1_s_p6_0[] = { 2, }; -static const long _vq_lengthlist__44c1_s_p6_0[] = { +static const char _vq_lengthlist__44c1_s_p6_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 6,10,10,11,11, 11,11,10,10, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, @@ -12918,7 +12917,7 @@ static const long _vq_lengthlist__44c1_s_p6_0[] = { static const static_codebook _44c1_s_p6_0 = { 4, 81, - (long *)_vq_lengthlist__44c1_s_p6_0, + (char *)_vq_lengthlist__44c1_s_p6_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c1_s_p6_0, 0 @@ -12938,7 +12937,7 @@ static const long _vq_quantlist__44c1_s_p6_1[] = { 10, }; -static const long _vq_lengthlist__44c1_s_p6_1[] = { +static const char _vq_lengthlist__44c1_s_p6_1[] = { 2, 3, 3, 6, 6, 7, 7, 7, 7, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, @@ -12951,7 +12950,7 @@ static const long _vq_lengthlist__44c1_s_p6_1[] = { static const static_codebook _44c1_s_p6_1 = { 2, 121, - (long *)_vq_lengthlist__44c1_s_p6_1, + (char *)_vq_lengthlist__44c1_s_p6_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c1_s_p6_1, 0 @@ -12973,7 +12972,7 @@ static const long _vq_quantlist__44c1_s_p7_0[] = { 12, }; -static const long _vq_lengthlist__44c1_s_p7_0[] = { +static const char _vq_lengthlist__44c1_s_p7_0[] = { 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 9, 7, 5, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -12989,7 +12988,7 @@ static const long _vq_lengthlist__44c1_s_p7_0[] = { static const static_codebook _44c1_s_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44c1_s_p7_0, + (char *)_vq_lengthlist__44c1_s_p7_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c1_s_p7_0, 0 @@ -13003,14 +13002,14 @@ static const long _vq_quantlist__44c1_s_p7_1[] = { 4, }; -static const long _vq_lengthlist__44c1_s_p7_1[] = { +static const char _vq_lengthlist__44c1_s_p7_1[] = { 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c1_s_p7_1 = { 2, 25, - (long *)_vq_lengthlist__44c1_s_p7_1, + (char *)_vq_lengthlist__44c1_s_p7_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c1_s_p7_1, 0 @@ -13032,7 +13031,7 @@ static const long _vq_quantlist__44c1_s_p8_0[] = { 12, }; -static const long _vq_lengthlist__44c1_s_p8_0[] = { +static const char _vq_lengthlist__44c1_s_p8_0[] = { 1, 4, 3,10,10,10,10,10,10,10,10,10,10, 4, 8, 6, 10,10,10,10,10,10,10,10,10,10, 4, 8, 7,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, @@ -13048,7 +13047,7 @@ static const long _vq_lengthlist__44c1_s_p8_0[] = { static const static_codebook _44c1_s_p8_0 = { 2, 169, - (long *)_vq_lengthlist__44c1_s_p8_0, + (char *)_vq_lengthlist__44c1_s_p8_0, 1, -514541568, 1627103232, 4, 0, (long *)_vq_quantlist__44c1_s_p8_0, 0 @@ -13070,7 +13069,7 @@ static const long _vq_quantlist__44c1_s_p8_1[] = { 12, }; -static const long _vq_lengthlist__44c1_s_p8_1[] = { +static const char _vq_lengthlist__44c1_s_p8_1[] = { 1, 4, 4, 6, 5, 7, 7, 9, 9,10,10,12,12, 6, 5, 5, 7, 7, 8, 8,10,10,12,11,12,12, 6, 5, 5, 7, 7, 8, 8,10,10,11,11,12,12,15, 7, 7, 8, 8, 9, 9,11,11, @@ -13086,7 +13085,7 @@ static const long _vq_lengthlist__44c1_s_p8_1[] = { static const static_codebook _44c1_s_p8_1 = { 2, 169, - (long *)_vq_lengthlist__44c1_s_p8_1, + (char *)_vq_lengthlist__44c1_s_p8_1, 1, -522616832, 1620115456, 4, 0, (long *)_vq_quantlist__44c1_s_p8_1, 0 @@ -13112,7 +13111,7 @@ static const long _vq_quantlist__44c1_s_p8_2[] = { 16, }; -static const long _vq_lengthlist__44c1_s_p8_2[] = { +static const char _vq_lengthlist__44c1_s_p8_2[] = { 2, 4, 4, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8,10,10,10, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, @@ -13136,13 +13135,13 @@ static const long _vq_lengthlist__44c1_s_p8_2[] = { static const static_codebook _44c1_s_p8_2 = { 2, 289, - (long *)_vq_lengthlist__44c1_s_p8_2, + (char *)_vq_lengthlist__44c1_s_p8_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c1_s_p8_2, 0 }; -static const long _huff_lengthlist__44c1_s_short[] = { +static const char _huff_lengthlist__44c1_s_short[] = { 6, 8,13,12,13,14,15,16,16, 4, 2, 4, 7, 6, 8,11, 13,15,10, 4, 4, 8, 6, 8,11,14,17,11, 5, 6, 5, 6, 8,12,14,17,11, 5, 5, 6, 5, 7,10,13,16,12, 6, 7, @@ -13153,13 +13152,13 @@ static const long _huff_lengthlist__44c1_s_short[] = { static const static_codebook _huff_book__44c1_s_short = { 2, 81, - (long *)_huff_lengthlist__44c1_s_short, + (char *)_huff_lengthlist__44c1_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44c1_sm_long[] = { +static const char _huff_lengthlist__44c1_sm_long[] = { 5, 4, 8,10, 9, 9,10,11,12, 4, 2, 5, 6, 6, 8,10, 11,13, 8, 4, 6, 8, 7, 9,12,12,14,10, 6, 8, 4, 5, 6, 9,11,12, 9, 5, 6, 5, 5, 6, 9,11,11, 9, 7, 9, @@ -13170,7 +13169,7 @@ static const long _huff_lengthlist__44c1_sm_long[] = { static const static_codebook _huff_book__44c1_sm_long = { 2, 81, - (long *)_huff_lengthlist__44c1_sm_long, + (char *)_huff_lengthlist__44c1_sm_long, 0, 0, 0, 0, 0, NULL, 0 @@ -13182,7 +13181,7 @@ static const long _vq_quantlist__44c1_sm_p1_0[] = { 2, }; -static const long _vq_lengthlist__44c1_sm_p1_0[] = { +static const char _vq_lengthlist__44c1_sm_p1_0[] = { 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -13598,7 +13597,7 @@ static const long _vq_lengthlist__44c1_sm_p1_0[] = { static const static_codebook _44c1_sm_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__44c1_sm_p1_0, + (char *)_vq_lengthlist__44c1_sm_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44c1_sm_p1_0, 0 @@ -13612,7 +13611,7 @@ static const long _vq_quantlist__44c1_sm_p2_0[] = { 4, }; -static const long _vq_lengthlist__44c1_sm_p2_0[] = { +static const char _vq_lengthlist__44c1_sm_p2_0[] = { 2, 3, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -13657,7 +13656,7 @@ static const long _vq_lengthlist__44c1_sm_p2_0[] = { static const static_codebook _44c1_sm_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44c1_sm_p2_0, + (char *)_vq_lengthlist__44c1_sm_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c1_sm_p2_0, 0 @@ -13675,7 +13674,7 @@ static const long _vq_quantlist__44c1_sm_p3_0[] = { 8, }; -static const long _vq_lengthlist__44c1_sm_p3_0[] = { +static const char _vq_lengthlist__44c1_sm_p3_0[] = { 1, 3, 3, 7, 7, 0, 0, 0, 0, 0, 5, 5, 6, 6, 0, 0, 0, 0, 0, 5, 5, 7, 7, 0, 0, 0, 0, 0, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, @@ -13686,7 +13685,7 @@ static const long _vq_lengthlist__44c1_sm_p3_0[] = { static const static_codebook _44c1_sm_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44c1_sm_p3_0, + (char *)_vq_lengthlist__44c1_sm_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c1_sm_p3_0, 0 @@ -13704,7 +13703,7 @@ static const long _vq_quantlist__44c1_sm_p4_0[] = { 8, }; -static const long _vq_lengthlist__44c1_sm_p4_0[] = { +static const char _vq_lengthlist__44c1_sm_p4_0[] = { 1, 3, 3, 6, 6, 7, 7, 9, 9, 0, 6, 6, 7, 7, 8, 8, 9, 9, 0, 6, 6, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, @@ -13715,7 +13714,7 @@ static const long _vq_lengthlist__44c1_sm_p4_0[] = { static const static_codebook _44c1_sm_p4_0 = { 2, 81, - (long *)_vq_lengthlist__44c1_sm_p4_0, + (char *)_vq_lengthlist__44c1_sm_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44c1_sm_p4_0, 0 @@ -13741,7 +13740,7 @@ static const long _vq_quantlist__44c1_sm_p5_0[] = { 16, }; -static const long _vq_lengthlist__44c1_sm_p5_0[] = { +static const char _vq_lengthlist__44c1_sm_p5_0[] = { 2, 3, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, 11, 0, 5, 5, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10,10, 11,11, 0, 5, 5, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10, @@ -13765,7 +13764,7 @@ static const long _vq_lengthlist__44c1_sm_p5_0[] = { static const static_codebook _44c1_sm_p5_0 = { 2, 289, - (long *)_vq_lengthlist__44c1_sm_p5_0, + (char *)_vq_lengthlist__44c1_sm_p5_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c1_sm_p5_0, 0 @@ -13777,7 +13776,7 @@ static const long _vq_quantlist__44c1_sm_p6_0[] = { 2, }; -static const long _vq_lengthlist__44c1_sm_p6_0[] = { +static const char _vq_lengthlist__44c1_sm_p6_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,10,11, 11,11,10,10, 6, 9, 9,11,11,10,11,10,10, 6, 9, 9, @@ -13788,7 +13787,7 @@ static const long _vq_lengthlist__44c1_sm_p6_0[] = { static const static_codebook _44c1_sm_p6_0 = { 4, 81, - (long *)_vq_lengthlist__44c1_sm_p6_0, + (char *)_vq_lengthlist__44c1_sm_p6_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44c1_sm_p6_0, 0 @@ -13808,7 +13807,7 @@ static const long _vq_quantlist__44c1_sm_p6_1[] = { 10, }; -static const long _vq_lengthlist__44c1_sm_p6_1[] = { +static const char _vq_lengthlist__44c1_sm_p6_1[] = { 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, @@ -13821,7 +13820,7 @@ static const long _vq_lengthlist__44c1_sm_p6_1[] = { static const static_codebook _44c1_sm_p6_1 = { 2, 121, - (long *)_vq_lengthlist__44c1_sm_p6_1, + (char *)_vq_lengthlist__44c1_sm_p6_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44c1_sm_p6_1, 0 @@ -13843,7 +13842,7 @@ static const long _vq_quantlist__44c1_sm_p7_0[] = { 12, }; -static const long _vq_lengthlist__44c1_sm_p7_0[] = { +static const char _vq_lengthlist__44c1_sm_p7_0[] = { 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 6, 7, 7, 8, 8, 8, 8, 9, 9,11,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -13859,7 +13858,7 @@ static const long _vq_lengthlist__44c1_sm_p7_0[] = { static const static_codebook _44c1_sm_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44c1_sm_p7_0, + (char *)_vq_lengthlist__44c1_sm_p7_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44c1_sm_p7_0, 0 @@ -13873,14 +13872,14 @@ static const long _vq_quantlist__44c1_sm_p7_1[] = { 4, }; -static const long _vq_lengthlist__44c1_sm_p7_1[] = { +static const char _vq_lengthlist__44c1_sm_p7_1[] = { 2, 4, 4, 4, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44c1_sm_p7_1 = { 2, 25, - (long *)_vq_lengthlist__44c1_sm_p7_1, + (char *)_vq_lengthlist__44c1_sm_p7_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44c1_sm_p7_1, 0 @@ -13902,7 +13901,7 @@ static const long _vq_quantlist__44c1_sm_p8_0[] = { 12, }; -static const long _vq_lengthlist__44c1_sm_p8_0[] = { +static const char _vq_lengthlist__44c1_sm_p8_0[] = { 1, 3, 3,13,13,13,13,13,13,13,13,13,13, 3, 6, 6, 13,13,13,13,13,13,13,13,13,13, 4, 8, 7,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, @@ -13918,7 +13917,7 @@ static const long _vq_lengthlist__44c1_sm_p8_0[] = { static const static_codebook _44c1_sm_p8_0 = { 2, 169, - (long *)_vq_lengthlist__44c1_sm_p8_0, + (char *)_vq_lengthlist__44c1_sm_p8_0, 1, -514541568, 1627103232, 4, 0, (long *)_vq_quantlist__44c1_sm_p8_0, 0 @@ -13940,7 +13939,7 @@ static const long _vq_quantlist__44c1_sm_p8_1[] = { 12, }; -static const long _vq_lengthlist__44c1_sm_p8_1[] = { +static const char _vq_lengthlist__44c1_sm_p8_1[] = { 1, 4, 4, 6, 6, 7, 7, 9, 9,10,11,12,12, 6, 5, 5, 7, 7, 8, 7,10,10,11,11,12,12, 6, 5, 5, 7, 7, 8, 8,10,10,11,11,12,12,16, 7, 7, 8, 8, 9, 9,11,11, @@ -13956,7 +13955,7 @@ static const long _vq_lengthlist__44c1_sm_p8_1[] = { static const static_codebook _44c1_sm_p8_1 = { 2, 169, - (long *)_vq_lengthlist__44c1_sm_p8_1, + (char *)_vq_lengthlist__44c1_sm_p8_1, 1, -522616832, 1620115456, 4, 0, (long *)_vq_quantlist__44c1_sm_p8_1, 0 @@ -13982,7 +13981,7 @@ static const long _vq_quantlist__44c1_sm_p8_2[] = { 16, }; -static const long _vq_lengthlist__44c1_sm_p8_2[] = { +static const char _vq_lengthlist__44c1_sm_p8_2[] = { 2, 5, 5, 6, 6, 7, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, @@ -14006,13 +14005,13 @@ static const long _vq_lengthlist__44c1_sm_p8_2[] = { static const static_codebook _44c1_sm_p8_2 = { 2, 289, - (long *)_vq_lengthlist__44c1_sm_p8_2, + (char *)_vq_lengthlist__44c1_sm_p8_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44c1_sm_p8_2, 0 }; -static const long _huff_lengthlist__44c1_sm_short[] = { +static const char _huff_lengthlist__44c1_sm_short[] = { 4, 7,13,14,14,15,16,18,18, 4, 2, 5, 8, 7, 9,12, 15,15,10, 4, 5,10, 6, 8,11,15,17,12, 5, 7, 5, 6, 8,11,14,17,11, 5, 6, 6, 5, 6, 9,13,17,12, 6, 7, @@ -14023,13 +14022,13 @@ static const long _huff_lengthlist__44c1_sm_short[] = { static const static_codebook _huff_book__44c1_sm_short = { 2, 81, - (long *)_huff_lengthlist__44c1_sm_short, + (char *)_huff_lengthlist__44c1_sm_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44cn1_s_long[] = { +static const char _huff_lengthlist__44cn1_s_long[] = { 4, 4, 7, 8, 7, 8,10,12,17, 3, 1, 6, 6, 7, 8,10, 12,15, 7, 6, 9, 9, 9,11,12,14,17, 8, 6, 9, 6, 7, 9,11,13,17, 7, 6, 9, 7, 7, 8, 9,12,15, 8, 8,10, @@ -14040,7 +14039,7 @@ static const long _huff_lengthlist__44cn1_s_long[] = { static const static_codebook _huff_book__44cn1_s_long = { 2, 81, - (long *)_huff_lengthlist__44cn1_s_long, + (char *)_huff_lengthlist__44cn1_s_long, 0, 0, 0, 0, 0, NULL, 0 @@ -14052,7 +14051,7 @@ static const long _vq_quantlist__44cn1_s_p1_0[] = { 2, }; -static const long _vq_lengthlist__44cn1_s_p1_0[] = { +static const char _vq_lengthlist__44cn1_s_p1_0[] = { 1, 4, 4, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -14468,7 +14467,7 @@ static const long _vq_lengthlist__44cn1_s_p1_0[] = { static const static_codebook _44cn1_s_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__44cn1_s_p1_0, + (char *)_vq_lengthlist__44cn1_s_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44cn1_s_p1_0, 0 @@ -14482,7 +14481,7 @@ static const long _vq_quantlist__44cn1_s_p2_0[] = { 4, }; -static const long _vq_lengthlist__44cn1_s_p2_0[] = { +static const char _vq_lengthlist__44cn1_s_p2_0[] = { 1, 4, 4, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -14527,7 +14526,7 @@ static const long _vq_lengthlist__44cn1_s_p2_0[] = { static const static_codebook _44cn1_s_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44cn1_s_p2_0, + (char *)_vq_lengthlist__44cn1_s_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44cn1_s_p2_0, 0 @@ -14545,7 +14544,7 @@ static const long _vq_quantlist__44cn1_s_p3_0[] = { 8, }; -static const long _vq_lengthlist__44cn1_s_p3_0[] = { +static const char _vq_lengthlist__44cn1_s_p3_0[] = { 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, @@ -14556,7 +14555,7 @@ static const long _vq_lengthlist__44cn1_s_p3_0[] = { static const static_codebook _44cn1_s_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44cn1_s_p3_0, + (char *)_vq_lengthlist__44cn1_s_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44cn1_s_p3_0, 0 @@ -14574,7 +14573,7 @@ static const long _vq_quantlist__44cn1_s_p4_0[] = { 8, }; -static const long _vq_lengthlist__44cn1_s_p4_0[] = { +static const char _vq_lengthlist__44cn1_s_p4_0[] = { 1, 3, 3, 6, 6, 6, 6, 8, 8, 0, 0, 0, 6, 6, 7, 7, 9, 9, 0, 0, 0, 6, 6, 7, 7, 9, 9, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, @@ -14585,7 +14584,7 @@ static const long _vq_lengthlist__44cn1_s_p4_0[] = { static const static_codebook _44cn1_s_p4_0 = { 2, 81, - (long *)_vq_lengthlist__44cn1_s_p4_0, + (char *)_vq_lengthlist__44cn1_s_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44cn1_s_p4_0, 0 @@ -14611,7 +14610,7 @@ static const long _vq_quantlist__44cn1_s_p5_0[] = { 16, }; -static const long _vq_lengthlist__44cn1_s_p5_0[] = { +static const char _vq_lengthlist__44cn1_s_p5_0[] = { 1, 4, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,10, 10, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,10, 11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, @@ -14635,7 +14634,7 @@ static const long _vq_lengthlist__44cn1_s_p5_0[] = { static const static_codebook _44cn1_s_p5_0 = { 2, 289, - (long *)_vq_lengthlist__44cn1_s_p5_0, + (char *)_vq_lengthlist__44cn1_s_p5_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44cn1_s_p5_0, 0 @@ -14647,7 +14646,7 @@ static const long _vq_quantlist__44cn1_s_p6_0[] = { 2, }; -static const long _vq_lengthlist__44cn1_s_p6_0[] = { +static const char _vq_lengthlist__44cn1_s_p6_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 6, 6,10, 9, 9,11, 9, 9, 4, 6, 6,10, 9, 9,10, 9, 9, 7,10,10,11,11, 11,12,11,11, 7, 9, 9,11,11,10,11,10,10, 7, 9, 9, @@ -14658,7 +14657,7 @@ static const long _vq_lengthlist__44cn1_s_p6_0[] = { static const static_codebook _44cn1_s_p6_0 = { 4, 81, - (long *)_vq_lengthlist__44cn1_s_p6_0, + (char *)_vq_lengthlist__44cn1_s_p6_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44cn1_s_p6_0, 0 @@ -14678,7 +14677,7 @@ static const long _vq_quantlist__44cn1_s_p6_1[] = { 10, }; -static const long _vq_lengthlist__44cn1_s_p6_1[] = { +static const char _vq_lengthlist__44cn1_s_p6_1[] = { 1, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 7, 6, 8, 8, 8, 8, 8, 8,10,10,10, 7, 6, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, @@ -14691,7 +14690,7 @@ static const long _vq_lengthlist__44cn1_s_p6_1[] = { static const static_codebook _44cn1_s_p6_1 = { 2, 121, - (long *)_vq_lengthlist__44cn1_s_p6_1, + (char *)_vq_lengthlist__44cn1_s_p6_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44cn1_s_p6_1, 0 @@ -14713,7 +14712,7 @@ static const long _vq_quantlist__44cn1_s_p7_0[] = { 12, }; -static const long _vq_lengthlist__44cn1_s_p7_0[] = { +static const char _vq_lengthlist__44cn1_s_p7_0[] = { 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,11,11, 7, 5, 5, 7, 7, 8, 8, 8, 8, 9,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -14729,7 +14728,7 @@ static const long _vq_lengthlist__44cn1_s_p7_0[] = { static const static_codebook _44cn1_s_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44cn1_s_p7_0, + (char *)_vq_lengthlist__44cn1_s_p7_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44cn1_s_p7_0, 0 @@ -14743,14 +14742,14 @@ static const long _vq_quantlist__44cn1_s_p7_1[] = { 4, }; -static const long _vq_lengthlist__44cn1_s_p7_1[] = { +static const char _vq_lengthlist__44cn1_s_p7_1[] = { 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44cn1_s_p7_1 = { 2, 25, - (long *)_vq_lengthlist__44cn1_s_p7_1, + (char *)_vq_lengthlist__44cn1_s_p7_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44cn1_s_p7_1, 0 @@ -14764,7 +14763,7 @@ static const long _vq_quantlist__44cn1_s_p8_0[] = { 4, }; -static const long _vq_lengthlist__44cn1_s_p8_0[] = { +static const char _vq_lengthlist__44cn1_s_p8_0[] = { 1, 7, 7,11,11, 8,11,11,11,11, 4,11, 3,11,11,11, 11,11,11,11,11,11,11,11,11,11,11,10,11,11,11,11, 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, @@ -14809,7 +14808,7 @@ static const long _vq_lengthlist__44cn1_s_p8_0[] = { static const static_codebook _44cn1_s_p8_0 = { 4, 625, - (long *)_vq_lengthlist__44cn1_s_p8_0, + (char *)_vq_lengthlist__44cn1_s_p8_0, 1, -518283264, 1627103232, 3, 0, (long *)_vq_quantlist__44cn1_s_p8_0, 0 @@ -14831,7 +14830,7 @@ static const long _vq_quantlist__44cn1_s_p8_1[] = { 12, }; -static const long _vq_lengthlist__44cn1_s_p8_1[] = { +static const char _vq_lengthlist__44cn1_s_p8_1[] = { 1, 4, 4, 6, 6, 8, 8, 9,10,10,11,11,11, 6, 5, 5, 7, 7, 8, 8, 9,10, 9,11,11,12, 5, 5, 5, 7, 7, 8, 9,10,10,12,12,14,13,15, 7, 7, 8, 8, 9,10,11,11, @@ -14847,7 +14846,7 @@ static const long _vq_lengthlist__44cn1_s_p8_1[] = { static const static_codebook _44cn1_s_p8_1 = { 2, 169, - (long *)_vq_lengthlist__44cn1_s_p8_1, + (char *)_vq_lengthlist__44cn1_s_p8_1, 1, -522616832, 1620115456, 4, 0, (long *)_vq_quantlist__44cn1_s_p8_1, 0 @@ -14873,7 +14872,7 @@ static const long _vq_quantlist__44cn1_s_p8_2[] = { 16, }; -static const long _vq_lengthlist__44cn1_s_p8_2[] = { +static const char _vq_lengthlist__44cn1_s_p8_2[] = { 3, 4, 3, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,10,11,11, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, @@ -14897,13 +14896,13 @@ static const long _vq_lengthlist__44cn1_s_p8_2[] = { static const static_codebook _44cn1_s_p8_2 = { 2, 289, - (long *)_vq_lengthlist__44cn1_s_p8_2, + (char *)_vq_lengthlist__44cn1_s_p8_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44cn1_s_p8_2, 0 }; -static const long _huff_lengthlist__44cn1_s_short[] = { +static const char _huff_lengthlist__44cn1_s_short[] = { 10, 9,12,15,12,13,16,14,16, 7, 1, 5,14, 7,10,13, 16,16, 9, 4, 6,16, 8,11,16,16,16,14, 4, 7,16, 9, 12,14,16,16,10, 5, 7,14, 9,12,14,15,15,13, 8, 9, @@ -14914,13 +14913,13 @@ static const long _huff_lengthlist__44cn1_s_short[] = { static const static_codebook _huff_book__44cn1_s_short = { 2, 81, - (long *)_huff_lengthlist__44cn1_s_short, + (char *)_huff_lengthlist__44cn1_s_short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44cn1_sm_long[] = { +static const char _huff_lengthlist__44cn1_sm_long[] = { 3, 3, 8, 8, 8, 8,10,12,14, 3, 2, 6, 7, 7, 8,10, 12,16, 7, 6, 7, 9, 8,10,12,14,16, 8, 6, 8, 4, 5, 7, 9,11,13, 7, 6, 8, 5, 6, 7, 9,11,14, 8, 8,10, @@ -14931,7 +14930,7 @@ static const long _huff_lengthlist__44cn1_sm_long[] = { static const static_codebook _huff_book__44cn1_sm_long = { 2, 81, - (long *)_huff_lengthlist__44cn1_sm_long, + (char *)_huff_lengthlist__44cn1_sm_long, 0, 0, 0, 0, 0, NULL, 0 @@ -14943,7 +14942,7 @@ static const long _vq_quantlist__44cn1_sm_p1_0[] = { 2, }; -static const long _vq_lengthlist__44cn1_sm_p1_0[] = { +static const char _vq_lengthlist__44cn1_sm_p1_0[] = { 1, 4, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -15359,7 +15358,7 @@ static const long _vq_lengthlist__44cn1_sm_p1_0[] = { static const static_codebook _44cn1_sm_p1_0 = { 8, 6561, - (long *)_vq_lengthlist__44cn1_sm_p1_0, + (char *)_vq_lengthlist__44cn1_sm_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44cn1_sm_p1_0, 0 @@ -15373,7 +15372,7 @@ static const long _vq_quantlist__44cn1_sm_p2_0[] = { 4, }; -static const long _vq_lengthlist__44cn1_sm_p2_0[] = { +static const char _vq_lengthlist__44cn1_sm_p2_0[] = { 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -15418,7 +15417,7 @@ static const long _vq_lengthlist__44cn1_sm_p2_0[] = { static const static_codebook _44cn1_sm_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44cn1_sm_p2_0, + (char *)_vq_lengthlist__44cn1_sm_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44cn1_sm_p2_0, 0 @@ -15436,7 +15435,7 @@ static const long _vq_quantlist__44cn1_sm_p3_0[] = { 8, }; -static const long _vq_lengthlist__44cn1_sm_p3_0[] = { +static const char _vq_lengthlist__44cn1_sm_p3_0[] = { 1, 3, 4, 7, 7, 0, 0, 0, 0, 0, 4, 4, 7, 7, 0, 0, 0, 0, 0, 4, 5, 7, 7, 0, 0, 0, 0, 0, 6, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, @@ -15447,7 +15446,7 @@ static const long _vq_lengthlist__44cn1_sm_p3_0[] = { static const static_codebook _44cn1_sm_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44cn1_sm_p3_0, + (char *)_vq_lengthlist__44cn1_sm_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44cn1_sm_p3_0, 0 @@ -15465,7 +15464,7 @@ static const long _vq_quantlist__44cn1_sm_p4_0[] = { 8, }; -static const long _vq_lengthlist__44cn1_sm_p4_0[] = { +static const char _vq_lengthlist__44cn1_sm_p4_0[] = { 1, 4, 3, 6, 6, 7, 7, 9, 9, 0, 5, 5, 7, 7, 8, 7, 9, 9, 0, 5, 5, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, @@ -15476,7 +15475,7 @@ static const long _vq_lengthlist__44cn1_sm_p4_0[] = { static const static_codebook _44cn1_sm_p4_0 = { 2, 81, - (long *)_vq_lengthlist__44cn1_sm_p4_0, + (char *)_vq_lengthlist__44cn1_sm_p4_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44cn1_sm_p4_0, 0 @@ -15502,7 +15501,7 @@ static const long _vq_quantlist__44cn1_sm_p5_0[] = { 16, }; -static const long _vq_lengthlist__44cn1_sm_p5_0[] = { +static const char _vq_lengthlist__44cn1_sm_p5_0[] = { 1, 4, 4, 6, 6, 8, 8, 9, 9, 8, 8, 9, 9,10,10,11, 11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, 12,12, 0, 6, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, @@ -15526,7 +15525,7 @@ static const long _vq_lengthlist__44cn1_sm_p5_0[] = { static const static_codebook _44cn1_sm_p5_0 = { 2, 289, - (long *)_vq_lengthlist__44cn1_sm_p5_0, + (char *)_vq_lengthlist__44cn1_sm_p5_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44cn1_sm_p5_0, 0 @@ -15538,7 +15537,7 @@ static const long _vq_quantlist__44cn1_sm_p6_0[] = { 2, }; -static const long _vq_lengthlist__44cn1_sm_p6_0[] = { +static const char _vq_lengthlist__44cn1_sm_p6_0[] = { 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 6,10, 9, 9,11, 9, 9, 4, 6, 7,10, 9, 9,11, 9, 9, 7,10,10,10,11, 11,11,11,10, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, @@ -15549,7 +15548,7 @@ static const long _vq_lengthlist__44cn1_sm_p6_0[] = { static const static_codebook _44cn1_sm_p6_0 = { 4, 81, - (long *)_vq_lengthlist__44cn1_sm_p6_0, + (char *)_vq_lengthlist__44cn1_sm_p6_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44cn1_sm_p6_0, 0 @@ -15569,7 +15568,7 @@ static const long _vq_quantlist__44cn1_sm_p6_1[] = { 10, }; -static const long _vq_lengthlist__44cn1_sm_p6_1[] = { +static const char _vq_lengthlist__44cn1_sm_p6_1[] = { 2, 4, 4, 5, 5, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,10, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, @@ -15582,7 +15581,7 @@ static const long _vq_lengthlist__44cn1_sm_p6_1[] = { static const static_codebook _44cn1_sm_p6_1 = { 2, 121, - (long *)_vq_lengthlist__44cn1_sm_p6_1, + (char *)_vq_lengthlist__44cn1_sm_p6_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44cn1_sm_p6_1, 0 @@ -15604,7 +15603,7 @@ static const long _vq_quantlist__44cn1_sm_p7_0[] = { 12, }; -static const long _vq_lengthlist__44cn1_sm_p7_0[] = { +static const char _vq_lengthlist__44cn1_sm_p7_0[] = { 1, 4, 4, 6, 6, 7, 7, 7, 7, 9, 9,10,10, 7, 5, 5, 7, 7, 8, 8, 8, 8,10, 9,11,10, 7, 5, 5, 7, 7, 8, 8, 8, 8, 9,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, @@ -15620,7 +15619,7 @@ static const long _vq_lengthlist__44cn1_sm_p7_0[] = { static const static_codebook _44cn1_sm_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44cn1_sm_p7_0, + (char *)_vq_lengthlist__44cn1_sm_p7_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44cn1_sm_p7_0, 0 @@ -15634,14 +15633,14 @@ static const long _vq_quantlist__44cn1_sm_p7_1[] = { 4, }; -static const long _vq_lengthlist__44cn1_sm_p7_1[] = { +static const char _vq_lengthlist__44cn1_sm_p7_1[] = { 2, 4, 4, 4, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, }; static const static_codebook _44cn1_sm_p7_1 = { 2, 25, - (long *)_vq_lengthlist__44cn1_sm_p7_1, + (char *)_vq_lengthlist__44cn1_sm_p7_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44cn1_sm_p7_1, 0 @@ -15659,7 +15658,7 @@ static const long _vq_quantlist__44cn1_sm_p8_0[] = { 8, }; -static const long _vq_lengthlist__44cn1_sm_p8_0[] = { +static const char _vq_lengthlist__44cn1_sm_p8_0[] = { 1, 4, 4,12,11,13,13,14,14, 4, 7, 7,11,13,14,14, 14,14, 3, 8, 3,14,14,14,14,14,14,14,10,12,14,14, 14,14,14,14,14,14, 5,14, 8,14,14,14,14,14,12,14, @@ -15670,7 +15669,7 @@ static const long _vq_lengthlist__44cn1_sm_p8_0[] = { static const static_codebook _44cn1_sm_p8_0 = { 2, 81, - (long *)_vq_lengthlist__44cn1_sm_p8_0, + (char *)_vq_lengthlist__44cn1_sm_p8_0, 1, -516186112, 1627103232, 4, 0, (long *)_vq_quantlist__44cn1_sm_p8_0, 0 @@ -15692,7 +15691,7 @@ static const long _vq_quantlist__44cn1_sm_p8_1[] = { 12, }; -static const long _vq_lengthlist__44cn1_sm_p8_1[] = { +static const char _vq_lengthlist__44cn1_sm_p8_1[] = { 1, 4, 4, 6, 6, 8, 8, 9, 9,10,11,11,11, 6, 5, 5, 7, 7, 8, 8,10,10,10,11,11,11, 6, 5, 5, 7, 7, 8, 8,10,10,11,12,12,12,14, 7, 7, 7, 8, 9, 9,11,11, @@ -15708,7 +15707,7 @@ static const long _vq_lengthlist__44cn1_sm_p8_1[] = { static const static_codebook _44cn1_sm_p8_1 = { 2, 169, - (long *)_vq_lengthlist__44cn1_sm_p8_1, + (char *)_vq_lengthlist__44cn1_sm_p8_1, 1, -522616832, 1620115456, 4, 0, (long *)_vq_quantlist__44cn1_sm_p8_1, 0 @@ -15734,7 +15733,7 @@ static const long _vq_quantlist__44cn1_sm_p8_2[] = { 16, }; -static const long _vq_lengthlist__44cn1_sm_p8_2[] = { +static const char _vq_lengthlist__44cn1_sm_p8_2[] = { 3, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,10, 6, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, @@ -15758,13 +15757,13 @@ static const long _vq_lengthlist__44cn1_sm_p8_2[] = { static const static_codebook _44cn1_sm_p8_2 = { 2, 289, - (long *)_vq_lengthlist__44cn1_sm_p8_2, + (char *)_vq_lengthlist__44cn1_sm_p8_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44cn1_sm_p8_2, 0 }; -static const long _huff_lengthlist__44cn1_sm_short[] = { +static const char _huff_lengthlist__44cn1_sm_short[] = { 5, 6,12,14,12,14,16,17,18, 4, 2, 5,11, 7,10,12, 14,15, 9, 4, 5,11, 7,10,13,15,18,15, 6, 7, 5, 6, 8,11,13,16,11, 5, 6, 5, 5, 6, 9,13,15,12, 5, 7, @@ -15775,8 +15774,9 @@ static const long _huff_lengthlist__44cn1_sm_short[] = { static const static_codebook _huff_book__44cn1_sm_short = { 2, 81, - (long *)_huff_lengthlist__44cn1_sm_short, + (char *)_huff_lengthlist__44cn1_sm_short, 0, 0, 0, 0, 0, NULL, 0 }; + diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/floor/floor_books.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/floor/floor_books.h similarity index 77% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/floor/floor_books.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/floor/floor_books.h index c20b5274..135923df 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/floor/floor_books.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/floor/floor_books.h @@ -6,43 +6,42 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: static codebooks autogenerated by huff/huffbuld - last modified: $Id: floor_books.h 16939 2010-03-01 08:38:14Z xiphmont $ ********************************************************************/ #include "../../codebook.h" -static const long _huff_lengthlist_line_256x7_0sub1[] = { +static const char _huff_lengthlist_line_256x7_0sub1[] = { 0, 2, 3, 3, 3, 3, 4, 3, 4, }; static const static_codebook _huff_book_line_256x7_0sub1 = { 1, 9, - (long *)_huff_lengthlist_line_256x7_0sub1, + (char *)_huff_lengthlist_line_256x7_0sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x7_0sub2[] = { +static const char _huff_lengthlist_line_256x7_0sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 3, 4, 3, 5, 3, 6, 3, 6, 4, 6, 4, 7, 5, 7, }; static const static_codebook _huff_book_line_256x7_0sub2 = { 1, 25, - (long *)_huff_lengthlist_line_256x7_0sub2, + (char *)_huff_lengthlist_line_256x7_0sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x7_0sub3[] = { +static const char _huff_lengthlist_line_256x7_0sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 2, 5, 3, 5, 3, 6, 3, 6, 4, 7, 6, 7, 8, 7, 9, 8, 9, 9, 9,10, 9, @@ -51,38 +50,38 @@ static const long _huff_lengthlist_line_256x7_0sub3[] = { static const static_codebook _huff_book_line_256x7_0sub3 = { 1, 64, - (long *)_huff_lengthlist_line_256x7_0sub3, + (char *)_huff_lengthlist_line_256x7_0sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x7_1sub1[] = { +static const char _huff_lengthlist_line_256x7_1sub1[] = { 0, 3, 3, 3, 3, 2, 4, 3, 4, }; static const static_codebook _huff_book_line_256x7_1sub1 = { 1, 9, - (long *)_huff_lengthlist_line_256x7_1sub1, + (char *)_huff_lengthlist_line_256x7_1sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x7_1sub2[] = { +static const char _huff_lengthlist_line_256x7_1sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 4, 3, 4, 4, 5, 4, 6, 5, 6, 7, 6, 8, 8, }; static const static_codebook _huff_book_line_256x7_1sub2 = { 1, 25, - (long *)_huff_lengthlist_line_256x7_1sub2, + (char *)_huff_lengthlist_line_256x7_1sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x7_1sub3[] = { +static const char _huff_lengthlist_line_256x7_1sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 4, 3, 6, 3, 7, 3, 8, 5, 8, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, @@ -91,13 +90,13 @@ static const long _huff_lengthlist_line_256x7_1sub3[] = { static const static_codebook _huff_book_line_256x7_1sub3 = { 1, 64, - (long *)_huff_lengthlist_line_256x7_1sub3, + (char *)_huff_lengthlist_line_256x7_1sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x7_class0[] = { +static const char _huff_lengthlist_line_256x7_class0[] = { 7, 5, 5, 9, 9, 6, 6, 9,12, 8, 7, 8,11, 8, 9,15, 6, 3, 3, 7, 7, 4, 3, 6, 9, 6, 5, 6, 8, 6, 8,15, 8, 5, 5, 9, 8, 5, 4, 6,10, 7, 5, 5,11, 8, 7,15, @@ -106,13 +105,13 @@ static const long _huff_lengthlist_line_256x7_class0[] = { static const static_codebook _huff_book_line_256x7_class0 = { 1, 64, - (long *)_huff_lengthlist_line_256x7_class0, + (char *)_huff_lengthlist_line_256x7_class0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x7_class1[] = { +static const char _huff_lengthlist_line_256x7_class1[] = { 5, 6, 8,15, 6, 9,10,15,10,11,12,15,15,15,15,15, 4, 6, 7,15, 6, 7, 8,15, 9, 8, 9,15,15,15,15,15, 6, 8, 9,15, 7, 7, 8,15,10, 9,10,15,15,15,15,15, @@ -133,13 +132,13 @@ static const long _huff_lengthlist_line_256x7_class1[] = { static const static_codebook _huff_book_line_256x7_class1 = { 1, 256, - (long *)_huff_lengthlist_line_256x7_class1, + (char *)_huff_lengthlist_line_256x7_class1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_0sub0[] = { +static const char _huff_lengthlist_line_512x17_0sub0[] = { 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 6, 6, 6, 5, 6, 6, 7, 6, 7, 6, 7, 6, 7, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 9, 7, 9, 7, @@ -152,26 +151,26 @@ static const long _huff_lengthlist_line_512x17_0sub0[] = { static const static_codebook _huff_book_line_512x17_0sub0 = { 1, 128, - (long *)_huff_lengthlist_line_512x17_0sub0, + (char *)_huff_lengthlist_line_512x17_0sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_1sub0[] = { +static const char _huff_lengthlist_line_512x17_1sub0[] = { 2, 4, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 6, 7, 6, 7, 6, 8, 7, 8, 7, 8, 7, 8, 7, }; static const static_codebook _huff_book_line_512x17_1sub0 = { 1, 32, - (long *)_huff_lengthlist_line_512x17_1sub0, + (char *)_huff_lengthlist_line_512x17_1sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_1sub1[] = { +static const char _huff_lengthlist_line_512x17_1sub1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 5, 3, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 6, 5, @@ -184,26 +183,26 @@ static const long _huff_lengthlist_line_512x17_1sub1[] = { static const static_codebook _huff_book_line_512x17_1sub1 = { 1, 128, - (long *)_huff_lengthlist_line_512x17_1sub1, + (char *)_huff_lengthlist_line_512x17_1sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_2sub1[] = { +static const char _huff_lengthlist_line_512x17_2sub1[] = { 0, 4, 5, 4, 4, 4, 5, 4, 4, 4, 5, 4, 5, 4, 5, 3, 5, 3, }; static const static_codebook _huff_book_line_512x17_2sub1 = { 1, 18, - (long *)_huff_lengthlist_line_512x17_2sub1, + (char *)_huff_lengthlist_line_512x17_2sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_2sub2[] = { +static const char _huff_lengthlist_line_512x17_2sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 6, 4, 6, 5, 6, 5, 7, 5, 7, 6, 8, 6, 8, 6, 8, 7, 8, 7, 9, 7, @@ -212,13 +211,13 @@ static const long _huff_lengthlist_line_512x17_2sub2[] = { static const static_codebook _huff_book_line_512x17_2sub2 = { 1, 50, - (long *)_huff_lengthlist_line_512x17_2sub2, + (char *)_huff_lengthlist_line_512x17_2sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_2sub3[] = { +static const char _huff_lengthlist_line_512x17_2sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -231,26 +230,26 @@ static const long _huff_lengthlist_line_512x17_2sub3[] = { static const static_codebook _huff_book_line_512x17_2sub3 = { 1, 128, - (long *)_huff_lengthlist_line_512x17_2sub3, + (char *)_huff_lengthlist_line_512x17_2sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_3sub1[] = { +static const char _huff_lengthlist_line_512x17_3sub1[] = { 0, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 4, 5, 4, 5, 5, 5, }; static const static_codebook _huff_book_line_512x17_3sub1 = { 1, 18, - (long *)_huff_lengthlist_line_512x17_3sub1, + (char *)_huff_lengthlist_line_512x17_3sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_3sub2[] = { +static const char _huff_lengthlist_line_512x17_3sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 4, 3, 5, 4, 6, 4, 6, 5, 7, 6, 7, 6, 8, 6, 8, 7, 9, 8,10, 8,12, 9,13,10,15,10,15, @@ -259,13 +258,13 @@ static const long _huff_lengthlist_line_512x17_3sub2[] = { static const static_codebook _huff_book_line_512x17_3sub2 = { 1, 50, - (long *)_huff_lengthlist_line_512x17_3sub2, + (char *)_huff_lengthlist_line_512x17_3sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_3sub3[] = { +static const char _huff_lengthlist_line_512x17_3sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -278,25 +277,25 @@ static const long _huff_lengthlist_line_512x17_3sub3[] = { static const static_codebook _huff_book_line_512x17_3sub3 = { 1, 128, - (long *)_huff_lengthlist_line_512x17_3sub3, + (char *)_huff_lengthlist_line_512x17_3sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_class1[] = { +static const char _huff_lengthlist_line_512x17_class1[] = { 1, 2, 3, 6, 5, 4, 7, 7, }; static const static_codebook _huff_book_line_512x17_class1 = { 1, 8, - (long *)_huff_lengthlist_line_512x17_class1, + (char *)_huff_lengthlist_line_512x17_class1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_class2[] = { +static const char _huff_lengthlist_line_512x17_class2[] = { 3, 3, 3,14, 5, 4, 4,11, 8, 6, 6,10,17,12,11,17, 6, 5, 5,15, 5, 3, 4,11, 8, 5, 5, 8,16, 9,10,14, 10, 8, 9,17, 8, 6, 6,13,10, 7, 7,10,16,11,13,14, @@ -305,13 +304,13 @@ static const long _huff_lengthlist_line_512x17_class2[] = { static const static_codebook _huff_book_line_512x17_class2 = { 1, 64, - (long *)_huff_lengthlist_line_512x17_class2, + (char *)_huff_lengthlist_line_512x17_class2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_512x17_class3[] = { +static const char _huff_lengthlist_line_512x17_class3[] = { 2, 4, 6,17, 4, 5, 7,17, 8, 7,10,17,17,17,17,17, 3, 4, 6,15, 3, 3, 6,15, 7, 6, 9,17,17,17,17,17, 6, 8,10,17, 6, 6, 8,16, 9, 8,10,17,17,15,16,17, @@ -320,13 +319,13 @@ static const long _huff_lengthlist_line_512x17_class3[] = { static const static_codebook _huff_book_line_512x17_class3 = { 1, 64, - (long *)_huff_lengthlist_line_512x17_class3, + (char *)_huff_lengthlist_line_512x17_class3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x4_class0[] = { +static const char _huff_lengthlist_line_128x4_class0[] = { 7, 7, 7,11, 6, 6, 7,11, 7, 6, 6,10,12,10,10,13, 7, 7, 8,11, 7, 7, 7,11, 7, 6, 7,10,11,10,10,13, 10,10, 9,12, 9, 9, 9,11, 8, 8, 8,11,13,11,10,14, @@ -347,50 +346,50 @@ static const long _huff_lengthlist_line_128x4_class0[] = { static const static_codebook _huff_book_line_128x4_class0 = { 1, 256, - (long *)_huff_lengthlist_line_128x4_class0, + (char *)_huff_lengthlist_line_128x4_class0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x4_0sub0[] = { +static const char _huff_lengthlist_line_128x4_0sub0[] = { 2, 2, 2, 2, }; static const static_codebook _huff_book_line_128x4_0sub0 = { 1, 4, - (long *)_huff_lengthlist_line_128x4_0sub0, + (char *)_huff_lengthlist_line_128x4_0sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x4_0sub1[] = { +static const char _huff_lengthlist_line_128x4_0sub1[] = { 0, 0, 0, 0, 3, 2, 3, 2, 3, 3, }; static const static_codebook _huff_book_line_128x4_0sub1 = { 1, 10, - (long *)_huff_lengthlist_line_128x4_0sub1, + (char *)_huff_lengthlist_line_128x4_0sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x4_0sub2[] = { +static const char _huff_lengthlist_line_128x4_0sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 6, 5, 6, }; static const static_codebook _huff_book_line_128x4_0sub2 = { 1, 25, - (long *)_huff_lengthlist_line_128x4_0sub2, + (char *)_huff_lengthlist_line_128x4_0sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x4_0sub3[] = { +static const char _huff_lengthlist_line_128x4_0sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 3, 5, 3, 5, 3, 5, 4, 6, 5, 6, 5, 7, 6, 6, 7, 7, 9, 9,11,11,16, @@ -399,13 +398,13 @@ static const long _huff_lengthlist_line_128x4_0sub3[] = { static const static_codebook _huff_book_line_128x4_0sub3 = { 1, 64, - (long *)_huff_lengthlist_line_128x4_0sub3, + (char *)_huff_lengthlist_line_128x4_0sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x4_class0[] = { +static const char _huff_lengthlist_line_256x4_class0[] = { 6, 7, 7,12, 6, 6, 7,12, 7, 6, 6,10,15,12,11,13, 7, 7, 8,13, 7, 7, 8,12, 7, 7, 7,11,12,12,11,13, 10, 9, 9,11, 9, 9, 9,10,10, 8, 8,12,14,12,12,14, @@ -426,50 +425,50 @@ static const long _huff_lengthlist_line_256x4_class0[] = { static const static_codebook _huff_book_line_256x4_class0 = { 1, 256, - (long *)_huff_lengthlist_line_256x4_class0, + (char *)_huff_lengthlist_line_256x4_class0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x4_0sub0[] = { +static const char _huff_lengthlist_line_256x4_0sub0[] = { 2, 2, 2, 2, }; static const static_codebook _huff_book_line_256x4_0sub0 = { 1, 4, - (long *)_huff_lengthlist_line_256x4_0sub0, + (char *)_huff_lengthlist_line_256x4_0sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x4_0sub1[] = { +static const char _huff_lengthlist_line_256x4_0sub1[] = { 0, 0, 0, 0, 2, 2, 3, 3, 3, 3, }; static const static_codebook _huff_book_line_256x4_0sub1 = { 1, 10, - (long *)_huff_lengthlist_line_256x4_0sub1, + (char *)_huff_lengthlist_line_256x4_0sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x4_0sub2[] = { +static const char _huff_lengthlist_line_256x4_0sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 4, 3, 4, 3, 5, 3, 5, 4, 5, 4, 6, 4, 6, }; static const static_codebook _huff_book_line_256x4_0sub2 = { 1, 25, - (long *)_huff_lengthlist_line_256x4_0sub2, + (char *)_huff_lengthlist_line_256x4_0sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x4_0sub3[] = { +static const char _huff_lengthlist_line_256x4_0sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 3, 5, 3, 5, 3, 6, 4, 7, 4, 7, 5, 7, 6, 7, 6, 7, 8,10,13,13,13, @@ -478,13 +477,13 @@ static const long _huff_lengthlist_line_256x4_0sub3[] = { static const static_codebook _huff_book_line_256x4_0sub3 = { 1, 64, - (long *)_huff_lengthlist_line_256x4_0sub3, + (char *)_huff_lengthlist_line_256x4_0sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x7_class0[] = { +static const char _huff_lengthlist_line_128x7_class0[] = { 10, 7, 8,13, 9, 6, 7,11,10, 8, 8,12,17,17,17,17, 7, 5, 5, 9, 6, 4, 4, 8, 8, 5, 5, 8,16,14,13,16, 7, 5, 5, 7, 6, 3, 3, 5, 8, 5, 4, 7,14,12,12,15, @@ -493,13 +492,13 @@ static const long _huff_lengthlist_line_128x7_class0[] = { static const static_codebook _huff_book_line_128x7_class0 = { 1, 64, - (long *)_huff_lengthlist_line_128x7_class0, + (char *)_huff_lengthlist_line_128x7_class0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x7_class1[] = { +static const char _huff_lengthlist_line_128x7_class1[] = { 8,13,17,17, 8,11,17,17,11,13,17,17,17,17,17,17, 6,10,16,17, 6,10,15,17, 8,10,16,17,17,17,17,17, 9,13,15,17, 8,11,17,17,10,12,17,17,17,17,17,17, @@ -520,38 +519,38 @@ static const long _huff_lengthlist_line_128x7_class1[] = { static const static_codebook _huff_book_line_128x7_class1 = { 1, 256, - (long *)_huff_lengthlist_line_128x7_class1, + (char *)_huff_lengthlist_line_128x7_class1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x7_0sub1[] = { +static const char _huff_lengthlist_line_128x7_0sub1[] = { 0, 3, 3, 3, 3, 3, 3, 3, 3, }; static const static_codebook _huff_book_line_128x7_0sub1 = { 1, 9, - (long *)_huff_lengthlist_line_128x7_0sub1, + (char *)_huff_lengthlist_line_128x7_0sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x7_0sub2[] = { +static const char _huff_lengthlist_line_128x7_0sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4, 4, 4, 4, 5, 4, 5, 4, 5, 4, 6, 4, 6, }; static const static_codebook _huff_book_line_128x7_0sub2 = { 1, 25, - (long *)_huff_lengthlist_line_128x7_0sub2, + (char *)_huff_lengthlist_line_128x7_0sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x7_0sub3[] = { +static const char _huff_lengthlist_line_128x7_0sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 3, 5, 3, 5, 4, 5, 4, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, @@ -560,38 +559,38 @@ static const long _huff_lengthlist_line_128x7_0sub3[] = { static const static_codebook _huff_book_line_128x7_0sub3 = { 1, 64, - (long *)_huff_lengthlist_line_128x7_0sub3, + (char *)_huff_lengthlist_line_128x7_0sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x7_1sub1[] = { +static const char _huff_lengthlist_line_128x7_1sub1[] = { 0, 3, 3, 2, 3, 3, 4, 3, 4, }; static const static_codebook _huff_book_line_128x7_1sub1 = { 1, 9, - (long *)_huff_lengthlist_line_128x7_1sub1, + (char *)_huff_lengthlist_line_128x7_1sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x7_1sub2[] = { +static const char _huff_lengthlist_line_128x7_1sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 3, 6, 3, 6, 3, 6, 3, 7, 3, 8, 4, 9, 4, 9, }; static const static_codebook _huff_book_line_128x7_1sub2 = { 1, 25, - (long *)_huff_lengthlist_line_128x7_1sub2, + (char *)_huff_lengthlist_line_128x7_1sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x7_1sub3[] = { +static const char _huff_lengthlist_line_128x7_1sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 2, 7, 3, 8, 4, 9, 5, 9, 8,10,11,11,12,14,14,14,14,14,14,14,14, @@ -600,25 +599,25 @@ static const long _huff_lengthlist_line_128x7_1sub3[] = { static const static_codebook _huff_book_line_128x7_1sub3 = { 1, 64, - (long *)_huff_lengthlist_line_128x7_1sub3, + (char *)_huff_lengthlist_line_128x7_1sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_class1[] = { +static const char _huff_lengthlist_line_128x11_class1[] = { 1, 6, 3, 7, 2, 4, 5, 7, }; static const static_codebook _huff_book_line_128x11_class1 = { 1, 8, - (long *)_huff_lengthlist_line_128x11_class1, + (char *)_huff_lengthlist_line_128x11_class1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_class2[] = { +static const char _huff_lengthlist_line_128x11_class2[] = { 1, 6,12,16, 4,12,15,16, 9,15,16,16,16,16,16,16, 2, 5,11,16, 5,11,13,16, 9,13,16,16,16,16,16,16, 4, 8,12,16, 5, 9,12,16, 9,13,15,16,16,16,16,16, @@ -627,13 +626,13 @@ static const long _huff_lengthlist_line_128x11_class2[] = { static const static_codebook _huff_book_line_128x11_class2 = { 1, 64, - (long *)_huff_lengthlist_line_128x11_class2, + (char *)_huff_lengthlist_line_128x11_class2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_class3[] = { +static const char _huff_lengthlist_line_128x11_class3[] = { 7, 6, 9,17, 7, 6, 8,17,12, 9,11,16,16,16,16,16, 5, 4, 7,16, 5, 3, 6,14, 9, 6, 8,15,16,16,16,16, 5, 4, 6,13, 3, 2, 4,11, 7, 4, 6,13,16,11,10,14, @@ -642,13 +641,13 @@ static const long _huff_lengthlist_line_128x11_class3[] = { static const static_codebook _huff_book_line_128x11_class3 = { 1, 64, - (long *)_huff_lengthlist_line_128x11_class3, + (char *)_huff_lengthlist_line_128x11_class3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_0sub0[] = { +static const char _huff_lengthlist_line_128x11_0sub0[] = { 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 6, 6, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 8, 6, 8, 6, 8, 7, @@ -661,26 +660,26 @@ static const long _huff_lengthlist_line_128x11_0sub0[] = { static const static_codebook _huff_book_line_128x11_0sub0 = { 1, 128, - (long *)_huff_lengthlist_line_128x11_0sub0, + (char *)_huff_lengthlist_line_128x11_0sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_1sub0[] = { +static const char _huff_lengthlist_line_128x11_1sub0[] = { 2, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 7, 6, 7, 6, 7, 6, 8, 6, 8, 6, }; static const static_codebook _huff_book_line_128x11_1sub0 = { 1, 32, - (long *)_huff_lengthlist_line_128x11_1sub0, + (char *)_huff_lengthlist_line_128x11_1sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_1sub1[] = { +static const char _huff_lengthlist_line_128x11_1sub1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 3, 5, 3, 6, 4, 6, 4, 7, 4, 7, 4, 7, 4, 8, 4, @@ -693,26 +692,26 @@ static const long _huff_lengthlist_line_128x11_1sub1[] = { static const static_codebook _huff_book_line_128x11_1sub1 = { 1, 128, - (long *)_huff_lengthlist_line_128x11_1sub1, + (char *)_huff_lengthlist_line_128x11_1sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_2sub1[] = { +static const char _huff_lengthlist_line_128x11_2sub1[] = { 0, 4, 5, 4, 5, 4, 5, 3, 5, 3, 5, 3, 5, 4, 4, 4, 5, 5, }; static const static_codebook _huff_book_line_128x11_2sub1 = { 1, 18, - (long *)_huff_lengthlist_line_128x11_2sub1, + (char *)_huff_lengthlist_line_128x11_2sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_2sub2[] = { +static const char _huff_lengthlist_line_128x11_2sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4, 4, 4, 4, 5, 4, 5, 4, 6, 5, 7, 5, 7, 6, 8, 6, 8, 6, 9, 7, 9, 7,10, 7, 9, 8,11, @@ -721,13 +720,13 @@ static const long _huff_lengthlist_line_128x11_2sub2[] = { static const static_codebook _huff_book_line_128x11_2sub2 = { 1, 50, - (long *)_huff_lengthlist_line_128x11_2sub2, + (char *)_huff_lengthlist_line_128x11_2sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_2sub3[] = { +static const char _huff_lengthlist_line_128x11_2sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -740,26 +739,26 @@ static const long _huff_lengthlist_line_128x11_2sub3[] = { static const static_codebook _huff_book_line_128x11_2sub3 = { 1, 128, - (long *)_huff_lengthlist_line_128x11_2sub3, + (char *)_huff_lengthlist_line_128x11_2sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_3sub1[] = { +static const char _huff_lengthlist_line_128x11_3sub1[] = { 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 5, 4, }; static const static_codebook _huff_book_line_128x11_3sub1 = { 1, 18, - (long *)_huff_lengthlist_line_128x11_3sub1, + (char *)_huff_lengthlist_line_128x11_3sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_3sub2[] = { +static const char _huff_lengthlist_line_128x11_3sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 3, 5, 4, 6, 4, 6, 4, 7, 4, 7, 4, 8, 4, 8, 4, 9, 4, 9, 4,10, 4,10, 5,10, 5,11, 5,12, 6, @@ -768,13 +767,13 @@ static const long _huff_lengthlist_line_128x11_3sub2[] = { static const static_codebook _huff_book_line_128x11_3sub2 = { 1, 50, - (long *)_huff_lengthlist_line_128x11_3sub2, + (char *)_huff_lengthlist_line_128x11_3sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x11_3sub3[] = { +static const char _huff_lengthlist_line_128x11_3sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -787,25 +786,25 @@ static const long _huff_lengthlist_line_128x11_3sub3[] = { static const static_codebook _huff_book_line_128x11_3sub3 = { 1, 128, - (long *)_huff_lengthlist_line_128x11_3sub3, + (char *)_huff_lengthlist_line_128x11_3sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_class1[] = { +static const char _huff_lengthlist_line_128x17_class1[] = { 1, 3, 4, 7, 2, 5, 6, 7, }; static const static_codebook _huff_book_line_128x17_class1 = { 1, 8, - (long *)_huff_lengthlist_line_128x17_class1, + (char *)_huff_lengthlist_line_128x17_class1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_class2[] = { +static const char _huff_lengthlist_line_128x17_class2[] = { 1, 4,10,19, 3, 8,13,19, 7,12,19,19,19,19,19,19, 2, 6,11,19, 8,13,19,19, 9,11,19,19,19,19,19,19, 6, 7,13,19, 9,13,19,19,10,13,18,18,18,18,18,18, @@ -814,13 +813,13 @@ static const long _huff_lengthlist_line_128x17_class2[] = { static const static_codebook _huff_book_line_128x17_class2 = { 1, 64, - (long *)_huff_lengthlist_line_128x17_class2, + (char *)_huff_lengthlist_line_128x17_class2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_class3[] = { +static const char _huff_lengthlist_line_128x17_class3[] = { 3, 6,10,17, 4, 8,11,20, 8,10,11,20,20,20,20,20, 2, 4, 8,18, 4, 6, 8,17, 7, 8,10,20,20,17,20,20, 3, 5, 8,17, 3, 4, 6,17, 8, 8,10,17,17,12,16,20, @@ -829,13 +828,13 @@ static const long _huff_lengthlist_line_128x17_class3[] = { static const static_codebook _huff_book_line_128x17_class3 = { 1, 64, - (long *)_huff_lengthlist_line_128x17_class3, + (char *)_huff_lengthlist_line_128x17_class3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_0sub0[] = { +static const char _huff_lengthlist_line_128x17_0sub0[] = { 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 8, 5, 8, 5, 8, 5, 8, 5, 8, 6, 8, 6, 8, 6, 9, 6, 9, 6, 9, 6, @@ -848,26 +847,26 @@ static const long _huff_lengthlist_line_128x17_0sub0[] = { static const static_codebook _huff_book_line_128x17_0sub0 = { 1, 128, - (long *)_huff_lengthlist_line_128x17_0sub0, + (char *)_huff_lengthlist_line_128x17_0sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_1sub0[] = { +static const char _huff_lengthlist_line_128x17_1sub0[] = { 2, 5, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 7, 6, 7, 6, 7, 6, 8, 6, 9, 7, 9, 7, }; static const static_codebook _huff_book_line_128x17_1sub0 = { 1, 32, - (long *)_huff_lengthlist_line_128x17_1sub0, + (char *)_huff_lengthlist_line_128x17_1sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_1sub1[] = { +static const char _huff_lengthlist_line_128x17_1sub1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 5, 3, 5, 3, 6, 3, 6, 4, 6, 4, 7, 4, 7, 5, @@ -880,26 +879,26 @@ static const long _huff_lengthlist_line_128x17_1sub1[] = { static const static_codebook _huff_book_line_128x17_1sub1 = { 1, 128, - (long *)_huff_lengthlist_line_128x17_1sub1, + (char *)_huff_lengthlist_line_128x17_1sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_2sub1[] = { +static const char _huff_lengthlist_line_128x17_2sub1[] = { 0, 4, 5, 4, 6, 4, 8, 3, 9, 3, 9, 2, 9, 3, 8, 4, 9, 4, }; static const static_codebook _huff_book_line_128x17_2sub1 = { 1, 18, - (long *)_huff_lengthlist_line_128x17_2sub1, + (char *)_huff_lengthlist_line_128x17_2sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_2sub2[] = { +static const char _huff_lengthlist_line_128x17_2sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 5, 3, 5, 3, 5, 4, 7, 5,10, 7,10, 7, 12,10,14,10,14, 9,14,11,14,14,14,13,13,13,13,13, @@ -908,13 +907,13 @@ static const long _huff_lengthlist_line_128x17_2sub2[] = { static const static_codebook _huff_book_line_128x17_2sub2 = { 1, 50, - (long *)_huff_lengthlist_line_128x17_2sub2, + (char *)_huff_lengthlist_line_128x17_2sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_2sub3[] = { +static const char _huff_lengthlist_line_128x17_2sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -927,26 +926,26 @@ static const long _huff_lengthlist_line_128x17_2sub3[] = { static const static_codebook _huff_book_line_128x17_2sub3 = { 1, 128, - (long *)_huff_lengthlist_line_128x17_2sub3, + (char *)_huff_lengthlist_line_128x17_2sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_3sub1[] = { +static const char _huff_lengthlist_line_128x17_3sub1[] = { 0, 4, 4, 4, 4, 4, 4, 4, 5, 3, 5, 3, 5, 4, 6, 4, 6, 4, }; static const static_codebook _huff_book_line_128x17_3sub1 = { 1, 18, - (long *)_huff_lengthlist_line_128x17_3sub1, + (char *)_huff_lengthlist_line_128x17_3sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_3sub2[] = { +static const char _huff_lengthlist_line_128x17_3sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 3, 6, 3, 6, 4, 7, 4, 7, 4, 7, 4, 8, 4, 8, 4, 8, 4, 8, 4, 9, 4, 9, 5,10, 5,10, 7,10, 8, @@ -955,13 +954,13 @@ static const long _huff_lengthlist_line_128x17_3sub2[] = { static const static_codebook _huff_book_line_128x17_3sub2 = { 1, 50, - (long *)_huff_lengthlist_line_128x17_3sub2, + (char *)_huff_lengthlist_line_128x17_3sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_128x17_3sub3[] = { +static const char _huff_lengthlist_line_128x17_3sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -974,37 +973,37 @@ static const long _huff_lengthlist_line_128x17_3sub3[] = { static const static_codebook _huff_book_line_128x17_3sub3 = { 1, 128, - (long *)_huff_lengthlist_line_128x17_3sub3, + (char *)_huff_lengthlist_line_128x17_3sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_class1[] = { +static const char _huff_lengthlist_line_1024x27_class1[] = { 2,10, 8,14, 7,12,11,14, 1, 5, 3, 7, 4, 9, 7,13, }; static const static_codebook _huff_book_line_1024x27_class1 = { 1, 16, - (long *)_huff_lengthlist_line_1024x27_class1, + (char *)_huff_lengthlist_line_1024x27_class1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_class2[] = { +static const char _huff_lengthlist_line_1024x27_class2[] = { 1, 4, 2, 6, 3, 7, 5, 7, }; static const static_codebook _huff_book_line_1024x27_class2 = { 1, 8, - (long *)_huff_lengthlist_line_1024x27_class2, + (char *)_huff_lengthlist_line_1024x27_class2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_class3[] = { +static const char _huff_lengthlist_line_1024x27_class3[] = { 1, 5, 7,21, 5, 8, 9,21,10, 9,12,20,20,16,20,20, 4, 8, 9,20, 6, 8, 9,20,11,11,13,20,20,15,17,20, 9,11,14,20, 8,10,15,20,11,13,15,20,20,20,20,20, @@ -1025,13 +1024,13 @@ static const long _huff_lengthlist_line_1024x27_class3[] = { static const static_codebook _huff_book_line_1024x27_class3 = { 1, 256, - (long *)_huff_lengthlist_line_1024x27_class3, + (char *)_huff_lengthlist_line_1024x27_class3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_class4[] = { +static const char _huff_lengthlist_line_1024x27_class4[] = { 2, 3, 7,13, 4, 4, 7,15, 8, 6, 9,17,21,16,15,21, 2, 5, 7,11, 5, 5, 7,14, 9, 7,10,16,17,15,16,21, 4, 7,10,17, 7, 7, 9,15,11, 9,11,16,21,18,15,21, @@ -1040,13 +1039,13 @@ static const long _huff_lengthlist_line_1024x27_class4[] = { static const static_codebook _huff_book_line_1024x27_class4 = { 1, 64, - (long *)_huff_lengthlist_line_1024x27_class4, + (char *)_huff_lengthlist_line_1024x27_class4, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_0sub0[] = { +static const char _huff_lengthlist_line_1024x27_0sub0[] = { 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 7, 5, 7, 5, 7, 5, 7, 5, 8, 6, 8, 6, 8, 6, 9, 6, 9, 6,10, 6,10, 6,11, 6, @@ -1059,26 +1058,26 @@ static const long _huff_lengthlist_line_1024x27_0sub0[] = { static const static_codebook _huff_book_line_1024x27_0sub0 = { 1, 128, - (long *)_huff_lengthlist_line_1024x27_0sub0, + (char *)_huff_lengthlist_line_1024x27_0sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_1sub0[] = { +static const char _huff_lengthlist_line_1024x27_1sub0[] = { 2, 5, 5, 4, 5, 4, 5, 4, 5, 4, 6, 5, 6, 5, 6, 5, 6, 5, 7, 5, 7, 6, 8, 6, 8, 6, 8, 6, 9, 6, 9, 6, }; static const static_codebook _huff_book_line_1024x27_1sub0 = { 1, 32, - (long *)_huff_lengthlist_line_1024x27_1sub0, + (char *)_huff_lengthlist_line_1024x27_1sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_1sub1[] = { +static const char _huff_lengthlist_line_1024x27_1sub1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 5, 8, 4, 9, 4, 9, 4, 9, 4, 9, 4, 9, 4, 9, 4, @@ -1091,26 +1090,26 @@ static const long _huff_lengthlist_line_1024x27_1sub1[] = { static const static_codebook _huff_book_line_1024x27_1sub1 = { 1, 128, - (long *)_huff_lengthlist_line_1024x27_1sub1, + (char *)_huff_lengthlist_line_1024x27_1sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_2sub0[] = { +static const char _huff_lengthlist_line_1024x27_2sub0[] = { 1, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 6, 7, 7, 7, 7, 8, 7, 8, 8, 9, 8,10, 9,10, 9, }; static const static_codebook _huff_book_line_1024x27_2sub0 = { 1, 32, - (long *)_huff_lengthlist_line_1024x27_2sub0, + (char *)_huff_lengthlist_line_1024x27_2sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_2sub1[] = { +static const char _huff_lengthlist_line_1024x27_2sub1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 5, 5, 6, 5, 6, 5, @@ -1123,26 +1122,26 @@ static const long _huff_lengthlist_line_1024x27_2sub1[] = { static const static_codebook _huff_book_line_1024x27_2sub1 = { 1, 128, - (long *)_huff_lengthlist_line_1024x27_2sub1, + (char *)_huff_lengthlist_line_1024x27_2sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_3sub1[] = { +static const char _huff_lengthlist_line_1024x27_3sub1[] = { 0, 4, 5, 4, 5, 3, 5, 3, 5, 3, 5, 4, 4, 4, 4, 5, 5, 5, }; static const static_codebook _huff_book_line_1024x27_3sub1 = { 1, 18, - (long *)_huff_lengthlist_line_1024x27_3sub1, + (char *)_huff_lengthlist_line_1024x27_3sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_3sub2[] = { +static const char _huff_lengthlist_line_1024x27_3sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 5, 7, 5, 8, 6, 8, 6, 9, 7,10, 7,10, 8,10, 8,11, @@ -1151,13 +1150,13 @@ static const long _huff_lengthlist_line_1024x27_3sub2[] = { static const static_codebook _huff_book_line_1024x27_3sub2 = { 1, 50, - (long *)_huff_lengthlist_line_1024x27_3sub2, + (char *)_huff_lengthlist_line_1024x27_3sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_3sub3[] = { +static const char _huff_lengthlist_line_1024x27_3sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1170,26 +1169,26 @@ static const long _huff_lengthlist_line_1024x27_3sub3[] = { static const static_codebook _huff_book_line_1024x27_3sub3 = { 1, 128, - (long *)_huff_lengthlist_line_1024x27_3sub3, + (char *)_huff_lengthlist_line_1024x27_3sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_4sub1[] = { +static const char _huff_lengthlist_line_1024x27_4sub1[] = { 0, 4, 5, 4, 5, 4, 5, 4, 5, 3, 5, 3, 5, 3, 5, 4, 5, 4, }; static const static_codebook _huff_book_line_1024x27_4sub1 = { 1, 18, - (long *)_huff_lengthlist_line_1024x27_4sub1, + (char *)_huff_lengthlist_line_1024x27_4sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_4sub2[] = { +static const char _huff_lengthlist_line_1024x27_4sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 4, 2, 5, 3, 5, 4, 6, 6, 6, 7, 7, 8, 7, 8, 7, 8, 7, 9, 8, 9, 8, 9, 8,10, 8,11, 9,12, @@ -1198,13 +1197,13 @@ static const long _huff_lengthlist_line_1024x27_4sub2[] = { static const static_codebook _huff_book_line_1024x27_4sub2 = { 1, 50, - (long *)_huff_lengthlist_line_1024x27_4sub2, + (char *)_huff_lengthlist_line_1024x27_4sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_1024x27_4sub3[] = { +static const char _huff_lengthlist_line_1024x27_4sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1217,37 +1216,37 @@ static const long _huff_lengthlist_line_1024x27_4sub3[] = { static const static_codebook _huff_book_line_1024x27_4sub3 = { 1, 128, - (long *)_huff_lengthlist_line_1024x27_4sub3, + (char *)_huff_lengthlist_line_1024x27_4sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_class1[] = { +static const char _huff_lengthlist_line_2048x27_class1[] = { 2, 6, 8, 9, 7,11,13,13, 1, 3, 5, 5, 6, 6,12,10, }; static const static_codebook _huff_book_line_2048x27_class1 = { 1, 16, - (long *)_huff_lengthlist_line_2048x27_class1, + (char *)_huff_lengthlist_line_2048x27_class1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_class2[] = { +static const char _huff_lengthlist_line_2048x27_class2[] = { 1, 2, 3, 6, 4, 7, 5, 7, }; static const static_codebook _huff_book_line_2048x27_class2 = { 1, 8, - (long *)_huff_lengthlist_line_2048x27_class2, + (char *)_huff_lengthlist_line_2048x27_class2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_class3[] = { +static const char _huff_lengthlist_line_2048x27_class3[] = { 3, 3, 6,16, 5, 5, 7,16, 9, 8,11,16,16,16,16,16, 5, 5, 8,16, 5, 5, 7,16, 8, 7, 9,16,16,16,16,16, 9, 9,12,16, 6, 8,11,16, 9,10,11,16,16,16,16,16, @@ -1268,13 +1267,13 @@ static const long _huff_lengthlist_line_2048x27_class3[] = { static const static_codebook _huff_book_line_2048x27_class3 = { 1, 256, - (long *)_huff_lengthlist_line_2048x27_class3, + (char *)_huff_lengthlist_line_2048x27_class3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_class4[] = { +static const char _huff_lengthlist_line_2048x27_class4[] = { 2, 4, 7,13, 4, 5, 7,15, 8, 7,10,16,16,14,16,16, 2, 4, 7,16, 3, 4, 7,14, 8, 8,10,16,16,16,15,16, 6, 8,11,16, 7, 7, 9,16,11, 9,13,16,16,16,15,16, @@ -1283,13 +1282,13 @@ static const long _huff_lengthlist_line_2048x27_class4[] = { static const static_codebook _huff_book_line_2048x27_class4 = { 1, 64, - (long *)_huff_lengthlist_line_2048x27_class4, + (char *)_huff_lengthlist_line_2048x27_class4, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_0sub0[] = { +static const char _huff_lengthlist_line_2048x27_0sub0[] = { 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 7, 5, 7, 5, 7, 5, 8, 5, 8, 5, 8, 5, 9, 5, 9, 6,10, 6,10, 6,11, 6,11, 6,11, 6,11, 6,11, 6, @@ -1302,26 +1301,26 @@ static const long _huff_lengthlist_line_2048x27_0sub0[] = { static const static_codebook _huff_book_line_2048x27_0sub0 = { 1, 128, - (long *)_huff_lengthlist_line_2048x27_0sub0, + (char *)_huff_lengthlist_line_2048x27_0sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_1sub0[] = { +static const char _huff_lengthlist_line_2048x27_1sub0[] = { 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 6, 7, 6, 7, 6, 7, 6, }; static const static_codebook _huff_book_line_2048x27_1sub0 = { 1, 32, - (long *)_huff_lengthlist_line_2048x27_1sub0, + (char *)_huff_lengthlist_line_2048x27_1sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_1sub1[] = { +static const char _huff_lengthlist_line_2048x27_1sub1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 7, 5, 7, 4, 7, 4, 8, 4, 8, 4, 8, 4, 8, 3, @@ -1334,26 +1333,26 @@ static const long _huff_lengthlist_line_2048x27_1sub1[] = { static const static_codebook _huff_book_line_2048x27_1sub1 = { 1, 128, - (long *)_huff_lengthlist_line_2048x27_1sub1, + (char *)_huff_lengthlist_line_2048x27_1sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_2sub0[] = { +static const char _huff_lengthlist_line_2048x27_2sub0[] = { 2, 4, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, }; static const static_codebook _huff_book_line_2048x27_2sub0 = { 1, 32, - (long *)_huff_lengthlist_line_2048x27_2sub0, + (char *)_huff_lengthlist_line_2048x27_2sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_2sub1[] = { +static const char _huff_lengthlist_line_2048x27_2sub1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 3, 4, 3, 4, 4, 5, 4, 5, 5, 5, 6, 6, 6, 7, @@ -1366,26 +1365,26 @@ static const long _huff_lengthlist_line_2048x27_2sub1[] = { static const static_codebook _huff_book_line_2048x27_2sub1 = { 1, 128, - (long *)_huff_lengthlist_line_2048x27_2sub1, + (char *)_huff_lengthlist_line_2048x27_2sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_3sub1[] = { +static const char _huff_lengthlist_line_2048x27_3sub1[] = { 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, }; static const static_codebook _huff_book_line_2048x27_3sub1 = { 1, 18, - (long *)_huff_lengthlist_line_2048x27_3sub1, + (char *)_huff_lengthlist_line_2048x27_3sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_3sub2[] = { +static const char _huff_lengthlist_line_2048x27_3sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 6, 7, 6, 8, 6, 9, 7, 9, 7, 9, 9,11, 9,12, @@ -1394,13 +1393,13 @@ static const long _huff_lengthlist_line_2048x27_3sub2[] = { static const static_codebook _huff_book_line_2048x27_3sub2 = { 1, 50, - (long *)_huff_lengthlist_line_2048x27_3sub2, + (char *)_huff_lengthlist_line_2048x27_3sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_3sub3[] = { +static const char _huff_lengthlist_line_2048x27_3sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1413,26 +1412,26 @@ static const long _huff_lengthlist_line_2048x27_3sub3[] = { static const static_codebook _huff_book_line_2048x27_3sub3 = { 1, 128, - (long *)_huff_lengthlist_line_2048x27_3sub3, + (char *)_huff_lengthlist_line_2048x27_3sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_4sub1[] = { +static const char _huff_lengthlist_line_2048x27_4sub1[] = { 0, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 5, 4, 5, 4, 4, 5, }; static const static_codebook _huff_book_line_2048x27_4sub1 = { 1, 18, - (long *)_huff_lengthlist_line_2048x27_4sub1, + (char *)_huff_lengthlist_line_2048x27_4sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_4sub2[] = { +static const char _huff_lengthlist_line_2048x27_4sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 4, 3, 4, 4, 4, 5, 5, 6, 5, 6, 5, 7, 6, 6, 6, 7, 7, 7, 8, 9, 9, 9,12,10,11,10,10,12, @@ -1441,13 +1440,13 @@ static const long _huff_lengthlist_line_2048x27_4sub2[] = { static const static_codebook _huff_book_line_2048x27_4sub2 = { 1, 50, - (long *)_huff_lengthlist_line_2048x27_4sub2, + (char *)_huff_lengthlist_line_2048x27_4sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_2048x27_4sub3[] = { +static const char _huff_lengthlist_line_2048x27_4sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1460,13 +1459,13 @@ static const long _huff_lengthlist_line_2048x27_4sub3[] = { static const static_codebook _huff_book_line_2048x27_4sub3 = { 1, 128, - (long *)_huff_lengthlist_line_2048x27_4sub3, + (char *)_huff_lengthlist_line_2048x27_4sub3, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x4low_class0[] = { +static const char _huff_lengthlist_line_256x4low_class0[] = { 4, 5, 6,11, 5, 5, 6,10, 7, 7, 6, 6,14,13, 9, 9, 6, 6, 6,10, 6, 6, 6, 9, 8, 7, 7, 9,14,12, 8,11, 8, 7, 7,11, 8, 8, 7,11, 9, 9, 7, 9,13,11, 9,13, @@ -1487,50 +1486,50 @@ static const long _huff_lengthlist_line_256x4low_class0[] = { static const static_codebook _huff_book_line_256x4low_class0 = { 1, 256, - (long *)_huff_lengthlist_line_256x4low_class0, + (char *)_huff_lengthlist_line_256x4low_class0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x4low_0sub0[] = { +static const char _huff_lengthlist_line_256x4low_0sub0[] = { 1, 3, 2, 3, }; static const static_codebook _huff_book_line_256x4low_0sub0 = { 1, 4, - (long *)_huff_lengthlist_line_256x4low_0sub0, + (char *)_huff_lengthlist_line_256x4low_0sub0, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x4low_0sub1[] = { +static const char _huff_lengthlist_line_256x4low_0sub1[] = { 0, 0, 0, 0, 2, 3, 2, 3, 3, 3, }; static const static_codebook _huff_book_line_256x4low_0sub1 = { 1, 10, - (long *)_huff_lengthlist_line_256x4low_0sub1, + (char *)_huff_lengthlist_line_256x4low_0sub1, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x4low_0sub2[] = { +static const char _huff_lengthlist_line_256x4low_0sub2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4, 3, 4, 4, 4, 4, 4, 5, 5, 5, 6, 6, }; static const static_codebook _huff_book_line_256x4low_0sub2 = { 1, 25, - (long *)_huff_lengthlist_line_256x4low_0sub2, + (char *)_huff_lengthlist_line_256x4low_0sub2, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist_line_256x4low_0sub3[] = { +static const char _huff_lengthlist_line_256x4low_0sub3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 2, 4, 3, 5, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 8, 6, 9, @@ -1539,8 +1538,9 @@ static const long _huff_lengthlist_line_256x4low_0sub3[] = { static const static_codebook _huff_book_line_256x4low_0sub3 = { 1, 64, - (long *)_huff_lengthlist_line_256x4low_0sub3, + (char *)_huff_lengthlist_line_256x4low_0sub3, 0, 0, 0, 0, 0, NULL, 0 }; + diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/uncoupled/res_books_uncoupled.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/uncoupled/res_books_uncoupled.h similarity index 91% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/uncoupled/res_books_uncoupled.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/uncoupled/res_books_uncoupled.h index c98017aa..8bfdb35e 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/books/uncoupled/res_books_uncoupled.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/books/uncoupled/res_books_uncoupled.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: static codebooks autogenerated by huff/huffbuld - last modified: $Id: res_books_uncoupled.h 17022 2010-03-25 03:45:42Z xiphmont $ ********************************************************************/ @@ -23,7 +22,7 @@ static const long _vq_quantlist__16u0__p1_0[] = { 2, }; -static const long _vq_lengthlist__16u0__p1_0[] = { +static const char _vq_lengthlist__16u0__p1_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 8, 5, 8, 8, 8,10,10, 8, 10,11, 5, 8, 8, 8,10,10, 8,10,10, 4, 9, 9, 9,12, 11, 8,11,11, 8,12,11,10,12,14,10,13,13, 7,11,11, @@ -34,7 +33,7 @@ static const long _vq_lengthlist__16u0__p1_0[] = { static const static_codebook _16u0__p1_0 = { 4, 81, - (long *)_vq_lengthlist__16u0__p1_0, + (char *)_vq_lengthlist__16u0__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__16u0__p1_0, 0 @@ -46,7 +45,7 @@ static const long _vq_quantlist__16u0__p2_0[] = { 2, }; -static const long _vq_lengthlist__16u0__p2_0[] = { +static const char _vq_lengthlist__16u0__p2_0[] = { 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 9, 7, 8, 9, 5, 7, 7, 7, 9, 8, 7, 9, 7, 4, 7, 7, 7, 9, 9, 7, 8, 8, 6, 9, 8, 7, 8,11, 9,11,10, 6, 8, 9, @@ -57,7 +56,7 @@ static const long _vq_lengthlist__16u0__p2_0[] = { static const static_codebook _16u0__p2_0 = { 4, 81, - (long *)_vq_lengthlist__16u0__p2_0, + (char *)_vq_lengthlist__16u0__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__16u0__p2_0, 0 @@ -71,7 +70,7 @@ static const long _vq_quantlist__16u0__p3_0[] = { 4, }; -static const long _vq_lengthlist__16u0__p3_0[] = { +static const char _vq_lengthlist__16u0__p3_0[] = { 1, 5, 5, 7, 7, 6, 7, 7, 8, 8, 6, 7, 8, 8, 8, 8, 9, 9,11,11, 8, 9, 9,11,11, 6, 9, 8,10,10, 8,10, 10,11,11, 8,10,10,11,11,10,11,10,13,12, 9,11,10, @@ -116,7 +115,7 @@ static const long _vq_lengthlist__16u0__p3_0[] = { static const static_codebook _16u0__p3_0 = { 4, 625, - (long *)_vq_lengthlist__16u0__p3_0, + (char *)_vq_lengthlist__16u0__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16u0__p3_0, 0 @@ -130,7 +129,7 @@ static const long _vq_quantlist__16u0__p4_0[] = { 4, }; -static const long _vq_lengthlist__16u0__p4_0[] = { +static const char _vq_lengthlist__16u0__p4_0[] = { 3, 5, 5, 8, 8, 6, 6, 6, 9, 9, 6, 6, 6, 9, 9, 9, 10, 9,11,11, 9, 9, 9,11,11, 6, 7, 7,10,10, 7, 7, 8,10,10, 7, 7, 8,10,10,10,10,10,11,12, 9,10,10, @@ -175,7 +174,7 @@ static const long _vq_lengthlist__16u0__p4_0[] = { static const static_codebook _16u0__p4_0 = { 4, 625, - (long *)_vq_lengthlist__16u0__p4_0, + (char *)_vq_lengthlist__16u0__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16u0__p4_0, 0 @@ -193,7 +192,7 @@ static const long _vq_quantlist__16u0__p5_0[] = { 8, }; -static const long _vq_lengthlist__16u0__p5_0[] = { +static const char _vq_lengthlist__16u0__p5_0[] = { 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 7, 8, 8, 9, 9, 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,11, 7, 8, 8, @@ -204,7 +203,7 @@ static const long _vq_lengthlist__16u0__p5_0[] = { static const static_codebook _16u0__p5_0 = { 2, 81, - (long *)_vq_lengthlist__16u0__p5_0, + (char *)_vq_lengthlist__16u0__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__16u0__p5_0, 0 @@ -226,7 +225,7 @@ static const long _vq_quantlist__16u0__p6_0[] = { 12, }; -static const long _vq_lengthlist__16u0__p6_0[] = { +static const char _vq_lengthlist__16u0__p6_0[] = { 1, 4, 4, 7, 7,10,10,12,12,13,13,18,17, 3, 6, 6, 9, 9,11,11,13,13,14,14,18,17, 3, 6, 6, 9, 9,11, 11,13,13,14,14,17,18, 7, 9, 9,11,11,13,13,14,14, @@ -242,7 +241,7 @@ static const long _vq_lengthlist__16u0__p6_0[] = { static const static_codebook _16u0__p6_0 = { 2, 169, - (long *)_vq_lengthlist__16u0__p6_0, + (char *)_vq_lengthlist__16u0__p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__16u0__p6_0, 0 @@ -256,14 +255,14 @@ static const long _vq_quantlist__16u0__p6_1[] = { 4, }; -static const long _vq_lengthlist__16u0__p6_1[] = { +static const char _vq_lengthlist__16u0__p6_1[] = { 1, 4, 5, 6, 6, 4, 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 6, 7, 7, }; static const static_codebook _16u0__p6_1 = { 2, 25, - (long *)_vq_lengthlist__16u0__p6_1, + (char *)_vq_lengthlist__16u0__p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16u0__p6_1, 0 @@ -275,7 +274,7 @@ static const long _vq_quantlist__16u0__p7_0[] = { 2, }; -static const long _vq_lengthlist__16u0__p7_0[] = { +static const char _vq_lengthlist__16u0__p7_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -286,7 +285,7 @@ static const long _vq_lengthlist__16u0__p7_0[] = { static const static_codebook _16u0__p7_0 = { 4, 81, - (long *)_vq_lengthlist__16u0__p7_0, + (char *)_vq_lengthlist__16u0__p7_0, 1, -518803456, 1628680192, 2, 0, (long *)_vq_quantlist__16u0__p7_0, 0 @@ -310,7 +309,7 @@ static const long _vq_quantlist__16u0__p7_1[] = { 14, }; -static const long _vq_lengthlist__16u0__p7_1[] = { +static const char _vq_lengthlist__16u0__p7_1[] = { 1, 5, 5, 6, 5, 9,10,11,11,10,10,10,10,10,10, 5, 8, 8, 8,10,10,10,10,10,10,10,10,10,10,10, 5, 8, 9, 9, 9,10,10,10,10,10,10,10,10,10,10, 5,10, 8, @@ -330,7 +329,7 @@ static const long _vq_lengthlist__16u0__p7_1[] = { static const static_codebook _16u0__p7_1 = { 2, 225, - (long *)_vq_lengthlist__16u0__p7_1, + (char *)_vq_lengthlist__16u0__p7_1, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__16u0__p7_1, 0 @@ -360,7 +359,7 @@ static const long _vq_quantlist__16u0__p7_2[] = { 20, }; -static const long _vq_lengthlist__16u0__p7_2[] = { +static const char _vq_lengthlist__16u0__p7_2[] = { 1, 6, 6, 7, 8, 7, 7,10, 9,10, 9,11,10, 9,11,10, 9, 9, 9, 9,10, 6, 8, 7, 9, 9, 8, 8,10,10, 9,11, 11,12,12,10, 9,11, 9,12,10, 9, 6, 9, 8, 9,12, 8, @@ -393,13 +392,13 @@ static const long _vq_lengthlist__16u0__p7_2[] = { static const static_codebook _16u0__p7_2 = { 2, 441, - (long *)_vq_lengthlist__16u0__p7_2, + (char *)_vq_lengthlist__16u0__p7_2, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__16u0__p7_2, 0 }; -static const long _huff_lengthlist__16u0__single[] = { +static const char _huff_lengthlist__16u0__single[] = { 3, 5, 8, 7,14, 8, 9,19, 5, 2, 5, 5, 9, 6, 9,19, 8, 4, 5, 7, 8, 9,13,19, 7, 4, 6, 5, 9, 6, 9,19, 12, 8, 7, 9,10,11,13,19, 8, 5, 8, 6, 9, 6, 7,19, @@ -408,13 +407,13 @@ static const long _huff_lengthlist__16u0__single[] = { static const static_codebook _huff_book__16u0__single = { 2, 64, - (long *)_huff_lengthlist__16u0__single, + (char *)_huff_lengthlist__16u0__single, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__16u1__long[] = { +static const char _huff_lengthlist__16u1__long[] = { 3, 6,10, 8,12, 8,14, 8,14,19, 5, 3, 5, 5, 7, 6, 11, 7,16,19, 7, 5, 6, 7, 7, 9,11,12,19,19, 6, 4, 7, 5, 7, 6,10, 7,18,18, 8, 6, 7, 7, 7, 7, 8, 9, @@ -426,7 +425,7 @@ static const long _huff_lengthlist__16u1__long[] = { static const static_codebook _huff_book__16u1__long = { 2, 100, - (long *)_huff_lengthlist__16u1__long, + (char *)_huff_lengthlist__16u1__long, 0, 0, 0, 0, 0, NULL, 0 @@ -438,7 +437,7 @@ static const long _vq_quantlist__16u1__p1_0[] = { 2, }; -static const long _vq_lengthlist__16u1__p1_0[] = { +static const char _vq_lengthlist__16u1__p1_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 7, 7,10,10, 7, 9,10, 5, 7, 8, 7,10, 9, 7,10,10, 5, 8, 8, 8,10, 10, 8,10,10, 7,10,10,10,11,12,10,12,13, 7,10,10, @@ -449,7 +448,7 @@ static const long _vq_lengthlist__16u1__p1_0[] = { static const static_codebook _16u1__p1_0 = { 4, 81, - (long *)_vq_lengthlist__16u1__p1_0, + (char *)_vq_lengthlist__16u1__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__16u1__p1_0, 0 @@ -461,7 +460,7 @@ static const long _vq_quantlist__16u1__p2_0[] = { 2, }; -static const long _vq_lengthlist__16u1__p2_0[] = { +static const char _vq_lengthlist__16u1__p2_0[] = { 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 7, 8, 6, 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 7, 5, 6, 6, 6, 8, 8, 6, 8, 8, 6, 8, 8, 7, 7,10, 8, 9, 9, 6, 8, 8, @@ -472,7 +471,7 @@ static const long _vq_lengthlist__16u1__p2_0[] = { static const static_codebook _16u1__p2_0 = { 4, 81, - (long *)_vq_lengthlist__16u1__p2_0, + (char *)_vq_lengthlist__16u1__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__16u1__p2_0, 0 @@ -486,7 +485,7 @@ static const long _vq_quantlist__16u1__p3_0[] = { 4, }; -static const long _vq_lengthlist__16u1__p3_0[] = { +static const char _vq_lengthlist__16u1__p3_0[] = { 1, 5, 5, 8, 8, 6, 7, 7, 9, 9, 5, 7, 7, 9, 9, 9, 10, 9,11,11, 9, 9,10,11,11, 6, 8, 8,10,10, 8, 9, 10,11,11, 8, 9,10,11,11,10,11,11,12,13,10,11,11, @@ -531,7 +530,7 @@ static const long _vq_lengthlist__16u1__p3_0[] = { static const static_codebook _16u1__p3_0 = { 4, 625, - (long *)_vq_lengthlist__16u1__p3_0, + (char *)_vq_lengthlist__16u1__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16u1__p3_0, 0 @@ -545,7 +544,7 @@ static const long _vq_quantlist__16u1__p4_0[] = { 4, }; -static const long _vq_lengthlist__16u1__p4_0[] = { +static const char _vq_lengthlist__16u1__p4_0[] = { 4, 5, 5, 8, 8, 6, 6, 7, 9, 9, 6, 6, 6, 9, 9, 9, 10, 9,11,11, 9, 9,10,11,11, 6, 7, 7,10, 9, 7, 7, 8, 9,10, 7, 7, 8,10,10,10,10,10,10,12, 9, 9,10, @@ -590,7 +589,7 @@ static const long _vq_lengthlist__16u1__p4_0[] = { static const static_codebook _16u1__p4_0 = { 4, 625, - (long *)_vq_lengthlist__16u1__p4_0, + (char *)_vq_lengthlist__16u1__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16u1__p4_0, 0 @@ -608,7 +607,7 @@ static const long _vq_quantlist__16u1__p5_0[] = { 8, }; -static const long _vq_lengthlist__16u1__p5_0[] = { +static const char _vq_lengthlist__16u1__p5_0[] = { 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, 10,10, 4, 5, 6, 8, 8, 8, 8,10,10, 7, 8, 8, 9, 9, 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 7, 8, 8, @@ -619,7 +618,7 @@ static const long _vq_lengthlist__16u1__p5_0[] = { static const static_codebook _16u1__p5_0 = { 2, 81, - (long *)_vq_lengthlist__16u1__p5_0, + (char *)_vq_lengthlist__16u1__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__16u1__p5_0, 0 @@ -637,7 +636,7 @@ static const long _vq_quantlist__16u1__p6_0[] = { 8, }; -static const long _vq_lengthlist__16u1__p6_0[] = { +static const char _vq_lengthlist__16u1__p6_0[] = { 3, 4, 4, 6, 6, 7, 7, 9, 9, 4, 4, 4, 6, 6, 8, 8, 9, 9, 4, 4, 4, 6, 6, 7, 7, 9, 9, 6, 6, 6, 7, 7, 8, 8,10, 9, 6, 6, 6, 7, 7, 8, 8, 9,10, 7, 8, 7, @@ -648,7 +647,7 @@ static const long _vq_lengthlist__16u1__p6_0[] = { static const static_codebook _16u1__p6_0 = { 2, 81, - (long *)_vq_lengthlist__16u1__p6_0, + (char *)_vq_lengthlist__16u1__p6_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__16u1__p6_0, 0 @@ -660,7 +659,7 @@ static const long _vq_quantlist__16u1__p7_0[] = { 2, }; -static const long _vq_lengthlist__16u1__p7_0[] = { +static const char _vq_lengthlist__16u1__p7_0[] = { 1, 4, 4, 4, 8, 8, 4, 8, 8, 5,11, 9, 8,12,11, 8, 12,11, 5,10,11, 8,11,12, 8,11,12, 4,11,11,11,14, 13,10,13,13, 8,14,13,12,14,16,12,16,15, 8,14,14, @@ -671,7 +670,7 @@ static const long _vq_lengthlist__16u1__p7_0[] = { static const static_codebook _16u1__p7_0 = { 4, 81, - (long *)_vq_lengthlist__16u1__p7_0, + (char *)_vq_lengthlist__16u1__p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__16u1__p7_0, 0 @@ -691,7 +690,7 @@ static const long _vq_quantlist__16u1__p7_1[] = { 10, }; -static const long _vq_lengthlist__16u1__p7_1[] = { +static const char _vq_lengthlist__16u1__p7_1[] = { 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 6, 5, 7, 7, 8, 8, 8, 8, 8, 8, 4, 5, 6, 7, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, @@ -704,7 +703,7 @@ static const long _vq_lengthlist__16u1__p7_1[] = { static const static_codebook _16u1__p7_1 = { 2, 121, - (long *)_vq_lengthlist__16u1__p7_1, + (char *)_vq_lengthlist__16u1__p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__16u1__p7_1, 0 @@ -724,7 +723,7 @@ static const long _vq_quantlist__16u1__p8_0[] = { 10, }; -static const long _vq_lengthlist__16u1__p8_0[] = { +static const char _vq_lengthlist__16u1__p8_0[] = { 1, 4, 4, 5, 5, 8, 8,10,10,12,12, 4, 7, 7, 8, 8, 9, 9,12,11,14,13, 4, 7, 7, 7, 8, 9,10,11,11,13, 12, 5, 8, 8, 9, 9,11,11,12,13,15,14, 5, 7, 8, 9, @@ -737,7 +736,7 @@ static const long _vq_lengthlist__16u1__p8_0[] = { static const static_codebook _16u1__p8_0 = { 2, 121, - (long *)_vq_lengthlist__16u1__p8_0, + (char *)_vq_lengthlist__16u1__p8_0, 1, -524582912, 1618345984, 4, 0, (long *)_vq_quantlist__16u1__p8_0, 0 @@ -757,7 +756,7 @@ static const long _vq_quantlist__16u1__p8_1[] = { 10, }; -static const long _vq_lengthlist__16u1__p8_1[] = { +static const char _vq_lengthlist__16u1__p8_1[] = { 2, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 4, 6, 6, 7, 7, 8, 7, 8, 8, 8, 8, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 6, 7, 7, 7, @@ -770,7 +769,7 @@ static const long _vq_lengthlist__16u1__p8_1[] = { static const static_codebook _16u1__p8_1 = { 2, 121, - (long *)_vq_lengthlist__16u1__p8_1, + (char *)_vq_lengthlist__16u1__p8_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__16u1__p8_1, 0 @@ -794,7 +793,7 @@ static const long _vq_quantlist__16u1__p9_0[] = { 14, }; -static const long _vq_lengthlist__16u1__p9_0[] = { +static const char _vq_lengthlist__16u1__p9_0[] = { 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -814,7 +813,7 @@ static const long _vq_lengthlist__16u1__p9_0[] = { static const static_codebook _16u1__p9_0 = { 2, 225, - (long *)_vq_lengthlist__16u1__p9_0, + (char *)_vq_lengthlist__16u1__p9_0, 1, -514071552, 1627381760, 4, 0, (long *)_vq_quantlist__16u1__p9_0, 0 @@ -838,7 +837,7 @@ static const long _vq_quantlist__16u1__p9_1[] = { 14, }; -static const long _vq_lengthlist__16u1__p9_1[] = { +static const char _vq_lengthlist__16u1__p9_1[] = { 1, 6, 5, 9, 9,10,10, 6, 7, 9, 9,10,10,10,10, 5, 10, 8,10, 8,10,10, 8, 8,10, 9,10,10,10,10, 5, 8, 9,10,10,10,10, 8,10,10,10,10,10,10,10, 9,10,10, @@ -858,7 +857,7 @@ static const long _vq_lengthlist__16u1__p9_1[] = { static const static_codebook _16u1__p9_1 = { 2, 225, - (long *)_vq_lengthlist__16u1__p9_1, + (char *)_vq_lengthlist__16u1__p9_1, 1, -522338304, 1620115456, 4, 0, (long *)_vq_quantlist__16u1__p9_1, 0 @@ -884,7 +883,7 @@ static const long _vq_quantlist__16u1__p9_2[] = { 16, }; -static const long _vq_lengthlist__16u1__p9_2[] = { +static const char _vq_lengthlist__16u1__p9_2[] = { 1, 6, 6, 7, 8, 8,11,10, 9, 9,11, 9,10, 9,11,11, 9, 6, 7, 6,11, 8,11, 9,10,10,11, 9,11,10,10,10, 11, 9, 5, 7, 7, 8, 8,10,11, 8, 8,11, 9, 9,10,11, @@ -908,13 +907,13 @@ static const long _vq_lengthlist__16u1__p9_2[] = { static const static_codebook _16u1__p9_2 = { 2, 289, - (long *)_vq_lengthlist__16u1__p9_2, + (char *)_vq_lengthlist__16u1__p9_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__16u1__p9_2, 0 }; -static const long _huff_lengthlist__16u1__short[] = { +static const char _huff_lengthlist__16u1__short[] = { 5, 7,10, 9,11,10,15,11,13,16, 6, 4, 6, 6, 7, 7, 10, 9,12,16,10, 6, 5, 6, 6, 7,10,11,16,16, 9, 6, 7, 6, 7, 7,10, 8,14,16,11, 6, 5, 4, 5, 6, 8, 9, @@ -926,13 +925,13 @@ static const long _huff_lengthlist__16u1__short[] = { static const static_codebook _huff_book__16u1__short = { 2, 100, - (long *)_huff_lengthlist__16u1__short, + (char *)_huff_lengthlist__16u1__short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__16u2__long[] = { +static const char _huff_lengthlist__16u2__long[] = { 5, 8,10,10,10,11,11,12,14,18, 7, 5, 5, 6, 8, 9, 10,12,14,17, 9, 5, 4, 5, 6, 8,10,11,13,19, 9, 5, 4, 4, 5, 6, 9,10,12,17, 8, 6, 5, 4, 4, 5, 7,10, @@ -944,7 +943,7 @@ static const long _huff_lengthlist__16u2__long[] = { static const static_codebook _huff_book__16u2__long = { 2, 100, - (long *)_huff_lengthlist__16u2__long, + (char *)_huff_lengthlist__16u2__long, 0, 0, 0, 0, 0, NULL, 0 @@ -956,7 +955,7 @@ static const long _vq_quantlist__16u2_p1_0[] = { 2, }; -static const long _vq_lengthlist__16u2_p1_0[] = { +static const char _vq_lengthlist__16u2_p1_0[] = { 1, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 9, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 8, 9, 9, 5, 7, 7, 8, 9, 9, 7, 9, 9, 7, 9, 9, 9,10,11, 9,10,10, 7, 9, 9, @@ -967,7 +966,7 @@ static const long _vq_lengthlist__16u2_p1_0[] = { static const static_codebook _16u2_p1_0 = { 4, 81, - (long *)_vq_lengthlist__16u2_p1_0, + (char *)_vq_lengthlist__16u2_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__16u2_p1_0, 0 @@ -981,7 +980,7 @@ static const long _vq_quantlist__16u2_p2_0[] = { 4, }; -static const long _vq_lengthlist__16u2_p2_0[] = { +static const char _vq_lengthlist__16u2_p2_0[] = { 3, 5, 5, 8, 8, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 9, 10, 9,11,11, 9, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12,12, 9,10,10, @@ -1026,7 +1025,7 @@ static const long _vq_lengthlist__16u2_p2_0[] = { static const static_codebook _16u2_p2_0 = { 4, 625, - (long *)_vq_lengthlist__16u2_p2_0, + (char *)_vq_lengthlist__16u2_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16u2_p2_0, 0 @@ -1044,7 +1043,7 @@ static const long _vq_quantlist__16u2_p3_0[] = { 8, }; -static const long _vq_lengthlist__16u2_p3_0[] = { +static const char _vq_lengthlist__16u2_p3_0[] = { 2, 4, 4, 6, 6, 7, 7, 9, 9, 4, 5, 5, 6, 6, 8, 7, 9, 9, 4, 5, 5, 6, 6, 7, 8, 9, 9, 6, 6, 6, 7, 7, 8, 8,10,10, 6, 6, 6, 7, 7, 8, 8,10,10, 7, 8, 7, @@ -1055,7 +1054,7 @@ static const long _vq_lengthlist__16u2_p3_0[] = { static const static_codebook _16u2_p3_0 = { 2, 81, - (long *)_vq_lengthlist__16u2_p3_0, + (char *)_vq_lengthlist__16u2_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__16u2_p3_0, 0 @@ -1081,7 +1080,7 @@ static const long _vq_quantlist__16u2_p4_0[] = { 16, }; -static const long _vq_lengthlist__16u2_p4_0[] = { +static const char _vq_lengthlist__16u2_p4_0[] = { 2, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11,11, 11, 5, 5, 5, 7, 6, 8, 7, 9, 9, 9, 9,10,10,11,11, 12,12, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 9,10,10,11, @@ -1105,7 +1104,7 @@ static const long _vq_lengthlist__16u2_p4_0[] = { static const static_codebook _16u2_p4_0 = { 2, 289, - (long *)_vq_lengthlist__16u2_p4_0, + (char *)_vq_lengthlist__16u2_p4_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__16u2_p4_0, 0 @@ -1117,7 +1116,7 @@ static const long _vq_quantlist__16u2_p5_0[] = { 2, }; -static const long _vq_lengthlist__16u2_p5_0[] = { +static const char _vq_lengthlist__16u2_p5_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 7, 9, 9, 7, 9,10, 5, 8, 8, 7,10, 9, 7,10, 9, 5, 8, 8, 8,11, 10, 8,10,10, 7,10,10, 9, 9,12,10,12,12, 7,10,10, @@ -1128,7 +1127,7 @@ static const long _vq_lengthlist__16u2_p5_0[] = { static const static_codebook _16u2_p5_0 = { 4, 81, - (long *)_vq_lengthlist__16u2_p5_0, + (char *)_vq_lengthlist__16u2_p5_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__16u2_p5_0, 0 @@ -1148,7 +1147,7 @@ static const long _vq_quantlist__16u2_p5_1[] = { 10, }; -static const long _vq_lengthlist__16u2_p5_1[] = { +static const char _vq_lengthlist__16u2_p5_1[] = { 2, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, @@ -1161,7 +1160,7 @@ static const long _vq_lengthlist__16u2_p5_1[] = { static const static_codebook _16u2_p5_1 = { 2, 121, - (long *)_vq_lengthlist__16u2_p5_1, + (char *)_vq_lengthlist__16u2_p5_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__16u2_p5_1, 0 @@ -1183,7 +1182,7 @@ static const long _vq_quantlist__16u2_p6_0[] = { 12, }; -static const long _vq_lengthlist__16u2_p6_0[] = { +static const char _vq_lengthlist__16u2_p6_0[] = { 1, 5, 4, 7, 7, 8, 8, 8, 8,10,10,11,11, 4, 6, 6, 7, 7, 9, 9, 9, 9,10,10,11,11, 4, 6, 6, 7, 7, 9, 9, 9, 9,10,10,11,11, 7, 8, 8, 9, 9, 9, 9,10,10, @@ -1199,7 +1198,7 @@ static const long _vq_lengthlist__16u2_p6_0[] = { static const static_codebook _16u2_p6_0 = { 2, 169, - (long *)_vq_lengthlist__16u2_p6_0, + (char *)_vq_lengthlist__16u2_p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__16u2_p6_0, 0 @@ -1213,14 +1212,14 @@ static const long _vq_quantlist__16u2_p6_1[] = { 4, }; -static const long _vq_lengthlist__16u2_p6_1[] = { +static const char _vq_lengthlist__16u2_p6_1[] = { 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _16u2_p6_1 = { 2, 25, - (long *)_vq_lengthlist__16u2_p6_1, + (char *)_vq_lengthlist__16u2_p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__16u2_p6_1, 0 @@ -1242,7 +1241,7 @@ static const long _vq_quantlist__16u2_p7_0[] = { 12, }; -static const long _vq_lengthlist__16u2_p7_0[] = { +static const char _vq_lengthlist__16u2_p7_0[] = { 1, 4, 4, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,10, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,11, 7, 8, 8,10, 9,10,10,10,10, @@ -1258,7 +1257,7 @@ static const long _vq_lengthlist__16u2_p7_0[] = { static const static_codebook _16u2_p7_0 = { 2, 169, - (long *)_vq_lengthlist__16u2_p7_0, + (char *)_vq_lengthlist__16u2_p7_0, 1, -523206656, 1618345984, 4, 0, (long *)_vq_quantlist__16u2_p7_0, 0 @@ -1278,7 +1277,7 @@ static const long _vq_quantlist__16u2_p7_1[] = { 10, }; -static const long _vq_lengthlist__16u2_p7_1[] = { +static const char _vq_lengthlist__16u2_p7_1[] = { 2, 5, 5, 7, 7, 7, 7, 7, 7, 8, 8, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, @@ -1291,7 +1290,7 @@ static const long _vq_lengthlist__16u2_p7_1[] = { static const static_codebook _16u2_p7_1 = { 2, 121, - (long *)_vq_lengthlist__16u2_p7_1, + (char *)_vq_lengthlist__16u2_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__16u2_p7_1, 0 @@ -1315,7 +1314,7 @@ static const long _vq_quantlist__16u2_p8_0[] = { 14, }; -static const long _vq_lengthlist__16u2_p8_0[] = { +static const char _vq_lengthlist__16u2_p8_0[] = { 1, 4, 4, 7, 7, 8, 8, 7, 7, 9, 8,10, 9,11,11, 4, 7, 6, 9, 8, 9, 9, 9, 9,10, 9,11, 9,12, 9, 4, 6, 7, 8, 8, 9, 9, 9, 9,10,10,10,11,11,12, 7, 9, 8, @@ -1335,7 +1334,7 @@ static const long _vq_lengthlist__16u2_p8_0[] = { static const static_codebook _16u2_p8_0 = { 2, 225, - (long *)_vq_lengthlist__16u2_p8_0, + (char *)_vq_lengthlist__16u2_p8_0, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__16u2_p8_0, 0 @@ -1365,7 +1364,7 @@ static const long _vq_quantlist__16u2_p8_1[] = { 20, }; -static const long _vq_lengthlist__16u2_p8_1[] = { +static const char _vq_lengthlist__16u2_p8_1[] = { 3, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10, 5, 6, 6, 7, 7, 8, @@ -1398,7 +1397,7 @@ static const long _vq_lengthlist__16u2_p8_1[] = { static const static_codebook _16u2_p8_1 = { 2, 441, - (long *)_vq_lengthlist__16u2_p8_1, + (char *)_vq_lengthlist__16u2_p8_1, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__16u2_p8_1, 0 @@ -1422,7 +1421,7 @@ static const long _vq_quantlist__16u2_p9_0[] = { 14, }; -static const long _vq_lengthlist__16u2_p9_0[] = { +static const char _vq_lengthlist__16u2_p9_0[] = { 1, 5, 3, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -1442,7 +1441,7 @@ static const long _vq_lengthlist__16u2_p9_0[] = { static const static_codebook _16u2_p9_0 = { 2, 225, - (long *)_vq_lengthlist__16u2_p9_0, + (char *)_vq_lengthlist__16u2_p9_0, 1, -510036736, 1631393792, 4, 0, (long *)_vq_quantlist__16u2_p9_0, 0 @@ -1470,7 +1469,7 @@ static const long _vq_quantlist__16u2_p9_1[] = { 18, }; -static const long _vq_lengthlist__16u2_p9_1[] = { +static const char _vq_lengthlist__16u2_p9_1[] = { 1, 4, 4, 7, 7, 7, 7, 7, 6, 9, 7,10, 8,12,12,13, 13,14,14, 4, 7, 7, 9, 9, 9, 8, 9, 8,10, 9,11, 9, 14, 9,14,10,13,11, 4, 7, 7, 9, 9, 9, 9, 8, 9,10, @@ -1498,7 +1497,7 @@ static const long _vq_lengthlist__16u2_p9_1[] = { static const static_codebook _16u2_p9_1 = { 2, 361, - (long *)_vq_lengthlist__16u2_p9_1, + (char *)_vq_lengthlist__16u2_p9_1, 1, -518287360, 1622704128, 5, 0, (long *)_vq_quantlist__16u2_p9_1, 0 @@ -1556,7 +1555,7 @@ static const long _vq_quantlist__16u2_p9_2[] = { 48, }; -static const long _vq_lengthlist__16u2_p9_2[] = { +static const char _vq_lengthlist__16u2_p9_2[] = { 2, 3, 4, 4, 4, 5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 7, 8, 8, 8, 8, 8, @@ -1565,13 +1564,13 @@ static const long _vq_lengthlist__16u2_p9_2[] = { static const static_codebook _16u2_p9_2 = { 1, 49, - (long *)_vq_lengthlist__16u2_p9_2, + (char *)_vq_lengthlist__16u2_p9_2, 1, -526909440, 1611661312, 6, 0, (long *)_vq_quantlist__16u2_p9_2, 0 }; -static const long _huff_lengthlist__16u2__short[] = { +static const char _huff_lengthlist__16u2__short[] = { 8,11,13,13,15,16,19,19,19,19,11, 8, 8, 9, 9,11, 13,15,19,20,14, 8, 7, 7, 8, 9,12,13,15,20,15, 9, 6, 5, 5, 7,10,12,14,18,14, 9, 7, 5, 3, 4, 7,10, @@ -1583,7 +1582,7 @@ static const long _huff_lengthlist__16u2__short[] = { static const static_codebook _huff_book__16u2__short = { 2, 100, - (long *)_huff_lengthlist__16u2__short, + (char *)_huff_lengthlist__16u2__short, 0, 0, 0, 0, 0, NULL, 0 @@ -1595,7 +1594,7 @@ static const long _vq_quantlist__8u0__p1_0[] = { 2, }; -static const long _vq_lengthlist__8u0__p1_0[] = { +static const char _vq_lengthlist__8u0__p1_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, 10,10, 5, 8, 8, 7,10,10, 8,10,10, 4, 9, 8, 8,11, 11, 8,11,11, 7,11,11,10,11,13,10,13,13, 7,11,11, @@ -1606,7 +1605,7 @@ static const long _vq_lengthlist__8u0__p1_0[] = { static const static_codebook _8u0__p1_0 = { 4, 81, - (long *)_vq_lengthlist__8u0__p1_0, + (char *)_vq_lengthlist__8u0__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__8u0__p1_0, 0 @@ -1618,7 +1617,7 @@ static const long _vq_quantlist__8u0__p2_0[] = { 2, }; -static const long _vq_lengthlist__8u0__p2_0[] = { +static const char _vq_lengthlist__8u0__p2_0[] = { 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 6, 7, 8, 6, 7, 8, 5, 7, 7, 6, 8, 8, 7, 9, 7, 5, 7, 7, 7, 9, 9, 7, 8, 8, 6, 9, 8, 7, 7,10, 8,10,10, 6, 8, 8, @@ -1629,7 +1628,7 @@ static const long _vq_lengthlist__8u0__p2_0[] = { static const static_codebook _8u0__p2_0 = { 4, 81, - (long *)_vq_lengthlist__8u0__p2_0, + (char *)_vq_lengthlist__8u0__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__8u0__p2_0, 0 @@ -1643,7 +1642,7 @@ static const long _vq_quantlist__8u0__p3_0[] = { 4, }; -static const long _vq_lengthlist__8u0__p3_0[] = { +static const char _vq_lengthlist__8u0__p3_0[] = { 1, 5, 5, 7, 7, 6, 7, 7, 9, 9, 6, 7, 7, 9, 9, 8, 10, 9,11,11, 8, 9, 9,11,11, 6, 8, 8,10,10, 8,10, 10,11,11, 8,10,10,11,11,10,11,11,12,12,10,11,11, @@ -1688,7 +1687,7 @@ static const long _vq_lengthlist__8u0__p3_0[] = { static const static_codebook _8u0__p3_0 = { 4, 625, - (long *)_vq_lengthlist__8u0__p3_0, + (char *)_vq_lengthlist__8u0__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__8u0__p3_0, 0 @@ -1702,7 +1701,7 @@ static const long _vq_quantlist__8u0__p4_0[] = { 4, }; -static const long _vq_lengthlist__8u0__p4_0[] = { +static const char _vq_lengthlist__8u0__p4_0[] = { 3, 5, 5, 8, 8, 5, 6, 7, 9, 9, 6, 7, 6, 9, 9, 9, 9, 9,10,11, 9, 9, 9,11,10, 6, 7, 7,10,10, 7, 7, 8,10,10, 7, 8, 8,10,10,10,10,10,10,11, 9,10,10, @@ -1747,7 +1746,7 @@ static const long _vq_lengthlist__8u0__p4_0[] = { static const static_codebook _8u0__p4_0 = { 4, 625, - (long *)_vq_lengthlist__8u0__p4_0, + (char *)_vq_lengthlist__8u0__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__8u0__p4_0, 0 @@ -1765,7 +1764,7 @@ static const long _vq_quantlist__8u0__p5_0[] = { 8, }; -static const long _vq_lengthlist__8u0__p5_0[] = { +static const char _vq_lengthlist__8u0__p5_0[] = { 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 7, 8, 8, 10,10, 4, 6, 6, 8, 8, 8, 8,10,10, 6, 8, 8, 9, 9, 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 7, 8, 8, @@ -1776,7 +1775,7 @@ static const long _vq_lengthlist__8u0__p5_0[] = { static const static_codebook _8u0__p5_0 = { 2, 81, - (long *)_vq_lengthlist__8u0__p5_0, + (char *)_vq_lengthlist__8u0__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__8u0__p5_0, 0 @@ -1798,7 +1797,7 @@ static const long _vq_quantlist__8u0__p6_0[] = { 12, }; -static const long _vq_lengthlist__8u0__p6_0[] = { +static const char _vq_lengthlist__8u0__p6_0[] = { 1, 4, 4, 7, 7, 9, 9,11,11,12,12,16,16, 3, 6, 6, 9, 9,11,11,12,12,13,14,18,16, 3, 6, 7, 9, 9,11, 11,13,12,14,14,17,16, 7, 9, 9,11,11,12,12,14,14, @@ -1814,7 +1813,7 @@ static const long _vq_lengthlist__8u0__p6_0[] = { static const static_codebook _8u0__p6_0 = { 2, 169, - (long *)_vq_lengthlist__8u0__p6_0, + (char *)_vq_lengthlist__8u0__p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__8u0__p6_0, 0 @@ -1828,14 +1827,14 @@ static const long _vq_quantlist__8u0__p6_1[] = { 4, }; -static const long _vq_lengthlist__8u0__p6_1[] = { +static const char _vq_lengthlist__8u0__p6_1[] = { 1, 4, 4, 6, 6, 4, 6, 5, 7, 7, 4, 5, 6, 7, 7, 6, 7, 7, 7, 7, 6, 7, 7, 7, 7, }; static const static_codebook _8u0__p6_1 = { 2, 25, - (long *)_vq_lengthlist__8u0__p6_1, + (char *)_vq_lengthlist__8u0__p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__8u0__p6_1, 0 @@ -1847,7 +1846,7 @@ static const long _vq_quantlist__8u0__p7_0[] = { 2, }; -static const long _vq_lengthlist__8u0__p7_0[] = { +static const char _vq_lengthlist__8u0__p7_0[] = { 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -1858,7 +1857,7 @@ static const long _vq_lengthlist__8u0__p7_0[] = { static const static_codebook _8u0__p7_0 = { 4, 81, - (long *)_vq_lengthlist__8u0__p7_0, + (char *)_vq_lengthlist__8u0__p7_0, 1, -518803456, 1628680192, 2, 0, (long *)_vq_quantlist__8u0__p7_0, 0 @@ -1882,7 +1881,7 @@ static const long _vq_quantlist__8u0__p7_1[] = { 14, }; -static const long _vq_lengthlist__8u0__p7_1[] = { +static const char _vq_lengthlist__8u0__p7_1[] = { 1, 5, 5, 5, 5,10,10,11,11,11,11,11,11,11,11, 5, 7, 6, 8, 8, 9,10,11,11,11,11,11,11,11,11, 6, 6, 7, 9, 7,11,10,11,11,11,11,11,11,11,11, 5, 6, 6, @@ -1902,7 +1901,7 @@ static const long _vq_lengthlist__8u0__p7_1[] = { static const static_codebook _8u0__p7_1 = { 2, 225, - (long *)_vq_lengthlist__8u0__p7_1, + (char *)_vq_lengthlist__8u0__p7_1, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__8u0__p7_1, 0 @@ -1932,7 +1931,7 @@ static const long _vq_quantlist__8u0__p7_2[] = { 20, }; -static const long _vq_lengthlist__8u0__p7_2[] = { +static const char _vq_lengthlist__8u0__p7_2[] = { 1, 6, 5, 7, 7, 9, 9, 9, 9,10,12,12,10,11,11,10, 11,11,11,10,11, 6, 8, 8, 9, 9,10,10, 9,10,11,11, 10,11,11,11,11,10,11,11,11,11, 6, 7, 8, 9, 9, 9, @@ -1965,13 +1964,13 @@ static const long _vq_lengthlist__8u0__p7_2[] = { static const static_codebook _8u0__p7_2 = { 2, 441, - (long *)_vq_lengthlist__8u0__p7_2, + (char *)_vq_lengthlist__8u0__p7_2, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__8u0__p7_2, 0 }; -static const long _huff_lengthlist__8u0__single[] = { +static const char _huff_lengthlist__8u0__single[] = { 4, 7,11, 9,12, 8, 7,10, 6, 4, 5, 5, 7, 5, 6,16, 9, 5, 5, 6, 7, 7, 9,16, 7, 4, 6, 5, 7, 5, 7,17, 10, 7, 7, 8, 7, 7, 8,18, 7, 5, 6, 4, 5, 4, 5,15, @@ -1980,7 +1979,7 @@ static const long _huff_lengthlist__8u0__single[] = { static const static_codebook _huff_book__8u0__single = { 2, 64, - (long *)_huff_lengthlist__8u0__single, + (char *)_huff_lengthlist__8u0__single, 0, 0, 0, 0, 0, NULL, 0 @@ -1992,7 +1991,7 @@ static const long _vq_quantlist__8u1__p1_0[] = { 2, }; -static const long _vq_lengthlist__8u1__p1_0[] = { +static const char _vq_lengthlist__8u1__p1_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 7, 9,10, 7, 9, 9, 5, 8, 8, 7,10, 9, 7, 9, 9, 5, 8, 8, 8,10, 10, 8,10,10, 7,10,10, 9,10,12,10,12,12, 7,10,10, @@ -2003,7 +2002,7 @@ static const long _vq_lengthlist__8u1__p1_0[] = { static const static_codebook _8u1__p1_0 = { 4, 81, - (long *)_vq_lengthlist__8u1__p1_0, + (char *)_vq_lengthlist__8u1__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__8u1__p1_0, 0 @@ -2015,7 +2014,7 @@ static const long _vq_quantlist__8u1__p2_0[] = { 2, }; -static const long _vq_lengthlist__8u1__p2_0[] = { +static const char _vq_lengthlist__8u1__p2_0[] = { 3, 4, 5, 5, 6, 6, 5, 6, 6, 5, 7, 6, 6, 7, 8, 6, 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 7, 5, 6, 6, 7, 8, 8, 6, 7, 7, 6, 8, 7, 7, 7, 9, 8, 9, 9, 6, 7, 8, @@ -2026,7 +2025,7 @@ static const long _vq_lengthlist__8u1__p2_0[] = { static const static_codebook _8u1__p2_0 = { 4, 81, - (long *)_vq_lengthlist__8u1__p2_0, + (char *)_vq_lengthlist__8u1__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__8u1__p2_0, 0 @@ -2040,7 +2039,7 @@ static const long _vq_quantlist__8u1__p3_0[] = { 4, }; -static const long _vq_lengthlist__8u1__p3_0[] = { +static const char _vq_lengthlist__8u1__p3_0[] = { 1, 5, 5, 7, 7, 6, 7, 7, 9, 9, 6, 7, 7, 9, 9, 8, 10, 9,11,11, 9, 9, 9,11,11, 6, 8, 8,10,10, 8,10, 10,11,11, 8, 9,10,11,11,10,11,11,12,12,10,11,11, @@ -2085,7 +2084,7 @@ static const long _vq_lengthlist__8u1__p3_0[] = { static const static_codebook _8u1__p3_0 = { 4, 625, - (long *)_vq_lengthlist__8u1__p3_0, + (char *)_vq_lengthlist__8u1__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__8u1__p3_0, 0 @@ -2099,7 +2098,7 @@ static const long _vq_quantlist__8u1__p4_0[] = { 4, }; -static const long _vq_lengthlist__8u1__p4_0[] = { +static const char _vq_lengthlist__8u1__p4_0[] = { 4, 5, 5, 9, 9, 6, 7, 7, 9, 9, 6, 7, 7, 9, 9, 9, 9, 9,11,11, 9, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 7, 8, 9,10, 7, 7, 8, 9,10, 9, 9,10,10,11, 9, 9,10, @@ -2144,7 +2143,7 @@ static const long _vq_lengthlist__8u1__p4_0[] = { static const static_codebook _8u1__p4_0 = { 4, 625, - (long *)_vq_lengthlist__8u1__p4_0, + (char *)_vq_lengthlist__8u1__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__8u1__p4_0, 0 @@ -2162,7 +2161,7 @@ static const long _vq_quantlist__8u1__p5_0[] = { 8, }; -static const long _vq_lengthlist__8u1__p5_0[] = { +static const char _vq_lengthlist__8u1__p5_0[] = { 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 5, 8, 7, 8, 8, 10,10, 4, 6, 6, 8, 8, 8, 8,10,10, 7, 8, 8, 9, 9, 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 8, 8, 8, @@ -2173,7 +2172,7 @@ static const long _vq_lengthlist__8u1__p5_0[] = { static const static_codebook _8u1__p5_0 = { 2, 81, - (long *)_vq_lengthlist__8u1__p5_0, + (char *)_vq_lengthlist__8u1__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__8u1__p5_0, 0 @@ -2191,7 +2190,7 @@ static const long _vq_quantlist__8u1__p6_0[] = { 8, }; -static const long _vq_lengthlist__8u1__p6_0[] = { +static const char _vq_lengthlist__8u1__p6_0[] = { 3, 4, 4, 6, 6, 7, 7, 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 4, 4, 4, 6, 6, 7, 7, 9, 9, 6, 6, 6, 7, 7, 8, 8, 9, 9, 6, 6, 6, 7, 7, 8, 8, 9, 9, 7, 7, 7, @@ -2202,7 +2201,7 @@ static const long _vq_lengthlist__8u1__p6_0[] = { static const static_codebook _8u1__p6_0 = { 2, 81, - (long *)_vq_lengthlist__8u1__p6_0, + (char *)_vq_lengthlist__8u1__p6_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__8u1__p6_0, 0 @@ -2214,7 +2213,7 @@ static const long _vq_quantlist__8u1__p7_0[] = { 2, }; -static const long _vq_lengthlist__8u1__p7_0[] = { +static const char _vq_lengthlist__8u1__p7_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 9, 8,10,10, 8, 10,10, 5, 9, 9, 7,10,10, 8,10,10, 4,10,10, 9,12, 12, 9,11,11, 7,12,11,10,11,13,10,13,13, 7,12,12, @@ -2225,7 +2224,7 @@ static const long _vq_lengthlist__8u1__p7_0[] = { static const static_codebook _8u1__p7_0 = { 4, 81, - (long *)_vq_lengthlist__8u1__p7_0, + (char *)_vq_lengthlist__8u1__p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__8u1__p7_0, 0 @@ -2245,7 +2244,7 @@ static const long _vq_quantlist__8u1__p7_1[] = { 10, }; -static const long _vq_lengthlist__8u1__p7_1[] = { +static const char _vq_lengthlist__8u1__p7_1[] = { 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9, 4, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, @@ -2258,7 +2257,7 @@ static const long _vq_lengthlist__8u1__p7_1[] = { static const static_codebook _8u1__p7_1 = { 2, 121, - (long *)_vq_lengthlist__8u1__p7_1, + (char *)_vq_lengthlist__8u1__p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__8u1__p7_1, 0 @@ -2278,7 +2277,7 @@ static const long _vq_quantlist__8u1__p8_0[] = { 10, }; -static const long _vq_lengthlist__8u1__p8_0[] = { +static const char _vq_lengthlist__8u1__p8_0[] = { 1, 4, 4, 6, 6, 8, 8,10,10,11,11, 4, 6, 6, 7, 7, 9, 9,11,11,13,12, 4, 6, 6, 7, 7, 9, 9,11,11,12, 12, 6, 7, 7, 9, 9,11,11,12,12,13,13, 6, 7, 7, 9, @@ -2291,7 +2290,7 @@ static const long _vq_lengthlist__8u1__p8_0[] = { static const static_codebook _8u1__p8_0 = { 2, 121, - (long *)_vq_lengthlist__8u1__p8_0, + (char *)_vq_lengthlist__8u1__p8_0, 1, -524582912, 1618345984, 4, 0, (long *)_vq_quantlist__8u1__p8_0, 0 @@ -2311,7 +2310,7 @@ static const long _vq_quantlist__8u1__p8_1[] = { 10, }; -static const long _vq_lengthlist__8u1__p8_1[] = { +static const char _vq_lengthlist__8u1__p8_1[] = { 2, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, @@ -2324,7 +2323,7 @@ static const long _vq_lengthlist__8u1__p8_1[] = { static const static_codebook _8u1__p8_1 = { 2, 121, - (long *)_vq_lengthlist__8u1__p8_1, + (char *)_vq_lengthlist__8u1__p8_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__8u1__p8_1, 0 @@ -2348,7 +2347,7 @@ static const long _vq_quantlist__8u1__p9_0[] = { 14, }; -static const long _vq_lengthlist__8u1__p9_0[] = { +static const char _vq_lengthlist__8u1__p9_0[] = { 1, 4, 4,11,11,11,11,11,11,11,11,11,11,11,11, 3, 11, 8,11,11,11,11,11,11,11,11,11,11,11,11, 3, 9, 9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, @@ -2368,7 +2367,7 @@ static const long _vq_lengthlist__8u1__p9_0[] = { static const static_codebook _8u1__p9_0 = { 2, 225, - (long *)_vq_lengthlist__8u1__p9_0, + (char *)_vq_lengthlist__8u1__p9_0, 1, -514071552, 1627381760, 4, 0, (long *)_vq_quantlist__8u1__p9_0, 0 @@ -2392,7 +2391,7 @@ static const long _vq_quantlist__8u1__p9_1[] = { 14, }; -static const long _vq_lengthlist__8u1__p9_1[] = { +static const char _vq_lengthlist__8u1__p9_1[] = { 1, 4, 4, 7, 7, 9, 9, 7, 7, 8, 8,10,10,11,11, 4, 7, 7, 9, 9,10,10, 8, 8,10,10,10,11,10,11, 4, 7, 7, 9, 9,10,10, 8, 8,10, 9,11,11,11,11, 7, 9, 9, @@ -2412,7 +2411,7 @@ static const long _vq_lengthlist__8u1__p9_1[] = { static const static_codebook _8u1__p9_1 = { 2, 225, - (long *)_vq_lengthlist__8u1__p9_1, + (char *)_vq_lengthlist__8u1__p9_1, 1, -522338304, 1620115456, 4, 0, (long *)_vq_quantlist__8u1__p9_1, 0 @@ -2438,7 +2437,7 @@ static const long _vq_quantlist__8u1__p9_2[] = { 16, }; -static const long _vq_lengthlist__8u1__p9_2[] = { +static const char _vq_lengthlist__8u1__p9_2[] = { 2, 5, 4, 6, 6, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, @@ -2462,13 +2461,13 @@ static const long _vq_lengthlist__8u1__p9_2[] = { static const static_codebook _8u1__p9_2 = { 2, 289, - (long *)_vq_lengthlist__8u1__p9_2, + (char *)_vq_lengthlist__8u1__p9_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__8u1__p9_2, 0 }; -static const long _huff_lengthlist__8u1__single[] = { +static const char _huff_lengthlist__8u1__single[] = { 4, 7,13, 9,15, 9,16, 8,10,13, 7, 5, 8, 6, 9, 7, 10, 7,10,11,11, 6, 7, 8, 8, 9, 9, 9,12,16, 8, 5, 8, 6, 8, 6, 9, 7,10,12,11, 7, 7, 7, 6, 7, 7, 7, @@ -2480,13 +2479,13 @@ static const long _huff_lengthlist__8u1__single[] = { static const static_codebook _huff_book__8u1__single = { 2, 100, - (long *)_huff_lengthlist__8u1__single, + (char *)_huff_lengthlist__8u1__single, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u0__long[] = { +static const char _huff_lengthlist__44u0__long[] = { 5, 8,13,10,17,11,11,15, 7, 2, 4, 5, 8, 7, 9,16, 13, 4, 3, 5, 6, 8,11,20,10, 4, 5, 5, 7, 6, 8,18, 15, 7, 6, 7, 8,10,14,20,10, 6, 7, 6, 9, 7, 8,17, @@ -2495,7 +2494,7 @@ static const long _huff_lengthlist__44u0__long[] = { static const static_codebook _huff_book__44u0__long = { 2, 64, - (long *)_huff_lengthlist__44u0__long, + (char *)_huff_lengthlist__44u0__long, 0, 0, 0, 0, 0, NULL, 0 @@ -2507,7 +2506,7 @@ static const long _vq_quantlist__44u0__p1_0[] = { 2, }; -static const long _vq_lengthlist__44u0__p1_0[] = { +static const char _vq_lengthlist__44u0__p1_0[] = { 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,11,11, 8, 10,10, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, 11, 8,11,11, 8,12,11,11,13,13,11,13,14, 7,11,11, @@ -2518,7 +2517,7 @@ static const long _vq_lengthlist__44u0__p1_0[] = { static const static_codebook _44u0__p1_0 = { 4, 81, - (long *)_vq_lengthlist__44u0__p1_0, + (char *)_vq_lengthlist__44u0__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u0__p1_0, 0 @@ -2530,7 +2529,7 @@ static const long _vq_quantlist__44u0__p2_0[] = { 2, }; -static const long _vq_lengthlist__44u0__p2_0[] = { +static const char _vq_lengthlist__44u0__p2_0[] = { 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 8, 6, 8, 8, 5, 7, 7, 6, 8, 8, 7, 8, 8, 4, 7, 7, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, @@ -2541,7 +2540,7 @@ static const long _vq_lengthlist__44u0__p2_0[] = { static const static_codebook _44u0__p2_0 = { 4, 81, - (long *)_vq_lengthlist__44u0__p2_0, + (char *)_vq_lengthlist__44u0__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u0__p2_0, 0 @@ -2555,7 +2554,7 @@ static const long _vq_quantlist__44u0__p3_0[] = { 4, }; -static const long _vq_lengthlist__44u0__p3_0[] = { +static const char _vq_lengthlist__44u0__p3_0[] = { 1, 5, 5, 8, 8, 5, 8, 7, 9, 9, 5, 7, 8, 9, 9, 9, 10, 9,12,12, 9, 9,10,12,12, 6, 8, 8,11,10, 8,10, 10,11,11, 8, 9,10,11,11,10,11,11,14,13,10,11,11, @@ -2600,7 +2599,7 @@ static const long _vq_lengthlist__44u0__p3_0[] = { static const static_codebook _44u0__p3_0 = { 4, 625, - (long *)_vq_lengthlist__44u0__p3_0, + (char *)_vq_lengthlist__44u0__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u0__p3_0, 0 @@ -2614,7 +2613,7 @@ static const long _vq_quantlist__44u0__p4_0[] = { 4, }; -static const long _vq_lengthlist__44u0__p4_0[] = { +static const char _vq_lengthlist__44u0__p4_0[] = { 4, 5, 5, 9, 9, 5, 6, 6, 9, 9, 5, 6, 6, 9, 9, 9, 10, 9,12,12, 9, 9,10,12,12, 5, 7, 7,10,10, 7, 7, 8,10,10, 6, 7, 8,10,10,10,10,10,11,13,10, 9,10, @@ -2659,7 +2658,7 @@ static const long _vq_lengthlist__44u0__p4_0[] = { static const static_codebook _44u0__p4_0 = { 4, 625, - (long *)_vq_lengthlist__44u0__p4_0, + (char *)_vq_lengthlist__44u0__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u0__p4_0, 0 @@ -2677,7 +2676,7 @@ static const long _vq_quantlist__44u0__p5_0[] = { 8, }; -static const long _vq_lengthlist__44u0__p5_0[] = { +static const char _vq_lengthlist__44u0__p5_0[] = { 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 7, 8, 8, 9, 9, 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,10, 7, 8, 8, @@ -2688,7 +2687,7 @@ static const long _vq_lengthlist__44u0__p5_0[] = { static const static_codebook _44u0__p5_0 = { 2, 81, - (long *)_vq_lengthlist__44u0__p5_0, + (char *)_vq_lengthlist__44u0__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u0__p5_0, 0 @@ -2710,7 +2709,7 @@ static const long _vq_quantlist__44u0__p6_0[] = { 12, }; -static const long _vq_lengthlist__44u0__p6_0[] = { +static const char _vq_lengthlist__44u0__p6_0[] = { 1, 4, 4, 6, 6, 8, 8,10, 9,11,10,14,13, 4, 6, 5, 8, 8, 9, 9,11,10,11,11,14,14, 4, 5, 6, 8, 8, 9, 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, @@ -2726,7 +2725,7 @@ static const long _vq_lengthlist__44u0__p6_0[] = { static const static_codebook _44u0__p6_0 = { 2, 169, - (long *)_vq_lengthlist__44u0__p6_0, + (char *)_vq_lengthlist__44u0__p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44u0__p6_0, 0 @@ -2740,14 +2739,14 @@ static const long _vq_quantlist__44u0__p6_1[] = { 4, }; -static const long _vq_lengthlist__44u0__p6_1[] = { +static const char _vq_lengthlist__44u0__p6_1[] = { 2, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 6, 6, 6, 6, }; static const static_codebook _44u0__p6_1 = { 2, 25, - (long *)_vq_lengthlist__44u0__p6_1, + (char *)_vq_lengthlist__44u0__p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u0__p6_1, 0 @@ -2761,7 +2760,7 @@ static const long _vq_quantlist__44u0__p7_0[] = { 4, }; -static const long _vq_lengthlist__44u0__p7_0[] = { +static const char _vq_lengthlist__44u0__p7_0[] = { 1, 4, 4,11,11, 9,11,11,11,11,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, @@ -2806,7 +2805,7 @@ static const long _vq_lengthlist__44u0__p7_0[] = { static const static_codebook _44u0__p7_0 = { 4, 625, - (long *)_vq_lengthlist__44u0__p7_0, + (char *)_vq_lengthlist__44u0__p7_0, 1, -518709248, 1626677248, 3, 0, (long *)_vq_quantlist__44u0__p7_0, 0 @@ -2828,7 +2827,7 @@ static const long _vq_quantlist__44u0__p7_1[] = { 12, }; -static const long _vq_lengthlist__44u0__p7_1[] = { +static const char _vq_lengthlist__44u0__p7_1[] = { 1, 4, 4, 6, 6, 6, 6, 7, 7, 8, 8, 9, 9, 5, 7, 7, 8, 7, 7, 7, 9, 8,10, 9,10,11, 5, 7, 7, 8, 8, 7, 7, 8, 9,10,10,11,11, 6, 8, 8, 9, 9, 9, 9,11,10, @@ -2844,7 +2843,7 @@ static const long _vq_lengthlist__44u0__p7_1[] = { static const static_codebook _44u0__p7_1 = { 2, 169, - (long *)_vq_lengthlist__44u0__p7_1, + (char *)_vq_lengthlist__44u0__p7_1, 1, -523010048, 1618608128, 4, 0, (long *)_vq_quantlist__44u0__p7_1, 0 @@ -2866,7 +2865,7 @@ static const long _vq_quantlist__44u0__p7_2[] = { 12, }; -static const long _vq_lengthlist__44u0__p7_2[] = { +static const char _vq_lengthlist__44u0__p7_2[] = { 2, 5, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 5, 5, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 5, 6, 5, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, 8, 8, 8, 9, 8, @@ -2882,13 +2881,13 @@ static const long _vq_lengthlist__44u0__p7_2[] = { static const static_codebook _44u0__p7_2 = { 2, 169, - (long *)_vq_lengthlist__44u0__p7_2, + (char *)_vq_lengthlist__44u0__p7_2, 1, -531103744, 1611661312, 4, 0, (long *)_vq_quantlist__44u0__p7_2, 0 }; -static const long _huff_lengthlist__44u0__short[] = { +static const char _huff_lengthlist__44u0__short[] = { 12,13,14,13,17,12,15,17, 5, 5, 6,10,10,11,15,16, 4, 3, 3, 7, 5, 7,10,16, 7, 7, 7,10, 9,11,12,16, 6, 5, 5, 9, 5, 6,10,16, 8, 7, 7, 9, 6, 7, 9,16, @@ -2897,13 +2896,13 @@ static const long _huff_lengthlist__44u0__short[] = { static const static_codebook _huff_book__44u0__short = { 2, 64, - (long *)_huff_lengthlist__44u0__short, + (char *)_huff_lengthlist__44u0__short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u1__long[] = { +static const char _huff_lengthlist__44u1__long[] = { 5, 8,13,10,17,11,11,15, 7, 2, 4, 5, 8, 7, 9,16, 13, 4, 3, 5, 6, 8,11,20,10, 4, 5, 5, 7, 6, 8,18, 15, 7, 6, 7, 8,10,14,20,10, 6, 7, 6, 9, 7, 8,17, @@ -2912,7 +2911,7 @@ static const long _huff_lengthlist__44u1__long[] = { static const static_codebook _huff_book__44u1__long = { 2, 64, - (long *)_huff_lengthlist__44u1__long, + (char *)_huff_lengthlist__44u1__long, 0, 0, 0, 0, 0, NULL, 0 @@ -2924,7 +2923,7 @@ static const long _vq_quantlist__44u1__p1_0[] = { 2, }; -static const long _vq_lengthlist__44u1__p1_0[] = { +static const char _vq_lengthlist__44u1__p1_0[] = { 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,11,11, 8, 10,10, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, 11, 8,11,11, 8,12,11,11,13,13,11,13,14, 7,11,11, @@ -2935,7 +2934,7 @@ static const long _vq_lengthlist__44u1__p1_0[] = { static const static_codebook _44u1__p1_0 = { 4, 81, - (long *)_vq_lengthlist__44u1__p1_0, + (char *)_vq_lengthlist__44u1__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u1__p1_0, 0 @@ -2947,7 +2946,7 @@ static const long _vq_quantlist__44u1__p2_0[] = { 2, }; -static const long _vq_lengthlist__44u1__p2_0[] = { +static const char _vq_lengthlist__44u1__p2_0[] = { 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 8, 6, 8, 8, 5, 7, 7, 6, 8, 8, 7, 8, 8, 4, 7, 7, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, @@ -2958,7 +2957,7 @@ static const long _vq_lengthlist__44u1__p2_0[] = { static const static_codebook _44u1__p2_0 = { 4, 81, - (long *)_vq_lengthlist__44u1__p2_0, + (char *)_vq_lengthlist__44u1__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u1__p2_0, 0 @@ -2972,7 +2971,7 @@ static const long _vq_quantlist__44u1__p3_0[] = { 4, }; -static const long _vq_lengthlist__44u1__p3_0[] = { +static const char _vq_lengthlist__44u1__p3_0[] = { 1, 5, 5, 8, 8, 5, 8, 7, 9, 9, 5, 7, 8, 9, 9, 9, 10, 9,12,12, 9, 9,10,12,12, 6, 8, 8,11,10, 8,10, 10,11,11, 8, 9,10,11,11,10,11,11,14,13,10,11,11, @@ -3017,7 +3016,7 @@ static const long _vq_lengthlist__44u1__p3_0[] = { static const static_codebook _44u1__p3_0 = { 4, 625, - (long *)_vq_lengthlist__44u1__p3_0, + (char *)_vq_lengthlist__44u1__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u1__p3_0, 0 @@ -3031,7 +3030,7 @@ static const long _vq_quantlist__44u1__p4_0[] = { 4, }; -static const long _vq_lengthlist__44u1__p4_0[] = { +static const char _vq_lengthlist__44u1__p4_0[] = { 4, 5, 5, 9, 9, 5, 6, 6, 9, 9, 5, 6, 6, 9, 9, 9, 10, 9,12,12, 9, 9,10,12,12, 5, 7, 7,10,10, 7, 7, 8,10,10, 6, 7, 8,10,10,10,10,10,11,13,10, 9,10, @@ -3076,7 +3075,7 @@ static const long _vq_lengthlist__44u1__p4_0[] = { static const static_codebook _44u1__p4_0 = { 4, 625, - (long *)_vq_lengthlist__44u1__p4_0, + (char *)_vq_lengthlist__44u1__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u1__p4_0, 0 @@ -3094,7 +3093,7 @@ static const long _vq_quantlist__44u1__p5_0[] = { 8, }; -static const long _vq_lengthlist__44u1__p5_0[] = { +static const char _vq_lengthlist__44u1__p5_0[] = { 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 7, 8, 8, 9, 9, 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,10, 7, 8, 8, @@ -3105,7 +3104,7 @@ static const long _vq_lengthlist__44u1__p5_0[] = { static const static_codebook _44u1__p5_0 = { 2, 81, - (long *)_vq_lengthlist__44u1__p5_0, + (char *)_vq_lengthlist__44u1__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u1__p5_0, 0 @@ -3127,7 +3126,7 @@ static const long _vq_quantlist__44u1__p6_0[] = { 12, }; -static const long _vq_lengthlist__44u1__p6_0[] = { +static const char _vq_lengthlist__44u1__p6_0[] = { 1, 4, 4, 6, 6, 8, 8,10, 9,11,10,14,13, 4, 6, 5, 8, 8, 9, 9,11,10,11,11,14,14, 4, 5, 6, 8, 8, 9, 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, @@ -3143,7 +3142,7 @@ static const long _vq_lengthlist__44u1__p6_0[] = { static const static_codebook _44u1__p6_0 = { 2, 169, - (long *)_vq_lengthlist__44u1__p6_0, + (char *)_vq_lengthlist__44u1__p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44u1__p6_0, 0 @@ -3157,14 +3156,14 @@ static const long _vq_quantlist__44u1__p6_1[] = { 4, }; -static const long _vq_lengthlist__44u1__p6_1[] = { +static const char _vq_lengthlist__44u1__p6_1[] = { 2, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 6, 6, 6, 6, }; static const static_codebook _44u1__p6_1 = { 2, 25, - (long *)_vq_lengthlist__44u1__p6_1, + (char *)_vq_lengthlist__44u1__p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u1__p6_1, 0 @@ -3180,7 +3179,7 @@ static const long _vq_quantlist__44u1__p7_0[] = { 6, }; -static const long _vq_lengthlist__44u1__p7_0[] = { +static const char _vq_lengthlist__44u1__p7_0[] = { 1, 3, 2, 9, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, @@ -3189,7 +3188,7 @@ static const long _vq_lengthlist__44u1__p7_0[] = { static const static_codebook _44u1__p7_0 = { 2, 49, - (long *)_vq_lengthlist__44u1__p7_0, + (char *)_vq_lengthlist__44u1__p7_0, 1, -518017024, 1626677248, 3, 0, (long *)_vq_quantlist__44u1__p7_0, 0 @@ -3211,7 +3210,7 @@ static const long _vq_quantlist__44u1__p7_1[] = { 12, }; -static const long _vq_lengthlist__44u1__p7_1[] = { +static const char _vq_lengthlist__44u1__p7_1[] = { 1, 4, 4, 6, 6, 6, 6, 7, 7, 8, 8, 9, 9, 5, 7, 7, 8, 7, 7, 7, 9, 8,10, 9,10,11, 5, 7, 7, 8, 8, 7, 7, 8, 9,10,10,11,11, 6, 8, 8, 9, 9, 9, 9,11,10, @@ -3227,7 +3226,7 @@ static const long _vq_lengthlist__44u1__p7_1[] = { static const static_codebook _44u1__p7_1 = { 2, 169, - (long *)_vq_lengthlist__44u1__p7_1, + (char *)_vq_lengthlist__44u1__p7_1, 1, -523010048, 1618608128, 4, 0, (long *)_vq_quantlist__44u1__p7_1, 0 @@ -3249,7 +3248,7 @@ static const long _vq_quantlist__44u1__p7_2[] = { 12, }; -static const long _vq_lengthlist__44u1__p7_2[] = { +static const char _vq_lengthlist__44u1__p7_2[] = { 2, 5, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 5, 5, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 5, 6, 5, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, 8, 8, 8, 9, 8, @@ -3265,13 +3264,13 @@ static const long _vq_lengthlist__44u1__p7_2[] = { static const static_codebook _44u1__p7_2 = { 2, 169, - (long *)_vq_lengthlist__44u1__p7_2, + (char *)_vq_lengthlist__44u1__p7_2, 1, -531103744, 1611661312, 4, 0, (long *)_vq_quantlist__44u1__p7_2, 0 }; -static const long _huff_lengthlist__44u1__short[] = { +static const char _huff_lengthlist__44u1__short[] = { 12,13,14,13,17,12,15,17, 5, 5, 6,10,10,11,15,16, 4, 3, 3, 7, 5, 7,10,16, 7, 7, 7,10, 9,11,12,16, 6, 5, 5, 9, 5, 6,10,16, 8, 7, 7, 9, 6, 7, 9,16, @@ -3280,13 +3279,13 @@ static const long _huff_lengthlist__44u1__short[] = { static const static_codebook _huff_book__44u1__short = { 2, 64, - (long *)_huff_lengthlist__44u1__short, + (char *)_huff_lengthlist__44u1__short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u2__long[] = { +static const char _huff_lengthlist__44u2__long[] = { 5, 9,14,12,15,13,10,13, 7, 4, 5, 6, 8, 7, 8,12, 13, 4, 3, 5, 5, 6, 9,15,12, 6, 5, 6, 6, 6, 7,14, 14, 7, 4, 6, 4, 6, 8,15,12, 6, 6, 5, 5, 5, 6,14, @@ -3295,7 +3294,7 @@ static const long _huff_lengthlist__44u2__long[] = { static const static_codebook _huff_book__44u2__long = { 2, 64, - (long *)_huff_lengthlist__44u2__long, + (char *)_huff_lengthlist__44u2__long, 0, 0, 0, 0, 0, NULL, 0 @@ -3307,7 +3306,7 @@ static const long _vq_quantlist__44u2__p1_0[] = { 2, }; -static const long _vq_lengthlist__44u2__p1_0[] = { +static const char _vq_lengthlist__44u2__p1_0[] = { 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,11,11, 8, 10,11, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, 11, 8,11,11, 8,11,11,11,13,14,11,13,13, 7,11,11, @@ -3318,7 +3317,7 @@ static const long _vq_lengthlist__44u2__p1_0[] = { static const static_codebook _44u2__p1_0 = { 4, 81, - (long *)_vq_lengthlist__44u2__p1_0, + (char *)_vq_lengthlist__44u2__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u2__p1_0, 0 @@ -3330,7 +3329,7 @@ static const long _vq_quantlist__44u2__p2_0[] = { 2, }; -static const long _vq_lengthlist__44u2__p2_0[] = { +static const char _vq_lengthlist__44u2__p2_0[] = { 2, 5, 5, 5, 6, 6, 5, 6, 6, 5, 6, 6, 7, 8, 8, 6, 8, 8, 5, 6, 6, 6, 8, 7, 7, 8, 8, 5, 6, 6, 7, 8, 8, 6, 8, 8, 6, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, @@ -3341,7 +3340,7 @@ static const long _vq_lengthlist__44u2__p2_0[] = { static const static_codebook _44u2__p2_0 = { 4, 81, - (long *)_vq_lengthlist__44u2__p2_0, + (char *)_vq_lengthlist__44u2__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u2__p2_0, 0 @@ -3355,7 +3354,7 @@ static const long _vq_quantlist__44u2__p3_0[] = { 4, }; -static const long _vq_lengthlist__44u2__p3_0[] = { +static const char _vq_lengthlist__44u2__p3_0[] = { 2, 4, 4, 7, 8, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 8, 9, 9,12,11, 8, 9, 9,11,12, 5, 7, 7,10,10, 7, 9, 9,11,11, 7, 9, 9,10,11,10,11,11,13,13, 9,10,11, @@ -3400,7 +3399,7 @@ static const long _vq_lengthlist__44u2__p3_0[] = { static const static_codebook _44u2__p3_0 = { 4, 625, - (long *)_vq_lengthlist__44u2__p3_0, + (char *)_vq_lengthlist__44u2__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u2__p3_0, 0 @@ -3414,7 +3413,7 @@ static const long _vq_quantlist__44u2__p4_0[] = { 4, }; -static const long _vq_lengthlist__44u2__p4_0[] = { +static const char _vq_lengthlist__44u2__p4_0[] = { 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 9, 9, 9,11,11, 9, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, 8,10,10, 7, 7, 8,10,10,10,10,10,11,12, 9,10,10, @@ -3459,7 +3458,7 @@ static const long _vq_lengthlist__44u2__p4_0[] = { static const static_codebook _44u2__p4_0 = { 4, 625, - (long *)_vq_lengthlist__44u2__p4_0, + (char *)_vq_lengthlist__44u2__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u2__p4_0, 0 @@ -3477,7 +3476,7 @@ static const long _vq_quantlist__44u2__p5_0[] = { 8, }; -static const long _vq_lengthlist__44u2__p5_0[] = { +static const char _vq_lengthlist__44u2__p5_0[] = { 1, 4, 4, 7, 7, 8, 8, 9, 9, 4, 6, 5, 8, 8, 8, 8, 10,10, 4, 5, 6, 8, 8, 8, 8,10,10, 7, 8, 8, 9, 9, 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 8, 8, 8, @@ -3488,7 +3487,7 @@ static const long _vq_lengthlist__44u2__p5_0[] = { static const static_codebook _44u2__p5_0 = { 2, 81, - (long *)_vq_lengthlist__44u2__p5_0, + (char *)_vq_lengthlist__44u2__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u2__p5_0, 0 @@ -3510,7 +3509,7 @@ static const long _vq_quantlist__44u2__p6_0[] = { 12, }; -static const long _vq_lengthlist__44u2__p6_0[] = { +static const char _vq_lengthlist__44u2__p6_0[] = { 1, 4, 4, 6, 6, 8, 8,10,10,11,11,14,13, 4, 6, 5, 8, 8, 9, 9,11,10,12,11,15,14, 4, 5, 6, 8, 8, 9, 9,11,11,11,11,14,14, 6, 8, 8,10, 9,11,11,11,11, @@ -3526,7 +3525,7 @@ static const long _vq_lengthlist__44u2__p6_0[] = { static const static_codebook _44u2__p6_0 = { 2, 169, - (long *)_vq_lengthlist__44u2__p6_0, + (char *)_vq_lengthlist__44u2__p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44u2__p6_0, 0 @@ -3540,14 +3539,14 @@ static const long _vq_quantlist__44u2__p6_1[] = { 4, }; -static const long _vq_lengthlist__44u2__p6_1[] = { +static const char _vq_lengthlist__44u2__p6_1[] = { 2, 4, 4, 5, 5, 4, 5, 5, 6, 5, 4, 5, 5, 5, 6, 5, 6, 5, 6, 6, 5, 5, 6, 6, 6, }; static const static_codebook _44u2__p6_1 = { 2, 25, - (long *)_vq_lengthlist__44u2__p6_1, + (char *)_vq_lengthlist__44u2__p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u2__p6_1, 0 @@ -3565,7 +3564,7 @@ static const long _vq_quantlist__44u2__p7_0[] = { 8, }; -static const long _vq_lengthlist__44u2__p7_0[] = { +static const char _vq_lengthlist__44u2__p7_0[] = { 1, 3, 2,12,12,12,12,12,12, 4,12,12,12,12,12,12, 12,12, 5,12,12,12,12,12,12,12,12,12,12,11,11,11, 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, @@ -3576,7 +3575,7 @@ static const long _vq_lengthlist__44u2__p7_0[] = { static const static_codebook _44u2__p7_0 = { 2, 81, - (long *)_vq_lengthlist__44u2__p7_0, + (char *)_vq_lengthlist__44u2__p7_0, 1, -516612096, 1626677248, 4, 0, (long *)_vq_quantlist__44u2__p7_0, 0 @@ -3598,7 +3597,7 @@ static const long _vq_quantlist__44u2__p7_1[] = { 12, }; -static const long _vq_lengthlist__44u2__p7_1[] = { +static const char _vq_lengthlist__44u2__p7_1[] = { 1, 4, 4, 7, 6, 7, 6, 8, 7, 9, 7, 9, 8, 4, 7, 6, 8, 8, 9, 8,10, 9,10,10,11,11, 4, 7, 7, 8, 8, 8, 8, 9,10,11,11,11,11, 6, 8, 8,10,10,10,10,11,11, @@ -3614,7 +3613,7 @@ static const long _vq_lengthlist__44u2__p7_1[] = { static const static_codebook _44u2__p7_1 = { 2, 169, - (long *)_vq_lengthlist__44u2__p7_1, + (char *)_vq_lengthlist__44u2__p7_1, 1, -523010048, 1618608128, 4, 0, (long *)_vq_quantlist__44u2__p7_1, 0 @@ -3636,7 +3635,7 @@ static const long _vq_quantlist__44u2__p7_2[] = { 12, }; -static const long _vq_lengthlist__44u2__p7_2[] = { +static const char _vq_lengthlist__44u2__p7_2[] = { 2, 5, 5, 6, 6, 7, 7, 8, 7, 8, 8, 8, 8, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 5, 6, 6, 7, 7, 8, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, 8, 8, 8, 8, 8, @@ -3652,13 +3651,13 @@ static const long _vq_lengthlist__44u2__p7_2[] = { static const static_codebook _44u2__p7_2 = { 2, 169, - (long *)_vq_lengthlist__44u2__p7_2, + (char *)_vq_lengthlist__44u2__p7_2, 1, -531103744, 1611661312, 4, 0, (long *)_vq_quantlist__44u2__p7_2, 0 }; -static const long _huff_lengthlist__44u2__short[] = { +static const char _huff_lengthlist__44u2__short[] = { 13,15,17,17,15,15,12,17,11, 9, 7,10,10, 9,12,17, 10, 6, 3, 6, 5, 7,10,17,15,10, 6, 9, 8, 9,11,17, 15, 8, 4, 7, 3, 5, 9,16,16,10, 5, 8, 4, 5, 8,16, @@ -3667,13 +3666,13 @@ static const long _huff_lengthlist__44u2__short[] = { static const static_codebook _huff_book__44u2__short = { 2, 64, - (long *)_huff_lengthlist__44u2__short, + (char *)_huff_lengthlist__44u2__short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u3__long[] = { +static const char _huff_lengthlist__44u3__long[] = { 6, 9,13,12,14,11,10,13, 8, 4, 5, 7, 8, 7, 8,12, 11, 4, 3, 5, 5, 7, 9,14,11, 6, 5, 6, 6, 6, 7,13, 13, 7, 5, 6, 4, 5, 7,14,11, 7, 6, 6, 5, 5, 6,13, @@ -3682,7 +3681,7 @@ static const long _huff_lengthlist__44u3__long[] = { static const static_codebook _huff_book__44u3__long = { 2, 64, - (long *)_huff_lengthlist__44u3__long, + (char *)_huff_lengthlist__44u3__long, 0, 0, 0, 0, 0, NULL, 0 @@ -3694,7 +3693,7 @@ static const long _vq_quantlist__44u3__p1_0[] = { 2, }; -static const long _vq_lengthlist__44u3__p1_0[] = { +static const char _vq_lengthlist__44u3__p1_0[] = { 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,10,11, 8, 10,11, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, 11, 8,11,11, 8,11,11,11,13,14,11,14,14, 8,11,11, @@ -3705,7 +3704,7 @@ static const long _vq_lengthlist__44u3__p1_0[] = { static const static_codebook _44u3__p1_0 = { 4, 81, - (long *)_vq_lengthlist__44u3__p1_0, + (char *)_vq_lengthlist__44u3__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u3__p1_0, 0 @@ -3717,7 +3716,7 @@ static const long _vq_quantlist__44u3__p2_0[] = { 2, }; -static const long _vq_lengthlist__44u3__p2_0[] = { +static const char _vq_lengthlist__44u3__p2_0[] = { 2, 5, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 7, 8, 8, 6, 8, 8, 5, 6, 6, 6, 8, 8, 7, 8, 8, 5, 7, 6, 7, 8, 8, 6, 8, 8, 7, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, @@ -3728,7 +3727,7 @@ static const long _vq_lengthlist__44u3__p2_0[] = { static const static_codebook _44u3__p2_0 = { 4, 81, - (long *)_vq_lengthlist__44u3__p2_0, + (char *)_vq_lengthlist__44u3__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u3__p2_0, 0 @@ -3742,7 +3741,7 @@ static const long _vq_quantlist__44u3__p3_0[] = { 4, }; -static const long _vq_lengthlist__44u3__p3_0[] = { +static const char _vq_lengthlist__44u3__p3_0[] = { 2, 4, 4, 7, 7, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 8, 9, 9,12,12, 8, 9, 9,11,12, 5, 7, 7,10,10, 7, 9, 9,11,11, 7, 9, 9,10,11,10,11,11,13,13, 9,10,11, @@ -3787,7 +3786,7 @@ static const long _vq_lengthlist__44u3__p3_0[] = { static const static_codebook _44u3__p3_0 = { 4, 625, - (long *)_vq_lengthlist__44u3__p3_0, + (char *)_vq_lengthlist__44u3__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u3__p3_0, 0 @@ -3801,7 +3800,7 @@ static const long _vq_quantlist__44u3__p4_0[] = { 4, }; -static const long _vq_lengthlist__44u3__p4_0[] = { +static const char _vq_lengthlist__44u3__p4_0[] = { 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 9, 9, 9,11,11, 9, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, 8,10,10, 7, 7, 8,10,10, 9,10,10,11,12, 9,10,10, @@ -3846,7 +3845,7 @@ static const long _vq_lengthlist__44u3__p4_0[] = { static const static_codebook _44u3__p4_0 = { 4, 625, - (long *)_vq_lengthlist__44u3__p4_0, + (char *)_vq_lengthlist__44u3__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u3__p4_0, 0 @@ -3864,7 +3863,7 @@ static const long _vq_quantlist__44u3__p5_0[] = { 8, }; -static const long _vq_lengthlist__44u3__p5_0[] = { +static const char _vq_lengthlist__44u3__p5_0[] = { 2, 3, 3, 6, 6, 7, 7, 9, 9, 4, 5, 5, 7, 7, 8, 8, 10,10, 4, 5, 5, 7, 7, 8, 8,10,10, 6, 7, 7, 8, 8, 9, 9,11,10, 6, 7, 7, 8, 8, 9, 9,10,10, 7, 8, 8, @@ -3875,7 +3874,7 @@ static const long _vq_lengthlist__44u3__p5_0[] = { static const static_codebook _44u3__p5_0 = { 2, 81, - (long *)_vq_lengthlist__44u3__p5_0, + (char *)_vq_lengthlist__44u3__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u3__p5_0, 0 @@ -3897,7 +3896,7 @@ static const long _vq_quantlist__44u3__p6_0[] = { 12, }; -static const long _vq_lengthlist__44u3__p6_0[] = { +static const char _vq_lengthlist__44u3__p6_0[] = { 1, 4, 4, 6, 6, 8, 8, 9, 9,10,11,13,14, 4, 6, 5, 8, 8, 9, 9,10,10,11,11,14,14, 4, 6, 6, 8, 8, 9, 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, @@ -3913,7 +3912,7 @@ static const long _vq_lengthlist__44u3__p6_0[] = { static const static_codebook _44u3__p6_0 = { 2, 169, - (long *)_vq_lengthlist__44u3__p6_0, + (char *)_vq_lengthlist__44u3__p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44u3__p6_0, 0 @@ -3927,14 +3926,14 @@ static const long _vq_quantlist__44u3__p6_1[] = { 4, }; -static const long _vq_lengthlist__44u3__p6_1[] = { +static const char _vq_lengthlist__44u3__p6_1[] = { 2, 4, 4, 5, 5, 4, 5, 5, 6, 5, 4, 5, 5, 5, 6, 5, 6, 5, 6, 6, 5, 5, 6, 6, 6, }; static const static_codebook _44u3__p6_1 = { 2, 25, - (long *)_vq_lengthlist__44u3__p6_1, + (char *)_vq_lengthlist__44u3__p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u3__p6_1, 0 @@ -3952,7 +3951,7 @@ static const long _vq_quantlist__44u3__p7_0[] = { 8, }; -static const long _vq_lengthlist__44u3__p7_0[] = { +static const char _vq_lengthlist__44u3__p7_0[] = { 1, 3, 3,10,10,10,10,10,10, 4,10,10,10,10,10,10, 10,10, 4,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -3963,7 +3962,7 @@ static const long _vq_lengthlist__44u3__p7_0[] = { static const static_codebook _44u3__p7_0 = { 2, 81, - (long *)_vq_lengthlist__44u3__p7_0, + (char *)_vq_lengthlist__44u3__p7_0, 1, -515907584, 1627381760, 4, 0, (long *)_vq_quantlist__44u3__p7_0, 0 @@ -3987,7 +3986,7 @@ static const long _vq_quantlist__44u3__p7_1[] = { 14, }; -static const long _vq_lengthlist__44u3__p7_1[] = { +static const char _vq_lengthlist__44u3__p7_1[] = { 1, 4, 4, 6, 6, 7, 6, 8, 7, 9, 8,10, 9,11,11, 4, 7, 7, 8, 7, 9, 9,10,10,11,11,11,11,12,12, 4, 7, 7, 7, 7, 9, 9,10,10,11,11,12,12,12,11, 6, 8, 8, @@ -4007,7 +4006,7 @@ static const long _vq_lengthlist__44u3__p7_1[] = { static const static_codebook _44u3__p7_1 = { 2, 225, - (long *)_vq_lengthlist__44u3__p7_1, + (char *)_vq_lengthlist__44u3__p7_1, 1, -522338304, 1620115456, 4, 0, (long *)_vq_quantlist__44u3__p7_1, 0 @@ -4033,7 +4032,7 @@ static const long _vq_quantlist__44u3__p7_2[] = { 16, }; -static const long _vq_lengthlist__44u3__p7_2[] = { +static const char _vq_lengthlist__44u3__p7_2[] = { 2, 5, 5, 7, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10,10, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 9, 9, 9, @@ -4057,13 +4056,13 @@ static const long _vq_lengthlist__44u3__p7_2[] = { static const static_codebook _44u3__p7_2 = { 2, 289, - (long *)_vq_lengthlist__44u3__p7_2, + (char *)_vq_lengthlist__44u3__p7_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44u3__p7_2, 0 }; -static const long _huff_lengthlist__44u3__short[] = { +static const char _huff_lengthlist__44u3__short[] = { 14,14,14,15,13,15,12,16,10, 8, 7, 9, 9, 8,12,16, 10, 5, 4, 6, 5, 6, 9,16,14, 8, 6, 8, 7, 8,10,16, 14, 7, 4, 6, 3, 5, 8,16,15, 9, 5, 7, 4, 4, 7,16, @@ -4072,13 +4071,13 @@ static const long _huff_lengthlist__44u3__short[] = { static const static_codebook _huff_book__44u3__short = { 2, 64, - (long *)_huff_lengthlist__44u3__short, + (char *)_huff_lengthlist__44u3__short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u4__long[] = { +static const char _huff_lengthlist__44u4__long[] = { 3, 8,12,12,13,12,11,13, 5, 4, 6, 7, 8, 8, 9,13, 9, 5, 4, 5, 5, 7, 9,13, 9, 6, 5, 6, 6, 7, 8,12, 12, 7, 5, 6, 4, 5, 8,13,11, 7, 6, 6, 5, 5, 6,12, @@ -4087,7 +4086,7 @@ static const long _huff_lengthlist__44u4__long[] = { static const static_codebook _huff_book__44u4__long = { 2, 64, - (long *)_huff_lengthlist__44u4__long, + (char *)_huff_lengthlist__44u4__long, 0, 0, 0, 0, 0, NULL, 0 @@ -4099,7 +4098,7 @@ static const long _vq_quantlist__44u4__p1_0[] = { 2, }; -static const long _vq_lengthlist__44u4__p1_0[] = { +static const char _vq_lengthlist__44u4__p1_0[] = { 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,10,11, 8, 10,11, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, 11, 8,11,11, 8,11,11,11,13,14,11,15,14, 8,11,11, @@ -4110,7 +4109,7 @@ static const long _vq_lengthlist__44u4__p1_0[] = { static const static_codebook _44u4__p1_0 = { 4, 81, - (long *)_vq_lengthlist__44u4__p1_0, + (char *)_vq_lengthlist__44u4__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u4__p1_0, 0 @@ -4122,7 +4121,7 @@ static const long _vq_quantlist__44u4__p2_0[] = { 2, }; -static const long _vq_lengthlist__44u4__p2_0[] = { +static const char _vq_lengthlist__44u4__p2_0[] = { 2, 5, 5, 5, 6, 6, 5, 6, 6, 5, 6, 6, 7, 8, 8, 6, 8, 8, 5, 6, 6, 6, 8, 8, 7, 8, 8, 5, 7, 6, 6, 8, 8, 6, 8, 8, 6, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, @@ -4133,7 +4132,7 @@ static const long _vq_lengthlist__44u4__p2_0[] = { static const static_codebook _44u4__p2_0 = { 4, 81, - (long *)_vq_lengthlist__44u4__p2_0, + (char *)_vq_lengthlist__44u4__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u4__p2_0, 0 @@ -4147,7 +4146,7 @@ static const long _vq_quantlist__44u4__p3_0[] = { 4, }; -static const long _vq_lengthlist__44u4__p3_0[] = { +static const char _vq_lengthlist__44u4__p3_0[] = { 2, 4, 4, 8, 8, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 8, 10, 9,12,12, 8, 9,10,12,12, 5, 7, 7,10,10, 7, 9, 9,11,11, 7, 9, 9,11,11,10,12,11,14,14, 9,10,11, @@ -4192,7 +4191,7 @@ static const long _vq_lengthlist__44u4__p3_0[] = { static const static_codebook _44u4__p3_0 = { 4, 625, - (long *)_vq_lengthlist__44u4__p3_0, + (char *)_vq_lengthlist__44u4__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u4__p3_0, 0 @@ -4206,7 +4205,7 @@ static const long _vq_quantlist__44u4__p4_0[] = { 4, }; -static const long _vq_lengthlist__44u4__p4_0[] = { +static const char _vq_lengthlist__44u4__p4_0[] = { 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 9, 9, 9,11,11, 8, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, 8,10,10, 7, 7, 8,10,10, 9,10,10,11,12, 9,10,10, @@ -4251,7 +4250,7 @@ static const long _vq_lengthlist__44u4__p4_0[] = { static const static_codebook _44u4__p4_0 = { 4, 625, - (long *)_vq_lengthlist__44u4__p4_0, + (char *)_vq_lengthlist__44u4__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u4__p4_0, 0 @@ -4269,7 +4268,7 @@ static const long _vq_quantlist__44u4__p5_0[] = { 8, }; -static const long _vq_lengthlist__44u4__p5_0[] = { +static const char _vq_lengthlist__44u4__p5_0[] = { 2, 3, 3, 6, 6, 7, 7, 9, 9, 4, 5, 5, 7, 7, 8, 8, 10, 9, 4, 5, 5, 7, 7, 8, 8,10,10, 6, 7, 7, 8, 8, 9, 9,11,10, 6, 7, 7, 8, 8, 9, 9,10,11, 7, 8, 8, @@ -4280,7 +4279,7 @@ static const long _vq_lengthlist__44u4__p5_0[] = { static const static_codebook _44u4__p5_0 = { 2, 81, - (long *)_vq_lengthlist__44u4__p5_0, + (char *)_vq_lengthlist__44u4__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u4__p5_0, 0 @@ -4302,7 +4301,7 @@ static const long _vq_quantlist__44u4__p6_0[] = { 12, }; -static const long _vq_lengthlist__44u4__p6_0[] = { +static const char _vq_lengthlist__44u4__p6_0[] = { 1, 4, 4, 6, 6, 8, 8, 9, 9,11,10,13,13, 4, 6, 5, 8, 8, 9, 9,10,10,11,11,14,14, 4, 6, 6, 8, 8, 9, 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, @@ -4318,7 +4317,7 @@ static const long _vq_lengthlist__44u4__p6_0[] = { static const static_codebook _44u4__p6_0 = { 2, 169, - (long *)_vq_lengthlist__44u4__p6_0, + (char *)_vq_lengthlist__44u4__p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44u4__p6_0, 0 @@ -4332,14 +4331,14 @@ static const long _vq_quantlist__44u4__p6_1[] = { 4, }; -static const long _vq_lengthlist__44u4__p6_1[] = { +static const char _vq_lengthlist__44u4__p6_1[] = { 2, 4, 4, 5, 5, 4, 5, 5, 6, 5, 4, 5, 5, 5, 6, 5, 6, 5, 6, 6, 5, 5, 6, 6, 6, }; static const static_codebook _44u4__p6_1 = { 2, 25, - (long *)_vq_lengthlist__44u4__p6_1, + (char *)_vq_lengthlist__44u4__p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u4__p6_1, 0 @@ -4361,7 +4360,7 @@ static const long _vq_quantlist__44u4__p7_0[] = { 12, }; -static const long _vq_lengthlist__44u4__p7_0[] = { +static const char _vq_lengthlist__44u4__p7_0[] = { 1, 3, 3,12,12,12,12,12,12,12,12,12,12, 3,12,11, 12,12,12,12,12,12,12,12,12,12, 4,11,10,12,12,12, 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, @@ -4377,7 +4376,7 @@ static const long _vq_lengthlist__44u4__p7_0[] = { static const static_codebook _44u4__p7_0 = { 2, 169, - (long *)_vq_lengthlist__44u4__p7_0, + (char *)_vq_lengthlist__44u4__p7_0, 1, -514332672, 1627381760, 4, 0, (long *)_vq_quantlist__44u4__p7_0, 0 @@ -4401,7 +4400,7 @@ static const long _vq_quantlist__44u4__p7_1[] = { 14, }; -static const long _vq_lengthlist__44u4__p7_1[] = { +static const char _vq_lengthlist__44u4__p7_1[] = { 1, 4, 4, 6, 6, 7, 7, 9, 8,10, 8,10, 9,11,11, 4, 7, 6, 8, 7, 9, 9,10,10,11,10,11,10,12,10, 4, 6, 7, 8, 8, 9, 9,10,10,11,11,11,11,12,12, 6, 8, 8, @@ -4421,7 +4420,7 @@ static const long _vq_lengthlist__44u4__p7_1[] = { static const static_codebook _44u4__p7_1 = { 2, 225, - (long *)_vq_lengthlist__44u4__p7_1, + (char *)_vq_lengthlist__44u4__p7_1, 1, -522338304, 1620115456, 4, 0, (long *)_vq_quantlist__44u4__p7_1, 0 @@ -4447,7 +4446,7 @@ static const long _vq_quantlist__44u4__p7_2[] = { 16, }; -static const long _vq_lengthlist__44u4__p7_2[] = { +static const char _vq_lengthlist__44u4__p7_2[] = { 2, 5, 5, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, @@ -4471,13 +4470,13 @@ static const long _vq_lengthlist__44u4__p7_2[] = { static const static_codebook _44u4__p7_2 = { 2, 289, - (long *)_vq_lengthlist__44u4__p7_2, + (char *)_vq_lengthlist__44u4__p7_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44u4__p7_2, 0 }; -static const long _huff_lengthlist__44u4__short[] = { +static const char _huff_lengthlist__44u4__short[] = { 14,17,15,17,16,14,13,16,10, 7, 7,10,13,10,15,16, 9, 4, 4, 6, 5, 7, 9,16,12, 8, 7, 8, 8, 8,11,16, 14, 7, 4, 6, 3, 5, 8,15,13, 8, 5, 7, 4, 5, 7,16, @@ -4486,13 +4485,13 @@ static const long _huff_lengthlist__44u4__short[] = { static const static_codebook _huff_book__44u4__short = { 2, 64, - (long *)_huff_lengthlist__44u4__short, + (char *)_huff_lengthlist__44u4__short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u5__long[] = { +static const char _huff_lengthlist__44u5__long[] = { 3, 8,13,12,14,12,16,11,13,14, 5, 4, 5, 6, 7, 8, 10, 9,12,15,10, 5, 5, 5, 6, 8, 9, 9,13,15,10, 5, 5, 6, 6, 7, 8, 8,11,13,12, 7, 5, 6, 4, 6, 7, 7, @@ -4504,7 +4503,7 @@ static const long _huff_lengthlist__44u5__long[] = { static const static_codebook _huff_book__44u5__long = { 2, 100, - (long *)_huff_lengthlist__44u5__long, + (char *)_huff_lengthlist__44u5__long, 0, 0, 0, 0, 0, NULL, 0 @@ -4516,7 +4515,7 @@ static const long _vq_quantlist__44u5__p1_0[] = { 2, }; -static const long _vq_lengthlist__44u5__p1_0[] = { +static const char _vq_lengthlist__44u5__p1_0[] = { 1, 4, 4, 5, 8, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, 9,10, 5, 8, 8, 7,10, 9, 8,10,10, 5, 8, 8, 8,10, 10, 8,10,10, 8,10,10,10,12,13,10,13,13, 7,10,10, @@ -4527,7 +4526,7 @@ static const long _vq_lengthlist__44u5__p1_0[] = { static const static_codebook _44u5__p1_0 = { 4, 81, - (long *)_vq_lengthlist__44u5__p1_0, + (char *)_vq_lengthlist__44u5__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u5__p1_0, 0 @@ -4539,7 +4538,7 @@ static const long _vq_quantlist__44u5__p2_0[] = { 2, }; -static const long _vq_lengthlist__44u5__p2_0[] = { +static const char _vq_lengthlist__44u5__p2_0[] = { 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 8, 8, 6, 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 8, 5, 6, 6, 6, 8, 8, 6, 8, 8, 6, 8, 8, 8, 9, 9, 8, 9, 9, 6, 8, 7, @@ -4550,7 +4549,7 @@ static const long _vq_lengthlist__44u5__p2_0[] = { static const static_codebook _44u5__p2_0 = { 4, 81, - (long *)_vq_lengthlist__44u5__p2_0, + (char *)_vq_lengthlist__44u5__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u5__p2_0, 0 @@ -4564,7 +4563,7 @@ static const long _vq_quantlist__44u5__p3_0[] = { 4, }; -static const long _vq_lengthlist__44u5__p3_0[] = { +static const char _vq_lengthlist__44u5__p3_0[] = { 2, 4, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, 10, 9,13,12, 8, 9,10,12,12, 5, 7, 7,10,10, 7, 9, 9,11,11, 6, 8, 9,11,11,10,11,11,14,14, 9,10,11, @@ -4609,7 +4608,7 @@ static const long _vq_lengthlist__44u5__p3_0[] = { static const static_codebook _44u5__p3_0 = { 4, 625, - (long *)_vq_lengthlist__44u5__p3_0, + (char *)_vq_lengthlist__44u5__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u5__p3_0, 0 @@ -4623,7 +4622,7 @@ static const long _vq_quantlist__44u5__p4_0[] = { 4, }; -static const long _vq_lengthlist__44u5__p4_0[] = { +static const char _vq_lengthlist__44u5__p4_0[] = { 4, 5, 5, 8, 8, 6, 7, 6, 9, 9, 6, 6, 7, 9, 9, 8, 9, 9,11,11, 8, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 8, 8,10,10, 6, 7, 8, 9,10, 9,10,10,11,12, 9, 9,10, @@ -4668,7 +4667,7 @@ static const long _vq_lengthlist__44u5__p4_0[] = { static const static_codebook _44u5__p4_0 = { 4, 625, - (long *)_vq_lengthlist__44u5__p4_0, + (char *)_vq_lengthlist__44u5__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u5__p4_0, 0 @@ -4686,7 +4685,7 @@ static const long _vq_quantlist__44u5__p5_0[] = { 8, }; -static const long _vq_lengthlist__44u5__p5_0[] = { +static const char _vq_lengthlist__44u5__p5_0[] = { 2, 3, 3, 6, 6, 8, 8,10,10, 4, 5, 5, 8, 7, 8, 8, 11,10, 3, 5, 5, 7, 8, 8, 8,10,11, 6, 8, 7,10, 9, 10,10,11,11, 6, 7, 8, 9, 9, 9,10,11,12, 8, 8, 8, @@ -4697,7 +4696,7 @@ static const long _vq_lengthlist__44u5__p5_0[] = { static const static_codebook _44u5__p5_0 = { 2, 81, - (long *)_vq_lengthlist__44u5__p5_0, + (char *)_vq_lengthlist__44u5__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u5__p5_0, 0 @@ -4715,7 +4714,7 @@ static const long _vq_quantlist__44u5__p6_0[] = { 8, }; -static const long _vq_lengthlist__44u5__p6_0[] = { +static const char _vq_lengthlist__44u5__p6_0[] = { 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 7, 7, 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, 8, 8,10,10, 6, 6, 6, 7, 7, 8, 8,10,10, 7, 7, 7, @@ -4726,7 +4725,7 @@ static const long _vq_lengthlist__44u5__p6_0[] = { static const static_codebook _44u5__p6_0 = { 2, 81, - (long *)_vq_lengthlist__44u5__p6_0, + (char *)_vq_lengthlist__44u5__p6_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u5__p6_0, 0 @@ -4738,7 +4737,7 @@ static const long _vq_quantlist__44u5__p7_0[] = { 2, }; -static const long _vq_lengthlist__44u5__p7_0[] = { +static const char _vq_lengthlist__44u5__p7_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 9, 8,11,10, 7, 11,10, 5, 9, 9, 7,10,10, 8,10,11, 4, 9, 9, 9,12, 12, 9,12,12, 8,12,12,11,12,12,10,12,13, 7,12,12, @@ -4749,7 +4748,7 @@ static const long _vq_lengthlist__44u5__p7_0[] = { static const static_codebook _44u5__p7_0 = { 4, 81, - (long *)_vq_lengthlist__44u5__p7_0, + (char *)_vq_lengthlist__44u5__p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44u5__p7_0, 0 @@ -4769,7 +4768,7 @@ static const long _vq_quantlist__44u5__p7_1[] = { 10, }; -static const long _vq_lengthlist__44u5__p7_1[] = { +static const char _vq_lengthlist__44u5__p7_1[] = { 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 7, 7, 8, 8, 9, 8, 8, 9, 4, 5, 5, 7, 7, 8, 8, 9, 9, 8, 9, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 6, 7, 7, 8, @@ -4782,7 +4781,7 @@ static const long _vq_lengthlist__44u5__p7_1[] = { static const static_codebook _44u5__p7_1 = { 2, 121, - (long *)_vq_lengthlist__44u5__p7_1, + (char *)_vq_lengthlist__44u5__p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44u5__p7_1, 0 @@ -4802,7 +4801,7 @@ static const long _vq_quantlist__44u5__p8_0[] = { 10, }; -static const long _vq_lengthlist__44u5__p8_0[] = { +static const char _vq_lengthlist__44u5__p8_0[] = { 1, 4, 4, 6, 6, 8, 8, 9, 9,10,10, 4, 6, 6, 7, 7, 9, 9,10,10,11,11, 4, 6, 6, 7, 7, 9, 9,10,10,11, 11, 6, 8, 7, 9, 9,10,10,11,11,13,12, 6, 8, 8, 9, @@ -4815,7 +4814,7 @@ static const long _vq_lengthlist__44u5__p8_0[] = { static const static_codebook _44u5__p8_0 = { 2, 121, - (long *)_vq_lengthlist__44u5__p8_0, + (char *)_vq_lengthlist__44u5__p8_0, 1, -524582912, 1618345984, 4, 0, (long *)_vq_quantlist__44u5__p8_0, 0 @@ -4835,7 +4834,7 @@ static const long _vq_quantlist__44u5__p8_1[] = { 10, }; -static const long _vq_lengthlist__44u5__p8_1[] = { +static const char _vq_lengthlist__44u5__p8_1[] = { 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 5, 7, 6, 7, 7, 8, 8, 8, 8, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 6, 7, 6, 7, 7, 8, 8, 8, 8, 8, 8, 6, 6, 7, 7, @@ -4848,7 +4847,7 @@ static const long _vq_lengthlist__44u5__p8_1[] = { static const static_codebook _44u5__p8_1 = { 2, 121, - (long *)_vq_lengthlist__44u5__p8_1, + (char *)_vq_lengthlist__44u5__p8_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44u5__p8_1, 0 @@ -4870,7 +4869,7 @@ static const long _vq_quantlist__44u5__p9_0[] = { 12, }; -static const long _vq_lengthlist__44u5__p9_0[] = { +static const char _vq_lengthlist__44u5__p9_0[] = { 1, 3, 2,12,10,13,13,13,13,13,13,13,13, 4, 9, 9, 13,13,13,13,13,13,13,13,13,13, 5,10, 9,13,13,13, 13,13,13,13,13,13,13,12,13,13,13,13,13,13,13,13, @@ -4886,7 +4885,7 @@ static const long _vq_lengthlist__44u5__p9_0[] = { static const static_codebook _44u5__p9_0 = { 2, 169, - (long *)_vq_lengthlist__44u5__p9_0, + (char *)_vq_lengthlist__44u5__p9_0, 1, -514332672, 1627381760, 4, 0, (long *)_vq_quantlist__44u5__p9_0, 0 @@ -4910,7 +4909,7 @@ static const long _vq_quantlist__44u5__p9_1[] = { 14, }; -static const long _vq_lengthlist__44u5__p9_1[] = { +static const char _vq_lengthlist__44u5__p9_1[] = { 1, 4, 4, 7, 7, 8, 8, 8, 7, 8, 7, 9, 8, 9, 9, 4, 7, 6, 9, 8,10,10, 9, 8, 9, 9, 9, 9, 9, 8, 5, 6, 6, 8, 9,10,10, 9, 9, 9,10,10,10,10,11, 7, 8, 8, @@ -4930,7 +4929,7 @@ static const long _vq_lengthlist__44u5__p9_1[] = { static const static_codebook _44u5__p9_1 = { 2, 225, - (long *)_vq_lengthlist__44u5__p9_1, + (char *)_vq_lengthlist__44u5__p9_1, 1, -522338304, 1620115456, 4, 0, (long *)_vq_quantlist__44u5__p9_1, 0 @@ -4956,7 +4955,7 @@ static const long _vq_quantlist__44u5__p9_2[] = { 16, }; -static const long _vq_lengthlist__44u5__p9_2[] = { +static const char _vq_lengthlist__44u5__p9_2[] = { 2, 5, 5, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, @@ -4980,13 +4979,13 @@ static const long _vq_lengthlist__44u5__p9_2[] = { static const static_codebook _44u5__p9_2 = { 2, 289, - (long *)_vq_lengthlist__44u5__p9_2, + (char *)_vq_lengthlist__44u5__p9_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44u5__p9_2, 0 }; -static const long _huff_lengthlist__44u5__short[] = { +static const char _huff_lengthlist__44u5__short[] = { 4,10,17,13,17,13,17,17,17,17, 3, 6, 8, 9,11, 9, 15,12,16,17, 6, 5, 5, 7, 7, 8,10,11,17,17, 7, 8, 7, 9, 9,10,13,13,17,17, 8, 6, 5, 7, 4, 7, 5, 8, @@ -4998,13 +4997,13 @@ static const long _huff_lengthlist__44u5__short[] = { static const static_codebook _huff_book__44u5__short = { 2, 100, - (long *)_huff_lengthlist__44u5__short, + (char *)_huff_lengthlist__44u5__short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u6__long[] = { +static const char _huff_lengthlist__44u6__long[] = { 3, 9,14,13,14,13,16,12,13,14, 5, 4, 6, 6, 8, 9, 11,10,12,15,10, 5, 5, 6, 6, 8,10,10,13,16,10, 6, 6, 6, 6, 8, 9, 9,12,14,13, 7, 6, 6, 4, 6, 6, 7, @@ -5016,7 +5015,7 @@ static const long _huff_lengthlist__44u6__long[] = { static const static_codebook _huff_book__44u6__long = { 2, 100, - (long *)_huff_lengthlist__44u6__long, + (char *)_huff_lengthlist__44u6__long, 0, 0, 0, 0, 0, NULL, 0 @@ -5028,7 +5027,7 @@ static const long _vq_quantlist__44u6__p1_0[] = { 2, }; -static const long _vq_lengthlist__44u6__p1_0[] = { +static const char _vq_lengthlist__44u6__p1_0[] = { 1, 4, 4, 4, 8, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, 9,10, 5, 8, 8, 7,10, 9, 8,10,10, 5, 8, 8, 8,10, 10, 8,10,10, 8,10,10,10,12,13,10,13,13, 7,10,10, @@ -5039,7 +5038,7 @@ static const long _vq_lengthlist__44u6__p1_0[] = { static const static_codebook _44u6__p1_0 = { 4, 81, - (long *)_vq_lengthlist__44u6__p1_0, + (char *)_vq_lengthlist__44u6__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u6__p1_0, 0 @@ -5051,7 +5050,7 @@ static const long _vq_quantlist__44u6__p2_0[] = { 2, }; -static const long _vq_lengthlist__44u6__p2_0[] = { +static const char _vq_lengthlist__44u6__p2_0[] = { 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 8, 8, 6, 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 8, 5, 6, 6, 6, 8, 8, 6, 8, 8, 6, 8, 8, 8, 9, 9, 8, 9, 9, 6, 7, 7, @@ -5062,7 +5061,7 @@ static const long _vq_lengthlist__44u6__p2_0[] = { static const static_codebook _44u6__p2_0 = { 4, 81, - (long *)_vq_lengthlist__44u6__p2_0, + (char *)_vq_lengthlist__44u6__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u6__p2_0, 0 @@ -5076,7 +5075,7 @@ static const long _vq_quantlist__44u6__p3_0[] = { 4, }; -static const long _vq_lengthlist__44u6__p3_0[] = { +static const char _vq_lengthlist__44u6__p3_0[] = { 2, 5, 4, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, 9, 9,13,12, 8, 9,10,12,13, 5, 7, 7,10, 9, 7, 9, 9,11,11, 7, 8, 9,11,11,10,11,11,14,14, 9,10,11, @@ -5121,7 +5120,7 @@ static const long _vq_lengthlist__44u6__p3_0[] = { static const static_codebook _44u6__p3_0 = { 4, 625, - (long *)_vq_lengthlist__44u6__p3_0, + (char *)_vq_lengthlist__44u6__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u6__p3_0, 0 @@ -5135,7 +5134,7 @@ static const long _vq_quantlist__44u6__p4_0[] = { 4, }; -static const long _vq_lengthlist__44u6__p4_0[] = { +static const char _vq_lengthlist__44u6__p4_0[] = { 4, 5, 5, 8, 8, 6, 7, 6, 9, 9, 6, 6, 7, 9, 9, 8, 9, 9,11,11, 8, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 8, 8,10,10, 7, 7, 8, 9,10, 9,10,10,11,11, 9, 9,10, @@ -5180,7 +5179,7 @@ static const long _vq_lengthlist__44u6__p4_0[] = { static const static_codebook _44u6__p4_0 = { 4, 625, - (long *)_vq_lengthlist__44u6__p4_0, + (char *)_vq_lengthlist__44u6__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u6__p4_0, 0 @@ -5198,7 +5197,7 @@ static const long _vq_quantlist__44u6__p5_0[] = { 8, }; -static const long _vq_lengthlist__44u6__p5_0[] = { +static const char _vq_lengthlist__44u6__p5_0[] = { 2, 3, 3, 6, 6, 8, 8,10,10, 4, 5, 5, 8, 7, 8, 8, 11,11, 3, 5, 5, 7, 8, 8, 8,11,11, 6, 8, 7, 9, 9, 10, 9,12,11, 6, 7, 8, 9, 9, 9,10,11,12, 8, 8, 8, @@ -5209,7 +5208,7 @@ static const long _vq_lengthlist__44u6__p5_0[] = { static const static_codebook _44u6__p5_0 = { 2, 81, - (long *)_vq_lengthlist__44u6__p5_0, + (char *)_vq_lengthlist__44u6__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u6__p5_0, 0 @@ -5227,7 +5226,7 @@ static const long _vq_quantlist__44u6__p6_0[] = { 8, }; -static const long _vq_lengthlist__44u6__p6_0[] = { +static const char _vq_lengthlist__44u6__p6_0[] = { 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 7, 7, 9, 9, 4, 4, 5, 6, 6, 7, 8, 9, 9, 5, 6, 6, 7, 7, 8, 8,10,10, 5, 6, 6, 7, 7, 8, 8,10,10, 7, 8, 7, @@ -5238,7 +5237,7 @@ static const long _vq_lengthlist__44u6__p6_0[] = { static const static_codebook _44u6__p6_0 = { 2, 81, - (long *)_vq_lengthlist__44u6__p6_0, + (char *)_vq_lengthlist__44u6__p6_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u6__p6_0, 0 @@ -5250,7 +5249,7 @@ static const long _vq_quantlist__44u6__p7_0[] = { 2, }; -static const long _vq_lengthlist__44u6__p7_0[] = { +static const char _vq_lengthlist__44u6__p7_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 8, 7,10,10, 8, 10,10, 5, 8, 9, 7,10,10, 7,10, 9, 4, 8, 8, 9,11, 11, 8,11,11, 7,11,11,10,10,13,10,13,13, 7,11,11, @@ -5261,7 +5260,7 @@ static const long _vq_lengthlist__44u6__p7_0[] = { static const static_codebook _44u6__p7_0 = { 4, 81, - (long *)_vq_lengthlist__44u6__p7_0, + (char *)_vq_lengthlist__44u6__p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44u6__p7_0, 0 @@ -5281,7 +5280,7 @@ static const long _vq_quantlist__44u6__p7_1[] = { 10, }; -static const long _vq_lengthlist__44u6__p7_1[] = { +static const char _vq_lengthlist__44u6__p7_1[] = { 3, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 7, 6, 8, 8, 8, 8, 8, 8, 4, 5, 5, 6, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, @@ -5294,7 +5293,7 @@ static const long _vq_lengthlist__44u6__p7_1[] = { static const static_codebook _44u6__p7_1 = { 2, 121, - (long *)_vq_lengthlist__44u6__p7_1, + (char *)_vq_lengthlist__44u6__p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44u6__p7_1, 0 @@ -5314,7 +5313,7 @@ static const long _vq_quantlist__44u6__p8_0[] = { 10, }; -static const long _vq_lengthlist__44u6__p8_0[] = { +static const char _vq_lengthlist__44u6__p8_0[] = { 1, 4, 4, 6, 6, 8, 8, 9, 9,10,10, 4, 6, 6, 7, 7, 9, 9,10,10,11,11, 4, 6, 6, 7, 7, 9, 9,10,10,11, 11, 6, 8, 8, 9, 9,10,10,11,11,12,12, 6, 8, 8, 9, @@ -5327,7 +5326,7 @@ static const long _vq_lengthlist__44u6__p8_0[] = { static const static_codebook _44u6__p8_0 = { 2, 121, - (long *)_vq_lengthlist__44u6__p8_0, + (char *)_vq_lengthlist__44u6__p8_0, 1, -524582912, 1618345984, 4, 0, (long *)_vq_quantlist__44u6__p8_0, 0 @@ -5347,7 +5346,7 @@ static const long _vq_quantlist__44u6__p8_1[] = { 10, }; -static const long _vq_lengthlist__44u6__p8_1[] = { +static const char _vq_lengthlist__44u6__p8_1[] = { 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 5, 7, 7, 7, 7, 8, 7, 8, 8, 5, 5, 6, 6, 7, 7, 7, 7, 7, 8, 8, 6, 7, 7, 7, 7, 8, 7, 8, 8, 8, 8, 6, 6, 7, 7, @@ -5360,7 +5359,7 @@ static const long _vq_lengthlist__44u6__p8_1[] = { static const static_codebook _44u6__p8_1 = { 2, 121, - (long *)_vq_lengthlist__44u6__p8_1, + (char *)_vq_lengthlist__44u6__p8_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44u6__p8_1, 0 @@ -5384,7 +5383,7 @@ static const long _vq_quantlist__44u6__p9_0[] = { 14, }; -static const long _vq_lengthlist__44u6__p9_0[] = { +static const char _vq_lengthlist__44u6__p9_0[] = { 1, 3, 2, 9, 8,15,15,15,15,15,15,15,15,15,15, 4, 8, 9,13,14,14,14,14,14,14,14,14,14,14,14, 5, 8, 9,14,14,14,14,14,14,14,14,14,14,14,14,11,14,14, @@ -5404,7 +5403,7 @@ static const long _vq_lengthlist__44u6__p9_0[] = { static const static_codebook _44u6__p9_0 = { 2, 225, - (long *)_vq_lengthlist__44u6__p9_0, + (char *)_vq_lengthlist__44u6__p9_0, 1, -514071552, 1627381760, 4, 0, (long *)_vq_quantlist__44u6__p9_0, 0 @@ -5428,7 +5427,7 @@ static const long _vq_quantlist__44u6__p9_1[] = { 14, }; -static const long _vq_lengthlist__44u6__p9_1[] = { +static const char _vq_lengthlist__44u6__p9_1[] = { 1, 4, 4, 7, 7, 8, 9, 8, 8, 9, 8, 9, 8, 9, 9, 4, 7, 6, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 4, 7, 6, 9, 9,10,10, 9, 9,10,10,10,10,11,11, 7, 9, 8, @@ -5448,7 +5447,7 @@ static const long _vq_lengthlist__44u6__p9_1[] = { static const static_codebook _44u6__p9_1 = { 2, 225, - (long *)_vq_lengthlist__44u6__p9_1, + (char *)_vq_lengthlist__44u6__p9_1, 1, -522338304, 1620115456, 4, 0, (long *)_vq_quantlist__44u6__p9_1, 0 @@ -5474,7 +5473,7 @@ static const long _vq_quantlist__44u6__p9_2[] = { 16, }; -static const long _vq_lengthlist__44u6__p9_2[] = { +static const char _vq_lengthlist__44u6__p9_2[] = { 3, 5, 5, 7, 7, 8, 8, 8, 8, 8, 8, 9, 8, 8, 9, 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, @@ -5498,13 +5497,13 @@ static const long _vq_lengthlist__44u6__p9_2[] = { static const static_codebook _44u6__p9_2 = { 2, 289, - (long *)_vq_lengthlist__44u6__p9_2, + (char *)_vq_lengthlist__44u6__p9_2, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44u6__p9_2, 0 }; -static const long _huff_lengthlist__44u6__short[] = { +static const char _huff_lengthlist__44u6__short[] = { 4,11,16,13,17,13,17,16,17,17, 4, 7, 9, 9,13,10, 16,12,16,17, 7, 6, 5, 7, 8, 9,12,12,16,17, 6, 9, 7, 9,10,10,15,15,17,17, 6, 7, 5, 7, 5, 7, 7,10, @@ -5516,13 +5515,13 @@ static const long _huff_lengthlist__44u6__short[] = { static const static_codebook _huff_book__44u6__short = { 2, 100, - (long *)_huff_lengthlist__44u6__short, + (char *)_huff_lengthlist__44u6__short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u7__long[] = { +static const char _huff_lengthlist__44u7__long[] = { 3, 9,14,13,15,14,16,13,13,14, 5, 5, 7, 7, 8, 9, 11,10,12,15,10, 6, 5, 6, 6, 9,10,10,13,16,10, 6, 6, 6, 6, 8, 9, 9,12,15,14, 7, 6, 6, 5, 6, 6, 8, @@ -5534,7 +5533,7 @@ static const long _huff_lengthlist__44u7__long[] = { static const static_codebook _huff_book__44u7__long = { 2, 100, - (long *)_huff_lengthlist__44u7__long, + (char *)_huff_lengthlist__44u7__long, 0, 0, 0, 0, 0, NULL, 0 @@ -5546,7 +5545,7 @@ static const long _vq_quantlist__44u7__p1_0[] = { 2, }; -static const long _vq_lengthlist__44u7__p1_0[] = { +static const char _vq_lengthlist__44u7__p1_0[] = { 1, 4, 4, 4, 7, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, 10,10, 5, 8, 8, 7,10,10, 8,10,10, 5, 8, 8, 8,11, 10, 8,10,10, 8,10,10,10,12,13,10,13,13, 7,10,10, @@ -5557,7 +5556,7 @@ static const long _vq_lengthlist__44u7__p1_0[] = { static const static_codebook _44u7__p1_0 = { 4, 81, - (long *)_vq_lengthlist__44u7__p1_0, + (char *)_vq_lengthlist__44u7__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u7__p1_0, 0 @@ -5569,7 +5568,7 @@ static const long _vq_quantlist__44u7__p2_0[] = { 2, }; -static const long _vq_lengthlist__44u7__p2_0[] = { +static const char _vq_lengthlist__44u7__p2_0[] = { 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 8, 8, 6, 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 8, 5, 6, 6, 6, 8, 7, 6, 8, 8, 6, 8, 8, 8, 9, 9, 8, 9, 9, 6, 8, 7, @@ -5580,7 +5579,7 @@ static const long _vq_lengthlist__44u7__p2_0[] = { static const static_codebook _44u7__p2_0 = { 4, 81, - (long *)_vq_lengthlist__44u7__p2_0, + (char *)_vq_lengthlist__44u7__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u7__p2_0, 0 @@ -5594,7 +5593,7 @@ static const long _vq_quantlist__44u7__p3_0[] = { 4, }; -static const long _vq_lengthlist__44u7__p3_0[] = { +static const char _vq_lengthlist__44u7__p3_0[] = { 2, 5, 4, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, 9, 9,13,12, 8, 9,10,12,13, 5, 7, 7,10, 9, 7, 9, 9,11,11, 6, 8, 9,11,11,10,11,11,14,14, 9,10,11, @@ -5639,7 +5638,7 @@ static const long _vq_lengthlist__44u7__p3_0[] = { static const static_codebook _44u7__p3_0 = { 4, 625, - (long *)_vq_lengthlist__44u7__p3_0, + (char *)_vq_lengthlist__44u7__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u7__p3_0, 0 @@ -5653,7 +5652,7 @@ static const long _vq_quantlist__44u7__p4_0[] = { 4, }; -static const long _vq_lengthlist__44u7__p4_0[] = { +static const char _vq_lengthlist__44u7__p4_0[] = { 4, 5, 5, 8, 8, 6, 7, 6, 9, 9, 6, 6, 7, 9, 9, 8, 9, 9,11,11, 8, 9, 9,10,11, 6, 7, 7, 9, 9, 7, 8, 8,10,10, 6, 7, 8, 9,10, 9,10,10,12,12, 9, 9,10, @@ -5698,7 +5697,7 @@ static const long _vq_lengthlist__44u7__p4_0[] = { static const static_codebook _44u7__p4_0 = { 4, 625, - (long *)_vq_lengthlist__44u7__p4_0, + (char *)_vq_lengthlist__44u7__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u7__p4_0, 0 @@ -5716,7 +5715,7 @@ static const long _vq_quantlist__44u7__p5_0[] = { 8, }; -static const long _vq_lengthlist__44u7__p5_0[] = { +static const char _vq_lengthlist__44u7__p5_0[] = { 2, 3, 3, 6, 6, 7, 8,10,10, 4, 5, 5, 8, 7, 8, 8, 11,11, 3, 5, 5, 7, 7, 8, 9,11,11, 6, 8, 7, 9, 9, 10,10,12,12, 6, 7, 8, 9,10,10,10,12,12, 8, 8, 8, @@ -5727,7 +5726,7 @@ static const long _vq_lengthlist__44u7__p5_0[] = { static const static_codebook _44u7__p5_0 = { 2, 81, - (long *)_vq_lengthlist__44u7__p5_0, + (char *)_vq_lengthlist__44u7__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u7__p5_0, 0 @@ -5745,7 +5744,7 @@ static const long _vq_quantlist__44u7__p6_0[] = { 8, }; -static const long _vq_lengthlist__44u7__p6_0[] = { +static const char _vq_lengthlist__44u7__p6_0[] = { 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 8, 7, 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, 8, 8,10,10, 5, 6, 6, 7, 7, 8, 8,10,10, 7, 8, 7, @@ -5756,7 +5755,7 @@ static const long _vq_lengthlist__44u7__p6_0[] = { static const static_codebook _44u7__p6_0 = { 2, 81, - (long *)_vq_lengthlist__44u7__p6_0, + (char *)_vq_lengthlist__44u7__p6_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u7__p6_0, 0 @@ -5768,7 +5767,7 @@ static const long _vq_quantlist__44u7__p7_0[] = { 2, }; -static const long _vq_lengthlist__44u7__p7_0[] = { +static const char _vq_lengthlist__44u7__p7_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 8, 8, 9, 9, 7, 10,10, 5, 8, 9, 7, 9,10, 8, 9, 9, 4, 9, 9, 9,11, 10, 8,10,10, 7,11,10,10,10,12,10,12,12, 7,10,10, @@ -5779,7 +5778,7 @@ static const long _vq_lengthlist__44u7__p7_0[] = { static const static_codebook _44u7__p7_0 = { 4, 81, - (long *)_vq_lengthlist__44u7__p7_0, + (char *)_vq_lengthlist__44u7__p7_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44u7__p7_0, 0 @@ -5799,7 +5798,7 @@ static const long _vq_quantlist__44u7__p7_1[] = { 10, }; -static const long _vq_lengthlist__44u7__p7_1[] = { +static const char _vq_lengthlist__44u7__p7_1[] = { 3, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 6, 6, 8, 7, 8, 8, 8, 8, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, 8, 6, 7, 6, 7, 7, 8, 8, 9, 9, 9, 9, 6, 6, 7, 7, @@ -5812,7 +5811,7 @@ static const long _vq_lengthlist__44u7__p7_1[] = { static const static_codebook _44u7__p7_1 = { 2, 121, - (long *)_vq_lengthlist__44u7__p7_1, + (char *)_vq_lengthlist__44u7__p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44u7__p7_1, 0 @@ -5832,7 +5831,7 @@ static const long _vq_quantlist__44u7__p8_0[] = { 10, }; -static const long _vq_lengthlist__44u7__p8_0[] = { +static const char _vq_lengthlist__44u7__p8_0[] = { 1, 4, 4, 6, 6, 8, 8,10,10,11,11, 4, 6, 6, 7, 7, 9, 9,11,10,12,12, 5, 6, 5, 7, 7, 9, 9,10,11,12, 12, 6, 7, 7, 8, 8,10,10,11,11,13,13, 6, 7, 7, 8, @@ -5845,7 +5844,7 @@ static const long _vq_lengthlist__44u7__p8_0[] = { static const static_codebook _44u7__p8_0 = { 2, 121, - (long *)_vq_lengthlist__44u7__p8_0, + (char *)_vq_lengthlist__44u7__p8_0, 1, -524582912, 1618345984, 4, 0, (long *)_vq_quantlist__44u7__p8_0, 0 @@ -5865,7 +5864,7 @@ static const long _vq_quantlist__44u7__p8_1[] = { 10, }; -static const long _vq_lengthlist__44u7__p8_1[] = { +static const char _vq_lengthlist__44u7__p8_1[] = { 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 6, 7, 7, 7, @@ -5878,7 +5877,7 @@ static const long _vq_lengthlist__44u7__p8_1[] = { static const static_codebook _44u7__p8_1 = { 2, 121, - (long *)_vq_lengthlist__44u7__p8_1, + (char *)_vq_lengthlist__44u7__p8_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44u7__p8_1, 0 @@ -5898,7 +5897,7 @@ static const long _vq_quantlist__44u7__p9_0[] = { 10, }; -static const long _vq_lengthlist__44u7__p9_0[] = { +static const char _vq_lengthlist__44u7__p9_0[] = { 1, 3, 3,10,10,10,10,10,10,10,10, 4,10,10,10,10, 10,10,10,10,10,10, 4,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, @@ -5911,7 +5910,7 @@ static const long _vq_lengthlist__44u7__p9_0[] = { static const static_codebook _44u7__p9_0 = { 2, 121, - (long *)_vq_lengthlist__44u7__p9_0, + (char *)_vq_lengthlist__44u7__p9_0, 1, -512171520, 1630791680, 4, 0, (long *)_vq_quantlist__44u7__p9_0, 0 @@ -5933,7 +5932,7 @@ static const long _vq_quantlist__44u7__p9_1[] = { 12, }; -static const long _vq_lengthlist__44u7__p9_1[] = { +static const char _vq_lengthlist__44u7__p9_1[] = { 1, 4, 4, 6, 5, 8, 6, 9, 8,10, 9,11,10, 4, 6, 6, 8, 8, 9, 9,11,10,11,11,11,11, 4, 6, 6, 8, 8,10, 9,11,11,11,11,11,12, 6, 8, 8,10,10,11,11,12,12, @@ -5949,7 +5948,7 @@ static const long _vq_lengthlist__44u7__p9_1[] = { static const static_codebook _44u7__p9_1 = { 2, 169, - (long *)_vq_lengthlist__44u7__p9_1, + (char *)_vq_lengthlist__44u7__p9_1, 1, -518889472, 1622704128, 4, 0, (long *)_vq_quantlist__44u7__p9_1, 0 @@ -6007,7 +6006,7 @@ static const long _vq_quantlist__44u7__p9_2[] = { 48, }; -static const long _vq_lengthlist__44u7__p9_2[] = { +static const char _vq_lengthlist__44u7__p9_2[] = { 2, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, @@ -6016,13 +6015,13 @@ static const long _vq_lengthlist__44u7__p9_2[] = { static const static_codebook _44u7__p9_2 = { 1, 49, - (long *)_vq_lengthlist__44u7__p9_2, + (char *)_vq_lengthlist__44u7__p9_2, 1, -526909440, 1611661312, 6, 0, (long *)_vq_quantlist__44u7__p9_2, 0 }; -static const long _huff_lengthlist__44u7__short[] = { +static const char _huff_lengthlist__44u7__short[] = { 5,12,17,16,16,17,17,17,17,17, 4, 7,11,11,12, 9, 17,10,17,17, 7, 7, 8, 9, 7, 9,11,10,15,17, 7, 9, 10,11,10,12,14,12,16,17, 7, 8, 5, 7, 4, 7, 7, 8, @@ -6034,13 +6033,13 @@ static const long _huff_lengthlist__44u7__short[] = { static const static_codebook _huff_book__44u7__short = { 2, 100, - (long *)_huff_lengthlist__44u7__short, + (char *)_huff_lengthlist__44u7__short, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u8__long[] = { +static const char _huff_lengthlist__44u8__long[] = { 3, 9,13,14,14,15,14,14,15,15, 5, 4, 6, 8,10,12, 12,14,15,15, 9, 5, 4, 5, 8,10,11,13,16,16,10, 7, 4, 3, 5, 7, 9,11,13,13,10, 9, 7, 4, 4, 6, 8,10, @@ -6052,13 +6051,13 @@ static const long _huff_lengthlist__44u8__long[] = { static const static_codebook _huff_book__44u8__long = { 2, 100, - (long *)_huff_lengthlist__44u8__long, + (char *)_huff_lengthlist__44u8__long, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u8__short[] = { +static const char _huff_lengthlist__44u8__short[] = { 6,14,18,18,17,17,17,17,17,17, 4, 7, 9, 9,10,13, 15,17,17,17, 6, 7, 5, 6, 8,11,16,17,16,17, 5, 7, 5, 4, 6,10,14,17,17,17, 6, 6, 6, 5, 7,10,13,16, @@ -6070,7 +6069,7 @@ static const long _huff_lengthlist__44u8__short[] = { static const static_codebook _huff_book__44u8__short = { 2, 100, - (long *)_huff_lengthlist__44u8__short, + (char *)_huff_lengthlist__44u8__short, 0, 0, 0, 0, 0, NULL, 0 @@ -6082,7 +6081,7 @@ static const long _vq_quantlist__44u8_p1_0[] = { 2, }; -static const long _vq_lengthlist__44u8_p1_0[] = { +static const char _vq_lengthlist__44u8_p1_0[] = { 1, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 8, 9, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 8, 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 9, 7, 9, 9, 9,10,11, 9,11,10, 7, 9, 9, @@ -6093,7 +6092,7 @@ static const long _vq_lengthlist__44u8_p1_0[] = { static const static_codebook _44u8_p1_0 = { 4, 81, - (long *)_vq_lengthlist__44u8_p1_0, + (char *)_vq_lengthlist__44u8_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u8_p1_0, 0 @@ -6107,7 +6106,7 @@ static const long _vq_quantlist__44u8_p2_0[] = { 4, }; -static const long _vq_lengthlist__44u8_p2_0[] = { +static const char _vq_lengthlist__44u8_p2_0[] = { 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, 9, 9,11,11, 8, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12, 9,10,10, @@ -6152,7 +6151,7 @@ static const long _vq_lengthlist__44u8_p2_0[] = { static const static_codebook _44u8_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44u8_p2_0, + (char *)_vq_lengthlist__44u8_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u8_p2_0, 0 @@ -6170,7 +6169,7 @@ static const long _vq_quantlist__44u8_p3_0[] = { 8, }; -static const long _vq_lengthlist__44u8_p3_0[] = { +static const char _vq_lengthlist__44u8_p3_0[] = { 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 7, 7, 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, 8, 8,10,10, 6, 6, 6, 7, 7, 8, 8,10,10, 7, 7, 7, @@ -6181,7 +6180,7 @@ static const long _vq_lengthlist__44u8_p3_0[] = { static const static_codebook _44u8_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44u8_p3_0, + (char *)_vq_lengthlist__44u8_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u8_p3_0, 0 @@ -6207,7 +6206,7 @@ static const long _vq_quantlist__44u8_p4_0[] = { 16, }; -static const long _vq_lengthlist__44u8_p4_0[] = { +static const char _vq_lengthlist__44u8_p4_0[] = { 4, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,10,10,11,11,11, 11, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11, 12,12, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, @@ -6231,7 +6230,7 @@ static const long _vq_lengthlist__44u8_p4_0[] = { static const static_codebook _44u8_p4_0 = { 2, 289, - (long *)_vq_lengthlist__44u8_p4_0, + (char *)_vq_lengthlist__44u8_p4_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44u8_p4_0, 0 @@ -6243,7 +6242,7 @@ static const long _vq_quantlist__44u8_p5_0[] = { 2, }; -static const long _vq_lengthlist__44u8_p5_0[] = { +static const char _vq_lengthlist__44u8_p5_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 8, 9, 9, 7, 9, 9, 5, 8, 8, 7, 9, 9, 8, 9, 9, 5, 8, 8, 8,10, 10, 8,10,10, 7,10,10, 9,10,12, 9,12,11, 7,10,10, @@ -6254,7 +6253,7 @@ static const long _vq_lengthlist__44u8_p5_0[] = { static const static_codebook _44u8_p5_0 = { 4, 81, - (long *)_vq_lengthlist__44u8_p5_0, + (char *)_vq_lengthlist__44u8_p5_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44u8_p5_0, 0 @@ -6274,7 +6273,7 @@ static const long _vq_quantlist__44u8_p5_1[] = { 10, }; -static const long _vq_lengthlist__44u8_p5_1[] = { +static const char _vq_lengthlist__44u8_p5_1[] = { 4, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 6, 6, 6, 7, @@ -6287,7 +6286,7 @@ static const long _vq_lengthlist__44u8_p5_1[] = { static const static_codebook _44u8_p5_1 = { 2, 121, - (long *)_vq_lengthlist__44u8_p5_1, + (char *)_vq_lengthlist__44u8_p5_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44u8_p5_1, 0 @@ -6309,7 +6308,7 @@ static const long _vq_quantlist__44u8_p6_0[] = { 12, }; -static const long _vq_lengthlist__44u8_p6_0[] = { +static const char _vq_lengthlist__44u8_p6_0[] = { 2, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 4, 6, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 6, 7, 7, 7, 8, 8, 8, 8, 9, @@ -6325,7 +6324,7 @@ static const long _vq_lengthlist__44u8_p6_0[] = { static const static_codebook _44u8_p6_0 = { 2, 169, - (long *)_vq_lengthlist__44u8_p6_0, + (char *)_vq_lengthlist__44u8_p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44u8_p6_0, 0 @@ -6339,14 +6338,14 @@ static const long _vq_quantlist__44u8_p6_1[] = { 4, }; -static const long _vq_lengthlist__44u8_p6_1[] = { +static const char _vq_lengthlist__44u8_p6_1[] = { 3, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44u8_p6_1 = { 2, 25, - (long *)_vq_lengthlist__44u8_p6_1, + (char *)_vq_lengthlist__44u8_p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u8_p6_1, 0 @@ -6368,7 +6367,7 @@ static const long _vq_quantlist__44u8_p7_0[] = { 12, }; -static const long _vq_lengthlist__44u8_p7_0[] = { +static const char _vq_lengthlist__44u8_p7_0[] = { 1, 4, 5, 6, 6, 7, 7, 8, 8,10,10,11,11, 5, 6, 6, 7, 7, 8, 8, 9, 9,11,10,12,11, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,11,11,12, 6, 7, 7, 8, 8, 9, 9,10,10, @@ -6384,7 +6383,7 @@ static const long _vq_lengthlist__44u8_p7_0[] = { static const static_codebook _44u8_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44u8_p7_0, + (char *)_vq_lengthlist__44u8_p7_0, 1, -523206656, 1618345984, 4, 0, (long *)_vq_quantlist__44u8_p7_0, 0 @@ -6404,7 +6403,7 @@ static const long _vq_quantlist__44u8_p7_1[] = { 10, }; -static const long _vq_lengthlist__44u8_p7_1[] = { +static const char _vq_lengthlist__44u8_p7_1[] = { 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 6, 7, 7, 7, @@ -6417,7 +6416,7 @@ static const long _vq_lengthlist__44u8_p7_1[] = { static const static_codebook _44u8_p7_1 = { 2, 121, - (long *)_vq_lengthlist__44u8_p7_1, + (char *)_vq_lengthlist__44u8_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44u8_p7_1, 0 @@ -6441,7 +6440,7 @@ static const long _vq_quantlist__44u8_p8_0[] = { 14, }; -static const long _vq_lengthlist__44u8_p8_0[] = { +static const char _vq_lengthlist__44u8_p8_0[] = { 1, 4, 4, 7, 7, 8, 8, 8, 7, 9, 8,10, 9,11,10, 4, 6, 6, 8, 8,10, 9, 9, 9,10,10,11,10,12,10, 4, 6, 6, 8, 8,10,10, 9, 9,10,10,11,11,11,12, 7, 8, 8, @@ -6461,7 +6460,7 @@ static const long _vq_lengthlist__44u8_p8_0[] = { static const static_codebook _44u8_p8_0 = { 2, 225, - (long *)_vq_lengthlist__44u8_p8_0, + (char *)_vq_lengthlist__44u8_p8_0, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__44u8_p8_0, 0 @@ -6491,7 +6490,7 @@ static const long _vq_quantlist__44u8_p8_1[] = { 20, }; -static const long _vq_lengthlist__44u8_p8_1[] = { +static const char _vq_lengthlist__44u8_p8_1[] = { 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, @@ -6524,7 +6523,7 @@ static const long _vq_lengthlist__44u8_p8_1[] = { static const static_codebook _44u8_p8_1 = { 2, 441, - (long *)_vq_lengthlist__44u8_p8_1, + (char *)_vq_lengthlist__44u8_p8_1, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__44u8_p8_1, 0 @@ -6542,7 +6541,7 @@ static const long _vq_quantlist__44u8_p9_0[] = { 8, }; -static const long _vq_lengthlist__44u8_p9_0[] = { +static const char _vq_lengthlist__44u8_p9_0[] = { 1, 3, 3, 9, 9, 9, 9, 9, 9, 4, 9, 9, 9, 9, 9, 9, 9, 9, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -6553,7 +6552,7 @@ static const long _vq_lengthlist__44u8_p9_0[] = { static const static_codebook _44u8_p9_0 = { 2, 81, - (long *)_vq_lengthlist__44u8_p9_0, + (char *)_vq_lengthlist__44u8_p9_0, 1, -511895552, 1631393792, 4, 0, (long *)_vq_quantlist__44u8_p9_0, 0 @@ -6581,7 +6580,7 @@ static const long _vq_quantlist__44u8_p9_1[] = { 18, }; -static const long _vq_lengthlist__44u8_p9_1[] = { +static const char _vq_lengthlist__44u8_p9_1[] = { 1, 4, 4, 7, 7, 8, 7, 8, 6, 9, 7,10, 8,11,10,11, 11,11,11, 4, 7, 6, 9, 9,10, 9, 9, 9,10,10,11,10, 11,10,11,11,13,11, 4, 7, 7, 9, 9, 9, 9, 9, 9,10, @@ -6609,7 +6608,7 @@ static const long _vq_lengthlist__44u8_p9_1[] = { static const static_codebook _44u8_p9_1 = { 2, 361, - (long *)_vq_lengthlist__44u8_p9_1, + (char *)_vq_lengthlist__44u8_p9_1, 1, -518287360, 1622704128, 5, 0, (long *)_vq_quantlist__44u8_p9_1, 0 @@ -6667,7 +6666,7 @@ static const long _vq_quantlist__44u8_p9_2[] = { 48, }; -static const long _vq_lengthlist__44u8_p9_2[] = { +static const char _vq_lengthlist__44u8_p9_2[] = { 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -6676,13 +6675,13 @@ static const long _vq_lengthlist__44u8_p9_2[] = { static const static_codebook _44u8_p9_2 = { 1, 49, - (long *)_vq_lengthlist__44u8_p9_2, + (char *)_vq_lengthlist__44u8_p9_2, 1, -526909440, 1611661312, 6, 0, (long *)_vq_quantlist__44u8_p9_2, 0 }; -static const long _huff_lengthlist__44u9__long[] = { +static const char _huff_lengthlist__44u9__long[] = { 3, 9,13,13,14,15,14,14,15,15, 5, 5, 9,10,12,12, 13,14,16,15,10, 6, 6, 6, 8,11,12,13,16,15,11, 7, 5, 3, 5, 8,10,12,15,15,10,10, 7, 4, 3, 5, 8,10, @@ -6694,13 +6693,13 @@ static const long _huff_lengthlist__44u9__long[] = { static const static_codebook _huff_book__44u9__long = { 2, 100, - (long *)_huff_lengthlist__44u9__long, + (char *)_huff_lengthlist__44u9__long, 0, 0, 0, 0, 0, NULL, 0 }; -static const long _huff_lengthlist__44u9__short[] = { +static const char _huff_lengthlist__44u9__short[] = { 9,16,18,18,17,17,17,17,17,17, 5, 8,11,12,11,12, 17,17,16,16, 6, 6, 8, 8, 9,10,14,15,16,16, 6, 7, 7, 4, 6, 9,13,16,16,16, 6, 6, 7, 4, 5, 8,11,15, @@ -6712,7 +6711,7 @@ static const long _huff_lengthlist__44u9__short[] = { static const static_codebook _huff_book__44u9__short = { 2, 100, - (long *)_huff_lengthlist__44u9__short, + (char *)_huff_lengthlist__44u9__short, 0, 0, 0, 0, 0, NULL, 0 @@ -6724,7 +6723,7 @@ static const long _vq_quantlist__44u9_p1_0[] = { 2, }; -static const long _vq_lengthlist__44u9_p1_0[] = { +static const char _vq_lengthlist__44u9_p1_0[] = { 1, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 9, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 9, 8, 9, 9, 9,10,11, 9,11,11, 7, 9, 9, @@ -6735,7 +6734,7 @@ static const long _vq_lengthlist__44u9_p1_0[] = { static const static_codebook _44u9_p1_0 = { 4, 81, - (long *)_vq_lengthlist__44u9_p1_0, + (char *)_vq_lengthlist__44u9_p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44u9_p1_0, 0 @@ -6749,7 +6748,7 @@ static const long _vq_quantlist__44u9_p2_0[] = { 4, }; -static const long _vq_lengthlist__44u9_p2_0[] = { +static const char _vq_lengthlist__44u9_p2_0[] = { 3, 5, 5, 8, 8, 5, 7, 7, 9, 9, 6, 7, 7, 9, 9, 8, 9, 9,11,10, 8, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 8, 8,10,10, 7, 8, 8, 9,10, 9,10,10,11,11, 9, 9,10, @@ -6794,7 +6793,7 @@ static const long _vq_lengthlist__44u9_p2_0[] = { static const static_codebook _44u9_p2_0 = { 4, 625, - (long *)_vq_lengthlist__44u9_p2_0, + (char *)_vq_lengthlist__44u9_p2_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u9_p2_0, 0 @@ -6812,7 +6811,7 @@ static const long _vq_quantlist__44u9_p3_0[] = { 8, }; -static const long _vq_lengthlist__44u9_p3_0[] = { +static const char _vq_lengthlist__44u9_p3_0[] = { 3, 4, 4, 5, 5, 7, 7, 8, 8, 4, 5, 5, 6, 6, 7, 7, 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, 8, 8, 9, 9, 5, 6, 6, 7, 7, 8, 8, 9, 9, 7, 7, 7, @@ -6823,7 +6822,7 @@ static const long _vq_lengthlist__44u9_p3_0[] = { static const static_codebook _44u9_p3_0 = { 2, 81, - (long *)_vq_lengthlist__44u9_p3_0, + (char *)_vq_lengthlist__44u9_p3_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44u9_p3_0, 0 @@ -6849,7 +6848,7 @@ static const long _vq_quantlist__44u9_p4_0[] = { 16, }; -static const long _vq_lengthlist__44u9_p4_0[] = { +static const char _vq_lengthlist__44u9_p4_0[] = { 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, 11, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 11,11, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10, @@ -6873,7 +6872,7 @@ static const long _vq_lengthlist__44u9_p4_0[] = { static const static_codebook _44u9_p4_0 = { 2, 289, - (long *)_vq_lengthlist__44u9_p4_0, + (char *)_vq_lengthlist__44u9_p4_0, 1, -529530880, 1611661312, 5, 0, (long *)_vq_quantlist__44u9_p4_0, 0 @@ -6885,7 +6884,7 @@ static const long _vq_quantlist__44u9_p5_0[] = { 2, }; -static const long _vq_lengthlist__44u9_p5_0[] = { +static const char _vq_lengthlist__44u9_p5_0[] = { 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 8, 9, 9, 7, 9, 9, 5, 8, 8, 7, 9, 9, 8, 9, 9, 5, 8, 8, 8,10, 10, 8,10,10, 7,10,10, 9,10,12, 9,11,11, 7,10,10, @@ -6896,7 +6895,7 @@ static const long _vq_lengthlist__44u9_p5_0[] = { static const static_codebook _44u9_p5_0 = { 4, 81, - (long *)_vq_lengthlist__44u9_p5_0, + (char *)_vq_lengthlist__44u9_p5_0, 1, -529137664, 1618345984, 2, 0, (long *)_vq_quantlist__44u9_p5_0, 0 @@ -6916,7 +6915,7 @@ static const long _vq_quantlist__44u9_p5_1[] = { 10, }; -static const long _vq_lengthlist__44u9_p5_1[] = { +static const char _vq_lengthlist__44u9_p5_1[] = { 5, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 7, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 6, 6, 6, 7, @@ -6929,7 +6928,7 @@ static const long _vq_lengthlist__44u9_p5_1[] = { static const static_codebook _44u9_p5_1 = { 2, 121, - (long *)_vq_lengthlist__44u9_p5_1, + (char *)_vq_lengthlist__44u9_p5_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44u9_p5_1, 0 @@ -6951,7 +6950,7 @@ static const long _vq_quantlist__44u9_p6_0[] = { 12, }; -static const long _vq_lengthlist__44u9_p6_0[] = { +static const char _vq_lengthlist__44u9_p6_0[] = { 2, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 4, 6, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 4, 5, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 6, 7, 7, 8, 8, 8, 8, 9, 9, @@ -6967,7 +6966,7 @@ static const long _vq_lengthlist__44u9_p6_0[] = { static const static_codebook _44u9_p6_0 = { 2, 169, - (long *)_vq_lengthlist__44u9_p6_0, + (char *)_vq_lengthlist__44u9_p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44u9_p6_0, 0 @@ -6981,14 +6980,14 @@ static const long _vq_quantlist__44u9_p6_1[] = { 4, }; -static const long _vq_lengthlist__44u9_p6_1[] = { +static const char _vq_lengthlist__44u9_p6_1[] = { 4, 4, 4, 5, 5, 4, 5, 4, 5, 5, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }; static const static_codebook _44u9_p6_1 = { 2, 25, - (long *)_vq_lengthlist__44u9_p6_1, + (char *)_vq_lengthlist__44u9_p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44u9_p6_1, 0 @@ -7010,7 +7009,7 @@ static const long _vq_quantlist__44u9_p7_0[] = { 12, }; -static const long _vq_lengthlist__44u9_p7_0[] = { +static const char _vq_lengthlist__44u9_p7_0[] = { 1, 4, 5, 6, 6, 7, 7, 8, 9,10,10,11,11, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11, 6, 7, 7, 8, 8, 9, 9,10,10, @@ -7026,7 +7025,7 @@ static const long _vq_lengthlist__44u9_p7_0[] = { static const static_codebook _44u9_p7_0 = { 2, 169, - (long *)_vq_lengthlist__44u9_p7_0, + (char *)_vq_lengthlist__44u9_p7_0, 1, -523206656, 1618345984, 4, 0, (long *)_vq_quantlist__44u9_p7_0, 0 @@ -7046,7 +7045,7 @@ static const long _vq_quantlist__44u9_p7_1[] = { 10, }; -static const long _vq_lengthlist__44u9_p7_1[] = { +static const char _vq_lengthlist__44u9_p7_1[] = { 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, @@ -7059,7 +7058,7 @@ static const long _vq_lengthlist__44u9_p7_1[] = { static const static_codebook _44u9_p7_1 = { 2, 121, - (long *)_vq_lengthlist__44u9_p7_1, + (char *)_vq_lengthlist__44u9_p7_1, 1, -531365888, 1611661312, 4, 0, (long *)_vq_quantlist__44u9_p7_1, 0 @@ -7083,7 +7082,7 @@ static const long _vq_quantlist__44u9_p8_0[] = { 14, }; -static const long _vq_lengthlist__44u9_p8_0[] = { +static const char _vq_lengthlist__44u9_p8_0[] = { 1, 4, 4, 7, 7, 8, 8, 8, 8, 9, 9,10, 9,11,10, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,10,12,10, 4, 6, 6, 8, 8, 9,10, 9, 9,10,10,11,11,12,12, 7, 8, 8, @@ -7103,7 +7102,7 @@ static const long _vq_lengthlist__44u9_p8_0[] = { static const static_codebook _44u9_p8_0 = { 2, 225, - (long *)_vq_lengthlist__44u9_p8_0, + (char *)_vq_lengthlist__44u9_p8_0, 1, -520986624, 1620377600, 4, 0, (long *)_vq_quantlist__44u9_p8_0, 0 @@ -7133,7 +7132,7 @@ static const long _vq_quantlist__44u9_p8_1[] = { 20, }; -static const long _vq_lengthlist__44u9_p8_1[] = { +static const char _vq_lengthlist__44u9_p8_1[] = { 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 6, 6, 6, 7, 7, 8, @@ -7166,7 +7165,7 @@ static const long _vq_lengthlist__44u9_p8_1[] = { static const static_codebook _44u9_p8_1 = { 2, 441, - (long *)_vq_lengthlist__44u9_p8_1, + (char *)_vq_lengthlist__44u9_p8_1, 1, -529268736, 1611661312, 5, 0, (long *)_vq_quantlist__44u9_p8_1, 0 @@ -7190,7 +7189,7 @@ static const long _vq_quantlist__44u9_p9_0[] = { 14, }; -static const long _vq_lengthlist__44u9_p9_0[] = { +static const char _vq_lengthlist__44u9_p9_0[] = { 1, 3, 3,11,11,11,11,11,11,11,11,11,11,11,11, 4, 10,11,11,11,11,11,11,11,11,11,11,11,11,11, 4,10, 10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, @@ -7210,7 +7209,7 @@ static const long _vq_lengthlist__44u9_p9_0[] = { static const static_codebook _44u9_p9_0 = { 2, 225, - (long *)_vq_lengthlist__44u9_p9_0, + (char *)_vq_lengthlist__44u9_p9_0, 1, -510036736, 1631393792, 4, 0, (long *)_vq_quantlist__44u9_p9_0, 0 @@ -7238,7 +7237,7 @@ static const long _vq_quantlist__44u9_p9_1[] = { 18, }; -static const long _vq_lengthlist__44u9_p9_1[] = { +static const char _vq_lengthlist__44u9_p9_1[] = { 1, 4, 4, 7, 7, 8, 7, 8, 7, 9, 8,10, 9,10,10,11, 11,12,12, 4, 7, 6, 9, 9,10, 9, 9, 8,10,10,11,10, 12,10,13,12,13,12, 4, 6, 6, 9, 9, 9, 9, 9, 9,10, @@ -7266,7 +7265,7 @@ static const long _vq_lengthlist__44u9_p9_1[] = { static const static_codebook _44u9_p9_1 = { 2, 361, - (long *)_vq_lengthlist__44u9_p9_1, + (char *)_vq_lengthlist__44u9_p9_1, 1, -518287360, 1622704128, 5, 0, (long *)_vq_quantlist__44u9_p9_1, 0 @@ -7324,7 +7323,7 @@ static const long _vq_quantlist__44u9_p9_2[] = { 48, }; -static const long _vq_lengthlist__44u9_p9_2[] = { +static const char _vq_lengthlist__44u9_p9_2[] = { 2, 4, 4, 5, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, @@ -7333,13 +7332,13 @@ static const long _vq_lengthlist__44u9_p9_2[] = { static const static_codebook _44u9_p9_2 = { 1, 49, - (long *)_vq_lengthlist__44u9_p9_2, + (char *)_vq_lengthlist__44u9_p9_2, 1, -526909440, 1611661312, 6, 0, (long *)_vq_quantlist__44u9_p9_2, 0 }; -static const long _huff_lengthlist__44un1__long[] = { +static const char _huff_lengthlist__44un1__long[] = { 5, 6,12, 9,14, 9, 9,19, 6, 1, 5, 5, 8, 7, 9,19, 12, 4, 4, 7, 7, 9,11,18, 9, 5, 6, 6, 8, 7, 8,17, 14, 8, 7, 8, 8,10,12,18, 9, 6, 8, 6, 8, 6, 8,18, @@ -7348,7 +7347,7 @@ static const long _huff_lengthlist__44un1__long[] = { static const static_codebook _huff_book__44un1__long = { 2, 64, - (long *)_huff_lengthlist__44un1__long, + (char *)_huff_lengthlist__44un1__long, 0, 0, 0, 0, 0, NULL, 0 @@ -7360,7 +7359,7 @@ static const long _vq_quantlist__44un1__p1_0[] = { 2, }; -static const long _vq_lengthlist__44un1__p1_0[] = { +static const char _vq_lengthlist__44un1__p1_0[] = { 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,10,11, 8, 10,11, 5, 8, 8, 8,11,10, 8,11,10, 4, 9, 9, 8,11, 11, 8,11,11, 8,12,11,10,12,14,11,13,13, 7,11,11, @@ -7371,7 +7370,7 @@ static const long _vq_lengthlist__44un1__p1_0[] = { static const static_codebook _44un1__p1_0 = { 4, 81, - (long *)_vq_lengthlist__44un1__p1_0, + (char *)_vq_lengthlist__44un1__p1_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44un1__p1_0, 0 @@ -7383,7 +7382,7 @@ static const long _vq_quantlist__44un1__p2_0[] = { 2, }; -static const long _vq_lengthlist__44un1__p2_0[] = { +static const char _vq_lengthlist__44un1__p2_0[] = { 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 8, 6, 7, 9, 5, 7, 7, 6, 8, 7, 7, 9, 8, 4, 7, 7, 7, 9, 8, 7, 8, 8, 7, 9, 8, 8, 8,10, 9,10,10, 6, 8, 8, @@ -7394,7 +7393,7 @@ static const long _vq_lengthlist__44un1__p2_0[] = { static const static_codebook _44un1__p2_0 = { 4, 81, - (long *)_vq_lengthlist__44un1__p2_0, + (char *)_vq_lengthlist__44un1__p2_0, 1, -535822336, 1611661312, 2, 0, (long *)_vq_quantlist__44un1__p2_0, 0 @@ -7408,7 +7407,7 @@ static const long _vq_quantlist__44un1__p3_0[] = { 4, }; -static const long _vq_lengthlist__44un1__p3_0[] = { +static const char _vq_lengthlist__44un1__p3_0[] = { 1, 5, 5, 8, 8, 5, 8, 7, 9, 9, 5, 7, 8, 9, 9, 9, 10, 9,12,12, 9, 9,10,11,12, 6, 8, 8,10,10, 8,10, 10,11,11, 8, 9,10,11,11,10,11,11,13,13,10,11,11, @@ -7453,7 +7452,7 @@ static const long _vq_lengthlist__44un1__p3_0[] = { static const static_codebook _44un1__p3_0 = { 4, 625, - (long *)_vq_lengthlist__44un1__p3_0, + (char *)_vq_lengthlist__44un1__p3_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44un1__p3_0, 0 @@ -7467,7 +7466,7 @@ static const long _vq_quantlist__44un1__p4_0[] = { 4, }; -static const long _vq_lengthlist__44un1__p4_0[] = { +static const char _vq_lengthlist__44un1__p4_0[] = { 3, 5, 5, 9, 9, 5, 6, 6,10, 9, 5, 6, 6, 9,10,10, 10,10,12,11, 9,10,10,12,12, 5, 7, 7,10,10, 7, 7, 8,10,11, 7, 7, 8,10,11,10,10,11,11,13,10,10,11, @@ -7512,7 +7511,7 @@ static const long _vq_lengthlist__44un1__p4_0[] = { static const static_codebook _44un1__p4_0 = { 4, 625, - (long *)_vq_lengthlist__44un1__p4_0, + (char *)_vq_lengthlist__44un1__p4_0, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44un1__p4_0, 0 @@ -7530,7 +7529,7 @@ static const long _vq_quantlist__44un1__p5_0[] = { 8, }; -static const long _vq_lengthlist__44un1__p5_0[] = { +static const char _vq_lengthlist__44un1__p5_0[] = { 1, 4, 4, 7, 7, 8, 8, 9, 9, 4, 6, 5, 8, 7, 8, 8, 10, 9, 4, 6, 6, 8, 8, 8, 8,10,10, 7, 8, 7, 9, 9, 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,11, 8, 8, 8, @@ -7541,7 +7540,7 @@ static const long _vq_lengthlist__44un1__p5_0[] = { static const static_codebook _44un1__p5_0 = { 2, 81, - (long *)_vq_lengthlist__44un1__p5_0, + (char *)_vq_lengthlist__44un1__p5_0, 1, -531628032, 1611661312, 4, 0, (long *)_vq_quantlist__44un1__p5_0, 0 @@ -7563,7 +7562,7 @@ static const long _vq_quantlist__44un1__p6_0[] = { 12, }; -static const long _vq_lengthlist__44un1__p6_0[] = { +static const char _vq_lengthlist__44un1__p6_0[] = { 1, 4, 4, 6, 6, 8, 8,10,10,11,11,15,15, 4, 5, 5, 8, 8, 9, 9,11,11,12,12,16,16, 4, 5, 6, 8, 8, 9, 9,11,11,12,12,14,14, 7, 8, 8, 9, 9,10,10,11,12, @@ -7579,7 +7578,7 @@ static const long _vq_lengthlist__44un1__p6_0[] = { static const static_codebook _44un1__p6_0 = { 2, 169, - (long *)_vq_lengthlist__44un1__p6_0, + (char *)_vq_lengthlist__44un1__p6_0, 1, -526516224, 1616117760, 4, 0, (long *)_vq_quantlist__44un1__p6_0, 0 @@ -7593,14 +7592,14 @@ static const long _vq_quantlist__44un1__p6_1[] = { 4, }; -static const long _vq_lengthlist__44un1__p6_1[] = { +static const char _vq_lengthlist__44un1__p6_1[] = { 2, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 6, 5, 5, 6, 5, 6, 6, 5, 6, 6, 6, 6, }; static const static_codebook _44un1__p6_1 = { 2, 25, - (long *)_vq_lengthlist__44un1__p6_1, + (char *)_vq_lengthlist__44un1__p6_1, 1, -533725184, 1611661312, 3, 0, (long *)_vq_quantlist__44un1__p6_1, 0 @@ -7614,7 +7613,7 @@ static const long _vq_quantlist__44un1__p7_0[] = { 4, }; -static const long _vq_lengthlist__44un1__p7_0[] = { +static const char _vq_lengthlist__44un1__p7_0[] = { 1, 5, 3,11,11,11,11,11,11,11, 8,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, @@ -7659,7 +7658,7 @@ static const long _vq_lengthlist__44un1__p7_0[] = { static const static_codebook _44un1__p7_0 = { 4, 625, - (long *)_vq_lengthlist__44un1__p7_0, + (char *)_vq_lengthlist__44un1__p7_0, 1, -518709248, 1626677248, 3, 0, (long *)_vq_quantlist__44un1__p7_0, 0 @@ -7681,7 +7680,7 @@ static const long _vq_quantlist__44un1__p7_1[] = { 12, }; -static const long _vq_lengthlist__44un1__p7_1[] = { +static const char _vq_lengthlist__44un1__p7_1[] = { 1, 4, 4, 6, 6, 6, 6, 9, 8, 9, 8, 8, 8, 5, 7, 7, 7, 7, 8, 8, 8,10, 8,10, 8, 9, 5, 7, 7, 8, 7, 7, 8,10,10,11,10,12,11, 7, 8, 8, 9, 9, 9,10,11,11, @@ -7697,7 +7696,7 @@ static const long _vq_lengthlist__44un1__p7_1[] = { static const static_codebook _44un1__p7_1 = { 2, 169, - (long *)_vq_lengthlist__44un1__p7_1, + (char *)_vq_lengthlist__44un1__p7_1, 1, -523010048, 1618608128, 4, 0, (long *)_vq_quantlist__44un1__p7_1, 0 @@ -7719,7 +7718,7 @@ static const long _vq_quantlist__44un1__p7_2[] = { 12, }; -static const long _vq_lengthlist__44un1__p7_2[] = { +static const char _vq_lengthlist__44un1__p7_2[] = { 3, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9, 9, 8, 4, 5, 5, 6, 6, 8, 8, 9, 8, 9, 9, 9, 9, 4, 5, 5, 7, 6, 8, 8, 8, 8, 9, 8, 9, 8, 6, 7, 7, 7, 8, 8, 8, 9, 9, @@ -7735,13 +7734,13 @@ static const long _vq_lengthlist__44un1__p7_2[] = { static const static_codebook _44un1__p7_2 = { 2, 169, - (long *)_vq_lengthlist__44un1__p7_2, + (char *)_vq_lengthlist__44un1__p7_2, 1, -531103744, 1611661312, 4, 0, (long *)_vq_quantlist__44un1__p7_2, 0 }; -static const long _huff_lengthlist__44un1__short[] = { +static const char _huff_lengthlist__44un1__short[] = { 12,12,14,12,14,14,14,14,12, 6, 6, 8, 9, 9,11,14, 12, 4, 2, 6, 6, 7,11,14,13, 6, 5, 7, 8, 9,11,14, 13, 8, 5, 8, 6, 8,12,14,12, 7, 7, 8, 8, 8,10,14, @@ -7750,8 +7749,9 @@ static const long _huff_lengthlist__44un1__short[] = { static const static_codebook _huff_book__44un1__short = { 2, 64, - (long *)_huff_lengthlist__44un1__short, + (char *)_huff_lengthlist__44un1__short, 0, 0, 0, 0, 0, NULL, 0 }; + diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/codebook.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/codebook.c similarity index 90% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/codebook.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/codebook.c index d6c475e0..7d5cd6f2 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/codebook.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/codebook.c @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: basic codebook pack/unpack/code/decode operations - last mod: $Id: codebook.c 17553 2010-10-21 17:54:26Z tterribe $ ********************************************************************/ @@ -53,16 +52,16 @@ int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *opb){ oggpack_write(opb,c->lengthlist[0]-1,5); /* 1 to 32 */ for(i=1;ientries;i++){ - long thisx=c->lengthlist[i]; - long last=c->lengthlist[i-1]; + char thisx=c->lengthlist[i]; + char last=c->lengthlist[i-1]; if(thisx>last){ for(j=last;jentries-count)); + oggpack_write(opb,i-count,ov_ilog(c->entries-count)); count=i; } } } - oggpack_write(opb,i-count,_ilog(c->entries-count)); + oggpack_write(opb,i-count,ov_ilog(c->entries-count)); }else{ /* length random. Again, we don't code the codeword itself, just @@ -159,7 +158,7 @@ static_codebook *vorbis_staticbook_unpack(oggpack_buffer *opb){ s->entries=oggpack_read(opb,24); if(s->entries==-1)goto _eofout; - if(_ilog(s->dim)+_ilog(s->entries)>24)goto _eofout; + if(ov_ilog(s->dim)+ov_ilog(s->entries)>24)goto _eofout; /* codeword ordering.... length ordered or unordered? */ switch((int)oggpack_read(opb,1)){ @@ -170,7 +169,7 @@ static_codebook *vorbis_staticbook_unpack(oggpack_buffer *opb){ if((s->entries*(unused?1:5)+7)>>3>opb->storage-oggpack_bytes(opb)) goto _eofout; /* unordered */ - s->lengthlist=(long*)_ogg_malloc(sizeof(*s->lengthlist)*s->entries); + s->lengthlist=(char*)_ogg_malloc(sizeof(*s->lengthlist)*s->entries); /* allocated but unused entries? */ if(unused){ @@ -200,10 +199,10 @@ static_codebook *vorbis_staticbook_unpack(oggpack_buffer *opb){ { long length=oggpack_read(opb,5)+1; if(length==0)goto _eofout; - s->lengthlist=(long*)_ogg_malloc(sizeof(*s->lengthlist)*s->entries); + s->lengthlist=(char*)_ogg_malloc(sizeof(*s->lengthlist)*s->entries); for(i=0;ientries;){ - long num=oggpack_read(opb,_ilog(s->entries-i)); + long num=oggpack_read(opb,ov_ilog(s->entries-i)); if(num==-1)goto _eofout; if(length>32 || num>s->entries-i || (num>0 && (num-1)>>(length-1)>1)){ @@ -248,7 +247,7 @@ static_codebook *vorbis_staticbook_unpack(oggpack_buffer *opb){ } /* quantized values */ - if(((quantvals * s->q_quant + 7) >> 3) > opb->storage-oggpack_bytes(opb)) + if(((quantvals*s->q_quant+7)>>3)>opb->storage-oggpack_bytes(opb)) goto _eofout; s->quantlist=(long*)_ogg_malloc(sizeof(*s->quantlist)*quantvals); for(i=0;iused_entries; } + /* Single entry codebooks use a firsttablen of 1 and a + dec_maxlength of 1. If a single-entry codebook gets here (due to + failure to read one bit above), the next look attempt will also + fail and we'll correctly kick out instead of trying to walk the + underformed tree */ + lok = oggpack_look(b, read); while(lok<0 && read>1) @@ -367,6 +372,7 @@ long vorbis_book_decode(codebook *book, oggpack_buffer *b){ } /* returns 0 on OK or -1 on eof *************************************/ +/* decode vector / dim granularity gaurding is done in the upper layer */ long vorbis_book_decodevs_add(codebook *book,float *a,oggpack_buffer *b,int n){ if(book->used_entries>0){ int step=n/book->dim; @@ -386,6 +392,7 @@ long vorbis_book_decodevs_add(codebook *book,float *a,oggpack_buffer *b,int n){ return(0); } +/* decode vector / dim granularity gaurding is done in the upper layer */ long vorbis_book_decodev_add(codebook *book,float *a,oggpack_buffer *b,int n){ if(book->used_entries>0){ int i,j,entry; @@ -402,6 +409,9 @@ long vorbis_book_decodev_add(codebook *book,float *a,oggpack_buffer *b,int n){ return(0); } +/* unlike the others, we guard against n not being an integer number + of internally rather than in the upper layer (called only by + floor0) */ long vorbis_book_decodev_set(codebook *book,float *a,oggpack_buffer *b,int n){ if(book->used_entries>0){ int i,j,entry; @@ -411,15 +421,15 @@ long vorbis_book_decodev_set(codebook *book,float *a,oggpack_buffer *b,int n){ entry = decode_packed_entry_number(book,b); if(entry==-1)return(-1); t = book->valuelist+entry*book->dim; - for (j=0;jdim;) + for (j=0;idim;){ a[i++]=t[j++]; + } } }else{ - int i,j; + int i; for(i=0;idim;) - a[i++]=0.f; + a[i++]=0.f; } } return(0); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/codebook.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/codebook.h similarity index 88% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/codebook.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/codebook.h index c8e11160..ce9324ad 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/codebook.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/codebook.h @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: basic shared codebook operations - last mod: $Id: codebook.h 17030 2010-03-25 06:52:55Z xiphmont $ ********************************************************************/ @@ -34,14 +33,14 @@ */ typedef struct static_codebook{ - long dim; /* codebook dimensions (elements per vector) */ - long entries; /* codebook entries */ - long *lengthlist; /* codeword lengths in bits */ + long dim; /* codebook dimensions (elements per vector) */ + long entries; /* codebook entries */ + char *lengthlist; /* codeword lengths in bits */ /* mapping ***************************************************************/ - int maptype; /* 0=none - 1=implicitly populated values from map column - 2=listed arbitrary values */ + int maptype; /* 0=none + 1=implicitly populated values from map column + 2=listed arbitrary values */ /* The below does a linear, single monotonic sequence mapping. */ long q_min; /* packed 32 bit float; quant value 0 maps to minval */ @@ -89,7 +88,6 @@ extern float *_book_logdist(const static_codebook *b,float *vals); extern float _float32_unpack(long val); extern long _float32_pack(float val); extern int _best(codebook *book, float *a, int step); -extern int _ilog(unsigned int v); extern long _book_maptype1_quantvals(const static_codebook *b); extern int vorbis_book_besterror(codebook *book,float *a,int step,int addmul); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/codec_internal.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/codec_internal.h similarity index 93% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/codec_internal.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/codec_internal.h index 8018488b..2ecf5e5c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/codec_internal.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/codec_internal.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: libvorbis codec headers - last mod: $Id: codec_internal.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ @@ -58,26 +57,6 @@ typedef void vorbis_info_mapping; #include "psy.h" #include "bitrate.h" -static int ilog(unsigned int v){ - int ret=0; - while(v){ - ret++; - v>>=1; - } - return(ret); -} - -static int ilog2(unsigned int v){ - int ret=0; - if(v)--v; - while(v){ - ret++; - v>>=1; - } - return(ret); -} - - typedef struct private_state { /* local lookup storage */ envelope_lookup *ve; /* envelope lookup */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/envelope.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/envelope.c similarity index 98% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/envelope.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/envelope.c index 16fbcf80..045c9a45 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/envelope.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/envelope.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: PCM data envelope analysis - last mod: $Id: envelope.c 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/envelope.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/envelope.h similarity index 94% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/envelope.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/envelope.h index fd15fb32..2ef60a82 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/envelope.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/envelope.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: PCM data envelope analysis and manipulation - last mod: $Id: envelope.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor0.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/floor0.c similarity index 94% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor0.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/floor0.c index 97a58983..fd33b608 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor0.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/floor0.c @@ -5,17 +5,15 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: floor backend 0 implementation - last mod: $Id: floor0.c 17558 2010-10-22 00:24:41Z tterribe $ ********************************************************************/ - #include #include #include @@ -166,7 +164,7 @@ static void *floor0_inverse1(vorbis_block *vb,vorbis_look_floor *i){ if(ampraw>0){ /* also handles the -1 out of data case */ long maxval=(1<ampbits)-1; float amp=(float)ampraw/maxval*info->ampdB; - int booknum=oggpack_read(&vb->opb,_ilog(info->numbooks)); + int booknum=oggpack_read(&vb->opb,ov_ilog(info->numbooks)); if(booknum!=-1 && booknumnumbooks){ /* be paranoid */ codec_setup_info *ci=(codec_setup_info *)vb->vd->vi->codec_setup; @@ -178,10 +176,9 @@ static void *floor0_inverse1(vorbis_block *vb,vorbis_look_floor *i){ vector */ float *lsp=(float*)_vorbis_block_alloc(vb,sizeof(*lsp)*(look->m+b->dim+1)); - for(j=0;jm;j+=b->dim) - if(vorbis_book_decodev_set(b,lsp+j,&vb->opb,b->dim)==-1)goto eop; + if(vorbis_book_decodev_set(b,lsp,&vb->opb,look->m)==-1)goto eop; for(j=0;jm;){ - for(k=0;kdim;k++,j++)lsp[j]+=last; + for(k=0;jm && kdim;k++,j++)lsp[j]+=last; last=lsp[j-1]; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/floor1.c similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/floor1.c index 817e60fc..a8c2ef96 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/floor1.c @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: floor backend 1 implementation - last mod: $Id: floor1.c 17555 2010-10-21 18:14:51Z tterribe $ ********************************************************************/ @@ -102,8 +101,10 @@ static void floor1_pack (vorbis_info_floor *i,oggpack_buffer *opb){ /* save out the post list */ oggpack_write(opb,info->mult-1,2); /* only 1,2,3,4 legal now */ - oggpack_write(opb,ilog2(maxposit),4); - rangebits=ilog2(maxposit); + /* maxposit cannot legally be less than 1; this is encode-side, we + can assume our setup is OK */ + oggpack_write(opb,ov_ilog(maxposit-1),4); + rangebits=ov_ilog(maxposit-1); for(j=0,k=0;jpartitions;j++){ count+=info->class_dim[info->partitionclass[j]]; @@ -152,6 +153,7 @@ static vorbis_info_floor *floor1_unpack (vorbis_info *vi,oggpack_buffer *opb){ for(j=0,k=0;jpartitions;j++){ count+=info->class_dim[info->partitionclass[j]]; + if(count>VIF_POSIT) goto err_out; for(;kpostlist[k+2]=oggpack_read(opb,rangebits); if(t<0 || t>=(1<frames++; - look->postbits+=ilog(look->quant_q-1)*2; - oggpack_write(opb,out[0],ilog(look->quant_q-1)); - oggpack_write(opb,out[1],ilog(look->quant_q-1)); + look->postbits+=ov_ilog(look->quant_q-1)*2; + oggpack_write(opb,out[0],ov_ilog(look->quant_q-1)); + oggpack_write(opb,out[1],ov_ilog(look->quant_q-1)); /* partition by partition */ @@ -854,13 +860,15 @@ int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, /* generate the partition's first stage cascade value */ if(csubbits){ - int maxval[8] = { 0 }; + int maxval[8]={0,0,0,0,0,0,0,0}; /* gcc's static analysis + issues a warning without + initialization */ for(k=0;kclass_subbook[classx][k]; + int booknum=info->class_subbook[classx][k]; if(booknum<0){ maxval[k]=1; }else{ - maxval[k]=sbooks[info->class_subbook[classx][k]]->entries; + maxval[k]=sbooks[info->class_subbook[classx][k]]->entries; } } for(k=0;kphrasebits+= - vorbis_book_encode(books+info->class_book[classx],cval,opb); + vorbis_book_encode(books+info->class_book[classx],cval,opb); #ifdef TRAIN_FLOOR1 { @@ -893,7 +901,7 @@ int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, /* write post values */ for(k=0;kclass_subbook[classx][bookas[k]]; + int book=info->class_subbook[classx][bookas[k]]; if(book>=0){ /* hack to allow training with 'bad' books */ if(out[j+k]<(books+book)->entries) @@ -962,8 +970,8 @@ static void *floor1_inverse1(vorbis_block *vb,vorbis_look_floor *in){ if(oggpack_read(&vb->opb,1)==1){ int *fit_value=(int*)_vorbis_block_alloc(vb,(look->posts)*sizeof(*fit_value)); - fit_value[0]=oggpack_read(&vb->opb,ilog(look->quant_q-1)); - fit_value[1]=oggpack_read(&vb->opb,ilog(look->quant_q-1)); + fit_value[0]=oggpack_read(&vb->opb,ov_ilog(look->quant_q-1)); + fit_value[1]=oggpack_read(&vb->opb,ov_ilog(look->quant_q-1)); /* partition by partition */ for(i=0,j=2;ipartitions;i++){ @@ -975,13 +983,13 @@ static void *floor1_inverse1(vorbis_block *vb,vorbis_look_floor *in){ /* decode the partition's first stage cascade value */ if(csubbits){ - cval=vorbis_book_decode(books+info->class_book[classx],&vb->opb); + cval=vorbis_book_decode(books+info->class_book[classx],&vb->opb); if(cval==-1)goto eop; } for(k=0;kclass_subbook[classx][cval&(csub-1)]; + int book=info->class_subbook[classx][cval&(csub-1)]; cval>>=csubbits; if(book>=0){ if((fit_value[j+k]=vorbis_book_decode(books+book,&vb->opb))==-1) @@ -1020,7 +1028,7 @@ static void *floor1_inverse1(vorbis_block *vb,vorbis_look_floor *in){ } } - fit_value[i] = (val + predicted) & 0x7fff; + fit_value[i]=(val+predicted)&0x7fff; fit_value[look->loneighbor[i-2]]&=0x7fff; fit_value[look->hineighbor[i-2]]&=0x7fff; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/highlevel.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/highlevel.h similarity index 93% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/highlevel.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/highlevel.h index e38f370f..7690e3eb 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/highlevel.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/highlevel.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: highlevel encoder setup struct separated out for vorbisenc clarity - last mod: $Id: highlevel.h 17195 2010-05-05 21:49:51Z giles $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/info.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/info.c similarity index 89% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/info.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/info.c index ab417e67..9af98540 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/info.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/info.c @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: maintain the info structure, info <-> header packets - last mod: $Id: info.c 17584 2010-11-01 19:26:16Z xiphmont $ ********************************************************************/ @@ -31,11 +30,10 @@ #include "misc.h" #include "os.h" -#define GENERAL_VENDOR_STRING "Xiph.Org libVorbis 1.3.2" -#define ENCODE_VENDOR_STRING "Xiph.Org libVorbis I 20101101 (Schaufenugget)" +#define GENERAL_VENDOR_STRING "Xiph.Org libVorbis 1.3.7" +#define ENCODE_VENDOR_STRING "Xiph.Org libVorbis I 20200704 (Reducing Environment)" /* helpers */ - static void _v_writestring(oggpack_buffer *o,const char *s, int bytes){ while(bytes--){ @@ -49,6 +47,10 @@ static void _v_readstring(oggpack_buffer *o,char *buf,int bytes){ } } +static int _v_toupper(int c) { + return (c >= 'a' && c <= 'z') ? (c & ~('a' - 'A')) : c; +} + void vorbis_comment_init(vorbis_comment *vc){ memset(vc,0,sizeof(*vc)); } @@ -66,11 +68,13 @@ void vorbis_comment_add(vorbis_comment *vc,const char *comment){ } void vorbis_comment_add_tag(vorbis_comment *vc, const char *tag, const char *contents){ - char *comment=(char*)alloca(strlen(tag)+strlen(contents)+2); /* +2 for = and \0 */ + /* Length for key and value +2 for = and \0 */ + char *comment=(char*)_ogg_malloc(strlen(tag)+strlen(contents)+2); strcpy(comment, tag); strcat(comment, "="); strcat(comment, contents); vorbis_comment_add(vc, comment); + _ogg_free(comment); } /* This is more or less the same as strncasecmp - but that doesn't exist @@ -78,7 +82,7 @@ void vorbis_comment_add_tag(vorbis_comment *vc, const char *tag, const char *con static int tagcompare(const char *s1, const char *s2, int n){ int c=0; while(c < n){ - if(toupper(s1[c]) != toupper(s2[c])) + if(_v_toupper(s1[c]) != _v_toupper(s2[c])) return !0; c++; } @@ -89,27 +93,30 @@ char *vorbis_comment_query(vorbis_comment *vc, const char *tag, int count){ long i; int found = 0; int taglen = strlen(tag)+1; /* +1 for the = we append */ - char *fulltag = (char*)alloca(taglen+ 1); + char *fulltag = (char*)_ogg_malloc(taglen+1); strcpy(fulltag, tag); strcat(fulltag, "="); for(i=0;icomments;i++){ if(!tagcompare(vc->user_comments[i], fulltag, taglen)){ - if(count == found) + if(count == found) { /* We return a pointer to the data, not a copy */ - return vc->user_comments[i] + taglen; - else + _ogg_free(fulltag); + return vc->user_comments[i] + taglen; + } else { found++; + } } } + _ogg_free(fulltag); return NULL; /* didn't find anything */ } int vorbis_comment_query_count(vorbis_comment *vc, const char *tag){ int i,count=0; int taglen = strlen(tag)+1; /* +1 for the = we append */ - char *fulltag = (char*)alloca(taglen+1); + char *fulltag = (char*)_ogg_malloc(taglen+1); strcpy(fulltag,tag); strcat(fulltag, "="); @@ -118,6 +125,7 @@ int vorbis_comment_query_count(vorbis_comment *vc, const char *tag){ count++; } + _ogg_free(fulltag); return count; } @@ -199,6 +207,7 @@ void vorbis_info_clear(vorbis_info *vi){ static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){ codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; + int bs; if(!ci)return(OV_EFAULT); vi->version=oggpack_read(opb,32); @@ -207,12 +216,16 @@ static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){ vi->channels=oggpack_read(opb,8); vi->rate=oggpack_read(opb,32); - vi->bitrate_upper=oggpack_read(opb,32); - vi->bitrate_nominal=oggpack_read(opb,32); - vi->bitrate_lower=oggpack_read(opb,32); + vi->bitrate_upper=(ogg_int32_t)oggpack_read(opb,32); + vi->bitrate_nominal=(ogg_int32_t)oggpack_read(opb,32); + vi->bitrate_lower=(ogg_int32_t)oggpack_read(opb,32); - ci->blocksizes[0]=1<blocksizes[1]=1<blocksizes[0]=1<blocksizes[1]=1<rate<1)goto err_out; if(vi->channels<1)goto err_out; @@ -263,7 +276,6 @@ static int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb){ static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb){ codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; int i; - if(!ci)return(OV_EFAULT); /* codebooks */ ci->books=oggpack_read(opb,8)+1; @@ -402,6 +414,10 @@ int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op) /* um... we didn't get the initial header */ return(OV_EBADHEADER); } + if(vc->vendor!=NULL){ + /* previously initialized comment header */ + return(OV_EBADHEADER); + } return(_vorbis_unpack_comment(vc,&opb)); @@ -410,6 +426,14 @@ int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op) /* um... we didn;t get the initial header or comments yet */ return(OV_EBADHEADER); } + if(vi->codec_setup==NULL){ + /* improperly initialized vorbis_info */ + return(OV_EFAULT); + } + if(((codec_setup_info *)vi->codec_setup)->books>0){ + /* previously initialized setup header */ + return(OV_EBADHEADER); + } return(_vorbis_unpack_books(vi,&opb)); @@ -427,7 +451,11 @@ int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op) static int _vorbis_pack_info(oggpack_buffer *opb,vorbis_info *vi){ codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; - if(!ci)return(OV_EFAULT); + if(!ci|| + ci->blocksizes[0]<64|| + ci->blocksizes[1]blocksizes[0]){ + return(OV_EFAULT); + } /* preamble */ oggpack_write(opb,0x01,8); @@ -442,8 +470,8 @@ static int _vorbis_pack_info(oggpack_buffer *opb,vorbis_info *vi){ oggpack_write(opb,vi->bitrate_nominal,32); oggpack_write(opb,vi->bitrate_lower,32); - oggpack_write(opb,ilog2(ci->blocksizes[0]),4); - oggpack_write(opb,ilog2(ci->blocksizes[1]),4); + oggpack_write(opb,ov_ilog(ci->blocksizes[0]-1),4); + oggpack_write(opb,ov_ilog(ci->blocksizes[1]-1),4); oggpack_write(opb,1,1); return(0); @@ -541,9 +569,12 @@ int vorbis_commentheader_out(vorbis_comment *vc, oggpack_buffer opb; oggpack_writeinit(&opb); - if(_vorbis_pack_comment(&opb,vc)) return OV_EIMPL; + if(_vorbis_pack_comment(&opb,vc)){ + oggpack_writeclear(&opb); + return OV_EIMPL; + } - op->packet = (unsigned char*) _ogg_malloc(oggpack_bytes(&opb)); + op->packet = (unsigned char*)_ogg_malloc(oggpack_bytes(&opb)); memcpy(op->packet, opb.buffer, oggpack_bytes(&opb)); op->bytes=oggpack_bytes(&opb); @@ -552,6 +583,7 @@ int vorbis_commentheader_out(vorbis_comment *vc, op->granulepos=0; op->packetno=1; + oggpack_writeclear(&opb); return 0; } @@ -565,7 +597,8 @@ int vorbis_analysis_headerout(vorbis_dsp_state *v, oggpack_buffer opb; private_state *b=(private_state*)v->backend_state; - if(!b){ + if(!b||vi->channels<=0||vi->channels>256){ + b = NULL; ret=OV_EFAULT; goto err_out; } @@ -624,7 +657,7 @@ int vorbis_analysis_headerout(vorbis_dsp_state *v, memset(op_code,0,sizeof(*op_code)); if(b){ - oggpack_writeclear(&opb); + if(vi->channels>0)oggpack_writeclear(&opb); if(b->header)_ogg_free(b->header); if(b->header1)_ogg_free(b->header1); if(b->header2)_ogg_free(b->header2); @@ -645,12 +678,7 @@ double vorbis_granule_time(vorbis_dsp_state *v,ogg_int64_t granulepos){ }else{ ogg_int64_t granuleoff=0xffffffff; granuleoff<<=31; - -#ifdef __GNUC__ - granuleoff |= 0x7ffffffffLL; -#else - granuleoff |= 0x7ffffffff; -#endif + granuleoff|=0x7ffffffff; return(((double)granulepos+2+granuleoff+granuleoff)/v->vi->rate); } } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lookup.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lookup.c similarity index 96% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lookup.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lookup.c index 3321ed3d..7cd01a44 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lookup.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lookup.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: lookup based functions - last mod: $Id: lookup.c 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lookup.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lookup.h similarity index 90% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lookup.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lookup.h index f8b5b827..ec05014f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lookup.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lookup.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: lookup based functions - last mod: $Id: lookup.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lookup_data.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lookup_data.h similarity index 98% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lookup_data.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lookup_data.h index 2424a1b3..7935715a 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lookup_data.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lookup_data.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: lookup data; generated by lookups.pl; edit there - last mod: $Id: lookup_data.h 16037 2009-05-26 21:10:58Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lpc.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lpc.c similarity index 97% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lpc.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lpc.c index 77d62ef0..eb260d84 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lpc.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lpc.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: LPC low level routines - last mod: $Id: lpc.c 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lpc.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lpc.h similarity index 89% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lpc.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lpc.h index e9b3865d..f3d4357b 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lpc.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lpc.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: LPC low level routines - last mod: $Id: lpc.h 16037 2009-05-26 21:10:58Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lsp.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lsp.c similarity index 97% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lsp.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lsp.c index 4824d0a3..eb76ce6c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lsp.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lsp.c @@ -6,19 +6,19 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: LSP (also called LSF) conversion routines - last mod: $Id: lsp.c 17538 2010-10-15 02:52:29Z tterribe $ The LSP generation code is taken (with minimal modification and a few bugfixes) from "On the Computation of the LSP Frequencies" by Joseph Rothweiler (see http://www.rothweiler.us for contact info). + The paper is available at: - http://www.myown1.com/joe/lsf + https://web.archive.org/web/20110810174000/http://home.myfairpoint.net/vzenxj75/myown1/joe/lsf/index.html ********************************************************************/ @@ -321,9 +321,9 @@ static int Laguerre_With_Deflation(float *a,int ord,float *r){ /* eval the polynomial and its first two derivatives */ for(i=m;i>0;i--){ - ppp = newx*ppp + pp; - pp = newx*pp + p; - p = newx*p + defl[i-1]; + ppp = newx*ppp + pp; + pp = newx*pp + p; + p = newx*p + defl[i-1]; } /* Laguerre's method */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lsp.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lsp.h similarity index 89% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lsp.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lsp.h index bacfb097..68b38daf 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lsp.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/lsp.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: LSP (also called LSF) conversion routines - last mod: $Id: lsp.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/mapping0.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/mapping0.c similarity index 98% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/mapping0.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/mapping0.c index 4af23ba4..943af830 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/mapping0.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/mapping0.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: channel mapping 0 implementation - last mod: $Id: mapping0.c 17022 2010-03-25 03:45:42Z xiphmont $ ********************************************************************/ @@ -45,16 +44,6 @@ static void mapping0_free_info(vorbis_info_mapping *i){ } } -static int ilog3(unsigned int v){ - int ret=0; - if(v)--v; - while(v){ - ret++; - v>>=1; - } - return(ret); -} - static void mapping0_pack(vorbis_info *vi,vorbis_info_mapping *vm, oggpack_buffer *opb){ int i; @@ -78,8 +67,8 @@ static void mapping0_pack(vorbis_info *vi,vorbis_info_mapping *vm, oggpack_write(opb,info->coupling_steps-1,8); for(i=0;icoupling_steps;i++){ - oggpack_write(opb,info->coupling_mag[i],ilog3(vi->channels)); - oggpack_write(opb,info->coupling_ang[i],ilog3(vi->channels)); + oggpack_write(opb,info->coupling_mag[i],ov_ilog(vi->channels-1)); + oggpack_write(opb,info->coupling_ang[i],ov_ilog(vi->channels-1)); } }else oggpack_write(opb,0,1); @@ -103,7 +92,7 @@ static vorbis_info_mapping *mapping0_unpack(vorbis_info *vi,oggpack_buffer *opb) int i,b; vorbis_info_mapping0 *info=(vorbis_info_mapping0*)_ogg_calloc(1,sizeof(*info)); codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; - memset(info,0,sizeof(*info)); + if(vi->channels<=0)goto err_out; b=oggpack_read(opb,1); if(b<0)goto err_out; @@ -119,8 +108,11 @@ static vorbis_info_mapping *mapping0_unpack(vorbis_info *vi,oggpack_buffer *opb) info->coupling_steps=oggpack_read(opb,8)+1; if(info->coupling_steps<=0)goto err_out; for(i=0;icoupling_steps;i++){ - int testM=info->coupling_mag[i]=oggpack_read(opb,ilog3(vi->channels)); - int testA=info->coupling_ang[i]=oggpack_read(opb,ilog3(vi->channels)); + /* vi->channels > 0 is enforced in the caller */ + int testM=info->coupling_mag[i]= + oggpack_read(opb,ov_ilog(vi->channels-1)); + int testA=info->coupling_ang[i]= + oggpack_read(opb,ov_ilog(vi->channels-1)); if(testM<0 || testA<0 || diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/masking.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/masking.h similarity index 99% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/masking.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/masking.h index 3576ab78..7a196a37 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/masking.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/masking.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: masking curve data for psychoacoustics - last mod: $Id: masking.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/mdct.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/mdct.c similarity index 99% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/mdct.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/mdct.c index df0737ea..3df6a0de 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/mdct.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/mdct.c @@ -6,13 +6,12 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: normalized modified discrete cosine transform power of two length transform only [64 <= n ] - last mod: $Id: mdct.c 16227 2009-07-08 06:58:46Z xiphmont $ Original algorithm adapted long ago from _The use of multirate filter banks for coding of high quality digital audio_, by T. Sporer, diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/mdct.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/mdct.h similarity index 92% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/mdct.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/mdct.h index db7bf88c..24844b9b 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/mdct.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/mdct.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: modified discrete cosine transform prototypes - last mod: $Id: mdct.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ @@ -28,7 +27,7 @@ #ifdef MDCT_INTEGERIZED #define DATA_TYPE int -#define REG_TYPE int +#define REG_TYPE register int #define TRIGBITS 14 #define cPI3_8 6270 #define cPI2_8 11585 diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/misc.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/misc.c new file mode 100644 index 00000000..70a091d7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/misc.c @@ -0,0 +1,216 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation https://xiph.org/ * + * * + ********************************************************************/ + +#define HEAD_ALIGN 32 +#include +#include +#include +#include +#include "vorbis/codec.h" +#define MISC_C +#include "misc.h" +#include + +static pthread_mutex_t memlock=PTHREAD_MUTEX_INITIALIZER; +static void **pointers=NULL; +static long *insertlist=NULL; /* We can't embed this in the pointer list; + a pointer can have any value... */ + +static char **files=NULL; +static long *file_bytes=NULL; +static int filecount=0; + +static int ptop=0; +static int palloced=0; +static int pinsert=0; + +typedef struct { + char *file; + long line; + long ptr; + long bytes; +} head; + +long global_bytes=0; +long start_time=-1; + +static void *_insert(void *ptr,long bytes,char *file,long line){ + ((head *)ptr)->file=file; + ((head *)ptr)->line=line; + ((head *)ptr)->ptr=pinsert; + ((head *)ptr)->bytes=bytes-HEAD_ALIGN; + + pthread_mutex_lock(&memlock); + if(pinsert>=palloced){ + palloced+=64; + if(pointers){ + pointers=(void **)realloc(pointers,sizeof(void **)*palloced); + insertlist=(long *)realloc(insertlist,sizeof(long *)*palloced); + }else{ + pointers=(void **)malloc(sizeof(void **)*palloced); + insertlist=(long *)malloc(sizeof(long *)*palloced); + } + } + + pointers[pinsert]=ptr; + + if(pinsert==ptop) + pinsert=++ptop; + else + pinsert=insertlist[pinsert]; + +#ifdef _VDBG_GRAPHFILE + { + FILE *out; + struct timeval tv; + static struct timezone tz; + int i; + char buffer[80]; + gettimeofday(&tv,&tz); + + for(i=0;ifile; + long bytes =((head *)ptr)->bytes; + int i; + + gettimeofday(&tv,&tz); + fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000), + global_bytes); + fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000), + global_bytes-((head *)ptr)->bytes); + fclose(out); + + for(i=0;ibytes; + + insert=((head *)ptr)->ptr; + insertlist[insert]=pinsert; + pinsert=insert; + + if(pointers[insert]==NULL){ + fprintf(stderr,"DEBUGGING MALLOC ERROR: freeing previously freed memory\n"); + fprintf(stderr,"\t%s %ld\n",((head *)ptr)->file,((head *)ptr)->line); + } + + if(global_bytes<0){ + fprintf(stderr,"DEBUGGING MALLOC ERROR: freeing unmalloced memory\n"); + } + + pointers[insert]=NULL; + pthread_mutex_unlock(&memlock); +} + +void _VDBG_dump(void){ + int i; + pthread_mutex_lock(&memlock); + for(i=0;ifile,ptr->line); + } + + pthread_mutex_unlock(&memlock); +} + +void *_VDBG_malloc(void *ptr,long bytes,char *file,long line){ + if(bytes<=0) + fprintf(stderr,"bad malloc request (%ld bytes) from %s:%ld\n",bytes,file,line); + + bytes+=HEAD_ALIGN; + if(ptr){ + ptr-=HEAD_ALIGN; + _ripremove(ptr); + ptr=realloc(ptr,bytes); + }else{ + ptr=malloc(bytes); + memset(ptr,0,bytes); + } + return _insert(ptr,bytes,file,line); +} + +void _VDBG_free(void *ptr,char *file,long line){ + if(ptr){ + ptr-=HEAD_ALIGN; + _ripremove(ptr); + free(ptr); + } +} + diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/misc.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/misc.h similarity index 90% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/misc.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/misc.h index 0692afe1..7a15764d 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/misc.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/misc.h @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: miscellaneous prototypes - last mod: $Id: misc.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ @@ -21,6 +20,7 @@ extern void *_vorbis_block_alloc(vorbis_block *vb,long bytes); extern void _vorbis_block_ripcord(vorbis_block *vb); +extern int ov_ilog(ogg_uint32_t v); #ifdef ANALYSIS extern int analysis_noisy; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/floor_all.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/floor_all.h similarity index 98% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/floor_all.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/floor_all.h index 4c220261..3ab034ef 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/floor_all.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/floor_all.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: key floor settings - last mod: $Id: floor_all.h 17050 2010-03-26 01:34:42Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_11.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_11.h similarity index 94% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_11.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_11.h index c7ac96c1..9d8ed357 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_11.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_11.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: 11kHz settings - last mod: $Id: psych_11.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ @@ -48,3 +47,4 @@ static const noise3 _psy_noisebias_11[3]={ }; static const double _noise_thresh_11[3]={ .3,.5,.5 }; + diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_16.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_16.h similarity index 97% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_16.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_16.h index 1c10b395..49cbf7c4 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_16.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_16.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: 16kHz settings - last mod: $Id: psych_16.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_44.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_44.h similarity index 99% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_44.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_44.h index 8cdc03e3..d69f6246 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_44.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_44.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: key psychoacoustic settings for 44.1/48kHz - last mod: $Id: psych_44.h 16962 2010-03-11 07:30:34Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_8.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_8.h similarity index 96% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_8.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_8.h index 0e2dd573..a19817f7 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/psych_8.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/psych_8.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: 8kHz psychoacoustic settings - last mod: $Id: psych_8.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_16.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_16.h similarity index 97% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_16.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_16.h index dcaca545..15e161c8 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_16.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_16.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: toplevel residue templates 16/22kHz - last mod: $Id: residue_16.h 16962 2010-03-11 07:30:34Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_44.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_44.h similarity index 98% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_44.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_44.h index d84c7527..56ce84cb 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_44.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_44.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: toplevel residue templates for 32/44.1/48kHz - last mod: $Id: residue_44.h 16962 2010-03-11 07:30:34Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_44p51.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_44p51.h similarity index 99% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_44p51.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_44p51.h index 45dd0c8d..4b9e0441 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_44p51.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_44p51.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: toplevel residue templates for 32/44.1/48kHz uncoupled - last mod: $Id$ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_44u.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_44u.h similarity index 98% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_44u.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_44u.h index 3bfa36fd..5394e34f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_44u.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_44u.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: toplevel residue templates for 32/44.1/48kHz uncoupled - last mod: $Id: residue_44u.h 16962 2010-03-11 07:30:34Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_8.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_8.h similarity index 96% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_8.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_8.h index 0dbe3f6e..3f945d9c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/residue_8.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/residue_8.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: toplevel residue templates 8/11kHz - last mod: $Id: residue_8.h 16962 2010-03-11 07:30:34Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_11.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_11.h similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_11.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_11.h index 4c2d619c..5ade5dd1 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_11.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_11.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: 11kHz settings - last mod: $Id: setup_11.h 16894 2010-02-12 20:32:12Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_16.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_16.h similarity index 96% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_16.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_16.h index 336007f9..8b2daafa 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_16.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_16.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: 16kHz settings - last mod: $Id: setup_16.h 16894 2010-02-12 20:32:12Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_22.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_22.h similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_22.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_22.h index 4fd5e571..eef5a4e7 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_22.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_22.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: 22kHz settings - last mod: $Id: setup_22.h 17026 2010-03-25 05:00:27Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_32.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_32.h similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_32.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_32.h index 2275ac96..f87cb767 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_32.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_32.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: toplevel settings for 32kHz - last mod: $Id: setup_32.h 16894 2010-02-12 20:32:12Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_44.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_44.h similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_44.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_44.h index 1926622a..7d206ac2 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_44.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_44.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: toplevel settings for 44.1/48kHz - last mod: $Id: setup_44.h 16962 2010-03-11 07:30:34Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_44p51.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_44p51.h similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_44p51.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_44p51.h index 5834f930..65a78055 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_44p51.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_44p51.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: toplevel settings for 44.1/48kHz 5.1 surround modes - last mod: $Id$ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_44u.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_44u.h similarity index 93% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_44u.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_44u.h index 1c245961..bcd89f28 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_44u.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_44u.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: toplevel settings for 44.1/48kHz uncoupled modes - last mod: $Id: setup_44u.h 16962 2010-03-11 07:30:34Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_8.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_8.h similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_8.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_8.h index 14c48374..16b02e01 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_8.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_8.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: 8kHz settings - last mod: $Id: setup_8.h 16894 2010-02-12 20:32:12Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_X.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_X.h similarity index 96% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_X.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_X.h index a69f5d40..27807c10 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/modes/setup_X.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/modes/setup_X.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: catch-all toplevel settings for q modes only - last mod: $Id: setup_X.h 16894 2010-02-12 20:32:12Z xiphmont $ ********************************************************************/ 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.7/lib/os.h similarity index 89% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/os.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/os.h index bec8a1ee..868857d6 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.7/lib/os.h @@ -7,13 +7,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: #ifdef jail to whip a few platforms into the UNIX ideal. - last mod: $Id: os.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ @@ -61,7 +60,7 @@ void *_alloca(size_t size); # define FAST_HYPOT hypot #endif -#endif +#endif /* _V_IFDEFJAIL_H_ */ #ifdef HAVE_ALLOCA_H # include @@ -119,8 +118,8 @@ static inline int vorbis_ftoi(double f){ /* yes, double! Otherwise, /* MSVC inline assembly. 32 bit only; inline ASM isn't implemented in the - * 64 bit compiler */ -#if defined(_MSC_VER) && !defined(_WIN64) && !defined(_WIN32_WCE) + * 64 bit compiler and doesn't work on arm. */ +#if (defined(_MSC_VER) && defined(_M_IX86) && !defined(_WIN32_WCE)) && (! JUCE_ARM) # define VORBIS_FPU_CONTROL typedef ogg_int16_t vorbis_fpu_control; @@ -135,9 +134,11 @@ static __inline int vorbis_ftoi(double f){ } static __inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){ + (void)fpu; } static __inline void vorbis_fpu_restore(vorbis_fpu_control fpu){ + (void)fpu; } #endif /* Special MSVC 32 bit implementation */ @@ -145,7 +146,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 && ((JUCE_MSVC && JUCE_64BIT) || (JUCE_GCC && defined (__x86_64__))) +#if (((JUCE_MSVC && JUCE_64BIT) || (JUCE_GCC && defined (__x86_64__)))) && (! JUCE_ARM) # define VORBIS_FPU_CONTROL typedef ogg_int16_t vorbis_fpu_control; @@ -155,10 +156,12 @@ static __inline int vorbis_ftoi(double f){ return _mm_cvtsd_si32(_mm_load_sd(&f)); } -static __inline void vorbis_fpu_setround(vorbis_fpu_control*){ +static __inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){ + (void)fpu; } -static __inline void vorbis_fpu_restore(vorbis_fpu_control){ +static __inline void vorbis_fpu_restore(vorbis_fpu_control fpu){ + (void)fpu; } #endif /* Special MSVC x64 implementation */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/psy.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/psy.c similarity index 98% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/psy.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/psy.c index ca180392..a43c92b6 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/psy.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/psy.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: psychoacoustics not including preecho - last mod: $Id: psy.c 17569 2010-10-26 17:09:47Z xiphmont $ ********************************************************************/ @@ -600,11 +599,12 @@ static void bark_noise_hybridmp(int n,const long *b, XY[i] = tXY; } - for (i = 0, x = 0.f;; i++, x += 1.f) { + for (i = 0, x = 0.f; i < n; i++, x += 1.f) { lo = b[i] >> 16; - if( lo>=0 ) break; hi = b[i] & 0xffff; + if( lo>=0 || -lo>=n ) break; + if( hi>=n ) break; tN = N[hi] + N[-lo]; tX = X[hi] - X[-lo]; @@ -616,17 +616,17 @@ static void bark_noise_hybridmp(int n,const long *b, B = tN * tXY - tX * tY; D = tN * tXX - tX * tX; R = (A + x * B) / D; - if (R < 0.f) - R = 0.f; + if (R < 0.f) R = 0.f; noise[i] = R - offset; } - for ( ;; i++, x += 1.f) { + for ( ; i < n; i++, x += 1.f) { lo = b[i] >> 16; hi = b[i] & 0xffff; - if(hi>=n)break; + if( lo<0 || lo>=n ) break; + if( hi>=n ) break; tN = N[hi] - N[lo]; tX = X[hi] - X[lo]; @@ -642,6 +642,7 @@ static void bark_noise_hybridmp(int n,const long *b, noise[i] = R - offset; } + for ( ; i < n; i++, x += 1.f) { R = (A + x * B) / D; @@ -652,10 +653,11 @@ static void bark_noise_hybridmp(int n,const long *b, if (fixed <= 0) return; - for (i = 0, x = 0.f;; i++, x += 1.f) { + for (i = 0, x = 0.f; i < n; i++, x += 1.f) { hi = i + fixed / 2; lo = hi - fixed; - if(lo>=0)break; + if ( hi>=n ) break; + if ( lo>=0 ) break; tN = N[hi] + N[-lo]; tX = X[hi] - X[-lo]; @@ -671,11 +673,12 @@ static void bark_noise_hybridmp(int n,const long *b, if (R - offset < noise[i]) noise[i] = R - offset; } - for ( ;; i++, x += 1.f) { + for ( ; i < n; i++, x += 1.f) { hi = i + fixed / 2; lo = hi - fixed; - if(hi>=n)break; + if ( hi>=n ) break; + if ( lo<0 ) break; tN = N[hi] - N[lo]; tX = X[hi] - X[lo]; @@ -1022,7 +1025,9 @@ void _vp_couple_quantize_normalize(int blobno, int limit = g->coupling_pointlimit[p->vi->blockflag][blobno]; float prepoint=stereo_threshholds[g->coupling_prepointamp[blobno]]; float postpoint=stereo_threshholds[g->coupling_postpointamp[blobno]]; - //float de=0.1*p->m_val; /* a blend of the AoTuV M2 and M3 code here and below */ +#if 0 + float de=0.1*p->m_val; /* a blend of the AoTuV M2 and M3 code here and below */ +#endif /* mdct is our raw mdct output, floor not removed. */ /* inout passes in the ifloor, passes back quantized result */ @@ -1156,27 +1161,28 @@ void _vp_couple_quantize_normalize(int blobno, reM[j] += reA[j]; qeM[j] = fabs(reM[j]); }else{ +#if 0 /* AoTuV */ /** @ M2 ** The boost problem by the combination of noise normalization and point stereo is eased. However, this is a temporary patch. by Aoyumi @ 2004/04/18 */ - /*float derate = (1.0 - de*((float)(j-limit+i) / (float)(n-limit)));*/ - /* elliptical + float derate = (1.0 - de*((float)(j-limit+i) / (float)(n-limit))); + /* elliptical */ if(reM[j]+reA[j]<0){ reM[j] = - (qeM[j] = (fabs(reM[j])+fabs(reA[j]))*derate*derate); }else{ reM[j] = (qeM[j] = (fabs(reM[j])+fabs(reA[j]))*derate*derate); - }*/ - + } +#else /* elliptical */ if(reM[j]+reA[j]<0){ reM[j] = - (qeM[j] = fabs(reM[j])+fabs(reA[j])); }else{ reM[j] = (qeM[j] = fabs(reM[j])+fabs(reA[j])); } - +#endif } reA[j]=qeA[j]=0.f; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/psy.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/psy.h similarity index 97% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/psy.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/psy.h index c1ea8244..d9a04e8b 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/psy.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/psy.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: random psychoacoustics (not including preecho) - last mod: $Id: psy.h 16946 2010-03-03 16:12:40Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/registry.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/registry.c similarity index 92% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/registry.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/registry.c index bea7e455..61abb367 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/registry.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/registry.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: registry for time, floor, res backends and channel mappings - last mod: $Id: registry.c 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/registry.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/registry.h similarity index 90% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/registry.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/registry.h index 3ae04776..b823aa60 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/registry.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/registry.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: registry for time, floor, res backends and channel mappings - last mod: $Id: registry.h 15531 2008-11-24 23:50:06Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/res0.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/res0.c similarity index 95% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/res0.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/res0.c index 6a3dfed5..36ea4afe 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/res0.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/res0.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: residue backend 0, 1 and 2 implementation - last mod: $Id: res0.c 17556 2010-10-21 18:25:19Z tterribe $ ********************************************************************/ @@ -31,9 +30,6 @@ #include "misc.h" #include "os.h" -//#define TRAIN_RES 1 -//#define TRAIN_RESAUX 1 - #if defined(TRAIN_RES) || defined (TRAIN_RESAUX) #include #endif @@ -152,17 +148,6 @@ static void res0_free_look(vorbis_look_residue *i){ } } -#if 0 -static int ilog(unsigned int v){ - int ret=0; - while(v){ - ret++; - v>>=1; - } - return(ret); -} -#endif - static int icount(unsigned int v){ int ret=0; while(v){ @@ -188,7 +173,7 @@ static void res0_pack(vorbis_info_residue *vr,oggpack_buffer *opb){ bitmask of one indicates this partition class has bits to write this pass */ for(j=0;jpartitions;j++){ - if(ilog(info->secondstages[j])>3){ + if(ov_ilog(info->secondstages[j])>3){ /* yes, this is a minor hack due to not thinking ahead */ oggpack_write(opb,info->secondstages[j],3); oggpack_write(opb,1,1); @@ -286,7 +271,7 @@ static vorbis_look_residue *res0_look(vorbis_dsp_state *vd, look->partbooks=(codebook***)_ogg_calloc(look->parts,sizeof(*look->partbooks)); for(j=0;jparts;j++){ - int stages=ilog(info->secondstages[j]); + int stages=ov_ilog(info->secondstages[j]); if(stages){ if(stages>maxstage)maxstage=stages; look->partbooks[j]=(codebook**) _ogg_calloc(stages,sizeof(*look->partbooks[j])); @@ -392,8 +377,13 @@ static int local_book_besterror(codebook *book,int *a){ return(index); } +#ifdef TRAIN_RES static int _encodepart(oggpack_buffer *opb,int *vec, int n, - codebook *book,long* /* acc */){ + codebook *book,long *acc){ +#else +static int _encodepart(oggpack_buffer *opb,int *vec, int n, + codebook *book){ +#endif int i,bits=0; int dim=book->dim; int step=n/dim; @@ -536,12 +526,18 @@ static long **_2class(vorbis_block *vb,vorbis_look_residue *vl,int **in, } static int _01forward(oggpack_buffer *opb, - vorbis_block*, vorbis_look_residue *vl, + vorbis_look_residue *vl, int **in,int ch, long **partword, +#ifdef TRAIN_RES int (*encode)(oggpack_buffer *,int *,int, codebook *,long *), - int /* submap */){ + int submap +#else + int (*encode)(oggpack_buffer *,int *,int, + codebook *) +#endif +){ long i,j,k,s; vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; vorbis_info_residue0 *info=look->info; @@ -611,9 +607,8 @@ static int _01forward(oggpack_buffer *opb, codebook *statebook=look->partbooks[partword[j][i]][s]; if(statebook){ int ret; - long *accumulator=NULL; - #ifdef TRAIN_RES + long *accumulator=NULL; accumulator=look->training_data[s][partword[j][i]]; { int l; @@ -625,10 +620,12 @@ static int _01forward(oggpack_buffer *opb, look->training_max[s][partword[j][i]]=samples[l]; } } -#endif - ret=encode(opb,in[j]+offset,samples_per_partition, statebook,accumulator); +#else + ret=encode(opb,in[j]+offset,samples_per_partition, + statebook); +#endif look->postbits+=ret; resbits[partword[j][i]]+=ret; @@ -639,19 +636,6 @@ static int _01forward(oggpack_buffer *opb, } } - /*{ - long total=0; - long totalbits=0; - fprintf(stderr,"%d :: ",vb->mode); - for(k=0;k0){ int partvals=n/samples_per_partition; int partwords=(partvals+partitions_per_word-1)/partitions_per_word; - int ***partword=(int***)alloca(ch*sizeof(*partword)); + int ***partword=(int***)alloca(ch*sizeof(*partword)); for(j=0;jstages;s++){ @@ -731,12 +715,18 @@ static int res0_inverse(vorbis_block *vb,vorbis_look_residue *vl, static int res1_forward(oggpack_buffer *opb,vorbis_block *vb,vorbis_look_residue *vl, int **in,int *nonzero,int ch, long **partword, int submap){ int i,used=0; + (void)vb; for(i=0;i0){ int partvals=n/samples_per_partition; int partwords=(partvals+partitions_per_word-1)/partitions_per_word; - int **partword=(int**)_vorbis_block_alloc(vb,partwords*sizeof(*partword)); + int **partword=(int**)_vorbis_block_alloc(vb,partwords*sizeof(*partword)); for(i=0;i dB, Bark and Mel scales - last mod: $Id: scales.h 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/sharedbook.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/sharedbook.c similarity index 80% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/sharedbook.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/sharedbook.c index f2cc9842..7a16f471 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/sharedbook.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/sharedbook.c @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: basic shared codebook operations - last mod: $Id: sharedbook.c 17030 2010-03-25 06:52:55Z xiphmont $ ********************************************************************/ @@ -20,6 +19,7 @@ #endif #include +#include #include #include #include "../../ogg.h" @@ -30,13 +30,11 @@ #include "scales.h" /**** pack/unpack helpers ******************************************/ -int _ilog(unsigned int v){ - int ret=0; - while(v){ - ret++; - v>>=1; - } - return(ret); + +int ov_ilog(ogg_uint32_t v){ + int ret; + for(ret=0;v;ret++)v>>=1; + return ret; } /* 32 bit float (not IEEE; nonnormalized mantissa + @@ -56,7 +54,7 @@ long _float32_pack(float val){ sign=0x80000000; val= -val; } - exp= floor(log(val)/log(2.f)+.001); //+epsilon + exp= floor(log(val)/log(2.f)+.001); /* +epsilon */ mant=rint(ldexp(val,(VQ_FMAN-1)-exp)); exp=(exp+VQ_FEXP_BIAS)<>VQ_FMAN; if(sign)mant= -mant; - return(ldexp(mant,exp-(VQ_FMAN-1)-VQ_FEXP_BIAS)); + exp=exp-(VQ_FMAN-1)-VQ_FEXP_BIAS; + /* clamp excessive exponent values */ + if (exp>63){ + exp=63; + } + if (exp<-63){ + exp=-63; + } + return(ldexp(mant,exp)); } /* given a list of word lengths, generate a list of codewords. Works for length ordered or unordered, always assigns the lowest valued codewords first. Extended to handle unused entries (length 0) */ -static ogg_uint32_t *_make_words(long *l,long n,long sparsecount){ +ogg_uint32_t *_make_words(char *l,long n,long sparsecount){ long i,j,count=0; ogg_uint32_t marker[33]; ogg_uint32_t *r=(ogg_uint32_t*)_ogg_malloc((sparsecount?sparsecount:n)*sizeof(*r)); @@ -129,16 +135,15 @@ static ogg_uint32_t *_make_words(long *l,long n,long sparsecount){ if(sparsecount==0)count++; } - /* sanity check the huffman tree; an underpopulated tree must be - rejected. The only exception is the one-node pseudo-nil tree, - which appears to be underpopulated because the tree doesn't - really exist; there's only one possible 'codeword' or zero bits, - but the above tree-gen code doesn't mark that. */ - if(sparsecount != 1){ + /* any underpopulated tree must be rejected. */ + /* Single-entry codebooks are a retconned extension to the spec. + They have a single codeword '0' of length 1 that results in an + underpopulated tree. Shield that case from the underformed tree check. */ + if(!(count==1 && marker[2]==2)){ for(i=1;i<33;i++) if(marker[i] & (0xffffffffUL>>(32-i))){ - _ogg_free(r); - return(NULL); + _ogg_free(r); + return(NULL); } } @@ -165,25 +170,34 @@ static ogg_uint32_t *_make_words(long *l,long n,long sparsecount){ that's portable and totally safe against roundoff, but I haven't thought of it. Therefore, we opt on the side of caution */ long _book_maptype1_quantvals(const static_codebook *b){ - long vals=floor(pow((float)b->entries,1.f/b->dim)); + long vals; + if(b->entries<1){ + return(0); + } + vals=floor(pow((float)b->entries,1.f/b->dim)); /* the above *should* be reliable, but we'll not assume that FP is ever reliable when bitstream sync is at stake; verify via integer means that vals really is the greatest value of dim for which vals^b->bim <= b->entries */ /* treat the above as an initial guess */ + if(vals<1){ + vals=1; + } while(1){ long acc=1; long acc1=1; int i; for(i=0;idim;i++){ + if(b->entries/valsentries && acc1>b->entries){ + if(i>=b->dim && acc<=b->entries && acc1>b->entries){ return(vals); }else{ - if(acc>b->entries){ + if(idim || acc>b->entries){ vals--; }else{ vals++; @@ -292,7 +306,7 @@ int vorbis_book_init_encode(codebook *c,const static_codebook *s){ c->used_entries=s->entries; c->dim=s->dim; c->codelist=_make_words(s->lengthlist,s->entries,0); - //c->valuelist=_book_unquantize(s,s->entries,NULL); + /* c->valuelist=_book_unquantize(s,s->entries,NULL); */ c->quantvals=_book_maptype1_quantvals(s); c->minval=(int)rint(_float32_unpack(s->q_min)); c->delta=(int)rint(_float32_unpack(s->q_delta)); @@ -319,9 +333,10 @@ static int JUCE_CDECL sort32a(const void *a,const void *b){ int vorbis_book_init_decode(codebook *c,const static_codebook *s){ int i,j,n=0,tabn; int *sortindex; + memset(c,0,sizeof(*c)); - /* count actually used entries */ + /* count actually used entries and find max length */ for(i=0;ientries;i++) if(s->lengthlist[i]>0) n++; @@ -331,7 +346,6 @@ int vorbis_book_init_decode(codebook *c,const static_codebook *s){ c->dim=s->dim; if(n>0){ - /* two different remappings go on here. First, we collapse the likely sparse codebook down only to @@ -367,7 +381,6 @@ int vorbis_book_init_decode(codebook *c,const static_codebook *s){ c->codelist[sortindex[i]]=codes[i]; _ogg_free(codes); - c->valuelist=_book_unquantize(s,n,sortindex); c->dec_index=(int*)_ogg_malloc(n*sizeof(*c->dec_index)); @@ -375,52 +388,63 @@ int vorbis_book_init_decode(codebook *c,const static_codebook *s){ if(s->lengthlist[i]>0) c->dec_index[sortindex[n++]]=i; - c->dec_codelengths=(char*)_ogg_malloc(n*sizeof(*c->dec_codelengths)); + c->dec_codelengths=(char*)_ogg_malloc(n*sizeof(*c->dec_codelengths)); + c->dec_maxlength=0; for(n=0,i=0;ientries;i++) - if(s->lengthlist[i]>0) + if(s->lengthlist[i]>0){ c->dec_codelengths[sortindex[n++]]=s->lengthlist[i]; + if(s->lengthlist[i]>c->dec_maxlength) + c->dec_maxlength=s->lengthlist[i]; + } - c->dec_firsttablen=_ilog(c->used_entries)-4; /* this is magic */ - if(c->dec_firsttablen<5)c->dec_firsttablen=5; - if(c->dec_firsttablen>8)c->dec_firsttablen=8; - - tabn=1<dec_firsttablen; - c->dec_firsttable=(ogg_uint32_t*)_ogg_calloc(tabn,sizeof(*c->dec_firsttable)); - c->dec_maxlength=0; + if(n==1 && c->dec_maxlength==1){ + /* special case the 'single entry codebook' with a single bit + fastpath table (that always returns entry 0 )in order to use + unmodified decode paths. */ + c->dec_firsttablen=1; + c->dec_firsttable=(ogg_uint32_t*)_ogg_calloc(2,sizeof(*c->dec_firsttable)); + c->dec_firsttable[0]=c->dec_firsttable[1]=1; - for(i=0;idec_maxlengthdec_codelengths[i]) - c->dec_maxlength=c->dec_codelengths[i]; - if(c->dec_codelengths[i]<=c->dec_firsttablen){ - ogg_uint32_t orig=bitreverse(c->codelist[i]); - for(j=0;j<(1<<(c->dec_firsttablen-c->dec_codelengths[i]));j++) - c->dec_firsttable[orig|(j<dec_codelengths[i])]=i+1; + }else{ + c->dec_firsttablen=ov_ilog(c->used_entries)-4; /* this is magic */ + if(c->dec_firsttablen<5)c->dec_firsttablen=5; + if(c->dec_firsttablen>8)c->dec_firsttablen=8; + + tabn=1<dec_firsttablen; + c->dec_firsttable=(ogg_uint32_t*)_ogg_calloc(tabn,sizeof(*c->dec_firsttable)); + + for(i=0;idec_codelengths[i]<=c->dec_firsttablen){ + ogg_uint32_t orig=bitreverse(c->codelist[i]); + for(j=0;j<(1<<(c->dec_firsttablen-c->dec_codelengths[i]));j++) + c->dec_firsttable[orig|(j<dec_codelengths[i])]=i+1; + } } - } - /* now fill in 'unused' entries in the firsttable with hi/lo search - hints for the non-direct-hits */ - { - ogg_uint32_t mask=0xfffffffeUL<<(31-c->dec_firsttablen); - long lo=0,hi=0; - - for(i=0;idec_firsttablen); - if(c->dec_firsttable[bitreverse(word)]==0){ - while((lo+1)codelist[lo+1]<=word)lo++; - while( hi=(c->codelist[hi]&mask))hi++; - - /* we only actually have 15 bits per hint to play with here. - In order to overflow gracefully (nothing breaks, efficiency - just drops), encode as the difference from the extremes. */ - { - unsigned long loval=lo; - unsigned long hival=n-hi; - - if(loval>0x7fff)loval=0x7fff; - if(hival>0x7fff)hival=0x7fff; - c->dec_firsttable[bitreverse(word)]= - 0x80000000UL | (loval<<15) | hival; + /* now fill in 'unused' entries in the firsttable with hi/lo search + hints for the non-direct-hits */ + { + ogg_uint32_t mask=0xfffffffeUL<<(31-c->dec_firsttablen); + long lo=0,hi=0; + + for(i=0;idec_firsttablen); + if(c->dec_firsttable[bitreverse(word)]==0){ + while((lo+1)codelist[lo+1]<=word)lo++; + while( hi=(c->codelist[hi]&mask))hi++; + + /* we only actually have 15 bits per hint to play with here. + In order to overflow gracefully (nothing breaks, efficiency + just drops), encode as the difference from the extremes. */ + { + unsigned long loval=lo; + unsigned long hival=n-hi; + + if(loval>0x7fff)loval=0x7fff; + if(hival>0x7fff)hival=0x7fff; + c->dec_firsttable[bitreverse(word)]= + 0x80000000UL | (loval<<15) | hival; + } } } } @@ -563,6 +587,7 @@ void run_test(static_codebook *b,float *comp){ exit(1); } } + free(out); } int main(){ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/smallft.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/smallft.c similarity index 99% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/smallft.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/smallft.c index 2dd0d3f6..bf68c831 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/smallft.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/smallft.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: *unnormalized* fft transform - last mod: $Id: smallft.c 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ @@ -835,6 +834,10 @@ static void dradb4(int ido,int l1,float *cc,float *ch,float *wa1, t1+=t2; t4+=t2; } + + // JUCE CHANGE STARTS HERE + (void) t5; + // JUCE CHANGE ENDS HERE } static void dradbg(int ido,int ip,int l1,int idl1,float *cc,float *c1, diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/smallft.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/smallft.h similarity index 90% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/smallft.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/smallft.h index dd2099bb..f195a49b 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/smallft.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/smallft.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: fft transform - last mod: $Id: smallft.h 13293 2007-07-24 00:09:47Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/synthesis.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/synthesis.c similarity index 92% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/synthesis.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/synthesis.c index 0b6b8d4a..cf7545e7 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/synthesis.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/synthesis.c @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: single-block PCM synthesis - last mod: $Id: synthesis.c 17474 2010-09-30 03:41:41Z gmaxwell $ ********************************************************************/ @@ -145,6 +144,11 @@ long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ oggpack_buffer opb; int mode; + if(ci==NULL || ci->modes<=0){ + /* codec setup not properly intialized */ + return(OV_EFAULT); + } + oggpack_readinit(&opb,op->packet,op->bytes); /* Check the packet type */ @@ -153,18 +157,9 @@ long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ return(OV_ENOTAUDIO); } - { - int modebits=0; - int v=ci->modes; - while(v>1){ - modebits++; - v>>=1; - } - - /* read our mode and pre/post windowsize */ - mode=oggpack_read(&opb,modebits); - } - if(mode==-1)return(OV_EBADPACKET); + /* read our mode and pre/post windowsize */ + mode=oggpack_read(&opb,ov_ilog(ci->modes-1)); + if(mode==-1 || !ci->mode_param[mode])return(OV_EBADPACKET); return(ci->blocksizes[ci->mode_param[mode]->blockflag]); } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/vorbisenc.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/vorbisenc.c similarity index 98% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/vorbisenc.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/vorbisenc.c index 50d858f8..be36e1c2 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/vorbisenc.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/vorbisenc.c @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: simple programmatic interface for encoder mode setup - last mod: $Id: vorbisenc.c 17028 2010-03-25 05:22:15Z xiphmont $ ********************************************************************/ @@ -133,9 +132,9 @@ typedef struct { const double *global_mapping; const adj_stereo *stereo_modes; - const static_codebook *const *const * floor_books; + const static_codebook *const *const *const floor_books; const vorbis_info_floor1 *floor_params; - int floor_mappings; + const int floor_mappings; const int **floor_mapping_list; const vorbis_mapping_template *maps; @@ -685,6 +684,7 @@ int vorbis_encode_setup_init(vorbis_info *vi){ highlevel_encode_setup *hi=&ci->hi; if(ci==NULL)return(OV_EINVAL); + if(vi->channels<1||vi->channels>255)return(OV_EINVAL); if(!hi->impulse_block_p)i0=1; /* too low/high an ATH floater is nonsensical, but doesn't break anything */ @@ -903,8 +903,12 @@ int vorbis_encode_setup_vbr(vorbis_info *vi, long channels, long rate, float quality){ - codec_setup_info *ci=(codec_setup_info*) vi->codec_setup; - highlevel_encode_setup *hi=&ci->hi; + codec_setup_info *ci; + highlevel_encode_setup *hi; + if(rate<=0) return OV_EINVAL; + + ci=(codec_setup_info*)vi->codec_setup; + hi=&ci->hi; quality+=.0000001; if(quality>=1.)quality=.9999; @@ -948,9 +952,14 @@ int vorbis_encode_setup_managed(vorbis_info *vi, long nominal_bitrate, long min_bitrate){ - codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; - highlevel_encode_setup *hi=&ci->hi; - double tnominal=nominal_bitrate; + codec_setup_info *ci; + highlevel_encode_setup *hi; + double tnominal; + if(rate<=0) return OV_EINVAL; + + ci=(codec_setup_info*)vi->codec_setup; + hi=&ci->hi; + tnominal=nominal_bitrate; if(nominal_bitrate<=0.){ if(max_bitrate>0.){ @@ -1202,7 +1211,7 @@ int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg){ hi->req, hi->managed, &new_base); - if(!hi->setup)return OV_EIMPL; + if(!new_template)return OV_EIMPL; hi->setup=new_template; hi->base_setting=new_base; vorbis_encode_setup_setting(vi,vi->channels,vi->rate); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/vorbisfile.c similarity index 89% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/vorbisfile.c index a22a283b..15bb0713 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/vorbisfile.c @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: stdio-based convenience library for opening/seeking/decoding - last mod: $Id: vorbisfile.c 17573 2010-10-27 14:53:59Z xiphmont $ ********************************************************************/ @@ -86,11 +85,14 @@ static long _get_data(OggVorbis_File *vf){ /* save a tiny smidge of verbosity to make the code more readable */ static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ if(vf->datasource){ - if(!(vf->callbacks.seek_func)|| - (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1) - return OV_EREAD; - vf->offset=offset; - ogg_sync_reset(&vf->oy); + /* only seek if the file position isn't already there */ + if(vf->offset != offset){ + if(!(vf->callbacks.seek_func)|| + (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1) + return OV_EREAD; + vf->offset=offset; + ogg_sync_reset(&vf->oy); + } }else{ /* shouldn't happen unless someone writes a broken callback */ return OV_EFAULT; @@ -144,14 +146,12 @@ static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, } } -/* find the latest page beginning before the current stream cursor - position. Much dirtier than the above as Ogg doesn't have any - backward search linkage. no 'readp' as it will certainly have to - read. */ +/* find the latest page beginning before the passed in position. Much + dirtier than the above as Ogg doesn't have any backward search + linkage. no 'readp' as it will certainly have to read. */ /* returns offset or OV_EREAD, OV_FAULT */ -static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){ - ogg_int64_t begin=vf->offset; - ogg_int64_t end=begin; +static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_int64_t begin,ogg_page *og){ + ogg_int64_t end = begin; ogg_int64_t ret; ogg_int64_t offset=-1; @@ -226,11 +226,10 @@ static int _lookup_page_serialno(ogg_page *og, long *serialno_list, int n){ info of last page of the matching serial number instead of the very last page. If no page of the specified serialno is seen, it will return the info of last page and alter *serialno. */ -static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, +static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, ogg_int64_t begin, long *serial_list, int serial_n, int *serialno, ogg_int64_t *granpos){ ogg_page og; - ogg_int64_t begin=vf->offset; ogg_int64_t end=begin; ogg_int64_t ret; @@ -271,6 +270,10 @@ static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, } } } + /*We started from the beginning of the stream and found nothing. + This should be impossible unless the contents of the stream changed out + from under us after we read from it.*/ + if(!begin&&vf->offset<0)return OV_EBADLINK; } /* we're not interested in the page... just the serialno and granpos. */ @@ -331,6 +334,9 @@ static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc, /* vorbis header; continue setup */ vf->ready_state=STREAMSET; if((ret=vorbis_synthesis_headerin(vi,vc,&op))){ + // JUCE CHANGE STARTS HERE + (void) ret; + // JUCE CHANGE ENDS HERE ret=OV_EBADHEADER; goto bail_header; } @@ -444,9 +450,11 @@ static ogg_int64_t _initial_pcmoffset(OggVorbis_File *vf, vorbis_info *vi){ while((result=ogg_stream_packetout(&vf->os,&op))){ if(result>0){ /* ignore holes */ long thisblock=vorbis_packet_blocksize(vi,&op); - if(lastblock!=-1) - accumulated+=(lastblock+thisblock)>>2; - lastblock=thisblock; + if(thisblock>=0){ + if(lastblock!=-1) + accumulated+=(lastblock+thisblock)>>2; + lastblock=thisblock; + } } } @@ -500,10 +508,10 @@ static int _bisect_forward_serialno(OggVorbis_File *vf, down to (or just started with) a single link. Now we need to find the last vorbis page belonging to the first vorbis stream for this link. */ - + searched = end; while(endserial != serialno){ endserial = serialno; - vf->offset=_get_prev_page_serial(vf,currentno_list,currentnos,&endserial,&endgran); + searched=_get_prev_page_serial(vf,searched,currentno_list,currentnos,&endserial,&endgran); } vf->links=m+1; @@ -524,10 +532,15 @@ static int _bisect_forward_serialno(OggVorbis_File *vf, }else{ + /* last page is not in the starting stream's serial number list, + so we have multiple links. Find where the stream that begins + our bisection ends. */ + long *next_serialno_list=NULL; int next_serialnos=0; vorbis_info vi; vorbis_comment vc; + int testserial = serialno+1; /* the below guards against garbage seperating the last and first pages of two links. */ @@ -540,10 +553,8 @@ static int _bisect_forward_serialno(OggVorbis_File *vf, bisect=(searched+endsearched)/2; } - if(bisect != vf->offset){ - ret=_seek_helper(vf,bisect); - if(ret)return(ret); - } + ret=_seek_helper(vf,bisect); + if(ret)return(ret); last=_get_next_page(vf,&og,-1); if(last==OV_EREAD)return(OV_EREAD); @@ -556,28 +567,22 @@ static int _bisect_forward_serialno(OggVorbis_File *vf, } /* Bisection point found */ - /* for the time being, fetch end PCM offset the simple way */ - { - int testserial = serialno+1; - vf->offset = next; - while(testserial != serialno){ - testserial = serialno; - vf->offset=_get_prev_page_serial(vf,currentno_list,currentnos,&testserial,&searchgran); - } + searched = next; + while(testserial != serialno){ + testserial = serialno; + searched = _get_prev_page_serial(vf,searched,currentno_list,currentnos,&testserial,&searchgran); } - if(vf->offset!=next){ - ret=_seek_helper(vf,next); - if(ret)return(ret); - } + ret=_seek_helper(vf,next); + if(ret)return(ret); ret=_fetch_headers(vf,&vi,&vc,&next_serialno_list,&next_serialnos,NULL); if(ret)return(ret); serialno = vf->os.serialno; dataoffset = vf->offset; - /* this will consume a page, however the next bistection always + /* this will consume a page, however the next bisection always starts with a raw seek */ pcmoffset = _initial_pcmoffset(vf,&vi); @@ -644,11 +649,11 @@ static int _open_seekable2(OggVorbis_File *vf){ /* Get the offset of the last page of the physical bitstream, or, if we're lucky the last vorbis page of this link as most OggVorbis files will contain a single logical bitstream */ - end=_get_prev_page_serial(vf,vf->serialnos+2,vf->serialnos[1],&endserial,&endgran); + end=_get_prev_page_serial(vf,vf->end,vf->serialnos+2,vf->serialnos[1],&endserial,&endgran); if(end<0)return(end); /* now determine bitstream structure recursively */ - if(_bisect_forward_serialno(vf,0,dataoffset,vf->offset,endgran,endserial, + if(_bisect_forward_serialno(vf,0,dataoffset,end,endgran,endserial, vf->serialnos+2,vf->serialnos[1],0)<0)return(OV_EREAD); vf->offsets[0]=0; @@ -782,6 +787,9 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, if(!readp)return(0); if((ret=_get_next_page(vf,&og,-1))<0){ + // JUCE CHANGE STARTS HERE + (void) ret; + // JUCE CHANGE ENDS HERE return(OV_EOF); /* eof. leave unitialized */ } @@ -862,7 +870,10 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, if(ret)return(ret); vf->current_serialno=vf->os.serialno; vf->current_link++; - //link=0; + link=0; + // JUCE CHANGE STARTS HERE + (void) link; + // JUCE CHANGE ENDS HERE } } } @@ -1061,7 +1072,11 @@ int ov_halfrate_p(OggVorbis_File *vf){ /* Only partially open the vorbis file; test for Vorbisness, and load the headers for the first chain. Do not seek (although test for seekability). Use ov_test_open to finish opening the file, else - ov_clear to close/free it. Same return codes as open. */ + ov_clear to close/free it. Same return codes as open. + + Note that vorbisfile does _not_ take ownership of the file if the + call fails; the calling applicaiton is responsible for closing the file + if this call returns an error. */ int ov_test_callbacks(void *f,OggVorbis_File *vf, const char *initial,long ibytes,ov_callbacks callbacks) @@ -1234,7 +1249,6 @@ double ov_time_total(OggVorbis_File *vf,int i){ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ ogg_stream_state work_os; - int ret; if(vf->ready_stateseekable) @@ -1257,8 +1271,12 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ vf->current_serialno); /* must set serialno */ vorbis_synthesis_restart(&vf->vd); - ret=_seek_helper(vf,pos); - if(ret)goto seek_error; + if(_seek_helper(vf,pos)) { + /* dump the machine so we're in a known state */ + vf->pcm_offset=-1; + _decode_clear(vf); + return OV_EBADLINK; + } /* we need to make sure the pcm_offset is set, but we don't want to advance the raw cursor past good packets just to get to the first @@ -1392,13 +1410,6 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ vf->bittrack=0.f; vf->samptrack=0.f; return(0); - - seek_error: - /* dump the machine so we're in a known state */ - vf->pcm_offset=-1; - ogg_stream_clear(&work_os); - _decode_clear(vf); - return OV_EBADLINK; } /* Page granularity seek (faster than sample granularity because we @@ -1423,22 +1434,48 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ if(pos>=total)break; } - /* search within the logical bitstream for the page with the highest - pcm_pos preceding (or equal to) pos. There is a danger here; - missing pages or incorrect frame number information in the - bitstream could make our task impossible. Account for that (it - would be an error condition) */ + /* Search within the logical bitstream for the page with the highest + pcm_pos preceding pos. If we're looking for a position on the + first page, bisection will halt without finding our position as + it's before the first explicit granulepos fencepost. That case is + handled separately below. + + There is a danger here; missing pages or incorrect frame number + information in the bitstream could make our task impossible. + Account for that (it would be an error condition) */ + + /* new search algorithm originally by HB (Nicholas Vinen) */ - /* new search algorithm by HB (Nicholas Vinen) */ { ogg_int64_t end=vf->offsets[link+1]; - ogg_int64_t begin=vf->offsets[link]; + ogg_int64_t begin=vf->dataoffsets[link]; ogg_int64_t begintime = vf->pcmlengths[link*2]; ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; ogg_int64_t target=pos-total+begintime; - ogg_int64_t best=begin; + ogg_int64_t best=-1; + int got_page=0; ogg_page og; + + // JUCE CHANGE STARTS HERE + if (pos < begin) + begin = pos; + + ogg_int64_t initialBegin = begin; + // JUCE CHANGE ENDS HERE + + /* if we have only one page, there will be no bisection. Grab the page here */ + if(begin==end){ + result=_seek_helper(vf,begin); + if(result) goto seek_error; + + result=_get_next_page(vf,&og,1); + if(result<0) goto seek_error; + + got_page=1; + } + + /* bisection loop */ while(beginoffset){ - result=_seek_helper(vf,bisect); - if(result) goto seek_error; - } + result=_seek_helper(vf,bisect); + if(result) goto seek_error; + /* read loop within the bisection loop */ while(beginoffset); if(result==OV_EREAD) goto seek_error; if(result<0){ + /* there is no next page! */ if(bisect<=begin+1) - end=begin; /* found it */ + /* No bisection left to perform. We've either found the + best candidate already or failed. Exit loop. */ + end=begin; else{ + /* We tried to load a fraction of the last page; back up a + bit and try to get the whole last page */ if(bisect==0) goto seek_error; bisect-=CHUNKSIZE; + + /* don't repeat/loop on a read we've already performed */ if(bisect<=begin)bisect=begin+1; + + /* seek and cntinue bisection */ result=_seek_helper(vf,bisect); if(result) goto seek_error; } }else{ ogg_int64_t granulepos; + got_page=1; + /* got a page. analyze it */ + /* only consider pages from primary vorbis stream */ if(ogg_page_serialno(&og)!=vf->serialnos[link]) continue; + /* only consider pages with the granulepos set */ granulepos=ogg_page_granulepos(&og); if(granulepos==-1)continue; if(granuleposoffset; /* raw offset of next page */ begintime=granulepos; + /* if we're before our target but within a short distance, + don't bisect; read forward */ if(target-begintime>44100)break; - bisect=begin; /* *not* begin + 1 */ + + bisect=begin; /* *not* begin + 1 as above */ }else{ - if(bisect<=begin+1) - end=begin; /* found it */ - else{ - if(end==vf->offset){ /* we're pretty close - we'd be stuck in */ + + /* This is one of our pages, but the granpos is + post-target; it is not a bisection return + candidate. (The only way we'd use it is if it's the + first page in the stream; we handle that case later + outside the bisection) */ + if(bisect<=begin+1){ + /* No bisection left to perform. We've either found the + best candidate already or failed. Exit loop. */ + end=begin; + }else{ + if(end==vf->offset){ + /* bisection read to the end; use the known page + boundary (result) to update bisection, back up a + little bit, and try again */ end=result; - bisect-=CHUNKSIZE; /* an endless loop otherwise. */ + bisect-=CHUNKSIZE; if(bisect<=begin)bisect=begin+1; result=_seek_helper(vf,bisect); if(result) goto seek_error; }else{ + /* Normal bisection */ end=bisect; endtime=granulepos; break; @@ -1508,9 +1574,49 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ } } - /* found our page. seek to it, update pcm offset. Easier case than - raw_seek, don't keep packets preceding granulepos. */ - { + /* Out of bisection: did it 'fail?' */ + if(best == -1){ + + /* Check the 'looking for data in first page' special case; + bisection would 'fail' because our search target was before the + first PCM granule position fencepost. */ + + if(got_page && + // JUCE CHANGE STARTS HERE + /*begin == vf->dataoffsets[link] &&*/ + begin == initialBegin && + // JUCE CHANGE ENDS HERE + ogg_page_serialno(&og)==vf->serialnos[link]){ + + /* Yes, this is the beginning-of-stream case. We already have + our page, right at the beginning of PCM data. Set state + and return. */ + + vf->pcm_offset=total; + + if(link!=vf->current_link){ + /* Different link; dump entire decode machine */ + _decode_clear(vf); + + vf->current_link=link; + vf->current_serialno=vf->serialnos[link]; + vf->ready_state=STREAMSET; + + }else{ + vorbis_synthesis_restart(&vf->vd); + } + + ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + ogg_stream_pagein(&vf->os,&og); + + }else + goto seek_error; + + }else{ + + /* Bisection found our page. seek to it, update pcm offset. Easier case than + raw_seek, don't keep packets preceding granulepos. */ + ogg_page og; ogg_packet op; @@ -1540,23 +1646,23 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ while(1){ result=ogg_stream_packetpeek(&vf->os,&op); if(result==0){ - /* !!! the packet finishing this page originated on a - preceding page. Keep fetching previous pages until we - get one with a granulepos or without the 'continued' flag - set. Then just use raw_seek for simplicity. */ - - result=_seek_helper(vf,best); - if(result<0) goto seek_error; - - while(1){ - result=_get_prev_page(vf,&og); + /* No packet returned; we exited the bisection with 'best' + pointing to a page with a granule position, so the packet + finishing this page ('best') originated on a preceding + page. Keep fetching previous pages until we get one with + a granulepos or without the 'continued' flag set. Then + just use raw_seek for simplicity. */ + /* Do not rewind past the beginning of link data; if we do, + it's either a bug or a broken stream */ + result=best; + while(result>vf->dataoffsets[link]){ + result=_get_prev_page(vf,result,&og); if(result<0) goto seek_error; if(ogg_page_serialno(&og)==vf->current_serialno && (ogg_page_granulepos(&og)>-1 || !ogg_page_continued(&og))){ return ov_raw_seek(vf,result); } - vf->offset=result; } } if(result<0){ @@ -1566,11 +1672,13 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ if(op.granulepos!=-1){ vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; if(vf->pcm_offset<0)vf->pcm_offset=0; - vf->pcm_offset+=total; + vf->pcm_offset+=total; break; }else result=ogg_stream_packetout(&vf->os,NULL); + // JUCE CHANGE STARTS HERE (void) result; + // JUCE CHANGE ENDS HERE } } } @@ -1884,6 +1992,7 @@ long ov_read_filter(OggVorbis_File *vf,char *buffer,int length, long samples; if(vf->ready_stateready_state==INITSET){ @@ -1910,6 +2019,8 @@ long ov_read_filter(OggVorbis_File *vf,char *buffer,int length, long bytespersample=word * channels; vorbis_fpu_control fpu; (void) fpu; + + if(channels<1||channels>255)return(OV_EINVAL); if(samples>length/bytespersample)samples=length/bytespersample; if(samples <= 0) @@ -1927,16 +2038,9 @@ long ov_read_filter(OggVorbis_File *vf,char *buffer,int length, vorbis_fpu_setround(&fpu); for(j=0;j= -1.0f ? f : -1.0f) : 1.0f); - val = vorbis_ftoi(f * 127.f); - #else val=vorbis_ftoi(pcm[i][j]*128.f); if(val>127)val=127; else if(val<-128)val=-128; - #endif *buffer++=val+off; } vorbis_fpu_restore(fpu); @@ -2069,14 +2173,14 @@ long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length, } } -extern float *vorbis_window(vorbis_dsp_state *v,int W); +extern const float *vorbis_window(vorbis_dsp_state *v,int W); static void _ov_splice(float **pcm,float **lappcm, int n1, int n2, int ch1, int ch2, - float *w1, float *w2){ + const float *w1, const float *w2){ int i,j; - float *w=w1; + const float *w=w1; int n=n1; if(n1>n2){ @@ -2175,8 +2279,9 @@ static void _ov_getlap(OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd, memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples); lapcount+=samples; } - + // JUCE CHANGE STARTS HERE (void) lapcount; + // JUCE CHANGE ENDS HERE } } @@ -2186,7 +2291,7 @@ int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){ vorbis_info *vi1,*vi2; float **lappcm; float **pcm; - float *w1,*w2; + const float *w1,*w2; int n1,n2,i,ret,hs1,hs2; if(vf1==vf2)return(0); /* degenerate case */ @@ -2240,7 +2345,7 @@ static int _ov_64_seek_lap(OggVorbis_File *vf,ogg_int64_t pos, vorbis_info *vi; float **lappcm; float **pcm; - float *w1,*w2; + const float *w1,*w2; int n1,n2,ch1,ch2,hs; int i,ret; @@ -2301,7 +2406,7 @@ static int _ov_d_seek_lap(OggVorbis_File *vf,double pos, vorbis_info *vi; float **lappcm; float **pcm; - float *w1,*w2; + const float *w1,*w2; int n1,n2,ch1,ch2,hs; int i,ret; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/window.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/window.c similarity index 99% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/window.c rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/window.c index 4946af91..2151b278 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/window.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/window.c @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: window functions - last mod: $Id: window.c 16227 2009-07-08 06:58:46Z xiphmont $ ********************************************************************/ @@ -19,8 +18,9 @@ #include #include "os.h" #include "misc.h" +#include "window.h" -static float vwin64[32] = { +static const float vwin64[32] = { 0.0009460463F, 0.0085006468F, 0.0235352254F, 0.0458950567F, 0.0753351908F, 0.1115073077F, 0.1539457973F, 0.2020557475F, 0.2551056759F, 0.3122276645F, 0.3724270287F, 0.4346027792F, @@ -31,7 +31,7 @@ static float vwin64[32] = { 0.9989462667F, 0.9997230082F, 0.9999638688F, 0.9999995525F, }; -static float vwin128[64] = { +static const float vwin128[64] = { 0.0002365472F, 0.0021280687F, 0.0059065254F, 0.0115626550F, 0.0190823442F, 0.0284463735F, 0.0396300935F, 0.0526030430F, 0.0673285281F, 0.0837631763F, 0.1018564887F, 0.1215504095F, @@ -50,7 +50,7 @@ static float vwin128[64] = { 0.9999331503F, 0.9999825563F, 0.9999977357F, 0.9999999720F, }; -static float vwin256[128] = { +static const float vwin256[128] = { 0.0000591390F, 0.0005321979F, 0.0014780301F, 0.0028960636F, 0.0047854363F, 0.0071449926F, 0.0099732775F, 0.0132685298F, 0.0170286741F, 0.0212513119F, 0.0259337111F, 0.0310727950F, @@ -85,7 +85,7 @@ static float vwin256[128] = { 0.9999958064F, 0.9999989077F, 0.9999998584F, 0.9999999983F, }; -static float vwin512[256] = { +static const float vwin512[256] = { 0.0000147849F, 0.0001330607F, 0.0003695946F, 0.0007243509F, 0.0011972759F, 0.0017882983F, 0.0024973285F, 0.0033242588F, 0.0042689632F, 0.0053312973F, 0.0065110982F, 0.0078081841F, @@ -152,7 +152,7 @@ static float vwin512[256] = { 0.9999997377F, 0.9999999317F, 0.9999999911F, 0.9999999999F, }; -static float vwin1024[512] = { +static const float vwin1024[512] = { 0.0000036962F, 0.0000332659F, 0.0000924041F, 0.0001811086F, 0.0002993761F, 0.0004472021F, 0.0006245811F, 0.0008315063F, 0.0010679699F, 0.0013339631F, 0.0016294757F, 0.0019544965F, @@ -283,7 +283,7 @@ static float vwin1024[512] = { 0.9999999836F, 0.9999999957F, 0.9999999994F, 1.0000000000F, }; -static float vwin2048[1024] = { +static const float vwin2048[1024] = { 0.0000009241F, 0.0000083165F, 0.0000231014F, 0.0000452785F, 0.0000748476F, 0.0001118085F, 0.0001561608F, 0.0002079041F, 0.0002670379F, 0.0003335617F, 0.0004074748F, 0.0004887765F, @@ -542,7 +542,7 @@ static float vwin2048[1024] = { 0.9999999990F, 0.9999999997F, 1.0000000000F, 1.0000000000F, }; -static float vwin4096[2048] = { +static const float vwin4096[2048] = { 0.0000002310F, 0.0000020791F, 0.0000057754F, 0.0000113197F, 0.0000187121F, 0.0000279526F, 0.0000390412F, 0.0000519777F, 0.0000667623F, 0.0000833949F, 0.0001018753F, 0.0001222036F, @@ -1057,7 +1057,7 @@ static float vwin4096[2048] = { 0.9999999999F, 1.0000000000F, 1.0000000000F, 1.0000000000F, }; -static float vwin8192[4096] = { +static const float vwin8192[4096] = { 0.0000000578F, 0.0000005198F, 0.0000014438F, 0.0000028299F, 0.0000046780F, 0.0000069882F, 0.0000097604F, 0.0000129945F, 0.0000166908F, 0.0000208490F, 0.0000254692F, 0.0000305515F, @@ -2084,7 +2084,7 @@ static float vwin8192[4096] = { 1.0000000000F, 1.0000000000F, 1.0000000000F, 1.0000000000F, }; -static float *vwin[8] = { +static const float *const vwin[8] = { vwin64, vwin128, vwin256, @@ -2095,7 +2095,7 @@ static float *vwin[8] = { vwin8192, }; -float *_vorbis_window_get(int n){ +const float *_vorbis_window_get(int n){ return vwin[n]; } @@ -2105,8 +2105,8 @@ void _vorbis_apply_window(float *d,int *winno,long *blocksizes, nW=(W?nW:0); { - float *windowLW=vwin[winno[lW]]; - float *windowNW=vwin[winno[nW]]; + const float *windowLW=vwin[winno[lW]]; + const float *windowNW=vwin[winno[nW]]; long n=blocksizes[W]; long ln=blocksizes[lW]; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/window.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/window.h similarity index 85% rename from JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/window.h rename to JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/window.h index 192bd9cf..33d83f85 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/window.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.7/lib/window.h @@ -6,19 +6,18 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: window functions - last mod: $Id: window.h 13293 2007-07-24 00:09:47Z xiphmont $ ********************************************************************/ #ifndef _V_WINDOW_ #define _V_WINDOW_ -extern float *_vorbis_window_get(int n); +extern const float *_vorbis_window_get(int n); extern void _vorbis_apply_window(float *d,int *winno,long *blocksizes, int lW,int W,int nW); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/ogg.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/ogg.h index 2555a42a..330ea3c6 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/ogg.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/ogg.h @@ -5,13 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** function: toplevel libogg include - last mod: $Id: ogg.h,v 1.1 2007/06/07 17:48:18 jules_rms Exp $ ********************************************************************/ #ifndef _OGG_H @@ -21,8 +20,14 @@ extern "C" { #endif +#include #include "os_types.h" +typedef struct { + void *iov_base; + size_t iov_len; +} ogg_iovec_t; + typedef struct { long endbyte; int endbit; @@ -41,16 +46,6 @@ typedef struct { long body_len; } ogg_page; - -static inline ogg_uint32_t ogg_bitreverse(ogg_uint32_t x){ - x= ((x>>16)&0x0000ffffUL) | ((x<<16)&0xffff0000UL); - x= ((x>> 8)&0x00ff00ffUL) | ((x<< 8)&0xff00ff00UL); - x= ((x>> 4)&0x0f0f0f0fUL) | ((x<< 4)&0xf0f0f0f0UL); - x= ((x>> 2)&0x33333333UL) | ((x<< 2)&0xccccccccUL); - return((x>> 1)&0x55555555UL) | ((x<< 1)&0xaaaaaaaaUL); -} - - /* ogg_stream_state contains the current encode/decode state of a logical Ogg bitstream **********************************************************/ @@ -63,8 +58,8 @@ typedef struct { int *lacing_vals; /* The values that will go to the segment table */ ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact - this way, but it is simple coupled to the - lacing fifo */ + this way, but it is simple coupled to the + lacing fifo */ long lacing_storage; long lacing_fill; long lacing_packet; @@ -79,10 +74,10 @@ typedef struct { of a logical bitstream */ long serialno; long pageno; - ogg_int64_t packetno; /* sequence number for decode; the framing + ogg_int64_t packetno; /* sequence number for decode; the framing knows where there's a hole in the data, but we need coupling so that the codec - (which is in a seperate abstraction + (which is in a separate abstraction layer) also knows about the gap */ ogg_int64_t granulepos; @@ -100,10 +95,10 @@ typedef struct { ogg_int64_t granulepos; ogg_int64_t packetno; /* sequence number for decode; the framing - knows where there's a hole in the data, - but we need coupling so that the codec - (which is in a seperate abstraction - layer) also knows about the gap */ + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a separate abstraction + layer) also knows about the gap */ } ogg_packet; typedef struct { @@ -120,6 +115,7 @@ typedef struct { /* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ extern void oggpack_writeinit(oggpack_buffer *b); +extern int oggpack_writecheck(oggpack_buffer *b); extern void oggpack_writetrunc(oggpack_buffer *b,long bits); extern void oggpack_writealign(oggpack_buffer *b); extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits); @@ -138,6 +134,7 @@ extern long oggpack_bits(oggpack_buffer *b); extern unsigned char *oggpack_get_buffer(oggpack_buffer *b); extern void oggpackB_writeinit(oggpack_buffer *b); +extern int oggpackB_writecheck(oggpack_buffer *b); extern void oggpackB_writetrunc(oggpack_buffer *b,long bits); extern void oggpackB_writealign(oggpack_buffer *b); extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits); @@ -158,15 +155,20 @@ extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b); /* Ogg BITSTREAM PRIMITIVES: encoding **************************/ extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +extern int ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov, + int count, long e_o_s, ogg_int64_t granulepos); extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill); extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_flush_fill(ogg_stream_state *os, ogg_page *og, int nfill); /* Ogg BITSTREAM PRIMITIVES: decoding **************************/ extern int ogg_sync_init(ogg_sync_state *oy); extern int ogg_sync_clear(ogg_sync_state *oy); extern int ogg_sync_reset(ogg_sync_state *oy); -extern int ogg_sync_destroy(ogg_sync_state *oy); +extern int ogg_sync_destroy(ogg_sync_state *oy); +extern int ogg_sync_check(ogg_sync_state *oy); extern char *ogg_sync_buffer(ogg_sync_state *oy, long size); extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes); @@ -183,18 +185,19 @@ extern int ogg_stream_clear(ogg_stream_state *os); extern int ogg_stream_reset(ogg_stream_state *os); extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno); extern int ogg_stream_destroy(ogg_stream_state *os); +extern int ogg_stream_check(ogg_stream_state *os); extern int ogg_stream_eos(ogg_stream_state *os); extern void ogg_page_checksum_set(ogg_page *og); -extern int ogg_page_version(ogg_page *og); -extern int ogg_page_continued(ogg_page *og); -extern int ogg_page_bos(ogg_page *og); -extern int ogg_page_eos(ogg_page *og); -extern ogg_int64_t ogg_page_granulepos(ogg_page *og); -extern int ogg_page_serialno(ogg_page *og); -extern long ogg_page_pageno(ogg_page *og); -extern int ogg_page_packets(ogg_page *og); +extern int ogg_page_version(const ogg_page *og); +extern int ogg_page_continued(const ogg_page *og); +extern int ogg_page_bos(const ogg_page *og); +extern int ogg_page_eos(const ogg_page *og); +extern ogg_int64_t ogg_page_granulepos(const ogg_page *og); +extern int ogg_page_serialno(const ogg_page *og); +extern long ogg_page_pageno(const ogg_page *og); +extern int ogg_page_packets(const ogg_page *og); extern void ogg_packet_clear(ogg_packet *op); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/os_types.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/os_types.h index ee75ae2c..644e3750 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/os_types.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/os_types.h @@ -10,8 +10,7 @@ * * ******************************************************************** - function: #ifdef jail to whip a few platforms into the UNIX ideal. - last mod: $Id: os_types.h,v 1.1 2007/06/07 17:48:18 jules_rms Exp $ + function: Define a consistent set of types on each platform. ********************************************************************/ #ifndef _OS_TYPES_H @@ -27,13 +26,15 @@ #if defined(_WIN32) # if defined(__CYGWIN__) -# include <_G_config.h> - typedef _G_int64_t ogg_int64_t; - typedef _G_int32_t ogg_int32_t; - typedef _G_uint32_t ogg_uint32_t; - typedef _G_int16_t ogg_int16_t; - typedef _G_uint16_t ogg_uint16_t; +# include + typedef int16_t ogg_int16_t; + typedef uint16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef uint32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + typedef uint64_t ogg_uint64_t; # elif defined(__MINGW32__) +# include typedef short ogg_int16_t; typedef unsigned short ogg_uint16_t; typedef int ogg_int32_t; @@ -42,46 +43,62 @@ typedef unsigned long long ogg_uint64_t; # elif defined(__MWERKS__) typedef long long ogg_int64_t; + typedef unsigned long long ogg_uint64_t; typedef int ogg_int32_t; typedef unsigned int ogg_uint32_t; typedef short ogg_int16_t; typedef unsigned short ogg_uint16_t; # else - /* MSVC/Borland */ - typedef __int64 ogg_int64_t; - typedef __int32 ogg_int32_t; - typedef unsigned __int32 ogg_uint32_t; - typedef __int16 ogg_int16_t; - typedef unsigned __int16 ogg_uint16_t; +# if defined(_MSC_VER) && (_MSC_VER >= 1800) /* MSVC 2013 and newer */ +# include + typedef int16_t ogg_int16_t; + typedef uint16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef uint32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + typedef uint64_t ogg_uint64_t; +# else + /* MSVC/Borland */ + typedef __int64 ogg_int64_t; + typedef __int32 ogg_int32_t; + typedef unsigned __int32 ogg_uint32_t; + typedef unsigned __int64 ogg_uint64_t; + typedef __int16 ogg_int16_t; + typedef unsigned __int16 ogg_uint16_t; +# endif # endif -#elif defined(__MACOS__) - -# include - typedef SInt16 ogg_int16_t; - typedef UInt16 ogg_uint16_t; - typedef SInt32 ogg_int32_t; - typedef UInt32 ogg_uint32_t; - typedef SInt64 ogg_int64_t; - -#elif defined(__MACOSX__) /* MacOS X Framework build */ +#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */ # include typedef int16_t ogg_int16_t; - typedef u_int16_t ogg_uint16_t; + typedef uint16_t ogg_uint16_t; typedef int32_t ogg_int32_t; - typedef u_int32_t ogg_uint32_t; + typedef uint32_t ogg_uint32_t; typedef int64_t ogg_int64_t; + typedef uint64_t ogg_uint64_t; + +#elif defined(__HAIKU__) + + /* Haiku */ +# include + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + typedef unsigned long long ogg_uint64_t; #elif defined(__BEOS__) /* Be */ # include typedef int16_t ogg_int16_t; - typedef u_int16_t ogg_uint16_t; + typedef uint16_t ogg_uint16_t; typedef int32_t ogg_int32_t; - typedef u_int32_t ogg_uint32_t; + typedef uint32_t ogg_uint32_t; typedef int64_t ogg_int64_t; + typedef uint64_t ogg_uint64_t; #elif defined (__EMX__) @@ -91,6 +108,8 @@ typedef int ogg_int32_t; typedef unsigned int ogg_uint32_t; typedef long long ogg_int64_t; + typedef unsigned long long ogg_uint64_t; + #elif defined (DJGPP) @@ -99,11 +118,13 @@ typedef int ogg_int32_t; typedef unsigned int ogg_uint32_t; typedef long long ogg_int64_t; + typedef unsigned long long ogg_uint64_t; #elif defined(R5900) /* PS2 EE */ typedef long ogg_int64_t; + typedef unsigned long ogg_uint64_t; typedef int ogg_int32_t; typedef unsigned ogg_uint32_t; typedef short ogg_int16_t; @@ -116,10 +137,20 @@ typedef signed int ogg_int32_t; typedef unsigned int ogg_uint32_t; typedef long long int ogg_int64_t; + typedef unsigned long long int ogg_uint64_t; + +#elif defined(__TMS320C6X__) + + /* TI C64x compiler */ + typedef signed short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef signed int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long int ogg_int64_t; + typedef unsigned long long int ogg_uint64_t; #else -# include # include "config_types.h" #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/vorbisenc.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/vorbisenc.h index 02332b50..085b15e6 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/vorbisenc.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/vorbisenc.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: vorbis encode-engine setup - last mod: $Id: vorbisenc.h 17021 2010-03-24 09:29:41Z xiphmont $ ********************************************************************/ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/vorbisfile.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/vorbisfile.h index a35f2f73..d34eef00 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/vorbisfile.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/vorbisfile.h @@ -6,12 +6,11 @@ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** function: stdio-based convenience library for opening/seeking/decoding - last mod: $Id: vorbisfile.h 17182 2010-04-29 03:48:32Z xiphmont $ ********************************************************************/ @@ -203,3 +202,4 @@ extern int ov_halfrate_p(OggVorbis_File *vf); #endif /* __cplusplus */ #endif + diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp new file mode 100644 index 00000000..3a4d4c2e --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp @@ -0,0 +1,310 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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 +{ + +ARAAudioSourceReader::ARAAudioSourceReader (ARAAudioSource* audioSource) + : AudioFormatReader (nullptr, "ARAAudioSourceReader"), + audioSourceBeingRead (audioSource) +{ + jassert (audioSourceBeingRead != nullptr); + + bitsPerSample = 32; + usesFloatingPointData = true; + sampleRate = audioSourceBeingRead->getSampleRate(); + numChannels = (unsigned int) audioSourceBeingRead->getChannelCount(); + lengthInSamples = audioSourceBeingRead->getSampleCount(); + tmpPtrs.resize (numChannels); + + audioSourceBeingRead->addListener (this); + + if (audioSourceBeingRead->isSampleAccessEnabled()) + hostReader.reset (new ARA::PlugIn::HostAudioReader (audioSourceBeingRead)); +} + +ARAAudioSourceReader::~ARAAudioSourceReader() +{ + invalidate(); +} + +void ARAAudioSourceReader::invalidate() +{ + ScopedWriteLock scopedLock (lock); + + if (! isValid()) + return; + + hostReader.reset(); + + audioSourceBeingRead->removeListener (this); + audioSourceBeingRead = nullptr; +} + +void ARAAudioSourceReader::willUpdateAudioSourceProperties (ARAAudioSource* audioSource, + ARAAudioSource::PropertiesPtr newProperties) +{ + if (audioSource->getSampleCount() != newProperties->sampleCount + || ! exactlyEqual (audioSource->getSampleRate(), newProperties->sampleRate) + || audioSource->getChannelCount() != newProperties->channelCount) + { + invalidate(); + } +} + +void ARAAudioSourceReader::doUpdateAudioSourceContent ([[maybe_unused]] ARAAudioSource* audioSource, + ARAContentUpdateScopes scopeFlags) +{ + jassert (audioSourceBeingRead == audioSource); + + // Don't invalidate if the audio signal is unchanged + if (scopeFlags.affectSamples()) + invalidate(); +} + +void ARAAudioSourceReader::willEnableAudioSourceSamplesAccess ([[maybe_unused]] ARAAudioSource* audioSource, bool enable) +{ + jassert (audioSourceBeingRead == audioSource); + + // Invalidate our reader if sample access is disabled + if (! enable) + { + ScopedWriteLock scopedLock (lock); + hostReader.reset(); + } +} + +void ARAAudioSourceReader::didEnableAudioSourceSamplesAccess ([[maybe_unused]] ARAAudioSource* audioSource, bool enable) +{ + jassert (audioSourceBeingRead == audioSource); + + // Recreate our reader if sample access is enabled + if (enable && isValid()) + { + ScopedWriteLock scopedLock (lock); + hostReader.reset (new ARA::PlugIn::HostAudioReader (audioSourceBeingRead)); + } +} + +void ARAAudioSourceReader::willDestroyAudioSource ([[maybe_unused]] ARAAudioSource* audioSource) +{ + jassert (audioSourceBeingRead == audioSource); + + invalidate(); +} + +bool ARAAudioSourceReader::readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, + int64 startSampleInFile, int numSamples) +{ + const auto destSize = (bitsPerSample / 8) * (size_t) numSamples; + const auto bufferOffset = (int) (bitsPerSample / 8) * startOffsetInDestBuffer; + + if (isValid()) + { + const ScopedTryReadLock readLock (lock); + + if (readLock.isLocked() && hostReader != nullptr) + { + for (size_t i = 0; i < tmpPtrs.size(); ++i) + { + if ((i < (size_t) numDestChannels) && (destSamples[i] != nullptr)) + { + tmpPtrs[i] = ((uint8_t*) destSamples[i]) + bufferOffset; + } + else + { + // We need to provide destination pointers for all channels in the ARA read call, even if + // readSamples is not reading all of them. Hence we use this dummy buffer to pad the read + // destination area. + static thread_local std::vector dummyBuffer; + + if (destSize > dummyBuffer.size()) + dummyBuffer.resize (destSize); + + tmpPtrs[i] = dummyBuffer.data(); + } + } + + return hostReader->readAudioSamples (startSampleInFile, numSamples, tmpPtrs.data()); + } + } + + for (int i = 0; i < numDestChannels; ++i) + if (destSamples[i] != nullptr) + zeromem (((uint8_t*) destSamples[i]) + bufferOffset, destSize); + + return false; +} + +//============================================================================== +ARAPlaybackRegionReader::ARAPlaybackRegionReader (ARAPlaybackRegion* playbackRegion) + : ARAPlaybackRegionReader (playbackRegion->getAudioModification()->getAudioSource()->getSampleRate(), + playbackRegion->getAudioModification()->getAudioSource()->getChannelCount(), + { playbackRegion }) +{} + +ARAPlaybackRegionReader::ARAPlaybackRegionReader (double rate, int numChans, + const std::vector& playbackRegions) + : AudioFormatReader (nullptr, "ARAPlaybackRegionReader") +{ + // We're only providing the minimal set of meaningful values, since the ARA renderer should only + // look at the time position and the playing state, and read any related tempo or bar signature + // information from the ARA model directly (MusicalContext). + positionInfo.setIsPlaying (true); + + sampleRate = rate; + numChannels = (unsigned int) numChans; + bitsPerSample = 32; + usesFloatingPointData = true; + + auto* documentController = (! playbackRegions.empty()) + ? playbackRegions.front()->getDocumentController() + : nullptr; + + playbackRenderer.reset (documentController ? static_cast (documentController->doCreatePlaybackRenderer()) + : nullptr); + + if (playbackRenderer != nullptr) + { + double regionsStartTime = std::numeric_limits::max(); + double regionsEndTime = std::numeric_limits::lowest(); + + for (const auto& playbackRegion : playbackRegions) + { + jassert (playbackRegion->getDocumentController() == documentController); + auto playbackRegionTimeRange = playbackRegion->getTimeRange (ARAPlaybackRegion::IncludeHeadAndTail::yes); + regionsStartTime = jmin (regionsStartTime, playbackRegionTimeRange.getStart()); + regionsEndTime = jmax (regionsEndTime, playbackRegionTimeRange.getEnd()); + + playbackRenderer->addPlaybackRegion (ARA::PlugIn::toRef (playbackRegion)); + playbackRegion->addListener (this); + } + + startInSamples = (int64) (regionsStartTime * sampleRate + 0.5); + lengthInSamples = (int64) ((regionsEndTime - regionsStartTime) * sampleRate + 0.5); + + playbackRenderer->prepareToPlay (rate, + maximumBlockSize, + numChans, + AudioProcessor::ProcessingPrecision::singlePrecision, + ARARenderer::AlwaysNonRealtime::yes); + } + else + { + startInSamples = 0; + lengthInSamples = 0; + } +} + +ARAPlaybackRegionReader::~ARAPlaybackRegionReader() +{ + invalidate(); +} + +void ARAPlaybackRegionReader::invalidate() +{ + ScopedWriteLock scopedWrite (lock); + + if (! isValid()) + return; + + for (auto& playbackRegion : playbackRenderer->getPlaybackRegions()) + playbackRegion->removeListener (this); + + playbackRenderer->releaseResources(); + playbackRenderer.reset(); +} + +bool ARAPlaybackRegionReader::readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, + int64 startSampleInFile, int numSamples) +{ + bool success = false; + bool needClearSamples = true; + + const ScopedTryReadLock readLock (lock); + + if (readLock.isLocked()) + { + if (isValid()) + { + success = true; + needClearSamples = false; + positionInfo.setTimeInSamples (startSampleInFile + startInSamples); + + while (numSamples > 0) + { + const int numSliceSamples = jmin (numSamples, maximumBlockSize); + AudioBuffer buffer ((float **) destSamples, numDestChannels, startOffsetInDestBuffer, numSliceSamples); + positionInfo.setTimeInSeconds (static_cast (*positionInfo.getTimeInSamples()) / sampleRate); + success &= playbackRenderer->processBlock (buffer, AudioProcessor::Realtime::no, positionInfo); + numSamples -= numSliceSamples; + startOffsetInDestBuffer += numSliceSamples; + positionInfo.setTimeInSamples (*positionInfo.getTimeInSamples() + numSliceSamples); + } + } + } + + if (needClearSamples) + for (int chan_i = 0; chan_i < numDestChannels; ++chan_i) + FloatVectorOperations::clear ((float *) destSamples[chan_i], numSamples); + + return success; +} + +void ARAPlaybackRegionReader::willUpdatePlaybackRegionProperties (ARAPlaybackRegion* playbackRegion, ARAPlaybackRegion::PropertiesPtr newProperties) +{ + jassert (ARA::contains (playbackRenderer->getPlaybackRegions(), playbackRegion)); + + if ((! exactlyEqual (playbackRegion->getStartInAudioModificationTime(), newProperties->startInModificationTime)) + || ! exactlyEqual (playbackRegion->getDurationInAudioModificationTime(), newProperties->durationInModificationTime) + || ! exactlyEqual (playbackRegion->getStartInPlaybackTime(), newProperties->startInPlaybackTime) + || ! exactlyEqual (playbackRegion->getDurationInPlaybackTime(), newProperties->durationInPlaybackTime) + || (playbackRegion->isTimestretchEnabled() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationTimestretch) != 0)) + || (playbackRegion->isTimeStretchReflectingTempo() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationTimestretchReflectingTempo) != 0)) + || (playbackRegion->hasContentBasedFadeAtHead() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationContentBasedFadeAtHead) != 0)) + || (playbackRegion->hasContentBasedFadeAtTail() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationContentBasedFadeAtTail) != 0))) + { + invalidate(); + } +} + +void ARAPlaybackRegionReader::didUpdatePlaybackRegionContent ([[maybe_unused]] ARAPlaybackRegion* playbackRegion, + ARAContentUpdateScopes scopeFlags) +{ + jassert (ARA::contains (playbackRenderer->getPlaybackRegions(), playbackRegion)); + + // Invalidate if the audio signal is changed + if (scopeFlags.affectSamples()) + invalidate(); +} + +void ARAPlaybackRegionReader::willDestroyPlaybackRegion ([[maybe_unused]] ARAPlaybackRegion* playbackRegion) +{ + jassert (ARA::contains (playbackRenderer->getPlaybackRegions(), playbackRegion)); + + invalidate(); +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_ARAAudioReaders.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_ARAAudioReaders.h new file mode 100644 index 00000000..4f28f3ab --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_ARAAudioReaders.h @@ -0,0 +1,194 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#pragma once + +namespace juce +{ + +class AudioProcessor; + +/* All these readers follow a common pattern of "invalidation": + + Whenever the samples they are reading are altered, the readers become invalid and will stop + accessing the model graph. These alterations are model edits such as property changes, content + changes (if affecting sample scope), or the deletion of some model object involved in the read + process. Since these edits are performed on the document controller thread, reader validity can + immediately be checked after the edit has been concluded, and any reader that has become invalid + can be recreated. + + Note that encountering a failure in any individual read call does not invalidate the reader, so + that the entity using the reader can decide whether to retry or to back out. This includes trying + to read an audio source for which the host has currently disabled access: the failure will be + immediately visible, but the reader will remain valid. This ensures that for example a realtime + renderer can just keep reading and will be seeing proper samples again once sample access is + re-enabled. + + If desired, the code calling readSamples() can also implement proper signaling of any read error + to the document controller thread to trigger rebuilding the reader as needed. This will typically + be done when implementing audio source analysis: if there is an error upon reading the samples + that cannot be resolved within a reasonable timeout, then the analysis would be aborted. The + document controller code that monitors the analysis tasks can evaluate this and re-launch a new + analysis when appropriate (e.g. when access is re-enabled). + + When reading playback regions (directly or through a region sequence reader), the reader will + represent the regions as a single source object that covers the union of all affected regions. + The first sample produced by the reader thus will be the first sample of the earliest region. + This means that the location of this region has to be taken into account by the calling code if + it wants to relate the samples to the model or any other reader output. +*/ + +//============================================================================== +/** + Subclass of AudioFormatReader that reads samples from a single ARA audio source. + + Plug-Ins typically use this from their rendering code, wrapped in a BufferingAudioReader + to bridge between realtime rendering and non-realtime audio reading. + + The reader becomes invalidated if + - the audio source content is updated in a way that affects its samples, + - the audio source sample access is disabled, or + - the audio source being read is destroyed. + + @tags{ARA} +*/ +class JUCE_API ARAAudioSourceReader : public AudioFormatReader, + private ARAAudioSource::Listener +{ +public: + /** Use an ARAAudioSource to construct an audio source reader for the given \p audioSource. */ + explicit ARAAudioSourceReader (ARAAudioSource* audioSource); + + ~ARAAudioSourceReader() override; + + bool readSamples (int* const* destSamples, + int numDestChannels, + int startOffsetInDestBuffer, + int64 startSampleInFile, + int numSamples) override; + + /** Returns true as long as the reader's underlying ARAAudioSource remains accessible and its + sample content is not changed. + */ + bool isValid() const { return audioSourceBeingRead != nullptr; } + + /** Invalidate the reader - the reader will call this internally if needed, but can also be + invalidated from the outside (from message thread only!). + */ + void invalidate(); + + void willUpdateAudioSourceProperties (ARAAudioSource* audioSource, + ARAAudioSource::PropertiesPtr newProperties) override; + void doUpdateAudioSourceContent (ARAAudioSource* audioSource, + ARAContentUpdateScopes scopeFlags) override; + void willEnableAudioSourceSamplesAccess (ARAAudioSource* audioSource, bool enable) override; + void didEnableAudioSourceSamplesAccess (ARAAudioSource* audioSource, bool enable) override; + void willDestroyAudioSource (ARAAudioSource* audioSource) override; + +private: + ARAAudioSource* audioSourceBeingRead; + std::unique_ptr hostReader; + ReadWriteLock lock; + std::vector tmpPtrs; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAAudioSourceReader) +}; + +//============================================================================== +/** + Subclass of AudioFormatReader that reads samples from a group of playback regions. + + Plug-Ins typically use this to draw the output of a playback region in their UI. + + In order to read from playback regions, the reader requires an audio processor that acts as ARA + playback renderer. Configuring the audio processor for real-time operation results in the reader + being real-time capable too, unlike most other AudioFormatReaders. The reader instance will take + care of adding all regions being read to the renderer and invoke its processBlock function in + order to read the region samples. + + The reader becomes invalid if + - any region properties are updated in a way that would affect its samples, + - any region content is updated in a way that would affect its samples, or + - any of its regions are destroyed. + + @tags{ARA} +*/ +class JUCE_API ARAPlaybackRegionReader : public AudioFormatReader, + private ARAPlaybackRegion::Listener +{ +public: + /** Create an ARAPlaybackRegionReader instance to read the given \p playbackRegion, using the + sample rate and channel count of the underlying ARAAudioSource. + + @param playbackRegion The playback region that should be read - must not be nullptr! + */ + explicit ARAPlaybackRegionReader (ARAPlaybackRegion* playbackRegion); + + /** Create an ARAPlaybackRegionReader instance to read the given \p playbackRegions + + @param sampleRate The sample rate that should be used for reading. + @param numChannels The channel count that should be used for reading. + @param playbackRegions The vector of playback regions that should be read - must not be empty! + All regions must be part of the same ARADocument. + */ + ARAPlaybackRegionReader (double sampleRate, int numChannels, + const std::vector& playbackRegions); + + ~ARAPlaybackRegionReader() override; + + /** Returns true as long as any of the reader's underlying playback region's haven't changed. */ + bool isValid() const { return (playbackRenderer != nullptr); } + + /** Invalidate the reader - this should be called if the sample content of any of the reader's + ARAPlaybackRegions changes. + */ + void invalidate(); + + bool readSamples (int* const* destSamples, + int numDestChannels, + int startOffsetInDestBuffer, + int64 startSampleInFile, + int numSamples) override; + + void willUpdatePlaybackRegionProperties (ARAPlaybackRegion* playbackRegion, + ARAPlaybackRegion::PropertiesPtr newProperties) override; + void didUpdatePlaybackRegionContent (ARAPlaybackRegion* playbackRegion, + ARAContentUpdateScopes scopeFlags) override; + void willDestroyPlaybackRegion (ARAPlaybackRegion* playbackRegion) override; + + /** The starting point of the reader in playback samples */ + int64 startInSamples = 0; + +private: + std::unique_ptr playbackRenderer; + AudioPlayHead::PositionInfo positionInfo; + ReadWriteLock lock; + + static constexpr int maximumBlockSize = 4 * 1024; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAPlaybackRegionReader) +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.cpp index dfc5034a..c1f87b1a 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h index 3556cbfa..c97c72e2 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp index 32cca881..e8b2f421 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -126,14 +125,14 @@ AudioFormatReader* AudioFormatManager::createReaderFor (const File& file) for (auto* af : knownFormats) if (af->canHandleFile (file)) - if (auto* in = file.createInputStream()) - if (auto* r = af->createReaderFor (in, true)) + if (auto in = file.createInputStream()) + if (auto* r = af->createReaderFor (in.release(), true)) return r; return nullptr; } -AudioFormatReader* AudioFormatManager::createReaderFor (InputStream* audioFileStream) +AudioFormatReader* AudioFormatManager::createReaderFor (std::unique_ptr audioFileStream) { // you need to actually register some formats before the manager can // use them to open a file! @@ -141,22 +140,21 @@ AudioFormatReader* AudioFormatManager::createReaderFor (InputStream* audioFileSt if (audioFileStream != nullptr) { - std::unique_ptr in (audioFileStream); - auto originalStreamPos = in->getPosition(); + auto originalStreamPos = audioFileStream->getPosition(); for (auto* af : knownFormats) { - if (auto* r = af->createReaderFor (in.get(), false)) + if (auto* r = af->createReaderFor (audioFileStream.get(), false)) { - in.release(); + audioFileStream.release(); return r; } - in->setPosition (originalStreamPos); + audioFileStream->setPosition (originalStreamPos); // the stream that is passed-in must be capable of being repositioned so // that all the formats can have a go at opening it. - jassert (in->getPosition() == originalStreamPos); + jassert (audioFileStream->getPosition() == originalStreamPos); } } diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h index a31ac107..b28dfc6c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -67,7 +66,7 @@ class JUCE_API AudioFormatManager /** Handy method to make it easy to register the formats that come with JUCE. 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. + in either the Projucer or your application's preprocessor definitions. */ void registerBasicFormats(); @@ -138,7 +137,7 @@ class JUCE_API AudioFormatManager If none of the registered formats can open the stream, it'll return nullptr. If it returns a reader, it's the caller's responsibility to delete the reader. */ - AudioFormatReader* createReaderFor (InputStream* audioFileStream); + AudioFormatReader* createReaderFor (std::unique_ptr audioFileStream); private: //============================================================================== diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp index 5b180525..405e9976 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -39,9 +38,11 @@ AudioFormatReader::~AudioFormatReader() static void convertFixedToFloat (int* const* channels, int numChannels, int numSamples) { + constexpr auto scaleFactor = 1.0f / static_cast (0x7fffffff); + for (int i = 0; i < numChannels; ++i) if (auto d = channels[i]) - FloatVectorOperations::convertFixedToFloat (reinterpret_cast (d), d, 1.0f / 0x7fffffff, numSamples); + FloatVectorOperations::convertFixedToFloat (reinterpret_cast (d), d, scaleFactor, numSamples); } bool AudioFormatReader::read (float* const* destChannels, int numDestChannels, @@ -85,7 +86,7 @@ bool AudioFormatReader::read (int* const* destChannels, if (numSamplesToRead <= 0) return true; - if (! readSamples (const_cast (destChannels), + if (! readSamples (destChannels, jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer, startSampleInSource, numSamplesToRead)) return false; @@ -121,7 +122,7 @@ bool AudioFormatReader::read (int* const* destChannels, return true; } -static void readChannels (AudioFormatReader& reader, int** chans, AudioBuffer* buffer, +static bool readChannels (AudioFormatReader& reader, int** chans, AudioBuffer* buffer, int startSample, int numSamples, int64 readerStartSample, int numTargetChannels, bool convertToFloat) { @@ -129,13 +130,16 @@ static void readChannels (AudioFormatReader& reader, int** chans, AudioBuffer (buffer->getWritePointer (j, startSample)); chans[numTargetChannels] = nullptr; - reader.read (chans, numTargetChannels, readerStartSample, numSamples, true); + + const bool success = reader.read (chans, numTargetChannels, readerStartSample, numSamples, true); if (convertToFloat) convertFixedToFloat (chans, numTargetChannels, numSamples); + + return success; } -void AudioFormatReader::read (AudioBuffer* buffer, +bool AudioFormatReader::read (AudioBuffer* buffer, int startSample, int numSamples, int64 readerStartSample, @@ -145,54 +149,61 @@ void AudioFormatReader::read (AudioBuffer* buffer, jassert (buffer != nullptr); jassert (startSample >= 0 && startSample + numSamples <= buffer->getNumSamples()); - if (numSamples > 0) - { - auto numTargetChannels = buffer->getNumChannels(); - - if (numTargetChannels <= 2) - { - int* dests[2] = { reinterpret_cast (buffer->getWritePointer (0, startSample)), - reinterpret_cast (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr) }; - int* chans[3] = {}; - - if (useReaderLeftChan == useReaderRightChan) - { - chans[0] = dests[0]; + if (numSamples <= 0) + return true; - if (numChannels > 1) - chans[1] = dests[1]; - } - else if (useReaderLeftChan || (numChannels == 1)) - { - chans[0] = dests[0]; - } - else if (useReaderRightChan) - { - chans[1] = dests[0]; - } + auto numTargetChannels = buffer->getNumChannels(); - read (chans, 2, readerStartSample, numSamples, true); + if (numTargetChannels <= 2) + { + int* dests[2] = { reinterpret_cast (buffer->getWritePointer (0, startSample)), + reinterpret_cast (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr) }; + int* chans[3] = {}; - // 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], (size_t) numSamples * sizeof (float)); + if (useReaderLeftChan == useReaderRightChan) + { + chans[0] = dests[0]; - if (! usesFloatingPointData) - convertFixedToFloat (dests, 2, numSamples); + if (numChannels > 1) + chans[1] = dests[1]; } - else if (numTargetChannels <= 64) + else if (useReaderLeftChan || (numChannels == 1)) { - int* chans[65]; - readChannels (*this, chans, buffer, startSample, numSamples, - readerStartSample, numTargetChannels, ! usesFloatingPointData); + chans[0] = dests[0]; } - else + else if (useReaderRightChan) + { + chans[1] = dests[0]; + } + + if (! read (chans, 2, readerStartSample, numSamples, true)) + return false; + + // if the target's stereo and the source is mono, dupe the first channel.. + if (numTargetChannels > 1 + && (chans[0] == nullptr || chans[1] == nullptr) + && (dests[0] != nullptr && dests[1] != nullptr)) { - HeapBlock chans (numTargetChannels + 1); - readChannels (*this, chans, buffer, startSample, numSamples, - readerStartSample, numTargetChannels, ! usesFloatingPointData); + memcpy (dests[1], dests[0], (size_t) numSamples * sizeof (float)); } + + if (! usesFloatingPointData) + convertFixedToFloat (dests, 2, numSamples); + + return true; } + + if (numTargetChannels <= 64) + { + int* chans[65]; + return readChannels (*this, chans, buffer, startSample, numSamples, + readerStartSample, numTargetChannels, ! usesFloatingPointData); + } + + HeapBlock chans (numTargetChannels + 1); + + return readChannels (*this, chans, buffer, startSample, numSamples, + readerStartSample, numTargetChannels, ! usesFloatingPointData); } void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, @@ -234,8 +245,8 @@ void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples { auto intRange = Range::findMinAndMax (intBuffer[i], numToDo); - r = Range (intRange.getStart() / (float) std::numeric_limits::max(), - intRange.getEnd() / (float) std::numeric_limits::max()); + r = Range ((float) intRange.getStart() / (float) std::numeric_limits::max(), + (float) intRange.getEnd() / (float) std::numeric_limits::max()); } results[i] = isFirstBlock ? r : results[i].getUnionWith (r); diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h index f3e55dd4..9686dc57 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -140,8 +139,12 @@ class JUCE_API AudioFormatReader the buffer's floating-point format, and will try to intelligently cope with mismatches between the number of channels in the reader and the buffer. + + @returns true if the operation succeeded, false if there was an error. Note + that reading sections of data beyond the extent of the stream isn't an + error - the reader should just return zeros for these regions */ - void read (AudioBuffer* buffer, + bool read (AudioBuffer* buffer, int startSampleInDestBuffer, int numSamples, int64 readerStartSample, @@ -264,7 +267,7 @@ class JUCE_API AudioFormatReader to begin reading. This value is guaranteed to be >= 0. @param numSamples the number of samples to read */ - virtual bool readSamples (int** destChannels, + virtual bool readSamples (int* const* destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, @@ -303,11 +306,16 @@ class JUCE_API AudioFormatReader /** Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie beyond the end of their available length. */ - static void clearSamplesBeyondAvailableLength (int** destChannels, int numDestChannels, + static void clearSamplesBeyondAvailableLength (int* const* destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int& numSamples, int64 fileLengthInSamples) { - jassert (destChannels != nullptr); + if (destChannels == nullptr) + { + jassertfalse; + return; + } + const int64 samplesAvailable = fileLengthInSamples - startSampleInFile; if (samplesAvailable < numSamples) diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp index 60bf795c..2a0d339b 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -82,8 +81,14 @@ void AudioFormatReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& i } else { - reader->read (info.buffer, info.startSample, - info.numSamples, start, true, true); + const auto samplesToRead = jlimit (int64{}, + (int64) info.numSamples, + reader->lengthInSamples - start); + + reader->read (info.buffer, info.startSample, (int) samplesToRead, start, true, true); + info.buffer->clear ((int) (info.startSample + samplesToRead), + (int) (info.numSamples - samplesToRead)); + nextPlayPos += info.numSamples; } } diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h index b3515822..6e279720 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp index db44d788..53f8cb92 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -36,7 +35,7 @@ AudioFormatWriter::AudioFormatWriter (OutputStream* const out, numChannels (numChannels_), bitsPerSample (bitsPerSample_), usesFloatingPointData (false), - channelLayout (AudioChannelSet::canonicalChannelSet(static_cast (numChannels_))), + channelLayout (AudioChannelSet::canonicalChannelSet (static_cast (numChannels_))), output (out), formatName (formatName_) { @@ -109,8 +108,10 @@ bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, { void* const b = *bufferChan++; + constexpr auto scaleFactor = 1.0f / static_cast (0x7fffffff); + if (isFloatingPoint()) - FloatVectorOperations::convertFixedToFloat ((float*) b, (int*) b, 1.0f / 0x7fffffff, numToDo); + FloatVectorOperations::convertFixedToFloat ((float*) b, (int*) b, scaleFactor, numToDo); else convertFloatsToInts ((int*) b, (float*) b, numToDo); } @@ -156,16 +157,16 @@ bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int if (isFloatingPoint()) return write ((const int**) channels, numSamples); - int* chans[256]; - int scratch[4096]; + std::vector chans (256); + std::vector scratch (4096); - jassert (numSourceChannels < numElementsInArray (chans)); - const int maxSamples = (int) (numElementsInArray (scratch) / numSourceChannels); + jassert (numSourceChannels < (int) chans.size()); + const int maxSamples = (int) scratch.size() / numSourceChannels; for (int i = 0; i < numSourceChannels; ++i) - chans[i] = scratch + (i * maxSamples); + chans[(size_t) i] = scratch.data() + (i * maxSamples); - chans[numSourceChannels] = nullptr; + chans[(size_t) numSourceChannels] = nullptr; int startSample = 0; while (numSamples > 0) @@ -173,9 +174,9 @@ bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int auto numToDo = jmin (numSamples, maxSamples); for (int i = 0; i < numSourceChannels; ++i) - convertFloatsToInts (chans[i], channels[i] + startSample, numToDo); + convertFloatsToInts (chans[(size_t) i], channels[(size_t) i] + startSample, numToDo); - if (! write ((const int**) chans, numToDo)) + if (! write ((const int**) chans.data(), numToDo)) return false; startSample += numToDo; @@ -210,7 +211,7 @@ bool AudioFormatWriter::flush() } //============================================================================== -class AudioFormatWriter::ThreadedWriter::Buffer : private TimeSliceClient +class AudioFormatWriter::ThreadedWriter::Buffer final : private TimeSliceClient { public: Buffer (TimeSliceThread& tst, AudioFormatWriter* w, int channels, int numSamples) diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.h index ca74f35e..ab47ac65 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp index 1874d37b..51eeda2c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -51,12 +50,15 @@ AudioSubsectionReader::~AudioSubsectionReader() } //============================================================================== -bool AudioSubsectionReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, +bool AudioSubsectionReader::readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples, length); + if (numSamples <= 0) + return true; + return source->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile + startSample, numSamples); } diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h index 2f294b17..cf903970 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -67,7 +66,7 @@ class JUCE_API AudioSubsectionReader : public AudioFormatReader //============================================================================== - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override; void readMaxLevels (int64 startSample, int64 numSamples, diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp index ec99242f..66727153 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -41,9 +40,6 @@ BufferingAudioReader::BufferingAudioReader (AudioFormatReader* sourceReader, bitsPerSample = 32; usesFloatingPointData = true; - for (int i = 3; --i >= 0;) - readNextBufferChunk(); - timeSliceThread.addTimeSliceClient (this); } @@ -57,7 +53,7 @@ void BufferingAudioReader::setReadTimeout (int timeoutMilliseconds) noexcept timeoutMs = timeoutMilliseconds; } -bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, +bool BufferingAudioReader::readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { auto startTime = Time::getMillisecondCounter(); @@ -67,6 +63,8 @@ bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, const ScopedLock sl (lock); nextReadPosition = startSampleInFile; + bool allSamplesRead = true; + while (numSamples > 0) { if (auto block = getBlockContaining (startSampleInFile)) @@ -76,7 +74,7 @@ bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, for (int j = 0; j < numDestChannels; ++j) { - if (auto dest = (float*) destSamples[j]) + if (auto* dest = (float*) destSamples[j]) { dest += startOffsetInDestBuffer; @@ -90,15 +88,18 @@ bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, startOffsetInDestBuffer += numToDo; startSampleInFile += numToDo; numSamples -= numToDo; + + allSamplesRead = allSamplesRead && block->allSamplesRead; } else { if (timeoutMs >= 0 && Time::getMillisecondCounter() >= startTime + (uint32) timeoutMs) { for (int j = 0; j < numDestChannels; ++j) - if (auto dest = (float*) destSamples[j]) + if (auto* dest = (float*) destSamples[j]) FloatVectorOperations::clear (dest + startOffsetInDestBuffer, numSamples); + allSamplesRead = false; break; } else @@ -109,14 +110,14 @@ bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, } } - return true; + return allSamplesRead; } BufferingAudioReader::BufferedBlock::BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples) : range (pos, pos + numSamples), - buffer ((int) reader.numChannels, numSamples) + buffer ((int) reader.numChannels, numSamples), + allSamplesRead (reader.read (&buffer, 0, numSamples, pos, true, true)) { - reader.read (&buffer, 0, numSamples, pos, true, true); } BufferingAudioReader::BufferedBlock* BufferingAudioReader::getBlockContaining (int64 pos) const noexcept @@ -135,15 +136,14 @@ int BufferingAudioReader::useTimeSlice() bool BufferingAudioReader::readNextBufferChunk() { - auto pos = nextReadPosition.load(); - auto startPos = ((pos - 1024) / samplesPerBlock) * samplesPerBlock; - auto endPos = startPos + numBlocks * samplesPerBlock; + auto pos = (nextReadPosition.load() / samplesPerBlock) * samplesPerBlock; + auto endPos = jmin (lengthInSamples, pos + numBlocks * samplesPerBlock); OwnedArray newBlocks; for (int i = blocks.size(); --i >= 0;) - if (blocks.getUnchecked(i)->range.intersects (Range (startPos, endPos))) - newBlocks.add (blocks.getUnchecked(i)); + if (blocks.getUnchecked (i)->range.intersects (Range (pos, endPos))) + newBlocks.add (blocks.getUnchecked (i)); if (newBlocks.size() == numBlocks) { @@ -151,7 +151,7 @@ bool BufferingAudioReader::readNextBufferChunk() return false; } - for (auto p = startPos; p < endPos; p += samplesPerBlock) + for (auto p = pos; p < endPos; p += samplesPerBlock) { if (getBlockContaining (p) == nullptr) { @@ -166,9 +166,176 @@ bool BufferingAudioReader::readNextBufferChunk() } for (int i = blocks.size(); --i >= 0;) - newBlocks.removeObject (blocks.getUnchecked(i), false); + newBlocks.removeObject (blocks.getUnchecked (i), false); return true; } + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +static bool isSilent (const AudioBuffer& b) +{ + for (int channel = 0; channel < b.getNumChannels(); ++channel) + if (b.findMinMax (channel, 0, b.getNumSamples()) != Range{}) + return false; + + return true; +} + +struct TestAudioFormatReader : public AudioFormatReader +{ + explicit TestAudioFormatReader (const AudioBuffer* b) + : AudioFormatReader (nullptr, {}), + buffer (b) + { + jassert (buffer != nullptr); + sampleRate = 44100.0f; + bitsPerSample = 32; + usesFloatingPointData = true; + lengthInSamples = buffer->getNumSamples(); + numChannels = (unsigned int) buffer->getNumChannels(); + } + + bool readSamples (int* const* destChannels, int numDestChannels, int startOffsetInDestBuffer, + int64 startSampleInFile, int numSamples) override + { + clearSamplesBeyondAvailableLength (destChannels, numDestChannels, startOffsetInDestBuffer, + startSampleInFile, numSamples, lengthInSamples); + + if (numSamples <= 0) + return true; + + for (int j = 0; j < numDestChannels; ++j) + { + static_assert (sizeof (int) == sizeof (float), + "Int and float size must match in order for pointer arithmetic to work correctly"); + + if (auto* dest = reinterpret_cast (destChannels[j])) + { + dest += startOffsetInDestBuffer; + + if (j < (int) numChannels) + FloatVectorOperations::copy (dest, buffer->getReadPointer (j, (int) startSampleInFile), numSamples); + else + FloatVectorOperations::clear (dest, numSamples); + } + } + + return true; + } + + const AudioBuffer* buffer; +}; + +static AudioBuffer generateTestBuffer (Random& random, int bufferSize) +{ + AudioBuffer buffer { 2, bufferSize }; + + for (int channel = 0; channel < buffer.getNumChannels(); ++channel) + for (int sample = 0; sample < buffer.getNumSamples(); ++sample) + buffer.setSample (channel, sample, random.nextFloat()); + + return buffer; +} + +class BufferingAudioReaderTests final : public UnitTest +{ +public: + BufferingAudioReaderTests() : UnitTest ("BufferingAudioReader", UnitTestCategories::audio) {} + + void runTest() override + { + TimeSliceThread thread ("TestBackgroundThread"); + thread.startThread (Thread::Priority::normal); + + beginTest ("Reading samples from a blocked reader should produce silence"); + { + struct BlockingReader final : public TestAudioFormatReader + { + explicit BlockingReader (const AudioBuffer* b) + : TestAudioFormatReader (b) + { + } + + bool readSamples (int* const* destChannels, + int numDestChannels, + int startOffsetInDestBuffer, + int64 startSampleInFile, + int numSamples) override + { + unblock.wait(); + return TestAudioFormatReader::readSamples (destChannels, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples); + } + + WaitableEvent unblock; + }; + + Random random { getRandom() }; + constexpr auto bufferSize = 1024; + + const auto source = generateTestBuffer (random, bufferSize); + expect (! isSilent (source)); + + auto* blockingReader = new BlockingReader (&source); + BufferingAudioReader reader (blockingReader, thread, bufferSize); + + auto destination = generateTestBuffer (random, bufferSize); + expect (! isSilent (destination)); + + read (reader, destination); + expect (isSilent (destination)); + + blockingReader->unblock.signal(); + } + + beginTest ("Reading samples from a reader should produce the same samples as its source"); + { + Random random { getRandom() }; + + for (auto i = 4; i < 18; ++i) + { + const auto bufferSize = 1 << i; + const auto source = generateTestBuffer (random, bufferSize); + expect (! isSilent (source)); + + BufferingAudioReader reader (new TestAudioFormatReader (&source), thread, bufferSize); + reader.setReadTimeout (-1); + + auto destination = generateTestBuffer (random, bufferSize); + expect (! isSilent (destination)); + expect (source != destination); + + read (reader, destination); + expect (source == destination); + } + } + } + +private: + void read (BufferingAudioReader& reader, AudioBuffer& readBuffer) + { + constexpr int blockSize = 1024; + + const auto numSamples = readBuffer.getNumSamples(); + int readPos = 0; + + for (;;) + { + reader.read (&readBuffer, readPos, jmin (blockSize, numSamples - readPos), readPos, true, true); + + readPos += blockSize; + + if (readPos >= numSamples) + break; + } + } +}; + +static BufferingAudioReaderTests bufferingAudioReaderTests; + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.h index 81b4ec4b..4048639c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -58,38 +57,40 @@ class JUCE_API BufferingAudioReader : public AudioFormatReader, /** Sets a number of milliseconds that the reader can block for in its readSamples() method before giving up and returning silence. - A value of less that 0 means "wait forever". - The default timeout is 0. + + A value of less that 0 means "wait forever". The default timeout is 0. */ void setReadTimeout (int timeoutMilliseconds) noexcept; - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + //============================================================================== + bool readSamples (int* const* destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override; private: - std::unique_ptr source; - TimeSliceThread& thread; - std::atomic nextReadPosition { 0 }; - const int numBlocks; - int timeoutMs = 0; - - enum { samplesPerBlock = 32768 }; - struct BufferedBlock { BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples); Range range; AudioBuffer buffer; + bool allSamplesRead = false; }; - CriticalSection lock; - OwnedArray blocks; - - BufferedBlock* getBlockContaining (int64 pos) const noexcept; int useTimeSlice() override; + BufferedBlock* getBlockContaining (int64 pos) const noexcept; bool readNextBufferChunk(); + static constexpr int samplesPerBlock = 32768; + + std::unique_ptr source; + TimeSliceThread& thread; + std::atomic nextReadPosition { 0 }; + const int numBlocks; + int timeoutMs = 0; + + CriticalSection lock; + OwnedArray blocks; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioReader) }; diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h index 551198e0..79303d92 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.cpp b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.cpp index 67c31fd6..d8ce961c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -69,6 +68,11 @@ #include "codecs/juce_WavAudioFormat.cpp" #include "codecs/juce_LAMEEncoderAudioFormat.cpp" +#if JucePlugin_Enable_ARA + #include "juce_audio_processors/utilities/ARA/juce_ARADocumentControllerCommon.cpp" + #include "format/juce_ARAAudioReaders.cpp" +#endif + #if JUCE_WINDOWS && JUCE_USE_WINDOWS_MEDIA_FORMAT #include "codecs/juce_WindowsMediaAudioFormat.cpp" #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h index 5c09e400..dcf173c7 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h +++ b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -29,18 +28,19 @@ The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. For details about the syntax and how to create or use a module, see the - JUCE Module Format.txt file. + JUCE Module Format.md file. BEGIN_JUCE_MODULE_DECLARATION ID: juce_audio_formats vendor: juce - version: 5.4.7 + version: 7.0.10 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 + minimumCppStandard: 17 dependencies: juce_audio_basics OSXFrameworks: CoreAudio CoreMIDI QuartzCore AudioToolbox @@ -79,8 +79,8 @@ Enables the software-based MP3AudioFormat class. IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile this MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, you are agreeing - that ROLI Ltd. is in no way responsible for any patent, copyright, or other legal issues - that you may suffer as a result. + that Raw Material Software Limited is in no way responsible for any patent, copyright, or other + legal issues that you may suffer as a result. The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party intellectual property. If you wish to use it, please seek your own independent advice about the @@ -105,7 +105,7 @@ #define JUCE_USE_WINDOWS_MEDIA_FORMAT 1 #endif -#if ! JUCE_MSVC +#if ! JUCE_WINDOWS || JUCE_MINGW #undef JUCE_USE_WINDOWS_MEDIA_FORMAT #define JUCE_USE_WINDOWS_MEDIA_FORMAT 0 #endif @@ -128,3 +128,9 @@ #include "codecs/juce_WavAudioFormat.h" #include "codecs/juce_WindowsMediaAudioFormat.h" #include "sampler/juce_Sampler.h" + +#if JucePlugin_Enable_ARA + #include + + #include "format/juce_ARAAudioReaders.h" +#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.mm b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.mm index 0a95f996..43f86cab 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.mm +++ b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.mm @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.cpp b/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.cpp index e4043ad0..fd5cec70 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h b/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h index 87a53f29..81e18462 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h +++ b/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp index 71740a3a..3999b873 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,8 +28,6 @@ AbstractFifo::AbstractFifo (int capacity) noexcept : bufferSize (capacity) jassert (bufferSize > 0); } -AbstractFifo::~AbstractFifo() {} - int AbstractFifo::getTotalSize() const noexcept { return bufferSize; } int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; } @@ -170,14 +168,14 @@ AbstractFifo::ScopedWrite AbstractFifo::write (int numToWrite) noexcept { ret //============================================================================== #if JUCE_UNIT_TESTS -class AbstractFifoTests : public UnitTest +class AbstractFifoTests final : public UnitTest { public: AbstractFifoTests() : UnitTest ("Abstract Fifo", UnitTestCategories::containers) {} - struct WriteThread : public Thread + struct WriteThread final : public Thread { WriteThread (AbstractFifo& f, int* b, Random rng) : Thread ("fifo writer"), fifo (f), buffer (b), random (rng) @@ -185,12 +183,12 @@ class AbstractFifoTests : public UnitTest startThread(); } - ~WriteThread() + ~WriteThread() override { stopThread (5000); } - void run() + void run() override { int n = 0; @@ -215,6 +213,8 @@ class AbstractFifoTests : public UnitTest Random random; }; + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262) + void runTest() override { beginTest ("AbstractFifo"); @@ -258,6 +258,8 @@ class AbstractFifoTests : public UnitTest } } } + + JUCE_END_IGNORE_WARNINGS_MSVC }; static AbstractFifoTests fifoUnitTests; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h b/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h index 4f00255c..c8f9aa97 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -42,30 +42,24 @@ namespace juce { void addToFifo (const int* someData, int numItems) { - int start1, size1, start2, size2; - abstractFifo.prepareToWrite (numItems, start1, size1, start2, size2); + const auto scope = abstractFifo.write (numItems); - if (size1 > 0) - copySomeData (myBuffer + start1, someData, size1); + if (scope.blockSize1 > 0) + copySomeData (myBuffer + scope.startIndex1, someData, scope.blockSize1); - if (size2 > 0) - copySomeData (myBuffer + start2, someData + size1, size2); - - abstractFifo.finishedWrite (size1 + size2); + if (scope.blockSize2 > 0) + copySomeData (myBuffer + scope.startIndex2, someData + scope.blockSize1, scope.blockSize2); } void readFromFifo (int* someData, int numItems) { - int start1, size1, start2, size2; - abstractFifo.prepareToRead (numItems, start1, size1, start2, size2); + const auto scope = abstractFifo.read (numItems); - if (size1 > 0) - copySomeData (someData, myBuffer + start1, size1); - - if (size2 > 0) - copySomeData (someData + size1, myBuffer + start2, size2); + if (scope.blockSize1 > 0) + copySomeData (someData, myBuffer + scope.startIndex1, scope.blockSize1); - abstractFifo.finishedRead (size1 + size2); + if (scope.blockSize2 > 0) + copySomeData (someData + scope.blockSize1, myBuffer + scope.startIndex2, scope.blockSize2); } AbstractFifo abstractFifo { 1024 }; @@ -82,9 +76,6 @@ class JUCE_API AbstractFifo /** Creates a FIFO to manage a buffer with the specified capacity. */ AbstractFifo (int capacity) noexcept; - /** Destructor */ - ~AbstractFifo(); - //============================================================================== /** Returns the total size of the buffer being managed. */ int getTotalSize() const noexcept; @@ -181,7 +172,7 @@ class JUCE_API AbstractFifo } @endcode - @param numWanted indicates how many items you'd like to add to the buffer + @param numWanted indicates how many items you'd like to read from the buffer @param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1 @param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into @@ -314,25 +305,25 @@ class JUCE_API AbstractFifo JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo) }; -template<> +template <> inline void AbstractFifo::ScopedReadWrite::finish (AbstractFifo& f, int num) noexcept { f.finishedRead (num); } -template<> +template <> inline void AbstractFifo::ScopedReadWrite::finish (AbstractFifo& f, int num) noexcept { f.finishedWrite (num); } -template<> +template <> inline void AbstractFifo::ScopedReadWrite::prepare (AbstractFifo& f, int num) noexcept { f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2); } -template<> +template <> inline void AbstractFifo::ScopedReadWrite::prepare (AbstractFifo& f, int num) noexcept { f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2); diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Array.h b/JuceLibraryCode/modules/juce_core/containers/juce_Array.h index f17f532f..002d3623 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Array.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Array.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -76,7 +76,7 @@ class Array { } - /** Initalises from a null-terminated raw array of values. + /** Initialises from a null-terminated raw array of values. @param data the data to copy from */ template @@ -86,7 +86,7 @@ class Array add (*data++); } - /** Initalises from a raw array of values. + /** Initialises from a raw array of values. @param data the data to copy from @param numValues the number of values in the array */ @@ -96,30 +96,30 @@ class Array values.addArray (data, numValues); } - /** Initalises an Array of size 1 containing a single element. */ + /** Initialises an Array of size 1 containing a single element. */ Array (const ElementType& singleElementToAdd) { add (singleElementToAdd); } - /** Initalises an Array of size 1 containing a single element. */ + /** Initialises an Array of size 1 containing a single element. */ Array (ElementType&& singleElementToAdd) { add (std::move (singleElementToAdd)); } - /** Initalises an Array from a list of items. */ + /** Initialises an Array from a list of items. */ template - Array (const ElementType& firstNewElement, OtherElements... otherElements) + Array (const ElementType& firstNewElement, OtherElements&&... otherElements) { - values.add (firstNewElement, otherElements...); + values.add (firstNewElement, std::forward (otherElements)...); } - /** Initalises an Array from a list of items. */ + /** Initialises an Array from a list of items. */ template - Array (ElementType&& firstNewElement, OtherElements... otherElements) + Array (ElementType&& firstNewElement, OtherElements&&... otherElements) { - values.add (std::move (firstNewElement), otherElements...); + values.add (std::move (firstNewElement), std::forward (otherElements)...); } template @@ -386,7 +386,7 @@ class Array auto endPtr = values.end(); for (; e != endPtr; ++e) - if (elementToLookFor == *e) + if (exactlyEqual (elementToLookFor, *e)) return static_cast (e - values.begin()); return -1; @@ -404,7 +404,7 @@ class Array auto endPtr = values.end(); for (; e != endPtr; ++e) - if (elementToLookFor == *e) + if (exactlyEqual (elementToLookFor, *e)) return true; return false; @@ -433,18 +433,18 @@ class Array /** Appends multiple new elements at the end of the array. */ template - void add (const ElementType& firstNewElement, OtherElements... otherElements) + void add (const ElementType& firstNewElement, OtherElements&&... otherElements) { const ScopedLockType lock (getLock()); - values.add (firstNewElement, otherElements...); + values.add (firstNewElement, std::forward (otherElements)...); } /** Appends multiple new elements at the end of the array. */ template - void add (ElementType&& firstNewElement, OtherElements... otherElements) + void add (ElementType&& firstNewElement, OtherElements&&... otherElements) { const ScopedLockType lock (getLock()); - values.add (std::move (firstNewElement), otherElements...); + values.add (std::move (firstNewElement), std::forward (otherElements)...); } /** Inserts a new element into the array at a given position. @@ -649,7 +649,7 @@ class Array @see add */ template - typename std::enable_if::value, void>::type + std::enable_if_t, void> addArray (const OtherArrayType& arrayToAddFrom, int startIndex, int numElementsToAdd = -1) @@ -727,32 +727,7 @@ class Array @see addSorted, sort */ template - int indexOfSorted (ElementComparator& comparator, TargetValueType elementToLookFor) const - { - ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this - // avoids getting warning messages about the parameter being unused - - const ScopedLockType lock (getLock()); - - for (int s = 0, e = values.size();;) - { - if (s >= e) - return -1; - - if (comparator.compareElements (elementToLookFor, values[s]) == 0) - return s; - - auto halfway = (s + e) / 2; - - if (halfway == s) - return -1; - - if (comparator.compareElements (elementToLookFor, values[halfway]) >= 0) - s = halfway; - else - e = halfway; - } - } + int indexOfSorted (ElementComparator& comparator, TargetValueType elementToLookFor) const; //============================================================================== /** Removes an element from the array. @@ -829,9 +804,10 @@ class Array If the item isn't found, no action is taken. @param valueToRemove the object to try to remove + @returns the index of the removed item, or -1 if the item isn't found @see remove, removeRange, removeIf */ - void removeFirstMatchingValue (ParameterType valueToRemove) + int removeFirstMatchingValue (ParameterType valueToRemove) { const ScopedLockType lock (getLock()); auto* e = values.begin(); @@ -841,9 +817,11 @@ class Array if (valueToRemove == e[i]) { removeInternal (i); - break; + return i; } } + + return -1; } /** Removes items from the array. @@ -1103,14 +1081,7 @@ class Array @see addSorted, indexOfSorted, sortArray */ template - void sort (ElementComparator& comparator, - bool retainOrderOfEquivalentItems = false) - { - const ScopedLockType lock (getLock()); - ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this - // avoids getting warning messages about the parameter being unused - sortArray (comparator, values.begin(), 0, size() - 1, retainOrderOfEquivalentItems); - } + void sort (ElementComparator& comparator, bool retainOrderOfEquivalentItems = false); //============================================================================== /** Returns the CriticalSection that locks this array. @@ -1125,9 +1096,9 @@ class Array //============================================================================== #ifndef DOXYGEN - // Note that the swapWithArray method has been replaced by a more flexible templated version, - // and renamed "swapWith" to be more consistent with the names used in other classes. - JUCE_DEPRECATED_WITH_BODY (void swapWithArray (Array& other) noexcept, { swapWith (other); }) + [[deprecated ("This method has been replaced by a more flexible templated version and renamed " + "to swapWith to be more consistent with the names used in other classes.")]] + void swapWithArray (Array& other) noexcept { swapWith (other); } #endif private: @@ -1147,4 +1118,43 @@ class Array } }; +//============================================================================== +template +template +int Array::indexOfSorted ( + [[maybe_unused]] ElementComparator& comparator, + TargetValueType elementToLookFor) const +{ + const ScopedLockType lock (getLock()); + + for (int s = 0, e = values.size();;) + { + if (s >= e) + return -1; + + if (comparator.compareElements (elementToLookFor, values[s]) == 0) + return s; + + auto halfway = (s + e) / 2; + + if (halfway == s) + return -1; + + if (comparator.compareElements (elementToLookFor, values[halfway]) >= 0) + s = halfway; + else + e = halfway; + } +} + +template +template +void Array::sort ( + [[maybe_unused]] ElementComparator& comparator, + bool retainOrderOfEquivalentItems) +{ + const ScopedLockType lock (getLock()); + sortArray (comparator, values.begin(), 0, size() - 1, retainOrderOfEquivalentItems); +} + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayAllocationBase.h b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayAllocationBase.h index 34f98b73..179f87cf 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayAllocationBase.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayAllocationBase.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.cpp index 9a979e2d..57b48617 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2018 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -87,27 +87,27 @@ namespace ArrayBaseTestsHelpers }; } -bool operator== (const ArrayBaseTestsHelpers::TriviallyCopyableType& tct, - const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct) +static bool operator== (const ArrayBaseTestsHelpers::TriviallyCopyableType& tct, + const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct) { return tct.getValue() == ntct.getValue(); } -bool operator== (const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct, - const ArrayBaseTestsHelpers::TriviallyCopyableType& tct) +static bool operator== (const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct, + const ArrayBaseTestsHelpers::TriviallyCopyableType& tct) { return tct == ntct; } -class ArrayBaseTests : public UnitTest +class ArrayBaseTests final : public UnitTest { using CopyableType = ArrayBaseTestsHelpers::TriviallyCopyableType; using NoncopyableType = ArrayBaseTestsHelpers::NonTriviallyCopyableType; - #if ! (defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__)) - static_assert (std::is_trivially_copyable::value, + #if ! (defined (__GNUC__) && __GNUC__ < 5 && ! defined (__clang__)) + static_assert (std::is_trivially_copyable_v, "Test TriviallyCopyableType is not trivially copyable"); - static_assert (! std::is_trivially_copyable::value, + static_assert (! std::is_trivially_copyable_v, "Test NonTriviallyCopyableType is trivially copyable"); #endif @@ -545,7 +545,7 @@ class ArrayBaseTests : public UnitTest virtual ~Base() = default; }; - struct Derived : Base + struct Derived final : public Base { }; @@ -562,7 +562,7 @@ class ArrayBaseTests : public UnitTest } } - template + template void checkEqual (const ArrayBase& a, const ArrayBase& b) { @@ -572,7 +572,7 @@ class ArrayBaseTests : public UnitTest expect (a[i] == b[i]); } - template + template void checkEqual (ArrayBase& a, std::vector& b) { @@ -582,7 +582,7 @@ class ArrayBaseTests : public UnitTest expect (a[i] == b[(size_t) i]); } - template + template void checkEqual (ArrayBase& a, ArrayBase& b, std::vector& c) diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h index 4e2869a2..a4ae790e 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -43,8 +43,8 @@ class ArrayBase : public TypeOfCriticalSectionToUse using ParameterType = typename TypeHelpers::ParameterType::type; template - using AllowConversion = typename std::enable_if, - std::tuple>::value>::type; + using AllowConversion = std::enable_if_t, + std::tuple>>; public: //============================================================================== @@ -123,7 +123,7 @@ class ArrayBase : public TypeOfCriticalSectionToUse auto* e = begin(); for (auto& o : other) - if (! (*e++ == o)) + if (! exactlyEqual (*e++, o)) return false; return true; @@ -255,32 +255,24 @@ class ArrayBase : public TypeOfCriticalSectionToUse //============================================================================== void add (const ElementType& newElement) { - checkSourceIsNotAMember (&newElement); - ensureAllocatedSize (numUsed + 1); - addAssumingCapacityIsReady (newElement); + addImpl (newElement); } void add (ElementType&& newElement) { - checkSourceIsNotAMember (&newElement); - ensureAllocatedSize (numUsed + 1); - addAssumingCapacityIsReady (std::move (newElement)); + addImpl (std::move (newElement)); } template - void add (const ElementType& firstNewElement, OtherElements... otherElements) + void add (const ElementType& firstNewElement, OtherElements&&... otherElements) { - checkSourceIsNotAMember (&firstNewElement); - ensureAllocatedSize (numUsed + 1 + (int) sizeof... (otherElements)); - addAssumingCapacityIsReady (firstNewElement, otherElements...); + addImpl (firstNewElement, std::forward (otherElements)...); } template - void add (ElementType&& firstNewElement, OtherElements... otherElements) + void add (ElementType&& firstNewElement, OtherElements&&... otherElements) { - checkSourceIsNotAMember (&firstNewElement); - ensureAllocatedSize (numUsed + 1 + (int) sizeof... (otherElements)); - addAssumingCapacityIsReady (std::move (firstNewElement), otherElements...); + addImpl (std::move (firstNewElement), std::forward (otherElements)...); } //============================================================================== @@ -312,7 +304,7 @@ class ArrayBase : public TypeOfCriticalSectionToUse } template - typename std::enable_if::value, int>::type + std::enable_if_t, int> addArray (const OtherArrayType& arrayToAddFrom, int startIndex, int numElementsToAdd = -1) { @@ -335,7 +327,7 @@ class ArrayBase : public TypeOfCriticalSectionToUse //============================================================================== void insert (int indexToInsertAt, ParameterType newElement, int numberOfTimesToInsertIt) { - checkSourceIsNotAMember (&newElement); + checkSourceIsNotAMember (newElement); auto* space = createInsertSpace (indexToInsertAt, numberOfTimesToInsertIt); for (int i = 0; i < numberOfTimesToInsertIt; ++i) @@ -393,63 +385,49 @@ class ArrayBase : public TypeOfCriticalSectionToUse private: //============================================================================== - template - #if defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__) - using IsTriviallyCopyable = std::is_scalar; + #if defined (__GNUC__) && __GNUC__ < 5 && ! defined (__clang__) + static constexpr auto isTriviallyCopyable = std::is_scalar_v; #else - using IsTriviallyCopyable = std::is_trivially_copyable; + static constexpr auto isTriviallyCopyable = std::is_trivially_copyable_v; #endif - template - using TriviallyCopyableVoid = typename std::enable_if::value, void>::type; - - template - using NonTriviallyCopyableVoid = typename std::enable_if::value, void>::type; - //============================================================================== - template - TriviallyCopyableVoid addArrayInternal (const ElementType* otherElements, int numElements) - { - memcpy (elements + numUsed, otherElements, (size_t) numElements * sizeof (ElementType)); - } - - template - TriviallyCopyableVoid addArrayInternal (const Type* otherElements, int numElements) - { - auto* start = elements + numUsed; - - while (--numElements >= 0) - new (start++) ElementType (*(otherElements++)); - } - - template - NonTriviallyCopyableVoid addArrayInternal (const Type* otherElements, int numElements) + template + void addArrayInternal (const Type* otherElements, int numElements) { - auto* start = elements + numUsed; + if constexpr (isTriviallyCopyable && std::is_same_v) + { + if (numElements > 0) + memcpy (elements + numUsed, otherElements, (size_t) numElements * sizeof (ElementType)); + } + else + { + auto* start = elements + numUsed; - while (--numElements >= 0) - new (start++) ElementType (*(otherElements++)); + while (--numElements >= 0) + new (start++) ElementType (*(otherElements++)); + } } //============================================================================== - template - TriviallyCopyableVoid setAllocatedSizeInternal (int numElements) - { - elements.realloc ((size_t) numElements); - } - - template - NonTriviallyCopyableVoid setAllocatedSizeInternal (int numElements) + void setAllocatedSizeInternal (int numElements) { - HeapBlock newElements (numElements); - - for (int i = 0; i < numUsed; ++i) + if constexpr (isTriviallyCopyable) { - new (newElements + i) ElementType (std::move (elements[i])); - elements[i].~ElementType(); + elements.realloc ((size_t) numElements); } + else + { + HeapBlock newElements (numElements); - elements = std::move (newElements); + for (int i = 0; i < numUsed; ++i) + { + new (newElements + i) ElementType (std::move (elements[i])); + elements[i].~ElementType(); + } + + elements = std::move (newElements); + } } //============================================================================== @@ -465,144 +443,138 @@ class ArrayBase : public TypeOfCriticalSectionToUse return elements + indexToInsertAt; } - template - TriviallyCopyableVoid createInsertSpaceInternal (int indexToInsertAt, int numElements) + void createInsertSpaceInternal (int indexToInsertAt, int numElements) { - auto* start = elements + indexToInsertAt; - auto numElementsToShift = numUsed - indexToInsertAt; - memmove (start + numElements, start, (size_t) numElementsToShift * sizeof (ElementType)); - } - - template - NonTriviallyCopyableVoid createInsertSpaceInternal (int indexToInsertAt, int numElements) - { - auto* end = elements + numUsed; - auto* newEnd = end + numElements; - auto numElementsToShift = numUsed - indexToInsertAt; - - for (int i = 0; i < numElementsToShift; ++i) + if constexpr (isTriviallyCopyable) { - new (--newEnd) ElementType (std::move (*(--end))); - end->~ElementType(); + auto* start = elements + indexToInsertAt; + auto numElementsToShift = numUsed - indexToInsertAt; + memmove (start + numElements, start, (size_t) numElementsToShift * sizeof (ElementType)); } - } - - //============================================================================== - template - TriviallyCopyableVoid removeElementsInternal (int indexToRemoveAt, int numElementsToRemove) - { - auto* start = elements + indexToRemoveAt; - auto numElementsToShift = numUsed - (indexToRemoveAt + numElementsToRemove); - memmove (start, start + numElementsToRemove, (size_t) numElementsToShift * sizeof (ElementType)); - } - - template - NonTriviallyCopyableVoid removeElementsInternal (int indexToRemoveAt, int numElementsToRemove) - { - auto numElementsToShift = numUsed - (indexToRemoveAt + numElementsToRemove); - auto* destination = elements + indexToRemoveAt; - auto* source = destination + numElementsToRemove; - - for (int i = 0; i < numElementsToShift; ++i) - moveAssignElement (destination++, std::move (*(source++))); + else + { + auto* end = elements + numUsed; + auto* newEnd = end + numElements; + auto numElementsToShift = numUsed - indexToInsertAt; - for (int i = 0; i < numElementsToRemove; ++i) - (destination++)->~ElementType(); + for (int i = 0; i < numElementsToShift; ++i) + { + new (--newEnd) ElementType (std::move (*(--end))); + end->~ElementType(); + } + } } //============================================================================== - template - TriviallyCopyableVoid moveInternal (int currentIndex, int newIndex) noexcept + void removeElementsInternal (int indexToRemoveAt, int numElementsToRemove) { - char tempCopy[sizeof (ElementType)]; - memcpy (tempCopy, elements + currentIndex, sizeof (ElementType)); - - if (newIndex > currentIndex) + if constexpr (isTriviallyCopyable) { - memmove (elements + currentIndex, - elements + currentIndex + 1, - (size_t) (newIndex - currentIndex) * sizeof (ElementType)); + auto* start = elements + indexToRemoveAt; + auto numElementsToShift = numUsed - (indexToRemoveAt + numElementsToRemove); + memmove (start, start + numElementsToRemove, (size_t) numElementsToShift * sizeof (ElementType)); } else { - memmove (elements + newIndex + 1, - elements + newIndex, - (size_t) (currentIndex - newIndex) * sizeof (ElementType)); - } + auto numElementsToShift = numUsed - (indexToRemoveAt + numElementsToRemove); + auto* destination = elements + indexToRemoveAt; + auto* source = destination + numElementsToRemove; + + for (int i = 0; i < numElementsToShift; ++i) + moveAssignElement (destination++, std::move (*(source++))); - memcpy (elements + newIndex, tempCopy, sizeof (ElementType)); + for (int i = 0; i < numElementsToRemove; ++i) + (destination++)->~ElementType(); + } } - template - NonTriviallyCopyableVoid moveInternal (int currentIndex, int newIndex) noexcept + //============================================================================== + void moveInternal (int currentIndex, int newIndex) noexcept { - auto* e = elements + currentIndex; - ElementType tempCopy (std::move (*e)); - auto delta = newIndex - currentIndex; - - if (delta > 0) + if constexpr (isTriviallyCopyable) { - for (int i = 0; i < delta; ++i) + char tempCopy[sizeof (ElementType)]; + memcpy (tempCopy, elements + currentIndex, sizeof (ElementType)); + + if (newIndex > currentIndex) + { + memmove (elements + currentIndex, + elements + currentIndex + 1, + (size_t) (newIndex - currentIndex) * sizeof (ElementType)); + } + else { - moveAssignElement (e, std::move (*(e + 1))); - ++e; + memmove (elements + newIndex + 1, + elements + newIndex, + (size_t) (currentIndex - newIndex) * sizeof (ElementType)); } + + memcpy (elements + newIndex, tempCopy, sizeof (ElementType)); } else { - for (int i = 0; i < -delta; ++i) + auto* e = elements + currentIndex; + ElementType tempCopy (std::move (*e)); + auto delta = newIndex - currentIndex; + + if (delta > 0) { - moveAssignElement (e, std::move (*(e - 1))); - --e; + for (int i = 0; i < delta; ++i) + { + moveAssignElement (e, std::move (*(e + 1))); + ++e; + } + } + else + { + for (int i = 0; i < -delta; ++i) + { + moveAssignElement (e, std::move (*(e - 1))); + --e; + } } - } - moveAssignElement (e, std::move (tempCopy)); + moveAssignElement (e, std::move (tempCopy)); + } } //============================================================================== - void addAssumingCapacityIsReady (const ElementType& element) { new (elements + numUsed++) ElementType (element); } - void addAssumingCapacityIsReady (ElementType&& element) { new (elements + numUsed++) ElementType (std::move (element)); } - - template - void addAssumingCapacityIsReady (const ElementType& firstNewElement, OtherElements... otherElements) + template + void addImpl (Elements&&... toAdd) { - addAssumingCapacityIsReady (firstNewElement); - addAssumingCapacityIsReady (otherElements...); + (checkSourceIsNotAMember (toAdd), ...); + ensureAllocatedSize (numUsed + (int) sizeof... (toAdd)); + addAssumingCapacityIsReady (std::forward (toAdd)...); } - template - void addAssumingCapacityIsReady (ElementType&& firstNewElement, OtherElements... otherElements) + template + void addAssumingCapacityIsReady (Elements&&... toAdd) { - addAssumingCapacityIsReady (std::move (firstNewElement)); - addAssumingCapacityIsReady (otherElements...); + (new (elements + numUsed++) ElementType (std::forward (toAdd)), ...); } //============================================================================== - template - typename std::enable_if::value, void>::type - moveAssignElement (ElementType* destination, ElementType&& source) + void moveAssignElement (ElementType* destination, ElementType&& source) { - *destination = std::move (source); - } - - template - typename std::enable_if::value, void>::type - moveAssignElement (ElementType* destination, ElementType&& source) - { - destination->~ElementType(); - new (destination) ElementType (std::move (source)); + if constexpr (std::is_move_assignable_v) + { + *destination = std::move (source); + } + else + { + destination->~ElementType(); + new (destination) ElementType (std::move (source)); + } } - void checkSourceIsNotAMember (const ElementType* element) + void checkSourceIsNotAMember ([[maybe_unused]] const ElementType& element) { // when you pass a reference to an existing element into a method like add() which // may need to reallocate the array to make more space, the incoming reference may // be deleted indirectly during the reallocation operation! To work around this, // make a local copy of the item you're trying to add (and maybe use std::move to // move it into the add() method to avoid any extra overhead) - jassert (element < begin() || element >= end()); - ignoreUnused (element); + jassert (std::addressof (element) < begin() || end() <= std::addressof (element)); } //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.cpp index 5e7ee2e5..a77afe5c 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -87,44 +87,54 @@ void DynamicObject::cloneAllProperties() *v = v->clone(); } -DynamicObject::Ptr DynamicObject::clone() +std::unique_ptr DynamicObject::clone() const { - Ptr d (new DynamicObject (*this)); - d->cloneAllProperties(); - return d; + auto result = std::make_unique (*this); + result->cloneAllProperties(); + return result; } -void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const bool allOnOneLine, int maximumDecimalPlaces) +void DynamicObject::writeAsJSON (OutputStream& out, const JSON::FormatOptions& format) { out << '{'; - if (! allOnOneLine) + if (format.getSpacing() == JSON::Spacing::multiLine) out << newLine; const int numValues = properties.size(); for (int i = 0; i < numValues; ++i) { - if (! allOnOneLine) - JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize); + if (format.getSpacing() == JSON::Spacing::multiLine) + JSONFormatter::writeSpaces (out, format.getIndentLevel() + JSONFormatter::indentSize); out << '"'; JSONFormatter::writeString (out, properties.getName (i)); - out << "\": "; - JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine, maximumDecimalPlaces); + out << "\":"; + + if (format.getSpacing() != JSON::Spacing::none) + out << ' '; + + JSON::writeToStream (out, + properties.getValueAt (i), + format.withIndentLevel (format.getIndentLevel() + JSONFormatter::indentSize)); if (i < numValues - 1) { - if (allOnOneLine) - out << ", "; - else - out << ',' << newLine; + out << ","; + + switch (format.getSpacing()) + { + case JSON::Spacing::none: break; + case JSON::Spacing::singleLine: out << ' '; break; + case JSON::Spacing::multiLine: out << newLine; break; + } } - else if (! allOnOneLine) + else if (format.getSpacing() == JSON::Spacing::multiLine) out << newLine; } - if (! allOnOneLine) - JSONFormatter::writeSpaces (out, indentLevel); + if (format.getSpacing() == JSON::Spacing::multiLine) + JSONFormatter::writeSpaces (out, format.getIndentLevel()); out << '}'; } diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h b/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h index 9b381080..f80f57b0 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -87,7 +87,7 @@ class JUCE_API DynamicObject : public ReferenceCountedObject This is basically the same as calling setProperty (methodName, (var::NativeFunction) myFunction), but helps to avoid accidentally invoking the wrong type of var constructor. It also makes - the code easier to read, + the code easier to read. */ void setMethod (Identifier methodName, var::NativeFunction function); @@ -96,7 +96,10 @@ class JUCE_API DynamicObject : public ReferenceCountedObject void clear(); /** Returns the NamedValueSet that holds the object's properties. */ - NamedValueSet& getProperties() noexcept { return properties; } + NamedValueSet& getProperties() noexcept { return properties; } + + /** Returns the NamedValueSet that holds the object's properties. */ + const NamedValueSet& getProperties() const noexcept { return properties; } /** Calls var::clone() on all the properties that this object contains. */ void cloneAllProperties(); @@ -107,7 +110,7 @@ class JUCE_API DynamicObject : public ReferenceCountedObject with a (deep) copy of all of its properties. Subclasses can override this to implement their own custom copy routines. */ - virtual Ptr clone(); + virtual std::unique_ptr clone() const; //============================================================================== /** Writes this object to a text stream in JSON format. @@ -115,17 +118,12 @@ class JUCE_API DynamicObject : public ReferenceCountedObject never need to call it directly, but it's virtual so that custom object types can stringify themselves appropriately. */ - virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine, int maximumDecimalPlaces); + virtual void writeAsJSON (OutputStream&, const JSON::FormatOptions&); private: //============================================================================== NamedValueSet properties; - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // This method has been deprecated - use var::invoke instead - virtual void invokeMethod (const Identifier&, const var*, int) {} - #endif - JUCE_LEAK_DETECTOR (DynamicObject) }; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ElementComparator.h b/JuceLibraryCode/modules/juce_core/containers/juce_ElementComparator.h index 0b4f4277..c13dda0c 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ElementComparator.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ElementComparator.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -35,12 +35,14 @@ template struct SortFunctionConverter { SortFunctionConverter (ElementComparator& e) : comparator (e) {} + SortFunctionConverter (const SortFunctionConverter&) = default; template bool operator() (Type a, Type b) { return comparator.compareElements (a, b) < 0; } private: ElementComparator& comparator; + SortFunctionConverter& operator= (const SortFunctionConverter&) = delete; }; @@ -121,7 +123,7 @@ static void sortArray (ElementComparator& comparator, @param lastElement the index of the last element in the range (this is non-inclusive) */ template -static int findInsertIndexInSortedArray (ElementComparator& comparator, +static int findInsertIndexInSortedArray ([[maybe_unused]] ElementComparator& comparator, ElementType* const array, const ElementType newElement, int firstElement, @@ -129,9 +131,6 @@ static int findInsertIndexInSortedArray (ElementComparator& comparator, { jassert (firstElement <= lastElement); - ignoreUnused (comparator); // if you pass in an object with a static compareElements() method, this - // avoids getting warning messages about the parameter being unused - while (firstElement < lastElement) { if (comparator.compareElements (newElement, array [firstElement]) == 0) diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Enumerate.h b/JuceLibraryCode/modules/juce_core/containers/juce_Enumerate.h new file mode 100644 index 00000000..7803b3da --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Enumerate.h @@ -0,0 +1,427 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 detail +{ + +template +constexpr auto canPreDecrement = false; + +template +constexpr auto canPreDecrement())>> = true; + +template +constexpr auto canAddAssign = false; + +template +constexpr auto canAddAssign() += std::declval())>> = true; + +template +constexpr auto canSubAssign = false; + +template +constexpr auto canSubAssign() -= std::declval())>> = true; + +template +constexpr auto canAdd = false; + +template +constexpr auto canAdd() + std::declval())>> = true; + +template +constexpr auto canSub = false; + +template +constexpr auto canSub() - std::declval())>> = true; + +template +constexpr auto canLessThan = false; + +template +constexpr auto canLessThan() < std::declval())>> = true; + +template +constexpr auto canLessThanEqual = false; + +template +constexpr auto canLessThanEqual() <= std::declval())>> = true; + +template +constexpr auto canGreaterThan = false; + +template +constexpr auto canGreaterThan() > std::declval())>> = true; + +template +constexpr auto canGreaterThanEqual = false; + +template +constexpr auto canGreaterThanEqual() >= std::declval())>> = true; + +namespace withAdlSize +{ + using std::size; + + template + using AdlSize = decltype (size (std::declval())); + + template + using AdlSignedSize = std::common_type_t>>; +} + +} // namespace detail + +/** + Returned when dereferencing an EnumerateIterator. + + Allows querying the index associated with an element, along with a reference to the element + itself. + + You should never need to construct an instance of this type yourself. Instead, use the + enumerate() function to construct a range that can be enumerated. + + @see enumerate() + @tags{Core} +*/ +template +struct Enumerated +{ + Index index; + Value value; +}; + +/** + An iterator that wraps some other iterator, keeping track of the relative position of that + iterator based on calls to arithmetic operators such as + operator++(), operator--(), operator+(), and operator-(). + + You should never need to construct an instance of this type yourself. Instead, use the + enumerate() function to construct a range that can be enumerated. + + @see enumerate() + @tags{Core} +*/ +template +class EnumerateIterator +{ +public: + /** Default constructor. */ + constexpr EnumerateIterator() = default; + + /** Wraps the provided iterator, and sets the internal count to 0. */ + constexpr explicit EnumerateIterator (Iter iter) + : EnumerateIterator (std::move (iter), Index{}) {} + + /** Wraps the provided iterator, and sets the internal count to the provided value. */ + constexpr EnumerateIterator (Iter iter, Index ind) + : iterator (std::move (iter)), index (ind) {} + + /** Two EnumerateIterators are considered equal if the wrapped iterators are equal. */ + template + [[nodiscard]] constexpr bool operator== (const EnumerateIterator& other) const + { + return iterator == other.iterator; + } + + /** @see operator==() */ + template + [[nodiscard]] constexpr bool operator!= (const EnumerateIterator& other) const + { + return ! operator== (other); + } + + /** Dereferencing the iterator produces an Enumerated instance *by value*. This type holds + a copy of the iterator's current index, along with the result of dereferencing the + wrapped iterator (normally a reference type). + */ + [[nodiscard]] constexpr Enumerated())> operator*() const + { + return { index, *iterator }; + } + + /** Increments the iterator and the index. */ + constexpr EnumerateIterator& operator++() + { + ++iterator; + ++index; + return *this; + } + + /** Increments the iterator and the index. */ + constexpr EnumerateIterator operator++ (int) + { + auto copy = *this; + operator++(); + return copy; + } + + /** Decrements the iterator and the index. + Only participates in overload resolution if the iterator can be pre-decremented. + */ + template , int> = 0> + constexpr EnumerateIterator& operator--() + { + --iterator; + --index; + return *this; + } + + /** Decrements the iterator and the index. + Only participates in overload resolution if the iterator can be pre-decremented. + */ + template , int> = 0> + constexpr EnumerateIterator operator-- (int) + { + auto copy = *this; + operator--(); + return copy; + } + + /** Adds an integral value to both the iterator and the index. + Only participates in overload resolution if the iterator can be add-assigned. + */ + template , int> = 0> + constexpr EnumerateIterator& operator+= (I diff) + { + iterator += diff; + index += static_cast (diff); + return *this; + } + + /** Subtracts an integral value from both the iterator and the index. + Only participates in overload resolution if the iterator can be sub-assigned. + */ + template , int> = 0> + constexpr EnumerateIterator& operator-= (I diff) + { + iterator -= diff; + index -= static_cast (diff); + return *this; + } + + /** Subtracts another enumerate iterator from this one, producing the same result as + subtracting the two wrapped iterators. For random-access iterators, this will normally + return the distance between the two iterators. + Only participates in overload resolution if the wrapped iterators can be subtracted. + */ + template , int> = 0> + [[nodiscard]] constexpr auto operator- (const EnumerateIterator& other) const + { + return iterator - other.iterator; + } + + /** Indexes into this iterator, equivalent to adding an integral value to this iterator and + then dereferencing the result. + Only participates in overload resolution if the wrapped iterator allows addition of + integral values. + */ + template , int> = 0> + [[nodiscard]] constexpr auto operator[] (I diff) const + { + return *(*this + diff); + } + + /** Returns the result of comparing the two wrapped iterators. + Only participates in overload resolution if the wrapped iterators are comparable. + */ + template , int> = 0> + [[nodiscard]] constexpr bool operator< (const EnumerateIterator& other) const + { + return iterator < other.iterator; + } + + /** Returns the result of comparing the two wrapped iterators. + Only participates in overload resolution if the wrapped iterators are comparable. + */ + template , int> = 0> + [[nodiscard]] constexpr bool operator<= (const EnumerateIterator& other) const + { + return iterator <= other.iterator; + } + + /** Returns the result of comparing the two wrapped iterators. + Only participates in overload resolution if the wrapped iterators are comparable. + */ + template , int> = 0> + [[nodiscard]] constexpr bool operator> (const EnumerateIterator& other) const + { + return iterator > other.iterator; + } + + /** Returns the result of comparing the two wrapped iterators. + Only participates in overload resolution if the wrapped iterators are comparable. + */ + template , int> = 0> + [[nodiscard]] constexpr bool operator>= (const EnumerateIterator& other) const + { + return iterator >= other.iterator; + } + + /** Returns the result of adding an integral value to this iterator. + Only participates in overload resolution if addition is supported by the wrapped iterator. + */ + template , int> = 0> + constexpr friend auto operator+ (EnumerateIterator iter, I ind) + { + return iter += ind; + } + + /** Returns the result of adding an integral value to this iterator. + Only participates in overload resolution if addition is supported by the wrapped iterator. + */ + template , int> = 0> + constexpr friend auto operator+ (I ind, EnumerateIterator iter) + { + return iter += ind; + } + + /** Returns the result of subtracting an integral value from this iterator. + Only participates in overload resolution if subtraction is supported by the wrapped iterator. + */ + template , int> = 0> + constexpr friend auto operator- (EnumerateIterator iter, I ind) + { + return iter -= ind; + } + +private: + Iter iterator{}; + Index index = 0; +}; + +//============================================================================== +/** + Wraps a pair of iterators, providing member begin() and end() functions that return + those iterators. + This is useful in situations where you have an iterator pair, but want to use that + pair somewhere that requires an iterable range, such as in a ranged-for loop. + + @see makeRange() + @tags{Core} +*/ +template +class IteratorPair +{ +public: + /** Constructs a pair from a begin and end iterator. + Instead of calling this directly, use makeRange(). + */ + constexpr IteratorPair (Begin bIn, End eIn) + : b (std::move (bIn)), e (std::move (eIn)) {} + + /** Returns the begin iterator. */ + constexpr auto begin() const { return b; } + + /** Returns the end iterator. */ + constexpr auto end() const { return e; } + +private: + Begin b; + End e; +}; + +/** + Given two iterators "begin" and "end", returns an IteratorPair with a member + begin() and end() function. This pair can be used in contexts that expect an + iterable range, the most significant of which is ranged-for loops. + This automatically deduces the Begin and End types, so it is more concise to use than + directly calling the IteratorPair constructor. +*/ +template +[[nodiscard]] constexpr auto makeRange (Begin begin, End end) +{ + return IteratorPair { std::move (begin), std::move (end) }; +} + +//============================================================================== +/** + Given a range and an optional starting offset, returns an IteratorPair that + holds EnumerateIterators wrapping the begin() and end() of the range. + + This is useful in situations where you need to iterate some range, but also query + the position of each item in the range. + + A simple usage might look like this: + + @code + std::list elements { 10, 20, 30, 40, 50 }; + + for (const auto pair : enumerate (elements)) + std::cout << pair.index << ' ' << pair.value << ' '; + + // output: 0 10 1 20 2 30 3 40 4 50 + @endcode + + You can also use structured bindings to concisely destructure each Enumerated instance: + + @code + for (const auto [index, value] : enumerate (elements)) + std::cout << index << ' ' << value << ' '; + @endcode + + Note that the Enumerated instance is returned by value. This is because each Enumerated + instance is created on-demand when the iterator is dereferenced. As a result, the following + will result in a dangling reference, and will probably trigger a compiler warning: + + @code + // BAD IDEA: creating a reference to a temporary Enumerated instance + for (auto& [index, value] : enumerate (elements)) + ... + @endcode + + The 'value' member of Enumerated automatically assumes the same type as dereferencing the + wrapped iterator, which is normally a reference to an element of a container. + In the following snippet, the type of '[index, value]' is 'const Enumerated', + the type of 'index' is 'ptrdiff_t', and the type of 'value' is 'int&'. + + @code + std::vector elements { 10, 20, 30, 40, 50 }; + for (const auto [index, value] : enumerate (elements)) + ... + @endcode + + By default, the constness of pair.value will match the constness of the range passed to + enumerate. If you pass a mutable lvalue reference to enumerate, then each value will also + be mutable. If you pass a constant lvalue reference to enumerate, then each value will be + const. If you know that you don't need the iterated elements to be mutable, it's good + practice to wrap the range with std::as_const before passing it to enumerate: + + @code + for (const auto [index, value] : enumerate (std::as_const (elements))) + { + // value is immutable here + } + @endcode +*/ +template > +[[nodiscard]] constexpr auto enumerate (Range&& range, Index startingValue = {}) +{ + // This ensures argument-dependent lookup works properly for user-defined non-member begin/end + using std::begin, std::end; + return makeRange (EnumerateIterator { begin (range), startingValue }, + EnumerateIterator { end (range), startingValue }); +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Enumerate_test.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_Enumerate_test.cpp new file mode 100644 index 00000000..1435cc28 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Enumerate_test.cpp @@ -0,0 +1,126 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 EnumerateUnitTest : public UnitTest +{ +public: + EnumerateUnitTest() : UnitTest ("Enumerate", UnitTestCategories::containers) {} + + void runTest() override + { + beginTest ("enumeration works for bidirectional iterators"); + { + const std::list elements { 10, 20, 30, 40, 50 }; + std::vector counts; + + for (const auto pair : enumerate (elements)) + counts.push_back ((int) pair.index); + + expect (counts == std::vector { 0, 1, 2, 3, 4 }); + } + + beginTest ("enumeration works for random-access iterators"); + { + const std::vector strings { "a", "bb", "ccc", "dddd", "eeeee" }; + + std::vector counts; + + for (const auto [count, element] : enumerate (strings)) + counts.push_back ((int) ((size_t) count + element.size())); + + expect (counts == std::vector { 1, 3, 5, 7, 9 }); + } + + beginTest ("enumeration works for mutable ranges"); + { + std::vector strings { "", "", "", "", "" }; + + for (const auto [count, element] : enumerate (strings)) + element = std::to_string (count); + + expect (strings == std::vector { "0", "1", "2", "3", "4" }); + } + + beginTest ("iterator can be incremented by more than one"); + { + std::vector ints (6); + + const auto enumerated = enumerate (ints); + + std::vector counts; + + for (auto b = enumerated.begin(), e = enumerated.end(); b != e; b += 2) + counts.push_back ((int) (*b).index); + + expect (counts == std::vector { 0, 2, 4 }); + } + + beginTest ("iterator can be started at a non-zero value"); + { + const std::vector ints (6); + + std::vector counts; + + for (const auto enumerated : enumerate (ints, 5)) + counts.push_back ((int) enumerated.index); + + expect (counts == std::vector { 5, 6, 7, 8, 9, 10 }); + } + + beginTest ("subtracting two EnumerateIterators returns the difference between the base iterators"); + { + const std::vector ints (6); + const auto enumerated = enumerate (ints); + expect ((int) (enumerated.end() - enumerated.begin()) == (int) ints.size()); + } + + beginTest ("EnumerateIterator can be decremented"); + { + const std::vector ints (5); + std::vector counts; + + const auto enumerated = enumerate (std::as_const (ints)); + + for (auto i = enumerated.end(), b = enumerated.begin(); i != b; --i) + counts.push_back ((int) (*(i - 1)).index); + + expect (counts == std::vector { -1, -2, -3, -4, -5 }); + } + + beginTest ("EnumerateIterator can be compared"); + { + const std::vector ints (6); + const auto enumerated = enumerate (ints); + expect (enumerated.begin() < enumerated.end()); + expect (enumerated.begin() <= enumerated.end()); + expect (enumerated.end() > enumerated.begin()); + expect (enumerated.end() >= enumerated.begin()); + } + } +}; + +static EnumerateUnitTest enumerateUnitTest; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_FixedSizeFunction.h b/JuceLibraryCode/modules/juce_core/containers/juce_FixedSizeFunction.h new file mode 100644 index 00000000..9536ca7c --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/containers/juce_FixedSizeFunction.h @@ -0,0 +1,237 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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 +{ + +#ifndef DOXYGEN + +namespace detail +{ + template + struct Vtable + { + using Storage = void*; + + using Move = void (*) (Storage, Storage); + using Call = Ret (*) (Storage, Args...); + using Clear = void (*) (Storage); + + constexpr Vtable (Move moveIn, Call callIn, Clear clearIn) noexcept + : move (moveIn), call (callIn), clear (clearIn) {} + + Move move = nullptr; + Call call = nullptr; + Clear clear = nullptr; + }; + + template + void move (void* from, void* to) + { + new (to) Fn (std::move (*reinterpret_cast (from))); + } + + template + std::enable_if_t, Ret> call (void* s, Args... args) + { + (*reinterpret_cast (s)) (std::forward (args)...); + } + + template + std::enable_if_t, Ret> call (void* s, Args... args) + { + return (*reinterpret_cast (s)) (std::forward (args)...); + } + + template + void clear (void* s) + { + // I know this looks insane, for some reason MSVC 14 sometimes thinks fn is unreferenced + [[maybe_unused]] auto& fn = *reinterpret_cast (s); + fn.~Fn(); + } + + template + constexpr Vtable makeVtable() + { + return { move , call , clear }; + } +} // namespace detail + +template +class FixedSizeFunction; + +#endif + +/** + A type similar to `std::function` that holds a callable object. + + Unlike `std::function`, the callable object will always be stored in + a buffer of size `len` that is internal to the FixedSizeFunction instance. + This in turn means that creating a FixedSizeFunction instance will never allocate, + making FixedSizeFunctions suitable for use in realtime contexts. + + @tags{DSP} +*/ +template +class FixedSizeFunction +{ +private: + using Storage = std::aligned_storage_t; + + template + using Decay = std::decay_t; + + template > + using IntIfValidConversion = std::enable_if_t, + int>; + +public: + /** Create an empty function. */ + FixedSizeFunction() noexcept = default; + + /** Create an empty function. */ + FixedSizeFunction (std::nullptr_t) noexcept + : FixedSizeFunction() {} + + FixedSizeFunction (const FixedSizeFunction&) = delete; + + /** Forwards the passed Callable into the internal storage buffer. */ + template , + IntIfValidConversion = 0> + FixedSizeFunction (Callable&& callable) + { + static_assert (sizeof (Fn) <= len, + "The requested function cannot fit in this FixedSizeFunction"); + static_assert (alignof (Fn) <= alignof (Storage), + "FixedSizeFunction cannot accommodate the requested alignment requirements"); + + static constexpr auto vtableForCallable = detail::makeVtable(); + vtable = &vtableForCallable; + + [[maybe_unused]] auto* ptr = new (&storage) Fn (std::forward (callable)); + jassert ((void*) ptr == (void*) &storage); + } + + /** Move constructor. */ + FixedSizeFunction (FixedSizeFunction&& other) noexcept + : vtable (other.vtable) + { + move (std::move (other)); + } + + /** Converting constructor from smaller FixedSizeFunctions. */ + template = 0> + FixedSizeFunction (FixedSizeFunction&& other) noexcept + : vtable (other.vtable) + { + move (std::move (other)); + } + + /** Nulls this instance. */ + FixedSizeFunction& operator= (std::nullptr_t) noexcept + { + return *this = FixedSizeFunction(); + } + + FixedSizeFunction& operator= (const FixedSizeFunction&) = delete; + + /** Assigns a new callable to this instance. */ + template = 0> + FixedSizeFunction& operator= (Callable&& callable) + { + return *this = FixedSizeFunction (std::forward (callable)); + } + + /** Move assignment from smaller FixedSizeFunctions. */ + template = 0> + FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept + { + return *this = FixedSizeFunction (std::move (other)); + } + + /** Move assignment operator. */ + FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept + { + clear(); + vtable = other.vtable; + move (std::move (other)); + return *this; + } + + /** Destructor. */ + ~FixedSizeFunction() noexcept { clear(); } + + /** If this instance is currently storing a callable object, calls that object, + otherwise throws `std::bad_function_call`. + */ + Ret operator() (Args... args) const + { + if (vtable != nullptr) + return vtable->call (&storage, std::forward (args)...); + + throw std::bad_function_call(); + } + + /** Returns true if this instance currently holds a callable. */ + explicit operator bool() const noexcept { return vtable != nullptr; } + +private: + template + friend class FixedSizeFunction; + + void clear() noexcept + { + if (vtable != nullptr) + vtable->clear (&storage); + } + + template + void move (FixedSizeFunction&& other) noexcept + { + if (vtable != nullptr) + vtable->move (&other.storage, &storage); + } + + const detail::Vtable* vtable = nullptr; + mutable Storage storage; +}; + +template +bool operator!= (const FixedSizeFunction& fn, std::nullptr_t) { return bool (fn); } + +template +bool operator!= (std::nullptr_t, const FixedSizeFunction& fn) { return bool (fn); } + +template +bool operator== (const FixedSizeFunction& fn, std::nullptr_t) { return ! (fn != nullptr); } + +template +bool operator== (std::nullptr_t, const FixedSizeFunction& fn) { return ! (fn != nullptr); } + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_FixedSizeFunction_test.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_FixedSizeFunction_test.cpp new file mode 100644 index 00000000..a914f908 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/containers/juce_FixedSizeFunction_test.cpp @@ -0,0 +1,364 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#if JUCE_ENABLE_ALLOCATION_HOOKS +#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE const UnitTestAllocationChecker checker (*this) +#else +#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE +#endif + +namespace juce +{ +namespace +{ + +class ConstructCounts +{ + auto tie() const noexcept { return std::tie (constructions, copies, moves, calls, destructions); } + +public: + int constructions = 0; + int copies = 0; + int moves = 0; + int calls = 0; + int destructions = 0; + + ConstructCounts withConstructions (int i) const noexcept { auto c = *this; c.constructions = i; return c; } + ConstructCounts withCopies (int i) const noexcept { auto c = *this; c.copies = i; return c; } + ConstructCounts withMoves (int i) const noexcept { auto c = *this; c.moves = i; return c; } + ConstructCounts withCalls (int i) const noexcept { auto c = *this; c.calls = i; return c; } + ConstructCounts withDestructions (int i) const noexcept { auto c = *this; c.destructions = i; return c; } + + bool operator== (const ConstructCounts& other) const noexcept { return tie() == other.tie(); } + bool operator!= (const ConstructCounts& other) const noexcept { return tie() != other.tie(); } +}; + +String& operator<< (String& str, const ConstructCounts& c) +{ + return str << "{ constructions: " << c.constructions + << ", copies: " << c.copies + << ", moves: " << c.moves + << ", calls: " << c.calls + << ", destructions: " << c.destructions + << " }"; +} + +class FixedSizeFunctionTest final : public UnitTest +{ + static void toggleBool (bool& b) { b = ! b; } + + struct ConstructCounter + { + explicit ConstructCounter (ConstructCounts& countsIn) + : counts (countsIn) {} + + ConstructCounter (const ConstructCounter& c) + : counts (c.counts) + { + counts.copies += 1; + } + + ConstructCounter (ConstructCounter&& c) noexcept + : counts (c.counts) + { + counts.moves += 1; + } + + ~ConstructCounter() noexcept { counts.destructions += 1; } + + void operator()() const noexcept { counts.calls += 1; } + + ConstructCounts& counts; + }; + +public: + FixedSizeFunctionTest() + : UnitTest ("Fixed Size Function", UnitTestCategories::containers) + {} + + void runTest() override + { + beginTest ("Can be constructed and called from a lambda"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + const auto result = 5; + bool wasCalled = false; + const auto lambda = [&] { wasCalled = true; return result; }; + + const FixedSizeFunction fn (lambda); + const auto out = fn(); + + expect (wasCalled); + expectEquals (result, out); + } + + beginTest ("void fn can be constructed from function with return value"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + bool wasCalled = false; + const auto lambda = [&] { wasCalled = true; return 5; }; + const FixedSizeFunction fn (lambda); + + fn(); + expect (wasCalled); + } + + beginTest ("Can be constructed and called from a function pointer"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + bool state = false; + + const FixedSizeFunction fn (toggleBool); + + fn (state); + expect (state); + + fn (state); + expect (! state); + + fn (state); + expect (state); + } + + beginTest ("Default constructed functions throw if called"); + { + const auto a = FixedSizeFunction<8, void()>(); + expectThrowsType (a(), std::bad_function_call) + + const auto b = FixedSizeFunction<8, void()> (nullptr); + expectThrowsType (b(), std::bad_function_call) + } + + beginTest ("Functions can be moved"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + ConstructCounts counts; + + auto a = FixedSizeFunction (ConstructCounter { counts }); + expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1)); // The temporary gets destroyed + + a(); + expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1).withCalls (1)); + + const auto b = std::move (a); + expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (1)); + + b(); + expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (2)); + + b(); + expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (3)); + } + + beginTest ("Functions are destructed properly"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + ConstructCounts counts; + const ConstructCounter toCopy { counts }; + + { + auto a = FixedSizeFunction (toCopy); + expectEquals (counts, ConstructCounts().withCopies (1)); + } + + expectEquals (counts, ConstructCounts().withCopies (1).withDestructions (1)); + } + + beginTest ("Avoid destructing functions that fail to construct"); + { + struct BadConstructor + { + explicit BadConstructor (ConstructCounts& c) + : counts (c) + { + counts.constructions += 1; + throw std::runtime_error { "this was meant to happen" }; + } + + BadConstructor (const BadConstructor&) = default; + BadConstructor& operator= (const BadConstructor&) = delete; + + ~BadConstructor() noexcept { counts.destructions += 1; } + + void operator()() const noexcept { counts.calls += 1; } + + ConstructCounts& counts; + }; + + ConstructCounts counts; + + expectThrowsType ((FixedSizeFunction (BadConstructor { counts })), + std::runtime_error) + + expectEquals (counts, ConstructCounts().withConstructions (1)); + } + + beginTest ("Equality checks work"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + FixedSizeFunction<8, void()> a; + expect (! bool (a)); + expect (a == nullptr); + expect (nullptr == a); + expect (! (a != nullptr)); + expect (! (nullptr != a)); + + FixedSizeFunction<8, void()> b ([] {}); + expect (bool (b)); + expect (b != nullptr); + expect (nullptr != b); + expect (! (b == nullptr)); + expect (! (nullptr == b)); + } + + beginTest ("Functions can be cleared"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + FixedSizeFunction<8, void()> fn ([] {}); + expect (bool (fn)); + + fn = nullptr; + expect (! bool (fn)); + } + + beginTest ("Functions can be assigned"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + using Fn = FixedSizeFunction<8, void()>; + + int numCallsA = 0; + int numCallsB = 0; + + Fn x; + Fn y; + expect (! bool (x)); + expect (! bool (y)); + + x = [&] { numCallsA += 1; }; + y = [&] { numCallsB += 1; }; + expect (bool (x)); + expect (bool (y)); + + x(); + expectEquals (numCallsA, 1); + expectEquals (numCallsB, 0); + + y(); + expectEquals (numCallsA, 1); + expectEquals (numCallsB, 1); + + x = std::move (y); + expectEquals (numCallsA, 1); + expectEquals (numCallsB, 1); + + x(); + expectEquals (numCallsA, 1); + expectEquals (numCallsB, 2); + } + + beginTest ("Functions may mutate internal state"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + using Fn = FixedSizeFunction<64, void()>; + + Fn x; + expect (! bool (x)); + + int numCalls = 0; + x = [&numCalls, counter = 0]() mutable { counter += 1; numCalls = counter; }; + expect (bool (x)); + + expectEquals (numCalls, 0); + + x(); + expectEquals (numCalls, 1); + + x(); + expectEquals (numCalls, 2); + } + + beginTest ("Functions can sink move-only parameters"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + using FnA = FixedSizeFunction<64, int (std::unique_ptr)>; + + auto value = 5; + auto ptr = std::make_unique (value); + + FnA fnA = [] (std::unique_ptr p) { return *p; }; + + expect (value == fnA (std::move (ptr))); + + using FnB = FixedSizeFunction<64, void (std::unique_ptr&&)>; + + FnB fnB = [&value] (std::unique_ptr&& p) + { + auto x = std::move (p); + value = *x; + }; + + const auto newValue = 10; + fnB (std::make_unique (newValue)); + expect (value == newValue); + } + + beginTest ("Functions be converted from smaller functions"); + { + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + using SmallFn = FixedSizeFunction<20, void()>; + using LargeFn = FixedSizeFunction<21, void()>; + + bool smallCalled = false; + bool largeCalled = false; + + SmallFn small = [&smallCalled, a = std::array{}] { smallCalled = true; ignoreUnused (a); }; + LargeFn large = [&largeCalled, a = std::array{}] { largeCalled = true; ignoreUnused (a); }; + + large = std::move (small); + + large(); + + expect (smallCalled); + expect (! largeCalled); + } + } +}; + +FixedSizeFunctionTest fixedSizedFunctionTest; + +} +} // namespace juce +#undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h b/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h index 4ac767a6..9ce2a886 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -142,7 +142,7 @@ class HashMap for (auto i = hashSlots.size(); --i >= 0;) { - auto* h = hashSlots.getUnchecked(i); + auto* h = hashSlots.getUnchecked (i); while (h != nullptr) { @@ -217,7 +217,7 @@ class HashMap const ScopedLockType sl (getLock()); for (auto i = getNumSlots(); --i >= 0;) - for (auto* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry) + for (auto* entry = hashSlots.getUnchecked (i); entry != nullptr; entry = entry->nextEntry) if (entry->value == valueToLookFor) return true; @@ -269,7 +269,7 @@ class HashMap for (auto i = getNumSlots(); --i >= 0;) { - auto* entry = hashSlots.getUnchecked(i); + auto* entry = hashSlots.getUnchecked (i); HashEntry* previous = nullptr; while (entry != nullptr) @@ -311,7 +311,7 @@ class HashMap { HashEntry* nextEntry = nullptr; - for (auto* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = nextEntry) + for (auto* entry = hashSlots.getUnchecked (i); entry != nullptr; entry = nextEntry) { auto hashIndex = generateHashFor (entry->key, newNumberOfSlots); @@ -489,7 +489,7 @@ class HashMap return hash; } - static inline HashEntry* getEntry (HashEntry* firstEntry, KeyType keyToLookFor) noexcept + static HashEntry* getEntry (HashEntry* firstEntry, KeyType keyToLookFor) noexcept { for (auto* entry = firstEntry; entry != nullptr; entry = entry->nextEntry) if (entry->key == keyToLookFor) diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_HashMap_test.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_HashMap_test.cpp index 5903dda3..e9362738 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_HashMap_test.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_HashMap_test.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,7 +23,7 @@ namespace juce { -struct HashMapTest : public UnitTest +struct HashMapTest final : public UnitTest { HashMapTest() : UnitTest ("HashMap", UnitTestCategories::containers) diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_LinkedListPointer.h b/JuceLibraryCode/modules/juce_core/containers/juce_LinkedListPointer.h index 88893269..46af2b18 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_LinkedListPointer.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_LinkedListPointer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -179,10 +179,12 @@ class LinkedListPointer */ void insertNext (ObjectType* const newItem) { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011) jassert (newItem != nullptr); jassert (newItem->nextListItem == nullptr); newItem->nextListItem = item; item = newItem; + JUCE_END_IGNORE_WARNINGS_MSVC } /** Inserts an item at a numeric index in the list. @@ -208,6 +210,7 @@ class LinkedListPointer */ ObjectType* replaceNext (ObjectType* const newItem) noexcept { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011 28182) jassert (newItem != nullptr); jassert (newItem->nextListItem == nullptr); @@ -216,6 +219,7 @@ class LinkedListPointer item->nextListItem = oldItem->nextListItem.item; oldItem->nextListItem.item = nullptr; return oldItem; + JUCE_END_IGNORE_WARNINGS_MSVC } /** Adds an item to the end of the list. @@ -308,10 +312,13 @@ class LinkedListPointer */ void copyToArray (ObjectType** destArray) const noexcept { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011) jassert (destArray != nullptr); for (auto* i = item; i != nullptr; i = i->nextListItem) *destArray++ = i; + + JUCE_END_IGNORE_WARNINGS_MSVC } /** Swaps this pointer with another one */ diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.cpp new file mode 100644 index 00000000..44502bae --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.cpp @@ -0,0 +1,462 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +#if JUCE_UNIT_TESTS + +class ListenerListTests final : public UnitTest +{ +public: + //============================================================================== + class TestListener + { + public: + explicit TestListener (std::function cb) : callback (std::move (cb)) {} + + void doCallback() + { + ++numCalls; + callback(); + } + + int getNumCalls() const { return numCalls; } + + private: + int numCalls = 0; + std::function callback; + }; + + class TestObject + { + public: + void addListener (std::function cb) + { + listeners.push_back (std::make_unique (std::move (cb))); + listenerList.add (listeners.back().get()); + } + + void removeListener (int i) { listenerList.remove (listeners[(size_t) i].get()); } + + void callListeners() + { + ++callLevel; + listenerList.call ([] (auto& l) { l.doCallback(); }); + --callLevel; + } + + int getNumListeners() const { return (int) listeners.size(); } + + auto& getListener (int i) { return *listeners[(size_t) i]; } + + int getCallLevel() const + { + return callLevel; + } + + bool wereAllNonRemovedListenersCalled (int numCalls) const + { + return std::all_of (std::begin (listeners), + std::end (listeners), + [&] (auto& listener) + { + return (! listenerList.contains (listener.get())) || listener->getNumCalls() == numCalls; + }); + } + + private: + std::vector> listeners; + ListenerList listenerList; + int callLevel = 0; + }; + + //============================================================================== + ListenerListTests() : UnitTest ("ListenerList", UnitTestCategories::containers) {} + + void runTest() override + { + // This is a test that the pre-iterator adjustment implementation should pass too + beginTest ("All non-removed listeners should be called - removing an already called listener"); + { + TestObject test; + + for (int i = 0; i < 20; ++i) + { + test.addListener ([i, &test] + { + if (i == 5) + test.removeListener (6); + }); + } + + test.callListeners(); + expect (test.wereAllNonRemovedListenersCalled (1)); + } + + // Iterator adjustment is necessary for passing this + beginTest ("All non-removed listeners should be called - removing a yet uncalled listener"); + { + TestObject test; + + for (int i = 0; i < 20; ++i) + { + test.addListener ([i, &test] + { + if (i == 5) + test.removeListener (4); + }); + } + + test.callListeners(); + expect (test.wereAllNonRemovedListenersCalled (1)); + } + + // This test case demonstrates why we have to call --it.index instead of it.next() + beginTest ("All non-removed listeners should be called - one callback removes multiple listeners"); + { + TestObject test; + + for (int i = 0; i < 20; ++i) + { + test.addListener ([i, &test] + { + if (i == 19) + { + test.removeListener (19); + test.removeListener (0); + } + }); + } + + test.callListeners(); + expect (test.wereAllNonRemovedListenersCalled (1)); + } + + beginTest ("All non-removed listeners should be called - removing listeners randomly"); + { + auto random = getRandom(); + + for (auto run = 0; run < 10; ++run) + { + const auto numListeners = random.nextInt ({ 10, 100 }); + const auto listenersThatRemoveListeners = chooseUnique (random, + numListeners, + random.nextInt ({ 0, numListeners / 2 })); + + // The listener in position [key] should remove listeners in [value] + std::map> removals; + + for (auto i : listenersThatRemoveListeners) + { + // Random::nextInt ({1, 1}); triggers an assertion + removals[i] = chooseUnique (random, + numListeners, + random.nextInt ({ 1, std::max (2, numListeners / 10) })); + } + + TestObject test; + + for (int i = 0; i < numListeners; ++i) + { + test.addListener ([i, &removals, &test] + { + const auto iter = removals.find (i); + + if (iter == removals.end()) + return; + + for (auto j : iter->second) + { + test.removeListener (j); + } + }); + } + + test.callListeners(); + expect (test.wereAllNonRemovedListenersCalled (1)); + } + } + + // Iterator adjustment is not necessary for passing this + beginTest ("All non-removed listeners should be called - add listener during iteration"); + { + TestObject test; + const auto numStartingListeners = 20; + + for (int i = 0; i < numStartingListeners; ++i) + { + test.addListener ([i, &test] + { + if (i == 5 || i == 6) + test.addListener ([] {}); + }); + } + + test.callListeners(); + + // Only the Listeners added before the test can be expected to have been called + bool success = true; + + for (int i = 0; i < numStartingListeners; ++i) + success = success && test.getListener (i).getNumCalls() == 1; + + // Listeners added during the iteration must not be called in that iteration + for (int i = numStartingListeners; i < test.getNumListeners(); ++i) + success = success && test.getListener (i).getNumCalls() == 0; + + expect (success); + } + + beginTest ("All non-removed listeners should be called - nested ListenerList::call()"); + { + TestObject test; + + for (int i = 0; i < 20; ++i) + { + test.addListener ([i, &test] + { + const auto callLevel = test.getCallLevel(); + + if (i == 6 && callLevel == 1) + { + test.callListeners(); + } + + if (i == 5) + { + if (callLevel == 1) + test.removeListener (4); + else if (callLevel == 2) + test.removeListener (6); + } + }); + } + + test.callListeners(); + expect (test.wereAllNonRemovedListenersCalled (2)); + } + + beginTest ("All non-removed listeners should be called - random ListenerList::call()"); + { + const auto numListeners = 20; + auto random = getRandom(); + + for (int run = 0; run < 10; ++run) + { + TestObject test; + auto numCalls = 0; + + auto listenersToRemove = chooseUnique (random, numListeners, numListeners / 2); + + for (int i = 0; i < numListeners; ++i) + { + // Capturing numListeners is a warning on MacOS, not capturing it is an error on Windows + test.addListener ([&] + { + const auto callLevel = test.getCallLevel(); + + if (callLevel < 4 && random.nextFloat() < 0.05f) + { + ++numCalls; + test.callListeners(); + } + + if (random.nextFloat() < 0.5f) + { + const auto listenerToRemove = random.nextInt ({ 0, numListeners }); + + if (listenersToRemove.erase (listenerToRemove) > 0) + test.removeListener (listenerToRemove); + } + }); + } + + while (listenersToRemove.size() > 0) + { + test.callListeners(); + ++numCalls; + } + + expect (test.wereAllNonRemovedListenersCalled (numCalls)); + } + } + + beginTest ("Deleting the listener list from a callback"); + { + struct Listener + { + std::function onCallback; + void notify() { onCallback(); } + }; + + auto listeners = std::make_unique>(); + + const auto callback = [&] + { + expect (listeners != nullptr); + listeners.reset(); + }; + + Listener listener1 { callback }; + Listener listener2 { callback }; + + listeners->add (&listener1); + listeners->add (&listener2); + + listeners->call (&Listener::notify); + + expect (listeners == nullptr); + } + + beginTest ("Using a BailOutChecker"); + { + struct Listener + { + std::function onCallback; + void notify() { onCallback(); } + }; + + ListenerList listeners; + + bool listener1Called = false; + bool listener2Called = false; + bool listener3Called = false; + + Listener listener1 { [&]{ listener1Called = true; } }; + Listener listener2 { [&]{ listener2Called = true; } }; + Listener listener3 { [&]{ listener3Called = true; } }; + + listeners.add (&listener1); + listeners.add (&listener2); + listeners.add (&listener3); + + struct BailOutChecker + { + bool& bailOutBool; + bool shouldBailOut() const { return bailOutBool; } + }; + + BailOutChecker bailOutChecker { listener2Called }; + listeners.callChecked (bailOutChecker, &Listener::notify); + + expect ( listener1Called); + expect ( listener2Called); + expect (! listener3Called); + } + + beginTest ("Using a critical section"); + { + struct Listener + { + std::function onCallback; + void notify() { onCallback(); } + }; + + struct TestCriticalSection + { + TestCriticalSection() { isAlive() = true; } + ~TestCriticalSection() { isAlive() = false; } + + static void enter() noexcept { numOutOfScopeCalls() += isAlive() ? 0 : 1; } + static void exit() noexcept { numOutOfScopeCalls() += isAlive() ? 0 : 1; } + + static bool tryEnter() noexcept + { + numOutOfScopeCalls() += isAlive() ? 0 : 1; + return true; + } + + using ScopedLockType = GenericScopedLock; + + static bool& isAlive() + { + static bool inScope = false; + return inScope; + } + + static int& numOutOfScopeCalls() + { + static int numOutOfScopeCalls = 0; + return numOutOfScopeCalls; + } + }; + + auto listeners = std::make_unique>>(); + + const auto callback = [&]{ listeners.reset(); }; + + Listener listener { callback }; + + listeners->add (&listener); + listeners->call (&Listener::notify); + + expect (listeners == nullptr); + expect (TestCriticalSection::numOutOfScopeCalls() == 0); + } + + beginTest ("Adding a listener during a callback when one has already been removed"); + { + struct Listener{}; + + ListenerList listeners; + expect (listeners.size() == 0); + + Listener listener; + listeners.add (&listener); + expect (listeners.size() == 1); + + bool listenerCalled = false; + + listeners.call ([&] (auto& l) + { + listeners.remove (&l); + expect (listeners.size() == 0); + + listeners.add (&l); + expect (listeners.size() == 1); + + listenerCalled = true; + }); + + expect (listenerCalled); + expect (listeners.size() == 1); + } + } + +private: + static std::set chooseUnique (Random& random, int max, int numChosen) + { + std::set result; + + while ((int) result.size() < numChosen) + result.insert (random.nextInt ({ 0, max })); + + return result; + } +}; + +static ListenerListTests listenerListTests; + +#endif + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.h b/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.h index 09daea8d..b15d86d6 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,11 +25,12 @@ namespace juce //============================================================================== /** - Holds a set of objects and can invoke a member function callback on each object - in the set with a single call. + Holds a set of objects and can invoke a member function callback on each + object in the set with a single call. Use a ListenerList to manage a set of objects which need a callback, and you - can invoke a member function by simply calling call() or callChecked(). + can invoke a member function by simply calling call(), callChecked(), or + callExcluding(). E.g. @code @@ -47,19 +48,30 @@ namespace juce listeners.call ([] (MyListenerType& l) { l.myCallbackMethod (1234, true); }); @endcode - If you add or remove listeners from the list during one of the callbacks - i.e. while - it's in the middle of iterating the listeners, then it's guaranteed that no listeners - will be mistakenly called after they've been removed, but it may mean that some of the - listeners could be called more than once, or not at all, depending on the list's order. + It is safe to add listeners, remove listeners, clear the listeners, and + delete the ListenerList itself during any listener callback. - Sometimes, there's a chance that invoking one of the callbacks might result in the - list itself being deleted while it's still iterating - to survive this situation, you can - use callChecked() instead of call(), passing it a local object to act as a "BailOutChecker". - The BailOutChecker must implement a method of the form "bool shouldBailOut()", and - the list will check this after each callback to determine whether it should abort the - operation. For an example of a bail-out checker, see the Component::BailOutChecker class, - which can be used to check when a Component has been deleted. See also - ListenerList::DummyBailOutChecker, which is a dummy checker that always returns false. + If a Listener is added during a callback, it is guaranteed not to be called + in the same iteration. + + If a Listener is removed during a callback, it is guaranteed not to be + called if it hasn't already been called. + + If the ListenerList is cleared or deleted during a callback, it is + guaranteed that no more listeners will be called. + + By default a ListenerList is not thread safe. If thread-safety is required, + you can provide a thread-safe Array type as the second type parameter e.g. + @code + using ThreadSafeList = ListenerList>; + @endcode + + When calling listeners the iteration can be escaped early by using a + "BailOutChecker". A BailOutChecker is a type that has a public member function + with the following signature: + @code bool shouldBailOut() const @endcode + This function will be called before making a call to each listener. + For an example see the DummyBailOutChecker. @tags{Core} */ @@ -73,240 +85,261 @@ class ListenerList ListenerList() = default; /** Destructor. */ - ~ListenerList() = default; + ~ListenerList() { clear(); } //============================================================================== /** Adds a listener to the list. A listener can only be added once, so if the listener is already in the list, this method has no effect. + + If a Listener is added during a callback, it is guaranteed not to be called + in the same iteration. + @see remove */ void add (ListenerClass* listenerToAdd) { if (listenerToAdd != nullptr) - listeners.addIfNotAlreadyThere (listenerToAdd); + listeners->addIfNotAlreadyThere (listenerToAdd); else - jassertfalse; // Listeners can't be null pointers! + jassertfalse; // Listeners can't be null pointers! } /** Removes a listener from the list. If the listener wasn't in the list, this has no effect. + + If a Listener is removed during a callback, it is guaranteed not to be + called if it hasn't already been called. */ void remove (ListenerClass* listenerToRemove) { jassert (listenerToRemove != nullptr); // Listeners can't be null pointers! - listeners.removeFirstMatchingValue (listenerToRemove); + + const ScopedLockType lock (listeners->getLock()); + + if (const auto index = listeners->removeFirstMatchingValue (listenerToRemove); index >= 0) + { + for (auto* it : *iterators) + { + --it->end; + + if (index <= it->index) + --it->index; + } + } + } + + /** Adds a listener that will be automatically removed again when the Guard is destroyed. + + Be very careful to ensure that the ErasedScopeGuard is destroyed or released before the + ListenerList is destroyed, otherwise the ErasedScopeGuard may attempt to dereference a + dangling pointer when it is destroyed, which will result in a crash. + */ + ErasedScopeGuard addScoped (ListenerClass& listenerToAdd) + { + add (&listenerToAdd); + return ErasedScopeGuard { [this, &listenerToAdd] { remove (&listenerToAdd); } }; } /** Returns the number of registered listeners. */ - int size() const noexcept { return listeners.size(); } + int size() const noexcept { return listeners->size(); } /** Returns true if no listeners are registered, false otherwise. */ - bool isEmpty() const noexcept { return listeners.isEmpty(); } + bool isEmpty() const noexcept { return listeners->isEmpty(); } + + /** Clears the list. + + If the ListenerList is cleared during a callback, it is guaranteed that + no more listeners will be called. + */ + void clear() + { + const ScopedLockType lock (listeners->getLock()); - /** Clears the list. */ - void clear() { listeners.clear(); } + listeners->clear(); + + for (auto* it : *iterators) + it->end = 0; + } /** Returns true if the specified listener has been added to the list. */ - bool contains (ListenerClass* listener) const noexcept { return listeners.contains (listener); } + bool contains (ListenerClass* listener) const noexcept { return listeners->contains (listener); } + + /** Returns the raw array of listeners. + + Any attempt to mutate the array may result in undefined behaviour. + + If the array uses a mutex/CriticalSection, reading from the array without first + obtaining the lock may potentially result in undefined behaviour. - /** Returns the raw array of listeners. */ - const ArrayType& getListeners() const noexcept { return listeners; } + @see add, remove, clear, contains + */ + const ArrayType& getListeners() const noexcept { return *listeners; } //============================================================================== - /** Calls a member function on each listener in the list, with multiple parameters. */ + /** Calls an invokable object for each listener in the list. */ template void call (Callback&& callback) { - typename ArrayType::ScopedLockType lock (listeners.getLock()); - - for (Iterator iter (*this); iter.next();) - callback (*iter.getListener()); + callCheckedExcluding (nullptr, + DummyBailOutChecker{}, + std::forward (callback)); } - /** Calls a member function with 1 parameter, on all but the specified listener in the list. - This can be useful if the caller is also a listener and needs to exclude itself. + /** Calls an invokable object for each listener in the list, except for the + listener specified by listenerToExclude. */ template void callExcluding (ListenerClass* listenerToExclude, Callback&& callback) { - typename ArrayType::ScopedLockType lock (listeners.getLock()); - - for (Iterator iter (*this); iter.next();) - { - auto* l = iter.getListener(); + callCheckedExcluding (listenerToExclude, + DummyBailOutChecker{}, + std::forward (callback)); - if (l != listenerToExclude) - callback (*l); - } } - /** Calls a member function on each listener in the list, with 1 parameter and a bail-out-checker. + /** Calls an invokable object for each listener in the list, additionally + checking the bail-out checker before each call. + See the class description for info about writing a bail-out checker. */ template void callChecked (const BailOutCheckerType& bailOutChecker, Callback&& callback) { - typename ArrayType::ScopedLockType lock (listeners.getLock()); - - for (Iterator iter (*this); iter.next (bailOutChecker);) - callback (*iter.getListener()); + callCheckedExcluding (nullptr, + bailOutChecker, + std::forward (callback)); } - /** Calls a member function, with 1 parameter, on all but the specified listener in the list - with a bail-out-checker. This can be useful if the caller is also a listener and needs to - exclude itself. See the class description for info about writing a bail-out checker. + /** Calls an invokable object for each listener in the list, except for the + listener specified by listenerToExclude, additionally checking the + bail-out checker before each call. + + See the class description for info about writing a bail-out checker. */ template void callCheckedExcluding (ListenerClass* listenerToExclude, const BailOutCheckerType& bailOutChecker, Callback&& callback) { - typename ArrayType::ScopedLockType lock (listeners.getLock()); - - for (Iterator iter (*this); iter.next (bailOutChecker);) - { - auto* l = iter.getListener(); - - if (l != listenerToExclude) - callback (*l); - } - } - - //============================================================================== - /** A dummy bail-out checker that always returns false. - See the ListenerList notes for more info about bail-out checkers. - */ - struct DummyBailOutChecker - { - bool shouldBailOut() const noexcept { return false; } - }; + const auto localListeners = listeners; + const ScopedLockType lock { localListeners->getLock() }; - using ThisType = ListenerList; - using ListenerType = ListenerClass; + Iterator it{}; + it.end = localListeners->size(); - //============================================================================== - /** Iterates the listeners in a ListenerList. */ - template - struct Iterator - { - Iterator (const ListType& listToIterate) noexcept - : list (listToIterate), index (listToIterate.size()) - {} - - ~Iterator() = default; + iterators->push_back (&it); - //============================================================================== - bool next() noexcept + const ScopeGuard scope { [i = iterators, &it] { - if (index <= 0) - return false; - - auto listSize = list.size(); + i->erase (std::remove (i->begin(), i->end(), &it), i->end()); + } }; - if (--index < listSize) - return true; - - index = listSize - 1; - return index >= 0; - } - - bool next (const BailOutCheckerType& bailOutChecker) noexcept + for (; it.index < it.end; ++it.index) { - return (! bailOutChecker.shouldBailOut()) && next(); - } - - typename ListType::ListenerType* getListener() const noexcept - { - return list.getListeners().getUnchecked (index); - } - - //============================================================================== - private: - const ListType& list; - int index; - - JUCE_DECLARE_NON_COPYABLE (Iterator) - }; - - //============================================================================== - #ifndef DOXYGEN - // There are now lambda-based call functions that can be used to replace these old method-based versions. - // We'll eventually deprecate these old ones, so please begin moving your code to use lambdas! - void call (void (ListenerClass::*callbackFunction) ()) - { - call ([=] (ListenerClass& l) { (l.*callbackFunction)(); }); - } + if (bailOutChecker.shouldBailOut()) + return; - void callExcluding (ListenerClass* listenerToExclude, void (ListenerClass::*callbackFunction) ()) - { - callExcluding (listenerToExclude, [=] (ListenerClass& l) { (l.*callbackFunction)(); }); - } + auto* listener = localListeners->getUnchecked (it.index); - template - void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) ()) - { - callChecked (bailOutChecker, [=] (ListenerClass& l) { (l.*callbackFunction)(); }); - } + if (listener == listenerToExclude) + continue; - template - void callCheckedExcluding (ListenerClass* listenerToExclude, - const BailOutCheckerType& bailOutChecker, - void (ListenerClass::*callbackFunction) ()) - { - callCheckedExcluding (listenerToExclude, bailOutChecker, [=] (ListenerClass& l) { (l.*callbackFunction)(); }); + callback (*listener); + } } + //============================================================================== + /** Calls a specific listener method for each listener in the list. */ template void call (void (ListenerClass::*callbackFunction) (MethodArgs...), Args&&... args) { - typename ArrayType::ScopedLockType lock (listeners.getLock()); - - for (Iterator iter (*this); iter.next();) - (iter.getListener()->*callbackFunction) (static_cast::type> (args)...); + callCheckedExcluding (nullptr, + DummyBailOutChecker{}, + callbackFunction, + std::forward (args)...); } + /** Calls a specific listener method for each listener in the list, except + for the listener specified by listenerToExclude. + */ template void callExcluding (ListenerClass* listenerToExclude, void (ListenerClass::*callbackFunction) (MethodArgs...), Args&&... args) { - typename ArrayType::ScopedLockType lock (listeners.getLock()); - - for (Iterator iter (*this); iter.next();) - if (iter.getListener() != listenerToExclude) - (iter.getListener()->*callbackFunction) (static_cast::type> (args)...); + callCheckedExcluding (listenerToExclude, + DummyBailOutChecker{}, + callbackFunction, + std::forward (args)...); } + /** Calls a specific listener method for each listener in the list, + additionally checking the bail-out checker before each call. + + See the class description for info about writing a bail-out checker. + */ template void callChecked (const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (MethodArgs...), Args&&... args) { - typename ArrayType::ScopedLockType lock (listeners.getLock()); - - for (Iterator iter (*this); iter.next (bailOutChecker);) - (iter.getListener()->*callbackFunction) (static_cast::type> (args)...); + callCheckedExcluding (nullptr, + bailOutChecker, + callbackFunction, + std::forward (args)...); } + /** Calls a specific listener method for each listener in the list, except + for the listener specified by listenerToExclude, additionally checking + the bail-out checker before each call. + + See the class description for info about writing a bail-out checker. + */ template void callCheckedExcluding (ListenerClass* listenerToExclude, const BailOutCheckerType& bailOutChecker, void (ListenerClass::*callbackFunction) (MethodArgs...), Args&&... args) { - typename ArrayType::ScopedLockType lock (listeners.getLock()); - - for (Iterator iter (*this); iter.next (bailOutChecker);) - if (iter.getListener() != listenerToExclude) - (iter.getListener()->*callbackFunction) (static_cast::type> (args)...); + callCheckedExcluding (listenerToExclude, bailOutChecker, [&] (ListenerClass& l) + { + (l.*callbackFunction) (args...); + }); } - #endif + + //============================================================================== + /** A dummy bail-out checker that always returns false. + See the class description for info about writing a bail-out checker. + */ + struct DummyBailOutChecker + { + constexpr bool shouldBailOut() const noexcept { return false; } + }; + + //============================================================================== + using ThisType = ListenerList; + using ListenerType = ListenerClass; private: //============================================================================== - ArrayType listeners; + using ScopedLockType = typename ArrayType::ScopedLockType; + + //============================================================================== + using SharedListeners = std::shared_ptr; + const SharedListeners listeners = std::make_shared(); + struct Iterator + { + int index{}; + int end{}; + }; + + using SafeIterators = std::vector; + using SharedIterators = std::shared_ptr; + const SharedIterators iterators = std::make_shared(); + + //============================================================================== JUCE_DECLARE_NON_COPYABLE (ListenerList) }; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp index be886ab5..06ce64c2 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -96,9 +96,9 @@ bool NamedValueSet::operator== (const NamedValueSet& other) const noexcept for (int i = 0; i < num; ++i) { // optimise for the case where the keys are in the same order - if (values.getReference(i).name == other.values.getReference(i).name) + if (values.getReference (i).name == other.values.getReference (i).name) { - if (values.getReference(i).value != other.values.getReference(i).value) + if (values.getReference (i).value != other.values.getReference (i).value) return false; } else @@ -106,8 +106,8 @@ bool NamedValueSet::operator== (const NamedValueSet& other) const noexcept // if we encounter keys that are in a different order, search remaining items by brute force.. for (int j = i; j < num; ++j) { - if (auto* otherVal = other.getVarPointer (values.getReference(j).name)) - if (values.getReference(j).value == *otherVal) + if (auto* otherVal = other.getVarPointer (values.getReference (j).name)) + if (values.getReference (j).value == *otherVal) continue; return false; @@ -205,7 +205,7 @@ int NamedValueSet::indexOf (const Identifier& name) const noexcept auto numValues = values.size(); for (int i = 0; i < numValues; ++i) - if (values.getReference(i).name == name) + if (values.getReference (i).name == name) return i; return -1; @@ -217,7 +217,7 @@ bool NamedValueSet::remove (const Identifier& name) for (int i = 0; i < numValues; ++i) { - if (values.getReference(i).name == name) + if (values.getReference (i).name == name) { values.remove (i); return true; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h index c1534b7b..edaec584 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Optional.h b/JuceLibraryCode/modules/juce_core/containers/juce_Optional.h new file mode 100644 index 00000000..c6764c40 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Optional.h @@ -0,0 +1,180 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +using Nullopt = std::nullopt_t; +constexpr auto nullopt = std::nullopt; + +// Without this, our tests can emit "unreachable code" warnings during +// link time code generation. +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4702) + +#ifndef DOXYGEN +#define JUCE_OPTIONAL_OPERATORS X(==) X(!=) X(<) X(<=) X(>) X(>=) +#endif + +/** + A simple optional type. + + In new code, you should probably prefer using std::optional directly. + + This is intended to stand-in for std::optional while JUCE's minimum + supported language standard is lower than C++17. When the minimum language + standard moves to C++17, this class will probably be deprecated, in much + the same way that juce::ScopedPointer was deprecated in favour of + std::unique_ptr after C++11. + + This isn't really intended to be used by JUCE clients. Instead, it's to be + used internally in JUCE code, with an API close-enough to std::optional + that the types can be swapped with fairly minor disruption at some point in + the future, but *without breaking any public APIs*. + + @tags{Core} +*/ +template +class Optional +{ + template struct IsOptional : std::false_type {}; + template struct IsOptional> : std::true_type {}; + +public: + Optional() = default; + Optional (const Optional&) = default; + Optional (Optional&&) = default; + Optional& operator= (const Optional&) = default; + Optional& operator= (Optional&&) = default; + + Optional (Nullopt) noexcept {} + + template >::value, int> = 0> + Optional (Head&& head, Tail&&... tail) + noexcept (std::is_nothrow_constructible_v, Head, Tail...>) + : opt (std::forward (head), std::forward (tail)...) {} + + template + Optional (const Optional& other) + noexcept (std::is_nothrow_constructible_v, const std::optional&>) + : opt (other.opt) {} + + template + Optional (Optional&& other) + noexcept (std::is_nothrow_constructible_v, std::optional&&>) + : opt (std::move (other.opt)) {} + + template >::value, int> = 0> + Optional& operator= (Other&& other) + noexcept (std::is_nothrow_assignable_v, Other>) + { + opt = std::forward (other); + return *this; + } + + template + Optional& operator= (const Optional& other) + noexcept (std::is_nothrow_assignable_v, const std::optional&>) + { + opt = other.opt; + return *this; + } + + template + Optional& operator= (Optional&& other) + noexcept (std::is_nothrow_assignable_v, std::optional&&>) + { + opt = std::move (other.opt); + return *this; + } + + template + auto& emplace (Other&&... other) + { + return opt.emplace (std::forward (other)...); + } + + void reset() noexcept + { + opt.reset(); + } + + void swap (Optional& other) + noexcept (std::is_nothrow_swappable_v>) + { + opt.swap (other.opt); + } + + decltype (auto) operator->() { return opt.operator->(); } + decltype (auto) operator->() const { return opt.operator->(); } + decltype (auto) operator* () { return opt.operator* (); } + decltype (auto) operator* () const { return opt.operator* (); } + + explicit operator bool() const noexcept { return opt.has_value(); } + bool hasValue() const noexcept { return opt.has_value(); } + + template + decltype (auto) orFallback (U&& fallback) const& { return opt.value_or (std::forward (fallback)); } + + template + decltype (auto) orFallback (U&& fallback) & { return opt.value_or (std::forward (fallback)); } + + #define X(op) \ + template friend bool operator op (const Optional&, const Optional&); \ + template friend bool operator op (const Optional&, Nullopt); \ + template friend bool operator op (Nullopt, const Optional&); \ + template friend bool operator op (const Optional&, const U&); \ + template friend bool operator op (const T&, const Optional&); + + JUCE_OPTIONAL_OPERATORS + + #undef X + +private: + template + friend class Optional; + + std::optional opt; +}; + +JUCE_END_IGNORE_WARNINGS_MSVC + +template +Optional> makeOptional (Value&& v) +{ + return std::forward (v); +} + +#ifndef DOXYGEN +#define X(op) \ + template bool operator op (const Optional& lhs, const Optional& rhs) { return lhs.opt op rhs.opt; } \ + template bool operator op (const Optional& lhs, Nullopt rhs) { return lhs.opt op rhs; } \ + template bool operator op (Nullopt lhs, const Optional& rhs) { return lhs op rhs.opt; } \ + template bool operator op (const Optional& lhs, const U& rhs) { return lhs.opt op rhs; } \ + template bool operator op (const T& lhs, const Optional& rhs) { return lhs op rhs.opt; } + +JUCE_OPTIONAL_OPERATORS + +#undef X +#undef JUCE_OPTIONAL_OPERATORS +#endif + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Optional_test.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_Optional_test.cpp new file mode 100644 index 00000000..f49e467a --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Optional_test.cpp @@ -0,0 +1,628 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +/* Not nested, so that ADL works for the swap function. */ +struct ThrowOnMoveOrSwap +{ + ThrowOnMoveOrSwap() = default; + ThrowOnMoveOrSwap (ThrowOnMoveOrSwap&&) { throw std::bad_alloc{}; } +}; +static void swap (ThrowOnMoveOrSwap&, ThrowOnMoveOrSwap&) { throw std::bad_alloc{}; } + +class OptionalUnitTest final : public UnitTest +{ +public: + OptionalUnitTest() : UnitTest ("Optional", UnitTestCategories::containers) {} + + void runTest() override + { + beginTest ("Default-constructed optional is invalid"); + { + Optional o; + expect (! o.hasValue()); + } + + beginTest ("Constructing from Nullopt is invalid"); + { + Optional o (nullopt); + expect (! o.hasValue()); + } + + beginTest ("Optional constructed from value is valid"); + { + Optional o = 5; + expect (o.hasValue()); + expectEquals (*o, 5); + } + + using Ptr = std::shared_ptr; + const auto makePtr = [] { return std::make_shared(); }; + + beginTest ("Constructing from a moved optional calls appropriate member functions"); + { + auto ptr = makePtr(); + Optional original (ptr); + expect (ptr.use_count() == 2); + auto other = std::move (original); + // A moved-from optional still contains a value! + expect (original.hasValue()); + expect (other.hasValue()); + expect (ptr.use_count() == 2); + } + + beginTest ("Moving an empty optional to a populated one destroys the instance"); + { + auto ptr = makePtr(); + Optional original (ptr); + expect (ptr.use_count() == 2); + original = Optional(); + expect (ptr.use_count() == 1); + } + + beginTest ("Copying an empty optional to a populated one destroys the instance"); + { + auto ptr = makePtr(); + Optional original (ptr); + expect (ptr.use_count() == 2); + Optional empty; + original = empty; + expect (ptr.use_count() == 1); + } + + beginTest ("Moving a populated optional calls appropriate member functions"); + { + auto a = makePtr(); + auto b = makePtr(); + + Optional aOpt (a); + Optional bOpt (b); + + expect (a.use_count() == 2); + expect (b.use_count() == 2); + + aOpt = std::move (bOpt); + + expect (aOpt.hasValue()); + expect (bOpt.hasValue()); + + expect (a.use_count() == 1); + expect (b.use_count() == 2); + } + + beginTest ("Copying a populated optional calls appropriate member functions"); + { + auto a = makePtr(); + auto b = makePtr(); + + Optional aOpt (a); + Optional bOpt (b); + + expect (a.use_count() == 2); + expect (b.use_count() == 2); + + aOpt = bOpt; + + expect (aOpt.hasValue()); + expect (bOpt.hasValue()); + + expect (a.use_count() == 1); + expect (b.use_count() == 3); + } + + beginTest ("Moving an empty optional to an empty one does nothing"); + { + Optional original; + original = Optional(); + expect (! original.hasValue()); + } + + beginTest ("Copying an empty optional to an empty one does nothing"); + { + Optional original; + Optional empty; + original = empty; + expect (! original.hasValue()); + expect (! empty.hasValue()); + } + + beginTest ("Moving a populated optional calls appropriate member functions"); + { + auto a = makePtr(); + + Optional aOpt (a); + Optional empty; + + expect (a.use_count() == 2); + + empty = std::move (aOpt); + + expect (empty.hasValue()); + expect (aOpt.hasValue()); + + expect (a.use_count() == 2); + } + + beginTest ("Copying a populated optional calls appropriate member functions"); + { + auto a = makePtr(); + + Optional aOpt (a); + Optional empty; + + expect (a.use_count() == 2); + + empty = aOpt; + + expect (aOpt.hasValue()); + expect (empty.hasValue()); + + expect (a.use_count() == 3); + } + + struct ThrowOnCopy + { + ThrowOnCopy() = default; + + // Put into an invalid state and throw + ThrowOnCopy (const ThrowOnCopy&) + { + value = -100; + throw std::bad_alloc{}; + } + + // Put into an invalid state and throw + ThrowOnCopy& operator= (const ThrowOnCopy&) + { + value = -100; + throw std::bad_alloc{}; + } + + ThrowOnCopy (ThrowOnCopy&&) noexcept = default; + ThrowOnCopy& operator= (ThrowOnCopy&&) noexcept = default; + + ~ThrowOnCopy() = default; + + int value = 0; + }; + + beginTest ("Strong exception safety is maintained when forwarding over empty object"); + { + bool threw = false; + Optional a; + + try + { + ThrowOnCopy t; + a = t; + } + catch (const std::bad_alloc&) + { + threw = true; + } + + expect (threw); + expect (! a.hasValue()); // If construction failed, this object should still be well-formed but empty + } + + beginTest ("Weak exception safety is maintained when forwarding over populated object"); + { + bool threw = false; + Optional a = ThrowOnCopy(); + a->value = 5; + + try + { + ThrowOnCopy t; + a = t; + } + catch (const std::bad_alloc&) + { + threw = true; + } + + expect (threw); + expect (a.hasValue()); + expect (a->value == -100); // If we assign to an extant object, it's up to that object to provide an exception guarantee + } + + beginTest ("Strong exception safety is maintained when copying over empty object"); + { + bool threw = false; + Optional a; + + try + { + Optional t = ThrowOnCopy{}; + a = t; + } + catch (const std::bad_alloc&) + { + threw = true; + } + + expect (threw); + expect (! a.hasValue()); + } + + beginTest ("Exception safety of contained type is maintained when copying over populated object"); + { + bool threw = false; + Optional a = ThrowOnCopy(); + a->value = 5; + + try + { + Optional t = ThrowOnCopy{}; + a = t; + } + catch (const std::bad_alloc&) + { + threw = true; + } + + expect (threw); + expect (a.hasValue()); + expect (a->value == -100); + } + + beginTest ("Assigning from nullopt clears the instance"); + { + auto ptr = makePtr(); + Optional a (ptr); + expect (ptr.use_count() == 2); + a = nullopt; + expect (ptr.use_count() == 1); + } + + struct Foo {}; + struct Bar final : public Foo {}; + + beginTest ("Can be constructed from compatible type"); + { + Optional> opt { std::make_shared() }; + } + + beginTest ("Can be assigned from compatible type"); + { + Optional> opt; + opt = std::make_shared(); + } + + beginTest ("Can copy from compatible type"); + { + auto ptr = std::make_shared(); + Optional> bar (ptr); + Optional> foo (bar); + expect (ptr.use_count() == 3); + } + + beginTest ("Can move from compatible type"); + { + auto ptr = std::make_shared(); + Optional> foo (Optional> { ptr }); + expect (ptr.use_count() == 2); + } + + beginTest ("Can copy assign from compatible type"); + { + auto ptr = std::make_shared(); + Optional> bar (ptr); + Optional> foo; + foo = bar; + expect (ptr.use_count() == 3); + } + + beginTest ("Can move assign from compatible type"); + { + auto ptr = std::make_shared(); + Optional> foo; + foo = Optional> (ptr); + expect (ptr.use_count() == 2); + } + + beginTest ("An exception thrown during emplace leaves the optional without a value"); + { + Optional opt { ThrowOnCopy{} }; + bool threw = false; + + try + { + ThrowOnCopy t; + opt.emplace (t); + } + catch (const std::bad_alloc&) + { + threw = true; + } + + expect (threw); + expect (! opt.hasValue()); + } + + beginTest ("Swap does nothing to two empty optionals"); + { + Optional a, b; + expect (! a.hasValue()); + expect (! b.hasValue()); + + a.swap (b); + + expect (! a.hasValue()); + expect (! b.hasValue()); + } + + beginTest ("Swap transfers ownership if one optional contains a value"); + { + { + Ptr ptr = makePtr(); + Optional a, b = ptr; + expect (! a.hasValue()); + expect (b.hasValue()); + expect (ptr.use_count() == 2); + + a.swap (b); + + expect (a.hasValue()); + expect (! b.hasValue()); + expect (ptr.use_count() == 2); + } + + { + auto ptr = makePtr(); + Optional a = ptr, b; + expect (a.hasValue()); + expect (! b.hasValue()); + expect (ptr.use_count() == 2); + + a.swap (b); + + expect (! a.hasValue()); + expect (b.hasValue()); + expect (ptr.use_count() == 2); + } + } + + beginTest ("Swap calls std::swap to swap two populated optionals"); + { + auto x = makePtr(), y = makePtr(); + Optional a = x, b = y; + expect (a.hasValue()); + expect (b.hasValue()); + expect (x.use_count() == 2); + expect (y.use_count() == 2); + expect (*a == x); + expect (*b == y); + + a.swap (b); + + expect (a.hasValue()); + expect (b.hasValue()); + expect (x.use_count() == 2); + expect (y.use_count() == 2); + expect (*a == y); + expect (*b == x); + } + + beginTest ("An exception thrown during a swap leaves both objects in the previous populated state"); + { + { + Optional a, b; + a.emplace(); + + expect (a.hasValue()); + expect (! b.hasValue()); + + bool threw = false; + + try + { + a.swap (b); + } + catch (const std::bad_alloc&) + { + threw = true; + } + + expect (threw); + expect (a.hasValue()); + expect (! b.hasValue()); + } + + { + Optional a, b; + b.emplace(); + + expect (! a.hasValue()); + expect (b.hasValue()); + + bool threw = false; + + try + { + a.swap (b); + } + catch (const std::bad_alloc&) + { + threw = true; + } + + expect (threw); + expect (! a.hasValue()); + expect (b.hasValue()); + } + + { + Optional a, b; + a.emplace(); + b.emplace(); + + expect (a.hasValue()); + expect (b.hasValue()); + + bool threw = false; + + try + { + a.swap (b); + } + catch (const std::bad_alloc&) + { + threw = true; + } + + expect (threw); + expect (a.hasValue()); + expect (b.hasValue()); + } + } + + beginTest ("Relational tests"); + { + expect (Optional (1) == Optional (1)); + expect (Optional() == Optional()); + expect (! (Optional (1) == Optional())); + expect (! (Optional() == Optional (1))); + expect (! (Optional (1) == Optional (2))); + + expect (Optional (1) != Optional (2)); + expect (! (Optional() != Optional())); + expect (Optional (1) != Optional()); + expect (Optional() != Optional (1)); + expect (! (Optional (1) != Optional (1))); + + expect (Optional() < Optional (1)); + expect (! (Optional (1) < Optional())); + expect (! (Optional() < Optional())); + expect (Optional (1) < Optional (2)); + + expect (Optional() <= Optional (1)); + expect (! (Optional (1) <= Optional())); + expect (Optional() <= Optional()); + expect (Optional (1) <= Optional (2)); + + expect (! (Optional() > Optional (1))); + expect (Optional (1) > Optional()); + expect (! (Optional() > Optional())); + expect (! (Optional (1) > Optional (2))); + + expect (! (Optional() >= Optional (1))); + expect (Optional (1) >= Optional()); + expect (Optional() >= Optional()); + expect (! (Optional (1) >= Optional (2))); + + expect (Optional() == nullopt); + expect (! (Optional (1) == nullopt)); + expect (nullopt == Optional()); + expect (! (nullopt == Optional (1))); + + expect (! (Optional() != nullopt)); + expect (Optional (1) != nullopt); + expect (! (nullopt != Optional())); + expect (nullopt != Optional (1)); + + expect (! (Optional() < nullopt)); + expect (! (Optional (1) < nullopt)); + + expect (! (nullopt < Optional())); + expect (nullopt < Optional (1)); + + expect (Optional() <= nullopt); + expect (! (Optional (1) <= nullopt)); + + expect (nullopt <= Optional()); + expect (nullopt <= Optional (1)); + + expect (! (Optional() > nullopt)); + expect (Optional (1) > nullopt); + + expect (! (nullopt > Optional())); + expect (! (nullopt > Optional (1))); + + expect (Optional() >= nullopt); + expect (Optional (1) >= nullopt); + + expect (nullopt >= Optional()); + expect (! (nullopt >= Optional (1))); + + expect (! (Optional() == 5)); + expect (! (Optional (1) == 5)); + expect (Optional (1) == 1); + expect (! (5 == Optional())); + expect (! (5 == Optional (1))); + expect (1 == Optional (1)); + + expect (Optional() != 5); + expect (Optional (1) != 5); + expect (! (Optional (1) != 1)); + expect (5 != Optional()); + expect (5 != Optional (1)); + expect (! (1 != Optional (1))); + + expect (Optional() < 5); + expect (Optional (1) < 5); + expect (! (Optional (1) < 1)); + expect (! (Optional (1) < 0)); + + expect (! (5 < Optional())); + expect (! (5 < Optional (1))); + expect (! (1 < Optional (1))); + expect (0 < Optional (1)); + + expect (Optional() <= 5); + expect (Optional (1) <= 5); + expect (Optional (1) <= 1); + expect (! (Optional (1) <= 0)); + + expect (! (5 <= Optional())); + expect (! (5 <= Optional (1))); + expect (1 <= Optional (1)); + expect (0 <= Optional (1)); + + expect (! (Optional() > 5)); + expect (! (Optional (1) > 5)); + expect (! (Optional (1) > 1)); + expect (Optional (1) > 0); + + expect (5 > Optional()); + expect (5 > Optional (1)); + expect (! (1 > Optional (1))); + expect (! (0 > Optional (1))); + + expect (! (Optional() >= 5)); + expect (! (Optional (1) >= 5)); + expect (Optional (1) >= 1); + expect (Optional (1) >= 0); + + expect (5 >= Optional()); + expect (5 >= Optional (1)); + expect (1 >= Optional (1)); + expect (! (0 >= Optional (1))); + } + } +}; + +static OptionalUnitTest optionalUnitTest; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.cpp index e228eba9..87654c71 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -35,7 +35,7 @@ static struct OwnedArrayTest : public UnitTest JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Base) }; - struct Derived : Base + struct Derived final : public Base { Derived() = default; @@ -57,7 +57,9 @@ static struct OwnedArrayTest : public UnitTest { parent.expect (o != nullptr); parent.expect (o != this); - parent.expectEquals (o->data, 956); + + if (o != nullptr) + parent.expectEquals (o->data, 956); } } diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h index 6f37df19..e07619b6 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -46,7 +46,6 @@ namespace juce */ template - class OwnedArray { public: @@ -531,17 +530,7 @@ class OwnedArray @see add, sort, indexOfSorted */ template - 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()); - auto index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size()); - insert (index, newObject); - return index; - } + int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept; /** Finds the index of an object in the array, assuming that the array is sorted. @@ -556,33 +545,7 @@ class OwnedArray @see addSorted, sort */ template - 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 - ignoreUnused (comparator); - - const ScopedLockType lock (getLock()); - int s = 0, e = values.size(); - - while (s < e) - { - if (comparator.compareElements (objectToLookFor, values[s]) == 0) - return s; - - auto halfway = (s + e) / 2; - - if (halfway == s) - break; - - if (comparator.compareElements (objectToLookFor, values[halfway]) >= 0) - s = halfway; - else - e = halfway; - } - - return -1; - } + int indexOfSorted (ElementComparator& comparator, const ObjectClass* objectToLookFor) const noexcept; //============================================================================== /** Removes an object from the array. @@ -818,18 +781,7 @@ class OwnedArray @see sortArray, indexOfSorted */ template - void sort (ElementComparator& comparator, - 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 - ignoreUnused (comparator); - - const ScopedLockType lock (getLock()); - - if (size() > 1) - sortArray (comparator, values.begin(), 0, size() - 1, retainOrderOfEquivalentItems); - } + void sort (ElementComparator& comparator, bool retainOrderOfEquivalentItems = false) noexcept; //============================================================================== /** Returns the CriticalSection that locks this array. @@ -843,9 +795,9 @@ class OwnedArray //============================================================================== #ifndef DOXYGEN - // Note that the swapWithArray method has been replaced by a more flexible templated version, - // and renamed "swapWith" to be more consistent with the names used in other classes. - JUCE_DEPRECATED_WITH_BODY (void swapWithArray (OwnedArray& other) noexcept, { swapWith (other); }) + [[deprecated ("This method has been replaced by a more flexible templated version and renamed " + "to swapWith to be more consistent with the names used in other classes.")]] + void swapWithArray (OwnedArray& other) noexcept { swapWith (other); } #endif private: @@ -870,4 +822,57 @@ class OwnedArray JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray) }; +//============================================================================== +template +template +int OwnedArray::addSorted ( + [[maybe_unused]] ElementComparator& comparator, + ObjectClass* newObject) noexcept +{ + const ScopedLockType lock (getLock()); + auto index = findInsertIndexInSortedArray (comparator, values.begin(), newObject, 0, values.size()); + insert (index, newObject); + return index; +} + +template +template +int OwnedArray::indexOfSorted ( + [[maybe_unused]] ElementComparator& comparator, + const ObjectClass* objectToLookFor) const noexcept +{ + const ScopedLockType lock (getLock()); + int s = 0, e = values.size(); + + while (s < e) + { + if (comparator.compareElements (objectToLookFor, values[s]) == 0) + return s; + + auto halfway = (s + e) / 2; + + if (halfway == s) + break; + + if (comparator.compareElements (objectToLookFor, values[halfway]) >= 0) + s = halfway; + else + e = halfway; + } + + return -1; +} + +template +template +void OwnedArray::sort ( + [[maybe_unused]] ElementComparator& comparator, + bool retainOrderOfEquivalentItems) noexcept +{ + const ScopedLockType lock (getLock()); + + if (size() > 1) + sortArray (comparator, values.begin(), 0, size() - 1, retainOrderOfEquivalentItems); +} + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp index 88a46633..20ef7fa7 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -115,7 +115,7 @@ std::unique_ptr PropertySet::getXmlValue (StringRef keyName) const return parseXML (getValue (keyName)); } -void PropertySet::setValue (const String& keyName, const var& v) +void PropertySet::setValue (StringRef keyName, const var& v) { jassert (keyName.isNotEmpty()); // shouldn't use an empty key name! @@ -148,7 +148,7 @@ void PropertySet::removeValue (StringRef keyName) } } -void PropertySet::setValue (const String& keyName, const XmlElement* xml) +void PropertySet::setValue (StringRef keyName, const XmlElement* xml) { setValue (keyName, xml == nullptr ? var() : var (xml->toString (XmlElement::TextFormat().singleLine().withoutHeader()))); @@ -196,7 +196,7 @@ void PropertySet::restoreFromXml (const XmlElement& xml) const ScopedLock sl (lock); clear(); - forEachXmlChildElementWithTagName (xml, e, "VALUE") + for (auto* e : xml.getChildWithTagNameIterator ("VALUE")) { if (e->hasAttribute ("name") && e->hasAttribute ("val")) diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h index be405509..d4e1d437 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -104,8 +104,8 @@ class JUCE_API PropertySet /** Returns one of the properties as an XML element. - The result will a new XMLElement object that the caller must delete. If may return nullptr - if the key isn't found, or if the entry contains an string that isn't valid XML. + The result will a new XMLElement object. It may return nullptr if the key isn't found, + or if the entry contains an string that isn't valid XML. If the value isn't found in this set, then this will look for it in a fallback property set (if you've specified one with the setFallbackPropertySet() method), @@ -121,7 +121,7 @@ class JUCE_API PropertySet @param keyName the name of the property to set. (This mustn't be an empty string) @param value the new value to set it to */ - void setValue (const String& keyName, const var& value); + void setValue (StringRef keyName, const var& value); /** Sets a named property to an XML element. @@ -130,7 +130,7 @@ class JUCE_API PropertySet be set to an empty string @see getXmlValue */ - void setValue (const String& keyName, const XmlElement* xml); + void setValue (StringRef keyName, const XmlElement* xml); /** This copies all the values from a source PropertySet to this one. This won't remove any existing settings, it just adds any that it finds in the source set. diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.cpp index 5b09e770..26e774cc 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2018 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,7 +25,7 @@ namespace juce #if JUCE_UNIT_TESTS -class ReferenceCountedArrayTests : public UnitTest +class ReferenceCountedArrayTests final : public UnitTest { public: ReferenceCountedArrayTests() @@ -134,7 +134,7 @@ class ReferenceCountedArrayTests : public UnitTest JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestBaseObj) }; - struct TestDerivedObj : public TestBaseObj + struct TestDerivedObj final : public TestBaseObj { using Ptr = ReferenceCountedObjectPtr; @@ -143,7 +143,7 @@ class ReferenceCountedArrayTests : public UnitTest JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestDerivedObj) }; - struct DestructorObj : public ReferenceCountedObject + struct DestructorObj final : public ReferenceCountedObject { DestructorObj (ReferenceCountedArrayTests& p, ReferenceCountedArray& arr) @@ -158,7 +158,9 @@ class ReferenceCountedArrayTests : public UnitTest { parent.expect (o != nullptr); parent.expect (o != this); - parent.expectEquals (o->data, 374); + + if (o != nullptr) + parent.expectEquals (o->data, 374); } } diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h b/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h index 1e2df4e7..59998257 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -563,31 +563,7 @@ class ReferenceCountedArray @see addSorted, sort */ template - int indexOfSorted (ElementComparator& comparator, - const ObjectClass* objectToLookFor) const noexcept - { - ignoreUnused (comparator); - const ScopedLockType lock (getLock()); - int s = 0, e = values.size(); - - while (s < e) - { - if (comparator.compareElements (objectToLookFor, values[s]) == 0) - return s; - - auto halfway = (s + e) / 2; - - if (halfway == s) - break; - - if (comparator.compareElements (objectToLookFor, values[halfway]) >= 0) - s = halfway; - else - e = halfway; - } - - return -1; - } + int indexOfSorted (ElementComparator& comparator, const ObjectClass* objectToLookFor) const noexcept; //============================================================================== /** Removes an object from the array. @@ -828,16 +804,7 @@ class ReferenceCountedArray @see sortArray */ template - void sort (ElementComparator& comparator, - 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 - ignoreUnused (comparator); - - const ScopedLockType lock (getLock()); - sortArray (comparator, values.begin(), 0, values.size() - 1, retainOrderOfEquivalentItems); - } + void sort (ElementComparator& comparator, bool retainOrderOfEquivalentItems = false) noexcept; //============================================================================== /** Reduces the amount of storage being used by the array. @@ -876,9 +843,9 @@ class ReferenceCountedArray //============================================================================== #ifndef DOXYGEN - // Note that the swapWithArray method has been replaced by a more flexible templated version, - // and renamed "swapWith" to be more consistent with the names used in other classes. - JUCE_DEPRECATED_WITH_BODY (void swapWithArray (ReferenceCountedArray& other) noexcept, { swapWith (other); }) + [[deprecated ("This method has been replaced by a more flexible templated version and renamed " + "to swapWith to be more consistent with the names used in other classes.")]] + void swapWithArray (ReferenceCountedArray& other) noexcept { swapWith (other); } #endif private: @@ -904,4 +871,43 @@ class ReferenceCountedArray } }; +//============================================================================== +template +template +int ReferenceCountedArray::indexOfSorted ( + [[maybe_unused]] ElementComparator& comparator, + const ObjectClass* objectToLookFor) const noexcept +{ + const ScopedLockType lock (getLock()); + int s = 0, e = values.size(); + + while (s < e) + { + if (comparator.compareElements (objectToLookFor, values[s]) == 0) + return s; + + auto halfway = (s + e) / 2; + + if (halfway == s) + break; + + if (comparator.compareElements (objectToLookFor, values[halfway]) >= 0) + s = halfway; + else + e = halfway; + } + + return -1; +} + +template +template +void ReferenceCountedArray::sort ( + [[maybe_unused]] ElementComparator& comparator, + bool retainOrderOfEquivalentItems) noexcept +{ + const ScopedLockType lock (getLock()); + sortArray (comparator, values.begin(), 0, values.size() - 1, retainOrderOfEquivalentItems); +} + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ScopedValueSetter.h b/JuceLibraryCode/modules/juce_core/containers/juce_ScopedValueSetter.h index d4ff9f6d..f720440f 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ScopedValueSetter.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ScopedValueSetter.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_SingleThreadedAbstractFifo.h b/JuceLibraryCode/modules/juce_core/containers/juce_SingleThreadedAbstractFifo.h new file mode 100644 index 00000000..d3021358 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/containers/juce_SingleThreadedAbstractFifo.h @@ -0,0 +1,126 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +//============================================================================== +/** + Encapsulates the logic for a single-threaded FIFO. + + This might be useful for building buffers which can be written and read in + blocks of different sizes. For example, in an audio effect we might wish to + run some processing on fixed-size blocks of audio input, but the host may + provide input blocks of varying sizes. In this situation, we might want to + store the previous input in a buffer, and extract a fixed-size block + whenever there are enough samples available. The SingleThreadedAbstractFifo + implements logic suitable for this use-case. + + This class is quite similar to AbstractFifo, in that it only keeps track of + the current read/write locations. The user is responsible for providing the + actual buffer that will be read/written. + + The intended usage of this class is as follows: + - Create some backing storage in a vector, AudioBuffer etc. + - Construct a SingleThreadedAbstractFifo to manage the buffer, passing the + number of items in the buffer. + - Each time new input is ready, call write(), passing the number of items + you wish to write into the buffer. This function returns a pair of ranges + describing which indices in the backing storage should be written. + - Call getNumReadable() to find out how many items are ready to read from + the buffer. + - If there are enough items ready to read, call read(), passing the number + of items you require. This function returns a pair of ranges describing + which indices in the backing storage may be read. + + Unlike AbstractFifo, the SingleThreadedAbstractFifo is intended for use + from a single thread. It is not safe to call any non-const member function + of SingleThreadedAbstractFifo concurrently with any other member function. + + @see AbstractFifo + + @tags{Core} +*/ +class SingleThreadedAbstractFifo +{ +public: + /** Creates a SingleThreadedAbstractFifo with no size. */ + SingleThreadedAbstractFifo() = default; + + /** Creates a SingleThreadedAbstractFifo that can manage a buffer of the specified size. */ + explicit SingleThreadedAbstractFifo (int sizeIn) + : size (sizeIn) + { + // This class only works properly when the size is a power of two. + // Use nextPowerOfTwo() to find a good size, and ensure that your + // backing storage is the same size. + jassert (isPowerOfTwo (sizeIn)); + } + + /** Returns the number of unused elements present in the buffer. */ + int getRemainingSpace() const { return size - numReadable; } + + /** Returns the number of pending elements present in the buffer. */ + int getNumReadable() const { return numReadable; } + + /** Returns the size of the managed buffer. */ + int getSize() const { return size; } + + /** Returns two blocks in the buffer where new items may be written. + + Note that if the buffer is running low on free space, the sum of the lengths of + the returned ranges may be less than num! + */ + std::array, 2> write (int num) + { + const auto startPos = (readPos + numReadable) & (size - 1); + const auto maxToWrite = jmin (getRemainingSpace(), num); + const auto firstBlockSize = jmin (maxToWrite, size - startPos); + + numReadable += maxToWrite; + + return { { { startPos, startPos + firstBlockSize }, { 0, maxToWrite - firstBlockSize } } }; + } + + /** Returns two blocks in the buffer from which new items may be read. + + Note that if the buffer doesn't have the requested number of items available, + the sum of the lengths of the returned ranges may be less than num! + */ + std::array, 2> read (int num) + { + const auto startPos = readPos; + const auto maxToRead = jmin (numReadable, num); + const auto firstBlockSize = jmin (maxToRead, size - startPos); + + readPos = (startPos + maxToRead) & (size - 1); + numReadable -= maxToRead; + + return { { { startPos, startPos + firstBlockSize }, { 0, maxToRead - firstBlockSize } } }; + } + +private: + int size = 0, readPos = 0, numReadable = 0; +}; + + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h b/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h index af774373..889dac17 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,10 +23,7 @@ namespace juce { -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4512) -#endif +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4512) //============================================================================== /** @@ -380,7 +377,7 @@ class SortedSet @param valueToRemove the object to try to remove @see remove, removeRange */ - void removeValue (const ElementType valueToRemove) noexcept + void removeValue (const ElementType& valueToRemove) noexcept { const ScopedLockType lock (getLock()); data.remove (indexOf (valueToRemove)); @@ -487,8 +484,6 @@ class SortedSet Array data; }; -#if JUCE_MSVC - #pragma warning (pop) -#endif +JUCE_END_IGNORE_WARNINGS_MSVC } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Span.h b/JuceLibraryCode/modules/juce_core/containers/juce_Span.h new file mode 100644 index 00000000..693d4fcb --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Span.h @@ -0,0 +1,154 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +//============================================================================== +inline constexpr auto dynamicExtent = std::numeric_limits::max(); + +namespace detail +{ + //============================================================================== + template + constexpr auto hasToAddress = false; + + template + constexpr auto hasToAddress::to_address (std::declval()))>> = true; + + template + constexpr auto hasDataAndSize = false; + + template + constexpr auto hasDataAndSize())), + decltype (std::size (std::declval()))>> = true; + + template + struct NumBase + { + constexpr NumBase() = default; + + constexpr explicit NumBase (size_t) {} + + constexpr size_t size() const { return Extent; } + }; + + template <> + struct NumBase + { + constexpr NumBase() = default; + + constexpr explicit NumBase (size_t arg) + : num (arg) {} + + constexpr size_t size() const { return num; } + + size_t num{}; + }; + + template + constexpr T* toAddress (T* p) + { + return p; + } + + template + constexpr auto toAddress (const It& it) + { + if constexpr (detail::hasToAddress) + return std::pointer_traits::to_address (it); + else + return toAddress (it.operator->()); + } +} + +//============================================================================== +/** + A non-owning view over contiguous objects stored in an Array or vector + or other similar container. + + This is a bit like std::span from C++20, but with a more limited interface. + + @tags{Core} +*/ +template +class Span : private detail::NumBase // for empty-base optimisation +{ + using Base = detail::NumBase; + +public: + static constexpr auto extent = Extent; + + template = 0> + constexpr Span() {} + + template + constexpr Span (It it, size_t end) + : Base (end), ptr (detail::toAddress (it)) {} + + template , int> = 0> + constexpr Span (Range&& range) + : Base (std::size (range)), ptr (std::data (range)) {} + + constexpr Span (const Span&) = default; + + constexpr Span& operator= (const Span&) = default; + + constexpr Span (Span&&) noexcept = default; + + constexpr Span& operator= (Span&&) noexcept = default; + + using Base::size; + + constexpr Value* begin() const { return ptr; } + constexpr Value* end() const { return ptr + size(); } + + constexpr auto& front() const { return ptr[0]; } + constexpr auto& back() const { return ptr[size() - 1]; } + + constexpr auto& operator[] (size_t index) const { return ptr[index]; } + constexpr Value* data() const { return ptr; } + + constexpr bool empty() const { return size() == 0; } + +private: + Value* ptr = nullptr; +}; + +template +Span (T, End) -> Span()))>>; + +template +Span (T (&) [N]) -> Span; + +template +Span (std::array&) -> Span; + +template +Span (const std::array&) -> Span; + +template +Span (Range&& r) -> Span>; + + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.cpp index 61f3906d..7a1c05b9 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2018 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,7 +25,7 @@ namespace juce #if JUCE_UNIT_TESTS -class SparseSetTests : public UnitTest +class SparseSetTests final : public UnitTest { public: SparseSetTests() diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.h b/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.h index 0c2ae72d..687c6858 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -161,7 +161,7 @@ class SparseSet { for (int i = ranges.size(); --i >= 0;) { - auto& r = ranges.getReference(i); + auto& r = ranges.getReference (i); if (r.getEnd() <= rangeToRemove.getStart()) break; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp index 273027b4..0046c20e 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -37,215 +37,248 @@ enum VariantStreamMarkers }; //============================================================================== -class var::VariantType -{ -public: - VariantType() noexcept {} - virtual ~VariantType() noexcept {} - - virtual int toInt (const ValueUnion&) const noexcept { return 0; } - virtual int64 toInt64 (const ValueUnion&) const noexcept { return 0; } - virtual double toDouble (const ValueUnion&) const noexcept { return 0; } - virtual String toString (const ValueUnion&) const { return {}; } - virtual bool toBool (const ValueUnion&) const noexcept { return false; } - virtual ReferenceCountedObject* toObject (const ValueUnion&) const noexcept { return nullptr; } - virtual Array* toArray (const ValueUnion&) const noexcept { return nullptr; } - virtual MemoryBlock* toBinary (const ValueUnion&) const noexcept { return nullptr; } - virtual var clone (const var& original) const { return original; } - - virtual bool isVoid() const noexcept { return false; } - virtual bool isUndefined() const noexcept { return false; } - virtual bool isInt() const noexcept { return false; } - virtual bool isInt64() const noexcept { return false; } - virtual bool isBool() const noexcept { return false; } - virtual bool isDouble() const noexcept { return false; } - virtual bool isString() const noexcept { return false; } - virtual bool isObject() const noexcept { return false; } - virtual bool isArray() const noexcept { return false; } - virtual bool isBinary() const noexcept { return false; } - virtual bool isMethod() const noexcept { return false; } - virtual bool isComparable() const noexcept { return false; } - - virtual void cleanUp (ValueUnion&) const noexcept {} - virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest = source; } - virtual bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept = 0; - virtual void writeToStream (const ValueUnion& data, OutputStream& output) const = 0; -}; +struct var::VariantType +{ + struct VoidTag {}; + struct UndefinedTag {}; + struct IntTag {}; + struct Int64Tag {}; + struct DoubleTag {}; + struct BoolTag {}; + struct StringTag {}; + struct ObjectTag {}; + struct ArrayTag {}; + struct BinaryTag {}; + struct MethodTag {}; + + // members ===================================================================== + bool isVoid = false; + bool isUndefined = false; + bool isInt = false; + bool isInt64 = false; + bool isBool = false; + bool isDouble = false; + bool isString = false; + bool isObject = false; + bool isArray = false; + bool isBinary = false; + bool isMethod = false; + bool isComparable = false; + + int (*toInt) (const ValueUnion&) = defaultToInt; + int64 (*toInt64) (const ValueUnion&) = defaultToInt64; + double (*toDouble) (const ValueUnion&) = defaultToDouble; + String (*toString) (const ValueUnion&) = defaultToString; + bool (*toBool) (const ValueUnion&) = defaultToBool; + ReferenceCountedObject* (*toObject) (const ValueUnion&) = defaultToObject; + Array* (*toArray) (const ValueUnion&) = defaultToArray; + MemoryBlock* (*toBinary) (const ValueUnion&) = defaultToBinary; + var (*clone) (const var&) = defaultClone; + void (*cleanUp) (ValueUnion&) = defaultCleanUp; + void (*createCopy) (ValueUnion&, const ValueUnion&) = defaultCreateCopy; + + bool (*equals) (const ValueUnion&, const ValueUnion&, const VariantType&) = nullptr; + void (*writeToStream) (const ValueUnion&, OutputStream&) = nullptr; + + // defaults ==================================================================== + static int defaultToInt (const ValueUnion&) { return 0; } + static int64 defaultToInt64 (const ValueUnion&) { return 0; } + static double defaultToDouble (const ValueUnion&) { return 0; } + static String defaultToString (const ValueUnion&) { return {}; } + static bool defaultToBool (const ValueUnion&) { return false; } + static ReferenceCountedObject* defaultToObject (const ValueUnion&) { return nullptr; } + static Array* defaultToArray (const ValueUnion&) { return nullptr; } + static MemoryBlock* defaultToBinary (const ValueUnion&) { return nullptr; } + static var defaultClone (const var& other) { return other; } + static void defaultCleanUp (ValueUnion&) {} + static void defaultCreateCopy (ValueUnion& dest, const ValueUnion& source) { dest = source; } + + // void ======================================================================== + static bool voidEquals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) noexcept + { + return otherType.isVoid || otherType.isUndefined; + } -//============================================================================== -class var::VariantType_Void : public var::VariantType -{ -public: - VariantType_Void() noexcept {} - static const VariantType_Void instance; + static void voidWriteToStream (const ValueUnion&, OutputStream& output) + { + output.writeCompressedInt (0); + } - bool isVoid() const noexcept override { return true; } - bool isComparable() const noexcept override { return true; } - bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } - void writeToStream (const ValueUnion&, OutputStream& output) const override { output.writeCompressedInt (0); } -}; + constexpr explicit VariantType (VoidTag) noexcept + : isVoid (true), + isComparable (true), + equals (voidEquals), + writeToStream (voidWriteToStream) {} -//============================================================================== -class var::VariantType_Undefined : public var::VariantType -{ -public: - VariantType_Undefined() noexcept {} - static const VariantType_Undefined instance; + // undefined =================================================================== + static String undefinedToString (const ValueUnion&) { return "undefined"; } - bool isUndefined() const noexcept override { return true; } - String toString (const ValueUnion&) const override { return "undefined"; } - bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } + static bool undefinedEquals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) noexcept + { + return otherType.isVoid || otherType.isUndefined; + } - void writeToStream (const ValueUnion&, OutputStream& output) const override + static void undefinedWriteToStream (const ValueUnion&, OutputStream& output) { output.writeCompressedInt (1); output.writeByte (varMarker_Undefined); } -}; -//============================================================================== -class var::VariantType_Int : public var::VariantType -{ -public: - VariantType_Int() noexcept {} - static const VariantType_Int instance; + constexpr explicit VariantType (UndefinedTag) noexcept + : isUndefined (true), + toString (undefinedToString), + equals (undefinedEquals), + writeToStream (undefinedWriteToStream) {} - int toInt (const ValueUnion& data) const noexcept override { return data.intValue; } - int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.intValue; } - double toDouble (const ValueUnion& data) const noexcept override { return (double) data.intValue; } - String toString (const ValueUnion& data) const override { return String (data.intValue); } - bool toBool (const ValueUnion& data) const noexcept override { return data.intValue != 0; } - bool isInt() const noexcept override { return true; } - bool isComparable() const noexcept override { return true; } + // int ========================================================================= + static int intToInt (const ValueUnion& data) noexcept { return data.intValue; } + static int64 intToInt64 (const ValueUnion& data) noexcept { return (int64) data.intValue; } + static double intToDouble (const ValueUnion& data) noexcept { return (double) data.intValue; } + static String intToString (const ValueUnion& data) { return String (data.intValue); } + static bool intToBool (const ValueUnion& data) noexcept { return data.intValue != 0; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + static bool intEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { - if (otherType.isDouble() || otherType.isInt64() || otherType.isString()) - return otherType.equals (otherData, data, *this); + if (otherType.isDouble || otherType.isInt64 || otherType.isString) + return otherType.equals (otherData, data, VariantType { IntTag{} }); return otherType.toInt (otherData) == data.intValue; } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void intWriteToStream (const ValueUnion& data, OutputStream& output) { output.writeCompressedInt (5); output.writeByte (varMarker_Int); output.writeInt (data.intValue); } -}; - -//============================================================================== -class var::VariantType_Int64 : public var::VariantType -{ -public: - VariantType_Int64() noexcept {} - static const VariantType_Int64 instance; - - int toInt (const ValueUnion& data) const noexcept override { return (int) data.int64Value; } - int64 toInt64 (const ValueUnion& data) const noexcept override { return data.int64Value; } - double toDouble (const ValueUnion& data) const noexcept override { return (double) data.int64Value; } - String toString (const ValueUnion& data) const override { return String (data.int64Value); } - bool toBool (const ValueUnion& data) const noexcept override { return data.int64Value != 0; } - bool isInt64() const noexcept override { return true; } - bool isComparable() const noexcept override { return true; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + constexpr explicit VariantType (IntTag) noexcept + : isInt (true), + isComparable (true), + toInt (intToInt), + toInt64 (intToInt64), + toDouble (intToDouble), + toString (intToString), + toBool (intToBool), + equals (intEquals), + writeToStream (intWriteToStream) {} + + // int64 ======================================================================= + static int int64ToInt (const ValueUnion& data) noexcept { return (int) data.int64Value; } + static int64 int64ToInt64 (const ValueUnion& data) noexcept { return data.int64Value; } + static double int64ToDouble (const ValueUnion& data) noexcept { return (double) data.int64Value; } + static String int64ToString (const ValueUnion& data) { return String (data.int64Value); } + static bool int64ToBool (const ValueUnion& data) noexcept { return data.int64Value != 0; } + + static bool int64Equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { - if (otherType.isDouble() || otherType.isString()) - return otherType.equals (otherData, data, *this); + if (otherType.isDouble || otherType.isString) + return otherType.equals (otherData, data, VariantType { Int64Tag{} }); return otherType.toInt64 (otherData) == data.int64Value; } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void int64WriteToStream (const ValueUnion& data, OutputStream& output) { output.writeCompressedInt (9); output.writeByte (varMarker_Int64); output.writeInt64 (data.int64Value); } -}; - -//============================================================================== -class var::VariantType_Double : public var::VariantType -{ -public: - VariantType_Double() noexcept {} - static const VariantType_Double instance; - - int toInt (const ValueUnion& data) const noexcept override { return (int) data.doubleValue; } - int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.doubleValue; } - double toDouble (const ValueUnion& data) const noexcept override { return data.doubleValue; } - String toString (const ValueUnion& data) const override { return serialiseDouble (data.doubleValue); } - bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0.0; } - bool isDouble() const noexcept override { return true; } - bool isComparable() const noexcept override { return true; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + constexpr explicit VariantType (Int64Tag) noexcept + : isInt64 (true), + isComparable (true), + toInt (int64ToInt), + toInt64 (int64ToInt64), + toDouble (int64ToDouble), + toString (int64ToString), + toBool (int64ToBool), + equals (int64Equals), + writeToStream (int64WriteToStream) {} + + // double ====================================================================== + static int doubleToInt (const ValueUnion& data) noexcept { return (int) data.doubleValue; } + static int64 doubleToInt64 (const ValueUnion& data) noexcept { return (int64) data.doubleValue; } + static double doubleToDouble (const ValueUnion& data) noexcept { return data.doubleValue; } + static String doubleToString (const ValueUnion& data) { return serialiseDouble (data.doubleValue); } + static bool doubleToBool (const ValueUnion& data) noexcept { return ! exactlyEqual (data.doubleValue, 0.0); } + + static bool doubleEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits::epsilon(); } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void doubleWriteToStream (const ValueUnion& data, OutputStream& output) { output.writeCompressedInt (9); output.writeByte (varMarker_Double); output.writeDouble (data.doubleValue); } -}; -//============================================================================== -class var::VariantType_Bool : public var::VariantType -{ -public: - VariantType_Bool() noexcept {} - static const VariantType_Bool instance; - - int toInt (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; } - int64 toInt64 (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; } - double toDouble (const ValueUnion& data) const noexcept override { return data.boolValue ? 1.0 : 0.0; } - String toString (const ValueUnion& data) const override { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); } - bool toBool (const ValueUnion& data) const noexcept override { return data.boolValue; } - bool isBool() const noexcept override { return true; } - bool isComparable() const noexcept override { return true; } - - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + constexpr explicit VariantType (DoubleTag) noexcept + : isDouble (true), + isComparable (true), + toInt (doubleToInt), + toInt64 (doubleToInt64), + toDouble (doubleToDouble), + toString (doubleToString), + toBool (doubleToBool), + equals (doubleEquals), + writeToStream (doubleWriteToStream) {} + + // bool ======================================================================== + static int boolToInt (const ValueUnion& data) noexcept { return data.boolValue ? 1 : 0; } + static int64 boolToInt64 (const ValueUnion& data) noexcept { return data.boolValue ? 1 : 0; } + static double boolToDouble (const ValueUnion& data) noexcept { return data.boolValue ? 1.0 : 0.0; } + static String boolToString (const ValueUnion& data) { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); } + static bool boolToBool (const ValueUnion& data) noexcept { return data.boolValue; } + + static bool boolEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { return otherType.toBool (otherData) == data.boolValue; } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void boolWriteToStream (const ValueUnion& data, OutputStream& output) { output.writeCompressedInt (1); output.writeByte (data.boolValue ? (char) varMarker_BoolTrue : (char) varMarker_BoolFalse); } -}; -//============================================================================== -class var::VariantType_String : public var::VariantType -{ -public: - VariantType_String() noexcept {} - static const VariantType_String instance; - - void cleanUp (ValueUnion& data) const noexcept override { getString (data)-> ~String(); } - void createCopy (ValueUnion& dest, const ValueUnion& source) const override { new (dest.stringValue) String (*getString (source)); } - - bool isString() const noexcept override { return true; } - int toInt (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue(); } - int64 toInt64 (const ValueUnion& data) const noexcept override { return getString (data)->getLargeIntValue(); } - double toDouble (const ValueUnion& data) const noexcept override { return getString (data)->getDoubleValue(); } - String toString (const ValueUnion& data) const override { return *getString (data); } - bool toBool (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue() != 0 - || getString (data)->trim().equalsIgnoreCase ("true") - || getString (data)->trim().equalsIgnoreCase ("yes"); } - bool isComparable() const noexcept override { return true; } - - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + constexpr explicit VariantType (BoolTag) noexcept + : isBool (true), + isComparable (true), + toInt (boolToInt), + toInt64 (boolToInt64), + toDouble (boolToDouble), + toString (boolToString), + toBool (boolToBool), + equals (boolEquals), + writeToStream (boolWriteToStream) {} + + // string ====================================================================== + static const String* getString (const ValueUnion& data) noexcept { return unalignedPointerCast (data.stringValue); } + static String* getString ( ValueUnion& data) noexcept { return unalignedPointerCast (data.stringValue); } + + static int stringToInt (const ValueUnion& data) noexcept { return getString (data)->getIntValue(); } + static int64 stringToInt64 (const ValueUnion& data) noexcept { return getString (data)->getLargeIntValue(); } + static double stringToDouble (const ValueUnion& data) noexcept { return getString (data)->getDoubleValue(); } + static String stringToString (const ValueUnion& data) { return *getString (data); } + static bool stringToBool (const ValueUnion& data) noexcept + { + return getString (data)->getIntValue() != 0 + || getString (data)->trim().equalsIgnoreCase ("true") + || getString (data)->trim().equalsIgnoreCase ("yes"); + } + + static void stringCleanUp (ValueUnion& data) noexcept { getString (data)-> ~String(); } + static void stringCreateCopy (ValueUnion& dest, const ValueUnion& source) { new (dest.stringValue) String (*getString (source)); } + + static bool stringEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { return otherType.toString (otherData) == *getString (data); } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void stringWriteToStream (const ValueUnion& data, OutputStream& output) { auto* s = getString (data); const size_t len = s->getNumBytesAsUTF8() + 1; @@ -256,65 +289,73 @@ class var::VariantType_String : public var::VariantType output.write (temp, len); } -private: - static inline const String* getString (const ValueUnion& data) noexcept { return reinterpret_cast (data.stringValue); } - static inline String* getString (ValueUnion& data) noexcept { return reinterpret_cast (data.stringValue); } -}; + constexpr explicit VariantType (StringTag) noexcept + : isString (true), + isComparable (true), + toInt (stringToInt), + toInt64 (stringToInt64), + toDouble (stringToDouble), + toString (stringToString), + toBool (stringToBool), + cleanUp (stringCleanUp), + createCopy (stringCreateCopy), + equals (stringEquals), + writeToStream (stringWriteToStream) {} + + // object ====================================================================== + static String objectToString (const ValueUnion& data) + { + return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); + } -//============================================================================== -class var::VariantType_Object : public var::VariantType -{ -public: - VariantType_Object() noexcept {} - static const VariantType_Object instance; + static bool objectToBool (const ValueUnion& data) noexcept { return data.objectValue != nullptr; } + static ReferenceCountedObject* objectToObject (const ValueUnion& data) noexcept { return data.objectValue; } - void cleanUp (ValueUnion& data) const noexcept override { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } + static var objectClone (const var& original) + { + if (auto* d = original.getDynamicObject()) + return d->clone().release(); + + jassertfalse; // can only clone DynamicObjects! + return {}; + } - void createCopy (ValueUnion& dest, const ValueUnion& source) const override + static void objectCleanUp (ValueUnion& data) noexcept { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } + + static void objectCreateCopy (ValueUnion& dest, const ValueUnion& source) { dest.objectValue = source.objectValue; if (dest.objectValue != nullptr) dest.objectValue->incReferenceCount(); } - String toString (const ValueUnion& data) const override { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); } - bool toBool (const ValueUnion& data) const noexcept override { return data.objectValue != nullptr; } - ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept override { return data.objectValue; } - bool isObject() const noexcept override { return true; } - - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + static bool objectEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { return otherType.toObject (otherData) == data.objectValue; } - var clone (const var& original) const override - { - if (auto* d = original.getDynamicObject()) - return d->clone().get(); - - jassertfalse; // can only clone DynamicObjects! - return {}; - } - - void writeToStream (const ValueUnion&, OutputStream& output) const override + static void objectWriteToStream (const ValueUnion&, OutputStream& output) { jassertfalse; // Can't write an object to a stream! output.writeCompressedInt (0); } -}; -//============================================================================== -class var::VariantType_Array : public var::VariantType_Object -{ -public: - VariantType_Array() noexcept {} - static const VariantType_Array instance; - - String toString (const ValueUnion&) const override { return "[Array]"; } - ReferenceCountedObject* toObject (const ValueUnion&) const noexcept override { return nullptr; } - bool isArray() const noexcept override { return true; } - - Array* toArray (const ValueUnion& data) const noexcept override + constexpr explicit VariantType (ObjectTag) noexcept + : isObject (true), + toString (objectToString), + toBool (objectToBool), + toObject (objectToObject), + clone (objectClone), + cleanUp (objectCleanUp), + createCopy (objectCreateCopy), + equals (objectEquals), + writeToStream (objectWriteToStream) {} + + // array ======================================================================= + static String arrayToString (const ValueUnion&) { return "[Array]"; } + static ReferenceCountedObject* arrayToObject (const ValueUnion&) noexcept { return nullptr; } + + static Array* arrayToArray (const ValueUnion& data) noexcept { if (auto* a = dynamic_cast (data.objectValue)) return &(a->array); @@ -322,18 +363,18 @@ class var::VariantType_Array : public var::VariantType_Object return nullptr; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + static bool arrayEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { - auto* thisArray = toArray (data); + auto* thisArray = arrayToArray (data); auto* otherArray = otherType.toArray (otherData); return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray); } - var clone (const var& original) const override + static var arrayClone (const var& original) { Array arrayCopy; - if (auto* array = toArray (original.value)) + if (auto* array = arrayToArray (original.value)) { arrayCopy.ensureStorageAllocated (array->size()); @@ -344,9 +385,9 @@ class var::VariantType_Array : public var::VariantType_Object return var (arrayCopy); } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void arrayWriteToStream (const ValueUnion& data, OutputStream& output) { - if (auto* array = toArray (data)) + if (auto* array = arrayToArray (data)) { MemoryOutputStream buffer (512); buffer.writeCompressedInt (array->size()); @@ -360,109 +401,122 @@ class var::VariantType_Array : public var::VariantType_Object } } - struct RefCountedArray : public ReferenceCountedObject + struct RefCountedArray final : public ReferenceCountedObject { RefCountedArray (const Array& a) : array (a) { incReferenceCount(); } RefCountedArray (Array&& a) : array (std::move (a)) { incReferenceCount(); } Array array; }; -}; -//============================================================================== -class var::VariantType_Binary : public var::VariantType -{ -public: - VariantType_Binary() noexcept {} - - static const VariantType_Binary instance; - - void cleanUp (ValueUnion& data) const noexcept override { delete data.binaryValue; } - void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.binaryValue = new MemoryBlock (*source.binaryValue); } - - String toString (const ValueUnion& data) const override { return data.binaryValue->toBase64Encoding(); } - bool isBinary() const noexcept override { return true; } - MemoryBlock* toBinary (const ValueUnion& data) const noexcept override { return data.binaryValue; } - - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + constexpr explicit VariantType (ArrayTag) noexcept + : isObject (true), + isArray (true), + toString (arrayToString), + toBool (objectToBool), + toObject (arrayToObject), + toArray (arrayToArray), + clone (arrayClone), + cleanUp (objectCleanUp), + createCopy (objectCreateCopy), + equals (arrayEquals), + writeToStream (arrayWriteToStream) {} + + // binary ====================================================================== + static void binaryCleanUp (ValueUnion& data) noexcept { delete data.binaryValue; } + static void binaryCreateCopy (ValueUnion& dest, const ValueUnion& source) { dest.binaryValue = new MemoryBlock (*source.binaryValue); } + + static String binaryToString (const ValueUnion& data) { return data.binaryValue->toBase64Encoding(); } + static MemoryBlock* binaryToBinary (const ValueUnion& data) noexcept { return data.binaryValue; } + + static bool binaryEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { const MemoryBlock* const otherBlock = otherType.toBinary (otherData); return otherBlock != nullptr && *otherBlock == *data.binaryValue; } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void binaryWriteToStream (const ValueUnion& data, OutputStream& output) { output.writeCompressedInt (1 + (int) data.binaryValue->getSize()); output.writeByte (varMarker_Binary); output << *data.binaryValue; } -}; -//============================================================================== -class var::VariantType_Method : public var::VariantType -{ -public: - VariantType_Method() noexcept {} - static const VariantType_Method instance; + constexpr explicit VariantType (BinaryTag) noexcept + : isBinary (true), + toString (binaryToString), + toBinary (binaryToBinary), + cleanUp (binaryCleanUp), + createCopy (binaryCreateCopy), + equals (binaryEquals), + writeToStream (binaryWriteToStream) {} - void cleanUp (ValueUnion& data) const noexcept override { if (data.methodValue != nullptr ) delete data.methodValue; } - void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.methodValue = new NativeFunction (*source.methodValue); } + // method ====================================================================== + static void methodCleanUp (ValueUnion& data) noexcept { if (data.methodValue != nullptr ) delete data.methodValue; } + static void methodCreateCopy (ValueUnion& dest, const ValueUnion& source) { dest.methodValue = new NativeFunction (*source.methodValue); } - String toString (const ValueUnion&) const override { return "Method"; } - bool toBool (const ValueUnion& data) const noexcept override { return data.methodValue != nullptr; } - bool isMethod() const noexcept override { return true; } + static String methodToString (const ValueUnion&) { return "Method"; } + static bool methodToBool (const ValueUnion& data) noexcept { return data.methodValue != nullptr; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + static bool methodEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { - return otherType.isMethod() && otherData.methodValue == data.methodValue; + return otherType.isMethod && otherData.methodValue == data.methodValue; } - void writeToStream (const ValueUnion&, OutputStream& output) const override + static void methodWriteToStream (const ValueUnion&, OutputStream& output) { jassertfalse; // Can't write a method to a stream! output.writeCompressedInt (0); } -}; -//============================================================================== -const var::VariantType_Void var::VariantType_Void::instance; -const var::VariantType_Undefined var::VariantType_Undefined::instance; -const var::VariantType_Int var::VariantType_Int::instance; -const var::VariantType_Int64 var::VariantType_Int64::instance; -const var::VariantType_Bool var::VariantType_Bool::instance; -const var::VariantType_Double var::VariantType_Double::instance; -const var::VariantType_String var::VariantType_String::instance; -const var::VariantType_Object var::VariantType_Object::instance; -const var::VariantType_Array var::VariantType_Array::instance; -const var::VariantType_Binary var::VariantType_Binary::instance; -const var::VariantType_Method var::VariantType_Method::instance; + constexpr explicit VariantType (MethodTag) noexcept + : isMethod (true), + toString (methodToString), + toBool (methodToBool), + cleanUp (methodCleanUp), + createCopy (methodCreateCopy), + equals (methodEquals), + writeToStream (methodWriteToStream) {} +}; +struct var::Instance +{ + static constexpr VariantType attributesVoid { VariantType::VoidTag{} }; + static constexpr VariantType attributesUndefined { VariantType::UndefinedTag{} }; + static constexpr VariantType attributesInt { VariantType::IntTag{} }; + static constexpr VariantType attributesInt64 { VariantType::Int64Tag{} }; + static constexpr VariantType attributesBool { VariantType::BoolTag{} }; + static constexpr VariantType attributesDouble { VariantType::DoubleTag{} }; + static constexpr VariantType attributesMethod { VariantType::MethodTag{} }; + static constexpr VariantType attributesArray { VariantType::ArrayTag{} }; + static constexpr VariantType attributesString { VariantType::StringTag{} }; + static constexpr VariantType attributesBinary { VariantType::BinaryTag{} }; + static constexpr VariantType attributesObject { VariantType::ObjectTag{} }; +}; //============================================================================== -var::var() noexcept : type (&VariantType_Void::instance) {} +var::var() noexcept : type (&Instance::attributesVoid) {} var::var (const VariantType& t) noexcept : type (&t) {} var::~var() noexcept { type->cleanUp (value); } -JUCE_DECLARE_DEPRECATED_STATIC (const var var::null;) - //============================================================================== var::var (const var& valueToCopy) : type (valueToCopy.type) { type->createCopy (value, valueToCopy.value); } -var::var (const int v) noexcept : type (&VariantType_Int::instance) { value.intValue = v; } -var::var (const int64 v) noexcept : type (&VariantType_Int64::instance) { value.int64Value = v; } -var::var (const bool v) noexcept : type (&VariantType_Bool::instance) { value.boolValue = v; } -var::var (const double v) noexcept : type (&VariantType_Double::instance) { value.doubleValue = v; } -var::var (NativeFunction m) noexcept : type (&VariantType_Method::instance) { value.methodValue = new NativeFunction (m); } -var::var (const Array& v) : type (&VariantType_Array::instance) { value.objectValue = new VariantType_Array::RefCountedArray(v); } -var::var (const String& v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } -var::var (const char* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } -var::var (const wchar_t* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } -var::var (const void* v, size_t sz) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v, sz); } -var::var (const MemoryBlock& v) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v); } +var::var (const int v) noexcept : type (&Instance::attributesInt) { value.intValue = v; } +var::var (const int64 v) noexcept : type (&Instance::attributesInt64) { value.int64Value = v; } +var::var (const bool v) noexcept : type (&Instance::attributesBool) { value.boolValue = v; } +var::var (const double v) noexcept : type (&Instance::attributesDouble) { value.doubleValue = v; } +var::var (NativeFunction m) noexcept : type (&Instance::attributesMethod) { value.methodValue = new NativeFunction (m); } +var::var (const Array& v) : type (&Instance::attributesArray) { value.objectValue = new VariantType::RefCountedArray (v); } +var::var (const String& v) : type (&Instance::attributesString) { new (value.stringValue) String (v); } +var::var (const char* const v) : type (&Instance::attributesString) { new (value.stringValue) String (v); } +var::var (const wchar_t* const v) : type (&Instance::attributesString) { new (value.stringValue) String (v); } +var::var (const void* v, size_t sz) : type (&Instance::attributesBinary) { value.binaryValue = new MemoryBlock (v, sz); } +var::var (const MemoryBlock& v) : type (&Instance::attributesBinary) { value.binaryValue = new MemoryBlock (v); } -var::var (const StringArray& v) : type (&VariantType_Array::instance) +var::var (const StringArray& v) : type (&Instance::attributesArray) { Array strings; strings.ensureStorageAllocated (v.size()); @@ -470,10 +524,10 @@ var::var (const StringArray& v) : type (&VariantType_Array::instance) for (auto& i : v) strings.add (var (i)); - value.objectValue = new VariantType_Array::RefCountedArray (strings); + value.objectValue = new VariantType::RefCountedArray (strings); } -var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::instance) +var::var (ReferenceCountedObject* const object) : type (&Instance::attributesObject) { value.objectValue = object; @@ -481,20 +535,20 @@ var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::in object->incReferenceCount(); } -var var::undefined() noexcept { return var (VariantType_Undefined::instance); } +var var::undefined() noexcept { return var (Instance::attributesUndefined); } //============================================================================== -bool var::isVoid() const noexcept { return type->isVoid(); } -bool var::isUndefined() const noexcept { return type->isUndefined(); } -bool var::isInt() const noexcept { return type->isInt(); } -bool var::isInt64() const noexcept { return type->isInt64(); } -bool var::isBool() const noexcept { return type->isBool(); } -bool var::isDouble() const noexcept { return type->isDouble(); } -bool var::isString() const noexcept { return type->isString(); } -bool var::isObject() const noexcept { return type->isObject(); } -bool var::isArray() const noexcept { return type->isArray(); } -bool var::isBinaryData() const noexcept { return type->isBinary(); } -bool var::isMethod() const noexcept { return type->isMethod(); } +bool var::isVoid() const noexcept { return type->isVoid; } +bool var::isUndefined() const noexcept { return type->isUndefined; } +bool var::isInt() const noexcept { return type->isInt; } +bool var::isInt64() const noexcept { return type->isInt64; } +bool var::isBool() const noexcept { return type->isBool; } +bool var::isDouble() const noexcept { return type->isDouble; } +bool var::isString() const noexcept { return type->isString; } +bool var::isObject() const noexcept { return type->isObject; } +bool var::isArray() const noexcept { return type->isArray; } +bool var::isBinaryData() const noexcept { return type->isBinary; } +bool var::isMethod() const noexcept { return type->isMethod; } var::operator int() const noexcept { return type->toInt (value); } var::operator int64() const noexcept { return type->toInt64 (value); } @@ -516,14 +570,14 @@ void var::swapWith (var& other) noexcept } var& var::operator= (const var& v) { type->cleanUp (value); type = v.type; type->createCopy (value, v.value); return *this; } -var& var::operator= (const int v) { type->cleanUp (value); type = &VariantType_Int::instance; value.intValue = v; return *this; } -var& var::operator= (const int64 v) { type->cleanUp (value); type = &VariantType_Int64::instance; value.int64Value = v; return *this; } -var& var::operator= (const bool v) { type->cleanUp (value); type = &VariantType_Bool::instance; value.boolValue = v; return *this; } -var& var::operator= (const double v) { type->cleanUp (value); type = &VariantType_Double::instance; value.doubleValue = v; return *this; } -var& var::operator= (const char* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } -var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } -var& var::operator= (const String& v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } -var& var::operator= (const MemoryBlock& v) { type->cleanUp (value); type = &VariantType_Binary::instance; value.binaryValue = new MemoryBlock (v); return *this; } +var& var::operator= (const int v) { type->cleanUp (value); type = &Instance::attributesInt; value.intValue = v; return *this; } +var& var::operator= (const int64 v) { type->cleanUp (value); type = &Instance::attributesInt64; value.int64Value = v; return *this; } +var& var::operator= (const bool v) { type->cleanUp (value); type = &Instance::attributesBool; value.boolValue = v; return *this; } +var& var::operator= (const double v) { type->cleanUp (value); type = &Instance::attributesDouble; value.doubleValue = v; return *this; } +var& var::operator= (const char* const v) { type->cleanUp (value); type = &Instance::attributesString; new (value.stringValue) String (v); return *this; } +var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = &Instance::attributesString; new (value.stringValue) String (v); return *this; } +var& var::operator= (const String& v) { type->cleanUp (value); type = &Instance::attributesString; new (value.stringValue) String (v); return *this; } +var& var::operator= (const MemoryBlock& v) { type->cleanUp (value); type = &Instance::attributesBinary; value.binaryValue = new MemoryBlock (v); return *this; } var& var::operator= (const Array& v) { var v2 (v); swapWith (v2); return *this; } var& var::operator= (ReferenceCountedObject* v) { var v2 (v); swapWith (v2); return *this; } var& var::operator= (NativeFunction v) { var v2 (v); swapWith (v2); return *this; } @@ -532,7 +586,7 @@ var::var (var&& other) noexcept : type (other.type), value (other.value) { - other.type = &VariantType_Void::instance; + other.type = &Instance::attributesVoid; } var& var::operator= (var&& other) noexcept @@ -541,25 +595,25 @@ var& var::operator= (var&& other) noexcept return *this; } -var::var (String&& v) : type (&VariantType_String::instance) +var::var (String&& v) : type (&Instance::attributesString) { new (value.stringValue) String (std::move (v)); } -var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance) +var::var (MemoryBlock&& v) : type (&Instance::attributesBinary) { value.binaryValue = new MemoryBlock (std::move (v)); } -var::var (Array&& v) : type (&VariantType_Array::instance) +var::var (Array&& v) : type (&Instance::attributesArray) { - value.objectValue = new VariantType_Array::RefCountedArray (std::move (v)); + value.objectValue = new VariantType::RefCountedArray (std::move (v)); } var& var::operator= (String&& v) { type->cleanUp (value); - type = &VariantType_String::instance; + type = &Instance::attributesString; new (value.stringValue) String (std::move (v)); return *this; } @@ -582,7 +636,7 @@ bool var::hasSameTypeAs (const var& other) const noexcept bool canCompare (const var& v1, const var& v2) { - return v1.type->isComparable() && v2.type->isComparable(); + return v1.type->isComparable && v2.type->isComparable; } static int compare (const var& v1, const var& v2) @@ -591,7 +645,7 @@ static int compare (const var& v1, const var& v2) return v1.toString().compare (v2.toString()); auto diff = static_cast (v1) - static_cast (v2); - return diff == 0 ? 0 : (diff < 0 ? -1 : 1); + return exactlyEqual (diff, 0.0) ? 0 : (diff < 0 ? -1 : 1); } bool operator== (const var& v1, const var& v2) { return v1.equals (v2); } @@ -827,4 +881,17 @@ var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int { } +//============================================================================== +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const var var::null; + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h index 75def782..207ccbb2 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -54,7 +54,7 @@ class JUCE_API var int numArguments; }; - using NativeFunction = std::function; + using NativeFunction = std::function; //============================================================================== /** Creates a void variant. */ @@ -147,6 +147,17 @@ class JUCE_API var /** Returns true if this var has the same value as the one supplied. Note that this ignores the type, so a string var "123" and an integer var with the value 123 are considered to be equal. + + Note that equality checking depends on the "wrapped" type of the object on which + equals() is called. That means the following code will convert the right-hand-side + argument to a string and compare the string values, because the object on the + left-hand-side was initialised from a string: + @code var ("123").equals (var (123)) @endcode + However, the following code will convert the right-hand-side argument to a double + and compare the values as doubles, because the object on the left-hand-side was + initialised from a double: + @code var (45.6).equals ("45.6000") @endcode + @see equalsWithSameType */ bool equals (const var& other) const noexcept; @@ -271,30 +282,19 @@ class JUCE_API var */ static var readFromStream (InputStream& input); - /* This was a static empty var object, but is now deprecated as it's too easy to accidentally - use it indirectly during a static constructor, leading to hard-to-find order-of-initialisation - problems. - @deprecated If you need a default-constructed var, just use var() or {}. - The only time you might miss having var::null available might be if you need to return an - empty var from a function by reference, but if you need to do that, it's easy enough to use - a function-local static var and return that, avoiding any order-of-initialisation issues. - */ - JUCE_DEPRECATED_STATIC (static const var null;) + //============================================================================== + #if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN) + [[deprecated ("This was a static empty var object, but is now deprecated as it's too easy to accidentally " + "use it indirectly during a static constructor leading to hard-to-find order-of-initialisation " + "problems. Use var() or {} instead. For returning an empty var from a function by reference, " + "use a function-local static var and return that.")]] + static const var null; + #endif private: //============================================================================== - class VariantType; - class VariantType_Void; - class VariantType_Undefined; - class VariantType_Int; - class VariantType_Int64; - class VariantType_Double; - class VariantType_Bool; - class VariantType_String; - class VariantType_Object; - class VariantType_Array; - class VariantType_Binary; - class VariantType_Method; + struct VariantType; + struct Instance; union ValueUnion { @@ -340,26 +340,4 @@ JUCE_API bool operator== (const var&, const String&); JUCE_API bool operator!= (const var&, const String&); JUCE_API bool operator== (const var&, const char*); JUCE_API bool operator!= (const var&, const char*); - -//============================================================================== -/** This template-overloaded class can be used to convert between var and custom types. - - @tags{Core} -*/ -template -struct VariantConverter -{ - static Type fromVar (const var& v) { return static_cast (v); } - static var toVar (const Type& t) { return t; } -}; - -#ifndef DOXYGEN -template <> -struct VariantConverter -{ - static String fromVar (const var& v) { return v.toString(); } - static var toVar (const String& s) { return s; } -}; -#endif - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/detail/juce_CallbackListenerList.h b/JuceLibraryCode/modules/juce_core/detail/juce_CallbackListenerList.h new file mode 100644 index 00000000..4117d11c --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/detail/juce_CallbackListenerList.h @@ -0,0 +1,69 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::detail +{ + +template +constexpr bool isValueOrLvalueReferenceToConst() +{ + return (( (! std::is_reference_v) + || (std::is_lvalue_reference_v && std::is_const_v>)) && ...); +} + +template +class CallbackListenerList +{ +public: + static_assert (isValueOrLvalueReferenceToConst(), + "CallbackListenerList can only forward values or const lvalue references"); + + using Callback = std::function; + + ErasedScopeGuard addListener (Callback callback) + { + jassert (callback != nullptr); + + const auto it = callbacks.insert (callbacks.end(), std::move (callback)); + listeners.add (&*it); + + return ErasedScopeGuard { [this, it] + { + listeners.remove (&*it); + callbacks.erase (it); + } }; + } + + void call (Args... args) + { + listeners.call ([&] (auto& l) { l (std::forward (args)...); }); + } + +private: + std::list callbacks; + ListenerList listeners; +}; + +} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_core/files/juce_AndroidDocument.h b/JuceLibraryCode/modules/juce_core/files/juce_AndroidDocument.h new file mode 100644 index 00000000..075b693f --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/files/juce_AndroidDocument.h @@ -0,0 +1,476 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + 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 +{ + +//============================================================================== +/** + Some information about a document. + + Each instance represents some information about the document at the point when the instance + was created. + + Instance information is not updated automatically. If you think some file information may + have changed, create a new instance. + + @tags{Core} +*/ +class AndroidDocumentInfo +{ +public: + AndroidDocumentInfo() = default; + + /** True if this file really exists. */ + bool exists() const { return isJuceFlagSet (flagExists); } + + /** True if this is a directory rather than a file. */ + bool isDirectory() const; + + /** True if this is a file rather than a directory. */ + bool isFile() const { return type.isNotEmpty() && ! isDirectory(); } + + /** True if this process has permission to read this file. + + If this returns true, and the AndroidDocument refers to a file rather than a directory, + then AndroidDocument::createInputStream should work on this document. + */ + bool canRead() const { return isJuceFlagSet (flagHasReadPermission) && type.isNotEmpty(); } + + /** True if this is a document that can be written, or a directory that can be modified. + + If this returns true, and the AndroidDocument refers to a file rather than a directory, + then AndroidDocument::createOutputStream should work on this document. + */ + bool canWrite() const + { + return isJuceFlagSet (flagHasWritePermission) + && type.isNotEmpty() + && (isNativeFlagSet (flagSupportsWrite) + || isNativeFlagSet (flagSupportsDelete) + || isNativeFlagSet (flagDirSupportsCreate)); + } + + /** True if this document can be removed completely from the filesystem. */ + bool canDelete() const { return isNativeFlagSet (flagSupportsDelete); } + + /** True if this is a directory and adding child documents is supported. */ + bool canCreateChildren() const { return isNativeFlagSet (flagDirSupportsCreate); } + + /** True if this document can be renamed. */ + bool canRename() const { return isNativeFlagSet (flagSupportsRename); } + + /** True if this document can be copied. */ + bool canCopy() const { return isNativeFlagSet (flagSupportsCopy); } + + /** True if this document can be moved. */ + bool canMove() const { return isNativeFlagSet (flagSupportsMove); } + + /** True if this document isn't a physical file on storage. */ + bool isVirtual() const { return isNativeFlagSet (flagVirtualDocument); } + + /** The user-facing name. + + This may or may not contain a file extension. For files identified by a URL, the MIME type + is stored separately. + */ + String getName() const { return name; } + + /** The MIME type of this document. */ + String getType() const { return isDirectory() ? String{} : type; } + + /** Timestamp when a document was last modified, in milliseconds since January 1, 1970 00:00:00.0 UTC. + + Use isLastModifiedValid() to determine whether or not the result of this + function is valid. + */ + int64 getLastModified() const { return isJuceFlagSet (flagValidModified) ? lastModified : 0; } + + /** True if the filesystem provided a modification time. */ + bool isLastModifiedValid() const { return isJuceFlagSet (flagValidModified); } + + /** The size of the document in bytes, if known. + + Use isSizeInBytesValid() to determine whether or not the result of this + function is valid. + */ + int64 getSizeInBytes() const { return isJuceFlagSet (flagValidSize) ? sizeInBytes : 0; } + + /** True if the filesystem provided a size in bytes. */ + bool isSizeInBytesValid() const { return isJuceFlagSet (flagValidSize); } + + /** @internal */ + class Args; + +private: + explicit AndroidDocumentInfo (Args); + + bool isNativeFlagSet (int flag) const { return (nativeFlags & flag) != 0; } + bool isJuceFlagSet (int flag) const { return (juceFlags & flag) != 0; } + + /* Native Android flags that might be set in the COLUMN_FLAGS for a particular document */ + enum + { + flagSupportsWrite = 0x0002, + flagSupportsDelete = 0x0004, + flagDirSupportsCreate = 0x0008, + flagSupportsRename = 0x0040, + flagSupportsCopy = 0x0080, + flagSupportsMove = 0x0100, + flagVirtualDocument = 0x0200, + }; + + /* Flags for other binary properties that aren't exposed in COLUMN_FLAGS */ + enum + { + flagExists = 1 << 0, + flagValidModified = 1 << 1, + flagValidSize = 1 << 2, + flagHasReadPermission = 1 << 3, + flagHasWritePermission = 1 << 4, + }; + + String name; + String type; + int64 lastModified = 0; + int64 sizeInBytes = 0; + int nativeFlags = 0, juceFlags = 0; +}; + +//============================================================================== +/** + Represents a permission granted to an application to read and/or write to a particular document + or tree. + + This class also contains static methods to request, revoke, and query the permissions of your + app. These functions are no-ops on all platforms other than Android. + + @tags{Core} +*/ +class AndroidDocumentPermission +{ +public: + /** The url of the document with persisted permissions. */ + URL getUrl() const { return url; } + + /** The time when the permissions were persisted, in milliseconds since January 1, 1970 00:00:00.0 UTC. */ + int64 getPersistedTime() const { return time; } + + /** True if the permission allows read access. */ + bool isReadPermission() const { return read; } + + /** True if the permission allows write access. */ + bool isWritePermission() const { return write; } + + /** Gives your app access to a particular document or tree, even after the device is rebooted. + + If you want to persist access to a folder selected through a native file chooser, make sure + to pass the exact URL returned by the file picker. Do NOT call AndroidDocument::fromTree + and then pass the result of getUrl to this function, as the resulting URL may differ from + the result of the file picker. + */ + static void takePersistentReadWriteAccess (const URL&); + + /** Revokes persistent access to a document or tree. */ + static void releasePersistentReadWriteAccess (const URL&); + + /** Returns all of the permissions that have previously been granted to the app, via + takePersistentReadWriteAccess(); + */ + static std::vector getPersistedPermissions(); + +private: + URL url; + int64 time = 0; + bool read = false, write = false; +}; + +//============================================================================== +/** + Provides access to a document on Android devices. + + In this context, a 'document' may be a file or a directory. + + The main purpose of this class is to provide access to files in shared storage on Android. + On newer Android versions, such files cannot be accessed directly by a file path, and must + instead be read and modified using a new URI-based DocumentsContract API. + + Example use-cases: + + - After showing the system open dialog to allow the user to open a file, pass the FileChooser's + URL result to AndroidDocument::fromDocument. Then, you can use getInfo() to retrieve + information about the file, and createInputStream to read from the file. Other functions allow + moving, copying, and deleting the file. + + - Similarly to the 'open' use-case, you may use createOutputStream to write to a file, normally + located using the system save dialog. + + - To allow reading or writing to a tree of files in shared storage, you can show the system + open dialog in 'selects directories' mode, and pass the resulting URL to + AndroidDocument::fromTree. Then, you can iterate the files in the directory, query them, + and create new files. This is a good way to store multiple files that the user can access from + other apps, and that will be persistent after uninstalling and reinstalling your app. + + Note that you probably do *not* need this class if your app only needs to access files in its + own internal sandbox. juce::File instances should work as expected in that case. + + AndroidDocument is a bit like the DocumentFile class from the androidx extension library, + in that it represents a single document, and is implemented using DocumentsContract functions. + + @tags{Core} +*/ +class AndroidDocument +{ +public: + /** Create a null document. */ + AndroidDocument(); + + /** Create an AndroidDocument representing a file or directory at a particular path. + + This is provided for use on older API versions (lower than 19), or on other platforms, so + that the same AndroidDocument API can be used regardless of the runtime platform version. + + If the runtime platform version is 19 or higher, and you wish to work with a URI obtained + from a native file picker, use fromDocument() or fromTree() instead. + + If this function fails, hasValue() will return false on the returned document. + */ + static AndroidDocument fromFile (const File& filePath); + + /** Create an AndroidDocument representing a single document. + + The argument should be a URL representing a document. Such URLs are returned by the system + file-picker when it is not in folder-selection mode. If you pass a tree URL, this function + will fail. + + This function may fail on Android devices with API level 18 or lower, and on non-Android + platforms. If this function fails, hasValue() will return false on the returned document. + If calling this function fails, you may want to retry creating an AndroidDocument + with fromFile(), passing the result of URL::getLocalFile(). + */ + static AndroidDocument fromDocument (const URL& documentUrl); + + /** Create an AndroidDocument representing the root of a tree of files. + + The argument should be a URL representing a tree. Such URLs are returned by the system + file-picker when it is in folder-selection mode. If you pass a URL referring to a document + inside a tree, this will return a document referring to the root of the tree. If you pass + a URL referring to a single file, this will fail. + + When targeting platform version 30 or later, access to the filesystem via file paths is + heavily restricted, and access to shared storage must use a new URI-based system instead. + At time of writing, apps uploaded to the Play Store must target API 30 or higher. + If you want read/write access to a shared folder, you must: + + - Use a native FileChooser in canSelectDirectories mode, to allow the user to select a + folder that your app can access. Your app will only have access to the contents of this + directory; it cannot escape to the filesystem root. The system will not allow the user + to grant access to certain locations, including filesystem roots and the Download folder. + - Pass the URI that the user selected to fromTree(), and use the resulting AndroidDocument + to read/write to the file system. + + This function may fail on Android devices with API level 20 or lower, and on non-Android + platforms. If this function fails, hasValue() will return false on the returned document. + */ + static AndroidDocument fromTree (const URL& treeUrl); + + AndroidDocument (const AndroidDocument&); + AndroidDocument (AndroidDocument&&) noexcept; + + AndroidDocument& operator= (const AndroidDocument&); + AndroidDocument& operator= (AndroidDocument&&) noexcept; + + ~AndroidDocument(); + + /** True if the URLs of the two documents match. */ + bool operator== (const AndroidDocument&) const; + + /** False if the URLs of the two documents match. */ + bool operator!= (const AndroidDocument&) const; + + /** Attempts to delete this document, and returns true on success. */ + bool deleteDocument() const; + + /** Renames the document, and returns true on success. + + This may cause the document's URI and metadata to change, so ensure to invalidate any + cached information about the document (URLs, AndroidDocumentInfo instances) after calling + this function. + */ + bool renameTo (const String& newDisplayName); + + /** Attempts to create a new nested document with a particular type and name. + + The type should be a standard MIME type string, e.g. "image/png", "text/plain". + + The file name doesn't need to contain an extension, as this information is passed via the + type argument. If this document is File-based rather than URL-based, then an appropriate + file extension will be chosen based on the MIME type. + + On failure, the returned AndroidDocument may be invalid, and will return false from hasValue(). + */ + AndroidDocument createChildDocumentWithTypeAndName (const String& type, const String& name) const; + + /** Attempts to create a new nested directory with a particular name. + + On failure, the returned AndroidDocument may be invalid, and will return false from hasValue(). + */ + AndroidDocument createChildDirectory (const String& name) const; + + /** True if this object actually refers to a document. + + If this function returns false, you *must not* call any function on this instance other + than the special member functions to copy, move, and/or destruct the instance. + */ + bool hasValue() const { return pimpl != nullptr; } + + /** Like hasValue(), but allows declaring AndroidDocument instances directly in 'if' statements. */ + explicit operator bool() const { return hasValue(); } + + /** Creates a stream for reading from this document. */ + std::unique_ptr createInputStream() const; + + /** Creates a stream for writing to this document. */ + std::unique_ptr createOutputStream() const; + + /** Returns the content URL describing this document. */ + URL getUrl() const; + + /** Fetches information about this document. */ + AndroidDocumentInfo getInfo() const; + + /** Experimental: Attempts to copy this document to a new parent, and returns an AndroidDocument + representing the copy. + + On failure, the returned AndroidDocument may be invalid, and will return false from hasValue(). + + This function may fail if the document doesn't allow copying, and when using URI-based + documents on devices with API level 23 or lower. On failure, the returned AndroidDocument + will return false from hasValue(). In testing, copying was not supported on the Android + emulator for API 24, 30, or 31, so there's a good chance this function won't work on real + devices. + + @see AndroidDocumentInfo::canCopy + */ + AndroidDocument copyDocumentToParentDocument (const AndroidDocument& target) const; + + /** Experimental: Attempts to move this document from one parent to another, and returns true on + success. + + This may cause the document's URI and metadata to change, so ensure to invalidate any + cached information about the document (URLs, AndroidDocumentInfo instances) after calling + this function. + + This function may fail if the document doesn't allow moving, and when using URI-based + documents on devices with API level 23 or lower. + */ + bool moveDocumentFromParentToParent (const AndroidDocument& currentParent, + const AndroidDocument& newParent); + + /** @internal */ + struct NativeInfo; + + /** @internal */ + NativeInfo getNativeInfo() const; + +private: + struct Utils; + class Pimpl; + + explicit AndroidDocument (std::unique_ptr); + + void swap (AndroidDocument& other) noexcept { std::swap (other.pimpl, pimpl); } + + std::unique_ptr pimpl; +}; + +//============================================================================== +/** + An iterator that visits child documents in a directory. + + Instances of this iterator can be created by calling makeRecursive() or + makeNonRecursive(). The results of these functions can additionally be used + in standard algorithms, and in range-for loops: + + @code + AndroidDocument findFileWithName (const AndroidDocument& parent, const String& name) + { + for (const auto& child : AndroidDocumentIterator::makeNonRecursive (parent)) + if (child.getInfo().getName() == name) + return child; + + return AndroidDocument(); + } + + std::vector findAllChildrenRecursive (const AndroidDocument& parent) + { + std::vector children; + std::copy (AndroidDocumentIterator::makeRecursive (doc), + AndroidDocumentIterator(), + std::back_inserter (children)); + return children; + } + @endcode + + @tags{Core} +*/ +class AndroidDocumentIterator final +{ +public: + using difference_type = std::ptrdiff_t; + using pointer = void; + using iterator_category = std::input_iterator_tag; + + /** Create an iterator that will visit each item in this directory. */ + static AndroidDocumentIterator makeNonRecursive (const AndroidDocument&); + + /** Create an iterator that will visit each item in this directory, and all nested directories. */ + static AndroidDocumentIterator makeRecursive (const AndroidDocument&); + + /** Creates an end/sentinel iterator. */ + AndroidDocumentIterator() = default; + + bool operator== (const AndroidDocumentIterator& other) const noexcept { return pimpl == nullptr && other.pimpl == nullptr; } + bool operator!= (const AndroidDocumentIterator& other) const noexcept { return ! operator== (other); } + + /** Returns the document to which this iterator points. */ + AndroidDocument operator*() const; + + /** Moves this iterator to the next position. */ + AndroidDocumentIterator& operator++(); + + /** Allows this iterator to be used directly in a range-for. */ + AndroidDocumentIterator begin() const { return *this; } + + /** Allows this iterator to be used directly in a range-for. */ + AndroidDocumentIterator end() const { return AndroidDocumentIterator{}; } + +private: + struct Utils; + struct Pimpl; + + explicit AndroidDocumentIterator (std::unique_ptr); + + std::shared_ptr pimpl; +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp b/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp index 0c0b152f..237a65a9 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,24 +23,6 @@ namespace juce { -DirectoryIterator::DirectoryIterator (const File& directory, bool recursive, - const String& pattern, int type) - : wildCards (parseWildcards (pattern)), - fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern), - wildCard (pattern), - path (File::addTrailingSeparator (directory.getFullPathName())), - whatToLookFor (type), - isRecursive (recursive) -{ - // you have to specify the type of files you're looking for! - jassert ((type & (File::findFiles | File::findDirectories)) != 0); - jassert (type > 0 && type <= 7); -} - -DirectoryIterator::~DirectoryIterator() -{ -} - StringArray DirectoryIterator::parseWildcards (const String& pattern) { StringArray s; @@ -64,6 +46,9 @@ bool DirectoryIterator::next() return next (nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + bool DirectoryIterator::next (bool* isDirResult, bool* isHiddenResult, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) { @@ -90,13 +75,26 @@ bool DirectoryIterator::next (bool* isDirResult, bool* isHiddenResult, int64* fi if (! filename.containsOnly (".")) { + const auto fullPath = File::createFileWithoutCheckingPath (path + filename); bool matches = false; if (isDirectory) { - if (isRecursive && ((whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden)) - subIterator.reset (new DirectoryIterator (File::createFileWithoutCheckingPath (path + filename), - true, wildCard, whatToLookFor)); + const auto mayRecurseIntoPossibleHiddenDir = [this, &isHidden] + { + return (whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden; + }; + + const auto mayRecurseIntoPossibleSymlink = [this, &fullPath] + { + return followSymlinks == File::FollowSymlinks::yes + || ! fullPath.isSymbolicLink() + || (followSymlinks == File::FollowSymlinks::noCycles + && knownPaths->find (fullPath.getLinkedTarget()) == knownPaths->end()); + }; + + if (isRecursive && mayRecurseIntoPossibleHiddenDir() && mayRecurseIntoPossibleSymlink()) + subIterator.reset (new DirectoryIterator (fullPath, true, wildCard, whatToLookFor, followSymlinks, knownPaths)); matches = (whatToLookFor & File::findDirectories) != 0; } @@ -114,7 +112,7 @@ bool DirectoryIterator::next (bool* isDirResult, bool* isHiddenResult, int64* fi if (matches) { - currentFile = File::createFileWithoutCheckingPath (path + filename); + currentFile = fullPath; if (isHiddenResult != nullptr) *isHiddenResult = isHidden; if (isDirResult != nullptr) *isDirResult = isDirectory; @@ -134,6 +132,9 @@ bool DirectoryIterator::next (bool* isDirResult, bool* isHiddenResult, int64* fi } } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + const File& DirectoryIterator::getFile() const { if (subIterator != nullptr && subIterator->hasBeenAdvanced) @@ -153,10 +154,10 @@ float DirectoryIterator::getEstimatedProgress() const if (totalNumFiles <= 0) return 0.0f; - auto detailedIndex = (subIterator != nullptr) ? index + subIterator->getEstimatedProgress() + auto detailedIndex = (subIterator != nullptr) ? (float) index + subIterator->getEstimatedProgress() : (float) index; - return jlimit (0.0f, 1.0f, detailedIndex / totalNumFiles); + return jlimit (0.0f, 1.0f, detailedIndex / (float) totalNumFiles); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h b/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h index 23711e42..a882bbd3 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,13 +23,21 @@ namespace juce { +#ifndef DOXYGEN + //============================================================================== /** + This class is now deprecated in favour of RangedDirectoryIterator. + Searches through the files in a directory, returning each file that is found. A DirectoryIterator will search through a directory and its subdirectories using a wildcard filepattern match. + The iterator keeps track of directories that it has previously traversed, and will + skip any previously-seen directories in the case of cycles caused by symbolic links. + It is also possible to avoid following symbolic links altogether. + If you may be scanning a large number of files, it's usually smarter to use this class than File::findChildFiles() because it allows you to stop at any time, rather than having to wait for the entire scan to finish before getting the results. @@ -42,6 +50,7 @@ namespace juce It also provides an estimate of its progress, using a (highly inaccurate!) algorithm. @tags{Core} + @see RangedDirectoryIterator */ class JUCE_API DirectoryIterator final { @@ -62,20 +71,17 @@ class JUCE_API DirectoryIterator final } @endcode - @param directory the directory to search in - @param isRecursive whether all the subdirectories should also be searched - @param wildCard the file pattern to match. This may contain multiple patterns - separated by a semi-colon or comma, e.g. "*.jpg;*.png" - @param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying - whether to look for files, directories, or both. + @see RangedDirectoryIterator */ + [[deprecated ("This class is now deprecated in favour of RangedDirectoryIterator.")]] DirectoryIterator (const File& directory, - bool isRecursive, - const String& wildCard = "*", - int whatToLookFor = File::findFiles); - - /** Destructor. */ - ~DirectoryIterator(); + bool recursive, + const String& pattern = "*", + int type = File::findFiles, + File::FollowSymlinks follow = File::FollowSymlinks::yes) + : DirectoryIterator (directory, recursive, pattern, type, follow, nullptr) + { + } /** Moves the iterator along to the next file. @@ -117,6 +123,39 @@ class JUCE_API DirectoryIterator final float getEstimatedProgress() const; private: + using KnownPaths = std::set; + + DirectoryIterator (const File& directory, + bool recursive, + const String& pattern, + int type, + File::FollowSymlinks follow, + KnownPaths* seenPaths) + : wildCards (parseWildcards (pattern)), + fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern), + wildCard (pattern), + path (File::addTrailingSeparator (directory.getFullPathName())), + whatToLookFor (type), + isRecursive (recursive), + followSymlinks (follow), + knownPaths (seenPaths) + { + // you have to specify the type of files you're looking for! + jassert ((whatToLookFor & (File::findFiles | File::findDirectories)) != 0); + jassert (whatToLookFor > 0 && whatToLookFor <= 7); + + if (followSymlinks == File::FollowSymlinks::noCycles) + { + if (knownPaths == nullptr) + { + heapKnownPaths = std::make_unique(); + knownPaths = heapKnownPaths.get(); + } + + knownPaths->insert (directory); + } + } + //============================================================================== struct NativeIterator { @@ -143,6 +182,9 @@ class JUCE_API DirectoryIterator final bool hasBeenAdvanced = false; std::unique_ptr subIterator; File currentFile; + File::FollowSymlinks followSymlinks = File::FollowSymlinks::yes; + KnownPaths* knownPaths = nullptr; + std::unique_ptr heapKnownPaths; static StringArray parseWildcards (const String& pattern); static bool fileMatches (const StringArray& wildCards, const String& filename); @@ -150,4 +192,6 @@ class JUCE_API DirectoryIterator final JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryIterator) }; +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/files/juce_File.cpp b/JuceLibraryCode/modules/juce_core/files/juce_File.cpp index 512c76c5..5456c48e 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_File.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_File.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -63,8 +63,6 @@ File& File::operator= (File&& other) noexcept return *this; } -JUCE_DECLARE_DEPRECATED_STATIC (const File File::nonexistent{};) - //============================================================================== static String removeEllipsis (const String& path) { @@ -231,7 +229,7 @@ String File::addTrailingSeparator (const String& path) } //============================================================================== -#if JUCE_LINUX +#if JUCE_LINUX || JUCE_BSD #define NAMES_ARE_CASE_SENSITIVE 1 #endif @@ -362,6 +360,14 @@ File File::getParentDirectory() const return createFileWithoutCheckingPath (getPathUpToLastSlash()); } +bool File::isNonEmptyDirectory() const +{ + if (! isDirectory()) + return false; + + return RangedDirectoryIterator (*this, false, "*", findFilesAndDirectories) != RangedDirectoryIterator(); +} + //============================================================================== String File::getFileName() const { @@ -485,7 +491,7 @@ String File::descriptionOfSizeInBytes (const int64 bytes) else if (bytes < 1024 * 1024 * 1024) { suffix = " MB"; divisor = 1024.0 * 1024.0; } else { suffix = " GB"; divisor = 1024.0 * 1024.0 * 1024.0; } - return (divisor > 0 ? String (bytes / divisor, 1) : String (bytes)) + suffix; + return (divisor > 0 ? String ((double) bytes / divisor, 1) : String (bytes)) + suffix; } //============================================================================== @@ -563,18 +569,18 @@ void File::readLines (StringArray& destLines) const } //============================================================================== -Array File::findChildFiles (int whatToLookFor, bool searchRecursively, const String& wildcard) const +Array File::findChildFiles (int whatToLookFor, bool searchRecursively, const String& wildcard, FollowSymlinks followSymlinks) const { Array results; - findChildFiles (results, whatToLookFor, searchRecursively, wildcard); + findChildFiles (results, whatToLookFor, searchRecursively, wildcard, followSymlinks); return results; } -int File::findChildFiles (Array& results, int whatToLookFor, bool searchRecursively, const String& wildcard) const +int File::findChildFiles (Array& results, int whatToLookFor, bool searchRecursively, const String& wildcard, FollowSymlinks followSymlinks) const { int total = 0; - for (DirectoryIterator di (*this, searchRecursively, wildcard, whatToLookFor); di.next();) + for (const auto& di : RangedDirectoryIterator (*this, searchRecursively, wildcard, whatToLookFor, followSymlinks)) { results.add (di.getFile()); ++total; @@ -585,12 +591,10 @@ int File::findChildFiles (Array& results, int whatToLookFor, bool searchRe int File::getNumberOfChildFiles (const int whatToLookFor, const String& wildCardPattern) const { - int total = 0; - - for (DirectoryIterator di (*this, false, wildCardPattern, whatToLookFor); di.next();) - ++total; - - return total; + return std::accumulate (RangedDirectoryIterator (*this, false, wildCardPattern, whatToLookFor), + RangedDirectoryIterator(), + 0, + [] (int acc, const DirectoryEntry&) { return acc + 1; }); } bool File::containsSubDirectories() const @@ -598,8 +602,7 @@ bool File::containsSubDirectories() const if (! isDirectory()) return false; - DirectoryIterator di (*this, false, "*", findDirectories); - return di.next(); + return RangedDirectoryIterator (*this, false, "*", findDirectories) != RangedDirectoryIterator(); } //============================================================================== @@ -726,22 +729,24 @@ bool File::startAsProcess (const String& parameters) const } //============================================================================== -FileInputStream* File::createInputStream() const +std::unique_ptr File::createInputStream() const { - std::unique_ptr fin (new FileInputStream (*this)); + auto fin = std::make_unique (*this); if (fin->openedOk()) - return fin.release(); + return fin; return nullptr; } -FileOutputStream* File::createOutputStream (size_t bufferSize) const +std::unique_ptr File::createOutputStream (size_t bufferSize) const { - std::unique_ptr out (new FileOutputStream (*this, bufferSize)); + auto fout = std::make_unique (*this, bufferSize); + + if (fout->openedOk()) + return fout; - return out->failedToOpen() ? nullptr - : out.release(); + return nullptr; } //============================================================================== @@ -753,8 +758,8 @@ bool File::appendData (const void* const dataToAppend, if (numberOfBytes == 0) return true; - FileOutputStream out (*this, 8192); - return out.openedOk() && out.write (dataToAppend, numberOfBytes); + FileOutputStream fout (*this, 8192); + return fout.openedOk() && fout.write (dataToAppend, numberOfBytes); } bool File::replaceWithData (const void* const dataToWrite, @@ -770,12 +775,12 @@ bool File::replaceWithData (const void* const dataToWrite, bool File::appendText (const String& text, bool asUnicode, bool writeHeaderBytes, const char* lineFeed) const { - FileOutputStream out (*this); + FileOutputStream fout (*this); - if (out.failedToOpen()) + if (fout.failedToOpen()) return false; - return out.writeText (text, asUnicode, writeHeaderBytes, lineFeed); + return fout.writeText (text, asUnicode, writeHeaderBytes, lineFeed); } bool File::replaceWithText (const String& textToWrite, bool asUnicode, bool writeHeaderBytes, const char* lineFeed) const @@ -956,7 +961,7 @@ File File::createTempFile (StringRef fileNameEnding) } bool File::createSymbolicLink (const File& linkFileToCreate, - const String& nativePathOfTarget, + [[maybe_unused]] const String& nativePathOfTarget, bool overwriteExisting) { if (linkFileToCreate.exists()) @@ -973,7 +978,7 @@ bool File::createSymbolicLink (const File& linkFileToCreate, linkFileToCreate.deleteFile(); } - #if JUCE_MAC || JUCE_LINUX + #if JUCE_MAC || JUCE_LINUX || JUCE_BSD // one common reason for getting an error here is that the file already exists if (symlink (nativePathOfTarget.toRawUTF8(), linkFileToCreate.getFullPathName().toRawUTF8()) == -1) { @@ -989,7 +994,6 @@ bool File::createSymbolicLink (const File& linkFileToCreate, nativePathOfTarget.toWideCharPointer(), targetFile.isDirectory() ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) != FALSE; #else - ignoreUnused (nativePathOfTarget); jassertfalse; // symbolic links not supported on this platform! return false; #endif @@ -1010,6 +1014,19 @@ File File::getLinkedTarget() const } #endif +//============================================================================== +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const File File::nonexistent{}; + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif + //============================================================================== MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode, bool exclusive) : range (0, file.getSize()) @@ -1028,7 +1045,7 @@ MemoryMappedFile::MemoryMappedFile (const File& file, const Range& fileRa //============================================================================== #if JUCE_UNIT_TESTS -class FileTests : public UnitTest +class FileTests final : public UnitTest { public: FileTests() @@ -1046,7 +1063,7 @@ class FileTests : public UnitTest expect (! File().existsAsFile()); expect (! File().isDirectory()); #if ! JUCE_WINDOWS - expect (File("/").isDirectory()); + expect (File ("/").isDirectory()); #endif expect (home.isDirectory()); expect (home.exists()); @@ -1062,7 +1079,25 @@ class FileTests : public UnitTest expect (! home.isOnCDRomDrive()); expect (File::getCurrentWorkingDirectory().exists()); expect (home.setAsCurrentWorkingDirectory()); - expect (File::getCurrentWorkingDirectory() == home); + + { + auto homeParent = home; + bool noSymlinks = true; + + while (! homeParent.isRoot()) + { + if (homeParent.isSymbolicLink()) + { + noSymlinks = false; + break; + } + + homeParent = homeParent.getParentDirectory(); + } + + if (noSymlinks) + expect (File::getCurrentWorkingDirectory() == home); + } { Array roots; @@ -1080,7 +1115,11 @@ class FileTests : public UnitTest beginTest ("Writing"); - File demoFolder (temp.getChildFile ("JUCE UnitTests Temp Folder.folder")); + auto random = getRandom(); + const auto tempFolderName = "JUCE UnitTests Temp Folder " + + String::toHexString (random.nextInt()) + + ".folder"; + File demoFolder (temp.getChildFile (tempFolderName)); expect (demoFolder.deleteRecursively()); expect (demoFolder.createDirectory()); expect (demoFolder.isDirectory()); @@ -1114,11 +1153,18 @@ class FileTests : public UnitTest expect (home.getChildFile ("./../xyz") == home.getParentDirectory().getChildFile ("xyz")); expect (home.getChildFile ("a1/a2/a3/./../../a4") == home.getChildFile ("a1/a4")); + expect (! File().hasReadAccess()); + expect (! File().hasWriteAccess()); + + expect (! tempFile.hasReadAccess()); + { FileOutputStream fo (tempFile); fo.write ("0123456789", 10); } + expect (tempFile.hasReadAccess()); + expect (tempFile.exists()); expect (tempFile.getSize() == 10); expect (std::abs ((int) (tempFile.getLastModificationTime().toMilliseconds() - Time::getCurrentTime().toMilliseconds())) < 3000); diff --git a/JuceLibraryCode/modules/juce_core/files/juce_File.h b/JuceLibraryCode/modules/juce_core/files/juce_File.h index 644ad9d7..0ec94515 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_File.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_File.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,10 @@ namespace juce { +#if ! DOXYGEN && (JUCE_MAC || JUCE_IOS) + using OSType = unsigned int; +#endif + //============================================================================== /** Represents a local file or directory. @@ -338,6 +342,12 @@ class JUCE_API File final */ bool hasWriteAccess() const; + /** Checks whether a file can be read. + + @returns true if it's possible to read this file. + */ + bool hasReadAccess() const; + /** Changes the write-permission of a file or directory. @param shouldBeReadOnly whether to add or remove write-permission @@ -373,21 +383,21 @@ class JUCE_API File final //============================================================================== /** Returns the last modification time of this file. - @returns the time, or an invalid time if the file doesn't exist. + @returns the time, or the Unix Epoch if the file doesn't exist. @see setLastModificationTime, getLastAccessTime, getCreationTime */ Time getLastModificationTime() const; /** Returns the last time this file was accessed. - @returns the time, or an invalid time if the file doesn't exist. + @returns the time, or the Unix Epoch if the file doesn't exist. @see setLastAccessTime, getLastModificationTime, getCreationTime */ Time getLastAccessTime() const; /** Returns the time that this file was created. - @returns the time, or an invalid time if the file doesn't exist. + @returns the time, or the Unix Epoch if the file doesn't exist. @see getLastModificationTime, getLastAccessTime */ Time getCreationTime() const; @@ -500,15 +510,21 @@ class JUCE_API File final Also note that on some OSes (e.g. Windows), moving files between different volumes may not be possible. + This function will often fail to move directories because of the ambiguities + about merging existing directories. Use copyDirectoryTo() and deleteRecursively() + in these situations. + @returns true if the operation succeeds */ bool moveFileTo (const File& targetLocation) const; /** Copies a file. - Tries to copy a file to a different location. - If the target file already exists, this will attempt to delete it first, and - will fail if this can't be done. + Tries to copy a file to a different location. If the target file already exists, + this will attempt to delete it first, and will fail if this can't be done. + + Note that the target file isn't the directory to put it in, it's the actual + filename that you want the new file to have. @returns true if the operation succeeds */ @@ -552,6 +568,23 @@ class JUCE_API File final ignoreHiddenFiles = 4 /**< Add this flag to avoid returning any hidden files in the results. */ }; + enum class FollowSymlinks + { + /** Requests that a file system traversal should not follow any symbolic links. */ + no, + + /** Requests that a file system traversal may follow symbolic links, but should attempt to + skip any symbolic links to directories that may cause a cycle. + */ + noCycles, + + /** Requests that a file system traversal follow all symbolic links. Use with care, as this + may produce inconsistent results, or fail to terminate, if the filesystem contains cycles + due to symbolic links. + */ + yes + }; + /** Searches this directory for files matching a wildcard pattern. Assuming that this file is a directory, this method will search it @@ -564,13 +597,15 @@ class JUCE_API File final @param searchRecursively if true, all subdirectories will be recursed into to do an exhaustive search @param wildCardPattern the filename pattern to search for, e.g. "*.txt" + @param followSymlinks the method that should be used to handle symbolic links @returns the set of files that were found - @see getNumberOfChildFiles, DirectoryIterator + @see getNumberOfChildFiles, RangedDirectoryIterator */ Array findChildFiles (int whatToLookFor, bool searchRecursively, - const String& wildCardPattern = "*") const; + const String& wildCardPattern = "*", + FollowSymlinks followSymlinks = FollowSymlinks::yes) const; /** Searches inside a directory for files matching a wildcard pattern. Note that there's a newer, better version of this method which returns the results @@ -578,7 +613,8 @@ class JUCE_API File final mainly for legacy code to use. */ int findChildFiles (Array& results, int whatToLookFor, - bool searchRecursively, const String& wildCardPattern = "*") const; + bool searchRecursively, const String& wildCardPattern = "*", + FollowSymlinks followSymlinks = FollowSymlinks::yes) const; /** Searches inside a directory and counts how many files match a wildcard pattern. @@ -594,7 +630,8 @@ class JUCE_API File final is also added to this value, hidden files won't be counted @param wildCardPattern the filename pattern to search for, e.g. "*.txt" @returns the number of matches found - @see findChildFiles, DirectoryIterator + + @see findChildFiles, RangedDirectoryIterator */ int getNumberOfChildFiles (int whatToLookFor, const String& wildCardPattern = "*") const; @@ -622,7 +659,7 @@ class JUCE_API File final start of the file), or nullptr if the file can't be opened for some reason @see createOutputStream, loadFileAsData */ - FileInputStream* createInputStream() const; + std::unique_ptr createInputStream() const; /** Creates a stream to write to this file. @@ -655,7 +692,7 @@ class JUCE_API File final end of the file), or nullptr if the file can't be opened for some reason @see createInputStream, appendData, appendText */ - FileOutputStream* createOutputStream (size_t bufferSize = 0x8000) const; + std::unique_ptr createOutputStream (size_t bufferSize = 0x8000) const; //============================================================================== /** Loads a file's contents into memory as a block of binary data. @@ -933,7 +970,10 @@ class JUCE_API File final @see globalApplicationsDirectory */ - globalApplicationsDirectoryX86 + globalApplicationsDirectoryX86, + + /** On a Windows machine returns the %LOCALAPPDATA% folder. */ + windowsLocalAppData #endif }; @@ -1024,7 +1064,7 @@ class JUCE_API File final bool isSymbolicLink() const; /** If this file is a link or alias, this returns the file that it points to. - If the file isn't actually link, it'll just return itself. + If the file isn't actually a link, it'll just return itself. */ File getLinkedTarget() const; @@ -1065,6 +1105,17 @@ class JUCE_API File final void addToDock() const; #endif + #if JUCE_MAC || JUCE_IOS + /** Returns the path to the container shared by all apps with the provided app group ID. + + You *must* pass one of the app group IDs listed in your app's entitlements file. + + On failure, this function may return a non-existent file, so you should check + that the path exists and is writable before trying to use it. + */ + static File getContainerForSecurityApplicationGroupIdentifier (const String& appGroup); + #endif + //============================================================================== /** Comparator for files */ struct NaturalFileComparator @@ -1086,14 +1137,16 @@ class JUCE_API File final bool foldersFirst; }; + #if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN) /* These static objects are deprecated because it's too easy to accidentally use them indirectly during a static constructor, which leads to very obscure order-of-initialisation bugs. Use File::getSeparatorChar() and File::getSeparatorString(), and instead of File::nonexistent, just use File() or {}. */ - JUCE_DEPRECATED_STATIC (static const juce_wchar separator;) - JUCE_DEPRECATED_STATIC (static const StringRef separatorString;) - JUCE_DEPRECATED_STATIC (static const File nonexistent;) + [[deprecated]] static const juce_wchar separator; + [[deprecated]] static const StringRef separatorString; + [[deprecated]] static const File nonexistent; + #endif private: //============================================================================== @@ -1101,6 +1154,7 @@ class JUCE_API File final static String parseAbsolutePath (const String&); String getPathUpToLastSlash() const; + bool isNonEmptyDirectory() const; Result createDirectoryInternal (const String&) const; bool copyInternal (const File&) const; diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp index 467f8d71..905e74ac 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h index 52dbbd01..4ebb51df 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp index 22d71f31..9b4df0ab 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -81,7 +81,7 @@ bool FileInputStream::setPosition (int64 pos) //============================================================================== #if JUCE_UNIT_TESTS -struct FileInputStreamTests : public UnitTest +struct FileInputStreamTests final : public UnitTest { FileInputStreamTests() : UnitTest ("FileInputStream", UnitTestCategories::streams) @@ -89,63 +89,84 @@ struct FileInputStreamTests : public UnitTest void runTest() override { + beginTest ("Open stream non-existent file"); + { + auto tempFile = File::createTempFile (".txt"); + expect (! tempFile.exists()); + + FileInputStream stream (tempFile); + expect (stream.failedToOpen()); + } + + beginTest ("Open stream existing file"); + { + auto tempFile = File::createTempFile (".txt"); + tempFile.create(); + expect (tempFile.exists()); + + FileInputStream stream (tempFile); + expect (stream.openedOk()); + } + const MemoryBlock data ("abcdefghijklmnopqrstuvwxyz", 26); File f (File::createTempFile (".txt")); f.appendData (data.getData(), data.getSize()); FileInputStream stream (f); beginTest ("Read"); - - expectEquals (stream.getPosition(), (int64) 0); - expectEquals (stream.getTotalLength(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); - expect (! stream.isExhausted()); - - size_t numBytesRead = 0; - MemoryBlock readBuffer (data.getSize()); - - while (numBytesRead < data.getSize()) { - numBytesRead += (size_t) stream.read (&readBuffer[numBytesRead], 3); + expectEquals (stream.getPosition(), (int64) 0); + expectEquals (stream.getTotalLength(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); + expect (! stream.isExhausted()); - expectEquals (stream.getPosition(), (int64) numBytesRead); - expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); - expect (stream.isExhausted() == (numBytesRead == data.getSize())); - } + size_t numBytesRead = 0; + MemoryBlock readBuffer (data.getSize()); - expectEquals (stream.getPosition(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), (int64) 0); - expect (stream.isExhausted()); + while (numBytesRead < data.getSize()) + { + numBytesRead += (size_t) stream.read (&readBuffer[numBytesRead], 3); - expect (readBuffer == data); + expectEquals (stream.getPosition(), (int64) numBytesRead); + expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); + expect (stream.isExhausted() == (numBytesRead == data.getSize())); + } - beginTest ("Skip"); + expectEquals (stream.getPosition(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), (int64) 0); + expect (stream.isExhausted()); - stream.setPosition (0); - expectEquals (stream.getPosition(), (int64) 0); - expectEquals (stream.getTotalLength(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); - expect (! stream.isExhausted()); - - numBytesRead = 0; - const int numBytesToSkip = 5; + expect (readBuffer == data); + } - while (numBytesRead < data.getSize()) + beginTest ("Skip"); { - stream.skipNextBytes (numBytesToSkip); - numBytesRead += numBytesToSkip; - numBytesRead = std::min (numBytesRead, data.getSize()); - - expectEquals (stream.getPosition(), (int64) numBytesRead); - expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); - expect (stream.isExhausted() == (numBytesRead == data.getSize())); + stream.setPosition (0); + expectEquals (stream.getPosition(), (int64) 0); + expectEquals (stream.getTotalLength(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); + expect (! stream.isExhausted()); + + size_t numBytesRead = 0; + const int numBytesToSkip = 5; + + while (numBytesRead < data.getSize()) + { + stream.skipNextBytes (numBytesToSkip); + numBytesRead += numBytesToSkip; + numBytesRead = std::min (numBytesRead, data.getSize()); + + expectEquals (stream.getPosition(), (int64) numBytesRead); + expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); + expect (stream.isExhausted() == (numBytesRead == data.getSize())); + } + + expectEquals (stream.getPosition(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), (int64) 0); + expect (stream.isExhausted()); + + f.deleteFile(); } - - expectEquals (stream.getPosition(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), (int64) 0); - expect (stream.isExhausted()); - - f.deleteFile(); } }; diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.h b/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.h index fdff83c4..e696bcf6 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp index f2eebb3f..67d0ae2d 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,8 +23,6 @@ namespace juce { -int64 juce_fileSetPosition (void* handle, int64 pos); - //============================================================================== FileOutputStream::FileOutputStream (const File& f, const size_t bufferSizeToUse) : file (f), diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.h b/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.h index 68307e26..66e761e3 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp index e2bcff25..00fecbe1 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,9 +23,6 @@ namespace juce { -FileSearchPath::FileSearchPath() {} -FileSearchPath::~FileSearchPath() {} - FileSearchPath::FileSearchPath (const String& path) { init (path); @@ -66,18 +63,28 @@ int FileSearchPath::getNumPaths() const File FileSearchPath::operator[] (int index) const { - return File (directories[index]); + return File (getRawString (index)); +} + +String FileSearchPath::getRawString (int index) const +{ + return directories[index]; } String FileSearchPath::toString() const +{ + return toStringWithSeparator (";"); +} + +String FileSearchPath::toStringWithSeparator (StringRef separator) const { auto dirs = directories; for (auto& d : dirs) - if (d.containsChar (';')) + if (d.contains (separator)) d = d.quoted(); - return dirs.joinIntoString (";"); + return dirs.joinIntoString (separator); } void FileSearchPath::add (const File& dir, int insertIndex) @@ -108,21 +115,30 @@ void FileSearchPath::addPath (const FileSearchPath& other) void FileSearchPath::removeRedundantPaths() { - for (int i = directories.size(); --i >= 0;) + std::vector reduced; + + for (const auto& directory : directories) { - const File d1 (directories[i]); + const auto checkedIsChildOf = [&] (const auto& a, const auto& b) + { + return File::isAbsolutePath (a) && File::isAbsolutePath (b) && File (a).isAChildOf (b); + }; - for (int j = directories.size(); --j >= 0;) + const auto fContainsDirectory = [&] (const auto& f) { - const File d2 (directories[j]); + return f == directory || checkedIsChildOf (directory, f); + }; - if (i != j && (d1.isAChildOf (d2) || d1 == d2)) - { - directories.remove (i); - break; - } - } + if (std::find_if (reduced.begin(), reduced.end(), fContainsDirectory) != reduced.end()) + continue; + + const auto directoryContainsF = [&] (const auto& f) { return checkedIsChildOf (f, directory); }; + + reduced.erase (std::remove_if (reduced.begin(), reduced.end(), directoryContainsF), reduced.end()); + reduced.push_back (directory); } + + directories = StringArray (reduced.data(), (int) reduced.size()); } void FileSearchPath::removeNonExistentPaths() @@ -170,4 +186,54 @@ bool FileSearchPath::isFileInPath (const File& fileToCheck, return false; } +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class FileSearchPathTests final : public UnitTest +{ +public: + FileSearchPathTests() : UnitTest ("FileSearchPath", UnitTestCategories::files) {} + + void runTest() override + { + beginTest ("removeRedundantPaths"); + { + #if JUCE_WINDOWS + const String prefix = "C:"; + #else + const String prefix = ""; + #endif + + { + FileSearchPath fsp { prefix + "/a/b/c/d;" + prefix + "/a/b/c/e;" + prefix + "/a/b/c" }; + fsp.removeRedundantPaths(); + expectEquals (fsp.toString(), prefix + "/a/b/c"); + } + + { + FileSearchPath fsp { prefix + "/a/b/c;" + prefix + "/a/b/c/d;" + prefix + "/a/b/c/e" }; + fsp.removeRedundantPaths(); + expectEquals (fsp.toString(), prefix + "/a/b/c"); + } + + { + FileSearchPath fsp { prefix + "/a/b/c/d;" + prefix + "/a/b/c;" + prefix + "/a/b/c/e" }; + fsp.removeRedundantPaths(); + expectEquals (fsp.toString(), prefix + "/a/b/c"); + } + + { + FileSearchPath fsp { "%FOO%;" + prefix + "/a/b/c;%FOO%;" + prefix + "/a/b/c/d" }; + fsp.removeRedundantPaths(); + expectEquals (fsp.toString(), "%FOO%;" + prefix + "/a/b/c"); + } + } + } +}; + +static FileSearchPathTests fileSearchPathTests; + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h index 4d900b88..f4be32f3 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -36,7 +36,10 @@ class JUCE_API FileSearchPath public: //============================================================================== /** Creates an empty search path. */ - FileSearchPath(); + FileSearchPath() = default; + + /** Destructor. */ + ~FileSearchPath() = default; /** Creates a search path from a string of pathnames. @@ -53,9 +56,6 @@ class JUCE_API FileSearchPath /** Copies another search path. */ FileSearchPath& operator= (const FileSearchPath&); - /** Destructor. */ - ~FileSearchPath(); - /** Uses a string containing a list of pathnames to re-initialise this list. This search path is cleared and the semicolon- or comma-separated folders @@ -71,13 +71,27 @@ class JUCE_API FileSearchPath /** Returns one of the folders in this search path. The file returned isn't guaranteed to actually be a valid directory. - @see getNumPaths + @see getNumPaths, getRawString */ File operator[] (int index) const; + /** Returns the unaltered text of the folder at the specified index. + + Unlike operator[], this function returns the exact text that was entered. It does not + attempt to convert the path into an absolute path. + + This may be useful if the directory string is expected to understand environment variables + or other placeholders that the File constructor doesn't necessarily understand. + @see operator[] + */ + String getRawString (int index) const; + /** Returns the search path as a semicolon-separated list of directories. */ String toString() const; + /** Returns the search paths, joined with the provided separator. */ + String toStringWithSeparator (StringRef separator) const; + //============================================================================== /** Adds a new directory to the search path. diff --git a/JuceLibraryCode/modules/juce_core/files/juce_MemoryMappedFile.h b/JuceLibraryCode/modules/juce_core/files/juce_MemoryMappedFile.h index 3be541f6..423d3af2 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_MemoryMappedFile.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_MemoryMappedFile.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.cpp b/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.cpp new file mode 100644 index 00000000..86d73baf --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.cpp @@ -0,0 +1,79 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +float DirectoryEntry::getEstimatedProgress() const +{ + if (auto it = iterator.lock()) + return it->getEstimatedProgress(); + + return 0.0f; +} + +// We implement this in terms of the deprecated DirectoryIterator, +// but the old DirectoryIterator might go away in the future! +RangedDirectoryIterator::RangedDirectoryIterator (const File& directory, + bool isRecursive, + const String& wildCard, + int whatToLookFor, + File::FollowSymlinks followSymlinks) + : iterator (new DirectoryIterator (directory, + isRecursive, + wildCard, + whatToLookFor, + followSymlinks)) +{ + entry.iterator = iterator; + increment(); +} + +bool RangedDirectoryIterator::next() +{ + const auto result = iterator->next (&entry.directory, + &entry.hidden, + &entry.fileSize, + &entry.modTime, + &entry.creationTime, + &entry.readOnly); + if (result) + entry.file = iterator->getFile(); + else + entry = {}; + + return result; +} + +void RangedDirectoryIterator::increment() +{ + if (iterator != nullptr && ! next()) + iterator = nullptr; +} + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.h b/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.h new file mode 100644 index 00000000..ada9ac64 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/files/juce_RangedDirectoryIterator.h @@ -0,0 +1,190 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +//============================================================================== +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +/** + Describes the attributes of a file or folder. + + @tags{Core} +*/ +class DirectoryEntry final +{ +public: + /** The path to a file or folder. */ + File getFile() const { return file; } + + /** The time at which the item was last modified. */ + Time getModificationTime() const { return modTime; } + + /** The time at which the item was created. */ + Time getCreationTime() const { return creationTime; } + + /** The size of the item. */ + int64 getFileSize() const { return fileSize; } + + /** True if the item is a directory, false otherwise. */ + bool isDirectory() const { return directory; } + + /** True if the item is hidden, false otherwise. */ + bool isHidden() const { return hidden; } + + /** True if the item is read-only, false otherwise. */ + bool isReadOnly() const { return readOnly; } + + /** The estimated proportion of the range that has been visited + by the iterator, from 0.0 to 1.0. + */ + float getEstimatedProgress() const; + +private: + std::weak_ptr iterator; + File file; + Time modTime; + Time creationTime; + int64 fileSize = 0; + bool directory = false; + bool hidden = false; + bool readOnly = false; + + friend class RangedDirectoryIterator; +}; + +/** A convenience operator so that the expression `*it++` works correctly when + `it` is an instance of RangedDirectoryIterator. +*/ +inline const DirectoryEntry& operator* (const DirectoryEntry& e) noexcept { return e; } + +//============================================================================== +/** + Allows iterating over files and folders using C++11 range-for syntax. + + In the following example, we recursively find all hidden files in a + specific directory. + + @code + std::vector hiddenFiles; + + for (DirectoryEntry entry : RangedDirectoryIterator (File ("/path/to/folder"), isRecursive)) + if (entry.isHidden()) + hiddenFiles.push_back (entry.getFile()); + @endcode + + @tags{Core} +*/ +class RangedDirectoryIterator final +{ +public: + using difference_type = std::ptrdiff_t; + using value_type = DirectoryEntry; + using reference = DirectoryEntry; + using pointer = void; + using iterator_category = std::input_iterator_tag; + + /** The default-constructed iterator acts as the 'end' sentinel. */ + RangedDirectoryIterator() = default; + + /** Creates a RangedDirectoryIterator for a given directory. + + The resulting iterator can be used directly in a 'range-for' expression. + + @param directory the directory to search in + @param isRecursive whether all the subdirectories should also be searched + @param wildCard the file pattern to match. This may contain multiple patterns + separated by a semi-colon or comma, e.g. "*.jpg;*.png" + @param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying + whether to look for files, directories, or both. + @param followSymlinks the policy to use when symlinks are encountered + */ + RangedDirectoryIterator (const File& directory, + bool isRecursive, + const String& wildCard = "*", + int whatToLookFor = File::findFiles, + File::FollowSymlinks followSymlinks = File::FollowSymlinks::yes); + + /** Returns true if both iterators are in their end/sentinel state, + otherwise returns false. + */ + bool operator== (const RangedDirectoryIterator& other) const noexcept + { + return iterator == nullptr && other.iterator == nullptr; + } + + /** Returns the inverse of operator== */ + bool operator!= (const RangedDirectoryIterator& other) const noexcept + { + return ! operator== (other); + } + + /** Return an object containing metadata about the file or folder to + which the iterator is currently pointing. + */ + const DirectoryEntry& operator* () const noexcept { return entry; } + const DirectoryEntry* operator->() const noexcept { return &entry; } + + /** Moves the iterator along to the next file. */ + RangedDirectoryIterator& operator++() + { + increment(); + return *this; + } + + /** Moves the iterator along to the next file. + + @returns an object containing metadata about the file or folder to + to which the iterator was previously pointing. + */ + DirectoryEntry operator++ (int) + { + auto result = *(*this); + ++(*this); + return result; + } + +private: + bool next(); + void increment(); + + std::shared_ptr iterator; + DirectoryEntry entry; +}; + +/** Returns the iterator that was passed in. + Provided for range-for compatibility. +*/ +inline RangedDirectoryIterator begin (const RangedDirectoryIterator& it) { return it; } + +/** Returns a default-constructed sentinel value. + Provided for range-for compatibility. +*/ +inline RangedDirectoryIterator end (const RangedDirectoryIterator&) { return {}; } + + +JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp index 92cb1be5..ee9522d7 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,23 @@ namespace juce { +// Using Random::getSystemRandom() can be a bit dangerous in multithreaded contexts! +class LockedRandom +{ +public: + int nextInt() + { + const ScopedLock lock (mutex); + return random.nextInt(); + } + +private: + CriticalSection mutex; + Random random; +}; + +static LockedRandom lockedRandom; + static File createTempFile (const File& parentDirectory, String name, const String& suffix, int optionFlags) { @@ -34,7 +51,7 @@ static File createTempFile (const File& parentDirectory, String name, TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags) : temporaryFile (createTempFile (File::getSpecialLocation (File::tempDirectory), - "temp_" + String::toHexString (Random::getSystemRandom().nextInt()), + "temp_" + String::toHexString (lockedRandom.nextInt()), suffix, optionFlags)), targetFile() { @@ -43,7 +60,7 @@ TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags) TemporaryFile::TemporaryFile (const File& target, const int optionFlags) : temporaryFile (createTempFile (target.getParentDirectory(), target.getFileNameWithoutExtension() - + "_temp" + String::toHexString (Random::getSystemRandom().nextInt()), + + "_temp" + String::toHexString (lockedRandom.nextInt()), target.getFileExtension(), optionFlags)), targetFile (target) { @@ -105,7 +122,7 @@ bool TemporaryFile::deleteTemporaryFile() const // Have a few attempts at deleting the file before giving up.. for (int i = 5; --i >= 0;) { - if (temporaryFile.deleteFile()) + if (temporaryFile.isDirectory() ? temporaryFile.deleteRecursively() : temporaryFile.deleteFile()) return true; Thread::sleep (50); diff --git a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h index 1050baa9..81fc529b 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp index d881ea14..31b8e7c2 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h index 861fde00..8ec34aba 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.cpp b/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.cpp new file mode 100644 index 00000000..d5397921 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.cpp @@ -0,0 +1,769 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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 +{ + +struct Entry +{ + const char* fileExtension; + const char* mimeType; +}; + +class Table +{ +public: + void addEntry (const Entry& entry) + { + typeForExtension.emplace (entry.fileExtension, entry.mimeType); + extensionForType.emplace (entry.mimeType, entry.fileExtension); + } + + StringArray getTypesForExtension (const String& extension) const + { + return getValuesForKey (typeForExtension, extension); + } + + StringArray getExtensionsForType (const String& type) const + { + return getValuesForKey (extensionForType, type); + } + + static Table& get() + { + static Table table; + return table; + } + +private: + Table() = default; + + static StringArray getValuesForKey (const std::multimap& map, const String& key) + { + const auto [begin, end] = map.equal_range (key); + + StringArray result; + std::for_each (begin, end, [&] (const auto& pair) { result.add (pair.second); }); + + return result; + } + + static inline constexpr Entry initialEntries[] + { + { "3dm", "x-world/x-3dmf" }, + { "3dmf", "x-world/x-3dmf" }, + { "a", "application/octet-stream" }, + { "aab", "application/x-authorware-bin" }, + { "aam", "application/x-authorware-map" }, + { "aas", "application/x-authorware-seg" }, + { "abc", "text/vnd.abc" }, + { "acgi", "text/html" }, + { "afl", "video/animaflex" }, + { "ai", "application/postscript" }, + { "aif", "audio/aiff" }, + { "aif", "audio/x-aiff" }, + { "aifc", "audio/aiff" }, + { "aifc", "audio/x-aiff" }, + { "aiff", "audio/aiff" }, + { "aiff", "audio/x-aiff" }, + { "aim", "application/x-aim" }, + { "aip", "text/x-audiosoft-intra" }, + { "ani", "application/x-navi-animation" }, + { "aos", "application/x-nokia-9000-communicator-add-on-software" }, + { "aps", "application/mime" }, + { "arc", "application/octet-stream" }, + { "arj", "application/arj" }, + { "arj", "application/octet-stream" }, + { "art", "image/x-jg" }, + { "asf", "video/x-ms-asf" }, + { "asm", "text/x-asm" }, + { "asp", "text/asp" }, + { "asx", "application/x-mplayer2" }, + { "asx", "video/x-ms-asf" }, + { "asx", "video/x-ms-asf-plugin" }, + { "au", "audio/basic" }, + { "au", "audio/x-au" }, + { "avi", "application/x-troff-msvideo" }, + { "avi", "video/avi" }, + { "avi", "video/msvideo" }, + { "avi", "video/x-msvideo" }, + { "avs", "video/avs-video" }, + { "bcpio", "application/x-bcpio" }, + { "bin", "application/mac-binary" }, + { "bin", "application/macbinary" }, + { "bin", "application/octet-stream" }, + { "bin", "application/x-binary" }, + { "bin", "application/x-macbinary" }, + { "bm", "image/bmp" }, + { "bmp", "image/bmp" }, + { "bmp", "image/x-windows-bmp" }, + { "boo", "application/book" }, + { "book", "application/book" }, + { "boz", "application/x-bzip2" }, + { "bsh", "application/x-bsh" }, + { "bz", "application/x-bzip" }, + { "bz2", "application/x-bzip2" }, + { "c", "text/plain" }, + { "c", "text/x-c" }, + { "c++", "text/plain" }, + { "cat", "application/vnd.ms-pki.seccat" }, + { "cc", "text/plain" }, + { "cc", "text/x-c" }, + { "ccad", "application/clariscad" }, + { "cco", "application/x-cocoa" }, + { "cdf", "application/cdf" }, + { "cdf", "application/x-cdf" }, + { "cdf", "application/x-netcdf" }, + { "cer", "application/pkix-cert" }, + { "cer", "application/x-x509-ca-cert" }, + { "cha", "application/x-chat" }, + { "chat", "application/x-chat" }, + { "class", "application/java" }, + { "class", "application/java-byte-code" }, + { "class", "application/x-java-class" }, + { "com", "application/octet-stream" }, + { "com", "text/plain" }, + { "conf", "text/plain" }, + { "cpio", "application/x-cpio" }, + { "cpp", "text/x-c" }, + { "cpt", "application/mac-compactpro" }, + { "cpt", "application/x-compactpro" }, + { "cpt", "application/x-cpt" }, + { "crl", "application/pkcs-crl" }, + { "crl", "application/pkix-crl" }, + { "crt", "application/pkix-cert" }, + { "crt", "application/x-x509-ca-cert" }, + { "crt", "application/x-x509-user-cert" }, + { "csh", "application/x-csh" }, + { "csh", "text/x-script.csh" }, + { "css", "application/x-pointplus" }, + { "css", "text/css" }, + { "cxx", "text/plain" }, + { "dcr", "application/x-director" }, + { "deepv", "application/x-deepv" }, + { "def", "text/plain" }, + { "der", "application/x-x509-ca-cert" }, + { "dif", "video/x-dv" }, + { "dir", "application/x-director" }, + { "dl", "video/dl" }, + { "dl", "video/x-dl" }, + { "doc", "application/msword" }, + { "dot", "application/msword" }, + { "dp", "application/commonground" }, + { "drw", "application/drafting" }, + { "dump", "application/octet-stream" }, + { "dv", "video/x-dv" }, + { "dvi", "application/x-dvi" }, + { "dwf", "drawing/x-dwf" }, + { "dwf", "model/vnd.dwf" }, + { "dwg", "application/acad" }, + { "dwg", "image/vnd.dwg" }, + { "dwg", "image/x-dwg" }, + { "dxf", "application/dxf" }, + { "dxf", "image/vnd.dwg" }, + { "dxf", "image/x-dwg" }, + { "dxr", "application/x-director" }, + { "el", "text/x-script.elisp" }, + { "elc", "application/x-bytecode.elisp" }, + { "elc", "application/x-elc" }, + { "env", "application/x-envoy" }, + { "eps", "application/postscript" }, + { "es", "application/x-esrehber" }, + { "etx", "text/x-setext" }, + { "evy", "application/envoy" }, + { "evy", "application/x-envoy" }, + { "exe", "application/octet-stream" }, + { "f", "text/plain" }, + { "f", "text/x-fortran" }, + { "f77", "text/x-fortran" }, + { "f90", "text/plain" }, + { "f90", "text/x-fortran" }, + { "fdf", "application/vnd.fdf" }, + { "fif", "application/fractals" }, + { "fif", "image/fif" }, + { "flac", "audio/flac" }, + { "fli", "video/fli" }, + { "fli", "video/x-fli" }, + { "flo", "image/florian" }, + { "flx", "text/vnd.fmi.flexstor" }, + { "fmf", "video/x-atomic3d-feature" }, + { "for", "text/plain" }, + { "for", "text/x-fortran" }, + { "fpx", "image/vnd.fpx" }, + { "fpx", "image/vnd.net-fpx" }, + { "frl", "application/freeloader" }, + { "funk", "audio/make" }, + { "g", "text/plain" }, + { "g3", "image/g3fax" }, + { "gif", "image/gif" }, + { "gl", "video/gl" }, + { "gl", "video/x-gl" }, + { "gsd", "audio/x-gsm" }, + { "gsm", "audio/x-gsm" }, + { "gsp", "application/x-gsp" }, + { "gss", "application/x-gss" }, + { "gtar", "application/x-gtar" }, + { "gz", "application/x-compressed" }, + { "gz", "application/x-gzip" }, + { "gzip", "application/x-gzip" }, + { "gzip", "multipart/x-gzip" }, + { "h", "text/plain" }, + { "h", "text/x-h" }, + { "hdf", "application/x-hdf" }, + { "help", "application/x-helpfile" }, + { "hgl", "application/vnd.hp-hpgl" }, + { "hh", "text/plain" }, + { "hh", "text/x-h" }, + { "hlb", "text/x-script" }, + { "hlp", "application/hlp" }, + { "hlp", "application/x-helpfile" }, + { "hlp", "application/x-winhelp" }, + { "hpg", "application/vnd.hp-hpgl" }, + { "hpgl", "application/vnd.hp-hpgl" }, + { "hqx", "application/binhex" }, + { "hqx", "application/binhex4" }, + { "hqx", "application/mac-binhex" }, + { "hqx", "application/mac-binhex40" }, + { "hqx", "application/x-binhex40" }, + { "hqx", "application/x-mac-binhex40" }, + { "hta", "application/hta" }, + { "htc", "text/x-component" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "htmls", "text/html" }, + { "htt", "text/webviewhtml" }, + { "htx", "text/html" }, + { "ice", "x-conference/x-cooltalk" }, + { "ico", "image/x-icon" }, + { "idc", "text/plain" }, + { "ief", "image/ief" }, + { "iefs", "image/ief" }, + { "iges", "application/iges" }, + { "iges", "model/iges" }, + { "igs", "application/iges" }, + { "igs", "model/iges" }, + { "ima", "application/x-ima" }, + { "imap", "application/x-httpd-imap" }, + { "inf", "application/inf" }, + { "ins", "application/x-internett-signup" }, + { "ip", "application/x-ip2" }, + { "isu", "video/x-isvideo" }, + { "it", "audio/it" }, + { "iv", "application/x-inventor" }, + { "ivr", "i-world/i-vrml" }, + { "ivy", "application/x-livescreen" }, + { "jam", "audio/x-jam" }, + { "jav", "text/plain" }, + { "jav", "text/x-java-source" }, + { "java", "text/plain" }, + { "java", "text/x-java-source" }, + { "jcm", "application/x-java-commerce" }, + { "jfif", "image/jpeg" }, + { "jfif", "image/pjpeg" }, + { "jpe", "image/jpeg" }, + { "jpe", "image/pjpeg" }, + { "jpeg", "image/jpeg" }, + { "jpeg", "image/pjpeg" }, + { "jpg", "image/jpeg" }, + { "jpg", "image/pjpeg" }, + { "jps", "image/x-jps" }, + { "js", "application/x-javascript" }, + { "json", "application/json" }, + { "jut", "image/jutvision" }, + { "kar", "audio/midi" }, + { "kar", "music/x-karaoke" }, + { "ksh", "application/x-ksh" }, + { "ksh", "text/x-script.ksh" }, + { "la", "audio/nspaudio" }, + { "la", "audio/x-nspaudio" }, + { "lam", "audio/x-liveaudio" }, + { "latex", "application/x-latex" }, + { "lha", "application/lha" }, + { "lha", "application/octet-stream" }, + { "lha", "application/x-lha" }, + { "lhx", "application/octet-stream" }, + { "list", "text/plain" }, + { "lma", "audio/nspaudio" }, + { "lma", "audio/x-nspaudio" }, + { "log", "text/plain" }, + { "lsp", "application/x-lisp" }, + { "lsp", "text/x-script.lisp" }, + { "lst", "text/plain" }, + { "lsx", "text/x-la-asf" }, + { "ltx", "application/x-latex" }, + { "lzh", "application/octet-stream" }, + { "lzh", "application/x-lzh" }, + { "lzx", "application/lzx" }, + { "lzx", "application/octet-stream" }, + { "lzx", "application/x-lzx" }, + { "m", "text/plain" }, + { "m", "text/x-m" }, + { "m1v", "video/mpeg" }, + { "m2a", "audio/mpeg" }, + { "m2v", "video/mpeg" }, + { "m3u", "audio/x-mpequrl" }, + { "man", "application/x-troff-man" }, + { "map", "application/x-navimap" }, + { "mar", "text/plain" }, + { "mbd", "application/mbedlet" }, + { "mc$", "application/x-magic-cap-package-1.0" }, + { "mcd", "application/mcad" }, + { "mcd", "application/x-mathcad" }, + { "mcf", "image/vasa" }, + { "mcf", "text/mcf" }, + { "mcp", "application/netmc" }, + { "me", "application/x-troff-me" }, + { "mht", "message/rfc822" }, + { "mhtml", "message/rfc822" }, + { "mid", "application/x-midi" }, + { "mid", "audio/midi" }, + { "mid", "audio/x-mid" }, + { "mid", "audio/x-midi" }, + { "mid", "music/crescendo" }, + { "mid", "x-music/x-midi" }, + { "midi", "application/x-midi" }, + { "midi", "audio/midi" }, + { "midi", "audio/x-mid" }, + { "midi", "audio/x-midi" }, + { "midi", "music/crescendo" }, + { "midi", "x-music/x-midi" }, + { "mif", "application/x-frame" }, + { "mif", "application/x-mif" }, + { "mime", "message/rfc822" }, + { "mime", "www/mime" }, + { "mjf", "audio/x-vnd.audioexplosion.mjuicemediafile" }, + { "mjpg", "video/x-motion-jpeg" }, + { "mm", "application/base64" }, + { "mm", "application/x-meme" }, + { "mme", "application/base64" }, + { "mod", "audio/mod" }, + { "mod", "audio/x-mod" }, + { "moov", "video/quicktime" }, + { "mov", "video/quicktime" }, + { "movie", "video/x-sgi-movie" }, + { "mp2", "audio/mpeg" }, + { "mp2", "audio/x-mpeg" }, + { "mp2", "video/mpeg" }, + { "mp2", "video/x-mpeg" }, + { "mp2", "video/x-mpeq2a" }, + { "mp3", "audio/mpeg" }, + { "mp3", "audio/mpeg3" }, + { "mp3", "audio/x-mpeg-3" }, + { "mp3", "video/mpeg" }, + { "mp3", "video/x-mpeg" }, + { "mpa", "audio/mpeg" }, + { "mpa", "video/mpeg" }, + { "mpc", "application/x-project" }, + { "mpe", "video/mpeg" }, + { "mpeg", "video/mpeg" }, + { "mpg", "audio/mpeg" }, + { "mpg", "video/mpeg" }, + { "mpga", "audio/mpeg" }, + { "mpp", "application/vnd.ms-project" }, + { "mpt", "application/x-project" }, + { "mpv", "application/x-project" }, + { "mpx", "application/x-project" }, + { "mrc", "application/marc" }, + { "ms", "application/x-troff-ms" }, + { "mv", "video/x-sgi-movie" }, + { "my", "audio/make" }, + { "mzz", "application/x-vnd.audioexplosion.mzz" }, + { "nap", "image/naplps" }, + { "naplps", "image/naplps" }, + { "nc", "application/x-netcdf" }, + { "ncm", "application/vnd.nokia.configuration-message" }, + { "nif", "image/x-niff" }, + { "niff", "image/x-niff" }, + { "nix", "application/x-mix-transfer" }, + { "nsc", "application/x-conference" }, + { "nvd", "application/x-navidoc" }, + { "o", "application/octet-stream" }, + { "oda", "application/oda" }, + { "omc", "application/x-omc" }, + { "omcd", "application/x-omcdatamaker" }, + { "omcr", "application/x-omcregerator" }, + { "p", "text/x-pascal" }, + { "p10", "application/pkcs10" }, + { "p10", "application/x-pkcs10" }, + { "p12", "application/pkcs-12" }, + { "p12", "application/x-pkcs12" }, + { "p7a", "application/x-pkcs7-signature" }, + { "p7c", "application/pkcs7-mime" }, + { "p7c", "application/x-pkcs7-mime" }, + { "p7m", "application/pkcs7-mime" }, + { "p7m", "application/x-pkcs7-mime" }, + { "p7r", "application/x-pkcs7-certreqresp" }, + { "p7s", "application/pkcs7-signature" }, + { "part", "application/pro_eng" }, + { "pas", "text/pascal" }, + { "pbm", "image/x-portable-bitmap" }, + { "pcl", "application/vnd.hp-pcl" }, + { "pcl", "application/x-pcl" }, + { "pct", "image/x-pict" }, + { "pcx", "image/x-pcx" }, + { "pdb", "chemical/x-pdb" }, + { "pdf", "application/pdf" }, + { "pfunk", "audio/make" }, + { "pfunk", "audio/make.my.funk" }, + { "pgm", "image/x-portable-graymap" }, + { "pgm", "image/x-portable-greymap" }, + { "pic", "image/pict" }, + { "pict", "image/pict" }, + { "pkg", "application/x-newton-compatible-pkg" }, + { "pko", "application/vnd.ms-pki.pko" }, + { "pl", "text/plain" }, + { "pl", "text/x-script.perl" }, + { "plx", "application/x-pixclscript" }, + { "pm", "image/x-xpixmap" }, + { "pm", "text/x-script.perl-module" }, + { "pm4", "application/x-pagemaker" }, + { "pm5", "application/x-pagemaker" }, + { "png", "image/png" }, + { "pnm", "application/x-portable-anymap" }, + { "pnm", "image/x-portable-anymap" }, + { "pot", "application/mspowerpoint" }, + { "pot", "application/vnd.ms-powerpoint" }, + { "pov", "model/x-pov" }, + { "ppa", "application/vnd.ms-powerpoint" }, + { "ppm", "image/x-portable-pixmap" }, + { "pps", "application/mspowerpoint" }, + { "pps", "application/vnd.ms-powerpoint" }, + { "ppt", "application/mspowerpoint" }, + { "ppt", "application/powerpoint" }, + { "ppt", "application/vnd.ms-powerpoint" }, + { "ppt", "application/x-mspowerpoint" }, + { "ppz", "application/mspowerpoint" }, + { "pre", "application/x-freelance" }, + { "prt", "application/pro_eng" }, + { "ps", "application/postscript" }, + { "psd", "application/octet-stream" }, + { "pvu", "paleovu/x-pv" }, + { "pwz", "application/vnd.ms-powerpoint" }, + { "py", "text/x-script.python" }, + { "pyc", "application/x-bytecode.python" }, + { "qcp", "audio/vnd.qcelp" }, + { "qd3", "x-world/x-3dmf" }, + { "qd3d", "x-world/x-3dmf" }, + { "qif", "image/x-quicktime" }, + { "qt", "video/quicktime" }, + { "qtc", "video/x-qtc" }, + { "qti", "image/x-quicktime" }, + { "qtif", "image/x-quicktime" }, + { "ra", "audio/x-pn-realaudio" }, + { "ra", "audio/x-pn-realaudio-plugin" }, + { "ra", "audio/x-realaudio" }, + { "ram", "audio/x-pn-realaudio" }, + { "ras", "application/x-cmu-raster" }, + { "ras", "image/cmu-raster" }, + { "ras", "image/x-cmu-raster" }, + { "rast", "image/cmu-raster" }, + { "rexx", "text/x-script.rexx" }, + { "rf", "image/vnd.rn-realflash" }, + { "rgb", "image/x-rgb" }, + { "rm", "application/vnd.rn-realmedia" }, + { "rm", "audio/x-pn-realaudio" }, + { "rmi", "audio/mid" }, + { "rmm", "audio/x-pn-realaudio" }, + { "rmp", "audio/x-pn-realaudio" }, + { "rmp", "audio/x-pn-realaudio-plugin" }, + { "rng", "application/ringing-tones" }, + { "rng", "application/vnd.nokia.ringing-tone" }, + { "rnx", "application/vnd.rn-realplayer" }, + { "roff", "application/x-troff" }, + { "rp", "image/vnd.rn-realpix" }, + { "rpm", "audio/x-pn-realaudio-plugin" }, + { "rt", "text/richtext" }, + { "rt", "text/vnd.rn-realtext" }, + { "rtf", "application/rtf" }, + { "rtf", "application/x-rtf" }, + { "rtf", "text/richtext" }, + { "rtx", "application/rtf" }, + { "rtx", "text/richtext" }, + { "rv", "video/vnd.rn-realvideo" }, + { "s", "text/x-asm" }, + { "s3m", "audio/s3m" }, + { "saveme", "application/octet-stream" }, + { "sbk", "application/x-tbook" }, + { "scm", "application/x-lotusscreencam" }, + { "scm", "text/x-script.guile" }, + { "scm", "text/x-script.scheme" }, + { "scm", "video/x-scm" }, + { "sdml", "text/plain" }, + { "sdp", "application/sdp" }, + { "sdp", "application/x-sdp" }, + { "sdr", "application/sounder" }, + { "sea", "application/sea" }, + { "sea", "application/x-sea" }, + { "set", "application/set" }, + { "sgm", "text/sgml" }, + { "sgm", "text/x-sgml" }, + { "sgml", "text/sgml" }, + { "sgml", "text/x-sgml" }, + { "sh", "application/x-bsh" }, + { "sh", "application/x-sh" }, + { "sh", "application/x-shar" }, + { "sh", "text/x-script.sh" }, + { "shar", "application/x-bsh" }, + { "shar", "application/x-shar" }, + { "shtml", "text/html" }, + { "shtml", "text/x-server-parsed-html" }, + { "sid", "audio/x-psid" }, + { "sit", "application/x-sit" }, + { "sit", "application/x-stuffit" }, + { "skd", "application/x-koan" }, + { "skm", "application/x-koan" }, + { "skp", "application/x-koan" }, + { "skt", "application/x-koan" }, + { "sl", "application/x-seelogo" }, + { "smi", "application/smil" }, + { "smil", "application/smil" }, + { "snd", "audio/basic" }, + { "snd", "audio/x-adpcm" }, + { "sol", "application/solids" }, + { "spc", "application/x-pkcs7-certificates" }, + { "spc", "text/x-speech" }, + { "spl", "application/futuresplash" }, + { "spr", "application/x-sprite" }, + { "sprite", "application/x-sprite" }, + { "src", "application/x-wais-source" }, + { "ssi", "text/x-server-parsed-html" }, + { "ssm", "application/streamingmedia" }, + { "sst", "application/vnd.ms-pki.certstore" }, + { "step", "application/step" }, + { "stl", "application/sla" }, + { "stl", "application/vnd.ms-pki.stl" }, + { "stl", "application/x-navistyle" }, + { "stp", "application/step" }, + { "sv4cpio,", "application/x-sv4cpio" }, + { "sv4crc", "application/x-sv4crc" }, + { "svf", "image/vnd.dwg" }, + { "svf", "image/x-dwg" }, + { "svr", "application/x-world" }, + { "svr", "x-world/x-svr" }, + { "swf", "application/x-shockwave-flash" }, + { "t", "application/x-troff" }, + { "talk", "text/x-speech" }, + { "tar", "application/x-tar" }, + { "tbk", "application/toolbook" }, + { "tbk", "application/x-tbook" }, + { "tcl", "application/x-tcl" }, + { "tcl", "text/x-script.tcl" }, + { "tcsh", "text/x-script.tcsh" }, + { "tex", "application/x-tex" }, + { "texi", "application/x-texinfo" }, + { "texinfo,", "application/x-texinfo" }, + { "text", "application/plain" }, + { "text", "text/plain" }, + { "tgz", "application/gnutar" }, + { "tgz", "application/x-compressed" }, + { "tif", "image/tiff" }, + { "tif", "image/x-tiff" }, + { "tiff", "image/tiff" }, + { "tiff", "image/x-tiff" }, + { "tr", "application/x-troff" }, + { "tsi", "audio/tsp-audio" }, + { "tsp", "application/dsptype" }, + { "tsp", "audio/tsplayer" }, + { "tsv", "text/tab-separated-values" }, + { "turbot", "image/florian" }, + { "txt", "text/plain" }, + { "uil", "text/x-uil" }, + { "uni", "text/uri-list" }, + { "unis", "text/uri-list" }, + { "unv", "application/i-deas" }, + { "uri", "text/uri-list" }, + { "uris", "text/uri-list" }, + { "ustar", "application/x-ustar" }, + { "ustar", "multipart/x-ustar" }, + { "uu", "application/octet-stream" }, + { "uu", "text/x-uuencode" }, + { "uue", "text/x-uuencode" }, + { "vcd", "application/x-cdlink" }, + { "vcs", "text/x-vcalendar" }, + { "vda", "application/vda" }, + { "vdo", "video/vdo" }, + { "vew", "application/groupwise" }, + { "viv", "video/vivo" }, + { "viv", "video/vnd.vivo" }, + { "vivo", "video/vivo" }, + { "vivo", "video/vnd.vivo" }, + { "vmd", "application/vocaltec-media-desc" }, + { "vmf", "application/vocaltec-media-file" }, + { "voc", "audio/voc" }, + { "voc", "audio/x-voc" }, + { "vos", "video/vosaic" }, + { "vox", "audio/voxware" }, + { "vqe", "audio/x-twinvq-plugin" }, + { "vqf", "audio/x-twinvq" }, + { "vql", "audio/x-twinvq-plugin" }, + { "vrml", "application/x-vrml" }, + { "vrml", "model/vrml" }, + { "vrml", "x-world/x-vrml" }, + { "vrt", "x-world/x-vrt" }, + { "vsd", "application/x-visio" }, + { "vst", "application/x-visio" }, + { "vsw", "application/x-visio" }, + { "w60", "application/wordperfect6.0" }, + { "w61", "application/wordperfect6.1" }, + { "w6w", "application/msword" }, + { "wav", "audio/wav" }, + { "wav", "audio/x-wav" }, + { "wb1", "application/x-qpro" }, + { "wbmp", "image/vnd.wap.wbmp" }, + { "web", "application/vnd.xara" }, + { "wiz", "application/msword" }, + { "wk1", "application/x-123" }, + { "wmf", "windows/metafile" }, + { "wml", "text/vnd.wap.wml" }, + { "wmlc", "application/vnd.wap.wmlc" }, + { "wmls", "text/vnd.wap.wmlscript" }, + { "wmlsc", "application/vnd.wap.wmlscriptc" }, + { "word", "application/msword" }, + { "wp", "application/wordperfect" }, + { "wp5", "application/wordperfect" }, + { "wp5", "application/wordperfect6.0" }, + { "wp6", "application/wordperfect" }, + { "wpd", "application/wordperfect" }, + { "wpd", "application/x-wpwin" }, + { "wq1", "application/x-lotus" }, + { "wri", "application/mswrite" }, + { "wri", "application/x-wri" }, + { "wrl", "application/x-world" }, + { "wrl", "model/vrml" }, + { "wrl", "x-world/x-vrml" }, + { "wrz", "model/vrml" }, + { "wrz", "x-world/x-vrml" }, + { "wsc", "text/scriplet" }, + { "wsrc", "application/x-wais-source" }, + { "wtk", "application/x-wintalk" }, + { "xbm", "image/x-xbitmap" }, + { "xbm", "image/x-xbm" }, + { "xbm", "image/xbm" }, + { "xdr", "video/x-amt-demorun" }, + { "xgz", "xgl/drawing" }, + { "xif", "image/vnd.xiff" }, + { "xl", "application/excel" }, + { "xla", "application/excel" }, + { "xla", "application/x-excel" }, + { "xla", "application/x-msexcel" }, + { "xlb", "application/excel" }, + { "xlb", "application/vnd.ms-excel" }, + { "xlb", "application/x-excel" }, + { "xlc", "application/excel" }, + { "xlc", "application/vnd.ms-excel" }, + { "xlc", "application/x-excel" }, + { "xld", "application/excel" }, + { "xld", "application/x-excel" }, + { "xlk", "application/excel" }, + { "xlk", "application/x-excel" }, + { "xll", "application/excel" }, + { "xll", "application/vnd.ms-excel" }, + { "xll", "application/x-excel" }, + { "xlm", "application/excel" }, + { "xlm", "application/vnd.ms-excel" }, + { "xlm", "application/x-excel" }, + { "xls", "application/excel" }, + { "xls", "application/vnd.ms-excel" }, + { "xls", "application/x-excel" }, + { "xls", "application/x-msexcel" }, + { "xlt", "application/excel" }, + { "xlt", "application/x-excel" }, + { "xlv", "application/excel" }, + { "xlv", "application/x-excel" }, + { "xlw", "application/excel" }, + { "xlw", "application/vnd.ms-excel" }, + { "xlw", "application/x-excel" }, + { "xlw", "application/x-msexcel" }, + { "xm", "audio/xm" }, + { "xml", "application/xml" }, + { "xml", "text/xml" }, + { "xmz", "xgl/movie" }, + { "xpix", "application/x-vnd.ls-xpix" }, + { "xpm", "image/x-xpixmap" }, + { "xpm", "image/xpm" }, + { "x-png", "image/png" }, + { "xsr", "video/x-amt-showrun" }, + { "xwd", "image/x-xwd" }, + { "xwd", "image/x-xwindowdump" }, + { "xyz", "chemical/x-pdb" }, + { "z", "application/x-compress" }, + { "z", "application/x-compressed" }, + { "zip", "application/x-compressed" }, + { "zip", "application/x-zip-compressed" }, + { "zip", "application/zip" }, + { "zip", "multipart/x-zip" }, + { "zoo", "application/octet-stream" }, + }; + + template + static std::multimap createMultiMap (EntryToPair&& entryToPair) + { + std::pair transformed[std::size (initialEntries)]; + std::transform (std::begin (initialEntries), + std::end (initialEntries), + std::begin (transformed), + entryToPair); + + return { std::begin (transformed), + std::end (transformed) }; + } + + std::multimap typeForExtension = createMultiMap ([] (auto e) + { + return std::make_pair (e.fileExtension, e.mimeType); + }); + + std::multimap extensionForType = createMultiMap ([] (auto e) + { + return std::make_pair (e.mimeType, e.fileExtension); + }); +}; + +} // namespace + +namespace detail +{ + +void MimeTypeTable::registerCustomMimeTypeForFileExtension (const String& mimeType, const String& fileExtension) +{ + Table::get().addEntry ({ fileExtension.toRawUTF8(), mimeType.toRawUTF8() }); +} + +StringArray MimeTypeTable::getMimeTypesForFileExtension (const String& fileExtension) +{ + return Table::get().getTypesForExtension (fileExtension); +} + +StringArray MimeTypeTable::getFileExtensionsForMimeType (const String& mimeType) +{ + return Table::get().getExtensionsForType (mimeType); +} + +} // namespace detail + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.h b/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.h new file mode 100644 index 00000000..60c0f13a --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.h @@ -0,0 +1,40 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 6 End-User License + Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). + + End User License Agreement: www.juce.com/juce-6-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#pragma once + +namespace juce::detail +{ + +struct MimeTypeTable +{ + static void registerCustomMimeTypeForFileExtension (const String& mimeType, const String& fileExtension); + + static StringArray getMimeTypesForFileExtension (const String& fileExtension); + + static StringArray getFileExtensionsForMimeType (const String& mimeType); +}; + +} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp index 7c42214e..b7c007cb 100644 --- a/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -126,6 +126,8 @@ struct JSONParser break; } + + default: break; } } @@ -304,60 +306,6 @@ struct JSONParser //============================================================================== struct JSONFormatter { - static void write (OutputStream& out, const var& v, - int indentLevel, bool allOnOneLine, int maximumDecimalPlaces) - { - if (v.isString()) - { - out << '"'; - writeString (out, v.toString().getCharPointer()); - out << '"'; - } - else if (v.isVoid()) - { - out << "null"; - } - else if (v.isUndefined()) - { - out << "undefined"; - } - else if (v.isBool()) - { - out << (static_cast (v) ? "true" : "false"); - } - else if (v.isDouble()) - { - auto d = static_cast (v); - - if (juce_isfinite (d)) - { - out << serialiseDouble (d); - } - else - { - out << "null"; - } - } - else if (v.isArray()) - { - writeArray (out, *v.getArray(), indentLevel, allOnOneLine, maximumDecimalPlaces); - } - else if (v.isObject()) - { - if (auto* object = v.getDynamicObject()) - object->writeAsJSON (out, indentLevel, allOnOneLine, maximumDecimalPlaces); - else - jassertfalse; // Only DynamicObjects can be converted to JSON! - } - else - { - // Can't convert these other types of object to JSON! - jassert (! (v.isMethod() || v.isBinaryData())); - - out << v.toString(); - } - } - static void writeEscapedChar (OutputStream& out, const unsigned short value) { out << "\\u" << String::toHexString ((int) value).paddedLeft ('0', 4); @@ -414,36 +362,39 @@ struct JSONFormatter out.writeRepeatedByte (' ', (size_t) numSpaces); } - static void writeArray (OutputStream& out, const Array& array, - int indentLevel, bool allOnOneLine, int maximumDecimalPlaces) + static void writeArray (OutputStream& out, const Array& array, const JSON::FormatOptions& format) { out << '['; if (! array.isEmpty()) { - if (! allOnOneLine) + if (format.getSpacing() == JSON::Spacing::multiLine) out << newLine; for (int i = 0; i < array.size(); ++i) { - if (! allOnOneLine) - writeSpaces (out, indentLevel + indentSize); + if (format.getSpacing() == JSON::Spacing::multiLine) + writeSpaces (out, format.getIndentLevel() + indentSize); - write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine, maximumDecimalPlaces); + JSON::writeToStream (out, array.getReference (i), format.withIndentLevel (format.getIndentLevel() + indentSize)); if (i < array.size() - 1) { - if (allOnOneLine) - out << ", "; - else - out << ',' << newLine; + out << ","; + + switch (format.getSpacing()) + { + case JSON::Spacing::none: break; + case JSON::Spacing::singleLine: out << ' '; break; + case JSON::Spacing::multiLine: out << newLine; break; + } } - else if (! allOnOneLine) + else if (format.getSpacing() == JSON::Spacing::multiLine) out << newLine; } - if (! allOnOneLine) - writeSpaces (out, indentLevel); + if (format.getSpacing() == JSON::Spacing::multiLine) + writeSpaces (out, format.getIndentLevel()); } out << ']'; @@ -452,6 +403,67 @@ struct JSONFormatter enum { indentSize = 2 }; }; + +void JSON::writeToStream (OutputStream& out, const var& v, const FormatOptions& opt) +{ + if (v.isString()) + { + out << '"'; + JSONFormatter::writeString (out, v.toString().getCharPointer()); + out << '"'; + } + else if (v.isVoid()) + { + out << "null"; + } + else if (v.isUndefined()) + { + out << "undefined"; + } + else if (v.isBool()) + { + out << (static_cast (v) ? "true" : "false"); + } + else if (v.isDouble()) + { + auto d = static_cast (v); + + if (juce_isfinite (d)) + { + out << serialiseDouble (d); + } + else + { + out << "null"; + } + } + else if (v.isArray()) + { + JSONFormatter::writeArray (out, *v.getArray(), opt); + } + else if (v.isObject()) + { + if (auto* object = v.getDynamicObject()) + object->writeAsJSON (out, opt); + else + jassertfalse; // Only DynamicObjects can be converted to JSON! + } + else + { + // Can't convert these other types of object to JSON! + jassert (! (v.isMethod() || v.isBinaryData())); + + out << v.toString(); + } +} + +String JSON::toString (const var& v, const FormatOptions& opt) +{ + MemoryOutputStream mo { 1024 }; + writeToStream (mo, v, opt); + return mo.toUTF8(); +} + //============================================================================== var JSON::parse (const String& text) { @@ -500,14 +512,14 @@ Result JSON::parse (const String& text, var& result) String JSON::toString (const var& data, const bool allOnOneLine, int maximumDecimalPlaces) { - MemoryOutputStream mo (1024); - JSONFormatter::write (mo, data, 0, allOnOneLine, maximumDecimalPlaces); - return mo.toUTF8(); + return toString (data, FormatOptions{}.withSpacing (allOnOneLine ? Spacing::singleLine : Spacing::multiLine) + .withMaxDecimalPlaces (maximumDecimalPlaces)); } void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine, int maximumDecimalPlaces) { - JSONFormatter::write (output, data, 0, allOnOneLine, maximumDecimalPlaces); + writeToStream (output, data, FormatOptions{}.withSpacing (allOnOneLine ? Spacing::singleLine : Spacing::multiLine) + .withMaxDecimalPlaces (maximumDecimalPlaces)); } String JSON::escapeString (StringRef s) @@ -543,7 +555,7 @@ Result JSON::parseQuotedString (String::CharPointerType& t, var& result) //============================================================================== #if JUCE_UNIT_TESTS -class JSONTests : public UnitTest +class JSONTests final : public UnitTest { public: JSONTests() @@ -651,10 +663,10 @@ class JSONTests : public UnitTest if (i > 0) v = createRandomVar (r, 0); - const bool oneLine = r.nextBool(); - String asString (JSON::toString (v, oneLine)); - var parsed = JSON::parse ("[" + asString + "]")[0]; - String parsedString (JSON::toString (parsed, oneLine)); + const auto oneLine = r.nextBool(); + const auto asString = JSON::toString (v, oneLine); + const auto parsed = JSON::parse ("[" + asString + "]")[0]; + const auto parsedString = JSON::toString (parsed, oneLine); expect (asString.isNotEmpty() && parsedString == asString); } } diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.h b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.h index 576be322..3112bd20 100644 --- a/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.h +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -88,6 +88,47 @@ class JUCE_API JSON */ static var parse (InputStream& input); + enum class Spacing + { + none, ///< All optional whitespace should be omitted + singleLine, ///< All output should be on a single line, but with some additional spacing, e.g. after commas and colons + multiLine, ///< Newlines and spaces will be included in the output, in order to make it easy to read for humans + }; + + /** + Allows formatting var objects as JSON with various configurable options. + */ + class [[nodiscard]] FormatOptions + { + public: + /** Returns a copy of this Formatter with the specified spacing. */ + FormatOptions withSpacing (Spacing x) const { return withMember (*this, &FormatOptions::spacing, x); } + + /** Returns a copy of this Formatter with the specified maximum number of decimal places. + This option determines the precision of floating point numbers in scientific notation. + */ + FormatOptions withMaxDecimalPlaces (int x) const { return withMember (*this, &FormatOptions::maxDecimalPlaces, x); } + + /** Returns a copy of this Formatter with the specified indent level. + This should only be necessary when serialising multiline nested types. + */ + FormatOptions withIndentLevel (int x) const { return withMember (*this, &FormatOptions::indent, x); } + + /** Returns the spacing used by this Formatter. */ + Spacing getSpacing() const { return spacing; } + + /** Returns the maximum number of decimal places used by this Formatter. */ + int getMaxDecimalPlaces() const { return maxDecimalPlaces; } + + /** Returns the indent level of this Formatter. */ + int getIndentLevel() const { return indent; } + + private: + Spacing spacing = Spacing::multiLine; + int maxDecimalPlaces = 15; + int indent = 0; + }; + //============================================================================== /** Returns a string which contains a JSON-formatted representation of the var object. If allOnOneLine is true, the result will be compacted into a single line of text @@ -100,6 +141,13 @@ class JUCE_API JSON bool allOnOneLine = false, int maximumDecimalPlaces = 15); + /** Returns a string which contains a JSON-formatted representation of the var object, using + formatting described by the FormatOptions parameter. + @see writeToStream + */ + static String toString (const var& objectToFormat, + const FormatOptions& formatOptions); + /** Parses a string that was created with the toString() method. This is slightly different to the parse() methods because they will reject primitive values and only accept array or object definitions, whereas this method will handle @@ -119,6 +167,14 @@ class JUCE_API JSON bool allOnOneLine = false, int maximumDecimalPlaces = 15); + /** Writes a JSON-formatted representation of the var object to the given stream, using + formatting described by the FormatOptions parameter. + @see toString + */ + static void writeToStream (OutputStream& output, + const var& objectToFormat, + const FormatOptions& formatOptions); + /** Returns a version of a string with any extended characters escaped. */ static String escapeString (StringRef); diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_JSONSerialisation.h b/JuceLibraryCode/modules/juce_core/javascript/juce_JSONSerialisation.h new file mode 100644 index 00000000..3ae48e74 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_JSONSerialisation.h @@ -0,0 +1,534 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +/** + Options that control conversion from arbitrary types to juce::var. + + @see ToVar + + @tags{Core} +*/ +class ToVarOptions +{ +public: + /** By default, conversion will serialise the type using the marshallingVersion defined for + that type. Setting an explicit version allows the type to be serialised as an earlier + version. + */ + [[nodiscard]] ToVarOptions withExplicitVersion (std::optional x) const { return withMember (*this, &ToVarOptions::explicitVersion, x); } + + /** By default, conversion will include version information for any type with a non-null + marshallingVersion. Setting versionIncluded to false will cause the version info to be + omitted, which is useful in situations where the version information is not needed + (e.g. when presenting transient information to the user, rather than writing data to + disk that must be deserialised in the future). + */ + [[nodiscard]] ToVarOptions withVersionIncluded (bool x) const { return withMember (*this, &ToVarOptions::versionIncluded, x); } + + /** @see withExplicitVersion() */ + [[nodiscard]] auto getExplicitVersion() const { return explicitVersion; } + + /** @see withVersionIncluded(). */ + [[nodiscard]] auto getVersionIncluded() const { return versionIncluded; } + +private: + std::optional> explicitVersion; + bool versionIncluded = true; +}; + +/** + Allows converting an object of arbitrary type to var. + + To use this, you must first ensure that the type passed to convert is set up for serialisation. + For details of what this entails, see the docs for SerialisationTraits. + + In short, the constant 'marshallingVersion', and either the single function 'serialise()', or + the function pair 'load()' and 'save()' must be defined for the type. These may be defined + as public members of the type T itself, or as public members of juce::SerialisationTraits, + which is a specialisation of the SerialisationTraits template struct for the type T. + + @see FromVar + + @tags{Core} +*/ +class ToVar +{ +public: + using Options = ToVarOptions; + + /** Attempts to convert the argument to a var using the serialisation utilities specified for + that type. + + This will return a non-null optional if conversion succeeds, or nullopt if conversion fails. + */ + template + static std::optional convert (const T& t, const Options& options = {}) + { + return Visitor::convert (t, options); + } + +private: + class Visitor + { + public: + template + static std::optional convert (const T& t, const Options& options) + { + constexpr auto fallbackVersion = detail::ForwardingSerialisationTraits::marshallingVersion; + const auto versionToUse = options.getExplicitVersion() + .value_or (fallbackVersion); + + if (versionToUse > fallbackVersion) + { + // The requested explicit version is higher than the declared version of the type. + return std::nullopt; + } + + Visitor visitor { versionToUse, options.getVersionIncluded() }; + detail::doSave (visitor, t); + return visitor.value; + } + + std::optional getVersion() const { return version; } + + template + void operator() (Ts&&... ts) + { + (visit (std::forward (ts)), ...); + } + + private: + Visitor (const std::optional& explicitVersion, bool includeVersion) + : version (explicitVersion), + value ([&]() -> var + { + if (! (version.has_value() && includeVersion)) + return var(); + + auto obj = std::make_unique(); + obj->setProperty ("__version__", *version); + return obj.release(); + }()), + versionIncluded (includeVersion) {} + + template + void visit (const T& t) + { + if constexpr (std::is_integral_v) + { + push ((int64) t); + } + else if constexpr (std::is_floating_point_v) + { + push ((double) t); + } + else if (auto converted = convert (t)) + { + push (*converted); + } + else + { + value.reset(); + } + } + + template + void visit (const Named& named) + { + if (! value.has_value()) + return; + + if (value == var()) + value = new DynamicObject; + + auto* obj = value->getDynamicObject(); + + if (obj == nullptr) + { + // Serialisation failure! This may be caused by archiving a primitive or + // SerialisationSize, and then attempting to archive a named pair to the same + // archive instance. + // When using named pairs, *all* items serialised with a particular archiver must be + // named pairs. + jassertfalse; + + value.reset(); + return; + } + + if (! trySetProperty (*obj, named)) + value.reset(); + } + + template + void visit (const SerialisationSize&) + { + push (Array{}); + } + + void visit (const bool& t) + { + push (t); + } + + void visit (const String& t) + { + push (t); + } + + void visit (const var& t) + { + push (t); + } + + template + std::optional convert (const T& t) + { + return convert (t, Options{}.withVersionIncluded (versionIncluded)); + } + + void push (var v) + { + if (! value.has_value()) + return; + + if (*value == var()) + *value = v; + else if (auto* array = value->getArray()) + array->add (v); + else + value.reset(); + } + + template + bool trySetProperty (DynamicObject& obj, const Named& n) + { + if (const auto converted = convert (n.value)) + { + obj.setProperty (Identifier (std::string (n.name)), *converted); + return true; + } + + return false; + } + + std::optional version; + std::optional value; + bool versionIncluded = true; + }; +}; + +//============================================================================== +/** + Allows converting a var to an object of arbitrary type. + + To use this, you must first ensure that the type passed to convert is set up for serialisation. + For details of what this entails, see the docs for SerialisationTraits. + + In short, the constant 'marshallingVersion', and either the single function 'serialise()', or + the function pair 'load()' and 'save()' must be defined for the type. These may be defined + as public members of the type T itself, or as public members of juce::SerialisationTraits, + which is a specialisation of the SerialisationTraits template struct for the type T. + + @see ToVar + + @tags{Core} +*/ +class FromVar +{ +public: + /** Attempts to convert a var to an instance of type T. + + This will return a non-null optional if conversion succeeds, or nullopt if conversion fails. + */ + template + static std::optional convert (const var& v) + { + return Visitor::convert (v); + } + +private: + class Visitor + { + public: + template + static std::optional convert (const var& v) + { + const auto version = [&]() -> std::optional + { + if (auto* obj = v.getDynamicObject()) + if (obj->hasProperty ("__version__")) + return (int) obj->getProperty ("__version__"); + + return std::nullopt; + }(); + + Visitor visitor { version, v }; + T t{}; + detail::doLoad (visitor, t); + return ! visitor.failed ? std::optional (std::move (t)) + : std::nullopt; + } + + std::optional getVersion() const { return version; } + + template + void operator() (Ts&&... ts) + { + (visit (std::forward (ts)), ...); + } + + private: + Visitor (std::optional vn, const var& i) + : version (vn), input (i) {} + + template + void visit (T& t) + { + if constexpr (std::is_integral_v) + { + readPrimitive (std::in_place_type, t); + } + else if constexpr (std::is_floating_point_v) + { + readPrimitive (std::in_place_type, t); + } + else + { + auto node = getNodeToRead(); + + if (! node.has_value()) + return; + + auto converted = convert (*node); + + if (converted.has_value()) + t = *converted; + else + failed = true; + } + } + + template + void visit (const Named& named) + { + auto node = getNodeToRead(); + + if (! node.has_value()) + return; + + auto* obj = node->getDynamicObject(); + + failed = obj == nullptr || ! tryGetProperty (*obj, named); + } + + template + void visit (const SerialisationSize& t) + { + if (failed) + return; + + if (auto* array = input.getArray()) + { + t.size = static_cast (array->size()); + currentArrayIndex = 0; + } + else + { + failed = true; + } + } + + void visit (bool& t) + { + readPrimitive (std::in_place_type, t); + } + + void visit (String& t) + { + readPrimitive (std::in_place_type, t); + } + + void visit (var& t) + { + t = input; + } + + static std::optional pullTyped (std::in_place_type_t, const var& source) + { + return source.isDouble() ? std::optional ((double) source) : std::nullopt; + } + + static std::optional pullTyped (std::in_place_type_t, const var& source) + { + return source.isInt() || source.isInt64() ? std::optional ((int64) source) : std::nullopt; + } + + static std::optional pullTyped (std::in_place_type_t, const var& source) + { + return std::optional ((bool) source); + } + + static std::optional pullTyped (std::in_place_type_t, const var& source) + { + return source.isString() ? std::optional (source.toString()) : std::nullopt; + } + + std::optional getNodeToRead() + { + if (failed) + return std::nullopt; + + if (currentArrayIndex == std::numeric_limits::max()) + return input; + + const auto* array = input.getArray(); + + if (array == nullptr) + return input; + + if ((int) currentArrayIndex < array->size()) + return array->getReference ((int) currentArrayIndex++); + + failed = true; + return std::nullopt; + } + + template + void readPrimitive (std::in_place_type_t tag, T& t) + { + auto node = getNodeToRead(); + + if (! node.has_value()) + return; + + auto typed = pullTyped (tag, *node); + + if (typed.has_value()) + t = static_cast (*typed); + else + failed = true; + } + + template + static bool tryGetProperty (const DynamicObject& obj, const Named& n) + { + const Identifier identifier (String (n.name.data(), n.name.size())); + + if (! obj.hasProperty (identifier)) + return false; + + const auto converted = convert (obj.getProperty (identifier)); + + if (! converted.has_value()) + return false; + + n.value = *converted; + return true; + } + + std::optional version; + var input; + size_t currentArrayIndex = std::numeric_limits::max(); + bool failed = false; + }; +}; + +//============================================================================== +/** + This template-overloaded class can be used to convert between var and custom types. + + If not specialised, the variant converter will attempt to use serialisation functions + if they are detected for the given type. + For details of what this entails, see the docs for SerialisationTraits. + + In short, the constant 'marshallingVersion', and either the single function 'serialise()', or + the function pair 'load()' and 'save()' must be defined for the type. These may be defined + as public members of the type T itself, or as public members of juce::SerialisationTraits, + which is a specialisation of the SerialisationTraits template struct for the type T. + + @see ToVar, FromVar + + @tags{Core} +*/ +template +struct VariantConverter +{ + static Type fromVar (const var& v) + { + return static_cast (v); + } + + static var toVar (const Type& t) + { + return t; + } +}; + +#ifndef DOXYGEN + +template <> +struct VariantConverter +{ + static String fromVar (const var& v) { return v.toString(); } + static var toVar (const String& s) { return s; } +}; + +#endif + +/** + A helper type that can be used to implement specialisations of VariantConverter that use + FromVar::convert and ToVar::convert internally. + + If you've already implemented SerialisationTraits for a specific type, and don't want to write + a custom VariantConverter that duplicates that implementation, you can instead write: + @code + template <> + struct juce::VariantConverter : public juce::StrictVariantConverter {}; + @endcode + + @tags{Core} +*/ +template +struct StrictVariantConverter +{ + static_assert (detail::serialisationKind != detail::SerialisationKind::none); + + static Type fromVar (const var& v) + { + auto converted = FromVar::convert (v); + jassert (converted.has_value()); + return std::move (converted).value_or (Type{}); + } + + static var toVar (const Type& t) + { + auto converted = ToVar::convert<> (t); + jassert (converted.has_value()); + return std::move (converted).value_or (var{}); + } +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_JSONSerialisation_test.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_JSONSerialisation_test.cpp new file mode 100644 index 00000000..526c3898 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_JSONSerialisation_test.cpp @@ -0,0 +1,617 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +struct TypeWithExternalUnifiedSerialisation +{ + int a; + std::string b; + std::vector c; + std::map d; + + auto operator== (const TypeWithExternalUnifiedSerialisation& other) const + { + const auto tie = [] (const auto& x) { return std::tie (x.a, x.b, x.c, x.d); }; + return tie (*this) == tie (other); + } + + auto operator!= (const TypeWithExternalUnifiedSerialisation& other) const { return ! operator== (other); } +}; + +template <> +struct SerialisationTraits +{ + static constexpr auto marshallingVersion = 2; + + template + static void serialise (Archive& archive, T& t) + { + archive (named ("a", t.a), + named ("b", t.b), + named ("c", t.c), + named ("d", t.d)); + } +}; + +// Now that the serialiser trait is visible, it should be detected +static_assert (detail::serialisationKind == detail::SerialisationKind::external); + +struct TypeWithInternalUnifiedSerialisation +{ + double a; + float b; + String c; + StringArray d; + + auto operator== (const TypeWithInternalUnifiedSerialisation& other) const + { + const auto tie = [] (const auto& x) { return std::tie (x.a, x.b, x.c, x.d); }; + return tie (*this) == tie (other); + } + + auto operator!= (const TypeWithInternalUnifiedSerialisation& other) const { return ! operator== (other); } + + static constexpr auto marshallingVersion = 5; + + template + static void serialise (Archive& archive, T& t) + { + archive (named ("a", t.a), + named ("b", t.b), + named ("c", t.c), + named ("d", t.d)); + } +}; + +static_assert (detail::serialisationKind == detail::SerialisationKind::internal); + +struct TypeWithExternalSplitSerialisation +{ + std::optional a; + Array b; + + auto operator== (const TypeWithExternalSplitSerialisation& other) const + { + const auto tie = [] (const auto& x) { return std::tie (x.a, x.b); }; + return tie (*this) == tie (other); + } + + auto operator!= (const TypeWithExternalSplitSerialisation& other) const { return ! operator== (other); } +}; + +template <> +struct SerialisationTraits +{ + static constexpr auto marshallingVersion = 10; + + template + static void load (Archive& archive, TypeWithExternalSplitSerialisation& t) + { + std::optional a; + Array hexStrings; + archive (named ("a", a), named ("b", hexStrings)); + + Array b; + + for (auto& i : hexStrings) + b.add (i.getHexValue32()); + + t = { a, b }; + } + + template + static void save (Archive& archive, const TypeWithExternalSplitSerialisation& t) + { + Array hexStrings; + + for (auto& i : t.b) + hexStrings.add ("0x" + String::toHexString (i)); + + archive (named ("a", t.a), named ("b", hexStrings)); + } +}; + +// Now that the serialiser trait is visible, it should be detected +static_assert (detail::serialisationKind == detail::SerialisationKind::external); + +// Check that serialisation kinds are correctly detected for primitives +static_assert (detail::serialisationKind == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind< int8_t> == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind< uint8_t> == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind< int16_t> == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind< int32_t> == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind< int64_t> == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind == detail::SerialisationKind::primitive); +static_assert (detail::serialisationKind == detail::SerialisationKind::primitive); + +// Check that serialisation is disabled for types with no serialsation defined +static_assert (detail::serialisationKind == detail::SerialisationKind::none); +static_assert (detail::serialisationKind == detail::SerialisationKind::none); + +struct TypeWithInternalSplitSerialisation +{ + std::string a; + Array b; + + auto operator== (const TypeWithInternalSplitSerialisation& other) const + { + const auto tie = [] (const auto& x) { return std::tie (x.a, x.b); }; + return tie (*this) == tie (other); + } + + auto operator!= (const TypeWithInternalSplitSerialisation& other) const { return ! operator== (other); } + + static constexpr auto marshallingVersion = 1; + + template + static void load (Archive& archive, TypeWithInternalSplitSerialisation& t) + { + std::string a; + Array hexStrings; + archive (named ("a", a), named ("b", hexStrings)); + + Array b; + + for (auto& i : hexStrings) + b.add (i.getHexValue32()); + + t = { a, b }; + } + + template + static void save (Archive& archive, const TypeWithInternalSplitSerialisation& t) + { + Array hexStrings; + + for (auto& i : t.b) + hexStrings.add ("0x" + String::toHexString (i)); + + archive (named ("a", t.a), named ("b", hexStrings)); + } +}; + +static_assert (detail::serialisationKind == detail::SerialisationKind::internal); + +struct TypeWithBrokenObjectSerialisation +{ + int a; + int b; + + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T& t) + { + // Archiving a named value will start reading/writing an object + archive (named ("a", t.a)); + // Archiving a non-named value will assume that the current node is convertible + archive (t.b); + } +}; + +struct TypeWithBrokenPrimitiveSerialisation +{ + int a; + int b; + + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T& t) + { + // Archiving a non-named value will assume that the current node is convertible + archive (t.a); + // Archiving a named value will fail if the current node holds a non-object type + archive (named ("b", t.b)); + } +}; + +struct TypeWithBrokenArraySerialisation +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T&) + { + size_t size = 5; + archive (size); + + // serialisationSize should always be serialised first! + archive (serialisationSize (size)); + } +}; + +struct TypeWithBrokenNestedSerialisation +{ + int a; + TypeWithBrokenObjectSerialisation b; + + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T& t) + { + archive (named ("a", t.a), named ("b", t.b)); + } +}; + +struct TypeWithBrokenDynamicSerialisation +{ + std::vector a; + + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T& t) + { + archive (t.a); + } +}; + +struct TypeWithVersionedSerialisation +{ + int a{}, b{}, c{}, d{}; + + bool operator== (const TypeWithVersionedSerialisation& other) const + { + const auto tie = [] (const auto& x) { return std::tie (x.a, x.b, x.c, x.d); }; + return tie (*this) == tie (other); + } + + bool operator!= (const TypeWithVersionedSerialisation& other) const { return ! operator== (other); } + + static constexpr auto marshallingVersion = 3; + + template + static void serialise (Archive& archive, T& t) + { + archive (named ("a", t.a)); + + if (archive.getVersion() >= 1) + archive (named ("b", t.b)); + + if (archive.getVersion() >= 2) + archive (named ("c", t.c)); + + if (archive.getVersion() >= 3) + archive (named ("d", t.d)); + } +}; + +struct TypeWithRawVarLast +{ + int status = 0; + String message; + var extended; + + bool operator== (const TypeWithRawVarLast& other) const + { + const auto tie = [] (const auto& x) { return std::tie (x.status, x.message, x.extended); }; + return tie (*this) == tie (other); + } + + bool operator!= (const TypeWithRawVarLast& other) const { return ! operator== (other); } + + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T& t) + { + archive (named ("status", t.status), + named ("message", t.message), + named ("extended", t.extended)); + } +}; + +struct TypeWithRawVarFirst +{ + int status = 0; + String message; + var extended; + + bool operator== (const TypeWithRawVarFirst& other) const + { + const auto tie = [] (const auto& x) { return std::tie (x.status, x.message, x.extended); }; + return tie (*this) == tie (other); + } + + bool operator!= (const TypeWithRawVarFirst& other) const { return ! operator== (other); } + + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T& t) + { + archive (named ("extended", t.extended), + named ("status", t.status), + named ("message", t.message)); + } +}; + +struct TypeWithInnerVar +{ + int eventId = 0; + var payload; + + bool operator== (const TypeWithInnerVar& other) const + { + const auto tie = [] (const auto& x) { return std::tie (x.eventId, x.payload); }; + return tie (*this) == tie (other); + } + + bool operator!= (const TypeWithInnerVar& other) const { return ! operator== (other); } + + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T& t) + { + archive (named ("eventId", t.eventId), + named ("payload", t.payload)); + } +}; + +class JSONSerialisationTest final : public UnitTest +{ +public: + JSONSerialisationTest() : UnitTest ("JSONSerialisation", UnitTestCategories::json) {} + + void runTest() override + { + beginTest ("ToVar"); + { + expectDeepEqual (ToVar::convert (false), false); + expectDeepEqual (ToVar::convert (true), true); + expectDeepEqual (ToVar::convert (1), 1); + expectDeepEqual (ToVar::convert (5.0f), 5.0); + expectDeepEqual (ToVar::convert (6LL), 6); + expectDeepEqual (ToVar::convert ("hello world"), "hello world"); + expectDeepEqual (ToVar::convert (String ("hello world")), "hello world"); + expectDeepEqual (ToVar::convert (std::vector { 1, 2, 3 }), Array { 1, 2, 3 }); + expectDeepEqual (ToVar::convert (TypeWithExternalUnifiedSerialisation { 7, + "hello world", + { 5, 6, 7 }, + { { "foo", 4 }, { "bar", 5 } } }), + JSONUtils::makeObject ({ { "__version__", 2 }, + { "a", 7 }, + { "b", "hello world" }, + { "c", Array { 5, 6, 7 } }, + { "d", + Array { JSONUtils::makeObject ({ { "first", "bar" }, + { "second", 5 } }), + JSONUtils::makeObject ({ { "first", "foo" }, + { "second", 4 } }) } } })); + expectDeepEqual (ToVar::convert (TypeWithInternalUnifiedSerialisation { 7.89, + 4.321f, + "custom string", + { "foo", "bar", "baz" } }), + JSONUtils::makeObject ({ { "__version__", 5 }, + { "a", 7.89 }, + { "b", 4.321f }, + { "c", "custom string" }, + { "d", Array { "foo", "bar", "baz" } } })); + expectDeepEqual (ToVar::convert (TypeWithExternalSplitSerialisation { "string", { 1, 2, 3 } }), + JSONUtils::makeObject ({ { "__version__", 10 }, + { "a", JSONUtils::makeObject ({ { "engaged", true }, { "value", "string" } }) }, + { "b", Array { "0x1", "0x2", "0x3" } } })); + expectDeepEqual (ToVar::convert (TypeWithInternalSplitSerialisation { "string", { 16, 32, 48 } }), + JSONUtils::makeObject ({ { "__version__", 1 }, + { "a", "string" }, + { "b", Array { "0x10", "0x20", "0x30" } } })); + + expect (ToVar::convert (TypeWithBrokenObjectSerialisation { 1, 2 }) == std::nullopt); + expect (ToVar::convert (TypeWithBrokenPrimitiveSerialisation { 1, 2 }) == std::nullopt); + expect (ToVar::convert (TypeWithBrokenArraySerialisation {}) == std::nullopt); + expect (ToVar::convert (TypeWithBrokenNestedSerialisation {}) == std::nullopt); + expect (ToVar::convert (TypeWithBrokenDynamicSerialisation { std::vector (10) }) == std::nullopt); + + expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }), + JSONUtils::makeObject ({ { "__version__", 3 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + { "d", 4 } })); + expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withVersionIncluded (false)), + JSONUtils::makeObject ({ { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + { "d", 4 } })); + // Requested explicit version is higher than the type's declared version + expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (4)), + std::nullopt); + expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (3)), + JSONUtils::makeObject ({ { "__version__", 3 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + { "d", 4 } })); + expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (2)), + JSONUtils::makeObject ({ { "__version__", 2 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 } })); + expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (1)), + JSONUtils::makeObject ({ { "__version__", 1 }, + { "a", 1 }, + { "b", 2 } })); + expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (0)), + JSONUtils::makeObject ({ { "__version__", 0 }, + { "a", 1 } })); + expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (std::nullopt)), + JSONUtils::makeObject ({ { "a", 1 } })); + + expectDeepEqual (ToVar::convert (TypeWithRawVarLast { 200, "success", true }), + JSONUtils::makeObject ({ { "status", 200 }, { "message", "success" }, { "extended", true } })); + expectDeepEqual (ToVar::convert (TypeWithRawVarLast { 200, + "success", + JSONUtils::makeObject ({ { "status", 123.456 }, + { "message", "failure" }, + { "extended", true } }) }), + JSONUtils::makeObject ({ { "status", 200 }, + { "message", "success" }, + { "extended", JSONUtils::makeObject ({ { "status", 123.456 }, + { "message", "failure" }, + { "extended", true } }) } })); + + expectDeepEqual (ToVar::convert (TypeWithRawVarFirst { 200, "success", true }), + JSONUtils::makeObject ({ { "status", 200 }, { "message", "success" }, { "extended", true } })); + expectDeepEqual (ToVar::convert (TypeWithRawVarFirst { 200, + "success", + JSONUtils::makeObject ({ { "status", 123.456 }, + { "message", "failure" }, + { "extended", true } }) }), + JSONUtils::makeObject ({ { "status", 200 }, + { "message", "success" }, + { "extended", JSONUtils::makeObject ({ { "status", 123.456 }, + { "message", "failure" }, + { "extended", true } }) } })); + + const auto payload = JSONUtils::makeObject ({ { "foo", 1 }, { "bar", 2 } }); + expectDeepEqual (ToVar::convert (TypeWithInnerVar { 404, payload }), + JSONUtils::makeObject ({ { "eventId", 404 }, { "payload", payload } })); + } + + beginTest ("FromVar"); + { + expect (FromVar::convert (JSON::fromString ("false")) == false); + expect (FromVar::convert (JSON::fromString ("true")) == true); + expect (FromVar::convert (JSON::fromString ("0")) == false); + expect (FromVar::convert (JSON::fromString ("1")) == true); + expect (FromVar::convert (JSON::fromString ("1")) == 1); + expect (FromVar::convert (JSON::fromString ("5.0f")) == 5.0f); + expect (FromVar::convert (JSON::fromString ("6")) == 6); + expect (FromVar::convert (JSON::fromString ("\"hello world\"")) == "hello world"); + expect (FromVar::convert> (JSON::fromString ("[1,2,3]")) == std::vector { 1, 2, 3 }); + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 2 }, + { "a", 7 }, + { "b", "hello world" }, + { "c", Array { 5, 6, 7 } }, + { "d", + Array { JSONUtils::makeObject ({ { "first", "bar" }, + { "second", 5 } }), + JSONUtils::makeObject ({ { "first", "foo" }, + { "second", 4 } }) } } })) + == TypeWithExternalUnifiedSerialisation { 7, + "hello world", + { 5, 6, 7 }, + { { "foo", 4 }, { "bar", 5 } } }); + + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 5 }, + { "a", 7.89 }, + { "b", 4.321f }, + { "c", "custom string" }, + { "d", Array { "foo", "bar", "baz" } } })) + == TypeWithInternalUnifiedSerialisation { 7.89, + 4.321f, + "custom string", + { "foo", "bar", "baz" } }); + + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 10 }, + { "a", JSONUtils::makeObject ({ { "engaged", true }, { "value", "string" } }) }, + { "b", Array { "0x1", "0x2", "0x3" } } })) + == TypeWithExternalSplitSerialisation { "string", { 1, 2, 3 } }); + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 1 }, + { "a", "string" }, + { "b", Array { "0x10", "0x20", "0x30" } } })) + == TypeWithInternalSplitSerialisation { "string", { 16, 32, 48 } }); + + expect (FromVar::convert (JSON::fromString ("null")) == std::nullopt); + expect (FromVar::convert (JSON::fromString ("null")) == std::nullopt); + expect (FromVar::convert (JSON::fromString ("null")) == std::nullopt); + expect (FromVar::convert (JSON::fromString ("null")) == std::nullopt); + expect (FromVar::convert (JSON::fromString ("null")) == std::nullopt); + + expect (FromVar::convert (JSONUtils::makeObject ({ { "a", 7.89 }, + { "b", 4.321f } })) + == std::nullopt); + + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 3 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + { "d", 4 } })) + == TypeWithVersionedSerialisation { 1, 2, 3, 4 }); + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 4 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + { "d", 4 } })) + == TypeWithVersionedSerialisation { 1, 2, 3, 4 }); + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 2 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 } })) + == TypeWithVersionedSerialisation { 1, 2, 3, 0 }); + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 1 }, + { "a", 1 }, + { "b", 2 } })) + == TypeWithVersionedSerialisation { 1, 2, 0, 0 }); + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 0 }, + { "a", 1 } })) + == TypeWithVersionedSerialisation { 1, 0, 0, 0 }); + expect (FromVar::convert (JSONUtils::makeObject ({ { "a", 1 } })) + == TypeWithVersionedSerialisation { 1, 0, 0, 0 }); + + const auto raw = JSONUtils::makeObject ({ { "status", 200 }, { "message", "success" }, { "extended", "another string" } }); + expect (FromVar::convert (raw) == TypeWithRawVarLast { 200, "success", "another string" }); + expect (FromVar::convert (raw) == TypeWithRawVarFirst { 200, "success", "another string" }); + + const var payloads[] { JSONUtils::makeObject ({ { "foo", 1 }, { "bar", 2 } }), + var (Array { 1, 2 }), + var() }; + + for (const auto& payload : payloads) + { + const auto objectWithPayload = JSONUtils::makeObject ({ { "eventId", 404 }, { "payload", payload } }); + expect (FromVar::convert (objectWithPayload) == TypeWithInnerVar { 404, payload }); + } + } + } + +private: + void expectDeepEqual (const std::optional& a, const std::optional& b) + { + const auto text = a.has_value() && b.has_value() + ? JSON::toString (*a) + " != " + JSON::toString (*b) + : String(); + expect (deepEqual (a, b), text); + } + + static bool deepEqual (const std::optional& a, const std::optional& b) + { + if (a.has_value() && b.has_value()) + return JSONUtils::deepEqual (*a, *b); + + return a == b; + } +}; + +static JSONSerialisationTest jsonSerialisationTest; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_JSONUtils.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_JSONUtils.cpp new file mode 100644 index 00000000..29297d4c --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_JSONUtils.cpp @@ -0,0 +1,218 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +var JSONUtils::makeObject (const std::map& source) +{ + auto result = std::make_unique(); + + for (const auto& [name, value] : source) + result->setProperty (name, value); + + return var (result.release()); +} + +var JSONUtils::makeObjectWithKeyFirst (const std::map& source, + Identifier key) +{ + auto result = std::make_unique(); + + if (const auto iter = source.find (key); iter != source.end()) + result->setProperty (key, iter->second); + + for (const auto& [name, value] : source) + if (name != key) + result->setProperty (name, value); + + return var (result.release()); +} + +std::optional JSONUtils::setPointer (const var& v, + String pointer, + const var& newValue) +{ + if (pointer.isEmpty()) + return newValue; + + if (! pointer.startsWith ("/")) + { + // This is not a well-formed JSON pointer + jassertfalse; + return {}; + } + + const auto findResult = pointer.indexOfChar (1, '/'); + const auto pos = findResult < 0 ? pointer.length() : findResult; + const String head (pointer.begin() + 1, pointer.begin() + pos); + const String tail (pointer.begin() + pos, pointer.end()); + + const auto unescaped = head.replace ("~1", "/").replace ("~0", "~"); + + if (auto* object = v.getDynamicObject()) + { + if (const auto newProperty = setPointer (object->getProperty (unescaped), tail, newValue)) + { + auto cloned = object->clone(); + cloned->setProperty (unescaped, *newProperty); + return var (cloned.release()); + } + } + else if (auto* array = v.getArray()) + { + const auto index = [&]() -> size_t + { + if (unescaped == "-") + return (size_t) array->size(); + + if (unescaped == "0") + return 0; + + if (! unescaped.startsWith ("0")) + return (size_t) unescaped.getLargeIntValue(); + + return std::numeric_limits::max(); + }(); + + if (const auto newIndex = setPointer ((*array)[(int) index], tail, newValue)) + { + auto copied = *array; + + if ((int) index == copied.size()) + copied.add ({}); + + if (isPositiveAndBelow (index, copied.size())) + { + copied.getReference ((int) index) = *newIndex; + return var (copied); + } + } + } + + return {}; +} + +bool JSONUtils::deepEqual (const var& a, const var& b) +{ + const auto compareObjects = [] (const DynamicObject& x, const DynamicObject& y) + { + if (x.getProperties().size() != y.getProperties().size()) + return false; + + for (const auto& [key, value] : x.getProperties()) + { + if (! y.hasProperty (key)) + return false; + + if (! deepEqual (value, y.getProperty (key))) + return false; + } + + return true; + }; + + if (auto* i = a.getDynamicObject()) + if (auto* j = b.getDynamicObject()) + return compareObjects (*i, *j); + + if (auto* i = a.getArray()) + if (auto* j = b.getArray()) + return std::equal (i->begin(), i->end(), j->begin(), j->end(), [] (const var& x, const var& y) { return deepEqual (x, y); }); + + return a == b; +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class JSONUtilsTests final : public UnitTest +{ +public: + JSONUtilsTests() : UnitTest ("JSONUtils", UnitTestCategories::json) {} + + void runTest() override + { + beginTest ("JSON pointers"); + { + const auto obj = JSON::parse (R"({ "name": "PIANO 4" + , "lfoSpeed": 30 + , "lfoWaveform": "triangle" + , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] } + })"); + expectDeepEqual (JSONUtils::setPointer (obj, "", "hello world"), var ("hello world")); + expectDeepEqual (JSONUtils::setPointer (obj, "/lfoWaveform/foobar", "str"), std::nullopt); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":0,"bar":1})"), "/foo", 2), JSON::parse (R"({"foo":2,"bar":1})")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":0,"bar":1})"), "/baz", 2), JSON::parse (R"({"foo":0,"bar":1,"baz":2})")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":{},"bar":{}})"), "/foo/bar", 2), JSON::parse (R"({"foo":{"bar":2},"bar":{}})")); + expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/01", "str"), std::nullopt); + expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/10", "str"), std::nullopt); + expectDeepEqual (JSONUtils::setPointer (obj, "/lfoSpeed", 10), JSON::parse (R"({ "name": "PIANO 4" + , "lfoSpeed": 10 + , "lfoWaveform": "triangle" + , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] } + })")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"([0,1,2])"), "/0", "bang"), JSON::parse (R"(["bang",1,2])")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"([0,1,2])"), "/0", "bang"), JSON::parse (R"(["bang",1,2])")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"/":"fizz"})"), "/~1", "buzz"), JSON::parse (R"({"/":"buzz"})")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"~":"fizz"})"), "/~0", "buzz"), JSON::parse (R"({"~":"buzz"})")); + expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/0", 80), JSON::parse (R"({ "name": "PIANO 4" + , "lfoSpeed": 30 + , "lfoWaveform": "triangle" + , "pitchEnvelope": { "rates": [80,67,95,60], "levels": [50,50,50,50] } + })")); + expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/levels/0", 80), JSON::parse (R"({ "name": "PIANO 4" + , "lfoSpeed": 30 + , "lfoWaveform": "triangle" + , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [80,50,50,50] } + })")); + expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/levels/-", 100), JSON::parse (R"({ "name": "PIANO 4" + , "lfoSpeed": 30 + , "lfoWaveform": "triangle" + , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50,100] } + })")); + } + } + + void expectDeepEqual (const std::optional& a, const std::optional& b) + { + const auto text = a.has_value() && b.has_value() + ? JSON::toString (*a) + " != " + JSON::toString (*b) + : String(); + expect (deepEqual (a, b), text); + } + + static bool deepEqual (const std::optional& a, const std::optional& b) + { + if (a.has_value() && b.has_value()) + return JSONUtils::deepEqual (*a, *b); + + return a == b; + } +}; + +static JSONUtilsTests jsonUtilsTests; + +#endif + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_JSONUtils.h b/JuceLibraryCode/modules/juce_core/javascript/juce_JSONUtils.h new file mode 100644 index 00000000..f35d19fd --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_JSONUtils.h @@ -0,0 +1,67 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +/** + A mini namespace to hold utility functions for working with juce::vars. + + @tags{Core} +*/ +struct JSONUtils +{ + /** No constructor. */ + JSONUtils() = delete; + + /** Given a JSON array/object 'v', a string representing a JSON pointer, + and a new property value 'newValue', returns a copy of 'v' where the + property or array index referenced by the pointer has been set to 'newValue'. + + If the pointer cannot be followed, due to referencing missing array indices + or fields, then this returns nullopt. + + For more details, check the JSON Pointer RFC 6901: + https://datatracker.ietf.org/doc/html/rfc6901 + */ + static std::optional setPointer (const var& v, String pointer, const var& newValue); + + /** Converts the provided key/value pairs into a JSON object. */ + static var makeObject (const std::map& source); + + /** Converts the provided key/value pairs into a JSON object with the provided + key at the first position in the object. + + This is useful because the MIDI-CI spec requires that certain fields (e.g. + status) should be placed at the beginning of a MIDI-CI header. + */ + static var makeObjectWithKeyFirst (const std::map& source, Identifier key); + + /** Returns true if and only if the contents of a match the contents of b. + + Unlike var::operator==, this will recursively check that contained DynamicObject and Array + instances compare equal. + */ + static bool deepEqual (const var& a, const var& b); +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp index f3438505..ebf4a653 100644 --- a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -54,13 +54,10 @@ namespace TokenTypes JUCE_DECLARE_JS_TOKEN (identifier, "$identifier") } -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4702) -#endif +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4702) //============================================================================== -struct JavascriptEngine::RootObject : public DynamicObject +struct JavascriptEngine::RootObject final : public DynamicObject { RootObject() { @@ -248,7 +245,7 @@ struct JavascriptEngine::RootObject : public DynamicObject struct Statement { Statement (const CodeLocation& l) noexcept : location (l) {} - virtual ~Statement() {} + virtual ~Statement() = default; enum ResultCode { ok = 0, returnWasHit, breakWasHit, continueWasHit }; virtual ResultCode perform (const Scope&, var*) const { return ok; } @@ -269,7 +266,7 @@ struct JavascriptEngine::RootObject : public DynamicObject using ExpPtr = std::unique_ptr; - struct BlockStatement : public Statement + struct BlockStatement final : public Statement { BlockStatement (const CodeLocation& l) noexcept : Statement (l) {} @@ -285,20 +282,20 @@ struct JavascriptEngine::RootObject : public DynamicObject OwnedArray statements; }; - struct IfStatement : public Statement + struct IfStatement final : public Statement { IfStatement (const CodeLocation& l) noexcept : Statement (l) {} ResultCode perform (const Scope& s, var* returnedValue) const override { - return (condition->getResult(s) ? trueBranch : falseBranch)->perform (s, returnedValue); + return (condition->getResult (s) ? trueBranch : falseBranch)->perform (s, returnedValue); } ExpPtr condition; std::unique_ptr trueBranch, falseBranch; }; - struct VarStatement : public Statement + struct VarStatement final : public Statement { VarStatement (const CodeLocation& l) noexcept : Statement (l) {} @@ -312,7 +309,7 @@ struct JavascriptEngine::RootObject : public DynamicObject ExpPtr initialiser; }; - struct LoopStatement : public Statement + struct LoopStatement final : public Statement { LoopStatement (const CodeLocation& l, bool isDo) noexcept : Statement (l), isDoLoop (isDo) {} @@ -342,7 +339,7 @@ struct JavascriptEngine::RootObject : public DynamicObject bool isDoLoop; }; - struct ReturnStatement : public Statement + struct ReturnStatement final : public Statement { ReturnStatement (const CodeLocation& l, Expression* v) noexcept : Statement (l), returnValue (v) {} @@ -355,26 +352,26 @@ struct JavascriptEngine::RootObject : public DynamicObject ExpPtr returnValue; }; - struct BreakStatement : public Statement + struct BreakStatement final : public Statement { BreakStatement (const CodeLocation& l) noexcept : Statement (l) {} ResultCode perform (const Scope&, var*) const override { return breakWasHit; } }; - struct ContinueStatement : public Statement + struct ContinueStatement final : public Statement { ContinueStatement (const CodeLocation& l) noexcept : Statement (l) {} ResultCode perform (const Scope&, var*) const override { return continueWasHit; } }; - struct LiteralValue : public Expression + struct LiteralValue final : public Expression { LiteralValue (const CodeLocation& l, const var& v) noexcept : Expression (l), value (v) {} var getResult (const Scope&) const override { return value; } var value; }; - struct UnqualifiedName : public Expression + struct UnqualifiedName final : public Expression { UnqualifiedName (const CodeLocation& l, const Identifier& n) noexcept : Expression (l), name (n) {} @@ -391,7 +388,7 @@ struct JavascriptEngine::RootObject : public DynamicObject Identifier name; }; - struct DotOperator : public Expression + struct DotOperator final : public Expression { DotOperator (const CodeLocation& l, ExpPtr& p, const Identifier& c) noexcept : Expression (l), parent (p.release()), child (c) {} @@ -425,7 +422,7 @@ struct JavascriptEngine::RootObject : public DynamicObject Identifier child; }; - struct ArraySubscript : public Expression + struct ArraySubscript final : public Expression { ArraySubscript (const CodeLocation& l) noexcept : Expression (l) {} @@ -479,7 +476,7 @@ struct JavascriptEngine::RootObject : public DynamicObject ExpPtr object, index; }; - struct BinaryOperatorBase : public Expression + struct BinaryOperatorBase : public Expression { BinaryOperatorBase (const CodeLocation& l, ExpPtr& a, ExpPtr& b, TokenType op) noexcept : Expression (l), lhs (a.release()), rhs (b.release()), operation (op) {} @@ -488,7 +485,7 @@ struct JavascriptEngine::RootObject : public DynamicObject TokenType operation; }; - struct BinaryOperator : public BinaryOperatorBase + struct BinaryOperator : public BinaryOperatorBase { BinaryOperator (const CodeLocation& l, ExpPtr& a, ExpPtr& b, TokenType op) noexcept : BinaryOperatorBase (l, a, b, op) {} @@ -519,27 +516,27 @@ struct JavascriptEngine::RootObject : public DynamicObject { location.throwError (getTokenName (operation) + " is not allowed on the " + typeName + " type"); return {}; } }; - struct EqualsOp : public BinaryOperator + struct EqualsOp final : public BinaryOperator { EqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::equals) {} var getWithUndefinedArg() const override { return true; } - var getWithDoubles (double a, double b) const override { return a == b; } + var getWithDoubles (double a, double b) const override { return exactlyEqual (a, b); } var getWithInts (int64 a, int64 b) const override { return a == b; } var getWithStrings (const String& a, const String& b) const override { return a == b; } var getWithArrayOrObject (const var& a, const var& b) const override { return a == b; } }; - struct NotEqualsOp : public BinaryOperator + struct NotEqualsOp final : public BinaryOperator { NotEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::notEquals) {} var getWithUndefinedArg() const override { return false; } - var getWithDoubles (double a, double b) const override { return a != b; } + var getWithDoubles (double a, double b) const override { return ! exactlyEqual (a, b); } var getWithInts (int64 a, int64 b) const override { return a != b; } var getWithStrings (const String& a, const String& b) const override { return a != b; } var getWithArrayOrObject (const var& a, const var& b) const override { return a != b; } }; - struct LessThanOp : public BinaryOperator + struct LessThanOp final : public BinaryOperator { LessThanOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::lessThan) {} var getWithDoubles (double a, double b) const override { return a < b; } @@ -547,7 +544,7 @@ struct JavascriptEngine::RootObject : public DynamicObject var getWithStrings (const String& a, const String& b) const override { return a < b; } }; - struct LessThanOrEqualOp : public BinaryOperator + struct LessThanOrEqualOp final : public BinaryOperator { LessThanOrEqualOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::lessThanOrEqual) {} var getWithDoubles (double a, double b) const override { return a <= b; } @@ -555,7 +552,7 @@ struct JavascriptEngine::RootObject : public DynamicObject var getWithStrings (const String& a, const String& b) const override { return a <= b; } }; - struct GreaterThanOp : public BinaryOperator + struct GreaterThanOp final : public BinaryOperator { GreaterThanOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::greaterThan) {} var getWithDoubles (double a, double b) const override { return a > b; } @@ -563,7 +560,7 @@ struct JavascriptEngine::RootObject : public DynamicObject var getWithStrings (const String& a, const String& b) const override { return a > b; } }; - struct GreaterThanOrEqualOp : public BinaryOperator + struct GreaterThanOrEqualOp final : public BinaryOperator { GreaterThanOrEqualOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::greaterThanOrEqual) {} var getWithDoubles (double a, double b) const override { return a >= b; } @@ -571,7 +568,7 @@ struct JavascriptEngine::RootObject : public DynamicObject var getWithStrings (const String& a, const String& b) const override { return a >= b; } }; - struct AdditionOp : public BinaryOperator + struct AdditionOp final : public BinaryOperator { AdditionOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::plus) {} var getWithDoubles (double a, double b) const override { return a + b; } @@ -579,95 +576,95 @@ struct JavascriptEngine::RootObject : public DynamicObject var getWithStrings (const String& a, const String& b) const override { return a + b; } }; - struct SubtractionOp : public BinaryOperator + struct SubtractionOp final : public BinaryOperator { SubtractionOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::minus) {} var getWithDoubles (double a, double b) const override { return a - b; } var getWithInts (int64 a, int64 b) const override { return a - b; } }; - struct MultiplyOp : public BinaryOperator + struct MultiplyOp final : public BinaryOperator { MultiplyOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::times) {} var getWithDoubles (double a, double b) const override { return a * b; } var getWithInts (int64 a, int64 b) const override { return a * b; } }; - struct DivideOp : public BinaryOperator + struct DivideOp final : public BinaryOperator { DivideOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::divide) {} - var getWithDoubles (double a, double b) const override { return b != 0 ? a / b : std::numeric_limits::infinity(); } - var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a / (double) b) : var (std::numeric_limits::infinity()); } + var getWithDoubles (double a, double b) const override { return exactlyEqual (b, 0.0) ? std::numeric_limits::infinity() : a / b; } + var getWithInts (int64 a, int64 b) const override { return b != 0 ? var ((double) a / (double) b) : var (std::numeric_limits::infinity()); } }; - struct ModuloOp : public BinaryOperator + struct ModuloOp final : public BinaryOperator { ModuloOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::modulo) {} - var getWithDoubles (double a, double b) const override { return b != 0 ? fmod (a, b) : std::numeric_limits::infinity(); } + var getWithDoubles (double a, double b) const override { return exactlyEqual (b, 0.0) ? std::numeric_limits::infinity() : fmod (a, b); } var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a % b) : var (std::numeric_limits::infinity()); } }; - struct BitwiseOrOp : public BinaryOperator + struct BitwiseOrOp final : public BinaryOperator { BitwiseOrOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseOr) {} var getWithInts (int64 a, int64 b) const override { return a | b; } }; - struct BitwiseAndOp : public BinaryOperator + struct BitwiseAndOp final : public BinaryOperator { BitwiseAndOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseAnd) {} var getWithInts (int64 a, int64 b) const override { return a & b; } }; - struct BitwiseXorOp : public BinaryOperator + struct BitwiseXorOp final : public BinaryOperator { BitwiseXorOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseXor) {} var getWithInts (int64 a, int64 b) const override { return a ^ b; } }; - struct LeftShiftOp : public BinaryOperator + struct LeftShiftOp final : public BinaryOperator { LeftShiftOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::leftShift) {} var getWithInts (int64 a, int64 b) const override { return ((int) a) << (int) b; } }; - struct RightShiftOp : public BinaryOperator + struct RightShiftOp final : public BinaryOperator { RightShiftOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::rightShift) {} var getWithInts (int64 a, int64 b) const override { return ((int) a) >> (int) b; } }; - struct RightShiftUnsignedOp : public BinaryOperator + struct RightShiftUnsignedOp final : public BinaryOperator { RightShiftUnsignedOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::rightShiftUnsigned) {} var getWithInts (int64 a, int64 b) const override { return (int) (((uint32) a) >> (int) b); } }; - struct LogicalAndOp : public BinaryOperatorBase + struct LogicalAndOp final : public BinaryOperatorBase { LogicalAndOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::logicalAnd) {} var getResult (const Scope& s) const override { return lhs->getResult (s) && rhs->getResult (s); } }; - struct LogicalOrOp : public BinaryOperatorBase + struct LogicalOrOp final : public BinaryOperatorBase { LogicalOrOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::logicalOr) {} var getResult (const Scope& s) const override { return lhs->getResult (s) || rhs->getResult (s); } }; - struct TypeEqualsOp : public BinaryOperatorBase + struct TypeEqualsOp final : public BinaryOperatorBase { TypeEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::typeEquals) {} var getResult (const Scope& s) const override { return areTypeEqual (lhs->getResult (s), rhs->getResult (s)); } }; - struct TypeNotEqualsOp : public BinaryOperatorBase + struct TypeNotEqualsOp final : public BinaryOperatorBase { TypeNotEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::typeNotEquals) {} var getResult (const Scope& s) const override { return ! areTypeEqual (lhs->getResult (s), rhs->getResult (s)); } }; - struct ConditionalOp : public Expression + struct ConditionalOp final : public Expression { ConditionalOp (const CodeLocation& l) noexcept : Expression (l) {} @@ -677,7 +674,7 @@ struct JavascriptEngine::RootObject : public DynamicObject ExpPtr condition, trueBranch, falseBranch; }; - struct Assignment : public Expression + struct Assignment final : public Expression { Assignment (const CodeLocation& l, ExpPtr& dest, ExpPtr& source) noexcept : Expression (l), target (dest.release()), newValue (source.release()) {} @@ -691,7 +688,7 @@ struct JavascriptEngine::RootObject : public DynamicObject ExpPtr target, newValue; }; - struct SelfAssignment : public Expression + struct SelfAssignment : public Expression { SelfAssignment (const CodeLocation& l, Expression* dest, Expression* source) noexcept : Expression (l), target (dest), newValue (source) {} @@ -708,7 +705,7 @@ struct JavascriptEngine::RootObject : public DynamicObject TokenType op; }; - struct PostAssignment : public SelfAssignment + struct PostAssignment final : public SelfAssignment { PostAssignment (const CodeLocation& l, Expression* dest, Expression* source) noexcept : SelfAssignment (l, dest, source) {} @@ -720,7 +717,7 @@ struct JavascriptEngine::RootObject : public DynamicObject } }; - struct FunctionCall : public Expression + struct FunctionCall : public Expression { FunctionCall (const CodeLocation& l) noexcept : Expression (l) {} @@ -764,7 +761,7 @@ struct JavascriptEngine::RootObject : public DynamicObject OwnedArray arguments; }; - struct NewOperator : public FunctionCall + struct NewOperator final : public FunctionCall { NewOperator (const CodeLocation& l) noexcept : FunctionCall (l) {} @@ -787,7 +784,7 @@ struct JavascriptEngine::RootObject : public DynamicObject } }; - struct ObjectDeclaration : public Expression + struct ObjectDeclaration final : public Expression { ObjectDeclaration (const CodeLocation& l) noexcept : Expression (l) {} @@ -796,7 +793,7 @@ struct JavascriptEngine::RootObject : public DynamicObject DynamicObject::Ptr newObject (new DynamicObject()); for (int i = 0; i < names.size(); ++i) - newObject->setProperty (names.getUnchecked(i), initialisers.getUnchecked(i)->getResult (s)); + newObject->setProperty (names.getUnchecked (i), initialisers.getUnchecked (i)->getResult (s)); return newObject.get(); } @@ -805,7 +802,7 @@ struct JavascriptEngine::RootObject : public DynamicObject OwnedArray initialisers; }; - struct ArrayDeclaration : public Expression + struct ArrayDeclaration final : public Expression { ArrayDeclaration (const CodeLocation& l) noexcept : Expression (l) {} @@ -814,17 +811,19 @@ struct JavascriptEngine::RootObject : public DynamicObject Array a; for (int i = 0; i < values.size(); ++i) - a.add (values.getUnchecked(i)->getResult (s)); + a.add (values.getUnchecked (i)->getResult (s)); // std::move() needed here for older compilers + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move") return std::move (a); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } OwnedArray values; }; //============================================================================== - struct FunctionObject : public DynamicObject + struct FunctionObject final : public DynamicObject { FunctionObject() noexcept {} @@ -834,9 +833,9 @@ struct JavascriptEngine::RootObject : public DynamicObject tb.parseFunctionParamsAndBody (*this); } - DynamicObject::Ptr clone() override { return *new FunctionObject (*this); } + std::unique_ptr clone() const override { return std::make_unique (*this); } - void writeAsJSON (OutputStream& out, int /*indentLevel*/, bool /*allOnOneLine*/, int /*maximumDecimalPlaces*/) override + void writeAsJSON (OutputStream& out, const JSON::FormatOptions&) override { out << "function " << functionCode; } @@ -849,7 +848,7 @@ struct JavascriptEngine::RootObject : public DynamicObject functionRoot->setProperty (thisIdent, args.thisObject); for (int i = 0; i < parameters.size(); ++i) - functionRoot->setProperty (parameters.getReference(i), + functionRoot->setProperty (parameters.getReference (i), i < args.numArguments ? args.arguments[i] : var::undefined()); var result; @@ -941,7 +940,7 @@ struct JavascriptEngine::RootObject : public DynamicObject { for (;;) { - p = p.findEndOfWhitespace(); + p.incrementToEndOfWhitespace(); if (*p == '/') { @@ -1057,7 +1056,7 @@ struct JavascriptEngine::RootObject : public DynamicObject }; //============================================================================== - struct ExpressionTreeBuilder : private TokenIterator + struct ExpressionTreeBuilder final : private TokenIterator { ExpressionTreeBuilder (const String code) : TokenIterator (code) {} @@ -1279,7 +1278,7 @@ struct JavascriptEngine::RootObject : public DynamicObject Expression* parseFunctionCall (FunctionCall* call, ExpPtr& function) { std::unique_ptr s (call); - s->object.reset (function.release()); + s->object = std::move (function); match (TokenTypes::openParen); while (currentType != TokenTypes::closeParen) @@ -1305,7 +1304,7 @@ struct JavascriptEngine::RootObject : public DynamicObject if (matchIf (TokenTypes::openBracket)) { std::unique_ptr s (new ArraySubscript (location)); - s->object.reset (input.release()); + s->object = std::move (input); s->index.reset (parseExpression()); match (TokenTypes::closeBracket); return parseSuffixes (s.release()); @@ -1514,7 +1513,7 @@ struct JavascriptEngine::RootObject : public DynamicObject Expression* parseTernaryOperator (ExpPtr& condition) { std::unique_ptr e (new ConditionalOp (location)); - e->condition.reset (condition.release()); + e->condition = std::move (condition); e->trueBranch.reset (parseExpression()); match (TokenTypes::colon); e->falseBranch.reset (parseExpression()); @@ -1532,7 +1531,7 @@ struct JavascriptEngine::RootObject : public DynamicObject static String getString (Args a, int index) noexcept { return get (a, index).toString(); } //============================================================================== - struct ObjectClass : public DynamicObject + struct ObjectClass final : public DynamicObject { ObjectClass() { @@ -1540,13 +1539,13 @@ struct JavascriptEngine::RootObject : public DynamicObject setMethod ("clone", cloneFn); } - static Identifier getClassName() { static const Identifier i ("Object"); return i; } - static var dump (Args a) { DBG (JSON::toString (a.thisObject)); ignoreUnused (a); return var::undefined(); } - static var cloneFn (Args a) { return a.thisObject.clone(); } + static Identifier getClassName() { static const Identifier i ("Object"); return i; } + static var dump ([[maybe_unused]] Args a) { DBG (JSON::toString (a.thisObject)); return var::undefined(); } + static var cloneFn (Args a) { return a.thisObject.clone(); } }; //============================================================================== - struct ArrayClass : public DynamicObject + struct ArrayClass final : public DynamicObject { ArrayClass() { @@ -1627,7 +1626,9 @@ struct JavascriptEngine::RootObject : public DynamicObject array->insert (start++, get (a, i)); // std::move() needed here for older compilers + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move") return std::move (itemsRemoved); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } return var::undefined(); @@ -1640,7 +1641,7 @@ struct JavascriptEngine::RootObject : public DynamicObject auto target = get (a, 0); for (int i = (a.numArguments > 1 ? getInt (a, 1) : 0); i < array->size(); ++i) - if (array->getReference(i) == target) + if (array->getReference (i) == target) return i; } @@ -1649,7 +1650,7 @@ struct JavascriptEngine::RootObject : public DynamicObject }; //============================================================================== - struct StringClass : public DynamicObject + struct StringClass final : public DynamicObject { StringClass() { @@ -1691,7 +1692,7 @@ struct JavascriptEngine::RootObject : public DynamicObject }; //============================================================================== - struct MathClass : public DynamicObject + struct MathClass final : public DynamicObject { MathClass() { @@ -1710,6 +1711,7 @@ struct JavascriptEngine::RootObject : public DynamicObject setMethod ("exp", Math_exp); setMethod ("pow", Math_pow); setMethod ("sqr", Math_sqr); setMethod ("sqrt", Math_sqrt); setMethod ("ceil", Math_ceil); setMethod ("floor", Math_floor); + setMethod ("hypot", Math_hypot); setProperty ("PI", MathConstants::pi); setProperty ("E", MathConstants::euler); @@ -1748,6 +1750,7 @@ struct JavascriptEngine::RootObject : public DynamicObject static var Math_sqrt (Args a) { return std::sqrt (getDouble (a, 0)); } static var Math_ceil (Args a) { return std::ceil (getDouble (a, 0)); } static var Math_floor (Args a) { return std::floor (getDouble (a, 0)); } + static var Math_hypot (Args a) { return std::hypot (getDouble (a, 0), getDouble (a, 1)); } // We can't use the std namespace equivalents of these functions without breaking // compatibility with older versions of OS X. @@ -1760,7 +1763,7 @@ struct JavascriptEngine::RootObject : public DynamicObject }; //============================================================================== - struct JSONClass : public DynamicObject + struct JSONClass final : public DynamicObject { JSONClass() { setMethod ("stringify", stringify); } static Identifier getClassName() { static const Identifier i ("JSON"); return i; } @@ -1768,7 +1771,7 @@ struct JavascriptEngine::RootObject : public DynamicObject }; //============================================================================== - struct IntegerClass : public DynamicObject + struct IntegerClass final : public DynamicObject { IntegerClass() { setMethod ("parseInt", parseInt); } static Identifier getClassName() { static const Identifier i ("Integer"); return i; } @@ -1777,7 +1780,7 @@ struct JavascriptEngine::RootObject : public DynamicObject { auto s = getString (a, 0).trim(); - return s[0] == '0' ? (s[1] == 'x' ? s.substring(2).getHexValue64() : getOctalValue (s)) + return s[0] == '0' ? (s[1] == 'x' ? s.substring (2).getHexValue64() : getOctalValue (s)) : s.getLargeIntValue(); } }; @@ -1913,8 +1916,6 @@ const NamedValueSet& JavascriptEngine::getRootObjectProperties() const noexcept return root->getProperties(); } -#if JUCE_MSVC - #pragma warning (pop) -#endif +JUCE_END_IGNORE_WARNINGS_MSVC } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h index 55e6f9ff..152e9d11 100644 --- a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/juce_core.cpp b/JuceLibraryCode/modules/juce_core/juce_core.cpp index 8bac812a..3a2e49f4 100644 --- a/JuceLibraryCode/modules/juce_core/juce_core.cpp +++ b/JuceLibraryCode/modules/juce_core/juce_core.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -36,11 +36,12 @@ #include "juce_core.h" -#include #include #include +#include +#include -#if ! JUCE_ANDROID +#if ! (JUCE_ANDROID || JUCE_BSD) #include #include #endif @@ -53,10 +54,9 @@ #include #include #else - #pragma warning (push) - #pragma warning (disable: 4091) + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4091) #include - #pragma warning (pop) + JUCE_END_IGNORE_WARNINGS_MSVC #if ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #pragma comment (lib, "DbgHelp.lib") @@ -64,7 +64,7 @@ #endif #else - #if JUCE_LINUX || JUCE_ANDROID + #if JUCE_LINUX || JUCE_BSD || JUCE_ANDROID #include #include #include @@ -72,7 +72,17 @@ #include #endif - #if JUCE_LINUX + #if JUCE_WASM + #include + #include + #include + #include + #include + #include + #include + #endif + + #if JUCE_LINUX || JUCE_BSD #include #include #include @@ -92,7 +102,7 @@ #include #include - #if ! JUCE_ANDROID + #if ! (JUCE_ANDROID || JUCE_WASM) #include #endif #endif @@ -100,7 +110,6 @@ #if JUCE_MAC || JUCE_IOS #include #include - #include #endif #if JUCE_ANDROID @@ -110,21 +119,17 @@ #undef check -//============================================================================== -#ifndef JUCE_STANDALONE_APPLICATION - JUCE_COMPILER_WARNING ("Please re-save your project with the latest Projucer version to avoid this warning") - #define JUCE_STANDALONE_APPLICATION 0 -#endif - //============================================================================== #include "containers/juce_AbstractFifo.cpp" #include "containers/juce_ArrayBase.cpp" +#include "containers/juce_ListenerList.cpp" #include "containers/juce_NamedValueSet.cpp" #include "containers/juce_OwnedArray.cpp" #include "containers/juce_PropertySet.cpp" #include "containers/juce_ReferenceCountedArray.cpp" #include "containers/juce_SparseSet.cpp" #include "files/juce_DirectoryIterator.cpp" +#include "files/juce_RangedDirectoryIterator.cpp" #include "files/juce_File.cpp" #include "files/juce_FileInputStream.cpp" #include "files/juce_FileOutputStream.cpp" @@ -136,10 +141,12 @@ #include "maths/juce_Expression.cpp" #include "maths/juce_Random.cpp" #include "memory/juce_MemoryBlock.cpp" +#include "memory/juce_AllocationHooks.cpp" #include "misc/juce_RuntimePermissions.cpp" #include "misc/juce_Result.cpp" #include "misc/juce_Uuid.cpp" #include "misc/juce_ConsoleApplication.cpp" +#include "misc/juce_ScopeGuard.cpp" #include "network/juce_MACAddress.cpp" #include "network/juce_NamedPipe.cpp" #include "network/juce_Socket.cpp" @@ -171,6 +178,7 @@ #include "unit_tests/juce_UnitTest.cpp" #include "containers/juce_Variant.cpp" #include "javascript/juce_JSON.cpp" +#include "javascript/juce_JSONUtils.cpp" #include "javascript/juce_Javascript.cpp" #include "containers/juce_DynamicObject.cpp" #include "xml/juce_XmlDocument.cpp" @@ -180,66 +188,107 @@ #include "zip/juce_ZipFile.cpp" #include "files/juce_FileFilter.cpp" #include "files/juce_WildcardFileFilter.cpp" +#include "native/juce_ThreadPriorities_native.h" +#include "native/juce_PlatformTimerListener.h" //============================================================================== #if ! JUCE_WINDOWS - #include "native/juce_posix_SharedCode.h" - #include "native/juce_posix_NamedPipe.cpp" + #include "native/juce_SharedCode_posix.h" + #include "native/juce_NamedPipe_posix.cpp" #if ! JUCE_ANDROID || __ANDROID_API__ >= 24 - #include "native/juce_posix_IPAddress.h" + #include "native/juce_IPAddress_posix.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_Files_mac.mm" + #include "native/juce_Network_mac.mm" + #include "native/juce_Strings_mac.mm" + #include "native/juce_SharedCode_intel.h" + #include "native/juce_SystemStats_mac.mm" + #include "native/juce_Threads_mac.mm" + #include "native/juce_PlatformTimer_generic.cpp" + #include "native/juce_Process_mac.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_Files_windows.cpp" + #include "native/juce_Network_windows.cpp" + #include "native/juce_Registry_windows.cpp" + #include "native/juce_SystemStats_windows.cpp" + #include "native/juce_Threads_windows.cpp" + #include "native/juce_PlatformTimer_windows.cpp" //============================================================================== #elif JUCE_LINUX - #include "native/juce_linux_CommonFile.cpp" - #include "native/juce_linux_Files.cpp" - #include "native/juce_linux_Network.cpp" + #include "native/juce_CommonFile_linux.cpp" + #include "native/juce_Files_linux.cpp" + #include "native/juce_Network_linux.cpp" + #if JUCE_USE_CURL + #include "native/juce_Network_curl.cpp" + #endif + #include "native/juce_SystemStats_linux.cpp" + #include "native/juce_Threads_linux.cpp" + #include "native/juce_PlatformTimer_generic.cpp" + +//============================================================================== +#elif JUCE_BSD + #include "native/juce_CommonFile_linux.cpp" + #include "native/juce_Files_linux.cpp" + #include "native/juce_Network_linux.cpp" #if JUCE_USE_CURL - #include "native/juce_curl_Network.cpp" + #include "native/juce_Network_curl.cpp" #endif - #include "native/juce_linux_SystemStats.cpp" - #include "native/juce_linux_Threads.cpp" + #include "native/juce_SharedCode_intel.h" + #include "native/juce_SystemStats_linux.cpp" + #include "native/juce_Threads_linux.cpp" + #include "native/juce_PlatformTimer_generic.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_CommonFile_linux.cpp" + #include "native/juce_JNIHelpers_android.cpp" + #include "native/juce_Files_android.cpp" + #include "native/juce_Misc_android.cpp" + #include "native/juce_Network_android.cpp" + #include "native/juce_SystemStats_android.cpp" + #include "native/juce_Threads_android.cpp" + #include "native/juce_RuntimePermissions_android.cpp" + #include "native/juce_PlatformTimer_generic.cpp" +//============================================================================== +#elif JUCE_WASM + #include "native/juce_SystemStats_wasm.cpp" + #include "native/juce_PlatformTimer_generic.cpp" #endif -#include "threads/juce_ChildProcess.cpp" +#include "files/juce_common_MimeTypes.h" +#include "files/juce_common_MimeTypes.cpp" +#include "native/juce_AndroidDocument_android.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_WASM + #include "threads/juce_ChildProcess.cpp" + #include "network/juce_WebInputStream.cpp" + #include "streams/juce_URLInputSource.cpp" +#endif //============================================================================== #if JUCE_UNIT_TESTS #include "containers/juce_HashMap_test.cpp" + #include "containers/juce_Optional_test.cpp" + #include "containers/juce_Enumerate_test.cpp" + #include "maths/juce_MathsFunctions_test.cpp" + #include "misc/juce_EnumHelpers_test.cpp" + #include "containers/juce_FixedSizeFunction_test.cpp" + #include "javascript/juce_JSONSerialisation_test.cpp" + #include "memory/juce_SharedResourcePointer_test.cpp" + #if JUCE_MAC || JUCE_IOS + #include "native/juce_ObjCHelpers_mac_test.mm" + #endif #endif //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/juce_core.h b/JuceLibraryCode/modules/juce_core/juce_core.h index 45e528be..dc22ab34 100644 --- a/JuceLibraryCode/modules/juce_core/juce_core.h +++ b/JuceLibraryCode/modules/juce_core/juce_core.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,21 +25,22 @@ The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. For details about the syntax and how to create or use a module, see the - JUCE Module Format.txt file. + JUCE Module Format.md file. BEGIN_JUCE_MODULE_DECLARATION ID: juce_core vendor: juce - version: 5.4.7 + version: 7.0.10 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 + minimumCppStandard: 17 dependencies: - OSXFrameworks: Cocoa IOKit + OSXFrameworks: Cocoa Foundation IOKit Security iOSFrameworks: Foundation linuxLibs: rt dl pthread mingwLibs: uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm @@ -177,6 +178,13 @@ #define JUCE_STRICT_REFCOUNTEDPOINTER 0 #endif +/** Config: JUCE_ENABLE_ALLOCATION_HOOKS + If enabled, this will add global allocation functions with built-in assertions, which may + help when debugging allocations in unit tests. +*/ +#ifndef JUCE_ENABLE_ALLOCATION_HOOKS + #define JUCE_ENABLE_ALLOCATION_HOOKS 0 +#endif #ifndef JUCE_STRING_UTF_TYPE #define JUCE_STRING_UTF_TYPE 8 @@ -211,25 +219,21 @@ namespace juce extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noexcept; } +#include "misc/juce_EnumHelpers.h" #include "memory/juce_Memory.h" #include "maths/juce_MathsFunctions.h" #include "memory/juce_ByteOrder.h" #include "memory/juce_Atomic.h" #include "text/juce_CharacterFunctions.h" -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4514 4996) -#endif +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4514 4996) #include "text/juce_CharPointer_UTF8.h" #include "text/juce_CharPointer_UTF16.h" #include "text/juce_CharPointer_UTF32.h" #include "text/juce_CharPointer_ASCII.h" -#if JUCE_MSVC - #pragma warning (pop) -#endif +JUCE_END_IGNORE_WARNINGS_MSVC #include "text/juce_String.h" #include "text/juce_StringRef.h" @@ -241,6 +245,9 @@ namespace juce #include "memory/juce_ReferenceCountedObject.h" #include "memory/juce_ScopedPointer.h" #include "memory/juce_OptionalScopedPointer.h" +#include "containers/juce_Optional.h" +#include "containers/juce_Enumerate.h" +#include "containers/juce_ScopedValueSetter.h" #include "memory/juce_Singleton.h" #include "memory/juce_WeakReference.h" #include "threads/juce_ScopedLock.h" @@ -253,13 +260,14 @@ namespace juce #include "containers/juce_ArrayBase.h" #include "containers/juce_Array.h" #include "containers/juce_LinkedListPointer.h" +#include "misc/juce_ScopeGuard.h" #include "containers/juce_ListenerList.h" #include "containers/juce_OwnedArray.h" #include "containers/juce_ReferenceCountedArray.h" -#include "containers/juce_ScopedValueSetter.h" #include "containers/juce_SortedSet.h" #include "containers/juce_SparseSet.h" #include "containers/juce_AbstractFifo.h" +#include "containers/juce_SingleThreadedAbstractFifo.h" #include "text/juce_NewLine.h" #include "text/juce_StringPool.h" #include "text/juce_Identifier.h" @@ -270,13 +278,17 @@ namespace juce #include "text/juce_TextDiff.h" #include "text/juce_LocalisedStrings.h" #include "text/juce_Base64.h" +#include "misc/juce_Functional.h" +#include "containers/juce_Span.h" #include "misc/juce_Result.h" #include "misc/juce_Uuid.h" #include "misc/juce_ConsoleApplication.h" #include "containers/juce_Variant.h" #include "containers/juce_NamedValueSet.h" +#include "javascript/juce_JSON.h" #include "containers/juce_DynamicObject.h" #include "containers/juce_HashMap.h" +#include "containers/juce_FixedSizeFunction.h" #include "time/juce_RelativeTime.h" #include "time/juce_Time.h" #include "streams/juce_InputStream.h" @@ -288,6 +300,7 @@ namespace juce #include "streams/juce_InputSource.h" #include "files/juce_File.h" #include "files/juce_DirectoryIterator.h" +#include "files/juce_RangedDirectoryIterator.h" #include "files/juce_FileInputStream.h" #include "files/juce_FileOutputStream.h" #include "files/juce_FileSearchPath.h" @@ -297,7 +310,9 @@ namespace juce #include "files/juce_WildcardFileFilter.h" #include "streams/juce_FileInputSource.h" #include "logging/juce_FileLogger.h" -#include "javascript/juce_JSON.h" +#include "javascript/juce_JSONUtils.h" +#include "serialisation/juce_Serialisation.h" +#include "javascript/juce_JSONSerialisation.h" #include "javascript/juce_Javascript.h" #include "maths/juce_BigInteger.h" #include "maths/juce_Expression.h" @@ -306,12 +321,12 @@ namespace juce #include "misc/juce_WindowsRegistry.h" #include "threads/juce_ChildProcess.h" #include "threads/juce_DynamicLibrary.h" -#include "threads/juce_HighResolutionTimer.h" #include "threads/juce_InterProcessLock.h" #include "threads/juce_Process.h" #include "threads/juce_SpinLock.h" #include "threads/juce_WaitableEvent.h" #include "threads/juce_Thread.h" +#include "threads/juce_HighResolutionTimer.h" #include "threads/juce_ThreadLocalValue.h" #include "threads/juce_ThreadPool.h" #include "threads/juce_TimeSliceThread.h" @@ -334,18 +349,25 @@ namespace juce #include "zip/juce_ZipFile.h" #include "containers/juce_PropertySet.h" #include "memory/juce_SharedResourcePointer.h" +#include "memory/juce_AllocationHooks.h" +#include "memory/juce_Reservoir.h" +#include "files/juce_AndroidDocument.h" +#include "streams/juce_AndroidDocumentInputSource.h" + +#include "detail/juce_CallbackListenerList.h" #if JUCE_CORE_INCLUDE_OBJC_HELPERS && (JUCE_MAC || JUCE_IOS) - #include "native/juce_osx_ObjCHelpers.h" + #include "native/juce_CFHelpers_mac.h" + #include "native/juce_ObjCHelpers_mac.h" #endif #if JUCE_CORE_INCLUDE_COM_SMART_PTR && JUCE_WINDOWS - #include "native/juce_win32_ComSmartPtr.h" + #include "native/juce_ComSmartPtr_windows.h" #endif #if JUCE_CORE_INCLUDE_JNI_HELPERS && JUCE_ANDROID #include - #include "native/juce_android_JNIHelpers.h" + #include "native/juce_JNIHelpers_android.h" #endif #if JUCE_UNIT_TESTS @@ -375,11 +397,9 @@ namespace juce } #endif -#if JUCE_MSVC - #pragma warning (pop) +JUCE_END_IGNORE_WARNINGS_MSVC - // In DLL builds, need to disable this warnings for other modules - #if defined (JUCE_DLL_BUILD) || defined (JUCE_DLL) - #pragma warning (disable: 4251) - #endif +// In DLL builds, need to disable this warnings for other modules +#if defined (JUCE_DLL_BUILD) || defined (JUCE_DLL) + JUCE_IGNORE_MSVC (4251) #endif diff --git a/JuceLibraryCode/modules/juce_core/juce_core.mm b/JuceLibraryCode/modules/juce_core/juce_core.mm index d41cc84f..9ff55f1e 100644 --- a/JuceLibraryCode/modules/juce_core/juce_core.mm +++ b/JuceLibraryCode/modules/juce_core/juce_core.mm @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.cpp b/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.cpp index 93dcdb5e..ca793fd5 100644 --- a/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.cpp +++ b/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h b/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h index bca462b4..e076161a 100644 --- a/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h +++ b/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/logging/juce_Logger.cpp b/JuceLibraryCode/modules/juce_core/logging/juce_Logger.cpp index f422fccb..39d91706 100644 --- a/JuceLibraryCode/modules/juce_core/logging/juce_Logger.cpp +++ b/JuceLibraryCode/modules/juce_core/logging/juce_Logger.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -49,7 +49,7 @@ void Logger::writeToLog (const String& message) void JUCE_API JUCE_CALLTYPE logAssertion (const char* const filename, const int lineNum) noexcept { String m ("JUCE Assertion failure in "); - m << File::createFileWithoutCheckingPath (filename).getFileName() << ':' << lineNum; + m << File::createFileWithoutCheckingPath (CharPointer_UTF8 (filename)).getFileName() << ':' << lineNum; #if JUCE_LOG_ASSERTIONS Logger::writeToLog (m); diff --git a/JuceLibraryCode/modules/juce_core/logging/juce_Logger.h b/JuceLibraryCode/modules/juce_core/logging/juce_Logger.h index 31cce7fc..5b8a8b83 100644 --- a/JuceLibraryCode/modules/juce_core/logging/juce_Logger.h +++ b/JuceLibraryCode/modules/juce_core/logging/juce_Logger.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp index d8a1e2ed..4dff2c64 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -130,10 +130,6 @@ BigInteger& BigInteger::operator= (BigInteger&& other) noexcept return *this; } -BigInteger::~BigInteger() -{ -} - void BigInteger::swapWith (BigInteger& other) noexcept { for (int i = 0; i < numPreallocatedInts; ++i) @@ -166,6 +162,8 @@ BigInteger& BigInteger::operator= (const BigInteger& other) return *this; } +BigInteger::~BigInteger() = default; + uint32* BigInteger::getValues() const noexcept { jassert (heapAllocation != nullptr || allocatedSize <= numPreallocatedInts); @@ -262,7 +260,7 @@ uint32 BigInteger::getBitRangeAsInt (const int startBit, int numBits) const noex return n & (((uint32) 0xffffffff) >> endSpace); } -void BigInteger::setBitRangeAsInt (const int startBit, int numBits, uint32 valueToSet) +BigInteger& BigInteger::setBitRangeAsInt (const int startBit, int numBits, uint32 valueToSet) { if (numBits > 32) { @@ -275,10 +273,12 @@ void BigInteger::setBitRangeAsInt (const int startBit, int numBits, uint32 value setBit (startBit + i, (valueToSet & 1) != 0); valueToSet >>= 1; } + + return *this; } //============================================================================== -void BigInteger::clear() noexcept +BigInteger& BigInteger::clear() noexcept { heapAllocation.free(); allocatedSize = numPreallocatedInts; @@ -287,9 +287,11 @@ void BigInteger::clear() noexcept for (int i = 0; i < numPreallocatedInts; ++i) preallocated[i] = 0; + + return *this; } -void BigInteger::setBit (const int bit) +BigInteger& BigInteger::setBit (const int bit) { if (bit >= 0) { @@ -301,17 +303,21 @@ void BigInteger::setBit (const int bit) getValues() [bitToIndex (bit)] |= bitToMask (bit); } + + return *this; } -void BigInteger::setBit (const int bit, const bool shouldBeSet) +BigInteger& BigInteger::setBit (const int bit, const bool shouldBeSet) { if (shouldBeSet) setBit (bit); else clearBit (bit); + + return *this; } -void BigInteger::clearBit (const int bit) noexcept +BigInteger& BigInteger::clearBit (const int bit) noexcept { if (bit >= 0 && bit <= highestBit) { @@ -320,20 +326,25 @@ void BigInteger::clearBit (const int bit) noexcept if (bit == highestBit) highestBit = getHighestBit(); } + + return *this; } -void BigInteger::setRange (int startBit, int numBits, const bool shouldBeSet) +BigInteger& BigInteger::setRange (int startBit, int numBits, const bool shouldBeSet) { while (--numBits >= 0) setBit (startBit++, shouldBeSet); + + return *this; } -void BigInteger::insertBit (const int bit, const bool shouldBeSet) +BigInteger& BigInteger::insertBit (const int bit, const bool shouldBeSet) { if (bit >= 0) shiftBits (1, bit); setBit (bit, shouldBeSet); + return *this; } //============================================================================== @@ -551,7 +562,7 @@ BigInteger& BigInteger::operator*= (const BigInteger& other) { auto uv = (uint64) totalValues[i + j] + (uint64) values[j] * (uint64) mValues[i] + (uint64) c; totalValues[i + j] = (uint32) uv; - c = uv >> 32; + c = static_cast (uv >> 32); } totalValues[i + n + 1] = c; @@ -859,7 +870,7 @@ void BigInteger::shiftRight (int bits, const int startBit) } } -void BigInteger::shiftBits (int bits, const int startBit) +BigInteger& BigInteger::shiftBits (int bits, const int startBit) { if (highestBit >= 0) { @@ -868,6 +879,8 @@ void BigInteger::shiftBits (int bits, const int startBit) else if (bits > 0) shiftLeft (bits, startBit); } + + return *this; } //============================================================================== @@ -992,7 +1005,7 @@ void BigInteger::montgomeryMultiplication (const BigInteger& other, const BigInt void BigInteger::extendedEuclidean (const BigInteger& a, const BigInteger& b, BigInteger& x, BigInteger& y) { - BigInteger p(a), q(b), gcd(1); + BigInteger p (a), q (b), gcd (1); Array tempValues; while (! q.isZero()) @@ -1283,7 +1296,7 @@ uint32 readLittleEndianBitsInBuffer (const void* buffer, uint32 startBit, uint32 //============================================================================== #if JUCE_UNIT_TESTS -class BigIntegerTests : public UnitTest +class BigIntegerTests final : public UnitTest { public: BigIntegerTests() @@ -1308,12 +1321,12 @@ class BigIntegerTests : public UnitTest Random r = getRandom(); expect (BigInteger().isZero()); - expect (BigInteger(1).isOne()); + expect (BigInteger (1).isOne()); for (int j = 10000; --j >= 0;) { - BigInteger b1 (getBigRandom(r)), - b2 (getBigRandom(r)); + BigInteger b1 (getBigRandom (r)), + b2 (getBigRandom (r)); BigInteger b3 = b1 + b2; expect (b3 > b1 && b3 > b2); diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h index 7c7b4054..5bfce437 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -102,16 +102,16 @@ class JUCE_API BigInteger //============================================================================== /** Resets the value to 0. */ - void clear() noexcept; + BigInteger& clear() noexcept; /** Clears a particular bit in the number. */ - void clearBit (int bitNumber) noexcept; + BigInteger& clearBit (int bitNumber) noexcept; /** Sets a specified bit to 1. */ - void setBit (int bitNumber); + BigInteger& setBit (int bitNumber); /** Sets or clears a specified bit. */ - void setBit (int bitNumber, bool shouldBeSet); + BigInteger& setBit (int bitNumber, bool shouldBeSet); /** Sets a range of bits to be either on or off. @@ -119,10 +119,10 @@ class JUCE_API BigInteger @param numBits the number of bits to change @param shouldBeSet whether to turn these bits on or off */ - void setRange (int startBit, int numBits, bool shouldBeSet); + BigInteger& setRange (int startBit, int numBits, bool shouldBeSet); /** Inserts a bit an a given position, shifting up any bits above it. */ - void insertBit (int bitNumber, bool shouldBeSet); + BigInteger& insertBit (int bitNumber, bool shouldBeSet); /** Returns a range of bits as a new BigInteger. @@ -145,14 +145,14 @@ class JUCE_API BigInteger Copies the given integer onto a range of bits, starting at startBit, and using up to numBits of the available bits. */ - void setBitRangeAsInt (int startBit, int numBits, uint32 valueToSet); + BigInteger& setBitRangeAsInt (int startBit, int numBits, uint32 valueToSet); /** Shifts a section of bits left or right. @param howManyBitsLeft how far to move the bits (+ve numbers shift it left, -ve numbers shift it right). @param startBit the first bit to affect - if this is > 0, only bits above that index will be affected. */ - void shiftBits (int howManyBitsLeft, int startBit); + BigInteger& shiftBits (int howManyBitsLeft, int startBit); /** Returns the total number of set bits in the value. */ int countNumberOfSetBits() const noexcept; diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp index 9c3ce9e2..c83e0a51 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,7 +23,7 @@ namespace juce { -class Expression::Term : public SingleThreadedReferenceCountedObject +class Expression::Term : public SingleThreadedReferenceCountedObject { public: Term() {} @@ -69,7 +69,7 @@ class Expression::Term : public SingleThreadedReferenceCountedObject virtual void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) { for (int i = getNumInputs(); --i >= 0;) - getInput(i)->visitAllSymbols (visitor, scope, recursionDepth); + getInput (i)->visitAllSymbols (visitor, scope, recursionDepth); } private: @@ -92,7 +92,7 @@ struct Expression::Helpers //============================================================================== /** An exception that can be thrown by Expression::evaluate(). */ - class EvaluationError : public std::exception + class EvaluationError final : public std::exception { public: EvaluationError (const String& desc) : description (desc) @@ -104,19 +104,19 @@ struct Expression::Helpers }; //============================================================================== - class Constant : public Term + class Constant final : public Term { public: Constant (double val, bool resolutionTarget) : value (val), isResolutionTarget (resolutionTarget) {} - Type getType() const noexcept { return constantType; } - Term* clone() const { return new Constant (value, isResolutionTarget); } - TermPtr resolve (const Scope&, int) { return *this; } - double toDouble() const { return value; } - TermPtr negated() { return *new Constant (-value, isResolutionTarget); } + Type getType() const noexcept override { return constantType; } + Term* clone() const override { return new Constant (value, isResolutionTarget); } + TermPtr resolve (const Scope&, int) override { return *this; } + double toDouble() const override { return value; } + TermPtr negated() override { return *new Constant (-value, isResolutionTarget); } - String toString() const + String toString() const override { String s (value); if (isResolutionTarget) @@ -138,25 +138,25 @@ struct Expression::Helpers jassert (left != nullptr && right != nullptr); } - int getInputIndexFor (const Term* possibleInput) const + int getInputIndexFor (const Term* possibleInput) const override { return possibleInput == left ? 0 : (possibleInput == right ? 1 : -1); } - Type getType() const noexcept { return operatorType; } - int getNumInputs() const { return 2; } - Term* getInput (int index) const { return index == 0 ? left.get() : (index == 1 ? right.get() : nullptr); } + Type getType() const noexcept override { return operatorType; } + int getNumInputs() const override { return 2; } + Term* getInput (int index) const override { return index == 0 ? left.get() : (index == 1 ? right.get() : nullptr); } virtual double performFunction (double left, double right) const = 0; virtual void writeOperator (String& dest) const = 0; - TermPtr resolve (const Scope& scope, int recursionDepth) + TermPtr resolve (const Scope& scope, int recursionDepth) override { return *new Constant (performFunction (left ->resolve (scope, recursionDepth)->toDouble(), right->resolve (scope, recursionDepth)->toDouble()), false); } - String toString() const + String toString() const override { String s; auto ourPrecendence = getOperatorPrecedence(); @@ -193,30 +193,30 @@ struct Expression::Helpers }; //============================================================================== - class SymbolTerm : public Term + class SymbolTerm final : public Term { public: explicit SymbolTerm (const String& sym) : symbol (sym) {} - TermPtr resolve (const Scope& scope, int recursionDepth) + TermPtr resolve (const Scope& scope, int recursionDepth) override { checkRecursionDepth (recursionDepth); return scope.getSymbolValue (symbol).term->resolve (scope, recursionDepth + 1); } - Type getType() const noexcept { return symbolType; } - Term* clone() const { return new SymbolTerm (symbol); } - String toString() const { return symbol; } - String getName() const { return symbol; } + Type getType() const noexcept override { return symbolType; } + Term* clone() const override { return new SymbolTerm (symbol); } + String toString() const override { return symbol; } + String getName() const override { return symbol; } - void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) + void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) override { checkRecursionDepth (recursionDepth); visitor.useSymbol (Symbol (scope.getScopeUID(), symbol)); scope.getSymbolValue (symbol).term->visitAllSymbols (visitor, scope, recursionDepth + 1); } - void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int /*recursionDepth*/) + void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int /*recursionDepth*/) override { if (oldSymbol.symbolName == symbol && scope.getScopeUID() == oldSymbol.scopeUID) symbol = newName; @@ -226,7 +226,7 @@ struct Expression::Helpers }; //============================================================================== - class Function : public Term + class Function final : public Term { public: explicit Function (const String& name) : functionName (name) {} @@ -235,13 +235,13 @@ struct Expression::Helpers : functionName (name), parameters (params) {} - Type getType() const noexcept { return functionType; } - Term* clone() const { return new Function (functionName, parameters); } - int getNumInputs() const { return parameters.size(); } - Term* getInput (int i) const { return parameters.getReference(i).term.get(); } - String getName() const { return functionName; } + Type getType() const noexcept override { return functionType; } + Term* clone() const override { return new Function (functionName, parameters); } + int getNumInputs() const override { return parameters.size(); } + Term* getInput (int i) const override { return parameters.getReference (i).term.get(); } + String getName() const override { return functionName; } - TermPtr resolve (const Scope& scope, int recursionDepth) + TermPtr resolve (const Scope& scope, int recursionDepth) override { checkRecursionDepth (recursionDepth); double result = 0; @@ -252,7 +252,7 @@ struct Expression::Helpers HeapBlock params (numParams); for (int i = 0; i < numParams; ++i) - params[i] = parameters.getReference(i).term->resolve (scope, recursionDepth + 1)->toDouble(); + params[i] = parameters.getReference (i).term->resolve (scope, recursionDepth + 1)->toDouble(); result = scope.evaluateFunction (functionName, params, numParams); } @@ -264,16 +264,16 @@ struct Expression::Helpers return *new Constant (result, false); } - int getInputIndexFor (const Term* possibleInput) const + int getInputIndexFor (const Term* possibleInput) const override { for (int i = 0; i < parameters.size(); ++i) - if (parameters.getReference(i).term == possibleInput) + if (parameters.getReference (i).term == possibleInput) return i; return -1; } - String toString() const + String toString() const override { if (parameters.size() == 0) return functionName + "()"; @@ -282,7 +282,7 @@ struct Expression::Helpers for (int i = 0; i < parameters.size(); ++i) { - s << parameters.getReference(i).term->toString(); + s << parameters.getReference (i).term->toString(); if (i < parameters.size() - 1) s << ", "; @@ -297,12 +297,12 @@ struct Expression::Helpers }; //============================================================================== - class DotOperator : public BinaryTerm + class DotOperator final : public BinaryTerm { public: DotOperator (SymbolTerm* l, TermPtr r) : BinaryTerm (TermPtr (l), r) {} - TermPtr resolve (const Scope& scope, int recursionDepth) + TermPtr resolve (const Scope& scope, int recursionDepth) override { checkRecursionDepth (recursionDepth); @@ -311,13 +311,13 @@ struct Expression::Helpers return visitor.output; } - Term* clone() const { return new DotOperator (getSymbol(), *right); } - String getName() const { return "."; } - int getOperatorPrecedence() const { return 1; } - void writeOperator (String& dest) const { dest << '.'; } - double performFunction (double, double) const { return 0.0; } + Term* clone() const override { return new DotOperator (getSymbol(), *right); } + String getName() const override { return "."; } + int getOperatorPrecedence() const override { return 1; } + void writeOperator (String& dest) const override { dest << '.'; } + double performFunction (double, double) const override { return 0.0; } - void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) + void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth) override { checkRecursionDepth (recursionDepth); visitor.useSymbol (Symbol (scope.getScopeUID(), getSymbol()->symbol)); @@ -331,7 +331,7 @@ struct Expression::Helpers catch (...) {} } - void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth) + void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth) override { checkRecursionDepth (recursionDepth); getSymbol()->renameSymbol (oldSymbol, newName, scope, recursionDepth); @@ -347,13 +347,13 @@ struct Expression::Helpers private: //============================================================================== - class EvaluationVisitor : public Scope::Visitor + class EvaluationVisitor final : public Scope::Visitor { public: EvaluationVisitor (const TermPtr& t, const int recursion) : input (t), output (t), recursionCount (recursion) {} - void visit (const Scope& scope) { output = input->resolve (scope, recursionCount); } + void visit (const Scope& scope) override { output = input->resolve (scope, recursionCount); } const TermPtr input; TermPtr output; @@ -363,13 +363,13 @@ struct Expression::Helpers JUCE_DECLARE_NON_COPYABLE (EvaluationVisitor) }; - class SymbolVisitingVisitor : public Scope::Visitor + class SymbolVisitingVisitor final : public Scope::Visitor { public: SymbolVisitingVisitor (const TermPtr& t, SymbolVisitor& v, const int recursion) : input (t), visitor (v), recursionCount (recursion) {} - void visit (const Scope& scope) { input->visitAllSymbols (visitor, scope, recursionCount); } + void visit (const Scope& scope) override { input->visitAllSymbols (visitor, scope, recursionCount); } private: const TermPtr input; @@ -379,13 +379,13 @@ struct Expression::Helpers JUCE_DECLARE_NON_COPYABLE (SymbolVisitingVisitor) }; - class SymbolRenamingVisitor : public Scope::Visitor + class SymbolRenamingVisitor final : public Scope::Visitor { public: SymbolRenamingVisitor (const TermPtr& t, const Expression::Symbol& symbol_, const String& newName_, const int recursionCount_) : input (t), symbol (symbol_), newName (newName_), recursionCount (recursionCount_) {} - void visit (const Scope& scope) { input->renameSymbol (symbol, newName, scope, recursionCount); } + void visit (const Scope& scope) override { input->renameSymbol (symbol, newName, scope, recursionCount); } private: const TermPtr input; @@ -402,7 +402,7 @@ struct Expression::Helpers }; //============================================================================== - class Negate : public Term + class Negate final : public Term { public: explicit Negate (const TermPtr& t) : input (t) @@ -410,23 +410,22 @@ struct Expression::Helpers jassert (t != nullptr); } - Type getType() const noexcept { return operatorType; } - int getInputIndexFor (const Term* possibleInput) const { return possibleInput == input ? 0 : -1; } - int getNumInputs() const { return 1; } - Term* getInput (int index) const { return index == 0 ? input.get() : nullptr; } - Term* clone() const { return new Negate (*input->clone()); } + Type getType() const noexcept override { return operatorType; } + int getInputIndexFor (const Term* possibleInput) const override { return possibleInput == input ? 0 : -1; } + int getNumInputs() const override { return 1; } + Term* getInput (int index) const override { return index == 0 ? input.get() : nullptr; } + Term* clone() const override { return new Negate (*input->clone()); } - TermPtr resolve (const Scope& scope, int recursionDepth) + TermPtr resolve (const Scope& scope, int recursionDepth) override { return *new Constant (-input->resolve (scope, recursionDepth)->toDouble(), false); } - String getName() const { return "-"; } - TermPtr negated() { return input; } + String getName() const override { return "-"; } + TermPtr negated() override { return input; } - TermPtr createTermToEvaluateInput (const Scope& scope, const Term* t, double overallTarget, Term* topLevelTerm) const + TermPtr createTermToEvaluateInput (const Scope& scope, [[maybe_unused]] const Term* t, double overallTarget, Term* topLevelTerm) const override { - ignoreUnused (t); jassert (t == input); const Term* const dest = findDestinationFor (topLevelTerm, this); @@ -435,7 +434,7 @@ struct Expression::Helpers : dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm)); } - String toString() const + String toString() const override { if (input->getOperatorPrecedence() > 0) return "-(" + input->toString() + ")"; @@ -448,18 +447,18 @@ struct Expression::Helpers }; //============================================================================== - class Add : public BinaryTerm + class Add final : public BinaryTerm { public: Add (TermPtr l, TermPtr r) : BinaryTerm (l, r) {} - Term* clone() const { return new Add (*left->clone(), *right->clone()); } - double performFunction (double lhs, double rhs) const { return lhs + rhs; } - int getOperatorPrecedence() const { return 3; } - String getName() const { return "+"; } - void writeOperator (String& dest) const { dest << " + "; } + Term* clone() const override { return new Add (*left->clone(), *right->clone()); } + double performFunction (double lhs, double rhs) const override { return lhs + rhs; } + int getOperatorPrecedence() const override { return 3; } + String getName() const override { return "+"; } + void writeOperator (String& dest) const override { dest << " + "; } - TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const + TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const override { if (auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm)) return *new Subtract (newDest, *(input == left ? right : left)->clone()); @@ -472,18 +471,18 @@ struct Expression::Helpers }; //============================================================================== - class Subtract : public BinaryTerm + class Subtract final : public BinaryTerm { public: Subtract (TermPtr l, TermPtr r) : BinaryTerm (l, r) {} - Term* clone() const { return new Subtract (*left->clone(), *right->clone()); } - double performFunction (double lhs, double rhs) const { return lhs - rhs; } - int getOperatorPrecedence() const { return 3; } - String getName() const { return "-"; } - void writeOperator (String& dest) const { dest << " - "; } + Term* clone() const override { return new Subtract (*left->clone(), *right->clone()); } + double performFunction (double lhs, double rhs) const override { return lhs - rhs; } + int getOperatorPrecedence() const override { return 3; } + String getName() const override { return "-"; } + void writeOperator (String& dest) const override { dest << " - "; } - TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const + TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const override { if (auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm)) { @@ -501,18 +500,18 @@ struct Expression::Helpers }; //============================================================================== - class Multiply : public BinaryTerm + class Multiply final : public BinaryTerm { public: Multiply (TermPtr l, TermPtr r) : BinaryTerm (l, r) {} - Term* clone() const { return new Multiply (*left->clone(), *right->clone()); } - double performFunction (double lhs, double rhs) const { return lhs * rhs; } - String getName() const { return "*"; } - void writeOperator (String& dest) const { dest << " * "; } - int getOperatorPrecedence() const { return 2; } + Term* clone() const override { return new Multiply (*left->clone(), *right->clone()); } + double performFunction (double lhs, double rhs) const override { return lhs * rhs; } + String getName() const override { return "*"; } + void writeOperator (String& dest) const override { dest << " * "; } + int getOperatorPrecedence() const override { return 2; } - TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const + TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const override { if (auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm)) return *new Divide (newDest, *(input == left ? right : left)->clone()); @@ -524,18 +523,18 @@ struct Expression::Helpers }; //============================================================================== - class Divide : public BinaryTerm + class Divide final : public BinaryTerm { public: Divide (TermPtr l, TermPtr r) : BinaryTerm (l, r) {} - Term* clone() const { return new Divide (*left->clone(), *right->clone()); } - double performFunction (double lhs, double rhs) const { return lhs / rhs; } - String getName() const { return "/"; } - void writeOperator (String& dest) const { dest << " / "; } - int getOperatorPrecedence() const { return 2; } + Term* clone() const override { return new Divide (*left->clone(), *right->clone()); } + double performFunction (double lhs, double rhs) const override { return lhs / rhs; } + String getName() const override { return "/"; } + void writeOperator (String& dest) const override { dest << " / "; } + int getOperatorPrecedence() const override { return 2; } - TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const + TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const override { auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm); @@ -571,7 +570,11 @@ struct Expression::Helpers static Constant* findTermToAdjust (Term* const term, const bool mustBeFlagged) { - jassert (term != nullptr); + if (term == nullptr) + { + jassertfalse; + return nullptr; + } if (term->getType() == constantType) { @@ -618,11 +621,11 @@ struct Expression::Helpers } //============================================================================== - class SymbolCheckVisitor : public Term::SymbolVisitor + class SymbolCheckVisitor final : public Term::SymbolVisitor { public: SymbolCheckVisitor (const Symbol& s) : symbol (s) {} - void useSymbol (const Symbol& s) { wasFound = wasFound || s == symbol; } + void useSymbol (const Symbol& s) override { wasFound = wasFound || s == symbol; } bool wasFound = false; @@ -633,11 +636,11 @@ struct Expression::Helpers }; //============================================================================== - class SymbolListVisitor : public Term::SymbolVisitor + class SymbolListVisitor final : public Term::SymbolVisitor { public: SymbolListVisitor (Array& list_) : list (list_) {} - void useSymbol (const Symbol& s) { list.addIfNotAlreadyThere (s); } + void useSymbol (const Symbol& s) override { list.addIfNotAlreadyThere (s); } private: Array& list; @@ -681,7 +684,7 @@ struct Expression::Helpers } //============================================================================== - static inline bool isDecimalDigit (const juce_wchar c) noexcept + static bool isDecimalDigit (const juce_wchar c) noexcept { return c >= '0' && c <= '9'; } @@ -699,7 +702,7 @@ struct Expression::Helpers bool readOperator (const char* ops, char* const opType = nullptr) noexcept { - text = text.findEndOfWhitespace(); + text.incrementToEndOfWhitespace(); while (*ops != 0) { @@ -719,7 +722,7 @@ struct Expression::Helpers bool readIdentifier (String& identifier) noexcept { - text = text.findEndOfWhitespace(); + text.incrementToEndOfWhitespace(); auto t = text; int numChars = 0; @@ -747,21 +750,21 @@ struct Expression::Helpers Term* readNumber() noexcept { - text = text.findEndOfWhitespace(); + text.incrementToEndOfWhitespace(); auto t = text; bool isResolutionTarget = (*t == '@'); if (isResolutionTarget) { ++t; - t = t.findEndOfWhitespace(); + t.incrementToEndOfWhitespace(); text = t; } if (*t == '-') { ++t; - t = t.findEndOfWhitespace(); + t.incrementToEndOfWhitespace(); } if (isDecimalDigit (*t) || (*t == '.' && isDecimalDigit (t[1]))) diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Expression.h b/JuceLibraryCode/modules/juce_core/maths/juce_Expression.h index 7e706aa9..0953bf09 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Expression.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Expression.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h index 0fbbe2a7..f8dd0278 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -86,38 +86,284 @@ using uint32 = unsigned int; using ssize_t = pointer_sized_int; #endif +//============================================================================== +/** Handy function for avoiding unused variables warning. */ +template +void ignoreUnused (Types&&...) noexcept {} + +/** Handy function for getting the number of elements in a simple const C array. + E.g. + @code + static int myArray[] = { 1, 2, 3 }; + + int numElements = numElementsInArray (myArray) // returns 3 + @endcode +*/ +template +constexpr int numElementsInArray (Type (&)[N]) noexcept { return N; } + +//============================================================================== +// Some useful maths functions that aren't always present with all compilers and build settings. + +/** Using juce_hypot is easier than dealing with the different types of hypot function + that are provided by the various platforms and compilers. */ +template +Type juce_hypot (Type a, Type b) noexcept +{ + #if JUCE_MSVC + return static_cast (_hypot (a, b)); + #else + return static_cast (hypot (a, b)); + #endif +} + +#ifndef DOXYGEN +template <> +inline float juce_hypot (float a, float b) noexcept +{ + #if JUCE_MSVC + return _hypotf (a, b); + #else + return hypotf (a, b); + #endif +} +#endif + +//============================================================================== +/** Commonly used mathematical constants + + @tags{Core} +*/ +template +struct MathConstants +{ + /** A predefined value for Pi */ + static constexpr FloatType pi = static_cast (3.141592653589793238L); + + /** A predefined value for 2 * Pi */ + static constexpr FloatType twoPi = static_cast (2 * 3.141592653589793238L); + + /** A predefined value for Pi / 2 */ + static constexpr FloatType halfPi = static_cast (3.141592653589793238L / 2); + + /** A predefined value for Euler's number */ + static constexpr FloatType euler = static_cast (2.71828182845904523536L); + + /** A predefined value for sqrt (2) */ + static constexpr FloatType sqrt2 = static_cast (1.4142135623730950488L); +}; + +#ifndef DOXYGEN +/** A double-precision constant for pi. */ +[[deprecated ("This is deprecated in favour of MathConstants::pi.")]] +const constexpr double double_Pi = MathConstants::pi; + +/** A single-precision constant for pi. */ +[[deprecated ("This is deprecated in favour of MathConstants::pi.")]] +const constexpr float float_Pi = MathConstants::pi; +#endif + +/** Converts an angle in degrees to radians. */ +template +constexpr FloatType degreesToRadians (FloatType degrees) noexcept { return degrees * (MathConstants::pi / FloatType (180)); } + +/** Converts an angle in radians to degrees. */ +template +constexpr FloatType radiansToDegrees (FloatType radians) noexcept { return radians * (FloatType (180) / MathConstants::pi); } + +//============================================================================== +/** The isfinite() method seems to vary between platforms, so this is a + platform-independent function for it. +*/ +template +bool juce_isfinite (NumericType value) noexcept +{ + if constexpr (std::numeric_limits::has_infinity + || std::numeric_limits::has_quiet_NaN + || std::numeric_limits::has_signaling_NaN) + { + return std::isfinite (value); + } + else + { + ignoreUnused (value); + return true; + } +} + +//============================================================================== +/** Equivalent to operator==, but suppresses float-equality warnings. + + This allows code to be explicit about float-equality checks that are known to have the correct + semantics. +*/ +template +constexpr bool exactlyEqual (Type a, Type b) +{ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") + return a == b; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE +} + +/** A class encapsulating both relative and absolute tolerances for use in floating-point comparisons. + + @see approximatelyEqual, absoluteTolerance, relativeTolerance + + @tags{Core} +*/ +template +class Tolerance +{ +public: + Tolerance() = default; + + /** Returns a copy of this Tolerance object with a new absolute tolerance. + + If you just need a Tolerance object with an absolute tolerance, it might be worth using the + absoluteTolerance() function. + + @see getAbsolute, absoluteTolerance + */ + [[nodiscard]] Tolerance withAbsolute (Type newAbsolute) + { + return withMember (*this, &Tolerance::absolute, std::abs (newAbsolute)); + } + + /** Returns a copy of this Tolerance object with a new relative tolerance. + + If you just need a Tolerance object with a relative tolerance, it might be worth using the + relativeTolerance() function. + + @see getRelative, relativeTolerance + */ + [[nodiscard]] Tolerance withRelative (Type newRelative) + { + return withMember (*this, &Tolerance::relative, std::abs (newRelative)); + } + + [[nodiscard]] Type getAbsolute() const { return absolute; } + [[nodiscard]] Type getRelative() const { return relative; } + +private: + Type absolute{}; + Type relative{}; +}; + +/** Returns a type deduced Tolerance object containing only an absolute tolerance. + + @see Tolerance::withAbsolute, approximatelyEqual + */ +template +static Tolerance absoluteTolerance (Type tolerance) +{ + return Tolerance{}.withAbsolute (tolerance); +} + +/** Returns a type deduced Tolerance object containing only a relative tolerance. + + @see Tolerance::withRelative, approximatelyEqual + */ +template +static Tolerance relativeTolerance (Type tolerance) +{ + return Tolerance{}.withRelative (tolerance); +} + + +/** Returns true if the two floating-point numbers are approximately equal. + + If either a or b are not finite, returns exactlyEqual (a, b). + + The default absolute tolerance is equal to the minimum normal value. This ensures + differences that are subnormal are always considered equal. It is highly recommend this + value is reviewed depending on the calculation being carried out. In general specifying an + absolute value is useful when considering values close to zero. For example you might + expect sin (pi) to return 0, but what it actually returns is close to the error of the value pi. + Therefore, in this example it might be better to set the absolute tolerance to sin (pi). + + The default relative tolerance is equal to the machine epsilon which is the difference between + 1.0 and the next floating-point value that can be represented by Type. In most cases this value + is probably reasonable. This value is multiplied by the largest absolute value of a and b so as + to scale relatively according to the input parameters. For example, specifying a relative value + of 0.05 will ensure values return equal if the difference between them is less than or equal to + 5% of the larger of the two absolute values. + + @param a The first number to compare. + @param b The second number to compare. + @param tolerance An object that represents both absolute and relative tolerances + when evaluating if a and b are equal. + + @see exactlyEqual +*/ +template , int> = 0> +constexpr bool approximatelyEqual (Type a, Type b, + Tolerance tolerance = Tolerance{} + .withAbsolute (std::numeric_limits::min()) + .withRelative (std::numeric_limits::epsilon())) +{ + if (! (juce_isfinite (a) && juce_isfinite (b))) + return exactlyEqual (a, b); + + const auto diff = std::abs (a - b); + + return diff <= tolerance.getAbsolute() + || diff <= tolerance.getRelative() * std::max (std::abs (a), std::abs (b)); +} + +/** Special case for non-floating-point types that returns true if both are exactly equal. */ +template , int> = 0> +constexpr bool approximatelyEqual (Type a, Type b) +{ + return a == b; +} + +//============================================================================== +/** Returns the next representable value by FloatType in the direction of the largest representable value. */ +template +FloatType nextFloatUp (FloatType value) noexcept +{ + return std::nextafter (value, std::numeric_limits::max()); +} + +/** Returns the next representable value by FloatType in the direction of the lowest representable value. */ +template +FloatType nextFloatDown (FloatType value) noexcept +{ + return std::nextafter (value, std::numeric_limits::lowest()); +} + //============================================================================== // Some indispensable min/max functions /** Returns the larger of two values. */ template -JUCE_CONSTEXPR Type jmax (Type a, Type b) { return a < b ? b : a; } +constexpr Type jmax (Type a, Type b) { return a < b ? b : a; } /** Returns the larger of three values. */ template -JUCE_CONSTEXPR Type jmax (Type a, Type b, Type c) { return a < b ? (b < c ? c : b) : (a < c ? c : a); } +constexpr Type jmax (Type a, Type b, Type c) { return a < b ? (b < c ? c : b) : (a < c ? c : a); } /** Returns the larger of four values. */ template -JUCE_CONSTEXPR Type jmax (Type a, Type b, Type c, Type d) { return jmax (a, jmax (b, c, d)); } +constexpr Type jmax (Type a, Type b, Type c, Type d) { return jmax (a, jmax (b, c, d)); } /** Returns the smaller of two values. */ template -JUCE_CONSTEXPR Type jmin (Type a, Type b) { return b < a ? b : a; } +constexpr Type jmin (Type a, Type b) { return b < a ? b : a; } /** Returns the smaller of three values. */ template -JUCE_CONSTEXPR Type jmin (Type a, Type b, Type c) { return b < a ? (c < b ? c : b) : (c < a ? c : a); } +constexpr Type jmin (Type a, Type b, Type c) { return b < a ? (c < b ? c : b) : (c < a ? c : a); } /** Returns the smaller of four values. */ template -JUCE_CONSTEXPR Type jmin (Type a, Type b, Type c, Type d) { return jmin (a, jmin (b, c, d)); } +constexpr Type jmin (Type a, Type b, Type c, Type d) { return jmin (a, jmin (b, c, d)); } /** Remaps a normalised value (between 0 and 1) to a target range. This effectively returns (targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin)). */ template -JUCE_CONSTEXPR Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax) +constexpr Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax) { return targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin); } @@ -126,13 +372,57 @@ JUCE_CONSTEXPR Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeM template Type jmap (Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax) { - jassert (sourceRangeMax != sourceRangeMin); // mapping from a range of zero will produce NaN! + jassert (! approximatelyEqual (sourceRangeMax, sourceRangeMin)); // mapping from a range of zero will produce NaN! return targetRangeMin + ((targetRangeMax - targetRangeMin) * (sourceValue - sourceRangeMin)) / (sourceRangeMax - sourceRangeMin); } -/** Scans an array of values, returning the minimum value that it contains. */ +/** Remaps a normalised value (between 0 and 1) to a logarithmic target range. + + The entire target range must be greater than zero. + + @see mapFromLog10 + + @code + mapToLog10 (0.5, 0.4, 40.0) == 4.0 + @endcode +*/ +template +Type mapToLog10 (Type value0To1, Type logRangeMin, Type logRangeMax) +{ + jassert (logRangeMin > 0); + jassert (logRangeMax > 0); + + auto logMin = std::log10 (logRangeMin); + auto logMax = std::log10 (logRangeMax); + + return std::pow ((Type) 10.0, value0To1 * (logMax - logMin) + logMin); +} + +/** Remaps a logarithmic value in a target range to a normalised value (between 0 and 1). + + The entire target range must be greater than zero. + + @see mapToLog10 + + @code + mapFromLog10 (4.0, 0.4, 40.0) == 0.5 + @endcode +*/ template -Type findMinimum (const Type* data, int numValues) +Type mapFromLog10 (Type valueInLogRange, Type logRangeMin, Type logRangeMax) +{ + jassert (logRangeMin > 0); + jassert (logRangeMax > 0); + + auto logMin = std::log10 (logRangeMin); + auto logMax = std::log10 (logRangeMax); + + return (std::log10 (valueInLogRange) - logMin) / (logMax - logMin); +} + +/** Scans an array of values, returning the minimum value that it contains. */ +template +Type findMinimum (const Type* data, Size numValues) { if (numValues <= 0) return Type (0); @@ -151,8 +441,8 @@ Type findMinimum (const Type* data, int numValues) } /** Scans an array of values, returning the maximum value that it contains. */ -template -Type findMaximum (const Type* values, int numValues) +template +Type findMaximum (const Type* values, Size numValues) { if (numValues <= 0) return Type (0); @@ -273,182 +563,6 @@ bool isWithin (Type a, Type b, Type tolerance) noexcept return std::abs (a - b) <= tolerance; } -/** Returns true if the two numbers are approximately equal. This is useful for floating-point - and double comparisons. -*/ -template -bool approximatelyEqual (Type a, Type b) noexcept -{ - return std::abs (a - b) <= (std::numeric_limits::epsilon() * std::max (a, b)) - || std::abs (a - b) < std::numeric_limits::min(); -} - -//============================================================================== -/** Handy function for avoiding unused variables warning. */ -template -void ignoreUnused (Types&&...) noexcept {} - -/** Handy function for getting the number of elements in a simple const C array. - E.g. - @code - static int myArray[] = { 1, 2, 3 }; - - int numElements = numElementsInArray (myArray) // returns 3 - @endcode -*/ -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. - -/** Using juce_hypot is easier than dealing with the different types of hypot function - that are provided by the various platforms and compilers. */ -template -Type juce_hypot (Type a, Type b) noexcept -{ - #if JUCE_MSVC - return static_cast (_hypot (a, b)); - #else - return static_cast (hypot (a, b)); - #endif -} - -#ifndef DOXYGEN -template <> -inline float juce_hypot (float a, float b) noexcept -{ - #if JUCE_MSVC - return _hypotf (a, b); - #else - return hypotf (a, b); - #endif -} -#endif - -//============================================================================== -#if JUCE_HAS_CONSTEXPR - -/** Commonly used mathematical constants - - @tags{Core} -*/ -template -struct MathConstants -{ - /** A predefined value for Pi */ - static constexpr FloatType pi = static_cast (3.141592653589793238L); - - /** A predefined value for 2 * Pi */ - static constexpr FloatType twoPi = static_cast (2 * 3.141592653589793238L); - - /** A predefined value for Pi / 2 */ - static constexpr FloatType halfPi = static_cast (3.141592653589793238L / 2); - - /** A predefined value for Euler's number */ - static constexpr FloatType euler = static_cast (2.71828182845904523536L); - - /** A predefined value for sqrt(2) */ - static constexpr FloatType sqrt2 = static_cast (1.4142135623730950488L); -}; - -#else - -/** Commonly used mathematical constants - - @tags{Core} -*/ -template -struct MathConstants -{ - /** A predefined value for Pi */ - static const FloatType pi; - - /** A predefined value for 2 * Pi */ - static const FloatType twoPi; - - /** A predefined value for Pi / 2 */ - static const FloatType halfPi; - - /** A predefined value for Euler's number */ - static const FloatType euler; - - /** A predefined value for sqrt(2) */ - static const FloatType sqrt2; -}; - -template -const FloatType MathConstants::pi = static_cast (3.141592653589793238L); - -template -const FloatType MathConstants::twoPi = static_cast (2 * 3.141592653589793238L); - -template -const FloatType MathConstants::halfPi = static_cast (3.141592653589793238L / 2); - -template -const FloatType MathConstants::euler = static_cast (2.71828182845904523536L); - -template -const FloatType MathConstants::sqrt2 = static_cast (1.4142135623730950488L); - -#endif - -#ifndef DOXYGEN -/** A double-precision constant for pi. - @deprecated This is deprecated in favour of MathConstants::pi. - The reason is that "double_Pi" was a confusing name, and many people misused it, - wrongly thinking it meant 2 * pi ! -*/ -const JUCE_CONSTEXPR double double_Pi = MathConstants::pi; - -/** A single-precision constant for pi. - @deprecated This is deprecated in favour of MathConstants::pi. - The reason is that "double_Pi" was a confusing name, and many people misused it, - wrongly thinking it meant 2 * pi ! -*/ -const JUCE_CONSTEXPR float float_Pi = MathConstants::pi; -#endif - -/** Converts an angle in degrees to radians. */ -template -JUCE_CONSTEXPR FloatType degreesToRadians (FloatType degrees) noexcept { return degrees * (MathConstants::pi / FloatType (180)); } - -/** Converts an angle in radians to degrees. */ -template -JUCE_CONSTEXPR FloatType radiansToDegrees (FloatType radians) noexcept { return radians * (FloatType (180) / MathConstants::pi); } - - -//============================================================================== -/** The isfinite() method seems to vary between platforms, so this is a - platform-independent function for it. -*/ -template -bool juce_isfinite (NumericType) noexcept -{ - return true; // Integer types are always finite -} - -template <> -inline bool juce_isfinite (float value) noexcept -{ - #if JUCE_WINDOWS && ! JUCE_MINGW - return _finite (value) != 0; - #else - return std::isfinite (value); - #endif -} - -template <> -inline bool juce_isfinite (double value) noexcept -{ - #if JUCE_WINDOWS && ! JUCE_MINGW - return _finite (value) != 0; - #else - return std::isfinite (value); - #endif -} - //============================================================================== #if JUCE_MSVC #pragma optimize ("t", off) @@ -521,7 +635,8 @@ template unsigned int truncatePositiveToUnsignedInt (FloatType value) noexcept { jassert (value >= static_cast (0)); - jassert (static_cast (value) <= std::numeric_limits::max()); + jassert (static_cast (value) + <= static_cast (std::numeric_limits::max())); return static_cast (value); } @@ -529,7 +644,7 @@ unsigned int truncatePositiveToUnsignedInt (FloatType value) noexcept //============================================================================== /** Returns true if the specified integer is a power-of-two. */ template -JUCE_CONSTEXPR bool isPowerOfTwo (IntegerType value) +constexpr bool isPowerOfTwo (IntegerType value) { return (value & (value - 1)) == 0; } @@ -553,7 +668,7 @@ inline int nextPowerOfTwo (int n) noexcept int findHighestSetBit (uint32 n) noexcept; /** Returns the number of bits in a 32-bit integer. */ -inline int countNumberOfBits (uint32 n) noexcept +constexpr int countNumberOfBits (uint32 n) noexcept { n -= ((n >> 1) & 0x55555555); n = (((n >> 2) & 0x33333333) + (n & 0x33333333)); @@ -564,7 +679,7 @@ inline int countNumberOfBits (uint32 n) noexcept } /** Returns the number of bits in a 64-bit integer. */ -inline int countNumberOfBits (uint64 n) noexcept +constexpr int countNumberOfBits (uint64 n) noexcept { return countNumberOfBits ((uint32) n) + countNumberOfBits ((uint32) (n >> 32)); } @@ -582,7 +697,7 @@ IntegerType negativeAwareModulo (IntegerType dividend, const IntegerType divisor /** Returns the square of its argument. */ template -inline JUCE_CONSTEXPR NumericType square (NumericType n) noexcept +inline constexpr NumericType square (NumericType n) noexcept { return n * n; } @@ -608,7 +723,7 @@ uint32 readLittleEndianBitsInBuffer (const void* sourceBuffer, uint32 startBit, //============================================================================== -#if JUCE_INTEL || defined (DOXYGEN) +#if JUCE_INTEL || DOXYGEN /** This macro can be applied to a float variable to check whether it contains a denormalised value, and to normalise it if necessary. On CPUs that aren't vulnerable to denormalisation problems, this will have no effect. @@ -636,7 +751,7 @@ namespace TypeHelpers */ template struct ParameterType { using type = const Type&; }; - #if ! DOXYGEN + #ifndef DOXYGEN template struct ParameterType { using type = Type&; }; template struct ParameterType { using type = Type*; }; template <> struct ParameterType { using type = char; }; @@ -659,11 +774,8 @@ namespace TypeHelpers @tags{Core} */ - template struct SmallestFloatType { using type = float; }; - - #if ! DOXYGEN - template <> struct SmallestFloatType { using type = double; }; - #endif + template + using SmallestFloatType = std::conditional_t, double, float>; /** These templates are designed to take an integer type, and return an unsigned int version with the same size. @@ -672,7 +784,7 @@ namespace TypeHelpers */ template struct UnsignedTypeWithSize {}; - #if ! DOXYGEN + #ifndef DOXYGEN template <> struct UnsignedTypeWithSize<1> { using type = uint8; }; template <> struct UnsignedTypeWithSize<2> { using type = uint16; }; template <> struct UnsignedTypeWithSize<4> { using type = uint32; }; @@ -681,13 +793,19 @@ namespace TypeHelpers } //============================================================================== -#if ! DOXYGEN - // These old functions are deprecated: Just use roundToInt instead. - JUCE_DEPRECATED_ATTRIBUTE inline int roundDoubleToInt (double value) noexcept { return roundToInt (value); } - JUCE_DEPRECATED_ATTRIBUTE inline int roundFloatToInt (float value) noexcept { return roundToInt (value); } - - // This old function isn't needed - just use std::abs() instead - JUCE_DEPRECATED_ATTRIBUTE inline int64 abs64 (int64 n) noexcept { return std::abs (n); } +#ifndef DOXYGEN + [[deprecated ("Use roundToInt instead.")]] inline int roundDoubleToInt (double value) noexcept { return roundToInt (value); } + [[deprecated ("Use roundToInt instead.")]] inline int roundFloatToInt (float value) noexcept { return roundToInt (value); } + [[deprecated ("Use std::abs() instead.")]] inline int64 abs64 (int64 n) noexcept { return std::abs (n); } #endif +/** Converts an enum to its underlying integral type. + Similar to std::to_underlying, which is only available in C++23 and above. +*/ +template +constexpr auto toUnderlyingType (T t) -> std::enable_if_t, std::underlying_type_t> +{ + return static_cast> (t); +} + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions_test.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions_test.cpp new file mode 100644 index 00000000..02848f50 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions_test.cpp @@ -0,0 +1,543 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +//============================================================================== +template +String getTemplatedMathsFunctionUnitTestName (const String& functionName) +{ + const auto getTypeName = []() -> String + { + if constexpr (std::is_same_v) + return "int"; + + if constexpr (std::is_same_v) + return "float"; + + if constexpr (std::is_same_v) + return "double"; + + if constexpr (std::is_same_v) + return "long double"; + }; + + return functionName + "<" + getTypeName() + ">"; +} + +template +class ApproximatelyEqualTests final : public UnitTest +{ +public: + ApproximatelyEqualTests() + : UnitTest { getTemplatedMathsFunctionUnitTestName ("approximatelyEqual"), UnitTestCategories::maths } + {} + + void runTest() final + { + using limits = std::numeric_limits; + + constexpr auto zero = T{}; + constexpr auto one = T (1); + constexpr auto min = limits::min(); + constexpr auto max = limits::max(); + constexpr auto epsilon = limits::epsilon(); + constexpr auto oneThird = one / (T) 3; + + beginTest ("Equal values are always equal"); + { + expect (approximatelyEqual (zero, zero)); + expect (approximatelyEqual (zero, -zero)); + expect (approximatelyEqual (-zero, -zero)); + + expect (approximatelyEqual (min, min)); + expect (approximatelyEqual (-min, -min)); + + expect (approximatelyEqual (one, one)); + expect (approximatelyEqual (-one, -one)); + + expect (approximatelyEqual (max, max)); + expect (approximatelyEqual (-max, -max)); + + const Tolerance zeroTolerance{}; + + expect (approximatelyEqual (zero, zero, zeroTolerance)); + expect (approximatelyEqual (zero, -zero, zeroTolerance)); + expect (approximatelyEqual (-zero, -zero, zeroTolerance)); + + expect (approximatelyEqual (min, min, zeroTolerance)); + expect (approximatelyEqual (-min, -min, zeroTolerance)); + + expect (approximatelyEqual (one, one, zeroTolerance)); + expect (approximatelyEqual (-one, -one, zeroTolerance)); + + expect (approximatelyEqual (max, max, zeroTolerance)); + expect (approximatelyEqual (-max, -max, zeroTolerance)); + } + + beginTest ("Comparing subnormal values to zero, returns true"); + { + expect (! exactlyEqual (zero, nextFloatUp (zero))); + expect (approximatelyEqual (zero, nextFloatUp (zero))); + + expect (! exactlyEqual (zero, nextFloatDown (zero))); + expect (approximatelyEqual (zero, nextFloatDown (zero))); + + expect (! exactlyEqual (zero, nextFloatDown (min))); + expect (approximatelyEqual (zero, nextFloatDown (min))); + + expect (! exactlyEqual (zero, nextFloatUp (-min))); + expect (approximatelyEqual (zero, nextFloatUp (-min))); + } + + beginTest ("Comparing the minimum normal value to zero, returns true"); + { + expect (approximatelyEqual (zero, min)); + expect (approximatelyEqual (zero, -min)); + } + + beginTest ("Comparing normal values greater than the minimum to zero, returns true"); + { + expect (! approximatelyEqual (zero, one)); + expect (! approximatelyEqual (zero, epsilon)); + expect (! approximatelyEqual (zero, nextFloatUp (min))); + expect (! approximatelyEqual (zero, nextFloatDown (-min))); + } + + beginTest ("Values with large ranges can be compared"); + { + expect (! approximatelyEqual (zero, max)); + expect ( approximatelyEqual (zero, max, absoluteTolerance (max))); + expect ( approximatelyEqual (zero, max, relativeTolerance (one))); + expect (! approximatelyEqual (-one, max)); + expect (! approximatelyEqual (-max, max)); + } + + beginTest ("Larger values have a boundary that is a factor of the epsilon"); + { + for (auto exponent = 0; exponent < 127; ++exponent) + { + const auto value = std::pow ((T) 2, (T) exponent); + const auto boundaryValue = value * (one + epsilon); + + expect (juce_isfinite (value)); + expect (juce_isfinite (boundaryValue)); + + expect ( approximatelyEqual (value, boundaryValue)); + expect (! approximatelyEqual (value, nextFloatUp (boundaryValue))); + + expect ( approximatelyEqual (-value, -boundaryValue)); + expect (! approximatelyEqual (-value, nextFloatDown (-boundaryValue))); + } + } + + beginTest ("Tolerances scale with the values being compared"); + { + + expect (approximatelyEqual ((T) 100'000'000'000'000.01, + (T) 100'000'000'000'000.011)); + + expect (! approximatelyEqual ((T) 100.01, + (T) 100.011)); + + expect (! approximatelyEqual ((T) 123'000, (T) 121'000, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 123'000, (T) 122'000, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 123'000, (T) 123'000, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 123'000, (T) 124'000, relativeTolerance ((T) 1e-2))); + expect (! approximatelyEqual ((T) 123'000, (T) 125'000, relativeTolerance ((T) 1e-2))); + + expect (! approximatelyEqual ((T) 123, (T) 121, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 123, (T) 122, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 123, (T) 123, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 123, (T) 124, relativeTolerance ((T) 1e-2))); + expect (! approximatelyEqual ((T) 123, (T) 125, relativeTolerance ((T) 1e-2))); + + expect (! approximatelyEqual ((T) 12.3, (T) 12.1, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 12.3, (T) 12.2, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 12.3, (T) 12.3, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 12.3, (T) 12.4, relativeTolerance ((T) 1e-2))); + expect (! approximatelyEqual ((T) 12.3, (T) 12.5, relativeTolerance ((T) 1e-2))); + + expect (! approximatelyEqual ((T) 1.23, (T) 1.21, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 1.23, (T) 1.22, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 1.23, (T) 1.23, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 1.23, (T) 1.24, relativeTolerance ((T) 1e-2))); + expect (! approximatelyEqual ((T) 1.23, (T) 1.25, relativeTolerance ((T) 1e-2))); + + expect (! approximatelyEqual ((T) 0.123, (T) 0.121, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 0.123, (T) 0.122, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 0.123, (T) 0.123, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 0.123, (T) 0.124, relativeTolerance ((T) 1e-2))); + expect (! approximatelyEqual ((T) 0.123, (T) 0.125, relativeTolerance ((T) 1e-2))); + + expect (! approximatelyEqual ((T) 0.000123, (T) 0.000121, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 0.000123, (T) 0.000122, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 0.000123, (T) 0.000123, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual ((T) 0.000123, (T) 0.000124, relativeTolerance ((T) 1e-2))); + expect (! approximatelyEqual ((T) 0.000123, (T) 0.000125, relativeTolerance ((T) 1e-2))); + } + + beginTest ("The square of the square root of 2 is approximately 2"); + { + constexpr auto two = (T) 2; + const auto sqrtOfTwo = std::sqrt (two); + + expect (approximatelyEqual (sqrtOfTwo * sqrtOfTwo, two)); + expect (approximatelyEqual (-sqrtOfTwo * sqrtOfTwo, -two)); + expect (approximatelyEqual (two / sqrtOfTwo, sqrtOfTwo)); + } + + if constexpr (limits::has_quiet_NaN) + { + beginTest ("Values are never equal to NaN"); + { + const auto nan = limits::quiet_NaN(); + + expect (! approximatelyEqual (nan, nan)); + + const auto expectNotEqualTo = [&] (auto value) + { + expect (! approximatelyEqual (value, nan)); + expect (! approximatelyEqual (nan, value)); + }; + + expectNotEqualTo (zero); + expectNotEqualTo (-zero); + expectNotEqualTo (min); + expectNotEqualTo (-min); + expectNotEqualTo (one); + expectNotEqualTo (-one); + expectNotEqualTo (max); + expectNotEqualTo (-max); + } + } + + if constexpr (limits::has_infinity) + { + beginTest ("Only infinity is equal to infinity"); + { + const auto inf = limits::infinity(); + expect (approximatelyEqual (inf, inf)); + expect (approximatelyEqual (-inf, -inf)); + expect (! approximatelyEqual (inf, -inf)); + expect (! approximatelyEqual (-inf, inf)); + + const auto expectNotEqualTo = [&] (auto value) + { + expect (! approximatelyEqual (value, inf)); + expect (! approximatelyEqual (value, -inf)); + expect (! approximatelyEqual (inf, value)); + expect (! approximatelyEqual (-inf, value)); + }; + + expectNotEqualTo (zero); + expectNotEqualTo (-zero); + expectNotEqualTo (min); + expectNotEqualTo (-min); + expectNotEqualTo (one); + expectNotEqualTo (-one); + expectNotEqualTo (max); + expectNotEqualTo (-max); + } + } + + beginTest ("Can set an absolute tolerance"); + { + constexpr std::array negativePowersOfTwo + { + (T) 0.5 /* 2^-1 */, + (T) 0.25 /* 2^-2 */, + (T) 0.125 /* 2^-3 */, + (T) 0.0625 /* 2^-4 */, + (T) 0.03125 /* 2^-5 */, + (T) 0.015625 /* 2^-6 */, + (T) 0.0078125 /* 2^-7 */ + }; + + const auto testTolerance = [&] (auto tolerance) + { + const auto t = Tolerance{}.withAbsolute ((T) tolerance); + + const auto testValue= [&] (auto value) + { + const auto boundary = value + tolerance; + + expect (approximatelyEqual (value, boundary, t)); + expect (! approximatelyEqual (value, nextFloatUp (boundary), t)); + + expect (approximatelyEqual (-value, -boundary, t)); + expect (! approximatelyEqual (-value, nextFloatDown (-boundary), t)); + }; + + testValue (zero); + testValue (min); + testValue (epsilon); + testValue (one); + + for (const auto value : negativePowersOfTwo) + testValue (value); + }; + + for (const auto toleranceValue : negativePowersOfTwo) + testTolerance (toleranceValue); + } + + beginTest ("Can set a relative tolerance"); + { + expect (! approximatelyEqual (oneThird, (T) 0.34, relativeTolerance ((T) 1e-2))); + expect ( approximatelyEqual (oneThird, (T) 0.334, relativeTolerance ((T) 1e-2))); + + expect (! approximatelyEqual (oneThird, (T) 0.334, relativeTolerance ((T) 1e-3))); + expect ( approximatelyEqual (oneThird, (T) 0.3334, relativeTolerance ((T) 1e-3))); + + expect (! approximatelyEqual (oneThird, (T) 0.3334, relativeTolerance ((T) 1e-4))); + expect ( approximatelyEqual (oneThird, (T) 0.33334, relativeTolerance ((T) 1e-4))); + + expect (! approximatelyEqual (oneThird, (T) 0.33334, relativeTolerance ((T) 1e-5))); + expect ( approximatelyEqual (oneThird, (T) 0.333334, relativeTolerance ((T) 1e-5))); + + expect (! approximatelyEqual (oneThird, (T) 0.333334, relativeTolerance ((T) 1e-6))); + expect ( approximatelyEqual (oneThird, (T) 0.3333334, relativeTolerance ((T) 1e-6))); + + expect (! approximatelyEqual (oneThird, (T) 0.3333334, relativeTolerance ((T) 1e-7))); + expect ( approximatelyEqual (oneThird, (T) 0.33333334, relativeTolerance ((T) 1e-7))); + + expect ( approximatelyEqual ((T) 1e6, (T) 1e6 + (T) 1, relativeTolerance ((T) 1e-6))); + expect (! approximatelyEqual ((T) 1e6, (T) 1e6 + (T) 1, relativeTolerance ((T) 1e-7))); + + expect ( approximatelyEqual ((T) -1e-6, (T) -1.0000009e-6, relativeTolerance ((T) 1e-6))); + expect (! approximatelyEqual ((T) -1e-6, (T) -1.0000009e-6, relativeTolerance ((T) 1e-7))); + + const auto a = (T) 1.234567; + const auto b = (T) 1.234568; + + for (auto exponent = 0; exponent < 39; ++exponent) + { + const auto m = std::pow ((T) 10, (T) exponent); + expect ( approximatelyEqual (a * m, b * m, relativeTolerance ((T) 1e-6))); + expect (! approximatelyEqual (a * m, b * m, relativeTolerance ((T) 1e-7))); + } + } + + beginTest ("A relative tolerance is always scaled by the maximum value"); + { + expect ( approximatelyEqual ((T) 9, (T) 10, absoluteTolerance ((T) 10.0 * (T) 0.1))); + expect (! approximatelyEqual ((T) 9, (T) 10, absoluteTolerance ((T) 9.0 * (T) 0.1))); + + expect (approximatelyEqual ((T) 9, (T) 10, relativeTolerance ((T) 0.1))); + expect (approximatelyEqual ((T) 10, (T) 9, relativeTolerance ((T) 0.1))); + } + + beginTest ("Documentation examples"); + { + constexpr auto pi = MathConstants::pi; + + expect (! approximatelyEqual (zero, std::sin (pi))); + expect ( approximatelyEqual (zero, std::sin (pi), absoluteTolerance (std::sin (pi)))); + + expect ( approximatelyEqual ((T) 100, (T) 95, relativeTolerance ((T) 0.05))); + expect (! approximatelyEqual ((T) 100, (T) 94, relativeTolerance ((T) 0.05))); + } + } +}; + +template<> +class ApproximatelyEqualTests final : public UnitTest +{ +public: + ApproximatelyEqualTests() + : UnitTest { getTemplatedMathsFunctionUnitTestName ("approximatelyEqual"), UnitTestCategories::maths } + {} + + void runTest() final + { + beginTest ("Identical integers are always equal"); + { + expect (approximatelyEqual ( 0, 0)); + expect (approximatelyEqual (-0, -0)); + + expect (approximatelyEqual ( 1, 1)); + expect (approximatelyEqual (-1, -1)); + + using limits = std::numeric_limits; + constexpr auto min = limits::min(); + constexpr auto max = limits::max(); + + expect (approximatelyEqual (min, min)); + expect (approximatelyEqual (max, max)); + } + + beginTest ("Non-identical integers are never equal"); + { + expect (! approximatelyEqual ( 0, 1)); + expect (! approximatelyEqual ( 0, -1)); + + expect (! approximatelyEqual ( 1, 2)); + expect (! approximatelyEqual (-1, -2)); + + using limits = std::numeric_limits; + constexpr auto min = limits::min(); + constexpr auto max = limits::max(); + + expect (! approximatelyEqual (min, min + 1)); + expect (! approximatelyEqual (max, max - 1)); + } + + beginTest ("Zero is equal regardless of the sign"); + { + expect (approximatelyEqual ( 0, -0)); + expect (approximatelyEqual (-0, 0)); + } + } +}; + +template +class IsFiniteTests final : public UnitTest +{ +public: + IsFiniteTests() + : UnitTest { getTemplatedMathsFunctionUnitTestName ("juce_isfinite"), UnitTestCategories::maths } + {} + + void runTest() final + { + using limits = std::numeric_limits; + + constexpr auto zero = T{}; + constexpr auto one = (T) 1; + constexpr auto max = limits::max(); + constexpr auto inf = limits::infinity(); + constexpr auto nan = limits::quiet_NaN(); + + beginTest ("Zero is finite"); + { + expect (juce_isfinite (zero)); + expect (juce_isfinite (-zero)); + } + + beginTest ("Subnormals are finite"); + { + expect (juce_isfinite (nextFloatUp (zero))); + expect (juce_isfinite (nextFloatDown (zero))); + } + + beginTest ("One is finite"); + { + expect (juce_isfinite (one)); + expect (juce_isfinite (-one)); + } + + beginTest ("Max is finite"); + { + expect (juce_isfinite (max)); + expect (juce_isfinite (-max)); + } + + beginTest ("Infinity is not finite"); + { + expect (! juce_isfinite (inf)); + expect (! juce_isfinite (-inf)); + } + + beginTest ("NaN is not finite"); + { + expect (! juce_isfinite (nan)); + expect (! juce_isfinite (-nan)); + expect (! juce_isfinite (std::sqrt ((T) -1))); + expect (! juce_isfinite (inf * zero)); + } + } +}; + +template +class NextFloatTests final : public UnitTest +{ +public: + NextFloatTests() + : UnitTest { getTemplatedMathsFunctionUnitTestName ("nextFloat"), UnitTestCategories::maths } + {} + + void runTest() final + { + using limits = std::numeric_limits; + + constexpr auto zero = T{}; + constexpr auto one = T (1); + constexpr auto min = limits::min(); + constexpr auto epsilon = limits::epsilon(); + + beginTest ("nextFloat from zero is subnormal"); + { + expect (juce_isfinite (nextFloatUp (zero))); + expect (! exactlyEqual (zero, nextFloatUp (zero))); + expect (! std::isnormal (nextFloatUp (zero))); + + expect (juce_isfinite (nextFloatDown (zero))); + expect (! exactlyEqual (zero, nextFloatDown (zero))); + expect (! std::isnormal (nextFloatDown (zero))); + } + + beginTest ("nextFloat from min, towards zero, is subnormal"); + { + expect (std::isnormal (min)); + expect (std::isnormal (-min)); + expect (! std::isnormal (nextFloatDown (min))); + expect (! std::isnormal (nextFloatUp (-min))); + } + + beginTest ("nextFloat from one matches epsilon"); + { + expect (! exactlyEqual (one, nextFloatUp (one))); + expect ( exactlyEqual (one + epsilon, nextFloatUp (one))); + + expect (! exactlyEqual (-one, nextFloatDown (-one))); + expect ( exactlyEqual (-one - epsilon, nextFloatDown (-one))); + } + } +}; + +template +struct MathsFloatingPointFunctionsTests +{ + IsFiniteTests isFiniteTests; + NextFloatTests nextFloatTests; + ApproximatelyEqualTests approximatelyEqualTests; +}; + +template<> +struct MathsFloatingPointFunctionsTests +{ + ApproximatelyEqualTests approximatelyEqualTests; +}; + +struct MathsFunctionsTests +{ + MathsFloatingPointFunctionsTests intFunctionTests; + MathsFloatingPointFunctionsTests floatFunctionTests; + MathsFloatingPointFunctionsTests doubleFunctionTests; + MathsFloatingPointFunctionsTests longDoubleFunctionTests; +}; + +static MathsFunctionsTests mathsFunctionsTests; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h b/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h index 0de7951f..4532bfd3 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -128,7 +128,7 @@ class NormalisableRange auto proportion = clampTo0To1 ((v - start) / (end - start)); - if (skew == static_cast (1)) + if (exactlyEqual (skew, static_cast (1))) return proportion; if (! symmetricSkew) @@ -154,7 +154,7 @@ class NormalisableRange if (! symmetricSkew) { - if (skew != static_cast (1) && proportion > ValueType()) + if (! exactlyEqual (skew, static_cast (1)) && proportion > ValueType()) proportion = std::exp (std::log (proportion) / skew); return start + (end - start) * proportion; @@ -162,7 +162,7 @@ class NormalisableRange auto distanceFromMiddle = static_cast (2) * proportion - static_cast (1); - if (skew != static_cast (1) && distanceFromMiddle != static_cast (0)) + if (! exactlyEqual (skew, static_cast (1)) && ! exactlyEqual (distanceFromMiddle, static_cast (0))) distanceFromMiddle = std::exp (std::log (std::abs (distanceFromMiddle)) / skew) * (distanceFromMiddle < ValueType() ? static_cast (-1) : static_cast (1)); @@ -250,7 +250,7 @@ class NormalisableRange // If you hit this assertion then either your normalisation function is not working // correctly or your input is out of the expected bounds. - jassert (clampedValue == value); + jassert (exactlyEqual (clampedValue, value)); return clampedValue; } diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp index f536288b..5dfa0d1f 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -32,10 +32,6 @@ Random::Random() : seed (1) setSeedRandomly(); } -Random::~Random() noexcept -{ -} - void Random::setSeed (const int64 newSeed) noexcept { if (this == &getSystemRandom()) @@ -105,7 +101,9 @@ bool Random::nextBool() noexcept float Random::nextFloat() noexcept { - return static_cast (nextInt()) / (std::numeric_limits::max() + 1.0f); + auto result = static_cast (static_cast (nextInt())) + / (static_cast (std::numeric_limits::max()) + 1.0f); + return jmin (result, 1.0f - std::numeric_limits::epsilon()); } double Random::nextDouble() noexcept @@ -166,7 +164,7 @@ void Random::fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numB //============================================================================== #if JUCE_UNIT_TESTS -class RandomTests : public UnitTest +class RandomTests final : public UnitTest { public: RandomTests() diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Random.h b/JuceLibraryCode/modules/juce_core/maths/juce_Random.h index 7d90c5d3..f89338a2 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Random.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Random.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -50,9 +50,6 @@ class JUCE_API Random final */ Random(); - /** Destructor. */ - ~Random() noexcept; - /** Returns the next random 32 bit integer. @returns a random integer from the full range 0x80000000 to 0x7fffffff */ diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Range.h b/JuceLibraryCode/modules/juce_core/maths/juce_Range.h index 6674f042..f8a37b7b 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Range.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Range.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -41,52 +41,52 @@ class Range public: //============================================================================== /** Constructs an empty range. */ - JUCE_CONSTEXPR Range() = default; + constexpr Range() = default; /** Constructs a range with given start and end values. */ - JUCE_CONSTEXPR Range (const ValueType startValue, const ValueType endValue) noexcept + constexpr Range (const ValueType startValue, const ValueType endValue) noexcept : start (startValue), end (jmax (startValue, endValue)) { } /** Constructs a copy of another range. */ - JUCE_CONSTEXPR Range (const Range&) = default; + constexpr Range (const Range&) = default; /** Copies another range object. */ Range& operator= (const Range&) = default; /** Returns the range that lies between two positions (in either order). */ - JUCE_CONSTEXPR static Range between (const ValueType position1, const ValueType position2) noexcept + constexpr static Range between (const ValueType position1, const ValueType position2) noexcept { return position1 < position2 ? Range (position1, position2) : Range (position2, position1); } /** Returns a range with a given start and length. */ - static Range withStartAndLength (const ValueType startValue, const ValueType length) noexcept + [[nodiscard]] static Range withStartAndLength (const ValueType startValue, const ValueType length) noexcept { jassert (length >= ValueType()); return Range (startValue, startValue + length); } /** Returns a range with the specified start position and a length of zero. */ - JUCE_CONSTEXPR static Range emptyRange (const ValueType start) noexcept + [[nodiscard]] constexpr static Range emptyRange (const ValueType start) noexcept { return Range (start, start); } //============================================================================== /** Returns the start of the range. */ - JUCE_CONSTEXPR inline ValueType getStart() const noexcept { return start; } + constexpr inline ValueType getStart() const noexcept { return start; } /** Returns the length of the range. */ - JUCE_CONSTEXPR inline ValueType getLength() const noexcept { return end - start; } + constexpr inline ValueType getLength() const noexcept { return end - start; } /** Returns the end of the range. */ - JUCE_CONSTEXPR inline ValueType getEnd() const noexcept { return end; } + constexpr inline ValueType getEnd() const noexcept { return end; } /** Returns true if the range has a length of zero. */ - JUCE_CONSTEXPR inline bool isEmpty() const noexcept { return start == end; } + constexpr inline bool isEmpty() const noexcept { return exactlyEqual (start, end); } //============================================================================== /** Changes the start position of the range, leaving the end position unchanged. @@ -104,13 +104,13 @@ class Range If the new start position is higher than the current end of the range, the end point will be pushed along to equal it, returning an empty range at the new position. */ - JUCE_CONSTEXPR Range withStart (const ValueType newStart) const noexcept + [[nodiscard]] constexpr Range withStart (const ValueType newStart) const noexcept { return Range (newStart, jmax (newStart, end)); } /** Returns a range with the same length as this one, but moved to have the given start position. */ - JUCE_CONSTEXPR Range movedToStartAt (const ValueType newStart) const noexcept + [[nodiscard]] constexpr Range movedToStartAt (const ValueType newStart) const noexcept { return Range (newStart, end + (newStart - start)); } @@ -130,13 +130,13 @@ class Range If the new end position is below the current start of the range, the start point will be pushed back to equal the new end point. */ - JUCE_CONSTEXPR Range withEnd (const ValueType newEnd) const noexcept + [[nodiscard]] constexpr Range withEnd (const ValueType newEnd) const noexcept { return Range (jmin (start, newEnd), newEnd); } /** Returns a range with the same length as this one, but moved to have the given end position. */ - JUCE_CONSTEXPR Range movedToEndAt (const ValueType newEnd) const noexcept + [[nodiscard]] constexpr Range movedToEndAt (const ValueType newEnd) const noexcept { return Range (start + (newEnd - end), newEnd); } @@ -152,7 +152,7 @@ class Range /** Returns a range with the same start as this one, but a different length. Lengths less than zero are treated as zero. */ - JUCE_CONSTEXPR Range withLength (const ValueType newLength) const noexcept + [[nodiscard]] constexpr Range withLength (const ValueType newLength) const noexcept { return Range (start, start + newLength); } @@ -161,7 +161,7 @@ class Range given amount. @returns The returned range will be (start - amount, end + amount) */ - JUCE_CONSTEXPR Range expanded (ValueType amount) const noexcept + [[nodiscard]] constexpr Range expanded (ValueType amount) const noexcept { return Range (start - amount, end + amount); } @@ -186,27 +186,32 @@ class Range /** Returns a range that is equal to this one with an amount added to its start and end. */ - JUCE_CONSTEXPR Range operator+ (const ValueType amountToAdd) const noexcept + constexpr Range operator+ (const ValueType amountToAdd) const noexcept { return Range (start + amountToAdd, end + amountToAdd); } /** Returns a range that is equal to this one with the specified amount subtracted from its start and end. */ - JUCE_CONSTEXPR Range operator- (const ValueType amountToSubtract) const noexcept + constexpr Range operator- (const ValueType amountToSubtract) const noexcept { return Range (start - amountToSubtract, end - amountToSubtract); } - JUCE_CONSTEXPR bool operator== (Range other) const noexcept { return start == other.start && end == other.end; } - JUCE_CONSTEXPR bool operator!= (Range other) const noexcept { return start != other.start || end != other.end; } + constexpr bool operator== (Range other) const noexcept + { + const auto tie = [] (const Range& r) { return std::tie (r.start, r.end); }; + return tie (*this) == tie (other); + } + + constexpr bool operator!= (Range other) const noexcept { return ! operator== (other); } //============================================================================== /** Returns true if the given position lies inside this range. When making this comparison, the start value is considered to be inclusive, and the end of the range exclusive. */ - JUCE_CONSTEXPR bool contains (const ValueType position) const noexcept + constexpr bool contains (const ValueType position) const noexcept { return start <= position && position < end; } @@ -218,34 +223,34 @@ class Range } /** Returns true if the given range lies entirely inside this range. */ - JUCE_CONSTEXPR bool contains (Range other) const noexcept + constexpr bool contains (Range other) const noexcept { return start <= other.start && end >= other.end; } /** Returns true if the given range intersects this one. */ - JUCE_CONSTEXPR bool intersects (Range other) const noexcept + constexpr bool intersects (Range other) const noexcept { return other.start < end && start < other.end; } /** Returns the range that is the intersection of the two ranges, or an empty range with an undefined start position if they don't overlap. */ - JUCE_CONSTEXPR Range getIntersectionWith (Range other) const noexcept + [[nodiscard]] constexpr Range getIntersectionWith (Range other) const noexcept { return Range (jmax (start, other.start), jmin (end, other.end)); } /** Returns the smallest range that contains both this one and the other one. */ - JUCE_CONSTEXPR Range getUnionWith (Range other) const noexcept + [[nodiscard]] constexpr Range getUnionWith (Range other) const noexcept { return Range (jmin (start, other.start), jmax (end, other.end)); } /** Returns the smallest range that contains both this one and the given value. */ - JUCE_CONSTEXPR Range getUnionWith (const ValueType valueToInclude) const noexcept + [[nodiscard]] constexpr Range getUnionWith (const ValueType valueToInclude) const noexcept { return Range (jmin (valueToInclude, start), jmax (valueToInclude, end)); @@ -270,7 +275,8 @@ class Range } /** Scans an array of values for its min and max, and returns these as a Range. */ - static Range findMinAndMax (const ValueType* values, int numValues) noexcept + template , int> = 0> + static Range findMinAndMax (const ValueType* values, Integral numValues) noexcept { if (numValues <= 0) return Range(); diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_StatisticsAccumulator.h b/JuceLibraryCode/modules/juce_core/maths/juce_StatisticsAccumulator.h index 6491d331..f69f286f 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_StatisticsAccumulator.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_StatisticsAccumulator.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_AllocationHooks.cpp b/JuceLibraryCode/modules/juce_core/memory/juce_AllocationHooks.cpp new file mode 100644 index 00000000..4b783c92 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/memory/juce_AllocationHooks.cpp @@ -0,0 +1,100 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#if JUCE_ENABLE_ALLOCATION_HOOKS + +namespace juce +{ + +static AllocationHooks& getAllocationHooksForThread() +{ + thread_local AllocationHooks hooks; + return hooks; +} + +void notifyAllocationHooksForThread() +{ + getAllocationHooksForThread().listenerList.call ([] (AllocationHooks::Listener& l) + { + l.newOrDeleteCalled(); + }); +} + +} + +void* operator new (size_t s) +{ + juce::notifyAllocationHooksForThread(); + return std::malloc (s); +} + +void* operator new[] (size_t s) +{ + juce::notifyAllocationHooksForThread(); + return std::malloc (s); +} + +void operator delete (void* p) noexcept +{ + juce::notifyAllocationHooksForThread(); + std::free (p); +} + +void operator delete[] (void* p) noexcept +{ + juce::notifyAllocationHooksForThread(); + std::free (p); +} + +void operator delete (void* p, size_t) noexcept +{ + juce::notifyAllocationHooksForThread(); + std::free (p); +} + +void operator delete[] (void* p, size_t) noexcept +{ + juce::notifyAllocationHooksForThread(); + std::free (p); +} + +namespace juce +{ + +//============================================================================== +UnitTestAllocationChecker::UnitTestAllocationChecker (UnitTest& test) + : unitTest (test) +{ + getAllocationHooksForThread().addListener (this); +} + +UnitTestAllocationChecker::~UnitTestAllocationChecker() noexcept +{ + getAllocationHooksForThread().removeListener (this); + unitTest.expectEquals ((int) calls, 0, "new or delete was incorrectly called while allocation checker was active"); +} + +void UnitTestAllocationChecker::newOrDeleteCalled() noexcept { ++calls; } + +} + +#endif diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_AllocationHooks.h b/JuceLibraryCode/modules/juce_core/memory/juce_AllocationHooks.h new file mode 100644 index 00000000..2304e964 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/memory/juce_AllocationHooks.h @@ -0,0 +1,73 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#if JUCE_ENABLE_ALLOCATION_HOOKS + +namespace juce +{ + +class AllocationHooks +{ +public: + struct Listener + { + virtual ~Listener() noexcept = default; + virtual void newOrDeleteCalled() noexcept = 0; + }; + + void addListener (Listener* l) { listenerList.add (l); } + void removeListener (Listener* l) noexcept { listenerList.remove (l); } + +private: + friend void notifyAllocationHooksForThread(); + ListenerList listenerList; +}; + +//============================================================================== +/** Scoped checker which will cause a unit test failure if any new/delete calls + are made during the lifetime of the UnitTestAllocationChecker. +*/ +class UnitTestAllocationChecker : private AllocationHooks::Listener +{ +public: + /** Create a checker which will log a failure to the passed test if + any calls to new/delete are made. + + Remember to call `UnitTest::beginTest` before constructing this checker! + */ + explicit UnitTestAllocationChecker (UnitTest& test); + + /** Will add a failure to the test if the number of new/delete calls during + this object's lifetime was greater than zero. + */ + ~UnitTestAllocationChecker() noexcept override; + +private: + void newOrDeleteCalled() noexcept override; + + UnitTest& unitTest; + size_t calls = 0; +}; + +} + +#endif diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h b/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h index 113bd1e1..b5c5e49a 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -136,10 +136,9 @@ struct Atomic final //============================================================================== #ifndef DOXYGEN - /* This method has been deprecated as there is no equivalent method in - std::atomic. Use compareAndSetBool instead. - */ - JUCE_DEPRECATED (Type compareAndSetValue (Type, Type) noexcept); + [[deprecated ("This method has been deprecated as there is no equivalent method in " + "std::atomic. Use compareAndSetBool instead.")]] + Type compareAndSetValue (Type, Type) noexcept; #endif }; diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h b/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h index b9e544c8..e83d1d22 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -34,10 +34,10 @@ class JUCE_API ByteOrder public: //============================================================================== /** Swaps the upper and lower bytes of a 16-bit integer. */ - JUCE_CONSTEXPR static uint16 swap (uint16 value) noexcept; + constexpr static uint16 swap (uint16 value) noexcept; /** Swaps the upper and lower bytes of a 16-bit integer. */ - JUCE_CONSTEXPR static int16 swap (int16 value) noexcept; + constexpr static int16 swap (int16 value) noexcept; /** Reverses the order of the 4 bytes in a 32-bit integer. */ static uint32 swap (uint32 value) noexcept; @@ -82,50 +82,50 @@ class JUCE_API ByteOrder //============================================================================== /** Turns 4 bytes into a little-endian integer. */ - JUCE_CONSTEXPR static uint32 littleEndianInt (const void* bytes) noexcept; + constexpr static uint32 littleEndianInt (const void* bytes) noexcept; /** Turns 8 bytes into a little-endian integer. */ - JUCE_CONSTEXPR static uint64 littleEndianInt64 (const void* bytes) noexcept; + constexpr static uint64 littleEndianInt64 (const void* bytes) noexcept; /** Turns 2 bytes into a little-endian integer. */ - JUCE_CONSTEXPR static uint16 littleEndianShort (const void* bytes) noexcept; + constexpr static uint16 littleEndianShort (const void* bytes) noexcept; /** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ - JUCE_CONSTEXPR static int littleEndian24Bit (const void* bytes) noexcept; + constexpr static int littleEndian24Bit (const void* bytes) noexcept; /** Copies a 24-bit number to 3 little-endian bytes. */ static void littleEndian24BitToChars (int32 value, void* destBytes) noexcept; //============================================================================== /** Turns 4 bytes into a big-endian integer. */ - JUCE_CONSTEXPR static uint32 bigEndianInt (const void* bytes) noexcept; + constexpr static uint32 bigEndianInt (const void* bytes) noexcept; /** Turns 8 bytes into a big-endian integer. */ - JUCE_CONSTEXPR static uint64 bigEndianInt64 (const void* bytes) noexcept; + constexpr static uint64 bigEndianInt64 (const void* bytes) noexcept; /** Turns 2 bytes into a big-endian integer. */ - JUCE_CONSTEXPR static uint16 bigEndianShort (const void* bytes) noexcept; + constexpr static uint16 bigEndianShort (const void* bytes) noexcept; /** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ - JUCE_CONSTEXPR static int bigEndian24Bit (const void* bytes) noexcept; + constexpr static int bigEndian24Bit (const void* bytes) noexcept; /** Copies a 24-bit number to 3 big-endian bytes. */ static void bigEndian24BitToChars (int32 value, void* destBytes) noexcept; //============================================================================== /** Constructs a 16-bit integer from its constituent bytes, in order of significance. */ - JUCE_CONSTEXPR static uint16 makeInt (uint8 leastSig, uint8 mostSig) noexcept; + constexpr static uint16 makeInt (uint8 leastSig, uint8 mostSig) noexcept; /** Constructs a 32-bit integer from its constituent bytes, in order of significance. */ - JUCE_CONSTEXPR static uint32 makeInt (uint8 leastSig, uint8 byte1, uint8 byte2, uint8 mostSig) noexcept; + constexpr static uint32 makeInt (uint8 leastSig, uint8 byte1, uint8 byte2, uint8 mostSig) noexcept; /** Constructs a 64-bit integer from its constituent bytes, in order of significance. */ - JUCE_CONSTEXPR static uint64 makeInt (uint8 leastSig, uint8 byte1, uint8 byte2, uint8 byte3, - uint8 byte4, uint8 byte5, uint8 byte6, uint8 mostSig) noexcept; + constexpr static uint64 makeInt (uint8 leastSig, uint8 byte1, uint8 byte2, uint8 byte3, + uint8 byte4, uint8 byte5, uint8 byte6, uint8 mostSig) noexcept; //============================================================================== /** Returns true if the current CPU is big-endian. */ - JUCE_CONSTEXPR static bool isBigEndian() noexcept + constexpr static bool isBigEndian() noexcept { #if JUCE_LITTLE_ENDIAN return false; @@ -140,8 +140,8 @@ class JUCE_API ByteOrder //============================================================================== -JUCE_CONSTEXPR inline uint16 ByteOrder::swap (uint16 v) noexcept { return static_cast ((v << 8) | (v >> 8)); } -JUCE_CONSTEXPR inline int16 ByteOrder::swap (int16 v) noexcept { return static_cast (swap (static_cast (v))); } +constexpr inline uint16 ByteOrder::swap (uint16 v) noexcept { return static_cast ((v << 8) | (v >> 8)); } +constexpr inline int16 ByteOrder::swap (int16 v) noexcept { return static_cast (swap (static_cast (v))); } inline int32 ByteOrder::swap (int32 v) noexcept { return static_cast (swap (static_cast (v))); } inline int64 ByteOrder::swap (int64 v) noexcept { return static_cast (swap (static_cast (v))); } inline float ByteOrder::swap (float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = swap (n.asUInt); return n.asFloat; } @@ -178,41 +178,41 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept #endif } -JUCE_CONSTEXPR inline uint16 ByteOrder::makeInt (uint8 b0, uint8 b1) noexcept +constexpr inline uint16 ByteOrder::makeInt (uint8 b0, uint8 b1) noexcept { return static_cast (static_cast (b0) | (static_cast (b1) << 8)); } -JUCE_CONSTEXPR inline uint32 ByteOrder::makeInt (uint8 b0, uint8 b1, uint8 b2, uint8 b3) noexcept +constexpr inline uint32 ByteOrder::makeInt (uint8 b0, uint8 b1, uint8 b2, uint8 b3) noexcept { return static_cast (b0) | (static_cast (b1) << 8) | (static_cast (b2) << 16) | (static_cast (b3) << 24); } -JUCE_CONSTEXPR inline uint64 ByteOrder::makeInt (uint8 b0, uint8 b1, uint8 b2, uint8 b3, uint8 b4, uint8 b5, uint8 b6, uint8 b7) noexcept +constexpr inline uint64 ByteOrder::makeInt (uint8 b0, uint8 b1, uint8 b2, uint8 b3, uint8 b4, uint8 b5, uint8 b6, uint8 b7) noexcept { return static_cast (b0) | (static_cast (b1) << 8) | (static_cast (b2) << 16) | (static_cast (b3) << 24) | (static_cast (b4) << 32) | (static_cast (b5) << 40) | (static_cast (b6) << 48) | (static_cast (b7) << 56); } -JUCE_CONSTEXPR inline uint16 ByteOrder::littleEndianShort (const void* bytes) noexcept { return makeInt (static_cast (bytes)[0], static_cast (bytes)[1]); } -JUCE_CONSTEXPR inline uint32 ByteOrder::littleEndianInt (const void* bytes) noexcept { return makeInt (static_cast (bytes)[0], static_cast (bytes)[1], +constexpr inline uint16 ByteOrder::littleEndianShort (const void* bytes) noexcept { return makeInt (static_cast (bytes)[0], static_cast (bytes)[1]); } +constexpr inline uint32 ByteOrder::littleEndianInt (const void* bytes) noexcept { return makeInt (static_cast (bytes)[0], static_cast (bytes)[1], static_cast (bytes)[2], static_cast (bytes)[3]); } -JUCE_CONSTEXPR inline uint64 ByteOrder::littleEndianInt64 (const void* bytes) noexcept { return makeInt (static_cast (bytes)[0], static_cast (bytes)[1], +constexpr inline uint64 ByteOrder::littleEndianInt64 (const void* bytes) noexcept { return makeInt (static_cast (bytes)[0], static_cast (bytes)[1], static_cast (bytes)[2], static_cast (bytes)[3], static_cast (bytes)[4], static_cast (bytes)[5], static_cast (bytes)[6], static_cast (bytes)[7]); } -JUCE_CONSTEXPR inline uint16 ByteOrder::bigEndianShort (const void* bytes) noexcept { return makeInt (static_cast (bytes)[1], static_cast (bytes)[0]); } -JUCE_CONSTEXPR inline uint32 ByteOrder::bigEndianInt (const void* bytes) noexcept { return makeInt (static_cast (bytes)[3], static_cast (bytes)[2], +constexpr inline uint16 ByteOrder::bigEndianShort (const void* bytes) noexcept { return makeInt (static_cast (bytes)[1], static_cast (bytes)[0]); } +constexpr inline uint32 ByteOrder::bigEndianInt (const void* bytes) noexcept { return makeInt (static_cast (bytes)[3], static_cast (bytes)[2], static_cast (bytes)[1], static_cast (bytes)[0]); } -JUCE_CONSTEXPR inline uint64 ByteOrder::bigEndianInt64 (const void* bytes) noexcept { return makeInt (static_cast (bytes)[7], static_cast (bytes)[6], +constexpr inline uint64 ByteOrder::bigEndianInt64 (const void* bytes) noexcept { return makeInt (static_cast (bytes)[7], static_cast (bytes)[6], static_cast (bytes)[5], static_cast (bytes)[4], static_cast (bytes)[3], static_cast (bytes)[2], static_cast (bytes)[1], static_cast (bytes)[0]); } -JUCE_CONSTEXPR inline int32 ByteOrder::littleEndian24Bit (const void* bytes) noexcept { return (int32) ((((uint32) static_cast (bytes)[2]) << 16) | (((uint32) static_cast (bytes)[1]) << 8) | ((uint32) static_cast (bytes)[0])); } -JUCE_CONSTEXPR inline int32 ByteOrder::bigEndian24Bit (const void* bytes) noexcept { return (int32) ((((uint32) static_cast (bytes)[0]) << 16) | (((uint32) static_cast (bytes)[1]) << 8) | ((uint32) static_cast (bytes)[2])); } +constexpr inline int32 ByteOrder::littleEndian24Bit (const void* bytes) noexcept { return (int32) ((((uint32) static_cast (bytes)[2]) << 16) | (((uint32) static_cast (bytes)[1]) << 8) | ((uint32) static_cast (bytes)[0])); } +constexpr inline int32 ByteOrder::bigEndian24Bit (const void* bytes) noexcept { return (int32) ((((uint32) static_cast (bytes)[0]) << 16) | (((uint32) static_cast (bytes)[1]) << 8) | ((uint32) static_cast (bytes)[2])); } inline void ByteOrder::littleEndian24BitToChars (int32 value, void* destBytes) noexcept { static_cast (destBytes)[0] = (uint8) value; static_cast (destBytes)[1] = (uint8) (value >> 8); static_cast (destBytes)[2] = (uint8) (value >> 16); } inline void ByteOrder::bigEndian24BitToChars (int32 value, void* destBytes) noexcept { static_cast (destBytes)[0] = (uint8) (value >> 16); static_cast (destBytes)[1] = (uint8) (value >> 8); static_cast (destBytes)[2] = (uint8) value; } diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h b/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h index 0c064027..d6130fa2 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -49,7 +49,7 @@ struct ContainerDeletePolicy // 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)); + [[maybe_unused]] constexpr auto size = sizeof (ObjectType); delete object; } diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h b/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h index e526d336..779dbbe8 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,13 +23,13 @@ namespace juce { -#if ! (defined (DOXYGEN) || JUCE_EXCEPTIONS_DISABLED) +#if ! (DOXYGEN || JUCE_EXCEPTIONS_DISABLED) namespace HeapBlockHelper { template struct ThrowOnFail { static void checkPointer (void*) {} }; - template<> + template <> struct ThrowOnFail { static void checkPointer (void* data) { if (data == nullptr) throw std::bad_alloc(); } }; } #endif @@ -87,8 +87,8 @@ class HeapBlock { private: template - using AllowConversion = typename std::enable_if::type, - typename std::remove_pointer::type>::value>::type; + using AllowConversion = std::enable_if_t, + std::remove_pointer_t>>; public: //============================================================================== @@ -107,11 +107,10 @@ class HeapBlock If you want an array of zero values, you can use the calloc() method or the other constructor that takes an InitialisationState parameter. */ - template + template , int> = 0> explicit HeapBlock (SizeType numElements) - : data (static_cast (std::malloc (static_cast (numElements) * sizeof (ElementType)))) + : data (mallocWrapper (static_cast (numElements) * sizeof (ElementType))) { - throwOnAllocationFailure(); } /** Creates a HeapBlock containing a number of elements. @@ -119,13 +118,11 @@ class HeapBlock The initialiseToZero parameter determines whether the new memory should be cleared, or left uninitialised. */ - template + template , int> = 0> HeapBlock (SizeType numElements, bool initialiseToZero) - : data (static_cast (initialiseToZero - ? std::calloc (static_cast (numElements), sizeof (ElementType)) - : std::malloc (static_cast (numElements) * sizeof (ElementType)))) + : data (initialiseToZero ? callocWrapper (static_cast (numElements), sizeof (ElementType)) + : mallocWrapper (static_cast (numElements) * sizeof (ElementType))) { - throwOnAllocationFailure(); } /** Destructor. @@ -152,7 +149,7 @@ class HeapBlock /** Converting move constructor. Only enabled if this is a HeapBlock and the other object is a HeapBlock, - where std::is_base_of::value == true. + where std::is_base_of_v == true. */ template > HeapBlock (HeapBlock&& other) noexcept @@ -163,7 +160,7 @@ class HeapBlock /** Converting move assignment operator. Only enabled if this is a HeapBlock and the other object is a HeapBlock, - where std::is_base_of::value == true. + where std::is_base_of_v == true. */ template > HeapBlock& operator= (HeapBlock&& other) noexcept @@ -252,8 +249,7 @@ class HeapBlock void malloc (SizeType newNumElements, size_t elementSize = sizeof (ElementType)) { std::free (data); - data = static_cast (std::malloc (static_cast (newNumElements) * elementSize)); - throwOnAllocationFailure(); + data = mallocWrapper (static_cast (newNumElements) * elementSize); } /** Allocates a specified amount of memory and clears it. @@ -263,8 +259,7 @@ class HeapBlock void calloc (SizeType newNumElements, const size_t elementSize = sizeof (ElementType)) { std::free (data); - data = static_cast (std::calloc (static_cast (newNumElements), elementSize)); - throwOnAllocationFailure(); + data = callocWrapper (static_cast (newNumElements), elementSize); } /** Allocates a specified amount of memory and optionally clears it. @@ -275,10 +270,8 @@ class HeapBlock void allocate (SizeType newNumElements, bool initialiseToZero) { std::free (data); - data = static_cast (initialiseToZero - ? std::calloc (static_cast (newNumElements), sizeof (ElementType)) - : std::malloc (static_cast (newNumElements) * sizeof (ElementType))); - throwOnAllocationFailure(); + data = initialiseToZero ? callocWrapper (static_cast (newNumElements), sizeof (ElementType)) + : mallocWrapper (static_cast (newNumElements) * sizeof (ElementType)); } /** Re-allocates a specified amount of memory. @@ -289,9 +282,7 @@ class HeapBlock template void realloc (SizeType newNumElements, size_t elementSize = sizeof (ElementType)) { - data = static_cast (data == nullptr ? std::malloc (static_cast (newNumElements) * elementSize) - : std::realloc (data, static_cast (newNumElements) * elementSize)); - throwOnAllocationFailure(); + data = reallocWrapper (data, static_cast (newNumElements) * elementSize); } /** Frees any currently-allocated data. @@ -327,20 +318,46 @@ class HeapBlock private: //============================================================================== - ElementType* data = nullptr; - - void throwOnAllocationFailure() const + // Calls to malloc, calloc and realloc with zero size have implementation-defined + // behaviour where either nullptr or a non-null pointer is returned. + template + static ElementType* wrapper (size_t size, Functor&& f) { + if (size == 0) + return nullptr; + + auto* memory = static_cast (f()); + #if JUCE_EXCEPTIONS_DISABLED - jassert (data != nullptr); // without exceptions, you'll need to find a better way to handle this failure case. + jassert (memory != nullptr); // without exceptions, you'll need to find a better way to handle this failure case. #else - HeapBlockHelper::ThrowOnFail::checkPointer (data); + HeapBlockHelper::ThrowOnFail::checkPointer (memory); #endif + + return memory; + } + + static ElementType* mallocWrapper (size_t size) + { + return wrapper (size, [size] { return std::malloc (size); }); + } + + static ElementType* callocWrapper (size_t num, size_t size) + { + return wrapper (num * size, [num, size] { return std::calloc (num, size); }); + } + + static ElementType* reallocWrapper (void* ptr, size_t newSize) + { + return wrapper (newSize, [ptr, newSize] { return std::realloc (ptr, newSize); }); } template friend class HeapBlock; + //============================================================================== + ElementType* data = nullptr; + #if ! (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD)) JUCE_DECLARE_NON_COPYABLE (HeapBlock) JUCE_PREVENT_HEAP_ALLOCATION // Creating a 'new HeapBlock' would be missing the point! diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h b/JuceLibraryCode/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h index fbc856ea..381d87a9 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h b/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h index ad9ba624..2c94d062 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -46,6 +46,8 @@ class LeakedObjectDetector LeakedObjectDetector() noexcept { ++(getCounter().numObjects); } LeakedObjectDetector (const LeakedObjectDetector&) noexcept { ++(getCounter().numObjects); } + LeakedObjectDetector& operator= (const LeakedObjectDetector&) noexcept = default; + ~LeakedObjectDetector() { if (--(getCounter().numObjects) < 0) @@ -111,7 +113,7 @@ class LeakedObjectDetector #if (DOXYGEN || JUCE_CHECK_MEMORY_LEAKS) /** This macro lets you embed a leak-detecting object inside a class. - To use it, simply declare a JUCE_LEAK_DETECTOR(YourClassName) inside a private section + To use it, simply declare a JUCE_LEAK_DETECTOR (YourClassName) inside a private section of the class declaration. E.g. @code diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h b/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h index 8c7f83c3..2fee7fd9 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -39,13 +39,6 @@ inline void zerostruct (Type& structure) noexcept { memset ((v template inline void deleteAndZero (Type& pointer) { delete pointer; pointer = nullptr; } -/** A handy function which adds a number of bytes to any type of pointer and returns the result. - This can be useful to avoid casting pointers to a char* and back when you want to move them by - a specific number of bytes, -*/ -template -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. */ template @@ -83,6 +76,55 @@ inline void writeUnaligned (void* dstPtr, Type value) noexcept memcpy (dstPtr, &value, sizeof (Type)); } +//============================================================================== +/** Casts a pointer to another type via `void*`, which suppresses the cast-align + warning which sometimes arises when casting pointers to types with different + alignment. + You should only use this when you know for a fact that the input pointer points + to a region that has suitable alignment for `Type`, e.g. regions returned from + malloc/calloc that should be suitable for any non-over-aligned type. +*/ +template +inline Type unalignedPointerCast (void* ptr) noexcept +{ + static_assert (std::is_pointer_v); + return reinterpret_cast (ptr); +} + +/** Casts a pointer to another type via `void*`, which suppresses the cast-align + warning which sometimes arises when casting pointers to types with different + alignment. + You should only use this when you know for a fact that the input pointer points + to a region that has suitable alignment for `Type`, e.g. regions returned from + malloc/calloc that should be suitable for any non-over-aligned type. +*/ +template +inline Type unalignedPointerCast (const void* ptr) noexcept +{ + static_assert (std::is_pointer_v); + return reinterpret_cast (ptr); +} + +/** A handy function which adds a number of bytes to any type of pointer and returns the result. + This can be useful to avoid casting pointers to a char* and back when you want to move them by + a specific number of bytes, +*/ +template +inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept +{ + return unalignedPointerCast (reinterpret_cast (basePointer) + bytes); +} + +/** A handy function which adds a number of bytes to any type of pointer and returns the result. + This can be useful to avoid casting pointers to a char* and back when you want to move them by + a specific number of bytes, +*/ +template +inline const Type* addBytesToPointer (const Type* basePointer, IntegerType bytes) noexcept +{ + return unalignedPointerCast (reinterpret_cast (basePointer) + bytes); +} + //============================================================================== #if JUCE_MAC || JUCE_IOS || DOXYGEN @@ -143,4 +185,18 @@ inline void writeUnaligned (void* dstPtr, Type value) noexcept #define juce_UseDebuggingNewOperator #endif +/** Converts an owning raw pointer into a unique_ptr, deriving the + type of the unique_ptr automatically. + + This should only be used with pointers to single objects. + Do NOT pass a pointer to an array to this function, as the + destructor of the unique_ptr will incorrectly call `delete` + instead of `delete[]` on the pointer. +*/ +template +std::unique_ptr rawToUniquePtr (T* ptr) +{ + return std::unique_ptr (ptr); +} + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp index 079dc6a5..97602b74 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp +++ b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -174,14 +174,17 @@ void MemoryBlock::append (const void* srcData, size_t numBytes) } } -void MemoryBlock::replaceWith (const void* srcData, size_t numBytes) +void MemoryBlock::replaceAll (const void* srcData, size_t numBytes) { - if (numBytes > 0) + if (numBytes <= 0) { - jassert (srcData != nullptr); // this must not be null! - setSize (numBytes); - memcpy (data, srcData, numBytes); + reset(); + return; } + + jassert (srcData != nullptr); // this must not be null! + setSize (numBytes); + memcpy (data, srcData, numBytes); } void MemoryBlock::insert (const void* srcData, size_t numBytes, size_t insertPosition) @@ -321,7 +324,7 @@ void MemoryBlock::loadFromHexString (StringRef hex) for (;;) { - int byte = 0; + juce_wchar byte = 0; for (int loop = 2; --loop >= 0;) { diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h index 83694f2e..73f05ec8 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -41,7 +41,7 @@ class JUCE_API MemoryBlock @param initialSize the size of block to create @param initialiseToZero whether to clear the memory or just leave it uninitialised */ - MemoryBlock (const size_t initialSize, + MemoryBlock (size_t initialSize, bool initialiseToZero = false); /** Creates a copy of another memory block. */ @@ -120,6 +120,9 @@ class JUCE_API MemoryBlock const char* end() const noexcept { return begin() + getSize(); } //============================================================================== + /** Returns true if the memory block has zero size. */ + bool isEmpty() const noexcept { return getSize() == 0; } + /** Returns the block's current allocated size, in bytes. */ size_t getSize() const noexcept { return size; } @@ -135,7 +138,7 @@ class JUCE_API MemoryBlock uninitialised @see ensureSize */ - void setSize (const size_t newSize, + void setSize (size_t newSize, bool initialiseNewSpaceToZero = false); /** Increases the block's size only if it's smaller than a given size. @@ -147,7 +150,7 @@ class JUCE_API MemoryBlock uninitialised @see setSize */ - void ensureSize (const size_t minimumSize, + void ensureSize (size_t minimumSize, bool initialiseNewSpaceToZero = false); /** Frees all the blocks data, setting its size to 0. */ @@ -167,7 +170,7 @@ class JUCE_API MemoryBlock /** Resizes this block to the given size and fills its contents from the supplied buffer. The data pointer must not be null. */ - void replaceWith (const void* data, size_t numBytes); + void replaceAll (const void* data, size_t numBytes); /** Inserts some data into the block. The dataToInsert pointer must not be null. This block's size will be increased accordingly. @@ -265,6 +268,15 @@ class JUCE_API MemoryBlock */ bool fromBase64Encoding (StringRef encodedString); + //============================================================================== + #ifndef DOXYGEN + [[deprecated ("Use the replaceAll method instead, which will also replace the data when numBytes == 0.")]] + void replaceWith (const void* srcData, size_t numBytes) + { + if (numBytes > 0) + replaceAll (srcData, numBytes); + } + #endif private: //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h b/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h index f8b77f8e..532f05fb 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -49,7 +49,8 @@ class OptionalScopedPointer OptionalScopedPointer just holds a normal pointer to the object, and won't delete it. */ OptionalScopedPointer (ObjectType* objectToHold, bool takeOwnership) - : object (objectToHold), shouldDelete (takeOwnership) + : object (objectToHold), + shouldDelete (takeOwnership) { } @@ -61,9 +62,21 @@ class OptionalScopedPointer The flag to indicate whether or not to delete the managed object is also copied from the source object. */ - OptionalScopedPointer (OptionalScopedPointer& objectToTransferFrom) - : object (objectToTransferFrom.release()), - shouldDelete (objectToTransferFrom.shouldDelete) + OptionalScopedPointer (OptionalScopedPointer&& other) noexcept + : object (std::move (other.object)), + shouldDelete (std::move (other.shouldDelete)) + { + } + + /** Takes ownership of the object owned by `ptr`. */ + explicit OptionalScopedPointer (std::unique_ptr&& ptr) noexcept + : OptionalScopedPointer (ptr.release(), true) + { + } + + /** Points to the same object as `ref`, but does not take ownership. */ + explicit OptionalScopedPointer (ObjectType& ref) noexcept + : OptionalScopedPointer (std::addressof (ref), false) { } @@ -75,15 +88,10 @@ class OptionalScopedPointer The ownership flag that says whether or not to delete the managed object is also copied from the source object. */ - OptionalScopedPointer& operator= (OptionalScopedPointer& objectToTransferFrom) + OptionalScopedPointer& operator= (OptionalScopedPointer&& other) noexcept { - if (object != objectToTransferFrom.object) - { - reset(); - object.reset (objectToTransferFrom.object.release()); - } - - shouldDelete = objectToTransferFrom.shouldDelete; + swapWith (other); + other.reset(); return *this; } @@ -91,23 +99,23 @@ class OptionalScopedPointer takeOwnership flag that was specified when the object was first passed into an OptionalScopedPointer constructor. */ - ~OptionalScopedPointer() + ~OptionalScopedPointer() noexcept { reset(); } //============================================================================== /** Returns the object that this pointer is managing. */ - inline operator ObjectType*() const noexcept { return object.get(); } + operator ObjectType*() const noexcept { return object.get(); } /** Returns the object that this pointer is managing. */ - inline ObjectType* get() const noexcept { return object.get(); } + ObjectType* get() const noexcept { return object.get(); } /** Returns the object that this pointer is managing. */ - inline ObjectType& operator*() const noexcept { return *object; } + ObjectType& operator*() const noexcept { return *object; } /** Lets you access methods and properties of the object that this pointer is holding. */ - inline ObjectType* operator->() const noexcept { return object.get(); } + ObjectType* operator->() const noexcept { return object.get(); } //============================================================================== /** Removes the current object from this OptionalScopedPointer without deleting it. @@ -118,7 +126,7 @@ class OptionalScopedPointer /** Resets this pointer to null, possibly deleting the object that it holds, if it has ownership of it. */ - void reset() + void reset() noexcept { if (! shouldDelete) object.release(); @@ -170,8 +178,8 @@ class OptionalScopedPointer */ void swapWith (OptionalScopedPointer& other) noexcept { - object.swapWith (other.object); - std::swap (shouldDelete, other.shouldDelete); + std::swap (other.object, object); + std::swap (other.shouldDelete, shouldDelete); } private: diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h b/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h index ce636341..fe9da225 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -428,9 +428,10 @@ class ReferenceCountedObjectPtr operator ReferencedType*() const noexcept { return referencedObject; } #endif - - // This old method is deprecated in favour of the shorter and more standard get() method. - JUCE_DEPRECATED_WITH_BODY (ReferencedType* getObject() const, { return get(); }) + #ifndef DOXYGEN + [[deprecated ("Use the get method instead.")]] + ReferencedType* getObject() const { return get(); } + #endif private: //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h b/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h new file mode 100644 index 00000000..86ef8bc8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h @@ -0,0 +1,99 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +/** + Helper functions for managing buffered readers. + + @tags{Audio} +*/ +struct Reservoir +{ + /** Attempts to read the requested range from some kind of input stream, + with intermediate buffering in a 'reservoir'. + + While there are still samples in the requested range left to read, this + function will check whether the next part of the requested range is + already loaded into the reservoir. If the range is available, then + doBufferedRead will call readFromReservoir with the range that should + be copied to the output buffer. If the range is not available, + doBufferedRead will call fillReservoir to request that a new region is + loaded into the reservoir. It will repeat these steps until either the + entire requested region has been read, or the stream ends. + + This will return the range that could not be read successfully, if any. + An empty range implies that the entire read was successful. + + Note that all ranges, including those provided as arguments to the + callbacks, are relative to the original unbuffered input. That is, if + getBufferedRange returns the range [200, 300), then readFromReservoir + might be passed the range [250, 300) in order to copy the final 50 + samples from the reservoir. + + @param rangeToRead the absolute position of the range that should + be read + @param getBufferedRange a function void -> Range that returns + the region currently held in the reservoir + @param readFromReservoir a function Range -> void that can be + used to copy samples from the region in the + reservoir specified in the input range + @param fillReservoir a function Index -> void that is given a + requested read location, and that should + attempt to fill the reservoir starting at this + location. After this function, + getBufferedRange should return the new region + contained in the managed buffer + */ + template + static Range doBufferedRead (Range rangeToRead, + GetBufferedRange&& getBufferedRange, + ReadFromReservoir&& readFromReservoir, + FillReservoir&& fillReservoir) + { + while (! rangeToRead.isEmpty()) + { + const auto bufferedRange = getBufferedRange(); + + if (bufferedRange.contains (rangeToRead.getStart())) + { + const auto rangeToReadInBuffer = rangeToRead.getIntersectionWith (getBufferedRange()); + readFromReservoir (rangeToReadInBuffer); + rangeToRead.setStart (rangeToReadInBuffer.getEnd()); + } + else + { + fillReservoir (rangeToRead.getStart()); + + const auto newRange = getBufferedRange(); + + if (newRange.isEmpty() || ! newRange.contains (rangeToRead.getStart())) + break; + } + } + + return rangeToRead; + } +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h b/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h index f0d99109..0eeb9443 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -30,30 +30,28 @@ namespace juce This class is deprecated. You should use std::unique_ptr instead. */ template -class ScopedPointer +class [[deprecated]] ScopedPointer { public: //============================================================================== - // ScopedPointer is deprecated! You should use std::unique_ptr instead. - JUCE_DEPRECATED_ATTRIBUTE inline ScopedPointer() = default; + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) - // ScopedPointer is deprecated! You should use std::unique_ptr instead. - JUCE_DEPRECATED_ATTRIBUTE inline ScopedPointer (decltype (nullptr)) noexcept {} + inline ScopedPointer() {} - // ScopedPointer is deprecated! You should use std::unique_ptr instead. - JUCE_DEPRECATED_ATTRIBUTE inline ScopedPointer (ObjectType* objectToTakePossessionOf) noexcept + inline ScopedPointer (decltype (nullptr)) noexcept {} + + inline ScopedPointer (ObjectType* objectToTakePossessionOf) noexcept : object (objectToTakePossessionOf) { } - // ScopedPointer is deprecated! You should use std::unique_ptr instead. ScopedPointer (ScopedPointer& objectToTransferFrom) noexcept : object (objectToTransferFrom.release()) { } - // ScopedPointer is deprecated! You should use std::unique_ptr instead. - JUCE_DEPRECATED_ATTRIBUTE inline ~ScopedPointer() { reset(); } + inline ~ScopedPointer() { reset(); } ScopedPointer& operator= (ScopedPointer& objectToTransferFrom) { @@ -143,9 +141,15 @@ class ScopedPointer ScopedPointer (const ScopedPointer&) = delete; ScopedPointer& operator= (const ScopedPointer&) = delete; #endif + + JUCE_END_IGNORE_WARNINGS_MSVC + JUCE_END_IGNORE_WARNINGS_GCC_LIKE }; //============================================================================== +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + template bool operator== (ObjectType1* pointer1, const ScopedPointer& pointer2) noexcept { @@ -212,6 +216,9 @@ template void deleteAndZero (ScopedPointer&) { static_assert (sizeof (Type) == 12345, "Attempt to call deleteAndZero() on a ScopedPointer"); } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + } // namespace juce #endif diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer.h b/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer.h index b9b9513a..57c5ae51 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -87,76 +87,94 @@ class SharedResourcePointer using. Otherwise, if this is the first SharedResourcePointer to be created, then a shared object will be created automatically. */ - SharedResourcePointer() - { - initialise(); - } + SharedResourcePointer() = default; - SharedResourcePointer (const SharedResourcePointer&) - { - initialise(); - } + /** Copy constructor. */ + SharedResourcePointer (const SharedResourcePointer&) = default; + + /** Move constructor. */ + SharedResourcePointer (SharedResourcePointer&&) noexcept = default; /** Destructor. If no other SharedResourcePointer objects exist, this will also delete the shared object to which it refers. */ - ~SharedResourcePointer() - { - auto& holder = getSharedObjectHolder(); - const SpinLock::ScopedLockType sl (holder.lock); + ~SharedResourcePointer() = default; - if (--(holder.refCount) == 0) - holder.sharedInstance = nullptr; - } + /** Returns a pointer to the shared object. */ + operator SharedObjectType*() const noexcept { return sharedObject.get(); } - /** Returns the shared object. */ - operator SharedObjectType*() const noexcept { return sharedObject; } - - /** Returns the shared object. */ + /** Returns a reference to the shared object. */ SharedObjectType& get() const noexcept { return *sharedObject; } - /** Returns the object that this pointer references. - The pointer returned may be a nullptr, of course. - */ + /** Returns a reference to the shared object. */ SharedObjectType& getObject() const noexcept { return *sharedObject; } - /** Returns the shared object. */ - SharedObjectType* operator->() const noexcept { return sharedObject; } + /** Returns a pointer to the shared object. */ + SharedObjectType* operator->() const noexcept { return sharedObject.get(); } - /** Returns the number of SharedResourcePointers that are currently holding the shared object. */ - int getReferenceCount() const noexcept { return getSharedObjectHolder().refCount; } + /** Returns a reference to the shared object. */ + SharedObjectType& operator*() const noexcept { return *sharedObject; } -private: - struct SharedObjectHolder - { - SpinLock lock; - std::unique_ptr sharedInstance; - int refCount; - }; + #ifndef DOXYGEN + [[deprecated ("If you are relying on this function please inform the JUCE team as we are planing on removing this in a subsequent release")]] + int getReferenceCount() const noexcept { return (int) sharedObject.use_count(); } + #endif - static SharedObjectHolder& getSharedObjectHolder() noexcept + /** Returns the SharedResourcePointer if one already exists, or a null optional otherwise. */ + static std::optional getSharedObjectWithoutCreating() { - static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { nullptr }; - return *reinterpret_cast (holder); + if (auto sharedPtr = weak().lock()) + return SharedResourcePointer { std::move (sharedPtr) }; + + return {}; } - SharedObjectType* sharedObject; +private: + explicit SharedResourcePointer (std::shared_ptr&& other) noexcept + : sharedObject (std::move (other)) + { + jassert (sharedObject != nullptr); + } - void initialise() + class Weak { - auto& holder = getSharedObjectHolder(); - const SpinLock::ScopedLockType sl (holder.lock); + public: + std::shared_ptr lock() + { + const SpinLock::ScopedLockType lock { mutex }; + return ptr.lock(); + } + + std::shared_ptr lockOrCreate() + { + const SpinLock::ScopedLockType lock { mutex }; - if (++(holder.refCount) == 1) - holder.sharedInstance.reset (new SharedObjectType()); + if (auto locked = ptr.lock()) + return locked; - sharedObject = holder.sharedInstance.get(); + const std::shared_ptr shared (new SharedObjectType()); + ptr = shared; + return shared; + } + + private: + SpinLock mutex; + std::weak_ptr ptr; + }; + + inline static Weak& weak() + { + static Weak weak; + return weak; } + std::shared_ptr sharedObject = weak().lockOrCreate(); + // There's no need to assign to a SharedResourcePointer because every // instance of the class is exactly the same! SharedResourcePointer& operator= (const SharedResourcePointer&) = delete; + SharedResourcePointer& operator= (SharedResourcePointer&&) noexcept = delete; JUCE_LEAK_DETECTOR (SharedResourcePointer) }; diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer_test.cpp b/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer_test.cpp new file mode 100644 index 00000000..7ec6437b --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer_test.cpp @@ -0,0 +1,99 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 SharedResourcePointerTest final : public UnitTest +{ +public: + SharedResourcePointerTest() + : UnitTest ("SharedResourcePointer", UnitTestCategories::memory) {} + + void runTest() final + { + beginTest ("Only one instance is created"); + { + static int count = 0; + struct CountIncrementer { CountIncrementer() { ++count; } }; + expect (count == 0); + + const SharedResourcePointer instance1; + expect (count == 1); + + const SharedResourcePointer instance2; + expect (count == 1); + + expect (&instance1.get() == &instance2.get()); + } + + beginTest ("The shared object is destroyed when the reference count reaches 0"); + { + static int count = 0; + struct ReferenceCounter + { + ReferenceCounter() { ++count; } + ~ReferenceCounter() { --count; } + }; + + expect (count == 0); + + { + const SharedResourcePointer instance1; + const SharedResourcePointer instance2; + expect (count == 1); + } + + expect (count == 0); + } + + beginTest ("getInstanceWithoutCreating()"); + { + struct Object{}; + + expect (SharedResourcePointer::getSharedObjectWithoutCreating() == std::nullopt); + + { + const SharedResourcePointer instance; + expect (&SharedResourcePointer::getSharedObjectWithoutCreating()->get() == &instance.get()); + } + + expect (SharedResourcePointer::getSharedObjectWithoutCreating() == std::nullopt); + } + + beginTest ("Create objects with private constructors"); + { + class ObjectWithPrivateConstructor + { + private: + ObjectWithPrivateConstructor() = default; + friend SharedResourcePointer; + }; + + SharedResourcePointer instance; + } + } +}; + +static SharedResourcePointerTest sharedResourcePointerTest; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h b/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h index 5d95a22a..76c927fd 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -47,53 +47,49 @@ struct SingletonHolder : private MutexType // (inherited so we can use the empt If you're having trouble cleaning up your singletons, perhaps consider using the SharedResourcePointer class instead. */ - jassert (instance == nullptr); + jassert (instance.load() == nullptr); } /** Returns the current instance, or creates a new instance if there isn't one. */ Type* get() { - if (instance == nullptr) + if (auto* ptr = instance.load()) + return ptr; + + typename MutexType::ScopedLockType sl (*this); + + if (auto* ptr = instance.load()) + return ptr; + + auto once = onlyCreateOncePerRun; // (local copy avoids VS compiler warning about this being constant) + + if (once) { - typename MutexType::ScopedLockType sl (*this); + static bool createdOnceAlready = false; - if (instance == nullptr) + if (createdOnceAlready) { - auto once = onlyCreateOncePerRun; // (local copy avoids VS compiler warning about this being constant) - - if (once) - { - static bool createdOnceAlready = false; - - if (createdOnceAlready) - { - // This means that the doNotRecreateAfterDeletion flag was set - // and you tried to create the singleton more than once. - jassertfalse; - return nullptr; - } - - createdOnceAlready = true; - } - - static bool alreadyInside = false; - - if (alreadyInside) - { - // This means that your object's constructor has done something which has - // ended up causing a recursive loop of singleton creation.. - jassertfalse; - } - else - { - alreadyInside = true; - getWithoutChecking(); - alreadyInside = false; - } + // This means that the doNotRecreateAfterDeletion flag was set + // and you tried to create the singleton more than once. + jassertfalse; + return nullptr; } + + createdOnceAlready = true; } - return instance; + static bool alreadyInside = false; + + if (alreadyInside) + { + // This means that your object's constructor has done something which has + // ended up causing a recursive loop of singleton creation. + jassertfalse; + return nullptr; + } + + const ScopedValueSetter scope (alreadyInside, true); + return getWithoutChecking(); } /** Returns the current instance, or creates a new instance if there isn't one, but doesn't do @@ -101,32 +97,30 @@ struct SingletonHolder : private MutexType // (inherited so we can use the empt */ Type* getWithoutChecking() { - if (instance == nullptr) - { - auto newObject = new Type(); // (create into a local so that instance is still null during construction) - instance = newObject; - } + if (auto* p = instance.load()) + return p; - return instance; + auto* newObject = new Type(); // (create into a local so that instance is still null during construction) + instance.store (newObject); + return newObject; } /** Deletes and resets the current instance, if there is one. */ void deleteInstance() { typename MutexType::ScopedLockType sl (*this); - auto old = instance; - instance = nullptr; - delete old; + delete instance.exchange (nullptr); } /** Called by the class's destructor to clear the pointer if it is currently set to the given object. */ void clear (Type* expectedObject) noexcept { - if (instance == expectedObject) - instance = nullptr; + instance.compare_exchange_strong (expectedObject, nullptr); } - Type* instance = nullptr; + // This must be atomic, otherwise a late call to get() may attempt to read instance while it is + // being modified by the very first call to get(). + std::atomic instance { nullptr }; }; @@ -134,10 +128,10 @@ struct SingletonHolder : private MutexType // (inherited so we can use the empt /** Macro to generate the appropriate methods and boilerplate for a singleton class. - To use this, add the line JUCE_DECLARE_SINGLETON(MyClass, doNotRecreateAfterDeletion) + To use this, add the line JUCE_DECLARE_SINGLETON (MyClass, doNotRecreateAfterDeletion) to the class's definition. - Then put a macro JUCE_IMPLEMENT_SINGLETON(MyClass) along with the class's + Then put a macro JUCE_IMPLEMENT_SINGLETON (MyClass) along with the class's implementation code. It's also a very good idea to also add the call clearSingletonInstance() in your class's @@ -190,7 +184,7 @@ struct SingletonHolder : private MutexType // (inherited so we can use the empt #define JUCE_DECLARE_SINGLETON(Classname, doNotRecreateAfterDeletion) \ \ static juce::SingletonHolder singletonHolder; \ - friend decltype (singletonHolder); \ + friend juce::SingletonHolder; \ \ static Classname* JUCE_CALLTYPE getInstance() { return singletonHolder.get(); } \ static Classname* JUCE_CALLTYPE getInstanceWithoutCreating() noexcept { return singletonHolder.instance; } \ diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h b/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h index 8433ef1d..710fe496 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -116,9 +116,6 @@ class WeakReference */ bool wasObjectDeleted() const noexcept { return holder != nullptr && holder->get() == nullptr; } - bool operator== (ObjectType* object) const noexcept { return get() == object; } - bool operator!= (ObjectType* object) const noexcept { return get() != object; } - //============================================================================== /** This class is used internally by the WeakReference class - don't use it directly in your code! @@ -201,7 +198,7 @@ class WeakReference private: SharedRef holder; - static inline SharedRef getRef (ObjectType* o) + static SharedRef getRef (ObjectType* o) { if (o != nullptr) return o->masterReference.getSharedPointer (o); diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.cpp index 23edc148..1dbd8034 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.cpp +++ b/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,12 +23,12 @@ namespace juce { -static inline File resolveFilename (const String& name) +static File resolveFilename (const String& name) { return File::getCurrentWorkingDirectory().getChildFile (name.unquoted()); } -static inline File checkFileExists (const File& f) +static File checkFileExists (const File& f) { if (! f.exists()) ConsoleApplication::fail ("Could not find file: " + f.getFullPathName()); @@ -36,7 +36,7 @@ static inline File checkFileExists (const File& f) return f; } -static inline File checkFolderExists (const File& f) +static File checkFolderExists (const File& f) { if (! f.isDirectory()) ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName()); @@ -44,7 +44,7 @@ static inline File checkFolderExists (const File& f) return f; } -static inline File resolveFilenameForOption (const ArgumentList& args, StringRef option, const String& filename) +static File resolveFilenameForOption (const ArgumentList& args, StringRef option, const String& filename) { if (filename.isEmpty()) { @@ -75,9 +75,9 @@ File ArgumentList::Argument::resolveAsExistingFolder() const return f; } -static inline bool isShortOptionFormat (StringRef s) { return s[0] == '-' && s[1] != '-'; } -static inline bool isLongOptionFormat (StringRef s) { return s[0] == '-' && s[1] == '-' && s[2] != '-'; } -static inline bool isOptionFormat (StringRef s) { return s[0] == '-'; } +static bool isShortOptionFormat (StringRef s) { return s[0] == '-' && s[1] != '-'; } +static bool isLongOptionFormat (StringRef s) { return s[0] == '-' && s[1] == '-' && s[2] != '-'; } +static bool isOptionFormat (StringRef s) { return s[0] == '-'; } bool ArgumentList::Argument::isLongOption() const { return isLongOptionFormat (text); } bool ArgumentList::Argument::isShortOption() const { return isShortOptionFormat (text); } @@ -141,7 +141,7 @@ ArgumentList::ArgumentList (String exeName, StringArray args) args.removeEmptyStrings(); for (auto& a : args) - arguments.add ({ a }); + arguments.add ({ a.unquoted() }); } ArgumentList::ArgumentList (int argc, char* argv[]) @@ -168,7 +168,7 @@ int ArgumentList::indexOfOption (StringRef option) const jassert (option == String (option).trim()); // passing non-trimmed strings will always fail to find a match! for (int i = 0; i < arguments.size(); ++i) - if (arguments.getReference(i) == option) + if (arguments.getReference (i) == option) return i; return -1; @@ -201,7 +201,7 @@ String ArgumentList::getValueForOption (StringRef option) const for (int i = 0; i < arguments.size(); ++i) { - auto& arg = arguments.getReference(i); + auto& arg = arguments.getReference (i); if (arg == option) { @@ -227,7 +227,7 @@ String ArgumentList::removeValueForOption (StringRef option) for (int i = 0; i < arguments.size(); ++i) { - auto& arg = arguments.getReference(i); + auto& arg = arguments.getReference (i); if (arg == option) { diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.h b/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.h index b8b02713..5e9a334d 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_ConsoleApplication.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -264,7 +264,7 @@ struct ConsoleApplication String longDescription; /** The actual command that should be invoked to perform this action. */ - std::function command; + std::function command; }; //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers.h b/JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers.h new file mode 100644 index 00000000..5461f3d4 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers.h @@ -0,0 +1,103 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +//============================================================================== +/** + Macro to enable bitwise operations for scoped enums (enum struct/class). + + To use this, add the line JUCE_DECLARE_SCOPED_ENUM_BITWISE_OPERATORS (MyEnum) + after your enum declaration at file scope level. + + e.g. @code + + enum class MyEnum + { + one = 1 << 0, + two = 1 << 1, + three = 1 << 2 + }; + + JUCE_DECLARE_SCOPED_ENUM_BITWISE_OPERATORS (MyEnum) + + MyEnum e = MyEnum::one | MyEnum::two; + + bool hasTwo = (e & MyEnum::two) != MyEnum{}; // true + bool hasTwo = hasBitValueSet (e, MyEnum::two); // true + + e = withBitValueCleared (e, MyEnum::two); + + bool hasTwo = hasBitValueSet (e, MyEnum::two); // false + + @endcode +*/ +#define JUCE_DECLARE_SCOPED_ENUM_BITWISE_OPERATORS(EnumType) \ + static_assert (std::is_enum_v, \ + "JUCE_DECLARE_SCOPED_ENUM_BITWISE_OPERATORS " \ + "should only be used with enum types"); \ + constexpr auto operator& (EnumType a, EnumType b) \ + { \ + using base_type = std::underlying_type::type; \ + return static_cast (base_type (a) & base_type (b)); \ + } \ + constexpr auto operator| (EnumType a, EnumType b) \ + { \ + using base_type = std::underlying_type::type; \ + return static_cast (base_type (a) | base_type (b)); \ + } \ + constexpr auto operator~ (EnumType a) \ + { \ + using base_type = std::underlying_type::type; \ + return static_cast (~base_type (a)); \ + } \ + constexpr auto& operator|= (EnumType& a, EnumType b) \ + { \ + a = (a | b); \ + return a; \ + } \ + constexpr auto& operator&= (EnumType& a, EnumType b) \ + { \ + a = (a & b); \ + return a; \ + } + + +namespace juce +{ + +template , int> = 0> +constexpr bool hasBitValueSet (EnumType enumValue, EnumType valueToLookFor) noexcept +{ + return (enumValue & valueToLookFor) != EnumType{}; +} + +template , int> = 0> +constexpr EnumType withBitValueSet (EnumType enumValue, EnumType valueToAdd) noexcept +{ + return enumValue | valueToAdd; +} + +template , int> = 0> +constexpr EnumType withBitValueCleared (EnumType enumValue, EnumType valueToRemove) noexcept +{ + return enumValue & ~valueToRemove; +} +} diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers_test.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers_test.cpp new file mode 100644 index 00000000..d8484f2a --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers_test.cpp @@ -0,0 +1,92 @@ +/* +============================================================================== + +This file is part of the JUCE library. +Copyright (c) 2022 - Raw Material Software Limited + +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 detail +{ +enum class TestEnum +{ + one = 1 << 0, + four = 1 << 1, + other = 1 << 2 +}; + +JUCE_DECLARE_SCOPED_ENUM_BITWISE_OPERATORS (TestEnum) +} + +class EnumHelperTest final : public UnitTest +{ +public: + EnumHelperTest() : UnitTest ("EnumHelpers", UnitTestCategories::containers) {} + + void runTest() override + { + using detail::TestEnum; + + TestEnum e = {}; + + beginTest ("Default initialised enum is 'none'"); + { + expect (e == TestEnum{}); + expect (! hasBitValueSet (e, TestEnum{})); + } + + beginTest ("withBitValueSet sets correct bit on empty enum"); + { + e = withBitValueSet (e, TestEnum::other); + expect (e == TestEnum::other); + expect (hasBitValueSet (e, TestEnum::other)); + } + + beginTest ("withBitValueSet sets correct bit on non-empty enum"); + { + e = withBitValueSet (e, TestEnum::one); + expect (hasBitValueSet (e, TestEnum::one)); + } + + beginTest ("withBitValueCleared clears correct bit"); + { + e = withBitValueCleared (e, TestEnum::one); + expect (e != TestEnum::one); + expect (hasBitValueSet (e, TestEnum::other)); + expect (! hasBitValueSet (e, TestEnum::one)); + } + + beginTest ("operators work as expected"); + { + e = TestEnum::one; + expect ((e & TestEnum::one) != TestEnum{}); + e |= TestEnum::other; + expect ((e & TestEnum::other) != TestEnum{}); + + e &= ~TestEnum::one; + expect ((e & TestEnum::one) == TestEnum{}); + expect ((e & TestEnum::other) != TestEnum{}); + } + } +}; + +static EnumHelperTest enumHelperTest; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_Functional.h b/JuceLibraryCode/modules/juce_core/misc/juce_Functional.h new file mode 100644 index 00000000..147b0cee --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Functional.h @@ -0,0 +1,104 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +#ifndef DOXYGEN +namespace detail +{ + template + using Void = void; + + template + constexpr auto equalityComparableToNullptr = false; + + template + constexpr auto equalityComparableToNullptr() != nullptr)>> = true; +} // namespace detail +#endif + +//============================================================================== +/** Some helper methods for checking a callable object before invoking with + the specified arguments. + + If the object provides a comparison operator for nullptr it will check before + calling. For other objects it will just invoke the function call operator. + + @tags{Core} +*/ +struct NullCheckedInvocation +{ + template + static void invoke (Callable&& fn, Args&&... args) + { + if constexpr (detail::equalityComparableToNullptr) + { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Waddress") + + if (fn != nullptr) + fn (std::forward (args)...); + + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } + else + { + fn (std::forward (args)...); + } + } + + template + static void invoke (std::nullptr_t, Args&&...) {} +}; + +/** Can be used to disable template constructors that would otherwise cause ambiguity with + compiler-generated copy and move constructors. + + Adapted from https://ericniebler.com/2013/08/07/universal-references-and-the-copy-constructo/ +*/ +template +using DisableIfSameOrDerived = std::enable_if_t>>; + +/** Copies an object, sets one of the copy's members to the specified value, and then returns the copy. */ +template +[[nodiscard]] Object withMember (Object copy, Member OtherObject::* member, Other&& value) +{ + copy.*member = std::forward (value); + return copy; +} + +#ifndef DOXYGEN +namespace detail +{ +template +static constexpr auto toFnPtr (Functor functor, Return (Functor::*) (Args...) const) +{ + return static_cast (functor); +} +} // namespace detail +#endif + +/** Converts a captureless lambda to its equivalent function pointer type. */ +template +static constexpr auto toFnPtr (Functor functor) { return detail::toFnPtr (functor, &Functor::operator()); } + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_Result.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_Result.cpp index 4fba8d11..37743138 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_Result.cpp +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Result.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_Result.h b/JuceLibraryCode/modules/juce_core/misc/juce_Result.h index 714134bc..cfcb37ba 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_Result.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Result.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.cpp index cb49bf6c..91cb1059 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.cpp +++ b/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.h b/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.h index be729ccf..39e2b169 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_RuntimePermissions.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -46,7 +46,7 @@ namespace juce New code: RuntimePermissions::request ( - RuntimePermissions::audioRecording, + RuntimePermissions::recordAudio, [this] (bool wasGranted) { if (! wasGranted) @@ -86,12 +86,27 @@ class JUCE_API RuntimePermissions writeExternalStorage = 4, /** Permission to use camera */ - camera = 5 + camera = 5, + + /** Permission to read audio files that your app didn't create. + Has the same effect as readExternalStorage on iOS and Android versions before 33. + */ + readMediaAudio = 6, + + /** Permission to read image files that your app didn't create. + Has the same effect as readExternalStorage on iOS and Android versions before 33. + */ + readMediaImages = 7, + + /** Permission to read video files that your app didn't create. + Has the same effect as readExternalStorage on iOS and Android versions before 33. + */ + readMediaVideo = 8 }; //============================================================================== /** 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_ScopeGuard.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_ScopeGuard.cpp new file mode 100644 index 00000000..7cb7f2a9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/misc/juce_ScopeGuard.cpp @@ -0,0 +1,55 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +ErasedScopeGuard::ErasedScopeGuard (std::function d) + : detach (std::move (d)) {} + +ErasedScopeGuard::ErasedScopeGuard (ErasedScopeGuard&& other) noexcept + : detach (std::exchange (other.detach, nullptr)) {} + +ErasedScopeGuard& ErasedScopeGuard::operator= (ErasedScopeGuard&& other) noexcept +{ + ErasedScopeGuard token { std::move (other) }; + std::swap (token.detach, detach); + return *this; +} + +ErasedScopeGuard::~ErasedScopeGuard() noexcept +{ + reset(); +} + +void ErasedScopeGuard::reset() +{ + if (auto d = std::exchange (detach, nullptr)) + d(); +} + +void ErasedScopeGuard::release() +{ + detach = nullptr; +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_ScopeGuard.h b/JuceLibraryCode/modules/juce_core/misc/juce_ScopeGuard.h new file mode 100644 index 00000000..67b6aceb --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/misc/juce_ScopeGuard.h @@ -0,0 +1,115 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +/** An easy way to ensure that a function is called at the end of the current + scope. + + Usage: + @code + { + if (flag == true) + return; + + // While this code executes, flag is true e.g. to prevent reentrancy + flag = true; + // When we exit this scope, flag must be false + const ScopeGuard scope { [&] { flag = false; } }; + + if (checkInitialCondition()) + return; // Scope's lambda will fire here... + + if (checkCriticalCondition()) + throw std::runtime_error{}; // ...or here... + + doWorkHavingEstablishedPreconditions(); + } // ...or here! + @endcode + + @tags{Core} +*/ +template struct ScopeGuard : Fn { ~ScopeGuard() { Fn::operator()(); } }; +template ScopeGuard (Fn) -> ScopeGuard; + +/** + A ScopeGuard that uses a std::function internally to allow type erasure. + This can be handy; it allows lots of ErasedScopeGuards, all with different + callbacks, to be stored in a homogeneous container. + + An instance of this type will automatically call its callback when it is destroyed. + + ErasedScopeGuard has a few similarities with std::unique_ptr: + - Calling reset() on a unique_ptr destroys the object if it hasn't been destroyed yet + and puts the unique_ptr back into a default/null state; calling reset() on an + ErasedScopeGuard calls the callback if it hasn't been called yet and puts the Guard + back into a default/null state. + - Calling release() on a unique_ptr returns the unique_ptr back to a default state + without destroying the managed object; calling release() on an ErasedScopeGuard + returns the Guard back to a default state without calling the callback. + - Moving a unique_ptr transfers the responsibility of destroying the managed object + to another unique_ptr instance; moving an ErasedScopeGuard transfers the + responsibility of calling the callback to another Guard instance. + + @tags{Core} +*/ +class [[nodiscard]] ErasedScopeGuard +{ +public: + /** Constructs an ErasedScopeGuard with no callback. */ + ErasedScopeGuard() = default; + + /** Constructs an ErasedScopeGuard that will call the provided callback + when the Guard is destroyed. + */ + explicit ErasedScopeGuard (std::function d); + + /** Constructs an instance that assumes responsibility for calling other's callback. */ + ErasedScopeGuard (ErasedScopeGuard&& other) noexcept; + + /** Calls the stored callback, if any, then assumes responsibility for calling + other's callback. After this call, other will be reset to its default state. + */ + ErasedScopeGuard& operator= (ErasedScopeGuard&& other) noexcept; + + /** Destructor, calls the callback assigned to this ScopeGuard. + */ + ~ErasedScopeGuard() noexcept; + + /** Calls the stored callback, if any, then resets this instance to its + default state. + */ + void reset(); + + /** Resets this instance to its default state without calling the stored + callback. + */ + void release(); + + JUCE_DECLARE_NON_COPYABLE (ErasedScopeGuard) + +private: + std::function detach; +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp index eaa2fbb7..6517b172 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h b/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h index c73feb5b..cec0666e 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -136,7 +136,7 @@ class JUCE_API Uuid } // namespace juce -#if ! DOXYGEN +#ifndef DOXYGEN namespace std { template <> struct hash diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h b/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h index e97afbc4..d6329db5 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -123,10 +123,12 @@ class JUCE_API WindowsRegistry bool registerForCurrentUserOnly, WoW64Mode mode = WoW64_Default); + #ifndef DOXYGEN // DEPRECATED: use the other methods with a WoW64Mode parameter of WoW64_64bit instead. - JUCE_DEPRECATED (static String getValueWow64 (const String&, const String& defaultValue = String())); - JUCE_DEPRECATED (static bool valueExistsWow64 (const String&)); - JUCE_DEPRECATED (static bool keyExistsWow64 (const String&)); + [[deprecated]] static String getValueWow64 (const String&, const String& defaultValue = String()); + [[deprecated]] static bool valueExistsWow64 (const String&); + [[deprecated]] static bool keyExistsWow64 (const String&); + #endif private: WindowsRegistry() = delete; diff --git a/JuceLibraryCode/modules/juce_core/native/java/README.txt b/JuceLibraryCode/modules/juce_core/native/java/README.txt index d7cf93cc..55662d15 100644 --- a/JuceLibraryCode/modules/juce_core/native/java/README.txt +++ b/JuceLibraryCode/modules/juce_core/native/java/README.txt @@ -9,30 +9,31 @@ required for the Java source code you wish to compile. 2. If you are creating byte-code for new .java files, move the new files into the native/javacore/app folder of the module, or create one if it doesn't exist. Remember that .java files need to be in nested sub-folders which -resemble their package, i.e. a Java class com.roli.juce.HelloWorld.java -should be in the module's native/javacore/app/com/roli/juce folder. -If you wish to modify existing .java files in the JUCE modules then just rename -native/java to native/javacore. +resemble their package, i.e. a Java class com.rmsl.juce.HelloWorld.java should +be in the module's native/javacore/app/com/rmsl/juce folder. If you wish to +modify existing .java files in the JUCE modules then just rename native/java to +native/javacore. -3. Build your project with AS and run. The app will now use the source code in -the folder created in step 2 so you can debug your Java code this way. +3. Build your project with Android Studio and run. The app will now use the +source code in the folder created in step 2 so you can debug your Java code +this way. 4. Once everything is working rebuild your app in release mode. 5. Go to your app's Builds/Android folder. Inside there you will find -build/intermediates/javac/release_Release/compileRelease_ReleaseJavaWithJavac/classes. +build/intermediates/javac/release_Release/compileRelease_ReleaseJavaWithJavac/classes. Inside of that folder, you will find all your Java byte-code compiled classes. 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). +Java.class and JuceApp.class which you will probably want to remove). -6. Inside of build/intermediates/javac/release_Release/compileRelease_ReleaseJavaWithJavac/classes +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 . (Replace with the minimal sdk version you used in step 1.) - + 7. gzip the output: gzip /tmp/JavaDexByteCode.dex diff --git a/JuceLibraryCode/modules/juce_core/native/java/app/com/roli/juce/FragmentOverlay.java b/JuceLibraryCode/modules/juce_core/native/java/app/com/rmsl/juce/FragmentOverlay.java similarity index 64% rename from JuceLibraryCode/modules/juce_core/native/java/app/com/roli/juce/FragmentOverlay.java rename to JuceLibraryCode/modules/juce_core/native/java/app/com/rmsl/juce/FragmentOverlay.java index 0e967619..590785df 100644 --- a/JuceLibraryCode/modules/juce_core/native/java/app/com/roli/juce/FragmentOverlay.java +++ b/JuceLibraryCode/modules/juce_core/native/java/app/com/rmsl/juce/FragmentOverlay.java @@ -1,4 +1,26 @@ -package com.roli.juce; +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +package com.rmsl.juce; import android.app.DialogFragment; import android.content.Intent; diff --git a/JuceLibraryCode/modules/juce_core/native/java/app/com/roli/juce/JuceHTTPStream.java b/JuceLibraryCode/modules/juce_core/native/java/app/com/rmsl/juce/JuceHTTPStream.java similarity index 93% rename from JuceLibraryCode/modules/juce_core/native/java/app/com/roli/juce/JuceHTTPStream.java rename to JuceLibraryCode/modules/juce_core/native/java/app/com/rmsl/juce/JuceHTTPStream.java index 737ded45..49ff92c5 100644 --- a/JuceLibraryCode/modules/juce_core/native/java/app/com/roli/juce/JuceHTTPStream.java +++ b/JuceLibraryCode/modules/juce_core/native/java/app/com/rmsl/juce/JuceHTTPStream.java @@ -1,4 +1,26 @@ -package com.roli.juce; +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +package com.rmsl.juce; import java.lang.Runnable; import java.io.*; diff --git a/JuceLibraryCode/modules/juce_core/native/java/app/com/rmsl/juce/JuceInvocationHandler.java b/JuceLibraryCode/modules/juce_core/native/java/app/com/rmsl/juce/JuceInvocationHandler.java new file mode 100644 index 00000000..cc47af87 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/java/app/com/rmsl/juce/JuceInvocationHandler.java @@ -0,0 +1,60 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +package com.rmsl.juce; + +import java.lang.reflect.*; + +public class JuceInvocationHandler implements InvocationHandler +{ + public JuceInvocationHandler (long nativeContextRef) + { + nativeContext = nativeContextRef; + } + + public void clear() + { + nativeContext = 0; + } + + @Override + public void finalize() + { + if (nativeContext != 0) + dispatchFinalize (nativeContext); + } + + @Override + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable + { + if (nativeContext != 0) + return dispatchInvoke (nativeContext, proxy, method, args); + + return null; + } + + //============================================================================== + private long nativeContext = 0; + + private native void dispatchFinalize (long nativeContextRef); + private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args); +} diff --git a/JuceLibraryCode/modules/juce_core/native/javacore/app/com/rmsl/juce/JuceApp.java b/JuceLibraryCode/modules/juce_core/native/javacore/app/com/rmsl/juce/JuceApp.java new file mode 100644 index 00000000..7b05248d --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/javacore/app/com/rmsl/juce/JuceApp.java @@ -0,0 +1,37 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +package com.rmsl.juce; + +import com.rmsl.juce.Java; + +import android.app.Application; + +public class JuceApp extends Application +{ + @Override + public void onCreate() + { + super.onCreate(); + Java.initialiseJUCE (this); + } +} diff --git a/JuceLibraryCode/modules/juce_core/native/javacore/app/com/roli/juce/JuceApp.java b/JuceLibraryCode/modules/juce_core/native/javacore/app/com/roli/juce/JuceApp.java deleted file mode 100644 index a8491e14..00000000 --- a/JuceLibraryCode/modules/juce_core/native/javacore/app/com/roli/juce/JuceApp.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.roli.juce; - -import com.roli.juce.Java; - -import android.app.Application; - -public class JuceApp extends Application -{ - @Override - public void onCreate () - { - super.onCreate (); - Java.initialiseJUCE (this); - } -} diff --git a/JuceLibraryCode/modules/juce_core/native/javacore/init/com/rmsl/juce/Java.java b/JuceLibraryCode/modules/juce_core/native/javacore/init/com/rmsl/juce/Java.java new file mode 100644 index 00000000..13c839d5 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/javacore/init/com/rmsl/juce/Java.java @@ -0,0 +1,35 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +package com.rmsl.juce; + +import android.content.Context; + +public class Java +{ + static + { + System.loadLibrary ("juce_jni"); + } + + public native static void initialiseJUCE (Context appContext); +} diff --git a/JuceLibraryCode/modules/juce_core/native/javacore/init/com/roli/juce/Java.java b/JuceLibraryCode/modules/juce_core/native/javacore/init/com/roli/juce/Java.java deleted file mode 100644 index dc79044b..00000000 --- a/JuceLibraryCode/modules/juce_core/native/javacore/init/com/roli/juce/Java.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.roli.juce; - -import android.content.Context; - -public class Java -{ - static - { - System.loadLibrary ("juce_jni"); - } - - public native static void initialiseJUCE (Context appContext); -} diff --git a/JuceLibraryCode/modules/juce_core/native/juce_AndroidDocument_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_AndroidDocument_android.cpp new file mode 100644 index 00000000..46adccfc --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_AndroidDocument_android.cpp @@ -0,0 +1,1073 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited + + 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 is mainly used to pass implementation information from AndroidDocument to + AndroidDocumentIterator. This needs to be defined in a .cpp because it uses the internal + GlobalRef type. + + To preserve encapsulation, this struct should only contain information that would normally be + public, were internal types not in use. +*/ +struct AndroidDocument::NativeInfo +{ + #if JUCE_ANDROID + GlobalRef uri; + #endif +}; + +//============================================================================== +struct AndroidDocumentDetail +{ + ~AndroidDocumentDetail() = delete; // This struct is a single-file namespace + + struct Opt + { + Opt() = default; + + explicit Opt (int64 v) : value (v), valid (true) {} + + int64 value = 0; + bool valid = false; + }; + + static constexpr auto dirMime = "vnd.android.document/directory"; + + #if JUCE_ANDROID + /* + A very basic type that acts a bit like an iterator, in that it can be incremented, and read-from. + + Instances of this type can be passed to the constructor of AndroidDirectoryIterator to provide + stdlib-like iterator facilities. + */ + template + class AndroidIteratorEngine + { + public: + AndroidIteratorEngine (Columns columnsIn, jobject uri) + : columns (std::move (columnsIn)), + cursor { LocalRef { getEnv()->CallObjectMethod (AndroidContentUriResolver::getContentResolver().get(), + ContentResolver.query, + uri, + columns.getColumnNames().get(), + nullptr, + nullptr, + nullptr) } } + { + // Creating the cursor may throw if the document doesn't exist. + // In that case, cursor will still be null. + jniCheckHasExceptionOccurredAndClear(); + } + + auto read() const { return columns.readFromCursor (cursor.get()); } + + bool increment() + { + if (cursor.get() == nullptr) + return false; + + return getEnv()->CallBooleanMethod (cursor.get(), AndroidCursor.moveToNext); + } + + private: + Columns columns; + GlobalRef cursor; + }; + + template + static LocalRef makeStringArray (std::index_sequence, Args&&... args) + { + auto* env = getEnv(); + LocalRef array { env->NewObjectArray (sizeof... (args), JavaString, nullptr) }; + + (env->SetObjectArrayElement (array.get(), Ix, args.get()), ...); + + return array; + } + + template + static LocalRef makeStringArray (Args&&... args) + { + return makeStringArray (std::make_index_sequence(), std::forward (args)...); + } + + static URL uriToUrl (jobject uri) + { + return URL (juceString ((jstring) getEnv()->CallObjectMethod (uri, AndroidUri.toString))); + } + + struct Columns + { + GlobalRef treeUri; + GlobalRefImpl idColumn; + + auto getColumnNames() const + { + return makeStringArray (idColumn); + } + + auto readFromCursor (jobject cursor) const + { + auto* env = getEnv(); + const auto idColumnIndex = env->CallIntMethod (cursor, AndroidCursor.getColumnIndex, idColumn.get()); + + const auto documentUri = [&] + { + if (idColumnIndex < 0) + return LocalRef{}; + + LocalRef documentId { (jstring) env->CallObjectMethod (cursor, AndroidCursor.getString, idColumnIndex) }; + return LocalRef { getEnv()->CallStaticObjectMethod (DocumentsContract21, + DocumentsContract21.buildDocumentUriUsingTree, + treeUri.get(), + documentId.get()) }; + }(); + + return AndroidDocument::fromDocument (uriToUrl (documentUri)); + } + }; + + using DocumentsContractIteratorEngine = AndroidIteratorEngine; + + static DocumentsContractIteratorEngine makeDocumentsContractIteratorEngine (const GlobalRef& uri) + { + const LocalRef documentId { getEnv()->CallStaticObjectMethod (DocumentsContract19, + DocumentsContract19.getDocumentId, + uri.get()) }; + const LocalRef childrenUri { getEnv()->CallStaticObjectMethod (DocumentsContract21, + DocumentsContract21.buildChildDocumentsUriUsingTree, + uri.get(), + documentId.get()) }; + + return DocumentsContractIteratorEngine { Columns { GlobalRef { uri }, + GlobalRefImpl { javaString ("document_id") } }, + childrenUri.get() }; + } + + class RecursiveEngine + { + public: + explicit RecursiveEngine (GlobalRef uri) + : engine (makeDocumentsContractIteratorEngine (uri)) {} + + AndroidDocument read() const + { + return subIterator != nullptr ? subIterator->read() : engine.read(); + } + + bool increment() + { + if (directory && subIterator == nullptr) + subIterator = std::make_unique (engine.read().getNativeInfo().uri); + + if (subIterator != nullptr) + { + if (subIterator->increment()) + return true; + + subIterator = nullptr; + } + + if (! engine.increment()) + return false; + + directory = engine.read().getInfo().isDirectory(); + return true; + } + + private: + DocumentsContractIteratorEngine engine; + std::unique_ptr subIterator; + bool directory = false; + }; + + enum { FLAG_GRANT_READ_URI_PERMISSION = 1, FLAG_GRANT_WRITE_URI_PERMISSION = 2 }; + + static void setPermissions (const URL& url, jmethodID func) + { + if (getAndroidSDKVersion() < 19) + return; + + const auto javaUri = urlToUri (url); + + if (const auto resolver = AndroidContentUriResolver::getContentResolver()) + { + const jint flags = FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION; + getEnv()->CallVoidMethod (resolver, func, javaUri.get(), flags); + jniCheckHasExceptionOccurredAndClear(); + } + } + #endif + + struct DirectoryIteratorEngine + { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + DirectoryIteratorEngine (const File& dir, bool recursive) + : iterator (dir, recursive, "*", File::findFilesAndDirectories) {} + JUCE_END_IGNORE_WARNINGS_MSVC + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + auto read() const { return AndroidDocument::fromFile (iterator.getFile()); } + bool increment() { return iterator.next(); } + DirectoryIterator iterator; + }; + +}; + +//============================================================================== +class AndroidDocumentInfo::Args +{ +public: + using Detail = AndroidDocumentDetail; + + Args withName (String x) const { return with (&Args::name, std::move (x)); } + Args withType (String x) const { return with (&Args::type, std::move (x)); } + Args withFlags (int x) const { return with (&Args::flags, x); } + Args withSize (Detail::Opt x) const { return with (&Args::sizeInBytes, x); } + Args withModified (Detail::Opt x) const { return with (&Args::lastModified, x); } + Args withReadPermission (bool x) const { return with (&Args::readPermission, x); } + Args withWritePermission (bool x) const { return with (&Args::writePermission, x); } + + String name; + String type; + Detail::Opt sizeInBytes, lastModified; + int flags = 0; + bool readPermission = false, writePermission = false; + + static int getFlagsForFile (const File& file) + { + int flags = 0; + + if (file.hasReadAccess()) + flags |= AndroidDocumentInfo::flagSupportsCopy; + + if (file.hasWriteAccess()) + flags |= AndroidDocumentInfo::flagSupportsWrite + | AndroidDocumentInfo::flagDirSupportsCreate + | AndroidDocumentInfo::flagSupportsMove + | AndroidDocumentInfo::flagSupportsRename + | AndroidDocumentInfo::flagSupportsDelete; + + return flags; + } + + AndroidDocumentInfo build() const + { + return AndroidDocumentInfo (*this); + } + +private: + template + Args with (Value Args::* member, Value v) const + { + auto copy = *this; + copy.*member = std::move (v); + return copy; + } +}; + +AndroidDocumentInfo::AndroidDocumentInfo (Args args) + : name (args.name), + type (args.type), + lastModified (args.lastModified.value), + sizeInBytes (args.sizeInBytes.value), + nativeFlags (args.flags), + juceFlags (flagExists + | (args.lastModified.valid ? flagValidModified : 0) + | (args.sizeInBytes.valid ? flagValidSize : 0) + | (args.readPermission ? flagHasReadPermission : 0) + | (args.writePermission ? flagHasWritePermission : 0)) +{ +} + +bool AndroidDocumentInfo::isDirectory() const { return type == AndroidDocumentDetail::dirMime; } + +//============================================================================== +class AndroidDocument::Pimpl +{ +public: + Pimpl() = default; + Pimpl (const Pimpl&) = default; + Pimpl (Pimpl&&) noexcept = default; + Pimpl& operator= (const Pimpl&) = default; + Pimpl& operator= (Pimpl&&) noexcept = default; + + virtual ~Pimpl() = default; + virtual std::unique_ptr clone() const = 0; + virtual bool deleteDocument() const = 0; + virtual std::unique_ptr createInputStream() const = 0; + virtual std::unique_ptr createOutputStream() const = 0; + virtual AndroidDocumentInfo getInfo() const = 0; + virtual URL getUrl() const = 0; + virtual NativeInfo getNativeInfo() const = 0; + + virtual std::unique_ptr copyDocumentToParentDocument (const Pimpl&) const + { + // This function is not supported on the current platform. + jassertfalse; + return {}; + } + + virtual std::unique_ptr moveDocumentFromParentToParent (const Pimpl&, const Pimpl&) const + { + // This function is not supported on the current platform. + jassertfalse; + return {}; + } + + virtual std::unique_ptr renameTo (const String&) const + { + // This function is not supported on the current platform. + jassertfalse; + return {}; + } + + virtual std::unique_ptr createChildDocumentWithTypeAndName (const String&, const String&) const + { + // This function is not supported on the current platform. + jassertfalse; + return {}; + } + + File getFile() const { return getUrl().getLocalFile(); } + + static const Pimpl& getPimpl (const AndroidDocument& doc) { return *doc.pimpl; } +}; + +//============================================================================== +struct AndroidDocument::Utils +{ + using Detail = AndroidDocumentDetail; + + ~Utils() = delete; // This stuct is a single-file namespace + + #if JUCE_ANDROID + template struct VersionTag { int version; }; + + class MimeConverter + { + public: + String getMimeTypeFromExtension (const String& str) const + { + const auto javaStr = javaString (str); + return juceString ((jstring) getEnv()->CallObjectMethod (map.get(), + AndroidMimeTypeMap.getMimeTypeFromExtension, + javaStr.get())); + } + + String getExtensionFromMimeType (const String& str) const + { + const auto javaStr = javaString (str); + return juceString ((jstring) getEnv()->CallObjectMethod (map.get(), + AndroidMimeTypeMap.getExtensionFromMimeType, + javaStr.get())); + } + + private: + GlobalRef map { LocalRef { getEnv()->CallStaticObjectMethod (AndroidMimeTypeMap, + AndroidMimeTypeMap.getSingleton) } }; + }; + + class AndroidDocumentPimplApi19 : public Pimpl + { + public: + AndroidDocumentPimplApi19() = default; + + explicit AndroidDocumentPimplApi19 (const URL& uriIn) + : AndroidDocumentPimplApi19 (urlToUri (uriIn)) {} + + explicit AndroidDocumentPimplApi19 (const LocalRef& uriIn) + : uri (uriIn) {} + + std::unique_ptr clone() const override { return std::make_unique (*this); } + + bool deleteDocument() const override + { + if (const auto resolver = AndroidContentUriResolver::getContentResolver()) + { + return getEnv()->CallStaticBooleanMethod (DocumentsContract19, + DocumentsContract19.deleteDocument, + resolver.get(), + uri.get()); + } + + return false; + } + + std::unique_ptr createInputStream() const override + { + auto result = std::make_unique (uri); + return result->openedSuccessfully() ? std::move (result) : nullptr; + } + + std::unique_ptr createOutputStream() const override + { + auto stream = AndroidStreamHelpers::createStream (uri, AndroidStreamHelpers::StreamKind::output); + + return stream.get() != nullptr ? std::make_unique (std::move (stream)) + : nullptr; + } + + AndroidDocumentInfo getInfo() const override + { + struct Columns + { + auto getColumnNames() const + { + return Detail::makeStringArray (flagsColumn, nameColumn, mimeColumn, idColumn, modifiedColumn, sizeColumn); + } + + auto readFromCursor (jobject cursor) const + { + auto* env = getEnv(); + + const auto flagsColumnIndex = env->CallIntMethod (cursor, AndroidCursor.getColumnIndex, flagsColumn.get()); + const auto nameColumnIndex = env->CallIntMethod (cursor, AndroidCursor.getColumnIndex, nameColumn.get()); + const auto mimeColumnIndex = env->CallIntMethod (cursor, AndroidCursor.getColumnIndex, mimeColumn.get()); + const auto idColumnIndex = env->CallIntMethod (cursor, AndroidCursor.getColumnIndex, idColumn.get()); + const auto modColumnIndex = env->CallIntMethod (cursor, AndroidCursor.getColumnIndex, modifiedColumn.get()); + const auto sizeColumnIndex = env->CallIntMethod (cursor, AndroidCursor.getColumnIndex, sizeColumn.get()); + + const auto indices = { flagsColumnIndex, nameColumnIndex, mimeColumnIndex, idColumnIndex, modColumnIndex, sizeColumnIndex }; + + if (std::any_of (indices.begin(), indices.end(), [] (auto index) { return index < 0; })) + return AndroidDocumentInfo::Args{}; + + const LocalRef nameString { (jstring) env->CallObjectMethod (cursor, AndroidCursor.getString, nameColumnIndex) }; + const LocalRef mimeString { (jstring) env->CallObjectMethod (cursor, AndroidCursor.getString, mimeColumnIndex) }; + + const auto readOpt = [&] (int column) -> Detail::Opt + { + const auto missing = env->CallBooleanMethod (cursor, AndroidCursor.isNull, column); + + if (missing) + return {}; + + return Detail::Opt { env->CallLongMethod (cursor, AndroidCursor.getLong, column) }; + }; + + return AndroidDocumentInfo::Args{}.withName (juceString (nameString.get())) + .withType (juceString (mimeString.get())) + .withFlags (env->CallIntMethod (cursor, AndroidCursor.getInt, flagsColumnIndex)) + .withModified (readOpt (modColumnIndex)) + .withSize (readOpt (sizeColumnIndex)); + } + + GlobalRefImpl flagsColumn { javaString ("flags") }; + GlobalRefImpl nameColumn { javaString ("_display_name") }; + GlobalRefImpl mimeColumn { javaString ("mime_type") }; + GlobalRefImpl idColumn { javaString ("document_id") }; + GlobalRefImpl modifiedColumn { javaString ("last_modified") }; + GlobalRefImpl sizeColumn { javaString ("_size") }; + }; + + Detail::AndroidIteratorEngine iterator { Columns{}, uri }; + + if (! iterator.increment()) + return AndroidDocumentInfo{}; + + auto* env = getEnv(); + auto ctx = getAppContext(); + + const auto hasPermission = [&] (auto permission) + { + return env->CallIntMethod (ctx, AndroidContext.checkCallingOrSelfUriPermission, uri.get(), permission) == 0; + }; + + return iterator.read() + .withReadPermission (hasPermission (Detail::FLAG_GRANT_READ_URI_PERMISSION)) + .withWritePermission (hasPermission (Detail::FLAG_GRANT_WRITE_URI_PERMISSION)) + .build(); + } + + URL getUrl() const override + { + return Detail::uriToUrl (uri); + } + + NativeInfo getNativeInfo() const override { return { uri }; } + + private: + GlobalRef uri; + }; + + //============================================================================== + class AndroidDocumentPimplApi21 : public AndroidDocumentPimplApi19 + { + public: + using AndroidDocumentPimplApi19::AndroidDocumentPimplApi19; + + std::unique_ptr clone() const override { return std::make_unique (*this); } + + std::unique_ptr createChildDocumentWithTypeAndName (const String& type, const String& name) const override + { + return Utils::createPimplForSdk (LocalRef { getEnv()->CallStaticObjectMethod (DocumentsContract21, + DocumentsContract21.createDocument, + AndroidContentUriResolver::getContentResolver().get(), + getNativeInfo().uri.get(), + javaString (type).get(), + javaString (name).get()) }); + } + + std::unique_ptr renameTo (const String& name) const override + { + if (const auto resolver = AndroidContentUriResolver::getContentResolver()) + { + return Utils::createPimplForSdk (LocalRef { getEnv()->CallStaticObjectMethod (DocumentsContract21, + DocumentsContract21.renameDocument, + resolver.get(), + getNativeInfo().uri.get(), + javaString (name).get()) }); + } + + return nullptr; + } + }; + + //============================================================================== + class AndroidDocumentPimplApi24 final : public AndroidDocumentPimplApi21 + { + public: + using AndroidDocumentPimplApi21::AndroidDocumentPimplApi21; + + std::unique_ptr clone() const override { return std::make_unique (*this); } + + std::unique_ptr copyDocumentToParentDocument (const Pimpl& target) const override + { + if (target.getNativeInfo().uri == nullptr) + { + // Cannot copy to a non-URI-based AndroidDocument + return {}; + } + + return Utils::createPimplForSdk (LocalRef { getEnv()->CallStaticObjectMethod (DocumentsContract24, + DocumentsContract24.copyDocument, + AndroidContentUriResolver::getContentResolver().get(), + getNativeInfo().uri.get(), + target.getNativeInfo().uri.get()) }); + } + + std::unique_ptr moveDocumentFromParentToParent (const Pimpl& currentParent, const Pimpl& newParent) const override + { + if (currentParent.getNativeInfo().uri == nullptr || newParent.getNativeInfo().uri == nullptr) + { + // Cannot move document between non-URI-based AndroidDocuments + return {}; + } + + return Utils::createPimplForSdk (LocalRef { getEnv()->CallStaticObjectMethod (DocumentsContract24, + DocumentsContract24.moveDocument, + AndroidContentUriResolver::getContentResolver().get(), + getNativeInfo().uri.get(), + currentParent.getNativeInfo().uri.get(), + newParent.getNativeInfo().uri.get()) }); + } + }; + + static std::unique_ptr createPimplForSdk (const LocalRef& uri) + { + if (jniCheckHasExceptionOccurredAndClear()) + return nullptr; + + return createPimplForSdkImpl (uri, + VersionTag { 24 }, + VersionTag { 21 }, + VersionTag { 19 }); + } + + static std::unique_ptr createPimplForSdkImpl (const LocalRef&) + { + // Failed to find a suitable implementation for this platform + jassertfalse; + return nullptr; + } + + template + static std::unique_ptr createPimplForSdkImpl (const LocalRef& uri, + VersionTag head, + VersionTag... tail) + { + if (head.version <= getAndroidSDKVersion()) + return std::make_unique (uri); + + return createPimplForSdkImpl (uri, tail...); + } + + #else + class MimeConverter + { + public: + static String getMimeTypeFromExtension (const String& str) + { + return detail::MimeTypeTable::getMimeTypesForFileExtension (str)[0]; + } + + static String getExtensionFromMimeType (const String& str) + { + return detail::MimeTypeTable::getFileExtensionsForMimeType (str)[0]; + } + }; + #endif + + //============================================================================== + class AndroidDocumentPimplFile final : public Pimpl + { + public: + explicit AndroidDocumentPimplFile (const File& f) + : file (f) + { + } + + std::unique_ptr clone() const override { return std::make_unique (*this); } + + bool deleteDocument() const override + { + return file.deleteRecursively (false); + } + + std::unique_ptr renameTo (const String& name) const override + { + const auto target = file.getSiblingFile (name); + + return file.moveFileTo (target) ? std::make_unique (target) + : nullptr; + } + + std::unique_ptr createInputStream() const override { return file.createInputStream(); } + + std::unique_ptr createOutputStream() const override + { + auto result = file.createOutputStream(); + result->setPosition (0); + result->truncate(); + return result; + } + + std::unique_ptr copyDocumentToParentDocument (const Pimpl& target) const override + { + const auto parent = target.getFile(); + + if (parent == File()) + return nullptr; + + const auto actual = parent.getChildFile (file.getFileName()); + + if (actual.exists()) + return nullptr; + + const auto success = file.isDirectory() ? file.copyDirectoryTo (actual) + : file.copyFileTo (actual); + + return success ? std::make_unique (actual) + : nullptr; + } + + std::unique_ptr createChildDocumentWithTypeAndName (const String& type, + const String& name) const override + { + const auto extension = mimeConverter.getExtensionFromMimeType (type); + const auto target = file.getChildFile (extension.isNotEmpty() ? name + "." + extension : name); + + if (! target.exists() && (type == Detail::dirMime ? target.createDirectory() : target.create())) + return std::make_unique (target); + + return nullptr; + } + + std::unique_ptr moveDocumentFromParentToParent (const Pimpl& currentParentPimpl, + const Pimpl& newParentPimpl) const override + { + const auto currentParent = currentParentPimpl.getFile(); + const auto newParent = newParentPimpl.getFile(); + + if (! file.isAChildOf (currentParent) || newParent == File()) + return nullptr; + + const auto target = newParent.getChildFile (file.getFileName()); + + if (target.exists() || ! file.moveFileTo (target)) + return nullptr; + + return std::make_unique (target); + } + + AndroidDocumentInfo getInfo() const override + { + if (! file.exists()) + return AndroidDocumentInfo{}; + + const auto size = file.getSize(); + const auto extension = file.getFileExtension().removeCharacters (".").toLowerCase(); + const auto type = file.isDirectory() ? Detail::dirMime + : mimeConverter.getMimeTypeFromExtension (extension); + + return AndroidDocumentInfo::Args{}.withName (file.getFileName()) + .withType (type.isNotEmpty() ? type : "application/octet-stream") + .withFlags (AndroidDocumentInfo::Args::getFlagsForFile (file)) + .withModified (Detail::Opt { file.getLastModificationTime().toMilliseconds() }) + .withSize (size != 0 ? Detail::Opt { size } : Detail::Opt{}) + .withReadPermission (file.hasReadAccess()) + .withWritePermission (file.hasWriteAccess()) + .build(); + } + + URL getUrl() const override { return URL (file); } + + NativeInfo getNativeInfo() const override { return {}; } + + private: + File file; + MimeConverter mimeConverter; + }; +}; + +//============================================================================== +void AndroidDocumentPermission::takePersistentReadWriteAccess ([[maybe_unused]] const URL& url) +{ + #if JUCE_ANDROID + AndroidDocumentDetail::setPermissions (url, ContentResolver19.takePersistableUriPermission); + #endif +} + +void AndroidDocumentPermission::releasePersistentReadWriteAccess ([[maybe_unused]] const URL& url) +{ + #if JUCE_ANDROID + AndroidDocumentDetail::setPermissions (url, ContentResolver19.releasePersistableUriPermission); + #endif +} + +std::vector AndroidDocumentPermission::getPersistedPermissions() +{ + #if ! JUCE_ANDROID + return {}; + #else + if (getAndroidSDKVersion() < 19) + return {}; + + auto* env = getEnv(); + const LocalRef permissions { env->CallObjectMethod (AndroidContentUriResolver::getContentResolver().get(), + ContentResolver19.getPersistedUriPermissions) }; + + if (permissions == nullptr) + return {}; + + std::vector result; + const auto size = env->CallIntMethod (permissions, JavaList.size); + + for (auto i = (decltype (size)) 0; i < size; ++i) + { + const LocalRef uriPermission { env->CallObjectMethod (permissions, JavaList.get, i) }; + + AndroidDocumentPermission permission; + permission.time = env->CallLongMethod (uriPermission, AndroidUriPermission.getPersistedTime); + permission.read = env->CallBooleanMethod (uriPermission, AndroidUriPermission.isReadPermission); + permission.write = env->CallBooleanMethod (uriPermission, AndroidUriPermission.isWritePermission); + permission.url = AndroidDocumentDetail::uriToUrl (env->CallObjectMethod (uriPermission, AndroidUriPermission.getUri)); + + result.push_back (std::move (permission)); + } + + return result; + #endif +} + +//============================================================================== +AndroidDocument::AndroidDocument() = default; + +AndroidDocument AndroidDocument::fromFile (const File& filePath) +{ + #if JUCE_ANDROID + const LocalRef info { getEnv()->CallObjectMethod (getAppContext(), AndroidContext.getApplicationInfo) }; + const auto targetSdkVersion = getEnv()->GetIntField (info.get(), AndroidApplicationInfo.targetSdkVersion); + + // At the current API level, plain file paths may not work for accessing files in shared + // locations. It's recommended to use fromDocument() or fromTree() instead when targeting this + // API level. + jassert (__ANDROID_API_Q__ <= targetSdkVersion); + #endif + + return AndroidDocument { filePath != File() ? std::make_unique (filePath) + : nullptr }; +} + +AndroidDocument AndroidDocument::fromDocument ([[maybe_unused]] const URL& documentUrl) +{ + #if JUCE_ANDROID + if (getAndroidSDKVersion() < 19) + { + // This function is unsupported on this platform. + jassertfalse; + return AndroidDocument{}; + } + + const auto javaUri = urlToUri (documentUrl); + + if (! getEnv()->CallStaticBooleanMethod (DocumentsContract19, + DocumentsContract19.isDocumentUri, + getAppContext().get(), + javaUri.get())) + { + return AndroidDocument{}; + } + + return AndroidDocument { Utils::createPimplForSdk (javaUri) }; + #else + return AndroidDocument{}; + #endif +} + +AndroidDocument AndroidDocument::fromTree ([[maybe_unused]] const URL& treeUrl) +{ + #if JUCE_ANDROID + if (getAndroidSDKVersion() < 21) + { + // This function is unsupported on this platform. + jassertfalse; + return AndroidDocument{}; + } + + const auto javaUri = urlToUri (treeUrl); + LocalRef treeDocumentId { getEnv()->CallStaticObjectMethod (DocumentsContract21, + DocumentsContract21.getTreeDocumentId, + javaUri.get()) }; + + jniCheckHasExceptionOccurredAndClear(); + + if (treeDocumentId == nullptr) + { + jassertfalse; + return AndroidDocument{}; + } + + LocalRef documentUri { getEnv()->CallStaticObjectMethod (DocumentsContract21, + DocumentsContract21.buildDocumentUriUsingTree, + javaUri.get(), + treeDocumentId.get()) }; + + return AndroidDocument { Utils::createPimplForSdk (documentUri) }; + #else + return AndroidDocument{}; + #endif +} + +AndroidDocument::AndroidDocument (const AndroidDocument& other) + : AndroidDocument (other.pimpl != nullptr ? other.pimpl->clone() : nullptr) {} + +AndroidDocument::AndroidDocument (std::unique_ptr pimplIn) + : pimpl (std::move (pimplIn)) {} + +AndroidDocument::AndroidDocument (AndroidDocument&&) noexcept = default; + +AndroidDocument& AndroidDocument::operator= (const AndroidDocument& other) +{ + AndroidDocument { other }.swap (*this); + return *this; +} + +AndroidDocument& AndroidDocument::operator= (AndroidDocument&&) noexcept = default; + +AndroidDocument::~AndroidDocument() = default; + +bool AndroidDocument::deleteDocument() const { return pimpl->deleteDocument(); } + +bool AndroidDocument::renameTo (const String& newDisplayName) +{ + jassert (hasValue()); + + auto renamed = pimpl->renameTo (newDisplayName); + + if (renamed == nullptr) + return false; + + pimpl = std::move (renamed); + return true; +} + +AndroidDocument AndroidDocument::copyDocumentToParentDocument (const AndroidDocument& target) const +{ + jassert (hasValue() && target.hasValue()); + return AndroidDocument { pimpl->copyDocumentToParentDocument (*target.pimpl) }; +} + +AndroidDocument AndroidDocument::createChildDocumentWithTypeAndName (const String& type, + const String& name) const +{ + jassert (hasValue()); + return AndroidDocument { pimpl->createChildDocumentWithTypeAndName (type, name) }; +} + +AndroidDocument AndroidDocument::createChildDirectory (const String& name) const +{ + return createChildDocumentWithTypeAndName (AndroidDocumentDetail::dirMime, name); +} + +bool AndroidDocument::moveDocumentFromParentToParent (const AndroidDocument& currentParent, + const AndroidDocument& newParent) +{ + jassert (hasValue() && currentParent.hasValue() && newParent.hasValue()); + auto moved = pimpl->moveDocumentFromParentToParent (*currentParent.pimpl, *newParent.pimpl); + + if (moved == nullptr) + return false; + + pimpl = std::move (moved); + return true; +} + +std::unique_ptr AndroidDocument::createInputStream() const +{ + jassert (hasValue()); + return pimpl->createInputStream(); +} + +std::unique_ptr AndroidDocument::createOutputStream() const +{ + jassert (hasValue()); + return pimpl->createOutputStream(); +} + +URL AndroidDocument::getUrl() const +{ + jassert (hasValue()); + return pimpl->getUrl(); +} + +AndroidDocumentInfo AndroidDocument::getInfo() const +{ + jassert (hasValue()); + return pimpl->getInfo(); +} + +bool AndroidDocument::operator== (const AndroidDocument& other) const +{ + return getUrl() == other.getUrl(); +} + +bool AndroidDocument::operator!= (const AndroidDocument& other) const +{ + return ! operator== (other); +} + +AndroidDocument::NativeInfo AndroidDocument::getNativeInfo() const +{ + jassert (hasValue()); + return pimpl->getNativeInfo(); +} + +//============================================================================== +struct AndroidDocumentIterator::Pimpl +{ + virtual ~Pimpl() = default; + virtual AndroidDocument read() const = 0; + virtual bool increment() = 0; +}; + +struct AndroidDocumentIterator::Utils +{ + using Detail = AndroidDocumentDetail; + + ~Utils() = delete; // This struct is a single-file namespace + + template + struct TemplatePimpl final : public Pimpl, public Engine + { + template + TemplatePimpl (Args&&... args) : Engine (std::forward (args)...) {} + + AndroidDocument read() const override { return Engine::read(); } + bool increment() override { return Engine::increment(); } + }; + + template + static AndroidDocumentIterator makeWithEngineInplace (Args&&... args) + { + return AndroidDocumentIterator { std::make_unique> (std::forward (args)...) }; + } + + template + static AndroidDocumentIterator makeWithEngine (Engine engine) + { + return AndroidDocumentIterator { std::make_unique> (std::move (engine)) }; + } + + static void increment (AndroidDocumentIterator& it) + { + if (it.pimpl == nullptr || ! it.pimpl->increment()) + it.pimpl = nullptr; + } +}; + +//============================================================================== +AndroidDocumentIterator AndroidDocumentIterator::makeNonRecursive (const AndroidDocument& dir) +{ + if (! dir.hasValue()) + return {}; + + using Detail = AndroidDocumentDetail; + + #if JUCE_ANDROID + if (21 <= getAndroidSDKVersion()) + { + if (auto uri = dir.getNativeInfo().uri) + return Utils::makeWithEngine (Detail::makeDocumentsContractIteratorEngine (uri)); + } + #endif + + return Utils::makeWithEngineInplace (dir.getUrl().getLocalFile(), false); +} + +AndroidDocumentIterator AndroidDocumentIterator::makeRecursive (const AndroidDocument& dir) +{ + if (! dir.hasValue()) + return {}; + + using Detail = AndroidDocumentDetail; + + #if JUCE_ANDROID + if (21 <= getAndroidSDKVersion()) + { + if (auto uri = dir.getNativeInfo().uri) + return Utils::makeWithEngine (Detail::RecursiveEngine { uri }); + } + #endif + + return Utils::makeWithEngineInplace (dir.getUrl().getLocalFile(), true); +} + +AndroidDocumentIterator::AndroidDocumentIterator (std::unique_ptr engine) + : pimpl (std::move (engine)) +{ + Utils::increment (*this); +} + +AndroidDocument AndroidDocumentIterator::operator*() const { return pimpl->read(); } + +AndroidDocumentIterator& AndroidDocumentIterator::operator++() +{ + Utils::increment (*this); + return *this; +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h b/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h index 900933c6..42adee05 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,61 +28,24 @@ #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 + #if JUCE_MODULE_AVAILABLE_juce_opengl #define GLES_SILENCE_DEPRECATION 1 #endif + #define Component CarbonDummyCompName #import + #undef Component + #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 + #if JUCE_MODULE_AVAILABLE_juce_opengl #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 - #define NSEventModifierFlagControl NSControlKeyMask - #define NSEventModifierFlagHelp NSHelpKeyMask - #define NSEventModifierFlagNumericPad NSNumericPadKeyMask - #define NSEventModifierFlagOption NSAlternateKeyMask - #define NSEventModifierFlagShift NSShiftKeyMask - #define NSCompositingOperationSourceOver NSCompositeSourceOver - #define NSEventMaskApplicationDefined NSApplicationDefinedMask - #define NSEventTypeApplicationDefined NSApplicationDefined - #define NSEventTypeCursorUpdate NSCursorUpdate - #define NSEventTypeMouseMoved NSMouseMoved - #define NSEventTypeLeftMouseDown NSLeftMouseDown - #define NSEventTypeRightMouseDown NSRightMouseDown - #define NSEventTypeOtherMouseDown NSOtherMouseDown - #define NSEventTypeLeftMouseUp NSLeftMouseUp - #define NSEventTypeRightMouseUp NSRightMouseUp - #define NSEventTypeOtherMouseUp NSOtherMouseUp - #define NSEventTypeLeftMouseDragged NSLeftMouseDragged - #define NSEventTypeRightMouseDragged NSRightMouseDragged - #define NSEventTypeOtherMouseDragged NSOtherMouseDragged - #define NSEventTypeScrollWheel NSScrollWheel - #define NSEventTypeKeyDown NSKeyDown - #define NSEventTypeKeyUp NSKeyUp - #define NSEventTypeFlagsChanged NSFlagsChanged - #define NSEventMaskAny NSAnyEventMask - #define NSWindowStyleMaskBorderless NSBorderlessWindowMask - #define NSWindowStyleMaskClosable NSClosableWindowMask - #define NSWindowStyleMaskFullScreen NSFullScreenWindowMask - #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask - #define NSWindowStyleMaskResizable NSResizableWindowMask - #define NSWindowStyleMaskTitled NSTitledWindowMask - #define NSAlertStyleCritical NSCriticalAlertStyle - #define NSControlSizeRegular NSRegularControlSize - #define NSEventTypeMouseEntered NSMouseEntered - #define NSEventTypeMouseExited NSMouseExited - #define NSAlertStyleInformational NSInformationalAlertStyle - #define NSEventTypeTabletPoint NSTabletPoint - #define NSEventTypeTabletProximity NSTabletProximity - #endif #import #include #endif @@ -126,14 +89,16 @@ #define STRICT 1 #define WIN32_LEAN_AND_MEAN 1 #if JUCE_MINGW - #define _WIN32_WINNT 0x0501 + #if ! defined (_WIN32_WINNT) + #define _WIN32_WINNT 0x0600 + #endif #else #define _WIN32_WINNT 0x0602 #endif #define _UNICODE 1 #define UNICODE 1 #ifndef _WIN32_IE - #define _WIN32_IE 0x0500 + #define _WIN32_IE 0x0501 #endif #include @@ -146,6 +111,8 @@ #include #include #include + #include + #include #include #include #include @@ -236,11 +203,14 @@ #include #include #include + #include #include #include #include #include #include + #include + #include #include #include @@ -252,7 +222,10 @@ #include #include #include + #include + #include #include + #include #include #include #include @@ -267,8 +240,10 @@ #include #include #include + #include #include #include + #include #include #include #include @@ -292,11 +267,13 @@ #include #include #include + #include + #include #include #include - // If you are getting include errors here, then you to re-build the Projucer - // and re-save your .jucer file. + // If you are getting include errors here, then you need to re-build + // the Projucer and re-save your .jucer file. #include #endif diff --git a/JuceLibraryCode/modules/juce_core/native/juce_CFHelpers_mac.h b/JuceLibraryCode/modules/juce_core/native/juce_CFHelpers_mac.h new file mode 100644 index 00000000..d04ba6a4 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_CFHelpers_mac.h @@ -0,0 +1,64 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +/* This file contains a few helper functions that are used internally but which + need to be kept away from the public headers because they use obj-C symbols. +*/ +namespace juce +{ + +template +struct CFObjectDeleter +{ + void operator() (CFType object) const noexcept + { + if (object != nullptr) + CFRelease (object); + } +}; + +template +using CFUniquePtr = std::unique_ptr, CFObjectDeleter>; + +template +struct CFObjectHolder +{ + CFObjectHolder() = default; + explicit CFObjectHolder (CFType obj) : object (obj) {} + + CFObjectHolder (const CFObjectHolder&) = delete; + CFObjectHolder (CFObjectHolder&&) = delete; + + CFObjectHolder& operator= (const CFObjectHolder&) = delete; + CFObjectHolder& operator= (CFObjectHolder&&) = delete; + + ~CFObjectHolder() noexcept + { + if (object != nullptr) + CFRelease (object); + } + + // Public to facilitate passing the pointer address to functions + CFType object = nullptr; +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h b/JuceLibraryCode/modules/juce_core/native/juce_ComSmartPtr_windows.h similarity index 65% rename from JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h rename to JuceLibraryCode/modules/juce_core/native/juce_ComSmartPtr_windows.h index abea32e8..c20af89e 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_ComSmartPtr_windows.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,12 +23,12 @@ namespace juce { -#if JUCE_MINGW || (! (defined (_MSC_VER) || defined (__uuidof))) +#if (JUCE_MINGW && JUCE_32BIT) || (! defined (_MSC_VER) && ! defined (__uuidof)) #ifdef __uuidof #undef __uuidof #endif - template struct UUIDGetter { static CLSID get() { jassertfalse; return {}; } }; + template struct UUIDGetter { static CLSID get() { jassertfalse; return {}; } }; #define __uuidof(x) UUIDGetter::get() template <> @@ -38,7 +38,7 @@ namespace juce }; #define JUCE_DECLARE_UUID_GETTER(name, uuid) \ - template<> struct UUIDGetter { static CLSID get() { return uuidFromString (uuid); } }; + template <> struct UUIDGetter { static CLSID get() { return uuidFromString (uuid); } }; #define JUCE_COMCLASS(name, guid) \ struct name; \ @@ -47,16 +47,22 @@ namespace juce #else #define JUCE_DECLARE_UUID_GETTER(name, uuid) - #define JUCE_COMCLASS(name, guid) struct __declspec (uuid (guid)) name + #define JUCE_COMCLASS(name, guid) struct DECLSPEC_UUID (guid) name #endif +#define JUCE_IUNKNOWNCLASS(name, guid) JUCE_COMCLASS(name, guid) : public IUnknown +#define JUCE_COMRESULT HRESULT STDMETHODCALLTYPE +#define JUCE_COMCALL virtual HRESULT STDMETHODCALLTYPE + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + inline GUID uuidFromString (const char* s) noexcept { uint32 ints[4] = {}; for (uint32 digitIndex = 0; digitIndex < 32;) { - auto c = *s++; + auto c = (uint32) *s++; uint32 digit; if (c >= '0' && c <= '9') digit = c - '0'; @@ -114,7 +120,7 @@ class ComSmartPtr HRESULT CoCreateInstance (REFCLSID classUUID, DWORD dwClsContext = CLSCTX_INPROC_SERVER) { - auto hr = ::CoCreateInstance (classUUID, 0, dwClsContext, __uuidof (ComClass), (void**) resetAndGetPointerAddress()); + auto hr = ::CoCreateInstance (classUUID, nullptr, dwClsContext, __uuidof (ComClass), (void**) resetAndGetPointerAddress()); jassert (hr != CO_E_NOTINITIALIZED); // You haven't called CoInitialize for the current thread! return hr; } @@ -134,6 +140,17 @@ class ComSmartPtr return this->QueryInterface (__uuidof (OtherComClass), destObject); } + template + ComSmartPtr getInterface() const + { + ComSmartPtr destObject; + + if (QueryInterface (destObject) == S_OK) + return destObject; + + return nullptr; + } + private: ComClass* p = nullptr; @@ -143,26 +160,23 @@ class ComSmartPtr }; //============================================================================== -#define JUCE_COMRESULT HRESULT __stdcall - -//============================================================================== -template -class ComBaseClassHelperBase : public ComClass +template +class ComBaseClassHelperBase : public First, public ComClasses... { public: ComBaseClassHelperBase (unsigned int initialRefCount) : refCount (initialRefCount) {} - virtual ~ComBaseClassHelperBase() {} + virtual ~ComBaseClassHelperBase() = default; - ULONG __stdcall AddRef() { return ++refCount; } - ULONG __stdcall Release() { auto r = --refCount; if (r == 0) delete this; return r; } + ULONG STDMETHODCALLTYPE AddRef() override { return ++refCount; } + ULONG STDMETHODCALLTYPE Release() override { auto r = --refCount; if (r == 0) delete this; return r; } protected: ULONG refCount; - JUCE_COMRESULT QueryInterface (REFIID refId, void** result) + JUCE_COMRESULT QueryInterface (REFIID refId, void** result) override { if (refId == __uuidof (IUnknown)) - return castToType (result); + return castToType (result); *result = nullptr; return E_NOINTERFACE; @@ -171,7 +185,10 @@ class ComBaseClassHelperBase : public ComClass template JUCE_COMRESULT castToType (void** result) { - this->AddRef(); *result = dynamic_cast (this); return S_OK; + this->AddRef(); + *result = dynamic_cast (this); + + return S_OK; } }; @@ -179,20 +196,35 @@ class ComBaseClassHelperBase : public ComClass @tags{Core} */ -template -class ComBaseClassHelper : public ComBaseClassHelperBase +template +class ComBaseClassHelper : public ComBaseClassHelperBase { public: - ComBaseClassHelper (unsigned int initialRefCount = 1) : ComBaseClassHelperBase (initialRefCount) {} - ~ComBaseClassHelper() {} + explicit ComBaseClassHelper (unsigned int initialRefCount = 1) + : ComBaseClassHelperBase (initialRefCount) {} - JUCE_COMRESULT QueryInterface (REFIID refId, void** result) + JUCE_COMRESULT QueryInterface (REFIID refId, void** result) override { - if (refId == __uuidof (ComClass)) - return this->template castToType (result); - - return ComBaseClassHelperBase::QueryInterface (refId, result); + const std::tuple bases[] + { + std::make_tuple (__uuidof (ComClasses), + static_cast (static_cast (this)))... + }; + + for (const auto& base : bases) + { + if (refId == std::get<0> (base)) + { + this->AddRef(); + *result = std::get<1> (base); + return S_OK; + } + } + + return ComBaseClassHelperBase::QueryInterface (refId, result); } }; +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_CommonFile.cpp b/JuceLibraryCode/modules/juce_core/native/juce_CommonFile_linux.cpp similarity index 94% rename from JuceLibraryCode/modules/juce_core/native/juce_linux_CommonFile.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_CommonFile_linux.cpp index 8000ddb9..9b1a10e2 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_CommonFile.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_CommonFile_linux.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -62,8 +62,9 @@ bool File::isSymbolicLink() const String File::getNativeLinkedTarget() const { - HeapBlock buffer (8194); - const int numBytes = (int) readlink (getFullPathName().toRawUTF8(), buffer, 8192); + constexpr int bufferSize = 8194; + HeapBlock buffer (bufferSize); + auto numBytes = (int) readlink (getFullPathName().toRawUTF8(), buffer, bufferSize - 2); return String::fromUTF8 (buffer, jmax (0, numBytes)); } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Files_android.cpp similarity index 66% rename from JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_Files_android.cpp index ce6b182f..9e3f6dc5 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_Files_android.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -33,18 +33,30 @@ DECLARE_JNI_CLASS (MediaScannerConnection, "android/media/MediaScannerConnection #undef JNI_CLASS_MEMBERS #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ - METHOD (query, "query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;") \ - METHOD (openInputStream, "openInputStream", "(Landroid/net/Uri;)Ljava/io/InputStream;") \ - METHOD (openOutputStream, "openOutputStream", "(Landroid/net/Uri;)Ljava/io/OutputStream;") + METHOD (query, "query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;") \ + METHOD (openInputStream, "openInputStream", "(Landroid/net/Uri;)Ljava/io/InputStream;") \ + METHOD (openOutputStream, "openOutputStream", "(Landroid/net/Uri;)Ljava/io/OutputStream;") DECLARE_JNI_CLASS (ContentResolver, "android/content/ContentResolver") #undef JNI_CLASS_MEMBERS +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + METHOD (takePersistableUriPermission, "takePersistableUriPermission", "(Landroid/net/Uri;I)V") \ + METHOD (releasePersistableUriPermission, "releasePersistableUriPermission", "(Landroid/net/Uri;I)V") \ + METHOD (getPersistedUriPermissions, "getPersistedUriPermissions", "()Ljava/util/List;") + +DECLARE_JNI_CLASS_WITH_MIN_SDK (ContentResolver19, "android/content/ContentResolver", 19) +#undef JNI_CLASS_MEMBERS + #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (moveToFirst, "moveToFirst", "()Z") \ + METHOD (moveToNext, "moveToNext", "()Z") \ METHOD (getColumnIndex, "getColumnIndex", "(Ljava/lang/String;)I") \ METHOD (getString, "getString", "(I)Ljava/lang/String;") \ - METHOD (close, "close", "()V") \ + METHOD (isNull, "isNull", "(I)Z") \ + METHOD (getInt, "getInt", "(I)I") \ + METHOD (getLong, "getLong", "(I)J") \ + METHOD (close, "close", "()V") DECLARE_JNI_CLASS (AndroidCursor, "android/database/Cursor") #undef JNI_CLASS_MEMBERS @@ -65,14 +77,74 @@ DECLARE_JNI_CLASS (AndroidEnvironment, "android/os/Environment") DECLARE_JNI_CLASS (AndroidOutputStream, "java/io/OutputStream") #undef JNI_CLASS_MEMBERS +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + METHOD (close, "close", "()V") \ + METHOD (read, "read", "([B)I") \ + METHOD (skip, "skip", "(J)J") + +DECLARE_JNI_CLASS (AndroidInputStream, "java/io/InputStream") +#undef JNI_CLASS_MEMBERS + #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ FIELD (publicSourceDir, "publicSourceDir", "Ljava/lang/String;") \ - FIELD (dataDir, "dataDir", "Ljava/lang/String;") + FIELD (dataDir, "dataDir", "Ljava/lang/String;") \ + FIELD (targetSdkVersion, "targetSdkVersion", "I") DECLARE_JNI_CLASS (AndroidApplicationInfo, "android/content/pm/ApplicationInfo") #undef JNI_CLASS_MEMBERS -//============================================================================== +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + STATICMETHOD (buildChildDocumentsUri, "buildChildDocumentsUri", "(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;") \ + STATICMETHOD (buildDocumentUri, "buildDocumentUri", "(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;") \ + STATICMETHOD (buildRecentDocumentsUri, "buildRecentDocumentsUri", "(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;") \ + STATICMETHOD (buildRootUri, "buildRootUri", "(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;") \ + STATICMETHOD (buildRootsUri, "buildRootsUri", "(Ljava/lang/String;)Landroid/net/Uri;") \ + STATICMETHOD (buildSearchDocumentsUri, "buildSearchDocumentsUri", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;") \ + STATICMETHOD (deleteDocument, "deleteDocument", "(Landroid/content/ContentResolver;Landroid/net/Uri;)Z") \ + STATICMETHOD (getDocumentId, "getDocumentId", "(Landroid/net/Uri;)Ljava/lang/String;") \ + STATICMETHOD (getRootId, "getRootId", "(Landroid/net/Uri;)Ljava/lang/String;") \ + STATICMETHOD (isDocumentUri, "isDocumentUri", "(Landroid/content/Context;Landroid/net/Uri;)Z") + +DECLARE_JNI_CLASS_WITH_MIN_SDK (DocumentsContract19, "android/provider/DocumentsContract", 19) +#undef JNI_CLASS_MEMBERS + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + STATICMETHOD (buildChildDocumentsUriUsingTree, "buildChildDocumentsUriUsingTree", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/net/Uri;") \ + STATICMETHOD (buildDocumentUriUsingTree, "buildDocumentUriUsingTree", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/net/Uri;") \ + STATICMETHOD (buildTreeDocumentUri, "buildTreeDocumentUri", "(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;") \ + STATICMETHOD (createDocument, "createDocument", "(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri;") \ + STATICMETHOD (getTreeDocumentId, "getTreeDocumentId", "(Landroid/net/Uri;)Ljava/lang/String;") \ + STATICMETHOD (renameDocument, "renameDocument", "(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;)Landroid/net/Uri;") + +DECLARE_JNI_CLASS_WITH_MIN_SDK (DocumentsContract21, "android/provider/DocumentsContract", 21) +#undef JNI_CLASS_MEMBERS + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + STATICMETHOD (copyDocument, "copyDocument", "(Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;") \ + STATICMETHOD (moveDocument, "moveDocument", "(Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;") \ + STATICMETHOD (removeDocument, "removeDocument", "(Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/net/Uri;)Z") + +DECLARE_JNI_CLASS_WITH_MIN_SDK (DocumentsContract24, "android/provider/DocumentsContract", 24) +#undef JNI_CLASS_MEMBERS + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + STATICMETHOD (getSingleton, "getSingleton", "()Landroid/webkit/MimeTypeMap;") \ + METHOD (getExtensionFromMimeType, "getExtensionFromMimeType", "(Ljava/lang/String;)Ljava/lang/String;") \ + METHOD (getMimeTypeFromExtension, "getMimeTypeFromExtension", "(Ljava/lang/String;)Ljava/lang/String;") + +DECLARE_JNI_CLASS (AndroidMimeTypeMap, "android/webkit/MimeTypeMap") +#undef JNI_CLASS_MEMBERS + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + METHOD (getPersistedTime, "getPersistedTime", "()J") \ + METHOD (getUri, "getUri", "()Landroid/net/Uri;") \ + METHOD (isReadPermission, "isReadPermission", "()Z") \ + METHOD (isWritePermission, "isWritePermission", "()Z") + +DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidUriPermission, "android/content/UriPermission", 19) +#undef JNI_CLASS_MEMBERS + + //============================================================================== static File juceFile (LocalRef obj) { auto* env = getEnv(); @@ -118,21 +190,9 @@ static LocalRef urlToUri (const URL& url) struct AndroidContentUriResolver { public: - static LocalRef getStreamForContentUri (const URL& url, bool inputStream) + static LocalRef getContentResolver() { - // only use this method for content URIs - jassert (url.getScheme() == "content"); - auto* env = getEnv(); - - LocalRef contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver)); - - if (contentResolver) - return LocalRef ((env->CallObjectMethod (contentResolver.get(), - inputStream ? ContentResolver.openInputStream - : ContentResolver.openOutputStream, - urlToUri (url).get()))); - - return LocalRef(); + return LocalRef (getEnv()->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver)); } static File getLocalFileFromContentUri (const URL& url) @@ -160,18 +220,15 @@ struct AndroidContentUriResolver auto downloadId = tokens[1]; if (type.equalsIgnoreCase ("raw")) - { return File (downloadId); - } - else if (type.equalsIgnoreCase ("downloads")) + + if (type.equalsIgnoreCase ("downloads")) { auto subDownloadPath = url.getSubPath().fromFirstOccurrenceOf ("tree/downloads", false, false); return File (getWellKnownFolder ("DIRECTORY_DOWNLOADS").getFullPathName() + "/" + subDownloadPath); } - else - { - return getLocalFileFromContentUri (URL ("content://downloads/public_downloads/" + documentId)); - } + + return getLocalFileFromContentUri (URL ("content://downloads/public_downloads/" + documentId)); } else if (authority == "com.android.providers.media.documents" && documentId.isNotEmpty()) { @@ -192,7 +249,7 @@ struct AndroidContentUriResolver { auto uri = urlToUri (url); auto* env = getEnv(); - LocalRef contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver)); + const auto contentResolver = getContentResolver(); if (contentResolver == nullptr) return {}; @@ -216,7 +273,7 @@ struct AndroidContentUriResolver { auto uri = urlToUri (url); auto* env = getEnv(); - LocalRef contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver)); + const auto contentResolver = getContentResolver(); if (contentResolver) { @@ -285,8 +342,7 @@ struct AndroidContentUriResolver static File getPrimaryStorageDirectory() { - auto* env = getEnv(); - return juceFile (LocalRef (env->CallStaticObjectMethod (AndroidEnvironment, AndroidEnvironment.getExternalStorageDirectory))); + return juceFile (LocalRef (getEnv()->CallStaticObjectMethod (AndroidEnvironment, AndroidEnvironment.getExternalStorageDirectory))); } static Array getSecondaryStorageDirectories() @@ -319,9 +375,8 @@ struct AndroidContentUriResolver return {}; auto rootFsDevice = info.st_dev; - DirectoryIterator iter (mountFolder, false, "*", File::findDirectories); - while (iter.next()) + for (const auto& iter : RangedDirectoryIterator (mountFolder, false, "*", File::findDirectories)) { auto candidate = iter.getFile(); @@ -432,12 +487,10 @@ struct AndroidContentUriResolver }; //============================================================================== -struct AndroidContentUriOutputStream : public OutputStream +struct AndroidContentUriOutputStream final : public OutputStream { - AndroidContentUriOutputStream (LocalRef&& outputStream) - : stream (outputStream) - { - } + explicit AndroidContentUriOutputStream (LocalRef&& streamIn) + : stream (std::move (streamIn)) {} ~AndroidContentUriOutputStream() override { @@ -480,12 +533,139 @@ struct AndroidContentUriOutputStream : public OutputStream int64 pos = 0; }; -OutputStream* juce_CreateContentURIOutputStream (const URL& url) +//============================================================================== +class CachedByteArray { - auto stream = AndroidContentUriResolver::getStreamForContentUri (url, false); +public: + CachedByteArray() = default; - return (stream.get() != nullptr ? new AndroidContentUriOutputStream (std::move (stream)) : nullptr); -} + explicit CachedByteArray (jsize sizeIn) + : byteArray { LocalRef { getEnv()->NewByteArray (sizeIn) } }, + size (sizeIn) {} + + jbyteArray getNativeArray() const { return byteArray.get(); } + jsize getSize() const { return size; } + +private: + GlobalRefImpl byteArray; + jsize size = 0; +}; + +//============================================================================== +struct AndroidStreamHelpers +{ + enum class StreamKind { output, input }; + + static LocalRef createStream (const GlobalRef& uri, StreamKind kind) + { + auto* env = getEnv(); + auto contentResolver = AndroidContentUriResolver::getContentResolver(); + + if (contentResolver == nullptr) + return {}; + + return LocalRef (env->CallObjectMethod (contentResolver.get(), + kind == StreamKind::input ? ContentResolver.openInputStream + : ContentResolver.openOutputStream, + uri.get())); + } +}; + +//============================================================================== +struct AndroidContentUriInputStream final : public InputStream +{ + explicit AndroidContentUriInputStream (const GlobalRef& uriIn) + : uri (uriIn), + stream (AndroidStreamHelpers::createStream (uri, AndroidStreamHelpers::StreamKind::input)) + {} + + ~AndroidContentUriInputStream() override + { + getEnv()->CallVoidMethod (stream.get(), AndroidInputStream.close); + jniCheckHasExceptionOccurredAndClear(); + } + + int64 getTotalLength() override { return -1; } + + bool isExhausted() override { return exhausted; } + + int read (void* destBuffer, int maxBytesToRead) override + { + auto* env = getEnv(); + + if ((jsize) maxBytesToRead != byteArray.getSize()) + byteArray = CachedByteArray { (jsize) maxBytesToRead }; + + const auto result = env->CallIntMethod (stream.get(), AndroidInputStream.read, byteArray.getNativeArray()); + + if (jniCheckHasExceptionOccurredAndClear() || result == -1) + { + exhausted = true; + return -1; + } + + pos += result; + + auto* rawBytes = env->GetByteArrayElements (byteArray.getNativeArray(), nullptr); + std::memcpy (destBuffer, rawBytes, static_cast (result)); + env->ReleaseByteArrayElements (byteArray.getNativeArray(), rawBytes, 0); + + return result; + } + + bool setPosition (int64 newPos) override + { + if (newPos == pos) + return true; + + if (pos < newPos) + return skipImpl (newPos - pos); + + AndroidContentUriInputStream (uri).swap (*this); + return skipImpl (newPos); + } + + int64 getPosition() override + { + return pos; + } + + bool openedSuccessfully() const { return stream != nullptr; } + + void skipNextBytes (int64 num) override + { + skipImpl (num); + } + +private: + bool skipImpl (int64 num) + { + if (stream == nullptr) + return false; + + const auto skipped = getEnv()->CallLongMethod (stream, AndroidInputStream.skip, (jlong) num); + + if (jniCheckHasExceptionOccurredAndClear()) + return false; + + pos += skipped; + return skipped == num; + } + + auto tie() { return std::tie (uri, byteArray, stream, pos, exhausted); } + + void swap (AndroidContentUriInputStream& other) noexcept + { + auto toSwap = other.tie(); + tie().swap (toSwap); + } + + GlobalRef uri; + CachedByteArray byteArray; + GlobalRef stream; + int64 pos = 0; + bool exhausted = false; +}; //============================================================================== class MediaScannerConnectionClient : public AndroidInterfaceImplementer @@ -644,7 +824,7 @@ void File::revealToUser() const } //============================================================================== -class SingleMediaScanner : public MediaScannerConnectionClient +class SingleMediaScanner final : public MediaScannerConnectionClient { public: SingleMediaScanner (const String& filename) diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Files_linux.cpp similarity index 81% rename from JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_Files_linux.cpp index 082a5357..12216a48 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_Files_linux.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -36,7 +36,7 @@ bool File::isOnCDRomDrive() const struct statfs buf; return statfs (getFullPathName().toUTF8(), &buf) == 0 - && buf.f_type == (short) U_ISOFS_SUPER_MAGIC; + && buf.f_type == (unsigned int) U_ISOFS_SUPER_MAGIC; } bool File::isOnHardDisk() const @@ -135,20 +135,25 @@ File File::getSpecialLocation (const SpecialLocationType type) case invokedExecutableFile: if (juce_argv != nullptr && juce_argc > 0) - return File (CharPointer_UTF8 (juce_argv[0])); - // deliberate fall-through... + return File (String (CharPointer_UTF8 (juce_argv[0]))); + // Falls through + JUCE_FALLTHROUGH case currentExecutableFile: case currentApplicationFile: - #if ! JUCE_STANDALONE_APPLICATION - return juce_getExecutableFile(); - #endif - // deliberate fall-through if this is not a shared-library + { + const auto f = juce_getExecutableFile(); + return f.isSymbolicLink() ? f.getLinkedTarget() : f; + } case hostApplicationPath: { + #if JUCE_BSD + return juce_getExecutableFile(); + #else const File f ("/proc/self/exe"); return f.isSymbolicLink() ? f.getLinkedTarget() : juce_getExecutableFile(); + #endif } default: @@ -189,34 +194,38 @@ static bool isFileExecutable (const String& filename) bool Process::openDocument (const String& fileName, const String& parameters) { - auto cmdString = fileName.replace (" ", "\\ ", false); - cmdString << " " << parameters; - - if (cmdString.startsWithIgnoreCase ("file:") - || File::createFileWithoutCheckingPath (fileName).isDirectory() - || ! isFileExecutable (fileName)) + const auto cmdString = [&] { - StringArray cmdLines; - - for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", - "google-chrome", "chromium-browser", "opera", "konqueror" }) + if (fileName.startsWithIgnoreCase ("file:") + || File::createFileWithoutCheckingPath (fileName).isDirectory() + || ! isFileExecutable (fileName)) { - cmdLines.add (String (browserName) + " " + cmdString.trim()); + const auto singleCommand = fileName.trim().quoted(); + + StringArray cmdLines; + + for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", + "google-chrome", "chromium-browser", "opera", "konqueror" }) + { + cmdLines.add (String (browserName) + " " + singleCommand); + } + + return cmdLines.joinIntoString (" || "); } - cmdString = cmdLines.joinIntoString (" || "); - } + return (fileName.replace (" ", "\\ ", false) + " " + parameters).trim(); + }(); - const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr }; + const char* const argv[] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr }; - auto cpid = fork(); + const auto cpid = fork(); if (cpid == 0) { setsid(); // Child process - execve (argv[0], (char**) argv, environ); + execv (argv[0], (char**) argv); exit (0); } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm b/JuceLibraryCode/modules/juce_core/native/juce_Files_mac.mm similarity index 88% rename from JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm rename to JuceLibraryCode/modules/juce_core/native/juce_Files_mac.mm index 308b6cf0..21b6d6e0 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_Files_mac.mm @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -201,8 +201,9 @@ static bool launchExecutable (const String& pathAndArguments) case invokedExecutableFile: if (juce_argv != nullptr && juce_argc > 0) - return File::getCurrentWorkingDirectory().getChildFile (String (juce_argv[0])); + return File::getCurrentWorkingDirectory().getChildFile (String (CharPointer_UTF8 (juce_argv[0]))); // deliberate fall-through... + JUCE_FALLTHROUGH case currentExecutableFile: return juce_getExecutableFile(); @@ -284,13 +285,15 @@ static bool launchExecutable (const String& pathAndArguments) JUCE_AUTORELEASEPOOL { - #if (defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0) \ - || (defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8) - NSError* error = nil; - return [[NSFileManager defaultManager] trashItemAtURL: createNSURLFromFile (*this) - resultingItemURL: nil - error: &error]; - #elif JUCE_IOS + if (@available (macOS 10.8, iOS 11.0, *)) + { + NSError* error = nil; + return [[NSFileManager defaultManager] trashItemAtURL: createNSURLFromFile (*this) + resultingItemURL: nil + error: &error]; + } + + #if JUCE_IOS return deleteFile(); #else [[NSWorkspace sharedWorkspace] recycleURLs: [NSArray arrayWithObject: createNSURLFromFile (*this)] @@ -394,7 +397,7 @@ bool next (String& filenameFound, //============================================================================== -bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters) +bool JUCE_CALLTYPE Process::openDocument (const String& fileName, [[maybe_unused]] const String& parameters) { JUCE_AUTORELEASEPOOL { @@ -403,14 +406,18 @@ bool next (String& filenameFound, : [NSURL URLWithString: fileNameAsNS]; #if JUCE_IOS - ignoreUnused (parameters); + if (@available (iOS 10.0, *)) + { + [[UIApplication sharedApplication] openURL: filenameAsURL + options: @{} + completionHandler: nil]; - #if (! defined __IPHONE_OS_VERSION_MIN_REQUIRED) || (! defined __IPHONE_10_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0) + return true; + } + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") return [[UIApplication sharedApplication] openURL: filenameAsURL]; - #else - [[UIApplication sharedApplication] openURL: filenameAsURL options: @{} completionHandler: nil]; - return true; - #endif + JUCE_END_IGNORE_WARNINGS_GCC_LIKE #else NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; @@ -424,13 +431,30 @@ bool next (String& filenameFound, StringArray params; params.addTokens (parameters, true); - NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease]; - NSMutableArray* paramArray = [[NSMutableArray new] autorelease]; for (int i = 0; i < params.size(); ++i) [paramArray addObject: juceStringToNS (params[i])]; + #if defined (MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15 + if (@available (macOS 10.15, *)) + { + auto config = [NSWorkspaceOpenConfiguration configuration]; + [config setCreatesNewApplicationInstance: YES]; + config.arguments = paramArray; + + [workspace openApplicationAtURL: filenameAsURL + configuration: config + completionHandler: nil]; + + return true; + } + #endif + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + + NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease]; + [dict setObject: paramArray forKey: nsStringLiteral ("NSWorkspaceLaunchConfigurationArguments")]; @@ -438,6 +462,8 @@ bool next (String& filenameFound, options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance configuration: dict error: nil]; + + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } if (file.exists()) @@ -494,4 +520,13 @@ bool next (String& filenameFound, } #endif +File File::getContainerForSecurityApplicationGroupIdentifier (const String& appGroup) +{ + if (@available (macOS 10.8, *)) + if (auto* url = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: juceStringToNS (appGroup)]) + return File (nsStringToJuce ([url path])); + + return File(); +} + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Files_windows.cpp similarity index 81% rename from JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_Files_windows.cpp index 25f05bcb..03b50525 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_Files_windows.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -32,6 +32,8 @@ namespace WindowsFileHelpers { //============================================================================== #if JUCE_WINDOWS + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnested-anon-types") + typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; USHORT ReparseDataLength; @@ -57,15 +59,17 @@ namespace WindowsFileHelpers } GenericReparseBuffer; } DUMMYUNIONNAME; } *PREPARSE_DATA_BUFFER, REPARSE_DATA_BUFFER; + + JUCE_END_IGNORE_WARNINGS_GCC_LIKE #endif //============================================================================== - DWORD getAtts (const String& path) noexcept + static DWORD getAtts (const String& path) noexcept { return GetFileAttributes (path.toWideCharPointer()); } - bool changeAtts (const String& path, DWORD bitsToSet, DWORD bitsToClear) noexcept + static bool changeAtts (const String& path, DWORD bitsToSet, DWORD bitsToClear) noexcept { auto oldAtts = getAtts (path); @@ -78,7 +82,7 @@ namespace WindowsFileHelpers || SetFileAttributes (path.toWideCharPointer(), newAtts) != FALSE; } - int64 fileTimeToTime (const FILETIME* const ft) noexcept + static int64 fileTimeToTime (const FILETIME* const ft) noexcept { static_assert (sizeof (ULARGE_INTEGER) == sizeof (FILETIME), "ULARGE_INTEGER is too small to hold FILETIME: please report!"); @@ -86,7 +90,7 @@ namespace WindowsFileHelpers return (int64) ((reinterpret_cast (ft)->QuadPart - 116444736000000000LL) / 10000); } - FILETIME* timeToFileTime (const int64 time, FILETIME* const ft) noexcept + static FILETIME* timeToFileTime (const int64 time, FILETIME* const ft) noexcept { if (time <= 0) return nullptr; @@ -95,7 +99,7 @@ namespace WindowsFileHelpers return ft; } - String getDriveFromPath (String path) + static String getDriveFromPath (String path) { if (path.isNotEmpty() && path[1] == ':' && path[2] == 0) path << '\\'; @@ -111,7 +115,7 @@ namespace WindowsFileHelpers return path; } - int64 getDiskSpaceInfo (const String& path, const bool total) + static int64 getDiskSpaceInfo (const String& path, const bool total) { ULARGE_INTEGER spc, tot, totFree; @@ -122,22 +126,22 @@ namespace WindowsFileHelpers return 0; } - unsigned int getWindowsDriveType (const String& path) + static unsigned int getWindowsDriveType (const String& path) { return GetDriveType (getDriveFromPath (path).toWideCharPointer()); } - File getSpecialFolderPath (int type) + static File getSpecialFolderPath (int type) { WCHAR path[MAX_PATH + 256]; - if (SHGetSpecialFolderPath (0, path, type, FALSE)) + if (SHGetSpecialFolderPath (nullptr, path, type, FALSE)) return File (String (path)); return {}; } - File getModuleFileName (HINSTANCE moduleHandle) + static File getModuleFileName (HINSTANCE moduleHandle) { WCHAR dest[MAX_PATH + 256]; dest[0] = 0; @@ -145,9 +149,9 @@ namespace WindowsFileHelpers return File (String (dest)); } - Result getResultForLastError() + static Result getResultForLastError() { - TCHAR messageBuffer[256] = { 0 }; + TCHAR messageBuffer[256] = {}; FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), @@ -155,11 +159,96 @@ namespace WindowsFileHelpers return Result::fail (String (messageBuffer)); } -} + + // The docs for the Windows security API aren't very clear. Some parts of the following + // function (the flags passed to GetNamedSecurityInfo, duplicating the primary access token) + // were guided by the example at https://blog.aaronballman.com/2011/08/how-to-check-access-rights/ + static bool hasFileAccess (const File& file, DWORD accessType) + { + const auto& path = file.getFullPathName(); + + if (path.isEmpty()) + return false; + + struct PsecurityDescriptorGuard + { + ~PsecurityDescriptorGuard() { if (psecurityDescriptor != nullptr) LocalFree (psecurityDescriptor); } + PSECURITY_DESCRIPTOR psecurityDescriptor = nullptr; + }; + + PsecurityDescriptorGuard descriptorGuard; + + if (GetNamedSecurityInfo (path.toWideCharPointer(), + SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + nullptr, + nullptr, + nullptr, + nullptr, + &descriptorGuard.psecurityDescriptor) != ERROR_SUCCESS) + { + return false; + } + + struct HandleGuard + { + ~HandleGuard() { if (handle != INVALID_HANDLE_VALUE) CloseHandle (handle); } + HANDLE handle = nullptr; + }; + + HandleGuard primaryTokenGuard; + + if (! OpenProcessToken (GetCurrentProcess(), + TOKEN_IMPERSONATE | TOKEN_DUPLICATE | TOKEN_QUERY | STANDARD_RIGHTS_READ, + &primaryTokenGuard.handle)) + { + return false; + } + + HandleGuard duplicatedTokenGuard; + + if (! DuplicateToken (primaryTokenGuard.handle, + SecurityImpersonation, + &duplicatedTokenGuard.handle)) + { + return false; + } + + GENERIC_MAPPING mapping { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS }; + + MapGenericMask (&accessType, &mapping); + DWORD allowed = 0; + BOOL granted = false; + PRIVILEGE_SET set; + DWORD setSize = sizeof (set); + + if (! AccessCheck (descriptorGuard.psecurityDescriptor, + duplicatedTokenGuard.handle, + accessType, + &mapping, + &set, + &setSize, + &allowed, + &granted)) + { + return false; + } + + return granted != FALSE; + } +} // namespace WindowsFileHelpers //============================================================================== -JUCE_DECLARE_DEPRECATED_STATIC (const juce_wchar File::separator = '\\';) -JUCE_DECLARE_DEPRECATED_STATIC (const StringRef File::separatorString ("\\");) +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const juce_wchar File::separator = '\\'; +const StringRef File::separatorString ("\\"); + +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif juce_wchar File::getSeparatorChar() { return '\\'; } StringRef File::getSeparatorString() { return "\\"; } @@ -187,17 +276,25 @@ bool File::isDirectory() const bool File::hasWriteAccess() const { - if (fullPath.isEmpty()) - return true; + if (exists()) + { + const auto attr = WindowsFileHelpers::getAtts (fullPath); - auto attr = WindowsFileHelpers::getAtts (fullPath); + return WindowsFileHelpers::hasFileAccess (*this, GENERIC_WRITE) + && (attr == INVALID_FILE_ATTRIBUTES + || (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 + || (attr & FILE_ATTRIBUTE_READONLY) == 0); + } - // NB: According to MS, the FILE_ATTRIBUTE_READONLY attribute doesn't work for - // folders, and can be incorrectly set for some special folders, so we'll just say - // that folders are always writable. - return attr == INVALID_FILE_ATTRIBUTES - || (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 - || (attr & FILE_ATTRIBUTE_READONLY) == 0; + if ((! isDirectory()) && fullPath.containsChar (getSeparatorChar())) + return getParentDirectory().hasWriteAccess(); + + return false; +} + +bool File::hasReadAccess() const +{ + return WindowsFileHelpers::hasFileAccess (*this, GENERIC_READ); } bool File::setFileReadOnlyInternal (bool shouldBeReadOnly) const @@ -239,7 +336,7 @@ bool File::moveToTrash() const doubleNullTermPath.calloc (numBytes, 1); fullPath.copyToUTF16 (doubleNullTermPath, numBytes); - SHFILEOPSTRUCT fos = { 0 }; + SHFILEOPSTRUCT fos = {}; fos.wFunc = FO_DELETE; fos.pFrom = doubleNullTermPath; fos.fFlags = FOF_ALLOWUNDO | FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION @@ -264,14 +361,14 @@ bool File::replaceInternal (const File& dest) const { return ReplaceFile (dest.getFullPathName().toWideCharPointer(), fullPath.toWideCharPointer(), - 0, REPLACEFILE_IGNORE_MERGE_ERRORS | 4 /*REPLACEFILE_IGNORE_ACL_ERRORS*/, + nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS | 4 /*REPLACEFILE_IGNORE_ACL_ERRORS*/, nullptr, nullptr) != 0; } Result File::createDirectoryInternal (const String& fileName) const { - return CreateDirectory (fileName.toWideCharPointer(), 0) ? Result::ok() - : WindowsFileHelpers::getResultForLastError(); + return CreateDirectory (fileName.toWideCharPointer(), nullptr) ? Result::ok() + : WindowsFileHelpers::getResultForLastError(); } //============================================================================== @@ -287,8 +384,8 @@ int64 juce_fileSetPosition (void* handle, int64 pos) void FileInputStream::openHandle() { auto h = CreateFile (file.getFullPathName().toWideCharPointer(), - GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); + GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr); if (h != INVALID_HANDLE_VALUE) fileHandle = (void*) h; @@ -303,11 +400,11 @@ FileInputStream::~FileInputStream() size_t FileInputStream::readInternal (void* buffer, size_t numBytes) { - if (fileHandle != 0) + if (fileHandle != nullptr) { DWORD actualNum = 0; - if (! ReadFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, 0)) + if (! ReadFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, nullptr)) status = WindowsFileHelpers::getResultForLastError(); return (size_t) actualNum; @@ -320,8 +417,8 @@ size_t FileInputStream::readInternal (void* buffer, size_t numBytes) void FileOutputStream::openHandle() { auto h = CreateFile (file.getFullPathName().toWideCharPointer(), - GENERIC_WRITE, FILE_SHARE_READ, 0, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + GENERIC_WRITE, FILE_SHARE_READ, nullptr, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (h != INVALID_HANDLE_VALUE) { @@ -350,7 +447,7 @@ ssize_t FileOutputStream::writeInternal (const void* bufferToWrite, size_t numBy DWORD actualNum = 0; if (fileHandle != nullptr) - if (! WriteFile ((HANDLE) fileHandle, bufferToWrite, (DWORD) numBytes, &actualNum, 0)) + if (! WriteFile ((HANDLE) fileHandle, bufferToWrite, (DWORD) numBytes, &actualNum, nullptr)) status = WindowsFileHelpers::getResultForLastError(); return (ssize_t) actualNum; @@ -398,18 +495,18 @@ void MemoryMappedFile::openInternal (const File& file, AccessMode mode, bool exc } auto h = CreateFile (file.getFullPathName().toWideCharPointer(), accessMode, - exclusive ? 0 : (FILE_SHARE_READ | FILE_SHARE_DELETE | (mode == readWrite ? FILE_SHARE_WRITE : 0)), 0, - createType, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); + exclusive ? 0 : (FILE_SHARE_READ | FILE_SHARE_DELETE | (mode == readWrite ? FILE_SHARE_WRITE : 0)), nullptr, + createType, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr); if (h != INVALID_HANDLE_VALUE) { fileHandle = (void*) h; - auto mappingHandle = CreateFileMapping (h, 0, protect, + auto mappingHandle = CreateFileMapping (h, nullptr, protect, (DWORD) (range.getEnd() >> 32), - (DWORD) range.getEnd(), 0); + (DWORD) range.getEnd(), nullptr); - if (mappingHandle != 0) + if (mappingHandle != nullptr) { address = MapViewOfFile (mappingHandle, access, (DWORD) (range.getStart() >> 32), (DWORD) range.getStart(), (SIZE_T) range.getLength()); @@ -465,8 +562,8 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 bool ok = false; auto h = CreateFile (fullPath.toWideCharPointer(), - GENERIC_WRITE, FILE_SHARE_READ, 0, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + GENERIC_WRITE, FILE_SHARE_READ, nullptr, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (h != INVALID_HANDLE_VALUE) { @@ -486,7 +583,7 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 //============================================================================== void File::findFileSystemRoots (Array& destArray) { - TCHAR buffer[2048] = { 0 }; + TCHAR buffer[2048] = {}; GetLogicalDriveStrings (2048, buffer); const TCHAR* n = buffer; @@ -512,7 +609,7 @@ String File::getVolumeLabel() const TCHAR dest[64]; if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest, - (DWORD) numElementsInArray (dest), 0, 0, 0, 0, 0)) + (DWORD) numElementsInArray (dest), nullptr, nullptr, nullptr, nullptr, 0)) dest[0] = 0; return dest; @@ -524,7 +621,7 @@ int File::getVolumeSerialNumber() const DWORD serialNum; if (! GetVolumeInformation (WindowsFileHelpers::getDriveFromPath (getFullPathName()).toWideCharPointer(), dest, - (DWORD) numElementsInArray (dest), &serialNum, 0, 0, 0, 0)) + (DWORD) numElementsInArray (dest), &serialNum, nullptr, nullptr, nullptr, 0)) return 0; return (int) serialNum; @@ -551,7 +648,7 @@ uint64 File::getFileIdentifier() const auto h = CreateFile (path.toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); if (h != INVALID_HANDLE_VALUE) { @@ -606,17 +703,18 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type) switch (type) { - case userHomeDirectory: csidlType = CSIDL_PROFILE; break; - case userDocumentsDirectory: csidlType = CSIDL_PERSONAL; break; - case userDesktopDirectory: csidlType = CSIDL_DESKTOP; break; - case userApplicationDataDirectory: csidlType = CSIDL_APPDATA; break; - case commonApplicationDataDirectory: csidlType = CSIDL_COMMON_APPDATA; break; - case commonDocumentsDirectory: csidlType = CSIDL_COMMON_DOCUMENTS; break; - case globalApplicationsDirectory: csidlType = CSIDL_PROGRAM_FILES; break; - case globalApplicationsDirectoryX86: csidlType = CSIDL_PROGRAM_FILESX86; break; - case userMusicDirectory: csidlType = 0x0d; /*CSIDL_MYMUSIC*/ break; - case userMoviesDirectory: csidlType = 0x0e; /*CSIDL_MYVIDEO*/ break; - case userPicturesDirectory: csidlType = 0x27; /*CSIDL_MYPICTURES*/ break; + case userHomeDirectory: csidlType = CSIDL_PROFILE; break; + case userDocumentsDirectory: csidlType = CSIDL_PERSONAL; break; + case userDesktopDirectory: csidlType = CSIDL_DESKTOP; break; + case userApplicationDataDirectory: csidlType = CSIDL_APPDATA; break; + case commonApplicationDataDirectory: csidlType = CSIDL_COMMON_APPDATA; break; + case commonDocumentsDirectory: csidlType = CSIDL_COMMON_DOCUMENTS; break; + case globalApplicationsDirectory: csidlType = CSIDL_PROGRAM_FILES; break; + case globalApplicationsDirectoryX86: csidlType = CSIDL_PROGRAM_FILESX86; break; + case windowsLocalAppData: csidlType = CSIDL_LOCAL_APPDATA; break; + case userMusicDirectory: csidlType = 0x0d; /*CSIDL_MYMUSIC*/ break; + case userMoviesDirectory: csidlType = 0x0e; /*CSIDL_MYVIDEO*/ break; + case userPicturesDirectory: csidlType = 0x27; /*CSIDL_MYPICTURES*/ break; case tempDirectory: { @@ -640,7 +738,7 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type) return WindowsFileHelpers::getModuleFileName ((HINSTANCE) Process::getCurrentModuleInstanceHandle()); case hostApplicationPath: - return WindowsFileHelpers::getModuleFileName (0); + return WindowsFileHelpers::getModuleFileName (nullptr); default: jassertfalse; // unknown type? @@ -679,7 +777,7 @@ String File::getVersion() const VS_FIXEDFILEINFO* vffi; UINT len = 0; - if (VerQueryValue (buffer, (LPTSTR) _T("\\"), (LPVOID*) &vffi, &len)) + if (VerQueryValue (buffer, (LPTSTR) _T ("\\"), (LPVOID*) &vffi, &len)) { result << (int) HIWORD (vffi->dwFileVersionMS) << '.' << (int) LOWORD (vffi->dwFileVersionMS) << '.' @@ -694,7 +792,8 @@ String File::getVersion() const //============================================================================== bool File::isSymbolicLink() const { - return (GetFileAttributes (fullPath.toWideCharPointer()) & FILE_ATTRIBUTE_REPARSE_POINT) != 0; + const auto attributes = WindowsFileHelpers::getAtts (fullPath); + return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0); } bool File::isShortcut() const @@ -715,9 +814,9 @@ static String readWindowsLnkFile (File lnkFile, bool wantsAbsolutePath) if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)) && SUCCEEDED (shellLink.QueryInterface (persistFile)) && SUCCEEDED (persistFile->Load (lnkFile.getFullPathName().toWideCharPointer(), STGM_READ)) - && (! wantsAbsolutePath || SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI)))) + && (! wantsAbsolutePath || SUCCEEDED (shellLink->Resolve (nullptr, SLR_ANY_MATCH | SLR_NO_UI)))) { - WIN32_FIND_DATA winFindData; + WIN32_FIND_DATA winFindData = {}; WCHAR resolvedPath[MAX_PATH]; DWORD flags = SLGP_UNCPRIORITY; @@ -741,7 +840,7 @@ static String readWindowsShortcutOrLink (const File& shortcut, bool wantsAbsolut HANDLE h = CreateFile (shortcut.getFullPathName().toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, - 0); + nullptr); if (h != INVALID_HANDLE_VALUE) { @@ -809,7 +908,7 @@ static String readWindowsShortcutOrLink (const File& shortcut, bool wantsAbsolut { HANDLE h = CreateFile (shortcut.getFullPathName().toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); if (h != INVALID_HANDLE_VALUE) { @@ -861,7 +960,7 @@ bool File::createShortcut (const String& description, const File& linkFileToCrea ComSmartPtr shellLink; ComSmartPtr persistFile; - CoInitialize (0); + [[maybe_unused]] const auto result = CoInitialize (nullptr); return SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)) && SUCCEEDED (shellLink->SetPath (getFullPathName().toWideCharPointer())) @@ -874,8 +973,8 @@ bool File::createShortcut (const String& description, const File& linkFileToCrea class DirectoryIterator::NativeIterator::Pimpl { public: - Pimpl (const File& directory, const String& wildCard) - : directoryWithWildCard (directory.getFullPathName().isNotEmpty() ? File::addTrailingSeparator (directory.getFullPathName()) + wildCard : String()), + Pimpl (const File& directory, const String& wildCardIn) + : directoryWithWildCard (directory.getFullPathName().isNotEmpty() ? File::addTrailingSeparator (directory.getFullPathName()) + wildCardIn : String()), handle (INVALID_HANDLE_VALUE) { } @@ -925,8 +1024,8 @@ class DirectoryIterator::NativeIterator::Pimpl JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; -DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) - : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCardIn) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCardIn)) { } @@ -945,8 +1044,8 @@ bool DirectoryIterator::NativeIterator::next (String& filenameFound, //============================================================================== bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters) { - HINSTANCE hInstance = ShellExecute (0, 0, fileName.toWideCharPointer(), - parameters.toWideCharPointer(), 0, SW_SHOWDEFAULT); + HINSTANCE hInstance = ShellExecute (nullptr, nullptr, fileName.toWideCharPointer(), + parameters.toWideCharPointer(), nullptr, SW_SHOWDEFAULT); return hInstance > (HINSTANCE) 32; } @@ -975,14 +1074,14 @@ class NamedPipe::Pimpl Pimpl (const String& pipeName, const bool createPipe, bool mustNotExist) : filename ("\\\\.\\pipe\\" + File::createLegalFileName (pipeName)), pipeH (INVALID_HANDLE_VALUE), - cancelEvent (CreateEvent (0, FALSE, FALSE, 0)), + cancelEvent (CreateEvent (nullptr, TRUE, FALSE, nullptr)), connected (false), ownsPipe (createPipe), shouldStop (false) { if (createPipe) { pipeH = CreateNamedPipe (filename.toWideCharPointer(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, - PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, 0); + PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, nullptr); if (mustNotExist && GetLastError() == ERROR_ALREADY_EXISTS) closePipeHandle(); @@ -1011,8 +1110,8 @@ class NamedPipe::Pimpl if (pipeH == INVALID_HANDLE_VALUE) pipeH = CreateFile (filename.toWideCharPointer(), - GENERIC_READ | GENERIC_WRITE, 0, 0, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + GENERIC_READ | GENERIC_WRITE, 0, nullptr, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr); } if (pipeH != INVALID_HANDLE_VALUE) @@ -1071,14 +1170,12 @@ class NamedPipe::Pimpl return 0; OverlappedEvent over; - unsigned long numRead; + unsigned long numRead = 0; if (ReadFile (pipeH, destBuffer, (DWORD) maxBytesToRead, &numRead, &over.over)) return (int) numRead; - const DWORD lastError = GetLastError(); - - if (lastError == ERROR_IO_PENDING) + if (GetLastError() == ERROR_IO_PENDING) { if (! waitForIO (over, timeOutMilliseconds)) return -1; @@ -1087,7 +1184,9 @@ class NamedPipe::Pimpl return (int) numRead; } - if (ownsPipe && (GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_PIPE_NOT_CONNECTED)) + const auto lastError = GetLastError(); + + if (ownsPipe && (lastError == ERROR_BROKEN_PIPE || lastError == ERROR_PIPE_NOT_CONNECTED)) disconnectPipe(); else break; @@ -1127,7 +1226,8 @@ class NamedPipe::Pimpl const String filename; HANDLE pipeH, cancelEvent; - bool connected, ownsPipe, shouldStop; + bool connected, ownsPipe; + std::atomic shouldStop; CriticalSection createFileLock; private: @@ -1136,7 +1236,7 @@ class NamedPipe::Pimpl OverlappedEvent() { zerostruct (over); - over.hEvent = CreateEvent (0, TRUE, FALSE, 0); + over.hEvent = CreateEvent (nullptr, TRUE, FALSE, nullptr); } ~OverlappedEvent() @@ -1150,11 +1250,14 @@ class NamedPipe::Pimpl bool waitForIO (OverlappedEvent& over, int timeOutMilliseconds) { if (shouldStop) + { + CancelIo (pipeH); return false; + } HANDLE handles[] = { over.over.hEvent, cancelEvent }; - DWORD waitResult = WaitForMultipleObjects (2, handles, FALSE, - timeOutMilliseconds >= 0 ? timeOutMilliseconds + DWORD waitResult = WaitForMultipleObjects (numElementsInArray (handles), handles, FALSE, + timeOutMilliseconds >= 0 ? (DWORD) timeOutMilliseconds : INFINITE); if (waitResult == WAIT_OBJECT_0) @@ -1169,11 +1272,17 @@ class NamedPipe::Pimpl void NamedPipe::close() { - if (pimpl != nullptr) { - pimpl->shouldStop = true; - SetEvent (pimpl->cancelEvent); + ScopedReadLock sl (lock); + + if (pimpl != nullptr) + { + pimpl->shouldStop = true; + SetEvent (pimpl->cancelEvent); + } + } + { ScopedWriteLock sl (lock); pimpl.reset(); } @@ -1181,22 +1290,19 @@ void NamedPipe::close() bool NamedPipe::openInternal (const String& pipeName, const bool createPipe, bool mustNotExist) { - pimpl.reset (new Pimpl (pipeName, createPipe, mustNotExist)); + auto newPimpl = std::make_unique (pipeName, createPipe, mustNotExist); if (createPipe) { - if (pimpl->pipeH == INVALID_HANDLE_VALUE) - { - pimpl.reset(); + if (newPimpl->pipeH == INVALID_HANDLE_VALUE) return false; - } } - else if (! pimpl->connect (200)) + else if (! newPimpl->connect (200)) { - pimpl.reset(); return false; } + pimpl = std::move (newPimpl); return true; } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_posix_IPAddress.h b/JuceLibraryCode/modules/juce_core/native/juce_IPAddress_posix.h similarity index 84% rename from JuceLibraryCode/modules/juce_core/native/juce_posix_IPAddress.h rename to JuceLibraryCode/modules/juce_core/native/juce_IPAddress_posix.h index b72d5c3f..983b5495 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_posix_IPAddress.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_IPAddress_posix.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,6 +25,18 @@ namespace juce namespace { + struct InterfaceInfo + { + IPAddress interfaceAddress, broadcastAddress; + }; + + inline bool operator== (const InterfaceInfo& lhs, const InterfaceInfo& rhs) + { + return lhs.interfaceAddress == rhs.interfaceAddress + && lhs.broadcastAddress == rhs.broadcastAddress; + } + + #if ! JUCE_WASM static IPAddress makeAddress (const sockaddr_in6* addr_in) { if (addr_in == nullptr) @@ -54,25 +66,14 @@ namespace return IPAddress (ntohl (addr_in->sin_addr.s_addr)); } - struct InterfaceInfo - { - IPAddress interfaceAddress, broadcastAddress; - }; - - bool operator== (const InterfaceInfo& lhs, const InterfaceInfo& rhs) - { - return lhs.interfaceAddress == rhs.interfaceAddress - && lhs.broadcastAddress == rhs.broadcastAddress; - } - bool populateInterfaceInfo (struct ifaddrs* ifa, InterfaceInfo& interfaceInfo) { if (ifa->ifa_addr != nullptr) { if (ifa->ifa_addr->sa_family == AF_INET) { - auto interfaceAddressInfo = reinterpret_cast (ifa->ifa_addr); - auto broadcastAddressInfo = reinterpret_cast (ifa->ifa_dstaddr); + auto interfaceAddressInfo = unalignedPointerCast (ifa->ifa_addr); + auto broadcastAddressInfo = unalignedPointerCast (ifa->ifa_dstaddr); if (interfaceAddressInfo->sin_addr.s_addr != INADDR_NONE) { @@ -83,18 +84,23 @@ namespace } else if (ifa->ifa_addr->sa_family == AF_INET6) { - interfaceInfo.interfaceAddress = makeAddress (reinterpret_cast (ifa->ifa_addr)); - interfaceInfo.broadcastAddress = makeAddress (reinterpret_cast (ifa->ifa_dstaddr)); + interfaceInfo.interfaceAddress = makeAddress (unalignedPointerCast (ifa->ifa_addr)); + interfaceInfo.broadcastAddress = makeAddress (unalignedPointerCast (ifa->ifa_dstaddr)); return true; } } return false; } + #endif Array getAllInterfaceInfo() { Array interfaces; + + #if JUCE_WASM + // TODO + #else struct ifaddrs* ifaddr = nullptr; if (getifaddrs (&ifaddr) != -1) @@ -109,6 +115,7 @@ namespace freeifaddrs (ifaddr); } + #endif return interfaces; } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.cpp b/JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.cpp similarity index 54% rename from JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.cpp index fcd7c77e..94b3c2c4 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.cpp @@ -1,51 +1,52 @@ /* - ============================================================================== + ============================================================================== - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited - JUCE is an open source library subject to commercial or open-source - licensing. + 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. + 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. + 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 { //============================================================================== -static const uint8 invocationHandleByteCode[] = { -31,139,8,8,170,94,229,91,0,3,105,110,118,111,99,97,116,105,111,110,95,104,97,110,100,108,101,114,46,100,101,120,0,109, -148,75,107,19,81,20,199,207,189,119,38,169,181,109,98,218,138,72,23,81,20,68,193,169,218,130,144,42,130,162,53,140,15, -104,200,66,187,232,52,185,181,147,78,103,98,58,141,33,184,168,197,47,224,66,168,187,186,241,83,168,184,16,247,162,31,193, -69,151,174,220,40,234,255,62,154,4,237,192,239,62,206,57,115,30,115,239,153,186,236,12,79,95,154,165,159,187,91,95,227, -217,197,247,87,42,31,231,191,156,157,58,241,253,199,220,201,79,75,175,118,58,14,81,147,136,58,213,153,2,217,231,131,32, -154,32,35,63,164,246,0,102,244,13,48,48,129,33,139,121,138,153,125,5,131,131,119,82,204,203,156,168,1,214,65,10,186,224, -53,120,7,62,131,61,144,131,237,57,112,30,92,4,183,192,93,176,4,154,160,11,182,193,11,97,252,171,216,46,200,144,137,59, -100,243,26,6,35,0,46,9,166,116,131,155,89,113,159,27,125,214,214,116,216,174,23,185,241,89,208,181,8,173,99,240,48,106, -247,99,182,198,156,149,231,245,204,232,136,246,203,173,189,65,189,61,7,209,31,60,167,48,239,26,119,90,183,35,84,190,66, -175,201,230,218,204,43,201,81,172,30,76,131,25,66,180,190,133,169,81,107,139,164,243,112,123,25,154,253,194,53,232,17, -231,2,74,190,140,106,212,190,57,205,201,97,99,168,207,209,223,71,61,147,202,182,57,104,59,74,11,45,212,255,56,251,60,251, -50,251,166,157,81,81,71,80,83,129,206,252,238,133,239,101,162,242,72,237,217,186,246,251,171,106,51,248,242,162,183,218, -183,207,204,133,113,152,94,37,86,38,215,47,251,190,79,142,175,198,211,126,45,89,247,90,73,20,122,141,205,154,244,202,24, -110,199,237,164,22,164,97,18,207,7,113,61,146,173,18,29,247,235,65,212,14,215,188,32,142,147,84,235,188,202,106,43,121, -178,81,162,130,223,8,218,129,23,5,241,35,239,222,114,67,214,210,18,77,14,200,180,93,176,28,201,18,162,245,197,45,185,18, -193,214,59,48,218,255,102,119,100,186,154,212,75,196,170,196,171,101,26,127,120,64,84,183,22,201,160,69,249,122,184,209, -12,210,218,234,205,48,14,162,176,43,105,108,95,162,130,173,73,26,90,217,215,100,66,35,25,141,145,66,91,94,79,226,84,118, -82,114,219,65,180,41,137,115,54,62,197,142,57,196,132,186,86,207,182,156,95,156,111,115,98,10,182,43,4,123,43,24,219,19, -185,127,206,70,247,159,237,77,62,208,159,98,160,71,157,129,62,117,169,223,171,25,234,247,171,200,155,181,62,231,162,121, -231,169,178,41,26,185,186,207,44,111,228,234,142,243,162,137,171,250,219,41,246,239,56,217,181,190,251,214,167,250,127, -252,5,76,239,47,69,120,4,0,0}; +static const uint8 invocationHandleByteCode[] = +{31,139,8,8,215,115,161,94,0,3,105,110,118,111,99,97,116,105,111,110,72,97,110,100,108,101,66,121,116,101,67,111,100,101,46, +100,101,120,0,109,148,65,107,19,65,20,199,223,236,78,146,90,211,116,77,141,214,88,33,151,130,7,117,91,172,80,73,17,161,32,53,93, +17,108,233,65,5,217,38,155,102,219,237,110,220,108,99,172,8,173,40,42,244,36,245,226,65,232,165,138,7,15,226,65,193,147,120,244, +166,130,95,192,155,23,189,21,68,252,207,206,180,137,218,133,223,204,155,247,222,206,123,111,119,230,85,156,86,247,208,201, +83,84,121,127,155,63,122,245,209,24,205,141,63,156,124,186,176,229,110,12,151,158,157,126,219,76,39,136,234,68,212,154,25,201, +146,122,38,56,81,158,164,126,15,248,10,160,162,95,128,129,99,24,82,152,71,152,92,123,24,86,116,162,53,204,203,26,209,29,112, +15,108,128,231,224,37,248,2,126,128,4,252,6,192,56,184,6,102,65,21,220,2,171,224,1,120,2,94,128,215,224,29,248,0,62,129,111,224, +59,248,169,203,184,72,157,146,36,115,233,82,185,118,131,189,160,7,232,138,171,154,204,95,200,53,77,218,83,170,214,180,146, +35,77,238,153,139,107,212,99,27,35,141,122,213,218,80,181,239,83,250,108,60,51,234,139,247,213,148,191,68,188,61,13,149,208,142, +97,24,228,180,99,63,194,69,206,122,44,111,233,50,223,186,33,52,7,32,93,30,2,35,68,25,229,1,95,46,235,140,173,5,97,17,107,153, +97,154,203,245,212,89,216,17,103,24,33,71,81,141,88,215,135,52,226,44,131,90,121,252,141,250,184,172,109,170,222,233,219,67,83, +33,234,191,158,186,155,122,156,218,108,38,69,212,52,106,204,210,209,223,180,243,48,53,139,60,214,88,251,219,203,178,116,236, +223,165,190,117,50,254,15,42,243,49,215,119,163,51,196,74,148,47,45,149,157,243,126,51,40,219,145,27,248,19,182,95,241,156,240, +196,188,221,180,41,97,149,44,203,34,110,137,113,208,42,7,139,102,184,216,240,204,121,188,98,238,250,94,145,242,86,197,246,154, +238,130,105,251,126,16,197,54,115,186,22,6,55,26,69,202,90,98,91,211,179,253,57,243,226,236,188,83,142,138,148,235,208,197,126, +246,172,231,20,17,173,173,14,157,170,7,95,115,215,104,255,187,93,112,162,90,80,41,18,155,33,109,166,68,125,87,118,137,202,237, +112,174,65,137,178,231,216,33,25,21,183,81,183,163,114,237,156,235,219,158,187,236,80,102,91,35,66,46,56,212,85,221,182,36,93, +169,73,46,198,81,168,199,71,66,77,103,60,240,35,167,21,145,241,215,242,146,83,165,68,61,12,90,55,137,71,53,23,1,155,182,183, +132,237,216,193,84,70,203,23,181,185,210,113,156,146,84,102,146,246,99,188,127,153,14,235,253,185,94,72,155,164,105,236,208,0, +235,231,196,116,113,134,87,87,248,186,174,225,246,50,1,123,163,235,236,179,206,216,138,248,207,198,63,103,65,204,219,61,66,235, +232,19,122,71,175,224,29,253,34,65,237,158,145,164,118,223,208,13,41,199,231,170,32,223,89,23,62,5,169,23,247,135,25,82,31,223, +169,130,140,43,250,140,174,252,197,61,226,133,246,253,34,37,15,170,196,133,44,122,218,31,165,24,139,249,12,5,0,0,0,0}; //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ @@ -61,26 +62,26 @@ static const uint8 invocationHandleByteCode[] = { CALLBACK (juce_invokeImplementer, "dispatchInvoke", "(JLjava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;") \ CALLBACK (juce_dispatchDelete, "dispatchFinalize", "(J)V") - DECLARE_JNI_CLASS_WITH_BYTECODE (JuceInvocationHandler, "com/roli/juce/JuceInvocationHandler", 10, invocationHandleByteCode, sizeof (invocationHandleByteCode)) + DECLARE_JNI_CLASS_WITH_BYTECODE (JuceInvocationHandler, "com/rmsl/juce/JuceInvocationHandler", 10, invocationHandleByteCode) #undef JNI_CLASS_MEMBERS #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ - METHOD (findClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;") \ + METHOD (loadClass, "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;") \ STATICMETHOD (getSystemClassLoader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;") -DECLARE_JNI_CLASS (JavaClassLoader, "java/lang/ClassLoader") + DECLARE_JNI_CLASS (JavaClassLoader, "java/lang/ClassLoader") #undef JNI_CLASS_MEMBERS #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (constructor, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V") -DECLARE_JNI_CLASS (AndroidDexClassLoader, "dalvik/system/DexClassLoader") + DECLARE_JNI_CLASS (AndroidDexClassLoader, "dalvik/system/DexClassLoader") #undef JNI_CLASS_MEMBERS #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (constructor, "", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V") -DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidInMemoryDexClassLoader, "dalvik/system/InMemoryDexClassLoader", 26) + DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidInMemoryDexClassLoader, "dalvik/system/InMemoryDexClassLoader", 26) #undef JNI_CLASS_MEMBERS //============================================================================== @@ -93,8 +94,8 @@ struct SystemJavaClassComparator if ((! isSysClassA) && (! isSysClassB)) { - return DefaultElementComparator::compareElements (first != nullptr ? first->byteCode != nullptr : false, - second != nullptr ? second->byteCode != nullptr : false); + return DefaultElementComparator::compareElements (first != nullptr && first->byteCode != nullptr, + second != nullptr && second->byteCode != nullptr); } return DefaultElementComparator::compareElements (isSystemClass (first), @@ -115,7 +116,7 @@ struct SystemJavaClassComparator }; //============================================================================== -JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const void* bc, size_t n) +JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const uint8* bc, size_t n) : classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (nullptr) { SystemJavaClassComparator comparator; @@ -138,17 +139,17 @@ Array& JNIClassBase::getClasses() static File getCodeCacheDirectory() { int pid = getpid(); - File cmdline("/proc/" + String(pid) + "/cmdline"); + File cmdline ("/proc/" + String (pid) + "/cmdline"); auto bundleId = cmdline.loadFileAsString().trimStart().trimEnd(); if (bundleId.isEmpty()) return {}; - return File("/data/data/" + bundleId + "/code_cache"); + return File ("/data/data/" + bundleId + "/code_cache"); } -void JNIClassBase::initialise (JNIEnv* env) +void JNIClassBase::initialise (JNIEnv* env, jobject context) { auto sdkVersion = getAndroidSDKVersion(); @@ -157,9 +158,16 @@ void JNIClassBase::initialise (JNIEnv* env) LocalRef classNameAndPackage (javaString (String (classPath).replaceCharacter (L'/', L'.'))); static Array byteCodeLoaders; - if (! SystemJavaClassComparator::isSystemClass(this)) + if (! SystemJavaClassComparator::isSystemClass (this)) { - LocalRef defaultClassLoader (env->CallStaticObjectMethod (JavaClassLoader, JavaClassLoader.getSystemClassLoader)); + // We use the context's class loader, rather than the 'system' class loader, because we + // may need to load classes from our library dependencies (such as the BillingClient + // library), and the system class loader is not aware of those libraries. + const LocalRef defaultClassLoader { env->CallObjectMethod (context, + env->GetMethodID (env->FindClass ("android/content/Context"), + "getClassLoader", + "()Ljava/lang/ClassLoader;")) }; + tryLoadingClassWithClassLoader (env, defaultClassLoader.get()); if (classRef == nullptr) @@ -228,7 +236,7 @@ void JNIClassBase::initialise (JNIEnv* env) if (byteCodeClassLoader != nullptr) { tryLoadingClassWithClassLoader (env, byteCodeClassLoader.get()); - byteCodeLoaders.add (GlobalRef(byteCodeClassLoader)); + byteCodeLoaders.add (GlobalRef (byteCodeClassLoader)); } } } @@ -248,9 +256,9 @@ void JNIClassBase::tryLoadingClassWithClassLoader (JNIEnv* env, jobject classLoa // Android SDK <= 19 has a bug where the class loader might throw an exception but still return // a non-nullptr. So don't assign the result of this call to a jobject just yet... - auto classObj = env->CallObjectMethod (classLoader, JavaClassLoader.findClass, classNameAndPackage.get()); + auto classObj = env->CallObjectMethod (classLoader, JavaClassLoader.loadClass, classNameAndPackage.get(), (jboolean) true); - if (jthrowable exception = env->ExceptionOccurred ()) + if (jthrowable exception = env->ExceptionOccurred()) { env->ExceptionClear(); classObj = nullptr; @@ -267,18 +275,18 @@ void JNIClassBase::release (JNIEnv* env) env->DeleteGlobalRef (classRef); } -void JNIClassBase::initialiseAllClasses (JNIEnv* env) +void JNIClassBase::initialiseAllClasses (JNIEnv* env, jobject context) { const Array& classes = getClasses(); for (int i = classes.size(); --i >= 0;) - classes.getUnchecked(i)->initialise (env); + classes.getUnchecked (i)->initialise (env, context); } void JNIClassBase::releaseAllClasses (JNIEnv* env) { const Array& classes = getClasses(); for (int i = classes.size(); --i >= 0;) - classes.getUnchecked(i)->release (env); + classes.getUnchecked (i)->release (env); } jmethodID JNIClassBase::resolveMethod (JNIEnv* env, const char* methodName, const char* params) @@ -413,34 +421,51 @@ jobject ActivityLifecycleCallbacks::invoke (jobject proxy, jobject method, jobje { auto* env = getEnv(); - auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName)); - - 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 == "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); + struct Comparator + { + bool operator() (const char* a, const char* b) const + { + return CharPointer_ASCII { a }.compare (CharPointer_ASCII { b }) < 0; + } + }; + + static const std::map entries + { + { "onActivityConfigurationChanged", [] (auto& t, auto activity, auto ) { t.onActivityConfigurationChanged (activity); } }, + { "onActivityCreated", [] (auto& t, auto activity, auto bundle) { t.onActivityCreated (activity, bundle); } }, + { "onActivityDestroyed", [] (auto& t, auto activity, auto ) { t.onActivityDestroyed (activity); } }, + { "onActivityPaused", [] (auto& t, auto activity, auto ) { t.onActivityPaused (activity); } }, + { "onActivityPostCreated", [] (auto& t, auto activity, auto bundle) { t.onActivityPostCreated (activity, bundle); } }, + { "onActivityPostDestroyed", [] (auto& t, auto activity, auto ) { t.onActivityPostDestroyed (activity); } }, + { "onActivityPostPaused", [] (auto& t, auto activity, auto ) { t.onActivityPostPaused (activity); } }, + { "onActivityPostResumed", [] (auto& t, auto activity, auto ) { t.onActivityPostResumed (activity); } }, + { "onActivityPostSaveInstanceState", [] (auto& t, auto activity, auto bundle) { t.onActivityPostSaveInstanceState (activity, bundle); } }, + { "onActivityPostStarted", [] (auto& t, auto activity, auto ) { t.onActivityPostStarted (activity); } }, + { "onActivityPostStopped", [] (auto& t, auto activity, auto ) { t.onActivityPostStopped (activity); } }, + { "onActivityPreCreated", [] (auto& t, auto activity, auto bundle) { t.onActivityPreCreated (activity, bundle); } }, + { "onActivityPreDestroyed", [] (auto& t, auto activity, auto ) { t.onActivityPreDestroyed (activity); } }, + { "onActivityPrePaused", [] (auto& t, auto activity, auto ) { t.onActivityPrePaused (activity); } }, + { "onActivityPreResumed", [] (auto& t, auto activity, auto ) { t.onActivityPreResumed (activity); } }, + { "onActivityPreSaveInstanceState", [] (auto& t, auto activity, auto bundle) { t.onActivityPreSaveInstanceState (activity, bundle); } }, + { "onActivityPreStarted", [] (auto& t, auto activity, auto ) { t.onActivityPreStarted (activity); } }, + { "onActivityPreStopped", [] (auto& t, auto activity, auto ) { t.onActivityPreStopped (activity); } }, + { "onActivityResumed", [] (auto& t, auto activity, auto ) { t.onActivityResumed (activity); } }, + { "onActivitySaveInstanceState", [] (auto& t, auto activity, auto bundle) { t.onActivitySaveInstanceState (activity, bundle); } }, + { "onActivityStarted", [] (auto& t, auto activity, auto ) { t.onActivityStarted (activity); } }, + { "onActivityStopped", [] (auto& t, auto activity, auto ) { t.onActivityStopped (activity); } }, + }; + + const auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName)); + const auto iter = entries.find (methodName.toRawUTF8()); + + if (iter == entries.end()) + return AndroidInterfaceImplementer::invoke (proxy, method, args); + + const auto activity = env->GetArrayLength (args) > 0 ? env->GetObjectArrayElement (args, 0) : (jobject) nullptr; + const auto bundle = env->GetArrayLength (args) > 1 ? env->GetObjectArrayElement (args, 1) : (jobject) nullptr; + (iter->second) (*this, activity, bundle); + + return nullptr; } //============================================================================== @@ -490,50 +515,49 @@ bool isPermissionDeclaredInManifest (const String& requestedPermission) } //============================================================================== -// This byte-code is generated from native/java/com/roli/juce/FragmentOverlay.java with min sdk version 16 +// This byte-code is generated from native/java/com/rmsl/juce/FragmentOverlay.java with min sdk version 16 // See juce_core/native/java/README.txt on how to generate this byte-code. static const uint8 javaFragmentOverlay[] = -{31,139,8,8,197,105,229,91,0,3,70,114,97,103,109,101,110,116,79,118,101,114,108,97,121,46,100,101,120,0,133,149,93,136, -28,69,16,199,171,231,243,62,39,235,93,60,206,243,244,214,136,73,4,201,156,104,32,186,107,184,36,34,236,58,248,145,11,251, -112,241,101,216,29,215,137,123,51,155,153,217,35,1,65,61,2,201,131,8,10,126,97,2,17,84,16,204,91,30,228,240,73,130,95,40, -104,124,9,137,47,9,248,230,23,8,65,52,130,255,234,238,201,133,35,226,178,191,169,234,234,238,170,234,154,158,238,78,116, -100,100,254,129,157,116,170,242,197,47,171,219,126,253,250,218,79,107,167,30,234,189,251,229,123,127,239,158,219,187,86, -124,127,193,33,234,19,209,145,214,131,19,164,127,187,96,187,151,148,125,4,108,22,74,214,33,241,167,179,120,84,32,63,213, -237,186,65,244,130,69,244,12,228,121,147,232,34,248,29,252,1,174,130,191,192,63,224,118,140,217,9,154,224,121,240,34,88, -5,39,192,171,224,117,240,14,56,13,62,0,31,129,51,224,51,240,21,56,15,46,128,203,224,55,240,39,112,108,162,105,48,15,30,6, -77,240,44,56,1,94,3,167,193,25,176,6,62,7,223,2,164,73,72,135,176,76,114,193,16,24,214,107,29,5,147,188,102,0,247,114, -125,199,48,216,214,109,210,99,92,173,143,105,253,21,140,25,215,250,219,208,61,173,191,15,125,147,214,63,54,85,221,88,255, -4,250,45,90,63,7,125,66,235,223,200,88,130,166,136,243,52,100,12,3,217,221,169,219,91,116,30,51,196,227,84,63,203,91,181, -156,38,53,255,54,41,77,154,149,210,161,59,164,84,126,108,172,120,78,74,139,170,82,186,116,151,158,191,69,74,155,238,38, -181,102,65,164,163,40,157,127,67,142,146,38,44,108,251,193,86,53,236,87,120,44,103,190,84,229,10,148,253,151,116,127,217, -147,84,28,140,243,80,71,75,190,131,43,182,90,255,34,54,220,20,130,221,15,55,187,208,187,152,161,38,135,197,49,241,134, -251,225,138,51,12,95,30,241,76,94,255,207,152,195,107,74,171,130,14,192,163,11,235,24,205,136,41,74,170,38,170,60,74,75, -11,240,184,112,163,71,87,182,251,11,136,251,180,39,223,163,138,127,245,127,226,187,50,254,184,140,207,181,229,189,195,19, -249,253,165,21,206,231,166,113,230,55,145,37,60,93,55,71,239,57,210,82,233,195,178,46,66,83,238,55,238,45,117,30,97,72, -221,210,99,156,122,156,196,197,110,218,252,88,22,118,151,163,164,120,114,37,202,122,225,209,29,135,194,149,144,68,131,68, -147,140,102,64,34,160,217,32,76,58,89,26,119,252,176,223,247,31,141,195,94,218,45,103,213,104,250,122,111,59,77,10,152, -252,134,20,53,154,188,222,147,230,254,222,65,210,233,69,53,154,11,218,233,178,159,165,189,216,63,52,104,71,254,134,240, -53,154,8,56,3,191,23,38,93,127,177,200,226,164,91,35,209,34,171,213,104,4,252,12,2,50,90,77,178,91,77,54,176,128,197,108, -53,217,12,14,54,104,242,224,77,92,216,237,94,154,71,228,182,251,253,3,207,197,57,89,157,176,8,201,237,196,249,114,156, -231,52,214,141,138,61,89,119,192,169,228,228,162,21,164,73,23,230,44,76,138,253,81,62,232,193,92,73,147,61,237,34,94,137, -139,163,202,68,83,27,45,79,132,104,69,52,148,38,251,178,40,44,34,242,74,77,247,204,164,201,254,232,240,32,202,139,167, -162,140,67,199,105,146,107,111,213,255,238,211,179,221,52,89,44,194,172,160,113,173,104,251,104,127,125,2,141,102,202, -201,190,180,19,209,72,38,231,75,221,206,11,78,201,42,184,0,46,185,158,113,95,141,118,64,62,94,167,237,230,214,109,211, -174,119,252,77,26,19,219,93,175,126,238,248,18,85,205,173,247,204,194,246,22,190,57,215,123,4,22,18,54,62,111,235,229, -151,172,31,45,123,21,39,201,13,216,226,154,101,138,147,182,33,190,3,39,29,72,103,124,195,55,207,178,188,19,120,63,150, -247,130,73,235,119,67,185,103,249,126,224,179,163,188,35,28,90,191,39,68,85,181,249,174,16,21,117,46,240,249,106,84,149, -127,190,63,76,61,134,207,21,62,160,68,121,230,84,148,206,247,211,191,48,134,254,198,216,6,0,0}; +{31,139,8,8,26,116,161,94,0,3,106,97,118,97,70,114,97,103,109,101,110,116,79,118,101,114,108,97,121,46,100,101,120,0,133,149, +77,136,28,69,20,199,255,53,253,181,159,179,147,221,184,140,235,198,140,43,70,197,224,172,104,36,56,99,216,152,32,204,100,226,71, +54,204,97,227,165,153,105,39,189,206,118,79,186,123,150,4,20,53,4,146,131,8,6,252,130,28,114,80,65,48,8,226,65,196,83,8,66,64, +65,146,75,252,184,152,179,160,160,4,17,5,255,175,187,58,27,150,136,195,252,250,189,122,245,234,189,170,215,213,85,93,239,248, +216,226,163,187,96,79,85,156,198,103,91,86,175,30,189,252,253,193,79,203,15,189,242,199,245,246,129,179,245,238,53,27,24,0,56, +222,126,108,26,250,183,155,182,7,145,217,199,200,86,149,201,58,37,255,248,156,143,18,229,87,186,93,47,0,47,155,192,11,148,87, +12,224,7,242,27,249,157,220,32,127,145,127,200,93,244,217,69,154,228,37,242,42,57,73,206,144,55,201,89,242,62,57,79,62,36,31, +147,11,228,34,185,76,174,144,107,228,103,242,43,249,147,216,22,80,38,139,228,9,210,36,47,146,51,228,45,114,158,92,32,95,146,175, +201,183,132,211,4,167,3,46,19,14,25,33,163,122,173,227,100,70,214,76,24,62,93,223,41,58,91,186,13,237,227,104,125,66,235,111, +208,103,82,235,239,81,47,106,253,3,234,83,90,255,196,200,234,38,250,23,212,183,104,253,18,245,105,173,127,147,230,82,152,133, +204,179,144,230,40,112,118,119,235,246,130,158,199,28,196,47,235,23,121,135,150,101,100,227,239,76,165,129,249,84,218,216,150, +202,44,142,197,21,111,79,165,137,74,42,29,220,163,199,47,164,210,194,189,200,214,172,0,157,37,211,229,55,98,103,210,160,69,108, +87,173,172,134,131,146,248,202,204,87,42,82,129,188,255,71,221,159,247,4,37,155,126,69,214,209,76,223,193,117,43,91,255,50,55, +220,44,147,61,194,48,187,217,187,28,177,38,199,212,41,245,182,243,209,186,61,202,88,69,200,72,89,255,47,28,35,107,10,43,10,135, +25,209,161,117,2,115,106,22,65,197,96,149,199,177,178,196,136,75,183,70,116,210,246,96,137,121,159,47,166,239,49,203,127,227, +127,242,59,105,254,201,52,191,212,86,246,142,12,148,247,23,150,100,62,183,205,179,56,5,83,21,117,221,108,189,231,160,101,166, +143,166,117,81,154,124,191,73,111,174,139,71,33,213,77,237,99,215,253,192,79,246,96,235,211,145,219,91,243,130,228,217,117,47, +234,187,39,30,94,117,215,93,168,6,84,19,133,102,11,170,133,249,150,27,116,163,208,239,86,221,193,160,186,223,119,251,97,47,31, +85,67,249,102,111,39,12,18,154,170,141,84,212,48,115,179,39,140,171,79,13,131,110,223,171,97,123,171,19,174,85,163,181,184,95, +93,29,118,188,234,166,244,53,76,183,100,6,213,190,27,244,170,203,73,228,7,189,26,84,27,102,187,209,104,201,179,213,66,161,221, +132,213,110,138,65,4,45,70,187,41,102,114,164,129,153,35,183,9,97,117,250,97,236,193,233,12,6,135,143,250,49,204,174,155,184, +112,186,126,188,230,199,49,38,122,94,178,55,234,13,101,42,49,28,182,90,97,208,163,57,114,131,228,144,23,15,251,52,151,194,96, +111,39,241,215,253,228,68,102,194,236,102,203,51,46,91,30,70,194,96,95,228,185,137,135,98,174,233,158,185,48,56,228,29,27,122, +113,242,156,23,73,106,63,12,98,29,173,242,223,125,122,180,19,6,203,137,27,37,152,212,138,182,143,15,54,6,96,60,202,130,236,11, +187,30,198,162,116,124,170,91,113,34,83,50,19,41,192,54,56,197,194,206,26,246,83,30,168,99,143,177,227,254,178,83,60,253,14,22, +212,3,78,177,126,233,244,10,30,55,118,220,55,79,219,187,216,73,167,39,105,129,178,248,121,155,175,191,102,254,100,90,39,121, +146,220,130,165,254,54,13,117,206,42,168,239,200,57,155,210,158,220,244,205,139,204,239,4,217,143,249,189,96,96,227,110,200,247, +172,220,15,114,118,228,119,132,141,141,123,66,85,178,182,220,21,170,148,157,11,114,190,22,42,89,124,185,63,12,237,35,231,138, +28,80,42,63,115,74,153,46,247,211,191,81,33,150,205,216,6,0,0,0,0}; //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (construct, "", "()V") \ METHOD (close, "close", "()V") \ - CALLBACK (FragmentOverlay::onActivityResultNative, "onActivityResultNative", "(JIILandroid/content/Intent;)V") \ - CALLBACK (FragmentOverlay::onCreateNative, "onCreateNative", "(JLandroid/os/Bundle;)V") \ - CALLBACK (FragmentOverlay::onStartNative, "onStartNative", "(J)V") \ - CALLBACK (FragmentOverlay::onRequestPermissionsResultNative, "onRequestPermissionsResultNative", "(JI[Ljava/lang/String;[I)V") + CALLBACK (generatedCallback<&FragmentOverlay::onActivityResultCallback>, "onActivityResultNative", "(JIILandroid/content/Intent;)V") \ + CALLBACK (generatedCallback<&FragmentOverlay::onCreatedCallback>, "onCreateNative", "(JLandroid/os/Bundle;)V") \ + CALLBACK (generatedCallback<&FragmentOverlay::onStartCallback>, "onStartNative", "(J)V") \ + CALLBACK (generatedCallback<&FragmentOverlay::onRequestPermissionsResultCallback>, "onRequestPermissionsResultNative", "(JI[Ljava/lang/String;[I)V") - DECLARE_JNI_CLASS_WITH_BYTECODE (JuceFragmentOverlay, "com/roli/juce/FragmentOverlay", 16, javaFragmentOverlay, sizeof(javaFragmentOverlay)) + DECLARE_JNI_CLASS_WITH_BYTECODE (JuceFragmentOverlay, "com/rmsl/juce/FragmentOverlay", 16, javaFragmentOverlay) #undef JNI_CLASS_MEMBERS #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ @@ -566,47 +590,39 @@ void FragmentOverlay::open() env->CallVoidMethod (native.get(), AndroidDialogFragment.show, fm.get(), javaString ("FragmentOverlay").get()); } -void FragmentOverlay::onActivityResultNative (JNIEnv* env, jobject, jlong host, - jint requestCode, jint resultCode, jobject data) +void FragmentOverlay::onCreatedCallback (JNIEnv* env, FragmentOverlay& t, jobject obj) { - if (auto* myself = reinterpret_cast (host)) - myself->onActivityResult (requestCode, resultCode, LocalRef (env->NewLocalRef (data))); + t.onCreated (LocalRef { env->NewLocalRef (obj) }); } -void FragmentOverlay::onCreateNative (JNIEnv* env, jobject, jlong host, jobject bundle) +void FragmentOverlay::onStartCallback (JNIEnv*, FragmentOverlay& t) { - if (auto* myself = reinterpret_cast (host)) - myself->onCreated (LocalRef (env->NewLocalRef (bundle))); + t.onStart(); } -void FragmentOverlay::onStartNative (JNIEnv*, jobject, jlong host) +void FragmentOverlay::onRequestPermissionsResultCallback (JNIEnv* env, FragmentOverlay& t, jint requestCode, jobjectArray jPermissions, jintArray jGrantResults) { - if (auto* myself = reinterpret_cast (host)) - myself->onStart(); -} + Array grantResults; + int n = (jGrantResults != nullptr ? env->GetArrayLength (jGrantResults) : 0); -void FragmentOverlay::onRequestPermissionsResultNative (JNIEnv* env, jobject, jlong host, jint requestCode, - jobjectArray jPermissions, jintArray jGrantResults) -{ - if (auto* myself = reinterpret_cast (host)) + if (n > 0) { - Array grantResults; - int n = (jGrantResults != nullptr ? env->GetArrayLength (jGrantResults) : 0); + auto* data = env->GetIntArrayElements (jGrantResults, nullptr); - if (n > 0) - { - auto* data = env->GetIntArrayElements (jGrantResults, nullptr); + for (int i = 0; i < n; ++i) + grantResults.add (data[i]); - for (int i = 0; i < n; ++i) - grantResults.add (data[i]); + env->ReleaseIntArrayElements (jGrantResults, data, 0); + } - env->ReleaseIntArrayElements (jGrantResults, data, 0); - } + t.onRequestPermissionsResult (requestCode, + javaStringArrayToJuce (LocalRef (jPermissions)), + grantResults); +} - myself->onRequestPermissionsResult (requestCode, - javaStringArrayToJuce (LocalRef (jPermissions)), - grantResults); - } +void FragmentOverlay::onActivityResultCallback (JNIEnv* env, FragmentOverlay& t, jint requestCode, jint resultCode, jobject data) +{ + t.onActivityResult (requestCode, resultCode, LocalRef (env->NewLocalRef (data))); } jobject FragmentOverlay::getNativeHandle() @@ -615,41 +631,17 @@ jobject FragmentOverlay::getNativeHandle() } //============================================================================== -class ActivityLauncher : public FragmentOverlay +void startAndroidActivityForResult (const LocalRef& intent, + int requestCode, + std::function)>&& callback) { -public: - ActivityLauncher (const LocalRef& intentToUse, - int requestCodeToUse, - std::function)> && callbackToUse) - : intent (intentToUse), requestCode (requestCodeToUse), callback (std::move (callbackToUse)) - {} - - void onStart() override - { - getEnv()->CallVoidMethod (getNativeHandle(), AndroidFragment.startActivityForResult, - intent.get(), requestCode); - } - - void onActivityResult (int activityRequestCode, int resultCode, LocalRef data) override + auto* launcher = new ActivityLauncher (intent, requestCode); + launcher->callback = [launcher, c = std::move (callback)] (auto&&... args) { - if (callback) - callback (activityRequestCode, resultCode, std::move (data)); - - getEnv()->CallVoidMethod (getNativeHandle(), JuceFragmentOverlay.close); - delete this; - } - -private: - GlobalRef intent; - int requestCode; - std::function)> callback; -}; - -void startAndroidActivityForResult (const LocalRef& intent, int requestCode, - std::function)> && callback) -{ - auto* activityLauncher = new ActivityLauncher (intent, requestCode, std::move (callback)); - activityLauncher->open(); + NullCheckedInvocation::invoke (c, args...); + delete launcher; + }; + launcher->open(); } //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h b/JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.h similarity index 76% rename from JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h rename to JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.h index 922fba2e..876584ad 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -31,11 +31,18 @@ template class LocalRef { public: - 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 (nullptr) { std::swap (obj, other.obj); } - ~LocalRef() { clear(); } + LocalRef() noexcept = default; + + /* This constructor must not be used to wrap local references that were not created through + JNI, i.e. for native function callback parameters. + */ + explicit LocalRef (JavaType o) noexcept + : LocalRef (o, false) + {} + + LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {} + LocalRef (LocalRef&& other) noexcept : obj (nullptr) { std::swap (obj, other.obj); } + ~LocalRef() { clear(); } void clear() { @@ -48,45 +55,97 @@ class LocalRef LocalRef& operator= (const LocalRef& other) { - JavaType newObj = retain (other.obj); - clear(); - obj = newObj; + auto tmp = other; + std::swap (tmp.obj, obj); return *this; } - LocalRef& operator= (LocalRef&& other) + LocalRef& operator= (LocalRef&& other) noexcept { - clear(); - std::swap (other.obj, obj); + auto tmp = std::move (other); + std::swap (tmp.obj, obj); return *this; } - inline operator JavaType() const noexcept { return obj; } - inline JavaType get() const noexcept { return obj; } + bool operator== (std::nullptr_t) const noexcept { return obj == nullptr; } + bool operator!= (std::nullptr_t) const noexcept { return obj != nullptr; } -private: - JavaType obj; + operator JavaType() const noexcept { return obj; } + JavaType get() const noexcept { return obj; } + + auto release() + { + return std::exchange (obj, nullptr); + } + + /** Creates a new internal local reference. */ + static auto addOwner (JavaType o) + { + return LocalRef { o, true }; + } + + /** Takes ownership of the passed in local reference, and deletes it when the LocalRef goes out + of scope. + */ + static auto becomeOwner (JavaType o) + { + return LocalRef { o, false }; + } + +private: static JavaType retain (JavaType obj) { return obj == nullptr ? nullptr : (JavaType) getEnv()->NewLocalRef (obj); } + + /* We cannot delete local references that were not created by JNI, e.g. references that were + created by the VM and passed into the native function. + + For these references we should use createNewLocalRef = true, which will create a new + local reference that this wrapper is allowed to delete. + + Doing otherwise will result in an "Attempt to remove non-JNI local reference" warning in the + VM, which could even cause crashes in future VM implementations. + */ + LocalRef (JavaType o, bool createNewLocalRef) noexcept + : obj (createNewLocalRef ? retain (o) : o) + {} + + JavaType obj = nullptr; }; -//============================================================================== -class GlobalRef +/* Creates a new local reference that shares ownership with the passed in pointer. + + Can be used for wrapping function parameters that were created outside the JNI. +*/ +template +auto addLocalRefOwner (JavaType t) { -public: - 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 (nullptr) { std::swap (other.obj, obj); } - ~GlobalRef() { clear(); } + return LocalRef::addOwner (t); +} +/* Wraps a local reference and destroys it when it goes out of scope. */ +template +auto becomeLocalRefOwner (JavaType t) +{ + return LocalRef::becomeOwner (t); +} - inline void clear() { if (obj != nullptr) clear (getEnv()); } - inline void clear (JNIEnv* env) +//============================================================================== +template +class GlobalRefImpl +{ +public: + GlobalRefImpl() noexcept : obj (nullptr) {} + explicit GlobalRefImpl (const LocalRef& o) : obj (retain (o.get(), getEnv())) {} + GlobalRefImpl (const LocalRef& o, JNIEnv* env) : obj (retain (o.get(), env)) {} + GlobalRefImpl (const GlobalRefImpl& other) : obj (retain (other.obj, getEnv())) {} + GlobalRefImpl (GlobalRefImpl&& other) noexcept : obj (nullptr) { std::swap (other.obj, obj); } + ~GlobalRefImpl() { clear(); } + + void clear() { if (obj != nullptr) clear (getEnv()); } + void clear (JNIEnv* env) { if (obj != nullptr) { @@ -95,15 +154,15 @@ class GlobalRef } } - inline GlobalRef& operator= (const GlobalRef& other) + GlobalRefImpl& operator= (const GlobalRefImpl& other) { - jobject newObj = retain (other.obj, getEnv()); + JavaType newObj = retain (other.obj, getEnv()); clear(); obj = newObj; return *this; } - inline GlobalRef& operator= (GlobalRef&& other) + GlobalRefImpl& operator= (GlobalRefImpl&& other) { clear(); std::swap (obj, other.obj); @@ -112,8 +171,8 @@ class GlobalRef } //============================================================================== - inline operator jobject() const noexcept { return obj; } - inline jobject get() const noexcept { return obj; } + operator JavaType() const noexcept { return obj; } + JavaType get() const noexcept { return obj; } //============================================================================== #define DECLARE_CALL_TYPE_METHOD(returnType, typeName) \ @@ -147,14 +206,20 @@ class GlobalRef private: //============================================================================== - jobject obj = nullptr; + JavaType obj = nullptr; - static inline jobject retain (jobject obj, JNIEnv* env) + static JavaType retain (JavaType obj, JNIEnv* env) { - return obj == nullptr ? nullptr : env->NewGlobalRef (obj); + return obj != nullptr ? static_cast (env->NewGlobalRef (obj)) + : nullptr; } }; +class GlobalRef : public GlobalRefImpl +{ +public: + using GlobalRefImpl::GlobalRefImpl; +}; //============================================================================== extern LocalRef getAppContext() noexcept; @@ -166,15 +231,15 @@ struct SystemJavaClassComparator; class JNIClassBase { public: - explicit JNIClassBase (const char* classPath, int minSDK, const void* byteCode, size_t byteCodeSize); + JNIClassBase (const char* classPath, int minSDK, const uint8* byteCode, size_t byteCodeSize); virtual ~JNIClassBase(); - inline operator jclass() const noexcept { return classRef; } + operator jclass() const noexcept { return classRef; } - static void initialiseAllClasses (JNIEnv*); + static void initialiseAllClasses (JNIEnv*, jobject context); static void releaseAllClasses (JNIEnv*); - inline const char* getClassPath() const noexcept { return classPath; } + const char* getClassPath() const noexcept { return classPath; } protected: virtual void initialiseFields (JNIEnv*) = 0; @@ -196,7 +261,7 @@ class JNIClassBase jclass classRef = nullptr; static Array& getClasses(); - void initialise (JNIEnv*); + void initialise (JNIEnv*, jobject context); void release (JNIEnv*); void tryLoadingClassWithClassLoader (JNIEnv* env, jobject classLoader); @@ -204,6 +269,9 @@ class JNIClassBase }; //============================================================================== +template constexpr auto numBytes (const T (&) [N]) { return N; } + constexpr auto numBytes (std::nullptr_t) { return static_cast (0); } + #define CREATE_JNI_METHOD(methodID, stringName, params) methodID = resolveMethod (env, stringName, params); #define CREATE_JNI_STATICMETHOD(methodID, stringName, params) methodID = resolveStaticMethod (env, stringName, params); #define CREATE_JNI_FIELD(fieldID, stringName, signature) fieldID = resolveField (env, stringName, signature); @@ -213,26 +281,26 @@ class JNIClassBase #define DECLARE_JNI_FIELD(fieldID, stringName, signature) jfieldID fieldID; #define DECLARE_JNI_CALLBACK(fieldID, stringName, signature) -#define DECLARE_JNI_CLASS_WITH_BYTECODE(CppClassName, javaPath, minSDK, byteCodeData, byteCodeSize) \ - class CppClassName ## _Class : public JNIClassBase \ - { \ - public: \ - CppClassName ## _Class() : JNIClassBase (javaPath, minSDK, byteCodeData, byteCodeSize) {} \ - \ - void initialiseFields (JNIEnv* env) \ - { \ - Array callbacks; \ - JNI_CLASS_MEMBERS (CREATE_JNI_METHOD, CREATE_JNI_STATICMETHOD, CREATE_JNI_FIELD, CREATE_JNI_STATICFIELD, CREATE_JNI_CALLBACK); \ - resolveCallbacks (env, callbacks); \ - } \ - \ - JNI_CLASS_MEMBERS (DECLARE_JNI_METHOD, DECLARE_JNI_METHOD, DECLARE_JNI_FIELD, DECLARE_JNI_FIELD, DECLARE_JNI_CALLBACK) \ - }; \ - static CppClassName ## _Class CppClassName; +#define DECLARE_JNI_CLASS_WITH_BYTECODE(CppClassName, javaPath, minSDK, byteCodeData) \ + class CppClassName ## _Class : public JNIClassBase \ + { \ + public: \ + CppClassName ## _Class() : JNIClassBase (javaPath, minSDK, byteCodeData, numBytes (byteCodeData)) {} \ + \ + void initialiseFields (JNIEnv* env) \ + { \ + Array callbacks; \ + JNI_CLASS_MEMBERS (CREATE_JNI_METHOD, CREATE_JNI_STATICMETHOD, CREATE_JNI_FIELD, CREATE_JNI_STATICFIELD, CREATE_JNI_CALLBACK); \ + resolveCallbacks (env, callbacks); \ + } \ + \ + JNI_CLASS_MEMBERS (DECLARE_JNI_METHOD, DECLARE_JNI_METHOD, DECLARE_JNI_FIELD, DECLARE_JNI_FIELD, DECLARE_JNI_CALLBACK) \ + }; \ + static inline const CppClassName ## _Class CppClassName; //============================================================================== #define DECLARE_JNI_CLASS_WITH_MIN_SDK(CppClassName, javaPath, minSDK) \ - DECLARE_JNI_CLASS_WITH_BYTECODE (CppClassName, javaPath, minSDK, nullptr, 0) + DECLARE_JNI_CLASS_WITH_BYTECODE (CppClassName, javaPath, minSDK, nullptr) //============================================================================== #define DECLARE_JNI_CLASS(CppClassName, javaPath) \ @@ -252,7 +320,9 @@ class JNIClassBase METHOD (getApplicationContext, "getApplicationContext", "()Landroid/content/Context;") \ METHOD (getApplicationInfo, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;") \ METHOD (checkCallingOrSelfPermission, "checkCallingOrSelfPermission", "(Ljava/lang/String;)I") \ - METHOD (getCacheDir, "getCacheDir", "()Ljava/io/File;") + METHOD (checkCallingOrSelfUriPermission, "checkCallingOrSelfUriPermission", "(Landroid/net/Uri;I)I") \ + METHOD (getCacheDir, "getCacheDir", "()Ljava/io/File;") \ + METHOD (registerReceiver, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;") \ DECLARE_JNI_CLASS (AndroidContext, "android/content/Context") #undef JNI_CLASS_MEMBERS @@ -351,7 +421,7 @@ DECLARE_JNI_CLASS (AndroidBundle, "android/os/Bundle") #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ STATICMETHOD (dumpReferenceTables, "dumpReferenceTables", "()V") - DECLARE_JNI_CLASS (AndroidDebug, "android/os/Debug") +DECLARE_JNI_CLASS (AndroidDebug, "android/os/Debug") #undef JNI_CLASS_MEMBERS #define JUCE_LOG_JNI_REFERENCES_TABLE getEnv()->CallStaticVoidMethod (AndroidDebug, AndroidDebug.dumpReferenceTables); @@ -364,6 +434,12 @@ DECLARE_JNI_CLASS (AndroidBundle, "android/os/Bundle") DECLARE_JNI_CLASS (AndroidDisplay, "android/view/Display") #undef JNI_CLASS_MEMBERS +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + METHOD (getRealMetrics, "getRealMetrics", "(Landroid/util/DisplayMetrics;)V") + +DECLARE_JNI_CLASS (AndroidDisplay17, "android/view/Display") +#undef JNI_CLASS_MEMBERS + #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (constructor, "", "()V") \ METHOD (constructorWithLooper, "", "(Landroid/os/Looper;)V") \ @@ -392,6 +468,7 @@ DECLARE_JNI_CLASS (AndroidHandlerThread, "android/os/HandlerThread") METHOD (getAction, "getAction", "()Ljava/lang/String;") \ METHOD (getCategories, "getCategories", "()Ljava/util/Set;") \ METHOD (getData, "getData", "()Landroid/net/Uri;") \ + METHOD (getClipData, "getClipData", "()Landroid/content/ClipData;") \ METHOD (getExtras, "getExtras", "()Landroid/os/Bundle;") \ METHOD (getIntExtra, "getIntExtra", "(Ljava/lang/String;I)I") \ METHOD (getStringExtra, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;") \ @@ -400,6 +477,8 @@ DECLARE_JNI_CLASS (AndroidHandlerThread, "android/os/HandlerThread") METHOD (putExtraString, "putExtra", "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;") \ METHOD (putExtraStrings, "putExtra", "(Ljava/lang/String;[Ljava/lang/String;)Landroid/content/Intent;") \ METHOD (putExtraParcelable, "putExtra", "(Ljava/lang/String;Landroid/os/Parcelable;)Landroid/content/Intent;") \ + METHOD (putExtraBool, "putExtra", "(Ljava/lang/String;Z)Landroid/content/Intent;") \ + METHOD (putExtraInt, "putExtra", "(Ljava/lang/String;I)Landroid/content/Intent;") \ METHOD (putParcelableArrayListExtra, "putParcelableArrayListExtra", "(Ljava/lang/String;Ljava/util/ArrayList;)Landroid/content/Intent;") \ METHOD (setAction, "setAction", "(Ljava/lang/String;)Landroid/content/Intent;") \ METHOD (setFlags, "setFlags", "(I)Landroid/content/Intent;") \ @@ -409,6 +488,12 @@ DECLARE_JNI_CLASS (AndroidHandlerThread, "android/os/HandlerThread") DECLARE_JNI_CLASS (AndroidIntent, "android/content/Intent") #undef JNI_CLASS_MEMBERS +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + STATICMETHOD (createChooser, "createChooser", "(Landroid/content/Intent;Ljava/lang/CharSequence;Landroid/content/IntentSender;)Landroid/content/Intent;") \ + +DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidIntent22, "android/content/Intent", 22) +#undef JNI_CLASS_MEMBERS + #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (constructor, "", "()V") \ METHOD (postRotate, "postRotate", "(FFF)Z") \ @@ -472,7 +557,8 @@ DECLARE_JNI_CLASS (AndroidPaint, "android/graphics/Paint") #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ STATICMETHOD (getActivity, "getActivity", "(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;") \ - METHOD (getIntentSender, "getIntentSender", "()Landroid/content/IntentSender;") + STATICMETHOD (getBroadcast, "getBroadcast", "(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;") \ + METHOD (getIntentSender, "getIntentSender", "()Landroid/content/IntentSender;") \ DECLARE_JNI_CLASS (AndroidPendingIntent, "android/app/PendingIntent") #undef JNI_CLASS_MEMBERS @@ -503,11 +589,18 @@ DECLARE_JNI_CLASS (AndroidRect, "android/graphics/Rect") #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (getIdentifier, "getIdentifier", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I") \ - METHOD (openRawResourceFd, "openRawResourceFd", "(I)Landroid/content/res/AssetFileDescriptor;") + METHOD (openRawResourceFd, "openRawResourceFd", "(I)Landroid/content/res/AssetFileDescriptor;") \ + METHOD (getConfiguration, "getConfiguration", "()Landroid/content/res/Configuration;") DECLARE_JNI_CLASS (AndroidResources, "android/content/res/Resources") #undef JNI_CLASS_MEMBERS +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + FIELD (uiMode, "uiMode", "I") \ + +DECLARE_JNI_CLASS (AndroidConfiguration, "android/content/res/Configuration") +#undef JNI_CLASS_MEMBERS + #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (getHeight, "getHeight", "()I") \ METHOD (getWidth, "getWidth", "()I") @@ -540,18 +633,35 @@ DECLARE_JNI_CLASS (AndroidUri, "android/net/Uri") 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") + METHOD (addOnLayoutChangeListener, "addOnLayoutChangeListener", "(Landroid/view/View$OnLayoutChangeListener;)V") \ + METHOD (announceForAccessibility, "announceForAccessibility", "(Ljava/lang/CharSequence;)V") \ DECLARE_JNI_CLASS (AndroidView, "android/view/View") #undef JNI_CLASS_MEMBERS #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ - METHOD (addView, "addView", "(Landroid/view/View;)V") \ - METHOD (removeView, "removeView", "(Landroid/view/View;)V") + METHOD (setOnApplyWindowInsetsListener, "setOnApplyWindowInsetsListener", "(Landroid/view/View$OnApplyWindowInsetsListener;)V") \ + METHOD (getRootWindowInsets, "getRootWindowInsets", "()Landroid/view/WindowInsets;") + + DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidView23, "android/view/View", 23) +#undef JNI_CLASS_MEMBERS + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + METHOD (addView, "addView", "(Landroid/view/View;)V") \ + METHOD (removeView, "removeView", "(Landroid/view/View;)V") \ + METHOD (requestSendAccessibilityEvent, "requestSendAccessibilityEvent", "(Landroid/view/View;Landroid/view/accessibility/AccessibilityEvent;)Z") \ DECLARE_JNI_CLASS (AndroidViewGroup, "android/view/ViewGroup") #undef JNI_CLASS_MEMBERS +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + METHOD (getDecorView, "getDecorView", "()Landroid/view/View;") \ + METHOD (setFlags, "setFlags", "(II)V") \ + METHOD (clearFlags, "clearFlags", "(I)V") + +DECLARE_JNI_CLASS (AndroidWindow, "android/view/Window") +#undef JNI_CLASS_MEMBERS + #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (getDefaultDisplay, "getDefaultDisplay", "()Landroid/view/Display;") @@ -579,6 +689,9 @@ DECLARE_JNI_CLASS (JavaBoolean, "java/lang/Boolean") #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (get, "get", "([B)Ljava/nio/ByteBuffer;") \ METHOD (remaining, "remaining", "()I") \ + METHOD (hasArray, "hasArray", "()Z") \ + METHOD (array, "array", "()[B") \ + METHOD (setOrder, "order", "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;") \ STATICMETHOD (wrap, "wrap", "([B)Ljava/nio/ByteBuffer;") DECLARE_JNI_CLASS (JavaByteBuffer, "java/nio/ByteBuffer") @@ -650,7 +763,8 @@ DECLARE_JNI_CLASS (JavaHashMap, "java/util/HashMap") #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ STATICMETHOD (parseInt, "parseInt", "(Ljava/lang/String;I)I") \ STATICMETHOD (valueOf, "valueOf", "(I)Ljava/lang/Integer;") \ - METHOD (intValue, "intValue", "()I") + METHOD (constructor, "", "(I)V") \ + METHOD (intValue, "intValue", "()I") DECLARE_JNI_CLASS (JavaInteger, "java/lang/Integer") #undef JNI_CLASS_MEMBERS @@ -695,7 +809,6 @@ DECLARE_JNI_CLASS (JavaMap, "java/util/Map") DECLARE_JNI_CLASS (JavaMethod, "java/lang/reflect/Method") #undef JNI_CLASS_MEMBERS - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (constructor, "", "()V") \ METHOD (getClass, "getClass", "()Ljava/lang/Class;") \ @@ -731,7 +844,7 @@ DECLARE_JNI_CLASS (AndroidBuildVersion, "android/os/Build$VERSION") METHOD (registerActivityLifecycleCallbacks, "registerActivityLifecycleCallbacks", "(Landroid/app/Application$ActivityLifecycleCallbacks;)V") \ METHOD (unregisterActivityLifecycleCallbacks, "unregisterActivityLifecycleCallbacks", "(Landroid/app/Application$ActivityLifecycleCallbacks;)V") - DECLARE_JNI_CLASS (AndroidApplication, "android/app/Application") +DECLARE_JNI_CLASS (AndroidApplication, "android/app/Application") #undef JNI_CLASS_MEMBERS #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ @@ -739,7 +852,7 @@ DECLARE_JNI_CLASS (AndroidBuildVersion, "android/os/Build$VERSION") METHOD (getHolder, "getHolder", "()Landroid/view/SurfaceHolder;") \ METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") - DECLARE_JNI_CLASS (AndroidSurfaceView, "android/view/SurfaceView") +DECLARE_JNI_CLASS (AndroidSurfaceView, "android/view/SurfaceView") #undef JNI_CLASS_MEMBERS @@ -748,7 +861,7 @@ DECLARE_JNI_CLASS (AndroidBuildVersion, "android/os/Build$VERSION") METHOD (addCallback, "addCallback", "(Landroid/view/SurfaceHolder$Callback;)V") \ METHOD (removeCallback, "removeCallback", "(Landroid/view/SurfaceHolder$Callback;)V") - DECLARE_JNI_CLASS (AndroidSurfaceHolder, "android/view/SurfaceHolder") +DECLARE_JNI_CLASS (AndroidSurfaceHolder, "android/view/SurfaceHolder") #undef JNI_CLASS_MEMBERS //============================================================================== @@ -792,7 +905,7 @@ namespace javaString ("").get())); for (int i = 0; i < juceArray.size(); ++i) - env->SetObjectArrayElement (result, i, javaString (juceArray [i]).get()); + env->SetObjectArrayElement (result.get(), i, javaString (juceArray [i]).get()); return result; } @@ -819,15 +932,12 @@ namespace { auto* env = getEnv(); - LocalRef exception (env->ExceptionOccurred()); - - if (exception != nullptr) - { - env->ExceptionClear(); - return true; - } - - return false; + const auto result = env->ExceptionCheck(); + #if JUCE_DEBUG + env->ExceptionDescribe(); + #endif + env->ExceptionClear(); + return result; } } @@ -899,6 +1009,8 @@ class ActivityLifecycleCallbacks : public AndroidInterfaceImplementer virtual void onActivityPostStarted (jobject /*activity*/) {} virtual void onActivityPostStopped (jobject /*activity*/) {} + virtual void onActivityConfigurationChanged (jobject /*activity*/) {} + private: jobject invoke (jobject, jobject, jobjectArray) override; }; @@ -964,29 +1076,82 @@ class FragmentOverlay const Array& /*grantResults*/) {} virtual void onActivityResult (int /*requestCode*/, int /*resultCode*/, LocalRef /*data*/) {} + /** @internal */ + static void onCreatedCallback (JNIEnv*, FragmentOverlay&, jobject obj); + /** @internal */ + static void onStartCallback (JNIEnv*, FragmentOverlay&); + /** @internal */ + static void onRequestPermissionsResultCallback (JNIEnv*, FragmentOverlay&, jint requestCode, jobjectArray jPermissions, jintArray jGrantResults); + /** @internal */ + static void onActivityResultCallback (JNIEnv*, FragmentOverlay&, jint requestCode, jint resultCode, jobject data); + protected: jobject getNativeHandle(); private: - GlobalRef native; - -public: - /* internal: do not use */ - static void onActivityResultNative (JNIEnv*, jobject, jlong, jint, jint, jobject); - static void onCreateNative (JNIEnv*, jobject, jlong, jobject); - static void onStartNative (JNIEnv*, jobject, jlong); - static void onRequestPermissionsResultNative (JNIEnv*, jobject, jlong, jint, - jobjectArray, jintArray); }; //============================================================================== // Allows you to start an activity without requiring to have an activity -void startAndroidActivityForResult (const LocalRef& intent, int requestCode, - std::function)> && callback); +void startAndroidActivityForResult (const LocalRef& intent, + int requestCode, + std::function)>&& callback); -//============================================================================== +class ActivityLauncher : public FragmentOverlay +{ +public: + ActivityLauncher (const LocalRef& intentToUse, int requestCodeToUse) + : intent (intentToUse), requestCode (requestCodeToUse) + {} + + void onStart() override + { + if (! std::exchange (activityHasStarted, true)) + getEnv()->CallVoidMethod (getNativeHandle(), AndroidFragment.startActivityForResult, intent.get(), requestCode); + } + + void onActivityResult (int activityRequestCode, int resultCode, LocalRef data) override + { + NullCheckedInvocation::invoke (callback, activityRequestCode, resultCode, std::move (data)); + } + + std::function)> callback; + +private: + GlobalRef intent; + int requestCode; + bool activityHasStarted = false; +}; + + //============================================================================== bool androidHasSystemFeature (const String& property); String audioManagerGetProperty (const String& property); +namespace detail +{ + +template +inline constexpr auto generatedCallbackImpl = + juce::toFnPtr (JNICALL [] (JNIEnv* env, jobject, jlong host, Args... args) -> Result + { + if (auto* object = reinterpret_cast (host)) + return Fn (env, *object, args...); + + return {}; + }); + +template +constexpr auto generateCallbackImpl (Result (*) (JNIEnv*, Class&, Args...)) { return generatedCallbackImpl; } + +template +constexpr auto generateCallbackImpl (Result (*) (JNIEnv*, const Class&, Args...)) { return generatedCallbackImpl; } + +} // namespace detail + +// Evaluates to a static function that forwards to the provided Fn, assuming that the +// 'host' argument points to an object on which it is valid to call Fn +template +inline constexpr auto generatedCallback = detail::generateCallbackImpl (Fn); + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Misc_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Misc_android.cpp new file mode 100644 index 00000000..b4babf07 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_Misc_android.cpp @@ -0,0 +1,44 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +void Logger::outputDebugString (const String& text) +{ + char* data = text.toUTF8().getAddress(); + const size_t length = CharPointer_UTF8::getBytesRequiredFor (text.getCharPointer()); + const size_t chunkSize = 1023; + + size_t position = 0; + size_t numToRead = jmin (chunkSize, length); + + while (numToRead > 0) + { + __android_log_print (ANDROID_LOG_INFO, "JUCE", "%s", data + position); + + position += numToRead; + numToRead = jmin (chunkSize, length - position); + } +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp b/JuceLibraryCode/modules/juce_core/native/juce_NamedPipe_posix.cpp similarity index 62% rename from JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_NamedPipe_posix.cpp index ecd859eb..590cbe8a 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_NamedPipe_posix.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,8 @@ namespace juce { +#if ! JUCE_WASM + class NamedPipe::Pimpl { public: @@ -37,8 +39,8 @@ class NamedPipe::Pimpl ~Pimpl() { - if (pipeIn != -1) ::close (pipeIn); - if (pipeOut != -1) ::close (pipeOut); + pipeIn .close(); + pipeOut.close(); if (createdPipe) { @@ -49,7 +51,7 @@ class NamedPipe::Pimpl bool connect (int timeOutMilliseconds) { - return openPipe (true, getTimeoutEnd (timeOutMilliseconds)); + return openPipe (true, getTimeoutEnd (timeOutMilliseconds)) != invalidPipe; } int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds) @@ -59,18 +61,22 @@ class NamedPipe::Pimpl while (bytesRead < maxBytesToRead) { + const auto pipe = pipeIn.get(); + auto bytesThisTime = maxBytesToRead - bytesRead; - auto numRead = (int) ::read (pipeIn, destBuffer, (size_t) bytesThisTime); + auto numRead = (int) ::read (pipe, destBuffer, (size_t) bytesThisTime); if (numRead <= 0) { - if (errno != EWOULDBLOCK || stopReadOperation.load() || hasExpired (timeoutEnd)) + const auto error = errno; + + if (! (error == EWOULDBLOCK || error == EAGAIN) || stopReadOperation.load() || hasExpired (timeoutEnd)) return -1; const int maxWaitingTime = 30; - waitForInput (pipeIn, timeoutEnd == 0 ? maxWaitingTime - : jmin (maxWaitingTime, - (int) (timeoutEnd - Time::getMillisecondCounter()))); + waitForInput (pipe, timeoutEnd == 0 ? maxWaitingTime + : jmin (maxWaitingTime, + (int) (timeoutEnd - Time::getMillisecondCounter()))); continue; } @@ -85,7 +91,9 @@ class NamedPipe::Pimpl { auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds); - if (! openPipe (false, timeoutEnd)) + const auto pipe = openPipe (false, timeoutEnd); + + if (pipe == invalidPipe) return -1; int bytesWritten = 0; @@ -93,10 +101,22 @@ class NamedPipe::Pimpl while (bytesWritten < numBytesToWrite && ! hasExpired (timeoutEnd)) { auto bytesThisTime = numBytesToWrite - bytesWritten; - auto numWritten = (int) ::write (pipeOut, sourceBuffer, (size_t) bytesThisTime); + auto numWritten = (int) ::write (pipe, sourceBuffer, (size_t) bytesThisTime); - if (numWritten <= 0) - return -1; + if (numWritten < 0) + { + const auto error = errno; + const int maxWaitingTime = 30; + + if (error == EWOULDBLOCK || error == EAGAIN) + waitToWrite (pipe, timeoutEnd == 0 ? maxWaitingTime + : jmin (maxWaitingTime, + (int) (timeoutEnd - Time::getMillisecondCounter()))); + else + return -1; + + numWritten = 0; + } bytesWritten += numWritten; sourceBuffer += numWritten; @@ -118,8 +138,52 @@ class NamedPipe::Pimpl return createdFifoIn && createdFifoOut; } + static constexpr auto invalidPipe = -1; + + class PipeDescriptor + { + public: + template + int get (Fn&& fn) + { + { + const ScopedReadLock l (mutex); + + if (descriptor != invalidPipe) + return descriptor; + } + + const ScopedWriteLock l (mutex); + return descriptor = fn(); + } + + void close() + { + { + const ScopedReadLock l (mutex); + + if (descriptor == invalidPipe) + return; + } + + const ScopedWriteLock l (mutex); + ::close (descriptor); + descriptor = invalidPipe; + } + + int get() + { + const ScopedReadLock l (mutex); + return descriptor; + } + + private: + ReadWriteLock mutex; + int descriptor = invalidPipe; + }; + const String pipeInName, pipeOutName; - int pipeIn = -1, pipeOut = -1; + PipeDescriptor pipeIn, pipeOut; bool createdFifoIn = false, createdFifoOut = false; const bool createdPipe; @@ -144,30 +208,25 @@ class NamedPipe::Pimpl { auto p = ::open (name.toUTF8(), flags); - if (p != -1 || hasExpired (timeoutEnd) || stopReadOperation.load()) + if (p != invalidPipe || hasExpired (timeoutEnd) || stopReadOperation.load()) return p; Thread::sleep (2); } } - bool openPipe (bool isInput, uint32 timeoutEnd) + int openPipe (bool isInput, uint32 timeoutEnd) { auto& pipe = isInput ? pipeIn : pipeOut; - int flags = isInput ? O_RDWR | O_NONBLOCK : O_WRONLY; + const auto flags = (isInput ? O_RDWR : O_WRONLY) | O_NONBLOCK; const String& pipeName = isInput ? (createdPipe ? pipeInName : pipeOutName) : (createdPipe ? pipeOutName : pipeInName); - if (pipe == -1) + return pipe.get ([this, &pipeName, &flags, &timeoutEnd] { - pipe = openPipe (pipeName, flags, timeoutEnd); - - if (pipe == -1) - return false; - } - - return true; + return openPipe (pipeName, flags, timeoutEnd); + }); } static void waitForInput (int handle, int timeoutMsecs) noexcept @@ -176,20 +235,31 @@ class NamedPipe::Pimpl poll (&pfd, 1, timeoutMsecs); } + static void waitToWrite (int handle, int timeoutMsecs) noexcept + { + pollfd pfd { handle, POLLOUT, 0 }; + poll (&pfd, 1, timeoutMsecs); + } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; void NamedPipe::close() { - if (pimpl != nullptr) { - pimpl->stopReadOperation = true; + const ScopedReadLock sl (lock); + + if (pimpl != nullptr) + { + pimpl->stopReadOperation = true; - char buffer[1] = { 0 }; - ssize_t done = ::write (pimpl->pipeIn, buffer, 1); - ignoreUnused (done); + const char buffer[] { 0 }; + [[maybe_unused]] const auto done = ::write (pimpl->pipeIn.get(), buffer, numElementsInArray (buffer)); + } + } - ScopedWriteLock sl (lock); + { + const ScopedWriteLock sl (lock); pimpl.reset(); } } @@ -235,4 +305,6 @@ int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOut return pimpl != nullptr ? pimpl->write (static_cast (sourceBuffer), numBytesToWrite, timeOutMilliseconds) : -1; } +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Network_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Network_android.cpp new file mode 100644 index 00000000..ca7ac66f --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_Network_android.cpp @@ -0,0 +1,644 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 byte-code is generated from native/java/com/rmsl/juce/JuceHTTPStream.java with min sdk version 16 +// See juce_core/native/java/README.txt on how to generate this byte-code. +static const uint8 javaJuceHttpStream[] = +{31,139,8,8,71,116,161,94,0,3,106,97,118,97,74,117,99,101,72,116,116,112,83,116,114,101,97,109,46,100,101,120,0,125,154,11,124, +84,87,157,199,255,231,222,185,119,30,153,153,220,76,30,147,132,60,38,33,132,4,18,38,80,104,41,9,148,242,42,144,80,40,12,84,8, +91,29,50,23,50,48,185,19,102,238,64,168,180,133,62,233,67,75,223,184,86,173,72,45,186,85,171,86,173,182,174,109,105,93,31,181, +85,87,119,171,187,186,186,91,171,246,211,221,118,235,171,171,31,55,251,59,143,201,76,218,44,132,239,253,255,207,255,252,207, +185,231,241,63,143,153,36,101,79,4,250,46,88,66,151,94,114,238,181,147,179,244,212,201,205,225,159,190,152,158,247,165,219,39, +126,178,240,215,230,173,135,207,118,18,141,19,209,196,142,197,17,82,255,94,153,67,244,35,146,246,69,224,105,157,40,14,121,194, +67,84,15,249,148,73,116,5,228,81,47,17,178,200,19,32,90,211,68,148,130,124,171,150,232,15,224,109,240,23,240,191,64,171,35,50, +64,8,84,129,40,232,6,23,131,45,96,27,184,18,236,6,123,128,13,174,6,215,128,227,224,102,112,27,184,19,156,2,103,192,89,240,44, +120,5,52,70,137,150,129,171,192,13,224,19,224,155,224,87,128,161,193,49,112,17,184,28,236,5,199,192,3,224,81,240,34,120,5,188, +13,194,13,68,237,96,0,92,9,14,128,235,193,67,224,49,240,29,240,11,240,22,240,55,18,189,7,164,193,213,224,111,193,147,224,183, +160,102,22,234,0,87,129,163,224,195,224,39,96,18,244,96,156,222,3,70,192,94,176,31,56,32,15,222,15,142,129,59,192,73,112,15,120, +0,124,24,124,12,124,2,60,1,158,2,223,2,63,6,63,7,175,130,55,192,159,192,95,129,209,76,20,4,81,16,3,61,96,13,216,1,70,193,213, +224,14,240,17,240,48,120,18,156,3,47,129,87,193,95,128,217,130,62,2,11,52,130,14,48,31,44,2,23,129,77,224,111,128,3,174,5,55, +131,7,192,25,240,37,240,60,248,30,248,103,240,91,240,22,152,4,190,86,162,58,208,9,250,193,101,96,35,216,6,222,7,246,131,195,224, +24,56,1,238,3,31,3,15,131,207,128,47,130,175,130,23,193,207,192,107,224,45,240,103,64,49,244,29,84,130,57,160,31,236,0,123,193, +1,48,14,14,129,163,224,102,112,18,124,12,60,12,62,11,190,1,126,12,126,6,254,29,252,26,252,25,120,219,136,102,131,62,176,18, +108,5,123,193,24,112,193,81,112,2,124,8,60,2,62,7,190,6,190,13,126,14,126,7,244,118,196,5,104,4,237,160,23,44,7,151,129,4,200, +128,2,184,6,220,6,238,2,167,64,16,221,178,0,194,138,16,62,132,233,37,76,15,97,40,73,117,153,80,61,193,149,102,131,14,128,229, +75,88,214,52,23,116,129,110,48,15,204,7,61,160,23,44,32,185,166,251,192,66,181,206,47,0,139,193,18,112,33,184,8,44,5,23,131,126, +48,0,86,128,75,192,74,112,41,88,5,214,128,117,224,50,176,30,108,4,91,193,118,176,3,92,73,178,31,197,127,33,37,151,98,111,8, +43,125,101,153,190,30,122,165,210,183,212,202,254,51,149,230,251,143,1,118,195,30,41,171,151,235,197,242,13,74,119,149,79,177, +174,90,229,183,84,217,107,149,189,90,233,71,149,61,90,102,143,42,123,141,210,111,132,94,167,244,59,148,189,94,217,107,149,190, +84,233,13,101,58,159,183,123,107,101,57,174,63,168,222,213,92,214,254,150,50,189,181,76,111,47,211,103,151,245,133,207,239,163, +170,126,62,199,93,170,206,121,202,135,207,67,175,210,7,149,206,251,178,89,233,143,67,31,82,250,83,101,122,95,153,206,219,191, +73,233,207,65,223,162,116,62,254,151,43,253,133,50,159,127,173,149,103,67,175,26,255,98,61,175,212,202,152,88,160,218,179,77, +233,175,195,158,80,186,171,250,178,80,189,87,199,76,127,139,184,92,64,223,135,244,96,228,108,33,107,105,159,144,61,148,22,50, +76,31,20,178,155,190,65,60,62,154,40,37,164,244,51,148,159,129,17,43,8,57,159,110,20,50,74,183,40,121,171,144,178,30,3,239,187, +155,100,156,157,22,146,209,167,132,140,211,167,133,172,167,207,10,217,71,95,23,178,248,94,172,121,37,95,0,26,85,209,125,162, +253,109,100,10,233,165,247,10,25,20,210,131,246,152,66,182,210,231,69,185,78,145,230,237,222,169,218,57,34,100,29,237,21,210, +164,81,101,63,40,164,159,142,8,105,208,117,74,30,87,249,39,133,212,233,81,33,91,233,51,106,28,190,76,124,205,180,139,247,132, +176,91,112,25,70,126,82,72,143,240,175,68,250,176,144,243,232,135,34,238,42,232,128,138,191,59,69,236,181,138,114,81,140,203, +30,37,63,64,50,182,239,23,114,1,125,77,200,74,122,70,72,75,200,122,172,168,221,66,70,104,76,72,89,174,30,35,37,165,44,95,175, +252,27,212,123,26,176,202,118,11,25,166,239,18,223,11,103,11,123,35,242,159,36,190,158,90,201,17,210,71,87,11,217,72,239,87,233, +163,66,6,232,90,146,235,238,152,144,81,186,94,200,24,125,81,200,110,122,92,201,47,41,251,87,132,236,160,39,132,156,67,95,37, +190,86,229,120,53,99,87,149,178,158,206,8,41,219,213,138,113,191,73,200,32,125,148,248,222,28,162,9,226,251,115,144,174,81,242, +6,226,235,121,22,185,196,215,114,3,221,75,124,29,55,211,211,196,247,234,22,202,8,217,68,31,34,190,166,123,233,125,66,6,69,61, +243,213,120,204,199,143,76,119,211,23,136,239,233,210,206,229,3,66,206,167,23,85,250,37,146,119,52,34,185,182,248,30,81,5,249, +4,54,174,196,28,105,247,148,229,247,169,252,151,145,63,166,242,121,60,51,42,237,151,60,255,109,228,223,164,242,121,253,255,132, +131,231,103,224,213,14,233,251,39,37,249,97,164,65,248,33,173,57,210,214,168,100,183,146,171,148,28,156,195,235,210,133,254,32, +54,61,31,228,48,18,187,53,70,227,214,44,17,161,60,151,215,247,112,187,220,35,219,89,144,182,199,24,206,41,139,18,49,162,131, +150,87,68,184,99,245,9,57,30,171,70,137,42,54,61,111,129,144,93,111,241,182,49,241,190,207,182,203,126,58,22,183,4,69,31,77, +252,240,188,199,219,229,121,34,219,48,204,52,26,214,12,26,214,61,52,236,209,105,216,48,197,202,225,237,50,232,165,118,121,30,38, +250,60,168,139,239,180,1,156,159,181,148,88,168,83,51,75,244,153,104,169,143,184,140,68,114,177,245,136,145,22,38,125,47,23,165, +52,228,123,133,116,172,94,97,49,148,197,128,165,118,170,230,45,98,52,66,40,61,23,207,146,173,235,143,72,45,148,41,38,78,8, +194,234,213,1,151,76,157,93,186,56,231,121,207,151,8,169,163,237,23,92,23,186,112,57,86,102,128,248,40,189,218,46,247,110,222, +103,31,70,185,7,218,38,120,14,31,247,35,85,43,236,60,197,243,230,35,181,81,164,42,176,47,241,155,64,144,29,180,46,227,227,200, +134,143,7,104,247,109,149,52,124,123,132,134,239,8,210,206,15,212,209,240,7,107,104,248,206,106,138,252,247,206,227,81,188,217, +162,157,199,44,188,165,138,134,143,133,104,251,13,149,148,184,49,66,137,155,130,180,21,87,255,196,45,33,242,30,247,222,125, +200,235,87,53,122,209,115,77,221,8,170,103,203,248,75,96,110,171,68,108,122,69,140,52,194,158,231,241,225,107,70,12,44,195,58, +118,172,1,188,35,232,107,245,53,145,247,198,86,79,19,69,124,78,236,66,156,4,78,108,49,253,30,207,139,240,108,244,189,7,90,43, +206,129,160,22,209,219,186,150,220,16,167,117,62,77,111,244,235,176,55,211,41,10,152,203,205,118,97,115,112,57,246,81,192,183, +228,230,70,145,142,248,29,92,17,79,153,65,198,83,47,251,77,230,196,98,40,17,244,114,79,47,60,151,251,48,139,125,23,83,141,247, +101,93,103,93,63,72,156,13,161,214,165,104,195,210,96,132,34,245,78,108,9,215,67,60,70,23,34,166,130,134,131,15,47,63,70,42, +138,216,170,242,132,68,84,120,208,171,16,53,250,253,232,89,13,234,159,235,147,119,156,43,249,140,178,89,251,170,113,46,4,160,31, +153,45,239,23,253,225,90,138,132,219,81,131,247,227,236,11,222,115,222,31,177,95,121,255,232,243,90,164,249,170,200,231,143,208, +161,128,41,198,238,226,240,127,77,86,135,251,10,93,175,133,88,132,186,222,150,99,91,37,235,109,172,16,49,30,166,7,103,243, +25,198,216,251,78,178,72,77,68,79,60,236,167,102,35,113,186,2,173,27,132,71,64,91,138,245,209,98,176,69,149,90,36,50,30,11,226, +28,9,106,195,167,195,148,56,93,67,17,51,241,136,23,158,139,248,136,120,7,189,154,41,173,107,52,205,92,124,253,63,82,68,155, +94,130,251,198,81,107,80,203,89,171,149,92,195,165,153,179,86,96,133,242,183,61,71,57,235,18,232,65,214,202,218,160,95,202,35, +5,246,55,39,115,214,42,165,191,54,153,120,164,26,35,215,128,104,237,194,188,21,189,90,181,40,230,160,27,187,110,80,107,244,121, +132,126,61,226,164,209,175,81,177,116,171,86,67,7,99,98,39,214,74,190,84,244,213,28,107,158,104,153,19,107,199,154,43,175,185, +209,27,128,87,19,229,208,206,139,181,179,147,165,26,99,20,241,230,98,43,145,35,61,15,90,13,178,14,171,81,140,225,206,211,81, +234,186,51,36,198,177,155,118,79,134,212,88,44,18,185,75,174,111,147,163,233,73,124,82,90,49,18,230,160,169,121,164,87,47,188, +102,206,151,245,45,158,156,121,46,186,105,217,100,72,107,49,230,106,33,45,113,230,221,30,30,115,238,59,230,180,155,226,40,209, +77,61,147,168,121,85,55,121,39,249,254,225,199,174,84,45,246,80,175,184,7,243,216,233,16,123,86,181,248,172,113,29,210,95,20, +123,79,72,156,177,252,204,255,190,242,251,23,101,255,55,149,254,79,149,126,83,236,218,140,254,42,234,173,38,31,147,118,227,186, +208,23,216,235,236,186,206,175,176,55,24,189,206,232,13,118,109,231,77,154,48,122,49,14,252,76,249,205,108,249,121,39,162,37, +182,249,168,89,79,92,33,163,149,81,128,45,101,136,86,221,219,134,83,226,138,0,181,99,79,31,239,99,148,48,115,177,181,248,140, +131,29,231,138,90,228,39,240,204,89,27,196,30,212,202,194,212,245,219,16,107,209,231,50,236,78,109,93,255,33,158,191,144,103, +105,3,124,248,186,243,225,189,115,213,62,171,81,167,86,221,61,167,167,6,186,23,173,15,97,48,248,157,161,26,31,110,34,161,67,172, +2,253,9,88,23,91,134,176,84,90,47,135,66,204,7,219,86,102,209,133,205,255,51,201,245,4,195,124,232,107,170,152,94,205,122,25, +191,93,46,178,106,168,154,45,80,58,198,150,197,149,14,127,118,129,208,47,176,126,170,74,87,83,163,190,1,209,216,129,27,118,128, +189,28,102,129,146,29,177,220,55,135,22,134,3,149,23,189,241,172,242,231,243,126,129,232,175,99,45,231,210,95,244,119,250,230, +210,47,43,131,21,197,221,245,20,181,7,161,245,245,211,29,21,220,55,136,216,88,116,247,71,167,222,27,209,133,230,65,73,171,19, +99,20,240,56,125,179,105,161,71,88,17,43,62,92,45,155,35,210,187,124,94,42,197,104,180,68,186,105,98,18,163,29,193,104,7,184, +165,155,198,203,218,216,11,111,158,218,174,87,146,124,79,68,74,35,40,228,86,179,78,166,189,33,196,76,53,21,79,18,110,27,70,13, +188,254,149,147,178,102,174,175,154,36,113,63,159,139,150,222,172,246,191,7,196,60,122,232,35,144,94,104,252,222,199,119,255, +103,85,90,167,251,176,69,158,98,215,205,122,158,221,199,72,221,98,136,110,239,144,123,232,182,88,149,248,28,91,180,223,213,33, +99,101,107,44,138,59,187,53,117,158,61,208,33,239,75,17,236,232,126,120,123,248,59,59,228,103,67,156,137,123,176,211,106,137, +100,152,150,50,63,113,25,209,157,149,117,116,240,134,0,181,104,203,209,158,109,123,170,232,184,231,201,85,187,32,43,9,99,166, +97,204,88,215,239,24,201,187,149,71,173,161,70,21,151,179,166,206,255,6,22,106,226,49,203,16,157,26,125,185,67,126,119,208,142, +18,137,131,184,127,228,176,23,231,241,78,23,183,151,66,45,29,50,121,77,137,156,104,207,193,10,138,84,59,177,33,196,85,132,13, +195,187,69,107,55,27,17,25,243,81,151,99,245,16,191,33,201,182,68,196,141,136,137,239,39,152,248,153,131,55,200,27,234,139,101, +125,231,223,37,242,127,26,149,164,27,144,99,121,20,242,198,128,188,51,22,191,23,224,231,215,201,128,28,211,83,144,15,169,252, +242,187,47,207,55,85,61,126,37,249,247,7,103,149,111,135,170,175,78,201,168,122,111,81,198,85,125,113,85,167,159,228,103,157, +184,240,232,19,159,95,162,170,236,130,178,182,149,247,193,162,26,97,55,84,155,75,229,163,194,62,79,249,241,207,160,140,228,189, +83,182,33,42,202,52,193,210,131,222,116,43,123,179,42,23,167,226,120,48,12,111,23,225,194,198,150,145,182,44,70,108,128,204,129, +180,147,118,87,144,182,162,159,60,43,250,187,119,144,181,58,235,56,246,136,155,206,58,49,59,151,203,230,40,12,139,107,59,110, +239,144,237,236,115,71,169,118,77,58,63,50,229,180,181,224,56,201,61,25,155,216,6,210,54,12,145,190,97,104,3,121,240,192,126, +184,145,170,55,22,70,236,245,137,196,150,109,110,206,78,142,45,216,159,60,148,36,54,68,26,156,116,238,163,13,161,200,16,30,222, +161,161,93,67,67,168,32,160,20,174,107,67,187,168,126,40,233,164,114,217,116,42,238,218,19,110,60,129,199,118,55,157,201,247, +83,108,104,36,59,22,207,141,229,51,241,253,120,77,124,250,187,58,22,246,211,226,243,123,204,216,145,126,106,57,111,169,126,106, +31,74,37,51,135,210,7,226,73,199,201,186,73,94,56,190,214,25,201,100,243,105,103,223,234,76,50,143,182,205,62,159,207,38,219, +29,205,166,248,139,222,237,180,1,237,201,169,74,218,102,200,223,100,143,237,81,14,54,92,154,103,112,217,150,222,231,36,221,66, +14,93,105,156,33,59,49,154,203,30,22,69,249,108,196,211,217,248,170,194,222,189,118,206,78,109,112,198,11,110,177,151,181,83, +217,27,54,175,157,24,177,199,121,225,105,230,114,239,186,41,243,230,130,91,102,175,151,246,76,210,217,23,95,61,154,204,109,179, +15,22,108,103,196,158,170,72,228,148,213,95,93,102,222,128,184,219,103,231,248,76,79,55,230,114,133,113,215,78,149,21,171,41, +247,128,131,156,201,72,153,117,243,158,253,152,232,233,158,165,57,47,247,68,219,49,73,211,219,46,109,114,160,250,169,97,134,156, +116,38,197,179,202,43,194,72,219,201,212,244,174,138,209,151,239,108,146,102,199,118,227,235,93,119,124,251,214,161,210,218, +235,167,112,41,23,57,83,173,81,233,114,79,213,163,2,86,133,236,187,155,69,67,172,50,235,80,58,239,78,53,67,88,54,37,199,59,214, +58,110,238,72,63,109,154,201,60,240,238,241,120,71,125,51,120,172,192,15,85,78,175,110,186,97,155,237,242,176,47,25,176,254,70, +10,185,28,182,151,248,234,100,38,35,118,146,214,243,231,247,83,207,255,231,128,192,130,15,31,149,178,208,232,158,217,123,237, +132,61,82,120,135,107,231,249,92,179,8,222,220,161,52,143,221,216,249,253,242,83,115,251,78,143,117,5,190,46,139,75,111,230,220, +126,26,56,95,246,192,121,151,45,38,160,99,230,210,50,24,215,37,71,208,64,204,250,252,153,189,16,59,99,233,145,248,165,66,172, +202,102,51,118,18,227,50,111,102,231,76,118,228,64,62,190,213,134,158,75,58,238,16,146,253,228,135,16,83,176,140,216,14,210,118, +96,143,223,129,61,126,7,246,120,19,15,190,215,35,177,139,2,59,202,246,249,29,187,136,237,34,109,215,70,0,57,188,10,108,160, +234,225,25,86,165,182,219,161,64,114,100,196,206,231,59,250,250,250,168,66,234,235,50,201,125,121,242,38,83,169,28,82,100,38, +199,199,109,39,69,222,61,201,188,189,61,151,33,115,143,24,45,242,140,32,140,200,28,17,177,66,6,223,145,109,242,99,195,31,79,230, +236,68,150,188,234,68,160,64,233,104,160,186,146,158,200,150,78,13,178,70,48,166,174,93,90,140,69,139,156,42,62,32,69,75,233,0, +41,90,100,74,248,84,170,17,45,14,29,153,41,123,36,155,178,169,38,101,239,77,22,50,238,180,201,227,185,25,219,181,41,144,42, +53,165,54,53,227,169,92,61,205,44,171,33,127,42,171,154,76,204,38,131,79,223,17,242,9,129,5,10,77,197,49,25,123,211,118,38,5, +145,41,228,71,73,223,135,204,26,60,138,11,13,47,80,93,10,195,186,150,223,22,84,186,18,233,245,120,149,157,91,199,107,200,11,135, +178,56,37,19,233,65,251,136,112,44,63,49,168,2,134,45,56,37,197,48,240,220,173,118,126,60,235,228,49,200,24,15,94,77,2,7,89,70, +221,69,120,53,216,13,201,7,185,35,153,41,216,100,141,38,243,171,16,145,170,141,54,66,0,150,203,113,107,160,138,81,209,162,161, +180,99,35,82,100,34,79,65,165,36,178,219,17,9,225,81,236,196,91,249,33,149,119,87,143,165,168,122,122,90,58,5,184,81,181,151, +165,201,155,118,82,246,196,230,189,84,145,46,235,161,47,237,168,38,85,164,243,107,39,70,147,133,188,203,91,147,206,139,113,32, +51,157,71,63,93,158,203,165,172,217,151,86,251,55,121,246,103,211,136,131,140,236,167,135,47,53,242,56,201,49,76,169,99,31,94, +157,28,25,181,83,114,58,183,96,149,82,136,27,75,97,88,129,100,41,152,144,192,43,132,228,75,193,227,240,209,208,157,2,26,137, +199,170,35,46,198,163,6,218,186,108,38,147,61,108,167,182,218,169,116,14,53,73,235,84,42,145,149,14,212,48,147,85,141,95,22, +203,174,172,33,122,22,93,213,199,241,118,15,30,11,197,115,17,249,198,139,51,204,53,119,77,210,77,82,168,168,201,138,60,34,82,189, +57,68,58,86,48,85,230,84,24,172,87,211,86,243,14,131,44,101,194,138,229,66,122,174,128,119,231,17,175,85,120,168,246,36,210,99, +54,111,78,5,76,107,178,50,234,168,49,207,3,51,239,242,112,145,29,41,245,158,59,78,197,98,56,207,99,49,153,42,214,98,137,180, +8,12,121,159,163,72,201,178,37,135,113,200,185,71,200,200,143,103,210,46,132,155,204,97,206,33,220,66,158,2,82,138,144,174,44, +233,178,15,193,188,8,32,117,84,152,249,194,158,49,212,224,135,204,139,45,144,66,238,104,249,150,83,205,147,239,220,117,74,198, +178,109,38,12,99,249,34,172,68,122,91,249,203,60,238,104,26,113,194,159,29,125,228,119,209,85,12,211,38,44,223,41,85,197,169, +155,149,251,49,85,184,101,203,177,142,159,3,249,189,217,220,152,157,186,188,44,2,189,216,89,68,11,204,130,35,34,185,226,80,50, +211,81,92,9,198,33,177,76,140,195,57,68,63,105,19,125,116,55,211,190,205,200,27,166,251,249,115,114,217,0,44,60,249,184,102, +156,99,223,100,207,179,231,240,137,219,248,7,54,111,157,55,60,208,203,255,209,211,26,28,78,44,31,76,183,238,30,28,30,188,106,88, +27,111,94,219,118,142,122,252,187,239,101,143,178,91,217,119,217,55,216,99,236,12,187,157,121,195,218,95,180,254,137,137,35,218, +245,215,156,17,133,123,7,6,135,88,164,146,58,217,143,144,75,59,205,221,247,176,191,99,39,216,119,132,247,43,186,231,195,172, +121,112,224,146,3,186,118,11,91,196,152,174,127,128,177,229,39,116,243,17,198,62,57,161,179,195,245,39,116,239,15,89,125,90,59, +212,111,48,195,171,133,251,13,115,120,193,208,130,65,221,248,36,179,6,46,49,140,22,67,51,244,86,157,242,77,3,180,210,63,85, +253,223,179,207,177,79,176,219,240,146,222,57,172,42,138,54,247,106,21,253,109,244,54,122,187,113,249,45,90,111,143,182,179,89, +171,56,252,196,196,181,172,209,242,48,183,189,255,57,214,80,201,234,67,43,126,201,234,171,13,198,171,12,227,216,52,136,107, +115,79,60,126,213,247,250,180,150,31,24,164,237,233,63,215,113,191,102,245,211,29,140,221,205,188,205,168,173,182,71,27,107,254, +105,219,93,43,209,5,202,215,180,182,24,36,180,206,86,250,52,31,222,94,221,250,60,139,183,233,161,211,140,13,245,106,207,80, +143,97,105,151,54,155,214,173,71,244,170,175,51,198,234,171,206,233,225,179,232,180,94,249,41,198,22,156,211,253,7,234,111,215, +43,30,100,245,187,116,223,85,45,187,244,224,71,89,203,144,174,189,200,234,227,204,178,142,60,172,133,118,26,214,74,244,221, +103,4,91,245,64,190,73,115,54,26,1,83,51,125,102,208,180,222,207,90,106,47,106,53,3,116,171,206,39,247,131,226,121,143,120,254, +94,211,222,251,113,12,75,155,78,15,97,172,7,39,218,123,247,107,19,205,3,244,75,62,205,127,208,105,220,88,190,127,160,117,151, +110,30,156,53,208,96,152,116,74,103,31,225,115,167,121,217,15,88,99,80,243,105,187,60,116,138,181,104,1,164,155,140,104,111,116, +121,212,136,118,68,189,90,5,55,96,4,139,74,149,102,66,169,55,68,17,223,41,22,173,144,101,60,209,213,170,132,72,250,163,44, +186,56,218,25,93,143,170,100,134,79,243,203,26,60,197,170,58,73,99,26,157,140,49,252,63,118,204,243,88,173,198,190,95,203,158, +137,61,84,71,6,99,26,50,197,15,121,142,31,243,60,85,103,176,223,212,81,149,233,245,106,44,34,126,68,30,211,166,253,136,180,9, +255,199,234,217,137,216,203,13,90,228,205,6,214,240,251,89,44,242,66,19,139,28,107,247,85,157,156,109,84,189,221,201,170,78, +206,101,85,103,193,83,224,177,46,86,117,166,91,254,125,4,149,125,223,192,101,241,239,183,248,119,4,197,191,225,42,126,119,193, +255,142,139,127,79,82,252,91,46,254,189,66,241,239,185,76,42,253,77,151,110,201,223,193,241,239,98,88,76,254,254,247,49,254,29, +74,76,250,240,223,33,50,171,244,123,69,45,38,223,203,255,6,76,87,254,252,247,120,158,24,137,223,63,241,223,17,146,42,43,126, +247,104,201,182,242,191,55,251,63,108,101,104,241,168,38,0,0,0,0}; + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + METHOD (constructor, "", "()V") \ + METHOD (toString, "toString", "()Ljava/lang/String;") \ + +DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer") +#undef JNI_CLASS_MEMBERS + +//============================================================================== +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)Lcom/rmsl/juce/JuceHTTPStream;") \ + METHOD (connect, "connect", "()Z") \ + METHOD (release, "release", "()V") \ + METHOD (read, "read", "([BI)I") \ + METHOD (getPosition, "getPosition", "()J") \ + METHOD (getTotalLength, "getTotalLength", "()J") \ + METHOD (isExhausted, "isExhausted", "()Z") \ + METHOD (setPosition, "setPosition", "(J)Z") \ + +DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/rmsl/juce/JuceHTTPStream", 16, javaJuceHttpStream) +#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 jobject getMulticastLock() +{ + static GlobalRef multicastLock = [&] + { + auto* env = getEnv(); + + LocalRef wifiManager (env->CallObjectMethod (getAppContext().get(), + AndroidContext.getSystemService, + javaString ("wifi").get())); + + if (wifiManager == nullptr) + return GlobalRef{}; + + return GlobalRef (LocalRef (env->CallObjectMethod (wifiManager.get(), + AndroidWifiManager.createMulticastLock, + javaString ("JUCE_MulticastLock").get()))); + }(); + + return multicastLock.get(); +} + +JUCE_API void JUCE_CALLTYPE acquireMulticastLock(); +JUCE_API void JUCE_CALLTYPE acquireMulticastLock() +{ + auto multicastLock = getMulticastLock(); + + if (multicastLock != nullptr) + getEnv()->CallVoidMethod (multicastLock, AndroidMulticastLock.acquire); +} + +JUCE_API void JUCE_CALLTYPE releaseMulticastLock(); +JUCE_API void JUCE_CALLTYPE releaseMulticastLock() +{ + auto multicastLock = getMulticastLock(); + + if (multicastLock != nullptr) + getEnv()->CallVoidMethod (multicastLock, AndroidMulticastLock.release); +} + +//============================================================================== +void MACAddress::findAllAddresses (Array& /*result*/) +{ + // TODO +} + + +JUCE_API bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /*targetEmailAddress*/, + const String& /*emailSubject*/, + const String& /*bodyText*/, + const StringArray& /*filesToAttach*/) +{ + // TODO + return false; +} + +//============================================================================== +bool URL::isLocalFile() const +{ + if (getScheme() == "file") + return true; + + if (getScheme() == "content") + { + auto file = AndroidContentUriResolver::getLocalFileFromContentUri (*this); + return (file != File()); + } + + return false; +} + +File URL::getLocalFile() const +{ + if (getScheme() == "content") + { + auto path = AndroidContentUriResolver::getLocalFileFromContentUri (*this); + + // This URL does not refer to a local file + // Call URL::isLocalFile to first check if the URL + // refers to a local file. + jassert (path != File()); + + return path; + } + + return fileFromFileSchemeURL (*this); +} + +String URL::getFileName() const +{ + if (getScheme() == "content") + return AndroidContentUriResolver::getFileNameFromContentUri (*this); + + return toString (false).fromLastOccurrenceOf ("/", false, true); +} + +//============================================================================== +class WebInputStream::Pimpl +{ +public: + enum { contentStreamCacheSize = 1024 }; + + Pimpl (WebInputStream&, const URL& urlToCopy, bool addParametersToBody) + : url (urlToCopy), + isContentURL (urlToCopy.getScheme() == "content"), + addParametersToRequestBody (addParametersToBody), + hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()), + httpRequest (hasBodyDataToSend ? "POST" : "GET") + { + } + + ~Pimpl() + { + cancel(); + } + + void cancel() + { + if (isContentURL) + { + stream.callVoidMethod (AndroidInputStream.close); + return; + } + + const ScopedLock lock (createStreamLock); + + if (stream != nullptr) + { + stream.callVoidMethod (HTTPStream.release); + stream.clear(); + } + + hasBeenCancelled = true; + } + + bool connect (WebInputStream::Listener* /*listener*/) + { + auto* env = getEnv(); + + if (isContentURL) + { + GlobalRef urlRef { urlToUri (url) }; + auto inputStream = AndroidStreamHelpers::createStream (urlRef, AndroidStreamHelpers::StreamKind::input); + + if (inputStream != nullptr) + { + stream = GlobalRef (inputStream); + statusCode = 200; + + return true; + } + } + else + { + String address = url.toString (! addParametersToRequestBody); + + if (! address.contains ("://")) + address = "http://" + address; + + MemoryBlock postData; + + if (hasBodyDataToSend) + WebInputStream::createHeadersAndPostData (url, + headers, + postData, + addParametersToRequestBody); + + jbyteArray postDataArray = nullptr; + + if (! postData.isEmpty()) + { + postDataArray = env->NewByteArray (static_cast (postData.getSize())); + env->SetByteArrayRegion (postDataArray, 0, static_cast (postData.getSize()), (const jbyte*) postData.getData()); + } + + LocalRef responseHeaderBuffer (env->NewObject (StringBuffer, StringBuffer.constructor)); + + // Annoyingly, the android HTTP functions will choke on this call if you try to do it on the message + // thread. You'll need to move your networking code to a background thread to keep it happy.. + jassert (Thread::getCurrentThread() != nullptr); + + jintArray statusCodeArray = env->NewIntArray (1); + jassert (statusCodeArray != nullptr); + + { + const ScopedLock lock (createStreamLock); + + if (! hasBeenCancelled) + stream = GlobalRef (LocalRef (env->CallStaticObjectMethod (HTTPStream, + HTTPStream.createHTTPStream, + javaString (address).get(), + (jboolean) addParametersToRequestBody, + postDataArray, + javaString (headers).get(), + (jint) timeOutMs, + statusCodeArray, + responseHeaderBuffer.get(), + (jint) numRedirectsToFollow, + javaString (httpRequest).get()))); + } + + if (stream != nullptr && ! stream.callBooleanMethod (HTTPStream.connect)) + stream.clear(); + + jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, nullptr); + statusCode = statusCodeElements[0]; + env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0); + env->DeleteLocalRef (statusCodeArray); + + if (postDataArray != nullptr) + env->DeleteLocalRef (postDataArray); + + if (stream != nullptr) + { + StringArray headerLines; + + { + LocalRef headersString ((jstring) env->CallObjectMethod (responseHeaderBuffer.get(), + StringBuffer.toString)); + headerLines.addLines (juceString (env, headersString)); + } + + for (int i = 0; i < headerLines.size(); ++i) + { + const String& header = headerLines[i]; + const String key (header.upToFirstOccurrenceOf (": ", false, false)); + const String value (header.fromFirstOccurrenceOf (": ", false, false)); + const String previousValue (responseHeaders[key]); + + responseHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + } + + return true; + } + } + + return false; + } + + //============================================================================== + // WebInputStream methods + void withExtraHeaders (const String& extraHeaders) + { + if (! headers.endsWithChar ('\n') && headers.isNotEmpty()) + headers << "\r\n"; + + headers << extraHeaders; + + if (! headers.endsWithChar ('\n') && headers.isNotEmpty()) + headers << "\r\n"; + } + + void withCustomRequestCommand (const String& customRequestCommand) { httpRequest = customRequestCommand; } + void withConnectionTimeout (int timeoutInMs) { timeOutMs = timeoutInMs; } + void withNumRedirectsToFollow (int maxRedirectsToFollow) { numRedirectsToFollow = maxRedirectsToFollow; } + StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (headers); } + StringPairArray getResponseHeaders() const { return responseHeaders; } + int getStatusCode() const { return statusCode; } + + //============================================================================== + bool isError() const { return stream == nullptr; } + bool isExhausted() { return (isContentURL ? eofStreamReached : stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted)); } + int64 getTotalLength() { return (isContentURL ? -1 : (stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0)); } + int64 getPosition() { return (isContentURL ? readPosition : (stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0)); } + + //============================================================================== + bool setPosition (int64 wantedPos) + { + if (isContentURL) + { + if (wantedPos < readPosition) + return false; + + auto bytesToSkip = wantedPos - readPosition; + + if (bytesToSkip == 0) + return true; + + HeapBlock buffer (bytesToSkip); + + return (read (buffer.getData(), (int) bytesToSkip) > 0); + } + + return stream != nullptr && stream.callBooleanMethod (HTTPStream.setPosition, (jlong) wantedPos); + } + + int read (void* buffer, int bytesToRead) + { + jassert (buffer != nullptr && bytesToRead >= 0); + + const ScopedLock lock (createStreamLock); + + if (stream == nullptr) + return 0; + + JNIEnv* env = getEnv(); + + jbyteArray javaArray = env->NewByteArray (bytesToRead); + + auto numBytes = (isContentURL ? stream.callIntMethod (AndroidInputStream.read, javaArray, 0, (jint) bytesToRead) + : stream.callIntMethod (HTTPStream.read, javaArray, (jint) bytesToRead)); + + if (numBytes > 0) + env->GetByteArrayRegion (javaArray, 0, numBytes, static_cast (buffer)); + + env->DeleteLocalRef (javaArray); + + readPosition += jmax (0, numBytes); + + if (numBytes == -1) + eofStreamReached = true; + + return numBytes; + } + + //============================================================================== + int statusCode = 0; + +private: + const URL url; + const bool isContentURL, addParametersToRequestBody, hasBodyDataToSend; + bool eofStreamReached = false; + int numRedirectsToFollow = 5, timeOutMs = 0; + String httpRequest, headers; + StringPairArray responseHeaders; + CriticalSection createStreamLock; + bool hasBeenCancelled = false; + int readPosition = 0; + + GlobalRef stream; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) +}; + +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) +{ + return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options); +} + +//============================================================================== +#if __ANDROID_API__ < 24 // Android support for getifadds was added in Android 7.0 (API 24) so the posix implementation does not apply + +static IPAddress makeAddress (const sockaddr_in *addr_in) +{ + if (addr_in->sin_addr.s_addr == INADDR_NONE) + return {}; + + return IPAddress (ntohl (addr_in->sin_addr.s_addr)); +} + +struct InterfaceInfo +{ + IPAddress interfaceAddress, broadcastAddress; +}; + +static Array findIPAddresses (int dummySocket) +{ + ifconf cfg; + HeapBlock buffer; + int bufferSize = 1024; + + do + { + bufferSize *= 2; + buffer.calloc (bufferSize); + + cfg.ifc_len = bufferSize; + cfg.ifc_buf = buffer; + + if (ioctl (dummySocket, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL) + return {}; + + } while (bufferSize < cfg.ifc_len + 2 * (int) (IFNAMSIZ + sizeof (struct sockaddr_in6))); + + Array result; + + for (size_t i = 0; i < (size_t) cfg.ifc_len / (size_t) sizeof (struct ifreq); ++i) + { + auto& item = cfg.ifc_req[i]; + + if (item.ifr_addr.sa_family == AF_INET) + { + InterfaceInfo info; + info.interfaceAddress = makeAddress (reinterpret_cast (&item.ifr_addr)); + + if (! info.interfaceAddress.isNull()) + { + if (ioctl (dummySocket, SIOCGIFBRDADDR, &item) == 0) + info.broadcastAddress = makeAddress (reinterpret_cast (&item.ifr_broadaddr)); + + result.add (info); + } + } + else if (item.ifr_addr.sa_family == AF_INET6) + { + // TODO: IPv6 + } + } + + return result; +} + +static Array findIPAddresses() +{ + auto dummySocket = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control + + if (dummySocket < 0) + return {}; + + auto result = findIPAddresses (dummySocket); + ::close (dummySocket); + return result; +} + +void IPAddress::findAllAddresses (Array& result, bool /*includeIPv6*/) +{ + for (auto& a : findIPAddresses()) + result.add (a.interfaceAddress); +} + +IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress& address) +{ + for (auto& a : findIPAddresses()) + if (a.interfaceAddress == address) + return a.broadcastAddress; + + return {}; +} + +#endif + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Network_curl.cpp similarity index 91% rename from JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_Network_curl.cpp index b68dda8d..d2456753 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_Network_curl.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -89,7 +89,9 @@ struct CURLSymbols static DynamicLibrary libcurl; if (libcurl.getNativeHandle() == nullptr) - for (auto libName : { "libcurl.so", "libcurl.so.4", "libcurl.so.3" }) + for (auto libName : { "libcurl.so", + "libcurl.so.4", "libcurl.so.3", + "libcurl-gnutls.so.4", "libcurl-gnutls.so.3" }) if (libcurl.open (libName)) break; @@ -110,9 +112,12 @@ struct CURLSymbols class WebInputStream::Pimpl { public: - Pimpl (WebInputStream& ownerStream, const URL& urlToCopy, bool shouldUsePost) - : owner (ownerStream), url (urlToCopy), isPost (shouldUsePost), - httpRequest (isPost ? "POST" : "GET") + Pimpl (WebInputStream& ownerStream, const URL& urlToCopy, bool addParametersToBody) + : owner (ownerStream), + url (urlToCopy), + addParametersToRequestBody (addParametersToBody), + hasBodyDataToSend (url.hasBodyDataToSend() || addParametersToRequestBody), + httpRequest (hasBodyDataToSend ? "POST" : "GET") { jassert (symbols); // Unable to load libcurl! @@ -141,7 +146,7 @@ class WebInputStream::Pimpl //============================================================================== // Input Stream overrides bool isError() const { return curl == nullptr || lastError != CURLE_OK; } - bool isExhausted() { return (isError() || finished) && curlBuffer.getSize() == 0; } + bool isExhausted() { return (isError() || finished) && curlBuffer.isEmpty(); } int64 getPosition() { return streamPos; } int64 getTotalLength() { return contentLength; } @@ -220,7 +225,7 @@ class WebInputStream::Pimpl //============================================================================== bool setOptions() { - auto address = url.toString (! isPost); + auto address = url.toString (! addParametersToRequestBody); curl_version_info_data* data = symbols->curl_version_info (CURLVERSION_NOW); jassert (data != nullptr); @@ -228,8 +233,11 @@ class WebInputStream::Pimpl if (! requestHeaders.endsWithChar ('\n')) requestHeaders << "\r\n"; - if (isPost) - WebInputStream::createHeadersAndPostData (url, requestHeaders, headersAndPostData); + if (hasBodyDataToSend) + WebInputStream::createHeadersAndPostData (url, + requestHeaders, + headersAndPostData, + addParametersToRequestBody); if (! requestHeaders.endsWithChar ('\n')) requestHeaders << "\r\n"; @@ -244,7 +252,7 @@ class WebInputStream::Pimpl && symbols->curl_easy_setopt (curl, CURLOPT_USERAGENT, userAgent.toRawUTF8()) == CURLE_OK && symbols->curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, (maxRedirects > 0 ? 1 : 0)) == CURLE_OK) { - if (isPost) + if (hasBodyDataToSend) { if (symbols->curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK || symbols->curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK) @@ -256,7 +264,7 @@ class WebInputStream::Pimpl } // handle special http request commands - bool hasSpecialRequestCmd = isPost ? (httpRequest != "POST") : (httpRequest != "GET"); + const auto hasSpecialRequestCmd = hasBodyDataToSend ? (httpRequest != "POST") : (httpRequest != "GET"); if (hasSpecialRequestCmd) if (symbols->curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, httpRequest.toRawUTF8()) != CURLE_OK) @@ -323,14 +331,14 @@ class WebInputStream::Pimpl listener = webInputListener; - if (isPost) + if (hasBodyDataToSend) postBuffer = &headersAndPostData; size_t lastPos = static_cast (-1); // step until either: 1) there is an error 2) the transaction is complete // or 3) data is in the in buffer - while ((! finished) && curlBuffer.getSize() == 0) + while ((! finished) && curlBuffer.isEmpty()) { { const ScopedLock lock (cleanupLock); @@ -342,7 +350,7 @@ class WebInputStream::Pimpl singleStep(); // call callbacks if this is a post request - if (isPost && listener != nullptr && lastPos != postPosition) + if (hasBodyDataToSend && listener != nullptr && lastPos != postPosition) { lastPos = postPosition; @@ -365,11 +373,18 @@ class WebInputStream::Pimpl if (symbols->curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK) statusCode = static_cast (responseCode); - // get content length size + #if LIBCURL_VERSION_MAJOR < 7 || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR < 55) double curlLength; if (symbols->curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curlLength) == CURLE_OK) + { + #else + curl_off_t curlLength; + if (symbols->curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &curlLength) == CURLE_OK) + { + #endif contentLength = static_cast (curlLength); - } + } + } return true; } @@ -613,7 +628,7 @@ class WebInputStream::Pimpl // Options int timeOutMs = 0; int maxRedirects = 5; - const bool isPost; + const bool addParametersToRequestBody, hasBodyDataToSend; String httpRequest; //============================================================================== @@ -642,9 +657,9 @@ class WebInputStream::Pimpl JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; -URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) { - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); + return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Network_linux.cpp similarity index 89% rename from JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_Network_linux.cpp index 5b7c846f..fa4393ca 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_Network_linux.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,6 +25,26 @@ namespace juce void MACAddress::findAllAddresses (Array& result) { + #if JUCE_BSD + struct ifaddrs* addrs = nullptr; + + if (getifaddrs (&addrs) != -1) + { + for (auto* i = addrs; i != nullptr; i = i->ifa_next) + { + if (i->ifa_addr->sa_family == AF_LINK) + { + auto sdl = unalignedPointerCast (i->ifa_addr); + MACAddress ma ((const uint8*) (sdl->sdl_data + sdl->sdl_nlen)); + + if (! ma.isNull()) + result.addIfNotAlreadyThere (ma); + } + } + + freeifaddrs (addrs); + } + #else auto s = socket (AF_INET, SOCK_DGRAM, 0); if (s != -1) @@ -53,6 +73,7 @@ void MACAddress::findAllAddresses (Array& result) ::close (s); } + #endif } @@ -70,10 +91,14 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /* targetEma class WebInputStream::Pimpl { public: - Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool shouldUsePost) - : owner (pimplOwner), url (urlToCopy), - isPost (shouldUsePost), httpRequestCmd (shouldUsePost ? "POST" : "GET") - {} + Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool addParametersToBody) + : owner (pimplOwner), + url (urlToCopy), + addParametersToRequestBody (addParametersToBody), + hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()), + httpRequestCmd (hasBodyDataToSend ? "POST" : "GET") + { + } ~Pimpl() { @@ -127,7 +152,7 @@ class WebInputStream::Pimpl return false; } - address = url.toString (! isPost); + address = url.toString (! addParametersToRequestBody); statusCode = createConnection (listener, numRedirectsToFollow); return statusCode != 0; @@ -256,7 +281,7 @@ class WebInputStream::Pimpl MemoryBlock postData; int64 contentLength = -1, position = 0; bool finished = false; - const bool isPost; + const bool addParametersToRequestBody, hasBodyDataToSend; int timeOutMs = 0; int numRedirectsToFollow = 5; String httpRequestCmd; @@ -285,8 +310,11 @@ class WebInputStream::Pimpl { closeSocket (false); - if (isPost) - WebInputStream::createHeadersAndPostData (url, headers, postData); + if (hasBodyDataToSend) + WebInputStream::createHeadersAndPostData (url, + headers, + postData, + addParametersToRequestBody); auto timeOutTime = Time::getMillisecondCounter(); @@ -333,7 +361,7 @@ class WebInputStream::Pimpl struct addrinfo* result = nullptr; - if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0) + if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == nullptr) return 0; { @@ -351,7 +379,7 @@ class WebInputStream::Pimpl int receiveBufferSize = 16384; setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); - setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); + setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, nullptr, 0); #if JUCE_MAC setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); @@ -367,8 +395,8 @@ class WebInputStream::Pimpl freeaddrinfo (result); { - const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, hostPath, - address, headers, postData, isPost, httpRequestCmd)); + const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, hostPath, address, + headers, postData, httpRequestCmd)); if (! sendHeader (socketHandle, requestHeader, timeOutTime, owner, listener)) { @@ -474,7 +502,7 @@ class WebInputStream::Pimpl const String& proxyName, int proxyPort, const String& hostPath, const String& originalURL, const String& userHeaders, const MemoryBlock& postData, - bool isPost, const String& httpRequestCmd) + const String& httpRequestCmd) { MemoryOutputStream header; @@ -483,20 +511,23 @@ class WebInputStream::Pimpl else writeHost (header, httpRequestCmd, originalURL, proxyName, proxyPort); - writeValueIfNotPresent (header, userHeaders, "User-Agent:", "JUCE/" JUCE_STRINGIFY(JUCE_MAJOR_VERSION) - "." JUCE_STRINGIFY(JUCE_MINOR_VERSION) - "." JUCE_STRINGIFY(JUCE_BUILDNUMBER)); + writeValueIfNotPresent (header, userHeaders, "User-Agent:", "JUCE/" JUCE_STRINGIFY (JUCE_MAJOR_VERSION) + "." JUCE_STRINGIFY (JUCE_MINOR_VERSION) + "." JUCE_STRINGIFY (JUCE_BUILDNUMBER)); writeValueIfNotPresent (header, userHeaders, "Connection:", "close"); - if (isPost) - writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize())); + const auto postDataSize = postData.getSize(); + const auto hasPostData = postDataSize > 0; + + if (hasPostData) + writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postDataSize)); if (userHeaders.isNotEmpty()) header << "\r\n" << userHeaders; header << "\r\n\r\n"; - if (isPost) + if (hasPostData) header << postData; return header.getMemoryBlock(); @@ -576,9 +607,9 @@ class WebInputStream::Pimpl JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; -URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) { - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); + return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options); } #endif diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm b/JuceLibraryCode/modules/juce_core/native/juce_Network_mac.mm similarity index 81% rename from JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm rename to JuceLibraryCode/modules/juce_core/native/juce_Network_mac.mm index fd332893..027c0002 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_Network_mac.mm @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -58,14 +58,12 @@ } //============================================================================== -bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress, - const String& emailSubject, - const String& bodyText, - const StringArray& filesToAttach) +bool JUCE_CALLTYPE Process::openEmailWithAttachments ([[maybe_unused]] const String& targetEmailAddress, + [[maybe_unused]] const String& emailSubject, + [[maybe_unused]] const String& bodyText, + [[maybe_unused]] const StringArray& filesToAttach) { #if JUCE_IOS - ignoreUnused (targetEmailAddress, emailSubject, bodyText, filesToAttach); - //xxx probably need to use MFMailComposeViewController jassertfalse; return false; @@ -109,18 +107,295 @@ } //============================================================================== -// 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_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) - -//============================================================================== -class URLConnectionState : private Thread +class URLConnectionStateBase : public Thread { public: - URLConnectionState (NSURLRequest* req, const int maxRedirects) + explicit URLConnectionStateBase (NSURLRequest* req, int maxRedirects) : Thread ("http connection"), request ([req retain]), data ([[NSMutableData data] retain]), numRedirectsToFollow (maxRedirects) + { + } + + virtual ~URLConnectionStateBase() = default; + + virtual void cancel() = 0; + virtual bool start (WebInputStream&, WebInputStream::Listener*) = 0; + virtual int read (char* dest, int numBytes) = 0; + + int64 getContentLength() const noexcept { return contentLength; } + NSDictionary* getHeaders() const noexcept { return headers; } + int getStatusCode() const noexcept { return statusCode; } + NSInteger getErrorCode() const noexcept { return nsUrlErrorCode; } + +protected: + CriticalSection dataLock, createConnectionLock; + id delegate = nil; + NSDictionary* headers = nil; + NSURLRequest* request = nil; + NSMutableData* data = nil; + int64 contentLength = -1; + int statusCode = 0; + NSInteger nsUrlErrorCode = 0; + + std::atomic initialised { false }, hasFailed { false }, hasFinished { false }; + const int numRedirectsToFollow; + int numRedirects = 0; + int64 latestTotalBytes = 0; + bool hasBeenCancelled = false; + +private: + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionStateBase) +}; + +#if JUCE_MAC +// This version is only used for backwards-compatibility with older OSX targets, +// so we'll turn off deprecation warnings. This code will be removed at some point +// in the future. +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated") +class URLConnectionStatePreYosemite final : public URLConnectionStateBase +{ +public: + URLConnectionStatePreYosemite (NSURLRequest* req, const int maxRedirects) + : URLConnectionStateBase (req, maxRedirects) + { + static DelegateClass cls; + delegate = [cls.createInstance() init]; + DelegateClass::setState (delegate, this); + } + + ~URLConnectionStatePreYosemite() override + { + stop(); + + [connection release]; + [request release]; + [headers release]; + [delegate release]; + [data release]; + } + + bool start (WebInputStream& inputStream, WebInputStream::Listener* listener) override + { + startThread(); + + while (isThreadRunning() && ! initialised) + { + if (listener != nullptr) + if (! listener->postDataSendProgress (inputStream, (int) latestTotalBytes, (int) [[request HTTPBody] length])) + return false; + + Thread::sleep (1); + } + + return connection != nil && ! hasFailed; + } + + void stop() + { + { + const ScopedLock dLock (dataLock); + const ScopedLock connectionLock (createConnectionLock); + + hasBeenCancelled = true; + + if (connection != nil) + [connection cancel]; + } + + stopThread (10000); + } + + void cancel() override + { + hasFinished = hasFailed = true; + stop(); + } + + int read (char* dest, int numBytes) override + { + int numDone = 0; + + while (numBytes > 0) + { + const ScopedLock sl (dataLock); + auto available = jmin (numBytes, (int) [data length]); + + if (available > 0) + { + [data getBytes: dest length: (NSUInteger) available]; + [data replaceBytesInRange: NSMakeRange (0, (NSUInteger) available) withBytes: nil length: 0]; + + numDone += available; + numBytes -= available; + dest += available; + } + else + { + if (hasFailed || hasFinished) + break; + + const ScopedUnlock sul (dataLock); + Thread::sleep (1); + } + } + + return numDone; + } + + void didReceiveResponse (NSURLResponse* response) + { + { + const ScopedLock sl (dataLock); + [data setLength: 0]; + } + + contentLength = [response expectedContentLength]; + + [headers release]; + headers = nil; + + if ([response isKindOfClass: [NSHTTPURLResponse class]]) + { + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response; + headers = [[httpResponse allHeaderFields] retain]; + statusCode = (int) [httpResponse statusCode]; + } + + initialised = true; + } + + NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) + { + if (redirectResponse != nullptr) + { + if (numRedirects >= numRedirectsToFollow) + return nil; // Cancel redirect and allow connection to continue + + ++numRedirects; + } + + return newRequest; + } + + void didFailWithError ([[maybe_unused]] NSError* error) + { + DBG (nsStringToJuce ([error description])); + nsUrlErrorCode = [error code]; + hasFailed = true; + initialised = true; + signalThreadShouldExit(); + } + + void didReceiveData (NSData* newData) + { + const ScopedLock sl (dataLock); + [data appendData: newData]; + initialised = true; + } + + void didSendBodyData (NSInteger totalBytesWritten, NSInteger /*totalBytesExpected*/) + { + latestTotalBytes = static_cast (totalBytesWritten); + } + + void finishedLoading() + { + hasFinished = true; + initialised = true; + signalThreadShouldExit(); + } + + void run() override + { + { + const ScopedLock lock (createConnectionLock); + + if (hasBeenCancelled) + return; + + connection = [[NSURLConnection alloc] initWithRequest: request + delegate: delegate]; + } + + while (! threadShouldExit()) + { + JUCE_AUTORELEASEPOOL + { + [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; + } + } + } + +private: + //============================================================================== + struct DelegateClass final : public ObjCClass + { + DelegateClass() : ObjCClass ("JUCENetworkDelegate_") + { + addIvar ("state"); + + addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse); + addMethod (@selector (connection:didFailWithError:), didFailWithError); + addMethod (@selector (connection:didReceiveData:), didReceiveData); + addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:), + connectionDidSendBodyData); + addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading); + addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest); + + registerClass(); + } + + static void setState (id self, URLConnectionStatePreYosemite* state) { object_setInstanceVariable (self, "state", state); } + static URLConnectionStatePreYosemite* getState (id self) { return getIvar (self, "state"); } + + private: + static void didReceiveResponse (id self, SEL, NSURLConnection*, NSURLResponse* response) + { + getState (self)->didReceiveResponse (response); + } + + static void didFailWithError (id self, SEL, NSURLConnection*, NSError* error) + { + getState (self)->didFailWithError (error); + } + + static void didReceiveData (id self, SEL, NSURLConnection*, NSData* newData) + { + getState (self)->didReceiveData (newData); + } + + static NSURLRequest* willSendRequest (id self, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse* response) + { + return getState (self)->willSendRequest (request, response); + } + + static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) + { + getState (self)->didSendBodyData (totalBytesWritten, totalBytesExpected); + } + + static void connectionDidFinishLoading (id self, SEL, NSURLConnection*) + { + getState (self)->finishedLoading(); + } + }; + + NSURLConnection* connection = nil; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionStatePreYosemite) +}; +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +#endif + +//============================================================================== +class API_AVAILABLE (macos (10.9)) URLConnectionState final : public URLConnectionStateBase +{ +public: + URLConnectionState (NSURLRequest* req, const int maxRedirects) + : URLConnectionStateBase (req, maxRedirects) { static DelegateClass cls; delegate = [cls.createInstance() init]; @@ -151,11 +426,10 @@ [data release]; } - void cancel() + void cancel() override { { - const ScopedLock lock (createTaskLock); - + const ScopedLock lock (createConnectionLock); hasBeenCancelled = true; } @@ -163,10 +437,10 @@ void cancel() stopThread (10000); } - bool start (WebInputStream& inputStream, WebInputStream::Listener* listener) + bool start (WebInputStream& inputStream, WebInputStream::Listener* listener) override { { - const ScopedLock lock (createTaskLock); + const ScopedLock lock (createConnectionLock); if (hasBeenCancelled) return false; @@ -186,17 +460,17 @@ bool start (WebInputStream& inputStream, WebInputStream::Listener* listener) return true; } - int read (char* dest, int numBytes) + int read (char* dest, int numBytes) override { int numDone = 0; while (numBytes > 0) { - const int available = jmin (numBytes, (int) [data length]); + const ScopedLock sl (dataLock); + auto available = jmin (numBytes, (int) [data length]); if (available > 0) { - const ScopedLock sl (dataLock); [data getBytes: dest length: (NSUInteger) available]; [data replaceBytesInRange: NSMakeRange (0, (NSUInteger) available) withBytes: nil length: 0]; @@ -209,6 +483,7 @@ int read (char* dest, int numBytes) if (hasFailed || hasFinished) break; + const ScopedUnlock ul (dataLock); Thread::sleep (1); } } @@ -303,7 +578,7 @@ void run() override delegateQueue: [NSOperationQueue currentQueue]] retain]; { - const ScopedLock lock (createTaskLock); + const ScopedLock lock (createConnectionLock); if (! hasBeenCancelled) task = [session dataTaskWithRequest: request]; @@ -322,40 +597,23 @@ void run() override initialised = true; } - int64 contentLength = -1; - CriticalSection dataLock; - id delegate = nil; - NSURLRequest* request = nil; - NSURLSession* session = nil; - NSURLSessionTask* task = nil; - NSMutableData* data = nil; - NSDictionary* headers = nil; - int statusCode = 0; - std::atomic initialised { false }, hasFailed { false }, hasFinished { false }; - bool isBeingDeleted = false; - const int numRedirectsToFollow; - int numRedirects = 0; - int64 latestTotalBytes = 0; - CriticalSection createTaskLock; - bool hasBeenCancelled = false; - private: //============================================================================== - struct DelegateClass : public ObjCClass + struct DelegateClass final : public ObjCClass { DelegateClass() : ObjCClass ("JUCE_URLDelegate_") { addIvar ("state"); addMethod (@selector (URLSession:dataTask:didReceiveResponse:completionHandler:), - didReceiveResponse, "v@:@@@@"); - addMethod (@selector (URLSession:didBecomeInvalidWithError:), didBecomeInvalidWithError, "v@:@@"); - addMethod (@selector (URLSession:dataTask:didReceiveData:), didReceiveData, "v@:@@@"); + didReceiveResponse); + addMethod (@selector (URLSession:didBecomeInvalidWithError:), didBecomeInvalidWithError); + addMethod (@selector (URLSession:dataTask:didReceiveData:), didReceiveData); addMethod (@selector (URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:), - didSendBodyData, "v@:@@qqq"); + didSendBodyData); addMethod (@selector (URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:), - willPerformHTTPRedirection, "v@:@@@@@"); - addMethod (@selector (URLSession:task:didCompleteWithError:), didCompleteWithError, "v@:@@@"); + willPerformHTTPRedirection); + addMethod (@selector (URLSession:task:didCompleteWithError:), didCompleteWithError); registerClass(); } @@ -402,19 +660,21 @@ static void didCompleteWithError (id self, SEL, NSURLConnection*, NSURLSessionTa } }; + NSURLSession* session = nil; + NSURLSessionTask* task = nil; + bool isBeingDeleted = false; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionState) }; //============================================================================== #if JUCE_IOS -struct BackgroundDownloadTask : public URL::DownloadTask +struct BackgroundDownloadTask final : public URL::DownloadTask { BackgroundDownloadTask (const URL& urlToUse, const File& targetLocationToUse, - String extraHeadersToUse, - URL::DownloadTask::Listener* listenerToUse, - bool shouldUsePostRequest) - : listener (listenerToUse), + const URL::DownloadTaskOptions& options) + : listener (options.listener), uniqueIdentifier (String (urlToUse.toString (true).hashCode64()) + String (Random().nextInt64())) { targetLocation = targetLocationToUse; @@ -426,13 +686,18 @@ static void didCompleteWithError (id self, SEL, NSURLConnection*, NSURLSessionTa activeSessions.set (uniqueIdentifier, this); auto nsUrl = [NSURL URLWithString: juceStringToNS (urlToUse.toString (true))]; + + jassert (nsUrl != nullptr); + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullable-to-nonnull-conversion") NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL: nsUrl]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE - if (shouldUsePostRequest) + if (options.usePost) [request setHTTPMethod: @"POST"]; StringArray headerLines; - headerLines.addLines (extraHeadersToUse); + headerLines.addLines (options.extraHeaders); headerLines.removeEmptyStrings (true); for (int i = 0; i < headerLines.size(); ++i) @@ -444,10 +709,14 @@ static void didCompleteWithError (id self, SEL, NSURLConnection*, NSURLSessionTa [request addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; } - session = - [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: juceStringToNS (uniqueIdentifier)] - delegate: delegate - delegateQueue: nullptr]; + auto* configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: juceStringToNS (uniqueIdentifier)]; + + if (options.sharedContainer.isNotEmpty()) + [configuration setSharedContainerIdentifier: juceStringToNS (options.sharedContainer)]; + + session = [NSURLSession sessionWithConfiguration: configuration + delegate: delegate + delegateQueue: nullptr]; if (session != nullptr) downloadTask = [session downloadTaskWithRequest:request]; @@ -517,7 +786,7 @@ void didWriteData (int64 totalBytesWritten, int64 totalBytesExpectedToWrite) void didFinishDownloadingToURL (NSURL* location) { - NSFileManager* fileManager = [[NSFileManager alloc] init]; + auto* fileManager = [NSFileManager defaultManager]; error = ([fileManager moveItemAtURL: location toURL: createNSURLFromFile (targetLocation) error: nil] == NO); @@ -600,17 +869,16 @@ static void invokeNotify (const String& identifier) } //============================================================================== - struct DelegateClass : public ObjCClass> + struct DelegateClass final : public ObjCClass> { DelegateClass() : ObjCClass> ("JUCE_URLDelegate_") { addIvar ("state"); - addMethod (@selector (URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:), - didWriteData, "v@:@@qqq"); - addMethod (@selector (URLSession:downloadTask:didFinishDownloadingToURL:), didFinishDownloadingToURL, "v@:@@@"); - addMethod (@selector (URLSession:task:didCompleteWithError:), didCompleteWithError, "v@:@@@"); - addMethod (@selector (URLSession:didBecomeInvalidWithError:), didBecomeInvalidWithError, "v@:@@@"); + addMethod (@selector (URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:), didWriteData); + addMethod (@selector (URLSession:downloadTask:didFinishDownloadingToURL:), didFinishDownloadingToURL); + addMethod (@selector (URLSession:task:didCompleteWithError:), didCompleteWithError); + addMethod (@selector (URLSession:didBecomeInvalidWithError:), didBecomeInvalidWithError); registerClass(); } @@ -647,12 +915,12 @@ static void didBecomeInvalidWithError (id self, SEL, NSURLSession*, NSURLSession HashMap BackgroundDownloadTask::activeSessions; -URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool usePostRequest) +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) { - std::unique_ptr downloadTask (new BackgroundDownloadTask (*this, targetLocation, extraHeaders, listener, usePostRequest)); + auto downloadTask = std::make_unique (*this, targetLocation, options); if (downloadTask->initOK() && downloadTask->connect()) - return downloadTask.release(); + return downloadTask; return nullptr; } @@ -662,289 +930,22 @@ static void didBecomeInvalidWithError (id self, SEL, NSURLSession*, NSURLSession BackgroundDownloadTask::invokeNotify (identifier); } #else -URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool usePost) +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) { - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, usePost); + return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options); } #endif -//============================================================================== -#else - -// This version is only used for backwards-compatibility with older OSX targets, -// so we'll turn off deprecation warnings. This code will be removed at some point -// in the future. - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" - -//============================================================================== -class URLConnectionState : public Thread -{ -public: - URLConnectionState (NSURLRequest* req, const int maxRedirects) - : Thread ("http connection"), - request ([req retain]), - data ([[NSMutableData data] retain]), - numRedirectsToFollow (maxRedirects) - { - static DelegateClass cls; - delegate = [cls.createInstance() init]; - DelegateClass::setState (delegate, this); - } - - ~URLConnectionState() override - { - stop(); - - [connection release]; - [request release]; - [headers release]; - [delegate release]; - [data release]; - } - - bool start (WebInputStream& inputStream, WebInputStream::Listener* listener) - { - startThread(); - - while (isThreadRunning() && ! initialised) - { - if (listener != nullptr) - if (! listener->postDataSendProgress (inputStream, latestTotalBytes, (int) [[request HTTPBody] length])) - return false; - - Thread::sleep (1); - } - - return connection != nil && ! hasFailed; - } - - void stop() - { - { - const ScopedLock dLock (dataLock); - const ScopedLock connectionLock (createConnectionLock); - - hasBeenCancelled = true; - - if (connection != nil) - [connection cancel]; - } - - stopThread (10000); - } - - void cancel() - { - hasFinished = hasFailed = true; - stop(); - } - - int read (char* dest, int numBytes) - { - int numDone = 0; - - while (numBytes > 0) - { - const int available = jmin (numBytes, (int) [data length]); - - if (available > 0) - { - const ScopedLock sl (dataLock); - [data getBytes: dest length: (NSUInteger) available]; - [data replaceBytesInRange: NSMakeRange (0, (NSUInteger) available) withBytes: nil length: 0]; - - numDone += available; - numBytes -= available; - dest += available; - } - else - { - if (hasFailed || hasFinished) - break; - - Thread::sleep (1); - } - } - - return numDone; - } - - void didReceiveResponse (NSURLResponse* response) - { - { - const ScopedLock sl (dataLock); - [data setLength: 0]; - } - - contentLength = [response expectedContentLength]; - - [headers release]; - headers = nil; - - if ([response isKindOfClass: [NSHTTPURLResponse class]]) - { - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response; - headers = [[httpResponse allHeaderFields] retain]; - statusCode = (int) [httpResponse statusCode]; - } - - initialised = true; - } - - NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) - { - if (redirectResponse != nullptr) - { - if (numRedirects >= numRedirectsToFollow) - return nil; // Cancel redirect and allow connection to continue - - ++numRedirects; - } - - return newRequest; - } - - void didFailWithError (NSError* error) - { - DBG (nsStringToJuce ([error description])); ignoreUnused (error); - nsUrlErrorCode = [error code]; - hasFailed = true; - initialised = true; - signalThreadShouldExit(); - } - - void didReceiveData (NSData* newData) - { - const ScopedLock sl (dataLock); - [data appendData: newData]; - initialised = true; - } - - void didSendBodyData (NSInteger totalBytesWritten, NSInteger /*totalBytesExpected*/) - { - latestTotalBytes = static_cast (totalBytesWritten); - } - - void finishedLoading() - { - hasFinished = true; - initialised = true; - signalThreadShouldExit(); - } - - void run() override - { - { - const ScopedLock lock (createConnectionLock); - - if (hasBeenCancelled) - return; - - connection = [[NSURLConnection alloc] initWithRequest: request - delegate: delegate]; - } - - while (! threadShouldExit()) - { - JUCE_AUTORELEASEPOOL - { - [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; - } - } - } - - int64 contentLength = -1; - CriticalSection dataLock; - NSObject* delegate = nil; - NSURLRequest* request = nil; - NSURLConnection* connection = nil; - NSMutableData* data = nil; - NSDictionary* headers = nil; - NSInteger nsUrlErrorCode = 0; - int statusCode = 0; - std::atomic initialised { false }, hasFailed { false }, hasFinished { false }; - const int numRedirectsToFollow; - int numRedirects = 0; - int latestTotalBytes = 0; - CriticalSection createConnectionLock; - bool hasBeenCancelled = false; - -private: - //============================================================================== - struct DelegateClass : public ObjCClass - { - DelegateClass() : ObjCClass ("JUCENetworkDelegate_") - { - addIvar ("state"); - - addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse, "v@:@@"); - addMethod (@selector (connection:didFailWithError:), didFailWithError, "v@:@@"); - addMethod (@selector (connection:didReceiveData:), didReceiveData, "v@:@@"); - addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:), - connectionDidSendBodyData, "v@:@iii"); - addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@"); - addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest, "@@:@@@"); - - registerClass(); - } - - static void setState (id self, URLConnectionState* state) { object_setInstanceVariable (self, "state", state); } - static URLConnectionState* getState (id self) { return getIvar (self, "state"); } - - private: - static void didReceiveResponse (id self, SEL, NSURLConnection*, NSURLResponse* response) - { - getState (self)->didReceiveResponse (response); - } - - static void didFailWithError (id self, SEL, NSURLConnection*, NSError* error) - { - getState (self)->didFailWithError (error); - } - - static void didReceiveData (id self, SEL, NSURLConnection*, NSData* newData) - { - getState (self)->didReceiveData (newData); - } - - static NSURLRequest* willSendRequest (id self, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse* response) - { - return getState (self)->willSendRequest (request, response); - } - - static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) - { - getState (self)->didSendBodyData (totalBytesWritten, totalBytesExpected); - } - - static void connectionDidFinishLoading (id self, SEL, NSURLConnection*) - { - getState (self)->finishedLoading(); - } - }; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionState) -}; - -URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) -{ - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); -} - -#pragma clang diagnostic pop - -#endif - - //============================================================================== class WebInputStream::Pimpl { public: - Pimpl (WebInputStream& pimplOwner, const URL& urlToUse, bool shouldBePost) - : owner (pimplOwner), url (urlToUse), isPost (shouldBePost), - httpRequestCmd (shouldBePost ? "POST" : "GET") + Pimpl (WebInputStream& pimplOwner, const URL& urlToUse, bool addParametersToBody) + : owner (pimplOwner), + url (urlToUse), + addParametersToRequestBody (addParametersToBody), + hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()), + httpRequestCmd (hasBodyDataToSend ? "POST" : "GET") { } @@ -953,10 +954,8 @@ static void connectionDidFinishLoading (id self, SEL, NSURLConnection*) connection.reset(); } - bool connect (WebInputStream::Listener* webInputListener, int numRetries = 0) + bool connect (WebInputStream::Listener* webInputListener, [[maybe_unused]] int numRetries = 0) { - ignoreUnused (numRetries); - { const ScopedLock lock (createConnectionLock); @@ -971,28 +970,29 @@ bool connect (WebInputStream::Listener* webInputListener, int numRetries = 0) 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_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10)) - if (numRetries == 0 && connection->nsUrlErrorCode == NSURLErrorNetworkConnectionLost) - { - connection.reset(); + const auto errorCode = connection->getErrorCode(); + connection.reset(); + + if (@available (macOS 10.10, *)) + return false; + + // Workaround for macOS versions below 10.10 where HTTPS POST requests with keep-alive + // fail with the NSURLErrorNetworkConnectionLost error code. + if (numRetries == 0 && errorCode == NSURLErrorNetworkConnectionLost) return connect (webInputListener, ++numRetries); - } - #endif - connection.reset(); return false; } - if (connection->headers != nil) + if (auto* connectionHeaders = connection->getHeaders()) { - statusCode = connection->statusCode; + statusCode = connection->getStatusCode(); - NSEnumerator* enumerator = [connection->headers keyEnumerator]; + NSEnumerator* enumerator = [connectionHeaders keyEnumerator]; while (NSString* key = [enumerator nextObject]) responseHeaders.set (nsStringToJuce (key), - nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); + nsStringToJuce ((NSString*) [connectionHeaders objectForKey: key])); return true; } @@ -1032,10 +1032,9 @@ void withExtraHeaders (const String& extraHeaders) StringPairArray getResponseHeaders() const { return responseHeaders; } int getStatusCode() const { return statusCode; } - //============================================================================== - bool isError() const { return (connection == nullptr || connection->headers == nullptr); } - int64 getTotalLength() { return connection == nullptr ? -1 : connection->contentLength; } + bool isError() const { return (connection == nullptr || connection->getHeaders() == nullptr); } + int64 getTotalLength() { return connection == nullptr ? -1 : connection->getContentLength(); } bool isExhausted() { return finished; } int64 getPosition() { return position; } @@ -1083,12 +1082,12 @@ bool setPosition (int64 wantedPos) private: WebInputStream& owner; URL url; - std::unique_ptr connection; + std::unique_ptr connection; String headers; MemoryBlock postData; int64 position = 0; bool finished = false; - const bool isPost; + const bool addParametersToRequestBody, hasBodyDataToSend; int timeOutMs = 0; int numRedirectsToFollow = 5; String httpRequestCmd; @@ -1100,21 +1099,32 @@ void createConnection() { jassert (connection == nullptr); - if (NSURL* nsURL = [NSURL URLWithString: juceStringToNS (url.toString (! isPost))]) + if (NSURL* nsURL = [NSURL URLWithString: juceStringToNS (url.toString (! addParametersToRequestBody))]) { + const auto timeOutSeconds = [this] + { + if (timeOutMs > 0) + return timeOutMs / 1000.0; + + return timeOutMs < 0 ? std::numeric_limits::infinity() : 60.0; + }(); + if (NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: nsURL cachePolicy: NSURLRequestReloadIgnoringLocalCacheData - timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]) + timeoutInterval: timeOutSeconds]) { if (NSString* httpMethod = [NSString stringWithUTF8String: httpRequestCmd.toRawUTF8()]) { [req setHTTPMethod: httpMethod]; - if (isPost) + if (hasBodyDataToSend) { - WebInputStream::createHeadersAndPostData (url, headers, postData); + WebInputStream::createHeadersAndPostData (url, + headers, + postData, + addParametersToRequestBody); - if (postData.getSize() > 0) + if (! postData.isEmpty()) [req setHTTPBody: [NSData dataWithBytes: postData.getData() length: postData.getSize()]]; } @@ -1135,7 +1145,12 @@ void createConnection() // Workaround for an Apple bug. See https://github.com/AFNetworking/AFNetworking/issues/2334 [req HTTPBody]; - connection.reset (new URLConnectionState (req, numRedirectsToFollow)); + if (@available (macOS 10.10, *)) + connection = std::make_unique (req, numRedirectsToFollow); + #if JUCE_MAC + else + connection = std::make_unique (req, numRedirectsToFollow); + #endif } } } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Network_windows.cpp similarity index 87% rename from JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_Network_windows.cpp index 56a2048a..ca8ab26d 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_Network_windows.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -35,10 +35,14 @@ namespace juce class WebInputStream::Pimpl { public: - Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool shouldBePost) - : statusCode (0), owner (pimplOwner), url (urlToCopy), isPost (shouldBePost), - httpRequestCmd (isPost ? "POST" : "GET") - {} + Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool addParametersToBody) + : owner (pimplOwner), + url (urlToCopy), + addParametersToRequestBody (addParametersToBody), + hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()), + httpRequestCmd (hasBodyDataToSend ? "POST" : "GET") + { + } ~Pimpl() { @@ -75,7 +79,7 @@ class WebInputStream::Pimpl return false; } - String address = url.toString (! isPost); + auto address = url.toString (! addParametersToRequestBody); while (numRedirectsToFollow-- >= 0) { @@ -90,7 +94,7 @@ class WebInputStream::Pimpl { HeapBlock buffer (bufferSizeBytes); - if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) + if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, nullptr)) { StringArray headersArray; headersArray.addLines (String (reinterpret_cast (buffer.getData()))); @@ -116,7 +120,7 @@ class WebInputStream::Pimpl DWORD status = 0; DWORD statusSize = sizeof (status); - if (HttpQueryInfo (request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &statusSize, 0)) + if (HttpQueryInfo (request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &statusSize, nullptr)) { statusCode = (int) status; @@ -151,10 +155,10 @@ class WebInputStream::Pimpl break; } - return (request != 0); + return (request != nullptr); } - bool isError() const { return request == 0; } + bool isError() const { return request == nullptr; } bool isExhausted() { return finished; } int64 getPosition() { return position; } @@ -173,7 +177,14 @@ class WebInputStream::Pimpl int read (void* buffer, int bytesToRead) { - jassert (buffer != nullptr && bytesToRead >= 0); + jassert (bytesToRead >= 0); + + if (buffer == nullptr) + { + jassertfalse; + return 0; + } + DWORD bytesRead = 0; if (! (finished || isError())) @@ -207,7 +218,7 @@ class WebInputStream::Pimpl if (wantedPos != position) { finished = false; - position = (int64) InternetSetFilePointer (request, (LONG) wantedPos, 0, FILE_BEGIN, 0); + position = (int64) InternetSetFilePointer (request, (LONG) wantedPos, nullptr, FILE_BEGIN, 0); if (position == wantedPos) return true; @@ -226,18 +237,18 @@ class WebInputStream::Pimpl return true; } - int statusCode; + int statusCode = 0; private: //============================================================================== WebInputStream& owner; const URL url; - HINTERNET connection = 0, request = 0; + HINTERNET connection = nullptr, request = nullptr; String headers; MemoryBlock postData; int64 position = 0; bool finished = false; - const bool isPost; + const bool addParametersToRequestBody, hasBodyDataToSend; int timeOutMs = 0; String httpRequestCmd; int numRedirectsToFollow = 5; @@ -249,25 +260,25 @@ class WebInputStream::Pimpl { HINTERNET requestCopy = request; - request = 0; + request = nullptr; - if (requestCopy != 0) + if (requestCopy != nullptr) InternetCloseHandle (requestCopy); - if (connection != 0) + if (connection != nullptr) { InternetCloseHandle (connection); - connection = 0; + connection = nullptr; } } void createConnection (const String& address, WebInputStream::Listener* listener) { - static HINTERNET sessionHandle = InternetOpen (_T("juce"), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0); + static HINTERNET sessionHandle = InternetOpen (_T ("juce"), INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0); closeConnection(); - if (sessionHandle != 0) + if (sessionHandle != nullptr) { // break up the url.. const int fileNumChars = 65536; @@ -277,7 +288,7 @@ class WebInputStream::Pimpl HeapBlock file (fileNumChars), server (serverNumChars), username (usernameNumChars), password (passwordNumChars); - URL_COMPONENTS uc = { 0 }; + URL_COMPONENTS uc = {}; uc.dwStructSize = sizeof (uc); uc.lpszUrlPath = file; uc.dwUrlPathLength = fileNumChars; @@ -288,8 +299,11 @@ class WebInputStream::Pimpl uc.lpszPassword = password; uc.dwPasswordLength = passwordNumChars; - if (isPost) - WebInputStream::createHeadersAndPostData (url, headers, postData); + if (hasBodyDataToSend) + WebInputStream::createHeadersAndPostData (url, + headers, + postData, + addParametersToRequestBody); if (InternetCrackUrl (address.toWideCharPointer(), 0, 0, &uc)) openConnection (uc, sessionHandle, address, listener); @@ -319,7 +333,7 @@ class WebInputStream::Pimpl { const ScopedLock lock (createConnectionLock); - connection = hasBeenCancelled ? 0 + connection = hasBeenCancelled ? nullptr : InternetConnect (sessionHandle, uc.lpszHostName, uc.nPort, uc.lpszUserName, uc.lpszPassword, @@ -328,7 +342,7 @@ class WebInputStream::Pimpl 0, 0); } - if (connection != 0) + if (connection != nullptr) { if (isFtp) request = FtpOpenFile (connection, uc.lpszUrlPath, GENERIC_READ, @@ -345,7 +359,7 @@ class WebInputStream::Pimpl void sendHTTPRequest (INTERNET_BUFFERS& buffers, WebInputStream::Listener* listener) { - if (! HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0)) + if (! HttpSendRequestEx (request, &buffers, nullptr, HSR_INITIATE, 0)) return; int totalBytesSent = 0; @@ -362,7 +376,7 @@ class WebInputStream::Pimpl return; } - totalBytesSent += bytesSent; + totalBytesSent += (int) bytesSent; if (listener != nullptr && ! listener->postDataSendProgress (owner, totalBytesSent, (int) postData.getSize())) @@ -374,7 +388,7 @@ class WebInputStream::Pimpl void openHTTPConnection (URL_COMPONENTS& uc, const String& address, WebInputStream::Listener* listener) { - const TCHAR* mimeTypes[] = { _T("*/*"), nullptr }; + const TCHAR* mimeTypes[] = { _T ("*/*"), nullptr }; DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_AUTO_REDIRECT; @@ -386,14 +400,14 @@ class WebInputStream::Pimpl { const ScopedLock lock (createConnectionLock); - request = hasBeenCancelled ? 0 + request = hasBeenCancelled ? nullptr : HttpOpenRequest (connection, httpRequestCmd.toWideCharPointer(), - uc.lpszUrlPath, 0, 0, mimeTypes, flags, 0); + uc.lpszUrlPath, nullptr, nullptr, mimeTypes, flags, 0); } - if (request != 0) + if (request != nullptr) { - INTERNET_BUFFERS buffers = { 0 }; + INTERNET_BUFFERS buffers = {}; buffers.dwStructSize = sizeof (INTERNET_BUFFERS); buffers.lpcszHeader = headers.toWideCharPointer(); buffers.dwHeadersLength = (DWORD) headers.length(); @@ -403,7 +417,7 @@ class WebInputStream::Pimpl { sendHTTPRequest (buffers, listener); - if (HttpEndRequest (request, 0, 0, 0)) + if (HttpEndRequest (request, nullptr, 0, 0)) return true; return false; @@ -440,10 +454,10 @@ struct GetAdaptersAddressesHelper adaptersAddresses.malloc (1); ULONG len = sizeof (IP_ADAPTER_ADDRESSES); - if (getAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, adaptersAddresses, &len) == ERROR_BUFFER_OVERFLOW) + if (getAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, adaptersAddresses, &len) == ERROR_BUFFER_OVERFLOW) adaptersAddresses.malloc (len, 1); - return getAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, adaptersAddresses, &len) == NO_ERROR; + return getAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, adaptersAddresses, &len) == NO_ERROR; } HeapBlock adaptersAddresses; @@ -476,12 +490,12 @@ namespace MACAddressHelpers DynamicLibrary dll ("netapi32.dll"); JUCE_LOAD_WINAPI_FUNCTION (dll, Netbios, NetbiosCall, UCHAR, (PNCB)) - if (NetbiosCall != 0) + if (NetbiosCall != nullptr) { - LANA_ENUM enums = { 0 }; + LANA_ENUM enums = {}; { - NCB ncb = { 0 }; + NCB ncb = {}; ncb.ncb_command = NCBENUM; ncb.ncb_buffer = (unsigned char*) &enums; ncb.ncb_length = sizeof (LANA_ENUM); @@ -490,13 +504,13 @@ namespace MACAddressHelpers for (int i = 0; i < enums.length; ++i) { - NCB ncb2 = { 0 }; + NCB ncb2 = {}; ncb2.ncb_command = NCBRESET; ncb2.ncb_lana_num = enums.lana[i]; if (NetbiosCall (&ncb2) == 0) { - NCB ncb = { 0 }; + NCB ncb = {}; memcpy (ncb.ncb_callname, "* ", NCBNAMSZ); ncb.ncb_command = NCBASTAT; ncb.ncb_lana_num = enums.lana[i]; @@ -555,9 +569,9 @@ namespace MACAddressHelpers for (auto addr = start; addr != nullptr; addr = addr->Next) { if (addr->Address.lpSockaddr->sa_family == AF_INET) - result.addIfNotAlreadyThere (createAddress ((sockaddr_in*) addr->Address.lpSockaddr)); + result.addIfNotAlreadyThere (createAddress (unalignedPointerCast (addr->Address.lpSockaddr))); else if (addr->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6) - result.addIfNotAlreadyThere (createAddress ((sockaddr_in6*) addr->Address.lpSockaddr)); + result.addIfNotAlreadyThere (createAddress (unalignedPointerCast (addr->Address.lpSockaddr))); } } } @@ -608,11 +622,11 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailA if (mapiSendMail == nullptr) return false; - MapiMessage message = { 0 }; + MapiMessage message = {}; message.lpszSubject = (LPSTR) emailSubject.toRawUTF8(); message.lpszNoteText = (LPSTR) bodyText.toRawUTF8(); - MapiRecipDesc recip = { 0 }; + MapiRecipDesc recip = {}; recip.ulRecipClass = MAPI_TO; String targetEmailAddress_ (targetEmailAddress); if (targetEmailAddress_.isEmpty()) @@ -636,9 +650,9 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailA return mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS; } -URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) +std::unique_ptr URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options) { - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); + return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_ObjCHelpers_mac.h b/JuceLibraryCode/modules/juce_core/native/juce_ObjCHelpers_mac.h new file mode 100644 index 00000000..56da6804 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_ObjCHelpers_mac.h @@ -0,0 +1,555 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +/* This file contains a few helper functions that are used internally but which + need to be kept away from the public headers because they use obj-C symbols. +*/ +namespace juce +{ + +//============================================================================== +inline Range nsRangeToJuce (NSRange range) +{ + return { (int) range.location, (int) (range.location + range.length) }; +} + +inline NSRange juceRangeToNS (Range range) +{ + return NSMakeRange ((NSUInteger) range.getStart(), (NSUInteger) range.getLength()); +} + +inline String nsStringToJuce (NSString* s) +{ + return CharPointer_UTF8 ([s UTF8String]); +} + +inline NSString* juceStringToNS (const String& s) +{ + // This cast helps linters determine nullability + return (NSString* _Nonnull) [NSString stringWithUTF8String: s.toUTF8()]; +} + +inline NSString* nsStringLiteral (const char* const s) noexcept +{ + jassert (s != nullptr); + + // This cast helps linters determine nullability + return (NSString* _Nonnull) [NSString stringWithUTF8String: s]; +} + +inline NSString* nsEmptyString() noexcept +{ + // This cast helps linters determine nullability + return (NSString* _Nonnull) [NSString string]; +} + +inline NSURL* createNSURLFromFile (const String& f) +{ + return [NSURL fileURLWithPath: juceStringToNS (f)]; +} + +inline NSURL* createNSURLFromFile (const File& f) +{ + return createNSURLFromFile (f.getFullPathName()); +} + +inline NSArray* createNSArrayFromStringArray (const StringArray& strings) +{ + auto array = [[NSMutableArray alloc] initWithCapacity: (NSUInteger) strings.size()]; + + for (const auto& string: strings) + [array addObject: juceStringToNS (string)]; + + return [array autorelease]; +} + +inline NSData* varToJsonData (const var& varToParse) +{ + return [juceStringToNS (JSON::toString (varToParse)) dataUsingEncoding: NSUTF8StringEncoding]; +} + +inline var jsonDataToVar (NSData* jsonData) +{ + auto* jsonString = [[NSString alloc] initWithData: jsonData + encoding: NSUTF8StringEncoding]; + + jassert (jsonString != nullptr); + return JSON::parse (nsStringToJuce ([jsonString autorelease])); +} + +// If for any reason the given var cannot be converted into a valid dictionary +// an empty dictionary will be returned instead +inline NSDictionary* varToNSDictionary (const var& varToParse) +{ + NSError* error { nullptr }; + NSDictionary* dictionary = [NSJSONSerialization JSONObjectWithData: varToJsonData (varToParse) + options: NSJSONReadingMutableContainers + error: &error]; + + if (dictionary == nullptr || error != nullptr) + return @{}; + + return dictionary; +} + +inline NSData* jsonObjectToData (const NSObject* jsonObject) +{ + NSError* error { nullptr }; + auto* jsonData = [NSJSONSerialization dataWithJSONObject: jsonObject + options: 0 + error: &error]; + + jassert (error == nullptr); + jassert (jsonData != nullptr); + + return jsonData; +} + +inline var nsDictionaryToVar (const NSDictionary* dictionary) +{ + return jsonDataToVar (jsonObjectToData (dictionary)); +} + +#if JUCE_MAC +template +NSRect makeNSRect (const RectangleType& r) noexcept +{ + return NSMakeRect (static_cast (r.getX()), + static_cast (r.getY()), + static_cast (r.getWidth()), + static_cast (r.getHeight())); +} +#endif + +#if JUCE_INTEL + template + struct NeedsStret + { + #if JUCE_32BIT + static constexpr auto value = sizeof (T) > 8; + #else + static constexpr auto value = sizeof (T) > 16; + #endif + }; + + template <> + struct NeedsStret { static constexpr auto value = false; }; + + template ::value> + struct MetaSuperFn { static constexpr auto value = objc_msgSendSuper_stret; }; + + template + struct MetaSuperFn { static constexpr auto value = objc_msgSendSuper; }; +#else + template + struct MetaSuperFn { static constexpr auto value = objc_msgSendSuper; }; +#endif + +template +inline ReturnType ObjCMsgSendSuper (id self, SEL sel, Params... params) +{ + using SuperFn = ReturnType (*) (struct objc_super*, SEL, Params...); + const auto fn = reinterpret_cast (MetaSuperFn::value); + + objc_super s = { self, [SuperType class] }; + return fn (&s, sel, params...); +} + +//============================================================================== +struct NSObjectDeleter +{ + void operator() (NSObject* object) const noexcept + { + if (object != nullptr) + [object release]; + } +}; + +template +using NSUniquePtr = std::unique_ptr; + +/* This has very similar semantics to NSUniquePtr, with the main difference that it doesn't + automatically add a pointer to the managed type. This makes it possible to declare + scoped handles to id or block types. +*/ +template +class ObjCObjectHandle +{ +public: + ObjCObjectHandle() = default; + + // Note that this does *not* retain the argument. + explicit ObjCObjectHandle (T ptr) : item (ptr) {} + + ~ObjCObjectHandle() noexcept { reset(); } + + ObjCObjectHandle (const ObjCObjectHandle& other) + : item (other.item) + { + if (item != nullptr) + [item retain]; + } + + ObjCObjectHandle& operator= (const ObjCObjectHandle& other) + { + auto copy = other; + swap (copy); + return *this; + } + + ObjCObjectHandle (ObjCObjectHandle&& other) noexcept { swap (other); } + + ObjCObjectHandle& operator= (ObjCObjectHandle&& other) noexcept + { + reset(); + swap (other); + return *this; + } + + // Note that this does *not* retain the argument. + void reset (T ptr) { *this = ObjCObjectHandle { ptr }; } + + T get() const { return item; } + + void reset() + { + if (item != nullptr) + [item release]; + + item = {}; + } + + bool operator== (const ObjCObjectHandle& other) const { return item == other.item; } + bool operator!= (const ObjCObjectHandle& other) const { return ! (*this == other); } + + bool operator== (std::nullptr_t) const { return item == nullptr; } + bool operator!= (std::nullptr_t) const { return ! (*this == nullptr); } + +private: + void swap (ObjCObjectHandle& other) noexcept { std::swap (other.item, item); } + + T item{}; +}; + +//============================================================================== +namespace detail +{ + constexpr auto makeCompileTimeStr() + { + return std::array { { '\0' } }; + } + + template + constexpr auto joinCompileTimeStrImpl (A&& a, std::index_sequence, + B&& b, std::index_sequence) + { + return std::array { { a[As]..., b[Bs]..., '\0' } }; + } + + template + constexpr auto joinCompileTimeStr (const char (&a)[A], std::array b) + { + return joinCompileTimeStrImpl (a, std::make_index_sequence(), + b, std::make_index_sequence()); + } + + template + constexpr auto makeCompileTimeStr (const char (&v)[A], Others&&... others) + { + return joinCompileTimeStr (v, makeCompileTimeStr (others...)); + } +} // namespace detail + +//============================================================================== +template +inline Type getIvar (id self, const char* name) +{ + void* v = nullptr; + object_getInstanceVariable (self, name, &v); + return static_cast (v); +} + +template +struct ObjCClass +{ + ObjCClass (const char* nameRoot) + : cls (objc_allocateClassPair ([SuperclassType class], getRandomisedName (nameRoot).toUTF8(), 0)) + { + // The class could not be created. Is the name already in use? + jassert (cls != nil); + } + + virtual ~ObjCClass() + { + auto kvoSubclassName = String ("NSKVONotifying_") + class_getName (cls); + + if (objc_getClass (kvoSubclassName.toUTF8()) == nullptr) + objc_disposeClassPair (cls); + } + + void registerClass() + { + if (cls != nil) + objc_registerClassPair (cls); + } + + SuperclassType* createInstance() const + { + return class_createInstance (cls, 0); + } + + template + void addIvar (const char* name) + { + [[maybe_unused]] BOOL b = class_addIvar (cls, name, sizeof (Type), (uint8_t) rint (log2 (sizeof (Type))), @encode (Type)); + jassert (b); + } + + template + void addMethod (SEL selector, Fn callbackFn) { addMethod (selector, toFnPtr (callbackFn)); } + + template + void addMethod (SEL selector, Result (*callbackFn) (id, SEL, Args...)) + { + const auto s = detail::makeCompileTimeStr (@encode (Result), @encode (id), @encode (SEL), @encode (Args)...); + [[maybe_unused]] const auto b = class_addMethod (cls, selector, (IMP) callbackFn, s.data()); + jassert (b); + } + + void addProtocol (Protocol* protocol) + { + [[maybe_unused]] BOOL b = class_addProtocol (cls, protocol); + jassert (b); + } + + template + static ReturnType sendSuperclassMessage (id self, SEL sel, Params... params) + { + return ObjCMsgSendSuper (self, sel, params...); + } + + Class cls; + +private: + static String getRandomisedName (const char* root) + { + return root + String::toHexString (juce::Random::getSystemRandom().nextInt64()); + } + + JUCE_DECLARE_NON_COPYABLE (ObjCClass) +}; + +//============================================================================== +#ifndef DOXYGEN +template +struct ObjCLifetimeManagedClass : public ObjCClass +{ + ObjCLifetimeManagedClass() + : ObjCClass ("ObjCLifetimeManagedClass_") + { + addIvar ("cppObject"); + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + addMethod (@selector (initWithJuceObject:), initWithJuceObject); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + addMethod (@selector (dealloc), dealloc); + + registerClass(); + } + + static id initWithJuceObject (id _self, SEL, JuceClass* obj) + { + NSObject* self = sendSuperclassMessage (_self, @selector (init)); + object_setInstanceVariable (self, "cppObject", obj); + + return self; + } + + static void dealloc (id _self, SEL) + { + if (auto* obj = getIvar (_self, "cppObject")) + { + delete obj; + object_setInstanceVariable (_self, "cppObject", nullptr); + } + + sendSuperclassMessage (_self, @selector (dealloc)); + } + + static ObjCLifetimeManagedClass objCLifetimeManagedClass; +}; + +template +ObjCLifetimeManagedClass ObjCLifetimeManagedClass::objCLifetimeManagedClass; +#endif + +// this will return an NSObject which takes ownership of the JUCE instance passed-in +// This is useful to tie the life-time of a juce instance to the life-time of an NSObject +template +NSObject* createNSObjectFromJuceClass (Class* obj) +{ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wobjc-method-access") + return [ObjCLifetimeManagedClass::objCLifetimeManagedClass.createInstance() initWithJuceObject:obj]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE +} + +// Get the JUCE class instance that was tied to the life-time of an NSObject with the +// function above +template +Class* getJuceClassFromNSObject (NSObject* obj) +{ + return obj != nullptr ? getIvar (obj, "cppObject") : nullptr; +} + +namespace detail +{ +template struct Signature; +template struct Signature {}; + +template +constexpr auto getSignature (Result (Class::*) (Args...)) { return Signature{}; } + +template +constexpr auto getSignature (Result (Class::*) (Args...) const) { return Signature{}; } + +template +auto createObjCBlockImpl (Class* object, Fn func, Signature) +{ + return [[^Result (Params... params) { return (object->*func) (params...); } copy] autorelease]; +} +} // namespace detail + +/* Creates an Obj-C block automatically from a member function. */ +template +auto CreateObjCBlock (Class* object, MemberFunc fn) +{ + return detail::createObjCBlockImpl (object, fn, detail::getSignature (fn)); +} + +/* Automatically copies and releases a block, a bit like a smart pointer for an Obj-C block. + + This is helpful to automatically manage the lifetime of blocks, e.g. if you need to keep a block + around to be used later. This is the case in the AudioUnit API, where the host may provide a + musicalContextBlock that can be called by the plugin during rendering. Copying blocks isn't + realtime-safe, so the plugin must cache the block before rendering. + + If you're just creating blocks to pass them directly to an Obj-C API, you probably won't need to + use this type. +*/ +template +class ObjCBlock +{ +public: + ObjCBlock() = default; + + ObjCBlock (BlockType b) + : block ([b copy]) {} + + ObjCBlock (const ObjCBlock& other) + : block (other.block != nullptr ? [other.block copy] : nullptr) {} + + ObjCBlock& operator= (const BlockType& other) + { + ObjCBlock { other }.swap (*this); + return *this; + } + + ~ObjCBlock() noexcept + { + if (block != nullptr) + [block release]; + } + + bool operator== (BlockType ptr) const { return block == ptr; } + bool operator!= (BlockType ptr) const { return block != ptr; } + + operator BlockType() const { return block; } + + void swap (ObjCBlock& other) noexcept + { + std::swap (other.block, block); + } + +private: + BlockType block = nullptr; +}; + +//============================================================================== +class ScopedNotificationCenterObserver +{ +public: + ScopedNotificationCenterObserver() = default; + + ScopedNotificationCenterObserver (id observerIn, + SEL selector, + NSNotificationName nameIn, + id objectIn, + Class klassIn = [NSNotificationCenter class]) + : observer (observerIn), name (nameIn), object (objectIn), klass (klassIn) + { + [[klass defaultCenter] addObserver: observer + selector: selector + name: name + object: object]; + } + + ~ScopedNotificationCenterObserver() + { + if (observer != nullptr && name != nullptr) + { + [[klass defaultCenter] removeObserver: observer + name: name + object: object]; + } + } + + ScopedNotificationCenterObserver (ScopedNotificationCenterObserver&& other) noexcept + { + swap (other); + } + + ScopedNotificationCenterObserver& operator= (ScopedNotificationCenterObserver&& other) noexcept + { + ScopedNotificationCenterObserver (std::move (other)).swap (*this); + return *this; + } + + ScopedNotificationCenterObserver (const ScopedNotificationCenterObserver&) = delete; + ScopedNotificationCenterObserver& operator= (const ScopedNotificationCenterObserver&) = delete; + +private: + void swap (ScopedNotificationCenterObserver& other) noexcept + { + std::swap (other.observer, observer); + std::swap (other.name, name); + std::swap (other.object, object); + std::swap (other.klass, klass); + } + + id observer = nullptr; + NSNotificationName name = nullptr; + id object = nullptr; + Class klass = nullptr; +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_ObjCHelpers_mac_test.mm b/JuceLibraryCode/modules/juce_core/native/juce_ObjCHelpers_mac_test.mm new file mode 100644 index 00000000..21dc5d66 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_ObjCHelpers_mac_test.mm @@ -0,0 +1,87 @@ +namespace juce +{ + +class ObjCHelpersTest final : public UnitTest +{ +public: + ObjCHelpersTest() : UnitTest { "ObjCHelpers", UnitTestCategories::native } {} + + void runTest() final + { + beginTest ("Range"); + { + constexpr auto start = 10; + constexpr auto length = 20; + + const auto juceRange = Range::withStartAndLength (start, length); + const auto nsRange = NSMakeRange (start, length); + + expect (nsRangeToJuce (nsRange) == juceRange); + expect (NSEqualRanges (nsRange, juceRangeToNS (juceRange))); + } + + beginTest ("String"); + { + String juceString { "Hello world!" }; + NSString *nsString { @"Hello world!" }; + + expect (nsStringToJuce (nsString) == juceString); + expect ([nsString isEqualToString: juceStringToNS (juceString)]); + expect ([nsString isEqualToString: nsStringLiteral ("Hello world!")]); + } + + beginTest ("StringArray"); + { + const StringArray stringArray { "Hello world!", "this", "is", "a", "test" }; + NSArray *nsArray { @[@"Hello world!", @"this", @"is", @"a", @"test"] }; + + expect ([nsArray isEqualToArray: createNSArrayFromStringArray (stringArray)]); + } + + beginTest ("Dictionary"); + { + DynamicObject::Ptr data { new DynamicObject() }; + data->setProperty ("integer", 1); + data->setProperty ("double", 2.3); + data->setProperty ("boolean", true); + data->setProperty ("string", "Hello world!"); + + Array array { 45, 67.8, true, "Hello array!" }; + data->setProperty ("array", array); + + const auto* nsDictionary = varToNSDictionary (data.get()); + expect (nsDictionary != nullptr); + + const auto clone = nsDictionaryToVar (nsDictionary); + expect (clone.isObject()); + + expect (clone.getProperty ("integer", {}).isInt()); + expect (clone.getProperty ("double", {}).isDouble()); + expect (clone.getProperty ("boolean", {}).isBool()); + expect (clone.getProperty ("string", {}).isString()); + expect (clone.getProperty ("array", {}).isArray()); + + expect (clone.getProperty ("integer", {}) == var { 1 }); + expect (clone.getProperty ("double", {}) == var { 2.3 }); + expect (clone.getProperty ("boolean", {}) == var { true }); + expect (clone.getProperty ("string", {}) == var { "Hello world!" }); + expect (clone.getProperty ("array", {}) == var { array }); + } + + beginTest ("varToNSDictionary converts a void variant to an empty dictionary"); + { + var voidVariant; + + const auto* nsDictionary = varToNSDictionary (voidVariant); + expect (nsDictionary != nullptr); + + const auto result = nsDictionaryToVar (nsDictionary); + expect (result.isObject()); + expect (result.getDynamicObject()->getProperties().isEmpty()); + } + } +}; + +static ObjCHelpersTest objCHelpersTest; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Misc.cpp b/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimerListener.h similarity index 83% rename from JuceLibraryCode/modules/juce_core/native/juce_android_Misc.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_PlatformTimerListener.h index f43c5453..21832a15 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Misc.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimerListener.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,9 +23,10 @@ namespace juce { -void Logger::outputDebugString (const String& text) +struct PlatformTimerListener { - __android_log_print (ANDROID_LOG_INFO, "JUCE", "%s", text.toUTF8().getAddress()); -} + virtual ~PlatformTimerListener() = default; + virtual void onTimerExpired() = 0; +}; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimer_generic.cpp b/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimer_generic.cpp new file mode 100644 index 00000000..7a2ff45c --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimer_generic.cpp @@ -0,0 +1,149 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 PlatformTimer final : private Thread +{ +public: + explicit PlatformTimer (PlatformTimerListener& ptl) + : Thread { "HighResolutionTimerThread" }, + listener { ptl } + { + startThread (Priority::highest); + } + + ~PlatformTimer() + { + stopThread (-1); + } + + void startTimer (int newIntervalMs) + { + jassert (newIntervalMs > 0); + jassert (timer == nullptr); + + { + std::scoped_lock lock { runCopyMutex }; + timer = std::make_shared (listener, newIntervalMs); + } + + notify(); + } + + void cancelTimer() + { + jassert (timer != nullptr); + + timer->cancel(); + + // Note the only race condition we need to protect against + // here is the copy in run(). + // + // Calls to startTimer(), cancelTimer(), and getIntervalMs() + // are already guaranteed to be both thread safe and well + // synchronised. + + std::scoped_lock lock { runCopyMutex }; + timer = nullptr; + } + + int getIntervalMs() const + { + return isThreadRunning() && timer != nullptr ? timer->getIntervalMs() : 0; + } + +private: + void run() final + { + const auto copyTimer = [&] + { + std::scoped_lock lock { runCopyMutex }; + return timer; + }; + + while (! threadShouldExit()) + { + if (auto t = copyTimer()) + t->run(); + + wait (-1); + } + } + + class Timer + { + public: + Timer (PlatformTimerListener& l, int i) + : listener { l }, intervalMs { i } {} + + int getIntervalMs() const + { + return intervalMs; + } + + void cancel() + { + stop.signal(); + } + + void run() + { + #if JUCE_MAC || JUCE_IOS + tryToUpgradeCurrentThreadToRealtime (Thread::RealtimeOptions{}.withPeriodMs (intervalMs)); + #endif + + const auto millisecondsUntil = [] (auto time) + { + return jmax (0.0, time - Time::getMillisecondCounterHiRes()); + }; + + while (! stop.wait (millisecondsUntil (nextEventTime))) + { + if (Time::getMillisecondCounterHiRes() >= nextEventTime) + { + listener.onTimerExpired(); + nextEventTime += intervalMs; + } + } + } + + private: + PlatformTimerListener& listener; + const int intervalMs; + double nextEventTime = Time::getMillisecondCounterHiRes() + intervalMs; + WaitableEvent stop { true }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Timer) + JUCE_DECLARE_NON_MOVEABLE (Timer) + }; + + PlatformTimerListener& listener; + mutable std::mutex runCopyMutex; + std::shared_ptr timer; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PlatformTimer) + JUCE_DECLARE_NON_MOVEABLE (PlatformTimer) +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimer_windows.cpp b/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimer_windows.cpp new file mode 100644 index 00000000..c6ceae28 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimer_windows.cpp @@ -0,0 +1,72 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 PlatformTimer final + { + public: + explicit PlatformTimer(PlatformTimerListener& ptl) + : listener{ ptl } {} + + void startTimer(int newIntervalMs) + { + jassert(newIntervalMs > 0); + + // Use the static callback + timerId = timeSetEvent((UINT)newIntervalMs, 1, &PlatformTimer::timerCallback, (DWORD_PTR)&listener, TIME_PERIODIC | TIME_CALLBACK_FUNCTION); + intervalMs = timerId != 0 ? newIntervalMs : 0; + } + + void cancelTimer() + { + jassert(timerId != 0); + + timeKillEvent(timerId); + timerId = 0; + intervalMs = 0; + } + + int getIntervalMs() const + { + return intervalMs; + } + + private: + PlatformTimerListener& listener; + UINT timerId{ 0 }; + int intervalMs{ 0 }; + + // Define the static callback here + static void __stdcall timerCallback(UINT, UINT, DWORD_PTR context, DWORD_PTR, DWORD_PTR) + { + auto* listener = reinterpret_cast(context); + if (listener != nullptr) + listener->onTimerExpired(); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PlatformTimer) + JUCE_DECLARE_NON_MOVEABLE(PlatformTimer) + }; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Process_mac.mm b/JuceLibraryCode/modules/juce_core/native/juce_Process_mac.mm new file mode 100644 index 00000000..5fab5234 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_Process_mac.mm @@ -0,0 +1,36 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +#if JUCE_MAC +void Process::setDockIconVisible (bool isVisible) +{ + ProcessSerialNumber psn { 0, kCurrentProcess }; + + [[maybe_unused]] OSStatus err = TransformProcessType (&psn, isVisible ? kProcessTransformToForegroundApplication + : kProcessTransformToUIElementApplication); + jassert (err == 0); +} +#endif + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Registry_windows.cpp similarity index 96% rename from JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_Registry_windows.cpp index dfa4b835..3aebc96d 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_Registry_windows.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -40,8 +40,8 @@ struct RegistryKeyWrapper DWORD result; if (createForWriting) - RegCreateKeyEx (rootKey, wideCharName, 0, 0, REG_OPTION_NON_VOLATILE, - KEY_WRITE | KEY_QUERY_VALUE | wow64Flags, 0, &key, &result); + RegCreateKeyEx (rootKey, wideCharName, 0, nullptr, REG_OPTION_NON_VOLATILE, + KEY_WRITE | KEY_QUERY_VALUE | wow64Flags, nullptr, &key, &result); else RegOpenKeyEx (rootKey, wideCharName, 0, KEY_READ | wow64Flags, &key); } @@ -65,7 +65,7 @@ struct RegistryKeyWrapper if (name.startsWithIgnoreCase ("HKU\\")) return HKEY_USERS; jassertfalse; // The name starts with an unknown root key (or maybe an old Win9x type) - return 0; + return nullptr; } static bool setValue (const String& regValuePath, const DWORD type, @@ -90,7 +90,7 @@ struct RegistryKeyWrapper result.setSize (bufferSize, false); DWORD type = REG_NONE; - auto err = RegQueryValueEx (key.key, key.wideCharValueName, 0, &type, + auto err = RegQueryValueEx (key.key, key.wideCharValueName, nullptr, &type, (LPBYTE) result.getData(), &bufferSize); if (err == ERROR_SUCCESS) @@ -138,7 +138,7 @@ struct RegistryKeyWrapper DWORD type = 0; auto result = RegQueryValueEx (key.key, key.wideCharValueName, - 0, &type, buffer, &bufferSize); + nullptr, &type, buffer, &bufferSize); return result == ERROR_SUCCESS || result == ERROR_MORE_DATA; } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_RuntimePermissions.cpp b/JuceLibraryCode/modules/juce_core/native/juce_RuntimePermissions_android.cpp similarity index 54% rename from JuceLibraryCode/modules/juce_core/native/juce_android_RuntimePermissions.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_RuntimePermissions_android.cpp index faa88cba..b6d9d734 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_RuntimePermissions.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_RuntimePermissions_android.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -24,15 +24,51 @@ namespace juce { //============================================================================== -static String jucePermissionToAndroidPermission (RuntimePermissions::PermissionID permission) +static StringArray jucePermissionToAndroidPermissions (RuntimePermissions::PermissionID permission) { + const auto externalStorageOrMedia = [] (const auto* newPermission) + { + return getAndroidSDKVersion() < 33 ? "android.permission.READ_EXTERNAL_STORAGE" : newPermission; + }; + switch (permission) { - case RuntimePermissions::recordAudio: return "android.permission.RECORD_AUDIO"; - case RuntimePermissions::bluetoothMidi: return "android.permission.ACCESS_COARSE_LOCATION"; - case RuntimePermissions::readExternalStorage: return "android.permission.READ_EXTERNAL_STORAGE"; - case RuntimePermissions::writeExternalStorage: return "android.permission.WRITE_EXTERNAL_STORAGE"; - case RuntimePermissions::camera: return "android.permission.CAMERA"; + case RuntimePermissions::recordAudio: return { "android.permission.RECORD_AUDIO" }; + case RuntimePermissions::bluetoothMidi: + { + if (getAndroidSDKVersion() < 31) + return { "android.permission.ACCESS_FINE_LOCATION" }; + + return { "android.permission.BLUETOOTH_SCAN", + "android.permission.BLUETOOTH_CONNECT" }; + } + + // WRITE_EXTERNAL_STORAGE has no effect on SDK 29+ + case RuntimePermissions::writeExternalStorage: + return getAndroidSDKVersion() < 29 ? StringArray { "android.permission.WRITE_EXTERNAL_STORAGE" } + : StringArray{}; + + case RuntimePermissions::camera: return { "android.permission.CAMERA" }; + + case RuntimePermissions::readExternalStorage: + { + // See: https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE + if (getAndroidSDKVersion() < 33) + return { "android.permission.READ_EXTERNAL_STORAGE" }; + + return { "android.permission.READ_MEDIA_AUDIO", + "android.permission.READ_MEDIA_IMAGES", + "android.permission.READ_MEDIA_VIDEO" }; + } + + case RuntimePermissions::readMediaAudio: + return { externalStorageOrMedia ("android.permission.READ_MEDIA_AUDIO") }; + + case RuntimePermissions::readMediaImages: + return { externalStorageOrMedia ("android.permission.READ_MEDIA_IMAGES") }; + + case RuntimePermissions::readMediaVideo: + return { externalStorageOrMedia ("android.permission.READ_MEDIA_VIDEO") }; } // invalid permission @@ -42,57 +78,33 @@ static String jucePermissionToAndroidPermission (RuntimePermissions::PermissionI static RuntimePermissions::PermissionID androidPermissionToJucePermission (const String& permission) { - if (permission == "android.permission.RECORD_AUDIO") return RuntimePermissions::recordAudio; - else if (permission == "android.permission.ACCESS_COARSE_LOCATION") return RuntimePermissions::bluetoothMidi; - else if (permission == "android.permission.READ_EXTERNAL_STORAGE") return RuntimePermissions::readExternalStorage; - else if (permission == "android.permission.WRITE_EXTERNAL_STORAGE") return RuntimePermissions::writeExternalStorage; - else if (permission == "android.permission.CAMERA") return RuntimePermissions::camera; + static const std::map map + { + { "android.permission.RECORD_AUDIO", RuntimePermissions::recordAudio }, + { "android.permission.ACCESS_FINE_LOCATION", RuntimePermissions::bluetoothMidi }, + { "android.permission.READ_EXTERNAL_STORAGE", RuntimePermissions::readExternalStorage }, + { "android.permission.WRITE_EXTERNAL_STORAGE", RuntimePermissions::writeExternalStorage }, + { "android.permission.CAMERA", RuntimePermissions::camera }, + { "android.permission.READ_MEDIA_AUDIO", RuntimePermissions::readMediaAudio }, + { "android.permission.READ_MEDIA_IMAGES", RuntimePermissions::readMediaImages }, + { "android.permission.READ_MEDIA_VIDEO", RuntimePermissions::readMediaVideo }, + { "android.permission.BLUETOOTH_SCAN", RuntimePermissions::bluetoothMidi }, + }; - return static_cast (-1); + const auto iter = map.find (permission); + return iter != map.cend() ? iter->second + : static_cast (-1); } //============================================================================== struct PermissionsRequest { - PermissionsRequest() {} - - // using "= default" on the following method triggers an internal compiler error - // in Android NDK 17 - PermissionsRequest (const PermissionsRequest& o) - : callback (o.callback), permission (o.permission) - {} - - PermissionsRequest (PermissionsRequest&& o) - : callback (std::move (o.callback)), permission (o.permission) - { - o.permission = static_cast (-1); - } - - PermissionsRequest (RuntimePermissions::Callback && callbackToUse, - RuntimePermissions::PermissionID permissionToRequest) - : callback (std::move (callbackToUse)), permission (permissionToRequest) - {} - - PermissionsRequest& operator= (const PermissionsRequest & o) - { - callback = o.callback; - permission = o.permission; - return *this; - } - - PermissionsRequest& operator= (PermissionsRequest && o) - { - callback = std::move (o.callback); - permission = o.permission; - return *this; - } - RuntimePermissions::Callback callback; - RuntimePermissions::PermissionID permission; + RuntimePermissions::PermissionID permission = static_cast (-1); }; //============================================================================== -struct PermissionsOverlay : FragmentOverlay +struct PermissionsOverlay final : public FragmentOverlay { PermissionsOverlay (CriticalSection& cs) : overlayGuard (cs) {} ~PermissionsOverlay() override = default; @@ -165,19 +177,18 @@ struct PermissionsOverlay : FragmentOverlay { auto &request = requests.front(); - StringArray permissionsArray{ - jucePermissionToAndroidPermission (request.permission)}; + auto permissionsArray = jucePermissionToAndroidPermissions (request.permission); auto jPermissionsArray = juceStringArrayToJava (permissionsArray); auto requestPermissionsMethodID - = env->GetMethodID(AndroidFragment, "requestPermissions", "([Ljava/lang/String;I)V"); + = env->GetMethodID (AndroidFragment, "requestPermissions", "([Ljava/lang/String;I)V"); // this code should only be reached for SDKs >= 23, so this method should be // be available - jassert(requestPermissionsMethodID != nullptr); + jassert (requestPermissionsMethodID != nullptr); - env->CallVoidMethod (getNativeHandle(), requestPermissionsMethodID, jPermissionsArray.get (), 0); + env->CallVoidMethod (getNativeHandle(), requestPermissionsMethodID, jPermissionsArray.get(), 0); } else { @@ -199,9 +210,16 @@ struct PermissionsOverlay : FragmentOverlay //============================================================================== void RuntimePermissions::request (PermissionID permission, Callback callback) { - auto requestedPermission = jucePermissionToAndroidPermission (permission); + const auto requestedPermissions = jucePermissionToAndroidPermissions (permission); + + const auto allPermissionsInManifest = std::all_of (requestedPermissions.begin(), + requestedPermissions.end(), + [] (const auto& p) + { + return isPermissionDeclaredInManifest (p); + }); - if (! isPermissionDeclaredInManifest (requestedPermission)) + if (! allPermissionsInManifest) { // Error! If you want to be able to request this runtime permission, you // also need to declare it in your app's manifest. You can do so via @@ -220,7 +238,7 @@ void RuntimePermissions::request (PermissionID permission, Callback callback) return; } - PermissionsRequest request (std::move (callback), permission); + PermissionsRequest request { std::move (callback), permission }; static CriticalSection overlayGuard; ScopedLock lock (overlayGuard); @@ -250,12 +268,15 @@ bool RuntimePermissions::isGranted (PermissionID permission) { auto* env = getEnv(); - auto requestedPermission = jucePermissionToAndroidPermission (permission); - int result = env->CallIntMethod (getAppContext().get(), AndroidContext.checkCallingOrSelfPermission, - javaString (requestedPermission).get()); + const auto requestedPermissions = jucePermissionToAndroidPermissions (permission); + return std::all_of (requestedPermissions.begin(), requestedPermissions.end(), [env] (const auto& p) + { + return 0 == env->CallIntMethod (getAppContext().get(), + AndroidContext.checkCallingOrSelfPermission, + javaString (p).get()); + }); - return result == 0 /* PERMISSION_GRANTED */; } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SharedCode_intel.h b/JuceLibraryCode/modules/juce_core/native/juce_SharedCode_intel.h new file mode 100644 index 00000000..492920d0 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_SharedCode_intel.h @@ -0,0 +1,103 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#if JUCE_INTEL && ! JUCE_NO_INLINE_ASM + +namespace juce::SystemStatsHelpers +{ + +static void doCPUID (uint32& a, uint32& b, uint32& c, uint32& d, uint32 type) +{ + uint32 la = a, lb = b, lc = c, ld = d; + + #if JUCE_32BIT && defined (__pic__) + asm ("mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a" (la), "=D" (lb), "=c" (lc), "=d" (ld) + : "a" (type), "c" (0)); + #else + asm ("cpuid\n" + : "=a" (la), "=b" (lb), "=c" (lc), "=d" (ld) + : "a" (type), "c" (0)); + #endif + + a = la; b = lb; c = lc; d = ld; +} + +static void getCPUInfo (bool& hasMMX, + bool& hasSSE, + bool& hasSSE2, + bool& has3DNow, + bool& hasSSE3, + bool& hasSSSE3, + bool& hasFMA3, + bool& hasSSE41, + bool& hasSSE42, + bool& hasAVX, + bool& hasFMA4, + bool& hasAVX2, + bool& hasAVX512F, + bool& hasAVX512DQ, + bool& hasAVX512IFMA, + bool& hasAVX512PF, + bool& hasAVX512ER, + bool& hasAVX512CD, + bool& hasAVX512BW, + bool& hasAVX512VL, + bool& hasAVX512VBMI, + bool& hasAVX512VPOPCNTDQ) +{ + uint32 a = 0, b = 0, d = 0, c = 0; + SystemStatsHelpers::doCPUID (a, b, c, d, 1); + + hasMMX = (d & (1u << 23)) != 0; + hasSSE = (d & (1u << 25)) != 0; + hasSSE2 = (d & (1u << 26)) != 0; + 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; + hasAVX512DQ = (b & (1u << 17)) != 0; + hasAVX512IFMA = (b & (1u << 21)) != 0; + hasAVX512PF = (b & (1u << 26)) != 0; + hasAVX512ER = (b & (1u << 27)) != 0; + hasAVX512CD = (b & (1u << 28)) != 0; + hasAVX512BW = (b & (1u << 30)) != 0; + hasAVX512VL = (b & (1u << 31)) != 0; + hasAVX512VBMI = (c & (1u << 1)) != 0; + hasAVX512VPOPCNTDQ = (c & (1u << 14)) != 0; +} + +} // namespace juce::SystemStatsHelpers + +#endif diff --git a/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h b/JuceLibraryCode/modules/juce_core/native/juce_SharedCode_posix.h similarity index 74% rename from JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h rename to JuceLibraryCode/modules/juce_core/native/juce_SharedCode_posix.h index 664727dd..d61c4de3 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_SharedCode_posix.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -59,7 +59,7 @@ void JUCE_CALLTYPE Process::terminate() } -#if JUCE_MAC || JUCE_LINUX +#if JUCE_MAC || JUCE_LINUX || JUCE_BSD bool Process::setMaxNumberOfFileHandles (int newMaxNumber) noexcept { rlimit lim; @@ -99,8 +99,16 @@ static MaxNumFileHandlesInitialiser maxNumFileHandlesInitialiser; #endif //============================================================================== -JUCE_DECLARE_DEPRECATED_STATIC (const juce_wchar File::separator = '/';) -JUCE_DECLARE_DEPRECATED_STATIC (const StringRef File::separatorString ("/");) +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + +const juce_wchar File::separator = '/'; +const StringRef File::separatorString ("/"); + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + +#endif juce_wchar File::getSeparatorChar() { return '/'; } StringRef File::getSeparatorString() { return "/"; } @@ -130,16 +138,19 @@ bool File::setAsCurrentWorkingDirectory() const return chdir (getFullPathName().toUTF8()) == 0; } -#if JUCE_ANDROID - using juce_sigactionflags_type = unsigned long; -#else - using juce_sigactionflags_type = int; -#endif - //============================================================================== // The unix siginterrupt function is deprecated - this does the same job. -int juce_siginterrupt (int sig, int flag) +int juce_siginterrupt ([[maybe_unused]] int sig, [[maybe_unused]] int flag) { + #if JUCE_WASM + return 0; + #else + #if JUCE_ANDROID + using juce_sigactionflags_type = unsigned long; + #else + using juce_sigactionflags_type = int; + #endif + struct ::sigaction act; (void) ::sigaction (sig, nullptr, &act); @@ -149,12 +160,13 @@ int juce_siginterrupt (int sig, int flag) act.sa_flags |= static_cast (SA_RESTART); return ::sigaction (sig, &act, nullptr); + #endif } //============================================================================== namespace { - #if JUCE_LINUX || (JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T) // (this iOS stuff is to avoid a simulator bug) + #if JUCE_LINUX || (JUCE_IOS && (! TARGET_OS_MACCATALYST) && (! __DARWIN_ONLY_64_BIT_INO_T)) // (this iOS stuff is to avoid a simulator bug) using juce_statStruct = struct stat64; #define JUCE_STAT stat64 #else @@ -168,6 +180,7 @@ namespace && JUCE_STAT (fileName.toUTF8(), &info) == 0; } + #if ! JUCE_WASM // if this file doesn't exist, find a parent of it that does.. bool juce_doStatFS (File f, struct statfs& result) { @@ -205,6 +218,7 @@ namespace if (isReadOnly != nullptr) *isReadOnly = access (path.toUTF8(), W_OK) != 0; } + #endif Result getResultForErrno() { @@ -253,7 +267,7 @@ uint64 File::getFileIdentifier() const static bool hasEffectiveRootFilePermissions() { - #if JUCE_LINUX + #if JUCE_LINUX || JUCE_BSD return geteuid() == 0; #else return false; @@ -273,6 +287,12 @@ bool File::hasWriteAccess() const return false; } +bool File::hasReadAccess() const +{ + return fullPath.isNotEmpty() + && access (fullPath.toUTF8(), R_OK) == 0; +} + static bool setFileModeFlags (const String& fullPath, mode_t flags, bool shouldSet) noexcept { juce_statStruct info; @@ -329,6 +349,7 @@ void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 /*creationTime*/) const { + #if ! JUCE_WASM juce_statStruct info; if ((modificationTime != 0 || accessTime != 0) && juce_stat (fullPath, info)) @@ -360,6 +381,7 @@ bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 return utime (fullPath.toUTF8(), ×) == 0; #endif } + #endif return false; } @@ -383,6 +405,9 @@ bool File::moveInternal (const File& dest) const if (rename (fullPath.toUTF8(), dest.getFullPathName().toUTF8()) == 0) return true; + if (isNonEmptyDirectory()) + return false; + if (hasWriteAccess() && copyInternal (dest)) { if (deleteFile()) @@ -415,7 +440,7 @@ int64 juce_fileSetPosition (void* handle, int64 pos) void FileInputStream::openHandle() { - auto f = open (file.getFullPathName().toUTF8(), O_RDONLY, 00644); + auto f = open (file.getFullPathName().toUTF8(), O_RDONLY); if (f != -1) fileHandle = fdToVoidPointer (f); @@ -452,7 +477,7 @@ void FileOutputStream::openHandle() { if (file.exists()) { - auto f = open (file.getFullPathName().toUTF8(), O_RDWR, 00644); + auto f = open (file.getFullPathName().toUTF8(), O_RDWR); if (f != -1) { @@ -475,7 +500,7 @@ void FileOutputStream::openHandle() } else { - auto f = open (file.getFullPathName().toUTF8(), O_RDWR + O_CREAT, 00644); + auto f = open (file.getFullPathName().toUTF8(), O_RDWR | O_CREAT, 00644); if (f != -1) fileHandle = fdToVoidPointer (f); @@ -533,6 +558,7 @@ String SystemStats::getEnvironmentVariable (const String& name, const String& de } //============================================================================== +#if ! JUCE_WASM void MemoryMappedFile::openInternal (const File& file, AccessMode mode, bool exclusive) { jassert (mode == readOnly || mode == readWrite); @@ -543,8 +569,12 @@ void MemoryMappedFile::openInternal (const File& file, AccessMode mode, bool exc range.setStart (range.getStart() - (range.getStart() % pageSize)); } - fileHandle = open (file.getFullPathName().toUTF8(), - mode == readWrite ? (O_CREAT + O_RDWR) : O_RDONLY, 00644); + auto filename = file.getFullPathName().toUTF8(); + + if (mode == readWrite) + fileHandle = open (filename, O_CREAT | O_RDWR, 00644); + else + fileHandle = open (filename, O_RDONLY); if (fileHandle != -1) { @@ -562,6 +592,9 @@ void MemoryMappedFile::openInternal (const File& file, AccessMode mode, bool exc { range = Range(); } + + close (fileHandle); + fileHandle = 0; } } @@ -655,13 +688,14 @@ int File::getVolumeSerialNumber() const return 0; } +#endif + //============================================================================== #if ! JUCE_IOS void juce_runSystemCommand (const String&); void juce_runSystemCommand (const String& command) { - int result = system (command.toUTF8()); - ignoreUnused (result); + [[maybe_unused]] int result = system (command.toUTF8()); } String juce_getOutputFromCommand (const String&); @@ -818,103 +852,133 @@ void InterProcessLock::exit() pimpl.reset(); } -//============================================================================== -void JUCE_API juce_threadEntryPoint (void*); - -#if JUCE_ANDROID -extern JavaVM* androidJNIJavaVM; -#endif - -static void* threadEntryProc (void* userData) +class PosixThreadAttribute { - auto* myself = static_cast (userData); - - JUCE_AUTORELEASEPOOL +public: + explicit PosixThreadAttribute (size_t stackSize) { - juce_threadEntryPoint (myself); + if (valid && stackSize != 0) + pthread_attr_setstacksize (&attr, stackSize); } - #if JUCE_ANDROID - if (androidJNIJavaVM != nullptr) + ~PosixThreadAttribute() { - void* env = nullptr; - androidJNIJavaVM->GetEnv(&env, JNI_VERSION_1_2); + if (valid) + pthread_attr_destroy (&attr); + } + + auto* get() { return valid ? &attr : nullptr; } + +private: + pthread_attr_t attr; + bool valid { pthread_attr_init (&attr) == 0 }; +}; - // only detach if we have actually been attached - if (env != nullptr) - androidJNIJavaVM->DetachCurrentThread(); +class PosixSchedulerPriority +{ +public: + static PosixSchedulerPriority findCurrentSchedulerAndPriority() + { + int scheduler{}; + sched_param param{}; + pthread_getschedparam (pthread_self(), &scheduler, ¶m); + return { scheduler, param.sched_priority }; } - #endif - return nullptr; -} + static PosixSchedulerPriority getNativeSchedulerAndPriority (const Optional& rt, + [[maybe_unused]] Thread::Priority prio) + { + const auto isRealtime = rt.hasValue(); -#if JUCE_ANDROID && JUCE_MODULE_AVAILABLE_juce_audio_devices && \ - ((JUCE_USE_ANDROID_OPENSLES || (! defined(JUCE_USE_ANDROID_OPENSLES) && JUCE_ANDROID_API_VERSION > 8)) \ - || (JUCE_USE_ANDROID_OBOE || (! defined(JUCE_USE_ANDROID_OBOE) && JUCE_ANDROID_API_VERSION > 15))) + const auto priority = [&] + { + if (isRealtime) + { + const auto min = jmax (0, sched_get_priority_min (SCHED_RR)); + const auto max = jmax (1, sched_get_priority_max (SCHED_RR)); - #define JUCE_ANDROID_REALTIME_THREAD_AVAILABLE 1 -#endif + return jmap (rt->getPriority(), 0, 10, min, max); + } -#if JUCE_ANDROID_REALTIME_THREAD_AVAILABLE -extern pthread_t juce_createRealtimeAudioThread (void* (*entry) (void*), void* userPtr); -#endif + // We only use this helper if we're on an old macos/ios platform that might + // still respect legacy pthread priorities for SCHED_OTHER. + #if JUCE_MAC || JUCE_IOS + const auto min = jmax (0, sched_get_priority_min (SCHED_OTHER)); + const auto max = jmax (0, sched_get_priority_max (SCHED_OTHER)); + + const auto p = [prio] + { + switch (prio) + { + case Thread::Priority::highest: return 4; + case Thread::Priority::high: return 3; + case Thread::Priority::normal: return 2; + case Thread::Priority::low: return 1; + case Thread::Priority::background: return 0; + } + + return 3; + }(); + + if (min != 0 && max != 0) + return jmap (p, 0, 4, min, max); + #endif + + return 0; + }(); + + #if JUCE_MAC || JUCE_IOS || JUCE_BSD + const auto scheduler = SCHED_OTHER; + #elif JUCE_LINUX + const auto backgroundSched = prio == Thread::Priority::background ? SCHED_IDLE + : SCHED_OTHER; + const auto scheduler = isRealtime ? SCHED_RR : backgroundSched; + #else + const auto scheduler = 0; + #endif -void Thread::launchThread() -{ - #if JUCE_ANDROID - if (isAndroidRealtimeThread) + return { scheduler, priority }; + } + + void apply ([[maybe_unused]] PosixThreadAttribute& attr) const { - #if JUCE_ANDROID_REALTIME_THREAD_AVAILABLE - threadHandle = (void*) juce_createRealtimeAudioThread (threadEntryProc, this); - threadId = (ThreadID) threadHandle.get(); + #if JUCE_LINUX || JUCE_BSD + const struct sched_param param { getPriority() }; - return; - #else - jassertfalse; - #endif + pthread_attr_setinheritsched (attr.get(), PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy (attr.get(), getScheduler()); + pthread_attr_setschedparam (attr.get(), ¶m); + #endif } - #endif - threadHandle = {}; - pthread_t handle = {}; - pthread_attr_t attr; - pthread_attr_t* attrPtr = nullptr; + constexpr int getScheduler() const { return scheduler; } + constexpr int getPriority() const { return priority; } - if (pthread_attr_init (&attr) == 0) - { - attrPtr = &attr; - pthread_attr_setstacksize (attrPtr, threadStackSize); - } +private: + constexpr PosixSchedulerPriority (int schedulerIn, int priorityIn) + : scheduler (schedulerIn), priority (priorityIn) {} + int scheduler; + int priority; +}; - if (pthread_create (&handle, attrPtr, threadEntryProc, this) == 0) - { - pthread_detach (handle); - threadHandle = (void*) handle; - threadId = (ThreadID) threadHandle.get(); - } +static void* makeThreadHandle (PosixThreadAttribute& attr, void* userData, void* (*threadEntryProc) (void*)) +{ + pthread_t handle = {}; + + const auto status = pthread_create (&handle, attr.get(), threadEntryProc, userData); - if (attrPtr != nullptr) - pthread_attr_destroy (attrPtr); + if (status != 0) + return nullptr; + + pthread_detach (handle); + return (void*) handle; } void Thread::closeThreadHandle() { threadId = {}; - threadHandle = {}; -} - -void Thread::killThread() -{ - if (threadHandle.get() != nullptr) - { - #if JUCE_ANDROID - jassertfalse; // pthread_cancel not available! - #else - pthread_cancel ((pthread_t) threadHandle.get()); - #endif - } + threadHandle = nullptr; } void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) @@ -924,9 +988,10 @@ void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) { [[NSThread currentThread] setName: juceStringToNS (name)]; } - #elif JUCE_LINUX || JUCE_ANDROID - #if ((JUCE_LINUX && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012) \ - || JUCE_ANDROID && __ANDROID_API__ >= 9) + #elif JUCE_LINUX || JUCE_BSD || JUCE_ANDROID + #if (JUCE_BSD \ + || (JUCE_LINUX && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012) \ + || (JUCE_ANDROID && __ANDROID_API__ >= 9)) pthread_setname_np (pthread_self(), name.toRawUTF8()); #else prctl (PR_SET_NAME, name.toRawUTF8(), 0, 0, 0); @@ -934,27 +999,6 @@ void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) #endif } -bool Thread::setThreadPriority (void* handle, int priority) -{ - struct sched_param param; - int policy; - priority = jlimit (0, 10, priority); - - if (handle == nullptr) - handle = (void*) pthread_self(); - - if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) != 0) - return false; - - policy = priority == 0 ? SCHED_OTHER : SCHED_RR; - - const int minPriority = sched_get_priority_min (policy); - const int maxPriority = sched_get_priority_max (policy); - - param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority; - return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0; -} - Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId() { return (ThreadID) pthread_self(); @@ -974,17 +1018,25 @@ void JUCE_CALLTYPE Thread::yield() #define SUPPORT_AFFINITIES 1 #endif -void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (uint32 affinityMask) +void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask ([[maybe_unused]] uint32 affinityMask) { #if SUPPORT_AFFINITIES cpu_set_t affinity; CPU_ZERO (&affinity); for (int i = 0; i < 32; ++i) + { if ((affinityMask & (uint32) (1 << i)) != 0) + { + // GCC 12 on FreeBSD complains about CPU_SET irrespective of + // the type of the first argument + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wsign-conversion") CPU_SET ((size_t) i, &affinity); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } + } - #if (! JUCE_ANDROID) && ((! JUCE_LINUX) || ((__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2004)) + #if (! JUCE_ANDROID) && ((! (JUCE_LINUX || JUCE_BSD)) || ((__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2004)) pthread_setaffinity_np (pthread_self(), sizeof (cpu_set_t), &affinity); #elif JUCE_ANDROID sched_setaffinity (gettid(), sizeof (cpu_set_t), &affinity); @@ -1001,11 +1053,11 @@ void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (uint32 affinityMask) // affinities aren't supported because either the appropriate header files weren't found, // or the SUPPORT_AFFINITIES macro was turned off jassertfalse; - ignoreUnused (affinityMask); #endif } //============================================================================== +#if ! JUCE_WASM bool DynamicLibrary::open (const String& name) { close(); @@ -1027,9 +1079,9 @@ void* DynamicLibrary::getFunction (const String& functionName) noexcept return handle != nullptr ? dlsym (handle, functionName.toUTF8()) : nullptr; } - //============================================================================== -static inline String readPosixConfigFileValue (const char* file, const char* key) +#if JUCE_LINUX || JUCE_ANDROID +static String readPosixConfigFileValue (const char* file, const char* key) { StringArray lines; File (file).readLines (lines); @@ -1040,6 +1092,7 @@ static inline String readPosixConfigFileValue (const char* file, const char* key return {}; } +#endif //============================================================================== @@ -1118,7 +1171,7 @@ class ChildProcess::ActiveProcess if (childPID == 0) return false; - int childState; + int childState = 0; auto pid = waitpid (childPID, &childState, WNOHANG); if (pid == 0) @@ -1215,238 +1268,6 @@ bool ChildProcess::start (const StringArray& args, int streamFlags) return activeProcess != nullptr; } -//============================================================================== -struct HighResolutionTimer::Pimpl -{ - Pimpl (HighResolutionTimer& t) : owner (t) - { - pthread_condattr_t attr; - pthread_condattr_init (&attr); - - #if JUCE_LINUX || (JUCE_ANDROID && defined(__ANDROID_API__) && __ANDROID_API__ >= 21) - pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); - #endif - - pthread_cond_init (&stopCond, &attr); - pthread_condattr_destroy (&attr); - pthread_mutex_init (&timerMutex, nullptr); - } - - ~Pimpl() - { - jassert (! isRunning); - stop(); - } - - void start (int newPeriod) - { - if (periodMs != newPeriod) - { - if (thread != pthread_self()) - { - stop(); - - periodMs = newPeriod; - destroyThread = false; - isRunning = true; - - if (pthread_create (&thread, nullptr, timerThread, this) == 0) - setThreadToRealtime (thread, (uint64) newPeriod); - else - jassertfalse; - } - else - { - periodMs = newPeriod; - isRunning = true; - destroyThread = false; - } - } - } - - void stop() - { - isRunning = false; - - if (thread == pthread_t()) - return; - - if (thread == pthread_self()) - { - periodMs = 3600000; - return; - } - - isRunning = false; - destroyThread = true; - - pthread_mutex_lock (&timerMutex); - pthread_cond_signal (&stopCond); - pthread_mutex_unlock (&timerMutex); - - pthread_join (thread, nullptr); - thread = {}; - } - - HighResolutionTimer& owner; - std::atomic periodMs { 0 }; - -private: - pthread_t thread = {}; - pthread_cond_t stopCond; - pthread_mutex_t timerMutex; - std::atomic destroyThread { false }, isRunning { false }; - - static void* timerThread (void* param) - { - #if ! JUCE_ANDROID - int dummy; - pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &dummy); - #endif - - reinterpret_cast (param)->timerThread(); - return nullptr; - } - - void timerThread() - { - auto lastPeriod = periodMs.load(); - Clock clock (lastPeriod); - - pthread_mutex_lock (&timerMutex); - - while (! destroyThread) - { - clock.next(); - while (! destroyThread && clock.wait (stopCond, timerMutex)); - - if (destroyThread) - break; - - if (isRunning) - owner.hiResTimerCallback(); - - auto newPeriod = periodMs.load(); - - if (lastPeriod != newPeriod) - { - lastPeriod = newPeriod; - clock = Clock (lastPeriod); - } - } - - periodMs = 0; - pthread_mutex_unlock (&timerMutex); - pthread_exit (nullptr); - } - - struct Clock - { - #if JUCE_MAC || JUCE_IOS - Clock (double millis) noexcept - { - (void) mach_timebase_info (&timebase); - delta = (((uint64_t) (millis * 1000000.0)) * timebase.denom) / timebase.numer; - time = mach_absolute_time(); - } - - bool wait (pthread_cond_t& cond, pthread_mutex_t& mutex) noexcept - { - struct timespec left; - - if (! hasExpired (left)) - return (pthread_cond_timedwait_relative_np (&cond, &mutex, &left) != ETIMEDOUT); - - return false; - } - - uint64_t time, delta; - mach_timebase_info_data_t timebase; - - bool hasExpired (struct timespec& time_left) noexcept - { - uint64_t now = mach_absolute_time(); - - if (now < time) - { - uint64_t left = time - now; - uint64_t nanos = (left * static_cast (timebase.numer)) / static_cast (timebase.denom); - time_left.tv_sec = static_cast<__darwin_time_t> (nanos / 1000000000ULL); - time_left.tv_nsec = static_cast (nanos - (static_cast (time_left.tv_sec) * 1000000000ULL)); - - return false; - } - - return true; - } - #else - Clock (double millis) noexcept : delta ((uint64) (millis * 1000000)) - { - struct timespec t; - clock_gettime (CLOCK_MONOTONIC, &t); - time = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec); - } - - bool wait (pthread_cond_t& cond, pthread_mutex_t& mutex) noexcept - { - struct timespec absExpire; - - if (! hasExpired (absExpire)) - return (pthread_cond_timedwait (&cond, &mutex, &absExpire) != ETIMEDOUT); - - return false; - } - - uint64 time, delta; - - bool hasExpired (struct timespec& expiryTime) noexcept - { - struct timespec t; - clock_gettime (CLOCK_MONOTONIC, &t); - auto now = (uint64) (1000000000 * (int64) t.tv_sec + (int64) t.tv_nsec); - - if (now < time) - { - expiryTime.tv_sec = (time_t) (time / 1000000000); - expiryTime.tv_nsec = (long) (time % 1000000000); - - return false; - } - - return true; - } - #endif - - void next() noexcept - { - time += delta; - } - }; - - static bool setThreadToRealtime (pthread_t thread, uint64 periodMs) - { - #if JUCE_MAC || JUCE_IOS - thread_time_constraint_policy_data_t policy; - policy.period = (uint32_t) (periodMs * 1000000); - policy.computation = 50000; - policy.constraint = policy.period; - policy.preemptible = true; - - return thread_policy_set (pthread_mach_thread_np (thread), - THREAD_TIME_CONSTRAINT_POLICY, - (thread_policy_t) &policy, - THREAD_TIME_CONSTRAINT_POLICY_COUNT) == KERN_SUCCESS; - - #else - ignoreUnused (periodMs); - struct sched_param param; - param.sched_priority = sched_get_priority_max (SCHED_RR); - return pthread_setschedparam (thread, SCHED_RR, ¶m) == 0; - - #endif - } - - JUCE_DECLARE_NON_COPYABLE (Pimpl) -}; +#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm b/JuceLibraryCode/modules/juce_core/native/juce_Strings_mac.mm similarity index 98% rename from JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm rename to JuceLibraryCode/modules/juce_core/native/juce_Strings_mac.mm index a83bf310..f98d1c5a 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_Strings_mac.mm @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_android.cpp similarity index 87% rename from JuceLibraryCode/modules/juce_core/native/juce_android_SystemStats.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_SystemStats_android.cpp index e22c1907..667ca166 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_android.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -40,14 +40,30 @@ namespace AndroidStatsHelpers DECLARE_JNI_CLASS (JavaLocale, "java/util/Locale") #undef JNI_CLASS_MEMBERS - static inline String getSystemProperty (const String& name) + static String getSystemProperty (const String& name) { return juceString (LocalRef ((jstring) getEnv()->CallStaticObjectMethod (SystemClass, SystemClass.getProperty, javaString (name).get()))); } - static inline String getLocaleValue (bool isRegion) + static String getAndroidID() + { + auto* env = getEnv(); + + if (auto settings = (jclass) env->FindClass ("android/provider/Settings$Secure")) + { + if (auto fId = env->GetStaticFieldID (settings, "ANDROID_ID", "Ljava/lang/String;")) + { + auto androidID = (jstring) env->GetStaticObjectField (settings, fId); + return juceString (LocalRef (androidID)); + } + } + + return ""; + } + + static String getLocaleValue (bool isRegion) { auto* env = getEnv(); LocalRef locale (env->CallStaticObjectMethod (JavaLocale, JavaLocale.getDefault)); @@ -58,7 +74,7 @@ namespace AndroidStatsHelpers return juceString (LocalRef ((jstring) stringResult)); } - static inline String getAndroidOsBuildValue (const char* fieldName) + static String getAndroidOsBuildValue (const char* fieldName) { return juceString (LocalRef ((jstring) getEnv()->GetStaticObjectField ( AndroidBuild, getEnv()->GetStaticFieldID (AndroidBuild, fieldName, "Ljava/lang/String;")))); @@ -112,7 +128,7 @@ int SystemStats::getCpuSpeedInMegahertz() for (int i = 0; i < getNumCpus(); ++i) { - int freqKHz = File ("/sys/devices/system/cpu/cpu" + String(i) + "/cpufreq/cpuinfo_max_freq") + int freqKHz = File ("/sys/devices/system/cpu/cpu" + String (i) + "/cpufreq/cpuinfo_max_freq") .loadFileAsString() .getIntValue(); @@ -170,6 +186,15 @@ String SystemStats::getUserLanguage() { return AndroidStatsHelpers::getLocale String SystemStats::getUserRegion() { return AndroidStatsHelpers::getLocaleValue (true); } String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); } +String SystemStats::getUniqueDeviceID() +{ + auto id = String ((uint64_t) AndroidStatsHelpers::getAndroidID().hashCode64()); + + // Please tell someone at JUCE if this occurs + jassert (id.isNotEmpty()); + return id; +} + //============================================================================== void CPUInformation::initialise() noexcept { @@ -229,7 +254,7 @@ int64 Time::getHighResolutionTicksPerSecond() noexcept double Time::getMillisecondCounterHiRes() noexcept { - return getHighResolutionTicks() * 0.001; + return (double) getHighResolutionTicks() * 0.001; } bool Time::setSystemTimeToThisTime() const diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_linux.cpp b/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_linux.cpp new file mode 100644 index 00000000..74601fa7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_linux.cpp @@ -0,0 +1,425 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#if JUCE_BELA +extern "C" int cobalt_thread_mode(); +#endif + +namespace juce +{ + +#if ! JUCE_BSD +static String getCpuInfo (const char* key) +{ + return readPosixConfigFileValue ("/proc/cpuinfo", key); +} + +static String getLocaleValue (nl_item key) +{ + auto oldLocale = ::setlocale (LC_ALL, ""); + auto result = String::fromUTF8 (nl_langinfo (key)); + ::setlocale (LC_ALL, oldLocale); + return result; +} +#endif + +//============================================================================== +void Logger::outputDebugString (const String& text) +{ + std::cerr << text << std::endl; +} + +//============================================================================== +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() +{ + return Linux; +} + +String SystemStats::getOperatingSystemName() +{ + return "Linux"; +} + +bool SystemStats::isOperatingSystem64Bit() +{ + #if JUCE_64BIT + return true; + #else + //xxx not sure how to find this out?.. + return false; + #endif +} + +//============================================================================== +String SystemStats::getDeviceDescription() +{ + #if JUCE_BSD + int mib[] = { + CTL_HW, + HW_MACHINE + }; + size_t machineDescriptionLength = 0; + auto result = sysctl (mib, numElementsInArray (mib), nullptr, &machineDescriptionLength, nullptr, 0); + + if (result != 0 || machineDescriptionLength == 0) + return {}; + + MemoryBlock machineDescription { machineDescriptionLength }; + result = sysctl (mib, numElementsInArray (mib), machineDescription.getData(), &machineDescriptionLength, nullptr, 0); + return String::fromUTF8 (result == 0 ? (char*) machineDescription.getData() : ""); + #else + return getCpuInfo ("Hardware"); + #endif +} + +String SystemStats::getDeviceManufacturer() +{ + return {}; +} + +String SystemStats::getCpuVendor() +{ + #if JUCE_BSD + return {}; + #else + auto v = getCpuInfo ("vendor_id"); + + if (v.isEmpty()) + v = getCpuInfo ("model name"); + + return v; + #endif +} + +String SystemStats::getCpuModel() +{ + #if JUCE_BSD + int mib[] = { + CTL_HW, + HW_MODEL + }; + size_t modelLength = 0; + auto result = sysctl (mib, numElementsInArray (mib), nullptr, &modelLength, nullptr, 0); + + if (result != 0 || modelLength == 0) + return {}; + + MemoryBlock model { modelLength }; + result = sysctl (mib, numElementsInArray (mib), model.getData(), &modelLength, nullptr, 0); + return String::fromUTF8 (result == 0 ? (char*) model.getData() : ""); + #else + return getCpuInfo ("model name"); + #endif +} + +int SystemStats::getCpuSpeedInMegahertz() +{ + #if JUCE_BSD + int32 clockRate = 0; + auto clockRateSize = sizeof (clockRate); + auto result = sysctlbyname ("hw.clockrate", &clockRate, &clockRateSize, nullptr, 0); + return result == 0 ? clockRate : 0; + #else + return roundToInt (getCpuInfo ("cpu MHz").getFloatValue()); + #endif +} + +int SystemStats::getMemorySizeInMegabytes() +{ + #if JUCE_BSD + int mib[] = { + CTL_HW, + HW_PHYSMEM + }; + int64 memory = 0; + auto memorySize = sizeof (memory); + auto result = sysctl (mib, numElementsInArray (mib), &memory, &memorySize, nullptr, 0); + return result == 0 ? (int) (memory / (int64) 1e6) : 0; + #else + struct sysinfo sysi; + + if (sysinfo (&sysi) == 0) + return (int) (sysi.totalram * sysi.mem_unit / (1024 * 1024)); + + return 0; + #endif +} + +int SystemStats::getPageSize() +{ + return (int) sysconf (_SC_PAGESIZE); +} + +//============================================================================== +String SystemStats::getLogonName() +{ + if (auto user = getenv ("USER")) + return String::fromUTF8 (user); + + if (auto pw = getpwuid (getuid())) + return String::fromUTF8 (pw->pw_name); + + return {}; +} + +String SystemStats::getFullUserName() +{ + return getLogonName(); +} + +String SystemStats::getComputerName() +{ + char name[256] = {}; + + if (gethostname (name, sizeof (name) - 1) == 0) + return name; + + return {}; +} + +String SystemStats::getUserLanguage() +{ + #if JUCE_BSD + if (auto langEnv = getenv ("LANG")) + return String::fromUTF8 (langEnv).upToLastOccurrenceOf (".UTF-8", false, true); + + return {}; + #else + return getLocaleValue (_NL_ADDRESS_LANG_AB); + #endif +} + +String SystemStats::getUserRegion() +{ + #if JUCE_BSD + return {}; + #else + return getLocaleValue (_NL_ADDRESS_COUNTRY_AB2); + #endif +} + +String SystemStats::getDisplayLanguage() +{ + auto result = getUserLanguage(); + auto region = getUserRegion(); + + if (region.isNotEmpty()) + result << "-" << region; + + return result; +} + +//============================================================================== +void CPUInformation::initialise() noexcept +{ + #if JUCE_BSD + #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM + SystemStatsHelpers::getCPUInfo (hasMMX, + hasSSE, + hasSSE2, + has3DNow, + hasSSE3, + hasSSSE3, + hasFMA3, + hasSSE41, + hasSSE42, + hasAVX, + hasFMA4, + hasAVX2, + hasAVX512F, + hasAVX512DQ, + hasAVX512IFMA, + hasAVX512PF, + hasAVX512ER, + hasAVX512CD, + hasAVX512BW, + hasAVX512VL, + hasAVX512VBMI, + hasAVX512VPOPCNTDQ); + #endif + + numLogicalCPUs = numPhysicalCPUs = [] + { + int mib[] = { + CTL_HW, + HW_NCPU + }; + int32 numCPUs = 1; + auto numCPUsSize = sizeof (numCPUs); + auto result = sysctl (mib, numElementsInArray (mib), &numCPUs, &numCPUsSize, nullptr, 0); + return result == 0 ? numCPUs : 1; + }(); + #else + 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"); + has3DNow = flags.contains ("3dnow"); + hasSSSE3 = flags.contains ("ssse3"); + hasSSE41 = flags.contains ("sse4_1"); + hasSSE42 = flags.contains ("sse4_2"); + hasAVX = flags.contains ("avx"); + hasAVX2 = flags.contains ("avx2"); + hasAVX512F = flags.contains ("avx512f"); + hasAVX512BW = flags.contains ("avx512bw"); + hasAVX512CD = flags.contains ("avx512cd"); + hasAVX512DQ = flags.contains ("avx512dq"); + hasAVX512ER = flags.contains ("avx512er"); + hasAVX512IFMA = flags.contains ("avx512ifma"); + hasAVX512PF = flags.contains ("avx512pf"); + hasAVX512VBMI = flags.contains ("avx512vbmi"); + hasAVX512VL = flags.contains ("avx512vl"); + hasAVX512VPOPCNTDQ = flags.contains ("avx512_vpopcntdq"); + + numLogicalCPUs = getCpuInfo ("processor").getIntValue() + 1; + + // Assume CPUs in all sockets have the same number of cores + numPhysicalCPUs = getCpuInfo ("cpu cores").getIntValue() * (getCpuInfo ("physical id").getIntValue() + 1); + + if (numPhysicalCPUs <= 0) + numPhysicalCPUs = numLogicalCPUs; + #endif +} + +String SystemStats::getUniqueDeviceID() +{ + static const auto deviceId = []() + { + const auto call = [] (auto command) -> String + { + ChildProcess proc; + + if (proc.start (command, ChildProcess::wantStdOut)) + return proc.readAllProcessOutput(); + + return {}; + }; + + auto data = call ("cat /sys/class/dmi/id/board_serial"); + + // 'board_serial' is enough on its own, fallback to bios stuff if we can't find it. + if (data.isEmpty()) + { + data = call ("cat /sys/class/dmi/id/bios_date") + + call ("cat /sys/class/dmi/id/bios_release") + + call ("cat /sys/class/dmi/id/bios_vendor") + + call ("cat /sys/class/dmi/id/bios_version"); + } + + auto cpuData = call ("lscpu"); + + if (cpuData.isNotEmpty()) + { + auto getCpuInfo = [&cpuData] (auto key) -> String + { + auto index = cpuData.indexOf (key); + + if (index >= 0) + { + auto start = cpuData.indexOf (index, ":"); + auto end = cpuData.indexOf (start, "\n"); + + return cpuData.substring (start + 1, end).trim(); + } + + return {}; + }; + + data += getCpuInfo ("CPU family:"); + data += getCpuInfo ("Model:"); + data += getCpuInfo ("Model name:"); + data += getCpuInfo ("Vendor ID:"); + } + + return String ((uint64_t) data.hashCode64()); + }(); + + // Please tell someone at JUCE if this occurs + jassert (deviceId.isNotEmpty()); + return deviceId; +} + +//============================================================================== +uint32 juce_millisecondsSinceStartup() noexcept +{ + 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); +} + +int64 Time::getHighResolutionTicksPerSecond() noexcept +{ + return 1000000; // (microseconds) +} + +double Time::getMillisecondCounterHiRes() noexcept +{ + return (double) getHighResolutionTicks() * 0.001; +} + +bool Time::setSystemTimeToThisTime() const +{ + timeval t; + t.tv_sec = decltype (timeval::tv_sec) (millisSinceEpoch / 1000); + t.tv_usec = decltype (timeval::tv_usec) ((millisSinceEpoch - t.tv_sec * 1000) * 1000); + + return settimeofday (&t, nullptr) == 0; +} + +JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept +{ + #if JUCE_BSD + int mib[] = + { + CTL_KERN, + KERN_PROC, + KERN_PROC_PID, + ::getpid() + }; + struct kinfo_proc info; + auto infoSize = sizeof (info); + auto result = sysctl (mib, numElementsInArray (mib), &info, &infoSize, nullptr, 0); + return result == 0 ? ((info.ki_flag & P_TRACED) != 0) : false; + #else + return readPosixConfigFileValue ("/proc/self/status", "TracerPid").getIntValue() > 0; + #endif +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm b/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_mac.mm similarity index 56% rename from JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm rename to JuceLibraryCode/modules/juce_core/native/juce_SystemStats_mac.mm index e1b3d38e..d06f9628 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_mac.mm @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -43,64 +43,34 @@ fflush (stderr); } -//============================================================================== -namespace SystemStatsHelpers -{ - #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM - static void doCPUID (uint32& a, uint32& b, uint32& c, uint32& d, uint32 type) - { - uint32 la = a, lb = b, lc = c, ld = d; - - #if JUCE_32BIT && defined (__pic__) - asm ("mov %%ebx, %%edi\n" - "cpuid\n" - "xchg %%edi, %%ebx\n" - : "=a" (la), "=D" (lb), "=c" (lc), "=d" (ld) - : "a" (type), "c" (0)); - #else - asm ("cpuid\n" - : "=a" (la), "=b" (lb), "=c" (lc), "=d" (ld) - : "a" (type), "c" (0)); - #endif - - a = la; b = lb; c = lc; d = ld; - } - #endif -} - //============================================================================== void CPUInformation::initialise() noexcept { #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM - uint32 a = 0, b = 0, d = 0, c = 0; - SystemStatsHelpers::doCPUID (a, b, c, d, 1); - - hasMMX = (d & (1u << 23)) != 0; - hasSSE = (d & (1u << 25)) != 0; - hasSSE2 = (d & (1u << 26)) != 0; - 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; - hasAVX512DQ = (b & (1u << 17)) != 0; - hasAVX512IFMA = (b & (1u << 21)) != 0; - hasAVX512PF = (b & (1u << 26)) != 0; - hasAVX512ER = (b & (1u << 27)) != 0; - hasAVX512CD = (b & (1u << 28)) != 0; - hasAVX512BW = (b & (1u << 30)) != 0; - hasAVX512VL = (b & (1u << 31)) != 0; - hasAVX512VBMI = (c & (1u << 1)) != 0; - hasAVX512VPOPCNTDQ = (c & (1u << 14)) != 0; + SystemStatsHelpers::getCPUInfo (hasMMX, + hasSSE, + hasSSE2, + has3DNow, + hasSSE3, + hasSSSE3, + hasFMA3, + hasSSE41, + hasSSE42, + hasAVX, + hasFMA4, + hasAVX2, + hasAVX512F, + hasAVX512DQ, + hasAVX512IFMA, + hasAVX512PF, + hasAVX512ER, + hasAVX512CD, + hasAVX512BW, + hasAVX512VL, + hasAVX512VBMI, + hasAVX512VPOPCNTDQ); + #elif JUCE_ARM && __ARM_ARCH > 7 + hasNeon = true; #endif numLogicalCPUs = (int) [[NSProcessInfo processInfo] activeProcessorCount]; @@ -121,10 +91,25 @@ static String getOSXVersion() { JUCE_AUTORELEASEPOOL { - NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile: - nsStringLiteral ("/System/Library/CoreServices/SystemVersion.plist")]; + const auto* dict = [] + { + const String systemVersionPlist ("/System/Library/CoreServices/SystemVersion.plist"); - return nsStringToJuce ([dict objectForKey: nsStringLiteral ("ProductVersion")]); + if (@available (macOS 10.13, *)) + { + NSError* error = nullptr; + return [NSDictionary dictionaryWithContentsOfURL: createNSURLFromFile (systemVersionPlist) + error: &error]; + } + + return [NSDictionary dictionaryWithContentsOfFile: juceStringToNS (systemVersionPlist)]; + }(); + + if (dict != nullptr) + return nsStringToJuce ([dict objectForKey: nsStringLiteral ("ProductVersion")]); + + jassertfalse; + return {}; } } #endif @@ -137,11 +122,24 @@ static String getOSXVersion() StringArray parts; parts.addTokens (getOSXVersion(), ".", StringRef()); - jassert (parts[0].getIntValue() == 10); - const int major = parts[1].getIntValue(); - jassert (major > 2); + const auto major = parts[0].getIntValue(); + const auto minor = parts[1].getIntValue(); - return (OperatingSystemType) (major + MacOSX_10_4 - 4); + switch (major) + { + case 10: + { + jassert (minor > 2); + return (OperatingSystemType) (minor + MacOSX_10_7 - 7); + } + + case 11: return MacOS_11; + case 12: return MacOS_12; + case 13: return MacOS_13; + case 14: return MacOS_14; + } + + return MacOSX; #endif } @@ -156,6 +154,10 @@ static String getOSXVersion() String SystemStats::getDeviceDescription() { + if (auto* userInfo = [[NSProcessInfo processInfo] environment]) + if (auto* simDeviceName = [userInfo objectForKey: @"SIMULATOR_MODEL_IDENTIFIER"]) + return nsStringToJuce (simDeviceName); + #if JUCE_IOS const char* name = "hw.machine"; #else @@ -169,22 +171,7 @@ static String getOSXVersion() HeapBlock model (size); 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 String (model.get()); } return {}; @@ -199,10 +186,8 @@ static String getOSXVersion() { #if JUCE_IOS return false; - #elif JUCE_64BIT - return true; #else - return getOperatingSystemType() >= MacOSX_10_6; + return true; #endif } @@ -225,7 +210,7 @@ static String getOSXVersion() return String (reinterpret_cast (vendor), 12); #else - return {}; + return "Apple"; #endif } @@ -242,17 +227,25 @@ static String getOSXVersion() int SystemStats::getCpuSpeedInMegahertz() { + #ifdef JUCE_INTEL uint64 speedHz = 0; - size_t speedSize = sizeof (speedHz); + size_t optSize = sizeof (speedHz); int mib[] = { CTL_HW, HW_CPU_FREQ }; - sysctl (mib, 2, &speedHz, &speedSize, nullptr, 0); - - #if JUCE_BIG_ENDIAN - if (speedSize == 4) - speedHz >>= 32; - #endif + sysctl (mib, 2, &speedHz, &optSize, nullptr, 0); return (int) (speedHz / 1000000); + #else + size_t hz = 0; + size_t optSize = sizeof (hz); + sysctlbyname ("hw.tbfrequency", &hz, &optSize, nullptr, 0); + + struct clockinfo ci{}; + optSize = sizeof (ci); + int mib[] = { CTL_KERN, KERN_CLOCKRATE }; + sysctl (mib, 2, &ci, &optSize, nullptr, 0); + + return (int) (double (hz * uint64_t (ci.hz)) / 1000000.0); + #endif } //============================================================================== @@ -277,9 +270,8 @@ static String getOSXVersion() static String getLocaleValue (CFStringRef key) { - CFLocaleRef cfLocale = CFLocaleCopyCurrent(); - const String result (String::fromCFString ((CFStringRef) CFLocaleGetValue (cfLocale, key))); - CFRelease (cfLocale); + CFUniquePtr cfLocale (CFLocaleCopyCurrent()); + const String result (String::fromCFString ((CFStringRef) CFLocaleGetValue (cfLocale.get(), key))); return result; } @@ -288,9 +280,8 @@ static String getLocaleValue (CFStringRef key) String SystemStats::getDisplayLanguage() { - CFArrayRef cfPrefLangs = CFLocaleCopyPreferredLanguages(); - const String result (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (cfPrefLangs, 0))); - CFRelease (cfPrefLangs); + CFUniquePtr cfPrefLangs (CFLocaleCopyPreferredLanguages()); + const String result (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (cfPrefLangs.get(), 0))); return result; } @@ -321,7 +312,7 @@ static String getLocaleValue (CFStringRef key) } highResTimerFrequency = (timebase.denom * (uint64) 1000000000) / timebase.numer; - highResTimerToMillisecRatio = hiResCounterNumerator / (double) hiResCounterDenominator; + highResTimerToMillisecRatio = (double) hiResCounterNumerator / (double) hiResCounterDenominator; } uint32 millisecondsSinceStartup() const noexcept @@ -331,7 +322,7 @@ uint32 millisecondsSinceStartup() const noexcept double getMillisecondCounterHiRes() const noexcept { - return mach_absolute_time() * highResTimerToMillisecRatio; + return (double) mach_absolute_time() * highResTimerToMillisecRatio; } int64 highResTimerFrequency; @@ -359,4 +350,65 @@ uint32 millisecondsSinceStartup() const noexcept return (int) NSPageSize(); } +String SystemStats::getUniqueDeviceID() +{ + #if JUCE_MAC + constexpr mach_port_t port = 0; + + const auto dict = IOServiceMatching ("IOPlatformExpertDevice"); + + if (const auto service = IOServiceGetMatchingService (port, dict); service != IO_OBJECT_NULL) + { + const ScopeGuard scope { [&] { IOObjectRelease (service); } }; + + if (const CFUniquePtr uuidTypeRef { IORegistryEntryCreateCFProperty (service, CFSTR ("IOPlatformUUID"), kCFAllocatorDefault, 0) }) + if (CFGetTypeID (uuidTypeRef.get()) == CFStringGetTypeID()) + return String::fromCFString ((CFStringRef) uuidTypeRef.get()).removeCharacters ("-"); + } + #elif JUCE_IOS + JUCE_AUTORELEASEPOOL + { + if (UIDevice* device = [UIDevice currentDevice]) + if (NSUUID* uuid = [device identifierForVendor]) + return nsStringToJuce ([uuid UUIDString]); + } + #endif + + return ""; +} + +#if JUCE_MAC +bool SystemStats::isAppSandboxEnabled() +{ + static const auto result = [&] + { + SecCodeRef ref = nullptr; + + if (const auto err = SecCodeCopySelf (kSecCSDefaultFlags, &ref); err != noErr) + return false; + + const CFUniquePtr managedRef (ref); + CFDictionaryRef infoDict = nullptr; + + if (const auto err = SecCodeCopySigningInformation (managedRef.get(), kSecCSDynamicInformation, &infoDict); err != noErr) + return false; + + const CFUniquePtr managedInfoDict (infoDict); + const void* entitlementsDict = nullptr; + + if (! CFDictionaryGetValueIfPresent (managedInfoDict.get(), kSecCodeInfoEntitlementsDict, &entitlementsDict)) + return false; + + const void* flag = nullptr; + + if (! CFDictionaryGetValueIfPresent (static_cast (entitlementsDict), @"com.apple.security.app-sandbox", &flag)) + return false; + + return static_cast (CFBooleanGetValue (static_cast (flag))); + }(); + + return result; +} +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_wasm.cpp b/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_wasm.cpp new file mode 100644 index 00000000..689cf215 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_wasm.cpp @@ -0,0 +1,87 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +void Logger::outputDebugString (const String& text) +{ + std::cerr << text << std::endl; +} + +//============================================================================== +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { return WASM; } +String SystemStats::getOperatingSystemName() { return "WASM"; } +bool SystemStats::isOperatingSystem64Bit() { return true; } +String SystemStats::getDeviceDescription() { return "Web-browser"; } +String SystemStats::getDeviceManufacturer() { return {}; } +String SystemStats::getCpuVendor() { return {}; } +String SystemStats::getCpuModel() { return {}; } +int SystemStats::getCpuSpeedInMegahertz() { return 0; } +int SystemStats::getMemorySizeInMegabytes() { return 0; } +int SystemStats::getPageSize() { return 0; } +String SystemStats::getLogonName() { return {}; } +String SystemStats::getFullUserName() { return {}; } +String SystemStats::getComputerName() { return {}; } +String SystemStats::getUserLanguage() { return {}; } +String SystemStats::getUserRegion() { return {}; } +String SystemStats::getDisplayLanguage() { return {}; } + +//============================================================================== +void CPUInformation::initialise() noexcept +{ + numLogicalCPUs = 1; + numPhysicalCPUs = 1; +} + +//============================================================================== +uint32 juce_millisecondsSinceStartup() noexcept +{ + return static_cast (emscripten_get_now()); +} + +int64 Time::getHighResolutionTicks() noexcept +{ + return static_cast (emscripten_get_now() * 1000.0); +} + +int64 Time::getHighResolutionTicksPerSecond() noexcept +{ + return 1000000; // (microseconds) +} + +double Time::getMillisecondCounterHiRes() noexcept +{ + return emscripten_get_now(); +} + +bool Time::setSystemTimeToThisTime() const +{ + return false; +} + +JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept +{ + return false; +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_windows.cpp b/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_windows.cpp new file mode 100644 index 00000000..6acdd7aa --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_windows.cpp @@ -0,0 +1,859 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +void Logger::outputDebugString (const String& text) +{ + OutputDebugString ((text + "\n").toWideCharPointer()); +} + +//============================================================================== +#ifdef JUCE_DLL_BUILD + JUCE_API void* juceDLL_malloc (size_t sz) { return std::malloc (sz); } + JUCE_API void juceDLL_free (void* block) { std::free (block); } +#endif + +static int findNumberOfPhysicalCores() noexcept +{ + #if JUCE_MINGW + // Not implemented in MinGW + jassertfalse; + + return 1; + #else + + DWORD bufferSize = 0; + GetLogicalProcessorInformation (nullptr, &bufferSize); + + const auto numBuffers = (size_t) (bufferSize / sizeof (SYSTEM_LOGICAL_PROCESSOR_INFORMATION)); + + if (numBuffers == 0) + { + jassertfalse; + return 0; + }; + + HeapBlock buffer (numBuffers); + + if (! GetLogicalProcessorInformation (buffer, &bufferSize)) + { + jassertfalse; + return 0; + } + + return (int) std::count_if (buffer.get(), buffer.get() + numBuffers, [] (const auto& info) + { + return info.Relationship == RelationProcessorCore; + }); + + #endif // JUCE_MINGW +} + +//============================================================================== +#if JUCE_INTEL + #if JUCE_MSVC && ! defined (__INTEL_COMPILER) + #pragma intrinsic (__cpuid) + #pragma intrinsic (__rdtsc) + #endif + + #if JUCE_MINGW || JUCE_CLANG +static void callCPUID (int result[4], uint32 type) +{ + uint32 la = (uint32) result[0], lb = (uint32) result[1], + lc = (uint32) result[2], ld = (uint32) result[3]; + + asm ("mov %%ebx, %%esi \n\t" + "cpuid \n\t" + "xchg %%esi, %%ebx" + : "=a" (la), "=S" (lb), "=c" (lc), "=d" (ld) : "a" (type) + #if JUCE_64BIT + , "b" (lb), "c" (lc), "d" (ld) + #endif + ); + + result[0] = (int) la; result[1] = (int) lb; + result[2] = (int) lc; result[3] = (int) ld; +} + #else +static void callCPUID (int result[4], int infoType) +{ + __cpuid (result, infoType); +} + #endif + +String SystemStats::getCpuVendor() +{ + int info[4] = { 0 }; + callCPUID (info, 0); + + char v [12]; + memcpy (v, info + 1, 4); + memcpy (v + 4, info + 3, 4); + memcpy (v + 8, info + 2, 4); + + return String (v, 12); +} + +String SystemStats::getCpuModel() +{ + char name[65] = { 0 }; + int info[4] = { 0 }; + + callCPUID (info, 0x80000000); + + const int numExtIDs = info[0]; + + if ((unsigned) numExtIDs < 0x80000004) // if brand string is unsupported + return {}; + + callCPUID (info, 0x80000002); + memcpy (name, info, sizeof (info)); + + callCPUID (info, 0x80000003); + memcpy (name + 16, info, sizeof (info)); + + callCPUID (info, 0x80000004); + memcpy (name + 32, info, sizeof (info)); + + return String (name).trim(); +} + +void CPUInformation::initialise() noexcept +{ + int info[4] = { 0 }; + callCPUID (info, 1); + + // NB: IsProcessorFeaturePresent doesn't work on XP + hasMMX = (info[3] & (1 << 23)) != 0; + hasSSE = (info[3] & (1 << 25)) != 0; + 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; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wshift-sign-overflow") + has3DNow = (info[1] & (1 << 31)) != 0; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + callCPUID (info, 0x80000001); + hasFMA4 = (info[2] & (1 << 16)) != 0; + + callCPUID (info, 7); + + hasAVX2 = ((unsigned int) info[1] & (1 << 5)) != 0; + hasAVX512F = ((unsigned int) info[1] & (1u << 16)) != 0; + hasAVX512DQ = ((unsigned int) info[1] & (1u << 17)) != 0; + hasAVX512IFMA = ((unsigned int) info[1] & (1u << 21)) != 0; + hasAVX512PF = ((unsigned int) info[1] & (1u << 26)) != 0; + hasAVX512ER = ((unsigned int) info[1] & (1u << 27)) != 0; + hasAVX512CD = ((unsigned int) info[1] & (1u << 28)) != 0; + hasAVX512BW = ((unsigned int) info[1] & (1u << 30)) != 0; + hasAVX512VL = ((unsigned int) info[1] & (1u << 31)) != 0; + hasAVX512VBMI = ((unsigned int) info[2] & (1u << 1)) != 0; + hasAVX512VPOPCNTDQ = ((unsigned int) info[2] & (1u << 14)) != 0; + + SYSTEM_INFO systemInfo; + GetNativeSystemInfo (&systemInfo); + numLogicalCPUs = (int) systemInfo.dwNumberOfProcessors; + numPhysicalCPUs = findNumberOfPhysicalCores(); + + if (numPhysicalCPUs <= 0) + numPhysicalCPUs = numLogicalCPUs; +} +#elif JUCE_ARM +String SystemStats::getCpuVendor() +{ + static const auto cpuVendor = [] + { + static constexpr auto* path = "HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\\VendorIdentifier"; + auto vendor = RegistryKeyWrapper::getValue (path, {}, 0).trim(); + + return vendor.isEmpty() ? String ("Unknown Vendor") : vendor; + }(); + + return cpuVendor; +} + +String SystemStats::getCpuModel() +{ + static const auto cpuModel = [] + { + static constexpr auto* path = "HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\\ProcessorNameString"; + auto model = RegistryKeyWrapper::getValue (path, {}, 0).trim(); + + return model.isEmpty() ? String ("Unknown Model") : model; + }(); + + return cpuModel; +} + +void CPUInformation::initialise() noexcept +{ + // Windows for arm requires at least armv7 which has neon support + hasNeon = true; + + SYSTEM_INFO systemInfo; + GetNativeSystemInfo (&systemInfo); + numLogicalCPUs = (int) systemInfo.dwNumberOfProcessors; + numPhysicalCPUs = findNumberOfPhysicalCores(); + + if (numPhysicalCPUs <= 0) + numPhysicalCPUs = numLogicalCPUs; +} +#else + #error Unknown CPU architecture type +#endif + +#if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS +struct DebugFlagsInitialiser +{ + DebugFlagsInitialiser() + { + _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + } +}; + +static DebugFlagsInitialiser debugFlagsInitialiser; +#endif + +//============================================================================== +#if JUCE_MINGW + static uint64 getWindowsVersion() + { + auto filename = _T ("kernel32.dll"); + DWORD handle = 0; + + if (auto size = GetFileVersionInfoSize (filename, &handle)) + { + HeapBlock data (size); + + 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 ((uint64) info->dwFileVersionMS << 32) | (uint64) info->dwFileVersionLS; + } + } + + return 0; + } +#else + RTL_OSVERSIONINFOW getWindowsVersionInfo(); + RTL_OSVERSIONINFOW getWindowsVersionInfo() + { + RTL_OSVERSIONINFOW versionInfo = {}; + + 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 = {}; + } + } + + return versionInfo; + } +#endif + +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() +{ + #if JUCE_MINGW + const auto v = getWindowsVersion(); + const auto major = (v >> 48) & 0xffff; + const auto minor = (v >> 32) & 0xffff; + const auto build = (v >> 16) & 0xffff; + #else + const auto versionInfo = getWindowsVersionInfo(); + const auto major = versionInfo.dwMajorVersion; + const auto minor = versionInfo.dwMinorVersion; + const auto build = versionInfo.dwBuildNumber; + #endif + + jassert (major <= 10); // need to add support for new version! + + if (major == 10 && build >= 22000) return Windows11; + 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 Windows; +} + +String SystemStats::getOperatingSystemName() +{ + const auto type = getOperatingSystemType(); + + if (type == Windows11) return "Windows 11"; + if (type == Windows10) return "Windows 10"; + if (type == Windows8_1) return "Windows 8.1"; + if (type == Windows8_0) return "Windows 8.0"; + if (type == Windows7) return "Windows 7"; + if (type == WinVista) return "Windows Vista"; + if (type == WinXP) return "Windows XP"; + if (type == Win2000) return "Windows 2000"; + + jassertfalse; + return "Unknown OS"; +} + +String SystemStats::getDeviceDescription() +{ + #if WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP + return "Windows (Desktop)"; + #elif WINAPI_FAMILY == WINAPI_FAMILY_PC_APP + return "Windows (Store)"; + #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + return "Windows (Phone)"; + #elif WINAPI_FAMILY == WINAPI_FAMILY_SYSTEM + return "Windows (System)"; + #elif WINAPI_FAMILY == WINAPI_FAMILY_SERVER + return "Windows (Server)"; + #else + return "Windows"; + #endif +} + +String SystemStats::getDeviceManufacturer() +{ + return {}; +} + +bool SystemStats::isOperatingSystem64Bit() +{ + #if JUCE_64BIT + return true; + #else + typedef BOOL (WINAPI* LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); + + const auto moduleHandle = GetModuleHandleA ("kernel32"); + + if (moduleHandle == nullptr) + { + jassertfalse; + return false; + } + + LPFN_ISWOW64PROCESS fnIsWow64Process + = (LPFN_ISWOW64PROCESS) GetProcAddress (moduleHandle, "IsWow64Process"); + + BOOL isWow64 = FALSE; + + return fnIsWow64Process != nullptr + && fnIsWow64Process (GetCurrentProcess(), &isWow64) + && isWow64 != FALSE; + #endif +} + +//============================================================================== +int SystemStats::getMemorySizeInMegabytes() +{ + MEMORYSTATUSEX mem; + mem.dwLength = sizeof (mem); + GlobalMemoryStatusEx (&mem); + return (int) (mem.ullTotalPhys / (1024 * 1024)) + 1; +} + +//============================================================================== +String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue) +{ + auto len = GetEnvironmentVariableW (name.toWideCharPointer(), nullptr, 0); + + if (len == 0) + return String (defaultValue); + + HeapBlock buffer (len); + len = GetEnvironmentVariableW (name.toWideCharPointer(), buffer, len); + + return String (CharPointer_wchar_t (buffer), + CharPointer_wchar_t (buffer + len)); +} + +//============================================================================== +uint32 juce_millisecondsSinceStartup() noexcept +{ + return (uint32) timeGetTime(); +} + +//============================================================================== +class HiResCounterHandler +{ +public: + HiResCounterHandler() + : hiResTicksOffset (0) + { + // This macro allows you to override the default timer-period + // used on Windows. By default this is set to 1, because that has + // always been the value used in JUCE apps, and changing it could + // affect the behaviour of existing code, but you may wish to make + // it larger (or set it to 0 to use the system default) to make your + // app less demanding on the CPU. + // For more info, see win32 documentation about the timeBeginPeriod + // function. + #ifndef JUCE_WIN32_TIMER_PERIOD + #define JUCE_WIN32_TIMER_PERIOD 1 + #endif + + #if JUCE_WIN32_TIMER_PERIOD > 0 + [[maybe_unused]] auto res = timeBeginPeriod (JUCE_WIN32_TIMER_PERIOD); + jassert (res == TIMERR_NOERROR); + #endif + + LARGE_INTEGER f; + QueryPerformanceFrequency (&f); + hiResTicksPerSecond = f.QuadPart; + hiResTicksScaleFactor = 1000.0 / (double) hiResTicksPerSecond; + } + + inline int64 getHighResolutionTicks() noexcept + { + LARGE_INTEGER ticks; + QueryPerformanceCounter (&ticks); + return ticks.QuadPart + hiResTicksOffset; + } + + inline double getMillisecondCounterHiRes() noexcept + { + return (double) getHighResolutionTicks() * hiResTicksScaleFactor; + } + + int64 hiResTicksPerSecond, hiResTicksOffset; + double hiResTicksScaleFactor; +}; + +static HiResCounterHandler hiResCounterHandler; + +int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterHandler.hiResTicksPerSecond; } +int64 Time::getHighResolutionTicks() noexcept { return hiResCounterHandler.getHighResolutionTicks(); } +double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterHandler.getMillisecondCounterHiRes(); } + +//============================================================================== +static int64 juce_getClockCycleCounter() noexcept +{ + #if JUCE_MSVC + #if JUCE_INTEL + // MS intrinsics version... + return (int64) __rdtsc(); + #elif JUCE_ARM + #if defined (_M_ARM) + return __rdpmccntr64(); + #elif defined (_M_ARM64) + return _ReadStatusReg (ARM64_PMCCNTR_EL0); + #else + #error Unknown arm architecture + #endif + #endif + #elif JUCE_GCC || JUCE_CLANG + #if JUCE_INTEL + // GNU inline asm version... + unsigned int hi = 0, lo = 0; + + __asm__ __volatile__ ( + "xor %%eax, %%eax \n\ + xor %%edx, %%edx \n\ + rdtsc \n\ + movl %%eax, %[lo] \n\ + movl %%edx, %[hi]" + : + : [hi] "m" (hi), + [lo] "m" (lo) + : "cc", "eax", "ebx", "ecx", "edx", "memory"); + + return (int64) ((((uint64) hi) << 32) | lo); + #elif JUCE_ARM + int64 retval; + + __asm__ __volatile__ ("mrs %0, cntvct_el0" : "=r"(retval)); + return retval; + #endif + #else + #error "unknown compiler?" + #endif +} + +int SystemStats::getCpuSpeedInMegahertz() +{ + auto cycles = juce_getClockCycleCounter(); + auto millis = Time::getMillisecondCounter(); + int lastResult = 0; + + for (;;) + { + int n = 1000000; + while (--n > 0) {} + + auto millisElapsed = Time::getMillisecondCounter() - millis; + auto cyclesNow = juce_getClockCycleCounter(); + + if (millisElapsed > 80) + { + auto newResult = (int) (((cyclesNow - cycles) / millisElapsed) / 1000); + + if (millisElapsed > 500 || (lastResult == newResult && newResult > 100)) + return newResult; + + lastResult = newResult; + } + } +} + + +//============================================================================== +bool Time::setSystemTimeToThisTime() const +{ + SYSTEMTIME st; + + st.wDayOfWeek = 0; + st.wYear = (WORD) getYear(); + st.wMonth = (WORD) (getMonth() + 1); + st.wDay = (WORD) getDayOfMonth(); + st.wHour = (WORD) getHours(); + st.wMinute = (WORD) getMinutes(); + st.wSecond = (WORD) getSeconds(); + st.wMilliseconds = (WORD) (millisSinceEpoch % 1000); + + // do this twice because of daylight saving conversion problems - the + // first one sets it up, the second one kicks it in. + // NB: the local variable is here to avoid analysers warning about having + // two identical sub-expressions in the return statement + auto firstCallToSetTimezone = SetLocalTime (&st) != 0; + return firstCallToSetTimezone && SetLocalTime (&st) != 0; +} + +int SystemStats::getPageSize() +{ + SYSTEM_INFO systemInfo; + GetNativeSystemInfo (&systemInfo); + + return (int) systemInfo.dwPageSize; +} + +//============================================================================== +String SystemStats::getLogonName() +{ + TCHAR text [256] = { 0 }; + auto len = (DWORD) numElementsInArray (text) - 1; + GetUserName (text, &len); + return String (text, len); +} + +String SystemStats::getFullUserName() +{ + return getLogonName(); +} + +String SystemStats::getComputerName() +{ + TCHAR text[128] = { 0 }; + auto len = (DWORD) numElementsInArray (text) - 1; + GetComputerNameEx (ComputerNamePhysicalDnsHostname, text, &len); + return String (text, len); +} + +static String getLocaleValue (LCID locale, LCTYPE key, const char* defaultValue) +{ + TCHAR buffer [256] = { 0 }; + if (GetLocaleInfo (locale, key, buffer, 255) > 0) + return buffer; + + return defaultValue; +} + +String SystemStats::getUserLanguage() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, "en"); } +String SystemStats::getUserRegion() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, "US"); } + +String SystemStats::getDisplayLanguage() +{ + DynamicLibrary dll ("kernel32.dll"); + JUCE_LOAD_WINAPI_FUNCTION (dll, + GetUserPreferredUILanguages, + getUserPreferredUILanguages, + BOOL, + (DWORD, PULONG, PZZWSTR, PULONG)) + + constexpr auto defaultResult = "en"; + + if (getUserPreferredUILanguages == nullptr) + return defaultResult; + + ULONG numLanguages = 0; + ULONG numCharsInLanguagesBuffer = 0; + + // Retrieving the necessary buffer size for storing the list of languages + if (! getUserPreferredUILanguages (MUI_LANGUAGE_NAME, &numLanguages, nullptr, &numCharsInLanguagesBuffer)) + return defaultResult; + + std::vector languagesBuffer (numCharsInLanguagesBuffer); + const auto success = getUserPreferredUILanguages (MUI_LANGUAGE_NAME, + &numLanguages, + languagesBuffer.data(), + &numCharsInLanguagesBuffer); + + if (! success || numLanguages == 0) + return defaultResult; + + // The buffer contains a zero delimited list of languages, the first being + // the currently displayed language. + return languagesBuffer.data(); +} + +static constexpr DWORD generateProviderID (const char* string) +{ + return (DWORD) string[0] << 0x18 + | (DWORD) string[1] << 0x10 + | (DWORD) string[2] << 0x08 + | (DWORD) string[3] << 0x00; +} + +static std::optional> readSMBIOSData() +{ + const auto sig = generateProviderID ("RSMB"); + const auto id = generateProviderID ("RSDT"); + + if (const auto bufLen = GetSystemFirmwareTable (sig, id, nullptr, 0); bufLen > 0) + { + std::vector buffer; + + buffer.resize (bufLen); + + if (GetSystemFirmwareTable (sig, id, buffer.data(), bufLen) == buffer.size()) + return std::make_optional (std::move (buffer)); + } + + return {}; +} + +String getLegacyUniqueDeviceID() +{ + if (const auto dump = readSMBIOSData()) + { + uint64_t hash = 0; + const auto start = dump->data(); + const auto end = start + jmin (1024, (int) dump->size()); + + for (auto dataPtr = start; dataPtr != end; ++dataPtr) + hash = hash * (uint64_t) 101 + (uint8_t) *dataPtr; + + return String (hash); + } + + return {}; +} + +String SystemStats::getUniqueDeviceID() +{ + if (const auto smbiosBuffer = readSMBIOSData()) + { + #pragma pack (push, 1) + struct RawSMBIOSData + { + uint8_t unused[4]; + uint32_t length; + }; + + struct SMBIOSHeader + { + uint8_t id; + uint8_t length; + uint16_t handle; + }; + #pragma pack (pop) + + if (smbiosBuffer->size() < sizeof (RawSMBIOSData)) + { + // Malformed buffer; not enough room for RawSMBIOSData instance + jassertfalse; + return {}; + } + + String uuid; + const auto* asRawSMBIOSData = unalignedPointerCast (smbiosBuffer->data()); + + if (smbiosBuffer->size() < sizeof (RawSMBIOSData) + static_cast (asRawSMBIOSData->length)) + { + // Malformed buffer; declared length is longer than the buffer we were given + jassertfalse; + return {}; + } + + Span content (smbiosBuffer->data() + sizeof (RawSMBIOSData), asRawSMBIOSData->length); + + while (! content.empty()) + { + if (content.size() < sizeof (SMBIOSHeader)) + { + // Malformed buffer; not enough room for header + jassertfalse; + break; + } + + const auto* header = unalignedPointerCast (content.data()); + + if (content.size() < header->length) + { + // Malformed buffer; declared length is longer than the buffer we were given + jassertfalse; + break; + } + + std::vector strings; + + // Each table comprises a struct and a varying number of null terminated + // strings. The string section is delimited by a pair of null terminators. + // Some fields in the header are indices into the string table. + + const auto endOfStringTable = [&header, &strings, &content] + { + const auto* dataTable = unalignedPointerCast (content.data()); + size_t stringOffset = header->length; + + while (stringOffset < content.size()) + { + const auto* str = dataTable + stringOffset; + const auto maxLength = content.size() - stringOffset; + const auto n = strnlen (str, maxLength); + + if (n == 0) + break; + + strings.emplace_back (str, n); + stringOffset += std::min (n + 1, maxLength); + } + + const auto lengthAfterHeader = jmax ((size_t) header->length + 2, stringOffset + 1); + return jmin (lengthAfterHeader, content.size()); + }(); + + const auto stringFromOffset = [&content, &strings] (size_t byteOffset) -> String + { + if (! isPositiveAndBelow (byteOffset, content.size())) + return std::string{}; + + const auto index = std::to_integer (content[byteOffset]); + + if (index <= 0 || strings.size() < index) + return std::string{}; + + const auto view = strings[index - 1]; + return std::string { view }; + }; + + enum + { + systemManufacturer = 0x04, + systemProductName = 0x05, + systemSerialNumber = 0x07, + systemUUID = 0x08, // 16byte UUID. Can be all 0xFF or all 0x00. Might be user changeable. + systemSKU = 0x19, + systemFamily = 0x1a, + + baseboardManufacturer = 0x04, + baseboardProduct = 0x05, + baseboardVersion = 0x06, + baseboardSerialNumber = 0x07, + baseboardAssetTag = 0x08, + + processorManufacturer = 0x07, + processorVersion = 0x10, + processorAssetTag = 0x21, + processorPartNumber = 0x22 + }; + + switch (header->id) + { + case 1: // System + { + uuid += stringFromOffset (systemManufacturer); + uuid += "\n"; + uuid += stringFromOffset (systemProductName); + uuid += "\n"; + + char hexBuf[(16 * 2) + 1]{}; + + if (systemUUID + 16 < content.size()) + { + const auto* src = content.data() + systemUUID; + + for (auto i = 0; i != 16; ++i) + snprintf (hexBuf + 2 * i, 3, "%02hhX", std::to_integer (src[i])); + } + + uuid += hexBuf; + uuid += "\n"; + break; + } + + case 2: // Baseboard + uuid += stringFromOffset (baseboardManufacturer); + uuid += "\n"; + uuid += stringFromOffset (baseboardProduct); + uuid += "\n"; + uuid += stringFromOffset (baseboardVersion); + uuid += "\n"; + uuid += stringFromOffset (baseboardSerialNumber); + uuid += "\n"; + uuid += stringFromOffset (baseboardAssetTag); + uuid += "\n"; + break; + + case 4: // Processor + uuid += stringFromOffset (processorManufacturer); + uuid += "\n"; + uuid += stringFromOffset (processorVersion); + uuid += "\n"; + uuid += stringFromOffset (processorAssetTag); + uuid += "\n"; + uuid += stringFromOffset (processorPartNumber); + uuid += "\n"; + break; + } + + content = Span (content.data() + endOfStringTable, content.size() - endOfStringTable); + } + + return String (uuid.hashCode64()); + } + + // Please tell someone at JUCE if this occurs + jassertfalse; + return {}; +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_ThreadPriorities_native.h b/JuceLibraryCode/modules/juce_core/native/juce_ThreadPriorities_native.h new file mode 100644 index 00000000..e836cb70 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_ThreadPriorities_native.h @@ -0,0 +1,109 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +struct ThreadPriorities +{ + struct Entry + { + Thread::Priority priority; + int native; + }; + + #if JUCE_ANDROID + enum AndroidThreadPriority + { + THREAD_PRIORITY_AUDIO = -16, + THREAD_PRIORITY_FOREGROUND = -2, + THREAD_PRIORITY_MORE_FAVORABLE = -1, + THREAD_PRIORITY_DEFAULT = 0, + THREAD_PRIORITY_LESS_FAVORABLE = 1, + THREAD_PRIORITY_BACKGROUND = 10, + THREAD_PRIORITY_LOWEST = 19 + }; + #endif + + inline static constexpr Entry table[] + { + #if JUCE_ANDROID + { Thread::Priority::highest, AndroidThreadPriority::THREAD_PRIORITY_AUDIO }, + { Thread::Priority::high, AndroidThreadPriority::THREAD_PRIORITY_FOREGROUND }, + { Thread::Priority::normal, AndroidThreadPriority::THREAD_PRIORITY_DEFAULT }, + { Thread::Priority::low, AndroidThreadPriority::THREAD_PRIORITY_BACKGROUND - 5 }, + { Thread::Priority::background, AndroidThreadPriority::THREAD_PRIORITY_BACKGROUND }, + #endif + + #if JUCE_LINUX || JUCE_BSD + { Thread::Priority::highest, 0 }, + { Thread::Priority::high, 0 }, + { Thread::Priority::normal, 0 }, + { Thread::Priority::low, 0 }, + { Thread::Priority::background, 0 }, + #endif + + #if JUCE_MAC || JUCE_IOS + { Thread::Priority::highest, 4 }, + { Thread::Priority::high, 3 }, + { Thread::Priority::normal, 2 }, + { Thread::Priority::low, 1 }, + { Thread::Priority::background, 0 }, + #endif + + #if JUCE_WINDOWS + { Thread::Priority::highest, THREAD_PRIORITY_TIME_CRITICAL }, + { Thread::Priority::high, THREAD_PRIORITY_HIGHEST }, + { Thread::Priority::normal, THREAD_PRIORITY_NORMAL }, + { Thread::Priority::low, THREAD_PRIORITY_LOWEST }, + { Thread::Priority::background, THREAD_PRIORITY_IDLE }, + #endif + }; + + static_assert (std::size (table) == 5, + "The platform may be unsupported or there may be a priority entry missing."); + + static Thread::Priority getJucePriority (const int value) + { + const auto iter = std::min_element (std::begin (table), + std::end (table), + [value] (const auto& a, const auto& b) + { + return std::abs (a.native - value) < std::abs (b.native - value); + }); + + jassert (iter != std::end (table)); + return iter != std::end (table) ? iter->priority : Thread::Priority{}; + } + + static int getNativePriority (const Thread::Priority value) + { + const auto iter = std::find_if (std::begin (table), + std::end (table), + [value] (const auto& entry) { return entry.priority == value; }); + + jassert (iter != std::end (table)); + return iter != std::end (table) ? iter->native : 0; + } +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Threads_android.cpp similarity index 80% rename from JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_Threads_android.cpp index 6ec76212..21b7a910 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_Threads_android.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -56,8 +56,9 @@ JNIEnv* getEnv() noexcept return nullptr; } -void JNICALL juce_JavainitialiseJUCE (JNIEnv* env, jobject /*jclass*/, jobject context) +static void JNICALL juce_JavainitialiseJUCE (JNIEnv* env, jobject /*jclass*/, jobject context) { + JNIClassBase::initialiseAllClasses (env, context); Thread::initialiseJUCE (env, context); } @@ -71,7 +72,7 @@ extern "C" jint JNIEXPORT JNI_OnLoad (JavaVM* vm, void*) auto* env = getEnv(); // register the initialisation function - auto juceJavaClass = env->FindClass("com/roli/juce/Java"); + auto juceJavaClass = env->FindClass ("com/rmsl/juce/Java"); if (juceJavaClass != nullptr) { @@ -83,19 +84,17 @@ extern "C" jint JNIEXPORT JNI_OnLoad (JavaVM* vm, void*) } else { - // com.roli.juce.Java class not found. Apparently this project is a library + // com.rmsl.juce.Java class not found. Apparently this project is a library // or was not generated by the Projucer. That's ok, the user will have to // call Thread::initialiseJUCE manually env->ExceptionClear(); } - JNIClassBase::initialiseAllClasses (env); - return JNI_VERSION_1_2; } //============================================================================== -class JuceActivityWatcher : public ActivityLifecycleCallbacks +class JuceActivityWatcher final : public ActivityLifecycleCallbacks { public: JuceActivityWatcher() @@ -345,36 +344,95 @@ LocalRef getMainActivity() noexcept } //============================================================================== -// sets the process to 0=low priority, 1=normal, 2=high, 3=realtime -JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior) +using RealtimeThreadFactory = pthread_t (*) (void* (*entry) (void*), void* userPtr); +// This is defined in the juce_audio_devices module, with different definitions depending on +// whether OpenSL/Oboe are enabled. +RealtimeThreadFactory getAndroidRealtimeThreadFactory(); + +#if ! JUCE_MODULE_AVAILABLE_juce_audio_devices +RealtimeThreadFactory getAndroidRealtimeThreadFactory() { return nullptr; } +#endif + +extern JavaVM* androidJNIJavaVM; + +static auto setPriorityOfThisThread (Thread::Priority p) { - // TODO + return setpriority (PRIO_PROCESS, + (id_t) gettid(), + ThreadPriorities::getNativePriority (p)) == 0; +} - struct sched_param param; - int policy, maxp, minp; +bool Thread::createNativeThread (Priority) +{ + const auto threadEntryProc = [] (void* userData) -> void* + { + auto* myself = static_cast (userData); - const int p = (int) prior; + setPriorityOfThisThread (myself->priority); - if (p <= 1) - policy = SCHED_OTHER; - else - policy = SCHED_RR; + juce_threadEntryPoint (myself); - minp = sched_get_priority_min (policy); - maxp = sched_get_priority_max (policy); + if (androidJNIJavaVM != nullptr) + { + void* env = nullptr; + androidJNIJavaVM->GetEnv (&env, JNI_VERSION_1_2); - if (p < 2) - param.sched_priority = 0; - else if (p == 2 ) - // Set to middle of lower realtime priority range - param.sched_priority = minp + (maxp - minp) / 4; - else - // Set to middle of higher realtime priority range - param.sched_priority = minp + (3 * (maxp - minp) / 4); + // only detach if we have actually been attached + if (env != nullptr) + androidJNIJavaVM->DetachCurrentThread(); + } + + return nullptr; + }; - pthread_setschedparam (pthread_self(), policy, ¶m); + if (isRealtime()) + { + if (const auto factory = getAndroidRealtimeThreadFactory()) + { + threadHandle = (void*) factory (threadEntryProc, this); + threadId = (ThreadID) threadHandle.load(); + return threadId != nullptr; + } + else + { + jassertfalse; + } + } + + PosixThreadAttribute attr { threadStackSize }; + threadId = threadHandle = makeThreadHandle (attr, this, threadEntryProc); + + return threadId != nullptr; +} + +void Thread::killThread() +{ + if (threadHandle != nullptr) + jassertfalse; // pthread_cancel not available! +} + +Thread::Priority Thread::getPriority() const +{ + jassert (Thread::getCurrentThreadId() == getThreadId()); + + const auto native = getpriority (PRIO_PROCESS, (id_t) gettid()); + return ThreadPriorities::getJucePriority (native); } +bool Thread::setPriority (Priority priorityIn) +{ + jassert (Thread::getCurrentThreadId() == getThreadId()); + + if (isRealtime()) + return false; + + const auto priorityToUse = priority = priorityIn; + return setPriorityOfThisThread (priorityToUse) == 0; +} + +//============================================================================== +JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) {} + JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept { StringArray lines; @@ -390,6 +448,4 @@ JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {} JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {} - - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Threads_linux.cpp similarity index 57% rename from JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_Threads_linux.cpp index a8dc7d9c..aae00d50 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_Threads_linux.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,27 +28,49 @@ namespace juce live in juce_posix_SharedCode.h! */ -//============================================================================== -JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior) +bool Thread::createNativeThread (Priority) { - 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; + PosixThreadAttribute attr { threadStackSize }; + PosixSchedulerPriority::getNativeSchedulerAndPriority (realtimeOptions, {}).apply (attr); - switch (prior) + threadId = threadHandle = makeThreadHandle (attr, this, [] (void* userData) -> void* { - case LowPriority: - case NormalPriority: param.sched_priority = 0; break; - case HighPriority: param.sched_priority = minp + (maxp - minp) / 4; break; - case RealtimePriority: param.sched_priority = minp + (3 * (maxp - minp) / 4); break; - default: jassertfalse; break; - } - - pthread_setschedparam (pthread_self(), policy, ¶m); + auto* myself = static_cast (userData); + + juce_threadEntryPoint (myself); + + return nullptr; + }); + + return threadId != nullptr; +} + +void Thread::killThread() +{ + if (threadHandle != nullptr) + pthread_cancel ((pthread_t) threadHandle.load()); +} + +// Until we implement Nice awareness, these don't do anything on Linux. +Thread::Priority Thread::getPriority() const +{ + jassert (Thread::getCurrentThreadId() == getThreadId()); + + return priority; } +bool Thread::setPriority (Priority newPriority) +{ + jassert (Thread::getCurrentThreadId() == getThreadId()); + + // Return true to make it compatible with other platforms. + priority = newPriority; + return true; +} + +//============================================================================== +JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) {} + static bool swapUserAndEffectiveUser() { auto result1 = setreuid (geteuid(), getuid()); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Threads_mac.mm b/JuceLibraryCode/modules/juce_core/native/juce_Threads_mac.mm new file mode 100644 index 00000000..64b10957 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_Threads_mac.mm @@ -0,0 +1,260 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.h! +*/ + +#if JUCE_IOS + bool isIOSAppActive = true; +#endif + +API_AVAILABLE (macos (10.10)) +static auto getNativeQOS (Thread::Priority priority) +{ + switch (priority) + { + case Thread::Priority::highest: return QOS_CLASS_USER_INTERACTIVE; + case Thread::Priority::high: return QOS_CLASS_USER_INITIATED; + case Thread::Priority::low: return QOS_CLASS_UTILITY; + case Thread::Priority::background: return QOS_CLASS_BACKGROUND; + case Thread::Priority::normal: break; + } + + return QOS_CLASS_DEFAULT; +} + +API_AVAILABLE (macos (10.10)) +static auto getJucePriority (qos_class_t qos) +{ + switch (qos) + { + case QOS_CLASS_USER_INTERACTIVE: return Thread::Priority::highest; + case QOS_CLASS_USER_INITIATED: return Thread::Priority::high; + case QOS_CLASS_UTILITY: return Thread::Priority::low; + case QOS_CLASS_BACKGROUND: return Thread::Priority::background; + + case QOS_CLASS_UNSPECIFIED: + case QOS_CLASS_DEFAULT: break; + } + + return Thread::Priority::normal; +} + +template +static std::optional firstOptionalWithValue (const std::initializer_list>& optionals) +{ + for (const auto& optional : optionals) + if (optional.has_value()) + return optional; + + return {}; +} + +static bool tryToUpgradeCurrentThreadToRealtime (const Thread::RealtimeOptions& options) +{ + const auto periodMs = options.getPeriodMs().value_or (0.0); + + const auto processingTimeMs = firstOptionalWithValue ( + { + options.getProcessingTimeMs(), + options.getMaximumProcessingTimeMs(), + options.getPeriodMs() + }).value_or (10.0); + + const auto maxProcessingTimeMs = options.getMaximumProcessingTimeMs() + .value_or (processingTimeMs); + + // The processing time can not exceed the maximum processing time! + jassert (maxProcessingTimeMs >= processingTimeMs); + + thread_time_constraint_policy_data_t policy; + policy.period = (uint32_t) Time::secondsToHighResolutionTicks (periodMs / 1'000.0); + policy.computation = (uint32_t) Time::secondsToHighResolutionTicks (processingTimeMs / 1'000.0); + policy.constraint = (uint32_t) Time::secondsToHighResolutionTicks (maxProcessingTimeMs / 1'000.0); + policy.preemptible = true; + + const auto result = thread_policy_set (pthread_mach_thread_np (pthread_self()), + THREAD_TIME_CONSTRAINT_POLICY, + (thread_policy_t) &policy, + THREAD_TIME_CONSTRAINT_POLICY_COUNT); + + if (result == KERN_SUCCESS) + return true; + + // testing has shown that passing a computation value > 50ms can + // lead to thread_policy_set returning an error indicating that an + // invalid argument was passed. If that happens this code tries to + // limit that value in the hope of resolving the issue. + + if (result == KERN_INVALID_ARGUMENT && options.getProcessingTimeMs() > 50.0) + return tryToUpgradeCurrentThreadToRealtime (options.withProcessingTimeMs (50.0)); + + return false; +} + +bool Thread::createNativeThread (Priority priority) +{ + PosixThreadAttribute attribute { threadStackSize }; + + if (@available (macos 10.10, *)) + pthread_attr_set_qos_class_np (attribute.get(), getNativeQOS (priority), 0); + else + PosixSchedulerPriority::getNativeSchedulerAndPriority (realtimeOptions, priority).apply (attribute); + + struct ThreadData + { + Thread& thread; + std::promise started{}; + }; + + ThreadData threadData { *this, {} }; + + threadId = threadHandle = makeThreadHandle (attribute, &threadData, [] (void* userData) -> void* + { + auto& data { *static_cast (userData) }; + auto& thread = data.thread; + + if (thread.isRealtime() + && ! tryToUpgradeCurrentThreadToRealtime (*thread.realtimeOptions)) + { + data.started.set_value (false); + return nullptr; + } + + data.started.set_value (true); + + JUCE_AUTORELEASEPOOL + { + juce_threadEntryPoint (&thread); + } + + return nullptr; + }); + + return threadId != nullptr + && threadData.started.get_future().get(); +} + +void Thread::killThread() +{ + if (threadHandle != nullptr) + pthread_cancel ((pthread_t) threadHandle.load()); +} + +Thread::Priority Thread::getPriority() const +{ + jassert (Thread::getCurrentThreadId() == getThreadId()); + + if (! isRealtime()) + { + if (@available (macOS 10.10, *)) + return getJucePriority (qos_class_self()); + + // fallback for older versions of macOS + const auto min = jmax (0, sched_get_priority_min (SCHED_OTHER)); + const auto max = jmax (0, sched_get_priority_max (SCHED_OTHER)); + + if (min != 0 && max != 0) + { + const auto native = PosixSchedulerPriority::findCurrentSchedulerAndPriority().getPriority(); + const auto mapped = jmap (native, min, max, 0, 4); + return ThreadPriorities::getJucePriority (mapped); + } + } + + return {}; +} + +bool Thread::setPriority (Priority priority) +{ + jassert (Thread::getCurrentThreadId() == getThreadId()); + + if (@available (macOS 10.10, *)) + return pthread_set_qos_class_self_np (getNativeQOS (priority), 0) == 0; + + #if JUCE_ARM + // M1 platforms should never reach this code!!!!!! + jassertfalse; + #endif + + // Just in case older versions of macOS support SCHED_OTHER priorities. + const auto psp = PosixSchedulerPriority::getNativeSchedulerAndPriority ({}, priority); + + struct sched_param param; + param.sched_priority = psp.getPriority(); + return pthread_setschedparam (pthread_self(), psp.getScheduler(), ¶m) == 0; +} + +//============================================================================== +JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() +{ + if (SystemStats::isRunningInAppExtensionSandbox()) + return true; + + #if JUCE_MAC + return [NSApp isActive]; + #else + return isIOSAppActive; + #endif +} + +JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() +{ + #if JUCE_MAC + if (! SystemStats::isRunningInAppExtensionSandbox()) + [NSApp activateIgnoringOtherApps: YES]; + #endif +} + +JUCE_API void JUCE_CALLTYPE Process::hide() +{ + if (! SystemStats::isRunningInAppExtensionSandbox()) + { + #if JUCE_MAC + [NSApp hide: nil]; + #elif JUCE_IOS + [[UIApplication sharedApplication] performSelector: @selector (suspend)]; + #endif + } +} + +JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {} +JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {} + +JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) {} + +//============================================================================== +JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept +{ + struct kinfo_proc info; + int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; + size_t sz = sizeof (info); + sysctl (m, 4, &info, &sz, nullptr, 0); + return (info.kp_proc.p_flag & P_TRACED) != 0; +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp b/JuceLibraryCode/modules/juce_core/native/juce_Threads_windows.cpp similarity index 73% rename from JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_Threads_windows.cpp index 62388452..594f145c 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_Threads_windows.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,13 +23,17 @@ namespace juce { -HWND juce_messageWindowHandle = 0; // (this is used by other parts of the codebase) +HWND juce_messageWindowHandle = nullptr; // (this is used by other parts of the codebase) void* getUser32Function (const char* functionName) { HMODULE module = GetModuleHandleA ("user32.dll"); - jassert (module != 0); - return (void*) GetProcAddress (module, functionName); + + if (module != nullptr) + return (void*) GetProcAddress (module, functionName); + + jassertfalse; + return nullptr; } //============================================================================== @@ -39,22 +43,19 @@ CriticalSection::CriticalSection() noexcept static_assert (sizeof (CRITICAL_SECTION) <= sizeof (lock), "win32 lock array too small to hold CRITICAL_SECTION: please report this JUCE bug!"); - InitializeCriticalSection ((CRITICAL_SECTION*) lock); + InitializeCriticalSection ((CRITICAL_SECTION*) &lock); } -CriticalSection::~CriticalSection() noexcept { DeleteCriticalSection ((CRITICAL_SECTION*) lock); } -void CriticalSection::enter() const noexcept { EnterCriticalSection ((CRITICAL_SECTION*) lock); } -bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSection ((CRITICAL_SECTION*) lock) != FALSE; } -void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) lock); } - +CriticalSection::~CriticalSection() noexcept { DeleteCriticalSection ((CRITICAL_SECTION*) &lock); } +void CriticalSection::enter() const noexcept { EnterCriticalSection ((CRITICAL_SECTION*) &lock); } +bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSection ((CRITICAL_SECTION*) &lock) != FALSE; } +void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) &lock); } //============================================================================== -void JUCE_API juce_threadEntryPoint (void*); - -static unsigned int __stdcall threadEntryProc (void* userData) +static unsigned int STDMETHODCALLTYPE threadEntryProc (void* userData) { - if (juce_messageWindowHandle != 0) - AttachThreadInput (GetWindowThreadProcessId (juce_messageWindowHandle, 0), + if (juce_messageWindowHandle != nullptr) + AttachThreadInput (GetWindowThreadProcessId (juce_messageWindowHandle, nullptr), GetCurrentThreadId(), TRUE); juce_threadEntryPoint (userData); @@ -63,33 +64,77 @@ static unsigned int __stdcall threadEntryProc (void* userData) return 0; } -void Thread::launchThread() +static bool setPriorityInternal (bool isRealtime, HANDLE handle, Thread::Priority priority) +{ + auto nativeThreadFlag = isRealtime ? THREAD_PRIORITY_TIME_CRITICAL + : ThreadPriorities::getNativePriority (priority); + + if (isRealtime) // This should probably be a fail state too? + Process::setPriority (Process::ProcessPriority::RealtimePriority); + + return SetThreadPriority (handle, nativeThreadFlag); +} + +bool Thread::createNativeThread (Priority priority) { unsigned int newThreadId; - threadHandle = (void*) _beginthreadex (0, (unsigned int) threadStackSize, - &threadEntryProc, this, 0, &newThreadId); - threadId = (ThreadID) (pointer_sized_int) newThreadId; + threadHandle = (void*) _beginthreadex (nullptr, (unsigned int) threadStackSize, + &threadEntryProc, this, CREATE_SUSPENDED, + &newThreadId); + + if (threadHandle != nullptr) + { + threadId = (ThreadID) (pointer_sized_int) newThreadId; + + if (setPriorityInternal (isRealtime(), threadHandle, priority)) + { + ResumeThread (threadHandle); + return true; + } + + killThread(); + closeThreadHandle(); + } + + return false; +} + +Thread::Priority Thread::getPriority() const +{ + jassert (Thread::getCurrentThreadId() == getThreadId()); + + const auto native = GetThreadPriority (threadHandle); + return ThreadPriorities::getJucePriority (native); +} + +bool Thread::setPriority (Priority priority) +{ + jassert (Thread::getCurrentThreadId() == getThreadId()); + return setPriorityInternal (isRealtime(), this, priority); } void Thread::closeThreadHandle() { - CloseHandle ((HANDLE) threadHandle.get()); - threadId = 0; - threadHandle = 0; + CloseHandle (threadHandle); + threadId = nullptr; + threadHandle = nullptr; } void Thread::killThread() { - if (threadHandle.get() != 0) + if (threadHandle != nullptr) { #if JUCE_DEBUG OutputDebugStringA ("** Warning - Forced thread termination **\n"); #endif - TerminateThread (threadHandle.get(), 0); + + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6258) + TerminateThread (threadHandle, 0); + JUCE_END_IGNORE_WARNINGS_MSVC } } -void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) +void JUCE_CALLTYPE Thread::setCurrentThreadName ([[maybe_unused]] const String& name) { #if JUCE_DEBUG && JUCE_MSVC struct @@ -109,10 +154,11 @@ void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) { RaiseException (0x406d1388 /*MS_VC_EXCEPTION*/, 0, sizeof (info) / sizeof (ULONG_PTR), (ULONG_PTR*) &info); } - __except (EXCEPTION_CONTINUE_EXECUTION) - {} - #else - ignoreUnused (name); + __except (GetExceptionCode() == EXCEPTION_NONCONTINUABLE_EXCEPTION ? EXCEPTION_EXECUTE_HANDLER + : EXCEPTION_CONTINUE_EXECUTION) + { + OutputDebugStringA ("** Warning - Encountered noncontinuable exception **\n"); + } #endif } @@ -121,23 +167,6 @@ Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId() return (ThreadID) (pointer_sized_int) GetCurrentThreadId(); } -bool Thread::setThreadPriority (void* handle, int priority) -{ - int pri = THREAD_PRIORITY_TIME_CRITICAL; - - if (priority < 1) pri = THREAD_PRIORITY_IDLE; - else if (priority < 2) pri = THREAD_PRIORITY_LOWEST; - else if (priority < 5) pri = THREAD_PRIORITY_BELOW_NORMAL; - else if (priority < 7) pri = THREAD_PRIORITY_NORMAL; - else if (priority < 9) pri = THREAD_PRIORITY_ABOVE_NORMAL; - else if (priority < 10) pri = THREAD_PRIORITY_HIGHEST; - - if (handle == 0) - handle = GetCurrentThread(); - - return SetThreadPriority (handle, pri) != FALSE; -} - void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) { SetThreadAffinityMask (GetCurrentThread(), affinityMask); @@ -149,7 +178,7 @@ struct SleepEvent SleepEvent() noexcept : handle (CreateEvent (nullptr, FALSE, FALSE, #if JUCE_DEBUG - _T("JUCE Sleep Event"))) + _T ("JUCE Sleep Event"))) #else nullptr)) #endif @@ -158,7 +187,7 @@ struct SleepEvent ~SleepEvent() noexcept { CloseHandle (handle); - handle = 0; + handle = nullptr; } HANDLE handle; @@ -170,7 +199,7 @@ void JUCE_CALLTYPE Thread::sleep (const int millisecs) { jassert (millisecs >= 0); - if (millisecs >= 10 || sleepEvent.handle == 0) + if (millisecs >= 10 || sleepEvent.handle == nullptr) Sleep ((DWORD) millisecs); else // unlike Sleep() this is guaranteed to return to the current thread after @@ -189,6 +218,7 @@ static int lastProcessPriority = -1; // called when the app gains focus because Windows does weird things to process priority // when you swap apps, and this forces an update when the app is brought to the front. +void juce_repeatLastProcessPriority(); void juce_repeatLastProcessPriority() { if (lastProcessPriority >= 0) // (avoid changing this if it's not been explicitly set by the app..) @@ -208,11 +238,11 @@ void juce_repeatLastProcessPriority() } } -void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior) +void JUCE_CALLTYPE Process::setPriority (ProcessPriority newPriority) { - if (lastProcessPriority != (int) prior) + if (lastProcessPriority != (int) newPriority) { - lastProcessPriority = (int) prior; + lastProcessPriority = (int) newPriority; juce_repeatLastProcessPriority(); } } @@ -257,10 +287,11 @@ void JUCE_CALLTYPE Process::terminate() ExitProcess (1); } +bool juce_isRunningInWine(); bool juce_isRunningInWine() { HMODULE ntdll = GetModuleHandleA ("ntdll"); - return ntdll != 0 && GetProcAddress (ntdll, "wine_get_version") != nullptr; + return ntdll != nullptr && GetProcAddress (ntdll, "wine_get_version") != nullptr; } //============================================================================== @@ -291,19 +322,19 @@ void* DynamicLibrary::getFunction (const String& functionName) noexcept class InterProcessLock::Pimpl { public: - Pimpl (String name, const int timeOutMillisecs) - : handle (0), refCount (1) + Pimpl (String nameIn, const int timeOutMillisecs) + : handle (nullptr), refCount (1) { - name = name.replaceCharacter ('\\', '/'); - handle = CreateMutexW (0, TRUE, ("Global\\" + name).toWideCharPointer()); + nameIn = nameIn.replaceCharacter ('\\', '/'); + handle = CreateMutexW (nullptr, TRUE, ("Global\\" + nameIn).toWideCharPointer()); // Not 100% sure why a global mutex sometimes can't be allocated, but if it fails, fall back to // a local one. (A local one also sometimes fails on other machines so neither type appears to be // universally reliable) - if (handle == 0) - handle = CreateMutexW (0, TRUE, ("Local\\" + name).toWideCharPointer()); + if (handle == nullptr) + handle = CreateMutexW (nullptr, TRUE, ("Local\\" + nameIn).toWideCharPointer()); - if (handle != 0 && GetLastError() == ERROR_ALREADY_EXISTS) + if (handle != nullptr && GetLastError() == ERROR_ALREADY_EXISTS) { if (timeOutMillisecs == 0) { @@ -311,7 +342,7 @@ class InterProcessLock::Pimpl return; } - switch (WaitForSingleObject (handle, timeOutMillisecs < 0 ? INFINITE : timeOutMillisecs)) + switch (WaitForSingleObject (handle, timeOutMillisecs < 0 ? INFINITE : (DWORD) timeOutMillisecs)) { case WAIT_OBJECT_0: case WAIT_ABANDONED: @@ -332,11 +363,11 @@ class InterProcessLock::Pimpl void close() { - if (handle != 0) + if (handle != nullptr) { ReleaseMutex (handle); CloseHandle (handle); - handle = 0; + handle = nullptr; } } @@ -361,7 +392,7 @@ bool InterProcessLock::enter (const int timeOutMillisecs) { pimpl.reset (new Pimpl (name, timeOutMillisecs)); - if (pimpl->handle == 0) + if (pimpl->handle == nullptr) pimpl.reset(); } else @@ -388,25 +419,27 @@ class ChildProcess::ActiveProcess { public: ActiveProcess (const String& command, int streamFlags) - : ok (false), readPipe (0), writePipe (0) + : ok (false), readPipe (nullptr), writePipe (nullptr) { - SECURITY_ATTRIBUTES securityAtts = { 0 }; + SECURITY_ATTRIBUTES securityAtts = {}; securityAtts.nLength = sizeof (securityAtts); securityAtts.bInheritHandle = TRUE; if (CreatePipe (&readPipe, &writePipe, &securityAtts, 0) && SetHandleInformation (readPipe, HANDLE_FLAG_INHERIT, 0)) { - STARTUPINFOW startupInfo = { 0 }; + STARTUPINFOW startupInfo = {}; startupInfo.cb = sizeof (startupInfo); - startupInfo.hStdOutput = (streamFlags & wantStdOut) != 0 ? writePipe : 0; - startupInfo.hStdError = (streamFlags & wantStdErr) != 0 ? writePipe : 0; + startupInfo.hStdOutput = (streamFlags & wantStdOut) != 0 ? writePipe : nullptr; + startupInfo.hStdError = (streamFlags & wantStdErr) != 0 ? writePipe : nullptr; startupInfo.dwFlags = STARTF_USESTDHANDLES; + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6335) ok = CreateProcess (nullptr, const_cast (command.toWideCharPointer()), nullptr, nullptr, TRUE, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, &startupInfo, &processInfo) != FALSE; + JUCE_END_IGNORE_WARNINGS_MSVC } } @@ -418,10 +451,10 @@ class ChildProcess::ActiveProcess CloseHandle (processInfo.hProcess); } - if (readPipe != 0) + if (readPipe != nullptr) CloseHandle (readPipe); - if (writePipe != 0) + if (writePipe != nullptr) CloseHandle (writePipe); } @@ -448,17 +481,17 @@ class ChildProcess::ActiveProcess if (! isRunning()) break; - Thread::yield(); + Thread::sleep (1); } else { DWORD numRead = 0; - if (! ReadFile ((HANDLE) readPipe, dest, numToDo, &numRead, nullptr)) + if (! ReadFile ((HANDLE) readPipe, dest, (DWORD) numToDo, &numRead, nullptr)) break; - total += numRead; + total += (int) numRead; dest = addBytesToPointer (dest, numRead); - numNeeded -= numRead; + numNeeded -= (int) numRead; } } @@ -515,56 +548,4 @@ bool ChildProcess::start (const StringArray& args, int streamFlags) return start (escaped.trim(), streamFlags); } -//============================================================================== -struct HighResolutionTimer::Pimpl -{ - Pimpl (HighResolutionTimer& t) noexcept : owner (t) - { - } - - ~Pimpl() - { - jassert (periodMs == 0); - } - - void start (int newPeriod) - { - if (newPeriod != periodMs) - { - stop(); - periodMs = newPeriod; - - TIMECAPS tc; - if (timeGetDevCaps (&tc, sizeof (tc)) == TIMERR_NOERROR) - { - const int actualPeriod = jlimit ((int) tc.wPeriodMin, (int) tc.wPeriodMax, newPeriod); - - timerID = timeSetEvent (actualPeriod, tc.wPeriodMin, callbackFunction, (DWORD_PTR) this, - TIME_PERIODIC | TIME_CALLBACK_FUNCTION | 0x100 /*TIME_KILL_SYNCHRONOUS*/); - } - } - } - - void stop() - { - periodMs = 0; - timeKillEvent (timerID); - } - - HighResolutionTimer& owner; - int periodMs = 0; - -private: - unsigned int timerID; - - static void __stdcall callbackFunction (UINT, UINT, DWORD_PTR userInfo, DWORD_PTR, DWORD_PTR) - { - if (Pimpl* const timer = reinterpret_cast (userInfo)) - if (timer->periodMs != 0) - timer->owner.hiResTimerCallback(); - } - - JUCE_DECLARE_NON_COPYABLE (Pimpl) -}; - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp deleted file mode 100644 index 46f3b9b7..00000000 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp +++ /dev/null @@ -1,654 +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 -{ - -//============================================================================== -// This byte-code is generated from native/java/com/roli/juce/JuceHTTPStream.java with min sdk version 16 -// See juce_core/native/java/README.txt on how to generate this byte-code. -static const uint8 javaJuceHttpStream[] = -{31,139,8,8,96,105,229,91,0,3,74,117,99,101,72,84,84,80,83,116,114,101,97,109,46,100,101,120,0,125,154,11,124,84,213,157, -199,255,231,222,185,119,30,153,153,220,76,30,147,132,60,38,33,132,4,18,38,32,40,146,128,200,75,32,65,17,6,20,146,181,29, -50,23,50,48,220,9,51,119,32,88,84,64,171,212,218,74,173,86,250,82,139,88,105,87,91,171,237,214,86,187,109,197,118,219, -218,90,109,245,179,174,187,125,91,119,237,167,187,186,218,118,221,246,227,102,127,231,49,153,137,77,33,124,239,255,127, -254,231,127,206,61,143,255,121,204,36,41,123,34,208,119,193,18,58,123,73,224,169,145,163,171,22,29,152,124,110,71,248, -214,145,193,207,157,62,245,139,197,215,175,242,156,233,36,26,39,162,137,237,139,35,164,254,253,114,14,209,11,36,237,139, -192,183,116,162,56,228,9,15,81,61,228,147,38,209,149,144,71,188,68,200,34,79,128,104,101,19,81,10,242,205,90,162,63,130, -183,193,95,192,255,1,173,142,200,0,33,80,5,162,160,27,92,12,54,131,173,224,42,48,2,118,1,27,92,11,174,3,199,192,205,224, -86,112,59,56,5,206,128,179,224,41,240,10,104,140,18,45,3,215,128,27,193,253,224,187,224,183,128,161,193,49,112,17,184,28, -236,6,71,193,221,224,33,240,44,120,5,188,13,194,13,68,237,96,0,92,5,246,129,227,224,62,240,8,248,1,248,37,120,19,248,27, -137,174,6,105,112,45,248,4,120,2,188,6,106,102,161,14,112,13,56,2,62,9,254,5,76,130,30,140,211,213,96,20,236,6,123,129,3, -242,224,125,224,40,184,13,156,4,31,5,119,131,79,130,123,193,253,224,113,240,36,248,30,120,17,252,28,188,10,94,7,255,3, -222,1,70,51,81,16,68,65,12,244,128,53,96,59,24,3,215,130,219,192,167,193,3,224,9,112,14,252,24,188,10,254,2,204,22,244, -17,88,160,17,116,128,249,96,17,184,8,108,2,127,7,28,112,61,184,25,220,13,206,128,175,128,239,128,31,129,127,6,175,129,55, -193,36,240,181,18,213,129,78,208,15,46,3,27,193,86,240,94,176,23,28,2,71,193,9,112,23,184,23,60,0,30,6,143,129,175,129, -103,193,207,192,239,192,155,224,207,128,98,232,59,168,4,115,64,63,216,14,118,131,125,96,28,28,4,71,192,205,224,36,184,23, -60,0,190,0,190,9,94,4,63,3,191,6,255,14,254,12,188,109,68,179,65,31,88,9,182,128,221,96,63,112,193,17,112,2,124,28,60,8, -190,8,190,14,190,15,126,14,222,2,122,59,226,2,52,130,118,208,11,150,131,203,64,2,100,64,1,92,7,110,5,31,1,167,64,16,221, -178,0,194,138,16,62,132,233,37,76,15,97,40,73,117,153,80,61,193,149,102,131,14,128,229,75,88,214,52,23,116,129,110,48,15, -204,7,61,160,23,44,32,185,166,251,192,66,181,206,47,0,139,193,18,112,33,184,8,44,5,23,131,126,48,0,86,128,75,192,74,112, -41,88,5,214,128,117,224,50,176,30,108,4,91,192,54,176,29,92,69,178,31,197,127,33,37,151,98,111,8,43,125,101,153,190,30, -122,165,210,55,215,202,254,51,149,230,251,143,1,70,96,143,148,213,203,245,98,249,6,165,187,202,167,88,87,173,242,91,170, -236,181,202,94,173,244,35,202,30,45,179,71,149,189,70,233,55,65,175,83,250,109,202,94,175,236,181,74,95,170,244,134,50, -157,207,219,157,181,178,28,215,63,165,222,213,92,214,254,150,50,189,181,76,111,47,211,103,151,245,133,207,239,67,170,126, -62,199,93,170,206,121,202,135,207,67,175,210,7,149,206,251,114,133,210,191,12,125,72,233,79,150,233,125,101,58,111,255, -38,165,63,13,125,179,210,249,248,95,174,244,31,150,249,252,91,173,60,27,122,213,248,23,235,121,165,86,198,196,2,213,158, -173,74,255,61,236,9,165,187,170,47,11,213,123,117,204,244,247,136,203,5,244,28,164,7,35,103,11,89,75,123,132,236,161,180, -144,97,250,176,144,221,244,77,226,241,209,68,41,33,165,159,161,252,12,140,88,65,200,249,116,147,144,81,186,69,201,15,8, -41,235,49,240,190,59,72,198,217,105,33,25,125,78,200,56,125,94,200,122,250,130,144,125,244,13,33,139,239,197,154,87,242, -135,64,163,42,186,75,180,191,141,76,33,189,244,30,33,131,66,122,208,30,83,200,86,250,146,40,215,41,210,188,221,59,84,59, -71,133,172,163,221,66,154,52,166,236,7,132,244,211,97,33,13,186,65,201,99,42,255,164,144,58,61,36,100,43,61,172,198,225, -31,136,175,153,118,241,158,16,118,11,46,195,200,79,10,233,17,254,149,72,31,18,114,30,253,68,196,93,5,237,83,241,119,187, -136,189,86,81,46,138,113,217,165,228,135,72,198,246,199,132,92,64,95,23,178,146,190,45,164,37,100,61,86,212,136,144,17, -218,47,164,44,87,143,145,146,82,150,175,87,254,13,234,61,13,88,101,35,66,134,233,25,226,123,225,108,97,111,68,254,19,196, -215,83,43,57,66,250,232,90,33,27,233,125,42,125,68,200,0,93,79,114,221,29,21,50,74,199,133,140,209,99,66,118,211,151,149, -252,138,178,127,85,200,14,122,92,200,57,244,53,226,107,85,142,87,51,118,85,41,235,233,140,144,178,93,173,24,247,247,11, -25,164,123,136,239,205,33,154,32,190,63,7,233,58,37,111,36,190,158,103,145,75,124,45,55,208,157,196,215,113,51,125,139, -248,94,221,66,25,33,155,232,227,196,215,116,47,189,87,200,160,168,103,190,26,143,249,248,145,233,110,122,148,248,158,46, -237,92,222,45,228,124,122,86,165,127,76,242,142,70,36,215,22,223,35,170,32,31,199,198,117,229,28,105,247,148,229,247,169, -252,151,144,191,87,229,243,120,102,84,218,47,121,254,219,200,63,174,242,121,253,63,197,193,243,50,248,77,135,244,253,131, -146,239,116,240,117,128,251,4,124,67,115,164,45,170,100,167,146,151,40,185,126,14,175,75,23,250,167,176,233,249,32,135, -145,24,209,24,141,91,179,68,132,242,92,94,223,153,118,185,71,182,179,32,109,139,49,156,83,22,37,98,68,7,44,175,136,112, -199,234,19,114,60,86,141,18,85,108,122,222,2,33,187,222,228,109,99,226,125,15,183,203,126,58,22,183,4,69,31,77,252,240, -188,71,219,229,121,34,219,48,204,52,26,214,12,26,214,61,52,236,209,105,216,48,197,202,225,237,50,232,71,237,242,60,76, -244,121,80,23,223,105,3,56,63,107,41,177,80,167,102,150,232,51,209,82,31,113,25,137,228,98,235,17,35,45,76,250,94,46,74, -105,200,247,10,233,88,189,194,98,40,139,1,75,237,84,205,155,197,104,132,80,122,46,158,37,91,215,159,144,90,40,83,76,156, -16,132,213,171,3,46,153,58,187,116,113,206,243,158,47,17,82,71,219,47,184,33,116,225,114,172,204,0,241,81,122,165,93,238, -221,188,207,62,140,114,15,180,77,240,28,62,230,71,170,86,216,121,138,231,205,71,106,163,72,85,96,95,226,55,129,32,59,96, -93,198,199,145,13,31,11,208,200,173,149,52,252,193,8,13,223,22,164,29,31,170,163,225,15,215,208,240,237,213,20,249,239, -29,199,162,120,179,69,59,142,90,120,75,21,13,31,13,209,182,27,43,41,113,83,132,18,239,15,210,22,92,253,19,183,132,200, -123,204,123,199,65,175,95,213,232,69,207,53,117,35,168,154,45,227,47,129,185,173,18,177,233,21,49,82,15,123,158,199,135, -175,25,49,176,12,235,216,177,6,240,142,160,175,213,215,68,222,155,90,61,77,20,241,57,177,11,113,18,56,177,197,244,7,60, -47,194,179,209,119,53,180,86,156,3,65,45,162,183,117,45,185,49,78,235,124,154,222,232,215,97,111,166,83,20,48,151,155, -237,194,230,224,114,236,163,128,111,201,205,141,34,29,241,59,184,34,158,50,131,140,167,94,242,155,204,137,197,80,34,232, -229,158,94,120,46,247,97,22,251,46,166,26,239,75,186,206,186,158,79,156,13,161,214,165,104,195,210,96,132,34,245,78,108, -9,215,67,60,70,23,34,166,130,134,131,15,47,47,34,21,69,108,85,121,66,34,42,60,232,85,136,26,253,126,244,172,6,245,207, -245,201,59,206,85,124,70,217,172,61,213,56,23,2,208,15,205,150,247,139,254,112,45,69,194,237,168,193,251,25,246,168,247, -156,247,5,246,91,239,159,124,94,139,52,95,21,249,252,17,58,24,48,197,216,93,28,254,175,201,234,112,95,161,235,119,33,22, -161,174,183,229,216,86,201,122,27,43,68,140,135,233,19,179,249,12,227,126,228,59,201,34,53,17,61,241,128,159,154,141,196, -233,10,180,110,16,30,1,109,41,214,71,139,193,22,85,106,145,200,120,44,136,115,36,168,13,159,14,83,226,116,13,69,204,196, -131,94,120,46,226,35,226,29,244,106,166,180,174,209,52,115,241,241,159,82,68,155,94,130,251,198,81,107,80,203,89,171,149, -92,195,165,153,179,86,96,133,242,183,61,77,57,235,18,232,65,214,202,218,160,95,202,35,5,246,55,38,115,214,42,165,255,110, -50,241,96,53,70,174,1,209,218,133,121,43,122,181,106,81,204,65,55,118,221,160,214,232,243,8,253,56,226,164,209,175,81, -177,116,171,86,67,7,98,98,39,214,74,190,84,244,213,28,107,158,104,153,19,107,199,154,43,175,185,209,27,128,87,19,229,208, -206,139,181,179,147,165,26,99,20,241,230,98,43,145,35,61,15,88,13,178,14,171,81,140,225,142,211,81,234,186,61,36,198,177, -155,70,38,67,106,44,22,137,220,37,199,219,228,104,122,18,159,149,86,140,132,57,104,106,30,233,213,11,175,153,243,101,125, -139,39,103,158,139,110,90,54,25,210,90,140,185,90,72,75,156,249,107,15,143,57,247,93,115,218,77,113,148,232,166,158,73, -212,188,170,155,188,147,124,255,240,99,87,170,22,123,168,87,220,131,121,236,116,136,61,171,90,124,214,184,1,233,199,196, -222,19,18,103,44,63,243,159,83,126,255,170,236,191,80,233,255,84,233,55,196,174,205,232,29,81,111,53,249,152,180,27,55, -132,30,101,191,103,55,116,126,149,189,206,232,247,140,94,103,215,119,190,95,19,70,47,198,129,159,41,175,206,150,159,119, -34,90,98,171,143,154,245,196,149,50,90,25,5,216,82,134,104,213,189,109,56,37,174,12,80,59,246,244,241,62,70,9,51,23,91, -139,207,56,216,113,174,172,69,126,2,207,156,181,65,236,65,173,44,76,93,175,133,88,139,62,151,97,119,106,235,250,141,120, -254,82,158,165,13,240,225,235,206,135,247,206,85,251,172,70,157,90,117,247,156,158,26,232,94,62,23,24,12,126,103,168,198, -135,155,72,232,32,171,64,127,2,214,197,150,33,44,149,214,75,161,16,243,193,182,133,89,116,97,243,255,78,114,61,193,48,31, -250,154,42,166,87,179,94,198,111,151,139,172,26,170,102,11,148,142,177,101,113,165,195,159,93,32,244,11,172,151,85,233, -106,106,212,55,32,26,59,112,195,14,176,151,194,44,80,178,35,150,251,230,208,194,112,160,242,162,215,159,82,254,124,222, -47,16,253,117,172,229,92,250,139,254,78,223,92,250,85,101,176,162,184,187,158,162,246,32,180,190,126,186,173,130,251,6, -17,27,139,238,184,103,234,189,17,93,104,30,148,180,58,49,70,1,143,211,55,155,22,122,132,21,177,226,195,213,178,57,34,189, -203,231,165,82,140,70,75,164,155,38,38,49,218,17,140,118,128,91,186,105,188,172,141,189,240,230,169,109,122,37,201,247, -68,164,52,130,66,110,49,235,100,218,27,66,204,84,83,241,36,225,182,97,212,192,235,95,57,41,107,230,250,170,73,18,247,243, -185,104,233,205,106,255,187,91,204,163,135,62,13,233,133,198,239,125,124,247,127,74,165,117,186,11,91,228,41,118,195,172, -239,176,187,24,169,91,12,209,137,14,185,135,110,141,85,137,207,177,69,251,135,59,100,172,108,137,69,113,103,183,166,206, -179,59,213,125,41,130,29,221,15,111,15,126,62,209,33,63,27,226,76,220,133,157,86,75,36,195,180,148,249,137,203,136,238, -172,172,163,3,55,6,168,69,91,142,246,108,221,85,69,199,60,79,172,218,9,89,73,24,51,13,99,198,186,222,98,36,239,86,30,181, -134,26,85,92,206,154,58,255,27,88,168,137,199,44,67,116,106,244,88,135,252,238,160,29,37,18,7,112,255,200,97,47,206,227, -157,46,110,47,133,90,58,104,242,154,18,57,209,158,3,21,20,169,118,98,67,136,171,8,27,134,119,139,214,110,54,34,50,230, -163,46,199,234,33,126,67,146,109,137,136,27,17,19,223,79,48,241,51,7,111,144,55,212,103,202,250,206,191,75,228,255,52,42, -73,55,32,199,242,8,228,77,1,121,103,44,126,47,192,207,175,147,1,57,166,167,32,239,83,249,229,119,95,158,111,170,122,252, -74,242,239,15,206,42,223,14,85,95,157,146,81,245,222,162,140,171,250,226,170,78,63,201,207,58,113,225,209,39,62,191,68, -85,217,5,101,109,43,239,131,69,53,194,110,168,54,151,202,71,133,125,158,242,227,159,65,25,201,123,167,108,67,84,148,105, -130,165,7,189,233,86,246,102,85,46,78,197,241,96,24,222,46,194,133,141,45,35,109,89,140,216,0,153,3,105,39,237,174,32, -109,69,63,121,86,244,119,111,39,107,117,214,113,236,81,55,157,117,98,118,46,151,205,81,24,22,215,118,220,222,33,219,217, -227,142,81,237,154,116,126,116,202,105,75,193,113,146,187,50,54,177,13,164,109,24,34,125,195,208,6,242,224,129,253,112, -35,85,111,44,140,218,235,19,137,205,91,221,156,157,220,191,96,111,242,96,146,216,16,105,112,210,185,143,54,132,34,67,120, -120,135,134,118,14,13,161,130,128,82,184,174,13,237,164,250,161,164,147,202,101,211,169,184,107,79,184,241,4,30,219,220, -116,38,223,79,177,161,209,236,254,120,46,155,73,199,247,226,53,241,233,239,234,88,216,79,139,207,239,49,99,71,250,169, -229,188,165,250,169,125,40,149,204,28,76,239,139,39,29,39,235,38,121,225,248,90,103,52,147,205,167,157,61,171,51,201,60, -218,54,251,124,62,155,108,119,44,155,226,47,250,107,167,13,104,79,78,85,210,54,67,254,38,123,255,46,229,96,195,165,121,6, -151,173,233,61,78,210,45,228,208,149,198,25,178,19,99,185,236,33,81,148,207,70,60,157,141,175,42,236,222,109,231,236,212, -6,103,188,224,22,123,89,59,149,189,225,138,181,19,163,246,56,47,60,205,92,238,93,55,101,190,162,224,150,217,235,165,61, -147,116,246,196,87,143,37,115,91,237,3,5,219,25,181,167,42,18,57,101,245,87,151,153,55,32,238,246,216,57,62,211,211,141, -185,92,97,220,181,83,101,197,106,202,61,224,32,103,50,82,102,189,98,215,94,76,244,116,207,210,156,151,123,162,237,152, -164,233,109,151,54,57,80,253,212,48,67,78,58,147,226,89,229,21,97,164,237,100,106,122,87,197,232,203,119,54,73,179,99, -187,241,245,174,59,190,109,203,80,105,237,245,83,184,148,139,156,169,214,168,116,185,167,234,81,1,171,66,246,221,205,162, -33,86,153,117,40,157,119,167,154,33,44,155,146,227,29,107,29,55,119,184,159,54,205,100,30,248,235,241,120,87,125,51,120, -172,192,15,85,78,175,110,186,97,171,237,242,176,47,25,176,254,70,11,185,28,182,151,248,234,100,38,35,118,146,214,243,231, -247,83,207,223,114,64,96,193,135,143,74,89,104,116,207,236,189,118,194,30,45,188,203,181,243,124,174,89,4,111,238,96,154, -199,110,236,252,126,249,169,185,125,183,199,186,2,95,151,197,165,55,115,110,63,13,156,47,123,224,188,203,22,19,208,49, -115,105,25,140,235,146,163,104,32,102,125,254,204,94,136,157,253,233,209,248,165,66,172,202,102,51,118,18,227,50,111,102, -231,76,118,116,95,62,190,197,134,158,75,58,238,16,146,253,228,135,16,83,176,140,216,118,210,182,99,143,223,142,61,126,59, -246,120,19,15,190,215,35,177,147,2,219,203,246,249,237,59,137,237,36,109,231,70,0,57,188,10,108,160,234,225,25,86,165,54, -226,80,32,57,58,106,231,243,29,125,125,125,84,33,245,117,153,228,158,60,121,147,169,84,14,41,50,147,227,227,182,147,34, -239,174,100,222,222,150,203,144,185,75,140,22,121,70,17,70,100,142,138,88,33,131,239,200,54,249,177,225,143,39,115,118, -34,75,94,117,34,80,160,116,52,80,93,73,79,100,75,167,6,89,163,24,83,215,46,45,198,162,69,78,21,31,144,162,165,116,128,20, -45,50,37,124,42,213,136,22,135,142,204,148,61,154,77,217,84,147,178,119,39,11,25,119,218,228,241,220,140,237,218,20,72, -149,154,82,155,154,241,84,174,158,102,150,213,144,63,149,85,77,38,102,147,193,167,239,48,249,132,192,2,133,166,226,152, -140,221,105,59,147,130,200,20,242,99,164,239,65,102,13,30,197,133,134,23,168,46,133,97,93,203,111,11,42,93,137,244,122, -188,202,206,173,227,53,228,133,67,89,156,146,137,244,160,125,88,56,150,159,24,84,1,195,102,156,146,98,24,120,238,22,59, -63,158,117,242,24,100,140,7,175,38,129,131,44,163,238,34,188,26,236,134,228,131,220,158,204,20,108,178,198,146,249,85, -136,72,213,70,27,33,0,203,229,184,53,80,197,152,104,209,80,218,177,17,41,50,145,167,160,82,18,217,109,136,132,240,24,118, -226,45,252,144,202,187,171,247,167,168,122,122,90,58,5,184,81,181,151,165,201,155,118,82,246,196,21,187,169,34,93,214,67, -95,218,81,77,170,72,231,215,78,140,37,11,121,151,183,38,157,23,227,64,102,58,143,126,186,60,151,75,89,179,47,173,246,111, -242,236,205,166,17,7,25,217,79,15,95,106,228,113,146,251,49,165,142,125,104,117,114,116,204,78,201,233,220,140,85,74,33, -110,44,133,97,5,146,165,96,66,2,175,16,146,47,5,143,195,71,67,119,10,104,36,30,171,14,187,24,143,26,104,235,178,153,76, -246,144,157,218,98,167,210,57,212,36,173,83,169,68,86,58,80,195,76,86,53,126,89,44,187,178,134,232,89,116,85,31,199,219, -61,120,44,20,207,69,228,27,47,206,48,215,220,53,73,55,73,161,162,38,43,242,136,72,245,230,16,233,88,193,84,153,83,97,176, -94,77,91,205,187,12,178,148,9,43,150,11,233,185,2,222,157,71,188,86,225,161,218,147,72,239,183,121,115,42,96,90,147,149, -81,71,141,121,30,152,121,151,135,139,236,72,169,247,220,113,42,22,195,121,30,139,201,84,177,22,75,164,69,96,200,251,28, -69,74,150,205,57,140,67,206,61,76,70,126,60,147,118,33,220,100,14,115,14,225,22,242,20,144,82,132,116,101,73,151,125,8, -230,69,0,169,163,194,204,23,118,237,71,13,126,200,188,216,2,41,228,142,149,111,57,213,60,249,238,93,167,100,44,219,102, -194,48,150,47,194,74,164,183,150,191,204,227,142,165,17,39,252,217,209,71,126,23,93,197,48,109,194,242,157,82,85,156,186, -89,185,31,83,133,91,182,28,235,248,57,144,223,157,205,237,183,83,151,151,69,160,23,59,139,104,129,89,112,68,36,87,28,76, -102,58,138,43,193,56,40,150,137,113,40,135,232,39,109,162,143,142,104,223,103,228,13,211,81,254,156,92,54,64,71,120,234, -30,205,56,199,190,203,190,195,158,198,231,109,227,159,216,188,117,222,240,64,47,255,71,159,215,224,112,98,249,96,186,117, -100,112,120,240,154,97,109,188,121,109,219,57,170,243,143,220,201,30,98,31,96,207,176,111,178,71,216,25,246,65,230,13, -107,127,209,250,39,38,14,107,199,175,59,35,10,247,14,12,14,177,72,37,89,236,5,228,210,106,115,228,163,236,239,217,9,246, -3,225,253,138,238,249,36,107,30,28,184,100,159,174,221,194,22,49,166,235,31,98,108,249,9,221,124,144,177,207,78,232,236, -80,253,9,221,251,19,86,159,214,14,246,27,204,240,106,225,126,195,28,94,48,180,96,80,55,62,203,172,129,75,12,163,197,208, -12,189,85,167,124,211,0,205,243,79,85,255,143,236,139,236,126,118,43,94,210,59,135,85,69,209,230,94,173,162,191,141,126, -141,94,111,92,126,139,214,219,163,237,104,214,42,14,61,62,113,61,107,180,60,204,109,239,127,154,53,84,178,250,208,138,95, -177,250,106,131,241,42,195,56,52,13,226,218,220,19,95,190,230,71,125,90,203,243,6,105,187,250,207,117,124,76,179,250,233, -32,187,131,121,155,81,89,109,143,182,191,249,229,182,143,172,68,15,40,95,211,218,98,144,208,58,91,233,78,62,202,189,186, -245,37,22,111,211,67,167,25,27,234,213,190,77,61,134,165,93,218,108,90,31,56,172,87,125,131,49,86,95,117,78,15,159,69, -159,245,202,207,49,182,224,156,238,223,87,255,65,189,226,83,172,126,167,238,187,166,101,167,30,188,135,181,12,233,218, -179,172,62,206,44,235,240,3,90,104,135,97,173,68,215,125,70,176,85,15,228,155,52,103,163,17,48,53,211,103,6,77,235,125, -172,165,246,162,86,51,64,111,241,169,163,183,197,243,29,241,252,185,166,189,231,51,24,149,54,157,238,195,80,15,78,180, -247,238,213,38,154,7,232,25,158,253,71,157,198,141,229,123,7,90,119,234,230,129,89,3,13,134,73,199,117,246,105,62,117, -154,151,61,207,26,131,154,79,219,233,161,83,172,69,11,32,221,100,68,123,163,203,163,70,180,35,234,213,42,184,1,3,88,84, -170,52,19,74,189,33,138,248,78,177,104,133,44,227,137,174,86,37,68,210,31,101,209,197,209,206,232,122,84,37,51,124,154, -95,214,224,41,86,213,73,26,211,232,100,140,225,255,209,163,158,71,106,53,246,92,45,251,118,236,190,58,50,24,211,144,41, -126,200,115,236,168,231,201,58,131,253,71,29,85,153,94,175,198,34,226,71,228,49,109,218,143,72,155,240,127,164,158,157, -136,189,212,160,69,222,104,96,13,127,152,197,34,63,108,98,145,163,237,190,170,147,179,141,170,183,59,89,213,201,185,172, -234,44,120,18,60,210,197,170,206,116,139,239,148,168,236,187,6,46,139,127,187,197,191,31,40,254,253,86,241,123,11,254,55, -92,252,59,146,226,223,113,241,239,20,138,127,203,101,82,233,239,185,116,75,254,254,141,127,15,195,98,242,119,191,143,240, -239,79,98,210,135,255,254,144,89,165,223,41,106,49,249,94,254,247,95,186,242,231,191,195,243,196,72,252,238,137,255,126, -144,84,89,241,123,71,75,182,149,255,173,217,255,3,11,139,181,139,164,38,0,0}; - -#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ - METHOD (constructor, "", "()V") \ - METHOD (toString, "toString", "()Ljava/lang/String;") \ - -DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer") -#undef JNI_CLASS_MEMBERS - -//============================================================================== -#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ - STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)Lcom/roli/juce/JuceHTTPStream;") \ - METHOD (connect, "connect", "()Z") \ - METHOD (release, "release", "()V") \ - METHOD (read, "read", "([BI)I") \ - METHOD (getPosition, "getPosition", "()J") \ - METHOD (getTotalLength, "getTotalLength", "()J") \ - METHOD (isExhausted, "isExhausted", "()Z") \ - METHOD (setPosition, "setPosition", "(J)Z") \ - -DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/roli/juce/JuceHTTPStream", 16, javaJuceHttpStream, sizeof(javaJuceHttpStream)) -#undef JNI_CLASS_MEMBERS - -//============================================================================== -#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ - METHOD (close, "close", "()V") \ - METHOD (read, "read", "([BII)I") \ - -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*/) -{ - // TODO -} - - -JUCE_API bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /*targetEmailAddress*/, - const String& /*emailSubject*/, - const String& /*bodyText*/, - const StringArray& /*filesToAttach*/) -{ - // TODO - return false; -} - -//============================================================================== -bool URL::isLocalFile() const -{ - if (getScheme() == "file") - return true; - - if (getScheme() == "content") - { - auto file = AndroidContentUriResolver::getLocalFileFromContentUri (*this); - return (file != File()); - } - - return false; -} - -File URL::getLocalFile() const -{ - if (getScheme() == "content") - { - auto path = AndroidContentUriResolver::getLocalFileFromContentUri (*this); - - // This URL does not refer to a local file - // Call URL::isLocalFile to first check if the URL - // refers to a local file. - jassert (path != File()); - - return path; - } - - return fileFromFileSchemeURL (*this); -} - -String URL::getFileName() const -{ - if (getScheme() == "content") - return AndroidContentUriResolver::getFileNameFromContentUri (*this); - - return toString (false).fromLastOccurrenceOf ("/", false, true); -} - -//============================================================================== -class WebInputStream::Pimpl -{ -public: - enum { contentStreamCacheSize = 1024 }; - - Pimpl (WebInputStream&, const URL& urlToCopy, bool shouldBePost) - : url (urlToCopy), - isContentURL (urlToCopy.getScheme() == "content"), - isPost (shouldBePost), - httpRequest (isPost ? "POST" : "GET") - {} - - ~Pimpl() - { - cancel(); - } - - void cancel() - { - if (isContentURL) - { - stream.callVoidMethod (AndroidInputStream.close); - return; - } - - const ScopedLock lock (createStreamLock); - - if (stream != nullptr) - { - stream.callVoidMethod (HTTPStream.release); - stream.clear(); - } - - hasBeenCancelled = true; - } - - bool connect (WebInputStream::Listener* /*listener*/) - { - auto* env = getEnv(); - - if (isContentURL) - { - auto inputStream = AndroidContentUriResolver::getStreamForContentUri (url, true); - - if (inputStream != nullptr) - { - stream = GlobalRef (inputStream); - statusCode = 200; - - return true; - } - } - else - { - String address = url.toString (! isPost); - - if (! address.contains ("://")) - address = "http://" + address; - - MemoryBlock postData; - if (isPost) - WebInputStream::createHeadersAndPostData (url, headers, postData); - - jbyteArray postDataArray = nullptr; - - if (postData.getSize() > 0) - { - postDataArray = env->NewByteArray (static_cast (postData.getSize())); - env->SetByteArrayRegion (postDataArray, 0, static_cast (postData.getSize()), (const jbyte*) postData.getData()); - } - - LocalRef responseHeaderBuffer (env->NewObject (StringBuffer, StringBuffer.constructor)); - - // Annoyingly, the android HTTP functions will choke on this call if you try to do it on the message - // thread. You'll need to move your networking code to a background thread to keep it happy.. - jassert (Thread::getCurrentThread() != nullptr); - - jintArray statusCodeArray = env->NewIntArray (1); - jassert (statusCodeArray != nullptr); - - { - const ScopedLock lock (createStreamLock); - - if (! hasBeenCancelled) - stream = GlobalRef (LocalRef (env->CallStaticObjectMethod (HTTPStream, - HTTPStream.createHTTPStream, - javaString (address).get(), - (jboolean) isPost, - postDataArray, - javaString (headers).get(), - (jint) timeOutMs, - statusCodeArray, - responseHeaderBuffer.get(), - (jint) numRedirectsToFollow, - javaString (httpRequest).get()))); - } - - if (stream != nullptr && ! stream.callBooleanMethod (HTTPStream.connect)) - stream.clear(); - - jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, nullptr); - statusCode = statusCodeElements[0]; - env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0); - env->DeleteLocalRef (statusCodeArray); - - if (postDataArray != nullptr) - env->DeleteLocalRef (postDataArray); - - if (stream != nullptr) - { - StringArray headerLines; - - { - LocalRef headersString ((jstring) env->CallObjectMethod (responseHeaderBuffer.get(), - StringBuffer.toString)); - headerLines.addLines (juceString (env, headersString)); - } - - for (int i = 0; i < headerLines.size(); ++i) - { - const String& header = headerLines[i]; - const String key (header.upToFirstOccurrenceOf (": ", false, false)); - const String value (header.fromFirstOccurrenceOf (": ", false, false)); - const String previousValue (responseHeaders[key]); - - responseHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); - } - - return true; - } - } - - return false; - } - - //============================================================================== - // WebInputStream methods - void withExtraHeaders (const String& extraHeaders) - { - if (! headers.endsWithChar ('\n') && headers.isNotEmpty()) - headers << "\r\n"; - - headers << extraHeaders; - - if (! headers.endsWithChar ('\n') && headers.isNotEmpty()) - headers << "\r\n"; - } - - void withCustomRequestCommand (const String& customRequestCommand) { httpRequest = customRequestCommand; } - void withConnectionTimeout (int timeoutInMs) { timeOutMs = timeoutInMs; } - void withNumRedirectsToFollow (int maxRedirectsToFollow) { numRedirectsToFollow = maxRedirectsToFollow; } - StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (headers); } - StringPairArray getResponseHeaders() const { return responseHeaders; } - int getStatusCode() const { return statusCode; } - - //============================================================================== - bool isError() const { return stream == nullptr; } - bool isExhausted() { return (isContentURL ? eofStreamReached : stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted)); } - int64 getTotalLength() { return (isContentURL ? -1 : (stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0)); } - int64 getPosition() { return (isContentURL ? readPosition : (stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0)); } - - //============================================================================== - bool setPosition (int64 wantedPos) - { - if (isContentURL) - { - if (wantedPos < readPosition) - return false; - - auto bytesToSkip = wantedPos - readPosition; - - if (bytesToSkip == 0) - return true; - - HeapBlock buffer (bytesToSkip); - - return (read (buffer.getData(), (int) bytesToSkip) > 0); - } - - return stream != nullptr && stream.callBooleanMethod (HTTPStream.setPosition, (jlong) wantedPos); - } - - int read (void* buffer, int bytesToRead) - { - jassert (buffer != nullptr && bytesToRead >= 0); - - const ScopedLock lock (createStreamLock); - - if (stream == nullptr) - return 0; - - JNIEnv* env = getEnv(); - - jbyteArray javaArray = env->NewByteArray (bytesToRead); - - auto numBytes = (isContentURL ? stream.callIntMethod (AndroidInputStream.read, javaArray, 0, (jint) bytesToRead) - : stream.callIntMethod (HTTPStream.read, javaArray, (jint) bytesToRead)); - - if (numBytes > 0) - env->GetByteArrayRegion (javaArray, 0, numBytes, static_cast (buffer)); - - env->DeleteLocalRef (javaArray); - - readPosition += jmax (0, numBytes); - - if (numBytes == -1) - eofStreamReached = true; - - return numBytes; - } - - //============================================================================== - int statusCode = 0; - -private: - const URL url; - bool isContentURL, isPost, eofStreamReached = false; - int numRedirectsToFollow = 5, timeOutMs = 0; - String httpRequest, headers; - StringPairArray responseHeaders; - CriticalSection createStreamLock; - bool hasBeenCancelled = false; - int readPosition = 0; - - GlobalRef stream; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) -}; - -URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost) -{ - return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost); -} - -//============================================================================== -#if __ANDROID_API__ < 24 // Android support for getifadds was added in Android 7.0 (API 24) so the posix implementation does not apply - -static IPAddress makeAddress (const sockaddr_in *addr_in) -{ - if (addr_in->sin_addr.s_addr == INADDR_NONE) - return {}; - - return IPAddress (ntohl (addr_in->sin_addr.s_addr)); -} - -struct InterfaceInfo -{ - IPAddress interfaceAddress, broadcastAddress; -}; - -static Array findIPAddresses (int dummySocket) -{ - ifconf cfg; - HeapBlock buffer; - int bufferSize = 1024; - - do - { - bufferSize *= 2; - buffer.calloc (bufferSize); - - cfg.ifc_len = bufferSize; - cfg.ifc_buf = buffer; - - if (ioctl (dummySocket, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL) - return {}; - - } while (bufferSize < cfg.ifc_len + 2 * (int) (IFNAMSIZ + sizeof (struct sockaddr_in6))); - - Array result; - - for (size_t i = 0; i < (size_t) cfg.ifc_len / (size_t) sizeof (struct ifreq); ++i) - { - auto& item = cfg.ifc_req[i]; - - if (item.ifr_addr.sa_family == AF_INET) - { - InterfaceInfo info; - info.interfaceAddress = makeAddress (reinterpret_cast (&item.ifr_addr)); - - if (! info.interfaceAddress.isNull()) - { - if (ioctl (dummySocket, SIOCGIFBRDADDR, &item) == 0) - info.broadcastAddress = makeAddress (reinterpret_cast (&item.ifr_broadaddr)); - - result.add (info); - } - } - else if (item.ifr_addr.sa_family == AF_INET6) - { - // TODO: IPv6 - } - } - - return result; -} - -static Array findIPAddresses() -{ - auto dummySocket = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control - - if (dummySocket < 0) - return {}; - - auto result = findIPAddresses (dummySocket); - ::close (dummySocket); - return result; -} - -void IPAddress::findAllAddresses (Array& result, bool /*includeIPv6*/) -{ - for (auto& a : findIPAddresses()) - result.add (a.interfaceAddress); -} - -IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress& address) -{ - for (auto& a : findIPAddresses()) - if (a.interfaceAddress == address) - return a.broadcastAddress; - - return {}; -} - -#endif - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp deleted file mode 100644 index 2d7faa38..00000000 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp +++ /dev/null @@ -1,233 +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. - - ============================================================================== -*/ - -#if JUCE_BELA -extern "C" int cobalt_thread_mode(); -#endif - -namespace juce -{ - -void Logger::outputDebugString (const String& text) -{ - std::cerr << text << std::endl; -} - -//============================================================================== -SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() -{ - return Linux; -} - -String SystemStats::getOperatingSystemName() -{ - return "Linux"; -} - -bool SystemStats::isOperatingSystem64Bit() -{ - #if JUCE_64BIT - return true; - #else - //xxx not sure how to find this out?.. - return false; - #endif -} - -//============================================================================== -static inline String getCpuInfo (const char* key) -{ - return readPosixConfigFileValue ("/proc/cpuinfo", key); -} - -String SystemStats::getDeviceDescription() -{ - return getCpuInfo ("Hardware"); -} - -String SystemStats::getDeviceManufacturer() -{ - return {}; -} - -String SystemStats::getCpuVendor() -{ - auto v = getCpuInfo ("vendor_id"); - - if (v.isEmpty()) - v = getCpuInfo ("model name"); - - return v; -} - -String SystemStats::getCpuModel() -{ - return getCpuInfo ("model name"); -} - -int SystemStats::getCpuSpeedInMegahertz() -{ - return roundToInt (getCpuInfo ("cpu MHz").getFloatValue()); -} - -int SystemStats::getMemorySizeInMegabytes() -{ - struct sysinfo sysi; - - if (sysinfo (&sysi) == 0) - return (int) (sysi.totalram * sysi.mem_unit / (1024 * 1024)); - - return 0; -} - -int SystemStats::getPageSize() -{ - return (int) sysconf (_SC_PAGESIZE); -} - -//============================================================================== -String SystemStats::getLogonName() -{ - if (auto user = getenv ("USER")) - return String::fromUTF8 (user); - - if (auto pw = getpwuid (getuid())) - return String::fromUTF8 (pw->pw_name); - - return {}; -} - -String SystemStats::getFullUserName() -{ - return getLogonName(); -} - -String SystemStats::getComputerName() -{ - char name[256] = {}; - - if (gethostname (name, sizeof (name) - 1) == 0) - return name; - - return {}; -} - -static String getLocaleValue (nl_item 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(); } - -//============================================================================== -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"); - has3DNow = flags.contains ("3dnow"); - hasSSSE3 = flags.contains ("ssse3"); - hasSSE41 = flags.contains ("sse4_1"); - hasSSE42 = flags.contains ("sse4_2"); - hasAVX = flags.contains ("avx"); - hasAVX2 = flags.contains ("avx2"); - hasAVX512F = flags.contains ("avx512f"); - hasAVX512BW = flags.contains ("avx512bw"); - hasAVX512CD = flags.contains ("avx512cd"); - hasAVX512DQ = flags.contains ("avx512dq"); - hasAVX512ER = flags.contains ("avx512er"); - hasAVX512IFMA = flags.contains ("avx512ifma"); - hasAVX512PF = flags.contains ("avx512pf"); - hasAVX512VBMI = flags.contains ("avx512vbmi"); - hasAVX512VL = flags.contains ("avx512vl"); - hasAVX512VPOPCNTDQ = flags.contains ("avx512_vpopcntdq"); - - numLogicalCPUs = getCpuInfo ("processor").getIntValue() + 1; - - // Assume CPUs in all sockets have the same number of cores - numPhysicalCPUs = getCpuInfo ("cpu cores").getIntValue() * (getCpuInfo ("physical id").getIntValue() + 1); - - if (numPhysicalCPUs <= 0) - numPhysicalCPUs = numLogicalCPUs; -} - -//============================================================================== -uint32 juce_millisecondsSinceStartup() noexcept -{ - 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); -} - -int64 Time::getHighResolutionTicksPerSecond() noexcept -{ - return 1000000; // (microseconds) -} - -double Time::getMillisecondCounterHiRes() noexcept -{ - return getHighResolutionTicks() * 0.001; -} - -bool Time::setSystemTimeToThisTime() const -{ - timeval t; - t.tv_sec = millisSinceEpoch / 1000; - t.tv_usec = (millisSinceEpoch - t.tv_sec * 1000) * 1000; - - return settimeofday (&t, nullptr) == 0; -} - -JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept -{ - #if JUCE_BSD - return false; - #else - return readPosixConfigFileValue ("/proc/self/status", "TracerPid").getIntValue() > 0; - #endif -} - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm deleted file mode 100644 index 41e7f1ca..00000000 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm +++ /dev/null @@ -1,83 +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 -{ - -/* - Note that a lot of methods that you'd expect to find in this file actually - live in juce_posix_SharedCode.h! -*/ - -#if JUCE_IOS -bool isIOSAppActive = true; -#endif - -//============================================================================== -JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() -{ - if (SystemStats::isRunningInAppExtensionSandbox()) - return true; - - #if JUCE_MAC - return [NSApp isActive]; - #else - return isIOSAppActive; - #endif -} - -JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() -{ - #if JUCE_MAC - if (! SystemStats::isRunningInAppExtensionSandbox()) - [NSApp activateIgnoringOtherApps: YES]; - #endif -} - -JUCE_API void JUCE_CALLTYPE Process::hide() -{ - if (! SystemStats::isRunningInAppExtensionSandbox()) - { - #if JUCE_MAC - [NSApp hide: nil]; - #elif JUCE_IOS - [[UIApplication sharedApplication] performSelector: @selector(suspend)]; - #endif - } -} - -JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {} -JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {} - -JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) {} - -//============================================================================== -JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept -{ - struct kinfo_proc info; - int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; - size_t sz = sizeof (info); - sysctl (m, 4, &info, &sz, nullptr, 0); - return (info.kp_proc.p_flag & P_TRACED) != 0; -} - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h b/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h deleted file mode 100644 index 2593790b..00000000 --- a/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h +++ /dev/null @@ -1,430 +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. - - ============================================================================== -*/ - -/* This file contains a few helper functions that are used internally but which - need to be kept away from the public headers because they use obj-C symbols. -*/ -namespace juce -{ - -//============================================================================== -static inline String nsStringToJuce (NSString* s) -{ - return CharPointer_UTF8 ([s UTF8String]); -} - -static inline NSString* juceStringToNS (const String& s) -{ - return [NSString stringWithUTF8String: s.toUTF8()]; -} - -static inline NSString* nsStringLiteral (const char* const s) noexcept -{ - return [NSString stringWithUTF8String: s]; -} - -static inline NSString* nsEmptyString() noexcept -{ - return [NSString string]; -} - -static inline NSURL* createNSURLFromFile (const String& f) -{ - return [NSURL fileURLWithPath: juceStringToNS (f)]; -} - -static inline NSURL* createNSURLFromFile (const File& f) -{ - return createNSURLFromFile (f.getFullPathName()); -} - -static inline NSArray* createNSArrayFromStringArray (const StringArray& strings) -{ - auto array = [[NSMutableArray alloc] init]; - - for (auto string: strings) - [array addObject:juceStringToNS (string)]; - - return [array autorelease]; -} - -static NSArray* varArrayToNSArray (const var& varToParse); - -static NSDictionary* varObjectToNSDictionary (const var& varToParse) -{ - auto dictionary = [NSMutableDictionary dictionary]; - - if (varToParse.isObject()) - { - auto* dynamicObject = varToParse.getDynamicObject(); - - auto& properties = dynamicObject->getProperties(); - - for (int i = 0; i < properties.size(); ++i) - { - auto* keyString = juceStringToNS (properties.getName (i).toString()); - - const var& valueVar = properties.getValueAt (i); - - if (valueVar.isObject()) - { - auto* valueDictionary = varObjectToNSDictionary (valueVar); - - [dictionary setObject: valueDictionary forKey: keyString]; - } - else if (valueVar.isArray()) - { - auto* valueArray = varArrayToNSArray (valueVar); - - [dictionary setObject: valueArray forKey: keyString]; - } - else - { - auto* valueString = juceStringToNS (valueVar.toString()); - - [dictionary setObject: valueString forKey: keyString]; - } - } - } - - return dictionary; -} - -static NSArray* varArrayToNSArray (const var& varToParse) -{ - jassert (varToParse.isArray()); - - if (! varToParse.isArray()) - return nil; - - const auto* varArray = varToParse.getArray(); - - auto array = [NSMutableArray arrayWithCapacity: (NSUInteger) varArray->size()]; - - for (const auto& aVar : *varArray) - { - if (aVar.isObject()) - { - auto* valueDictionary = varObjectToNSDictionary (aVar); - - [array addObject: valueDictionary]; - } - else if (aVar.isArray()) - { - auto* valueArray = varArrayToNSArray (aVar); - - [array addObject: valueArray]; - } - else - { - auto* valueString = juceStringToNS (aVar.toString()); - - [array addObject: valueString]; - } - } - - return array; -} - -static var nsObjectToVar (NSObject* array); - -static var nsDictionaryToVar (NSDictionary* dictionary) -{ - DynamicObject::Ptr dynamicObject (new DynamicObject()); - - for (NSString* key in dictionary) - dynamicObject->setProperty (nsStringToJuce (key), nsObjectToVar (dictionary[key])); - - return var (dynamicObject.get()); -} - -static var nsArrayToVar (NSArray* array) -{ - Array resultArray; - - for (id value in array) - resultArray.add (nsObjectToVar (value)); - - return var (resultArray); -} - -static var nsObjectToVar (NSObject* obj) -{ - if ([obj isKindOfClass: [NSString class]]) return nsStringToJuce ((NSString*) obj); - else if ([obj isKindOfClass: [NSNumber class]]) return nsStringToJuce ([(NSNumber*) obj stringValue]); - else if ([obj isKindOfClass: [NSDictionary class]]) return nsDictionaryToVar ((NSDictionary*) obj); - else if ([obj isKindOfClass: [NSArray class]]) return nsArrayToVar ((NSArray*) obj); - else - { - // Unsupported yet, add here! - jassertfalse; - } - - return {}; -} - -#if JUCE_MAC -template -static NSRect makeNSRect (const RectangleType& r) noexcept -{ - return NSMakeRect (static_cast (r.getX()), - static_cast (r.getY()), - static_cast (r.getWidth()), - static_cast (r.getHeight())); -} -#endif -#if JUCE_MAC || JUCE_IOS - -// This is necessary as on iOS builds, some arguments may be passed on registers -// depending on the argument type. The re-cast objc_msgSendSuper to a function -// take the same arguments as the target method. -template -static inline ReturnValue ObjCMsgSendSuper (struct objc_super* s, SEL sel, Params... params) -{ - using SuperFn = ReturnValue (*)(struct objc_super*, SEL, Params...); - SuperFn fn = reinterpret_cast (objc_msgSendSuper); - return fn (s, sel, params...); -} - -// These hacks are a workaround for newer Xcode builds which by default prevent calls to these objc functions.. -typedef id (*MsgSendSuperFn) (struct objc_super*, SEL, ...); -static inline MsgSendSuperFn getMsgSendSuperFn() noexcept { return (MsgSendSuperFn) (void*) objc_msgSendSuper; } - -#if ! JUCE_IOS -typedef double (*MsgSendFPRetFn) (id, SEL op, ...); -static inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPRetFn) (void*) objc_msgSend_fpret; } -#endif -#endif - -//============================================================================== -struct NSObjectDeleter -{ - void operator()(NSObject* object) const - { - [object release]; - } -}; - -//============================================================================== -template -struct ObjCClass -{ - ObjCClass (const char* nameRoot) - : cls (objc_allocateClassPair ([SuperclassType class], getRandomisedName (nameRoot).toUTF8(), 0)) - { - } - - ~ObjCClass() - { - objc_disposeClassPair (cls); - } - - void registerClass() - { - objc_registerClassPair (cls); - } - - SuperclassType* createInstance() const - { - return class_createInstance (cls, 0); - } - - template - void addIvar (const char* name) - { - BOOL b = class_addIvar (cls, name, sizeof (Type), (uint8_t) rint (log2 (sizeof (Type))), @encode (Type)); - jassert (b); ignoreUnused (b); - } - - template - void addMethod (SEL selector, FunctionType callbackFn, const char* signature) - { - BOOL b = class_addMethod (cls, selector, (IMP) callbackFn, signature); - jassert (b); ignoreUnused (b); - } - - template - void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2) - { - addMethod (selector, callbackFn, (String (sig1) + sig2).toUTF8()); - } - - template - void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3) - { - addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3).toUTF8()); - } - - template - void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3, const char* sig4) - { - addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3 + sig4).toUTF8()); - } - - void addProtocol (Protocol* protocol) - { - BOOL b = class_addProtocol (cls, protocol); - jassert (b); ignoreUnused (b); - } - - #if JUCE_MAC || JUCE_IOS - static id sendSuperclassMessage (id self, SEL selector) - { - objc_super s = { self, [SuperclassType class] }; - return getMsgSendSuperFn() (&s, selector); - } - #endif - - template - static Type getIvar (id self, const char* name) - { - void* v = nullptr; - object_getInstanceVariable (self, name, &v); - return static_cast (v); - } - - Class cls; - -private: - static String getRandomisedName (const char* root) - { - return root + String::toHexString (juce::Random::getSystemRandom().nextInt64()); - } - - JUCE_DECLARE_NON_COPYABLE (ObjCClass) -}; - -//============================================================================== -#ifndef DOXYGEN -template -struct ObjCLifetimeManagedClass : public ObjCClass -{ - ObjCLifetimeManagedClass() - : ObjCClass ("ObjCLifetimeManagedClass_") - { - addIvar ("cppObject"); - - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wundeclared-selector" - addMethod (@selector (initWithJuceObject:), initWithJuceObject, "@@:@"); - #pragma clang diagnostic pop - - addMethod (@selector (dealloc), dealloc, "v@:"); - - - registerClass(); - } - - static id initWithJuceObject (id _self, SEL, JuceClass* obj) - { - NSObject* self = _self; - - objc_super s = { self, [NSObject class] }; - self = ObjCMsgSendSuper (&s, @selector(init)); - - object_setInstanceVariable (self, "cppObject", obj); - return self; - } - - static void dealloc (id _self, SEL) - { - if (auto* obj = getIvar (_self, "cppObject")) - { - delete obj; - object_setInstanceVariable (_self, "cppObject", nullptr); - } - - objc_super s = { _self, [NSObject class] }; - ObjCMsgSendSuper (&s, @selector(dealloc)); - } - - - static ObjCLifetimeManagedClass objCLifetimeManagedClass; -}; - -template -ObjCLifetimeManagedClass ObjCLifetimeManagedClass::objCLifetimeManagedClass; -#endif - -// this will return an NSObject which takes ownership of the JUCE instance passed-in -// This is useful to tie the life-time of a juce instance to the life-time of an NSObject -template -NSObject* createNSObjectFromJuceClass (Class* obj) -{ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wobjc-method-access" - return [ObjCLifetimeManagedClass::objCLifetimeManagedClass.createInstance() initWithJuceObject:obj]; - #pragma clang diagnostic pop -} - -// Get the JUCE class instance that was tied to the life-time of an NSObject with the -// function above -template -Class* getJuceClassFromNSObject (NSObject* obj) -{ - return obj != nullptr ? ObjCLifetimeManagedClass:: template getIvar (obj, "cppObject") : nullptr; -} - -template -ReturnT (^CreateObjCBlock(Class* object, ReturnT (Class::*fn)(Params...))) (Params...) -{ - __block Class* _this = object; - __block ReturnT (Class::*_fn)(Params...) = fn; - - return [[^ReturnT (Params... params) { return (_this->*_fn) (params...); } copy] autorelease]; -} - -template -class ObjCBlock -{ -public: - ObjCBlock() { block = nullptr; } - template - ObjCBlock (C* _this, R (C::*fn)(P...)) : block (CreateObjCBlock (_this, fn)) {} - ObjCBlock (BlockType b) : block ([b copy]) {} - ObjCBlock& operator= (const BlockType& other) { if (block != nullptr) { [block release]; } block = [other copy]; return *this; } - bool operator== (const void* ptr) const { return ((const void*) block == ptr); } - bool operator!= (const void* ptr) const { return ((const void*) block != ptr); } - ~ObjCBlock() { if (block != nullptr) [block release]; } - - operator BlockType() { return block; } - -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_win32_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp deleted file mode 100644 index 343e6319..00000000 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp +++ /dev/null @@ -1,544 +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 -{ - -#if JUCE_MSVC - #pragma intrinsic (__cpuid) - #pragma intrinsic (__rdtsc) -#endif - -void Logger::outputDebugString (const String& text) -{ - OutputDebugString ((text + "\n").toWideCharPointer()); -} - -//============================================================================== -#ifdef JUCE_DLL_BUILD - JUCE_API void* juceDLL_malloc (size_t sz) { return std::malloc (sz); } - JUCE_API void juceDLL_free (void* block) { std::free (block); } -#endif - -//============================================================================== - -#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]; - - asm ("mov %%ebx, %%esi \n\t" - "cpuid \n\t" - "xchg %%esi, %%ebx" - : "=a" (la), "=S" (lb), "=c" (lc), "=d" (ld) : "a" (type) - #if JUCE_64BIT - , "b" (lb), "c" (lc), "d" (ld) - #endif - ); - - result[0] = la; result[1] = lb; result[2] = lc; result[3] = ld; -} -#else -static void callCPUID (int result[4], int infoType) -{ - #if JUCE_PROJUCER_LIVE_BUILD - std::fill (result, result + 4, 0); - #else - __cpuid (result, infoType); - #endif -} -#endif - -String SystemStats::getCpuVendor() -{ - int info[4] = { 0 }; - callCPUID (info, 0); - - char v [12]; - memcpy (v, info + 1, 4); - memcpy (v + 4, info + 3, 4); - memcpy (v + 8, info + 2, 4); - - return String (v, 12); -} - -String SystemStats::getCpuModel() -{ - char name[65] = { 0 }; - int info[4] = { 0 }; - - callCPUID (info, 0x80000000); - - const int numExtIDs = info[0]; - - if ((unsigned) numExtIDs < 0x80000004) // if brand string is unsupported - return {}; - - callCPUID (info, 0x80000002); - memcpy (name, info, sizeof (info)); - - callCPUID (info, 0x80000003); - memcpy (name + 16, info, sizeof (info)); - - callCPUID (info, 0x80000004); - memcpy (name + 32, info, sizeof (info)); - - return String (name).trim(); -} - -static int findNumberOfPhysicalCores() noexcept -{ - #if JUCE_MINGW - // Not implemented in MinGW - jassertfalse; - - return 1; - #else - - int numPhysicalCores = 0; - DWORD bufferSize = 0; - GetLogicalProcessorInformation (nullptr, &bufferSize); - - if (auto numBuffers = (size_t) (bufferSize / sizeof (SYSTEM_LOGICAL_PROCESSOR_INFORMATION))) - { - HeapBlock buffer (numBuffers); - - if (GetLogicalProcessorInformation (buffer, &bufferSize)) - for (size_t i = 0; i < numBuffers; ++i) - if (buffer[i].Relationship == RelationProcessorCore) - ++numPhysicalCores; - } - - return numPhysicalCores; - #endif // JUCE_MINGW -} - -//============================================================================== -void CPUInformation::initialise() noexcept -{ - int info[4] = { 0 }; - callCPUID (info, 1); - - // NB: IsProcessorFeaturePresent doesn't work on XP - hasMMX = (info[3] & (1 << 23)) != 0; - hasSSE = (info[3] & (1 << 25)) != 0; - 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; - hasAVX512F = (info[1] & (1u << 16)) != 0; - hasAVX512DQ = (info[1] & (1u << 17)) != 0; - hasAVX512IFMA = (info[1] & (1u << 21)) != 0; - hasAVX512PF = (info[1] & (1u << 26)) != 0; - hasAVX512ER = (info[1] & (1u << 27)) != 0; - hasAVX512CD = (info[1] & (1u << 28)) != 0; - hasAVX512BW = (info[1] & (1u << 30)) != 0; - hasAVX512VL = (info[1] & (1u << 31)) != 0; - hasAVX512VBMI = (info[2] & (1u << 1)) != 0; - hasAVX512VPOPCNTDQ = (info[2] & (1u << 14)) != 0; - - SYSTEM_INFO systemInfo; - GetNativeSystemInfo (&systemInfo); - numLogicalCPUs = (int) systemInfo.dwNumberOfProcessors; - numPhysicalCPUs = findNumberOfPhysicalCores(); - - if (numPhysicalCPUs <= 0) - numPhysicalCPUs = numLogicalCPUs; -} - -#if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS -struct DebugFlagsInitialiser -{ - DebugFlagsInitialiser() - { - _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); - } -}; - -static DebugFlagsInitialiser debugFlagsInitialiser; -#endif - -//============================================================================== -#if JUCE_MINGW - static uint32 getWindowsVersion() - { - auto filename = _T("kernel32.dll"); - DWORD handle = 0; - - if (auto size = GetFileVersionInfoSize (filename, &handle)) - { - HeapBlock data (size); - - 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; - } - } - - 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) & 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 (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; -} - -String SystemStats::getOperatingSystemName() -{ - const char* name = "Unknown OS"; - - switch (getOperatingSystemType()) - { - case Windows10: name = "Windows 10"; break; - case Windows8_1: name = "Windows 8.1"; break; - case Windows8_0: name = "Windows 8.0"; break; - case Windows7: name = "Windows 7"; break; - case WinVista: name = "Windows Vista"; break; - case WinXP: name = "Windows XP"; break; - case Win2000: name = "Windows 2000"; break; - default: jassertfalse; break; // !! new type of OS? - } - - return name; -} - -String SystemStats::getDeviceDescription() -{ - #if WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP - return "Windows (Desktop)"; - #elif WINAPI_FAMILY == WINAPI_FAMILY_PC_APP - return "Windows (Store)"; - #elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP - return "Windows (Phone)"; - #elif WINAPI_FAMILY == WINAPI_FAMILY_SYSTEM - return "Windows (System)"; - #elif WINAPI_FAMILY == WINAPI_FAMILY_SERVER - return "Windows (Server)"; - #else - return "Windows"; - #endif -} - -String SystemStats::getDeviceManufacturer() -{ - return {}; -} - -bool SystemStats::isOperatingSystem64Bit() -{ - #if JUCE_64BIT - return true; - #else - typedef BOOL (WINAPI* LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); - - LPFN_ISWOW64PROCESS fnIsWow64Process - = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandleA ("kernel32"), "IsWow64Process"); - - BOOL isWow64 = FALSE; - - return fnIsWow64Process != nullptr - && fnIsWow64Process (GetCurrentProcess(), &isWow64) - && isWow64 != FALSE; - #endif -} - -//============================================================================== -int SystemStats::getMemorySizeInMegabytes() -{ - MEMORYSTATUSEX mem; - mem.dwLength = sizeof (mem); - GlobalMemoryStatusEx (&mem); - return (int) (mem.ullTotalPhys / (1024 * 1024)) + 1; -} - -//============================================================================== -String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue) -{ - auto len = GetEnvironmentVariableW (name.toWideCharPointer(), nullptr, 0); - - if (len == 0) - return String (defaultValue); - - HeapBlock buffer (len); - len = GetEnvironmentVariableW (name.toWideCharPointer(), buffer, len); - - return String (CharPointer_wchar_t (buffer), - CharPointer_wchar_t (buffer + len)); -} - -//============================================================================== -uint32 juce_millisecondsSinceStartup() noexcept -{ - return (uint32) timeGetTime(); -} - -//============================================================================== -class HiResCounterHandler -{ -public: - HiResCounterHandler() - : hiResTicksOffset (0) - { - // This macro allows you to override the default timer-period - // used on Windows. By default this is set to 1, because that has - // always been the value used in JUCE apps, and changing it could - // affect the behaviour of existing code, but you may wish to make - // it larger (or set it to 0 to use the system default) to make your - // app less demanding on the CPU. - // For more info, see win32 documentation about the timeBeginPeriod - // function. - #ifndef JUCE_WIN32_TIMER_PERIOD - #define JUCE_WIN32_TIMER_PERIOD 1 - #endif - - #if JUCE_WIN32_TIMER_PERIOD > 0 - auto res = timeBeginPeriod (JUCE_WIN32_TIMER_PERIOD); - ignoreUnused (res); - jassert (res == TIMERR_NOERROR); - #endif - - LARGE_INTEGER f; - QueryPerformanceFrequency (&f); - hiResTicksPerSecond = f.QuadPart; - hiResTicksScaleFactor = 1000.0 / hiResTicksPerSecond; - } - - inline int64 getHighResolutionTicks() noexcept - { - LARGE_INTEGER ticks; - QueryPerformanceCounter (&ticks); - return ticks.QuadPart + hiResTicksOffset; - } - - inline double getMillisecondCounterHiRes() noexcept - { - return getHighResolutionTicks() * hiResTicksScaleFactor; - } - - int64 hiResTicksPerSecond, hiResTicksOffset; - double hiResTicksScaleFactor; -}; - -static HiResCounterHandler hiResCounterHandler; - -int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterHandler.hiResTicksPerSecond; } -int64 Time::getHighResolutionTicks() noexcept { return hiResCounterHandler.getHighResolutionTicks(); } -double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterHandler.getMillisecondCounterHiRes(); } - -//============================================================================== -static int64 juce_getClockCycleCounter() noexcept -{ - #if JUCE_MSVC - // MS intrinsics version... - return (int64) __rdtsc(); - - #elif JUCE_GCC || JUCE_CLANG - // GNU inline asm version... - unsigned int hi = 0, lo = 0; - - __asm__ __volatile__ ( - "xor %%eax, %%eax \n\ - xor %%edx, %%edx \n\ - rdtsc \n\ - movl %%eax, %[lo] \n\ - movl %%edx, %[hi]" - : - : [hi] "m" (hi), - [lo] "m" (lo) - : "cc", "eax", "ebx", "ecx", "edx", "memory"); - - return (int64) ((((uint64) hi) << 32) | lo); - #else - #error "unknown compiler?" - #endif -} - -int SystemStats::getCpuSpeedInMegahertz() -{ - auto cycles = juce_getClockCycleCounter(); - auto millis = Time::getMillisecondCounter(); - int lastResult = 0; - - for (;;) - { - int n = 1000000; - while (--n > 0) {} - - auto millisElapsed = Time::getMillisecondCounter() - millis; - auto cyclesNow = juce_getClockCycleCounter(); - - if (millisElapsed > 80) - { - auto newResult = (int) (((cyclesNow - cycles) / millisElapsed) / 1000); - - if (millisElapsed > 500 || (lastResult == newResult && newResult > 100)) - return newResult; - - lastResult = newResult; - } - } -} - - -//============================================================================== -bool Time::setSystemTimeToThisTime() const -{ - SYSTEMTIME st; - - st.wDayOfWeek = 0; - st.wYear = (WORD) getYear(); - st.wMonth = (WORD) (getMonth() + 1); - st.wDay = (WORD) getDayOfMonth(); - st.wHour = (WORD) getHours(); - st.wMinute = (WORD) getMinutes(); - st.wSecond = (WORD) getSeconds(); - st.wMilliseconds = (WORD) (millisSinceEpoch % 1000); - - // do this twice because of daylight saving conversion problems - the - // first one sets it up, the second one kicks it in. - // NB: the local variable is here to avoid analysers warning about having - // two identical sub-expressions in the return statement - auto firstCallToSetTimezone = SetLocalTime (&st) != 0; - return firstCallToSetTimezone && SetLocalTime (&st) != 0; -} - -int SystemStats::getPageSize() -{ - SYSTEM_INFO systemInfo; - GetNativeSystemInfo (&systemInfo); - - return (int) systemInfo.dwPageSize; -} - -//============================================================================== -String SystemStats::getLogonName() -{ - TCHAR text [256] = { 0 }; - auto len = (DWORD) numElementsInArray (text) - 1; - GetUserName (text, &len); - return String (text, len); -} - -String SystemStats::getFullUserName() -{ - return getLogonName(); -} - -String SystemStats::getComputerName() -{ - TCHAR text[128] = { 0 }; - auto len = (DWORD) numElementsInArray (text) - 1; - GetComputerNameEx (ComputerNamePhysicalDnsHostname, text, &len); - return String (text, len); -} - -static String getLocaleValue (LCID locale, LCTYPE key, const char* defaultValue) -{ - TCHAR buffer [256] = { 0 }; - if (GetLocaleInfo (locale, key, buffer, 255) > 0) - return buffer; - - return defaultValue; -} - -String SystemStats::getUserLanguage() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, "en"); } -String SystemStats::getUserRegion() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, "US"); } - -String SystemStats::getDisplayLanguage() -{ - DynamicLibrary dll ("kernel32.dll"); - JUCE_LOAD_WINAPI_FUNCTION (dll, GetUserDefaultUILanguage, getUserDefaultUILanguage, LANGID, (void)) - - if (getUserDefaultUILanguage == nullptr) - return "en"; - - auto langID = MAKELCID (getUserDefaultUILanguage(), SORT_DEFAULT); - - auto mainLang = getLocaleValue (langID, LOCALE_SISO639LANGNAME, "en"); - auto region = getLocaleValue (langID, LOCALE_SISO3166CTRYNAME, nullptr); - - if (region.isNotEmpty()) - mainLang << '-' << region; - - return mainLang; -} - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp index 73f5b4f7..8ff7f48f 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -90,10 +90,10 @@ IPAddress::IPAddress (uint16 a1, uint16 a2, uint16 a3, uint16 a4, IPAddress::IPAddress (uint32 n) noexcept : isIPv6 (false) { - address[0] = (n >> 24); - address[1] = (n >> 16) & 255; - address[2] = (n >> 8) & 255; - address[3] = (n & 255); + address[0] = static_cast (n >> 24); + address[1] = static_cast ((n >> 16) & 255); + address[2] = static_cast ((n >> 8) & 255); + address[3] = static_cast ((n & 255)); zeroUnusedBytes (address); } @@ -166,7 +166,7 @@ IPAddress::IPAddress (const String& adr) } IPAddressByteUnion temp; - temp.combined = (uint16) CharacterFunctions::HexParser::parse (tokens[i].getCharPointer()); + temp.combined = CharacterFunctions::HexParser::parse (tokens[i].getCharPointer()); address[i * 2] = temp.split[0]; address[i * 2 + 1] = temp.split[1]; @@ -377,7 +377,7 @@ Array IPAddress::getAllAddresses (bool includeIPv6) //============================================================================== #if JUCE_UNIT_TESTS -struct IPAddressTests : public UnitTest +struct IPAddressTests final : public UnitTest { IPAddressTests() : UnitTest ("IPAddress", UnitTestCategories::networking) diff --git a/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.h b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.h index ec8cd1ba..903af445 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.cpp b/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.cpp index 0808beb1..cd62354c 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.h b/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.h index e745f8df..916eff41 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp b/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp index 751bc09f..e4a11456 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,8 @@ namespace juce { +#if ! JUCE_WASM + NamedPipe::NamedPipe() {} NamedPipe::~NamedPipe() @@ -41,6 +43,7 @@ bool NamedPipe::openExisting (const String& pipeName) bool NamedPipe::isOpen() const { + ScopedReadLock sl (lock); return pimpl != nullptr; } @@ -55,6 +58,7 @@ bool NamedPipe::createNewPipe (const String& pipeName, bool mustNotExist) String NamedPipe::getName() const { + ScopedReadLock sl (lock); return currentPipeName; } @@ -65,7 +69,7 @@ String NamedPipe::getName() const //============================================================================== #if JUCE_UNIT_TESTS -class NamedPipeTests : public UnitTest +class NamedPipeTests final : public UnitTest { public: //============================================================================== @@ -75,7 +79,7 @@ class NamedPipeTests : public UnitTest void runTest() override { - const String pipeName ("TestPipe"); + const auto pipeName = "TestPipe" + String ((intptr_t) Thread::getCurrentThreadId()); beginTest ("Pre test cleanup"); { @@ -196,7 +200,7 @@ class NamedPipeTests : public UnitTest private: //============================================================================== - struct NamedPipeThread : public Thread + struct NamedPipeThread : public Thread { NamedPipeThread (const String& tName, const String& pName, bool shouldCreatePipe, WaitableEvent& completed) @@ -208,11 +212,6 @@ class NamedPipeTests : public UnitTest pipe.openExisting (pipeName); } - ~NamedPipeThread() - { - stopThread (100); - } - NamedPipe pipe; const String& pipeName; WaitableEvent& workCompleted; @@ -221,7 +220,7 @@ class NamedPipeTests : public UnitTest }; //============================================================================== - struct SenderThread : public NamedPipeThread + struct SenderThread final : public NamedPipeThread { SenderThread (const String& pName, bool shouldCreatePipe, WaitableEvent& completed, int sData) @@ -229,6 +228,11 @@ class NamedPipeTests : public UnitTest sendData (sData) {} + ~SenderThread() override + { + stopThread (100); + } + void run() override { result = pipe.write (&sendData, sizeof (sendData), 2000); @@ -239,13 +243,18 @@ class NamedPipeTests : public UnitTest }; //============================================================================== - struct ReceiverThread : public NamedPipeThread + struct ReceiverThread final : public NamedPipeThread { ReceiverThread (const String& pName, bool shouldCreatePipe, WaitableEvent& completed) : NamedPipeThread ("NamePipeSender", pName, shouldCreatePipe, completed) {} + ~ReceiverThread() override + { + stopThread (100); + } + void run() override { result = pipe.read (&recvData, sizeof (recvData), 2000); @@ -258,6 +267,7 @@ class NamedPipeTests : public UnitTest static NamedPipeTests namedPipeTests; +#endif #endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.h b/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.h index b3e8671d..3585638d 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp b/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp index 41c7d964..26f9425d 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,10 +23,9 @@ namespace juce { -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable : 4127 4389 4018) -#endif +#if ! JUCE_WASM + +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4127 4389 4018) #ifndef AI_NUMERICSERV // (missing in older Mac SDKs) #define AI_NUMERICSERV 0x1000 @@ -59,11 +58,9 @@ namespace SocketHelpers if (! socketsStarted) { - socketsStarted = true; - WSADATA wsaData; const WORD wVersionRequested = MAKEWORD (1, 1); - WSAStartup (wVersionRequested, &wsaData); + socketsStarted = WSAStartup (wVersionRequested, &wsaData) == 0; } #endif } @@ -85,24 +82,52 @@ namespace SocketHelpers return setOption (handle, SOL_SOCKET, property, value); } - static bool resetSocketOptions (SocketHandle handle, bool isDatagram, bool allowBroadcast) noexcept + static std::optional getBufferSize (SocketHandle handle, int property) + { + int result; + auto outParamSize = (socklen_t) sizeof (result); + + if (getsockopt (handle, SOL_SOCKET, property, reinterpret_cast (&result), &outParamSize) != 0 + || outParamSize != (socklen_t) sizeof (result)) + { + return std::nullopt; + } + + return result; + } + + static bool resetSocketOptions (SocketHandle handle, bool isDatagram, bool allowBroadcast, const SocketOptions& options) noexcept { + auto getCurrentBufferSizeWithMinimum = [handle] (int property) + { + constexpr auto minBufferSize = 65536; + + if (auto currentBufferSize = getBufferSize (handle, property)) + return std::max (*currentBufferSize, minBufferSize); + + return minBufferSize; + }; + + const auto receiveBufferSize = options.getReceiveBufferSize().value_or (getCurrentBufferSizeWithMinimum (SO_RCVBUF)); + const auto sendBufferSize = options.getSendBufferSize() .value_or (getCurrentBufferSizeWithMinimum (SO_SNDBUF)); + return handle != invalidSocket - && setOption (handle, SO_RCVBUF, (int) 65536) - && setOption (handle, SO_SNDBUF, (int) 65536) + && setOption (handle, SO_RCVBUF, receiveBufferSize) + && setOption (handle, SO_SNDBUF, sendBufferSize) && (isDatagram ? ((! allowBroadcast) || setOption (handle, SO_BROADCAST, (int) 1)) : setOption (handle, IPPROTO_TCP, TCP_NODELAY, (int) 1)); } - static void closeSocket (std::atomic& handle, CriticalSection& readLock, - bool isListener, int portNumber, std::atomic& connected) noexcept + static void closeSocket (std::atomic& handle, + [[maybe_unused]] CriticalSection& readLock, + [[maybe_unused]] bool isListener, + [[maybe_unused]] int portNumber, + std::atomic& connected) noexcept { - const SocketHandle h = handle.load(); + const auto h = (SocketHandle) handle.load(); handle = -1; #if JUCE_WINDOWS - ignoreUnused (portNumber, isListener, readLock); - if (h != invalidSocket || connected) closesocket (h); @@ -133,7 +158,7 @@ namespace SocketHelpers // a chance to process before close is called. On Mac OS X shutdown // does not unblock a select call, so using a lock here will dead-lock // both threads. - #if JUCE_LINUX || JUCE_ANDROID + #if JUCE_LINUX || JUCE_BSD || JUCE_ANDROID CriticalSection::ScopedLockType lock (readLock); ::close (h); #else @@ -190,7 +215,7 @@ namespace SocketHelpers { #if JUCE_WINDOWS u_long nonBlocking = shouldBlock ? 0 : (u_long) 1; - return ioctlsocket (handle, FIONBIO, &nonBlocking) == 0; + return ioctlsocket (handle, (long) FIONBIO, &nonBlocking) == 0; #else int socketFlags = fcntl (handle, F_GETFL, 0); @@ -265,7 +290,7 @@ namespace SocketHelpers break; } - bytesRead += bytesThisTime; + bytesRead = static_cast (bytesRead + bytesThisTime); if (! blockUntilSpecifiedAmountHasArrived) break; @@ -283,9 +308,9 @@ namespace SocketHelpers if (! lock.isLocked()) return -1; - auto hasErrorOccurred = [&handle] () -> bool + auto hasErrorOccurred = [&handle]() -> bool { - auto h = handle.load(); + auto h = (SocketHandle) handle.load(); if (h == invalidSocket) return true; @@ -318,9 +343,9 @@ namespace SocketHelpers fd_set rset, wset; FD_ZERO (&rset); - FD_SET (h, &rset); + FD_SET ((SOCKET) h, &rset); FD_ZERO (&wset); - FD_SET (h, &wset); + FD_SET ((SOCKET) h, &wset); fd_set* prset = forReading ? &rset : nullptr; fd_set* pwset = forReading ? nullptr : &wset; @@ -372,7 +397,8 @@ namespace SocketHelpers CriticalSection& readLock, const String& hostName, int portNumber, - int timeOutMillisecs) noexcept + int timeOutMillisecs, + const SocketOptions& options) noexcept { bool success = false; @@ -421,8 +447,9 @@ namespace SocketHelpers if (success) { - setSocketBlockingState (handle, true); - resetSocketOptions (handle, false, false); + auto h = (SocketHandle) handle.load(); + setSocketBlockingState (h, true); + resetSocketOptions (h, false, false, options); } } @@ -431,7 +458,7 @@ namespace SocketHelpers static void makeReusable (int handle) noexcept { - setOption (handle, SO_REUSEADDR, (int) 1); + setOption ((SocketHandle) handle, SO_REUSEADDR, (int) 1); } static bool multicast (int handle, const String& multicastIPAddress, @@ -446,7 +473,7 @@ namespace SocketHelpers if (interfaceIPAddress.isNotEmpty()) mreq.imr_interface.s_addr = inet_addr (interfaceIPAddress.toRawUTF8()); - return setsockopt (handle, IPPROTO_IP, + return setsockopt ((SocketHandle) handle, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (const char*) &mreq, sizeof (mreq)) == 0; @@ -459,8 +486,9 @@ StreamingSocket::StreamingSocket() SocketHelpers::initSockets(); } -StreamingSocket::StreamingSocket (const String& host, int portNum, int h) - : hostName (host), +StreamingSocket::StreamingSocket (const String& host, int portNum, int h, const SocketOptions& optionsIn) + : options (optionsIn), + hostName (host), portNumber (portNum), handle (h), connected (true) @@ -468,7 +496,7 @@ StreamingSocket::StreamingSocket (const String& host, int portNum, int h) jassert (SocketHelpers::isValidPortNumber (portNum)); SocketHelpers::initSockets(); - SocketHelpers::resetSocketOptions (h, false, false); + SocketHelpers::resetSocketOptions ((SocketHandle) h, false, false, options); } StreamingSocket::~StreamingSocket() @@ -479,7 +507,7 @@ StreamingSocket::~StreamingSocket() //============================================================================== int StreamingSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock) { - return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, + return (connected && ! isListener) ? SocketHelpers::readSocket ((SocketHandle) handle.load(), destBuffer,maxBytesToRead, connected, shouldBlock, readLock) : -1; } @@ -489,7 +517,7 @@ int StreamingSocket::write (const void* sourceBuffer, int numBytesToWrite) if (isListener || ! connected) return -1; - return (int) ::send (handle, (const char*) sourceBuffer, (juce_recvsend_size_t) numBytesToWrite, 0); + return (int) ::send ((SocketHandle) handle.load(), (const char*) sourceBuffer, (juce_recvsend_size_t) numBytesToWrite, 0); } //============================================================================== @@ -509,12 +537,12 @@ bool StreamingSocket::bindToPort (int port, const String& addr) { jassert (SocketHelpers::isValidPortNumber (port)); - return SocketHelpers::bindSocket (handle, port, addr); + return SocketHelpers::bindSocket ((SocketHandle) handle.load(), port, addr); } int StreamingSocket::getBoundPort() const noexcept { - return SocketHelpers::getBoundPort (handle); + return SocketHelpers::getBoundPort ((SocketHandle) handle.load()); } bool StreamingSocket::connect (const String& remoteHostName, int remotePortNumber, int timeOutMillisecs) @@ -536,12 +564,12 @@ bool StreamingSocket::connect (const String& remoteHostName, int remotePortNumbe isListener = false; connected = SocketHelpers::connectSocket (handle, readLock, remoteHostName, - remotePortNumber, timeOutMillisecs); + remotePortNumber, timeOutMillisecs, options); if (! connected) return false; - if (! SocketHelpers::resetSocketOptions (handle, false, false)) + if (! SocketHelpers::resetSocketOptions ((SocketHandle) handle.load(), false, false, options)) { close(); return false; @@ -582,8 +610,8 @@ bool StreamingSocket::createListener (int newPortNumber, const String& localHost SocketHelpers::makeReusable (handle); #endif - if (SocketHelpers::bindSocket (handle, portNumber, localHostName) - && listen (handle, SOMAXCONN) >= 0) + if (SocketHelpers::bindSocket ((SocketHandle) handle.load(), portNumber, localHostName) + && listen ((SocketHandle) handle.load(), SOMAXCONN) >= 0) { connected = true; return true; @@ -603,11 +631,11 @@ StreamingSocket* StreamingSocket::waitForNextConnection() const { struct sockaddr_storage address; juce_socklen_t len = sizeof (address); - auto newSocket = (int) accept (handle, (struct sockaddr*) &address, &len); + auto newSocket = (int) accept ((SocketHandle) handle.load(), (struct sockaddr*) &address, &len); if (newSocket >= 0 && connected) return new StreamingSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr), - portNumber, newSocket); + portNumber, newSocket, options); } return nullptr; @@ -618,7 +646,7 @@ bool StreamingSocket::isLocal() const noexcept if (! isConnected()) return false; - IPAddress currentIP (SocketHelpers::getConnectedAddress (handle)); + IPAddress currentIP (SocketHelpers::getConnectedAddress ((SocketHandle) handle.load())); for (auto& a : IPAddress::getAllAddresses()) if (a == currentIP) @@ -630,7 +658,8 @@ bool StreamingSocket::isLocal() const noexcept //============================================================================== //============================================================================== -DatagramSocket::DatagramSocket (bool canBroadcast) +DatagramSocket::DatagramSocket (bool canBroadcast, const SocketOptions& optionsIn) + : options { optionsIn } { SocketHelpers::initSockets(); @@ -638,7 +667,7 @@ DatagramSocket::DatagramSocket (bool canBroadcast) if (handle >= 0) { - SocketHelpers::resetSocketOptions (handle, true, canBroadcast); + SocketHelpers::resetSocketOptions ((SocketHandle) handle.load(), true, canBroadcast, options); SocketHelpers::makeReusable (handle); } } @@ -677,7 +706,7 @@ bool DatagramSocket::bindToPort (int port, const String& addr) if (handle < 0) return false; - if (SocketHelpers::bindSocket (handle, port, addr)) + if (SocketHelpers::bindSocket ((SocketHandle) handle.load(), port, addr)) { isBound = true; lastBindAddress = addr; @@ -689,7 +718,7 @@ bool DatagramSocket::bindToPort (int port, const String& addr) int DatagramSocket::getBoundPort() const noexcept { - return (handle >= 0 && isBound) ? SocketHelpers::getBoundPort (handle) : -1; + return (handle >= 0 && isBound) ? SocketHelpers::getBoundPort ((SocketHandle) handle.load()) : -1; } //============================================================================== @@ -707,7 +736,7 @@ int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock return -1; std::atomic connected { true }; - return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, + return SocketHelpers::readSocket ((SocketHandle) handle.load(), destBuffer, maxBytesToRead, connected, shouldBlock, readLock); } @@ -717,7 +746,7 @@ int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock return -1; std::atomic connected { true }; - return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, + return SocketHelpers::readSocket ((SocketHandle) handle.load(), destBuffer, maxBytesToRead, connected, shouldBlock, readLock, &senderIPAddress, &senderPort); } @@ -744,7 +773,7 @@ int DatagramSocket::write (const String& remoteHostname, int remotePortNumber, lastServerPort = remotePortNumber; } - return (int) ::sendto (handle, (const char*) sourceBuffer, + return (int) ::sendto ((SocketHandle) handle.load(), (const char*) sourceBuffer, (juce_recvsend_size_t) numBytesToWrite, 0, info->ai_addr, (socklen_t) info->ai_addrlen); } @@ -770,17 +799,15 @@ bool DatagramSocket::setMulticastLoopbackEnabled (bool enable) if (handle < 0 || ! isBound) return false; - return SocketHelpers::setOption (handle, IPPROTO_IP, IP_MULTICAST_LOOP, enable); + return SocketHelpers::setOption ((SocketHandle) handle.load(), IPPROTO_IP, IP_MULTICAST_LOOP, enable); } -bool DatagramSocket::setEnablePortReuse (bool enabled) +bool DatagramSocket::setEnablePortReuse ([[maybe_unused]] bool enabled) { - #if JUCE_ANDROID - ignoreUnused (enabled); - #else + #if ! JUCE_ANDROID if (handle >= 0) - return SocketHelpers::setOption (handle, - #if JUCE_WINDOWS || JUCE_LINUX + return SocketHelpers::setOption ((SocketHandle) handle.load(), + #if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD SO_REUSEADDR, // port re-use is implied by addr re-use on these platforms #else SO_REUSEPORT, @@ -791,16 +818,13 @@ bool DatagramSocket::setEnablePortReuse (bool enabled) return false; } -#if JUCE_MSVC - #pragma warning (pop) -#endif - +JUCE_END_IGNORE_WARNINGS_MSVC //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS -struct SocketTests : public UnitTest +struct SocketTests final : public UnitTest { SocketTests() : UnitTest ("Sockets", UnitTestCategories::networking) @@ -819,7 +843,7 @@ struct SocketTests : public UnitTest expect (socketServer.isConnected() == false); expect (socketServer.getHostName().isEmpty()); expect (socketServer.getBoundPort() == -1); - expect (socketServer.getRawSocketHandle() == invalidSocket); + expect (static_cast (socketServer.getRawSocketHandle()) == invalidSocket); expect (socketServer.createListener (portNum, localHost.toString())); @@ -830,14 +854,14 @@ struct SocketTests : public UnitTest expect (socket.isConnected() == true); expect (socket.getHostName() == localHost.toString()); expect (socket.getBoundPort() != -1); - expect (socket.getRawSocketHandle() != invalidSocket); + expect (static_cast (socket.getRawSocketHandle()) != invalidSocket); socket.close(); expect (socket.isConnected() == false); expect (socket.getHostName().isEmpty()); expect (socket.getBoundPort() == -1); - expect (socket.getRawSocketHandle() == invalidSocket); + expect (static_cast (socket.getRawSocketHandle()) == invalidSocket); } beginTest ("DatagramSocket"); @@ -845,23 +869,24 @@ struct SocketTests : public UnitTest DatagramSocket socket; expect (socket.getBoundPort() == -1); - expect (socket.getRawSocketHandle() != invalidSocket); + expect (static_cast (socket.getRawSocketHandle()) != invalidSocket); expect (socket.bindToPort (portNum, localHost.toString())); expect (socket.getBoundPort() == portNum); - expect (socket.getRawSocketHandle() != invalidSocket); + expect (static_cast (socket.getRawSocketHandle()) != invalidSocket); socket.shutdown(); expect (socket.getBoundPort() == -1); - expect (socket.getRawSocketHandle() == invalidSocket); + expect (static_cast (socket.getRawSocketHandle()) == invalidSocket); } } }; static SocketTests socketTests; +#endif #endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/network/juce_Socket.h b/JuceLibraryCode/modules/juce_core/network/juce_Socket.h index cdc4f2f1..e2fdcccd 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_Socket.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_Socket.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,50 @@ namespace juce { +/** + Options used for the configuration of the underlying system socket in the + StreamingSocket and DatagramSocket classes. + + @see StreamingSocket, DatagramSocket + + @tags{Core} +*/ +class JUCE_API SocketOptions +{ +public: + /** The provided size will be used to configure the socket's SO_RCVBUF property. Increasing the + buffer size can reduce the number of lost packets with the DatagramSocket class, if the + socket is to receive packets in large bursts. + + If this property is not specified, the system default value will be used, but a minimum of + 65536 will be ensured. + */ + [[nodiscard]] SocketOptions withReceiveBufferSize (int size) const + { + return withMember (*this, &SocketOptions::receiveBufferSize, size); + } + + /** The provided size will be used to configure the socket's SO_SNDBUF property. + + If this property is not specified, the system default value will be used, but a minimum of + 65536 will be ensured. + */ + [[nodiscard]] SocketOptions withSendBufferSize (int size) const + { + return withMember (*this, &SocketOptions::sendBufferSize, size); + } + + /** @see withReceiveBufferSize() */ + [[nodiscard]] auto getReceiveBufferSize() const { return receiveBufferSize; } + + /** @see withSendBufferSize() */ + [[nodiscard]] auto getSendBufferSize() const { return sendBufferSize; } + +private: + std::optional receiveBufferSize; + std::optional sendBufferSize; +}; + //============================================================================== /** A wrapper for a streaming (TCP) socket. @@ -37,6 +81,8 @@ namespace juce class JUCE_API StreamingSocket final { public: + using Options = SocketOptions; + //============================================================================== /** Creates an uninitialised socket. @@ -49,6 +95,20 @@ class JUCE_API StreamingSocket final */ StreamingSocket(); + /** Creates an uninitialised socket and allows specifying options related to the + configuration of the underlying socket. + + To connect it, use the connect() method, after which you can read() or write() + to it. + + To wait for other sockets to connect to this one, the createListener() method + enters "listener" mode, and can be used to spawn new sockets for each connection + that comes along. + */ + explicit StreamingSocket (const SocketOptions& optionsIn) + : options { optionsIn } + {} + /** Destructor. */ ~StreamingSocket(); @@ -69,7 +129,7 @@ class JUCE_API StreamingSocket final @returns true on success; false may indicate that another socket is already bound on the same port - @see bindToPort(int localPortNumber), IPAddress::getAllAddresses + @see bindToPort (int localPortNumber), IPAddress::getAllAddresses */ bool bindToPort (int localPortNumber, const String& localAddress); @@ -178,12 +238,13 @@ class JUCE_API StreamingSocket final private: //============================================================================== + SocketOptions options; String hostName; std::atomic portNumber { 0 }, handle { -1 }; std::atomic connected { false }, isListener { false }; mutable CriticalSection readLock; - StreamingSocket (const String& hostname, int portNumber, int handle); + StreamingSocket (const String& hostname, int portNumber, int handle, const SocketOptions& options); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StreamingSocket) }; @@ -203,7 +264,20 @@ class JUCE_API StreamingSocket final class JUCE_API DatagramSocket final { public: + using Options = SocketOptions; + //============================================================================== + /** Creates a datagram socket and allows specifying options related to the + configuration of the underlying socket. + + You first need to bind this socket to a port with bindToPort if you intend to read + from this socket. + + If enableBroadcasting is true, the socket will be allowed to send broadcast messages + (may require extra privileges on linux) + */ + DatagramSocket (bool enableBroadcasting, const SocketOptions& optionsIn); + /** Creates a datagram socket. You first need to bind this socket to a port with bindToPort if you intend to read @@ -212,8 +286,19 @@ class JUCE_API DatagramSocket final If enableBroadcasting is true, the socket will be allowed to send broadcast messages (may require extra privileges on linux) */ - DatagramSocket (bool enableBroadcasting = false); + explicit DatagramSocket (bool enableBroadcasting) + : DatagramSocket (enableBroadcasting, SocketOptions{}) + {} + + /** Creates a datagram socket. + You first need to bind this socket to a port with bindToPort if you intend to read + from this socket. + + This constructor creates a socket that does not allow sending broadcast messages. + */ + DatagramSocket() : DatagramSocket (false) + {} /** Destructor. */ ~DatagramSocket(); @@ -238,7 +323,7 @@ class JUCE_API DatagramSocket final @returns true on success; false may indicate that another socket is already bound on the same port - @see bindToPort(int localPortNumber), IPAddress::getAllAddresses + @see bindToPort (int localPortNumber), IPAddress::getAllAddresses */ bool bindToPort (int localPortNumber, const String& localAddress); @@ -354,6 +439,7 @@ class JUCE_API DatagramSocket final private: //============================================================================== + SocketOptions options; std::atomic handle { -1 }; bool isBound = false; String lastBindAddress, lastServerHost; diff --git a/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp b/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp index b3d3dd1f..22213806 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,16 +23,16 @@ namespace juce { -struct FallbackDownloadTask : public URL::DownloadTask, - public Thread +struct FallbackDownloadTask final : public URL::DownloadTask, + public Thread { - FallbackDownloadTask (FileOutputStream* outputStreamToUse, + FallbackDownloadTask (std::unique_ptr outputStreamToUse, size_t bufferSizeToUse, - WebInputStream* streamToUse, + std::unique_ptr streamToUse, URL::DownloadTask::Listener* listenerToUse) : Thread ("DownloadTask thread"), - fileStream (outputStreamToUse), - stream (streamToUse), + fileStream (std::move (outputStreamToUse)), + stream (std::move (streamToUse)), bufferSize (bufferSizeToUse), buffer (bufferSize), listener (listenerToUse) @@ -106,26 +106,26 @@ struct FallbackDownloadTask : public URL::DownloadTask, JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FallbackDownloadTask) }; -void URL::DownloadTask::Listener::progress (DownloadTask*, int64, int64) {} -URL::DownloadTask::Listener::~Listener() {} +void URL::DownloadTaskListener::progress (DownloadTask*, int64, int64) {} //============================================================================== -URL::DownloadTask* URL::DownloadTask::createFallbackDownloader (const URL& urlToUse, - const File& targetFileToUse, - const String& extraHeadersToUse, - Listener* listenerToUse, - bool usePostRequest) +std::unique_ptr URL::DownloadTask::createFallbackDownloader (const URL& urlToUse, + const File& targetFileToUse, + const DownloadTaskOptions& options) { const size_t bufferSize = 0x8000; targetFileToUse.deleteFile(); - if (auto outputStream = std::unique_ptr (targetFileToUse.createOutputStream (bufferSize))) + if (auto outputStream = targetFileToUse.createOutputStream (bufferSize)) { - std::unique_ptr stream (new WebInputStream (urlToUse, usePostRequest)); - stream->withExtraHeaders (extraHeadersToUse); + auto stream = std::make_unique (urlToUse, options.usePost); + stream->withExtraHeaders (options.extraHeaders); if (stream->connect (nullptr)) - return new FallbackDownloadTask (outputStream.release(), bufferSize, stream.release(), listenerToUse); + return std::make_unique (std::move (outputStream), + bufferSize, + std::move (stream), + options.listener); } return nullptr; @@ -178,7 +178,15 @@ URL::URL (File localFile) void URL::init() { - auto i = url.indexOfChar ('?'); + auto i = url.indexOfChar ('#'); + + if (i >= 0) + { + anchor = removeEscapeChars (url.substring (i + 1)); + url = url.upToFirstOccurrenceOf ("#", false, false); + } + + i = url.indexOfChar ('?'); if (i >= 0) { @@ -346,8 +354,21 @@ String URL::getSubPath (bool includeGetParameters) const String URL::getQueryString() const { + String result; + if (parameterNames.size() > 0) - return "?" + URLHelpers::getMangledParameters (*this); + result += "?" + URLHelpers::getMangledParameters (*this); + + if (anchor.isNotEmpty()) + result += getAnchorString(); + + return result; +} + +String URL::getAnchorString() const +{ + if (anchor.isNotEmpty()) + return "#" + URL::addEscapeChars (anchor, true); return {}; } @@ -374,6 +395,11 @@ String URL::getFileName() const } #endif +URL::ParameterHandling URL::toHandling (bool usePostData) +{ + return usePostData ? ParameterHandling::inPostData : ParameterHandling::inAddress; +} + File URL::fileFromFileSchemeURL (const URL& fileURL) { if (! fileURL.isLocalFile()) @@ -444,14 +470,21 @@ URL URL::getChildURL (const String& subPath) const return u; } -void URL::createHeadersAndPostData (String& headers, MemoryBlock& postDataToWrite) const +bool URL::hasBodyDataToSend() const +{ + return filesToUpload.size() > 0 || ! postData.isEmpty(); +} + +void URL::createHeadersAndPostData (String& headers, + MemoryBlock& postDataToWrite, + bool addParametersToBody) const { MemoryOutputStream data (postDataToWrite, false); if (filesToUpload.size() > 0) { // (this doesn't currently support mixing custom post-data with uploads..) - jassert (postData.getSize() == 0); + jassert (postData.isEmpty()); auto boundary = String::toHexString (Random::getSystemRandom().nextInt64()); @@ -488,8 +521,10 @@ void URL::createHeadersAndPostData (String& headers, MemoryBlock& postDataToWrit } else { - data << URLHelpers::getMangledParameters (*this) - << postData; + if (addParametersToBody) + data << URLHelpers::getMangledParameters (*this); + + data << postData; // if the user-supplied headers didn't contain a content-type, add one now.. if (! headers.containsIgnoreCase ("Content-Type")) @@ -563,7 +598,7 @@ template struct iOSFileStreamWrapperFlush { static void flu template <> struct iOSFileStreamWrapperFlush { static void flush (OutputStream* o) { o->flush(); } }; template -class iOSFileStreamWrapper : public Stream +class iOSFileStreamWrapper final : public Stream { public: iOSFileStreamWrapper (URL& urlToUse) @@ -595,8 +630,7 @@ class iOSFileStreamWrapper : public Stream } else { - auto desc = [error localizedDescription]; - ignoreUnused (desc); + [[maybe_unused]] auto desc = [error localizedDescription]; jassertfalse; } } @@ -629,8 +663,7 @@ class iOSFileStreamWrapper : public Stream return urlToUse.getLocalFile(); } - auto desc = [error localizedDescription]; - ignoreUnused (desc); + [[maybe_unused]] auto desc = [error localizedDescription]; jassertfalse; } @@ -653,101 +686,154 @@ class iOSFileStreamWrapper : public Stream } }; #endif +//============================================================================== +template +static URL::InputStreamOptions with (URL::InputStreamOptions options, Member&& member, Item&& item) +{ + options.*member = std::forward (item); + return options; +} + +URL::InputStreamOptions::InputStreamOptions (ParameterHandling handling) : parameterHandling (handling) {} + +URL::InputStreamOptions URL::InputStreamOptions::withProgressCallback (std::function cb) const +{ + return with (*this, &InputStreamOptions::progressCallback, std::move (cb)); +} + +URL::InputStreamOptions URL::InputStreamOptions::withExtraHeaders (const String& headers) const +{ + return with (*this, &InputStreamOptions::extraHeaders, headers); +} + +URL::InputStreamOptions URL::InputStreamOptions::withConnectionTimeoutMs (int timeout) const +{ + return with (*this, &InputStreamOptions::connectionTimeOutMs, timeout); +} + +URL::InputStreamOptions URL::InputStreamOptions::withResponseHeaders (StringPairArray* headers) const +{ + return with (*this, &InputStreamOptions::responseHeaders, headers); +} + +URL::InputStreamOptions URL::InputStreamOptions::withStatusCode (int* status) const +{ + return with (*this, &InputStreamOptions::statusCode, status); +} + +URL::InputStreamOptions URL::InputStreamOptions::withNumRedirectsToFollow (int numRedirects) const +{ + return with (*this, &InputStreamOptions::numRedirectsToFollow, numRedirects); +} + +URL::InputStreamOptions URL::InputStreamOptions::withHttpRequestCmd (const String& cmd) const +{ + return with (*this, &InputStreamOptions::httpRequestCmd, cmd); +} //============================================================================== -InputStream* URL::createInputStream (bool usePostCommand, - OpenStreamProgressCallback* progressCallback, - void* progressCallbackContext, - String headers, - int timeOutMs, - StringPairArray* responseHeaders, - int* statusCode, - int numRedirectsToFollow, - String httpRequestCmd) const +std::unique_ptr URL::createInputStream (const InputStreamOptions& options) const { if (isLocalFile()) { #if JUCE_IOS // We may need to refresh the embedded bookmark. - return new iOSFileStreamWrapper (const_cast(*this)); + return std::make_unique> (const_cast (*this)); #else return getLocalFile().createInputStream(); #endif } - auto wi = std::make_unique (*this, usePostCommand); + auto webInputStream = [&] + { + const auto usePost = options.getParameterHandling() == ParameterHandling::inPostData; + auto stream = std::make_unique (*this, usePost); + + auto extraHeaders = options.getExtraHeaders(); + + if (extraHeaders.isNotEmpty()) + stream->withExtraHeaders (extraHeaders); + + auto timeout = options.getConnectionTimeoutMs(); + + if (timeout != 0) + stream->withConnectionTimeout (timeout); + + auto requestCmd = options.getHttpRequestCmd(); + + if (requestCmd.isNotEmpty()) + stream->withCustomRequestCommand (requestCmd); - struct ProgressCallbackCaller : public WebInputStream::Listener + stream->withNumRedirectsToFollow (options.getNumRedirectsToFollow()); + + return stream; + }(); + + struct ProgressCallbackCaller final : public WebInputStream::Listener { - ProgressCallbackCaller (OpenStreamProgressCallback* progressCallbackToUse, void* progressCallbackContextToUse) - : callback (progressCallbackToUse), data (progressCallbackContextToUse) - {} + ProgressCallbackCaller (std::function progressCallbackToUse) + : callback (std::move (progressCallbackToUse)) + { + } bool postDataSendProgress (WebInputStream&, int bytesSent, int totalBytes) override { - return callback (data, bytesSent, totalBytes); + return callback (bytesSent, totalBytes); } - OpenStreamProgressCallback* callback; - void* const data; + std::function callback; }; - std::unique_ptr callbackCaller - (progressCallback != nullptr ? new ProgressCallbackCaller (progressCallback, progressCallbackContext) : nullptr); - - if (headers.isNotEmpty()) - wi->withExtraHeaders (headers); - - if (timeOutMs != 0) - wi->withConnectionTimeout (timeOutMs); - - if (httpRequestCmd.isNotEmpty()) - wi->withCustomRequestCommand (httpRequestCmd); + auto callbackCaller = [&options]() -> std::unique_ptr + { + if (auto progressCallback = options.getProgressCallback()) + return std::make_unique (progressCallback); - wi->withNumRedirectsToFollow (numRedirectsToFollow); + return {}; + }(); - bool success = wi->connect (callbackCaller.get()); + auto success = webInputStream->connect (callbackCaller.get()); - if (statusCode != nullptr) - *statusCode = wi->getStatusCode(); + if (auto* status = options.getStatusCode()) + *status = webInputStream->getStatusCode(); - if (responseHeaders != nullptr) - *responseHeaders = wi->getResponseHeaders(); + if (auto* responseHeaders = options.getResponseHeaders()) + *responseHeaders = webInputStream->getResponseHeaders(); - if (! success || wi->isError()) + if (! success || webInputStream->isError()) return nullptr; - return wi.release(); + // std::move() needed here for older compilers + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move") + return std::move (webInputStream); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } -#if JUCE_ANDROID -OutputStream* juce_CreateContentURIOutputStream (const URL&); -#endif - -OutputStream* URL::createOutputStream() const +std::unique_ptr URL::createOutputStream() const { + #if JUCE_ANDROID + if (auto stream = AndroidDocument::fromDocument (*this).createOutputStream()) + return stream; + #endif + if (isLocalFile()) { #if JUCE_IOS // We may need to refresh the embedded bookmark. - return new iOSFileStreamWrapper (const_cast (*this)); + return std::make_unique> (const_cast (*this)); #else - return new FileOutputStream (getLocalFile()); + return std::make_unique (getLocalFile()); #endif } - #if JUCE_ANDROID - return juce_CreateContentURIOutputStream (*this); - #else return nullptr; - #endif } //============================================================================== bool URL::readEntireBinaryStream (MemoryBlock& destData, bool usePostCommand) const { const std::unique_ptr in (isLocalFile() ? getLocalFile().createInputStream() - : createInputStream (usePostCommand)); + : createInputStream (InputStreamOptions (toHandling (usePostCommand)))); if (in != nullptr) { @@ -761,7 +847,7 @@ bool URL::readEntireBinaryStream (MemoryBlock& destData, bool usePostCommand) co String URL::readEntireTextStream (bool usePostCommand) const { const std::unique_ptr in (isLocalFile() ? getLocalFile().createInputStream() - : createInputStream (usePostCommand)); + : createInputStream (InputStreamOptions (toHandling (usePostCommand)))); if (in != nullptr) return in->readEntireStreamAsString(); @@ -794,6 +880,14 @@ URL URL::withParameters (const StringPairArray& parametersToAdd) const return u; } +URL URL::withAnchor (const String& anchorToAdd) const +{ + auto u = *this; + + u.anchor = anchorToAdd; + return u; +} + URL URL::withPOSTData (const String& newPostData) const { return withPOSTData (MemoryBlock (newPostData.toRawUTF8(), newPostData.getNumBytesAsUTF8())); @@ -818,7 +912,7 @@ URL URL::withUpload (Upload* const f) const auto u = *this; for (int i = u.filesToUpload.size(); --i >= 0;) - if (u.filesToUpload.getObjectPointerUnchecked(i)->parameterName == f->parameterName) + if (u.filesToUpload.getObjectPointerUnchecked (i)->parameterName == f->parameterName) u.filesToUpload.remove (i); u.filesToUpload.add (f); @@ -853,7 +947,7 @@ String URL::removeEscapeChars (const String& s) for (int i = 0; i < utf8.size(); ++i) { - if (utf8.getUnchecked(i) == '%') + if (utf8.getUnchecked (i) == '%') { auto hexDigit1 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 1]); auto hexDigit2 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 2]); @@ -881,7 +975,7 @@ String URL::addEscapeChars (const String& s, bool isParameter, bool roundBracket for (int i = 0; i < utf8.size(); ++i) { - auto c = utf8.getUnchecked(i); + auto c = utf8.getUnchecked (i); if (! (CharacterFunctions::isLetterOrDigit (c) || legalChars.containsChar ((juce_wchar) c))) @@ -906,4 +1000,41 @@ bool URL::launchInDefaultBrowser() const return Process::openDocument (u, {}); } +//============================================================================== +std::unique_ptr URL::createInputStream (bool usePostCommand, + OpenStreamProgressCallback* cb, + void* context, + String headers, + int timeOutMs, + StringPairArray* responseHeaders, + int* statusCode, + int numRedirectsToFollow, + String httpRequestCmd) const +{ + std::function callback; + + if (cb != nullptr) + callback = [context, cb] (int sent, int total) { return cb (context, sent, total); }; + + return createInputStream (InputStreamOptions (toHandling (usePostCommand)) + .withProgressCallback (std::move (callback)) + .withExtraHeaders (headers) + .withConnectionTimeoutMs (timeOutMs) + .withResponseHeaders (responseHeaders) + .withStatusCode (statusCode) + .withNumRedirectsToFollow (numRedirectsToFollow) + .withHttpRequestCmd (httpRequestCmd)); +} + +std::unique_ptr URL::downloadToFile (const File& targetLocation, + String extraHeaders, + DownloadTask::Listener* listener, + bool usePostCommand) +{ + auto options = DownloadTaskOptions().withExtraHeaders (std::move (extraHeaders)) + .withListener (listener) + .withUsePost (usePostCommand); + return downloadToFile (targetLocation, std::move (options)); +} + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/network/juce_URL.h b/JuceLibraryCode/modules/juce_core/network/juce_URL.h index f94a717f..b14b13e3 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_URL.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_URL.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -30,7 +30,7 @@ class WebInputStream; Represents a URL and has a bunch of useful functions to manipulate it. This class can be used to launch URLs in browsers, and also to create - InputStreams that can read from remote http or ftp sources. + InputStreams that can read from remote HTTP or FTP sources. @tags{Core} */ @@ -42,24 +42,18 @@ class JUCE_API URL URL(); /** Creates a URL from a string. + This will parse any embedded parameters after a '?' character and store them in the list (see getParameterNames etc). If you don't want this to happen, you can use createWithoutParsing(). */ URL (const String& url); - URL (const URL&) = default; - URL& operator= (const URL&) = default; - 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() = default; + explicit URL (File localFile); /** Compares two URLs. + All aspects of the URLs must be identical for them to match, including any parameters, upload files, etc. */ @@ -69,9 +63,11 @@ class JUCE_API URL //============================================================================== /** Returns a string version of the URL. - 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. + @param includeGetParameters if this 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. + + @see getQueryString */ String toString (bool includeGetParameters) const; @@ -82,26 +78,36 @@ class JUCE_API URL bool isWellFormed() const; /** Returns just the domain part of the URL. - E.g. for "http://www.xyz.com/foobar", this will return "www.xyz.com". + + e.g. for "http://www.xyz.com/foobar", this will return "www.xyz.com". */ String getDomain() const; /** 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. + e.g. for "http://www.xyz.com/foo/bar?x=1", this will return "foo/bar". + + @param includeGetParameters if this 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. + + @see getQueryString */ String getSubPath (bool includeGetParameters = false) const; - /** If any parameters are set, returns these URL encoded, including the "?" - * prefix. + /** If any parameters are set, returns these URL-encoded, including the "?" + prefix. */ String getQueryString() const; + /** If any anchor is set, returns URL-encoded anchor, including the "#" + prefix. + */ + String getAnchorString() const; + /** Returns the scheme of the URL. - E.g. for "http://www.xyz.com/foobar", this will return "http". (It won't + + e.g. for "http://www.xyz.com/foobar", this will return "http" (it won't include the colon). */ String getScheme() const; @@ -110,19 +116,20 @@ class JUCE_API URL bool isLocalFile() const; /** Returns the file path of the local file to which this URL refers to. + If the URL does not represent a local file URL (i.e. the URL's scheme is not 'file') then this method will assert. - This method also supports converting Android's content:// URLs to - local file paths. + This method also supports converting Android's content:// URLs to local file paths. @see isLocalFile */ File getLocalFile() const; - /** Returns the file name. For all but Android's content:// scheme, it will - simply return the last segment of the URL. - E.g. for "http://www.xyz.com/foo/bar.txt", this will return "bar.txt". + /** Returns the file name. + + For all but Android's content:// scheme, it will simply return the last segment of + the URL, e.g. for "http://www.xyz.com/foo/bar.txt", this will return "bar.txt". For Android's content:// scheme, it will attempt to resolve the filename located under the URL. @@ -130,36 +137,44 @@ class JUCE_API URL String getFileName() const; /** Attempts to read a port number from the URL. + @returns the port number, or 0 if none is explicitly specified. */ int getPort() const; /** Returns a new version of this URL with a different domain and path. - E.g. if the URL is "http://www.xyz.com/foo?x=1" and you call this with + + e.g. if the URL is "http://www.xyz.com/foo?x=1" and you call this with "abc.com/zzz", it'll return "http://abc.com/zzz?x=1". + @see withNewSubPath */ - URL withNewDomainAndPath (const String& newFullPath) const; + [[nodiscard]] URL withNewDomainAndPath (const String& newFullPath) const; /** Returns a new version of this URL with a different sub-path. - E.g. if the URL is "http://www.xyz.com/foo?x=1" and you call this with + + e.g. if the URL is "http://www.xyz.com/foo?x=1" and you call this with "bar", it'll return "http://www.xyz.com/bar?x=1". + @see withNewDomainAndPath */ - URL withNewSubPath (const String& newPath) const; + [[nodiscard]] 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 - this method to know whether the original URL is a file or directory, so it's - up to you to make sure it's a directory. It also won't attempt to be smart about - the content of the childPath string, so if this string is an absolute URL, it'll - still just get bolted onto the end of the path. + + 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 this method to know whether the original URL is + a file or directory, so it's up to you to make sure it's a directory. It also + won't attempt to be smart about the content of the childPath string, so if this + string is an absolute URL, it'll still just get bolted onto the end of the path. @see File::getChildFile */ @@ -168,21 +183,28 @@ class JUCE_API URL //============================================================================== /** Returns a copy of this URL, with a GET or POST parameter added to the end. - Any control characters in the value will be encoded. + Any control characters in the value will be URL-encoded. + e.g. calling "withParameter ("amount", "some fish") for the url "www.fish.com" - would produce a new url whose toString(true) method would return + would produce a new url whose `toString (true)` method would return "www.fish.com?amount=some+fish". @see getParameterNames, getParameterValues */ - URL withParameter (const String& parameterName, - const String& parameterValue) const; + [[nodiscard]] URL withParameter (const String& parameterName, + const String& parameterValue) const; /** Returns a copy of this URL, with a set of GET or POST parameters added. + This is a convenience method, equivalent to calling withParameter for each value. + @see withParameter */ - URL withParameters (const StringPairArray& parametersToAdd) const; + [[nodiscard]] URL withParameters (const StringPairArray& parametersToAdd) const; + + /** Returns a copy of this URL, with an anchor added to the end of the URL. + */ + [[nodiscard]] URL withAnchor (const String& anchor) const; /** Returns a copy of this URL, with a file-upload type parameter added to it. @@ -195,27 +217,28 @@ class JUCE_API URL @see withDataToUpload */ - URL withFileToUpload (const String& parameterName, - const File& fileToUpload, - const String& mimeType) const; + [[nodiscard]] URL withFileToUpload (const String& parameterName, + const File& fileToUpload, + const String& mimeType) const; /** Returns a copy of this URL, with a file-upload type parameter added to it. When performing a POST where one of your parameters is a binary file, this lets you specify the file content. + Note that the filename parameter should not be a full path, it's just the last part of the filename. @see withFileToUpload */ - URL withDataToUpload (const String& parameterName, - const String& filename, - const MemoryBlock& fileContentToUpload, - const String& mimeType) const; + [[nodiscard]] URL withDataToUpload (const String& parameterName, + const String& filename, + const MemoryBlock& fileContentToUpload, + const String& mimeType) const; /** Returns an array of the names of all the URL's parameters. - E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would + e.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would contain two items: "type" and "amount". You can call getParameterValues() to get the corresponding value of each @@ -227,7 +250,7 @@ class JUCE_API URL /** Returns an array of the values of all the URL's parameters. - E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would + e.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would contain two items: "haddock" and "some fish". The values returned will have been cleaned up to remove any escape characters. @@ -241,41 +264,34 @@ class JUCE_API URL /** Returns a copy of this URL, with a block of data to send as the POST data. - If you're setting the POST data, be careful not to have any parameters set - as well, otherwise it'll all get thrown in together, and might not have the - desired effect. - If the URL already contains some POST data, this will replace it, rather than being appended to it. - This data will only be used if you specify a post operation when you call - createInputStream(). + If no HTTP command is set when calling createInputStream() to read from + this URL and some data has been set, it will do a POST request. */ - URL withPOSTData (const String& postData) const; + [[nodiscard]] URL withPOSTData (const String& postData) const; /** Returns a copy of this URL, with a block of data to send as the POST data. - If you're setting the POST data, be careful not to have any parameters set - as well, otherwise it'll all get thrown in together, and might not have the - desired effect. - If the URL already contains some POST data, this will replace it, rather than being appended to it. - This data will only be used if you specify a post operation when you call - createInputStream(). + If no HTTP command is set when calling createInputStream() to read from + this URL and some data has been set, it will do a POST request. */ - URL withPOSTData (const MemoryBlock& postData) const; + [[nodiscard]] URL withPOSTData (const MemoryBlock& postData) const; /** Returns the data that was set using withPOSTData(). */ String getPostData() const { return postData.toString(); } - /** Returns the data that was set using withPOSTData() as MemoryBlock. */ + /** Returns the data that was set using withPOSTData() as a MemoryBlock. */ const MemoryBlock& getPostDataAsMemoryBlock() const noexcept { return postData; } //============================================================================== /** Tries to launch the system's default browser to open the URL. - Returns true if this seems to have worked. + + @returns true if this seems to have worked. */ bool launchInDefaultBrowser() const; @@ -291,12 +307,104 @@ class JUCE_API URL static bool isProbablyAnEmailAddress (const String& possibleEmailAddress); //============================================================================== - /** This callback function can be used by the createInputStream() method. + enum class ParameterHandling + { + inAddress, + inPostData + }; + + /** Class used to create a set of options to pass to the createInputStream() method. - It allows your app to receive progress updates during a lengthy POST operation. If you - want to continue the operation, this should return true, or false to abort. + You can chain together a series of calls to this class's methods to create + a set of whatever options you want to specify, e.g. + @code + if (auto inputStream = URL ("http://www.xyz.com/foobar") + .createInputStream (URL::InputStreamOptions (URL::ParameterHandling::inAddress) + .withConnectionTimeoutMs (1000) + .withNumRedirectsToFollow (0))) + { + ... + } + @endcode */ - using OpenStreamProgressCallback = bool (void* context, int bytesSent, int totalBytes); + class JUCE_API InputStreamOptions + { + public: + /** Constructor. + + If parameterHandling is ParameterHandling::inPostData, any URL parameters + that have been set will be transferred via the request body data. Otherwise + the parameters will be added to the URL address. + */ + explicit InputStreamOptions (ParameterHandling parameterHandling); + + //============================================================================== + /** A callback function to keep track of the operation's progress. + + This can be useful for lengthy POST operations, so that you can provide user feedback. + */ + [[nodiscard]] InputStreamOptions withProgressCallback (std::function progressCallback) const; + + /** A string that will be appended onto the headers that are used for the request. + + It must be a valid set of HTML header directives, separated by newlines. + */ + [[nodiscard]] InputStreamOptions withExtraHeaders (const String& extraHeaders) const; + + /** Specifies a timeout for the request in milliseconds. + + If 0, this will use whatever default setting the OS chooses. If a negative + number, it will be infinite. + */ + [[nodiscard]] InputStreamOptions withConnectionTimeoutMs (int connectionTimeoutMs) const; + + /** If this is non-null, all the (key, value) pairs received as headers + in the response will be stored in this array. + */ + [[nodiscard]] InputStreamOptions withResponseHeaders (StringPairArray* responseHeaders) const; + + /** If this is non-null, it will get set to the http status code, if one + is known, or 0 if a code isn't available. + */ + [[nodiscard]] InputStreamOptions withStatusCode (int* statusCode) const; + + /** Specifies the number of redirects that will be followed before returning a response. + + N.B. This will be ignored on Android which follows up to 5 redirects. + */ + [[nodiscard]] InputStreamOptions withNumRedirectsToFollow (int numRedirectsToFollow) const; + + /** Specifies which HTTP request command to use. + + If this is not set, then the command will be POST if parameterHandling is + set to ParameterHandling::inPostData or if any POST data has been specified + via withPOSTData(), withFileToUpload(), or withDataToUpload(). Otherwise it + will be GET. + */ + [[nodiscard]] InputStreamOptions withHttpRequestCmd (const String& httpRequestCmd) const; + + //============================================================================== + ParameterHandling getParameterHandling() const noexcept { return parameterHandling; } + std::function getProgressCallback() const noexcept { return progressCallback; } + String getExtraHeaders() const noexcept { return extraHeaders; } + int getConnectionTimeoutMs() const noexcept { return connectionTimeOutMs; } + StringPairArray* getResponseHeaders() const noexcept { return responseHeaders; } + int* getStatusCode() const noexcept { return statusCode; } + int getNumRedirectsToFollow() const noexcept { return numRedirectsToFollow; } + String getHttpRequestCmd() const noexcept { return httpRequestCmd; } + + private: + //============================================================================== + const ParameterHandling parameterHandling; + + std::function progressCallback = nullptr; + String extraHeaders; + int connectionTimeOutMs = 0; + StringPairArray* responseHeaders = nullptr; + int* statusCode = nullptr; + int numRedirectsToFollow = 5; + String httpRequestCmd; + }; /** Attempts to open a stream that can read from this URL. @@ -312,79 +420,96 @@ class JUCE_API URL If the URL represents a local file, then this method simply returns a FileInputStream. - @param doPostLikeRequest if true, the parameters added to this class will be transferred - via the HTTP headers which is typical for POST requests. Otherwise - the parameters will be added to the URL address. Additionally, - if the parameter httpRequestCmd is not specified (or empty) then this - parameter will determine which HTTP request command will be used - (POST or GET). - @param progressCallback if this is not a nullptr, it lets you supply a callback function - to keep track of the operation's progress. This can be useful - for lengthy POST operations, so that you can provide user feedback. - @param progressCallbackContext if a callback is specified, this value will be passed to - the function - @param extraHeaders if not empty, this string is appended onto the headers that - are used for the request. It must therefore be a valid set of HTML - header directives, separated by newlines. - @param connectionTimeOutMs if 0, this will use whatever default setting the OS chooses. If - a negative number, it will be infinite. Otherwise it specifies a - time in milliseconds. - @param responseHeaders if this is non-null, all the (key, value) pairs received as headers - in the response will be stored in this array - @param statusCode if this is non-null, it will get set to the http status code, if one - is known, or 0 if a code isn't available - @param numRedirectsToFollow specifies the number of redirects that will be followed before - returning a response (ignored for Android which follows up to 5 redirects) - @param httpRequestCmd Specify which HTTP Request to use. If this is empty, then doPostRequest - will determine the HTTP request. - @returns an input stream that the caller must delete, or a null pointer if there was an - error trying to open it. - */ - InputStream* createInputStream (bool doPostLikeRequest, - OpenStreamProgressCallback* progressCallback = nullptr, - void* progressCallbackContext = nullptr, - String extraHeaders = {}, - int connectionTimeOutMs = 0, - StringPairArray* responseHeaders = nullptr, - int* statusCode = nullptr, - int numRedirectsToFollow = 5, - String httpRequestCmd = {}) const; + @param options a set of options that will be used when opening the stream. + + @returns a valid input stream, or nullptr if there was an error trying to open it. + */ + std::unique_ptr createInputStream (const InputStreamOptions& options) const; /** Attempts to open an output stream to a URL for writing This method can only be used for certain scheme types such as local files and content:// URIs on Android. */ - OutputStream* createOutputStream() const; + std::unique_ptr createOutputStream() const; //============================================================================== - /** Represents a download task. - Returned by downloadToFile to allow querying and controlling the download task. + class DownloadTask; + + /** Used to receive callbacks for download progress. */ + struct JUCE_API DownloadTaskListener + { + virtual ~DownloadTaskListener() = default; + + /** Called when the download has finished. Be aware that this callback may + come on an arbitrary thread. + */ + virtual void finished (DownloadTask* task, bool success) = 0; + + /** Called periodically by the OS to indicate download progress. + + Beware that this callback may come on an arbitrary thread. + */ + virtual void progress (DownloadTask* task, int64 bytesDownloaded, int64 totalLength); + }; + + /** Holds options that can be specified when starting a new download + with downloadToFile(). */ - class JUCE_API DownloadTask + class DownloadTaskOptions { public: - /** Used to receive callbacks for download progress */ - struct JUCE_API Listener + String extraHeaders; + String sharedContainer; + DownloadTaskListener* listener = nullptr; + bool usePost = false; + + /** Specifies headers to add to the request. */ + [[nodiscard]] auto withExtraHeaders (String value) const { return with (&DownloadTaskOptions::extraHeaders, std::move (value)); } + + /** On iOS, specifies the container where the downloaded file will be stored. + + If you initiate a download task from inside an app extension on iOS, + you must supply this option. + + This is currently unused on other platforms. + */ + [[nodiscard]] auto withSharedContainer (String value) const { return with (&DownloadTaskOptions::sharedContainer, std::move (value)); } + + /** Specifies an observer for the download task. */ + [[nodiscard]] auto withListener (DownloadTaskListener* value) const { return with (&DownloadTaskOptions::listener, std::move (value)); } + + /** Specifies whether a post command should be used. */ + [[nodiscard]] auto withUsePost (bool value) const { return with (&DownloadTaskOptions::usePost, value); } + + private: + template + [[nodiscard]] DownloadTaskOptions with (Member&& member, Value&& value) const { - virtual ~Listener(); + auto copy = *this; + copy.*member = std::forward (value); + return copy; + } + }; - /** Called when the download has finished. Be aware that this callback may - come on an arbitrary thread. */ - virtual void finished (URL::DownloadTask* task, bool success) = 0; + /** Represents a download task. - /** Called periodically by the OS to indicate download progress. - Beware that this callback may come on an arbitrary thread. - */ - virtual void progress (URL::DownloadTask* task, int64 bytesDownloaded, int64 totalLength); - }; + Returned by downloadToFile() to allow querying and controlling the download task. + */ + class JUCE_API DownloadTask + { + public: + using Listener = DownloadTaskListener; /** Releases the resources of the download task, unregisters the listener - and cancels the download if necessary. */ + and cancels the download if necessary. + */ virtual ~DownloadTask(); - /** Returns the total length of the download task. This may return -1 if the length - was not returned by the server. */ + /** Returns the total length of the download task. + + This may return -1 if the length was not returned by the server. + */ int64 getTotalLength() const { return contentLength; } /** Returns the number of bytes that have been downloaded so far. */ @@ -394,7 +519,9 @@ class JUCE_API URL bool isFinished() const { return finished; } /** Returns the status code of the server's response. + This will only be valid after the download has finished. + @see isFinished */ int statusCode() const { return httpCode; } @@ -415,7 +542,7 @@ class JUCE_API URL private: friend class URL; - static DownloadTask* createFallbackDownloader (const URL&, const File&, const String&, Listener*, bool); + static std::unique_ptr createFallbackDownloader (const URL&, const File&, const DownloadTaskOptions&); public: #if JUCE_IOS @@ -427,6 +554,13 @@ class JUCE_API URL JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DownloadTask) }; + /** This function is replaced by a new overload accepting a DownloadTaskOptions argument. */ + [[deprecated ("Use the overload with a DownloadTaskOptions argument instead")]] + std::unique_ptr downloadToFile (const File& targetLocation, + String extraHeaders = String(), + DownloadTaskListener* listener = nullptr, + bool usePostCommand = false); + /** Download the URL to a file. This method attempts to download the URL to a given file location. @@ -436,10 +570,8 @@ class JUCE_API URL using a native OS background network task. Such tasks automatically deal with network re-connections and continuing your download while your app is suspended. */ - DownloadTask* downloadToFile (const File& targetLocation, - String extraHeaders = String(), - DownloadTask::Listener* listener = nullptr, - bool usePostCommand = false); + std::unique_ptr downloadToFile (const File& targetLocation, + const DownloadTaskOptions& options); //============================================================================== /** Tries to download the entire contents of this URL into a binary data block. @@ -450,9 +582,10 @@ class JUCE_API URL Note that on some platforms (Android, for example) it's not permitted to do any network action from the message thread, so you must only call it from a background thread. - @param destData the memory block to append the new data to - @param usePostCommand whether to use a POST command to get the data (uses - a GET command if this is false) + @param destData the memory block to append the new data to. + @param usePostCommand whether to use a POST command to get the data (uses + a GET command if this is false). + @see readEntireTextStream, readEntireXmlStream */ bool readEntireBinaryStream (MemoryBlock& destData, @@ -468,8 +601,9 @@ class JUCE_API URL Note that on some platforms (Android, for example) it's not permitted to do any network action from the message thread, so you must only call it from a background thread. - @param usePostCommand whether to use a POST command to get the data (uses - a GET command if this is false) + @param usePostCommand whether to use a POST command to get the data (uses + a GET command if this is false). + @see readEntireBinaryStream, readEntireXmlStream */ String readEntireTextStream (bool usePostCommand = false) const; @@ -479,14 +613,11 @@ class JUCE_API URL If it fails, or if the text that it reads can't be parsed as XML, this will return nullptr. - When it returns a valid XmlElement object, the caller is responsible for deleting - this object when no longer needed. - Note that on some platforms (Android, for example) it's not permitted to do any network action from the message thread, so you must only call it from a background thread. - @param usePostCommand whether to use a POST command to get the data (uses - a GET command if this is false) + @param usePostCommand whether to use a POST command to get the data (uses + a GET command if this is false). @see readEntireBinaryStream, readEntireTextStream */ @@ -500,14 +631,14 @@ class JUCE_API URL This is the opposite of removeEscapeChars(). - @param stringToAddEscapeCharsTo The string to escape. - @param isParameter If true then the string is going to be - used as a parameter, so it also encodes - '$' and ',' (which would otherwise be - legal in a URL. - @param roundBracketsAreLegal Technically round brackets are ok in URLs, - however, some servers (like AWS) also want - round brackets to be escaped. + @param stringToAddEscapeCharsTo the string to escape. + @param isParameter if true then the string is going to be + used as a parameter, so it also encodes + '$' and ',' (which would otherwise be + legal in a URL. + @param roundBracketsAreLegal technically round brackets are ok in URLs, + however, some servers (like AWS) also want + round brackets to be escaped. @see removeEscapeChars */ @@ -527,35 +658,35 @@ class JUCE_API URL static String removeEscapeChars (const String& stringToRemoveEscapeCharsFrom); /** Returns a URL without attempting to remove any embedded parameters from the string. + This may be necessary if you need to create a request that involves both POST parameters and parameters which are embedded in the URL address itself. */ static URL createWithoutParsing (const String& url); -private: //============================================================================== - friend class WebInputStream; - - String url; - MemoryBlock postData; - StringArray parameterNames, parameterValues; - - static File fileFromFileSchemeURL (const URL&); - String getDomainInternal (bool) const; - - struct Upload : public ReferenceCountedObject - { - Upload (const String&, const String&, const String&, const File&, MemoryBlock*); - String parameterName, filename, mimeType; - File file; - std::unique_ptr data; + #ifndef DOXYGEN + using OpenStreamProgressCallback = bool (void* context, int bytesSent, int totalBytes); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Upload) - }; + /** This method has been deprecated. - ReferenceCountedArray filesToUpload; + @see InputStreamOptions + */ + [[deprecated ("New code should use the method which takes an InputStreamOptions argument instead.")]] + std::unique_ptr createInputStream (bool doPostLikeRequest, + OpenStreamProgressCallback* progressCallback = nullptr, + void* progressCallbackContext = nullptr, + String extraHeaders = {}, + int connectionTimeOutMs = 0, + StringPairArray* responseHeaders = nullptr, + int* statusCode = nullptr, + int numRedirectsToFollow = 5, + String httpRequestCmd = {}) const; + #endif - #if JUCE_IOS +private: + //============================================================================== + #if JUCE_IOS struct Bookmark : public ReferenceCountedObject { using Ptr = ReferenceCountedObjectPtr; @@ -570,14 +701,42 @@ class JUCE_API URL friend void setURLBookmark (URL&, void*); friend void* getURLBookmark (URL&); - #endif + #endif + + //============================================================================== + struct Upload : public ReferenceCountedObject + { + Upload (const String&, const String&, const String&, const File&, MemoryBlock*); + String parameterName, filename, mimeType; + File file; + std::unique_ptr data; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Upload) + }; + + //============================================================================== + friend class WebInputStream; URL (const String&, int); void init(); void addParameter (const String&, const String&); - void createHeadersAndPostData (String&, MemoryBlock&) const; + bool hasBodyDataToSend() const; + void createHeadersAndPostData (String&, MemoryBlock&, bool) const; URL withUpload (Upload*) const; + static ParameterHandling toHandling (bool); + static File fileFromFileSchemeURL (const URL&); + String getDomainInternal (bool) const; + + //============================================================================== + String url; + MemoryBlock postData; + StringArray parameterNames, parameterValues; + String anchor; + + ReferenceCountedArray filesToUpload; + + //============================================================================== JUCE_LEAK_DETECTOR (URL) }; diff --git a/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.cpp b/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.cpp index 9afc8af8..891bfc1b 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -24,17 +24,16 @@ namespace juce { WebInputStream::WebInputStream (const URL& url, const bool usePost) - : pimpl (new Pimpl (*this, url, usePost)), hasCalledConnect (false) + : pimpl (std::make_unique (*this, url, usePost)) { } WebInputStream::~WebInputStream() { - delete pimpl; } WebInputStream& WebInputStream::withExtraHeaders (const String& extra) { pimpl->withExtraHeaders (extra); return *this; } -WebInputStream& WebInputStream::withCustomRequestCommand (const String& cmd) { pimpl->withCustomRequestCommand(cmd); return *this; } +WebInputStream& WebInputStream::withCustomRequestCommand (const String& cmd) { pimpl->withCustomRequestCommand (cmd); return *this; } WebInputStream& WebInputStream::withConnectionTimeout (int t) { pimpl->withConnectionTimeout (t); return *this; } WebInputStream& WebInputStream::withNumRedirectsToFollow (int num) { pimpl->withNumRedirectsToFollow (num); return *this; } StringPairArray WebInputStream::getRequestHeaders() const { return pimpl->getRequestHeaders(); } @@ -60,28 +59,45 @@ bool WebInputStream::connect (Listener* listener) StringPairArray WebInputStream::parseHttpHeaders (const String& headerData) { StringPairArray headerPairs; - StringArray headerLines = StringArray::fromLines (headerData); + auto headerLines = StringArray::fromLines (headerData); - // ignore the first line as this is the status line - for (int i = 1; i < headerLines.size(); ++i) + for (const auto& headersEntry : headerLines) { - const String& headersEntry = headerLines[i]; - if (headersEntry.isNotEmpty()) { - const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); - const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); - const String previousValue (headerPairs [key]); - headerPairs.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + const auto key = headersEntry.upToFirstOccurrenceOf (": ", false, false); + + auto value = [&headersEntry, &headerPairs, &key] + { + const auto currentValue = headersEntry.fromFirstOccurrenceOf (": ", false, false); + const auto previousValue = headerPairs [key]; + + if (previousValue.isNotEmpty()) + return previousValue + "," + currentValue; + + return currentValue; + }(); + + headerPairs.set (key, value); } } return headerPairs; } -void WebInputStream::createHeadersAndPostData (const URL& aURL, String& headers, MemoryBlock& data) +void WebInputStream::createHeadersAndPostData (const URL& aURL, + String& headers, + MemoryBlock& data, + bool addParametersToBody) +{ + aURL.createHeadersAndPostData (headers, data, addParametersToBody); +} + +bool WebInputStream::Listener::postDataSendProgress ([[maybe_unused]] WebInputStream& request, + [[maybe_unused]] int bytesSent, + [[maybe_unused]] int totalBytes) { - aURL.createHeadersAndPostData (headers, data); + return true; } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.h b/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.h index 94e353d7..07d5b275 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,35 +25,28 @@ namespace juce //============================================================================== /** - An InputStream which can be used to read from a given url. + An InputStream which can be used to read from a given URL. @tags{Core} */ -class JUCE_API WebInputStream : public InputStream +class JUCE_API WebInputStream : public InputStream { public: - /** Used to receive callbacks for data send progress */ - class JUCE_API Listener - { - public: - virtual ~Listener() = default; - - virtual bool postDataSendProgress (WebInputStream& /*request*/, int /*bytesSent*/, int /*totalBytes*/) { return true; } - }; - - /** Creates a new WebInputstream which can be used to read from a url. - - @param url The url that should be retrieved. This parameter may also contain - post data and/or parameters. - @param usePost Specifies whether a GET or a POST command should be used. This - parameter will also influence the way parameters are encoded. + /** Creates a new WebInputStream which can be used to read from a URL. + + @param url the URL that should be retrieved. This parameter may also contain + POST data and/or parameters. + @param addParametersToRequestBody specifies whether any URL parameters that have + been set will be transferred via the request body data or added + to the URL address. This will also determine whether a POST or GET + command will be used if a custom command is not set. */ - WebInputStream (const URL& url, const bool usePost); + WebInputStream (const URL& url, bool addParametersToRequestBody); + /** Destructor. */ ~WebInputStream() override; - - /** Add extra headers to http request + /** Add extra headers to the HTTP request. Returns a reference to itself so that several methods can be chained. @@ -63,76 +56,74 @@ class JUCE_API WebInputStream : public InputStream */ WebInputStream& withExtraHeaders (const String& extraHeaders); - /** Override the http command that is sent + /** Override the HTTP command that is sent. Returns a reference to itself so that several methods can be chained. Note that this command will not change the way parameters are sent. This must be specified in the constructor. - @param customRequestCommand this string is the custom http request command such - as POST or GET. + @param customRequestCommand this string is the custom HTTP request command such + as POST or GET. */ WebInputStream& withCustomRequestCommand (const String& customRequestCommand); - /** Specify the connection time-out + /** Specify the connection time-out. Returns a reference to itself so that several methods can be chained. - @param timeoutInMs the number of milliseconds to wait until the connection - request is aborted. + @param timeoutInMs the number of milliseconds to wait until the connection + request is aborted. */ WebInputStream& withConnectionTimeout (int timeoutInMs); - /** Specify the number of redirects to be followed + /** Specify the number of redirects to be followed. Returns a reference to itself so that several methods can be chained. - @param numRedirects specifies the number of redirects that will be followed - before returning a response (ignored for Android which - follows up to 5 redirects) + @param numRedirects specifies the number of redirects that will be followed + before returning a response (ignored for Android which + follows up to 5 redirects) */ WebInputStream& withNumRedirectsToFollow (int numRedirects); - /** Returns a string array pair of the request headers */ - StringPairArray getRequestHeaders() const; - - /** Returns a string array pair of response headers - - If getResponseHeaders is called without an established connection, then - getResponseHeaders will call connect internally and block until connect - returns - either due to a successful connection or a connection - error. + //============================================================================== + /** Used to receive callbacks for POST data send progress. - @see connect + Pass one of these into the connect() method and its postDataSendProgress() + method will be called periodically with updates on POST data upload progress. */ - StringPairArray getResponseHeaders(); + class JUCE_API Listener + { + public: + /** Destructor. */ + virtual ~Listener() = default; - /** Returns the status code returned by the http server + /** This method will be called periodically with updates on POST data upload progress. - If getStatusCode is called without an established connection, then - getStatusCode will call connect internally and block until connect - returns - either due to a successful connection or a connection - error. + @param request the original request + @param bytesSent the number of bytes sent so far + @param totalBytes the total number of bytes to send - @see connect - */ - int getStatusCode(); + @returns true to continue or false to cancel the upload + */ + virtual bool postDataSendProgress (WebInputStream& request, int bytesSent, int totalBytes); + }; - /** Wait until the first byte is ready for reading + /** Wait until the first byte is ready for reading. - This method will attempt to connect to the url given in the constructor + This method will attempt to connect to the URL given in the constructor and block until the status code and all response headers have been received or an error has occurred. - Note that most methods will call connect internally if they are called without + Note that most methods will call connect() internally if they are called without an established connection. Therefore, it is not necessary to explicitly call connect unless you would like to use a custom listener. - After a successful call to connect, getResponseHeaders, getTotalLength and - getStatusCode will all be non-blocking. + After a successful call to connect(), getResponseHeaders(), getTotalLength() + and getStatusCode() will all be non-blocking. - @param listener A listener to receive progress callbacks on the status + @param listener a listener to receive progress callbacks on the status of a POST data upload. @see getResponseHeaders, getTotalLength, getStatusCode @@ -145,14 +136,39 @@ class JUCE_API WebInputStream : public InputStream /** Will cancel a blocking read and prevent any subsequent connection attempts. */ void cancel(); + /** Returns a StringPairArray of the request headers. */ + StringPairArray getRequestHeaders() const; + + /** Returns a StringPairArray of response headers. + + If getResponseHeaders() is called without an established connection, then + getResponseHeaders() will call connect internally and block until connect + returns - either due to a successful connection or a connection + error. + + @see connect + */ + StringPairArray getResponseHeaders(); + + /** Returns the status code returned by the HTTP server + + If getStatusCode() is called without an established connection, then + getStatusCode() will call connect internally and block until connect + returns - either due to a successful connection or a connection + error. + + @see connect + */ + int getStatusCode(); + //============================================================================== /** Returns the total number of bytes available for reading in this stream. Note that this is the number of bytes available from the start of the stream, not from the current position. - If getTotalLength is called without an established connection, then - getTotalLength will call connect internally and block until connect + If getTotalLength() is called without an established connection, then + getTotalLength() will call connect internally and block until connect returns - either due to a successful connection or a connection error. @@ -162,9 +178,9 @@ class JUCE_API WebInputStream : public InputStream /** Reads some data from the stream into a memory buffer. - This method will block until the bytesToRead bytes are available. + This method will block until the maxBytesToRead bytes are available. - This method calls connect internally if the connection hasn't already + This method calls connect() internally if the connection hasn't already been established. @param destBuffer the destination buffer for the data. This must not be null. @@ -181,6 +197,7 @@ class JUCE_API WebInputStream : public InputStream bool isExhausted() override; /** Returns the offset of the next byte that will be read from the stream. + @see setPosition */ int64 getPosition() override; @@ -191,7 +208,7 @@ class JUCE_API WebInputStream : public InputStream For a WebInputStream, this method will fail if wantedPos is smaller than the current position. If wantedPos is greater than the current - position, then calling setPosition is the same as calling read, i.e. + 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 immediately. @@ -201,14 +218,14 @@ class JUCE_API WebInputStream : public InputStream bool setPosition (int64 wantedPos) override; private: - static void createHeadersAndPostData (const URL&, String&, MemoryBlock&); - static StringPairArray parseHttpHeaders (const String& headerData); + static void createHeadersAndPostData (const URL&, String&, MemoryBlock&, bool); + static StringPairArray parseHttpHeaders (const String&); class Pimpl; friend class Pimpl; - Pimpl* const pimpl; - bool hasCalledConnect; + std::unique_ptr pimpl; + bool hasCalledConnect = false; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; diff --git a/JuceLibraryCode/modules/juce_core/serialisation/juce_Serialisation.h b/JuceLibraryCode/modules/juce_core/serialisation/juce_Serialisation.h new file mode 100644 index 00000000..09b5c5f5 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/serialisation/juce_Serialisation.h @@ -0,0 +1,576 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +/** + Allows serialisation functions to be attached to a specific type without having to modify the + declaration of that type. + + A specialisation of SerialisationTraits must include: + - A static constexpr data member named 'marshallingVersion' with a value that is convertible + to std::optional. + - Either: + - Normally, a single function with the following signature: + @code + template + static void serialise (Archive& archive, Item& item); + @endcode + - For types that must do slightly different work when loading and saving, you may supply two + functions with the following signatures, where "T" is a placeholder for the type on which + SerialisationTraits is specialised: + @code + template + static void load (Archive& archive, T& item); + + template + static void save (Archive& archive, const T& item); + @endcode + + If the marshallingVersion converts to a null optional, then all versioning information will be + ignored when marshalling the type. Otherwise, if the value converts to a non-null optional, this + versioning information will be included when serialising the type. + + Inside serialise() and load() you may call archive.getVersion() to find the detected version + of the object being deserialised. archive.getVersion() will return an std::optional, + where 'nullopt' indicates that no versioning information was detected. + + Marshalling functions can also be specified directly inside the type to be marshalled. This + approach may be preferable as it is more concise. Internal marshalling functions are written + in exactly the same way as external ones; i.e. the type must include a marshallingVersion, + and either a single serialise function, or a load/save pair of functions, as specified above. + + @tags{Core} +*/ +template struct SerialisationTraits +{ + /* Intentionally left blank. */ +}; + +#define JUCE_COMPARISON_OPS X(==) X(!=) X(<) X(<=) X(>) X(>=) + +/** + Combines an object with a name. + + Instances of Named have reference-like semantics. That is, Named stores a reference + to a wrapped value, rather than storing the value internally. + + @tparam T the type of reference that is wrapped. Passing "const T" will cause the Named + instance to hold a "const T&"; passing "T" will cause the Named instance to + hold a "T&". + + @see named() + + @tags{Core} +*/ +template +struct Named +{ + #define X(op) auto operator op (const Named& other) const { return value op other.value; } + JUCE_COMPARISON_OPS + #undef X + + std::string_view name; ///< A name that corresponds to the value + T& value; ///< A reference to a value to wrap +}; + +/** Produces a Named instance that holds a mutable reference. */ +template constexpr auto named (std::string_view c, T& t) { return Named { c, t }; } + +/** Produces a Named instance that holds an immutable reference. */ +template constexpr auto named (std::string_view c, const T& t) { return Named { c, t }; } + +/** + Holds a reference to some kind of size value, used to indicate that an object being marshalled + is of variable size (e.g. Array, vector, map, set, etc.). + + If you need to write your own serialisation routines for a dynamically-sized type, ensure + that you archive an instance of SerialisationSize before any of the contents of the container. + + @tparam T the (probably numeric) type of the size value + + @see serialisztionSize() + + @tags{Core} +*/ +template +struct SerialisationSize +{ + #define X(op) auto operator op (const SerialisationSize& other) const { return size op other.size; } + JUCE_COMPARISON_OPS + #undef X + + T& size; +}; + +/** Produces a SerialisationSize instance that holds a mutable reference to a size value. */ +template constexpr auto serialisationSize (T& t) -> std::enable_if_t, SerialisationSize> { return { t }; } + +/** Produces a SerialisationSize instance that holds an immutable reference to a size value. */ +template constexpr auto serialisationSize (const T& t) -> std::enable_if_t, SerialisationSize> { return { t }; } + +#undef JUCE_COMPARISON_OPS + +//============================================================================== +/* + The following are specialisations of SerialisationTraits for commonly-used types. +*/ + +#ifndef DOXYGEN + +template +struct SerialisationTraits> +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void load (Archive& archive, T& t) + { + auto size = t.size(); + archive (serialisationSize (size)); + t.resize (size); + + for (auto& element : t) + archive (element); + } + + template + static void save (Archive& archive, const T& t) + { + archive (serialisationSize (t.size())); + + for (auto& element : t) + archive (element); + } +}; + +template +struct SerialisationTraits> +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void load (Archive& archive, T& t) + { + auto size = t.size(); + archive (serialisationSize (size)); + t.resize (size); + + for (auto& element : t) + archive (element); + } + + template + static void save (Archive& archive, const T& t) + { + archive (serialisationSize (t.size())); + + for (auto& element : t) + archive (element); + } +}; + +template <> +struct SerialisationTraits +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T& t) + { + archive (t.strings); + } +}; + +template +struct SerialisationTraits> +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T& t) + { + archive (named ("first", t.first), named ("second", t.second)); + } +}; + +template +struct SerialisationTraits> +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void load (Archive& archive, std::optional& t) + { + bool engaged = false; + + archive (named ("engaged", engaged)); + + if (! engaged) + return; + + t.emplace(); + archive (named ("value", *t)); + } + + template + static void save (Archive& archive, const std::optional& t) + { + archive (named ("engaged", t.has_value())); + + if (t.has_value()) + archive (named ("value", *t)); + } +}; + +template <> +struct SerialisationTraits +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void load (Archive& archive, std::string& t) + { + String temporary; + archive (temporary); + t = temporary.toStdString(); + } + + template + static void save (Archive& archive, const std::string& t) + { + archive (String (t)); + } +}; + +template +struct SerialisationTraits> +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void load (Archive& archive, T& t) + { + auto size = t.size(); + archive (serialisationSize (size)); + + for (auto i = (decltype (size)) 0; i < size; ++i) + { + std::pair element; + archive (element); + t.insert (element); + } + } + + template + static void save (Archive& archive, const T& t) + { + auto size = t.size(); + archive (serialisationSize (size)); + + for (const auto& element : t) + archive (element); + } +}; + +template +struct SerialisationTraits> +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void load (Archive& archive, T& t) + { + auto size = t.size(); + archive (serialisationSize (size)); + + for (auto i = (decltype (size)) 0; i < size; ++i) + { + typename T::value_type element; + archive (element); + t.insert (element); + } + } + + template + static void save (Archive& archive, const T& t) + { + auto size = t.size(); + archive (serialisationSize (size)); + + for (const auto& element : t) + archive (element); + } +}; + +template +struct SerialisationTraits +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void serialise (Archive& archive, T& t) { archive (String (t, N)); } +}; + +template +struct SerialisationTraits +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void load (Archive& archive, T& t) + { + auto size = N; + archive (serialisationSize (size)); + + for (auto& element : t) + archive (element); + } + + template + static void save (Archive& archive, const T& t) + { + const auto size = N; + archive (serialisationSize (size)); + + for (auto& element : t) + archive (element); + } +}; + +template +struct SerialisationTraits> +{ + static constexpr auto marshallingVersion = std::nullopt; + + template + static void load (Archive& archive, T& t) + { + auto size = N; + archive (serialisationSize (size)); + + for (auto& element : t) + archive (element); + } + + template + static void save (Archive& archive, const T& t) + { + const auto size = N; + archive (serialisationSize (size)); + + for (auto& element : t) + archive (element); + } +}; + +/* + This namespace holds utilities for detecting and using serialisation functions. + + The contents of this namespace are private, and liable to change, so you shouldn't use any of + the contents directly. +*/ +namespace detail +{ + struct DummyArchive + { + template + bool operator() (Ts&&...); + + std::optional getVersion() const { return {}; } + }; + + template + constexpr auto hasInternalVersion = false; + + template + constexpr auto hasInternalVersion> = true; + + template + constexpr auto hasInternalSerialise = false; + + template + constexpr auto hasInternalSerialise(), std::declval()))>> = true; + + template + constexpr auto hasInternalLoad = false; + + template + constexpr auto hasInternalLoad(), std::declval()))>> = true; + + template + constexpr auto hasInternalSave = false; + + template + constexpr auto hasInternalSave(), std::declval()))>> = true; + + template + struct SerialisedTypeTrait { using type = T; }; + + template + struct SerialisedTypeTrait> { using type = T; }; + + template + using SerialisedType = typename SerialisedTypeTrait::type; + + template + constexpr auto hasSerialisation = hasInternalVersion> + || hasInternalSerialise> + || hasInternalLoad> + || hasInternalSave>; + + /* Different kinds of serialisation function. */ + enum class SerialisationKind + { + none, // The type doesn't have any serialisation + primitive, // The type has serialisation handling defined directly on the archiver. enums will be converted to equivalent integral values + internal, // The type has internally-defined serialisation utilities + external, // The type has an external specialisation of SerialisationTraits + }; + + /* The SerialisationKind to use for the type T. + + Primitive serialisation is used for arithmetic types, enums, Strings, and vars. + Internal serialisation is used for types that declare an internal marshallingVersion, + serialise(), load(), or save(). + External serialisation is used in all other cases. + */ + template + constexpr auto serialisationKind = [] + { + if constexpr (std::is_arithmetic_v || std::is_enum_v || std::is_same_v || std::is_same_v) + return SerialisationKind::primitive; + else if constexpr (hasSerialisation) + return SerialisationKind::internal; + else if constexpr (hasSerialisation>) + return SerialisationKind::external; + else + return SerialisationKind::none; + }(); + + /* This trait defines the serialisation utilities that are used for primitive types. */ + template > + struct ForwardingSerialisationTraits + { + static constexpr auto marshallingVersion = std::nullopt; + + template + static auto load (Archive& archive, Primitive& t) + { + if constexpr (std::is_enum_v) + return archive (*reinterpret_cast*> (&t)); + else + return archive (t); + } + + template + static auto save (Archive& archive, const Primitive& t) + { + if constexpr (std::is_enum_v) + return archive (*reinterpret_cast*> (&t)); + else + return archive (t); + } + }; + + /* This specialisation will be used for types with internal serialisation. + + All members of ForwardingSerialisationTraits forward to the corresponding member of T. + */ + template + struct ForwardingSerialisationTraits + { + static constexpr std::optional marshallingVersion { T::marshallingVersion }; + + template + static auto serialise (Archive& archive, Item& t) -> decltype (Item::serialise (archive, t)) { return Item::serialise (archive, t); } + + template + static auto load (Archive& archive, Item& t) -> decltype (Item::load (archive, t)) { return Item::load (archive, t); } + + template + static auto save (Archive& archive, const Item& t) -> decltype (Item::save (archive, t)) { return Item::save (archive, t); } + }; + + /* This specialisation will be used for types with external serialisation. + + @see SerialisationTraits + */ + template + struct ForwardingSerialisationTraits : SerialisationTraits {}; + + template + constexpr auto hasSerialise = false; + + template + constexpr auto hasSerialise::serialise (std::declval(), std::declval()))>> = true; + + template + constexpr auto hasLoad = false; + + template + constexpr auto hasLoad::load (std::declval(), std::declval()))>> = true; + + template + constexpr auto hasSave = false; + + template + constexpr auto hasSave::save (std::declval(), std::declval()))>> = true; + + template + constexpr auto delayStaticAssert = false; + + /* Calls the correct function (serialise or save) to save the argument t to the archive. + */ + template + auto doSave (Archive& archive, const T& t) + { + if constexpr (serialisationKind == SerialisationKind::none) + static_assert (delayStaticAssert, "No serialisation function found or marshallingVersion unset"); + else if constexpr (hasSerialise && ! hasSave) + return ForwardingSerialisationTraits::serialise (archive, t); + else if constexpr (! hasSerialise && hasSave) + return ForwardingSerialisationTraits::save (archive, t); + else + static_assert (delayStaticAssert, "Multiple serialisation functions found"); + } + + /* Calls the correct function (serialise or load) to load the argument t from the archive. + */ + template + auto doLoad (Archive& archive, T& t) + { + if constexpr (serialisationKind == SerialisationKind::none) + static_assert (delayStaticAssert, "No serialisation function found or marshallingVersion unset"); + else if constexpr (hasSerialise && ! hasLoad) + return ForwardingSerialisationTraits::serialise (archive, t); + else if constexpr (! hasSerialise && hasLoad) + return ForwardingSerialisationTraits::load (archive, t); + else + static_assert (delayStaticAssert, "Multiple serialisation functions found"); + } +} // namespace detail + +#endif + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_AndroidDocumentInputSource.h b/JuceLibraryCode/modules/juce_core/streams/juce_AndroidDocumentInputSource.h new file mode 100644 index 00000000..33adf444 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/streams/juce_AndroidDocumentInputSource.h @@ -0,0 +1,79 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +//============================================================================== +/** + An InputSource backed by an AndroidDocument. + + @see InputSource, AndroidDocument + + @tags{Core} +*/ +class JUCE_API AndroidDocumentInputSource : public InputSource +{ +public: + //============================================================================== + /** Creates a new AndroidDocumentInputSource, backed by the provided document. + */ + explicit AndroidDocumentInputSource (const AndroidDocument& doc) + : document (doc) {} + + //============================================================================== + /** Returns a new InputStream to read this item. + + @returns an inputstream that the caller will delete, or nullptr if + the document can't be opened. + */ + InputStream* createInputStream() override + { + return document.createInputStream().release(); + } + + /** @internal + + An AndroidDocument doesn't use conventional filesystem paths. + Use the member functions of AndroidDocument to locate relative items. + + @param relatedItemPath the relative pathname of the resource that is required + @returns an input stream if relatedItemPath was empty, otherwise + nullptr. + */ + InputStream* createInputStreamFor (const String& relatedItemPath) override + { + return relatedItemPath.isEmpty() ? document.createInputStream().release() : nullptr; + } + + /** Returns a hash code that uniquely represents this item. + */ + int64 hashCode() const override + { + return document.getUrl().toString (true).hashCode64(); + } + +private: + AndroidDocument document; +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp index 69dac4df..5ea063ed 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,7 +23,7 @@ namespace juce { -static inline int calcBufferStreamBufferSize (int requestedSize, InputStream* source) noexcept +static int calcBufferStreamBufferSize (int requestedSize, InputStream* source) noexcept { // You need to supply a real stream when creating a BufferedInputStream jassert (source != nullptr); @@ -39,22 +39,20 @@ static inline int calcBufferStreamBufferSize (int requestedSize, InputStream* so //============================================================================== BufferedInputStream::BufferedInputStream (InputStream* sourceStream, int size, bool takeOwnership) - : source (sourceStream, takeOwnership), - bufferSize (calcBufferStreamBufferSize (size, sourceStream)), - position (sourceStream->getPosition()), - bufferStart (position) + : source (sourceStream, takeOwnership), + bufferedRange (sourceStream->getPosition(), sourceStream->getPosition()), + position (bufferedRange.getStart()), + bufferLength (calcBufferStreamBufferSize (size, sourceStream)) { - buffer.malloc (bufferSize); + buffer.malloc (bufferLength); } BufferedInputStream::BufferedInputStream (InputStream& sourceStream, int size) - : BufferedInputStream (&sourceStream, size, false) + : BufferedInputStream (&sourceStream, size, false) { } -BufferedInputStream::~BufferedInputStream() -{ -} +BufferedInputStream::~BufferedInputStream() = default; //============================================================================== char BufferedInputStream::peekByte() @@ -62,7 +60,7 @@ char BufferedInputStream::peekByte() if (! ensureBuffered()) return 0; - return position < lastReadPos ? buffer[(int) (position - bufferStart)] : 0; + return position < lastReadPos ? buffer[(int) (position - bufferedRange.getStart())] : 0; } int64 BufferedInputStream::getTotalLength() @@ -90,20 +88,19 @@ bool BufferedInputStream::ensureBuffered() { auto bufferEndOverlap = lastReadPos - bufferOverlap; - if (position < bufferStart || position >= bufferEndOverlap) + if (position < bufferedRange.getStart() || position >= bufferEndOverlap) { - int bytesRead; + int bytesRead = 0; if (position < lastReadPos && position >= bufferEndOverlap - && position >= bufferStart) + && position >= bufferedRange.getStart()) { auto bytesToKeep = (int) (lastReadPos - position); - memmove (buffer, buffer + (int) (position - bufferStart), (size_t) bytesToKeep); + memmove (buffer, buffer + (int) (position - bufferedRange.getStart()), (size_t) bytesToKeep); - bufferStart = position; bytesRead = source->read (buffer + bytesToKeep, - (int) (bufferSize - bytesToKeep)); + (int) (bufferLength - bytesToKeep)); if (bytesRead < 0) return false; @@ -113,75 +110,62 @@ bool BufferedInputStream::ensureBuffered() } else { - bufferStart = position; - - if (! source->setPosition (bufferStart)) + if (! source->setPosition (position)) return false; - bytesRead = source->read (buffer, bufferSize); + bytesRead = (int) source->read (buffer, (size_t) bufferLength); if (bytesRead < 0) return false; - lastReadPos = bufferStart + bytesRead; + lastReadPos = position + bytesRead; } - while (bytesRead < bufferSize) + bufferedRange = Range (position, lastReadPos); + + while (bytesRead < bufferLength) buffer[bytesRead++] = 0; } return true; } -int BufferedInputStream::read (void* destBuffer, int maxBytesToRead) +int BufferedInputStream::read (void* destBuffer, const int maxBytesToRead) { - jassert (destBuffer != nullptr && maxBytesToRead >= 0); + const auto initialPosition = position; - if (position >= bufferStart - && position + maxBytesToRead <= lastReadPos) - { - memcpy (destBuffer, buffer + (int) (position - bufferStart), (size_t) maxBytesToRead); - position += maxBytesToRead; - return maxBytesToRead; - } + const auto getBufferedRange = [this] { return bufferedRange; }; - if (position < bufferStart || position >= lastReadPos) - if (! ensureBuffered()) - return 0; - - int bytesRead = 0; - - while (maxBytesToRead > 0) + const auto readFromReservoir = [this, &destBuffer, &initialPosition] (const Range rangeToRead) { - auto numToRead = jmin (maxBytesToRead, (int) (lastReadPos - position)); - - if (numToRead > 0) - { - memcpy (destBuffer, buffer + (int) (position - bufferStart), (size_t) numToRead); - maxBytesToRead -= numToRead; - bytesRead += numToRead; - position += numToRead; - destBuffer = static_cast (destBuffer) + numToRead; - } + memcpy (static_cast (destBuffer) + (rangeToRead.getStart() - initialPosition), + buffer + (rangeToRead.getStart() - bufferedRange.getStart()), + (size_t) rangeToRead.getLength()); + }; - auto oldLastReadPos = lastReadPos; - - if (! ensureBuffered() - || oldLastReadPos == lastReadPos - || isExhausted()) - break; - } - - return bytesRead; + const auto fillReservoir = [this] (int64 requestedStart) + { + position = requestedStart; + ensureBuffered(); + }; + + const auto remaining = Reservoir::doBufferedRead (Range (position, position + maxBytesToRead), + getBufferedRange, + readFromReservoir, + fillReservoir); + + const auto bytesRead = maxBytesToRead - remaining.getLength(); + position = remaining.getStart(); + return (int) bytesRead; } String BufferedInputStream::readString() { - if (position >= bufferStart + if (position >= bufferedRange.getStart() && position < lastReadPos) { auto maxChars = (int) (lastReadPos - position); - auto* src = buffer + (int) (position - bufferStart); + auto* src = buffer + (int) (position - bufferedRange.getStart()); for (int i = 0; i < maxChars; ++i) { @@ -201,73 +185,133 @@ String BufferedInputStream::readString() //============================================================================== #if JUCE_UNIT_TESTS -struct BufferedInputStreamTests : public UnitTest +struct BufferedInputStreamTests final : public UnitTest { + template + static void applyImpl (Fn&& fn, std::index_sequence, Values&& values) + { + fn (std::get (values)...); + } + + template + static void apply (Fn&& fn, std::tuple values) + { + applyImpl (fn, std::make_index_sequence(), values); + } + + template + static void allCombinationsImpl (Fn&& fn, Values&& values) + { + apply (fn, values); + } + + template + static void allCombinationsImpl (Fn&& fn, Values&& values, Range&& range, Ranges&&... ranges) + { + for (auto& item : range) + allCombinationsImpl (fn, std::tuple_cat (values, std::tie (item)), ranges...); + } + + template + static void allCombinations (Fn&& fn, Ranges&&... ranges) + { + allCombinationsImpl (fn, std::tie(), ranges...); + } + BufferedInputStreamTests() : UnitTest ("BufferedInputStream", UnitTestCategories::streams) {} void runTest() override { - const MemoryBlock data ("abcdefghijklmnopqrstuvwxyz", 26); - MemoryInputStream mi (data, true); + const MemoryBlock testBufferA ("abcdefghijklmnopqrstuvwxyz", 26); - BufferedInputStream stream (mi, (int) data.getSize()); + const auto testBufferB = [&] + { + MemoryBlock mb { 8192 }; + auto r = getRandom(); - beginTest ("Read"); + std::for_each (mb.begin(), mb.end(), [&] (char& item) + { + item = (char) r.nextInt (std::numeric_limits::max()); + }); - expectEquals (stream.getPosition(), (int64) 0); - expectEquals (stream.getTotalLength(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); - expect (! stream.isExhausted()); + return mb; + }(); - size_t numBytesRead = 0; - MemoryBlock readBuffer (data.getSize()); + const MemoryBlock buffers[] { testBufferA, testBufferB }; + const int readSizes[] { 3, 10, 50 }; + const bool shouldPeek[] { false, true }; - while (numBytesRead < data.getSize()) + const auto runTest = [this] (const MemoryBlock& data, const int readSize, const bool peek) { - expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead)); + MemoryInputStream mi (data, true); - numBytesRead += (size_t) stream.read (&readBuffer[numBytesRead], 3); + BufferedInputStream stream (mi, jmin (200, (int) data.getSize())); - expectEquals (stream.getPosition(), (int64) numBytesRead); - expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); - expect (stream.isExhausted() == (numBytesRead == data.getSize())); - } + beginTest ("Read"); - expectEquals (stream.getPosition(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), (int64) 0); - expect (stream.isExhausted()); + expectEquals (stream.getPosition(), (int64) 0); + expectEquals (stream.getTotalLength(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); + expect (! stream.isExhausted()); - expect (readBuffer == data); + size_t numBytesRead = 0; + MemoryBlock readBuffer (data.getSize()); - beginTest ("Skip"); + while (numBytesRead < data.getSize()) + { + if (peek) + expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead)); + + const auto startingPos = numBytesRead; + numBytesRead += (size_t) stream.read (readBuffer.begin() + numBytesRead, readSize); + + expect (std::equal (readBuffer.begin() + startingPos, + readBuffer.begin() + numBytesRead, + data.begin() + startingPos, + data.begin() + numBytesRead)); + expectEquals (stream.getPosition(), (int64) numBytesRead); + expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); + expect (stream.isExhausted() == (numBytesRead == data.getSize())); + } - stream.setPosition (0); - expectEquals (stream.getPosition(), (int64) 0); - expectEquals (stream.getTotalLength(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); - expect (! stream.isExhausted()); + expectEquals (stream.getPosition(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), (int64) 0); + expect (stream.isExhausted()); - numBytesRead = 0; - const int numBytesToSkip = 5; + expect (readBuffer == data); - while (numBytesRead < data.getSize()) - { - expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead)); + beginTest ("Skip"); - stream.skipNextBytes (numBytesToSkip); - numBytesRead += numBytesToSkip; - numBytesRead = std::min (numBytesRead, data.getSize()); + stream.setPosition (0); + expectEquals (stream.getPosition(), (int64) 0); + expectEquals (stream.getTotalLength(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), stream.getTotalLength()); + expect (! stream.isExhausted()); - expectEquals (stream.getPosition(), (int64) numBytesRead); - expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); - expect (stream.isExhausted() == (numBytesRead == data.getSize())); - } + numBytesRead = 0; + const int numBytesToSkip = 5; + + while (numBytesRead < data.getSize()) + { + expectEquals (stream.peekByte(), *(char*) (data.begin() + numBytesRead)); + + stream.skipNextBytes (numBytesToSkip); + numBytesRead += numBytesToSkip; + numBytesRead = std::min (numBytesRead, data.getSize()); + + expectEquals (stream.getPosition(), (int64) numBytesRead); + expectEquals (stream.getNumBytesRemaining(), (int64) (data.getSize() - numBytesRead)); + expect (stream.isExhausted() == (numBytesRead == data.getSize())); + } + + expectEquals (stream.getPosition(), (int64) data.getSize()); + expectEquals (stream.getNumBytesRemaining(), (int64) 0); + expect (stream.isExhausted()); + }; - expectEquals (stream.getPosition(), (int64) data.getSize()); - expectEquals (stream.getNumBytesRemaining(), (int64) 0); - expect (stream.isExhausted()); + allCombinations (runTest, buffers, readSizes, shouldPeek); } }; diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h index 38927902..7a265b62 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -79,8 +79,8 @@ class JUCE_API BufferedInputStream : public InputStream private: //============================================================================== OptionalScopedPointer source; - int bufferSize; - int64 position, lastReadPos = 0, bufferStart, bufferOverlap = 128; + Range bufferedRange; + int64 position, bufferLength, lastReadPos = 0, bufferOverlap = 128; HeapBlock buffer; bool ensureBuffered(); diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.cpp index 50558097..3174f560 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -34,12 +34,12 @@ FileInputSource::~FileInputSource() InputStream* FileInputSource::createInputStream() { - return file.createInputStream(); + return file.createInputStream().release(); } InputStream* FileInputSource::createInputStreamFor (const String& relatedItemPath) { - return file.getSiblingFile (relatedItemPath).createInputStream(); + return file.getSiblingFile (relatedItemPath).createInputStream().release(); } int64 FileInputSource::hashCode() const diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.h b/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.h index f1cce972..bab47cd4 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_InputSource.h b/JuceLibraryCode/modules/juce_core/streams/juce_InputSource.h index 9c4e293d..9a9c435e 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_InputSource.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_InputSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp index fc81f97f..552320f7 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h index 4dde935f..40037223 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp index 9f563d5e..ca11dd34 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -49,11 +49,10 @@ MemoryInputStream::MemoryInputStream (MemoryBlock&& source) : internalCopy (std::move (source)) { data = internalCopy.getData(); + dataSize = internalCopy.getSize(); } -MemoryInputStream::~MemoryInputStream() -{ -} +MemoryInputStream::~MemoryInputStream() = default; int64 MemoryInputStream::getTotalLength() { @@ -105,7 +104,7 @@ void MemoryInputStream::skipNextBytes (int64 numBytesToSkip) //============================================================================== #if JUCE_UNIT_TESTS -class MemoryStreamTests : public UnitTest +class MemoryStreamTests final : public UnitTest { public: MemoryStreamTests() @@ -139,8 +138,8 @@ class MemoryStreamTests : public UnitTest expectEquals (mi.readString(), randomString); expect (mi.readInt64() == randomInt64); expect (mi.readInt64BigEndian() == randomInt64); - expect (mi.readDouble() == randomDouble); - expect (mi.readDoubleBigEndian() == randomDouble); + expectEquals (mi.readDouble(), randomDouble); + expectEquals (mi.readDoubleBigEndian(), randomDouble); const MemoryBlock data ("abcdefghijklmnopqrstuvwxyz", 26); MemoryInputStream stream (data, true); diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h index 12bc26db..08c50f40 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.cpp index e498d31a..d2bb8bf4 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -172,15 +172,15 @@ bool MemoryOutputStream::setPosition (int64 newPosition) int64 MemoryOutputStream::writeFromInputStream (InputStream& source, int64 maxNumBytesToWrite) { // before writing from an input, see if we can preallocate to make it more efficient.. - int64 availableData = source.getTotalLength() - source.getPosition(); + const auto availableData = source.getTotalLength() - source.getPosition(); if (availableData > 0) { - if (maxNumBytesToWrite > availableData || maxNumBytesToWrite < 0) + if (maxNumBytesToWrite < 0 || availableData < maxNumBytesToWrite) maxNumBytesToWrite = availableData; if (blockToUse != nullptr) - preallocate (blockToUse->getSize() + (size_t) maxNumBytesToWrite); + preallocate (position + (size_t) maxNumBytesToWrite); } return OutputStream::writeFromInputStream (source, maxNumBytesToWrite); diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h index 7af1ba6f..12dc5788 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp index 231f5fb3..cdacf33c 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -364,7 +364,7 @@ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const cha JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock& data) { - if (data.getSize() > 0) + if (! data.isEmpty()) stream.write (data.getData(), data.getSize()); return stream; diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.h index 84a51c65..b64e0e01 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp index 28a925dd..8b27e908 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -83,7 +83,7 @@ bool SubregionStream::isExhausted() //============================================================================== #if JUCE_UNIT_TESTS -struct SubregionInputStreamTests : public UnitTest +struct SubregionInputStreamTests final : public UnitTest { SubregionInputStreamTests() : UnitTest ("SubregionInputStream", UnitTestCategories::streams) diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.h index afc12eac..10a9b4bd 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.cpp index c257faf0..83fb49a9 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -39,16 +39,19 @@ URLInputSource::~URLInputSource() InputStream* URLInputSource::createInputStream() { - return u.createInputStream (false); + return u.createInputStream (URL::InputStreamOptions (URL::ParameterHandling::inAddress)).release(); } InputStream* URLInputSource::createInputStreamFor (const String& relatedItemPath) { auto sub = u.getSubPath(); auto parent = sub.containsChar (L'/') ? sub.upToLastOccurrenceOf ("/", false, false) - : String (); + : String(); - return u.withNewSubPath (parent).getChildURL (relatedItemPath).createInputStream (false); + return u.withNewSubPath (parent) + .getChildURL (relatedItemPath) + .createInputStream (URL::InputStreamOptions (URL::ParameterHandling::inAddress)) + .release(); } int64 URLInputSource::hashCode() const diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.h b/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.h index e9d88e0e..21ebac23 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_URLInputSource.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h b/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h index 8e530409..77c57345 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_CompilerSupport.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -30,16 +30,8 @@ // GCC #if JUCE_GCC - #if (__GNUC__ * 100 + __GNUC_MINOR__) < 407 - #error "JUCE requires GCC 4.7 or later" - #endif - - #if ! (__cplusplus >= 201103L || defined (__GXX_EXPERIMENTAL_CXX0X__)) - #error "JUCE requires that GCC has C++11 compatibility enabled" - #endif - - #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 500 - #define JUCE_HAS_CONSTEXPR 1 + #if (__GNUC__ * 100 + __GNUC_MINOR__) < 700 + #error "JUCE requires GCC 7.0 or later" #endif #ifndef JUCE_EXCEPTIONS_DISABLED @@ -48,7 +40,7 @@ #endif #endif - #define JUCE_CXX14_IS_AVAILABLE ((__cplusplus >= 201402L) || ((__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && (__cplusplus >= 201300L))) + #define JUCE_CXX14_IS_AVAILABLE (__cplusplus >= 201402L) #define JUCE_CXX17_IS_AVAILABLE (__cplusplus >= 201703L) #endif @@ -57,12 +49,10 @@ // Clang #if JUCE_CLANG - #if (__clang_major__ < 3) || (__clang_major__ == 3 && __clang_minor__ < 3) - #error "JUCE requires Clang 3.3 or later" + #if (__clang_major__ < 6) + #error "JUCE requires Clang 6 or later" #endif - #define JUCE_HAS_CONSTEXPR 1 - #ifndef JUCE_COMPILER_SUPPORTS_ARC #define JUCE_COMPILER_SUPPORTS_ARC 1 #endif @@ -73,6 +63,15 @@ #endif #endif + #if ! defined (JUCE_SILENCE_XCODE_15_LINKER_WARNING) \ + && defined (__apple_build_version__) \ + && __apple_build_version__ >= 15000000 \ + && __apple_build_version__ < 15000100 + + // Due to known issues, the linker in Xcode 15.0 may produce broken binaries. + #error Please upgrade to Xcode 15.1 or higher + #endif + #define JUCE_CXX14_IS_AVAILABLE (__cplusplus >= 201402L) #define JUCE_CXX17_IS_AVAILABLE (__cplusplus >= 201703L) @@ -82,12 +81,10 @@ // MSVC #if JUCE_MSVC - #if _MSC_VER < 1900 // VS2015 - #error "JUCE requires Visual Studio 2015 or later" + #if _MSC_FULL_VER < 191025017 // VS2017 + #error "JUCE requires Visual Studio 2017 or later" #endif - #define JUCE_HAS_CONSTEXPR 1 - #ifndef JUCE_EXCEPTIONS_DISABLED #if ! _CPPUNWIND #define JUCE_EXCEPTIONS_DISABLED 1 @@ -99,34 +96,18 @@ #endif //============================================================================== -// C++ library -#if (defined (__GLIBCXX__) && __GLIBCXX__ < 20130322) || (defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION < 3700)) - #error "JUCE requires a C++ library containing std::atomic" +#if ! JUCE_CXX17_IS_AVAILABLE + #error "JUCE requires C++17 or later" #endif //============================================================================== -#if JUCE_HAS_CONSTEXPR - #define JUCE_CONSTEXPR constexpr -#else - #define JUCE_CONSTEXPR -#endif - -#if (! JUCE_MSVC) && (! JUCE_CXX14_IS_AVAILABLE) -namespace std -{ - template - unique_ptr make_unique (Args&&... args) - { - return unique_ptr (new T (std::forward (args)...)); - } -} -#endif - -#if ! DOXYGEN +#ifndef DOXYGEN // These are old flags that are now supported on all compatible build targets #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 + #define JUCE_CONSTEXPR constexpr + #define JUCE_NODISCARD [[nodiscard]] #endif diff --git a/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h b/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h new file mode 100644 index 00000000..f8b819ad --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h @@ -0,0 +1,242 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#pragma once + +#include "juce_TargetPlatform.h" + +/** Return the Nth argument. By passing a variadic pack followed by N other + parameters, we can select one of those N parameter based on the length of + the parameter pack. +*/ +#define JUCE_NTH_ARG_(_00, _01, _02, _03, _04, _05, _06, _07, _08, _09, \ + _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, \ + _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, N, ...)\ + N + +#define JUCE_EACH_00_(FN) +#define JUCE_EACH_01_(FN, X) FN(X) +#define JUCE_EACH_02_(FN, X, ...) FN(X) JUCE_EACH_01_(FN, __VA_ARGS__) +#define JUCE_EACH_03_(FN, X, ...) FN(X) JUCE_EACH_02_(FN, __VA_ARGS__) +#define JUCE_EACH_04_(FN, X, ...) FN(X) JUCE_EACH_03_(FN, __VA_ARGS__) +#define JUCE_EACH_05_(FN, X, ...) FN(X) JUCE_EACH_04_(FN, __VA_ARGS__) +#define JUCE_EACH_06_(FN, X, ...) FN(X) JUCE_EACH_05_(FN, __VA_ARGS__) +#define JUCE_EACH_07_(FN, X, ...) FN(X) JUCE_EACH_06_(FN, __VA_ARGS__) +#define JUCE_EACH_08_(FN, X, ...) FN(X) JUCE_EACH_07_(FN, __VA_ARGS__) +#define JUCE_EACH_09_(FN, X, ...) FN(X) JUCE_EACH_08_(FN, __VA_ARGS__) +#define JUCE_EACH_10_(FN, X, ...) FN(X) JUCE_EACH_09_(FN, __VA_ARGS__) +#define JUCE_EACH_11_(FN, X, ...) FN(X) JUCE_EACH_10_(FN, __VA_ARGS__) +#define JUCE_EACH_12_(FN, X, ...) FN(X) JUCE_EACH_11_(FN, __VA_ARGS__) +#define JUCE_EACH_13_(FN, X, ...) FN(X) JUCE_EACH_12_(FN, __VA_ARGS__) +#define JUCE_EACH_14_(FN, X, ...) FN(X) JUCE_EACH_13_(FN, __VA_ARGS__) +#define JUCE_EACH_15_(FN, X, ...) FN(X) JUCE_EACH_14_(FN, __VA_ARGS__) +#define JUCE_EACH_16_(FN, X, ...) FN(X) JUCE_EACH_15_(FN, __VA_ARGS__) +#define JUCE_EACH_17_(FN, X, ...) FN(X) JUCE_EACH_16_(FN, __VA_ARGS__) +#define JUCE_EACH_18_(FN, X, ...) FN(X) JUCE_EACH_17_(FN, __VA_ARGS__) +#define JUCE_EACH_19_(FN, X, ...) FN(X) JUCE_EACH_18_(FN, __VA_ARGS__) +#define JUCE_EACH_20_(FN, X, ...) FN(X) JUCE_EACH_19_(FN, __VA_ARGS__) +#define JUCE_EACH_21_(FN, X, ...) FN(X) JUCE_EACH_20_(FN, __VA_ARGS__) +#define JUCE_EACH_22_(FN, X, ...) FN(X) JUCE_EACH_21_(FN, __VA_ARGS__) +#define JUCE_EACH_23_(FN, X, ...) FN(X) JUCE_EACH_22_(FN, __VA_ARGS__) +#define JUCE_EACH_24_(FN, X, ...) FN(X) JUCE_EACH_23_(FN, __VA_ARGS__) +#define JUCE_EACH_25_(FN, X, ...) FN(X) JUCE_EACH_24_(FN, __VA_ARGS__) +#define JUCE_EACH_26_(FN, X, ...) FN(X) JUCE_EACH_25_(FN, __VA_ARGS__) +#define JUCE_EACH_27_(FN, X, ...) FN(X) JUCE_EACH_26_(FN, __VA_ARGS__) +#define JUCE_EACH_28_(FN, X, ...) FN(X) JUCE_EACH_27_(FN, __VA_ARGS__) +#define JUCE_EACH_29_(FN, X, ...) FN(X) JUCE_EACH_28_(FN, __VA_ARGS__) +#define JUCE_EACH_30_(FN, X, ...) FN(X) JUCE_EACH_29_(FN, __VA_ARGS__) +#define JUCE_EACH_31_(FN, X, ...) FN(X) JUCE_EACH_30_(FN, __VA_ARGS__) +#define JUCE_EACH_32_(FN, X, ...) FN(X) JUCE_EACH_31_(FN, __VA_ARGS__) +#define JUCE_EACH_33_(FN, X, ...) FN(X) JUCE_EACH_32_(FN, __VA_ARGS__) +#define JUCE_EACH_34_(FN, X, ...) FN(X) JUCE_EACH_33_(FN, __VA_ARGS__) +#define JUCE_EACH_35_(FN, X, ...) FN(X) JUCE_EACH_34_(FN, __VA_ARGS__) +#define JUCE_EACH_36_(FN, X, ...) FN(X) JUCE_EACH_35_(FN, __VA_ARGS__) +#define JUCE_EACH_37_(FN, X, ...) FN(X) JUCE_EACH_36_(FN, __VA_ARGS__) +#define JUCE_EACH_38_(FN, X, ...) FN(X) JUCE_EACH_37_(FN, __VA_ARGS__) +#define JUCE_EACH_39_(FN, X, ...) FN(X) JUCE_EACH_38_(FN, __VA_ARGS__) +#define JUCE_EACH_40_(FN, X, ...) FN(X) JUCE_EACH_39_(FN, __VA_ARGS__) +#define JUCE_EACH_41_(FN, X, ...) FN(X) JUCE_EACH_40_(FN, __VA_ARGS__) +#define JUCE_EACH_42_(FN, X, ...) FN(X) JUCE_EACH_41_(FN, __VA_ARGS__) +#define JUCE_EACH_43_(FN, X, ...) FN(X) JUCE_EACH_42_(FN, __VA_ARGS__) +#define JUCE_EACH_44_(FN, X, ...) FN(X) JUCE_EACH_43_(FN, __VA_ARGS__) +#define JUCE_EACH_45_(FN, X, ...) FN(X) JUCE_EACH_44_(FN, __VA_ARGS__) +#define JUCE_EACH_46_(FN, X, ...) FN(X) JUCE_EACH_45_(FN, __VA_ARGS__) +#define JUCE_EACH_47_(FN, X, ...) FN(X) JUCE_EACH_46_(FN, __VA_ARGS__) +#define JUCE_EACH_48_(FN, X, ...) FN(X) JUCE_EACH_47_(FN, __VA_ARGS__) +#define JUCE_EACH_49_(FN, X, ...) FN(X) JUCE_EACH_48_(FN, __VA_ARGS__) + +/** Apply the macro FN to each of the other arguments. */ +#define JUCE_EACH(FN, ...) \ + JUCE_NTH_ARG_(, __VA_ARGS__, \ + JUCE_EACH_49_, \ + JUCE_EACH_48_, \ + JUCE_EACH_47_, \ + JUCE_EACH_46_, \ + JUCE_EACH_45_, \ + JUCE_EACH_44_, \ + JUCE_EACH_43_, \ + JUCE_EACH_42_, \ + JUCE_EACH_41_, \ + JUCE_EACH_40_, \ + JUCE_EACH_39_, \ + JUCE_EACH_38_, \ + JUCE_EACH_37_, \ + JUCE_EACH_36_, \ + JUCE_EACH_35_, \ + JUCE_EACH_34_, \ + JUCE_EACH_33_, \ + JUCE_EACH_32_, \ + JUCE_EACH_31_, \ + JUCE_EACH_30_, \ + JUCE_EACH_29_, \ + JUCE_EACH_28_, \ + JUCE_EACH_27_, \ + JUCE_EACH_26_, \ + JUCE_EACH_25_, \ + JUCE_EACH_24_, \ + JUCE_EACH_23_, \ + JUCE_EACH_22_, \ + JUCE_EACH_21_, \ + JUCE_EACH_20_, \ + JUCE_EACH_19_, \ + JUCE_EACH_18_, \ + JUCE_EACH_17_, \ + JUCE_EACH_16_, \ + JUCE_EACH_15_, \ + JUCE_EACH_14_, \ + JUCE_EACH_13_, \ + JUCE_EACH_12_, \ + JUCE_EACH_11_, \ + JUCE_EACH_10_, \ + JUCE_EACH_09_, \ + JUCE_EACH_08_, \ + JUCE_EACH_07_, \ + JUCE_EACH_06_, \ + JUCE_EACH_05_, \ + JUCE_EACH_04_, \ + JUCE_EACH_03_, \ + JUCE_EACH_02_, \ + JUCE_EACH_01_, \ + JUCE_EACH_00_) \ + (FN, __VA_ARGS__) + +/** Concatenate two tokens to form a new token. */ +#define JUCE_CONCAT_(a, b) a##b +#define JUCE_CONCAT(a, b) JUCE_CONCAT_(a, b) + +/** Quote the argument, turning it into a string. */ +#define JUCE_TO_STRING(x) #x + +#if JUCE_CLANG || JUCE_GCC || JUCE_MINGW + #define JUCE_IGNORE_GCC_IMPL_(compiler, warning) + #define JUCE_IGNORE_GCC_IMPL_0(compiler, warning) + #define JUCE_IGNORE_GCC_IMPL_1(compiler, warning) \ + _Pragma(JUCE_TO_STRING(compiler diagnostic ignored warning)) + + /** If 'warning' is recognised by this compiler, ignore it. */ + #if defined (__has_warning) + #define JUCE_IGNORE_GCC_LIKE(compiler, warning) \ + JUCE_CONCAT(JUCE_IGNORE_GCC_IMPL_, __has_warning(warning))(compiler, warning) + #else + #define JUCE_IGNORE_GCC_LIKE(compiler, warning) \ + JUCE_IGNORE_GCC_IMPL_1(compiler, warning) + #endif + + /** Ignore GCC/clang-specific warnings. */ + #define JUCE_IGNORE_GCC(warning) JUCE_IGNORE_GCC_LIKE(GCC, warning) + #define JUCE_IGNORE_clang(warning) JUCE_IGNORE_GCC_LIKE(clang, warning) + + #define JUCE_IGNORE_WARNINGS_GCC_LIKE(compiler, ...) \ + _Pragma(JUCE_TO_STRING(compiler diagnostic push)) \ + JUCE_EACH(JUCE_CONCAT(JUCE_IGNORE_, compiler), __VA_ARGS__) + + /** Push a new warning scope, and then ignore each warning for either clang + or gcc. If the compiler doesn't support __has_warning, we add -Wpragmas + as the first disabled warning because otherwise we might get complaints + about unknown warning options. + */ + #if defined (__has_warning) + #define JUCE_PUSH_WARNINGS_GCC_LIKE(compiler, ...) \ + JUCE_IGNORE_WARNINGS_GCC_LIKE(compiler, __VA_ARGS__) + #else + #define JUCE_PUSH_WARNINGS_GCC_LIKE(compiler, ...) \ + JUCE_IGNORE_WARNINGS_GCC_LIKE(compiler, "-Wpragmas", __VA_ARGS__) + #endif + + /** Pop the current warning scope. */ + #define JUCE_POP_WARNINGS_GCC_LIKE(compiler) \ + _Pragma(JUCE_TO_STRING(compiler diagnostic pop)) + + /** Push/pop warnings on compilers with gcc-like warning flags. + These macros expand to nothing on other compilers (like MSVC). + */ + #if JUCE_CLANG + #define JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE(...) JUCE_PUSH_WARNINGS_GCC_LIKE(clang, __VA_ARGS__) + #define JUCE_END_IGNORE_WARNINGS_GCC_LIKE JUCE_POP_WARNINGS_GCC_LIKE(clang) + #else + #define JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE(...) JUCE_PUSH_WARNINGS_GCC_LIKE(GCC, __VA_ARGS__) + #define JUCE_END_IGNORE_WARNINGS_GCC_LIKE JUCE_POP_WARNINGS_GCC_LIKE(GCC) + #endif +#else + #define JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE(...) + #define JUCE_END_IGNORE_WARNINGS_GCC_LIKE +#endif + +/** Push/pop warnings on MSVC. These macros expand to nothing on other + compilers (like clang and gcc). +*/ +#if JUCE_MSVC + #define JUCE_IGNORE_MSVC(warnings) __pragma(warning(disable:warnings)) + #define JUCE_BEGIN_IGNORE_WARNINGS_LEVEL_MSVC(level, warnings) \ + __pragma(warning(push, level)) JUCE_IGNORE_MSVC(warnings) + #define JUCE_BEGIN_IGNORE_WARNINGS_MSVC(warnings) \ + __pragma(warning(push)) JUCE_IGNORE_MSVC(warnings) + #define JUCE_END_IGNORE_WARNINGS_MSVC __pragma(warning(pop)) +#else + #define JUCE_IGNORE_MSVC(warnings) + #define JUCE_BEGIN_IGNORE_WARNINGS_LEVEL_MSVC(level, warnings) + #define JUCE_BEGIN_IGNORE_WARNINGS_MSVC(warnings) + #define JUCE_END_IGNORE_WARNINGS_MSVC +#endif + +#if JUCE_MAC || JUCE_IOS + #define JUCE_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION 11 +#else + #define JUCE_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION 9 +#endif + +/** Disable sanitizers for a range of functions. + + This functionality doesn't seem to exist on GCC yet, so at the moment this only works for clang. +*/ +#if JUCE_CLANG && __clang_major__ >= JUCE_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION + #define JUCE_BEGIN_NO_SANITIZE(warnings) \ + _Pragma (JUCE_TO_STRING (clang attribute push (__attribute__ ((no_sanitize (warnings))), apply_to=function))) + #define JUCE_END_NO_SANITIZE _Pragma (JUCE_TO_STRING (clang attribute pop)) +#else + #define JUCE_BEGIN_NO_SANITIZE(warnings) + #define JUCE_END_NO_SANITIZE +#endif + +#undef JUCE_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION diff --git a/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h b/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h index d9896c7e..77a35d7d 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -20,6 +20,8 @@ ============================================================================== */ +#pragma once + namespace juce { @@ -37,7 +39,7 @@ namespace juce #endif /** This macro defines the C calling convention used as the standard for JUCE calls. */ -#if JUCE_MSVC +#if JUCE_WINDOWS #define JUCE_CALLTYPE __stdcall #define JUCE_CDECL __cdecl #else @@ -57,7 +59,7 @@ namespace juce #endif //============================================================================== -#if JUCE_IOS || JUCE_LINUX +#if JUCE_IOS || JUCE_LINUX || JUCE_BSD /** This will try to break into the debugger if the app is currently being debugged. If called by an app that's not being debugged, the behaviour isn't defined - it may crash or not, depending on the platform. @@ -69,12 +71,14 @@ namespace juce #pragma intrinsic (__debugbreak) #endif #define JUCE_BREAK_IN_DEBUGGER { __debugbreak(); } -#elif JUCE_GCC || JUCE_MAC +#elif JUCE_INTEL && (JUCE_GCC || JUCE_CLANG || JUCE_MAC) #if JUCE_NO_INLINE_ASM #define JUCE_BREAK_IN_DEBUGGER { } #else #define JUCE_BREAK_IN_DEBUGGER { asm ("int $3"); } #endif +#elif JUCE_ARM && JUCE_MAC + #define JUCE_BREAK_IN_DEBUGGER { __builtin_debugtrap(); } #elif JUCE_ANDROID #define JUCE_BREAK_IN_DEBUGGER { __builtin_trap(); } #else @@ -83,7 +87,7 @@ namespace juce #if JUCE_CLANG && defined (__has_feature) && ! defined (JUCE_ANALYZER_NORETURN) #if __has_feature (attribute_analyzer_noreturn) - inline void __attribute__((analyzer_noreturn)) juce_assert_noreturn() {} + inline void __attribute__ ((analyzer_noreturn)) juce_assert_noreturn() {} #define JUCE_ANALYZER_NORETURN juce::juce_assert_noreturn(); #endif #endif @@ -92,8 +96,28 @@ namespace juce #define JUCE_ANALYZER_NORETURN #endif +/** Used to silence Wimplicit-fallthrough on Clang and GCC where available + as there are a few places in the codebase where we need to do this + deliberately and want to ignore the warning. +*/ +#if JUCE_CLANG + #if __has_cpp_attribute(clang::fallthrough) + #define JUCE_FALLTHROUGH [[clang::fallthrough]]; + #else + #define JUCE_FALLTHROUGH + #endif +#elif JUCE_GCC + #if __GNUC__ >= 7 + #define JUCE_FALLTHROUGH [[gnu::fallthrough]]; + #else + #define JUCE_FALLTHROUGH + #endif +#else + #define JUCE_FALLTHROUGH +#endif + //============================================================================== -#if JUCE_MSVC && ! DOXYGEN +#if JUCE_MSVC && ! defined (DOXYGEN) #define JUCE_BLOCK_WITH_FORCED_SEMICOLON(x) \ __pragma(warning(push)) \ __pragma(warning(disable:4127)) \ @@ -118,14 +142,14 @@ namespace juce that have important side-effects! @see Logger::outputDebugString */ - #define DBG(textToWrite) JUCE_BLOCK_WITH_FORCED_SEMICOLON (juce::String tempDbgBuf; tempDbgBuf << textToWrite; juce::Logger::outputDebugString (tempDbgBuf);) + #define DBG(textToWrite) JUCE_BLOCK_WITH_FORCED_SEMICOLON (juce::String tempDbgBuf; tempDbgBuf << textToWrite; juce::Logger::outputDebugString (tempDbgBuf);) //============================================================================== /** This will always cause an assertion failure. It is only compiled in a debug build, (unless JUCE_LOG_ASSERTIONS is enabled for your build). @see jassert */ - #define jassertfalse JUCE_BLOCK_WITH_FORCED_SEMICOLON (JUCE_LOG_CURRENT_ASSERTION; if (juce::juce_isRunningUnderDebugger()) JUCE_BREAK_IN_DEBUGGER; JUCE_ANALYZER_NORETURN) + #define jassertfalse JUCE_BLOCK_WITH_FORCED_SEMICOLON (JUCE_LOG_CURRENT_ASSERTION; if (juce::juce_isRunningUnderDebugger()) JUCE_BREAK_IN_DEBUGGER; JUCE_ANALYZER_NORETURN) //============================================================================== /** Platform-independent assertion macro. @@ -135,25 +159,33 @@ namespace juce correct behaviour of your program! @see jassertfalse */ - #define jassert(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) + #define jassert(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) + + /** Platform-independent assertion macro which suppresses ignored-variable + warnings in all build modes. You should probably use a plain jassert() + and [[maybe_unused]] by default. + */ + #define jassertquiet(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) #else //============================================================================== // If debugging is disabled, these dummy debug and assertion macros are used.. #define DBG(textToWrite) - #define jassertfalse JUCE_BLOCK_WITH_FORCED_SEMICOLON (JUCE_LOG_CURRENT_ASSERTION) + #define jassertfalse JUCE_BLOCK_WITH_FORCED_SEMICOLON (JUCE_LOG_CURRENT_ASSERTION;) #if JUCE_LOG_ASSERTIONS - #define jassert(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) + #define jassert(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) + #define jassertquiet(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON (if (! (expression)) jassertfalse;) #else - #define jassert(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON ( ; ) + #define jassert(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON ( ; ) + #define jassertquiet(expression) JUCE_BLOCK_WITH_FORCED_SEMICOLON (if (false) (void) (expression);) #endif #endif //============================================================================== -#if ! DOXYGEN +#ifndef DOXYGEN #define JUCE_JOIN_MACRO_HELPER(a, b) a ## b #define JUCE_STRINGIFY_MACRO_HELPER(a) #a #endif @@ -168,7 +200,8 @@ namespace juce #define JUCE_STRINGIFY(item) JUCE_STRINGIFY_MACRO_HELPER (item) //============================================================================== -/** This is a shorthand macro for declaring stubs for a class's copy constructor and operator=. +/** This is a shorthand macro for deleting a class's copy constructor and + copy assignment operator. For example, instead of @code @@ -196,6 +229,13 @@ namespace juce className (const className&) = delete;\ className& operator= (const className&) = delete; +/** This is a shorthand macro for deleting a class's move constructor and + move assignment operator. +*/ +#define JUCE_DECLARE_NON_MOVEABLE(className) \ + className (className&&) = delete;\ + className& operator= (className&&) = delete; + /** This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for a class. */ @@ -243,7 +283,7 @@ namespace juce #if JUCE_MSVC #define forcedinline __forceinline #else - #define forcedinline inline __attribute__((always_inline)) + #define forcedinline inline __attribute__ ((always_inline)) #endif #endif @@ -256,60 +296,18 @@ namespace juce #endif //============================================================================== -// Cross-compiler deprecation macros.. -#ifdef DOXYGEN - /** This macro can be used to wrap a function which has been deprecated. */ - #define JUCE_DEPRECATED(functionDef) - #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) -#elif JUCE_MSVC && ! JUCE_NO_DEPRECATION_WARNINGS - #define JUCE_DEPRECATED_ATTRIBUTE __declspec(deprecated) - #define JUCE_DEPRECATED(functionDef) JUCE_DEPRECATED_ATTRIBUTE functionDef - #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) JUCE_DEPRECATED_ATTRIBUTE functionDef body -#elif (JUCE_GCC || JUCE_CLANG) && ! JUCE_NO_DEPRECATION_WARNINGS - #define JUCE_DEPRECATED_ATTRIBUTE __attribute__ ((deprecated)) - #define JUCE_DEPRECATED(functionDef) functionDef JUCE_DEPRECATED_ATTRIBUTE - #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) functionDef JUCE_DEPRECATED_ATTRIBUTE body -#else - #define JUCE_DEPRECATED_ATTRIBUTE - #define JUCE_DEPRECATED(functionDef) functionDef - #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) functionDef body -#endif - -#if JUCE_ALLOW_STATIC_NULL_VARIABLES - #if ! (defined (DOXYGEN) || defined (JUCE_GCC) || (JUCE_MSVC && _MSC_VER <= 1900)) - #define JUCE_DEPRECATED_STATIC(valueDef) JUCE_DEPRECATED_ATTRIBUTE valueDef - - #if JUCE_MSVC - #define JUCE_DECLARE_DEPRECATED_STATIC(valueDef) \ - __pragma(warning(push)) \ - __pragma(warning(disable:4996)) \ - valueDef \ - __pragma(warning(pop)) - #else - #define JUCE_DECLARE_DEPRECATED_STATIC(valueDef) valueDef - #endif - #else - #define JUCE_DEPRECATED_STATIC(valueDef) valueDef - #define JUCE_DECLARE_DEPRECATED_STATIC(valueDef) valueDef - #endif -#else - #define JUCE_DEPRECATED_STATIC(valueDef) - #define JUCE_DECLARE_DEPRECATED_STATIC(valueDef) -#endif - -//============================================================================== -#if JUCE_ANDROID && ! DOXYGEN +#if JUCE_ANDROID && ! defined (DOXYGEN) #define JUCE_MODAL_LOOPS_PERMITTED 0 #elif ! defined (JUCE_MODAL_LOOPS_PERMITTED) /** Some operating environments don't provide a modal loop mechanism, so this flag can be used to disable any functions that try to run a modal loop. */ - #define JUCE_MODAL_LOOPS_PERMITTED 1 + #define JUCE_MODAL_LOOPS_PERMITTED 0 #endif //============================================================================== #if JUCE_GCC || JUCE_CLANG - #define JUCE_PACKED __attribute__((packed)) -#elif ! DOXYGEN + #define JUCE_PACKED __attribute__ ((packed)) +#elif ! defined (DOXYGEN) #define JUCE_PACKED #endif @@ -317,7 +315,7 @@ namespace juce #if JUCE_GCC || DOXYGEN /** This can be appended to a function declaration to tell gcc to disable associative math optimisations which break some floating point algorithms. */ - #define JUCE_NO_ASSOCIATIVE_MATH_OPTIMISATIONS __attribute__((__optimize__("no-associative-math"))) + #define JUCE_NO_ASSOCIATIVE_MATH_OPTIMISATIONS __attribute__ ((__optimize__ ("no-associative-math"))) #else #define JUCE_NO_ASSOCIATIVE_MATH_OPTIMISATIONS #endif diff --git a/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h b/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h index b18f4ad8..5e9d449a 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -27,9 +27,9 @@ See also SystemStats::getJUCEVersion() for a string version. */ -#define JUCE_MAJOR_VERSION 5 -#define JUCE_MINOR_VERSION 4 -#define JUCE_BUILDNUMBER 7 +#define JUCE_MAJOR_VERSION 7 +#define JUCE_MINOR_VERSION 0 +#define JUCE_BUILDNUMBER 10 /** Current JUCE version number. @@ -41,46 +41,63 @@ */ #define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8) + JUCE_BUILDNUMBER) +#if ! DOXYGEN +#define JUCE_VERSION_ID \ + [[maybe_unused]] volatile auto juceVersionId = "juce_version_" JUCE_STRINGIFY(JUCE_MAJOR_VERSION) "_" JUCE_STRINGIFY(JUCE_MINOR_VERSION) "_" JUCE_STRINGIFY(JUCE_BUILDNUMBER); +#endif //============================================================================== -#include -#include -#include -#include -#include #include -#include +#include #include -#include +#include +#include +#include +#include +#include #include +#include +#include +#include #include -#include -#include +#include #include -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //============================================================================== #include "juce_CompilerSupport.h" +#include "juce_CompilerWarnings.h" #include "juce_PlatformDefs.h" //============================================================================== // Now we'll include some common OS headers.. +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4514 4245 4100) + #if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4514 4245 4100) #include #endif + #if JUCE_MAC || JUCE_IOS #include + #include #include - #if JUCE_IOS - #include - #endif + #include #endif -#if JUCE_LINUX +#if JUCE_LINUX || JUCE_BSD #include #include @@ -97,9 +114,7 @@ #include #endif -#if JUCE_MSVC - #pragma warning (pop) -#endif +JUCE_END_IGNORE_WARNINGS_MSVC #if JUCE_MINGW #include @@ -108,7 +123,6 @@ #if JUCE_ANDROID #include - #include #include #endif @@ -134,7 +148,7 @@ #pragma warning (disable: 1125) // (virtual override warning) #endif #elif defined (JUCE_DLL) || defined (JUCE_DLL_BUILD) - #define JUCE_API __attribute__ ((visibility("default"))) + #define JUCE_API __attribute__ ((visibility ("default"))) #endif //============================================================================== @@ -151,13 +165,6 @@ /** This macro is added to all JUCE public function declarations. */ #define JUCE_PUBLIC_FUNCTION JUCE_API JUCE_CALLTYPE -#if (! defined (JUCE_CATCH_DEPRECATED_CODE_MISUSE)) && JUCE_DEBUG && ! DOXYGEN - /** This turns on some non-essential bits of code that should prevent old code from compiling - in cases where method signatures have changed, etc. - */ - #define JUCE_CATCH_DEPRECATED_CODE_MISUSE 1 -#endif - #ifndef DOXYGEN #define JUCE_NAMESPACE juce // This old macro is deprecated: you should just use the juce namespace directly. #endif diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp index 757ea246..73ea6a48 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -37,9 +37,9 @@ String SystemStats::getJUCEVersion() static_assert (sizeof (int64) == 8, "Basic sanity test failed: please report!"); static_assert (sizeof (uint64) == 8, "Basic sanity test failed: please report!"); - return "JUCE v" JUCE_STRINGIFY(JUCE_MAJOR_VERSION) - "." JUCE_STRINGIFY(JUCE_MINOR_VERSION) - "." JUCE_STRINGIFY(JUCE_BUILDNUMBER); + return "JUCE v" JUCE_STRINGIFY (JUCE_MAJOR_VERSION) + "." JUCE_STRINGIFY (JUCE_MINOR_VERSION) + "." JUCE_STRINGIFY (JUCE_BUILDNUMBER); } #if JUCE_ANDROID && ! defined (JUCE_DISABLE_JUCE_VERSION_PRINTING) @@ -60,24 +60,64 @@ String SystemStats::getJUCEVersion() StringArray SystemStats::getDeviceIdentifiers() { - StringArray ids; + for (const auto flag : { MachineIdFlags::fileSystemId, MachineIdFlags::macAddresses }) + if (auto ids = getMachineIdentifiers (flag); ! ids.isEmpty()) + return ids; - #if JUCE_WINDOWS - File f (File::getSpecialLocation (File::windowsSystemDirectory)); - #else - File f ("~"); - #endif - if (auto num = f.getFileIdentifier()) + jassertfalse; // Failed to create any IDs! + return {}; +} + +String getLegacyUniqueDeviceID(); + +StringArray SystemStats::getMachineIdentifiers (MachineIdFlags flags) +{ + auto macAddressProvider = [] (StringArray& arr) { - ids.add (String::toHexString ((int64) num)); - } - else + for (const auto& mac : MACAddress::getAllAddresses()) + arr.add (mac.toString()); + }; + + auto fileSystemProvider = [] (StringArray& arr) + { + #if JUCE_WINDOWS + File f (File::getSpecialLocation (File::windowsSystemDirectory)); + #else + File f ("~"); + #endif + if (auto num = f.getFileIdentifier()) + arr.add (String::toHexString ((int64) num)); + }; + + auto legacyIdProvider = [] ([[maybe_unused]] StringArray& arr) + { + #if JUCE_WINDOWS + arr.add (getLegacyUniqueDeviceID()); + #endif + }; + + auto uniqueIdProvider = [] (StringArray& arr) + { + arr.add (getUniqueDeviceID()); + }; + + struct Provider { MachineIdFlags flag; void (*func) (StringArray&); }; + static const Provider providers[] = + { + { MachineIdFlags::macAddresses, macAddressProvider }, + { MachineIdFlags::fileSystemId, fileSystemProvider }, + { MachineIdFlags::legacyUniqueId, legacyIdProvider }, + { MachineIdFlags::uniqueId, uniqueIdProvider } + }; + + StringArray ids; + + for (const auto& provider : providers) { - for (auto& address : MACAddress::getAllAddresses()) - ids.add (address.toString()); + if (hasBitValueSet (flags, provider.flag)) + provider.func (ids); } - jassert (! ids.isEmpty()); // Failed to create any IDs! return ids; } @@ -138,7 +178,7 @@ String SystemStats::getStackBacktrace() { String result; - #if JUCE_ANDROID || JUCE_MINGW + #if JUCE_ANDROID || JUCE_MINGW || JUCE_WASM jassertfalse; // sorry, not implemented yet! #elif JUCE_WINDOWS @@ -174,10 +214,10 @@ String SystemStats::getStackBacktrace() #else void* stack[128]; - int frames = backtrace (stack, numElementsInArray (stack)); + auto frames = backtrace (stack, numElementsInArray (stack)); char** frameStrings = backtrace_symbols (stack, frames); - for (int i = 0; i < frames; ++i) + for (auto i = (decltype (frames)) 0; i < frames; ++i) result << frameStrings[i] << newLine; ::free (frameStrings); @@ -187,6 +227,8 @@ String SystemStats::getStackBacktrace() } //============================================================================== +#if ! JUCE_WASM + static SystemStats::CrashHandlerFunction globalCrashHandler = nullptr; #if JUCE_WINDOWS @@ -223,16 +265,13 @@ void SystemStats::setApplicationCrashHandler (CrashHandlerFunction handler) #endif } +#endif + bool SystemStats::isRunningInAppExtensionSandbox() noexcept { #if JUCE_MAC || JUCE_IOS - static bool firstQuery = true; - static bool isRunningInAppSandbox = false; - - if (firstQuery) + static bool isRunningInAppSandbox = [&] { - firstQuery = false; - File bundle = File::getSpecialLocation (File::invokedExecutableFile).getParentDirectory(); #if JUCE_MAC @@ -240,8 +279,10 @@ bool SystemStats::isRunningInAppExtensionSandbox() noexcept #endif if (bundle.isDirectory()) - isRunningInAppSandbox = (bundle.getFileExtension() == ".appex"); - } + return bundle.getFileExtension() == ".appex"; + + return false; + }(); return isRunningInAppSandbox; #else @@ -249,4 +290,25 @@ bool SystemStats::isRunningInAppExtensionSandbox() noexcept #endif } +#if JUCE_UNIT_TESTS + +class UniqueHardwareIDTest final : public UnitTest +{ +public: + //============================================================================== + UniqueHardwareIDTest() : UnitTest ("UniqueHardwareID", UnitTestCategories::analytics) {} + + void runTest() override + { + beginTest ("getUniqueDeviceID returns usable data."); + { + expect (SystemStats::getUniqueDeviceID().isNotEmpty()); + } + } +}; + +static UniqueHardwareIDTest uniqueHardwareIDTest; + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h index 7a22d2f0..355c7ce9 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -51,10 +51,8 @@ class JUCE_API SystemStats final Linux = 0x0400, Android = 0x0800, iOS = 0x1000, + WASM = 0x2000, - MacOSX_10_4 = MacOSX | 4, - MacOSX_10_5 = MacOSX | 5, - MacOSX_10_6 = MacOSX | 6, MacOSX_10_7 = MacOSX | 7, MacOSX_10_8 = MacOSX | 8, MacOSX_10_9 = MacOSX | 9, @@ -63,6 +61,11 @@ class JUCE_API SystemStats final MacOSX_10_12 = MacOSX | 12, MacOSX_10_13 = MacOSX | 13, MacOSX_10_14 = MacOSX | 14, + MacOSX_10_15 = MacOSX | 15, + MacOS_11 = MacOSX | 16, + MacOS_12 = MacOSX | 17, + MacOS_13 = MacOSX | 18, + MacOS_14 = MacOSX | 19, Win2000 = Windows | 1, WinXP = Windows | 2, @@ -70,7 +73,8 @@ class JUCE_API SystemStats final Windows7 = Windows | 4, Windows8_0 = Windows | 5, Windows8_1 = Windows | 6, - Windows10 = Windows | 7 + Windows10 = Windows | 7, + Windows11 = Windows | 8 }; /** Returns the type of operating system we're running on. @@ -143,8 +147,48 @@ class JUCE_API SystemStats final The first choice for an ID is a filesystem ID for the user's home folder or windows directory. If that fails then this function returns the MAC addresses. */ + [[deprecated ("The identifiers produced by this function are not reliable. Use getUniqueDeviceID() instead.")]] static StringArray getDeviceIdentifiers(); + /** This method returns a machine unique ID unaffected by storage or peripheral + changes. + + This ID will be invalidated by changes to the motherboard and CPU on non-mobile + platforms, or performing a system restore on an Android device. + + There are some extra caveats on iOS: The returned ID is unique to the vendor part of + your 'Bundle Identifier' and is stable for all associated apps. The key is invalidated + once all associated apps are uninstalled. This function can return an empty string + under certain conditions, for example, If the device has not been unlocked since a + restart. + */ + static String getUniqueDeviceID(); + + /** Kinds of identifier that are passed to getMachineIdentifiers(). */ + enum class MachineIdFlags + { + macAddresses = 1 << 0, ///< All Mac addresses of the machine. + fileSystemId = 1 << 1, ///< The filesystem id of the user's home directory (or system directory on Windows). + legacyUniqueId = 1 << 2, ///< Only implemented on Windows. A hash of the full smbios table, may be unstable on certain machines. + uniqueId = 1 << 3, ///< The most stable kind of machine identifier. A good default to use. + }; + + /** Returns a list of strings that can be used to uniquely identify a machine. + + To get multiple kinds of identifier at once, you can combine flags using + bitwise-or, e.g. `uniqueId | legacyUniqueId`. + + If a particular kind of identifier isn't available, it will be omitted from + the StringArray of results, so passing `uniqueId | legacyUniqueId` + may return 0, 1, or 2 results, depending on the platform and whether any + errors are encountered. + + If you've previously generated a machine ID and just want to check it against + all possible identifiers, you can enable all of the flags and check whether + the stored identifier matches any of the results. + */ + static StringArray getMachineIdentifiers (MachineIdFlags flags); + //============================================================================== // CPU and memory information.. @@ -216,7 +260,7 @@ class JUCE_API SystemStats final /** A function type for use in setApplicationCrashHandler(). When called, its void* argument will contain platform-specific data about the crash. */ - using CrashHandlerFunction = void(*)(void*); + using CrashHandlerFunction = void (*) (void*); /** Sets up a global callback function that will be called if the application executes some kind of illegal instruction. @@ -231,14 +275,21 @@ class JUCE_API SystemStats final */ static bool isRunningInAppExtensionSandbox() noexcept; + #if JUCE_MAC + static bool isAppSandboxEnabled(); + #endif //============================================================================== - // This method was spelt wrong! Please change your code to use getCpuSpeedInMegahertz() instead - JUCE_DEPRECATED_WITH_BODY (static int getCpuSpeedInMegaherz(), { return getCpuSpeedInMegahertz(); }) + #ifndef DOXYGEN + [[deprecated ("This method was spelt wrong! Please change your code to use getCpuSpeedInMegahertz instead.")]] + static int getCpuSpeedInMegaherz() { return getCpuSpeedInMegahertz(); } + #endif private: SystemStats() = delete; // uses only static methods JUCE_DECLARE_NON_COPYABLE (SystemStats) }; +JUCE_DECLARE_SCOPED_ENUM_BITWISE_OPERATORS (SystemStats::MachineIdFlags) + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h b/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h index 4d6ba10c..0e7ee214 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -58,19 +58,18 @@ //============================================================================== #if defined (_WIN32) || defined (_WIN64) - #define JUCE_WIN32 1 #define JUCE_WINDOWS 1 #elif defined (JUCE_ANDROID) #undef JUCE_ANDROID #define JUCE_ANDROID 1 -#elif defined (__FreeBSD__) || (__OpenBSD__) +#elif defined (__FreeBSD__) || defined (__OpenBSD__) #define JUCE_BSD 1 #elif defined (LINUX) || defined (__linux__) - #define JUCE_LINUX 1 + #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" + #include // (needed to find out what platform we're using) + #include #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR #define JUCE_IPHONE 1 @@ -78,6 +77,8 @@ #else #define JUCE_MAC 1 #endif +#elif defined (__wasm__) + #define JUCE_WASM 1 #else #error "Unknown platform!" #endif @@ -108,7 +109,11 @@ /** If defined, this indicates that the processor is little-endian. */ #define JUCE_LITTLE_ENDIAN 1 - #define JUCE_INTEL 1 + #if defined (_M_ARM) || defined (_M_ARM64) || defined (__arm__) || defined (__aarch64__) + #define JUCE_ARM 1 + #else + #define JUCE_INTEL 1 + #endif #endif //============================================================================== @@ -143,16 +148,16 @@ #endif #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!" + #if ! defined (MAC_OS_X_VERSION_10_14) + #error "The 10.14 SDK (Xcode 10.1+) is required to build JUCE apps. You can create apps that run on macOS 10.9+ by changing the deployment target." + #elif MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 + #error "Building for OSX 10.8 and earlier is no longer supported!" #endif #endif #endif //============================================================================== -#if JUCE_LINUX || JUCE_ANDROID +#if JUCE_LINUX || JUCE_ANDROID || JUCE_BSD #ifdef _DEBUG #define JUCE_DEBUG 1 diff --git a/JuceLibraryCode/modules/juce_core/text/juce_Base64.cpp b/JuceLibraryCode/modules/juce_core/text/juce_Base64.cpp index 77dd67cf..c82eee0c 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_Base64.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_Base64.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -108,9 +108,8 @@ bool Base64::convertFromBase64 (OutputStream& binaryOutput, StringRef base64Text String Base64::toBase64 (const void* sourceData, size_t sourceDataSize) { MemoryOutputStream m ((sourceDataSize * 4) / 3 + 3); - bool ok = convertToBase64 (m, sourceData, sourceDataSize); + [[maybe_unused]] bool ok = convertToBase64 (m, sourceData, sourceDataSize); jassert (ok); // should always succeed for this simple case - ignoreUnused (ok); return m.toString(); } @@ -124,7 +123,7 @@ String Base64::toBase64 (const String& text) //============================================================================== #if JUCE_UNIT_TESTS -class Base64Tests : public UnitTest +class Base64Tests final : public UnitTest { public: Base64Tests() diff --git a/JuceLibraryCode/modules/juce_core/text/juce_Base64.h b/JuceLibraryCode/modules/juce_core/text/juce_Base64.h index ba107753..2c48d25d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_Base64.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_Base64.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_ASCII.h b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_ASCII.h index cb884f62..2b55bc71 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_ASCII.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_ASCII.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -182,7 +182,7 @@ class CharPointer_ASCII final /** Returns the number of bytes that would be needed to represent the given unicode character in this encoding format. */ - static inline size_t getBytesRequiredFor (const juce_wchar) noexcept + static size_t getBytesRequiredFor (const juce_wchar) noexcept { return 1; } @@ -335,7 +335,7 @@ class CharPointer_ASCII final /** Parses this string as a 64-bit integer. */ int64 getIntValue64() const noexcept { - #if JUCE_LINUX || JUCE_ANDROID || JUCE_MINGW + #if JUCE_LINUX || JUCE_BSD || JUCE_ANDROID || JUCE_MINGW return atoll (data); #elif JUCE_WINDOWS return _atoi64 (data); @@ -350,6 +350,9 @@ class CharPointer_ASCII final /** Returns the first non-whitespace character in the string. */ CharPointer_ASCII findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); } + /** Move this pointer to the first non-whitespace character in the string. */ + void incrementToEndOfWhitespace() noexcept { CharacterFunctions::incrementToEndOfWhitespace (*this); } + /** Returns true if the given unicode character can be represented in this encoding. */ static bool canRepresent (juce_wchar character) noexcept { diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h index e231c344..4773c7de 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -344,7 +344,7 @@ class CharPointer_UTF16 final return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } - #if JUCE_MSVC && ! DOXYGEN + #if JUCE_MSVC && ! defined (DOXYGEN) int compareIgnoreCase (CharPointer_UTF16 other) const noexcept { return _wcsicmp (data, other.data); @@ -426,6 +426,9 @@ class CharPointer_UTF16 final /** Returns the first non-whitespace character in the string. */ CharPointer_UTF16 findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); } + /** Move this pointer to the first non-whitespace character in the string. */ + void incrementToEndOfWhitespace() noexcept { CharacterFunctions::incrementToEndOfWhitespace (*this); } + /** Returns true if the given unicode character can be represented in this encoding. */ static bool canRepresent (juce_wchar character) noexcept { @@ -483,11 +486,13 @@ class CharPointer_UTF16 final */ static bool isByteOrderMarkBigEndian (const void* possibleByteOrder) noexcept { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (28182) jassert (possibleByteOrder != nullptr); auto c = static_cast (possibleByteOrder); return c[0] == (uint8) byteOrderMarkBE1 && c[1] == (uint8) byteOrderMarkBE2; + JUCE_END_IGNORE_WARNINGS_MSVC } /** Returns true if the first pair of bytes in this pointer are the UTF16 byte-order mark (little endian). @@ -495,11 +500,13 @@ class CharPointer_UTF16 final */ static bool isByteOrderMarkLittleEndian (const void* possibleByteOrder) noexcept { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (28182) jassert (possibleByteOrder != nullptr); auto c = static_cast (possibleByteOrder); return c[0] == (uint8) byteOrderMarkLE1 && c[1] == (uint8) byteOrderMarkLE2; + JUCE_END_IGNORE_WARNINGS_MSVC } private: diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF32.h b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF32.h index 66d3220e..1cf12db6 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF32.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF32.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -186,7 +186,7 @@ class CharPointer_UTF32 final /** Returns the number of bytes that would be needed to represent the given unicode character in this encoding format. */ - static inline size_t getBytesRequiredFor (juce_wchar) noexcept + static size_t getBytesRequiredFor (juce_wchar) noexcept { return sizeof (CharType); } @@ -341,6 +341,9 @@ class CharPointer_UTF32 final /** Returns the first non-whitespace character in the string. */ CharPointer_UTF32 findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); } + /** Move this pointer to the first non-whitespace character in the string. */ + void incrementToEndOfWhitespace() noexcept { CharacterFunctions::incrementToEndOfWhitespace (*this); } + /** Returns true if the given unicode character can be represented in this encoding. */ static bool canRepresent (juce_wchar character) noexcept { diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h index 33217117..a86947b5 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -36,44 +36,44 @@ class CharPointer_UTF8 final public: using CharType = char; - inline explicit CharPointer_UTF8 (const CharType* rawPointer) noexcept + explicit CharPointer_UTF8 (const CharType* rawPointer) noexcept : data (const_cast (rawPointer)) { } - inline CharPointer_UTF8 (const CharPointer_UTF8& other) = default; + CharPointer_UTF8 (const CharPointer_UTF8& other) = default; - inline CharPointer_UTF8 operator= (CharPointer_UTF8 other) noexcept + CharPointer_UTF8 operator= (CharPointer_UTF8 other) noexcept { data = other.data; return *this; } - inline CharPointer_UTF8 operator= (const CharType* text) noexcept + CharPointer_UTF8 operator= (const CharType* text) noexcept { data = const_cast (text); return *this; } /** This is a pointer comparison, it doesn't compare the actual text. */ - inline bool operator== (CharPointer_UTF8 other) const noexcept { return data == other.data; } - inline bool operator!= (CharPointer_UTF8 other) const noexcept { return data != other.data; } - inline bool operator<= (CharPointer_UTF8 other) const noexcept { return data <= other.data; } - inline bool operator< (CharPointer_UTF8 other) const noexcept { return data < other.data; } - inline bool operator>= (CharPointer_UTF8 other) const noexcept { return data >= other.data; } - inline bool operator> (CharPointer_UTF8 other) const noexcept { return data > other.data; } + bool operator== (CharPointer_UTF8 other) const noexcept { return data == other.data; } + bool operator!= (CharPointer_UTF8 other) const noexcept { return data != other.data; } + bool operator<= (CharPointer_UTF8 other) const noexcept { return data <= other.data; } + bool operator< (CharPointer_UTF8 other) const noexcept { return data < other.data; } + bool operator>= (CharPointer_UTF8 other) const noexcept { return data >= other.data; } + bool operator> (CharPointer_UTF8 other) const noexcept { return data > other.data; } /** Returns the address that this pointer is pointing to. */ - inline CharType* getAddress() const noexcept { return data; } + CharType* getAddress() const noexcept { return data; } /** Returns the address that this pointer is pointing to. */ - inline operator const CharType*() const noexcept { return data; } + operator const CharType*() const noexcept { return data; } /** Returns true if this pointer is pointing to a null character. */ - inline bool isEmpty() const noexcept { return *data == 0; } + bool isEmpty() const noexcept { return *data == 0; } /** Returns true if this pointer is not pointing to a null character. */ - inline bool isNotEmpty() const noexcept { return *data != 0; } + bool isNotEmpty() const noexcept { return *data != 0; } /** Returns the unicode character that this pointer is pointing to. */ juce_wchar operator*() const noexcept @@ -124,7 +124,7 @@ class CharPointer_UTF8 final while ((static_cast (n) & bit) != 0 && bit > 0x8) { ++data; - bit >>= 1; + bit = static_cast (bit >> 1); } } @@ -274,8 +274,10 @@ class CharPointer_UTF8 final */ size_t sizeInBytes() const noexcept { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6387) jassert (data != nullptr); return strlen (data) + 1; + JUCE_END_IGNORE_WARNINGS_MSVC } /** Returns the number of bytes that would be needed to represent the given @@ -348,7 +350,7 @@ class CharPointer_UTF8 final } /** Writes a null character to this string (leaving the pointer's position unchanged). */ - inline void writeNull() const noexcept + void writeNull() const noexcept { *data = 0; } @@ -447,7 +449,7 @@ class CharPointer_UTF8 final } /** Returns true if the first character of this string is whitespace. */ - bool isWhitespace() const noexcept { const CharType c = *data; return c == ' ' || (c <= 13 && c >= 9); } + bool isWhitespace() const noexcept { return CharacterFunctions::isWhitespace ((juce_wchar) *(*this)); } /** Returns true if the first character of this string is a digit. */ bool isDigit() const noexcept { const CharType c = *data; return c >= '0' && c <= '9'; } /** Returns true if the first character of this string is a letter. */ @@ -483,6 +485,9 @@ class CharPointer_UTF8 final /** Returns the first non-whitespace character in the string. */ CharPointer_UTF8 findEndOfWhitespace() const noexcept { return CharacterFunctions::findEndOfWhitespace (*this); } + /** Move this pointer to the first non-whitespace character in the string. */ + void incrementToEndOfWhitespace() noexcept { CharacterFunctions::incrementToEndOfWhitespace (*this); } + /** Returns true if the given unicode character can be represented in this encoding. */ static bool canRepresent (juce_wchar character) noexcept { @@ -549,12 +554,14 @@ class CharPointer_UTF8 final */ static bool isByteOrderMark (const void* possibleByteOrder) noexcept { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (28182) jassert (possibleByteOrder != nullptr); auto c = static_cast (possibleByteOrder); return c[0] == (uint8) byteOrderMark1 && c[1] == (uint8) byteOrderMark2 && c[2] == (uint8) byteOrderMark3; + JUCE_END_IGNORE_WARNINGS_MSVC } private: diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp index f849f1c4..a8a7546d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,10 +23,7 @@ namespace juce { -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4514 4996) -#endif +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4514 4996) juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) noexcept { @@ -56,9 +53,7 @@ bool CharacterFunctions::isLowerCase (const juce_wchar character) noexcept #endif } -#if JUCE_MSVC - #pragma warning (pop) -#endif +JUCE_END_IGNORE_WARNINGS_MSVC //============================================================================== bool CharacterFunctions::isWhitespace (const char character) noexcept @@ -139,7 +134,7 @@ double CharacterFunctions::mulexp10 (const double value, int exponent) noexcept if (exponent == 0) return value; - if (value == 0.0) + if (exactlyEqual (value, 0.0)) return 0; const bool negative = (exponent < 0); @@ -186,19 +181,209 @@ juce_wchar CharacterFunctions::getUnicodeCharFromWindows1252Codepage (const uint #define QUOTE(x) #x #define STR(value) QUOTE(value) -#define ASYM_STRING_DOUBLE_PAIR(str, value) std::pair (STR(str), value) -#define STRING_DOUBLE_PAIR(value) ASYM_STRING_DOUBLE_PAIR(value, value) -#define STRING_DOUBLE_PAIR_COMBOS(value) \ - STRING_DOUBLE_PAIR(value), \ - STRING_DOUBLE_PAIR(-value), \ - ASYM_STRING_DOUBLE_PAIR(+value, value), \ - ASYM_STRING_DOUBLE_PAIR(000000 ## value, value), \ - ASYM_STRING_DOUBLE_PAIR(+000 ## value, value), \ - ASYM_STRING_DOUBLE_PAIR(-0 ## value, -value) - -class CharacterFunctionsTests : public UnitTest +#define ASYM_CHARPTR_DOUBLE_PAIR(str, value) std::pair (STR(str), value) +#define CHARPTR_DOUBLE_PAIR(value) ASYM_CHARPTR_DOUBLE_PAIR(value, value) +#define CHARPTR_DOUBLE_PAIR_COMBOS(value) \ + CHARPTR_DOUBLE_PAIR(value), \ + CHARPTR_DOUBLE_PAIR(-value), \ + ASYM_CHARPTR_DOUBLE_PAIR(+value, value), \ + ASYM_CHARPTR_DOUBLE_PAIR(000000 ## value, value), \ + ASYM_CHARPTR_DOUBLE_PAIR(+000 ## value, value), \ + ASYM_CHARPTR_DOUBLE_PAIR(-0 ## value, -value) + +namespace characterFunctionsTests +{ + +template +MemoryBlock memoryBlockFromCharPtr (const typename CharPointerType::CharType* charPtr) +{ + using CharType = typename CharPointerType::CharType; + + MemoryBlock result; + CharPointerType source (charPtr); + + result.setSize (CharPointerType::getBytesRequiredFor (source) + sizeof (CharType)); + CharPointerType dest { (CharType*) result.getData() }; + dest.writeAll (source); + return result; +} + +template +MemoryBlock convert (const MemoryBlock& source, bool removeNullTerminator = false) +{ + using ToCharType = typename ToCharPointerType ::CharType; + using FromCharType = typename FromCharPointerType::CharType; + + FromCharPointerType sourcePtr { (FromCharType*) source.getData() }; + + std::vector sourceChars; + size_t requiredSize = 0; + juce_wchar c; + + while ((c = sourcePtr.getAndAdvance()) != '\0') + { + requiredSize += ToCharPointerType::getBytesRequiredFor (c); + sourceChars.push_back (c); + } + + if (! removeNullTerminator) + requiredSize += sizeof (ToCharType); + + MemoryBlock result; + result.setSize (requiredSize); + + ToCharPointerType dest { (ToCharType*) result.getData() }; + + for (auto wc : sourceChars) + dest.write (wc); + + if (! removeNullTerminator) + dest.writeNull(); + + return result; +} + +struct SeparatorStrings +{ + std::vector terminals, nulls; +}; + +template +SeparatorStrings getSeparators() +{ + jassertfalse; + return {}; +} + +template <> +SeparatorStrings getSeparators() +{ + SeparatorStrings result; + + const CharPointer_ASCII::CharType* terminalCharPtrs[] = { + "", "-", "+", "e", "e+", "E-", "f", " ", ",", ";", "<", "'", "\"", "_", "k", + " +", " -", " -e", "-In ", " +n", "n", " r" + }; + + for (auto ptr : terminalCharPtrs) + result.terminals.push_back (memoryBlockFromCharPtr (ptr)); + + const CharPointer_ASCII::CharType* nullCharPtrs[] = { "." }; + + result.nulls = result.terminals; + + for (auto ptr : nullCharPtrs) + result.nulls.push_back (memoryBlockFromCharPtr (ptr)); + + return result; +} + +template <> +SeparatorStrings getSeparators() +{ + auto result = getSeparators(); + + const CharPointer_UTF8::CharType* terminalCharPtrs[] = { + "\xe2\x82\xac", // € + "\xf0\x90\x90\xB7", // 𐐷 + "\xf0\x9f\x98\x83", // 😃 + "\xf0\x9f\x8f\x81\xF0\x9F\x9A\x97" // 🏁🚗 + }; + + for (auto ptr : terminalCharPtrs) + { + auto block = memoryBlockFromCharPtr (ptr); + + for (auto vec : { &result.terminals, &result.nulls }) + vec->push_back (block); + } + + return result; +} + +template +SeparatorStrings prefixWithAsciiSeparators (const std::vector>& terminalCharPtrs) +{ + auto asciiSeparators = getSeparators(); + + SeparatorStrings result; + + for (const auto& block : asciiSeparators.terminals) + result.terminals.push_back (convert (block)); + + for (const auto& block : asciiSeparators.nulls) + result.nulls.push_back (convert (block)); + + for (auto& t : terminalCharPtrs) + { + const auto block = memoryBlockFromCharPtr ((typename CharPointerType::CharType*) t.data()); + + for (auto vec : { &result.terminals, &result.nulls }) + vec->push_back (block); + } + + return result; +} + +template <> +SeparatorStrings getSeparators() +{ + const std::vector> terminalCharPtrs { + { 0x0 }, + { 0x0076, 0x0 }, // v + { 0x20ac, 0x0 }, // € + { 0xd801, 0xdc37, 0x0 }, // 𐐷 + { 0x0065, 0xd83d, 0xde03, 0x0 }, // e😃 + { 0xd83c, 0xdfc1, 0xd83d, 0xde97, 0x0 } // 🏁🚗 + }; + + return prefixWithAsciiSeparators (terminalCharPtrs); +} + +template <> +SeparatorStrings getSeparators() +{ + const std::vector> terminalCharPtrs = { + { 0x00000076, 0x0 }, // v + { 0x000020aC, 0x0 }, // € + { 0x00010437, 0x0 }, // 𐐷 + { 0x00000065, 0x0001f603, 0x0 }, // e😃 + { 0x0001f3c1, 0x0001f697, 0x0 } // 🏁🚗 + }; + + return prefixWithAsciiSeparators (terminalCharPtrs); +} + +template +void withAllPrefixesAndSuffixes (const std::vector& prefixes, + const std::vector& suffixes, + const std::vector& testValues, + TestFunction&& test) +{ + for (const auto& prefix : prefixes) + { + for (const auto& testValue : testValues) + { + MemoryBlock testBlock = prefix; + testBlock.append (testValue.getData(), testValue.getSize()); + + for (const auto& suffix : suffixes) + { + MemoryBlock data = testBlock; + data.append (suffix.getData(), suffix.getSize()); + + test (data, suffix); + } + } + } +} + +template +class CharacterFunctionsTests final : public UnitTest { public: + using CharType = typename CharPointerType::CharType; + CharacterFunctionsTests() : UnitTest ("CharacterFunctions", UnitTestCategories::text) {} @@ -207,96 +392,159 @@ class CharacterFunctionsTests : public UnitTest { beginTest ("readDoubleValue"); - static const std::pair testValues[] = + const std::pair trials[] = { // Integers - STRING_DOUBLE_PAIR_COMBOS (0), - STRING_DOUBLE_PAIR_COMBOS (3), - STRING_DOUBLE_PAIR_COMBOS (4931), - STRING_DOUBLE_PAIR_COMBOS (5000), - STRING_DOUBLE_PAIR_COMBOS (9862097), + CHARPTR_DOUBLE_PAIR_COMBOS (0), + CHARPTR_DOUBLE_PAIR_COMBOS (3), + CHARPTR_DOUBLE_PAIR_COMBOS (4931), + CHARPTR_DOUBLE_PAIR_COMBOS (5000), + CHARPTR_DOUBLE_PAIR_COMBOS (9862097), // Floating point numbers - STRING_DOUBLE_PAIR_COMBOS (7.000), - STRING_DOUBLE_PAIR_COMBOS (0.2), - STRING_DOUBLE_PAIR_COMBOS (.298630), - STRING_DOUBLE_PAIR_COMBOS (1.118), - STRING_DOUBLE_PAIR_COMBOS (0.9000), - STRING_DOUBLE_PAIR_COMBOS (0.0000001), - STRING_DOUBLE_PAIR_COMBOS (500.0000001), - STRING_DOUBLE_PAIR_COMBOS (9862098.2398604), + CHARPTR_DOUBLE_PAIR_COMBOS (0.), + CHARPTR_DOUBLE_PAIR_COMBOS (9.), + CHARPTR_DOUBLE_PAIR_COMBOS (7.000), + CHARPTR_DOUBLE_PAIR_COMBOS (0.2), + CHARPTR_DOUBLE_PAIR_COMBOS (.298630), + CHARPTR_DOUBLE_PAIR_COMBOS (1.118), + CHARPTR_DOUBLE_PAIR_COMBOS (0.9000), + CHARPTR_DOUBLE_PAIR_COMBOS (0.0000001), + CHARPTR_DOUBLE_PAIR_COMBOS (500.0000001), + CHARPTR_DOUBLE_PAIR_COMBOS (9862098.2398604), // Exponents - STRING_DOUBLE_PAIR_COMBOS (0e0), - STRING_DOUBLE_PAIR_COMBOS (0.e0), - STRING_DOUBLE_PAIR_COMBOS (0.00000e0), - STRING_DOUBLE_PAIR_COMBOS (.0e7), - STRING_DOUBLE_PAIR_COMBOS (0e-5), - STRING_DOUBLE_PAIR_COMBOS (2E0), - STRING_DOUBLE_PAIR_COMBOS (4.E0), - STRING_DOUBLE_PAIR_COMBOS (1.2000000E0), - STRING_DOUBLE_PAIR_COMBOS (1.2000000E6), - STRING_DOUBLE_PAIR_COMBOS (.398e3), - STRING_DOUBLE_PAIR_COMBOS (10e10), - STRING_DOUBLE_PAIR_COMBOS (1.4962e+2), - STRING_DOUBLE_PAIR_COMBOS (3198693.0973e4), - STRING_DOUBLE_PAIR_COMBOS (10973097.2087E-4), - STRING_DOUBLE_PAIR_COMBOS (1.3986e00006), - STRING_DOUBLE_PAIR_COMBOS (2087.3087e+00006), - STRING_DOUBLE_PAIR_COMBOS (6.0872e-00006), + CHARPTR_DOUBLE_PAIR_COMBOS (0e0), + CHARPTR_DOUBLE_PAIR_COMBOS (0.e0), + CHARPTR_DOUBLE_PAIR_COMBOS (0.00000e0), + CHARPTR_DOUBLE_PAIR_COMBOS (.0e7), + CHARPTR_DOUBLE_PAIR_COMBOS (0e-5), + CHARPTR_DOUBLE_PAIR_COMBOS (2E0), + CHARPTR_DOUBLE_PAIR_COMBOS (4.E0), + CHARPTR_DOUBLE_PAIR_COMBOS (1.2000000E0), + CHARPTR_DOUBLE_PAIR_COMBOS (1.2000000E6), + CHARPTR_DOUBLE_PAIR_COMBOS (.398e3), + CHARPTR_DOUBLE_PAIR_COMBOS (10e10), + CHARPTR_DOUBLE_PAIR_COMBOS (1.4962e+2), + CHARPTR_DOUBLE_PAIR_COMBOS (3198693.0973e4), + CHARPTR_DOUBLE_PAIR_COMBOS (10973097.2087E-4), + CHARPTR_DOUBLE_PAIR_COMBOS (1.3986e00006), + CHARPTR_DOUBLE_PAIR_COMBOS (2087.3087e+00006), + CHARPTR_DOUBLE_PAIR_COMBOS (6.0872e-00006), + + CHARPTR_DOUBLE_PAIR_COMBOS (1.7976931348623157e+308), + CHARPTR_DOUBLE_PAIR_COMBOS (2.2250738585072014e-308), // Too many sig figs. The parsing routine on MinGW gets the last // significant figure wrong. - STRING_DOUBLE_PAIR_COMBOS (17654321098765432.9), - STRING_DOUBLE_PAIR_COMBOS (183456789012345678.9), - STRING_DOUBLE_PAIR_COMBOS (1934567890123456789.9), - STRING_DOUBLE_PAIR_COMBOS (20345678901234567891.9), - STRING_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000), - STRING_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752e3), - STRING_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752e100), - STRING_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000e-5), - STRING_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000e-40), - - STRING_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890), - STRING_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890e-111) - - // Limits. DBL_MAX may not exist on Linux. - #if ! JUCE_LINUX - , STRING_DOUBLE_PAIR (DBL_MAX), - STRING_DOUBLE_PAIR (-DBL_MAX), - STRING_DOUBLE_PAIR (DBL_MIN) - #endif + CHARPTR_DOUBLE_PAIR_COMBOS (17654321098765432.9), + CHARPTR_DOUBLE_PAIR_COMBOS (183456789012345678.9), + CHARPTR_DOUBLE_PAIR_COMBOS (1934567890123456789.9), + CHARPTR_DOUBLE_PAIR_COMBOS (20345678901234567891.9), + CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000), + CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752e3), + CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752e100), + CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000e-5), + CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000005e-40), + + CHARPTR_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890), + CHARPTR_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890e-111), }; - for (auto trial : testValues) + auto asciiToMemoryBlock = [] (const char* asciiPtr, bool removeNullTerminator) { - auto charPtr = trial.first.getCharPointer(); - expectEquals (CharacterFunctions::readDoubleValue (charPtr), trial.second); + auto block = memoryBlockFromCharPtr (asciiPtr); + return convert (block, removeNullTerminator); + }; + + const auto separators = getSeparators(); + + for (const auto& trial : trials) + { + for (const auto& terminal : separators.terminals) + { + MemoryBlock data { asciiToMemoryBlock (trial.first, true) }; + data.append (terminal.getData(), terminal.getSize()); + + CharPointerType charPtr { (CharType*) data.getData() }; + expectEquals (CharacterFunctions::readDoubleValue (charPtr), trial.second); + expect (*charPtr == *(CharPointerType ((CharType*) terminal.getData()))); + } } + auto asciiToMemoryBlocks = [&] (const std::vector& asciiPtrs, bool removeNullTerminator) + { + std::vector result; + + for (auto* ptr : asciiPtrs) + result.push_back (asciiToMemoryBlock (ptr, removeNullTerminator)); + + return result; + }; + + std::vector prefixCharPtrs = { "" , "+", "-" }; + const auto prefixes = asciiToMemoryBlocks (prefixCharPtrs, true); + { - String nans[] = { "NaN", "-nan", "+NAN", "1.0E1024", "-1.0E-999", "1.23456789012345678901234567890e123456789"}; + std::vector nanCharPtrs = { "NaN", "nan", "NAN", "naN" }; + auto nans = asciiToMemoryBlocks (nanCharPtrs, true); - for (auto nan : nans) + withAllPrefixesAndSuffixes (prefixes, separators.terminals, nans, [this] (const MemoryBlock& data, + const MemoryBlock& suffix) { - auto charPtr = nan.getCharPointer(); + CharPointerType charPtr { (CharType*) data.getData() }; expect (std::isnan (CharacterFunctions::readDoubleValue (charPtr))); - } + expect (*charPtr == *(CharPointerType ((CharType*) suffix.getData()))); + }); + } + + { + std::vector infCharPtrs = { "Inf", "inf", "INF", "InF", "1.0E1024", "1.23456789012345678901234567890e123456789" }; + auto infs = asciiToMemoryBlocks (infCharPtrs, true); + + withAllPrefixesAndSuffixes (prefixes, separators.terminals, infs, [this] (const MemoryBlock& data, + const MemoryBlock& suffix) + { + CharPointerType charPtr { (CharType*) data.getData() }; + auto expected = charPtr[0] == '-' ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + expectEquals (CharacterFunctions::readDoubleValue (charPtr), expected); + expect (*charPtr == *(CharPointerType ((CharType*) suffix.getData()))); + }); } { - String infs[] = { "Inf", "-inf", "INF"}; + std::vector zeroCharPtrs = { "1.0E-400", "1.23456789012345678901234567890e-123456789" }; + auto zeros = asciiToMemoryBlocks (zeroCharPtrs, true); - for (auto inf : infs) + withAllPrefixesAndSuffixes (prefixes, separators.terminals, zeros, [this] (const MemoryBlock& data, + const MemoryBlock& suffix) { - auto charPtr = inf.getCharPointer(); - expect (std::isinf (CharacterFunctions::readDoubleValue (charPtr))); + CharPointerType charPtr { (CharType*) data.getData() }; + auto expected = charPtr[0] == '-' ? -0.0 : 0.0; + expectEquals (CharacterFunctions::readDoubleValue (charPtr), expected); + expect (*charPtr == *(CharPointerType ((CharType*) suffix.getData()))); + }); + } + + { + for (const auto& n : separators.nulls) + { + MemoryBlock data { n.getData(), n.getSize() }; + CharPointerType charPtr { (CharType*) data.getData() }; + expectEquals (CharacterFunctions::readDoubleValue (charPtr), 0.0); + expect (charPtr == CharPointerType { (CharType*) data.getData() }.findEndOfWhitespace()); } } } }; -static CharacterFunctionsTests characterFunctionsTests; +static CharacterFunctionsTests characterFunctionsTestsAscii; +static CharacterFunctionsTests characterFunctionsTestsUtf8; +static CharacterFunctionsTests characterFunctionsTestsUtf16; +static CharacterFunctionsTests characterFunctionsTestsUtf32; + +} #endif diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h index 516028f7..6cdab260 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -24,7 +24,7 @@ namespace juce { //============================================================================== -#if JUCE_WINDOWS && ! DOXYGEN +#if JUCE_WINDOWS && ! defined (DOXYGEN) #define JUCE_NATIVE_WCHAR_IS_UTF8 0 #define JUCE_NATIVE_WCHAR_IS_UTF16 1 #define JUCE_NATIVE_WCHAR_IS_UTF32 0 @@ -60,7 +60,7 @@ namespace juce #define T(stringLiteral) JUCE_T(stringLiteral) #endif -#if ! DOXYGEN +#ifndef DOXYGEN //============================================================================== // GNU libstdc++ does not have std::make_unsigned @@ -146,43 +146,65 @@ class JUCE_API CharacterFunctions template static double readDoubleValue (CharPointerType& text) noexcept { - #if JUCE_MINGW + constexpr auto inf = std::numeric_limits::infinity(); + bool isNegative = false; - #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 + #if ! JUCE_MINGW + constexpr const int maxSignificantDigits = 17 + 1; // An additional digit for rounding + constexpr const int bufferSize = maxSignificantDigits + 7 + 1; // -.E-XXX and a trailing null-terminator char buffer[(size_t) bufferSize] = {}; - char* currentCharacter = &(buffer[0]); + char* writePtr = &(buffer[0]); #endif - text = text.findEndOfWhitespace(); + const auto endOfWhitspace = text.findEndOfWhitespace(); + text = endOfWhitspace; + auto c = *text; switch (c) { case '-': - #if JUCE_MINGW isNegative = true; - #else - *currentCharacter++ = '-'; + #if ! JUCE_MINGW + *writePtr++ = '-'; #endif - // Fall-through.. + JUCE_FALLTHROUGH case '+': c = *++text; + break; + default: + break; } switch (c) { case 'n': case 'N': + { if ((text[1] == 'a' || text[1] == 'A') && (text[2] == 'n' || text[2] == 'N')) + { + text += 3; return std::numeric_limits::quiet_NaN(); - break; + } + + text = endOfWhitspace; + return 0.0; + } case 'i': case 'I': + { if ((text[1] == 'n' || text[1] == 'N') && (text[2] == 'f' || text[2] == 'F')) - return std::numeric_limits::infinity(); + { + text += 3; + return isNegative ? -inf : inf; + } + + text = endOfWhitspace; + return 0.0; + } + + default: break; } @@ -194,7 +216,7 @@ class JUCE_API CharacterFunctions int exponent = 0, decPointIndex = 0, digit = 0; int lastDigit = 0, numSignificantDigits = 0; bool digitsFound = false; - JUCE_CONSTEXPR const int maxSignificantDigits = 17 + 1; + constexpr const int maxSignificantDigits = 17 + 1; for (;;) { @@ -274,7 +296,7 @@ class JUCE_API CharacterFunctions switch (*++text) { - case '-': negativeExponent = true; // fall-through.. + case '-': negativeExponent = true; JUCE_FALLTHROUGH case '+': ++text; } @@ -293,9 +315,8 @@ class JUCE_API CharacterFunctions #else // ! JUCE_MINGW - int numSigFigs = 0; - bool decimalPointFound = false; - int extraExponent = 0; + int numSigFigs = 0, extraExponent = 0; + bool decimalPointFound = false, leadingZeros = false; for (;;) { @@ -317,16 +338,19 @@ class JUCE_API CharacterFunctions } if (numSigFigs == 0 && digit == 0) + { + leadingZeros = true; continue; + } } - *currentCharacter++ = (char) ('0' + (char) digit); + *writePtr++ = (char) ('0' + (char) digit); numSigFigs++; } else if ((! decimalPointFound) && *text == '.') { ++text; - *currentCharacter++ = '.'; + *writePtr++ = '.'; decimalPointFound = true; } else @@ -335,9 +359,13 @@ class JUCE_API CharacterFunctions } } - c = *text; + if ((! leadingZeros) && (numSigFigs == 0)) + { + text = endOfWhitspace; + return 0.0; + } - auto writeExponentDigits = [](int exponent, char* destination) + auto writeExponentDigits = [] (int exponent, char* destination) { auto exponentDivisor = 100; @@ -352,18 +380,28 @@ class JUCE_API CharacterFunctions *destination++ = (char) ('0' + (char) exponent); }; - if ((c == 'e' || c == 'E') && numSigFigs > 0) + c = *text; + + if (c == 'e' || c == 'E') { - *currentCharacter++ = 'e'; + const auto startOfExponent = text; + *writePtr++ = 'e'; bool parsedExponentIsPositive = true; switch (*++text) { - case '-': parsedExponentIsPositive = false; // Fall-through.. - case '+': ++text; + case '-': + parsedExponentIsPositive = false; + JUCE_FALLTHROUGH + case '+': + ++text; + break; + default: + break; } int exponent = 0; + const auto startOfExponentDigits = text; while (text.isDigit()) { @@ -373,22 +411,30 @@ class JUCE_API CharacterFunctions exponent = (exponent * 10) + digit; } + if (text == startOfExponentDigits) + text = startOfExponent; + exponent = extraExponent + (parsedExponentIsPositive ? exponent : -exponent); if (exponent < 0) - *currentCharacter++ = '-'; - - exponent = std::abs (exponent); + { + if (exponent < std::numeric_limits::min_exponent10 - 1) + return isNegative ? -0.0 : 0.0; - if (exponent > std::numeric_limits::max_exponent10) - return std::numeric_limits::quiet_NaN(); + *writePtr++ = '-'; + exponent = -exponent; + } + else if (exponent > std::numeric_limits::max_exponent10 + 1) + { + return isNegative ? -inf : inf; + } - writeExponentDigits (exponent, currentCharacter); + writeExponentDigits (exponent, writePtr); } else if (extraExponent > 0) { - *currentCharacter++ = 'e'; - writeExponentDigits (extraExponent, currentCharacter); + *writePtr++ = 'e'; + writeExponentDigits (extraExponent, writePtr); } #if JUCE_WINDOWS @@ -444,6 +490,9 @@ class JUCE_API CharacterFunctions template struct HexParser { + static_assert (std::is_unsigned_v, "ResultType must be unsigned because " + "left-shifting a negative value is UB"); + template static ResultType parse (CharPointerType t) noexcept { @@ -454,7 +503,7 @@ class JUCE_API CharacterFunctions auto hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); if (hexValue >= 0) - result = (result << 4) | hexValue; + result = static_cast (result << 4) | static_cast (hexValue); } return result; @@ -544,7 +593,7 @@ class JUCE_API CharacterFunctions } /** Compares two characters. */ - static inline int compare (juce_wchar char1, juce_wchar char2) noexcept + static int compare (juce_wchar char1, juce_wchar char2) noexcept { if (auto diff = static_cast (char1) - static_cast (char2)) return diff < 0 ? -1 : 1; @@ -589,7 +638,7 @@ class JUCE_API CharacterFunctions } /** Compares two characters, using a case-independant match. */ - static inline int compareIgnoreCase (juce_wchar char1, juce_wchar char2) noexcept + static int compareIgnoreCase (juce_wchar char1, juce_wchar char2) noexcept { return char1 != char2 ? compare (toUpperCase (char1), toUpperCase (char2)) : 0; } @@ -748,6 +797,19 @@ class JUCE_API CharacterFunctions return -1; } + /** Increments a pointer until it points to the first non-whitespace character + in a string. + + If the string contains only whitespace, the pointer will point to the + string's null terminator. + */ + template + static void incrementToEndOfWhitespace (Type& text) noexcept + { + while (text.isWhitespace()) + ++text; + } + /** Returns a pointer to the first non-whitespace character in a string. If the string contains only whitespace, this will return a pointer to its null terminator. @@ -755,9 +817,7 @@ class JUCE_API CharacterFunctions template static Type findEndOfWhitespace (Type text) noexcept { - while (text.isWhitespace()) - ++text; - + incrementToEndOfWhitespace (text); return text; } diff --git a/JuceLibraryCode/modules/juce_core/text/juce_Identifier.cpp b/JuceLibraryCode/modules/juce_core/text/juce_Identifier.cpp index cebb2dde..7fbd9109 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_Identifier.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_Identifier.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/text/juce_Identifier.h b/JuceLibraryCode/modules/juce_core/text/juce_Identifier.h index ea3377b1..67c2820e 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_Identifier.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_Identifier.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp index a2a34f4a..a59a6676 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -48,10 +48,6 @@ LocalisedStrings& LocalisedStrings::operator= (const LocalisedStrings& other) return *this; } -LocalisedStrings::~LocalisedStrings() -{ -} - //============================================================================== String LocalisedStrings::translate (const String& text) const { diff --git a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h index fa6beaff..b0c9b467 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -50,15 +50,15 @@ namespace juce "goodbye" = "au revoir" @endcode - If the strings need to contain a quote character, they can use '\"' instead, and + If the strings need to contain a quote character, they can use \" instead, and if the first non-whitespace character on a line isn't a quote, then it's ignored, (you can use this to add comments). Note that this is a singleton class, so don't create or destroy the object directly. - There's also a TRANS(text) macro defined to make it easy to use the this. + There's also a TRANS (text) macro defined to make it easy to use the this. - E.g. @code - printSomething (TRANS("hello")); + @code + printSomething (TRANS ("hello")); @endcode This macro is used in the JUCE classes themselves, so your application has a chance to @@ -90,7 +90,7 @@ class JUCE_API LocalisedStrings LocalisedStrings& operator= (const LocalisedStrings&); /** Destructor. */ - ~LocalisedStrings(); + ~LocalisedStrings() = default; //============================================================================== /** Selects the current set of mappings to be used by the system. diff --git a/JuceLibraryCode/modules/juce_core/text/juce_NewLine.h b/JuceLibraryCode/modules/juce_core/text/juce_NewLine.h index c215f35b..25fcd7eb 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_NewLine.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_NewLine.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/text/juce_String.cpp b/JuceLibraryCode/modules/juce_core/text/juce_String.cpp index 62d6f965..bf1c0423 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_String.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_String.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,10 +23,7 @@ namespace juce { -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4514 4996) -#endif +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4514 4996) NewLine newLine; @@ -42,46 +39,46 @@ NewLine newLine; using CharPointer_wchar_t = CharPointer_UTF32; #endif -static inline CharPointer_wchar_t castToCharPointer_wchar_t (const void* t) noexcept +static CharPointer_wchar_t castToCharPointer_wchar_t (const void* t) noexcept { return CharPointer_wchar_t (static_cast (t)); } //============================================================================== -// (Mirrors the structure of StringHolder, but without the atomic member, so can be statically constructed) -struct EmptyString +struct StringHolder { - int refCount; - size_t allocatedBytes; - String::CharPointerType::CharType text; + using CharPointerType = String::CharPointerType; + using CharType = String::CharPointerType::CharType; + + std::atomic refCount { 0 }; + size_t allocatedNumBytes = sizeof (CharType); + CharType text[1] { 0 }; }; -static const EmptyString emptyString { 0x3fffffff, sizeof (String::CharPointerType::CharType), 0 }; +constexpr StringHolder emptyString; //============================================================================== -class StringHolder +class StringHolderUtils { public: - StringHolder() = delete; + using CharPointerType = StringHolder::CharPointerType; + using CharType = StringHolder::CharType; - using CharPointerType = String::CharPointerType; - using CharType = String::CharPointerType::CharType; - - //============================================================================== static CharPointerType createUninitialisedBytes (size_t numBytes) { numBytes = (numBytes + 3) & ~(size_t) 3; - auto s = reinterpret_cast (new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]); - s->refCount.value = 0; + auto* bytes = new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]; + auto s = unalignedPointerCast (bytes); + s->refCount = 0; s->allocatedNumBytes = numBytes; - return CharPointerType (s->text); + return CharPointerType (unalignedPointerCast (bytes + offsetof (StringHolder, text))); } template static CharPointerType createFromCharPointer (const CharPointer text) { if (text.getAddress() == nullptr || text.isEmpty()) - return CharPointerType (&(emptyString.text)); + return CharPointerType (emptyString.text); auto bytesNeeded = sizeof (CharType) + CharPointerType::getBytesRequiredFor (text); auto dest = createUninitialisedBytes (bytesNeeded); @@ -93,7 +90,7 @@ class StringHolder static CharPointerType createFromCharPointer (const CharPointer text, size_t maxChars) { if (text.getAddress() == nullptr || text.isEmpty() || maxChars == 0) - return CharPointerType (&(emptyString.text)); + return CharPointerType (emptyString.text); auto end = text; size_t numChars = 0; @@ -114,7 +111,7 @@ class StringHolder static CharPointerType createFromCharPointer (const CharPointer start, const CharPointer end) { if (start.getAddress() == nullptr || start.isEmpty()) - return CharPointerType (&(emptyString.text)); + return CharPointerType (emptyString.text); auto e = start; int numChars = 0; @@ -134,7 +131,7 @@ class StringHolder static CharPointerType createFromCharPointer (const CharPointerType start, const CharPointerType end) { if (start.getAddress() == nullptr || start.isEmpty()) - return CharPointerType (&(emptyString.text)); + return CharPointerType (emptyString.text); auto numBytes = (size_t) (reinterpret_cast (end.getAddress()) - reinterpret_cast (start.getAddress())); @@ -160,7 +157,7 @@ class StringHolder ++(b->refCount); } - static inline void release (StringHolder* const b) noexcept + static void release (StringHolder* const b) noexcept { if (! isEmptyString (b)) if (--(b->refCount) == -1) @@ -172,9 +169,9 @@ class StringHolder release (bufferFromText (text)); } - static inline int getReferenceCount (const CharPointerType text) noexcept + static int getReferenceCount (const CharPointerType text) noexcept { - return bufferFromText (text)->refCount.get() + 1; + return bufferFromText (text)->refCount + 1; } //============================================================================== @@ -189,7 +186,7 @@ class StringHolder return newText; } - if (b->allocatedNumBytes >= numBytes && b->refCount.get() <= 0) + if (b->allocatedNumBytes >= numBytes && b->refCount <= 0) return text; auto newText = createUninitialisedBytes (jmax (b->allocatedNumBytes, numBytes)); @@ -204,22 +201,18 @@ class StringHolder return bufferFromText (text)->allocatedNumBytes; } - //============================================================================== - Atomic refCount; - size_t allocatedNumBytes; - CharType text[1]; - private: - static inline StringHolder* bufferFromText (const CharPointerType text) noexcept + StringHolderUtils() = delete; + ~StringHolderUtils() = delete; + + static StringHolder* bufferFromText (const CharPointerType charPtr) noexcept { - // (Can't use offsetof() here because of warnings about this not being a POD) - return reinterpret_cast (reinterpret_cast (text.getAddress()) - - (reinterpret_cast (reinterpret_cast (128)->text) - 128)); + return unalignedPointerCast (unalignedPointerCast (charPtr.getAddress()) - offsetof (StringHolder, text)); } - static inline bool isEmptyString (StringHolder* other) + static bool isEmptyString (StringHolder* other) { - return (other->refCount.get() & 0x30000000) != 0; + return other == &emptyString; } void compileTimeChecks() @@ -234,27 +227,22 @@ class StringHolder #else #error "native wchar_t size is unknown" #endif - - static_assert (sizeof (EmptyString) == sizeof (StringHolder), - "StringHolder is not large enough to hold an empty String"); } }; -JUCE_DECLARE_DEPRECATED_STATIC (const String String::empty;) - //============================================================================== -String::String() noexcept : text (&(emptyString.text)) +String::String() noexcept : text (emptyString.text) { } String::~String() noexcept { - StringHolder::release (text); + StringHolderUtils::release (text); } String::String (const String& other) noexcept : text (other.text) { - StringHolder::retain (text); + StringHolderUtils::retain (text); } void String::swapWith (String& other) noexcept @@ -264,20 +252,20 @@ void String::swapWith (String& other) noexcept void String::clear() noexcept { - StringHolder::release (text); - text = &(emptyString.text); + StringHolderUtils::release (text); + text = emptyString.text; } String& String::operator= (const String& other) noexcept { - StringHolder::retain (other.text); - StringHolder::release (text.atomicSwap (other.text)); + StringHolderUtils::retain (other.text); + StringHolderUtils::release (text.atomicSwap (other.text)); return *this; } String::String (String&& other) noexcept : text (other.text) { - other.text = &(emptyString.text); + other.text = emptyString.text; } String& String::operator= (String&& other) noexcept @@ -289,23 +277,23 @@ String& String::operator= (String&& other) noexcept inline String::PreallocationBytes::PreallocationBytes (const size_t num) noexcept : numBytes (num) {} String::String (const PreallocationBytes& preallocationSize) - : text (StringHolder::createUninitialisedBytes (preallocationSize.numBytes + sizeof (CharPointerType::CharType))) + : text (StringHolderUtils::createUninitialisedBytes (preallocationSize.numBytes + sizeof (CharPointerType::CharType))) { } void String::preallocateBytes (const size_t numBytesNeeded) { - text = StringHolder::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType)); + text = StringHolderUtils::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType)); } int String::getReferenceCount() const noexcept { - return StringHolder::getReferenceCount (text); + return StringHolderUtils::getReferenceCount (text); } //============================================================================== String::String (const char* const t) - : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t))) + : text (StringHolderUtils::createFromCharPointer (CharPointer_ASCII (t))) { /* If you get an assertion here, then you're trying to create a string from 8-bit data that contains values greater than 127. These can NOT be correctly converted to unicode @@ -328,7 +316,7 @@ String::String (const char* const t) } String::String (const char* const t, const size_t maxChars) - : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t), maxChars)) + : text (StringHolderUtils::createFromCharPointer (CharPointer_ASCII (t), maxChars)) { /* If you get an assertion here, then you're trying to create a string from 8-bit data that contains values greater than 127. These can NOT be correctly converted to unicode @@ -350,23 +338,23 @@ String::String (const char* const t, const size_t maxChars) jassert (t == nullptr || CharPointer_ASCII::isValidString (t, (int) maxChars)); } -String::String (const wchar_t* const t) : text (StringHolder::createFromCharPointer (castToCharPointer_wchar_t (t))) {} -String::String (const CharPointer_UTF8 t) : text (StringHolder::createFromCharPointer (t)) {} -String::String (const CharPointer_UTF16 t) : text (StringHolder::createFromCharPointer (t)) {} -String::String (const CharPointer_UTF32 t) : text (StringHolder::createFromCharPointer (t)) {} -String::String (const CharPointer_ASCII t) : text (StringHolder::createFromCharPointer (t)) {} +String::String (const wchar_t* const t) : text (StringHolderUtils::createFromCharPointer (castToCharPointer_wchar_t (t))) {} +String::String (const CharPointer_UTF8 t) : text (StringHolderUtils::createFromCharPointer (t)) {} +String::String (const CharPointer_UTF16 t) : text (StringHolderUtils::createFromCharPointer (t)) {} +String::String (const CharPointer_UTF32 t) : text (StringHolderUtils::createFromCharPointer (t)) {} +String::String (const CharPointer_ASCII t) : text (StringHolderUtils::createFromCharPointer (t)) {} -String::String (CharPointer_UTF8 t, size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} -String::String (CharPointer_UTF16 t, size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} -String::String (CharPointer_UTF32 t, size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} -String::String (const wchar_t* t, size_t maxChars) : text (StringHolder::createFromCharPointer (castToCharPointer_wchar_t (t), maxChars)) {} +String::String (CharPointer_UTF8 t, size_t maxChars) : text (StringHolderUtils::createFromCharPointer (t, maxChars)) {} +String::String (CharPointer_UTF16 t, size_t maxChars) : text (StringHolderUtils::createFromCharPointer (t, maxChars)) {} +String::String (CharPointer_UTF32 t, size_t maxChars) : text (StringHolderUtils::createFromCharPointer (t, maxChars)) {} +String::String (const wchar_t* t, size_t maxChars) : text (StringHolderUtils::createFromCharPointer (castToCharPointer_wchar_t (t), maxChars)) {} -String::String (CharPointer_UTF8 start, CharPointer_UTF8 end) : text (StringHolder::createFromCharPointer (start, end)) {} -String::String (CharPointer_UTF16 start, CharPointer_UTF16 end) : text (StringHolder::createFromCharPointer (start, end)) {} -String::String (CharPointer_UTF32 start, CharPointer_UTF32 end) : text (StringHolder::createFromCharPointer (start, end)) {} +String::String (CharPointer_UTF8 start, CharPointer_UTF8 end) : text (StringHolderUtils::createFromCharPointer (start, end)) {} +String::String (CharPointer_UTF16 start, CharPointer_UTF16 end) : text (StringHolderUtils::createFromCharPointer (start, end)) {} +String::String (CharPointer_UTF32 start, CharPointer_UTF32 end) : text (StringHolderUtils::createFromCharPointer (start, end)) {} -String::String (const std::string& s) : text (StringHolder::createFromFixedLength (s.data(), s.size())) {} -String::String (StringRef s) : text (StringHolder::createFromCharPointer (s.text)) {} +String::String (const std::string& s) : text (StringHolderUtils::createFromFixedLength (s.data(), s.size())) {} +String::String (StringRef s) : text (StringHolderUtils::createFromCharPointer (s.text)) {} String String::charToString (juce_wchar character) { @@ -393,7 +381,7 @@ namespace NumberToStringConverters do { - *--t = '0' + (char) (v % 10); + *--t = static_cast ('0' + (char) (v % 10)); v /= 10; } while (v > 0); @@ -451,7 +439,7 @@ namespace NumberToStringConverters return printDigits (t, v); } - struct StackArrayStream : public std::basic_streambuf> + struct StackArrayStream final : public std::basic_streambuf> { explicit StackArrayStream (char* d) { @@ -492,7 +480,7 @@ namespace NumberToStringConverters char buffer [charsNeededForInt]; auto* end = buffer + numElementsInArray (buffer); auto* start = numberToString (end, number); - return StringHolder::createFromFixedLength (start, (size_t) (end - start - 1)); + return StringHolderUtils::createFromFixedLength (start, (size_t) (end - start - 1)); } static String::CharPointerType createFromDouble (double number, int numberOfDecimalPlaces, bool useScientificNotation) @@ -500,7 +488,7 @@ namespace NumberToStringConverters char buffer [charsNeededForDouble]; size_t len; auto start = doubleToString (buffer, number, numberOfDecimalPlaces, useScientificNotation, len); - return StringHolder::createFromFixedLength (start, len); + return StringHolderUtils::createFromFixedLength (start, len); } } @@ -801,7 +789,7 @@ namespace StringHelpers template inline String& operationAddAssign (String& str, const T number) { - char buffer [(sizeof(T) * 8) / 2]; + char buffer [(sizeof (T) * 8) / 2]; auto* end = buffer + numElementsInArray (buffer); auto* start = NumberToStringConverters::numberToString (end, number); @@ -815,9 +803,10 @@ namespace StringHelpers } } -String& String::operator+= (const int number) { return StringHelpers::operationAddAssign (*this, number); } -String& String::operator+= (const int64 number) { return StringHelpers::operationAddAssign (*this, number); } -String& String::operator+= (const uint64 number) { return StringHelpers::operationAddAssign (*this, number); } +String& String::operator+= (const int number) { return StringHelpers::operationAddAssign (*this, number); } +String& String::operator+= (const long number) { return StringHelpers::operationAddAssign (*this, number); } +String& String::operator+= (const int64 number) { return StringHelpers::operationAddAssign (*this, number); } +String& String::operator+= (const uint64 number) { return StringHelpers::operationAddAssign (*this, number); } //============================================================================== JUCE_API String JUCE_CALLTYPE operator+ (const char* s1, const String& s2) { String s (s1); return s += s2; } @@ -1282,7 +1271,7 @@ String String::replaceSection (int index, int numCharsToReplace, StringRef strin dest += newStringBytes; memcpy (dest, startOfRemainder.getAddress(), remainderBytes); dest += remainderBytes; - CharPointerType ((CharPointerType::CharType*) dest).writeNull(); + CharPointerType (unalignedPointerCast (dest)).writeNull(); return result; } @@ -1326,7 +1315,7 @@ struct StringCreationHelper } StringCreationHelper (const String::CharPointerType s) - : source (s), allocatedBytes (StringHolder::getAllocatedNumBytes (s)) + : source (s), allocatedBytes (StringHolderUtils::getAllocatedNumBytes (s)) { result.preallocateBytes (allocatedBytes); dest = result.getCharPointer(); @@ -1840,6 +1829,10 @@ String String::formattedRaw (const char* pf, ...) va_list args; va_start (args, pf); + #if JUCE_WINDOWS + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + #endif + #if JUCE_ANDROID HeapBlock temp (bufferSize); int num = (int) vsnprintf (temp.get(), bufferSize - 1, pf, args); @@ -1856,6 +1849,10 @@ String String::formattedRaw (const char* pf, ...) #endif (temp.get(), bufferSize - 1, wideCharVersion.toWideCharPointer(), args); #endif + + #if JUCE_WINDOWS + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + #endif va_end (args); if (num > 0) @@ -1892,7 +1889,7 @@ int String::getTrailingIntValue() const noexcept break; } - n += static_cast (mult) * (*t - '0'); + n += (int) (((juce_wchar) mult) * (*t - '0')); mult *= 10; } @@ -1912,7 +1909,7 @@ static String hexToString (Type v) do { *--t = hexDigits [(int) (v & 15)]; - v >>= 4; + v = static_cast (v >> 4); } while (v != 0); @@ -1953,8 +1950,8 @@ String String::toHexString (const void* const d, const int size, const int group return s; } -int String::getHexValue32() const noexcept { return CharacterFunctions::HexParser ::parse (text); } -int64 String::getHexValue64() const noexcept { return CharacterFunctions::HexParser::parse (text); } +int String::getHexValue32() const noexcept { return (int32) CharacterFunctions::HexParser::parse (text); } +int64 String::getHexValue64() const noexcept { return (int64) CharacterFunctions::HexParser::parse (text); } //============================================================================== static String getStringFromWindows1252Codepage (const char* data, size_t num) @@ -1985,7 +1982,7 @@ String String::createStringFromData (const void* const unknownData, int size) StringCreationHelper builder ((size_t) numChars); - auto src = reinterpret_cast (data + 2); + auto src = unalignedPointerCast (data + 2); if (CharPointer_UTF16::isByteOrderMarkBigEndian (data)) { @@ -2055,19 +2052,19 @@ struct StringEncodingConverter template <> struct StringEncodingConverter { - static CharPointer_UTF8 convert (const String& source) noexcept { return CharPointer_UTF8 (reinterpret_cast (source.getCharPointer().getAddress())); } + static CharPointer_UTF8 convert (const String& source) noexcept { return CharPointer_UTF8 (unalignedPointerCast (source.getCharPointer().getAddress())); } }; template <> struct StringEncodingConverter { - static CharPointer_UTF16 convert (const String& source) noexcept { return CharPointer_UTF16 (reinterpret_cast (source.getCharPointer().getAddress())); } + static CharPointer_UTF16 convert (const String& source) noexcept { return CharPointer_UTF16 (unalignedPointerCast (source.getCharPointer().getAddress())); } }; template <> struct StringEncodingConverter { - static CharPointer_UTF32 convert (const String& source) noexcept { return CharPointer_UTF32 (reinterpret_cast (source.getCharPointer().getAddress())); } + static CharPointer_UTF32 convert (const String& source) noexcept { return CharPointer_UTF32 (unalignedPointerCast (source.getCharPointer().getAddress())); } }; CharPointer_UTF8 String::toUTF8() const { return StringEncodingConverter::convert (*this); } @@ -2142,12 +2139,10 @@ String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) return {}; } -#if JUCE_MSVC - #pragma warning (pop) -#endif +JUCE_END_IGNORE_WARNINGS_MSVC //============================================================================== -StringRef::StringRef() noexcept : text ((const String::CharPointerType::CharType*) "\0\0\0") +StringRef::StringRef() noexcept : text (unalignedPointerCast ("\0\0\0")) { } @@ -2191,7 +2186,6 @@ StringRef::StringRef (const String& string) noexcept : text (string.getCharPoi StringRef::StringRef (const std::string& string) : StringRef (string.c_str()) {} //============================================================================== - static String reduceLengthOfFloatString (const String& input) { const auto start = input.getCharPointer(); @@ -2272,7 +2266,7 @@ static String serialiseDouble (double input) int intInput = (int) input; - if ((double) intInput == input) + if (exactlyEqual ((double) intInput, input)) return { input, 1 }; auto numberOfDecimalPlaces = [absInput] @@ -2305,6 +2299,18 @@ static String serialiseDouble (double input) return reduceLengthOfFloatString (String (input, numberOfDecimalPlaces)); } +//============================================================================== +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const String String::empty; + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif //============================================================================== //============================================================================== @@ -2313,7 +2319,7 @@ static String serialiseDouble (double input) #define STRINGIFY2(X) #X #define STRINGIFY(X) STRINGIFY2(X) -class StringTests : public UnitTest +class StringTests final : public UnitTest { public: StringTests() @@ -2414,12 +2420,12 @@ class StringTests : public UnitTest expect (s.compare (String ("012345678")) == 0); expect (s.compare (String ("012345679")) < 0); expect (s.compare (String ("012345676")) > 0); - expect (String("a").compareNatural ("A") == 0); - expect (String("A").compareNatural ("B") < 0); - expect (String("a").compareNatural ("B") < 0); - expect (String("10").compareNatural ("2") > 0); - expect (String("Abc 10").compareNatural ("aBC 2") > 0); - expect (String("Abc 1").compareNatural ("aBC 2") < 0); + expect (String ("a").compareNatural ("A") == 0); + expect (String ("A").compareNatural ("B") < 0); + expect (String ("a").compareNatural ("B") < 0); + expect (String ("10").compareNatural ("2") > 0); + expect (String ("Abc 10").compareNatural ("aBC 2") > 0); + expect (String ("Abc 1").compareNatural ("aBC 2") < 0); expect (s.substring (2, 3) == String::charToString (s[2])); expect (s.substring (0, 1) == String::charToString (s[0])); expect (s.getLastCharacter() == s [s.length() - 1]); @@ -2454,6 +2460,9 @@ class StringTests : public UnitTest expect (String (StringRef ("abc")) == StringRef ("abc")); expect (String ("abc") + StringRef ("def") == "abcdef"); + expect (String ("0x00").getHexValue32() == 0); + expect (String ("0x100").getHexValue32() == 256); + String s2 ("123"); s2 << ((int) 4) << ((short) 5) << "678" << L"9" << '0'; s2 += "xyz"; @@ -2558,21 +2567,23 @@ class StringTests : public UnitTest beginTest ("Numeric conversions"); expect (String().getIntValue() == 0); - expect (String().getDoubleValue() == 0.0); - expect (String().getFloatValue() == 0.0f); + expectEquals (String().getDoubleValue(), 0.0); + expectEquals (String().getFloatValue(), 0.0f); expect (s.getIntValue() == 12345678); expect (s.getLargeIntValue() == (int64) 12345678); - expect (s.getDoubleValue() == 12345678.0); - expect (s.getFloatValue() == 12345678.0f); + expectEquals (s.getDoubleValue(), 12345678.0); + expectEquals (s.getFloatValue(), 12345678.0f); expect (String (-1234).getIntValue() == -1234); expect (String ((int64) -1234).getLargeIntValue() == -1234); - expect (String (-1234.56).getDoubleValue() == -1234.56); - expect (String (-1234.56f).getFloatValue() == -1234.56f); + expectEquals (String (-1234.56).getDoubleValue(), -1234.56); + expectEquals (String (-1234.56f).getFloatValue(), -1234.56f); expect (String (std::numeric_limits::max()).getIntValue() == std::numeric_limits::max()); expect (String (std::numeric_limits::min()).getIntValue() == std::numeric_limits::min()); expect (String (std::numeric_limits::max()).getLargeIntValue() == std::numeric_limits::max()); expect (String (std::numeric_limits::min()).getLargeIntValue() == std::numeric_limits::min()); expect (("xyz" + s).getTrailingIntValue() == s.getIntValue()); + expect (String ("xyz-5").getTrailingIntValue() == -5); + expect (String ("-12345").getTrailingIntValue() == -12345); expect (s.getHexValue32() == 0x12345678); expect (s.getHexValue64() == (int64) 0x12345678); expect (String::toHexString (0x1234abcd).equalsIgnoreCase ("1234abcd")); @@ -2696,7 +2707,7 @@ class StringTests : public UnitTest expectEquals (s5.upToLastOccurrenceOf (String(), true, false), s5); expectEquals (s5.upToLastOccurrenceOf ("zword", true, false), s5); expectEquals (s5.upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1)); - expectEquals (s5.dropLastCharacters(1).upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1)); + expectEquals (s5.dropLastCharacters (1).upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1)); expectEquals (s5.upToLastOccurrenceOf ("Word", true, true), s5.dropLastCharacters (1)); expectEquals (s5.upToLastOccurrenceOf ("word", false, false), s5.dropLastCharacters (5)); expectEquals (s5.upToLastOccurrenceOf ("Word", false, true), s5.dropLastCharacters (5)); @@ -2935,6 +2946,17 @@ class StringTests : public UnitTest expectEquals (serialiseDouble (-test.first), "-" + test.second); } } + + { + beginTest ("Loops"); + + String str (CharPointer_UTF8 ("\xc2\xaf\\_(\xe3\x83\x84)_/\xc2\xaf")); + std::vector parts { 175, 92, 95, 40, 12484, 41, 95, 47, 175 }; + size_t index = 0; + + for (auto c : str) + expectEquals (c, parts[index++]); + } } }; diff --git a/JuceLibraryCode/modules/juce_core/text/juce_String.h b/JuceLibraryCode/modules/juce_core/text/juce_String.h index 9ee1b34d..4ba8244d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_String.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_String.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -20,6 +20,20 @@ ============================================================================== */ +#if ! defined (DOXYGEN) && (JUCE_MAC || JUCE_IOS) + // Annoyingly we can only forward-declare a typedef by forward-declaring the + // aliased type + #if __has_attribute(objc_bridge) + #define JUCE_CF_BRIDGED_TYPE(T) __attribute__ ((objc_bridge (T))) + #else + #define JUCE_CF_BRIDGED_TYPE(T) + #endif + + typedef const struct JUCE_CF_BRIDGED_TYPE(NSString) __CFString * CFStringRef; + + #undef JUCE_CF_BRIDGED_TYPE +#endif + namespace juce { @@ -293,13 +307,13 @@ class JUCE_API String final Note that there's also an isNotEmpty() method to help write readable code. @see containsNonWhitespaceChars() */ - inline bool isEmpty() const noexcept { return text.isEmpty(); } + bool isEmpty() const noexcept { return text.isEmpty(); } /** Returns true if the string contains at least one character. Note that there's also an isEmpty() method to help write readable code. @see containsNonWhitespaceChars() */ - inline bool isNotEmpty() const noexcept { return ! text.isEmpty(); } + bool isNotEmpty() const noexcept { return ! text.isEmpty(); } /** Resets this string to be empty. */ void clear() noexcept; @@ -905,6 +919,35 @@ class JUCE_API String final template static String formatted (const String& formatStr, Args... args) { return formattedRaw (formatStr.toRawUTF8(), args...); } + /** Returns an iterator pointing at the beginning of the string. */ + CharPointerType begin() const { return getCharPointer(); } + + /** Returns an iterator pointing at the terminating null of the string. + + Note that this has to find the terminating null before returning it, so prefer to + call this once before looping and then reuse the result, rather than calling 'end()' + each time through the loop. + + @code + String str = ...; + + // BEST + for (auto c : str) + DBG (c); + + // GOOD + for (auto ptr = str.begin(), end = str.end(); ptr != end; ++ptr) + DBG (*ptr); + + std::for_each (str.begin(), str.end(), [] (juce_wchar c) { DBG (c); }); + + // BAD + for (auto ptr = str.begin(); ptr != str.end(); ++ptr) + DBG (*ptr); + @endcode + */ + CharPointerType end() const { return begin().findTerminatingNull(); } + //============================================================================== // Numeric conversions.. @@ -1006,7 +1049,8 @@ class JUCE_API String final This will look for a value at the end of the string. e.g. for "321 xyz654" it will return 654; for "2 3 4" it'll return 4. - Negative numbers are not handled, so "xyz-5" returns 5. + If the string ends with a hyphen followed by numeric characters, the + return value will be negative. @see getIntValue */ @@ -1073,7 +1117,7 @@ class JUCE_API String final { jassert (numberOfSignificantFigures > 0); - if (number == 0) + if (exactlyEqual (number, DecimalType())) { if (numberOfSignificantFigures > 1) { @@ -1090,94 +1134,6 @@ class JUCE_API String final auto numDigitsBeforePoint = (int) std::ceil (std::log10 (number < 0 ? -number : number)); - #if JUCE_PROJUCER_LIVE_BUILD - auto doubleNumber = (double) number; - constexpr int bufferSize = 311; - char buffer[bufferSize]; - auto* ptr = &(buffer[0]); - auto* const safeEnd = ptr + (bufferSize - 1); - auto numSigFigsParsed = 0; - - auto writeToBuffer = [safeEnd] (char* destination, char data) - { - *destination++ = data; - - if (destination == safeEnd) - { - *destination = '\0'; - return true; - } - - return false; - }; - - auto truncateOrRound = [numberOfSignificantFigures] (double fractional, int sigFigsParsed) - { - return (sigFigsParsed == numberOfSignificantFigures - 1) ? (int) std::round (fractional) - : (int) fractional; - }; - - if (doubleNumber < 0) - { - doubleNumber *= -1; - *ptr++ = '-'; - } - - if (numDigitsBeforePoint > 0) - { - doubleNumber /= std::pow (10.0, numDigitsBeforePoint); - - while (numDigitsBeforePoint-- > 0) - { - if (numSigFigsParsed == numberOfSignificantFigures) - { - if (writeToBuffer (ptr++, '0')) - return buffer; - - continue; - } - - doubleNumber *= 10; - auto digit = truncateOrRound (doubleNumber, numSigFigsParsed); - - if (writeToBuffer (ptr++, (char) ('0' + digit))) - return buffer; - - ++numSigFigsParsed; - doubleNumber -= digit; - } - - if (numSigFigsParsed == numberOfSignificantFigures) - { - *ptr++ = '\0'; - return buffer; - } - } - else - { - *ptr++ = '0'; - } - - if (writeToBuffer (ptr++, '.')) - return buffer; - - while (numSigFigsParsed < numberOfSignificantFigures) - { - doubleNumber *= 10; - auto digit = truncateOrRound (doubleNumber, numSigFigsParsed); - - if (writeToBuffer (ptr++, (char) ('0' + digit))) - return buffer; - - if (numSigFigsParsed != 0 || digit != 0) - ++numSigFigsParsed; - - doubleNumber -= digit; - } - - *ptr++ = '\0'; - return buffer; - #else auto shift = numberOfSignificantFigures - numDigitsBeforePoint; auto factor = std::pow (10.0, shift); auto rounded = std::round (number * factor) / factor; @@ -1185,7 +1141,6 @@ class JUCE_API String final std::stringstream ss; ss << std::fixed << std::setprecision (std::max (shift, 0)) << rounded; return ss.str(); - #endif } //============================================================================== @@ -1195,7 +1150,7 @@ class JUCE_API String final that is returned must not be stored anywhere, as it can be deleted whenever the string changes. */ - inline CharPointerType getCharPointer() const noexcept { return text; } + CharPointerType getCharPointer() const noexcept { return text; } /** Returns a pointer to a UTF-8 version of this string. @@ -1371,15 +1326,13 @@ class JUCE_API String final int getReferenceCount() const noexcept; //============================================================================== - /* This was a static empty string object, but is now deprecated as it's too easy to accidentally - use it indirectly during a static constructor, leading to hard-to-find order-of-initialisation - problems. - @deprecated If you need an empty String object, just use String() or {}. - The only time you might miss having String::empty available might be if you need to return an - empty string from a function by reference, but if you need to do that, it's easy enough to use - a function-local static String object and return that, avoiding any order-of-initialisation issues. - */ - JUCE_DEPRECATED_STATIC (static const String empty;) + #if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN) + [[deprecated ("This was a static empty string object, but is now deprecated as it's too easy to accidentally " + "use it indirectly during a static constructor, leading to hard-to-find order-of-initialisation " + "problems. If you need an empty String object, just use String() or {}. For returning an empty " + "String from a function by reference, use a function-local static String object and return that.")]] + static const String empty; + #endif private: //============================================================================== @@ -1394,7 +1347,6 @@ class JUCE_API String final explicit String (const PreallocationBytes&); // This constructor preallocates a certain amount of memory size_t getByteOffsetOfEnd() const noexcept; - JUCE_DEPRECATED (String (const String&, size_t)); // This private cast operator should prevent strings being accidentally cast // to bools (this is possible because the compiler can add an implicit cast @@ -1544,7 +1496,7 @@ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, StringRef } // namespace juce -#if ! DOXYGEN +#ifndef DOXYGEN namespace std { template <> struct hash diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp index 9f776b27..46437377 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -89,10 +89,6 @@ StringArray& StringArray::operator= (StringArray&& other) noexcept return *this; } -StringArray::~StringArray() -{ -} - bool StringArray::operator== (const StringArray& other) const noexcept { return strings == other.strings; @@ -132,6 +128,11 @@ String& StringArray::getReference (int index) noexcept return strings.getReference (index); } +const String& StringArray::getReference (int index) const noexcept +{ + return strings.getReference (index); +} + void StringArray::add (String newString) { // NB: the local temp copy is to avoid a dangling pointer if the @@ -200,7 +201,7 @@ int StringArray::indexOf (StringRef stringToLookFor, bool ignoreCase, int i) con if (ignoreCase) { for (; i < numElements; ++i) - if (strings.getReference(i).equalsIgnoreCase (stringToLookFor)) + if (strings.getReference (i).equalsIgnoreCase (stringToLookFor)) return i; } else @@ -229,7 +230,7 @@ void StringArray::removeString (StringRef stringToRemove, bool ignoreCase) if (ignoreCase) { for (int i = size(); --i >= 0;) - if (strings.getReference(i).equalsIgnoreCase (stringToRemove)) + if (strings.getReference (i).equalsIgnoreCase (stringToRemove)) strings.remove (i); } else @@ -251,13 +252,13 @@ void StringArray::removeEmptyStrings (bool removeWhitespaceStrings) if (removeWhitespaceStrings) { for (int i = size(); --i >= 0;) - if (! strings.getReference(i).containsNonWhitespaceChars()) + if (! strings.getReference (i).containsNonWhitespaceChars()) strings.remove (i); } else { for (int i = size(); --i >= 0;) - if (strings.getReference(i).isEmpty()) + if (strings.getReference (i).isEmpty()) strings.remove (i); } } @@ -303,7 +304,7 @@ String StringArray::joinIntoString (StringRef separator, int start, int numberTo 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); + bytesNeeded += strings.getReference (i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType); String result; result.preallocateBytes (bytesNeeded); @@ -411,7 +412,7 @@ void StringArray::removeDuplicates (bool ignoreCase) { for (int i = 0; i < size() - 1; ++i) { - auto s = strings.getReference(i); + auto s = strings.getReference (i); for (int nextIndex = i + 1;;) { @@ -438,7 +439,7 @@ void StringArray::appendNumbersToDuplicates (bool ignoreCase, for (int i = 0; i < size() - 1; ++i) { - auto& s = strings.getReference(i); + auto& s = strings.getReference (i); auto nextIndex = indexOf (s, ignoreCase, i + 1); if (nextIndex >= 0) diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h index 44fb1f9a..308645ca 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -49,7 +49,8 @@ class JUCE_API StringArray /** Creates an array containing a list of strings. */ template - StringArray (StringRef firstValue, OtherElements... otherValues) : strings (firstValue, otherValues...) {} + StringArray (StringRef firstValue, OtherElements&&... otherValues) + : strings (firstValue, std::forward (otherValues)...) {} /** Creates an array containing a list of strings. */ StringArray (const std::initializer_list& strings); @@ -98,7 +99,7 @@ class JUCE_API StringArray StringArray (const wchar_t* const* strings, int numberOfStrings); /** Destructor. */ - ~StringArray(); + ~StringArray() = default; /** Copies the contents of another string array into this one */ StringArray& operator= (const StringArray&); @@ -152,6 +153,12 @@ class JUCE_API StringArray */ String& getReference (int index) noexcept; + /** Returns a reference to one of the strings in the array. + This lets you modify a string in-place in the array, but you must be sure that + the index is in-range. + */ + const String& getReference (int index) const noexcept; + /** Returns a pointer to the first String in the array. This method is provided for compatibility with standard C++ iteration mechanisms. */ diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp index fb9fc85d..2556ffef 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -34,10 +34,6 @@ StringPairArray::StringPairArray (const StringPairArray& other) { } -StringPairArray::~StringPairArray() -{ -} - StringPairArray& StringPairArray::operator= (const StringPairArray& other) { keys = other.keys; @@ -145,6 +141,11 @@ void StringPairArray::setIgnoresCase (bool shouldIgnoreCase) ignoreCase = shouldIgnoreCase; } +bool StringPairArray::getIgnoresCase() const noexcept +{ + return ignoreCase; +} + String StringPairArray::getDescription() const { String s; @@ -166,4 +167,145 @@ void StringPairArray::minimiseStorageOverheads() values.minimiseStorageOverheads(); } +template +void StringPairArray::addMapImpl (const Map& toAdd) +{ + // If we just called `set` for each item in `toAdd`, that would + // perform badly when adding to large StringPairArrays, as `set` + // has to loop through the whole container looking for matching keys. + // Instead, we use a temporary map to give us better lookup performance. + std::map contents; + + const auto normaliseKey = [this] (const String& key) + { + return ignoreCase ? key.toLowerCase() : key; + }; + + for (auto i = 0; i != size(); ++i) + contents.emplace (normaliseKey (getAllKeys().getReference (i)), i); + + for (const auto& pair : toAdd) + { + const auto key = normaliseKey (pair.first); + const auto it = contents.find (key); + + if (it != contents.cend()) + { + values.getReference (it->second) = pair.second; + } + else + { + contents.emplace (key, static_cast (contents.size())); + keys.add (pair.first); + values.add (pair.second); + } + } +} + +void StringPairArray::addUnorderedMap (const std::unordered_map& toAdd) { addMapImpl (toAdd); } +void StringPairArray::addMap (const std::map& toAdd) { addMapImpl (toAdd); } + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +static String operator""_S (const char* chars, size_t) +{ + return String { chars }; +} + +class StringPairArrayTests final : public UnitTest +{ +public: + StringPairArrayTests() + : UnitTest ("StringPairArray", UnitTestCategories::text) + {} + + void runTest() override + { + beginTest ("addMap respects case sensitivity of StringPairArray"); + { + StringPairArray insensitive { true }; + insensitive.addMap ({ { "duplicate", "a" }, + { "Duplicate", "b" } }); + + expect (insensitive.size() == 1); + expectEquals (insensitive["DUPLICATE"], "a"_S); + + StringPairArray sensitive { false }; + sensitive.addMap ({ { "duplicate", "a"_S }, + { "Duplicate", "b"_S } }); + + expect (sensitive.size() == 2); + expectEquals (sensitive["duplicate"], "a"_S); + expectEquals (sensitive["Duplicate"], "b"_S); + expectEquals (sensitive["DUPLICATE"], ""_S); + } + + beginTest ("addMap overwrites existing pairs"); + { + StringPairArray insensitive { true }; + insensitive.set ("key", "value"); + insensitive.addMap ({ { "KEY", "VALUE" } }); + + expect (insensitive.size() == 1); + expectEquals (insensitive.getAllKeys()[0], "key"_S); + expectEquals (insensitive.getAllValues()[0], "VALUE"_S); + + StringPairArray sensitive { false }; + sensitive.set ("key", "value"); + sensitive.addMap ({ { "KEY", "VALUE" }, + { "key", "another value" } }); + + expect (sensitive.size() == 2); + expect (sensitive.getAllKeys() == StringArray { "key", "KEY" }); + expect (sensitive.getAllValues() == StringArray { "another value", "VALUE" }); + } + + beginTest ("addMap doesn't change the order of existing keys"); + { + StringPairArray array; + array.set ("a", "a"); + array.set ("z", "z"); + array.set ("b", "b"); + array.set ("y", "y"); + array.set ("c", "c"); + + array.addMap ({ { "B", "B" }, + { "0", "0" }, + { "Z", "Z" } }); + + expect (array.getAllKeys() == StringArray { "a", "z", "b", "y", "c", "0" }); + expect (array.getAllValues() == StringArray { "a", "Z", "B", "y", "c", "0" }); + } + + beginTest ("addMap has equivalent behaviour to addArray"); + { + StringPairArray initial; + initial.set ("aaa", "aaa"); + initial.set ("zzz", "zzz"); + initial.set ("bbb", "bbb"); + + auto withAddMap = initial; + withAddMap.addMap ({ { "ZZZ", "ZZZ" }, + { "ddd", "ddd" } }); + + auto withAddArray = initial; + withAddArray.addArray ([] + { + StringPairArray toAdd; + toAdd.set ("ZZZ", "ZZZ"); + toAdd.set ("ddd", "ddd"); + return toAdd; + }()); + + expect (withAddMap == withAddArray); + } + } +}; + +static StringPairArrayTests stringPairArrayTests; + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h index aab5b049..6e0adf80 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -42,7 +42,7 @@ class JUCE_API StringPairArray StringPairArray (const StringPairArray& other); /** Destructor. */ - ~StringPairArray(); + ~StringPairArray() = default; /** Copies the contents of another string array into this one */ StringPairArray& operator= (const StringPairArray& other); @@ -124,6 +124,10 @@ class JUCE_API StringPairArray */ void setIgnoresCase (bool shouldIgnoreCase); + /** Indicates whether a case-insensitive search is used when looking up a key string. + */ + bool getIgnoresCase() const noexcept; + //============================================================================== /** Returns a descriptive string containing the items. This is handy for dumping the contents of an array. @@ -139,9 +143,18 @@ class JUCE_API StringPairArray */ void minimiseStorageOverheads(); + //============================================================================== + /** Adds the contents of a map to this StringPairArray. */ + void addMap (const std::map& mapToAdd); + + /** Adds the contents of an unordered map to this StringPairArray. */ + void addUnorderedMap (const std::unordered_map& mapToAdd); private: //============================================================================== + template + void addMapImpl (const Map& mapToAdd); + StringArray keys, values; bool ignoreCase; diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp index bf004da6..e1cc5981 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,7 +28,6 @@ static const uint32 garbageCollectionInterval = 30000; StringPool::StringPool() noexcept : lastGarbageCollectionTime (0) {} -StringPool::~StringPool() {} struct StartEndString { @@ -150,7 +149,7 @@ void StringPool::garbageCollect() const ScopedLock sl (lock); for (int i = strings.size(); --i >= 0;) - if (strings.getReference(i).getReferenceCount() == 1) + if (strings.getReference (i).getReferenceCount() == 1) strings.remove (i); lastGarbageCollectionTime = Time::getApproximateMillisecondCounter(); diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h b/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h index 7e1f513b..a43b557d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -43,9 +43,6 @@ class JUCE_API StringPool /** Creates an empty pool. */ StringPool() noexcept; - /** Destructor */ - ~StringPool(); - //============================================================================== /** Returns a pointer to a shared copy of the string that is passed in. The pool will always return the same String object when asked for a string that matches it. diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h b/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h index 547a7383..5d7669ac 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp b/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp index d65b6e25..906afde5 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -116,7 +116,10 @@ struct TextDiffHelpers if (scratchSpace < 4096) { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255) auto* scratch = (int*) alloca (scratchSpace); + JUCE_END_IGNORE_WARNINGS_MSVC + return findLongestCommonSubstring (a, lenA, indexInA, b, lenB, indexInB, scratchSpace, scratch); } @@ -221,7 +224,7 @@ String TextDiff::Change::appliedTo (const String& text) const noexcept //============================================================================== #if JUCE_UNIT_TESTS -class DiffTests : public UnitTest +class DiffTests final : public UnitTest { public: DiffTests() diff --git a/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.h b/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.h index 84b56625..3d3a266a 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp index 98dc84af..8e81aeaa 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -85,7 +85,7 @@ String ChildProcess::readAllProcessOutput() //============================================================================== #if JUCE_UNIT_TESTS -class ChildProcessTests : public UnitTest +class ChildProcessTests final : public UnitTest { public: ChildProcessTests() @@ -96,7 +96,7 @@ class ChildProcessTests : public UnitTest { beginTest ("Child Processes"); - #if JUCE_WINDOWS || JUCE_MAC || JUCE_LINUX + #if JUCE_WINDOWS || JUCE_MAC || JUCE_LINUX || JUCE_BSD ChildProcess p; #if JUCE_WINDOWS diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.h b/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.h index e2c46a6b..f0ded531 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_CriticalSection.h b/JuceLibraryCode/modules/juce_core/threads/juce_CriticalSection.h index 2d929ad7..8dad6c4d 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_CriticalSection.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_CriticalSection.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -106,9 +106,9 @@ class JUCE_API CriticalSection // a block of memory here that's big enough to be used internally as a windows // CRITICAL_SECTION structure. #if JUCE_64BIT - uint8 lock[44]; + std::aligned_storage_t<44, 8> lock; #else - uint8 lock[24]; + std::aligned_storage_t<24, 8> lock; #endif #else mutable pthread_mutex_t lock; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_DynamicLibrary.h b/JuceLibraryCode/modules/juce_core/threads/juce_DynamicLibrary.h index f4f9a84e..a2184876 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_DynamicLibrary.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_DynamicLibrary.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp index c4ea8274..5384e45c 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,13 +23,411 @@ namespace juce { -HighResolutionTimer::HighResolutionTimer() { pimpl.reset (new Pimpl (*this)); } -HighResolutionTimer::~HighResolutionTimer() { stopTimer(); } +//============================================================================== +class HighResolutionTimer::Impl : private PlatformTimerListener +{ +public: + explicit Impl (HighResolutionTimer& o) + : owner { o } {} + + void startTimer (int newIntervalMs) + { + shouldCancelCallbacks.store (true); + + const auto shouldWaitForPendingCallbacks = [&] + { + const std::scoped_lock lock { timerMutex }; + + if (timer.getIntervalMs() > 0) + timer.cancelTimer(); + + jassert (timer.getIntervalMs() == 0); + + if (newIntervalMs > 0) + timer.startTimer (jmax (0, newIntervalMs)); + + return callbackThreadId != std::this_thread::get_id() + && timer.getIntervalMs() <= 0; + }(); + + if (shouldWaitForPendingCallbacks) + std::scoped_lock lock { callbackMutex }; + } + + int getIntervalMs() const + { + const std::scoped_lock lock { timerMutex }; + return timer.getIntervalMs(); + } + + bool isTimerRunning() const + { + return getIntervalMs() > 0; + } + +private: + void onTimerExpired() final + { + callbackThreadId.store (std::this_thread::get_id()); + + { + std::scoped_lock lock { callbackMutex }; + + if (isTimerRunning()) + { + try + { + owner.hiResTimerCallback(); + } + catch (...) + { + // Exceptions thrown in a timer callback won't be + // propagated to the main thread, it's best to find + // a way to avoid them if possible + jassertfalse; + } + } + } + + callbackThreadId.store ({}); + } + + HighResolutionTimer& owner; + mutable std::mutex timerMutex; + std::mutex callbackMutex; + std::atomic callbackThreadId{}; + std::atomic shouldCancelCallbacks { false }; + PlatformTimer timer { *this }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Impl) + JUCE_DECLARE_NON_MOVEABLE (Impl) +}; + +//============================================================================== +HighResolutionTimer::HighResolutionTimer() + : impl (std::make_unique (*this)) {} + +HighResolutionTimer::~HighResolutionTimer() +{ + // You *must* call stopTimer from the derived class destructor to + // avoid data races on the timer's vtable + jassert (! isTimerRunning()); + stopTimer(); +} + +void HighResolutionTimer::startTimer (int newIntervalMs) +{ + impl->startTimer (newIntervalMs); +} + +void HighResolutionTimer::stopTimer() +{ + impl->startTimer (0); +} + +int HighResolutionTimer::getTimerInterval() const noexcept +{ + return impl->getIntervalMs(); +} + +bool HighResolutionTimer::isTimerRunning() const noexcept +{ + return impl->isTimerRunning(); +} + +//============================================================================== +#if JUCE_UNIT_TESTS + +class HighResolutionTimerTests final : public UnitTest +{ +public: + HighResolutionTimerTests() + : UnitTest ("HighResolutionTimer", UnitTestCategories::threads) {} + + void runTest() override + { + constexpr int maximumTimeoutMs {30'000}; + + beginTest ("Start/stop a timer"); + { + WaitableEvent timerFiredOnce; + WaitableEvent timerFiredTwice; + + Timer timer {[&, callbackCount = 0]() mutable + { + switch (++callbackCount) + { + case 1: timerFiredOnce.signal(); return; + case 2: timerFiredTwice.signal(); return; + default: return; + } + }}; + + expect (! timer.isTimerRunning()); + expect (timer.getTimerInterval() == 0); + + timer.startTimer (1); + expect (timer.isTimerRunning()); + expect (timer.getTimerInterval() == 1); + expect (timerFiredOnce.wait (maximumTimeoutMs)); + expect (timerFiredTwice.wait (maximumTimeoutMs)); + + timer.stopTimer(); + expect (! timer.isTimerRunning()); + expect (timer.getTimerInterval() == 0); + } + + beginTest ("Stop a timer from the timer callback"); + { + WaitableEvent stoppedTimer; + + auto timerCallback = [&] (Timer& timer) + { + expect (timer.isTimerRunning()); + timer.stopTimer(); + expect (! timer.isTimerRunning()); + stoppedTimer.signal(); + }; + + Timer timer {[&]{ timerCallback (timer); }}; + timer.startTimer (1); + expect (stoppedTimer.wait (maximumTimeoutMs)); + } + + beginTest ("Restart a timer from the timer callback"); + { + WaitableEvent restartTimer; + WaitableEvent timerRestarted; + WaitableEvent timerFiredAfterRestart; + + Timer timer {[&, callbackCount = 0]() mutable + { + switch (++callbackCount) + { + case 1: + expect (restartTimer.wait (maximumTimeoutMs)); + expect (timer.getTimerInterval() == 1); + + timer.startTimer (2); + expect (timer.getTimerInterval() == 2); + timerRestarted.signal(); + return; + + case 2: + expect (timer.getTimerInterval() == 2); + timerFiredAfterRestart.signal(); + return; + + default: + return; + } + }}; + + timer.startTimer (1); + expect (timer.getTimerInterval() == 1); + + restartTimer.signal(); + expect (timerRestarted.wait (maximumTimeoutMs)); + expect (timer.getTimerInterval() == 2); + expect (timerFiredAfterRestart.wait (maximumTimeoutMs)); + + timer.stopTimer(); + } + + beginTest ("Calling stopTimer on a timer, waits for any timer callbacks to finish"); + { + WaitableEvent timerCallbackStarted; + WaitableEvent stoppingTimer; + std::atomic timerCallbackFinished { false }; + + Timer timer {[&, callbackCount = 0]() mutable + { + switch (++callbackCount) + { + case 1: + timerCallbackStarted.signal(); + expect (stoppingTimer.wait (maximumTimeoutMs)); + Thread::sleep (10); + timerCallbackFinished = true; + return; + + default: + return; + } + }}; + + timer.startTimer (1); + expect (timerCallbackStarted.wait (maximumTimeoutMs)); + + stoppingTimer.signal(); + timer.stopTimer(); + expect (timerCallbackFinished); + } + + beginTest ("Calling stopTimer on a timer, waits for any timer callbacks to finish, even if the timer callback calls stopTimer first"); + { + WaitableEvent stoppedFromInsideTimerCallback; + WaitableEvent stoppingFromOutsideTimerCallback; + std::atomic timerCallbackFinished { false }; + + Timer timer {[&]() + { + timer.stopTimer(); + stoppedFromInsideTimerCallback.signal(); + expect (stoppingFromOutsideTimerCallback.wait (maximumTimeoutMs)); + Thread::sleep (10); + timerCallbackFinished = true; + + }}; + + timer.startTimer (1); + expect (stoppedFromInsideTimerCallback.wait (maximumTimeoutMs)); + + stoppingFromOutsideTimerCallback.signal(); + timer.stopTimer(); + expect (timerCallbackFinished); + } + + beginTest ("Adjusting a timer period from outside the timer callback doesn't cause data races"); + { + WaitableEvent timerCallbackStarted; + WaitableEvent timerRestarted; + WaitableEvent timerFiredAfterRestart; + std::atomic lastCallbackCount {0}; + + Timer timer {[&, callbackCount = 0]() mutable + { + switch (++callbackCount) + { + case 1: + expect (timer.getTimerInterval() == 1); + timerCallbackStarted.signal(); + Thread::sleep (10); + lastCallbackCount = 1; + return; + + case 2: + expect (timerRestarted.wait (maximumTimeoutMs)); + expect (timer.getTimerInterval() == 2); + lastCallbackCount = 2; + timerFiredAfterRestart.signal(); + return; + + default: + return; + } + }}; + + timer.startTimer (1); + expect (timerCallbackStarted.wait (maximumTimeoutMs)); + + timer.startTimer (2); + timerRestarted.signal(); + + expect (timerFiredAfterRestart.wait (maximumTimeoutMs)); + expect (lastCallbackCount == 2); + + timer.stopTimer(); + expect (lastCallbackCount == 2); + } + + beginTest ("A timer can be restarted externally, after being stopped internally"); + { + WaitableEvent timerStopped; + WaitableEvent timerFiredAfterRestart; + + Timer timer {[&, callbackCount = 0]() mutable + { + switch (++callbackCount) + { + case 1: + timer.stopTimer(); + timerStopped.signal(); + return; + + case 2: + timerFiredAfterRestart.signal(); + return; + + default: + return; + } + }}; + + expect (! timer.isTimerRunning()); + timer.startTimer (1); + expect (timer.isTimerRunning()); + + expect (timerStopped.wait (maximumTimeoutMs)); + expect (! timer.isTimerRunning()); + + timer.startTimer (1); + expect (timer.isTimerRunning()); + expect (timerFiredAfterRestart.wait (maximumTimeoutMs)); + } + + beginTest ("Calls to `startTimer` and `getTimerInterval` succeed while a callback is blocked"); + { + WaitableEvent timerBlocked; + WaitableEvent unblockTimer; + + Timer timer {[&] + { + timerBlocked.signal(); + unblockTimer.wait(); + timer.stopTimer(); + }}; + + timer.startTimer (1); + timerBlocked.wait(); + + expect (timer.getTimerInterval() == 1); + timer.startTimer (2); + expect (timer.getTimerInterval() == 2); + + unblockTimer.signal(); + timer.stopTimer(); + } + + beginTest ("Stress test"); + { + constexpr auto maxNumTimers { 100 }; + + std::vector> timers; + timers.reserve (maxNumTimers); + + for (int i = 0; i < maxNumTimers; ++i) + { + auto timer = std::make_unique ([]{}); + timer->startTimer (1); + + if (! timer->isTimerRunning()) + break; + + timers.push_back (std::move (timer)); + } + + expect (timers.size() >= 16); + } + } + + class Timer final : public HighResolutionTimer + { + public: + explicit Timer (std::function fn) + : callback (std::move (fn)) {} + + ~Timer() override { stopTimer(); } + + void hiResTimerCallback() override { callback(); } + + private: + std::function callback; + }; +}; -void HighResolutionTimer::startTimer (int periodMs) { pimpl->start (jmax (1, periodMs)); } -void HighResolutionTimer::stopTimer() { pimpl->stop(); } +static HighResolutionTimerTests highResolutionTimerTests; -bool HighResolutionTimer::isTimerRunning() const noexcept { return pimpl->periodMs != 0; } -int HighResolutionTimer::getTimerInterval() const noexcept { return pimpl->periodMs; } +#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h index ad4aaea3..00fc1245 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -32,8 +32,8 @@ namespace juce You should only use this class in situations where you really need accuracy, because unlike the normal Timer class, which is very lightweight and cheap - to start/stop, the HighResolutionTimer will use far more resources, and - starting/stopping it may involve launching and killing threads. + the HighResolutionTimer will use far more resources and require thread + safety considerations. @see Timer @@ -57,20 +57,29 @@ class JUCE_API HighResolutionTimer This will be called on a dedicated timer thread, so make sure your implementation is thread-safe! + On some platforms the dedicated timer thread may be shared with + other HighResolutionTimer's so aim to complete any work in this + callback as fast as possible. + It's perfectly ok to call startTimer() or stopTimer() from within this - callback to change the subsequent intervals. + callback to change the subsequent intervals. However, if you call + stopTimer() in the callback it's still best practice to call stopTimer() + from the destructor in order to avoid data races. */ virtual void hiResTimerCallback() = 0; //============================================================================== /** Starts the timer and sets the length of interval required. - If the timer is already started, this will reset its counter, so the - time between calling this method and the next timer callback will not be - less than the interval length passed in. + If the timer has already started, this will reset the timer, so the + time between calling this method and the next timer callback + will not be less than the interval length passed in. + + In exceptional circumstances the dedicated timer thread may not start, + if this is a potential concern for your use case, you can call isTimerRunning() + to confirm if the timer actually started. - @param intervalInMilliseconds the interval to use (any values less than 1 will be - rounded up to 1) + @param intervalInMilliseconds the interval to use (a value of zero or less will stop the timer) */ void startTimer (int intervalInMilliseconds); @@ -79,6 +88,9 @@ class JUCE_API HighResolutionTimer This method may block while it waits for pending callbacks to complete. Once it returns, no more callbacks will be made. If it is called from the timer's own thread, it will cancel the timer after the current callback returns. + + To prevent data races it's normally best practice to call this in the derived classes + destructor, even if stopTimer() was called in the hiResTimerCallback(). */ void stopTimer(); @@ -93,8 +105,8 @@ class JUCE_API HighResolutionTimer int getTimerInterval() const noexcept; private: - struct Pimpl; - std::unique_ptr pimpl; + class Impl; + std::unique_ptr impl; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HighResolutionTimer) }; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_InterProcessLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_InterProcessLock.h index 756814ca..05563b27 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_InterProcessLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_InterProcessLock.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_Process.h b/JuceLibraryCode/modules/juce_core/threads/juce_Process.h index c23cfa5b..cc8708cd 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_Process.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_Process.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -50,7 +50,7 @@ class JUCE_API Process @param priority the process priority, where 0=low, 1=normal, 2=high, 3=realtime */ - static void JUCE_CALLTYPE setPriority (const ProcessPriority priority); + static void JUCE_CALLTYPE setPriority (ProcessPriority priority); /** Kills the current process immediately. @@ -106,8 +106,8 @@ class JUCE_API Process const String& bodyText, const StringArray& filesToAttach); - #if JUCE_WINDOWS || DOXYGEN //============================================================================== + #if JUCE_WINDOWS || DOXYGEN /** WINDOWS ONLY - This returns the HINSTANCE of the current module. The return type is a void* to avoid being dependent on windows.h - just cast @@ -133,14 +133,14 @@ class JUCE_API Process static void JUCE_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) noexcept; #endif - #if (JUCE_MAC && JUCE_MODULE_AVAILABLE_juce_gui_basics) || DOXYGEN //============================================================================== + #if JUCE_MAC || DOXYGEN /** OSX ONLY - Shows or hides the OSX dock icon for this app. */ static void setDockIconVisible (bool isVisible); #endif - #if JUCE_MAC || JUCE_LINUX || DOXYGEN //============================================================================== + #if JUCE_MAC || JUCE_LINUX || JUCE_BSD || DOXYGEN /** UNIX ONLY - Attempts to use setrlimit to change the maximum number of file handles that the app can open. Pass 0 or less as the parameter to mean 'infinite'. Returns true if it succeeds. diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp index b7d4e54e..cc972343 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h index 1bfea5e1..45eed76c 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -74,7 +74,7 @@ class JUCE_API ReadWriteLock thread has it locked for writing, then this will fail and return false. @returns true if the lock is successfully gained. - @see exitRead, ScopedReadLock + @see exitRead, ScopedTryReadLock */ bool tryEnterRead() const noexcept; @@ -106,7 +106,7 @@ class JUCE_API ReadWriteLock to obtain the lock. @returns true if the lock is successfully gained. - @see enterWrite + @see enterWrite, ScopedTryWriteLock */ bool tryEnterWrite() const noexcept; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedLock.h index 3d33dd49..ede6e910 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedLock.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedReadLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedReadLock.h index 064d8576..cc5a73cd 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedReadLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedReadLock.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -81,4 +81,90 @@ class JUCE_API ScopedReadLock JUCE_DECLARE_NON_COPYABLE (ScopedReadLock) }; +//============================================================================== +/** + Automatically locks and unlocks a ReadWriteLock object. + + Use one of these as a local variable to control access to a ReadWriteLock. + + e.g. @code + + ReadWriteLock myLock; + + for (;;) + { + const ScopedTryReadLock myScopedTryLock (myLock); + + // Unlike using a ScopedReadLock, this may fail to actually get the lock, so you + // should test this with the isLocked() method before doing your thread-unsafe + // action. + + if (myScopedTryLock.isLocked()) + { + ...do some stuff... + } + else + { + ..our attempt at locking failed because a write lock has already been issued.. + } + + // myLock gets unlocked here (if it was locked). + } + @endcode + + @see ReadWriteLock, ScopedTryWriteLock + + @tags{Core} +*/ +class JUCE_API ScopedTryReadLock +{ +public: + //============================================================================== + /** Creates a ScopedTryReadLock and calls ReadWriteLock::tryEnterRead() as soon as it is + created. When the ScopedTryReadLock object is destructed, the ReadWriteLock will be unlocked + (if it was successfully acquired). + + Make sure this object is created and destructed by the same thread, otherwise there are no + guarantees what will happen! Best just to use it as a local stack object, rather than creating + one with the new() operator. + */ + explicit ScopedTryReadLock (ReadWriteLock& lockIn) + : ScopedTryReadLock (lockIn, true) {} + + /** Creates a ScopedTryReadLock. + + If acquireLockOnInitialisation is true then as soon as it is created, this will call + ReadWriteLock::tryEnterRead(), and when the ScopedTryReadLock object is destructed, the + ReadWriteLock will be unlocked (if it was successfully acquired). + + Make sure this object is created and destructed by the same thread, otherwise there are no + guarantees what will happen! Best just to use it as a local stack object, rather than creating + one with the new() operator. + */ + ScopedTryReadLock (ReadWriteLock& lockIn, bool acquireLockOnInitialisation) noexcept + : lock (lockIn), lockWasSuccessful (acquireLockOnInitialisation && lock.tryEnterRead()) {} + + /** Destructor. + + The ReadWriteLock's exitRead() method will be called when the destructor is called. + + Make sure this object is created and destructed by the same thread, otherwise there are no + guarantees what will happen! + */ + ~ScopedTryReadLock() noexcept { if (lockWasSuccessful) lock.exitRead(); } + + /** Returns true if the mutex was successfully locked. */ + bool isLocked() const noexcept { return lockWasSuccessful; } + + /** Retry gaining the lock by calling tryEnter on the underlying lock. */ + bool retryLock() noexcept { return lockWasSuccessful = lock.tryEnterRead(); } + +private: + //============================================================================== + ReadWriteLock& lock; + bool lockWasSuccessful; + + JUCE_DECLARE_NON_COPYABLE (ScopedTryReadLock) +}; + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedWriteLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedWriteLock.h index 117c6ca2..8f2678cd 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedWriteLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedWriteLock.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -81,4 +81,90 @@ class JUCE_API ScopedWriteLock JUCE_DECLARE_NON_COPYABLE (ScopedWriteLock) }; +//============================================================================== +/** + Automatically locks and unlocks a ReadWriteLock object. + + Use one of these as a local variable to control access to a ReadWriteLock. + + e.g. @code + + ReadWriteLock myLock; + + for (;;) + { + const ScopedTryWriteLock myScopedTryLock (myLock); + + // Unlike using a ScopedWriteLock, this may fail to actually get the lock, so you + // should test this with the isLocked() method before doing your thread-unsafe + // action. + + if (myScopedTryLock.isLocked()) + { + ...do some stuff... + } + else + { + ..our attempt at locking failed because some other thread has already locked the object.. + } + + // myLock gets unlocked here (if it was locked). + } + @endcode + + @see ReadWriteLock, ScopedTryWriteLock + + @tags{Core} +*/ +class JUCE_API ScopedTryWriteLock +{ +public: + //============================================================================== + /** Creates a ScopedTryWriteLock and calls ReadWriteLock::tryEnterWrite() immediately. + When the ScopedTryWriteLock object is destructed, the ReadWriteLock will be unlocked + (if it was successfully acquired). + + Make sure this object is created and destructed by the same thread, otherwise there are no + guarantees what will happen! Best just to use it as a local stack object, rather than creating + one with the new() operator. + */ + ScopedTryWriteLock (ReadWriteLock& lockIn) noexcept + : ScopedTryWriteLock (lockIn, true) {} + + /** Creates a ScopedTryWriteLock. + + If acquireLockOnInitialisation is true then as soon as it is created, this will call + ReadWriteLock::tryEnterWrite(), and when the ScopedTryWriteLock object is destructed, the + ReadWriteLock will be unlocked (if it was successfully acquired). + + Make sure this object is created and destructed by the same thread, otherwise there are no + guarantees what will happen! Best just to use it as a local stack object, rather than creating + one with the new() operator. + */ + ScopedTryWriteLock (ReadWriteLock& lockIn, bool acquireLockOnInitialisation) noexcept + : lock (lockIn), lockWasSuccessful (acquireLockOnInitialisation && lock.tryEnterWrite()) {} + + /** Destructor. + + The ReadWriteLock's exitWrite() method will be called when the destructor is called. + + Make sure this object is created and destructed by the same thread, + otherwise there are no guarantees what will happen! + */ + ~ScopedTryWriteLock() noexcept { if (lockWasSuccessful) lock.exitWrite(); } + + /** Returns true if the mutex was successfully locked. */ + bool isLocked() const noexcept { return lockWasSuccessful; } + + /** Retry gaining the lock by calling tryEnter on the underlying lock. */ + bool retryLock() noexcept { return lockWasSuccessful = lock.tryEnterWrite(); } + +private: + //============================================================================== + ReadWriteLock& lock; + bool lockWasSuccessful; + + JUCE_DECLARE_NON_COPYABLE (ScopedTryWriteLock) +}; + } diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_SpinLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_SpinLock.h index 6b9265fd..1e92ab3e 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_SpinLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_SpinLock.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -75,6 +75,9 @@ class JUCE_API SpinLock /** Provides the type of scoped unlocker to use with a SpinLock. */ using ScopedUnlockType = GenericScopedUnlock; + /** Provides the type of scoped try-lock to use for locking a SpinLock. */ + using ScopedTryLockType = GenericScopedTryLock; + private: //============================================================================== mutable Atomic lock; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp index 361fb4ee..942ecbdf 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -22,9 +22,9 @@ namespace juce { - -Thread::Thread (const String& name, size_t stackSize) - : threadName (name), threadStackSize (stackSize) +//============================================================================== +Thread::Thread (const String& name, size_t stackSize) : threadName (name), + threadStackSize (stackSize) { } @@ -48,7 +48,7 @@ Thread::~Thread() //============================================================================== // Use a ref-counted object to hold this shared data, so that it can outlive its static // shared pointer when threads are still running during static shutdown. -struct CurrentThreadHolder : public ReferenceCountedObject +struct CurrentThreadHolder final : public ReferenceCountedObject { CurrentThreadHolder() noexcept {} @@ -84,9 +84,11 @@ void Thread::threadEntryPoint() if (threadName.isNotEmpty()) setCurrentThreadName (threadName); + // This 'startSuspensionEvent' protects 'threadId' which is initialised after the platform's native 'CreateThread' method. + // This ensures it has been initialised correctly before it reaches this point. if (startSuspensionEvent.wait (10000)) { - jassert (getCurrentThreadId() == threadId.get()); + jassert (getCurrentThreadId() == threadId); if (affinityMask != 0) setCurrentThreadAffinityMask (affinityMask); @@ -119,47 +121,65 @@ void JUCE_API juce_threadEntryPoint (void* userData) } //============================================================================== -void Thread::startThread() +bool Thread::startThreadInternal (Priority threadPriority) { - const ScopedLock sl (startStopLock); - - shouldExit = 0; + shouldExit = false; + + // 'priority' is essentially useless on Linux as only realtime + // has any options but we need to set this here to satisfy + // later queries, otherwise we get inconsistent results across + // platforms. + #if JUCE_ANDROID || JUCE_LINUX || JUCE_BSD + priority = threadPriority; + #endif - if (threadHandle.get() == nullptr) + if (createNativeThread (threadPriority)) { - launchThread(); - setThreadPriority (threadHandle.get(), threadPriority); startSuspensionEvent.signal(); + return true; } + + return false; +} + +bool Thread::startThread() +{ + return startThread (Priority::normal); } -void Thread::startThread (int priority) +bool Thread::startThread (Priority threadPriority) { const ScopedLock sl (startStopLock); - if (threadHandle.get() == nullptr) + if (threadHandle == nullptr) { - auto isRealtime = (priority == realtimeAudioPriority); + realtimeOptions.reset(); + return startThreadInternal (threadPriority); + } - #if JUCE_ANDROID - isAndroidRealtimeThread = isRealtime; - #endif + return false; +} - if (isRealtime) - priority = 9; +bool Thread::startRealtimeThread (const RealtimeOptions& options) +{ + const ScopedLock sl (startStopLock); - threadPriority = priority; - startThread(); - } - else + if (threadHandle == nullptr) { - setPriority (priority); + realtimeOptions = std::make_optional (options); + + if (startThreadInternal (Priority::normal)) + return true; + + realtimeOptions.reset(); } + + return false; } bool Thread::isThreadRunning() const { - return threadHandle.get() != nullptr; + return threadHandle != nullptr; } Thread* JUCE_CALLTYPE Thread::getCurrentThread() @@ -169,19 +189,19 @@ Thread* JUCE_CALLTYPE Thread::getCurrentThread() Thread::ThreadID Thread::getThreadId() const noexcept { - return threadId.get(); + return threadId; } //============================================================================== void Thread::signalThreadShouldExit() { - shouldExit = 1; + shouldExit = true; listeners.call ([] (Listener& l) { l.exitSignalSent(); }); } bool Thread::threadShouldExit() const { - return shouldExit.get() != 0; + return shouldExit; } bool Thread::currentThreadShouldExit() @@ -254,41 +274,9 @@ void Thread::removeListener (Listener* listener) listeners.remove (listener); } -//============================================================================== -bool Thread::setPriority (int newPriority) -{ - bool isRealtime = (newPriority == realtimeAudioPriority); - - if (isRealtime) - newPriority = 9; - - // NB: deadlock possible if you try to set the thread prio from the thread itself, - // so using setCurrentThreadPriority instead in that case. - if (getCurrentThreadId() == getThreadId()) - return setCurrentThreadPriority (newPriority); - - const ScopedLock sl (startStopLock); - - #if JUCE_ANDROID - // you cannot switch from or to an Android realtime thread once the - // thread is already running! - jassert (isThreadRunning() && (isRealtime == isAndroidRealtimeThread)); - - isAndroidRealtimeThread = isRealtime; - #endif - - if ((! isThreadRunning()) || setThreadPriority (threadHandle.get(), newPriority)) - { - threadPriority = newPriority; - return true; - } - - return false; -} - -bool Thread::setCurrentThreadPriority (const int newPriority) +bool Thread::isRealtime() const { - return setThreadPriority ({}, newPriority); + return realtimeOptions.has_value(); } void Thread::setAffinityMask (const uint32 newAffinityMask) @@ -297,7 +285,7 @@ void Thread::setAffinityMask (const uint32 newAffinityMask) } //============================================================================== -bool Thread::wait (const int timeOutMilliseconds) const +bool Thread::wait (double timeOutMilliseconds) const { return defaultEvent.wait (timeOutMilliseconds); } @@ -308,9 +296,9 @@ void Thread::notify() const } //============================================================================== -struct LambdaThread : public Thread +struct LambdaThread final : public Thread { - LambdaThread (std::function f) : Thread ("anonymous"), fn (f) {} + LambdaThread (std::function&& f) : Thread ("anonymous"), fn (std::move (f)) {} void run() override { @@ -323,11 +311,23 @@ struct LambdaThread : public Thread JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaThread) }; -void Thread::launch (std::function functionToRun) +bool Thread::launch (std::function functionToRun) +{ + return launch (Priority::normal, std::move (functionToRun)); +} + +bool Thread::launch (Priority priority, std::function functionToRun) { - auto anon = new LambdaThread (functionToRun); + auto anon = std::make_unique (std::move (functionToRun)); anon->deleteOnThreadEnd = true; - anon->startThread(); + + if (anon->startThread (priority)) + { + anon.release(); + return true; + } + + return false; } //============================================================================== @@ -350,12 +350,11 @@ bool JUCE_CALLTYPE Process::isRunningUnderDebugger() noexcept return juce_isRunningUnderDebugger(); } - //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS -class AtomicTests : public UnitTest +class AtomicTests final : public UnitTest { public: AtomicTests() @@ -367,13 +366,13 @@ class AtomicTests : public UnitTest beginTest ("Misc"); char a1[7]; - expect (numElementsInArray(a1) == 7); + expect (numElementsInArray (a1) == 7); int a2[3]; - expect (numElementsInArray(a2) == 3); + expect (numElementsInArray (a2) == 3); expect (ByteOrder::swap ((uint16) 0x1122) == 0x2211); expect (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211); - expect (ByteOrder::swap ((uint64) 0x1122334455667788ULL) == 0x8877665544332211LL); + expect (ByteOrder::swap ((uint64) 0x1122334455667788ULL) == (uint64) 0x8877665544332211LL); beginTest ("Atomic int"); AtomicTester ::testInteger (*this); @@ -418,7 +417,7 @@ class AtomicTests : public UnitTest class AtomicTester { public: - AtomicTester() {} + AtomicTester() = default; static void testInteger (UnitTest& test) { @@ -461,17 +460,17 @@ class AtomicTests : public UnitTest /* These are some simple test cases to check the atomics - let me know if any of these assertions fail on your system! */ - test.expect (a.get() == (Type) 101); + test.expect (exactlyEqual (a.get(), (Type) 101)); test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200)); - test.expect (a.get() == (Type) 101); + test.expect (exactlyEqual (a.get(), (Type) 101)); test.expect (a.compareAndSetBool ((Type) 200, a.get())); - test.expect (a.get() == (Type) 200); + test.expect (exactlyEqual (a.get(), (Type) 200)); - test.expect (a.exchange ((Type) 300) == (Type) 200); - test.expect (a.get() == (Type) 300); + test.expect (exactlyEqual (a.exchange ((Type) 300), (Type) 200)); + test.expect (exactlyEqual (a.get(), (Type) 300)); b = a; - test.expect (b.get() == a.get()); + test.expect (exactlyEqual (b.get(), a.get())); } }; }; @@ -479,8 +478,8 @@ class AtomicTests : public UnitTest static AtomicTests atomicUnitTests; //============================================================================== -class ThreadLocalValueUnitTest : public UnitTest, - private Thread +class ThreadLocalValueUnitTest final : public UnitTest, + private Thread { public: ThreadLocalValueUnitTest() diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h index 6432f565..f8ca019b 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,8 +28,8 @@ namespace juce Encapsulates a thread. Subclasses derive from Thread and implement the run() method, in which they - do their business. The thread can then be started with the startThread() method - and controlled with various other methods. + do their business. The thread can then be started with the startThread() or + startRealtimeThread() methods and controlled with various other methods. This class also contains some thread-related static methods, such as sleep(), yield(), getCurrentThreadId() etc. @@ -42,6 +42,168 @@ namespace juce class JUCE_API Thread { public: + //============================================================================== + static constexpr size_t osDefaultStackSize { 0 }; + + //============================================================================== + /** The different runtime priorities of non-realtime threads. + + @see startThread + */ + enum class Priority + { + /** The highest possible priority that isn't a dedicated realtime thread. */ + highest = 2, + + /** Makes use of performance cores and higher clocks. */ + high = 1, + + /** The OS default. It will balance out across all cores. */ + normal = 0, + + /** Uses efficiency cores when possible. */ + low = -1, + + /** Restricted to efficiency cores on platforms that have them. */ + background = -2 + }; + + //============================================================================== + /** A selection of options available when creating realtime threads. + + @see startRealtimeThread + */ + struct RealtimeOptions + { + /** A value with a range of 0-10, where 10 is the highest priority. + + Currently only used by Posix platforms. + + @see getPriority + */ + [[nodiscard]] RealtimeOptions withPriority (int newPriority) const + { + jassert (isPositiveAndNotGreaterThan (newPriority, 10)); + return withMember (*this, &RealtimeOptions::priority, juce::jlimit (0, 10, newPriority)); + } + + /** Specify the expected amount of processing time required each time the thread wakes up. + + Only used by macOS/iOS. + + @see getProcessingTimeMs, withMaximumProcessingTimeMs, withPeriodMs, withPeriodHz + */ + [[nodiscard]] RealtimeOptions withProcessingTimeMs (double newProcessingTimeMs) const + { + jassert (newProcessingTimeMs > 0.0); + return withMember (*this, &RealtimeOptions::processingTimeMs, newProcessingTimeMs); + } + + /** Specify the maximum amount of processing time required each time the thread wakes up. + + Only used by macOS/iOS. + + @see getMaximumProcessingTimeMs, withProcessingTimeMs, withPeriodMs, withPeriodHz + */ + [[nodiscard]] RealtimeOptions withMaximumProcessingTimeMs (double newMaximumProcessingTimeMs) const + { + jassert (newMaximumProcessingTimeMs > 0.0); + return withMember (*this, &RealtimeOptions::maximumProcessingTimeMs, newMaximumProcessingTimeMs); + } + + /** Specify the maximum amount of processing time required each time the thread wakes up. + + This is identical to 'withMaximumProcessingTimeMs' except it calculates the processing time + from a sample rate and block size. This is useful if you want to run this thread in parallel + to an audio device thread. + + Only used by macOS/iOS. + + @see withMaximumProcessingTimeMs, AudioWorkgroup, ScopedWorkgroupToken + */ + [[nodiscard]] RealtimeOptions withApproximateAudioProcessingTime (int samplesPerFrame, double sampleRate) const + { + jassert (samplesPerFrame > 0); + jassert (sampleRate > 0.0); + + const auto approxFrameTimeMs = (samplesPerFrame / sampleRate) * 1000.0; + return withMaximumProcessingTimeMs (approxFrameTimeMs); + } + + /** Specify the approximate amount of time between each thread wake up. + + Alternatively call withPeriodHz(). + + Only used by macOS/iOS. + + @see getPeriodMs, withPeriodHz, withProcessingTimeMs, withMaximumProcessingTimeMs, + */ + [[nodiscard]] RealtimeOptions withPeriodMs (double newPeriodMs) const + { + jassert (newPeriodMs > 0.0); + return withMember (*this, &RealtimeOptions::periodMs, newPeriodMs); + } + + /** Specify the approximate frequency at which the thread will be woken up. + + Alternatively call withPeriodMs(). + + Only used by macOS/iOS. + + @see getPeriodHz, withPeriodMs, withProcessingTimeMs, withMaximumProcessingTimeMs, + */ + [[nodiscard]] RealtimeOptions withPeriodHz (double newPeriodHz) const + { + jassert (newPeriodHz > 0.0); + return withPeriodMs (1'000.0 / newPeriodHz); + } + + /** Returns a value with a range of 0-10, where 10 is the highest priority. + + @see withPriority + */ + [[nodiscard]] int getPriority() const + { + return priority; + } + + /** Returns the expected amount of processing time required each time the thread + wakes up. + + @see withProcessingTimeMs, getMaximumProcessingTimeMs, getPeriodMs + */ + [[nodiscard]] std::optional getProcessingTimeMs() const + { + return processingTimeMs; + } + + /** Returns the maximum amount of processing time required each time the thread + wakes up. + + @see withMaximumProcessingTimeMs, getProcessingTimeMs, getPeriodMs + */ + [[nodiscard]] std::optional getMaximumProcessingTimeMs() const + { + return maximumProcessingTimeMs; + } + + /** Returns the approximate amount of time between each thread wake up, or + nullopt if there is no inherent periodicity. + + @see withPeriodMs, withPeriodHz, getProcessingTimeMs, getMaximumProcessingTimeMs + */ + [[nodiscard]] std::optional getPeriodMs() const + { + return periodMs; + } + + private: + int priority { 5 }; + std::optional processingTimeMs; + std::optional maximumProcessingTimeMs; + std::optional periodMs{}; + }; + //============================================================================== /** Creates a thread. @@ -55,7 +217,7 @@ class JUCE_API Thread is zero then the default stack size of the OS will be used. */ - explicit Thread (const String& threadName, size_t threadStackSize = 0); + explicit Thread (const String& threadName, size_t threadStackSize = osDefaultStackSize); /** Destructor. @@ -78,23 +240,51 @@ class JUCE_API Thread virtual void run() = 0; //============================================================================== - /** Starts the thread running. + /** Attempts to start a new thread with default ('Priority::normal') priority. This will cause the thread's run() method to be called by a new thread. If this thread is already running, startThread() won't do anything. + If a thread cannot be created with the requested priority, this will return false + and Thread::run() will not be called. An exception to this is the Android platform, + which always starts a thread and attempts to upgrade the thread after creation. + + @returns true if the thread started successfully. false if it was unsuccesful. + @see stopThread */ - void startThread(); + bool startThread(); + + /** Attempts to start a new thread with a given priority. + + This will cause the thread's run() method to be called by a new thread. + If this thread is already running, startThread() won't do anything. + + If a thread cannot be created with the requested priority, this will return false + and Thread::run() will not be called. An exception to this is the Android platform, + which always starts a thread and attempts to upgrade the thread after creation. + + @param newPriority Priority the thread should be assigned. This parameter is ignored + on Linux. + + @returns true if the thread started successfully, false if it was unsuccesful. + + @see startThread, setPriority, startRealtimeThread + */ + bool startThread (Priority newPriority); + + /** Starts the thread with realtime performance characteristics on platforms + that support it. - /** Starts the thread with a given priority. + You cannot change the options of a running realtime thread, nor switch + a non-realtime thread to a realtime thread. To make these changes you must + first stop the thread and then restart with different options. - Launches the thread with a given priority, where 0 = lowest, 10 = highest. - If the thread is already running, its priority will be changed. + @param options Realtime options the thread should be created with. - @see startThread, setPriority, realtimeAudioPriority + @see startThread, RealtimeOptions */ - void startThread (int priority); + bool startRealtimeThread (const RealtimeOptions& options); /** Attempts to stop the thread running. @@ -119,7 +309,27 @@ class JUCE_API Thread bool stopThread (int timeOutMilliseconds); //============================================================================== - /** Invokes a lambda or function on its own thread. + /** Invokes a lambda or function on its own thread with the default priority. + + This will spin up a Thread object which calls the function and then exits. + Bear in mind that starting and stopping a thread can be a fairly heavyweight + operation, so you might prefer to use a ThreadPool if you're kicking off a lot + of short background tasks. + + Also note that using an anonymous thread makes it very difficult to interrupt + the function when you need to stop it, e.g. when your app quits. So it's up to + you to deal with situations where the function may fail to stop in time. + + @param functionToRun The lambda to be called from the new Thread. + + @returns true if the thread started successfully, or false if it failed. + + @see launch. + */ + static bool launch (std::function functionToRun); + + //============================================================================== + /** Invokes a lambda or function on its own thread with a custom priority. This will spin up a Thread object which calls the function and then exits. Bear in mind that starting and stopping a thread can be a fairly heavyweight @@ -129,8 +339,13 @@ class JUCE_API Thread Also note that using an anonymous thread makes it very difficult to interrupt the function when you need to stop it, e.g. when your app quits. So it's up to you to deal with situations where the function may fail to stop in time. + + @param priority The priority the thread is started with. + @param functionToRun The lambda to be called from the new Thread. + + @returns true if the thread started successfully, or false if it failed. */ - static void launch (std::function functionToRun); + static bool launch (Priority priority, std::function functionToRun); //============================================================================== /** Returns true if the thread is currently active */ @@ -198,50 +413,8 @@ class JUCE_API Thread /** Removes a listener added with addListener. */ void removeListener (Listener*); - //============================================================================== - /** Special realtime audio thread priority - - This priority will create a high-priority thread which is best suited - for realtime audio processing. - - Currently, this priority is identical to priority 9, except when building - for Android with OpenSL/Oboe support. - - In this case, JUCE will ask OpenSL/Oboe to construct a super high priority thread - specifically for realtime audio processing. - - Note that this priority can only be set **before** the thread has - started. Switching to this priority, or from this priority to a different - priority, is not supported under Android and will assert. - - For best performance this thread should yield at regular intervals - and not call any blocking APIs. - - @see startThread, setPriority, sleep, WaitableEvent - */ - enum - { - realtimeAudioPriority = -1 - }; - - /** Changes the thread's priority. - - May return false if for some reason the priority can't be changed. - - @param priority the new priority, in the range 0 (lowest) to 10 (highest). A priority - of 5 is normal. - @see realtimeAudioPriority - */ - bool setPriority (int priority); - - /** Changes the priority of the caller thread. - - Similar to setPriority(), but this static method acts on the caller thread. - May return false if for some reason the priority can't be changed. - - @see setPriority - */ - static bool setCurrentThreadPriority (int priority); + /** Returns true if this Thread represents a realtime thread. */ + bool isRealtime() const; //============================================================================== /** Sets the affinity mask for the thread. @@ -286,7 +459,7 @@ class JUCE_API Thread @returns true if the event has been signalled, false if the timeout expires. */ - bool wait (int timeOutMilliseconds) const; + bool wait (double timeOutMilliseconds) const; /** Wakes up the thread. @@ -338,7 +511,7 @@ class JUCE_API Thread */ static void JUCE_CALLTYPE setCurrentThreadName (const String& newThreadName); - #if JUCE_ANDROID || defined (DOXYGEN) + #if JUCE_ANDROID || DOXYGEN //============================================================================== /** Initialises the JUCE subsystem for projects not created by the Projucer @@ -352,7 +525,7 @@ class JUCE_API Thread following java method: @code - com.roli.juce.Java.initialiseJUCE (myContext); + com.rmsl.juce.Java.initialiseJUCE (myContext); @endcode Note that the above java method is only available in Android Studio projects @@ -361,7 +534,7 @@ class JUCE_API Thread your project: @code - package com.roli.juce; + package com.rmsl.juce; public class Java { @@ -380,33 +553,58 @@ class JUCE_API Thread static void initialiseJUCE (void* jniEnv, void* jContext); #endif +protected: + //============================================================================== + /** Returns the current priority of this thread. + + This can only be called from the target thread. Doing so from another thread + will cause an assert. + + @see setPriority + */ + Priority getPriority() const; + + /** Attempts to set the priority for this thread. Returns true if the new priority + was set successfully, false if not. + + This can only be called from the target thread. Doing so from another thread + will cause an assert. + + @param newPriority The new priority to be applied to the thread. Note: This + has no effect on Linux platforms, subsequent calls to + 'getPriority' will return this value. + + @see Priority + */ + bool setPriority (Priority newPriority); + private: //============================================================================== const String threadName; - Atomic threadHandle { nullptr }; - Atomic threadId = {}; + std::atomic threadHandle { nullptr }; + std::atomic threadId { nullptr }; + std::optional realtimeOptions = {}; CriticalSection startStopLock; WaitableEvent startSuspensionEvent, defaultEvent; - int threadPriority = 5; size_t threadStackSize; uint32 affinityMask = 0; bool deleteOnThreadEnd = false; - Atomic shouldExit { 0 }; + std::atomic shouldExit { false }; ListenerList> listeners; - #if JUCE_ANDROID - bool isAndroidRealtimeThread = false; + #if JUCE_ANDROID || JUCE_LINUX || JUCE_BSD + std::atomic priority; #endif #ifndef DOXYGEN friend void JUCE_API juce_threadEntryPoint (void*); #endif - void launchThread(); + bool startThreadInternal (Priority); + bool createNativeThread (Priority); void closeThreadHandle(); void killThread(); void threadEntryPoint(); - static bool setThreadPriority (void*, int); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Thread) }; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadLocalValue.h b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadLocalValue.h index f40617e3..8e916790 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadLocalValue.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadLocalValue.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp index 6757ed21..98f4fc24 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,21 +23,25 @@ namespace juce { -struct ThreadPool::ThreadPoolThread : public Thread +struct ThreadPool::ThreadPoolThread final : public Thread { - ThreadPoolThread (ThreadPool& p, size_t stackSize) - : Thread ("Pool", stackSize), pool (p) + ThreadPoolThread (ThreadPool& p, const Options& options) + : Thread { options.threadName, options.threadStackSizeBytes }, + pool { p } { } void run() override { while (! threadShouldExit()) + { if (! pool.runNextJob (*this)) wait (500); + } } std::atomic currentJob { nullptr }; + ThreadPool& pool; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread) @@ -90,16 +94,25 @@ ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob() } //============================================================================== -ThreadPool::ThreadPool (int numThreads, size_t threadStackSize) +ThreadPool::ThreadPool (const Options& options) { - jassert (numThreads > 0); // not much point having a pool without any threads! + // not much point having a pool without any threads! + jassert (options.numberOfThreads > 0); - createThreads (numThreads, threadStackSize); + for (int i = jmax (1, options.numberOfThreads); --i >= 0;) + threads.add (new ThreadPoolThread (*this, options)); + + for (auto* t : threads) + t->startThread (options.desiredThreadPriority); } -ThreadPool::ThreadPool() +ThreadPool::ThreadPool (int numberOfThreads, + size_t threadStackSizeBytes, + Thread::Priority desiredThreadPriority) + : ThreadPool { Options{}.withNumberOfThreads (numberOfThreads) + .withThreadStackSizeBytes (threadStackSizeBytes) + .withDesiredThreadPriority (desiredThreadPriority) } { - createThreads (SystemStats::getNumCpus()); } ThreadPool::~ThreadPool() @@ -108,15 +121,6 @@ ThreadPool::~ThreadPool() stopThreads(); } -void ThreadPool::createThreads (int numThreads, size_t threadStackSize) -{ - for (int i = jmax (1, numThreads); --i >= 0;) - threads.add (new ThreadPoolThread (*this, threadStackSize)); - - for (auto* t : threads) - t->startThread(); -} - void ThreadPool::stopThreads() { for (auto* t : threads) @@ -150,7 +154,7 @@ void ThreadPool::addJob (ThreadPoolJob* job, bool deleteJobWhenFinished) void ThreadPool::addJob (std::function jobToRun) { - struct LambdaJobWrapper : public ThreadPoolJob + struct LambdaJobWrapper final : public ThreadPoolJob { LambdaJobWrapper (std::function j) : ThreadPoolJob ("lambda"), job (j) {} JobStatus runJob() override { return job(); } @@ -163,15 +167,15 @@ void ThreadPool::addJob (std::function jobToRun) void ThreadPool::addJob (std::function jobToRun) { - struct LambdaJobWrapper : public ThreadPoolJob + struct LambdaJobWrapper final : public ThreadPoolJob { - LambdaJobWrapper (std::function j) : ThreadPoolJob ("lambda"), job (j) {} + LambdaJobWrapper (std::function j) : ThreadPoolJob ("lambda"), job (std::move (j)) {} JobStatus runJob() override { job(); return ThreadPoolJob::jobHasFinished; } std::function job; }; - addJob (new LambdaJobWrapper (jobToRun), true); + addJob (new LambdaJobWrapper (std::move (jobToRun)), true); } int ThreadPool::getNumJobs() const noexcept @@ -273,7 +277,7 @@ bool ThreadPool::removeAllJobs (bool interruptRunningJobs, int timeOutMs, for (int i = jobs.size(); --i >= 0;) { - auto* job = jobs.getUnchecked(i); + auto* job = jobs.getUnchecked (i); if (selectedJobsToRemove == nullptr || selectedJobsToRemove->isJobSuitable (job)) { @@ -330,17 +334,6 @@ StringArray ThreadPool::getNamesOfAllJobs (bool onlyReturnActiveJobs) const return s; } -bool ThreadPool::setThreadPriorities (int newPriority) -{ - bool ok = true; - - for (auto* t : threads) - if (! t->setPriority (newPriority)) - ok = false; - - return ok; -} - ThreadPoolJob* ThreadPool::pickNextJobToRun() { OwnedArray deletionList; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h index 8413d2ad..fa36803e 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -140,6 +140,51 @@ class JUCE_API ThreadPoolJob JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolJob) }; +//============================================================================== +/** + A set of threads that will run a list of jobs. + + When a ThreadPoolJob object is added to the ThreadPool's list, its runJob() method + will be called by the next pooled thread that becomes free. + + @see ThreadPoolJob, Thread + + @tags{Core} +*/ +struct ThreadPoolOptions +{ + /** The name to give each thread in the pool. */ + [[nodiscard]] ThreadPoolOptions withThreadName (String newThreadName) const + { + return withMember (*this, &ThreadPoolOptions::threadName, newThreadName); + } + + /** The number of threads to run. + These will be started when a pool is created, and run until the pool is destroyed. + */ + [[nodiscard]] ThreadPoolOptions withNumberOfThreads (int newNumberOfThreads) const + { + return withMember (*this, &ThreadPoolOptions::numberOfThreads, newNumberOfThreads); + } + + /** The size of the stack of each thread in the pool. */ + [[nodiscard]] ThreadPoolOptions withThreadStackSizeBytes (size_t newThreadStackSizeBytes) const + { + return withMember (*this, &ThreadPoolOptions::threadStackSizeBytes, newThreadStackSizeBytes); + } + + /** The desired priority of each thread in the pool. */ + [[nodiscard]] ThreadPoolOptions withDesiredThreadPriority (Thread::Priority newDesiredThreadPriority) const + { + return withMember (*this, &ThreadPoolOptions::desiredThreadPriority, newDesiredThreadPriority); + } + + String threadName { "Pool" }; + int numberOfThreads { SystemStats::getNumCpus() }; + size_t threadStackSizeBytes { Thread::osDefaultStackSize }; + Thread::Priority desiredThreadPriority { Thread::Priority::normal }; +}; + //============================================================================== /** @@ -155,25 +200,38 @@ class JUCE_API ThreadPoolJob class JUCE_API ThreadPool { public: + using Options = ThreadPoolOptions; + //============================================================================== - /** Creates a thread pool. + /** Creates a thread pool based on the provided options. Once you've created a pool, you can give it some jobs by calling addJob(). - @param numberOfThreads the number of threads to run. These will be started - immediately, and will run until the pool is deleted. - @param threadStackSize the size of the stack of each thread. If this value - is zero then the default stack size of the OS will - be used. + @see ThreadPool::ThreadPoolOptions */ - ThreadPool (int numberOfThreads, size_t threadStackSize = 0); + explicit ThreadPool (const Options& options); + + /** Creates a thread pool based using the default arguments provided by + ThreadPoolOptions. - /** Creates a thread pool with one thread per CPU core. Once you've created a pool, you can give it some jobs by calling addJob(). - If you want to specify the number of threads, use the other constructor; this - one creates a pool which has one thread for each CPU core. - @see SystemStats::getNumCpus() + + @see ThreadPoolOptions */ - ThreadPool(); + ThreadPool() : ThreadPool { Options{} } {} + + /** Creates a thread pool. + Once you've created a pool, you can give it some jobs by calling addJob(). + + @param numberOfThreads the number of threads to run. These will be started + immediately, and will run until the pool is deleted. + @param threadStackSizeBytes the size of the stack of each thread. If this value + is zero then the default stack size of the OS will + be used. + @param desiredThreadPriority the desired priority of each thread in the pool. + */ + ThreadPool (int numberOfThreads, + size_t threadStackSizeBytes = Thread::osDefaultStackSize, + Thread::Priority desiredThreadPriority = Thread::Priority::normal); /** Destructor. @@ -309,13 +367,6 @@ class JUCE_API ThreadPool */ StringArray getNamesOfAllJobs (bool onlyReturnActiveJobs) const; - /** Changes the priority of all the threads. - This will call Thread::setPriority() for each thread in the pool. - May return false if for some reason the priority can't be changed. - */ - bool setThreadPriorities (int newPriority); - - private: //============================================================================== Array jobs; @@ -330,7 +381,6 @@ class JUCE_API ThreadPool bool runNextJob (ThreadPoolThread&); ThreadPoolJob* pickNextJobToRun(); void addToDeleteList (OwnedArray&, ThreadPoolJob*) const; - void createThreads (int numThreads, size_t threadStackSize = 0); void stopThreads(); // Note that this method has changed, and no longer has a parameter to indicate diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.cpp index c8eb3d8a..ec6e9e39 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -89,6 +89,7 @@ void TimeSliceThread::moveToFrontOfQueue (TimeSliceClient* client) int TimeSliceThread::getNumClients() const { + const ScopedLock sl (listLock); return clients.size(); } @@ -98,6 +99,12 @@ TimeSliceClient* TimeSliceThread::getClient (const int i) const return clients[i]; } +bool TimeSliceThread::contains (const TimeSliceClient* c) const +{ + const ScopedLock sl (listLock); + return std::any_of (clients.begin(), clients.end(), [=] (auto* registered) { return registered == c; }); +} + //============================================================================== TimeSliceClient* TimeSliceThread::getNextClient (int index) const { @@ -108,7 +115,7 @@ TimeSliceClient* TimeSliceThread::getNextClient (int index) const { auto* c = clients.getUnchecked ((i + index) % clients.size()); - if (client == nullptr || c->nextCallTime < soonest) + if (c != nullptr && (client == nullptr || c->nextCallTime < soonest)) { client = c; soonest = c->nextCallTime; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h b/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h index 61601992..b30d98e0 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -131,6 +131,9 @@ class JUCE_API TimeSliceThread : public Thread /** Returns one of the registered clients. */ TimeSliceClient* getClient (int index) const; + /** Returns true if the client is currently registered. */ + bool contains (const TimeSliceClient*) const; + //============================================================================== #ifndef DOXYGEN void run() override; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp index f202f243..34cf538e 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,19 +28,19 @@ WaitableEvent::WaitableEvent (bool manualReset) noexcept { } -bool WaitableEvent::wait (int timeOutMilliseconds) const +bool WaitableEvent::wait (double timeOutMilliseconds) const { std::unique_lock lock (mutex); if (! triggered) { - if (timeOutMilliseconds < 0) + if (timeOutMilliseconds < 0.0) { condition.wait (lock, [this] { return triggered == true; }); } else { - if (! condition.wait_for (lock, std::chrono::milliseconds (timeOutMilliseconds), + if (! condition.wait_for (lock, std::chrono::duration { timeOutMilliseconds }, [this] { return triggered == true; })) { return false; @@ -56,7 +56,7 @@ bool WaitableEvent::wait (int timeOutMilliseconds) const void WaitableEvent::signal() const { - std::unique_lock lock (mutex); + std::lock_guard lock (mutex); triggered = true; condition.notify_all(); diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h index d2c7ca4e..859763ff 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -61,7 +61,7 @@ class JUCE_API WaitableEvent @returns true if the object has been signalled, false if the timeout expires first. @see signal, reset */ - bool wait (int timeOutMilliseconds = -1) const; + bool wait (double timeOutMilliseconds = -1.0) const; /** Wakes up any threads that are currently waiting on this object. diff --git a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp index 3ae946e6..64ea3c1a 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -114,7 +114,7 @@ void PerformanceCounter::printStatistics() { const String desc (getStatisticsAndReset().toString()); - Logger::outputDebugString (desc); + Logger::writeToLog (desc); appendToFile (outputFile, desc); } @@ -124,7 +124,7 @@ PerformanceCounter::Statistics PerformanceCounter::getStatisticsAndReset() stats.clear(); if (s.numRuns > 0) - s.averageSeconds = s.totalSeconds / s.numRuns; + s.averageSeconds = s.totalSeconds / (float) s.numRuns; return s; } diff --git a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h index fef5d5eb..69cfed01 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h +++ b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp index 1033bc36..f5858ce3 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,8 +28,8 @@ RelativeTime::RelativeTime (const RelativeTime& other) noexcept : numSeconds ( RelativeTime::~RelativeTime() noexcept {} //============================================================================== -RelativeTime RelativeTime::milliseconds (int milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } -RelativeTime RelativeTime::milliseconds (int64 milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } +RelativeTime RelativeTime::milliseconds (int milliseconds) noexcept { return RelativeTime ((double) milliseconds * 0.001); } +RelativeTime RelativeTime::milliseconds (int64 milliseconds) noexcept { return RelativeTime ((double) milliseconds * 0.001); } RelativeTime RelativeTime::seconds (double s) noexcept { return RelativeTime (s); } RelativeTime RelativeTime::minutes (double numberOfMinutes) noexcept { return RelativeTime (numberOfMinutes * 60.0); } RelativeTime RelativeTime::hours (double numberOfHours) noexcept { return RelativeTime (numberOfHours * (60.0 * 60.0)); } @@ -54,8 +54,16 @@ RelativeTime RelativeTime::operator-= (double secs) noexcept { numSeconds 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; } -JUCE_API bool JUCE_CALLTYPE operator== (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() == t2.inSeconds(); } -JUCE_API bool JUCE_CALLTYPE operator!= (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() != t2.inSeconds(); } +JUCE_API bool JUCE_CALLTYPE operator== (RelativeTime t1, RelativeTime t2) noexcept +{ + return exactlyEqual (t1.inSeconds(), t2.inSeconds()); +} + +JUCE_API bool JUCE_CALLTYPE operator!= (RelativeTime t1, RelativeTime t2) noexcept +{ + return ! (t1 == t2); +} + JUCE_API bool JUCE_CALLTYPE operator> (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() > t2.inSeconds(); } JUCE_API bool JUCE_CALLTYPE operator< (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() < t2.inSeconds(); } JUCE_API bool JUCE_CALLTYPE operator>= (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() >= t2.inSeconds(); } @@ -67,13 +75,13 @@ static String translateTimeField (int n, const char* singular, const char* plura 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")); } +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 { @@ -86,7 +94,7 @@ String RelativeTime::getApproximateDescription() const if (weeks > 8) return describeMonths ((weeks * 12) / 52); if (weeks > 1) return describeWeeks (weeks); - auto days = (int) inWeeks(); + auto days = (int) inDays(); if (days > 1) return describeDays (days); diff --git a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h index b0235c0b..77cbf0ac 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h +++ b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp b/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp index b3b9b904..6d97ad4e 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -91,7 +91,7 @@ namespace TimeHelpers : (value - ((value / modulo) + 1) * modulo)); } - static inline String formatString (const String& format, const std::tm* const tm) + static String formatString (const String& format, const std::tm* const tm) { #if JUCE_ANDROID using StringType = CharPointer_UTF8; @@ -126,12 +126,12 @@ namespace TimeHelpers } //============================================================================== - static inline bool isLeapYear (int year) noexcept + static bool isLeapYear (int year) noexcept { return (year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0)); } - static inline int daysFromJan1 (int year, int month) noexcept + static int daysFromJan1 (int year, int month) noexcept { const short dayOfYear[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }; @@ -139,18 +139,18 @@ namespace TimeHelpers return dayOfYear [(isLeapYear (year) ? 12 : 0) + month]; } - static inline int64 daysFromYear0 (int year) noexcept + static int64 daysFromYear0 (int year) noexcept { --year; return 365 * year + (year / 400) - (year / 100) + (year / 4); } - static inline int64 daysFrom1970 (int year) noexcept + static int64 daysFrom1970 (int year) noexcept { return daysFromYear0 (year) - daysFromYear0 (1970); } - static inline int64 daysFrom1970 (int year, int month) noexcept + static int64 daysFrom1970 (int year, int month) noexcept { if (month > 11) { @@ -169,7 +169,7 @@ namespace TimeHelpers // There's no posix function that does a UTC version of mktime, // so annoyingly we need to implement this manually.. - static inline int64 mktime_utc (const std::tm& t) noexcept + static int64 mktime_utc (const std::tm& t) noexcept { return 24 * 3600 * (daysFrom1970 (t.tm_year + 1900, t.tm_mon) + (t.tm_mday - 1)) + 3600 * t.tm_hour @@ -178,6 +178,21 @@ namespace TimeHelpers } static Atomic lastMSCounterValue { (uint32) 0 }; + + static String getUTCOffsetString (int utcOffsetSeconds, bool includeSemiColon) + { + if (const auto seconds = utcOffsetSeconds) + { + auto minutes = seconds / 60; + + return String::formatted (includeSemiColon ? "%+03d:%02d" + : "%+03d%02d", + minutes / 60, + abs (minutes) % 60); + } + + return "Z"; + } } //============================================================================== @@ -277,7 +292,7 @@ void Time::waitForMillisecondCounter (uint32 targetTime) noexcept //============================================================================== double Time::highResolutionTicksToSeconds (const int64 ticks) noexcept { - return ticks / (double) getHighResolutionTicksPerSecond(); + return (double) ticks / (double) getHighResolutionTicksPerSecond(); } int64 Time::secondsToHighResolutionTicks (const double seconds) noexcept @@ -364,8 +379,7 @@ String Time::getTimeZone() const { String zone[2]; - #if JUCE_WINDOWS - #if JUCE_MSVC || JUCE_CLANG + #if JUCE_WINDOWS && (JUCE_MSVC || JUCE_CLANG) _tzset(); for (int i = 0; i < 2; ++i) @@ -375,9 +389,6 @@ String Time::getTimeZone() const _get_tzname (&length, name, sizeof (name) - 1, i); zone[i] = name; } - #else - #warning "Can't find a replacement for tzset on mingw - ideas welcome!" - #endif #else tzset(); @@ -406,17 +417,7 @@ int Time::getUTCOffsetSeconds() const noexcept String Time::getUTCOffsetString (bool includeSemiColon) const { - if (auto seconds = getUTCOffsetSeconds()) - { - auto minutes = seconds / 60; - - return String::formatted (includeSemiColon ? "%+03d:%02d" - : "%+03d%02d", - minutes / 60, - minutes % 60); - } - - return "Z"; + return TimeHelpers::getUTCOffsetString (getUTCOffsetSeconds(), includeSemiColon); } String Time::toISO8601 (bool includeDividerCharacters) const @@ -567,7 +568,8 @@ Time& Time::operator-= (RelativeTime delta) noexcept { millisSinceEpoc Time operator+ (Time time, RelativeTime delta) noexcept { Time t (time); return t += delta; } Time operator- (Time time, RelativeTime delta) noexcept { Time t (time); return t -= delta; } Time operator+ (RelativeTime delta, Time time) noexcept { Time t (time); return t += delta; } -const RelativeTime operator- (Time time1, Time time2) noexcept { return RelativeTime::milliseconds (time1.toMilliseconds() - time2.toMilliseconds()); } + +RelativeTime operator- (Time time1, Time time2) noexcept { return RelativeTime::milliseconds (time1.toMilliseconds() - time2.toMilliseconds()); } bool operator== (Time time1, Time time2) noexcept { return time1.toMilliseconds() == time2.toMilliseconds(); } bool operator!= (Time time1, Time time2) noexcept { return time1.toMilliseconds() != time2.toMilliseconds(); } @@ -609,7 +611,7 @@ Time Time::getCompilationDate() //============================================================================== #if JUCE_UNIT_TESTS -class TimeTests : public UnitTest +class TimeTests final : public UnitTest { public: TimeTests() @@ -630,6 +632,12 @@ class TimeTests : public UnitTest expect (t.getUTCOffsetString (true) == "Z" || t.getUTCOffsetString (true).length() == 6); expect (t.getUTCOffsetString (false) == "Z" || t.getUTCOffsetString (false).length() == 5); + expect (TimeHelpers::getUTCOffsetString (-(3 * 60 + 15) * 60, true) == "-03:15"); + expect (TimeHelpers::getUTCOffsetString (-(3 * 60 + 30) * 60, true) == "-03:30"); + expect (TimeHelpers::getUTCOffsetString (-(3 * 60 + 45) * 60, true) == "-03:45"); + + expect (TimeHelpers::getUTCOffsetString ((3 * 60 + 15) * 60, true) == "+03:15"); + expect (Time::fromISO8601 (t.toISO8601 (true)) == t); expect (Time::fromISO8601 (t.toISO8601 (false)) == t); diff --git a/JuceLibraryCode/modules/juce_core/time/juce_Time.h b/JuceLibraryCode/modules/juce_core/time/juce_Time.h index 60ec4efb..4fdc53b1 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_Time.h +++ b/JuceLibraryCode/modules/juce_core/time/juce_Time.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -383,7 +383,7 @@ JUCE_API Time operator+ (RelativeTime delta, Time time) noexcept; /** Subtracts a RelativeTime from a Time. */ JUCE_API Time operator- (Time time, RelativeTime delta) noexcept; /** Returns the relative time difference between two times. */ -JUCE_API const RelativeTime operator- (Time time1, Time time2) noexcept; +JUCE_API RelativeTime operator- (Time time1, Time time2) noexcept; /** Compares two Time objects. */ JUCE_API bool operator== (Time time1, Time time2) noexcept; diff --git a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.cpp b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.cpp index 0e788646..1edf0721 100644 --- a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.cpp +++ b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -194,20 +194,21 @@ bool UnitTestRunner::shouldAbortTests() return false; } +static String getTestNameString (const String& testName, const String& subCategory) +{ + return testName + " / " + subCategory; +} + void UnitTestRunner::beginNewTest (UnitTest* const test, const String& subCategory) { endTest(); currentTest = test; - auto* r = new TestResult(); - results.add (r); - r->unitTestName = test->getName(); - r->subcategoryName = subCategory; - r->passes = 0; - r->failures = 0; + auto testName = test->getName(); + results.add (new TestResult (testName, subCategory)); logMessage ("-----------------------------------------------------------------"); - logMessage ("Starting test: " + r->unitTestName + " / " + subCategory + "..."); + logMessage ("Starting tests in: " + getTestNameString (testName, subCategory) + "..."); resultsUpdated(); } @@ -216,6 +217,8 @@ void UnitTestRunner::endTest() { if (auto* r = results.getLast()) { + r->endTime = Time::getCurrentTime(); + if (r->failures > 0) { String m ("FAILED!! "); @@ -228,7 +231,7 @@ void UnitTestRunner::endTest() } else { - logMessage ("All tests completed successfully"); + logMessage ("Completed tests in " + getTestNameString (r->unitTestName, r->subcategoryName)); } } } diff --git a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h index d49e610d..0dd7fed7 100644 --- a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h +++ b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -150,7 +150,7 @@ class JUCE_API UnitTest template void expectEquals (ValueType actual, ValueType expected, String failureMessage = String()) { - bool result = actual == expected; + bool result = exactlyEqual (actual, expected); expectResultAndPrint (actual, expected, result, "", failureMessage); } @@ -160,7 +160,7 @@ class JUCE_API UnitTest template void expectNotEquals (ValueType value, ValueType valueToCompareTo, String failureMessage = String()) { - bool result = value != valueToCompareTo; + bool result = ! exactlyEqual (value, valueToCompareTo); expectResultAndPrint (value, valueToCompareTo, result, "unequal to", failureMessage); } @@ -376,18 +376,31 @@ class JUCE_API UnitTestRunner */ struct TestResult { + TestResult() = default; + + explicit TestResult (const String& name, const String& subCategory) + : unitTestName (name), + subcategoryName (subCategory) + { + } + /** The main name of this test (i.e. the name of the UnitTest object being run). */ String unitTestName; /** The name of the current subcategory (i.e. the name that was set when UnitTest::beginTest() was called). */ String subcategoryName; /** The number of UnitTest::expect() calls that succeeded. */ - int passes; + int passes = 0; /** The number of UnitTest::expect() calls that failed. */ - int failures; + int failures = 0; /** A list of messages describing the failed tests. */ StringArray messages; + + /** The time at which this test was started. */ + Time startTime = Time::getCurrentTime(); + /** The time at which this test ended. */ + Time endTime; }; /** Returns the number of TestResult objects that have been performed. diff --git a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTestCategories.h b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTestCategories.h index fcf4ff29..63984e27 100644 --- a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTestCategories.h +++ b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTestCategories.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -20,25 +20,26 @@ ============================================================================== */ -namespace juce +namespace juce::UnitTestCategories { -namespace UnitTestCategories -{ static const String analytics { "Analytics" }; static const String audio { "Audio" }; static const String audioProcessorParameters { "AudioProcessorParameters" }; + static const String audioProcessors { "AudioProcessors" }; 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 graphics { "Graphics" }; static const String gui { "GUI" }; static const String json { "JSON" }; static const String maths { "Maths" }; + static const String memory { "Memory" }; static const String midi { "MIDI" }; + static const String native { "Native" }; static const String networking { "Networking" }; static const String osc { "OSC" }; static const String smoothedValues { "SmoothedValues" }; @@ -48,6 +49,5 @@ namespace UnitTestCategories static const String time { "Time" }; static const String values { "Values" }; static const String xml { "XML" }; -} -} // namespace juce +} // namespace juce::UnitTestCategories diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp index 6ff60ac2..38d7fd87 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -291,7 +291,7 @@ void XmlDocument::skipNextWhiteSpace() { for (;;) { - input = input.findEndOfWhitespace(); + input.incrementToEndOfWhitespace(); if (input.isEmpty()) { @@ -684,7 +684,7 @@ void XmlDocument::readEntity (String& result) } else if (*input == '#') { - int charCode = 0; + int64_t charCode = 0; ++input; if (*input == 'x' || *input == 'X') @@ -712,15 +712,26 @@ void XmlDocument::readEntity (String& result) { int numChars = 0; - while (input[0] != ';') + for (;;) { + const auto firstChar = input[0]; + + if (firstChar == 0) + { + setLastError ("unexpected end of input", true); + return; + } + + if (firstChar == ';') + break; + if (++numChars > 12) { setLastError ("illegal escape sequence", true); break; } - charCode = charCode * 10 + ((int) input[0] - '0'); + charCode = charCode * 10 + ((int) firstChar - '0'); ++input; } diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h index 0e6197d5..277c6788 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -93,8 +93,7 @@ class JUCE_API XmlDocument allows quick checking of large files to see if they contain the correct type of 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. + @returns a new XmlElement, or nullptr if there was an error. @see getLastParseError, getDocumentElementIfTagMatches */ std::unique_ptr getDocumentElement (bool onlyReadOuterDocumentElement = false); @@ -126,7 +125,7 @@ class JUCE_API XmlDocument /** Sets a flag to change the treatment of empty text elements. If this is true (the default state), then any text elements that contain only - whitespace characters will be ingored during parsing. If you need to catch + whitespace characters will be ignored during parsing. If you need to catch whitespace-only text, then you should set this to false before calling the getDocumentElement() method. */ @@ -135,15 +134,13 @@ class JUCE_API XmlDocument //============================================================================== /** A handy static method that parses a file. 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. + @returns a new XmlElement, or nullptr if there was an error. */ 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. + @returns a new XmlElement, or nullptr if there was an error. */ static std::unique_ptr parse (const String& xmlData); diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp index 1b28bf16..bb531c4f 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,7 +23,7 @@ namespace juce { -inline static bool isValidXmlNameStartCharacter (juce_wchar character) noexcept +static bool isValidXmlNameStartCharacter (juce_wchar character) noexcept { return character == ':' || character == '_' @@ -43,7 +43,7 @@ inline static bool isValidXmlNameStartCharacter (juce_wchar character) noexcept || (character >= 0x10000 && character <= 0xeffff); } -inline static bool isValidXmlNameBodyCharacter (juce_wchar character) noexcept +static bool isValidXmlNameBodyCharacter (juce_wchar character) noexcept { return isValidXmlNameStartCharacter (character) || character == '-' @@ -237,7 +237,7 @@ namespace XmlOutputFunctions outputStream << (char) character; break; } - // Note: Deliberate fall-through here! + JUCE_FALLTHROUGH default: outputStream << "&#" << ((int) character) << ';'; break; @@ -993,7 +993,7 @@ void XmlElement::deleteAllTextElements() noexcept //============================================================================== #if JUCE_UNIT_TESTS -class XmlElementTests : public UnitTest +class XmlElementTests final : public UnitTest { public: XmlElementTests() diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h index 0244123f..4a675931 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,61 +23,6 @@ namespace juce { -//============================================================================== -/** A handy macro to make it easy to iterate all the child elements in an XmlElement. - - The parentXmlElement should be a reference to the parent XML, and the childElementVariableName - will be the name of a pointer to each child element. - - E.g. @code - XmlElement* myParentXml = createSomeKindOfXmlDocument(); - - forEachXmlChildElement (*myParentXml, child) - { - if (child->hasTagName ("FOO")) - doSomethingWithXmlElement (child); - } - - @endcode - - @see forEachXmlChildElementWithTagName -*/ -#define forEachXmlChildElement(parentXmlElement, childElementVariableName) \ -\ - for (auto* childElementVariableName = (parentXmlElement).getFirstChildElement(); \ - childElementVariableName != nullptr; \ - childElementVariableName = childElementVariableName->getNextElement()) - -/** A macro that makes it easy to iterate all the child elements of an XmlElement - which have a specified tag. - - This does the same job as the forEachXmlChildElement macro, but only for those - elements that have a particular tag name. - - The parentXmlElement should be a reference to the parent XML, and the childElementVariableName - will be the name of a pointer to each child element. The requiredTagName is the - tag name to match. - - E.g. @code - XmlElement* myParentXml = createSomeKindOfXmlDocument(); - - forEachXmlChildElementWithTagName (*myParentXml, child, "MYTAG") - { - // the child object is now guaranteed to be a element.. - doSomethingWithMYTAGElement (child); - } - - @endcode - - @see forEachXmlChildElement -*/ -#define forEachXmlChildElementWithTagName(parentXmlElement, childElementVariableName, requiredTagName) \ -\ - for (auto* childElementVariableName = (parentXmlElement).getChildByName (requiredTagName); \ - childElementVariableName != nullptr; \ - childElementVariableName = childElementVariableName->getNextElementWithTagName (requiredTagName)) - - //============================================================================== /** Used to build a tree of elements representing an XML document. @@ -95,7 +40,7 @@ namespace juce if (myElement->hasTagName ("ANIMALS")) { // now we'll iterate its sub-elements looking for 'giraffe' elements.. - forEachXmlChildElement (*myElement, e) + for (auto* e : myElement->getChildIterator()) { if (e->hasTagName ("GIRAFFE")) { @@ -199,8 +144,8 @@ class JUCE_API XmlElement int lineWrapLength = 60; /**< A maximum line length before wrapping is done. (If newLineChars is nullptr, this is ignored) */ const char* newLineChars = "\r\n"; /**< Allows the newline characters to be set. If you set this to nullptr, then the whole XML document will be placed on a single line. */ - TextFormat singleLine() const; /**< returns a copy of this format with newLineChars set to nullptr. */ - TextFormat withoutHeader() const; /**< returns a copy of this format with the addDefaultHeader flag set to false. */ + [[nodiscard]] TextFormat singleLine() const; /**< returns a copy of this format with newLineChars set to nullptr. */ + [[nodiscard]] TextFormat withoutHeader() const; /**< returns a copy of this format with the addDefaultHeader flag set to false. */ }; /** Returns a text version of this XML element. @@ -261,7 +206,7 @@ class JUCE_API XmlElement /** Returns the name of one of the elements attributes. E.g. for an element such as \, then - getAttributeName(1) would return "antlers". + getAttributeName (1) would return "antlers". @see getAttributeValue, getStringAttribute */ @@ -270,7 +215,7 @@ class JUCE_API XmlElement /** Returns the value of one of the elements attributes. E.g. for an element such as \, then - getAttributeName(1) would return "2". + getAttributeName (1) would return "2". @see getAttributeName, getStringAttribute */ @@ -400,7 +345,8 @@ class JUCE_API XmlElement /** Returns the first of this element's sub-elements. see getNextElement() for an example of how to iterate the sub-elements. - @see forEachXmlChildElement + + @see getChildIterator */ XmlElement* getFirstChildElement() const noexcept { return firstChildElement; } @@ -423,12 +369,12 @@ class JUCE_API XmlElement out. Also, it's much easier and neater to use this method indirectly via the - forEachXmlChildElement macro. + getChildIterator() method. @returns the sibling element that follows this one, or a nullptr if this is the last element in its parent - @see getNextElement, isTextElement, forEachXmlChildElement + @see getNextElement, isTextElement, getChildIterator */ inline XmlElement* getNextElement() const noexcept { return nextListItem; } @@ -438,7 +384,7 @@ class JUCE_API XmlElement This is like getNextElement(), but will scan through the list until it finds an element with the given tag name. - @see getNextElement, forEachXmlChildElementWithTagName + @see getNextElement, getChildIterator */ XmlElement* getNextElementWithTagName (StringRef requiredTagName) const; @@ -695,30 +641,124 @@ class JUCE_API XmlElement /** Checks if a given string is a valid XML name */ static bool isValidXmlName (StringRef possibleName) noexcept; +private: //============================================================================== - /** This has been deprecated in favour of the toString() method. */ - JUCE_DEPRECATED (String createDocument (StringRef dtdToUse, - bool allOnOneLine = false, - bool includeXmlHeader = true, - StringRef encodingType = "UTF-8", - int lineWrapLength = 60) const); - - /** This has been deprecated in favour of the writeTo() method. */ - JUCE_DEPRECATED (void writeToStream (OutputStream& output, - StringRef dtdToUse, - bool allOnOneLine = false, - bool includeXmlHeader = true, - StringRef encodingType = "UTF-8", - int lineWrapLength = 60) const); - - /** This has been deprecated in favour of the writeTo() method. */ - JUCE_DEPRECATED (bool writeToFile (const File& destinationFile, - StringRef dtdToUse, - StringRef encodingType = "UTF-8", - int lineWrapLength = 60) const); + struct GetNextElement + { + XmlElement* getNext (const XmlElement& e) const { return e.getNextElement(); } + }; + + struct GetNextElementWithTagName + { + GetNextElementWithTagName() = default; + explicit GetNextElementWithTagName (String n) : name (std::move (n)) {} + XmlElement* getNext (const XmlElement& e) const { return e.getNextElementWithTagName (name); } + + String name; + }; //============================================================================== + template + class Iterator : private Traits + { + public: + using difference_type = ptrdiff_t; + using value_type = XmlElement*; + using pointer = const value_type*; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + Iterator() = default; + + template + Iterator (XmlElement* e, Args&&... args) + : Traits (std::forward (args)...), element (e) {} + + Iterator begin() const { return *this; } + Iterator end() const { return Iterator{}; } + + bool operator== (const Iterator& other) const { return element == other.element; } + bool operator!= (const Iterator& other) const { return ! operator== (other); } + + reference operator*() const { return element; } + pointer operator->() const { return &element; } + + Iterator& operator++() + { + element = Traits::getNext (*element); + return *this; + } + + Iterator operator++ (int) + { + auto copy = *this; + ++(*this); + return copy; + } + + private: + value_type element = nullptr; + }; + +public: + //============================================================================== + /** Allows iterating the children of an XmlElement using range-for syntax. + + @code + void doSomethingWithXmlChildren (const XmlElement& myParentXml) + { + for (auto* element : myParentXml.getChildIterator()) + doSomethingWithXmlElement (element); + } + @endcode + */ + Iterator getChildIterator() const + { + return Iterator { getFirstChildElement() }; + } + + /** Allows iterating children of an XmlElement with a specific tag using range-for syntax. + + @code + void doSomethingWithXmlChildren (const XmlElement& myParentXml) + { + for (auto* element : myParentXml.getChildWithTagNameIterator ("MYTAG")) + doSomethingWithXmlElement (element); + } + @endcode + */ + Iterator getChildWithTagNameIterator (StringRef name) const + { + return Iterator { getChildByName (name), name }; + } + + #ifndef DOXYGEN + [[deprecated]] void macroBasedForLoop() const noexcept {} + + [[deprecated ("This has been deprecated in favour of the toString method.")]] + String createDocument (StringRef dtdToUse, + bool allOnOneLine = false, + bool includeXmlHeader = true, + StringRef encodingType = "UTF-8", + int lineWrapLength = 60) const; + + [[deprecated ("This has been deprecated in favour of the writeTo method.")]] + void writeToStream (OutputStream& output, + StringRef dtdToUse, + bool allOnOneLine = false, + bool includeXmlHeader = true, + StringRef encodingType = "UTF-8", + int lineWrapLength = 60) const; + + [[deprecated ("This has been deprecated in favour of the writeTo method.")]] + bool writeToFile (const File& destinationFile, + StringRef dtdToUse, + StringRef encodingType = "UTF-8", + int lineWrapLength = 60) const; + #endif + private: + //============================================================================== struct XmlAttributeNode { XmlAttributeNode (const XmlAttributeNode&) noexcept; @@ -750,7 +790,7 @@ class JUCE_API XmlElement void reorderChildElements (XmlElement**, int) noexcept; XmlAttributeNode* getAttribute (StringRef) const noexcept; - // Sigh.. L"" or _T("") string literals are problematic in general, and really inappropriate + // Sigh.. L"" or _T ("") string literals are problematic in general, and really inappropriate // for XML tags. Use a UTF-8 encoded literal instead, or if you're really determined to use // UTF-16, cast it to a String and use the other constructor. XmlElement (const wchar_t*) = delete; @@ -758,4 +798,60 @@ class JUCE_API XmlElement JUCE_LEAK_DETECTOR (XmlElement) }; +//============================================================================== +#ifndef DOXYGEN + +/** DEPRECATED: A handy macro to make it easy to iterate all the child elements in an XmlElement. + + New code should avoid this macro, and instead use getChildIterator directly. + + The parentXmlElement should be a reference to the parent XML, and the childElementVariableName + will be the name of a pointer to each child element. + + E.g. @code + XmlElement* myParentXml = createSomeKindOfXmlDocument(); + + forEachXmlChildElement (*myParentXml, child) + { + if (child->hasTagName ("FOO")) + doSomethingWithXmlElement (child); + } + + @endcode + + @see forEachXmlChildElementWithTagName +*/ +#define forEachXmlChildElement(parentXmlElement, childElementVariableName) \ + for (auto* (childElementVariableName) : ((parentXmlElement).macroBasedForLoop(), (parentXmlElement).getChildIterator())) + +/** DEPRECATED: A macro that makes it easy to iterate all the child elements of an XmlElement + which have a specified tag. + + New code should avoid this macro, and instead use getChildWithTagNameIterator directly. + + This does the same job as the forEachXmlChildElement macro, but only for those + elements that have a particular tag name. + + The parentXmlElement should be a reference to the parent XML, and the childElementVariableName + will be the name of a pointer to each child element. The requiredTagName is the + tag name to match. + + E.g. @code + XmlElement* myParentXml = createSomeKindOfXmlDocument(); + + forEachXmlChildElementWithTagName (*myParentXml, child, "MYTAG") + { + // the child object is now guaranteed to be a element.. + doSomethingWithMYTAGElement (child); + } + + @endcode + + @see forEachXmlChildElement +*/ +#define forEachXmlChildElementWithTagName(parentXmlElement, childElementVariableName, requiredTagName) \ + for (auto* (childElementVariableName) : ((parentXmlElement).macroBasedForLoop(), (parentXmlElement).getChildWithTagNameIterator ((requiredTagName)))) + +#endif + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp index 0f3c951c..552bc3f9 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -92,7 +92,7 @@ class GZIPCompressorOutputStream::GZIPCompressorHelper { case Z_STREAM_END: finished = true; - // Deliberate fall-through.. + JUCE_FALLTHROUGH case Z_OK: { data += dataSize - stream.avail_in; @@ -159,7 +159,7 @@ bool GZIPCompressorOutputStream::setPosition (int64 /*newPosition*/) //============================================================================== #if JUCE_UNIT_TESTS -struct GZIPTests : public UnitTest +struct GZIPTests final : public UnitTest { GZIPTests() : UnitTest ("GZIP", UnitTestCategories::compression) diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h index 297b2585..8367ece7 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h +++ b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp index aee0b7ac..c28fc0d9 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,34 +23,21 @@ namespace juce { -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4309 4305 4365) -#endif +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4309 4305 4365 6385 6326 6340) namespace zlibNamespace { #if JUCE_INCLUDE_ZLIB_CODE - #if JUCE_CLANG - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wconversion" - #pragma clang diagnostic ignored "-Wshadow" - #pragma clang diagnostic ignored "-Wdeprecated-register" - #if __has_warning("-Wzero-as-null-pointer-constant") - #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" - #endif - #if __has_warning("-Wcomma") - #pragma clang diagnostic ignored "-Wcomma" - #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 + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wconversion", + "-Wsign-conversion", + "-Wshadow", + "-Wdeprecated-register", + "-Wswitch-enum", + "-Wswitch-default", + "-Wredundant-decls", + "-Wimplicit-fallthrough", + "-Wzero-as-null-pointer-constant", + "-Wcomma") #undef OS_CODE #undef fdopen @@ -83,13 +70,7 @@ namespace zlibNamespace #undef Dad #undef Len - #if JUCE_CLANG - #pragma clang diagnostic pop - #endif - - #if JUCE_GCC - #pragma GCC diagnostic pop - #endif + JUCE_END_IGNORE_WARNINGS_GCC_LIKE #else #include JUCE_ZLIB_INCLUDE_PATH @@ -104,9 +85,7 @@ namespace zlibNamespace #endif } -#if JUCE_MSVC - #pragma warning (pop) -#endif +JUCE_END_IGNORE_WARNINGS_MSVC //============================================================================== // internal helper object that holds the zlib structures so they don't have to be @@ -151,7 +130,7 @@ class GZIPDecompressorInputStream::GZIPDecompressHelper { case Z_STREAM_END: finished = true; - // deliberate fall-through + JUCE_FALLTHROUGH case Z_OK: data += dataSize - stream.avail_in; dataSize = (z_uInt) stream.avail_in; @@ -166,7 +145,7 @@ class GZIPDecompressorInputStream::GZIPDecompressHelper case Z_DATA_ERROR: case Z_MEM_ERROR: error = true; - + JUCE_FALLTHROUGH default: break; } @@ -315,7 +294,7 @@ bool GZIPDecompressorInputStream::setPosition (int64 newPos) //============================================================================== #if JUCE_UNIT_TESTS -struct GZIPDecompressorInputStreamTests : public UnitTest +struct GZIPDecompressorInputStreamTests final : public UnitTest { GZIPDecompressorInputStreamTests() : UnitTest ("GZIPDecompressorInputStreamTests", UnitTestCategories::streams) diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h index cc4cfe81..8752e6e2 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h +++ b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -92,11 +92,6 @@ class JUCE_API GZIPDecompressorInputStream : public InputStream class GZIPDecompressHelper; std::unique_ptr helper; - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // The arguments to this method have changed! Please pass a Format enum instead of the old dontWrap bool. - GZIPDecompressorInputStream (InputStream*, bool, bool, int64 x = -1); - #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPDecompressorInputStream) }; diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp index 3feb7fa3..963183c2 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp +++ b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -122,8 +122,21 @@ static int64 findCentralDirectoryFileHeader (InputStream& input, int& numEntries return 0; } +static bool hasSymbolicPart (const File& root, const File& f) +{ + jassert (root == f || f.isAChildOf (root)); + + for (auto p = f; p != root; p = p.getParentDirectory()) + { + if (p.isSymbolicLink()) + return true; + } + + return false; +} + //============================================================================== -struct ZipFile::ZipInputStream : public InputStream +struct ZipFile::ZipInputStream final : public InputStream { ZipInputStream (ZipFile& zf, const ZipFile::ZipEntryHolder& zei) : file (zf), @@ -400,6 +413,14 @@ Result ZipFile::uncompressTo (const File& targetDirectory, } Result ZipFile::uncompressEntry (int index, const File& targetDirectory, bool shouldOverwriteFiles) +{ + return uncompressEntry (index, + targetDirectory, + shouldOverwriteFiles ? OverwriteFiles::yes : OverwriteFiles::no, + FollowSymlinks::no); +} + +Result ZipFile::uncompressEntry (int index, const File& targetDirectory, OverwriteFiles overwriteFiles, FollowSymlinks followSymlinks) { auto* zei = entries.getUnchecked (index); @@ -414,6 +435,9 @@ Result ZipFile::uncompressEntry (int index, const File& targetDirectory, bool sh auto targetFile = targetDirectory.getChildFile (entryPath); + if (! targetFile.isAChildOf (targetDirectory)) + return Result::fail ("Entry " + entryPath + " is outside the target directory"); + if (entryPath.endsWithChar ('/') || entryPath.endsWithChar ('\\')) return targetFile.createDirectory(); // (entry is a directory, not a file) @@ -424,13 +448,16 @@ Result ZipFile::uncompressEntry (int index, const File& targetDirectory, bool sh if (targetFile.exists()) { - if (! shouldOverwriteFiles) + if (overwriteFiles == OverwriteFiles::no) return Result::ok(); if (! targetFile.deleteFile()) return Result::fail ("Failed to write to target file: " + targetFile.getFullPathName()); } + if (followSymlinks == FollowSymlinks::no && hasSymbolicPart (targetDirectory, targetFile.getParentDirectory())) + return Result::fail ("Parent directory leads through symlink for target file: " + targetFile.getFullPathName()); + if (! targetFile.getParentDirectory().createDirectory()) return Result::fail ("Failed to create target folder: " + targetFile.getParentDirectory().getFullPathName()); @@ -541,7 +568,7 @@ struct ZipFile::Builder::Item { if (stream == nullptr) { - stream.reset (file.createInputStream()); + stream = file.createInputStream(); if (stream == nullptr) return false; @@ -577,7 +604,7 @@ struct ZipFile::Builder::Item target.writeInt ((int) checksum); target.writeInt ((int) (uint32) compressedSize); target.writeInt ((int) (uint32) uncompressedSize); - target.writeShort ((short) storedPathname.toUTF8().sizeInBytes() - 1); + target.writeShort (static_cast (storedPathname.toUTF8().sizeInBytes() - 1)); target.writeShort (0); // extra field length } @@ -643,18 +670,15 @@ bool ZipFile::Builder::writeToStream (OutputStream& target, double* const progre //============================================================================== #if JUCE_UNIT_TESTS -struct ZIPTests : public UnitTest +struct ZIPTests final : public UnitTest { ZIPTests() : UnitTest ("ZIP", UnitTestCategories::compression) {} - void runTest() override + static MemoryBlock createZipMemoryBlock (const StringArray& entryNames) { - beginTest ("ZIP"); - ZipFile::Builder builder; - StringArray entryNames { "first", "second", "third" }; HashMap blocks; for (auto& entryName : entryNames) @@ -669,8 +693,61 @@ struct ZIPTests : public UnitTest MemoryBlock data; MemoryOutputStream mo (data, false); builder.writeToStream (mo, nullptr); + + return data; + } + + void runZipSlipTest() + { + const std::map testCases = { { "a", true }, +#if JUCE_WINDOWS + { "C:/b", false }, +#else + { "/b", false }, +#endif + { "c/d", true }, + { "../e/f", false }, + { "../../g/h", false }, + { "i/../j", true }, + { "k/l/../", true }, + { "m/n/../../", false }, + { "o/p/../../../", false } }; + + StringArray entryNames; + + for (const auto& testCase : testCases) + entryNames.add (testCase.first); + + TemporaryFile tmpDir; + tmpDir.getFile().createDirectory(); + auto data = createZipMemoryBlock (entryNames); MemoryInputStream mi (data, false); + ZipFile zip (mi); + + for (int i = 0; i < zip.getNumEntries(); ++i) + { + const auto result = zip.uncompressEntry (i, tmpDir.getFile()); + const auto caseIt = testCases.find (zip.getEntry (i)->filename); + if (caseIt != testCases.end()) + { + expect (result.wasOk() == caseIt->second, + zip.getEntry (i)->filename + " was unexpectedly " + (result.wasOk() ? "OK" : "not OK")); + } + else + { + expect (false); + } + } + } + + void runTest() override + { + beginTest ("ZIP"); + + StringArray entryNames { "first", "second", "third" }; + auto data = createZipMemoryBlock (entryNames); + MemoryInputStream mi (data, false); ZipFile zip (mi); expectEquals (zip.getNumEntries(), entryNames.size()); @@ -681,6 +758,9 @@ struct ZIPTests : public UnitTest std::unique_ptr input (zip.createStreamForEntry (*entry)); expectEquals (input->readEntireStreamAsString(), entryName); } + + beginTest ("ZipSlip"); + runZipSlipTest(); } }; diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h index f4b8b12e..1ac14ddc 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h +++ b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -179,6 +179,25 @@ class JUCE_API ZipFile const File& targetDirectory, bool shouldOverwriteFiles = true); + enum class OverwriteFiles { no, yes }; + enum class FollowSymlinks { no, yes }; + + /** Uncompresses one of the entries from the zip file. + + This will expand the entry and write it in a target directory. The entry's path is used to + determine which subfolder of the target should contain the new file. + + @param index the index of the entry to uncompress - this must be a valid index + between 0 and (getNumEntries() - 1). + @param targetDirectory the root folder to uncompress into + @param overwriteFiles whether to overwrite existing files with similarly-named ones + @param followSymlinks whether to follow symlinks inside the target directory + @returns success if all the files are successfully unzipped + */ + Result uncompressEntry (int index, + const File& targetDirectory, + OverwriteFiles overwriteFiles, + FollowSymlinks followSymlinks); //============================================================================== /** Used to create a new zip file. diff --git a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp index fb5e813d..d07f0c7f 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -27,10 +26,6 @@ namespace juce { -ApplicationProperties::ApplicationProperties() -{ -} - ApplicationProperties::~ApplicationProperties() { closeFiles(); diff --git a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h index 36ac2b0f..e12130b7 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -58,7 +57,7 @@ class JUCE_API ApplicationProperties Before using it, you must call setStorageParameters() to give it the info it needs to create the property files. */ - ApplicationProperties(); + ApplicationProperties() = default; /** Destructor. */ ~ApplicationProperties(); 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 c729a70d..189fdd36 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -29,13 +28,13 @@ namespace juce namespace PropertyFileConstants { - JUCE_CONSTEXPR static const int magicNumber = (int) ByteOrder::makeInt ('P', 'R', 'O', 'P'); - JUCE_CONSTEXPR static const int magicNumberCompressed = (int) ByteOrder::makeInt ('C', 'P', 'R', 'P'); + constexpr static const int magicNumber = (int) ByteOrder::makeInt ('P', 'R', 'O', 'P'); + constexpr static const int magicNumberCompressed = (int) ByteOrder::makeInt ('C', 'P', 'R', 'P'); - JUCE_CONSTEXPR static const char* const fileTag = "PROPERTIES"; - JUCE_CONSTEXPR static const char* const valueTag = "VALUE"; - JUCE_CONSTEXPR static const char* const nameAttribute = "name"; - JUCE_CONSTEXPR static const char* const valueAttribute = "val"; + constexpr static const char* const fileTag = "PROPERTIES"; + constexpr static const char* const valueTag = "VALUE"; + constexpr static const char* const nameAttribute = "name"; + constexpr static const char* const valueAttribute = "val"; } //============================================================================== @@ -90,7 +89,7 @@ File PropertiesFile::Options::getDefaultFile() const if (folderName.isNotEmpty()) dir = dir.getChildFile (folderName); - #elif JUCE_LINUX || JUCE_ANDROID + #elif JUCE_LINUX || JUCE_BSD || JUCE_ANDROID auto dir = File (commonToAllUsers ? "/var" : "~") .getChildFile (folderName.isNotEmpty() ? folderName : ("." + applicationName)); @@ -188,7 +187,7 @@ bool PropertiesFile::loadAsXml() { if (auto doc = parseXMLIfTagMatches (file, PropertyFileConstants::fileTag)) { - forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag) + for (auto* e : doc->getChildWithTagNameIterator (PropertyFileConstants::valueTag)) { auto name = e->getStringAttribute (PropertyFileConstants::nameAttribute); diff --git a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h index f4704f41..8a15df2d 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp index 96207950..027f05dc 100644 --- a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -39,7 +38,11 @@ #include "values/juce_ValueTree.cpp" #include "values/juce_ValueTreeSynchroniser.cpp" #include "values/juce_CachedValue.cpp" -#include "values/juce_ValueWithDefault.cpp" #include "undomanager/juce_UndoManager.cpp" +#include "undomanager/juce_UndoableAction.cpp" #include "app_properties/juce_ApplicationProperties.cpp" #include "app_properties/juce_PropertiesFile.cpp" + +#if JUCE_UNIT_TESTS + #include "values/juce_ValueTreePropertyWithDefault_test.cpp" +#endif diff --git a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h index 4878fa39..5402d733 100644 --- a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h +++ b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -29,18 +28,19 @@ The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. For details about the syntax and how to create or use a module, see the - JUCE Module Format.txt file. + JUCE Module Format.md file. BEGIN_JUCE_MODULE_DECLARATION ID: juce_data_structures vendor: juce - version: 5.4.7 + version: 7.0.10 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 + minimumCppStandard: 17 dependencies: juce_events @@ -61,6 +61,6 @@ #include "values/juce_ValueTree.h" #include "values/juce_ValueTreeSynchroniser.h" #include "values/juce_CachedValue.h" -#include "values/juce_ValueWithDefault.h" +#include "values/juce_ValueTreePropertyWithDefault.h" #include "app_properties/juce_PropertiesFile.h" #include "app_properties/juce_ApplicationProperties.h" diff --git a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.mm b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.mm index 1f5d94a0..bb15842a 100644 --- a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.mm +++ b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.mm @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp index 5f35f2ac..9429cdf2 100644 --- a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -44,7 +43,7 @@ struct UndoManager::ActionSet bool undo() const { for (int i = actions.size(); --i >= 0;) - if (! actions.getUnchecked(i)->undo()) + if (! actions.getUnchecked (i)->undo()) return false; return true; diff --git a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h index d86b9695..eb6a3581 100644 --- a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h +++ b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.cpp b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.cpp new file mode 100644 index 00000000..1257b1f6 --- /dev/null +++ b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.cpp @@ -0,0 +1,31 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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 +{ + +UndoableAction* UndoableAction::createCoalescedAction ([[maybe_unused]] UndoableAction* nextAction) { return nullptr; } + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.h b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.h index b9e21707..bc89efd2 100644 --- a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.h +++ b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -95,7 +94,7 @@ class JUCE_API UndoableAction If it's not possible to merge the two actions, the method should return a nullptr. */ - virtual UndoableAction* createCoalescedAction (UndoableAction* nextAction) { ignoreUnused (nextAction); return nullptr; } + virtual UndoableAction* createCoalescedAction (UndoableAction* nextAction); }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.cpp index cc5516b5..6a5930ba 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -29,7 +28,7 @@ namespace juce #if JUCE_UNIT_TESTS -class CachedValueTests : public UnitTest +class CachedValueTests final : public UnitTest { public: CachedValueTests() diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h index c56eb754..4e3d1943 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -106,24 +105,27 @@ class CachedValue : private ValueTree::Listener Type get() const noexcept { return cachedValue; } /** Dereference operator. Provides direct access to the property. */ - Type& operator*() noexcept { return cachedValue; } + const Type& operator*() const noexcept { return cachedValue; } /** Dereference operator. Provides direct access to members of the property if it is of object type. */ - Type* operator->() noexcept { return &cachedValue; } + const Type* operator->() const noexcept { return &cachedValue; } /** Returns true if the current value of the property (or the fallback value) is equal to other. */ template - bool operator== (const OtherType& other) const { return cachedValue == other; } + bool operator== (const OtherType& other) const + { + return cachedValue == other; + } /** Returns true if the current value of the property (or the fallback value) is not equal to other. */ template - bool operator!= (const OtherType& other) const { return cachedValue != other; } + bool operator!= (const OtherType& other) const { return ! operator== (other); } //============================================================================== /** Returns the current property as a Value object. */ @@ -246,7 +248,7 @@ inline CachedValue& CachedValue::operator= (const Type& newValue) template inline void CachedValue::setValue (const Type& newValue, UndoManager* undoManagerToUse) { - if (cachedValue != newValue || isUsingDefault()) + if (! exactlyEqual (cachedValue, newValue) || isUsingDefault()) { cachedValue = newValue; targetTree.setProperty (targetProperty, VariantConverter::toVar (newValue), undoManagerToUse); diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.cpp index 3c99cc22..9e4784ad 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -65,7 +64,7 @@ void Value::ValueSource::sendChangeMessage (const bool synchronous) } //============================================================================== -class SimpleValueSource : public Value::ValueSource +class SimpleValueSource final : public Value::ValueSource { public: SimpleValueSource() diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.h index ca62e214..a45e1b23 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -115,7 +114,8 @@ class JUCE_API Value final */ void referTo (const Value& valueToReferTo); - /** Returns true if this value and the other one are references to the same value. + /** Returns true if this object and the other one use the same underlying + ValueSource object. */ bool refersToSameSourceAs (const Value& other) const; diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp index bd57732e..6433344c 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -27,7 +26,7 @@ namespace juce { -class ValueTree::SharedObject : public ReferenceCountedObject +class ValueTree::SharedObject final : public ReferenceCountedObject { public: using Ptr = ReferenceCountedObjectPtr; @@ -47,7 +46,7 @@ class ValueTree::SharedObject : public ReferenceCountedObject SharedObject& operator= (const SharedObject&) = delete; - ~SharedObject() + ~SharedObject() override { jassert (parent == nullptr); // this should never happen unless something isn't obeying the ref-counting! @@ -409,7 +408,7 @@ class ValueTree::SharedObject : public ReferenceCountedObject } //============================================================================== - struct SetPropertyAction : public UndoableAction + struct SetPropertyAction final : public UndoableAction { SetPropertyAction (Ptr targetObject, const Identifier& propertyName, const var& newVal, const var& oldVal, bool isAdding, bool isDeleting, @@ -473,7 +472,7 @@ class ValueTree::SharedObject : public ReferenceCountedObject }; //============================================================================== - struct AddOrRemoveChildAction : public UndoableAction + struct AddOrRemoveChildAction final : public UndoableAction { AddOrRemoveChildAction (Ptr parentObject, int index, SharedObject* newChild) : target (std::move (parentObject)), @@ -525,7 +524,7 @@ class ValueTree::SharedObject : public ReferenceCountedObject }; //============================================================================== - struct MoveChildAction : public UndoableAction + struct MoveChildAction final : public UndoableAction { MoveChildAction (Ptr parentObject, int fromIndex, int toIndex) noexcept : parent (std::move (parentObject)), startIndex (fromIndex), endIndex (toIndex) @@ -580,8 +579,6 @@ ValueTree::ValueTree() noexcept { } -JUCE_DECLARE_DEPRECATED_STATIC (const ValueTree ValueTree::invalid;) - ValueTree::ValueTree (const Identifier& type) : object (new ValueTree::SharedObject (type)) { jassert (type.toString().isNotEmpty()); // All objects must be given a sensible type name! @@ -672,6 +669,9 @@ void ValueTree::copyPropertiesFrom (const ValueTree& source, UndoManager* undoMa { jassert (object != nullptr || source.object == nullptr); // Trying to add properties to a null ValueTree will fail! + if (source == *this) + return; + if (source.object == nullptr) removeAllProperties (undoManager); else if (object != nullptr) @@ -682,6 +682,9 @@ void ValueTree::copyPropertiesAndChildrenFrom (const ValueTree& source, UndoMana { jassert (object != nullptr || source.object == nullptr); // Trying to copy to a null ValueTree will fail! + if (source == *this) + return; + copyPropertiesFrom (source, undoManager); removeAllChildren (undoManager); @@ -806,8 +809,8 @@ int ValueTree::getReferenceCount() const noexcept } //============================================================================== -struct ValueTreePropertyValueSource : public Value::ValueSource, - private ValueTree::Listener +struct ValueTreePropertyValueSource final : public Value::ValueSource, + private ValueTree::Listener { ValueTreePropertyValueSource (const ValueTree& vt, const Identifier& prop, UndoManager* um, bool sync) : tree (vt), property (prop), undoManager (um), updateSynchronously (sync) @@ -870,7 +873,7 @@ ValueTree::Iterator::Iterator (const ValueTree& v, bool isEnd) ValueTree::Iterator& ValueTree::Iterator::operator++() { - internal = static_cast (internal) + 1; + ++internal; return *this; } @@ -879,7 +882,7 @@ bool ValueTree::Iterator::operator!= (const Iterator& other) const { return int ValueTree ValueTree::Iterator::operator*() const { - return ValueTree (SharedObject::Ptr (*static_cast (internal))); + return ValueTree (SharedObject::Ptr (*internal)); } ValueTree::Iterator ValueTree::begin() const noexcept { return Iterator (*this, false); } @@ -950,19 +953,16 @@ void ValueTree::moveChild (int currentIndex, int newIndex, UndoManager* undoMana //============================================================================== void ValueTree::createListOfChildren (OwnedArray& list) const { - jassert (object != nullptr); - - for (auto* o : object->children) - { - jassert (o != nullptr); - list.add (new ValueTree (*o)); - } + if (object != nullptr) + for (auto* o : object->children) + if (o != nullptr) + list.add (new ValueTree (*o)); } void ValueTree::reorderChildren (const OwnedArray& newOrder, UndoManager* undoManager) { - jassert (object != nullptr); - object->reorderChildren (newOrder, undoManager); + if (object != nullptr) + object->reorderChildren (newOrder, undoManager); } //============================================================================== @@ -1004,7 +1004,7 @@ ValueTree ValueTree::fromXml (const XmlElement& xml) ValueTree v (xml.getTagName()); v.object->properties.setFromXmlAttributes (xml); - forEachXmlChildElement (xml, e) + for (auto* e : xml.getChildIterator()) v.appendChild (fromXml (*e), nullptr); return v; @@ -1101,12 +1101,24 @@ void ValueTree::Listener::valueTreeChildOrderChanged (ValueTree&, int, int) void ValueTree::Listener::valueTreeParentChanged (ValueTree&) {} void ValueTree::Listener::valueTreeRedirected (ValueTree&) {} +//============================================================================== +#if JUCE_ALLOW_STATIC_NULL_VARIABLES + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +const ValueTree ValueTree::invalid; + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_WARNINGS_MSVC + +#endif //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS -class ValueTreeTests : public UnitTest +class ValueTreeTests final : public UnitTest { public: ValueTreeTests() diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h index 831b971e..ad45b2d5 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -71,6 +70,8 @@ namespace juce */ class JUCE_API ValueTree final { + JUCE_PUBLIC_IN_DLL_BUILD (class SharedObject) + public: //============================================================================== /** Creates an empty, invalid ValueTree. @@ -414,7 +415,7 @@ class JUCE_API ValueTree final using iterator_category = std::forward_iterator_tag; private: - void* internal; + SharedObject** internal = nullptr; }; /** Returns a start iterator for the children in this tree. */ @@ -427,7 +428,6 @@ class JUCE_API ValueTree final /** Creates an XmlElement that holds a complete image of this tree and all its children. If this tree is invalid, this may return nullptr. Otherwise, the XML that is produced can be used to recreate a similar tree by calling ValueTree::fromXml(). - The caller must delete the object that is returned. @see fromXml, toXmlString */ std::unique_ptr createXml() const; @@ -608,14 +608,14 @@ class JUCE_API ValueTree final */ int getReferenceCount() const noexcept; - /* An invalid ValueTree that can be used if you need to return one as an error condition, etc. - @deprecated If you need an empty ValueTree object, just use ValueTree() or {}. - */ - JUCE_DEPRECATED_STATIC (static const ValueTree invalid;) + #if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN) + /* An invalid ValueTree that can be used if you need to return one as an error condition, etc. */ + [[deprecated ("If you need an empty ValueTree object, just use ValueTree() or {}.")]] + static const ValueTree invalid; + #endif private: //============================================================================== - JUCE_PUBLIC_IN_DLL_BUILD (class SharedObject) friend class SharedObject; ReferenceCountedObjectPtr object; diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault.h new file mode 100644 index 00000000..8a2f1356 --- /dev/null +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault.h @@ -0,0 +1,333 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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 class acts as a wrapper around a property inside a ValueTree. + + If the property inside the ValueTree is missing it will return a default value, + which can be specified in the constructor or by calling setDefault(). + + @tags{DataStructures} +*/ +class JUCE_API ValueTreePropertyWithDefault : private Value::Listener +{ +public: + //============================================================================== + /** Creates an uninitialised ValueTreePropertyWithDefault object. + + Initialise it using one of the referTo() methods. + */ + ValueTreePropertyWithDefault() = default; + + /** Creates a ValueTreePropertyWithDefault object for the specified property. + + The default value will be an empty var. + */ + ValueTreePropertyWithDefault (ValueTree& tree, + const Identifier& propertyID, + UndoManager* um) + { + referTo (tree, propertyID, um); + } + + /** Creates an ValueTreePropertyWithDefault object for the specified property. + + The default value will be defaultToUse. + */ + ValueTreePropertyWithDefault (ValueTree& tree, + const Identifier& propertyID, + UndoManager* um, + var defaultToUse) + { + referTo (tree, propertyID, um, defaultToUse); + } + + /** Creates a ValueTreePropertyWithDefault object for the specified property. + + The default value will be defaultToUse. + + Use this constructor if the underlying var object being controlled is an array and + it will handle the conversion to/from a delimited String that can be written to + XML format. + */ + ValueTreePropertyWithDefault (ValueTree& tree, + const Identifier& propertyID, + UndoManager* um, + var defaultToUse, + StringRef arrayDelimiter) + { + referTo (tree, propertyID, um, defaultToUse, arrayDelimiter); + } + + /** Creates a ValueTreePropertyWithDefault object from another ValueTreePropertyWithDefault object. */ + ValueTreePropertyWithDefault (const ValueTreePropertyWithDefault& other) + { + referToWithDefault (other.targetTree, + other.targetProperty, + other.undoManager, + other.defaultValue, + other.delimiter); + } + + /** Destructor. */ + ~ValueTreePropertyWithDefault() override + { + defaultValue.removeListener (this); + } + + //============================================================================== + /** Returns the current value of the property. + + If the property does not exist this returns the default value. + */ + var get() const noexcept + { + if (isUsingDefault()) + return defaultValue; + + if (delimiter.isNotEmpty()) + return delimitedStringToVarArray (targetTree[targetProperty].toString(), delimiter); + + return targetTree[targetProperty]; + } + + /** Returns the current property as a Value object. */ + Value getPropertyAsValue() { return targetTree.getPropertyAsValue (targetProperty, undoManager); } + + /** Returns the current default value. */ + var getDefault() const { return defaultValue; } + + /** Sets the default value to a new var. */ + void setDefault (const var& newDefault) { defaultValue = newDefault; } + + /** Returns true if the property does not exist in the referenced ValueTree. */ + bool isUsingDefault() const { return ! targetTree.hasProperty (targetProperty); } + + /** Removes the property from the referenced ValueTree. */ + void resetToDefault() noexcept { targetTree.removeProperty (targetProperty, nullptr); } + + /** You can assign a lambda to this callback and it will called when the default + value is changed. + + @see setDefault + */ + std::function onDefaultChange; + + //============================================================================== + /** Sets the property and returns the new ValueTreePropertyWithDefault. + + This will modify the property in the referenced ValueTree. + */ + ValueTreePropertyWithDefault& operator= (const var& newValue) + { + setValue (newValue, undoManager); + return *this; + } + + /** Sets the property. + + This will modify the property in the referenced ValueTree. + */ + void setValue (const var& newValue, UndoManager* undoManagerToUse) + { + if (auto* array = newValue.getArray()) + targetTree.setProperty (targetProperty, varArrayToDelimitedString (*array, delimiter), undoManagerToUse); + else + targetTree.setProperty (targetProperty, newValue, undoManagerToUse); + } + + //============================================================================== + /** Makes the ValueTreePropertyWithDefault refer to the specified property inside + the given ValueTree. + + The default value will be an empty var. + */ + void referTo (ValueTree tree, + const Identifier& property, + UndoManager* um) + { + referToWithDefault (tree, + property, + um, + Value (new SynchronousValueSource (var())), + {}); + } + + /** Makes the ValueTreePropertyWithDefault refer to the specified property inside + the given ValueTree. + + The default value will be defaultVal. + */ + void referTo (ValueTree tree, + const Identifier& property, + UndoManager* um, + var defaultVal) + { + referToWithDefault (tree, + property, + um, + Value (new SynchronousValueSource (defaultVal)), + {}); + } + + /** Makes the ValueTreePropertyWithDefault refer to the specified property inside + the given ValueTree. + + The default value will be defaultVal. + */ + void referTo (ValueTree tree, + const Identifier& property, + UndoManager* um, + var defaultVal, + StringRef arrayDelimiter) + { + referToWithDefault (tree, + property, + um, + Value (new SynchronousValueSource (defaultVal)), + arrayDelimiter); + } + + //============================================================================== + /** Returns a reference to the ValueTree containing the referenced property. */ + ValueTree& getValueTree() noexcept { return targetTree; } + + /** Returns the property ID of the referenced property. */ + Identifier& getPropertyID() noexcept { return targetProperty; } + + /** Returns the UndoManager that is being used. */ + UndoManager* getUndoManager() noexcept { return undoManager; } + + //============================================================================== + ValueTreePropertyWithDefault& operator= (const ValueTreePropertyWithDefault& other) + { + referToWithDefault (other.targetTree, + other.targetProperty, + other.undoManager, + other.defaultValue, + other.delimiter); + + return *this; + } + +private: + //============================================================================== + class SynchronousValueSource : public Value::ValueSource + { + public: + explicit SynchronousValueSource (const var& initialValue) + : value (initialValue) + { + } + + var getValue() const override + { + return value; + } + + void setValue (const var& newValue) override + { + if (! newValue.equalsWithSameType (value)) + { + value = newValue; + sendChangeMessage (true); + } + } + + private: + var value; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SynchronousValueSource) + }; + + //============================================================================== + static String varArrayToDelimitedString (const Array& input, StringRef delim) + { + // if you are trying to control a var that is an array then you need to + // set a delimiter string that will be used when writing to XML! + jassert (delim.isNotEmpty()); + + StringArray elements; + + for (auto& v : input) + elements.add (v.toString()); + + return elements.joinIntoString (delim); + } + + static Array delimitedStringToVarArray (StringRef input, StringRef delim) + { + Array arr; + + for (auto t : StringArray::fromTokens (input, delim, {})) + arr.add (t); + + return arr; + } + + void valueChanged (Value&) override + { + NullCheckedInvocation::invoke (onDefaultChange); + } + + void referToWithDefault (ValueTree v, + const Identifier& i, + UndoManager* um, + const Value& defaultVal, + StringRef del) + { + targetTree = v; + targetProperty = i; + undoManager = um; + defaultValue.referTo (defaultVal); + delimiter = del; + + defaultValue.addListener (this); + } + + //============================================================================== + ValueTree targetTree; + Identifier targetProperty; + UndoManager* undoManager = nullptr; + Value defaultValue; + String delimiter; + + //============================================================================== + JUCE_LEAK_DETECTOR (ValueTreePropertyWithDefault) +}; + +//============================================================================== +#ifndef DOXYGEN +using ValueWithDefault [[deprecated ("This class has been renamed to better describe what is does. " + "This declaration is here for backwards compatibility and new " + "code should use the new class name.")]] + = ValueTreePropertyWithDefault; +#endif + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault_test.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault_test.cpp new file mode 100644 index 00000000..9b92ee18 --- /dev/null +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreePropertyWithDefault_test.cpp @@ -0,0 +1,97 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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 ValueTreePropertyWithDefaultTests final : public UnitTest +{ +public: + ValueTreePropertyWithDefaultTests() + : UnitTest ("ValueTreePropertyWithDefault", UnitTestCategories::values) + {} + + void runTest() override + { + beginTest ("default constructor"); + { + ValueTreePropertyWithDefault value; + expect (value.isUsingDefault()); + expect (value.get() == var()); + } + + beginTest ("missing property"); + { + ValueTree t ("root"); + ValueTreePropertyWithDefault value (t, "testKey", nullptr, "default"); + + expect (value.isUsingDefault()); + expectEquals (value.get().toString(), String ("default")); + } + + beginTest ("non-empty property"); + { + ValueTree t ("root"); + t.setProperty ("testKey", "non-default", nullptr); + + ValueTreePropertyWithDefault value (t, "testKey", nullptr, "default"); + + expect (! value.isUsingDefault()); + expectEquals (value.get().toString(), String ("non-default")); + } + + beginTest ("set default"); + { + ValueTree t ("root"); + + ValueTreePropertyWithDefault value (t, "testkey", nullptr); + value.setDefault ("default"); + + expect (value.isUsingDefault()); + expectEquals (value.get().toString(), String ("default")); + } + + beginTest ("set value"); + { + ValueTree t ("root"); + t.setProperty ("testkey", "testvalue", nullptr); + + ValueTreePropertyWithDefault value (t, "testkey", nullptr, "default"); + value = "newvalue"; + + expect (! value.isUsingDefault()); + expectEquals (t["testkey"].toString(), String ("newvalue")); + + value.resetToDefault(); + + expect (value.isUsingDefault()); + expect (t["testkey"] == var()); + } + } +}; + +static ValueTreePropertyWithDefaultTests valueTreePropertyWithDefaultTests; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp index 2f95704c..a261caa8 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -69,7 +68,7 @@ namespace ValueTreeSynchroniserHelpers stream.writeCompressedInt (path.size()); for (int i = path.size(); --i >= 0;) - stream.writeCompressedInt (path.getUnchecked(i)); + stream.writeCompressedInt (path.getUnchecked (i)); } static ValueTree readSubTreeLocation (MemoryInputStream& input, ValueTree v) @@ -229,6 +228,9 @@ bool ValueTreeSynchroniser::applyChange (ValueTree& root, const void* data, size break; } + case ValueTreeSynchroniserHelpers::fullSync: + break; + default: jassertfalse; // Seem to have received some corrupt data? break; diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h index d72b32b6..5d6653a2 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.cpp deleted file mode 100644 index 6b42ad8e..00000000 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.cpp +++ /dev/null @@ -1,102 +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. - - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). - - 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 -{ - -#if JUCE_UNIT_TESTS - -class ValueWithDefaultTests : public UnitTest -{ -public: - ValueWithDefaultTests() - : UnitTest ("ValueWithDefault", UnitTestCategories::values) - {} - - void runTest() override - { - beginTest ("default constructor"); - { - ValueWithDefault vwd; - expect (vwd.isUsingDefault()); - expect (vwd.get() == var()); - } - - beginTest ("missing property"); - { - ValueTree t ("root"); - ValueWithDefault vwd (t, "testKey", nullptr, "default"); - - expect (vwd.isUsingDefault()); - expectEquals (vwd.get().toString(), String ("default")); - } - - beginTest ("non-empty property"); - { - ValueTree t ("root"); - t.setProperty ("testKey", "non-default", nullptr); - - ValueWithDefault vwd (t, "testKey", nullptr, "default"); - - expect (! vwd.isUsingDefault()); - expectEquals (vwd.get().toString(), String ("non-default")); - } - - beginTest ("set default"); - { - ValueTree t ("root"); - - ValueWithDefault vwd (t, "testkey", nullptr); - vwd.setDefault ("default"); - - expect (vwd.isUsingDefault()); - expectEquals (vwd.get().toString(), String ("default")); - } - - beginTest ("set value"); - { - ValueTree t ("root"); - t.setProperty ("testkey", "testvalue", nullptr); - - ValueWithDefault vwd (t, "testkey", nullptr, "default"); - vwd = "newvalue"; - - expect (! vwd.isUsingDefault()); - expectEquals (t["testkey"].toString(), String ("newvalue")); - - vwd.resetToDefault(); - - expect (vwd.isUsingDefault()); - expect (t["testkey"] == var()); - } - } -}; - -static ValueWithDefaultTests valueWithDefaultTests; - -#endif - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.h deleted file mode 100644 index b9eaa1dc..00000000 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueWithDefault.h +++ /dev/null @@ -1,245 +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. - - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). - - 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 class acts as a wrapper around a property inside a ValueTree. - - If the property inside the ValueTree is missing or empty the ValueWithDefault will automatically - return a default value, which can be specified when initialising the ValueWithDefault. - - @tags{DataStructures} -*/ -class ValueWithDefault -{ -public: - //============================================================================== - /** Creates an unitialised ValueWithDefault. Initialise it using one of the referTo() methods. */ - ValueWithDefault() = default; - - /** Creates an ValueWithDefault object. The default value will be an empty var. */ - ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um) - : targetTree (tree), - targetProperty (propertyID), - undoManager (um), - defaultValue() - { - } - - /** Creates an ValueWithDefault object. The default value will be defaultToUse. */ - ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um, - const var& defaultToUse) - : targetTree (tree), - targetProperty (propertyID), - undoManager (um), - defaultValue (defaultToUse) - { - } - - /** Creates an ValueWithDefault object. The default value will be defaultToUse. - - Use this constructor if the underlying var object being controlled is an array and - it will handle the conversion to/from a delimited String that can be written to - XML format. - */ - ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um, - const var& defaultToUse, StringRef arrayDelimiter) - : targetTree (tree), - targetProperty (propertyID), - undoManager (um), - defaultValue (defaultToUse), - delimiter (arrayDelimiter) - { - } - - /** Creates a ValueWithDefault object from another ValueWithDefault object. */ - ValueWithDefault (const ValueWithDefault& other) - : targetTree (other.targetTree), - targetProperty (other.targetProperty), - undoManager (other.undoManager), - defaultValue (other.defaultValue), - delimiter (other.delimiter) - { - } - - //============================================================================== - /** Returns the current value of the property. If the property does not exist or is empty, - returns the default value. - */ - var get() const noexcept - { - if (isUsingDefault()) - return defaultValue; - - if (delimiter.isNotEmpty()) - return delimitedStringToVarArray (targetTree[targetProperty].toString()); - - return targetTree[targetProperty]; - } - - /** Returns the current property as a Value object. */ - Value getPropertyAsValue() { return targetTree.getPropertyAsValue (targetProperty, undoManager); } - - /** Returns the current default value. */ - var getDefault() const { return defaultValue; } - - /** Sets the default value to a new var. */ - void setDefault (const var& newDefault) - { - if (defaultValue != newDefault) - { - defaultValue = newDefault; - - if (onDefaultChange != nullptr) - onDefaultChange(); - } - } - - /** Returns true if the property does not exist in the referenced ValueTree. */ - bool isUsingDefault() const - { - return ! targetTree.hasProperty (targetProperty); - } - - /** Removes the property from the referenced ValueTree. */ - void resetToDefault() noexcept - { - targetTree.removeProperty (targetProperty, nullptr); - } - - /** You can assign a lambda to this callback object to have it called when the default value is changed. */ - std::function onDefaultChange; - - //============================================================================== - /** Sets the property and returns the new ValueWithDefault. This will modify the property in the referenced ValueTree. */ - ValueWithDefault& operator= (const var& newValue) - { - setValue (newValue, undoManager); - return *this; - } - - /** Sets the property. This will actually modify the property in the referenced ValueTree. */ - void setValue (const var& newValue, UndoManager* undoManagerToUse) - { - if (auto* array = newValue.getArray()) - targetTree.setProperty (targetProperty, varArrayToDelimitedString (*array), undoManagerToUse); - else - targetTree.setProperty (targetProperty, newValue, undoManagerToUse); - } - - //============================================================================== - /** Makes the ValueWithDefault refer to the specified property inside the given ValueTree. */ - void referTo (ValueTree& tree, const Identifier& property, UndoManager* um) - { - referToWithDefault (tree, property, um, var(), {}); - } - - /** Makes the ValueWithDefault refer to the specified property inside the given ValueTree, - and specifies a default value to use. - */ - void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, const var& defaultVal) - { - referToWithDefault (tree, property, um, defaultVal, {}); - } - - void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, - const var& defaultVal, StringRef arrayDelimiter) - { - referToWithDefault (tree, property, um, defaultVal, arrayDelimiter); - } - - //============================================================================== - /** Returns a reference to the ValueTree containing the referenced property. */ - ValueTree& getValueTree() noexcept { return targetTree; } - - /** Returns the property ID of the referenced property. */ - Identifier& getPropertyID() noexcept { return targetProperty; } - - /** Returns the UndoManager that is being used. */ - UndoManager* getUndoManager() noexcept { return undoManager; } - - //============================================================================== - ValueWithDefault& operator= (const ValueWithDefault& other) - { - referToWithDefault (other.targetTree, other.targetProperty, other.undoManager, - other.defaultValue, other.delimiter); - - return *this; - } - -private: - //============================================================================== - ValueTree targetTree; - Identifier targetProperty; - UndoManager* undoManager = nullptr; - var defaultValue; - - String delimiter; - - //============================================================================== - void referToWithDefault (const ValueTree& v, const Identifier& i, UndoManager* um, - const var& defaultVal, StringRef del) - { - targetTree = v; - targetProperty = i; - undoManager = um; - defaultValue = defaultVal; - delimiter = del; - } - - //============================================================================== - String varArrayToDelimitedString (const Array& input) const noexcept - { - // if you are trying to control a var that is an array then you need to - // set a delimiter string that will be used when writing to XML! - jassert (delimiter.isNotEmpty()); - - StringArray elements; - - for (auto& v : input) - elements.add (v.toString()); - - return elements.joinIntoString (delimiter); - } - - Array delimitedStringToVarArray (StringRef input) const noexcept - { - Array arr; - - for (auto t : StringArray::fromTokens (input, delimiter, {})) - arr.add (t); - - return arr; - } - - //============================================================================== - JUCE_DECLARE_WEAK_REFERENCEABLE (ValueWithDefault) -}; - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock.h b/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock.h index 73bc90fe..a88ae39e 100644 --- a/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock.h +++ b/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,20 +23,24 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { #ifndef DOXYGEN namespace SampleTypeHelpers // Internal classes needed for handling sample type classes { - template ::value> + template > struct ElementType { using Type = T; }; + template + struct ElementType + { + using Type = const typename T::value_type; + }; + template struct ElementType { @@ -66,10 +69,10 @@ class AudioBlock private: template using MayUseConvertingConstructor = - std::enable_if_t, - std::remove_const_t>::value - && std::is_const::value - && ! std::is_const::value, + std::enable_if_t, + std::remove_const_t> + && std::is_const_v + && ! std::is_const_v, int>; public: @@ -125,10 +128,10 @@ class AudioBlock heapBlockToUseForAllocation.malloc (channelListBytes + extraBytes + channelSize * numberOfChannels); - auto* chanArray = reinterpret_cast (heapBlockToUseForAllocation.getData()); + auto* chanArray = unalignedPointerCast (heapBlockToUseForAllocation.getData()); channels = chanArray; - auto* data = reinterpret_cast (addBytesToPointer (chanArray, channelListBytes)); + auto* data = unalignedPointerCast (addBytesToPointer (chanArray, channelListBytes)); data = snapPointerToAlignment (data, alignmentInBytes); for (ChannelCountType i = 0; i < numChannels; ++i) @@ -151,6 +154,19 @@ class AudioBlock { } + /** Creates an AudioBlock that points to the data in an AudioBuffer. + AudioBlock does not copy nor own the memory pointed to by dataToUse. + Therefore it is the user's responsibility to ensure that the buffer is retained + throughout the life-time of the AudioBlock without being modified. + */ + template + constexpr AudioBlock (const AudioBuffer& buffer) noexcept + : channels (buffer.getArrayOfReadPointers()), + numChannels (static_cast (buffer.getNumChannels())), + numSamples (static_cast (buffer.getNumSamples())) + { + } + /** Creates an AudioBlock that points to the data in an AudioBuffer. AudioBlock does not copy nor own the memory pointed to by dataToUse. Therefore it is the user's responsibility to ensure that the buffer is retained @@ -319,16 +335,15 @@ class AudioBlock SIMDRegister then incrementing dstPos by one will increase the sample position in the AudioBuffer's units by a factor of SIMDRegister::SIMDNumElements. */ - void copyTo (AudioBuffer::type>& dst, size_t srcPos = 0, size_t dstPos = 0, + void copyTo (AudioBuffer>& dst, size_t srcPos = 0, size_t dstPos = 0, size_t numElements = std::numeric_limits::max()) const { auto dstlen = static_cast (dst.getNumSamples()) / sizeFactor; - auto n = static_cast (jmin (numSamples - srcPos, dstlen - dstPos, numElements) * sizeFactor); + auto n = jmin (numSamples - srcPos, dstlen - dstPos, numElements) * sizeFactor; auto maxChannels = jmin (static_cast (dst.getNumChannels()), static_cast (numChannels)); for (size_t ch = 0; ch < maxChannels; ++ch) - FloatVectorOperations::copy (dst.getWritePointer (static_cast (ch), - static_cast (dstPos * sizeFactor)), + FloatVectorOperations::copy (dst.getWritePointer ((int) ch, (int) (dstPos * sizeFactor)), getDataPointer (ch) + (srcPos * sizeFactor), n); } @@ -445,16 +460,16 @@ class AudioBlock //============================================================================== /** Multiplies each channels of this block by a smoothly changing value. */ - template - AudioBlock& multiplyBy (SmoothedValue& value) noexcept { multiplyByInternal (value); return *this; } - template - const AudioBlock& multiplyBy (SmoothedValue& value) const noexcept { multiplyByInternal (value); return *this; } - - /** Replaces each channel of this block with the product of the src block and a smoothed value. */ template - AudioBlock& replaceWithProductOf (AudioBlock src, SmoothedValue& value) noexcept { replaceWithProductOfInternal (src, value); return *this; } + AudioBlock& multiplyBy (SmoothedValue& value) noexcept { multiplyByInternal (value); return *this; } template - const AudioBlock& replaceWithProductOf (AudioBlock src, SmoothedValue& value) const noexcept { replaceWithProductOfInternal (src, value); return *this; } + const AudioBlock& multiplyBy (SmoothedValue& value) const noexcept { multiplyByInternal (value); return *this; } + + /** Replaces each channel of this block with the product of the src block and a smoothed value. */ + template + AudioBlock& replaceWithProductOf (AudioBlock src, SmoothedValue& value) noexcept { replaceWithProductOfInternal (src, value); return *this; } + template + const AudioBlock& replaceWithProductOf (AudioBlock src, SmoothedValue& value) const noexcept { replaceWithProductOfInternal (src, value); return *this; } //============================================================================== /** Multiplies each value in src by a fixed value and adds the result to this block. */ @@ -501,12 +516,12 @@ class AudioBlock //============================================================================== /** Finds the minimum and maximum value of the buffer. */ - Range::type> findMinAndMax() const noexcept + Range> findMinAndMax() const noexcept { if (numChannels == 0) return {}; - auto n = static_cast (numSamples * sizeFactor); + auto n = numSamples * sizeFactor; auto minmax = FloatVectorOperations::findMinAndMax (getDataPointer (0), n); for (size_t ch = 1; ch < numChannels; ++ch) @@ -535,18 +550,18 @@ class AudioBlock AudioBlock& operator*= (AudioBlock src) noexcept { return multiplyBy (src); } const AudioBlock& operator*= (AudioBlock src) const noexcept { return multiplyBy (src); } - template - AudioBlock& operator*= (SmoothedValue& value) noexcept { return multiplyBy (value); } - template - const AudioBlock& operator*= (SmoothedValue& value) const noexcept { return multiplyBy (value); } + template + AudioBlock& operator*= (SmoothedValue& value) noexcept { return multiplyBy (value); } + template + const AudioBlock& operator*= (SmoothedValue& value) const noexcept { return multiplyBy (value); } //============================================================================== // This class can only be used with floating point types - static_assert (std::is_same, float>::value - || std::is_same, double>::value + static_assert (std::is_same_v, float> + || std::is_same_v, double> #if JUCE_USE_SIMD - || std::is_same, SIMDRegister>::value - || std::is_same, SIMDRegister>::value + || std::is_same_v, SIMDRegister> + || std::is_same_v, SIMDRegister> #endif , "AudioBlock only supports single or double precision floating point types"); @@ -583,7 +598,7 @@ class AudioBlock //============================================================================== void JUCE_VECTOR_CALLTYPE clearInternal() const noexcept { - auto n = static_cast (numSamples * sizeFactor); + auto n = numSamples * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::clear (getDataPointer (ch), n); @@ -591,7 +606,7 @@ class AudioBlock void JUCE_VECTOR_CALLTYPE fillInternal (NumericType value) const noexcept { - auto n = static_cast (numSamples * sizeFactor); + auto n = numSamples * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::fill (getDataPointer (ch), value, n); @@ -601,8 +616,7 @@ class AudioBlock void copyFromInternal (const AudioBlock& src) const noexcept { auto maxChannels = jmin (src.numChannels, numChannels); - auto n = static_cast (jmin (src.numSamples * src.sizeFactor, - numSamples * sizeFactor)); + auto n = jmin (src.numSamples * src.sizeFactor, numSamples * sizeFactor); for (size_t ch = 0; ch < maxChannels; ++ch) FloatVectorOperations::copy (getDataPointer (ch), src.getDataPointer (ch), n); @@ -612,13 +626,12 @@ class AudioBlock void copyFromInternal (const AudioBuffer& src, size_t srcPos, size_t dstPos, size_t numElements) const { auto srclen = static_cast (src.getNumSamples()) / sizeFactor; - auto n = static_cast (jmin (srclen - srcPos, numSamples - dstPos, numElements) * sizeFactor); + auto n = jmin (srclen - srcPos, numSamples - dstPos, numElements) * sizeFactor; auto maxChannels = jmin (static_cast (src.getNumChannels()), static_cast (numChannels)); for (size_t ch = 0; ch < maxChannels; ++ch) FloatVectorOperations::copy (getDataPointer (ch) + (dstPos * sizeFactor), - src.getReadPointer (static_cast (ch), - static_cast (srcPos * sizeFactor)), + src.getReadPointer ((int) ch, (int) (srcPos * sizeFactor)), n); } @@ -637,7 +650,7 @@ class AudioBlock //============================================================================== void JUCE_VECTOR_CALLTYPE addInternal (NumericType value) const noexcept { - auto n = static_cast (numSamples * sizeFactor); + auto n = numSamples * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::add (getDataPointer (ch), value, n); @@ -647,7 +660,7 @@ class AudioBlock void addInternal (AudioBlock src) const noexcept { jassert (numChannels == src.numChannels); - auto n = static_cast (jmin (numSamples, src.numSamples) * sizeFactor); + auto n = jmin (numSamples, src.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::add (getDataPointer (ch), src.getDataPointer (ch), n); @@ -657,7 +670,7 @@ class AudioBlock void JUCE_VECTOR_CALLTYPE replaceWithSumOfInternal (AudioBlock src, NumericType value) const noexcept { jassert (numChannels == src.numChannels); - auto n = static_cast (jmin (numSamples, src.numSamples) * sizeFactor); + auto n = jmin (numSamples, src.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::add (getDataPointer (ch), src.getDataPointer (ch), value, n); @@ -667,7 +680,7 @@ class AudioBlock void replaceWithSumOfInternal (AudioBlock src1, AudioBlock src2) const noexcept { jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels); - auto n = static_cast (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor); + auto n = jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::add (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n); @@ -683,7 +696,7 @@ class AudioBlock void subtractInternal (AudioBlock src) const noexcept { jassert (numChannels == src.numChannels); - auto n = static_cast (jmin (numSamples, src.numSamples) * sizeFactor); + auto n = jmin (numSamples, src.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::subtract (getDataPointer (ch), src.getDataPointer (ch), n); @@ -699,7 +712,7 @@ class AudioBlock void replaceWithDifferenceOfInternal (AudioBlock src1, AudioBlock src2) const noexcept { jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels); - auto n = static_cast (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor); + auto n = jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::subtract (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n); @@ -708,7 +721,7 @@ class AudioBlock //============================================================================== void JUCE_VECTOR_CALLTYPE multiplyByInternal (NumericType value) const noexcept { - auto n = static_cast (numSamples * sizeFactor); + auto n = numSamples * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::multiply (getDataPointer (ch), value, n); @@ -718,7 +731,7 @@ class AudioBlock void multiplyByInternal (AudioBlock src) const noexcept { jassert (numChannels == src.numChannels); - auto n = static_cast (jmin (numSamples, src.numSamples) * sizeFactor); + auto n = jmin (numSamples, src.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::multiply (getDataPointer (ch), src.getDataPointer (ch), n); @@ -728,7 +741,7 @@ class AudioBlock void JUCE_VECTOR_CALLTYPE replaceWithProductOfInternal (AudioBlock src, NumericType value) const noexcept { jassert (numChannels == src.numChannels); - auto n = static_cast (jmin (numSamples, src.numSamples) * sizeFactor); + auto n = jmin (numSamples, src.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::multiply (getDataPointer (ch), src.getDataPointer (ch), value, n); @@ -738,24 +751,24 @@ class AudioBlock void replaceWithProductOfInternal (AudioBlock src1, AudioBlock src2) const noexcept { jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels); - auto n = static_cast (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor); + auto n = jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::multiply (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n); } - template - void multiplyByInternal (SmoothedValue& value) const noexcept + template + void multiplyByInternal (SmoothedValue& value) const noexcept { if (! value.isSmoothing()) { - multiplyByInternal (value.getTargetValue()); + multiplyByInternal ((NumericType) value.getTargetValue()); } else { for (size_t i = 0; i < numSamples; ++i) { - const auto scaler = value.getNextValue(); + const auto scaler = (NumericType) value.getNextValue(); for (size_t ch = 0; ch < numChannels; ++ch) getDataPointer (ch)[i] *= scaler; @@ -763,14 +776,14 @@ class AudioBlock } } - template - void replaceWithProductOfInternal (AudioBlock src, SmoothedValue& value) const noexcept + template + void replaceWithProductOfInternal (AudioBlock src, SmoothedValue& value) const noexcept { jassert (numChannels == src.numChannels); if (! value.isSmoothing()) { - replaceWithProductOfInternal (src, value.getTargetValue()); + replaceWithProductOfInternal (src, (NumericType) value.getTargetValue()); } else { @@ -778,7 +791,7 @@ class AudioBlock for (size_t i = 0; i < n; ++i) { - const auto scaler = value.getNextValue(); + const auto scaler = (NumericType) value.getNextValue(); for (size_t ch = 0; ch < numChannels; ++ch) getDataPointer (ch)[i] = scaler * src.getChannelPointer (ch)[i]; @@ -791,7 +804,7 @@ class AudioBlock void JUCE_VECTOR_CALLTYPE addProductOfInternal (AudioBlock src, NumericType factor) const noexcept { jassert (numChannels == src.numChannels); - auto n = static_cast (jmin (numSamples, src.numSamples) * sizeFactor); + auto n = jmin (numSamples, src.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::addWithMultiply (getDataPointer (ch), src.getDataPointer (ch), factor, n); @@ -801,7 +814,7 @@ class AudioBlock void addProductOfInternal (AudioBlock src1, AudioBlock src2) const noexcept { jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels); - auto n = static_cast (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor); + auto n = jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::addWithMultiply (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n); @@ -817,7 +830,7 @@ class AudioBlock void replaceWithNegativeOfInternal (AudioBlock src) const noexcept { jassert (numChannels == src.numChannels); - auto n = static_cast (jmin (numSamples, src.numSamples) * sizeFactor); + auto n = jmin (numSamples, src.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::negate (getDataPointer (ch), src.getDataPointer (ch), n); @@ -827,7 +840,7 @@ class AudioBlock void replaceWithAbsoluteValueOfInternal (AudioBlock src) const noexcept { jassert (numChannels == src.numChannels); - auto n = static_cast (jmin (numSamples, src.numSamples) * sizeFactor); + auto n = jmin (numSamples, src.numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::abs (getDataPointer (ch), src.getDataPointer (ch), n); @@ -838,7 +851,7 @@ class AudioBlock void replaceWithMinOfInternal (AudioBlock src1, AudioBlock src2) const noexcept { jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels); - auto n = static_cast (jmin (src1.numSamples, src2.numSamples, numSamples) * sizeFactor); + auto n = jmin (src1.numSamples, src2.numSamples, numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::min (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n); @@ -848,7 +861,7 @@ class AudioBlock void replaceWithMaxOfInternal (AudioBlock src1, AudioBlock src2) const noexcept { jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels); - auto n = static_cast (jmin (src1.numSamples, src2.numSamples, numSamples) * sizeFactor); + auto n = jmin (src1.numSamples, src2.numSamples, numSamples) * sizeFactor; for (size_t ch = 0; ch < numChannels; ++ch) FloatVectorOperations::max (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n); @@ -876,5 +889,4 @@ class AudioBlock friend class AudioBlock; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock_test.cpp b/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock_test.cpp index acbb731b..a8b7e091 100644 --- a/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock_test.cpp +++ b/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock_test.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,13 +23,16 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { +#if JUCE_USE_SIMD +template +String& operator<< (String& str, SIMDRegister) { return str; } +#endif + template -class AudioBlockUnitTests : public UnitTest +class AudioBlockUnitTests final : public UnitTest { public: //============================================================================== @@ -80,25 +82,25 @@ class AudioBlockUnitTests : public UnitTest resetBlocks(); expect (block != otherBlock); - expect (block.getSample (0, 0) == SampleType (1.0)); - expect (block.getSample (0, 4) == SampleType (5.0)); - expect (otherBlock.getSample (0, 0) == SampleType (-1.0)); - expect (otherBlock.getSample (0, 3) == SampleType (-4.0)); + expectEquals (block.getSample (0, 0), SampleType (1.0)); + expectEquals (block.getSample (0, 4), SampleType (5.0)); + expectEquals (otherBlock.getSample (0, 0), SampleType (-1.0)); + expectEquals (otherBlock.getSample (0, 3), SampleType (-4.0)); block.swap (otherBlock); expect (block != otherBlock); - expect (otherBlock.getSample (0, 0) == SampleType (1.0)); - expect (otherBlock.getSample (0, 4) == SampleType (5.0)); - expect (block.getSample (0, 0) == SampleType (-1.0)); - expect (block.getSample (0, 3) == SampleType (-4.0)); + expectEquals (otherBlock.getSample (0, 0), SampleType (1.0)); + expectEquals (otherBlock.getSample (0, 4), SampleType (5.0)); + expectEquals (block.getSample (0, 0), SampleType (-1.0)); + expectEquals (block.getSample (0, 3), SampleType (-4.0)); block.swap (otherBlock); - expect (block.getSample (0, 0) == SampleType (1.0)); - expect (block.getSample (0, 4) == SampleType (5.0)); - expect (otherBlock.getSample (0, 0) == SampleType (-1.0)); - expect (otherBlock.getSample (0, 3) == SampleType (-4.0)); + expectEquals (block.getSample (0, 0), SampleType (1.0)); + expectEquals (block.getSample (0, 4), SampleType (5.0)); + expectEquals (otherBlock.getSample (0, 0), SampleType (-1.0)); + expectEquals (otherBlock.getSample (0, 3), SampleType (-4.0)); } beginTest ("Getters and setters"); @@ -108,49 +110,49 @@ class AudioBlockUnitTests : public UnitTest expectEquals ((int) block.getNumChannels(), (int) data.size()); expectEquals ((int) block.getNumSamples(), numSamples); - expect (block.getChannelPointer (0)[2] == SampleType (3.0)); + expectEquals (block.getChannelPointer (0)[2], SampleType (3.0)); block.getChannelPointer (0)[2] = SampleType (999.0); - expect (block.getChannelPointer (0)[2] == SampleType (999.0)); + expectEquals (block.getChannelPointer (0)[2], SampleType (999.0)); - expect (block.getSample (0, 4) == SampleType (5.0)); - expect (block.getSample (1, 4) == SampleType (11.0)); + expectEquals (block.getSample (0, 4), SampleType (5.0)); + expectEquals (block.getSample (1, 4), SampleType (11.0)); - expect (block.getSingleChannelBlock (1).getSample (0, 3) == block.getSample (1, 3)); + expectEquals (block.getSingleChannelBlock (1).getSample (0, 3), block.getSample (1, 3)); - expect (block.getSubsetChannelBlock (0, 2).getSample (1, 3) == block.getSample (1, 3)); - expect (block.getSubsetChannelBlock (1, 1).getSample (0, 3) == block.getSample (1, 3)); + expectEquals (block.getSubsetChannelBlock (0, 2).getSample (1, 3), block.getSample (1, 3)); + expectEquals (block.getSubsetChannelBlock (1, 1).getSample (0, 3), block.getSample (1, 3)); block.setSample (1, 1, SampleType (777.0)); - expect (block.getSample (1, 1) == SampleType (777.0)); + expectEquals (block.getSample (1, 1), SampleType (777.0)); block.addSample (1, 1, SampleType (1.0)); - expect (block.getSample (1, 1) == SampleType (778.0)); + expectEquals (block.getSample (1, 1), SampleType (778.0)); } beginTest ("Basic copying"); { block.clear(); - expect (block.getSample (0, 2) == SampleType (0.0)); - expect (block.getSample (1, 4) == SampleType (0.0)); + expectEquals (block.getSample (0, 2), SampleType (0.0)); + expectEquals (block.getSample (1, 4), SampleType (0.0)); block.fill ((NumericType) 456.0); - expect (block.getSample (0, 2) == SampleType (456.0)); - expect (block.getSample (1, 4) == SampleType (456.0)); + expectEquals (block.getSample (0, 2), SampleType (456.0)); + expectEquals (block.getSample (1, 4), SampleType (456.0)); block.copyFrom (otherBlock); expect (block != otherBlock); - expect (block.getSample (0, 2) == otherBlock.getSample (0, 2)); - expect (block.getSample (1, 4) == otherBlock.getSample (1, 4)); + expectEquals (block.getSample (0, 2), otherBlock.getSample (0, 2)); + expectEquals (block.getSample (1, 4), otherBlock.getSample (1, 4)); resetBlocks(); SampleType testSample1 = block.getSample (0, 2); SampleType testSample2 = block.getSample (1, 3); - expect (testSample1 != block.getSample (0, 4)); - expect (testSample2 != block.getSample (1, 5)); + expectNotEquals (testSample1, block.getSample (0, 4)); + expectNotEquals (testSample2, block.getSample (1, 5)); block.move (0, 2); - expect (block.getSample (0, 4) == testSample1); - expect (block.getSample (1, 5) == testSample2); + expectEquals (block.getSample (0, 4), testSample1); + expectEquals (block.getSample (1, 5), testSample2); } beginTest ("Addition"); @@ -158,22 +160,22 @@ class AudioBlockUnitTests : public UnitTest resetBlocks(); block.add ((NumericType) 15.0); - expect (block.getSample (0, 4) == SampleType (20.0)); - expect (block.getSample (1, 4) == SampleType (26.0)); + expectEquals (block.getSample (0, 4), SampleType (20.0)); + expectEquals (block.getSample (1, 4), SampleType (26.0)); block.add (otherBlock); - expect (block.getSample (0, 4) == SampleType (15.0)); - expect (block.getSample (1, 4) == SampleType (15.0)); + expectEquals (block.getSample (0, 4), SampleType (15.0)); + expectEquals (block.getSample (1, 4), SampleType (15.0)); block.replaceWithSumOf (otherBlock, (NumericType) 9.0); - expect (block.getSample (0, 4) == SampleType (4.0)); - expect (block.getSample (1, 4) == SampleType (-2.0)); + expectEquals (block.getSample (0, 4), SampleType (4.0)); + expectEquals (block.getSample (1, 4), SampleType (-2.0)); resetBlocks(); block.replaceWithSumOf (block, otherBlock); - expect (block.getSample (0, 4) == SampleType (0.0)); - expect (block.getSample (1, 4) == SampleType (0.0)); + expectEquals (block.getSample (0, 4), SampleType (0.0)); + expectEquals (block.getSample (1, 4), SampleType (0.0)); } beginTest ("Subtraction"); @@ -181,22 +183,22 @@ class AudioBlockUnitTests : public UnitTest resetBlocks(); block.subtract ((NumericType) 15.0); - expect (block.getSample (0, 4) == SampleType (-10.0)); - expect (block.getSample (1, 4) == SampleType (-4.0)); + expectEquals (block.getSample (0, 4), SampleType (-10.0)); + expectEquals (block.getSample (1, 4), SampleType (-4.0)); block.subtract (otherBlock); - expect (block.getSample (0, 4) == SampleType (-5.0)); - expect (block.getSample (1, 4) == SampleType (7.0)); + expectEquals (block.getSample (0, 4), SampleType (-5.0)); + expectEquals (block.getSample (1, 4), SampleType (7.0)); block.replaceWithDifferenceOf (otherBlock, (NumericType) 9.0); - expect (block.getSample (0, 4) == SampleType (-14.0)); - expect (block.getSample (1, 4) == SampleType (-20.0)); + expectEquals (block.getSample (0, 4), SampleType (-14.0)); + expectEquals (block.getSample (1, 4), SampleType (-20.0)); resetBlocks(); block.replaceWithDifferenceOf (block, otherBlock); - expect (block.getSample (0, 4) == SampleType (10.0)); - expect (block.getSample (1, 4) == SampleType (22.0)); + expectEquals (block.getSample (0, 4), SampleType (10.0)); + expectEquals (block.getSample (1, 4), SampleType (22.0)); } beginTest ("Multiplication"); @@ -204,22 +206,22 @@ class AudioBlockUnitTests : public UnitTest resetBlocks(); block.multiplyBy ((NumericType) 10.0); - expect (block.getSample (0, 4) == SampleType (50.0)); - expect (block.getSample (1, 4) == SampleType (110.0)); + expectEquals (block.getSample (0, 4), SampleType (50.0)); + expectEquals (block.getSample (1, 4), SampleType (110.0)); block.multiplyBy (otherBlock); - expect (block.getSample (0, 4) == SampleType (-250.0)); - expect (block.getSample (1, 4) == SampleType (-1210.0)); + expectEquals (block.getSample (0, 4), SampleType (-250.0)); + expectEquals (block.getSample (1, 4), SampleType (-1210.0)); block.replaceWithProductOf (otherBlock, (NumericType) 3.0); - expect (block.getSample (0, 4) == SampleType (-15.0)); - expect (block.getSample (1, 4) == SampleType (-33.0)); + expectEquals (block.getSample (0, 4), SampleType (-15.0)); + expectEquals (block.getSample (1, 4), SampleType (-33.0)); resetBlocks(); block.replaceWithProductOf (block, otherBlock); - expect (block.getSample (0, 4) == SampleType (-25.0)); - expect (block.getSample (1, 4) == SampleType (-121.0)); + expectEquals (block.getSample (0, 4), SampleType (-25.0)); + expectEquals (block.getSample (1, 4), SampleType (-121.0)); } beginTest ("Multiply add"); @@ -227,12 +229,12 @@ class AudioBlockUnitTests : public UnitTest resetBlocks(); block.addProductOf (otherBlock, (NumericType) -1.0); - expect (block.getSample (0, 4) == SampleType (10.0)); - expect (block.getSample (1, 4) == SampleType (22.0)); + expectEquals (block.getSample (0, 4), SampleType (10.0)); + expectEquals (block.getSample (1, 4), SampleType (22.0)); block.addProductOf (otherBlock, otherBlock); - expect (block.getSample (0, 4) == SampleType (35.0)); - expect (block.getSample (1, 4) == SampleType (143.0)); + expectEquals (block.getSample (0, 4), SampleType (35.0)); + expectEquals (block.getSample (1, 4), SampleType (143.0)); } beginTest ("Negative abs min max"); @@ -241,68 +243,68 @@ class AudioBlockUnitTests : public UnitTest otherBlock.negate(); block.add (otherBlock); - expect (block.getSample (0, 4) == SampleType (10.0)); - expect (block.getSample (1, 4) == SampleType (22.0)); + expectEquals (block.getSample (0, 4), SampleType (10.0)); + expectEquals (block.getSample (1, 4), SampleType (22.0)); block.replaceWithNegativeOf (otherBlock); - expect (block.getSample (0, 4) == SampleType (-5.0)); - expect (block.getSample (1, 4) == SampleType (-11.0)); + expectEquals (block.getSample (0, 4), SampleType (-5.0)); + expectEquals (block.getSample (1, 4), SampleType (-11.0)); block.clear(); otherBlock.negate(); block.replaceWithAbsoluteValueOf (otherBlock); - expect (block.getSample (0, 4) == SampleType (5.0)); - expect (block.getSample (1, 4) == SampleType (11.0)); + expectEquals (block.getSample (0, 4), SampleType (5.0)); + expectEquals (block.getSample (1, 4), SampleType (11.0)); resetBlocks(); block.replaceWithMinOf (block, otherBlock); - expect (block.getSample (0, 4) == SampleType (-5.0)); - expect (block.getSample (1, 4) == SampleType (-11.0)); + expectEquals (block.getSample (0, 4), SampleType (-5.0)); + expectEquals (block.getSample (1, 4), SampleType (-11.0)); resetBlocks(); block.replaceWithMaxOf (block, otherBlock); - expect (block.getSample (0, 4) == SampleType (5.0)); - expect (block.getSample (1, 4) == SampleType (11.0)); + expectEquals (block.getSample (0, 4), SampleType (5.0)); + expectEquals (block.getSample (1, 4), SampleType (11.0)); resetBlocks(); auto range = block.findMinAndMax(); - expect (SampleType (range.getStart()) == SampleType (1.0)); - expect (SampleType (range.getEnd()) == SampleType (12.0)); + expectEquals (SampleType (range.getStart()), SampleType (1.0)); + expectEquals (SampleType (range.getEnd()), SampleType (12.0)); } beginTest ("Operators"); { resetBlocks(); block += (NumericType) 10.0; - expect (block.getSample (0, 4) == SampleType (15.0)); - expect (block.getSample (1, 4) == SampleType (21.0)); + expectEquals (block.getSample (0, 4), SampleType (15.0)); + expectEquals (block.getSample (1, 4), SampleType (21.0)); block += otherBlock; - expect (block.getSample (0, 4) == SampleType (10.0)); - expect (block.getSample (1, 4) == SampleType (10.0)); + expectEquals (block.getSample (0, 4), SampleType (10.0)); + expectEquals (block.getSample (1, 4), SampleType (10.0)); resetBlocks(); block -= (NumericType) 10.0; - expect (block.getSample (0, 4) == SampleType (-5.0)); - expect (block.getSample (1, 4) == SampleType (1.0)); + expectEquals (block.getSample (0, 4), SampleType (-5.0)); + expectEquals (block.getSample (1, 4), SampleType (1.0)); block -= otherBlock; - expect (block.getSample (0, 4) == SampleType (0.0)); - expect (block.getSample (1, 4) == SampleType (12.0)); + expectEquals (block.getSample (0, 4), SampleType (0.0)); + expectEquals (block.getSample (1, 4), SampleType (12.0)); resetBlocks(); block *= (NumericType) 10.0; - expect (block.getSample (0, 4) == SampleType (50.0)); - expect (block.getSample (1, 4) == SampleType (110.0)); + expectEquals (block.getSample (0, 4), SampleType (50.0)); + expectEquals (block.getSample (1, 4), SampleType (110.0)); block *= otherBlock; - expect (block.getSample (0, 4) == SampleType (-250.0)); - expect (block.getSample (1, 4) == SampleType (-1210.0)); + expectEquals (block.getSample (0, 4), SampleType (-250.0)); + expectEquals (block.getSample (1, 4), SampleType (-1210.0)); } beginTest ("Process"); { resetBlocks(); - AudioBlock::process (block, otherBlock, [](SampleType x) { return x + (NumericType) 1.0; }); - expect (otherBlock.getSample (0, 4) == SampleType (6.0)); - expect (otherBlock.getSample (1, 4) == SampleType (12.0)); + AudioBlock::process (block, otherBlock, [] (SampleType x) { return x + (NumericType) 1.0; }); + expectEquals (otherBlock.getSample (0, 4), SampleType (6.0)); + expectEquals (otherBlock.getSample (1, 4), SampleType (12.0)); } beginTest ("Copying"); @@ -320,116 +322,110 @@ class AudioBlockUnitTests : public UnitTest private: //============================================================================== - template - using ScalarVoid = typename std::enable_if_t < std::is_scalar ::value, void>; - - template - using SIMDVoid = typename std::enable_if_t ::value, void>; - - //============================================================================== - template - ScalarVoid copyingTests() + void copyingTests() { - auto unchangedElement1 = block.getSample (0, 4); - auto unchangedElement2 = block.getSample (1, 1); - - AudioBuffer otherBuffer (otherData.data(), (int) otherData.size(), numSamples); + if constexpr (std::is_scalar_v) + { + auto unchangedElement1 = block.getSample (0, 4); + auto unchangedElement2 = block.getSample (1, 1); - block.copyFrom (otherBuffer, 1, 2, 2); + AudioBuffer otherBuffer (otherData.data(), (int) otherData.size(), numSamples); - expectEquals (block.getSample (0, 4), unchangedElement1); - expectEquals (block.getSample (1, 1), unchangedElement2); - expectEquals (block.getSample (0, 2), otherBuffer.getSample (0, 1)); - expectEquals (block.getSample (1, 3), otherBuffer.getSample (1, 2)); + block.copyFrom (otherBuffer, 1, 2, 2); - resetBlocks(); + expectEquals (block.getSample (0, 4), unchangedElement1); + expectEquals (block.getSample (1, 1), unchangedElement2); + expectEquals (block.getSample (0, 2), otherBuffer.getSample (0, 1)); + expectEquals (block.getSample (1, 3), otherBuffer.getSample (1, 2)); - unchangedElement1 = otherBuffer.getSample (0, 4); - unchangedElement2 = otherBuffer.getSample (1, 3); + resetBlocks(); - block.copyTo (otherBuffer, 2, 1, 2); + unchangedElement1 = otherBuffer.getSample (0, 4); + unchangedElement2 = otherBuffer.getSample (1, 3); - expectEquals (otherBuffer.getSample (0, 4), unchangedElement1); - expectEquals (otherBuffer.getSample (1, 3), unchangedElement2); - expectEquals (otherBuffer.getSample (0, 1), block.getSample (0, 2)); - expectEquals (otherBuffer.getSample (1, 2), block.getSample (1, 3)); - } + block.copyTo (otherBuffer, 2, 1, 2); - template - SIMDVoid copyingTests() - { - auto numSIMDElements = SIMDRegister::SIMDNumElements; - AudioBuffer numericData ((int) block.getNumChannels(), - (int) (block.getNumSamples() * numSIMDElements)); + expectEquals (otherBuffer.getSample (0, 4), unchangedElement1); + expectEquals (otherBuffer.getSample (1, 3), unchangedElement2); + expectEquals (otherBuffer.getSample (0, 1), block.getSample (0, 2)); + expectEquals (otherBuffer.getSample (1, 2), block.getSample (1, 3)); + } + #if JUCE_USE_SIMD + else + { + auto numSIMDElements = SIMDRegister::SIMDNumElements; + AudioBuffer numericData ((int) block.getNumChannels(), + (int) (block.getNumSamples() * numSIMDElements)); - for (int c = 0; c < numericData.getNumChannels(); ++c) - std::fill_n (numericData.getWritePointer (c), numericData.getNumSamples(), (NumericType) 1.0); + for (int c = 0; c < numericData.getNumChannels(); ++c) + std::fill_n (numericData.getWritePointer (c), numericData.getNumSamples(), (NumericType) 1.0); - numericData.applyGainRamp (0, numericData.getNumSamples(), (NumericType) 0.127, (NumericType) 17.3); + numericData.applyGainRamp (0, numericData.getNumSamples(), (NumericType) 0.127, (NumericType) 17.3); - auto lastUnchangedIndexBeforeCopiedRange = (int) ((numSIMDElements * 2) - 1); - auto firstUnchangedIndexAfterCopiedRange = (int) ((numSIMDElements * 4) + 1); - auto unchangedElement1 = numericData.getSample (0, lastUnchangedIndexBeforeCopiedRange); - auto unchangedElement2 = numericData.getSample (1, firstUnchangedIndexAfterCopiedRange); + auto lastUnchangedIndexBeforeCopiedRange = (int) ((numSIMDElements * 2) - 1); + auto firstUnchangedIndexAfterCopiedRange = (int) ((numSIMDElements * 4) + 1); + auto unchangedElement1 = numericData.getSample (0, lastUnchangedIndexBeforeCopiedRange); + auto unchangedElement2 = numericData.getSample (1, firstUnchangedIndexAfterCopiedRange); - block.copyTo (numericData, 1, 2, 2); + block.copyTo (numericData, 1, 2, 2); - expectEquals (numericData.getSample (0, lastUnchangedIndexBeforeCopiedRange), unchangedElement1); - expectEquals (numericData.getSample (1, firstUnchangedIndexAfterCopiedRange), unchangedElement2); - expect (SampleType (numericData.getSample (0, 2 * (int) numSIMDElements)) == block.getSample (0, 1)); - expect (SampleType (numericData.getSample (1, 3 * (int) numSIMDElements)) == block.getSample (1, 2)); + expectEquals (numericData.getSample (0, lastUnchangedIndexBeforeCopiedRange), unchangedElement1); + expectEquals (numericData.getSample (1, firstUnchangedIndexAfterCopiedRange), unchangedElement2); + expect (SampleType (numericData.getSample (0, 2 * (int) numSIMDElements)) == block.getSample (0, 1)); + expect (SampleType (numericData.getSample (1, 3 * (int) numSIMDElements)) == block.getSample (1, 2)); - numericData.applyGainRamp (0, numericData.getNumSamples(), (NumericType) 15.1, (NumericType) 0.7); + numericData.applyGainRamp (0, numericData.getNumSamples(), (NumericType) 15.1, (NumericType) 0.7); - auto unchangedSIMDElement1 = block.getSample (0, 1); - auto unchangedSIMDElement2 = block.getSample (1, 4); + auto unchangedSIMDElement1 = block.getSample (0, 1); + auto unchangedSIMDElement2 = block.getSample (1, 4); - block.copyFrom (numericData, 1, 2, 2); + block.copyFrom (numericData, 1, 2, 2); - expect (block.getSample (0, 1) == unchangedSIMDElement1); - expect (block.getSample (1, 4) == unchangedSIMDElement2); - expectEquals (block.getSample (0, 2).get (0), numericData.getSample (0, (int) numSIMDElements)); - expectEquals (block.getSample (1, 3).get (0), numericData.getSample (1, (int) (numSIMDElements * 2))); + expect (block.getSample (0, 1) == unchangedSIMDElement1); + expect (block.getSample (1, 4) == unchangedSIMDElement2); + expectEquals (block.getSample (0, 2).get (0), numericData.getSample (0, (int) numSIMDElements)); + expectEquals (block.getSample (1, 3).get (0), numericData.getSample (1, (int) (numSIMDElements * 2))); - if (numSIMDElements > 1) - { - expectEquals (block.getSample (0, 2).get (1), numericData.getSample (0, (int) (numSIMDElements + 1))); - expectEquals (block.getSample (1, 3).get (1), numericData.getSample (1, (int) ((numSIMDElements * 2) + 1))); + if (numSIMDElements > 1) + { + expectEquals (block.getSample (0, 2).get (1), numericData.getSample (0, (int) (numSIMDElements + 1))); + expectEquals (block.getSample (1, 3).get (1), numericData.getSample (1, (int) ((numSIMDElements * 2) + 1))); + } } + #endif } //============================================================================== - template - ScalarVoid smoothedValueTests() + void smoothedValueTests() { - block.fill ((SampleType) 1.0); - SmoothedValue sv { (SampleType) 1.0 }; - sv.reset (1, 4); - sv.setTargetValue ((SampleType) 0.0); - - block.multiplyBy (sv); - expect (block.getSample (0, 2) < (SampleType) 1.0); - expect (block.getSample (1, 2) < (SampleType) 1.0); - expect (block.getSample (0, 2) > (SampleType) 0.0); - expect (block.getSample (1, 2) > (SampleType) 0.0); - expectEquals (block.getSample (0, 5), (SampleType) 0.0); - expectEquals (block.getSample (1, 5), (SampleType) 0.0); - - sv.setCurrentAndTargetValue (-1.0f); - sv.setTargetValue (0.0f); - otherBlock.fill (-1.0f); - block.replaceWithProductOf (otherBlock, sv); - expect (block.getSample (0, 2) < (SampleType) 1.0); - expect (block.getSample (1, 2) < (SampleType) 1.0); - expect (block.getSample (0, 2) > (SampleType) 0.0); - expect (block.getSample (1, 2) > (SampleType) 0.0); - expectEquals (block.getSample (0, 5), (SampleType) 0.0); - expectEquals (block.getSample (1, 5), (SampleType) 0.0); + if constexpr (std::is_scalar_v) + { + block.fill ((SampleType) 1.0); + SmoothedValue sv { (SampleType) 1.0 }; + sv.reset (1, 4); + sv.setTargetValue ((SampleType) 0.0); + + block.multiplyBy (sv); + expect (block.getSample (0, 2) < (SampleType) 1.0); + expect (block.getSample (1, 2) < (SampleType) 1.0); + expect (block.getSample (0, 2) > (SampleType) 0.0); + expect (block.getSample (1, 2) > (SampleType) 0.0); + expectEquals (block.getSample (0, 5), (SampleType) 0.0); + expectEquals (block.getSample (1, 5), (SampleType) 0.0); + + sv.setCurrentAndTargetValue (-1.0f); + sv.setTargetValue (0.0f); + otherBlock.fill (-1.0f); + block.replaceWithProductOf (otherBlock, sv); + expect (block.getSample (0, 2) < (SampleType) 1.0); + expect (block.getSample (1, 2) < (SampleType) 1.0); + expect (block.getSample (0, 2) > (SampleType) 0.0); + expect (block.getSample (1, 2) > (SampleType) 0.0); + expectEquals (block.getSample (0, 5), (SampleType) 0.0); + expectEquals (block.getSample (1, 5), (SampleType) 0.0); + } } - template - SIMDVoid smoothedValueTests() {} - //============================================================================== void resetBlocks() { @@ -450,7 +446,7 @@ class AudioBlockUnitTests : public UnitTest //============================================================================== static SampleType* allocateAlignedMemory (int numSamplesToAllocate) { - auto alignmentLowerBound = std::alignment_of::value; + auto alignmentLowerBound = std::alignment_of_v; #if ! JUCE_WINDOWS alignmentLowerBound = jmax (sizeof (void*), alignmentLowerBound); #endif @@ -492,8 +488,10 @@ class AudioBlockUnitTests : public UnitTest static AudioBlockUnitTests audioBlockFloatUnitTests; static AudioBlockUnitTests audioBlockDoubleUnitTests; + +#if JUCE_USE_SIMD static AudioBlockUnitTests> audioBlockSIMDFloatUnitTests; static AudioBlockUnitTests> audioBlockSIMDDoubleUnitTests; +#endif -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister.h b/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister.h index bcf8a976..fe739be5 100644 --- a/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister.h +++ b/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { #ifndef DOXYGEN @@ -71,7 +68,7 @@ struct SIMDRegister /** The corresponding primitive integer type, for example, this will be int32_t if type is a float. */ - using MaskType = typename SIMDInternal::MaskTypeFor::type; + using MaskType = SIMDInternal::MaskType; //============================================================================== // Here are some types which are needed internally @@ -117,9 +114,6 @@ struct SIMDRegister /** Constructs an object from a scalar type by broadcasting it to all elements. */ inline SIMDRegister (Type s) noexcept { *this = s; } - /** Destructor. */ - inline ~SIMDRegister() noexcept = default; - //============================================================================== /** Returns the number of elements in this vector. */ static constexpr size_t size() noexcept { return SIMDNumElements; } @@ -127,14 +121,14 @@ struct SIMDRegister //============================================================================== /** Creates a new SIMDRegister from the corresponding scalar primitive. The scalar is extended to all elements of the vector. */ - inline static SIMDRegister JUCE_VECTOR_CALLTYPE expand (ElementType s) noexcept { return {CmplxOps::expand (s)}; } + static SIMDRegister JUCE_VECTOR_CALLTYPE expand (ElementType s) noexcept { return {CmplxOps::expand (s)}; } /** Creates a new SIMDRegister from the internal SIMD type (for example __mm128 for single-precision floating point on SSE architectures). */ - inline static SIMDRegister JUCE_VECTOR_CALLTYPE fromNative (vSIMDType a) noexcept { return {a}; } + static SIMDRegister JUCE_VECTOR_CALLTYPE fromNative (vSIMDType a) noexcept { return {a}; } /** Creates a new SIMDRegister from the first SIMDNumElements of a scalar array. */ - inline static SIMDRegister JUCE_VECTOR_CALLTYPE fromRawArray (const ElementType* a) noexcept + static SIMDRegister JUCE_VECTOR_CALLTYPE fromRawArray (const ElementType* a) noexcept { jassert (isSIMDAligned (a)); return {CmplxOps::load (a)}; @@ -283,43 +277,43 @@ struct SIMDRegister /** Returns a SIMDRegister of the corresponding integral type where each element has each bit set if the corresponding element of a is equal to the corresponding element of b, or zero otherwise. The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */ - static inline vMaskType JUCE_VECTOR_CALLTYPE equal (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::equal (a.value, b.value)); } + static vMaskType JUCE_VECTOR_CALLTYPE equal (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::equal (a.value, b.value)); } /** Returns a SIMDRegister of the corresponding integral type where each element has each bit set if the corresponding element of a is not equal to the corresponding element of b, or zero otherwise. The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */ - static inline vMaskType JUCE_VECTOR_CALLTYPE notEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::notEqual (a.value, b.value)); } + static vMaskType JUCE_VECTOR_CALLTYPE notEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::notEqual (a.value, b.value)); } /** Returns a SIMDRegister of the corresponding integral type where each element has each bit set if the corresponding element of a is less than to the corresponding element of b, or zero otherwise. The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */ - static inline vMaskType JUCE_VECTOR_CALLTYPE lessThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (b.value, a.value)); } + static vMaskType JUCE_VECTOR_CALLTYPE lessThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (b.value, a.value)); } /** Returns a SIMDRegister of the corresponding integral type where each element has each bit set if the corresponding element of a is than or equal to the corresponding element of b, or zero otherwise. The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */ - static inline vMaskType JUCE_VECTOR_CALLTYPE lessThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (b.value, a.value)); } + static vMaskType JUCE_VECTOR_CALLTYPE lessThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (b.value, a.value)); } /** Returns a SIMDRegister of the corresponding integral type where each element has each bit set if the corresponding element of a is greater than to the corresponding element of b, or zero otherwise. The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */ - static inline vMaskType JUCE_VECTOR_CALLTYPE greaterThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (a.value, b.value)); } + static vMaskType JUCE_VECTOR_CALLTYPE greaterThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (a.value, b.value)); } /** Returns a SIMDRegister of the corresponding integral type where each element has each bit set if the corresponding element of a is greater than or equal to the corresponding element of b, or zero otherwise. The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */ - static inline vMaskType JUCE_VECTOR_CALLTYPE greaterThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (a.value, b.value)); } + static vMaskType JUCE_VECTOR_CALLTYPE greaterThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (a.value, b.value)); } //============================================================================== /** Returns a new vector where each element is the minimum of the corresponding element of a and b. */ - static inline SIMDRegister JUCE_VECTOR_CALLTYPE min (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::min (a.value, b.value) }; } + static SIMDRegister JUCE_VECTOR_CALLTYPE min (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::min (a.value, b.value) }; } /** Returns a new vector where each element is the maximum of the corresponding element of a and b. */ - static inline SIMDRegister JUCE_VECTOR_CALLTYPE max (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::max (a.value, b.value) }; } + static SIMDRegister JUCE_VECTOR_CALLTYPE max (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::max (a.value, b.value) }; } //============================================================================== /** Multiplies b and c and adds the result to a. */ - static inline SIMDRegister JUCE_VECTOR_CALLTYPE multiplyAdd (SIMDRegister a, const SIMDRegister b, SIMDRegister c) noexcept + static SIMDRegister JUCE_VECTOR_CALLTYPE multiplyAdd (SIMDRegister a, const SIMDRegister b, SIMDRegister c) noexcept { return { CmplxOps::muladd (a.value, b.value, c.value) }; } @@ -331,18 +325,18 @@ struct SIMDRegister //============================================================================== /** Truncates each element to its integer part. Effectively discards the fractional part of each element. A.k.a. round to zero. */ - static inline SIMDRegister JUCE_VECTOR_CALLTYPE truncate (SIMDRegister a) noexcept { return { NativeOps::truncate (a.value) }; } + static SIMDRegister JUCE_VECTOR_CALLTYPE truncate (SIMDRegister a) noexcept { return { NativeOps::truncate (a.value) }; } //============================================================================== /** Returns the absolute value of each element. */ - static inline SIMDRegister JUCE_VECTOR_CALLTYPE abs (SIMDRegister a) noexcept + static SIMDRegister JUCE_VECTOR_CALLTYPE abs (SIMDRegister a) noexcept { return a - (a * (expand (ElementType (2)) & lessThan (a, expand (ElementType (0))))); } //============================================================================== /** Checks if the given pointer is sufficiently aligned for using SIMD operations. */ - static inline bool isSIMDAligned (const ElementType* ptr) noexcept + static bool isSIMDAligned (const ElementType* ptr) noexcept { uintptr_t bitmask = SIMDRegisterSize - 1; return (reinterpret_cast (ptr) & bitmask) == 0; @@ -353,13 +347,13 @@ struct SIMDRegister If the current position in memory is already aligned then this method will simply return the pointer. */ - static inline ElementType* getNextSIMDAlignedPtr (ElementType* ptr) noexcept + static ElementType* getNextSIMDAlignedPtr (ElementType* ptr) noexcept { return snapPointerToAlignment (ptr, SIMDRegisterSize); } private: - static inline vMaskType JUCE_VECTOR_CALLTYPE toMaskType (vSIMDType a) noexcept + static vMaskType JUCE_VECTOR_CALLTYPE toMaskType (vSIMDType a) noexcept { union { @@ -371,7 +365,7 @@ struct SIMDRegister return vMaskType::fromNative (u.out); } - static inline vSIMDType JUCE_VECTOR_CALLTYPE toVecType (vMaskSIMDType a) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE toVecType (vMaskSIMDType a) noexcept { union { @@ -383,7 +377,7 @@ struct SIMDRegister return u.out; } - static inline vSIMDType JUCE_VECTOR_CALLTYPE toVecType (MaskType a) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE toVecType (MaskType a) noexcept { union { @@ -396,9 +390,4 @@ struct SIMDRegister } }; -} // namespace dsp -} // namespace juce - -#ifndef DOXYGEN - #include "juce_SIMDRegister_Impl.h" -#endif +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_Impl.h b/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_Impl.h index d5d3db20..b6cf320c 100644 --- a/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_Impl.h +++ b/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_Impl.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -34,9 +33,10 @@ namespace dsp template struct SIMDRegister::ElementAccess { - operator Type() const { return simd.get (idx); } - ElementAccess& operator= (Type scalar) noexcept { simd.set (idx, scalar); return *this; } - ElementAccess& operator= (ElementAccess& o) noexcept { return operator= ((Type) o); } + ElementAccess (const ElementAccess&) = default; + operator Type() const { return simd.get (idx); } + ElementAccess& operator= (Type scalar) noexcept { simd.set (idx, scalar); return *this; } + ElementAccess& operator= (const ElementAccess& o) noexcept { return operator= ((Type) o); } private: friend struct SIMDRegister; @@ -56,42 +56,42 @@ struct CmplxSIMDOps { using vSIMDType = typename SIMDNativeOps::vSIMDType; - static inline vSIMDType JUCE_VECTOR_CALLTYPE load (const Scalar* a) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE load (const Scalar* a) noexcept { return SIMDNativeOps::load (a); } - static inline void JUCE_VECTOR_CALLTYPE store (vSIMDType value, Scalar* dest) noexcept + static void JUCE_VECTOR_CALLTYPE store (vSIMDType value, Scalar* dest) noexcept { SIMDNativeOps::store (value, dest); } - static inline vSIMDType JUCE_VECTOR_CALLTYPE expand (Scalar s) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE expand (Scalar s) noexcept { return SIMDNativeOps::expand (s); } - static inline Scalar JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept + static Scalar JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept { return SIMDNativeOps::get (v, i); } - static inline vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, Scalar s) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, Scalar s) noexcept { return SIMDNativeOps::set (v, i, s); } - static inline Scalar JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept + static Scalar JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept { return SIMDNativeOps::sum (a); } - static inline vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept { return SIMDNativeOps::mul (a, b); } - static inline vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return SIMDNativeOps::multiplyAdd (a, b, c); } @@ -103,17 +103,17 @@ struct CmplxSIMDOps> { using vSIMDType = typename SIMDNativeOps::vSIMDType; - static inline vSIMDType JUCE_VECTOR_CALLTYPE load (const std::complex* a) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE load (const std::complex* a) noexcept { return SIMDNativeOps::load (reinterpret_cast (a)); } - static inline void JUCE_VECTOR_CALLTYPE store (vSIMDType value, std::complex* dest) noexcept + static void JUCE_VECTOR_CALLTYPE store (vSIMDType value, std::complex* dest) noexcept { SIMDNativeOps::store (value, reinterpret_cast (dest)); } - static inline vSIMDType JUCE_VECTOR_CALLTYPE expand (std::complex s) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE expand (std::complex s) noexcept { const int n = sizeof (vSIMDType) / sizeof (Scalar); @@ -129,31 +129,31 @@ struct CmplxSIMDOps> return u.v; } - static inline std::complex JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept + static std::complex JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept { auto j = i << 1; return std::complex (SIMDNativeOps::get (v, j), SIMDNativeOps::get (v, j + 1)); } - static inline vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, std::complex s) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, std::complex s) noexcept { auto j = i << 1; return SIMDNativeOps::set (SIMDNativeOps::set (v, j, s.real()), j + 1, s.imag()); } - static inline std::complex JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept + static std::complex JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept { vSIMDType result = SIMDNativeOps::oddevensum (a); auto* ptr = reinterpret_cast (&result); return std::complex (ptr[0], ptr[1]); } - static inline vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept { return SIMDNativeOps::cmplxmul (a, b); } - static inline vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept + static vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return SIMDNativeOps::add (a, SIMDNativeOps::cmplxmul (b, c)); } diff --git a/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp b/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp index 382c1a6b..1751f204 100644 --- a/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp +++ b/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,37 +23,37 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { namespace SIMDRegister_test_internal { - template struct RandomPrimitive {}; - template - struct RandomPrimitive::value>::type> + struct RandomPrimitive { static type next (Random& random) { - return static_cast (std::is_signed::value ? (random.nextFloat() * 16.0) - 8.0 - : (random.nextFloat() * 8.0)); - + if constexpr (std::is_floating_point_v) + { + return static_cast (std::is_signed_v ? (random.nextFloat() * 16.0) - 8.0 + : (random.nextFloat() * 8.0)); + } + else if constexpr (std::is_integral_v) + { + return static_cast (random.nextInt64()); + } } }; template - struct RandomPrimitive::value>::type> + struct RandomValue { static type next (Random& random) { - return static_cast (random.nextInt64()); - + return RandomPrimitive::next (random); } }; - template struct RandomValue { static type next (Random& random) { return RandomPrimitive::next (random); } }; template struct RandomValue> { @@ -64,45 +63,14 @@ namespace SIMDRegister_test_internal } }; - - template - struct VecFiller - { - static void fill (type* dst, const int size, Random& random) - { - for (int i = 0; i < size; ++i) - dst[i] = RandomValue::next (random); - } - }; - - // We need to specialise for complex types: otherwise GCC 6 gives - // us an ICE internal compiler error after which the compiler seg faults. template - struct VecFiller> + static void fillVec (type* dst, Random& random) { - static void fill (std::complex* dst, const int size, Random& random) + std::generate_n (dst, SIMDRegister::SIMDNumElements, [&] { - for (int i = 0; i < size; ++i) - dst[i] = std::complex (RandomValue::next (random), RandomValue::next (random)); - } - }; - - template - struct VecFiller> - { - static SIMDRegister fill (Random& random) - { - constexpr int size = (int) SIMDRegister::SIMDNumElements; - #ifdef _MSC_VER - __declspec(align(sizeof (SIMDRegister))) type elements[size]; - #else - type elements[(size_t) size] __attribute__((aligned(sizeof (SIMDRegister)))); - #endif - - VecFiller::fill (elements, size, random); - return SIMDRegister::fromRawArray (elements); - } - }; + return RandomValue::next (random); + }); + } // Avoid visual studio warning template @@ -128,14 +96,16 @@ namespace SIMDRegister_test_internal { return difference (a - b); } -} +} // namespace SIMDRegister_test_internal // These tests need to be strictly run on all platforms supported by JUCE as the // SIMD code is highly platform dependent. -class SIMDRegisterUnitTests : public UnitTest +class SIMDRegisterUnitTests final : public UnitTest { public: + template struct Tag {}; + SIMDRegisterUnitTests() : UnitTest ("SIMDRegister UnitTests", UnitTestCategories::dsp) {} @@ -145,19 +115,12 @@ class SIMDRegisterUnitTests : public UnitTest template static bool allValuesEqualTo (const SIMDRegister& vec, const type scalar) { - #ifdef _MSC_VER - __declspec(align(sizeof (SIMDRegister))) type elements[SIMDRegister::SIMDNumElements]; - #else - type elements[SIMDRegister::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister)))); - #endif + alignas (sizeof (SIMDRegister)) type elements[SIMDRegister::SIMDNumElements]; vec.copyToRawArray (elements); // as we do not want to rely on the access operator we cast this to a primitive pointer - for (size_t i = 0; i < SIMDRegister::SIMDNumElements; ++i) - if (elements[i] != scalar) return false; - - return true; + return std::all_of (std::begin (elements), std::end (elements), [scalar] (const auto x) { return exactlyEqual (x, scalar); }); } template @@ -291,17 +254,17 @@ class SIMDRegisterUnitTests : public UnitTest struct InitializationTest { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { u.expect (allValuesEqualTo (SIMDRegister::expand (static_cast (23)), 23)); { #ifdef _MSC_VER - __declspec(align(sizeof (SIMDRegister))) type elements[SIMDRegister::SIMDNumElements]; + __declspec (align (sizeof (SIMDRegister))) type elements[SIMDRegister::SIMDNumElements]; #else - type elements[SIMDRegister::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister)))); + type elements[SIMDRegister::SIMDNumElements] __attribute__ ((aligned (sizeof (SIMDRegister)))); #endif - SIMDRegister_test_internal::VecFiller::fill (elements, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (elements, random); SIMDRegister a (SIMDRegister::fromRawArray (elements)); u.expect (vecEqualToArray (a, elements)); @@ -317,13 +280,13 @@ class SIMDRegisterUnitTests : public UnitTest struct AccessTest { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { // set-up SIMDRegister a; type array [SIMDRegister::SIMDNumElements]; - SIMDRegister_test_internal::VecFiller::fill (array, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array, random); // Test non-const access operator for (size_t i = 0; i < SIMDRegister::SIMDNumElements; ++i) @@ -335,7 +298,7 @@ class SIMDRegisterUnitTests : public UnitTest const SIMDRegister& b = a; for (size_t i = 0; i < SIMDRegister::SIMDNumElements; ++i) - u.expect (b[i] == array[i]); + u.expect (exactlyEqual (b[i], array[i])); } }; @@ -343,7 +306,7 @@ class SIMDRegisterUnitTests : public UnitTest struct OperatorTests { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { for (int n = 0; n < 100; ++n) { @@ -356,9 +319,9 @@ class SIMDRegisterUnitTests : public UnitTest type array_b [SIMDRegister::SIMDNumElements]; type array_c [SIMDRegister::SIMDNumElements]; - SIMDRegister_test_internal::VecFiller::fill (array_a, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_b, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_c, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array_a, random); + SIMDRegister_test_internal::fillVec (array_b, random); + SIMDRegister_test_internal::fillVec (array_c, random); copy (a, array_a); copy (b, array_b); copy (c, array_c); @@ -371,9 +334,9 @@ class SIMDRegisterUnitTests : public UnitTest u.expect (vecEqualToArray (a, array_a)); u.expect (vecEqualToArray (b, array_b)); - SIMDRegister_test_internal::VecFiller::fill (array_a, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_b, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_c, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array_a, random); + SIMDRegister_test_internal::fillVec (array_b, random); + SIMDRegister_test_internal::fillVec (array_c, random); copy (a, array_a); copy (b, array_b); copy (c, array_c); @@ -387,9 +350,9 @@ class SIMDRegisterUnitTests : public UnitTest u.expect (vecEqualToArray (b, array_b)); // set-up again - SIMDRegister_test_internal::VecFiller::fill (array_a, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_b, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_c, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array_a, random); + SIMDRegister_test_internal::fillVec (array_b, random); + SIMDRegister_test_internal::fillVec (array_c, random); copy (a, array_a); copy (b, array_b); copy (c, array_c); // test out-of-place with both params being vectors @@ -419,7 +382,7 @@ class SIMDRegisterUnitTests : public UnitTest struct BitOperatorTests { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { typedef typename SIMDRegister::vMaskType vMaskType; typedef typename SIMDRegister::MaskType MaskType; @@ -439,7 +402,7 @@ class SIMDRegisterUnitTests : public UnitTest } a, b; vMaskType bitmask = vMaskType::expand (static_cast (1) << (sizeof (MaskType) - 1)); - SIMDRegister_test_internal::VecFiller::fill (array_a, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array_a, random); copy (a.floatVersion, array_a); copy (b.floatVersion, array_a); @@ -447,9 +410,9 @@ class SIMDRegisterUnitTests : public UnitTest Operation::template inplace (b.intVersion, bitmask); #ifdef _MSC_VER - __declspec(align(sizeof (SIMDRegister))) type elements[SIMDRegister::SIMDNumElements]; + __declspec (align (sizeof (SIMDRegister))) type elements[SIMDRegister::SIMDNumElements]; #else - type elements[SIMDRegister::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister)))); + type elements[SIMDRegister::SIMDNumElements] __attribute__ ((aligned (sizeof (SIMDRegister)))); #endif b.floatVersion.copyToRawArray (elements); @@ -467,9 +430,9 @@ class SIMDRegisterUnitTests : public UnitTest type float_a [SIMDRegister::SIMDNumElements]; type float_c [SIMDRegister::SIMDNumElements]; - SIMDRegister_test_internal::VecFiller::fill (float_a, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_b, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (float_c, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (float_a, random); + SIMDRegister_test_internal::fillVec (array_b, random); + SIMDRegister_test_internal::fillVec (float_c, random); memcpy (array_a, float_a, sizeof (type) * SIMDRegister::SIMDNumElements); memcpy (array_c, float_c, sizeof (type) * SIMDRegister::SIMDNumElements); @@ -485,9 +448,9 @@ class SIMDRegisterUnitTests : public UnitTest u.expect (vecEqualToArray (a, float_a)); u.expect (vecEqualToArray (b, array_b)); - SIMDRegister_test_internal::VecFiller::fill (float_a, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_b, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (float_c, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (float_a, random); + SIMDRegister_test_internal::fillVec (array_b, random); + SIMDRegister_test_internal::fillVec (float_c, random); memcpy (array_a, float_a, sizeof (type) * SIMDRegister::SIMDNumElements); memcpy (array_c, float_c, sizeof (type) * SIMDRegister::SIMDNumElements); copy (a, float_a); copy (b, array_b); copy (c, float_c); @@ -503,9 +466,9 @@ class SIMDRegisterUnitTests : public UnitTest u.expect (vecEqualToArray (b, array_b)); // set-up again - SIMDRegister_test_internal::VecFiller::fill (float_a, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_b, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (float_c, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (float_a, random); + SIMDRegister_test_internal::fillVec (array_b, random); + SIMDRegister_test_internal::fillVec (float_c, random); memcpy (array_a, float_a, sizeof (type) * SIMDRegister::SIMDNumElements); memcpy (array_c, float_c, sizeof (type) * SIMDRegister::SIMDNumElements); copy (a, float_a); copy (b, array_b); copy (c, float_c); @@ -543,7 +506,7 @@ class SIMDRegisterUnitTests : public UnitTest struct CheckComparisonOps { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { typedef typename SIMDRegister::vMaskType vMaskType; typedef typename SIMDRegister::MaskType MaskType; @@ -561,14 +524,14 @@ class SIMDRegisterUnitTests : public UnitTest MaskType array_ge [SIMDRegister::SIMDNumElements]; - SIMDRegister_test_internal::VecFiller::fill (array_a, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_b, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array_a, random); + SIMDRegister_test_internal::fillVec (array_b, random); // do check for (size_t j = 0; j < SIMDRegister::SIMDNumElements; ++j) { - array_eq [j] = (array_a[j] == array_b[j]) ? static_cast (-1) : 0; - array_neq [j] = (array_a[j] != array_b[j]) ? static_cast (-1) : 0; + array_eq [j] = ( exactlyEqual (array_a[j], array_b[j])) ? static_cast (-1) : 0; + array_neq [j] = (! exactlyEqual (array_a[j], array_b[j])) ? static_cast (-1) : 0; array_lt [j] = (array_a[j] < array_b[j]) ? static_cast (-1) : 0; array_le [j] = (array_a[j] <= array_b[j]) ? static_cast (-1) : 0; array_gt [j] = (array_a[j] > array_b[j]) ? static_cast (-1) : 0; @@ -599,8 +562,8 @@ class SIMDRegisterUnitTests : public UnitTest do { - SIMDRegister_test_internal::VecFiller::fill (array_a, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_b, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array_a, random); + SIMDRegister_test_internal::fillVec (array_b, random); } while (std::equal (array_a, array_a + SIMDRegister::SIMDNumElements, array_b)); copy (a, array_a); @@ -610,7 +573,7 @@ class SIMDRegisterUnitTests : public UnitTest u.expect (! (a == b)); u.expect (! (b == a)); - SIMDRegister_test_internal::VecFiller::fill (array_a, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array_a, random); copy (a, array_a); copy (b, array_a); @@ -636,7 +599,7 @@ class SIMDRegisterUnitTests : public UnitTest struct CheckMultiplyAdd { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { // set-up type array_a [SIMDRegister::SIMDNumElements]; @@ -644,10 +607,10 @@ class SIMDRegisterUnitTests : public UnitTest type array_c [SIMDRegister::SIMDNumElements]; type array_d [SIMDRegister::SIMDNumElements]; - SIMDRegister_test_internal::VecFiller::fill (array_a, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_b, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_c, SIMDRegister::SIMDNumElements, random); - SIMDRegister_test_internal::VecFiller::fill (array_d, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array_a, random); + SIMDRegister_test_internal::fillVec (array_b, random); + SIMDRegister_test_internal::fillVec (array_c, random); + SIMDRegister_test_internal::fillVec (array_d, random); // check for (size_t i = 0; i < SIMDRegister::SIMDNumElements; ++i) @@ -668,7 +631,7 @@ class SIMDRegisterUnitTests : public UnitTest struct CheckMinMax { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { for (int i = 0; i < 100; ++i) { @@ -718,17 +681,14 @@ class SIMDRegisterUnitTests : public UnitTest struct CheckSum { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { type array [SIMDRegister::SIMDNumElements]; - type sumCheck = 0; - SIMDRegister_test_internal::VecFiller::fill (array, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array, random); - for (size_t j = 0; j < SIMDRegister::SIMDNumElements; ++j) - { - sumCheck += array[j]; - } + using AddedType = decltype (type{} + type{}); + const auto sumCheck = (type) std::accumulate (std::begin (array), std::end (array), AddedType{}); SIMDRegister a; copy (a, array); @@ -740,18 +700,18 @@ class SIMDRegisterUnitTests : public UnitTest struct CheckAbs { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { type inArray[SIMDRegister::SIMDNumElements]; type outArray[SIMDRegister::SIMDNumElements]; - SIMDRegister_test_internal::VecFiller::fill (inArray, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (inArray, random); SIMDRegister a; copy (a, inArray); a = SIMDRegister::abs (a); - auto calcAbs = [] (type x) -> type { return x >= type (0) ? x : -x; }; + auto calcAbs = [] (type x) -> type { return x >= type (0) ? x : type (-x); }; for (size_t j = 0; j < SIMDRegister::SIMDNumElements; ++j) outArray[j] = calcAbs (inArray[j]); @@ -763,12 +723,12 @@ class SIMDRegisterUnitTests : public UnitTest struct CheckTruncate { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { type inArray[SIMDRegister::SIMDNumElements]; type outArray[SIMDRegister::SIMDNumElements]; - SIMDRegister_test_internal::VecFiller::fill (inArray, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (inArray, random); SIMDRegister a; copy (a, inArray); @@ -784,13 +744,12 @@ class SIMDRegisterUnitTests : public UnitTest struct CheckBoolEquals { template - static void run (UnitTest& u, Random& random) + static void run (UnitTest& u, Random& random, Tag) { - bool is_signed = std::is_signed::value; type array [SIMDRegister::SIMDNumElements]; - auto value = is_signed ? static_cast ((random.nextFloat() * 16.0) - 8.0) - : static_cast (random.nextFloat() * 8.0); + auto value = std::is_signed_v ? static_cast ((random.nextFloat() * 16.0) - 8.0) + : static_cast (random.nextFloat() * 8.0); std::fill (array, array + SIMDRegister::SIMDNumElements, value); SIMDRegister a, b; @@ -803,14 +762,14 @@ class SIMDRegisterUnitTests : public UnitTest u.expect (a != value); u.expect (! (a == value)); - SIMDRegister_test_internal::VecFiller::fill (array, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array, random); copy (a, array); copy (b, array); u.expect (a == b); u.expect (! (a != b)); - SIMDRegister_test_internal::VecFiller::fill (array, SIMDRegister::SIMDNumElements, random); + SIMDRegister_test_internal::fillVec (array, random); copy (b, array); u.expect (a != b); @@ -820,100 +779,99 @@ class SIMDRegisterUnitTests : public UnitTest //============================================================================== template - void runTestFloatingPoint (const char* unitTestName) + void runTestFloatingPoint (const char* unitTestName, TheTest) { beginTest (unitTestName); Random random = getRandom(); - TheTest::template run (*this, random); - TheTest::template run (*this, random); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag{}); } //============================================================================== template - void runTestForAllTypes (const char* unitTestName) + void runTestForAllTypes (const char* unitTestName, TheTest) { beginTest (unitTestName); Random random = getRandom(); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run(*this, random); - TheTest::template run (*this, random); - TheTest::template run(*this, random); - TheTest::template run (*this, random); - TheTest::template run(*this, random); - TheTest::template run> (*this, random); - TheTest::template run> (*this, random); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag> {}); + TheTest::run (*this, random, Tag>{}); } template - void runTestNonComplex (const char* unitTestName) + void runTestNonComplex (const char* unitTestName, TheTest) { beginTest (unitTestName); Random random = getRandom(); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run(*this, random); - TheTest::template run (*this, random); - TheTest::template run(*this, random); - TheTest::template run (*this, random); - TheTest::template run(*this, random); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag{}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag{}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag{}); } template - void runTestSigned (const char* unitTestName) + void runTestSigned (const char* unitTestName, TheTest) { beginTest (unitTestName); Random random = getRandom(); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run (*this, random); - TheTest::template run (*this, random); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag {}); + TheTest::run (*this, random, Tag{}); + TheTest::run (*this, random, Tag{}); + TheTest::run (*this, random, Tag{}); } - void runTest() + void runTest() override { - runTestForAllTypes ("InitializationTest"); + runTestForAllTypes ("InitializationTest", InitializationTest{}); - runTestForAllTypes ("AccessTest"); + runTestForAllTypes ("AccessTest", AccessTest{}); - runTestForAllTypes> ("AdditionOperators"); - runTestForAllTypes> ("SubtractionOperators"); - runTestForAllTypes> ("MultiplicationOperators"); + runTestForAllTypes ("AdditionOperators", OperatorTests{}); + runTestForAllTypes ("SubtractionOperators", OperatorTests{}); + runTestForAllTypes ("MultiplicationOperators", OperatorTests{}); - runTestForAllTypes> ("BitANDOperators"); - runTestForAllTypes> ("BitOROperators"); - runTestForAllTypes> ("BitXOROperators"); + runTestForAllTypes ("BitANDOperators", BitOperatorTests{}); + runTestForAllTypes ("BitOROperators", BitOperatorTests{}); + runTestForAllTypes ("BitXOROperators", BitOperatorTests{}); - runTestNonComplex ("CheckComparisons"); - runTestNonComplex ("CheckBoolEquals"); - runTestNonComplex ("CheckMinMax"); + runTestNonComplex ("CheckComparisons", CheckComparisonOps{}); + runTestNonComplex ("CheckBoolEquals", CheckBoolEquals{}); + runTestNonComplex ("CheckMinMax", CheckMinMax{}); - runTestForAllTypes ("CheckMultiplyAdd"); - runTestForAllTypes ("CheckSum"); + runTestForAllTypes ("CheckMultiplyAdd", CheckMultiplyAdd{}); + runTestForAllTypes ("CheckSum", CheckSum{}); - runTestSigned ("CheckAbs"); + runTestSigned ("CheckAbs", CheckAbs{}); - runTestFloatingPoint ("CheckTruncate"); + runTestFloatingPoint ("CheckTruncate", CheckTruncate{}); } }; static SIMDRegisterUnitTests SIMDRegisterUnitTests; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.cpp b/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.cpp index 15efdf32..62a9e95d 100644 --- a/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.cpp +++ b/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { template @@ -46,7 +43,7 @@ typename FIR::Coefficients::Ptr for (size_t i = 0; i <= order; ++i) { - if (i == order * 0.5) + if (i == order / 2) { c[i] = static_cast (normalisedFrequency * 2); } @@ -114,8 +111,8 @@ typename FIR::Coefficients::Ptr } else { - auto indice = MathConstants::pi * (i - 0.5 * order); - auto indice2 = MathConstants::pi * normalisedTransitionWidth * (i - 0.5 * order) / spline; + auto indice = MathConstants::pi * ((double) i - 0.5 * (double) order); + auto indice2 = MathConstants::pi * normalisedTransitionWidth * ((double) i - 0.5 * (double) order) / spline; c[i] = static_cast (std::sin (2 * indice * normalisedFrequency) / indice * std::pow (std::sin (indice2) / indice2, spline)); } @@ -146,6 +143,11 @@ typename FIR::Coefficients::Ptr auto* result = new typename FIR::Coefficients (static_cast (N)); auto* c = result->getRawCoefficients(); + auto sinc = [] (double x) + { + return approximatelyEqual (x, 0.0) ? 1 : std::sin (x * MathConstants::pi) / (MathConstants::pi * x); + }; + if (N % 2 == 1) { // Type I @@ -154,19 +156,16 @@ typename FIR::Coefficients::Ptr Matrix b (M + 1, 1), q (2 * M + 1, 1); - auto sinc = [](double x) { return x == 0 ? 1 : std::sin (x * MathConstants::pi) - / (MathConstants::pi * x); }; - auto factorp = wp / MathConstants::pi; auto factors = ws / MathConstants::pi; for (size_t i = 0; i <= M; ++i) - b (i, 0) = factorp * sinc (factorp * i); + b (i, 0) = factorp * sinc (factorp * (double) i); q (0, 0) = factorp + stopBandWeight * (1.0 - factors); for (size_t i = 1; i <= 2 * M; ++i) - q (i, 0) = factorp * sinc (factorp * i) - stopBandWeight * factors * sinc (factors * i); + q (i, 0) = factorp * sinc (factorp * (double) i) - stopBandWeight * factors * sinc (factors * (double) i); auto Q1 = Matrix::toeplitz (q, M + 1); auto Q2 = Matrix::hankel (q, M + 1, 0); @@ -192,19 +191,16 @@ typename FIR::Coefficients::Ptr Matrix qp (2 * M, 1); Matrix qs (2 * M, 1); - auto sinc = [](double x) { return x == 0 ? 1 : std::sin (x * MathConstants::pi) - / (MathConstants::pi * x); }; - auto factorp = wp / MathConstants::pi; auto factors = ws / MathConstants::pi; for (size_t i = 0; i < M; ++i) - b (i, 0) = factorp * sinc (factorp * (i + 0.5)); + b (i, 0) = factorp * sinc (factorp * ((double) i + 0.5)); for (size_t i = 0; i < 2 * M; ++i) { - qp (i, 0) = 0.25 * factorp * sinc (factorp * i); - qs (i, 0) = -0.25 * stopBandWeight * factors * sinc (factors * i); + qp (i, 0) = 0.25 * factorp * sinc (factorp * (double) i); + qs (i, 0) = -0.25 * stopBandWeight * factors * sinc (factors * (double) i); } auto Q1p = Matrix::toeplitz (qp, M); @@ -271,19 +267,19 @@ typename FIR::Coefficients::Ptr for (int i = 0; i < hh.size(); ++i) c[i] = (float) hh[i]; - double NN; - - if (n % 2 == 0) - { - NN = 2.0 * result->getMagnitudeForFrequency (0.5, 1.0); - } - else + auto NN = [&] { + if (n % 2 == 0) + return 2.0 * result->getMagnitudeForFrequency (0.5, 1.0); + auto w01 = std::sqrt (kp * kp + (1 - kp * kp) * std::pow (std::cos (MathConstants::pi / (2.0 * n + 1.0)), 2.0)); - auto om01 = std::acos (-w01); - NN = -2.0 * result->getMagnitudeForFrequency (om01 / MathConstants::twoPi, 1.0); - } + if (std::abs (w01) > 1.0) + return 2.0 * result->getMagnitudeForFrequency (0.5, 1.0); + + auto om01 = std::acos (-w01); + return -2.0 * result->getMagnitudeForFrequency (om01 / MathConstants::twoPi, 1.0); + }(); for (int i = 0; i < hh.size(); ++i) c[i] = static_cast ((A * hn[i] + B * hnm[i]) / NN); @@ -387,16 +383,19 @@ ReferenceCountedArray> FloatType passbandAmplitudedB, FloatType stopbandAmplitudedB) { - jassert (sampleRate > 0); - jassert (frequency > 0 && frequency <= sampleRate * 0.5); - jassert (normalisedTransitionWidth > 0 && normalisedTransitionWidth <= 0.5); - jassert (passbandAmplitudedB > -20 && passbandAmplitudedB < 0); - jassert (stopbandAmplitudedB > -300 && stopbandAmplitudedB < -20); + jassert (0 < sampleRate); + jassert (0 < frequency && frequency <= sampleRate * 0.5); + jassert (0 < normalisedTransitionWidth && normalisedTransitionWidth <= 0.5); + jassert (-20 < passbandAmplitudedB && passbandAmplitudedB < 0); + jassert (-300 < stopbandAmplitudedB && stopbandAmplitudedB < -20); auto normalisedFrequency = frequency / sampleRate; auto fp = normalisedFrequency - normalisedTransitionWidth / 2; + jassert (0.0 < fp && fp < 0.5); + auto fs = normalisedFrequency + normalisedTransitionWidth / 2; + jassert (0.0 < fs && fs < 0.5); double Ap = passbandAmplitudedB; double As = stopbandAmplitudedB; @@ -468,7 +467,7 @@ ReferenceCountedArray> auto v0 = std::asinh (epss) / (N * halfPi); if (r == 1) - pa.add(-1.0 / (k / omegap * std::sinh (v0 * halfPi))); + pa.add (-1.0 / (k / omegap * std::sinh (v0 * halfPi))); for (int i = 1; i <= L; ++i) { @@ -553,7 +552,7 @@ ReferenceCountedArray> { arrayFilters.add (*IIR::Coefficients::makeFirstOrderLowPass (sampleRate, frequency)); - for (auto i = 0; i < order / 2; ++i) + for (int i = 0; i < order / 2; ++i) { auto Q = 1.0 / (2.0 * std::cos ((i + 1.0) * MathConstants::pi / order)); arrayFilters.add (*IIR::Coefficients::makeLowPass (sampleRate, frequency, @@ -562,7 +561,7 @@ ReferenceCountedArray> } else { - for (auto i = 0; i < order / 2; ++i) + for (int i = 0; i < order / 2; ++i) { auto Q = 1.0 / (2.0 * std::cos ((2.0 * i + 1.0) * MathConstants::pi / (order * 2.0))); arrayFilters.add (*IIR::Coefficients::makeLowPass (sampleRate, frequency, @@ -588,7 +587,7 @@ ReferenceCountedArray> { arrayFilters.add (*IIR::Coefficients::makeFirstOrderHighPass (sampleRate, frequency)); - for (auto i = 0; i < order / 2; ++i) + for (int i = 0; i < order / 2; ++i) { auto Q = 1.0 / (2.0 * std::cos ((i + 1.0) * MathConstants::pi / order)); arrayFilters.add (*IIR::Coefficients::makeHighPass (sampleRate, frequency, @@ -597,7 +596,7 @@ ReferenceCountedArray> } else { - for (auto i = 0; i < order / 2; ++i) + for (int i = 0; i < order / 2; ++i) { auto Q = 1.0 / (2.0 * std::cos ((2.0 * i + 1.0) * MathConstants::pi / (order * 2.0))); arrayFilters.add (*IIR::Coefficients::makeHighPass (sampleRate, frequency, @@ -696,5 +695,4 @@ typename FilterDesign::IIRPolyphaseAllpassStructure template struct FilterDesign; template struct FilterDesign; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.h b/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.h index 7a59d408..facdd39a 100644 --- a/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.h +++ b/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -296,5 +293,4 @@ struct FilterDesign FilterDesign() = delete; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.cpp b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.cpp index 137866b7..c4b8e4bb 100644 --- a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.cpp +++ b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,160 +23,202 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { -/** This class is the convolution engine itself, processing only one channel at - a time of input signal. -*/ -struct ConvolutionEngine +template +class Queue { - ConvolutionEngine() = default; +public: + explicit Queue (int size) + : fifo (size), storage (static_cast (size)) {} - //============================================================================== - struct ProcessingInformation + bool push (Element& element) noexcept { - enum class SourceType - { - sourceBinaryData, - sourceAudioFile, - sourceAudioBuffer, - sourceNone - }; + if (fifo.getFreeSpace() == 0) + return false; - SourceType sourceType = SourceType::sourceNone; + const auto writer = fifo.write (1); - const void* sourceData; - int sourceDataSize; - File fileImpulseResponse; + if (writer.blockSize1 != 0) + storage[static_cast (writer.startIndex1)] = std::move (element); + else if (writer.blockSize2 != 0) + storage[static_cast (writer.startIndex2)] = std::move (element); - double originalSampleRate; - int originalSize = 0; - int originalNumChannels = 1; + return true; + } - AudioBuffer* buffer; + template + void pop (Fn&& fn) { popN (1, std::forward (fn)); } - bool wantsStereo = true; - bool wantsTrimming = true; - bool wantsNormalisation = true; - int64 wantedSize = 0; - int finalSize = 0; + template + void popAll (Fn&& fn) { popN (fifo.getNumReady(), std::forward (fn)); } - double sampleRate = 0; - size_t maximumBufferSize = 0; - }; + bool hasPendingMessages() const noexcept { return fifo.getNumReady() > 0; } - //============================================================================== - void reset() +private: + template + void popN (int n, Fn&& fn) { - bufferInput.clear(); - bufferOverlap.clear(); - bufferTempOutput.clear(); + fifo.read (n).forEach ([&] (int index) + { + fn (storage[static_cast (index)]); + }); + } + + AbstractFifo fifo; + std::vector storage; +}; - for (auto i = 0; i < buffersInputSegments.size(); ++i) - buffersInputSegments.getReference (i).clear(); +class BackgroundMessageQueue : private Thread +{ +public: + explicit BackgroundMessageQueue (int entries) + : Thread ("Convolution background loader"), queue (entries) + {} - currentSegment = 0; - inputDataPos = 0; + using IncomingCommand = FixedSizeFunction<400, void()>; + + // Push functions here, and they'll be called later on a background thread. + // This function is wait-free. + // This function is only safe to call from a single thread at a time. + bool push (IncomingCommand& command) { return queue.push (command); } + + void popAll() + { + const ScopedLock lock (popMutex); + queue.popAll ([] (IncomingCommand& command) { command(); command = nullptr; }); } - /** Initalize all the states and objects to perform the convolution. */ - void initializeConvolutionEngine (ProcessingInformation& info, int channel) + using Thread::startThread; + using Thread::stopThread; + +private: + void run() override { - blockSize = (size_t) nextPowerOfTwo ((int) info.maximumBufferSize); + while (! threadShouldExit()) + { + const auto tryPop = [&] + { + const ScopedLock lock (popMutex); - FFTSize = blockSize > 128 ? 2 * blockSize - : 4 * blockSize; + if (! queue.hasPendingMessages()) + return false; - numSegments = ((size_t) info.finalSize) / (FFTSize - blockSize) + 1u; + queue.pop ([] (IncomingCommand& command) { command(); command = nullptr;}); + return true; + }; - numInputSegments = (blockSize > 128 ? numSegments : 3 * numSegments); + if (! tryPop()) + sleep (10); + } + } - FFTobject.reset (new FFT (roundToInt (std::log2 (FFTSize)))); + CriticalSection popMutex; + Queue queue; - bufferInput.setSize (1, static_cast (FFTSize)); - bufferOutput.setSize (1, static_cast (FFTSize * 2)); - bufferTempOutput.setSize (1, static_cast (FFTSize * 2)); - bufferOverlap.setSize (1, static_cast (FFTSize)); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BackgroundMessageQueue) +}; - buffersInputSegments.clear(); - buffersImpulseSegments.clear(); +struct ConvolutionMessageQueue::Impl : public BackgroundMessageQueue +{ + using BackgroundMessageQueue::BackgroundMessageQueue; +}; + +ConvolutionMessageQueue::ConvolutionMessageQueue() + : ConvolutionMessageQueue (1000) +{} + +ConvolutionMessageQueue::ConvolutionMessageQueue (int entries) + : pimpl (std::make_unique (entries)) +{ + pimpl->startThread(); +} + +ConvolutionMessageQueue::~ConvolutionMessageQueue() noexcept +{ + pimpl->stopThread (-1); +} + +ConvolutionMessageQueue::ConvolutionMessageQueue (ConvolutionMessageQueue&&) noexcept = default; +ConvolutionMessageQueue& ConvolutionMessageQueue::operator= (ConvolutionMessageQueue&&) noexcept = default; + +//============================================================================== +struct ConvolutionEngine +{ + ConvolutionEngine (const float* samples, + size_t numSamples, + size_t maxBlockSize) + : blockSize ((size_t) nextPowerOfTwo ((int) maxBlockSize)), + fftSize (blockSize > 128 ? 2 * blockSize : 4 * blockSize), + fftObject (std::make_unique (roundToInt (std::log2 (fftSize)))), + numSegments (numSamples / (fftSize - blockSize) + 1u), + numInputSegments ((blockSize > 128 ? numSegments : 3 * numSegments)), + bufferInput (1, static_cast (fftSize)), + bufferOutput (1, static_cast (fftSize * 2)), + bufferTempOutput (1, static_cast (fftSize * 2)), + bufferOverlap (1, static_cast (fftSize)) + { bufferOutput.clear(); - for (size_t i = 0; i < numInputSegments; ++i) + auto updateSegmentsIfNecessary = [this] (size_t numSegmentsToUpdate, + std::vector>& segments) { - AudioBuffer newInputSegment; - newInputSegment.setSize (1, static_cast (FFTSize * 2)); - buffersInputSegments.add (newInputSegment); - } + if (numSegmentsToUpdate == 0 + || numSegmentsToUpdate != (size_t) segments.size() + || (size_t) segments[0].getNumSamples() != fftSize * 2) + { + segments.clear(); - for (auto i = 0u; i < numSegments; ++i) - { - AudioBuffer newImpulseSegment; - newImpulseSegment.setSize (1, static_cast (FFTSize * 2)); - buffersImpulseSegments.add (newImpulseSegment); - } + for (size_t i = 0; i < numSegmentsToUpdate; ++i) + segments.push_back ({ 1, static_cast (fftSize * 2) }); + } + }; - std::unique_ptr FFTTempObject (new FFT (roundToInt (std::log2 (FFTSize)))); + updateSegmentsIfNecessary (numInputSegments, buffersInputSegments); + updateSegmentsIfNecessary (numSegments, buffersImpulseSegments); - auto* channelData = info.buffer->getWritePointer (channel); + auto FFTTempObject = std::make_unique (roundToInt (std::log2 (fftSize))); + size_t currentPtr = 0; - for (size_t n = 0; n < numSegments; ++n) + for (auto& buf : buffersImpulseSegments) { - buffersImpulseSegments.getReference (static_cast (n)).clear(); + buf.clear(); - auto* impulseResponse = buffersImpulseSegments.getReference (static_cast (n)).getWritePointer (0); + auto* impulseResponse = buf.getWritePointer (0); - if (n == 0) + if (&buf == &buffersImpulseSegments.front()) impulseResponse[0] = 1.0f; - for (size_t i = 0; i < FFTSize - blockSize; ++i) - if (i + n * (FFTSize - blockSize) < (size_t) info.finalSize) - impulseResponse[i] = channelData[i + n * (FFTSize - blockSize)]; + FloatVectorOperations::copy (impulseResponse, + samples + currentPtr, + static_cast (jmin (fftSize - blockSize, numSamples - currentPtr))); FFTTempObject->performRealOnlyForwardTransform (impulseResponse); prepareForConvolution (impulseResponse); + + currentPtr += (fftSize - blockSize); } reset(); - - isReady = true; } - /** Copy the states of another engine. */ - void copyStateFromOtherEngine (const ConvolutionEngine& other) + void reset() { - if (FFTSize != other.FFTSize) - { - FFTobject.reset (new FFT (roundToInt (std::log2 (other.FFTSize)))); - FFTSize = other.FFTSize; - } - - currentSegment = other.currentSegment; - numInputSegments = other.numInputSegments; - numSegments = other.numSegments; - blockSize = other.blockSize; - inputDataPos = other.inputDataPos; - - bufferInput = other.bufferInput; - bufferTempOutput = other.bufferTempOutput; - bufferOutput = other.bufferOutput; + bufferInput.clear(); + bufferOverlap.clear(); + bufferTempOutput.clear(); + bufferOutput.clear(); - buffersInputSegments = other.buffersInputSegments; - buffersImpulseSegments = other.buffersImpulseSegments; - bufferOverlap = other.bufferOverlap; + for (auto& buf : buffersInputSegments) + buf.clear(); - isReady = true; + currentSegment = 0; + inputDataPos = 0; } - /** Performs the uniform partitioned convolution using FFT. */ void processSamples (const float* input, float* output, size_t numSamples) { - if (! isReady) - return; - // Overlap-add, zero latency convolution algorithm with uniform partitioning size_t numSamplesProcessed = 0; @@ -193,20 +234,18 @@ struct ConvolutionEngine const bool inputDataWasEmpty = (inputDataPos == 0); auto numSamplesToProcess = jmin (numSamples - numSamplesProcessed, blockSize - inputDataPos); - // copy the input samples FloatVectorOperations::copy (inputData + inputDataPos, input + numSamplesProcessed, static_cast (numSamplesToProcess)); - auto* inputSegmentData = buffersInputSegments.getReference (static_cast (currentSegment)).getWritePointer (0); - FloatVectorOperations::copy (inputSegmentData, inputData, static_cast (FFTSize)); + auto* inputSegmentData = buffersInputSegments[currentSegment].getWritePointer (0); + FloatVectorOperations::copy (inputSegmentData, inputData, static_cast (fftSize)); - // Forward FFT - FFTobject->performRealOnlyForwardTransform (inputSegmentData); + fftObject->performRealOnlyForwardTransform (inputSegmentData); prepareForConvolution (inputSegmentData); // Complex multiplication if (inputDataWasEmpty) { - FloatVectorOperations::fill (outputTempData, 0, static_cast (FFTSize + 1)); + FloatVectorOperations::fill (outputTempData, 0, static_cast (fftSize + 1)); auto index = currentSegment; @@ -217,25 +256,23 @@ struct ConvolutionEngine if (index >= numInputSegments) index -= numInputSegments; - convolutionProcessingAndAccumulate (buffersInputSegments.getReference (static_cast (index)).getWritePointer (0), - buffersImpulseSegments.getReference (static_cast (i)).getWritePointer (0), + convolutionProcessingAndAccumulate (buffersInputSegments[index].getWritePointer (0), + buffersImpulseSegments[i].getWritePointer (0), outputTempData); } } - FloatVectorOperations::copy (outputData, outputTempData, static_cast (FFTSize + 1)); + FloatVectorOperations::copy (outputData, outputTempData, static_cast (fftSize + 1)); - convolutionProcessingAndAccumulate (buffersInputSegments.getReference (static_cast (currentSegment)).getWritePointer (0), - buffersImpulseSegments.getReference (0).getWritePointer (0), + convolutionProcessingAndAccumulate (inputSegmentData, + buffersImpulseSegments.front().getWritePointer (0), outputData); - // Inverse FFT updateSymmetricFrequencyDomainData (outputData); - FFTobject->performRealOnlyInverseTransform (outputData); + fftObject->performRealOnlyInverseTransform (outputData); // Add overlap - for (size_t i = 0; i < numSamplesToProcess; ++i) - output[i + numSamplesProcessed] = outputData[inputDataPos + i] + overlapData[inputDataPos + i]; + FloatVectorOperations::add (&output[numSamplesProcessed], &outputData[inputDataPos], &overlapData[inputDataPos], (int) numSamplesToProcess); // Input buffer full => Next block inputDataPos += numSamplesToProcess; @@ -243,17 +280,16 @@ struct ConvolutionEngine if (inputDataPos == blockSize) { // Input buffer is empty again now - FloatVectorOperations::fill (inputData, 0.0f, static_cast (FFTSize)); + FloatVectorOperations::fill (inputData, 0.0f, static_cast (fftSize)); inputDataPos = 0; // Extra step for segSize > blockSize - FloatVectorOperations::add (&(outputData[blockSize]), &(overlapData[blockSize]), static_cast (FFTSize - 2 * blockSize)); + FloatVectorOperations::add (&(outputData[blockSize]), &(overlapData[blockSize]), static_cast (fftSize - 2 * blockSize)); // Save the overlap - FloatVectorOperations::copy (overlapData, &(outputData[blockSize]), static_cast (FFTSize - blockSize)); + FloatVectorOperations::copy (overlapData, &(outputData[blockSize]), static_cast (fftSize - blockSize)); - // Update current segment currentSegment = (currentSegment > 0) ? (currentSegment - 1) : (numInputSegments - 1); } @@ -261,24 +297,102 @@ struct ConvolutionEngine } } - /** After each FFT, this function is called to allow convolution to be performed with only 4 SIMD functions calls. */ + void processSamplesWithAddedLatency (const float* input, float* output, size_t numSamples) + { + // Overlap-add, zero latency convolution algorithm with uniform partitioning + size_t numSamplesProcessed = 0; + + auto indexStep = numInputSegments / numSegments; + + auto* inputData = bufferInput.getWritePointer (0); + auto* outputTempData = bufferTempOutput.getWritePointer (0); + auto* outputData = bufferOutput.getWritePointer (0); + auto* overlapData = bufferOverlap.getWritePointer (0); + + while (numSamplesProcessed < numSamples) + { + auto numSamplesToProcess = jmin (numSamples - numSamplesProcessed, blockSize - inputDataPos); + + FloatVectorOperations::copy (inputData + inputDataPos, input + numSamplesProcessed, static_cast (numSamplesToProcess)); + + FloatVectorOperations::copy (output + numSamplesProcessed, outputData + inputDataPos, static_cast (numSamplesToProcess)); + + numSamplesProcessed += numSamplesToProcess; + inputDataPos += numSamplesToProcess; + + // processing itself when needed (with latency) + if (inputDataPos == blockSize) + { + // Copy input data in input segment + auto* inputSegmentData = buffersInputSegments[currentSegment].getWritePointer (0); + FloatVectorOperations::copy (inputSegmentData, inputData, static_cast (fftSize)); + + fftObject->performRealOnlyForwardTransform (inputSegmentData); + prepareForConvolution (inputSegmentData); + + // Complex multiplication + FloatVectorOperations::fill (outputTempData, 0, static_cast (fftSize + 1)); + + auto index = currentSegment; + + for (size_t i = 1; i < numSegments; ++i) + { + index += indexStep; + + if (index >= numInputSegments) + index -= numInputSegments; + + convolutionProcessingAndAccumulate (buffersInputSegments[index].getWritePointer (0), + buffersImpulseSegments[i].getWritePointer (0), + outputTempData); + } + + FloatVectorOperations::copy (outputData, outputTempData, static_cast (fftSize + 1)); + + convolutionProcessingAndAccumulate (inputSegmentData, + buffersImpulseSegments.front().getWritePointer (0), + outputData); + + updateSymmetricFrequencyDomainData (outputData); + fftObject->performRealOnlyInverseTransform (outputData); + + // Add overlap + FloatVectorOperations::add (outputData, overlapData, static_cast (blockSize)); + + // Input buffer is empty again now + FloatVectorOperations::fill (inputData, 0.0f, static_cast (fftSize)); + + // Extra step for segSize > blockSize + FloatVectorOperations::add (&(outputData[blockSize]), &(overlapData[blockSize]), static_cast (fftSize - 2 * blockSize)); + + // Save the overlap + FloatVectorOperations::copy (overlapData, &(outputData[blockSize]), static_cast (fftSize - blockSize)); + + currentSegment = (currentSegment > 0) ? (currentSegment - 1) : (numInputSegments - 1); + + inputDataPos = 0; + } + } + } + + // After each FFT, this function is called to allow convolution to be performed with only 4 SIMD functions calls. void prepareForConvolution (float *samples) noexcept { - auto FFTSizeDiv2 = FFTSize / 2; + auto FFTSizeDiv2 = fftSize / 2; for (size_t i = 0; i < FFTSizeDiv2; i++) - samples[i] = samples[2 * i]; + samples[i] = samples[i << 1]; samples[FFTSizeDiv2] = 0; for (size_t i = 1; i < FFTSizeDiv2; i++) - samples[i + FFTSizeDiv2] = -samples[2 * (FFTSize - i) + 1]; + samples[i + FFTSizeDiv2] = -samples[((fftSize - i) << 1) + 1]; } - /** Does the convolution operation itself only on half of the frequency domain samples. */ + // Does the convolution operation itself only on half of the frequency domain samples. void convolutionProcessingAndAccumulate (const float *input, const float *impulse, float *output) { - auto FFTSizeDiv2 = FFTSize / 2; + auto FFTSizeDiv2 = fftSize / 2; FloatVectorOperations::addWithMultiply (output, input, impulse, static_cast (FFTSizeDiv2)); FloatVectorOperations::subtractWithMultiply (output, &(input[FFTSizeDiv2]), &(impulse[FFTSizeDiv2]), static_cast (FFTSizeDiv2)); @@ -286,929 +400,759 @@ struct ConvolutionEngine FloatVectorOperations::addWithMultiply (&(output[FFTSizeDiv2]), input, &(impulse[FFTSizeDiv2]), static_cast (FFTSizeDiv2)); FloatVectorOperations::addWithMultiply (&(output[FFTSizeDiv2]), &(input[FFTSizeDiv2]), impulse, static_cast (FFTSizeDiv2)); - output[FFTSize] += input[FFTSize] * impulse[FFTSize]; + output[fftSize] += input[fftSize] * impulse[fftSize]; } - /** Undo the re-organization of samples from the function prepareForConvolution. - Then, takes the conjugate of the frequency domain first half of samples, to fill the - second half, so that the inverse transform will return real samples in the time domain. - */ + // Undoes the re-organization of samples from the function prepareForConvolution. + // Then takes the conjugate of the frequency domain first half of samples to fill the + // second half, so that the inverse transform will return real samples in the time domain. void updateSymmetricFrequencyDomainData (float* samples) noexcept { - auto FFTSizeDiv2 = FFTSize / 2; + auto FFTSizeDiv2 = fftSize / 2; for (size_t i = 1; i < FFTSizeDiv2; i++) { - samples[2 * (FFTSize - i)] = samples[i]; - samples[2 * (FFTSize - i) + 1] = -samples[FFTSizeDiv2 + i]; + samples[(fftSize - i) << 1] = samples[i]; + samples[((fftSize - i) << 1) + 1] = -samples[FFTSizeDiv2 + i]; } samples[1] = 0.f; for (size_t i = 1; i < FFTSizeDiv2; i++) { - samples[2 * i] = samples[2 * (FFTSize - i)]; - samples[2 * i + 1] = -samples[2 * (FFTSize - i) + 1]; + samples[i << 1] = samples[(fftSize - i) << 1]; + samples[(i << 1) + 1] = -samples[((fftSize - i) << 1) + 1]; } } //============================================================================== - std::unique_ptr FFTobject; - - size_t FFTSize = 0; - size_t currentSegment = 0, numInputSegments = 0, numSegments = 0, blockSize = 0, inputDataPos = 0; + const size_t blockSize; + const size_t fftSize; + const std::unique_ptr fftObject; + const size_t numSegments; + const size_t numInputSegments; + size_t currentSegment = 0, inputDataPos = 0; AudioBuffer bufferInput, bufferOutput, bufferTempOutput, bufferOverlap; - Array> buffersInputSegments, buffersImpulseSegments; - - bool isReady = false; - - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConvolutionEngine) + std::vector> buffersInputSegments, buffersImpulseSegments; }; - - //============================================================================== -/** Manages all the changes requested by the main convolution engine, to minimize - the number of calls of the convolution engine initialization, and the potential - consequences of multiple quick calls to the function Convolution::loadImpulseResponse. -*/ -struct Convolution::Pimpl : private Thread +class MultichannelEngine { - enum class ChangeRequest +public: + MultichannelEngine (const AudioBuffer& buf, + int maxBlockSize, + int maxBufferSize, + Convolution::NonUniform headSizeIn, + bool isZeroDelayIn) + : tailBuffer (1, maxBlockSize), + latency (isZeroDelayIn ? 0 : maxBufferSize), + irSize (buf.getNumSamples()), + blockSize (maxBlockSize), + isZeroDelay (isZeroDelayIn) { - changeEngine = 0, - changeSampleRate, - changeMaximumBufferSize, - changeSource, - changeImpulseResponseSize, - changeStereo, - changeTrimming, - changeNormalisation, - changeIgnore, - numChangeRequestTypes - }; - - using SourceType = ConvolutionEngine::ProcessingInformation::SourceType; + constexpr auto numChannels = 2; - //============================================================================== - Pimpl() : Thread ("Convolution"), abstractFifo (fifoSize) - { - abstractFifo.reset(); - fifoRequestsType.resize (fifoSize); - fifoRequestsParameter.resize (fifoSize); - - requestsType.resize (fifoSize); - requestsParameter.resize (fifoSize); + const auto makeEngine = [&] (int channel, int offset, int length, uint32 thisBlockSize) + { + return std::make_unique (buf.getReadPointer (jmin (buf.getNumChannels() - 1, channel), offset), + length, + static_cast (thisBlockSize)); + }; - for (auto i = 0; i < 4; ++i) - engines.add (new ConvolutionEngine()); + if (headSizeIn.headSizeInSamples == 0) + { + for (int i = 0; i < numChannels; ++i) + head.emplace_back (makeEngine (i, 0, buf.getNumSamples(), static_cast (maxBufferSize))); + } + else + { + const auto size = jmin (buf.getNumSamples(), headSizeIn.headSizeInSamples); - currentInfo.maximumBufferSize = 0; - currentInfo.buffer = &impulseResponse; + for (int i = 0; i < numChannels; ++i) + head.emplace_back (makeEngine (i, 0, size, static_cast (maxBufferSize))); - temporaryBuffer.setSize (2, static_cast (maximumTimeInSamples), false, false, true); - impulseResponseOriginal.setSize (2, static_cast (maximumTimeInSamples), false, false, true); - impulseResponse.setSize (2, static_cast (maximumTimeInSamples), false, false, true); - } + const auto tailBufferSize = static_cast (headSizeIn.headSizeInSamples + (isZeroDelay ? 0 : maxBufferSize)); - ~Pimpl() override - { - stopThread (10000); + if (size != buf.getNumSamples()) + for (int i = 0; i < numChannels; ++i) + tail.emplace_back (makeEngine (i, size, buf.getNumSamples() - size, tailBufferSize)); + } } - //============================================================================== - /** Inits the size of the interpolation buffer. */ - void initProcessing (int maximumBufferSize) + void reset() { - stopThread (1000); + for (const auto& e : head) + e->reset(); - interpolationBuffer.setSize (1, maximumBufferSize, false, false, true); - mustInterpolate = false; + for (const auto& e : tail) + e->reset(); } - //============================================================================== - /** Adds a new change request. */ - void addToFifo (ChangeRequest type, juce::var parameter) + void processSamples (const AudioBlock& input, AudioBlock& output) { - int start1, size1, start2, size2; - abstractFifo.prepareToWrite (1, start1, size1, start2, size2); + const auto numChannels = jmin (head.size(), input.getNumChannels(), output.getNumChannels()); + const auto numSamples = jmin (input.getNumSamples(), output.getNumSamples()); - // If you hit this assertion then you have requested more impulse response - // changes than the Convolution class can handle. - jassert (size1 + size2 > 0); + const AudioBlock fullTailBlock (tailBuffer); + const auto tailBlock = fullTailBlock.getSubBlock (0, (size_t) numSamples); - if (size1 > 0) - { - fifoRequestsType.setUnchecked (start1, type); - fifoRequestsParameter.setUnchecked (start1, parameter); - } + const auto isUniform = tail.empty(); - if (size2 > 0) + for (size_t channel = 0; channel < numChannels; ++channel) { - fifoRequestsType.setUnchecked (start2, type); - fifoRequestsParameter.setUnchecked (start2, parameter); - } - - abstractFifo.finishedWrite (size1 + size2); - } - - /** Adds a new array of change requests. */ - void addToFifo (ChangeRequest* types, juce::var* parameters, int numEntries) - { - int start1, size1, start2, size2; - abstractFifo.prepareToWrite (numEntries, start1, size1, start2, size2); - - // If you hit this assertion then you have requested more impulse response - // changes than the Convolution class can handle. - jassert (numEntries > 0 && size1 + size2 > 0); + if (! isUniform) + tail[channel]->processSamplesWithAddedLatency (input.getChannelPointer (channel), + tailBlock.getChannelPointer (0), + numSamples); + + if (isZeroDelay) + head[channel]->processSamples (input.getChannelPointer (channel), + output.getChannelPointer (channel), + numSamples); + else + head[channel]->processSamplesWithAddedLatency (input.getChannelPointer (channel), + output.getChannelPointer (channel), + numSamples); - if (size1 > 0) - { - for (auto i = 0; i < size1; ++i) - { - fifoRequestsType.setUnchecked (start1 + i, types[i]); - fifoRequestsParameter.setUnchecked (start1 + i, parameters[i]); - } + if (! isUniform) + output.getSingleChannelBlock (channel) += tailBlock; } - if (size2 > 0) - { - for (auto i = 0; i < size2; ++i) - { - fifoRequestsType.setUnchecked (start2 + i, types[i + size1]); - fifoRequestsParameter.setUnchecked (start2 + i, parameters[i + size1]); - } - } + const auto numOutputChannels = output.getNumChannels(); - abstractFifo.finishedWrite (size1 + size2); + for (auto i = numChannels; i < numOutputChannels; ++i) + output.getSingleChannelBlock (i).copyFrom (output.getSingleChannelBlock (0)); } - /** Reads requests from the fifo. */ - void readFromFifo (ChangeRequest& type, juce::var& parameter) - { - int start1, size1, start2, size2; - abstractFifo.prepareToRead (1, start1, size1, start2, size2); + int getIRSize() const noexcept { return irSize; } + int getLatency() const noexcept { return latency; } + int getBlockSize() const noexcept { return blockSize; } - if (size1 > 0) - { - type = fifoRequestsType[start1]; - parameter = fifoRequestsParameter[start1]; - } +private: + std::vector> head, tail; + AudioBuffer tailBuffer; - if (size2 > 0) - { - type = fifoRequestsType[start2]; - parameter = fifoRequestsParameter[start2]; - } + const int latency; + const int irSize; + const int blockSize; + const bool isZeroDelay; +}; - abstractFifo.finishedRead (size1 + size2); - } +static AudioBuffer fixNumChannels (const AudioBuffer& buf, Convolution::Stereo stereo) +{ + const auto numChannels = jmin (buf.getNumChannels(), stereo == Convolution::Stereo::yes ? 2 : 1); + const auto numSamples = buf.getNumSamples(); - /** Returns the number of requests that still need to be processed. */ - int getNumRemainingEntries() const noexcept - { - return abstractFifo.getNumReady(); - } + AudioBuffer result (numChannels, buf.getNumSamples()); - //============================================================================== - /** This function processes all the change requests to remove all the the - redundant ones, and to tell what kind of initialization must be done. + for (auto channel = 0; channel != numChannels; ++channel) + result.copyFrom (channel, 0, buf.getReadPointer (channel), numSamples); - Depending on the results, the convolution engines might be reset, or - simply updated, or they might not need any change at all. - */ - void processFifo() + if (result.getNumSamples() == 0 || result.getNumChannels() == 0) { - if (getNumRemainingEntries() == 0 || isThreadRunning() || mustInterpolate) - return; - - auto numRequests = 0; + result.setSize (1, 1); + result.setSample (0, 0, 1.0f); + } - // retrieve the information from the FIFO for processing - while (getNumRemainingEntries() > 0 && numRequests < fifoSize) - { - ChangeRequest type = ChangeRequest::changeEngine; - juce::var parameter; + return result; +} - readFromFifo (type, parameter); +static AudioBuffer trimImpulseResponse (const AudioBuffer& buf) +{ + const auto thresholdTrim = Decibels::decibelsToGain (-80.0f); - requestsType.setUnchecked (numRequests, type); - requestsParameter.setUnchecked (numRequests, parameter); + const auto numChannels = buf.getNumChannels(); + const auto numSamples = buf.getNumSamples(); - numRequests++; - } + std::ptrdiff_t offsetBegin = numSamples; + std::ptrdiff_t offsetEnd = numSamples; - // remove any useless messages - for (auto i = 0; i < (int) ChangeRequest::numChangeRequestTypes; ++i) + for (auto channel = 0; channel < numChannels; ++channel) + { + const auto indexAboveThreshold = [&] (auto begin, auto end) { - bool exists = false; - - for (auto n = numRequests; --n >= 0;) + return std::distance (begin, std::find_if (begin, end, [&] (float sample) { - if (requestsType[n] == (ChangeRequest) i) - { - if (! exists) - exists = true; - else - requestsType.setUnchecked (n, ChangeRequest::changeIgnore); - } - } - } + return std::abs (sample) >= thresholdTrim; + })); + }; - changeLevel = 0; + const auto channelBegin = buf.getReadPointer (channel); + const auto channelEnd = channelBegin + numSamples; + const auto itStart = indexAboveThreshold (channelBegin, channelEnd); + const auto itEnd = indexAboveThreshold (std::make_reverse_iterator (channelEnd), + std::make_reverse_iterator (channelBegin)); - for (auto n = 0; n < numRequests; ++n) - { - switch (requestsType[n]) - { - case ChangeRequest::changeEngine: - changeLevel = 3; - break; + offsetBegin = jmin (offsetBegin, itStart); + offsetEnd = jmin (offsetEnd, itEnd); + } - case ChangeRequest::changeSampleRate: - { - double newSampleRate = requestsParameter[n]; + if (offsetBegin == numSamples) + { + auto result = AudioBuffer (numChannels, 1); + result.clear(); + return result; + } - if (currentInfo.sampleRate != newSampleRate) - changeLevel = 3; + const auto newLength = jmax (1, numSamples - static_cast (offsetBegin + offsetEnd)); - currentInfo.sampleRate = newSampleRate; - } - break; + AudioBuffer result (numChannels, newLength); - case ChangeRequest::changeMaximumBufferSize: - { - int newMaximumBufferSize = requestsParameter[n]; + for (auto channel = 0; channel < numChannels; ++channel) + { + result.copyFrom (channel, + 0, + buf.getReadPointer (channel, static_cast (offsetBegin)), + result.getNumSamples()); + } - if (currentInfo.maximumBufferSize != (size_t) newMaximumBufferSize) - changeLevel = 3; + return result; +} - currentInfo.maximumBufferSize = (size_t) newMaximumBufferSize; - } - break; +static float calculateNormalisationFactor (float sumSquaredMagnitude) +{ + if (sumSquaredMagnitude < 1e-8f) + return 1.0f; - case ChangeRequest::changeSource: - { - auto* arrayParameters = requestsParameter[n].getArray(); - auto newSourceType = static_cast (static_cast (arrayParameters->getUnchecked (0))); - - if (currentInfo.sourceType != newSourceType) - changeLevel = jmax (2, changeLevel); - - if (newSourceType == SourceType::sourceBinaryData) - { - auto& prm = arrayParameters->getRawDataPointer()[1]; - auto* newMemoryBlock = prm.getBinaryData(); - - auto* newPtr = newMemoryBlock->getData(); - auto newSize = (int) newMemoryBlock->getSize(); - - if (currentInfo.sourceData != newPtr || currentInfo.sourceDataSize != newSize) - changeLevel = jmax (2, changeLevel); - - currentInfo.sourceType = SourceType::sourceBinaryData; - currentInfo.sourceData = newPtr; - currentInfo.sourceDataSize = newSize; - currentInfo.fileImpulseResponse = File(); - } - else if (newSourceType == SourceType::sourceAudioFile) - { - File newFile (arrayParameters->getUnchecked (1).toString()); - - if (currentInfo.fileImpulseResponse != newFile) - changeLevel = jmax (2, changeLevel); - - currentInfo.sourceType = SourceType::sourceAudioFile; - currentInfo.fileImpulseResponse = newFile; - currentInfo.sourceData = nullptr; - currentInfo.sourceDataSize = 0; - } - else if (newSourceType == SourceType::sourceAudioBuffer) - { - double originalSampleRate (arrayParameters->getUnchecked (1)); - changeLevel = jmax (2, changeLevel); - - currentInfo.sourceType = SourceType::sourceAudioBuffer; - currentInfo.originalSampleRate = originalSampleRate; - currentInfo.fileImpulseResponse = File(); - currentInfo.sourceData = nullptr; - currentInfo.sourceDataSize = 0; - } - } - break; + return 0.125f / std::sqrt (sumSquaredMagnitude); +} - case ChangeRequest::changeImpulseResponseSize: - { - int64 newSize = requestsParameter[n]; +static void normaliseImpulseResponse (AudioBuffer& buf) +{ + const auto numChannels = buf.getNumChannels(); + const auto numSamples = buf.getNumSamples(); + const auto channelPtrs = buf.getArrayOfWritePointers(); - if (currentInfo.wantedSize != newSize) - changeLevel = jmax (1, changeLevel); + const auto maxSumSquaredMag = std::accumulate (channelPtrs, channelPtrs + numChannels, 0.0f, [numSamples] (auto max, auto* channel) + { + return jmax (max, std::accumulate (channel, channel + numSamples, 0.0f, [] (auto sum, auto samp) + { + return sum + (samp * samp); + })); + }); - currentInfo.wantedSize = newSize; - } - break; + const auto normalisationFactor = calculateNormalisationFactor (maxSumSquaredMag); - case ChangeRequest::changeStereo: - { - bool newWantsStereo = requestsParameter[n]; + std::for_each (channelPtrs, channelPtrs + numChannels, [normalisationFactor, numSamples] (auto* channel) + { + FloatVectorOperations::multiply (channel, normalisationFactor, numSamples); + }); +} - if (currentInfo.wantsStereo != newWantsStereo) - changeLevel = jmax (0, changeLevel); +static AudioBuffer resampleImpulseResponse (const AudioBuffer& buf, + const double srcSampleRate, + const double destSampleRate) +{ + if (approximatelyEqual (srcSampleRate, destSampleRate)) + return buf; - currentInfo.wantsStereo = newWantsStereo; - } - break; + const auto factorReading = srcSampleRate / destSampleRate; - case ChangeRequest::changeTrimming: - { - bool newWantsTrimming = requestsParameter[n]; + AudioBuffer original = buf; + MemoryAudioSource memorySource (original, false); + ResamplingAudioSource resamplingSource (&memorySource, false, buf.getNumChannels()); - if (currentInfo.wantsTrimming != newWantsTrimming) - changeLevel = jmax (1, changeLevel); + const auto finalSize = roundToInt (jmax (1.0, buf.getNumSamples() / factorReading)); + resamplingSource.setResamplingRatio (factorReading); + resamplingSource.prepareToPlay (finalSize, srcSampleRate); - currentInfo.wantsTrimming = newWantsTrimming; - } - break; + AudioBuffer result (buf.getNumChannels(), finalSize); + resamplingSource.getNextAudioBlock ({ &result, 0, result.getNumSamples() }); - case ChangeRequest::changeNormalisation: - { - bool newWantsNormalisation = requestsParameter[n]; + return result; +} - if (currentInfo.wantsNormalisation != newWantsNormalisation) - changeLevel = jmax (1, changeLevel); +//============================================================================== +template +class TryLockedPtr +{ +public: + void set (std::unique_ptr p) + { + const SpinLock::ScopedLockType lock (mutex); + ptr = std::move (p); + } - currentInfo.wantsNormalisation = newWantsNormalisation; - } - break; + std::unique_ptr get() + { + const SpinLock::ScopedTryLockType lock (mutex); + return lock.isLocked() ? std::move (ptr) : nullptr; + } - case ChangeRequest::changeIgnore: - break; +private: + std::unique_ptr ptr; + SpinLock mutex; +}; - default: - jassertfalse; - break; - } - } +struct BufferWithSampleRate +{ + BufferWithSampleRate() = default; - if (currentInfo.sourceType == SourceType::sourceNone) - { - currentInfo.sourceType = SourceType::sourceAudioBuffer; + BufferWithSampleRate (AudioBuffer&& bufferIn, double sampleRateIn) + : buffer (std::move (bufferIn)), sampleRate (sampleRateIn) {} - if (currentInfo.sampleRate == 0) - currentInfo.sampleRate = 44100; + AudioBuffer buffer; + double sampleRate = 0.0; +}; - if (currentInfo.maximumBufferSize == 0) - currentInfo.maximumBufferSize = 128; +static BufferWithSampleRate loadStreamToBuffer (std::unique_ptr stream, size_t maxLength) +{ + AudioFormatManager manager; + manager.registerBasicFormats(); + std::unique_ptr formatReader (manager.createReaderFor (std::move (stream))); - currentInfo.originalSampleRate = currentInfo.sampleRate; - currentInfo.wantedSize = 1; - currentInfo.fileImpulseResponse = File(); - currentInfo.sourceData = nullptr; - currentInfo.sourceDataSize = 0; + if (formatReader == nullptr) + return {}; - AudioBuffer newBuffer; - newBuffer.setSize (1, 1); - newBuffer.setSample (0, 0, 1.f); + const auto fileLength = static_cast (formatReader->lengthInSamples); + const auto lengthToLoad = maxLength == 0 ? fileLength : jmin (maxLength, fileLength); - copyBufferToTemporaryLocation (newBuffer); - } + BufferWithSampleRate result { { jlimit (1, 2, static_cast (formatReader->numChannels)), + static_cast (lengthToLoad) }, + formatReader->sampleRate }; - // action depending on the change level - if (changeLevel == 3) - { - loadImpulseResponse(); - processImpulseResponse(); - initializeConvolutionEngines(); - } - else if (changeLevel > 0) - { - startThread(); - } - } + formatReader->read (result.buffer.getArrayOfWritePointers(), + result.buffer.getNumChannels(), + 0, + result.buffer.getNumSamples()); - //============================================================================== - /** This function copies a buffer to a temporary location, so that any external - audio source can be processed then in the dedicated thread. - */ - void copyBufferToTemporaryLocation (dsp::AudioBlock block) - { - const SpinLock::ScopedLockType sl (processLock); + return result; +} - currentInfo.originalNumChannels = (block.getNumChannels() > 1 ? 2 : 1); - currentInfo.originalSize = (int) jmin ((size_t) maximumTimeInSamples, block.getNumSamples()); +// This class caches the data required to build a new convolution engine +// (in particular, impulse response data and a ProcessSpec). +// Calls to `setProcessSpec` and `setImpulseResponse` construct a +// new engine, which can be retrieved by calling `getEngine`. +class ConvolutionEngineFactory +{ +public: + ConvolutionEngineFactory (Convolution::Latency requiredLatency, + Convolution::NonUniform requiredHeadSize) + : latency { (requiredLatency.latencyInSamples <= 0) ? 0 : jmax (64, nextPowerOfTwo (requiredLatency.latencyInSamples)) }, + headSize { (requiredHeadSize.headSizeInSamples <= 0) ? 0 : jmax (64, nextPowerOfTwo (requiredHeadSize.headSizeInSamples)) }, + shouldBeZeroLatency (requiredLatency.latencyInSamples == 0) + {} + + // It is safe to call this method simultaneously with other public + // member functions. + void setProcessSpec (const ProcessSpec& spec) + { + const std::lock_guard lock (mutex); + processSpec = spec; - for (auto channel = 0; channel < currentInfo.originalNumChannels; ++channel) - temporaryBuffer.copyFrom (channel, 0, block.getChannelPointer ((size_t) channel), (int) currentInfo.originalSize); + engine.set (makeEngine()); } - //============================================================================== - /** Resets the convolution engines states. */ - void reset() + // It is safe to call this method simultaneously with other public + // member functions. + void setImpulseResponse (BufferWithSampleRate&& buf, + Convolution::Stereo stereo, + Convolution::Trim trim, + Convolution::Normalise normalise) { - for (auto* e : engines) - e->reset(); + const std::lock_guard lock (mutex); + wantsNormalise = normalise; + originalSampleRate = buf.sampleRate; - mustInterpolate = false; + impulseResponse = [&] + { + auto corrected = fixNumChannels (buf.buffer, stereo); + return trim == Convolution::Trim::yes ? trimImpulseResponse (corrected) : corrected; + }(); - processFifo(); + engine.set (makeEngine()); } - /** Convolution processing handling interpolation between previous and new states - of the convolution engines. - */ - void processSamples (const AudioBlock& input, AudioBlock& output) - { - processFifo(); + // Returns the most recently-created engine, or nullptr + // if there is no pending engine, or if the engine is currently + // being updated by one of the setter methods. + // It is safe to call this simultaneously with other public + // member functions. + std::unique_ptr getEngine() { return engine.get(); } - size_t numChannels = jmin (input.getNumChannels(), (size_t) (currentInfo.wantsStereo ? 2 : 1)); - size_t numSamples = jmin (input.getNumSamples(), output.getNumSamples()); +private: + std::unique_ptr makeEngine() + { + auto resampled = resampleImpulseResponse (impulseResponse, originalSampleRate, processSpec.sampleRate); - if (mustInterpolate == false) - { - for (size_t channel = 0; channel < numChannels; ++channel) - engines[(int) channel]->processSamples (input.getChannelPointer (channel), output.getChannelPointer (channel), numSamples); - } + if (wantsNormalise == Convolution::Normalise::yes) + normaliseImpulseResponse (resampled); else - { - auto interpolated = dsp::AudioBlock (interpolationBuffer).getSubBlock (0, numSamples); + resampled.applyGain ((float) (originalSampleRate / processSpec.sampleRate)); - for (size_t channel = 0; channel < numChannels; ++channel) - { - auto&& buffer = output.getSingleChannelBlock (channel); + const auto currentLatency = jmax (processSpec.maximumBlockSize, (uint32) latency.latencyInSamples); + const auto maxBufferSize = shouldBeZeroLatency ? static_cast (processSpec.maximumBlockSize) + : nextPowerOfTwo (static_cast (currentLatency)); - interpolationBuffer.copyFrom (0, 0, input.getChannelPointer (channel), (int) numSamples); + return std::make_unique (resampled, + processSpec.maximumBlockSize, + maxBufferSize, + headSize, + shouldBeZeroLatency); + } - engines[(int) channel]->processSamples (input.getChannelPointer (channel), buffer.getChannelPointer (0), numSamples); - changeVolumes[channel].applyGain (buffer.getChannelPointer (0), (int) numSamples); + static AudioBuffer makeImpulseBuffer() + { + AudioBuffer result (1, 1); + result.setSample (0, 0, 1.0f); + return result; + } - auto* interPtr = interpolationBuffer.getWritePointer (0); - engines[(int) channel + 2]->processSamples (interPtr, interPtr, numSamples); - changeVolumes[channel + 2].applyGain (interPtr, (int) numSamples); + ProcessSpec processSpec { 44100.0, 128, 2 }; + AudioBuffer impulseResponse = makeImpulseBuffer(); + double originalSampleRate = processSpec.sampleRate; + Convolution::Normalise wantsNormalise = Convolution::Normalise::no; + const Convolution::Latency latency; + const Convolution::NonUniform headSize; + const bool shouldBeZeroLatency; - buffer += interpolated; - } + TryLockedPtr engine; - if (input.getNumChannels() > 1 && currentInfo.wantsStereo == false) - { - auto&& buffer = output.getSingleChannelBlock (1); + mutable std::mutex mutex; +}; - changeVolumes[1].applyGain (buffer.getChannelPointer (0), (int) numSamples); - changeVolumes[3].applyGain (buffer.getChannelPointer (0), (int) numSamples); - } +static void setImpulseResponse (ConvolutionEngineFactory& factory, + const void* sourceData, + size_t sourceDataSize, + Convolution::Stereo stereo, + Convolution::Trim trim, + size_t size, + Convolution::Normalise normalise) +{ + factory.setImpulseResponse (loadStreamToBuffer (std::make_unique (sourceData, sourceDataSize, false), size), + stereo, trim, normalise); +} - if (changeVolumes[0].isSmoothing() == false) - { - mustInterpolate = false; +static void setImpulseResponse (ConvolutionEngineFactory& factory, + const File& fileImpulseResponse, + Convolution::Stereo stereo, + Convolution::Trim trim, + size_t size, + Convolution::Normalise normalise) +{ + factory.setImpulseResponse (loadStreamToBuffer (std::make_unique (fileImpulseResponse), size), + stereo, trim, normalise); +} - for (auto channel = 0; channel < 2; ++channel) - engines[channel]->copyStateFromOtherEngine (*engines[channel + 2]); - } - } +// This class acts as a destination for convolution engines which are loaded on +// a background thread. - if (input.getNumChannels() > 1 && currentInfo.wantsStereo == false) - output.getSingleChannelBlock (1).copyFrom (output.getSingleChannelBlock (0)); +// Deriving from `enable_shared_from_this` allows us to capture a reference to +// this object when adding commands to the background message queue. +// That way, we can avoid dangling references in the background thread in the case +// that a Convolution instance is deleted before the background message queue. +class ConvolutionEngineQueue final : public std::enable_shared_from_this +{ +public: + ConvolutionEngineQueue (BackgroundMessageQueue& queue, + Convolution::Latency latencyIn, + Convolution::NonUniform headSizeIn) + : messageQueue (queue), factory (latencyIn, headSizeIn) {} + + void loadImpulseResponse (AudioBuffer&& buffer, + double sr, + Convolution::Stereo stereo, + Convolution::Trim trim, + Convolution::Normalise normalise) + { + callLater ([b = std::move (buffer), sr, stereo, trim, normalise] (ConvolutionEngineFactory& f) mutable + { + f.setImpulseResponse ({ std::move (b), sr }, stereo, trim, normalise); + }); } - //============================================================================== - const int64 maximumTimeInSamples = 10 * 96000; - -private: - //============================================================================== - /** This the thread run function which does the preparation of data depending - on the requested change level. - */ - void run() override + void loadImpulseResponse (const void* sourceData, + size_t sourceDataSize, + Convolution::Stereo stereo, + Convolution::Trim trim, + size_t size, + Convolution::Normalise normalise) { - if (changeLevel == 2) + callLater ([sourceData, sourceDataSize, stereo, trim, size, normalise] (ConvolutionEngineFactory& f) mutable { - loadImpulseResponse(); - - if (isThreadRunning() && threadShouldExit()) - return; - } - - processImpulseResponse(); - - if (isThreadRunning() && threadShouldExit()) - return; - - initializeConvolutionEngines(); + setImpulseResponse (f, sourceData, sourceDataSize, stereo, trim, size, normalise); + }); } - /** Loads the impulse response from the requested audio source. */ - void loadImpulseResponse() + void loadImpulseResponse (const File& fileImpulseResponse, + Convolution::Stereo stereo, + Convolution::Trim trim, + size_t size, + Convolution::Normalise normalise) { - if (currentInfo.sourceType == SourceType::sourceBinaryData) + callLater ([fileImpulseResponse, stereo, trim, size, normalise] (ConvolutionEngineFactory& f) mutable { - if (! (copyAudioStreamInAudioBuffer (new MemoryInputStream (currentInfo.sourceData, (size_t) currentInfo.sourceDataSize, false)))) - return; - } - else if (currentInfo.sourceType == SourceType::sourceAudioFile) - { - if (! (copyAudioStreamInAudioBuffer (new FileInputStream (currentInfo.fileImpulseResponse)))) - return; - } - else if (currentInfo.sourceType == SourceType::sourceAudioBuffer) - { - copyBufferFromTemporaryLocation(); - } + setImpulseResponse (f, fileImpulseResponse, stereo, trim, size, normalise); + }); } - /** Processes the impulse response data with the requested treatments - and resampling if needed. - */ - void processImpulseResponse() + void prepare (const ProcessSpec& spec) { - trimAndResampleImpulseResponse (currentInfo.originalNumChannels, currentInfo.originalSampleRate, currentInfo.wantsTrimming); + factory.setProcessSpec (spec); + } - if (isThreadRunning() && threadShouldExit()) + // Call this regularly to try to resend any pending message. + // This allows us to always apply the most recently requested + // state (eventually), even if the message queue fills up. + void postPendingCommand() + { + if (pendingCommand == nullptr) return; - if (currentInfo.wantsNormalisation) - { - if (currentInfo.originalNumChannels > 1) - { - normaliseImpulseResponse (currentInfo.buffer->getWritePointer (0), (int) currentInfo.finalSize, 1.0); - normaliseImpulseResponse (currentInfo.buffer->getWritePointer (1), (int) currentInfo.finalSize, 1.0); - } - else - { - normaliseImpulseResponse (currentInfo.buffer->getWritePointer (0), (int) currentInfo.finalSize, 1.0); - } - } - - if (currentInfo.originalNumChannels == 1) - currentInfo.buffer->copyFrom (1, 0, *currentInfo.buffer, 0, 0, (int) currentInfo.finalSize); + if (messageQueue.push (pendingCommand)) + pendingCommand = nullptr; } - /** Converts the data from an audio file into a stereo audio buffer of floats, and - performs resampling if necessary. - */ - bool copyAudioStreamInAudioBuffer (InputStream* stream) - { - AudioFormatManager manager; - manager.registerBasicFormats(); - std::unique_ptr formatReader (manager.createReaderFor (stream)); + std::unique_ptr getEngine() { return factory.getEngine(); } - if (formatReader != nullptr) +private: + template + void callLater (Fn&& fn) + { + // If there was already a pending command (because the queue was full) we'll end up deleting it here. + // Not much we can do about that! + pendingCommand = [weak = weakFromThis(), callback = std::forward (fn)]() mutable { - currentInfo.originalNumChannels = formatReader->numChannels > 1 ? 2 : 1; - currentInfo.originalSampleRate = formatReader->sampleRate; - currentInfo.originalSize = static_cast (jmin (maximumTimeInSamples, formatReader->lengthInSamples)); + if (auto t = weak.lock()) + callback (t->factory); + }; - impulseResponseOriginal.clear(); - formatReader->read (&(impulseResponseOriginal), 0, (int) currentInfo.originalSize, 0, true, currentInfo.originalNumChannels > 1); + postPendingCommand(); + } - return true; - } + std::weak_ptr weakFromThis() { return shared_from_this(); } - return false; - } + BackgroundMessageQueue& messageQueue; + ConvolutionEngineFactory factory; + BackgroundMessageQueue::IncomingCommand pendingCommand; +}; - /** Copies a buffer from a temporary location to the impulseResponseOriginal - buffer for the sourceAudioBuffer. - */ - void copyBufferFromTemporaryLocation() +class CrossoverMixer +{ +public: + void reset() { - const SpinLock::ScopedLockType sl (processLock); - - for (auto channel = 0; channel < currentInfo.originalNumChannels; ++channel) - impulseResponseOriginal.copyFrom (channel, 0, temporaryBuffer, channel, 0, (int) currentInfo.originalSize); + smoother.setCurrentAndTargetValue (1.0f); } - /** Trim and resample the impulse response if needed. */ - void trimAndResampleImpulseResponse (int numChannels, double srcSampleRate, bool mustTrim) + void prepare (const ProcessSpec& spec) { - auto thresholdTrim = Decibels::decibelsToGain (-80.0f); - auto indexStart = 0; - auto indexEnd = currentInfo.originalSize - 1; + smoother.reset (spec.sampleRate, 0.05); + smootherBuffer.setSize (1, static_cast (spec.maximumBlockSize)); + mixBuffer.setSize (static_cast (spec.numChannels), static_cast (spec.maximumBlockSize)); + reset(); + } - if (mustTrim) + template + void processSamples (const AudioBlock& input, + AudioBlock& output, + ProcessCurrent&& current, + ProcessPrevious&& previous, + NotifyDone&& notifyDone) + { + if (smoother.isSmoothing()) { - indexStart = currentInfo.originalSize - 1; - indexEnd = 0; + const auto numSamples = static_cast (input.getNumSamples()); - for (auto channel = 0; channel < numChannels; ++channel) - { - auto localIndexStart = 0; - auto localIndexEnd = currentInfo.originalSize - 1; - - auto* channelData = impulseResponseOriginal.getReadPointer (channel); + for (auto sample = 0; sample != numSamples; ++sample) + smootherBuffer.setSample (0, sample, smoother.getNextValue()); - while (localIndexStart < currentInfo.originalSize - 1 - && channelData[localIndexStart] <= thresholdTrim - && channelData[localIndexStart] >= -thresholdTrim) - ++localIndexStart; + AudioBlock mixBlock (mixBuffer); + mixBlock.clear(); + previous (input, mixBlock); - while (localIndexEnd >= 0 - && channelData[localIndexEnd] <= thresholdTrim - && channelData[localIndexEnd] >= -thresholdTrim) - --localIndexEnd; - - indexStart = jmin (indexStart, localIndexStart); - indexEnd = jmax (indexEnd, localIndexEnd); + for (size_t channel = 0; channel != output.getNumChannels(); ++channel) + { + FloatVectorOperations::multiply (mixBlock.getChannelPointer (channel), + smootherBuffer.getReadPointer (0), + numSamples); } - if (indexStart > 0) - { - for (auto channel = 0; channel < numChannels; ++channel) - { - auto* channelData = impulseResponseOriginal.getWritePointer (channel); + FloatVectorOperations::multiply (smootherBuffer.getWritePointer (0), -1.0f, numSamples); + FloatVectorOperations::add (smootherBuffer.getWritePointer (0), 1.0f, numSamples); - for (auto i = 0; i < indexEnd - indexStart + 1; ++i) - channelData[i] = channelData[i + indexStart]; + current (input, output); - for (auto i = indexEnd - indexStart + 1; i < currentInfo.originalSize - 1; ++i) - channelData[i] = 0.0f; - } + for (size_t channel = 0; channel != output.getNumChannels(); ++channel) + { + FloatVectorOperations::multiply (output.getChannelPointer (channel), + smootherBuffer.getReadPointer (0), + numSamples); + FloatVectorOperations::add (output.getChannelPointer (channel), + mixBlock.getChannelPointer (channel), + numSamples); } - } - - if (currentInfo.sampleRate == srcSampleRate) - { - // No resampling - currentInfo.finalSize = jmin (static_cast (currentInfo.wantedSize), indexEnd - indexStart + 1); - impulseResponse.clear(); - - for (auto channel = 0; channel < numChannels; ++channel) - impulseResponse.copyFrom (channel, 0, impulseResponseOriginal, channel, 0, (int) currentInfo.finalSize); + if (! smoother.isSmoothing()) + notifyDone(); } else { - // Resampling - auto factorReading = srcSampleRate / currentInfo.sampleRate; - currentInfo.finalSize = jmin (static_cast (currentInfo.wantedSize), roundToInt ((indexEnd - indexStart + 1) / factorReading)); - - impulseResponse.clear(); - - MemoryAudioSource memorySource (impulseResponseOriginal, false); - ResamplingAudioSource resamplingSource (&memorySource, false, (int) numChannels); - - resamplingSource.setResamplingRatio (factorReading); - resamplingSource.prepareToPlay ((int) currentInfo.finalSize, currentInfo.sampleRate); - - AudioSourceChannelInfo info; - info.startSample = 0; - info.numSamples = (int) currentInfo.finalSize; - info.buffer = &impulseResponse; - - resamplingSource.getNextAudioBlock (info); + current (input, output); } - - // Filling the second channel with the first if necessary - if (numChannels == 1) - impulseResponse.copyFrom (1, 0, impulseResponse, 0, 0, (int) currentInfo.finalSize); } - /** Normalisation of the impulse response based on its energy. */ - void normaliseImpulseResponse (float* samples, int numSamples, double factorResampling) const + void beginTransition() { - auto magnitude = 0.0f; + smoother.setCurrentAndTargetValue (1.0f); + smoother.setTargetValue (0.0f); + } - for (auto i = 0; i < numSamples; ++i) - magnitude += samples[i] * samples[i]; +private: + LinearSmoothedValue smoother; + AudioBuffer smootherBuffer; + AudioBuffer mixBuffer; +}; - auto magnitudeInv = 1.0f / (4.0f * std::sqrt (magnitude)) * 0.5f * static_cast (factorResampling); +using OptionalQueue = OptionalScopedPointer; - for (auto i = 0; i < numSamples; ++i) - samples[i] *= magnitudeInv; - } +class Convolution::Impl +{ +public: + Impl (Latency requiredLatency, + NonUniform requiredHeadSize, + OptionalQueue&& queue) + : messageQueue (std::move (queue)), + engineQueue (std::make_shared (*messageQueue->pimpl, + requiredLatency, + requiredHeadSize)) + {} - // ================================================================================================================ - /** Initializes the convolution engines depending on the provided sizes - and performs the FFT on the impulse responses. - */ - void initializeConvolutionEngines() + void reset() { - if (currentInfo.maximumBufferSize == 0) - return; - - if (changeLevel == 3) - { - for (auto i = 0; i < 2; ++i) - engines[i]->initializeConvolutionEngine (currentInfo, i); - - mustInterpolate = false; - } - else - { - for (auto i = 0; i < 2; ++i) - { - engines[i + 2]->initializeConvolutionEngine (currentInfo, i); - engines[i + 2]->reset(); + mixer.reset(); - if (isThreadRunning() && threadShouldExit()) - return; - } + if (currentEngine != nullptr) + currentEngine->reset(); - for (auto i = 0; i < 2; ++i) - { - changeVolumes[i].setTargetValue (1.0f); - changeVolumes[i].reset (currentInfo.sampleRate, 0.05); - changeVolumes[i].setTargetValue (0.0f); + destroyPreviousEngine(); + } - changeVolumes[i + 2].setTargetValue (0.0f); - changeVolumes[i + 2].reset (currentInfo.sampleRate, 0.05); - changeVolumes[i + 2].setTargetValue (1.0f); + void prepare (const ProcessSpec& spec) + { + messageQueue->pimpl->popAll(); + mixer.prepare (spec); + engineQueue->prepare (spec); - } + if (auto newEngine = engineQueue->getEngine()) + currentEngine = std::move (newEngine); - mustInterpolate = true; - } + previousEngine = nullptr; + jassert (currentEngine != nullptr); } + void processSamples (const AudioBlock& input, AudioBlock& output) + { + engineQueue->postPendingCommand(); + + if (previousEngine == nullptr) + installPendingEngine(); + + mixer.processSamples (input, + output, + [this] (const AudioBlock& in, AudioBlock& out) + { + currentEngine->processSamples (in, out); + }, + [this] (const AudioBlock& in, AudioBlock& out) + { + if (previousEngine != nullptr) + previousEngine->processSamples (in, out); + else + out.copyFrom (in); + }, + [this] { destroyPreviousEngine(); }); + } - //============================================================================== - static constexpr int fifoSize = 1024; // the size of the fifo which handles all the change requests - AbstractFifo abstractFifo; // the abstract fifo - - Array fifoRequestsType; // an array of ChangeRequest - Array fifoRequestsParameter; // an array of change parameters - - Array requestsType; // an array of ChangeRequest - Array requestsParameter; // an array of change parameters + int getCurrentIRSize() const { return currentEngine != nullptr ? currentEngine->getIRSize() : 0; } - int changeLevel = 0; // the current level of requested change in the convolution engine + int getLatency() const { return currentEngine != nullptr ? currentEngine->getLatency() : 0; } - //============================================================================== - ConvolutionEngine::ProcessingInformation currentInfo; // the information about the impulse response to load + void loadImpulseResponse (AudioBuffer&& buffer, + double originalSampleRate, + Stereo stereo, + Trim trim, + Normalise normalise) + { + engineQueue->loadImpulseResponse (std::move (buffer), originalSampleRate, stereo, trim, normalise); + } - AudioBuffer temporaryBuffer; // a temporary buffer that is used when the function copyAndLoadImpulseResponse is called in the main API - SpinLock processLock; // a necessary lock to use with this temporary buffer + void loadImpulseResponse (const void* sourceData, + size_t sourceDataSize, + Stereo stereo, + Trim trim, + size_t size, + Normalise normalise) + { + engineQueue->loadImpulseResponse (sourceData, sourceDataSize, stereo, trim, size, normalise); + } - AudioBuffer impulseResponseOriginal; // a buffer with the original impulse response - AudioBuffer impulseResponse; // a buffer with the impulse response trimmed, resampled, resized and normalised + void loadImpulseResponse (const File& fileImpulseResponse, + Stereo stereo, + Trim trim, + size_t size, + Normalise normalise) + { + engineQueue->loadImpulseResponse (fileImpulseResponse, stereo, trim, size, normalise); + } - //============================================================================== - OwnedArray engines; // the 4 convolution engines being used +private: + void destroyPreviousEngine() + { + // If the queue is full, we'll destroy this straight away + BackgroundMessageQueue::IncomingCommand command = [p = std::move (previousEngine)]() mutable { p = nullptr; }; + messageQueue->pimpl->push (command); + } - AudioBuffer interpolationBuffer; // a buffer to do the interpolation between the convolution engines 0-1 and 2-3 - LogRampedValue changeVolumes[4]; // the volumes for each convolution engine during interpolation + void installNewEngine (std::unique_ptr newEngine) + { + destroyPreviousEngine(); + previousEngine = std::move (currentEngine); + currentEngine = std::move (newEngine); + mixer.beginTransition(); + } - bool mustInterpolate = false; // tells if the convolution engines outputs must be currently interpolated + void installPendingEngine() + { + if (auto newEngine = engineQueue->getEngine()) + installNewEngine (std::move (newEngine)); + } - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) + OptionalQueue messageQueue; + std::shared_ptr engineQueue; + std::unique_ptr previousEngine, currentEngine; + CrossoverMixer mixer; }; - //============================================================================== -Convolution::Convolution() +void Convolution::Mixer::prepare (const ProcessSpec& spec) { - pimpl.reset (new Pimpl()); - pimpl->addToFifo (Convolution::Pimpl::ChangeRequest::changeEngine, juce::var (0)); -} - -Convolution::~Convolution() -{ -} - -void Convolution::loadImpulseResponse (const void* sourceData, size_t sourceDataSize, - bool wantsStereo, bool wantsTrimming, size_t size, - bool wantsNormalisation) -{ - if (sourceData == nullptr) - return; - - auto maximumSamples = (size_t) pimpl->maximumTimeInSamples; - auto wantedSize = (size == 0 ? maximumSamples : jmin (size, maximumSamples)); - - Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource, - Pimpl::ChangeRequest::changeImpulseResponseSize, - Pimpl::ChangeRequest::changeStereo, - Pimpl::ChangeRequest::changeTrimming, - Pimpl::ChangeRequest::changeNormalisation }; - - Array sourceParameter; - - sourceParameter.add (juce::var ((int) ConvolutionEngine::ProcessingInformation::SourceType::sourceBinaryData)); - sourceParameter.add (juce::var (sourceData, sourceDataSize)); - - juce::var parameters[] = { juce::var (sourceParameter), - juce::var (static_cast (wantedSize)), - juce::var (wantsStereo), - juce::var (wantsTrimming), - juce::var (wantsNormalisation) }; - - pimpl->addToFifo (types, parameters, 5); -} - -void Convolution::loadImpulseResponse (const File& fileImpulseResponse, bool wantsStereo, - bool wantsTrimming, size_t size, bool wantsNormalisation) -{ - if (! fileImpulseResponse.existsAsFile()) - return; - - auto maximumSamples = (size_t) pimpl->maximumTimeInSamples; - auto wantedSize = (size == 0 ? maximumSamples : jmin (size, maximumSamples)); - - Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource, - Pimpl::ChangeRequest::changeImpulseResponseSize, - Pimpl::ChangeRequest::changeStereo, - Pimpl::ChangeRequest::changeTrimming, - Pimpl::ChangeRequest::changeNormalisation }; - - Array sourceParameter; - - sourceParameter.add (juce::var ((int) ConvolutionEngine::ProcessingInformation::SourceType::sourceAudioFile)); - sourceParameter.add (juce::var (fileImpulseResponse.getFullPathName())); - - juce::var parameters[] = { juce::var (sourceParameter), - juce::var (static_cast (wantedSize)), - juce::var (wantsStereo), - juce::var (wantsTrimming), - juce::var (wantsNormalisation) }; - - pimpl->addToFifo (types, parameters, 5); -} - -void Convolution::copyAndLoadImpulseResponseFromBuffer (AudioBuffer& buffer, - double bufferSampleRate, bool wantsStereo, bool wantsTrimming, bool wantsNormalisation, size_t size) -{ - copyAndLoadImpulseResponseFromBlock (AudioBlock (buffer), bufferSampleRate, - wantsStereo, wantsTrimming, wantsNormalisation, size); -} - -void Convolution::copyAndLoadImpulseResponseFromBlock (AudioBlock block, double bufferSampleRate, - bool wantsStereo, bool wantsTrimming, bool wantsNormalisation, size_t size) -{ - jassert (bufferSampleRate > 0); - - if (block.getNumSamples() == 0) - return; - - auto maximumSamples = (size_t) pimpl->maximumTimeInSamples; - auto wantedSize = (size == 0 ? maximumSamples : jmin (size, maximumSamples)); - - pimpl->copyBufferToTemporaryLocation (block); + for (auto& dry : volumeDry) + dry.reset (spec.sampleRate, 0.05); - Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSource, - Pimpl::ChangeRequest::changeImpulseResponseSize, - Pimpl::ChangeRequest::changeStereo, - Pimpl::ChangeRequest::changeTrimming, - Pimpl::ChangeRequest::changeNormalisation }; - - Array sourceParameter; - sourceParameter.add (juce::var ((int) ConvolutionEngine::ProcessingInformation::SourceType::sourceAudioBuffer)); - sourceParameter.add (juce::var (bufferSampleRate)); - - juce::var parameters[] = { juce::var (sourceParameter), - juce::var (static_cast (wantedSize)), - juce::var (wantsStereo), - juce::var (wantsTrimming), - juce::var (wantsNormalisation) }; - - pimpl->addToFifo (types, parameters, 5); -} - -void Convolution::prepare (const ProcessSpec& spec) -{ - jassert (isPositiveAndBelow (spec.numChannels, static_cast (3))); // only mono and stereo is supported - - Pimpl::ChangeRequest types[] = { Pimpl::ChangeRequest::changeSampleRate, - Pimpl::ChangeRequest::changeMaximumBufferSize }; - - juce::var parameters[] = { juce::var (spec.sampleRate), - juce::var (static_cast (spec.maximumBlockSize)) }; - - pimpl->addToFifo (types, parameters, 2); - pimpl->initProcessing (static_cast (spec.maximumBlockSize)); - - for (size_t channel = 0; channel < spec.numChannels; ++channel) - { - volumeDry[channel].reset (spec.sampleRate, 0.05); - volumeWet[channel].reset (spec.sampleRate, 0.05); - } + for (auto& wet : volumeWet) + wet.reset (spec.sampleRate, 0.05); sampleRate = spec.sampleRate; - dryBuffer = AudioBlock (dryBufferStorage, - jmin (spec.numChannels, 2u), - spec.maximumBlockSize); - isActive = true; -} + dryBlock = AudioBlock (dryBlockStorage, + jmin (spec.numChannels, 2u), + spec.maximumBlockSize); -void Convolution::reset() noexcept -{ - dryBuffer.clear(); - pimpl->reset(); } -void Convolution::processSamples (const AudioBlock& input, AudioBlock& output, bool isBypassed) noexcept +template +void Convolution::Mixer::processSamples (const AudioBlock& input, + AudioBlock& output, + bool isBypassed, + ProcessWet&& processWet) noexcept { - if (! isActive) - return; - - jassert (input.getNumChannels() == output.getNumChannels()); - jassert (isPositiveAndBelow (input.getNumChannels(), static_cast (3))); // only mono and stereo is supported - - auto numChannels = jmin (input.getNumChannels(), (size_t) 2); - auto numSamples = jmin (input.getNumSamples(), output.getNumSamples()); + const auto numChannels = jmin (input.getNumChannels(), volumeDry.size()); + const auto numSamples = jmin (input.getNumSamples(), output.getNumSamples()); - auto dry = dryBuffer.getSubsetChannelBlock (0, numChannels); + auto dry = dryBlock.getSubsetChannelBlock (0, numChannels); if (volumeDry[0].isSmoothing()) { @@ -1217,7 +1161,7 @@ void Convolution::processSamples (const AudioBlock& input, AudioBlo for (size_t channel = 0; channel < numChannels; ++channel) volumeDry[channel].applyGain (dry.getChannelPointer (channel), (int) numSamples); - pimpl->processSamples (input, output); + processWet (input, output); for (size_t channel = 0; channel < numChannels; ++channel) volumeWet[channel].applyGain (output.getChannelPointer (channel), (int) numSamples); @@ -1227,7 +1171,7 @@ void Convolution::processSamples (const AudioBlock& input, AudioBlo else { if (! currentIsBypassed) - pimpl->processSamples (input, output); + processWet (input, output); if (isBypassed != currentIsBypassed) { @@ -1247,5 +1191,104 @@ void Convolution::processSamples (const AudioBlock& input, AudioBlo } } -} // namespace dsp -} // namespace juce +void Convolution::Mixer::reset() { dryBlock.clear(); } + +//============================================================================== +Convolution::Convolution() + : Convolution (Latency { 0 }) +{} + +Convolution::Convolution (ConvolutionMessageQueue& queue) + : Convolution (Latency { 0 }, queue) +{} + +Convolution::Convolution (const Latency& requiredLatency) + : Convolution (requiredLatency, + {}, + OptionalQueue { std::make_unique() }) +{} + +Convolution::Convolution (const NonUniform& nonUniform) + : Convolution ({}, + nonUniform, + OptionalQueue { std::make_unique() }) +{} + +Convolution::Convolution (const Latency& requiredLatency, ConvolutionMessageQueue& queue) + : Convolution (requiredLatency, {}, OptionalQueue { queue }) +{} + +Convolution::Convolution (const NonUniform& nonUniform, ConvolutionMessageQueue& queue) + : Convolution ({}, nonUniform, OptionalQueue { queue }) +{} + +Convolution::Convolution (const Latency& latency, + const NonUniform& nonUniform, + OptionalQueue&& queue) + : pimpl (std::make_unique (latency, nonUniform, std::move (queue))) +{} + +Convolution::~Convolution() noexcept = default; + +void Convolution::loadImpulseResponse (const void* sourceData, + size_t sourceDataSize, + Stereo stereo, + Trim trim, + size_t size, + Normalise normalise) +{ + pimpl->loadImpulseResponse (sourceData, sourceDataSize, stereo, trim, size, normalise); +} + +void Convolution::loadImpulseResponse (const File& fileImpulseResponse, + Stereo stereo, + Trim trim, + size_t size, + Normalise normalise) +{ + pimpl->loadImpulseResponse (fileImpulseResponse, stereo, trim, size, normalise); +} + +void Convolution::loadImpulseResponse (AudioBuffer&& buffer, + double originalSampleRate, + Stereo stereo, + Trim trim, + Normalise normalise) +{ + pimpl->loadImpulseResponse (std::move (buffer), originalSampleRate, stereo, trim, normalise); +} + +void Convolution::prepare (const ProcessSpec& spec) +{ + mixer.prepare (spec); + pimpl->prepare (spec); + isActive = true; +} + +void Convolution::reset() noexcept +{ + mixer.reset(); + pimpl->reset(); +} + +void Convolution::processSamples (const AudioBlock& input, + AudioBlock& output, + bool isBypassed) noexcept +{ + if (! isActive) + return; + + jassert (input.getNumChannels() == output.getNumChannels()); + jassert (isPositiveAndBelow (input.getNumChannels(), static_cast (3))); // only mono and stereo is supported + + mixer.processSamples (input, output, isBypassed, [this] (const auto& in, auto& out) + { + pimpl->processSamples (in, out); + }); +} + +int Convolution::getCurrentIRSize() const { return pimpl->getCurrentIRSize(); } + +int Convolution::getLatency() const { return pimpl->getLatency(); } + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.h b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.h index 854a88c3..9330d856 100644 --- a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.h +++ b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,24 +23,77 @@ ============================================================================== */ -namespace juce +namespace juce::dsp { -namespace dsp + +/** + Used by the Convolution to dispatch engine-update messages on a background + thread. + + May be shared between multiple Convolution instances. + + @tags{DSP} +*/ +class JUCE_API ConvolutionMessageQueue { +public: + /** Initialises the queue to a default size. + + If your Convolution is updated very frequently, or you are sharing + this queue between multiple Convolutions, consider using the alternative + constructor taking an explicit size argument. + */ + ConvolutionMessageQueue(); + ~ConvolutionMessageQueue() noexcept; + + /** Initialises the queue with the specified number of entries. + + In general, the number of required entries scales with the number + of Convolutions sharing the same Queue, and the frequency of updates + to those Convolutions. + */ + explicit ConvolutionMessageQueue (int numEntries); + + ConvolutionMessageQueue (ConvolutionMessageQueue&&) noexcept; + ConvolutionMessageQueue& operator= (ConvolutionMessageQueue&&) noexcept; + + ConvolutionMessageQueue (const ConvolutionMessageQueue&) = delete; + ConvolutionMessageQueue& operator= (const ConvolutionMessageQueue&) = delete; + +private: + struct Impl; + std::unique_ptr pimpl; + + friend class Convolution; +}; /** - Performs stereo uniform-partitioned convolution of an input signal with an - impulse response in the frequency domain, using the juce FFT class. + Performs stereo partitioned convolution of an input signal with an + impulse response in the frequency domain, using the JUCE FFT class. - It provides some thread-safe functions to load impulse responses as well, - from audio files or memory on the fly without any noticeable artefacts, + This class provides some thread-safe functions to load impulse responses + from audio files or memory on-the-fly without noticeable artefacts, performing resampling and trimming if necessary. - The processing is equivalent to the time domain convolution done in the - class FIRFilter, with a FIRFilter::Coefficients object having as - coefficients the samples of the impulse response. However, it is more - efficient in general to do frequency domain convolution when the size of - the impulse response is higher than 64 samples. + The processing performed by this class is equivalent to the time domain + convolution done in the FIRFilter class, with a FIRFilter::Coefficients + object having the samples of the impulse response as its coefficients. + However, in general it is more efficient to do frequency domain + convolution when the size of the impulse response is 64 samples or + greater. + + Note: The default operation of this class uses zero latency and a uniform + partitioned algorithm. If the impulse response size is large, or if the + algorithm is too CPU intensive, it is possible to use either a fixed + latency version of the algorithm, or a simple non-uniform partitioned + convolution algorithm. + + Threading: It is not safe to interleave calls to the methods of this + class. If you need to load new impulse responses during processing the + load() calls must be synchronised with process() calls, which in practice + means making the load() call from the audio thread. The + loadImpulseResponse() functions *are* wait-free and are therefore + suitable for use in a realtime context. @see FIRFilter, FIRFilter::Coefficients, FFT @@ -54,121 +106,202 @@ class JUCE_API Convolution /** Initialises an object for performing convolution in the frequency domain. */ Convolution(); - /** Destructor. */ - ~Convolution(); + /** Initialises a convolution engine using a shared background message queue. + + IMPORTANT: the queue *must* remain alive throughout the lifetime of the + Convolution. + */ + explicit Convolution (ConvolutionMessageQueue& queue); + + /** Contains configuration information for a convolution with a fixed latency. */ + struct Latency { int latencyInSamples; }; + + /** Initialises an object for performing convolution with a fixed latency. + + If the requested latency is zero, the actual latency will also be zero. + For requested latencies greater than zero, the actual latency will + always at least as large as the requested latency. Using a fixed + non-zero latency can reduce the CPU consumption of the convolution + algorithm. + + @param requiredLatency the minimum latency + */ + explicit Convolution (const Latency& requiredLatency); + + /** Contains configuration information for a non-uniform convolution. */ + struct NonUniform { int headSizeInSamples; }; + + /** Initialises an object for performing convolution in the frequency domain + using a non-uniform partitioned algorithm. + + A requiredHeadSize of 256 samples or greater will improve the + efficiency of the processing for IR sizes of 4096 samples or greater + (recommended for reverberation IRs). + + @param requiredHeadSize the head IR size for two stage non-uniform + partitioned convolution + */ + explicit Convolution (const NonUniform& requiredHeadSize); + + /** Behaves the same as the constructor taking a single Latency argument, + but with a shared background message queue. + + IMPORTANT: the queue *must* remain alive throughout the lifetime of the + Convolution. + */ + Convolution (const Latency&, ConvolutionMessageQueue&); + + /** Behaves the same as the constructor taking a single NonUniform argument, + but with a shared background message queue. + + IMPORTANT: the queue *must* remain alive throughout the lifetime of the + Convolution. + */ + Convolution (const NonUniform&, ConvolutionMessageQueue&); + + ~Convolution() noexcept; //============================================================================== - /** Must be called before loading any impulse response, to provide to the - convolution the maximumBufferSize to handle, and the sample rate useful for - optional resampling. + /** Must be called before first calling process. + + In general, calls to loadImpulseResponse() load the impulse response (IR) + asynchronously. The IR will become active once it has been completely loaded + and processed, which may take some time. + + Calling prepare() will ensure that the IR supplied to the most recent call to + loadImpulseResponse() is fully initialised. This IR will then be active during + the next call to process(). It is recommended to call loadImpulseResponse() *before* + prepare() if a specific IR must be active during the first process() call. */ void prepare (const ProcessSpec&); - /** Resets the processing pipeline, ready to start a new stream of data. */ + /** Resets the processing pipeline ready to start a new stream of data. */ void reset() noexcept; - /** Performs the filter operation on the given set of samples, with optional + /** Performs the filter operation on the given set of samples with optional stereo processing. */ - template + template , int> = 0> void process (const ProcessContext& context) noexcept { - static_assert (std::is_same::value, - "Convolution engine only supports single precision floating point data"); - processSamples (context.getInputBlock(), context.getOutputBlock(), context.isBypassed); } + //============================================================================== + enum class Stereo { no, yes }; + enum class Trim { no, yes }; + enum class Normalise { no, yes }; + //============================================================================== /** This function loads an impulse response audio file from memory, added in a JUCE project with the Projucer as binary data. It can load any of the audio formats registered in JUCE, and performs some resampling and pre-processing as well if needed. - Note: Obviously, don't try to use this function on float samples, since the - data is supposed to be an audio file in its binary format, and be sure that - the original data is not going to move at all its memory location during the - process !! + Note: Don't try to use this function on float samples, since the data is + expected to be an audio file in its binary format. Be sure that the original + data remains constant throughout the lifetime of the Convolution object, as + the loading process will happen on a background thread once this function has + returned. @param sourceData the block of data to use as the stream's source @param sourceDataSize the number of bytes in the source data block - @param wantsStereo requests to process both stereo channels or only one mono channel - @param wantsTrimming requests to trim the start and the end of the impulse response + @param isStereo selects either stereo or mono + @param requiresTrimming optionally trim the start and the end of the impulse response @param size the expected size for the impulse response after loading, can be - set to 0 for requesting maximum original impulse response size - @param wantsNormalisation requests to normalise the impulse response amplitude + set to 0 to requesting the original impulse response size + @param requiresNormalisation optionally normalise the impulse response amplitude */ void loadImpulseResponse (const void* sourceData, size_t sourceDataSize, - bool wantsStereo, bool wantsTrimming, size_t size, - bool wantsNormalisation = true); + Stereo isStereo, Trim requiresTrimming, size_t size, + Normalise requiresNormalisation = Normalise::yes); - /** This function loads an impulse response from an audio file on any drive. It - can load any of the audio formats registered in JUCE, and performs some - resampling and pre-processing as well if needed. + /** This function loads an impulse response from an audio file. It can load any + of the audio formats registered in JUCE, and performs some resampling and + pre-processing as well if needed. @param fileImpulseResponse the location of the audio file - @param wantsStereo requests to process both stereo channels or only one mono channel - @param wantsTrimming requests to trim the start and the end of the impulse response + @param isStereo selects either stereo or mono + @param requiresTrimming optionally trim the start and the end of the impulse response @param size the expected size for the impulse response after loading, can be - set to 0 for requesting maximum original impulse response size - @param wantsNormalisation requests to normalise the impulse response amplitude + set to 0 to requesting the original impulse response size + @param requiresNormalisation optionally normalise the impulse response amplitude */ void loadImpulseResponse (const File& fileImpulseResponse, - bool wantsStereo, bool wantsTrimming, size_t size, - bool wantsNormalisation = true); + Stereo isStereo, Trim requiresTrimming, size_t size, + Normalise requiresNormalisation = Normalise::yes); - /** This function loads an impulse response from an audio buffer, which is - copied before doing anything else. Performs some resampling and - pre-processing as well if needed. + /** This function loads an impulse response from an audio buffer. + To avoid memory allocation on the audio thread, this function takes + ownership of the buffer passed in. + + If calling this function during processing, make sure that the buffer is + not allocated on the audio thread (be careful of accidental copies!). + If you need to pass arbitrary/generated buffers it's recommended to + create these buffers on a separate thread and to use some wait-free + construct (a lock-free queue or a SpinLock/GenericScopedTryLock combination) + to transfer ownership to the audio thread without allocating. @param buffer the AudioBuffer to use @param bufferSampleRate the sampleRate of the data in the AudioBuffer - @param wantsStereo requests to process both stereo channels or only one mono channel - @param wantsTrimming requests to trim the start and the end of the impulse response - @param wantsNormalisation requests to normalise the impulse response amplitude - @param size the expected size for the impulse response after loading, can be - set to 0 for requesting maximum original impulse response size + @param isStereo selects either stereo or mono + @param requiresTrimming optionally trim the start and the end of the impulse response + @param requiresNormalisation optionally normalise the impulse response amplitude */ - void copyAndLoadImpulseResponseFromBuffer (AudioBuffer& buffer, double bufferSampleRate, - bool wantsStereo, bool wantsTrimming, bool wantsNormalisation, - size_t size); + void loadImpulseResponse (AudioBuffer&& buffer, double bufferSampleRate, + Stereo isStereo, Trim requiresTrimming, Normalise requiresNormalisation); - /** This function loads an impulse response from an audio block, which is - copied before doing anything else. Performs some resampling and - pre-processing as well if needed. + /** This function returns the size of the current IR in samples. */ + int getCurrentIRSize() const; - @param block the AudioBlock to use - @param bufferSampleRate the sampleRate of the data in the AudioBuffer - @param wantsStereo requests to process both stereo channels or only one channel - @param wantsTrimming requests to trim the start and the end of the impulse response - @param wantsNormalisation requests to normalise the impulse response amplitude - @param size the expected size for the impulse response after loading, - -1 for maximum length - */ - void copyAndLoadImpulseResponseFromBlock (AudioBlock block, double bufferSampleRate, - bool wantsStereo, bool wantsTrimming, bool wantsNormalisation, - size_t size); + /** This function returns the current latency of the process in samples. + Note: This is the latency of the convolution engine, not the latency + associated with the current impulse response choice that has to be + considered separately (linear phase filters, for example). + */ + int getLatency() const; private: //============================================================================== - struct Pimpl; - std::unique_ptr pimpl; + Convolution (const Latency&, + const NonUniform&, + OptionalScopedPointer&&); - //============================================================================== void processSamples (const AudioBlock&, AudioBlock&, bool isBypassed) noexcept; + class Mixer + { + public: + void prepare (const ProcessSpec&); + + template + void processSamples (const AudioBlock&, + AudioBlock&, + bool isBypassed, + ProcessWet&&) noexcept; + + void reset(); + + private: + std::array, 2> volumeDry, volumeWet; + AudioBlock dryBlock; + HeapBlock dryBlockStorage; + double sampleRate = 0; + bool currentIsBypassed = false; + }; + + //============================================================================== + class Impl; + std::unique_ptr pimpl; + //============================================================================== - double sampleRate; - bool currentIsBypassed = false; + Mixer mixer; bool isActive = false; - SmoothedValue volumeDry[2], volumeWet[2]; - AudioBlock dryBuffer; - HeapBlock dryBufferStorage; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Convolution) }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution_test.cpp b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution_test.cpp new file mode 100644 index 00000000..e7f0d111 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution_test.cpp @@ -0,0 +1,578 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#if JUCE_ENABLE_ALLOCATION_HOOKS +#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE const UnitTestAllocationChecker checker (*this) +#else +#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE +#endif + +namespace juce::dsp +{ +namespace +{ + +class ConvolutionTest final : public UnitTest +{ + template + static void nTimes (int n, Callback&& callback) + { + for (auto i = 0; i < n; ++i) + callback(); + } + + static AudioBuffer makeRamp (int length) + { + AudioBuffer result (1, length); + result.clear(); + + const auto writePtr = result.getWritePointer (0); + std::fill (writePtr, writePtr + length, 1.0f); + result.applyGainRamp (0, length, 1.0f, 0.0f); + + return result; + } + + static AudioBuffer makeStereoRamp (int length) + { + AudioBuffer result (2, length); + result.clear(); + + auto* const* channels = result.getArrayOfWritePointers(); + std::for_each (channels, channels + result.getNumChannels(), [length] (auto* channel) + { + std::fill (channel, channel + length, 1.0f); + }); + + result.applyGainRamp (0, 0, length, 1.0f, 0.0f); + result.applyGainRamp (1, 0, length, 0.0f, 1.0f); + + return result; + } + + static void addDiracImpulse (const AudioBlock& block) + { + block.clear(); + + for (size_t channel = 0; channel != block.getNumChannels(); ++channel) + block.setSample ((int) channel, 0, 1.0f); + } + + void checkForNans (const AudioBlock& block) + { + for (size_t channel = 0; channel != block.getNumChannels(); ++channel) + for (size_t sample = 0; sample != block.getNumSamples(); ++sample) + expect (! std::isnan (block.getSample ((int) channel, (int) sample))); + } + + void checkAllChannelsNonZero (const AudioBlock& block) + { + for (size_t i = 0; i != block.getNumChannels(); ++i) + { + const auto* channel = block.getChannelPointer (i); + + expect (std::any_of (channel, channel + block.getNumSamples(), [] (float sample) + { + return ! approximatelyEqual (sample, 0.0f); + })); + } + } + + template + void nonAllocatingExpectWithinAbsoluteError (const T& a, const T& b, const T& error) + { + expect (std::abs (a - b) < error); + } + + enum class InitSequence { prepareThenLoad, loadThenPrepare }; + + void checkLatency (const Convolution& convolution, const Convolution::Latency& latency) + { + const auto reportedLatency = convolution.getLatency(); + + if (latency.latencyInSamples == 0) + expect (reportedLatency == 0); + + expect (reportedLatency >= latency.latencyInSamples); + } + + void checkLatency (const Convolution&, const Convolution::NonUniform&) {} + + template + void testConvolution (const ProcessSpec& spec, + const ConvolutionConfig& config, + const AudioBuffer& ir, + double irSampleRate, + Convolution::Stereo stereo, + Convolution::Trim trim, + Convolution::Normalise normalise, + const AudioBlock& expectedResult, + InitSequence initSequence) + { + AudioBuffer buffer (static_cast (spec.numChannels), + static_cast (spec.maximumBlockSize)); + AudioBlock block { buffer }; + ProcessContextReplacing context { block }; + + const auto numBlocksPerSecond = (int) std::ceil (spec.sampleRate / spec.maximumBlockSize); + const auto numBlocksForImpulse = (int) std::ceil ((double) expectedResult.getNumSamples() / spec.maximumBlockSize); + + AudioBuffer outBuffer (static_cast (spec.numChannels), + numBlocksForImpulse * static_cast (spec.maximumBlockSize)); + + Convolution convolution (config); + + auto copiedIr = ir; + + if (initSequence == InitSequence::loadThenPrepare) + convolution.loadImpulseResponse (std::move (copiedIr), irSampleRate, stereo, trim, normalise); + + convolution.prepare (spec); + + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + if (initSequence == InitSequence::prepareThenLoad) + convolution.loadImpulseResponse (std::move (copiedIr), irSampleRate, stereo, trim, normalise); + + checkLatency (convolution, config); + + auto processBlocksWithDiracImpulse = [&] + { + for (auto i = 0; i != numBlocksForImpulse; ++i) + { + if (i == 0) + addDiracImpulse (block); + else + block.clear(); + + convolution.process (context); + + for (auto c = 0; c != static_cast (spec.numChannels); ++c) + { + outBuffer.copyFrom (c, + i * static_cast (spec.maximumBlockSize), + block.getChannelPointer (static_cast (c)), + static_cast (spec.maximumBlockSize)); + } + } + }; + + // If we load an IR while the convolution is already running, we'll need to wait + // for it to be loaded on a background thread + if (initSequence == InitSequence::prepareThenLoad) + { + const auto time = Time::getMillisecondCounter(); + + // Wait 10 seconds to load the impulse response + while (Time::getMillisecondCounter() - time < 10'000) + { + processBlocksWithDiracImpulse(); + + // Check if the impulse response was loaded + if (! approximatelyEqual (block.getSample (0, 1), 0.0f)) + break; + } + } + + // At this point, our convolution should be loaded and the current IR size should + // match the expected result size + expect (convolution.getCurrentIRSize() == static_cast (expectedResult.getNumSamples())); + + // Make sure we get any smoothing out of the way + nTimes (numBlocksPerSecond, processBlocksWithDiracImpulse); + + nTimes (5, [&] + { + processBlocksWithDiracImpulse(); + + const auto actualLatency = static_cast (convolution.getLatency()); + + // The output should be the same as the IR + for (size_t c = 0; c != static_cast (expectedResult.getNumChannels()); ++c) + { + for (size_t i = 0; i != static_cast (expectedResult.getNumSamples()); ++i) + { + const auto equivalentSample = i + actualLatency; + + if (static_cast (equivalentSample) >= outBuffer.getNumSamples()) + continue; + + nonAllocatingExpectWithinAbsoluteError (outBuffer.getSample ((int) c, (int) equivalentSample), + expectedResult.getSample ((int) c, (int) i), + 0.01f); + } + } + }); + } + + template + void testConvolution (const ProcessSpec& spec, + const ConvolutionConfig& config, + const AudioBuffer& ir, + double irSampleRate, + Convolution::Stereo stereo, + Convolution::Trim trim, + Convolution::Normalise normalise, + const AudioBlock& expectedResult) + { + for (const auto sequence : { InitSequence::prepareThenLoad, InitSequence::loadThenPrepare }) + testConvolution (spec, config, ir, irSampleRate, stereo, trim, normalise, expectedResult, sequence); + } + +public: + ConvolutionTest() + : UnitTest ("Convolution", UnitTestCategories::dsp) + {} + + void runTest() override + { + const ProcessSpec spec { 44100.0, 512, 2 }; + AudioBuffer buffer (static_cast (spec.numChannels), + static_cast (spec.maximumBlockSize)); + AudioBlock block { buffer }; + ProcessContextReplacing context { block }; + + const auto impulseData = [] + { + Random random; + AudioBuffer result (2, 1000); + + for (auto channel = 0; channel != result.getNumChannels(); ++channel) + for (auto sample = 0; sample != result.getNumSamples(); ++sample) + result.setSample (channel, sample, random.nextFloat()); + + return result; + }(); + + beginTest ("Impulse responses can be loaded without allocating on the audio thread"); + { + Convolution convolution; + convolution.prepare (spec); + + auto copy = impulseData; + + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + nTimes (100, [&] + { + convolution.loadImpulseResponse (std::move (copy), + 1000, + Convolution::Stereo::yes, + Convolution::Trim::yes, + Convolution::Normalise::no); + addDiracImpulse (block); + convolution.process (context); + checkForNans (block); + }); + } + + beginTest ("Convolution can be reset without allocating on the audio thread"); + { + Convolution convolution; + convolution.prepare (spec); + + auto copy = impulseData; + + convolution.loadImpulseResponse (std::move (copy), + 1000, + Convolution::Stereo::yes, + Convolution::Trim::yes, + Convolution::Normalise::yes); + + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + nTimes (100, [&] + { + addDiracImpulse (block); + convolution.reset(); + convolution.process (context); + convolution.reset(); + }); + + checkForNans (block); + } + + beginTest ("Completely empty IRs don't crash"); + { + AudioBuffer emptyBuffer; + + Convolution convolution; + convolution.prepare (spec); + + auto copy = impulseData; + + convolution.loadImpulseResponse (std::move (copy), + 2000, + Convolution::Stereo::yes, + Convolution::Trim::yes, + Convolution::Normalise::yes); + + JUCE_FAIL_ON_ALLOCATION_IN_SCOPE; + + nTimes (100, [&] + { + addDiracImpulse (block); + convolution.reset(); + convolution.process (context); + convolution.reset(); + }); + + checkForNans (block); + } + + beginTest ("Convolutions can cope with a change in samplerate and blocksize"); + { + Convolution convolution; + + auto copy = impulseData; + convolution.loadImpulseResponse (std::move (copy), + 2000, + Convolution::Stereo::yes, + Convolution::Trim::no, + Convolution::Normalise::yes); + + const dsp::ProcessSpec specs[] = { { 96'000.0, 1024, 2 }, + { 48'000.0, 512, 2 }, + { 44'100.0, 256, 2 } }; + + for (const auto& thisSpec : specs) + { + convolution.prepare (thisSpec); + + expectWithinAbsoluteError ((double) convolution.getCurrentIRSize(), + thisSpec.sampleRate * 0.5, + 1.0); + + juce::AudioBuffer thisBuffer ((int) thisSpec.numChannels, + (int) thisSpec.maximumBlockSize); + AudioBlock thisBlock { thisBuffer }; + ProcessContextReplacing thisContext { thisBlock }; + + nTimes (100, [&] + { + addDiracImpulse (thisBlock); + convolution.process (thisContext); + + checkForNans (thisBlock); + checkAllChannelsNonZero (thisBlock); + }); + } + } + + beginTest ("Short uniform convolutions work"); + { + const auto ramp = makeRamp (static_cast (spec.maximumBlockSize) / 2); + testConvolution (spec, + Convolution::Latency { 0 }, + ramp, + spec.sampleRate, + Convolution::Stereo::yes, + Convolution::Trim::yes, + Convolution::Normalise::no, + ramp); + } + + beginTest ("Longer uniform convolutions work"); + { + const auto ramp = makeRamp (static_cast (spec.maximumBlockSize) * 8); + testConvolution (spec, + Convolution::Latency { 0 }, + ramp, + spec.sampleRate, + Convolution::Stereo::yes, + Convolution::Trim::yes, + Convolution::Normalise::no, + ramp); + } + + beginTest ("Normalisation works"); + { + const auto ramp = makeRamp (static_cast (spec.maximumBlockSize) * 8); + + auto copy = ramp; + const auto channels = copy.getArrayOfWritePointers(); + const auto numChannels = copy.getNumChannels(); + const auto numSamples = copy.getNumSamples(); + + const auto factor = 0.125f / std::sqrt (std::accumulate (channels, channels + numChannels, 0.0f, + [numSamples] (auto max, auto* channel) + { + return juce::jmax (max, std::accumulate (channel, channel + numSamples, 0.0f, + [] (auto sum, auto sample) + { + return sum + sample * sample; + })); + })); + + std::for_each (channels, channels + numChannels, [factor, numSamples] (auto* channel) + { + FloatVectorOperations::multiply (channel, factor, numSamples); + }); + + testConvolution (spec, + Convolution::Latency { 0 }, + ramp, + spec.sampleRate, + Convolution::Stereo::yes, + Convolution::Trim::yes, + Convolution::Normalise::yes, + copy); + } + + beginTest ("Stereo convolutions work"); + { + const auto ramp = makeStereoRamp (static_cast (spec.maximumBlockSize) * 5); + testConvolution (spec, + Convolution::Latency { 0 }, + ramp, + spec.sampleRate, + Convolution::Stereo::yes, + Convolution::Trim::yes, + Convolution::Normalise::no, + ramp); + } + + beginTest ("Stereo IRs only use first channel if stereo is disabled"); + { + const auto length = static_cast (spec.maximumBlockSize) * 5; + const auto ramp = makeStereoRamp (length); + + const float* channels[] { ramp.getReadPointer (0), ramp.getReadPointer (0) }; + + testConvolution (spec, + Convolution::Latency { 0 }, + ramp, + spec.sampleRate, + Convolution::Stereo::no, + Convolution::Trim::yes, + Convolution::Normalise::no, + AudioBlock (channels, numElementsInArray (channels), (size_t) length)); + } + + beginTest ("IRs with extra silence are trimmed appropriately"); + { + const auto length = static_cast (spec.maximumBlockSize) * 3; + const auto ramp = makeRamp (length); + AudioBuffer paddedRamp (ramp.getNumChannels(), ramp.getNumSamples() * 2); + paddedRamp.clear(); + + const auto offset = (paddedRamp.getNumSamples() - ramp.getNumSamples()) / 2; + + for (auto channel = 0; channel != ramp.getNumChannels(); ++channel) + paddedRamp.copyFrom (channel, offset, ramp.getReadPointer (channel), length); + + testConvolution (spec, + Convolution::Latency { 0 }, + paddedRamp, + spec.sampleRate, + Convolution::Stereo::no, + Convolution::Trim::yes, + Convolution::Normalise::no, + ramp); + } + + beginTest ("IRs are resampled if their sample rate is different to the playback rate"); + { + for (const auto resampleRatio : { 0.1, 0.5, 2.0, 10.0 }) + { + const auto length = static_cast (spec.maximumBlockSize) * 2; + const auto ramp = makeStereoRamp (length); + + const auto resampled = [&] + { + AudioBuffer original = ramp; + MemoryAudioSource memorySource (original, false); + ResamplingAudioSource resamplingSource (&memorySource, false, original.getNumChannels()); + + const auto finalSize = roundToInt (original.getNumSamples() / resampleRatio); + resamplingSource.setResamplingRatio (resampleRatio); + resamplingSource.prepareToPlay (finalSize, spec.sampleRate * resampleRatio); + + AudioBuffer result (original.getNumChannels(), finalSize); + resamplingSource.getNextAudioBlock ({ &result, 0, result.getNumSamples() }); + + result.applyGain ((float) resampleRatio); + + return result; + }(); + + testConvolution (spec, + Convolution::Latency { 0 }, + ramp, + spec.sampleRate * resampleRatio, + Convolution::Stereo::yes, + Convolution::Trim::yes, + Convolution::Normalise::no, + resampled); + } + } + + beginTest ("Non-uniform convolutions work"); + { + const auto ramp = makeRamp (static_cast (spec.maximumBlockSize) * 8); + + for (auto headSize : { spec.maximumBlockSize / 2, spec.maximumBlockSize, spec.maximumBlockSize * 9 }) + { + testConvolution (spec, + Convolution::NonUniform { static_cast (headSize) }, + ramp, + spec.sampleRate, + Convolution::Stereo::yes, + Convolution::Trim::yes, + Convolution::Normalise::no, + ramp); + } + } + + beginTest ("Convolutions with latency work"); + { + const auto ramp = makeRamp (static_cast (spec.maximumBlockSize) * 8); + using BlockSize = decltype (spec.maximumBlockSize); + + for (auto latency : { static_cast (0), + spec.maximumBlockSize / 3, + spec.maximumBlockSize, + spec.maximumBlockSize * 2, + static_cast (spec.maximumBlockSize * 2.5) }) + { + testConvolution (spec, + Convolution::Latency { static_cast (latency) }, + ramp, + spec.sampleRate, + Convolution::Stereo::yes, + Convolution::Trim::yes, + Convolution::Normalise::no, + ramp); + } + } + } +}; + +ConvolutionTest convolutionUnitTest; + +} +} // namespace juce::dsp + +#undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE diff --git a/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT.cpp b/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT.cpp index 01663675..edcdaf59 100644 --- a/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT.cpp +++ b/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,14 +23,12 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { struct FFT::Instance { - virtual ~Instance() {} + virtual ~Instance() = default; virtual void perform (const Complex* input, Complex* output, bool inverse) const noexcept = 0; virtual void performRealOnlyForwardTransform (float*, bool) const noexcept = 0; virtual void performRealOnlyInverseTransform (float*) const noexcept = 0; @@ -46,7 +43,7 @@ struct FFT::Engine std::sort (list.begin(), list.end(), [] (Engine* a, Engine* b) { return b->enginePriority < a->enginePriority; }); } - virtual ~Engine() {} + virtual ~Engine() = default; virtual FFT::Instance* create (int order) const = 0; @@ -80,7 +77,7 @@ struct FFT::EngineImpl : public FFT::Engine //============================================================================== //============================================================================== -struct FFTFallback : public FFT::Instance +struct FFTFallback final : public FFT::Instance { // this should have the least priority of all engines static constexpr int priority = -1; @@ -106,7 +103,7 @@ struct FFTFallback : public FFT::Instance return; } - const SpinLock::ScopedLockType sl(processLock); + const SpinLock::ScopedLockType sl (processLock); jassert (configForward != nullptr); @@ -114,7 +111,7 @@ struct FFTFallback : public FFT::Instance { configInverse->perform (input, output); - const float scaleFactor = 1.0f / size; + const float scaleFactor = 1.0f / (float) size; for (int i = 0; i < size; ++i) output[i] *= scaleFactor; @@ -136,12 +133,14 @@ struct FFTFallback : public FFT::Instance if (scratchSize < maxFFTScratchSpaceToAlloca) { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255) performRealOnlyForwardTransform (static_cast*> (alloca (scratchSize)), d); + JUCE_END_IGNORE_WARNINGS_MSVC } else { HeapBlock heapSpace (scratchSize); - performRealOnlyForwardTransform (reinterpret_cast*> (heapSpace.getData()), d); + performRealOnlyForwardTransform (unalignedPointerCast*> (heapSpace.getData()), d); } } @@ -154,12 +153,14 @@ struct FFTFallback : public FFT::Instance if (scratchSize < maxFFTScratchSpaceToAlloca) { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255) performRealOnlyInverseTransform (static_cast*> (alloca (scratchSize)), d); + JUCE_END_IGNORE_WARNINGS_MSVC } else { HeapBlock heapSpace (scratchSize); - performRealOnlyInverseTransform (reinterpret_cast*> (heapSpace.getData()), d); + performRealOnlyInverseTransform (unalignedPointerCast*> (heapSpace.getData()), d); } } @@ -175,7 +176,7 @@ struct FFTFallback : public FFT::Instance { auto* input = reinterpret_cast*> (d); - for (auto i = size >> 1; i < size; ++i) + for (int i = size >> 1; i < size; ++i) input[i] = std::conj (input[size - i]); perform (input, scratch, true); @@ -229,7 +230,7 @@ struct FFTFallback : public FFT::Instance for (int i = fftSize / 2; i < fftSize; ++i) { auto index = fftSize / 2 - (i - fftSize / 2); - twiddleTable[i] = conj(twiddleTable[index]); + twiddleTable[i] = conj (twiddleTable[index]); } } @@ -316,13 +317,17 @@ struct FFTFallback : public FFT::Instance default: jassertfalse; break; } + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255) auto* scratch = static_cast*> (alloca ((size_t) factor.radix * sizeof (Complex))); + JUCE_END_IGNORE_WARNINGS_MSVC for (int i = 0; i < factor.length; ++i) { for (int k = i, q1 = 0; q1 < factor.radix; ++q1) { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6386) scratch[q1] = data[k]; + JUCE_END_IGNORE_WARNINGS_MSVC k += factor.length; } @@ -338,7 +343,9 @@ struct FFTFallback : public FFT::Instance if (twiddleIndex >= fftSize) twiddleIndex -= fftSize; + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6385) data[k] += scratch[q] * twiddleTable[twiddleIndex]; + JUCE_END_IGNORE_WARNINGS_MSVC } k += factor.length; @@ -425,7 +432,7 @@ FFT::EngineImpl fftFallback; //============================================================================== //============================================================================== #if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK -struct AppleFFT : public FFT::Instance +struct AppleFFT final : public FFT::Instance { static constexpr int priority = 5; @@ -689,7 +696,7 @@ struct FFTWImpl : public FFT::Instance auto size = (1 << order); if (! ignoreNegativeFreqs) - for (auto i = size >> 1; i < size; ++i) + for (int i = size >> 1; i < size; ++i) out[i] = std::conj (out[size - i]); } @@ -724,7 +731,7 @@ FFT::EngineImpl fftwEngine; //============================================================================== //============================================================================== #if JUCE_DSP_USE_INTEL_MKL -struct IntelFFT : public FFT::Instance +struct IntelFFT final : public FFT::Instance { static constexpr int priority = 8; @@ -763,7 +770,7 @@ struct IntelFFT : public FFT::Instance : order (orderToUse), c2c (c2cToUse), c2r (cr2ToUse) {} - ~IntelFFT() + ~IntelFFT() override { DftiFreeDescriptor (&c2c); DftiFreeDescriptor (&c2r); @@ -788,7 +795,7 @@ struct IntelFFT : public FFT::Instance auto size = (1 << order); if (! ignoreNegativeFreqs) - for (auto i = size >> 1; i < size; ++i) + for (int i = size >> 1; i < size; ++i) out[i] = std::conj (out[size - i]); } @@ -804,6 +811,142 @@ struct IntelFFT : public FFT::Instance FFT::EngineImpl fftwEngine; #endif +//============================================================================== +//============================================================================== +// Visual Studio should define no more than one of these, depending on the +// setting at 'Project' > 'Properties' > 'Configuration Properties' > 'Intel +// Performance Libraries' > 'Use Intel(R) IPP' +#if _IPP_SEQUENTIAL_STATIC || _IPP_SEQUENTIAL_DYNAMIC || _IPP_PARALLEL_STATIC || _IPP_PARALLEL_DYNAMIC +class IntelPerformancePrimitivesFFT final : public FFT::Instance +{ +public: + static constexpr auto priority = 9; + + static IntelPerformancePrimitivesFFT* create (const int order) + { + auto complexContext = Context::create (order); + auto realContext = Context ::create (order); + + if (complexContext.isValid() && realContext.isValid()) + return new IntelPerformancePrimitivesFFT (std::move (complexContext), std::move (realContext), order); + + return {}; + } + + void perform (const Complex* input, Complex* output, bool inverse) const noexcept override + { + if (inverse) + { + ippsFFTInv_CToC_32fc (reinterpret_cast (input), + reinterpret_cast (output), + cplx.specPtr, + cplx.workBuf.get()); + } + else + { + ippsFFTFwd_CToC_32fc (reinterpret_cast (input), + reinterpret_cast (output), + cplx.specPtr, + cplx.workBuf.get()); + } + } + + void performRealOnlyForwardTransform (float* inoutData, bool ignoreNegativeFreqs) const noexcept override + { + ippsFFTFwd_RToCCS_32f_I (inoutData, real.specPtr, real.workBuf.get()); + + if (order == 0) + return; + + auto* out = reinterpret_cast*> (inoutData); + const auto size = (1 << order); + + if (! ignoreNegativeFreqs) + for (auto i = size >> 1; i < size; ++i) + out[i] = std::conj (out[size - i]); + } + + void performRealOnlyInverseTransform (float* inoutData) const noexcept override + { + ippsFFTInv_CCSToR_32f_I (inoutData, real.specPtr, real.workBuf.get()); + } + +private: + static constexpr auto flag = IPP_FFT_DIV_INV_BY_N; + static constexpr auto hint = ippAlgHintFast; + + struct IppFree + { + template + void operator() (Ptr* ptr) const noexcept { ippsFree (ptr); } + }; + + using IppPtr = std::unique_ptr; + + template + struct Context + { + using SpecPtr = typename Traits::Spec*; + + static Context create (const int order) + { + int specSize = 0, initSize = 0, workSize = 0; + + if (Traits::getSize (order, flag, hint, &specSize, &initSize, &workSize) != ippStsNoErr) + return {}; + + const auto initBuf = IppPtr (ippsMalloc_8u (initSize)); + auto specBuf = IppPtr (ippsMalloc_8u (specSize)); + SpecPtr specPtr = nullptr; + + if (Traits::init (&specPtr, order, flag, hint, specBuf.get(), initBuf.get()) != ippStsNoErr) + return {}; + + return { std::move (specBuf), IppPtr (ippsMalloc_8u (workSize)), specPtr }; + } + + Context() noexcept = default; + + Context (IppPtr&& spec, IppPtr&& work, typename Traits::Spec* ptr) noexcept + : specBuf (std::move (spec)), workBuf (std::move (work)), specPtr (ptr) + {} + + bool isValid() const noexcept { return specPtr != nullptr; } + + IppPtr specBuf, workBuf; + SpecPtr specPtr = nullptr; + }; + + struct ComplexTraits + { + static constexpr auto getSize = ippsFFTGetSize_C_32fc; + static constexpr auto init = ippsFFTInit_C_32fc; + using Spec = IppsFFTSpec_C_32fc; + }; + + struct RealTraits + { + static constexpr auto getSize = ippsFFTGetSize_R_32f; + static constexpr auto init = ippsFFTInit_R_32f; + using Spec = IppsFFTSpec_R_32f; + }; + + IntelPerformancePrimitivesFFT (Context&& complexToUse, + Context&& realToUse, + const int orderToUse) + : cplx (std::move (complexToUse)), + real (std::move (realToUse)), + order (orderToUse) + {} + + Context cplx; + Context real; + int order = 0; +}; + +FFT::EngineImpl intelPerformancePrimitivesFFT; +#endif + //============================================================================== //============================================================================== FFT::FFT (int order) @@ -812,7 +955,11 @@ FFT::FFT (int order) { } -FFT::~FFT() {} +FFT::FFT (FFT&&) noexcept = default; + +FFT& FFT::operator= (FFT&&) noexcept = default; + +FFT::~FFT() = default; void FFT::perform (const Complex* input, Complex* output, bool inverse) const noexcept { @@ -820,10 +967,10 @@ void FFT::perform (const Complex* input, Complex* output, bool inv engine->perform (input, output, inverse); } -void FFT::performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNeagtiveFreqs) const noexcept +void FFT::performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNegativeFreqs) const noexcept { if (engine != nullptr) - engine->performRealOnlyForwardTransform (inputOutputData, ignoreNeagtiveFreqs); + engine->performRealOnlyForwardTransform (inputOutputData, ignoreNegativeFreqs); } void FFT::performRealOnlyInverseTransform (float* inputOutputData) const noexcept @@ -832,19 +979,20 @@ void FFT::performRealOnlyInverseTransform (float* inputOutputData) const noexcep engine->performRealOnlyInverseTransform (inputOutputData); } -void FFT::performFrequencyOnlyForwardTransform (float* inputOutputData) const noexcept +void FFT::performFrequencyOnlyForwardTransform (float* inputOutputData, bool ignoreNegativeFreqs) const noexcept { if (size == 1) return; - performRealOnlyForwardTransform (inputOutputData); + performRealOnlyForwardTransform (inputOutputData, ignoreNegativeFreqs); auto* out = reinterpret_cast*> (inputOutputData); - for (auto i = 0; i < size; ++i) + const auto limit = ignoreNegativeFreqs ? (size / 2) + 1 : size; + + for (int i = 0; i < limit; ++i) inputOutputData[i] = std::abs (out[i]); - zeromem (&inputOutputData[size], static_cast (size) * sizeof (float)); + zeromem (inputOutputData + limit, static_cast (size * 2 - limit) * sizeof (float)); } -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT.h b/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT.h index 27589820..a6b3e9f6 100644 --- a/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT.h +++ b/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -51,6 +48,12 @@ class JUCE_API FFT */ FFT (int order); + /** Move constructor. */ + FFT (FFT&&) noexcept; + + /** Move assignment operator. */ + FFT& operator= (FFT&&) noexcept; + /** Destructor. */ ~FFT(); @@ -65,22 +68,22 @@ class JUCE_API FFT As the coefficients of the negative frequencies (frequencies higher than N/2 or pi) are the complex conjugate of their positive counterparts, it may not be necessary to calculate them for your particular application. - You can use dontCalculateNegativeFrequencies to let the FFT + You can use onlyCalculateNonNegativeFrequencies to let the FFT engine know that you do not plan on using them. Note that this is only a hint: some FFT engines (currently only the Fallback engine), will still - calculate the negative frequencies even if dontCalculateNegativeFrequencies + calculate the negative frequencies even if onlyCalculateNonNegativeFrequencies is true. The size of the array passed in must be 2 * getSize(), and the first half should contain your raw input sample data. On return, if - dontCalculateNegativeFrequencies is false, the array will contain size + onlyCalculateNonNegativeFrequencies is false, the array will contain size complex real + imaginary parts data interleaved. If - dontCalculateNegativeFrequencies is true, the array will contain at least + onlyCalculateNonNegativeFrequencies is true, the array will contain at least (size / 2) + 1 complex numbers. Both outputs can be passed to performRealOnlyInverseTransform() in order to convert it back to reals. */ void performRealOnlyForwardTransform (float* inputOutputData, - bool dontCalculateNegativeFrequencies = false) const noexcept; + bool onlyCalculateNonNegativeFrequencies = false) const noexcept; /** Performs a reverse operation to data created in performRealOnlyForwardTransform(). @@ -94,8 +97,13 @@ class JUCE_API FFT /** Takes an array and simply transforms it to the magnitude frequency response spectrum. This may be handy for things like frequency displays or analysis. The size of the array passed in must be 2 * getSize(). + + On return, if onlyCalculateNonNegativeFrequencies is false, the array will contain size + magnitude values. If onlyCalculateNonNegativeFrequencies is true, the array will contain + at least size / 2 + 1 magnitude values. */ - void performFrequencyOnlyForwardTransform (float* inputOutputData) const noexcept; + void performFrequencyOnlyForwardTransform (float* inputOutputData, + bool onlyCalculateNonNegativeFrequencies = false) const noexcept; /** Returns the number of data points that this FFT was created to work with. */ int getSize() const noexcept { return size; } @@ -118,5 +126,4 @@ class JUCE_API FFT JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FFT) }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT_test.cpp b/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT_test.cpp index 453fda0b..ba83cb28 100644 --- a/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT_test.cpp +++ b/JuceLibraryCode/modules/juce_dsp/frequency/juce_FFT_test.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,12 +23,10 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { -struct FFTUnitTest : public UnitTest +struct FFTUnitTest final : public UnitTest { FFTUnitTest() : UnitTest ("FFT", UnitTestCategories::dsp) @@ -64,7 +61,7 @@ struct FFTUnitTest : public UnitTest / static_cast (n)); for (size_t i = 0; i < n; ++i) - out[i] = freqConvolution (in, static_cast(i) * base_freq, n); + out[i] = freqConvolution (in, static_cast (i) * base_freq, n); } static void performReferenceFourier (const float* in, Complex* out, @@ -79,7 +76,7 @@ struct FFTUnitTest : public UnitTest / static_cast (n)); for (size_t i = 0; i < n; ++i) - out[i] = freqConvolution (buffer.getData(), static_cast(i) * base_freq, n); + out[i] = freqConvolution (buffer.getData(), static_cast (i) * base_freq, n); } @@ -136,7 +133,7 @@ struct FFTUnitTest : public UnitTest struct FrequencyOnlyTest { - static void run(FFTUnitTest& u) + static void run (FFTUnitTest& u) { Random random (378272); for (size_t order = 0; order <= 8; ++order) @@ -145,26 +142,30 @@ struct FFTUnitTest : public UnitTest FFT fft ((int) order); - HeapBlock inout (n << 1), reference (n << 1); - HeapBlock> frequency (n); + std::vector inout ((size_t) n << 1), reference ((size_t) n << 1); + std::vector> frequency (n); - fillRandom (random, inout.getData(), n); - zeromem (reference.getData(), sizeof (float) * (n << 1)); - performReferenceFourier (inout.getData(), frequency.getData(), n, false); + fillRandom (random, inout.data(), n); + zeromem (reference.data(), sizeof (float) * ((size_t) n << 1)); + performReferenceFourier (inout.data(), frequency.data(), n, false); for (size_t i = 0; i < n; ++i) - reference.getData()[i] = std::abs (frequency.getData()[i]); - - fft.performFrequencyOnlyForwardTransform (inout.getData()); - - u.expect (checkArrayIsSimilar (inout.getData(), reference.getData(), n)); + reference[i] = std::abs (frequency[i]); + + for (auto ignoreNegative : { false, true }) + { + auto inoutCopy = inout; + fft.performFrequencyOnlyForwardTransform (inoutCopy.data(), ignoreNegative); + auto numMatching = ignoreNegative ? (n / 2) + 1 : n; + u.expect (checkArrayIsSimilar (inoutCopy.data(), reference.data(), numMatching)); + } } } }; struct ComplexTest { - static void run(FFTUnitTest& u) + static void run (FFTUnitTest& u) { Random random (378272); @@ -211,5 +212,4 @@ struct FFTUnitTest : public UnitTest static FFTUnitTest fftUnitTest; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Windowing.cpp b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Windowing.cpp index 9cfa69ed..e6090e40 100644 --- a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Windowing.cpp +++ b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Windowing.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,13 +23,11 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { template -static inline FloatType ncos (size_t order, size_t i, size_t size) noexcept +static FloatType ncos (size_t order, size_t i, size_t size) noexcept { return std::cos (static_cast (order * i) * MathConstants::pi / static_cast (size - 1)); @@ -137,14 +134,16 @@ void WindowingFunction::fillWindowingTables (FloatType* samples, size case kaiser: { const double factor = 1.0 / SpecialFunctions::besselI0 (beta); + const auto doubleSize = (double) size; for (size_t i = 0; i < size; ++i) - samples[i] = static_cast (SpecialFunctions::besselI0 (beta * std::sqrt (1.0 - std::pow ((i - 0.5 * (size - 1.0)) - / ( 0.5 * (size - 1.0)), 2.0))) + samples[i] = static_cast (SpecialFunctions::besselI0 (beta * std::sqrt (1.0 - std::pow (((double) i - 0.5 * (doubleSize - 1.0)) + / ( 0.5 * (doubleSize - 1.0)), 2.0))) * factor); } break; + case numWindowingMethods: default: jassertfalse; break; @@ -165,7 +164,7 @@ void WindowingFunction::fillWindowingTables (FloatType* samples, size } template -void WindowingFunction::multiplyWithWindowingTable (FloatType* samples, size_t size) noexcept +void WindowingFunction::multiplyWithWindowingTable (FloatType* samples, size_t size) const noexcept { FloatVectorOperations::multiply (samples, windowTable.getRawDataPointer(), jmin (static_cast (size), windowTable.size())); } @@ -175,20 +174,20 @@ const char* WindowingFunction::getWindowingMethodName (WindowingMetho { switch (type) { - case rectangular: return "Rectangular"; - case triangular: return "Triangular"; - case hann: return "Hann"; - case hamming: return "Hamming"; - case blackman: return "Blackman"; - case blackmanHarris: return "Blackman-Harris"; - case flatTop: return "Flat Top"; - case kaiser: return "Kaiser"; - default: jassertfalse; return ""; + case rectangular: return "Rectangular"; + case triangular: return "Triangular"; + case hann: return "Hann"; + case hamming: return "Hamming"; + case blackman: return "Blackman"; + case blackmanHarris: return "Blackman-Harris"; + case flatTop: return "Flat Top"; + case kaiser: return "Kaiser"; + case numWindowingMethods: + default: jassertfalse; return ""; } } template class WindowingFunction; template class WindowingFunction; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Windowing.h b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Windowing.h index c085907d..5fac54d8 100644 --- a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Windowing.h +++ b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Windowing.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -95,7 +92,7 @@ class JUCE_API WindowingFunction bool normalise = true, FloatType beta = 0) noexcept; /** Multiplies the content of a buffer with the given window. */ - void multiplyWithWindowingTable (FloatType* samples, size_t size) noexcept; + void multiplyWithWindowingTable (FloatType* samples, size_t size) const noexcept; /** Returns the name of a given windowing method. */ static const char* getWindowingMethodName (WindowingMethod) noexcept; @@ -108,5 +105,4 @@ class JUCE_API WindowingFunction JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowingFunction) }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/juce_dsp.cpp b/JuceLibraryCode/modules/juce_dsp/juce_dsp.cpp index 0e4783e3..ebca4b3b 100644 --- a/JuceLibraryCode/modules/juce_dsp/juce_dsp.cpp +++ b/JuceLibraryCode/modules/juce_dsp/juce_dsp.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -35,12 +34,6 @@ #include "juce_dsp.h" -#if ! JUCE_HAS_CONSTEXPR - #ifndef JUCE_DEMO_RUNNER - #error "The juce_dsp module requires a compiler that supports constexpr" - #endif -#else - #ifndef JUCE_USE_VDSP_FRAMEWORK #define JUCE_USE_VDSP_FRAMEWORK 1 #endif @@ -55,10 +48,22 @@ #include #endif +#if _IPP_SEQUENTIAL_STATIC || _IPP_SEQUENTIAL_DYNAMIC || _IPP_PARALLEL_STATIC || _IPP_PARALLEL_DYNAMIC + #include + #include + #define JUCE_IPP_AVAILABLE 1 +#endif + #include "processors/juce_FIRFilter.cpp" #include "processors/juce_IIRFilter.cpp" -#include "processors/juce_LadderFilter.cpp" +#include "processors/juce_FirstOrderTPTFilter.cpp" +#include "processors/juce_Panner.cpp" #include "processors/juce_Oversampling.cpp" +#include "processors/juce_BallisticsFilter.cpp" +#include "processors/juce_LinkwitzRileyFilter.cpp" +#include "processors/juce_DelayLine.cpp" +#include "processors/juce_DryWetMixer.cpp" +#include "processors/juce_StateVariableTPTFilter.cpp" #include "maths/juce_SpecialFunctions.cpp" #include "maths/juce_Matrix.cpp" #include "maths/juce_LookupTable.cpp" @@ -66,16 +71,22 @@ #include "frequency/juce_Convolution.cpp" #include "frequency/juce_Windowing.cpp" #include "filter_design/juce_FilterDesign.cpp" +#include "widgets/juce_LadderFilter.cpp" +#include "widgets/juce_Compressor.cpp" +#include "widgets/juce_NoiseGate.cpp" +#include "widgets/juce_Limiter.cpp" +#include "widgets/juce_Phaser.cpp" +#include "widgets/juce_Chorus.cpp" #if JUCE_USE_SIMD - #if defined(__i386__) || defined(__amd64__) || defined(_M_X64) || defined(_X86_) || defined(_M_IX86) + #if JUCE_INTEL #ifdef __AVX2__ - #include "native/juce_avx_SIMDNativeOps.cpp" + #include "native/juce_SIMDNativeOps_avx.cpp" #else - #include "native/juce_sse_SIMDNativeOps.cpp" + #include "native/juce_SIMDNativeOps_sse.cpp" #endif - #elif defined(__arm__) || defined(_M_ARM) || defined (__arm64__) || defined (__aarch64__) - #include "native/juce_neon_SIMDNativeOps.cpp" + #elif JUCE_ARM + #include "native/juce_SIMDNativeOps_neon.cpp" #else #error "SIMD register support not implemented for this platform" #endif @@ -90,8 +101,8 @@ #endif #include "containers/juce_AudioBlock_test.cpp" + #include "frequency/juce_Convolution_test.cpp" #include "frequency/juce_FFT_test.cpp" #include "processors/juce_FIRFilter_test.cpp" -#endif - + #include "processors/juce_ProcessorChain_test.cpp" #endif diff --git a/JuceLibraryCode/modules/juce_dsp/juce_dsp.h b/JuceLibraryCode/modules/juce_dsp/juce_dsp.h index 50b6fd96..4208c342 100644 --- a/JuceLibraryCode/modules/juce_dsp/juce_dsp.h +++ b/JuceLibraryCode/modules/juce_dsp/juce_dsp.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -29,21 +28,21 @@ The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. For details about the syntax and how to create or use a module, see the - JUCE Module Format.txt file. + JUCE Module Format.md file. BEGIN_JUCE_MODULE_DECLARATION ID: juce_dsp vendor: juce - version: 5.4.7 + version: 7.0.10 name: JUCE DSP classes description: Classes for audio buffer manipulation, digital audio processing, filtering, oversampling, fast math functions etc. website: http://www.juce.com/juce license: GPL/Commercial - minimumCppStandard: 14 + minimumCppStandard: 17 - dependencies: juce_audio_basics, juce_audio_formats + dependencies: juce_audio_formats OSXFrameworks: Accelerate iOSFrameworks: Accelerate @@ -59,15 +58,9 @@ #include #include -#if ! JUCE_HAS_CONSTEXPR - #ifndef JUCE_DEMO_RUNNER - #error "The juce_dsp module requires a compiler that supports constexpr" - #endif -#else +#if defined (_M_X64) || defined (__amd64__) || defined (__SSE2__) || (defined (_M_IX86_FP) && _M_IX86_FP == 2) -#if defined(_M_X64) || defined(__amd64__) || defined(__SSE2__) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) - - #if defined(_M_X64) || defined(__amd64__) + #if defined (_M_X64) || defined (__amd64__) #ifndef __SSE2__ #define __SSE2__ #endif @@ -81,7 +74,9 @@ #include #endif -#elif defined (__ARM_NEON__) || defined (__ARM_NEON) || defined (__arm64__) || defined (__aarch64__) +// it's ok to check for _M_ARM below as this is only defined on Windows for Arm 32-bit +// which has a minimum requirement of armv7, which supports neon. +#elif defined (__ARM_NEON__) || defined (__ARM_NEON) || defined (__arm64__) || defined (__aarch64__) || defined (_M_ARM) || defined (_M_ARM64) #ifndef JUCE_USE_SIMD #define JUCE_USE_SIMD 1 @@ -100,18 +95,15 @@ #ifndef JUCE_VECTOR_CALLTYPE // __vectorcall does not work on 64-bit due to internal compiler error in - // release mode in both VS2015 and VS2017. Re-enable when Microsoft fixes this - #if _MSC_VER && JUCE_USE_SIMD && ! (defined(_M_X64) || defined(__amd64__)) + // release mode VS2017. Re-enable when Microsoft fixes this + #if _MSC_VER && JUCE_USE_SIMD && ! (defined (_M_X64) || defined (__amd64__)) #define JUCE_VECTOR_CALLTYPE __vectorcall #else #define JUCE_VECTOR_CALLTYPE #endif #endif -#include #include -#include -#include //============================================================================== @@ -133,10 +125,11 @@ If this flag is set, then JUCE will use Intel's MKL for JUCE's FFT and convolution classes. - The folder containing the mkl_dfti.h header must be in your header - search paths when using this flag. You also need to add all the necessary - intel mkl libraries to the "External Libraries to Link" field in the - Projucer. + If you're using the Projucer's Visual Studio exporter, you should also set + the "Use MKL Library (oneAPI)" option in the exporter settings to + "Sequential" or "Parallel". If you're not using the Visual Studio exporter, + the folder containing the mkl_dfti.h header must be in your header search + paths, and you must link against all the necessary MKL libraries. */ #ifndef JUCE_DSP_USE_INTEL_MKL #define JUCE_DSP_USE_INTEL_MKL 0 @@ -195,54 +188,57 @@ #undef Factor #undef check -namespace juce +namespace juce::dsp +{ + +template +using Complex = std::complex; + +template +using FixedSizeFunction = juce::FixedSizeFunction; + +//============================================================================== +namespace util { - namespace dsp - { - template - using Complex = std::complex; - - //============================================================================== - namespace util - { - /** Use this function to prevent denormals on intel CPUs. - This function will work with both primitives and simple containers. - */ - #if JUCE_DSP_ENABLE_SNAP_TO_ZERO - inline void snapToZero (float& x) noexcept { JUCE_SNAP_TO_ZERO (x); } - #ifndef DOXYGEN - inline void snapToZero (double& x) noexcept { JUCE_SNAP_TO_ZERO (x); } - inline void snapToZero (long double& x) noexcept { JUCE_SNAP_TO_ZERO (x); } - #endif - #else - inline void snapToZero (float& x) noexcept { ignoreUnused (x); } - #ifndef DOXYGEN - inline void snapToZero (double& x) noexcept { ignoreUnused (x); } - inline void snapToZero (long double& x) noexcept { ignoreUnused (x); } - #endif - #endif - } - } + /** Use this function to prevent denormals on intel CPUs. + This function will work with both primitives and simple containers. + */ + #if JUCE_DSP_ENABLE_SNAP_TO_ZERO + inline void snapToZero (float& x) noexcept { JUCE_SNAP_TO_ZERO (x); } + #ifndef DOXYGEN + inline void snapToZero (double& x) noexcept { JUCE_SNAP_TO_ZERO (x); } + inline void snapToZero (long double& x) noexcept { JUCE_SNAP_TO_ZERO (x); } + #endif + #else + inline void snapToZero ([[maybe_unused]] float& x) noexcept {} + #ifndef DOXYGEN + inline void snapToZero ([[maybe_unused]] double& x) noexcept {} + inline void snapToZero ([[maybe_unused]] long double& x) noexcept {} + #endif + #endif +} + } //============================================================================== #if JUCE_USE_SIMD - #include "native/juce_fallback_SIMDNativeOps.h" + #include "native/juce_SIMDNativeOps_fallback.h" // include the correct native file for this build target CPU - #if defined(__i386__) || defined(__amd64__) || defined(_M_X64) || defined(_X86_) || defined(_M_IX86) + #if defined (__i386__) || defined (__amd64__) || defined (_M_X64) || defined (_X86_) || defined (_M_IX86) #ifdef __AVX2__ - #include "native/juce_avx_SIMDNativeOps.h" + #include "native/juce_SIMDNativeOps_avx.h" #else - #include "native/juce_sse_SIMDNativeOps.h" + #include "native/juce_SIMDNativeOps_sse.h" #endif - #elif defined(__arm__) || defined(_M_ARM) || defined (__arm64__) || defined (__aarch64__) - #include "native/juce_neon_SIMDNativeOps.h" + #elif JUCE_ARM + #include "native/juce_SIMDNativeOps_neon.h" #else #error "SIMD register support not implemented for this platform" #endif #include "containers/juce_SIMDRegister.h" + #include "containers/juce_SIMDRegister_Impl.h" #endif #include "maths/juce_SpecialFunctions.h" @@ -257,19 +253,30 @@ namespace juce #include "processors/juce_ProcessorWrapper.h" #include "processors/juce_ProcessorChain.h" #include "processors/juce_ProcessorDuplicator.h" -#include "processors/juce_Bias.h" -#include "processors/juce_Gain.h" -#include "processors/juce_WaveShaper.h" #include "processors/juce_IIRFilter.h" +#include "processors/juce_IIRFilter_Impl.h" #include "processors/juce_FIRFilter.h" -#include "processors/juce_Oscillator.h" -#include "processors/juce_LadderFilter.h" #include "processors/juce_StateVariableFilter.h" +#include "processors/juce_FirstOrderTPTFilter.h" +#include "processors/juce_Panner.h" +#include "processors/juce_DelayLine.h" #include "processors/juce_Oversampling.h" -#include "processors/juce_Reverb.h" +#include "processors/juce_BallisticsFilter.h" +#include "processors/juce_LinkwitzRileyFilter.h" +#include "processors/juce_DryWetMixer.h" +#include "processors/juce_StateVariableTPTFilter.h" #include "frequency/juce_FFT.h" #include "frequency/juce_Convolution.h" #include "frequency/juce_Windowing.h" #include "filter_design/juce_FilterDesign.h" - -#endif +#include "widgets/juce_Reverb.h" +#include "widgets/juce_Bias.h" +#include "widgets/juce_Gain.h" +#include "widgets/juce_WaveShaper.h" +#include "widgets/juce_Oscillator.h" +#include "widgets/juce_LadderFilter.h" +#include "widgets/juce_Compressor.h" +#include "widgets/juce_NoiseGate.h" +#include "widgets/juce_Limiter.h" +#include "widgets/juce_Phaser.h" +#include "widgets/juce_Chorus.h" diff --git a/JuceLibraryCode/modules/juce_dsp/juce_dsp.mm b/JuceLibraryCode/modules/juce_dsp/juce_dsp.mm index 74cc9730..b82237f3 100644 --- a/JuceLibraryCode/modules/juce_dsp/juce_dsp.mm +++ b/JuceLibraryCode/modules/juce_dsp/juce_dsp.mm @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_FastMathApproximations.h b/JuceLibraryCode/modules/juce_dsp/maths/juce_FastMathApproximations.h index 4dd8c5d9..45041592 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_FastMathApproximations.h +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_FastMathApproximations.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -261,5 +258,4 @@ struct FastMathApproximations } }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue.h b/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue.h index e2f53406..81e38459 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue.h +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { //============================================================================== @@ -109,7 +106,7 @@ class LogRampedValue : public SmoothedValueBase > */ void setTargetValue (FloatType newValue) noexcept { - if (newValue == this->target) + if (approximatelyEqual (newValue, this->target)) return; if (stepsToTarget <= 0) @@ -186,5 +183,4 @@ class LogRampedValue : public SmoothedValueBase > FloatType temp = 0, source = 0, r = 0, d = 1; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue_test.cpp b/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue_test.cpp index 57c4f887..32d86afc 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue_test.cpp +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue_test.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,14 +23,12 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { static CommonSmoothedValueTests > commonLogRampedValueTests; -class LogRampedValueTests : public UnitTest +class LogRampedValueTests final : public UnitTest { public: LogRampedValueTests() @@ -93,5 +90,4 @@ class LogRampedValueTests : public UnitTest static LogRampedValueTests LogRampedValueTests; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_LookupTable.cpp b/JuceLibraryCode/modules/juce_dsp/maths/juce_LookupTable.cpp index fc037ce6..51095e9f 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_LookupTable.cpp +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_LookupTable.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { template @@ -36,7 +33,7 @@ LookupTable::LookupTable() } template -LookupTable::LookupTable (const std::function& functionToApproximate, +LookupTable::LookupTable (const std::function& functionToApproximate, size_t numPointsToUse) { initialise (functionToApproximate, numPointsToUse); @@ -44,7 +41,7 @@ LookupTable::LookupTable (const std::function& fun //============================================================================== template -void LookupTable::initialise (const std::function& functionToApproximate, +void LookupTable::initialise (const std::function& functionToApproximate, size_t numPointsToUse) { data.resize (static_cast (getRequiredBufferSize (numPointsToUse))); @@ -72,7 +69,7 @@ void LookupTable::prepare() noexcept } template -void LookupTableTransform::initialise (const std::function& functionToApproximate, +void LookupTableTransform::initialise (const std::function& functionToApproximate, FloatType minInputValueToUse, FloatType maxInputValueToUse, size_t numPoints) @@ -98,7 +95,7 @@ void LookupTableTransform::initialise (const std::function -double LookupTableTransform::calculateMaxRelativeError (const std::function& functionToApproximate, +double LookupTableTransform::calculateMaxRelativeError (const std::function& functionToApproximate, FloatType minInputValue, FloatType maxInputValue, size_t numPoints, @@ -153,5 +150,4 @@ template class LookupTable; template class LookupTableTransform; template class LookupTableTransform; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_LookupTable.h b/JuceLibraryCode/modules/juce_dsp/maths/juce_LookupTable.h index c7a167a6..a9021571 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_LookupTable.h +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_LookupTable.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -68,7 +65,7 @@ class LookupTable mapping from the integer range [0, numPointsToUse - 1]. @param numPointsToUse The number of pre-calculated values stored. */ - LookupTable (const std::function& functionToApproximate, size_t numPointsToUse); + LookupTable (const std::function& functionToApproximate, size_t numPointsToUse); /** Initialises or changes the parameters of a LookupTable object. @@ -80,7 +77,7 @@ class LookupTable mapping from the integer range [0, numPointsToUse - 1]. @param numPointsToUse The number of pre-calculated values stored. */ - void initialise (const std::function& functionToApproximate, size_t numPointsToUse); + void initialise (const std::function& functionToApproximate, size_t numPointsToUse); //============================================================================== /** Calculates the approximated value for the given index without range checking. @@ -123,7 +120,7 @@ class LookupTable */ FloatType get (FloatType index) const noexcept { - if (index >= getNumPoints()) + if (index >= (FloatType) getNumPoints()) index = static_cast (getGuardIndex()); else if (index < 0) index = {}; @@ -196,7 +193,7 @@ class LookupTableTransform fail for values higher than this. @param numPoints The number of pre-calculated values stored. */ - LookupTableTransform (const std::function& functionToApproximate, + LookupTableTransform (const std::function& functionToApproximate, FloatType minInputValueToUse, FloatType maxInputValueToUse, size_t numPoints) @@ -215,7 +212,7 @@ class LookupTableTransform fail for values higher than this. @param numPoints The number of pre-calculated values stored. */ - void initialise (const std::function& functionToApproximate, + void initialise (const std::function& functionToApproximate, FloatType minInputValueToUse, FloatType maxInputValueToUse, size_t numPoints); @@ -309,7 +306,7 @@ class LookupTableTransform accuracy of the error calculation. If it's zero then 100 * numPoints will be used. */ - static double calculateMaxRelativeError (const std::function& functionToApproximate, + static double calculateMaxRelativeError (const std::function& functionToApproximate, FloatType minInputValue, FloatType maxInputValue, size_t numPoints, @@ -327,5 +324,4 @@ class LookupTableTransform JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookupTableTransform) }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.cpp b/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.cpp index 23f70ce7..f60bc194 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.cpp +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { template @@ -35,7 +32,7 @@ Matrix Matrix::identity (size_t size) Matrix result (size, size); for (size_t i = 0; i < size; ++i) - result(i, i) = 1; + result (i, i) = 1; return result; } @@ -61,8 +58,8 @@ Matrix Matrix::toeplitz (const Matrix& vector, size_t template Matrix Matrix::hankel (const Matrix& vector, size_t size, size_t offset) { - jassert(vector.isOneColumnVector()); - jassert(vector.rows >= (2 * (size - 1) + 1)); + jassert (vector.isOneColumnVector()); + jassert (vector.rows >= (2 * (size - 1) + 1)); Matrix result (size, size); @@ -177,7 +174,7 @@ bool Matrix::solve (Matrix& b) const noexcept { auto denominator = A (0,0); - if (denominator == 0) + if (approximatelyEqual (denominator, (ElementType) 0)) return false; b (0, 0) /= denominator; @@ -188,7 +185,7 @@ bool Matrix::solve (Matrix& b) const noexcept { auto denominator = A (0, 0) * A (1, 1) - A (0, 1) * A (1, 0); - if (denominator == 0) + if (approximatelyEqual (denominator, (ElementType) 0)) return false; auto factor = (1 / denominator); @@ -205,7 +202,7 @@ bool Matrix::solve (Matrix& b) const noexcept + A (0, 1) * (A (1, 2) * A (2, 0) - A (1, 0) * A (2, 2)) + A (0, 2) * (A (1, 0) * A (2, 1) - A (1, 1) * A (2, 0)); - if (denominator == 0) + if (approximatelyEqual (denominator, (ElementType) 0)) return false; auto factor = 1 / denominator; @@ -232,10 +229,10 @@ bool Matrix::solve (Matrix& b) const noexcept for (size_t j = 0; j < n; ++j) { - if (M (j, j) == 0) + if (approximatelyEqual (M (j, j), (ElementType) 0)) { auto i = j; - while (i < n && M (i, j) == 0) + while (i < n && approximatelyEqual (M (i, j), (ElementType) 0)) ++i; if (i == n) @@ -314,5 +311,4 @@ String Matrix::toString() const template class Matrix; template class Matrix; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.h b/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.h index cb360b43..e84d6d4e 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.h +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -37,7 +34,7 @@ namespace dsp @tags{DSP} */ -template +template class Matrix { public: @@ -165,7 +162,7 @@ class Matrix inline Matrix& hadarmard (const Matrix& other) noexcept { return apply (other, [] (ElementType a, ElementType b) { return a * b; } ); } /** Does a hadarmard product with a and b returns the result. */ - static inline Matrix hadarmard (const Matrix& a, const Matrix& b) { Matrix result (a); result.hadarmard (b); return result; } + static Matrix hadarmard (const Matrix& a, const Matrix& b) { Matrix result (a); result.hadarmard (b); return result; } //============================================================================== /** Compare to matrices with a given tolerance */ @@ -251,5 +248,4 @@ class Matrix JUCE_LEAK_DETECTOR (Matrix) }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix_test.cpp b/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix_test.cpp index 3e47ac5d..22e03c58 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix_test.cpp +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix_test.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,12 +23,10 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { -struct LinearAlgebraUnitTest : public UnitTest +struct LinearAlgebraUnitTest final : public UnitTest { LinearAlgebraUnitTest() : UnitTest ("Linear Algebra UnitTests", UnitTestCategories::dsp) @@ -48,7 +45,7 @@ struct LinearAlgebraUnitTest : public UnitTest Matrix mat2 (2, 4, data2); Matrix mat3 (2, 4, data3); - u.expect((mat1 + mat2) == mat3); + u.expect ((mat1 + mat2) == mat3); } }; @@ -65,7 +62,7 @@ struct LinearAlgebraUnitTest : public UnitTest Matrix mat2 (2, 4, data2); Matrix mat3 (2, 4, data3); - u.expect((mat1 - mat2) == mat3); + u.expect ((mat1 - mat2) == mat3); } }; @@ -115,7 +112,7 @@ struct LinearAlgebraUnitTest : public UnitTest Matrix mat2 (4, 2, data2); Matrix mat3 (2, 2, data3); - u.expect((mat1 * mat2) == mat3); + u.expect ((mat1 * mat2) == mat3); } }; @@ -170,5 +167,4 @@ struct LinearAlgebraUnitTest : public UnitTest static LinearAlgebraUnitTest linearAlgebraUnitTest; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_Phase.h b/JuceLibraryCode/modules/juce_dsp/maths/juce_Phase.h index 31425f53..10760511 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_Phase.h +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_Phase.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -64,5 +61,4 @@ struct Phase Type phase = 0; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_Polynomial.h b/JuceLibraryCode/modules/juce_dsp/maths/juce_Polynomial.h index 4d0b5fd8..744b6db6 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_Polynomial.h +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_Polynomial.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -95,7 +92,7 @@ class Polynomial FloatingType y (0); for (int i = coeffs.size(); --i >= 0;) - y = (x * y) + coeffs.getUnchecked(i); + y = (x * y) + coeffs.getUnchecked (i); return y; } @@ -165,5 +162,4 @@ class Polynomial JUCE_LEAK_DETECTOR (Polynomial) }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_SpecialFunctions.cpp b/JuceLibraryCode/modules/juce_dsp/maths/juce_SpecialFunctions.cpp index 74ee1f6e..cfe6dfb9 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_SpecialFunctions.cpp +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_SpecialFunctions.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { double SpecialFunctions::besselI0 (double x) noexcept @@ -140,5 +137,4 @@ Complex SpecialFunctions::asne (Complex w, double k) noexcept return 2.0 / MathConstants::pi * std::asin (last); } -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_SpecialFunctions.h b/JuceLibraryCode/modules/juce_dsp/maths/juce_SpecialFunctions.h index 51ee0228..7cdcafd3 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_SpecialFunctions.h +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_SpecialFunctions.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,11 +23,8 @@ ============================================================================== */ -namespace juce +namespace juce::dsp { -namespace dsp -{ - /** Contains miscellaneous filter design and windowing functions. @@ -64,5 +60,4 @@ struct SpecialFunctions static Complex asne (Complex w, double k) noexcept; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_avx.cpp b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_avx.cpp new file mode 100644 index 00000000..6ae058bf --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_avx.cpp @@ -0,0 +1,55 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + DEFINE_AVX_SIMD_CONST (int32_t, float, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 }; + DEFINE_AVX_SIMD_CONST (int32_t, float, kEvenHighBit) = { static_cast (0x80000000), 0, static_cast (0x80000000), 0, static_cast (0x80000000), 0, static_cast (0x80000000), 0 }; + DEFINE_AVX_SIMD_CONST (float, float, kOne) = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + + DEFINE_AVX_SIMD_CONST (int64_t, double, kAllBitsSet) = { -1, -1, -1, -1 }; + DEFINE_AVX_SIMD_CONST (int64_t, double, kEvenHighBit) = { static_cast (0x8000000000000000), 0, static_cast (0x8000000000000000), 0 }; + DEFINE_AVX_SIMD_CONST (double, double, kOne) = { 1.0, 1.0, 1.0, 1.0 }; + + DEFINE_AVX_SIMD_CONST (int8_t, int8_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + + DEFINE_AVX_SIMD_CONST (uint8_t, uint8_t, kAllBitsSet) = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + DEFINE_AVX_SIMD_CONST (uint8_t, uint8_t, kHighBit) = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }; + + DEFINE_AVX_SIMD_CONST (int16_t, int16_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + + DEFINE_AVX_SIMD_CONST (uint16_t, uint16_t, kAllBitsSet) = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; + DEFINE_AVX_SIMD_CONST (uint16_t, uint16_t, kHighBit) = { 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000 }; + + DEFINE_AVX_SIMD_CONST (int32_t, int32_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 }; + + DEFINE_AVX_SIMD_CONST (uint32_t, uint32_t, kAllBitsSet) = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; + DEFINE_AVX_SIMD_CONST (uint32_t, uint32_t, kHighBit) = { 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000 }; + + DEFINE_AVX_SIMD_CONST (int64_t, int64_t, kAllBitsSet) = { -1LL, -1LL, -1LL, -1LL }; + + DEFINE_AVX_SIMD_CONST (uint64_t, uint64_t, kAllBitsSet) = { 0xffffffffffffffffULL, 0xffffffffffffffffULL, 0xffffffffffffffffULL, 0xffffffffffffffffULL }; + DEFINE_AVX_SIMD_CONST (uint64_t, uint64_t, kHighBit) = { 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL }; +} diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_avx_SIMDNativeOps.h b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_avx.h similarity index 98% rename from JuceLibraryCode/modules/juce_dsp/native/juce_avx_SIMDNativeOps.h rename to JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_avx.h index cf9ebb1a..cc2fd5bc 100644 --- a/JuceLibraryCode/modules/juce_dsp/native/juce_avx_SIMDNativeOps.h +++ b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_avx.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,31 +23,26 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { #ifndef DOXYGEN -#if JUCE_GCC && (__GNUC__ >= 6) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wignored-attributes" -#endif +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wignored-attributes") #ifdef _MSC_VER #define DECLARE_AVX_SIMD_CONST(type, name) \ - static __declspec(align(32)) const type name[32 / sizeof (type)] + static __declspec (align (32)) const type name[32 / sizeof (type)] #define DEFINE_AVX_SIMD_CONST(type, class_type, name) \ - __declspec(align(32)) const type SIMDNativeOps:: name[32 / sizeof (type)] + __declspec (align (32)) const type SIMDNativeOps:: name[32 / sizeof (type)] #else #define DECLARE_AVX_SIMD_CONST(type, name) \ - static const type name[32 / sizeof (type)] __attribute__((aligned(32))) + static const type name[32 / sizeof (type)] __attribute__ ((aligned (32))) #define DEFINE_AVX_SIMD_CONST(type, class_type, name) \ - const type SIMDNativeOps:: name[32 / sizeof (type)] __attribute__((aligned(32))) + const type SIMDNativeOps:: name[32 / sizeof (type)] __attribute__ ((aligned (32))) #endif @@ -659,9 +653,6 @@ struct SIMDNativeOps #endif -#if JUCE_GCC && (__GNUC__ >= 6) - #pragma GCC diagnostic pop -#endif +JUCE_END_IGNORE_WARNINGS_GCC_LIKE -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_fallback.h similarity index 91% rename from JuceLibraryCode/modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h rename to JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_fallback.h index 32051bba..14069770 100644 --- a/JuceLibraryCode/modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h +++ b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_fallback.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** A template specialisation to find corresponding mask type for primitives. */ @@ -43,8 +40,10 @@ namespace SIMDInternal template <> struct MaskTypeFor > { using type = uint32_t; }; template <> struct MaskTypeFor > { using type = uint64_t; }; - template struct PrimitiveType { using type = typename std::remove_cv::type; }; - template struct PrimitiveType> { using type = typename std::remove_cv::type; }; + template using MaskType = typename MaskTypeFor::type; + + template struct PrimitiveType { using type = std::remove_cv_t; }; + template struct PrimitiveType> { using type = std::remove_cv_t; }; template struct Log2Helper { enum { value = Log2Helper::value + 1 }; }; template <> struct Log2Helper<1> { enum { value = 0 }; }; @@ -64,7 +63,7 @@ struct SIMDFallbackOps static constexpr size_t bits = SIMDInternal::Log2Helper<(int) n>::value; // helper types - using MaskType = typename SIMDInternal::MaskTypeFor::type; + using MaskType = SIMDInternal::MaskType; union UnionType { vSIMDType v; ScalarType s[n]; }; union UnionMaskType { vSIMDType v; MaskType m[n]; }; @@ -115,7 +114,7 @@ struct SIMDFallbackOps auto retval = static_cast (0); for (size_t i = 0; i < n; ++i) - retval += a.s[i]; + retval = static_cast (retval + a.s[i]); return retval; } @@ -125,10 +124,7 @@ struct SIMDFallbackOps UnionType a {av}; for (size_t i = 0; i < n; ++i) - { - jassert (a.s[i] >= ScalarType (0)); - a.s[i] = static_cast (static_cast (a.s[i])); - } + a.s[i] = static_cast (static_cast (a.s[i])); return a.v; } @@ -149,7 +145,7 @@ struct SIMDFallbackOps UnionType a {av}, b {bv}; for (size_t i = 0; i < n; ++i) - if (a.s[i] != b.s[i]) + if (! exactlyEqual (a.s[i], b.s[i])) return false; return true; @@ -183,8 +179,8 @@ struct SIMDFallbackOps struct ScalarOr { static forcedinline MaskType op (MaskType a, MaskType b) noexcept { return a | b; } }; struct ScalarXor { static forcedinline MaskType op (MaskType a, MaskType b) noexcept { return a ^ b; } }; struct ScalarNot { static forcedinline MaskType op (MaskType a, MaskType b) noexcept { return (~a) & b; } }; - struct ScalarEq { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return (a == b); } }; - struct ScalarNeq { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return (a != b); } }; + struct ScalarEq { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return exactlyEqual (a, b); } }; + struct ScalarNeq { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return ! exactlyEqual (a, b); } }; struct ScalarGt { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return (a > b); } }; struct ScalarGeq { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return (a >= b); } }; @@ -265,5 +261,4 @@ struct SIMDFallbackOps } }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_neon.cpp b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_neon.cpp new file mode 100644 index 00000000..b1a5e9e7 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_neon.cpp @@ -0,0 +1,45 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + DEFINE_NEON_SIMD_CONST (int32_t, float, kAllBitsSet) = { -1, -1, -1, -1 }; + DEFINE_NEON_SIMD_CONST (int32_t, float, kEvenHighBit) = { static_cast (0x80000000), 0, static_cast (0x80000000), 0 }; + DEFINE_NEON_SIMD_CONST (float, float, kOne) = { 1.0f, 1.0f, 1.0f, 1.0f }; + + #if JUCE_64BIT + DEFINE_NEON_SIMD_CONST (int64_t, double, kAllBitsSet) = { -1, -1 }; + DEFINE_NEON_SIMD_CONST (double, double, kOne) = { 1.0, 1.0 }; + #endif + + DEFINE_NEON_SIMD_CONST (int8_t, int8_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + DEFINE_NEON_SIMD_CONST (uint8_t, uint8_t, kAllBitsSet) = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + DEFINE_NEON_SIMD_CONST (int16_t, int16_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 }; + DEFINE_NEON_SIMD_CONST (uint16_t, uint16_t, kAllBitsSet) = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; + DEFINE_NEON_SIMD_CONST (int32_t, int32_t, kAllBitsSet) = { -1, -1, -1, -1 }; + DEFINE_NEON_SIMD_CONST (uint32_t, uint32_t, kAllBitsSet) = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; + DEFINE_NEON_SIMD_CONST (int64_t, int64_t, kAllBitsSet) = { -1, -1 }; + DEFINE_NEON_SIMD_CONST (uint64_t, uint64_t, kAllBitsSet) = { 0xffffffffffffffff, 0xffffffffffffffff }; +} diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_neon_SIMDNativeOps.h b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_neon.h similarity index 81% rename from JuceLibraryCode/modules/juce_dsp/native/juce_neon_SIMDNativeOps.h rename to JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_neon.h index bfd6e1c6..489f75d1 100644 --- a/JuceLibraryCode/modules/juce_dsp/native/juce_neon_SIMDNativeOps.h +++ b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_neon.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,31 +23,26 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { #ifndef DOXYGEN -#if JUCE_GCC && (__GNUC__ >= 6) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wignored-attributes" -#endif +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wignored-attributes") #ifdef _MSC_VER #define DECLARE_NEON_SIMD_CONST(type, name) \ - static __declspec(align(16)) const type name [16 / sizeof (type)] + static __declspec (align (16)) const type name [16 / sizeof (type)] #define DEFINE_NEON_SIMD_CONST(type, class_type, name) \ - __declspec(align(16)) const type SIMDNativeOps:: name [16 / sizeof (type)] + __declspec (align (16)) const type SIMDNativeOps:: name [16 / sizeof (type)] #else #define DECLARE_NEON_SIMD_CONST(type, name) \ - static const type name [16 / sizeof (type)] __attribute__((aligned(16))) + static const type name [16 / sizeof (type)] __attribute__ ((aligned (16))) #define DEFINE_NEON_SIMD_CONST(type, class_type, name) \ - const type SIMDNativeOps:: name [16 / sizeof (type)] __attribute__((aligned(16))) + const type SIMDNativeOps:: name [16 / sizeof (type)] __attribute__ ((aligned (16))) #endif @@ -73,9 +67,9 @@ struct SIMDNativeOps //============================================================================== static forcedinline vSIMDType expand (uint32_t s) noexcept { return vdupq_n_u32 (s); } static forcedinline vSIMDType load (const uint32_t* a) noexcept { return vld1q_u32 (a); } - static forcedinline void store (vSIMDType value, uint32_t* a) noexcept { vst1q_u32 (a, value); } - static forcedinline uint32_t get (vSIMDType v, size_t i) noexcept { return v[i]; } - static forcedinline vSIMDType set (vSIMDType v, size_t i, uint32_t s) noexcept { v[i] = s; return v; } + static forcedinline void store (vSIMDType value, uint32_t* a) noexcept { vst1q_u32 (a, value); } + static forcedinline uint32_t get (vSIMDType v, size_t i) noexcept { return fb::get (v, i); } + static forcedinline vSIMDType set (vSIMDType v, size_t i, uint32_t s) noexcept { return fb::set (v, i, s); } static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_u32 (a, b); } static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_u32 (a, b); } static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_u32 (a, b); } @@ -119,9 +113,9 @@ struct SIMDNativeOps //============================================================================== static forcedinline vSIMDType expand (int32_t s) noexcept { return vdupq_n_s32 (s); } static forcedinline vSIMDType load (const int32_t* a) noexcept { return vld1q_s32 (a); } - static forcedinline void store (vSIMDType value, int32_t* a) noexcept { vst1q_s32 (a, value); } - static forcedinline int32_t get (vSIMDType v, size_t i) noexcept { return v[i]; } - static forcedinline vSIMDType set (vSIMDType v, size_t i, int32_t s) noexcept { v[i] = s; return v; } + static forcedinline void store (vSIMDType value, int32_t* a) noexcept { vst1q_s32 (a, value); } + static forcedinline int32_t get (vSIMDType v, size_t i) noexcept { return fb::get (v, i); } + static forcedinline vSIMDType set (vSIMDType v, size_t i, int32_t s) noexcept { return fb::set (v, i, s); } static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_s32 (a, b); } static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_s32 (a, b); } static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_s32 (a, b); } @@ -166,9 +160,9 @@ struct SIMDNativeOps //============================================================================== static forcedinline vSIMDType expand (int8_t s) noexcept { return vdupq_n_s8 (s); } static forcedinline vSIMDType load (const int8_t* a) noexcept { return vld1q_s8 (a); } - static forcedinline void store (vSIMDType value, int8_t* a) noexcept { vst1q_s8 (a, value); } - static forcedinline int8_t get (vSIMDType v, size_t i) noexcept { return v[i]; } - static forcedinline vSIMDType set (vSIMDType v, size_t i, int8_t s) noexcept { v[i] = s; return v; } + static forcedinline void store (vSIMDType value, int8_t* a) noexcept { vst1q_s8 (a, value); } + static forcedinline int8_t get (vSIMDType v, size_t i) noexcept { return fb::get (v, i); } + static forcedinline vSIMDType set (vSIMDType v, size_t i, int8_t s) noexcept { return fb::set (v, i, s); } static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_s8 (a, b); } static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_s8 (a, b); } static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_s8 (a, b); } @@ -185,7 +179,7 @@ struct SIMDNativeOps static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_s8 (a, b); } static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps::sum ((SIMDNativeOps::vSIMDType) notEqual (a, b)) == 0); } static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_s8 (a, b, c); } - static forcedinline int8_t sum (vSIMDType a) noexcept { return fb::sum (a); } + static forcedinline int8_t sum (vSIMDType a) noexcept { return fb::sum (a); } static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; } }; @@ -207,9 +201,9 @@ struct SIMDNativeOps //============================================================================== static forcedinline vSIMDType expand (uint8_t s) noexcept { return vdupq_n_u8 (s); } static forcedinline vSIMDType load (const uint8_t* a) noexcept { return vld1q_u8 (a); } - static forcedinline void store (vSIMDType value, uint8_t* a) noexcept { vst1q_u8 (a, value); } - static forcedinline uint8_t get (vSIMDType v, size_t i) noexcept { return v[i]; } - static forcedinline vSIMDType set (vSIMDType v, size_t i, uint8_t s) noexcept { v[i] = s; return v; } + static forcedinline void store (vSIMDType value, uint8_t* a) noexcept { vst1q_u8 (a, value); } + static forcedinline uint8_t get (vSIMDType v, size_t i) noexcept { return fb::get (v, i); } + static forcedinline vSIMDType set (vSIMDType v, size_t i, uint8_t s) noexcept { return fb::set (v, i, s); } static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_u8 (a, b); } static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_u8 (a, b); } static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_u8 (a, b); } @@ -226,7 +220,7 @@ struct SIMDNativeOps static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_u8 (a, b); } static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps::sum ((SIMDNativeOps::vSIMDType) notEqual (a, b)) == 0); } static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_u8 (a, b, c); } - static forcedinline uint8_t sum (vSIMDType a) noexcept { return fb::sum (a); } + static forcedinline uint8_t sum (vSIMDType a) noexcept { return fb::sum (a); } static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; } }; @@ -248,9 +242,9 @@ struct SIMDNativeOps //============================================================================== static forcedinline vSIMDType expand (int16_t s) noexcept { return vdupq_n_s16 (s); } static forcedinline vSIMDType load (const int16_t* a) noexcept { return vld1q_s16 (a); } - static forcedinline void store (vSIMDType value, int16_t* a) noexcept { vst1q_s16 (a, value); } - static forcedinline int16_t get (vSIMDType v, size_t i) noexcept { return v[i]; } - static forcedinline vSIMDType set (vSIMDType v, size_t i, int16_t s) noexcept { v[i] = s; return v; } + static forcedinline void store (vSIMDType value, int16_t* a) noexcept { vst1q_s16 (a, value); } + static forcedinline int16_t get (vSIMDType v, size_t i) noexcept { return fb::get (v, i); } + static forcedinline vSIMDType set (vSIMDType v, size_t i, int16_t s) noexcept { return fb::set (v, i, s); } static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_s16 (a, b); } static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_s16 (a, b); } static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_s16 (a, b); } @@ -267,7 +261,7 @@ struct SIMDNativeOps static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_s16 (a, b); } static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps::sum ((SIMDNativeOps::vSIMDType) notEqual (a, b)) == 0); } static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_s16 (a, b, c); } - static forcedinline int16_t sum (vSIMDType a) noexcept { return fb::sum (a); } + static forcedinline int16_t sum (vSIMDType a) noexcept { return fb::sum (a); } static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; } }; @@ -290,9 +284,9 @@ struct SIMDNativeOps //============================================================================== static forcedinline vSIMDType expand (uint16_t s) noexcept { return vdupq_n_u16 (s); } static forcedinline vSIMDType load (const uint16_t* a) noexcept { return vld1q_u16 (a); } - static forcedinline void store (vSIMDType value, uint16_t* a) noexcept { vst1q_u16 (a, value); } - static forcedinline uint16_t get (vSIMDType v, size_t i) noexcept { return v[i]; } - static forcedinline vSIMDType set (vSIMDType v, size_t i, uint16_t s) noexcept { v[i] = s; return v; } + static forcedinline void store (vSIMDType value, uint16_t* a) noexcept { vst1q_u16 (a, value); } + static forcedinline uint16_t get (vSIMDType v, size_t i) noexcept { return fb::get (v, i); } + static forcedinline vSIMDType set (vSIMDType v, size_t i, uint16_t s) noexcept { return fb::set (v, i, s); } static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_u16 (a, b); } static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_u16 (a, b); } static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_u16 (a, b); } @@ -309,7 +303,7 @@ struct SIMDNativeOps static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_u16 (a, b); } static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps::sum ((SIMDNativeOps::vSIMDType) notEqual (a, b)) == 0); } static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_u16 (a, b, c); } - static forcedinline uint16_t sum (vSIMDType a) noexcept { return fb::sum (a); } + static forcedinline uint16_t sum (vSIMDType a) noexcept { return fb::sum (a); } static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; } }; @@ -331,9 +325,9 @@ struct SIMDNativeOps //============================================================================== static forcedinline vSIMDType expand (int64_t s) noexcept { return vdupq_n_s64 (s); } static forcedinline vSIMDType load (const int64_t* a) noexcept { return vld1q_s64 (a); } - static forcedinline void store (vSIMDType value, int64_t* a) noexcept { vst1q_s64 (a, value); } - static forcedinline int64_t get (vSIMDType v, size_t i) noexcept { return v[i]; } - static forcedinline vSIMDType set (vSIMDType v, size_t i, int64_t s) noexcept { v[i] = s; return v; } + static forcedinline void store (vSIMDType value, int64_t* a) noexcept { vst1q_s64 (a, value); } + static forcedinline int64_t get (vSIMDType v, size_t i) noexcept { return fb::get (v, i); } + static forcedinline vSIMDType set (vSIMDType v, size_t i, int64_t s) noexcept { return fb::set (v, i, s); } static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_s64 (a, b); } static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_s64 (a, b); } static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return fb::mul (a, b); } @@ -350,7 +344,7 @@ struct SIMDNativeOps static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return fb::greaterThanOrEqual (a, b); } static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps::sum ((SIMDNativeOps::vSIMDType) notEqual (a, b)) == 0); } static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return fb::multiplyAdd (a, b, c); } - static forcedinline int64_t sum (vSIMDType a) noexcept { return fb::sum (a); } + static forcedinline int64_t sum (vSIMDType a) noexcept { return fb::sum (a); } static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; } }; @@ -373,9 +367,9 @@ struct SIMDNativeOps //============================================================================== static forcedinline vSIMDType expand (uint64_t s) noexcept { return vdupq_n_u64 (s); } static forcedinline vSIMDType load (const uint64_t* a) noexcept { return vld1q_u64 (a); } - static forcedinline void store (vSIMDType value, uint64_t* a) noexcept { vst1q_u64 (a, value); } - static forcedinline uint64_t get (vSIMDType v, size_t i) noexcept { return v[i]; } - static forcedinline vSIMDType set (vSIMDType v, size_t i, uint64_t s) noexcept { v[i] = s; return v; } + static forcedinline void store (vSIMDType value, uint64_t* a) noexcept { vst1q_u64 (a, value); } + static forcedinline uint64_t get (vSIMDType v, size_t i) noexcept { return fb::get (v, i); } + static forcedinline vSIMDType set (vSIMDType v, size_t i, uint64_t s) noexcept { return fb::set (v, i, s); } static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_u64 (a, b); } static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_u64 (a, b); } static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return fb::mul (a, b); } @@ -392,7 +386,7 @@ struct SIMDNativeOps static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return fb::greaterThanOrEqual (a, b); } static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps::sum ((SIMDNativeOps::vSIMDType) notEqual (a, b)) == 0); } static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return fb::multiplyAdd (a, b, c); } - static forcedinline uint64_t sum (vSIMDType a) noexcept { return fb::sum (a); } + static forcedinline uint64_t sum (vSIMDType a) noexcept { return fb::sum (a); } static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return a; } }; @@ -417,9 +411,9 @@ struct SIMDNativeOps //============================================================================== static forcedinline vSIMDType expand (float s) noexcept { return vdupq_n_f32 (s); } static forcedinline vSIMDType load (const float* a) noexcept { return vld1q_f32 (a); } - static forcedinline float get (vSIMDType v, size_t i) noexcept { return v[i]; } - static forcedinline vSIMDType set (vSIMDType v, size_t i, float s) noexcept { v[i] = s; return v; } - static forcedinline void store (vSIMDType value, float* a) noexcept { vst1q_f32 (a, value); } + static forcedinline float get (vSIMDType v, size_t i) noexcept { return fb::get (v, i); } + static forcedinline vSIMDType set (vSIMDType v, size_t i, float s) noexcept { return fb::set (v, i, s); } + static forcedinline void store (vSIMDType value, float* a) noexcept { vst1q_f32 (a, value); } static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_f32 (a, b); } static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_f32 (a, b); } static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_f32 (a, b); } @@ -463,6 +457,47 @@ struct SIMDNativeOps @tags{DSP} */ +#if JUCE_64BIT +template <> +struct SIMDNativeOps +{ + //============================================================================== + using vSIMDType = float64x2_t; + using vMaskType = uint64x2_t; + using fb = SIMDFallbackOps; + + //============================================================================== + DECLARE_NEON_SIMD_CONST (int64_t, kAllBitsSet); + DECLARE_NEON_SIMD_CONST (double, kOne); + + //============================================================================== + static forcedinline vSIMDType expand (double s) noexcept { return vdupq_n_f64 (s); } + static forcedinline vSIMDType load (const double* a) noexcept { return vld1q_f64 (a); } + static forcedinline double get (vSIMDType v, size_t i) noexcept { return fb::get (v, i); } + static forcedinline vSIMDType set (vSIMDType v, size_t i, double s) noexcept { return fb::set (v, i, s); } + static forcedinline void store (vSIMDType value, double* a) noexcept { vst1q_f64 (a, value); } + static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return vaddq_f64 (a, b); } + static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return vsubq_f64 (a, b); } + static forcedinline vSIMDType mul (vSIMDType a, vSIMDType b) noexcept { return vmulq_f64 (a, b); } + static forcedinline vSIMDType bit_and (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vandq_u64 ((vMaskType) a, (vMaskType) b); } + static forcedinline vSIMDType bit_or (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vorrq_u64 ((vMaskType) a, (vMaskType) b); } + static forcedinline vSIMDType bit_xor (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) veorq_u64 ((vMaskType) a, (vMaskType) b); } + static forcedinline vSIMDType bit_notand (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vbicq_u64 ((vMaskType) b, (vMaskType) a); } + static forcedinline vSIMDType bit_not (vSIMDType a) noexcept { return bit_notand (a, vld1q_f64 ((double*) kAllBitsSet)); } + static forcedinline vSIMDType min (vSIMDType a, vSIMDType b) noexcept { return vminq_f64 (a, b); } + static forcedinline vSIMDType max (vSIMDType a, vSIMDType b) noexcept { return vmaxq_f64 (a, b); } + static forcedinline vSIMDType equal (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vceqq_f64 (a, b); } + static forcedinline vSIMDType notEqual (vSIMDType a, vSIMDType b) noexcept { return bit_not (equal (a, b)); } + static forcedinline vSIMDType greaterThan (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgtq_f64 (a, b); } + static forcedinline vSIMDType greaterThanOrEqual (vSIMDType a, vSIMDType b) noexcept { return (vSIMDType) vcgeq_f64 (a, b); } + static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return (SIMDNativeOps::sum ((SIMDNativeOps::vSIMDType) notEqual (a, b)) == 0); } + static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return vmlaq_f64 (a, b, c); } + static forcedinline vSIMDType cmplxmul (vSIMDType a, vSIMDType b) noexcept { return fb::cmplxmul (a, b); } + static forcedinline double sum (vSIMDType a) noexcept { return fb::sum (a); } + static forcedinline vSIMDType oddevensum (vSIMDType a) noexcept { return a; } + static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return vcvtq_f64_s64 (vcvtq_s64_f64 (a)); } +}; +#else template <> struct SIMDNativeOps { @@ -472,8 +507,8 @@ struct SIMDNativeOps static forcedinline vSIMDType expand (double s) noexcept { return {{s, s}}; } static forcedinline vSIMDType load (const double* a) noexcept { return {{a[0], a[1]}}; } - static forcedinline void store (vSIMDType v, double* a) noexcept { a[0] = v.v[0]; a[1] = v.v[1]; } - static forcedinline double get (vSIMDType v, size_t i) noexcept { return v.v[i]; } + static forcedinline void store (vSIMDType v, double* a) noexcept { a[0] = v.v[0]; a[1] = v.v[1]; } + static forcedinline double get (vSIMDType v, size_t i) noexcept { return v.v[i]; } static forcedinline vSIMDType set (vSIMDType v, size_t i, double s) noexcept { v.v[i] = s; return v; } static forcedinline vSIMDType add (vSIMDType a, vSIMDType b) noexcept { return {{a.v[0] + b.v[0], a.v[1] + b.v[1]}}; } static forcedinline vSIMDType sub (vSIMDType a, vSIMDType b) noexcept { return {{a.v[0] - b.v[0], a.v[1] - b.v[1]}}; } @@ -492,16 +527,13 @@ struct SIMDNativeOps static forcedinline bool allEqual (vSIMDType a, vSIMDType b) noexcept { return fb::allEqual (a, b); } static forcedinline vSIMDType multiplyAdd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept { return fb::multiplyAdd (a, b, c); } static forcedinline vSIMDType cmplxmul (vSIMDType a, vSIMDType b) noexcept { return fb::cmplxmul (a, b); } - static forcedinline double sum (vSIMDType a) noexcept { return fb::sum (a); } + static forcedinline double sum (vSIMDType a) noexcept { return fb::sum (a); } static forcedinline vSIMDType oddevensum (vSIMDType a) noexcept { return a; } static forcedinline vSIMDType truncate (vSIMDType a) noexcept { return fb::truncate (a); } }; +#endif // JUCE_64BIT +#endif // #ifndef DOXYGEN -#endif - -#if JUCE_GCC && (__GNUC__ >= 6) - #pragma GCC diagnostic pop -#endif +JUCE_END_IGNORE_WARNINGS_GCC_LIKE -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_sse.cpp b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_sse.cpp new file mode 100644 index 00000000..f219f1e4 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_sse.cpp @@ -0,0 +1,55 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + DEFINE_SSE_SIMD_CONST (int32_t, float, kAllBitsSet) = { -1, -1, -1, -1 }; + DEFINE_SSE_SIMD_CONST (int32_t, float, kEvenHighBit) = { static_cast (0x80000000), 0, static_cast (0x80000000), 0 }; + DEFINE_SSE_SIMD_CONST (float, float, kOne) = { 1.0f, 1.0f, 1.0f, 1.0f }; + + DEFINE_SSE_SIMD_CONST (int64_t, double, kAllBitsSet) = { -1LL, -1LL }; + DEFINE_SSE_SIMD_CONST (int64_t, double, kEvenHighBit) = { static_cast (0x8000000000000000), 0 }; + DEFINE_SSE_SIMD_CONST (double, double, kOne) = { 1.0, 1.0 }; + + DEFINE_SSE_SIMD_CONST (int8_t, int8_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + + DEFINE_SSE_SIMD_CONST (uint8_t, uint8_t, kAllBitsSet) = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + DEFINE_SSE_SIMD_CONST (uint8_t, uint8_t, kHighBit) = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }; + + DEFINE_SSE_SIMD_CONST (int16_t, int16_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 }; + + DEFINE_SSE_SIMD_CONST (uint16_t, uint16_t, kAllBitsSet) = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; + DEFINE_SSE_SIMD_CONST (uint16_t, uint16_t, kHighBit) = { 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000 }; + + DEFINE_SSE_SIMD_CONST (int32_t, int32_t, kAllBitsSet) = { -1, -1, -1, -1 }; + + DEFINE_SSE_SIMD_CONST (uint32_t, uint32_t, kAllBitsSet) = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; + DEFINE_SSE_SIMD_CONST (uint32_t, uint32_t, kHighBit) = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }; + + DEFINE_SSE_SIMD_CONST (int64_t, int64_t, kAllBitsSet) = { -1, -1 }; + + DEFINE_SSE_SIMD_CONST (uint64_t, uint64_t, kAllBitsSet) = { 0xffffffffffffffff, 0xffffffffffffffff }; + DEFINE_SSE_SIMD_CONST (uint64_t, uint64_t, kHighBit) = { 0x8000000000000000, 0x8000000000000000 }; +} diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_sse.h similarity index 96% rename from JuceLibraryCode/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h rename to JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_sse.h index 268cab0c..96d8ec3e 100644 --- a/JuceLibraryCode/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h +++ b/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_sse.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,31 +23,26 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { #ifndef DOXYGEN -#if JUCE_GCC && (__GNUC__ >= 6) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wignored-attributes" -#endif +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wignored-attributes") #ifdef _MSC_VER #define DECLARE_SSE_SIMD_CONST(type, name) \ - static __declspec(align(16)) const type name [16 / sizeof (type)] + static __declspec (align (16)) const type name [16 / sizeof (type)] #define DEFINE_SSE_SIMD_CONST(type, class_type, name) \ - __declspec(align(16)) const type SIMDNativeOps:: name [16 / sizeof (type)] + __declspec (align (16)) const type SIMDNativeOps:: name [16 / sizeof (type)] #else #define DECLARE_SSE_SIMD_CONST(type, name) \ - static const type name [16 / sizeof (type)] __attribute__((aligned(16))) + static const type name [16 / sizeof (type)] __attribute__ ((aligned (16))) #define DEFINE_SSE_SIMD_CONST(type, class_type, name) \ - const type SIMDNativeOps:: name [16 / sizeof (type)] __attribute__((aligned(16))) + const type SIMDNativeOps:: name [16 / sizeof (type)] __attribute__ ((aligned (16))) #endif @@ -109,12 +103,14 @@ struct SIMDNativeOps static forcedinline float JUCE_VECTOR_CALLTYPE sum (__m128 a) noexcept { - #if defined(__SSE4__) - __m128 retval = _mm_dp_ps (a, _mm_loadu_ps (kOne), 0xff); - #elif defined(__SSE3__) - __m128 retval = _mm_hadd_ps (_mm_hadd_ps (a, a), a); + #if defined (__SSE4__) + const auto retval = _mm_dp_ps (a, _mm_loadu_ps (kOne), 0xff); + #elif defined (__SSE3__) + const auto shuffled = _mm_movehdup_ps (a); + const auto sums = _mm_add_ps (a, shuffled); + const auto retval = _mm_add_ss (sums, _mm_movehl_ps (shuffled, sums)); #else - __m128 retval = _mm_add_ps (_mm_shuffle_ps (a, a, 0x4e), a); + auto retval = _mm_add_ps (_mm_shuffle_ps (a, a, 0x4e), a); retval = _mm_add_ps (retval, _mm_shuffle_ps (retval, retval, 0xb1)); #endif return _mm_cvtss_f32 (retval); @@ -177,9 +173,9 @@ struct SIMDNativeOps static forcedinline double JUCE_VECTOR_CALLTYPE sum (__m128d a) noexcept { - #if defined(__SSE4__) + #if defined (__SSE4__) __m128d retval = _mm_dp_pd (a, vconst (kOne), 0xff); - #elif defined(__SSE3__) + #elif defined (__SSE3__) __m128d retval = _mm_hadd_pd (a, a); #else __m128d retval = _mm_add_pd (_mm_shuffle_pd (a, a, 0x01), a); @@ -213,7 +209,7 @@ struct SIMDNativeOps static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_xor (__m128i a, __m128i b) noexcept { return _mm_xor_si128 (a, b); } static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_andnot (__m128i a, __m128i b) noexcept { return _mm_andnot_si128 (a, b); } static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_not (__m128i a) noexcept { return _mm_andnot_si128 (a, vconst (kAllBitsSet)); } - #if defined(__SSE4__) + #if defined (__SSE4__) static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { return _mm_min_epi8 (a, b); } static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { return _mm_max_epi8 (a, b); } #else @@ -413,7 +409,7 @@ struct SIMDNativeOps static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_xor (__m128i a, __m128i b) noexcept { return _mm_xor_si128 (a, b); } static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_andnot (__m128i a, __m128i b) noexcept { return _mm_andnot_si128 (a, b); } static forcedinline __m128i JUCE_VECTOR_CALLTYPE bit_not (__m128i a) noexcept { return _mm_andnot_si128 (a, vconst (kAllBitsSet)); } - #if defined(__SSE4__) + #if defined (__SSE4__) static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { return _mm_min_epu16 (a, b); } static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { return _mm_max_epu16 (a, b); } #else @@ -494,19 +490,19 @@ struct SIMDNativeOps static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b) noexcept { - #if defined(__SSE4_1__) + #if defined (__SSE4_1__) return _mm_mullo_epi32 (a, b); #else __m128i even = _mm_mul_epu32 (a,b); __m128i odd = _mm_mul_epu32 (_mm_srli_si128 (a,4), _mm_srli_si128 (b,4)); - return _mm_unpacklo_epi32 (_mm_shuffle_epi32(even, _MM_SHUFFLE (0,0,2,0)), - _mm_shuffle_epi32(odd, _MM_SHUFFLE (0,0,2,0))); + return _mm_unpacklo_epi32 (_mm_shuffle_epi32 (even, _MM_SHUFFLE (0,0,2,0)), + _mm_shuffle_epi32 (odd, _MM_SHUFFLE (0,0,2,0))); #endif } static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { - #if defined(__SSE4_1__) + #if defined (__SSE4_1__) return _mm_min_epi32 (a, b); #else __m128i lt = greaterThan (b, a); @@ -516,7 +512,7 @@ struct SIMDNativeOps static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { - #if defined(__SSE4_1__) + #if defined (__SSE4_1__) return _mm_max_epi32 (a, b); #else __m128i gt = greaterThan (a, b); @@ -576,19 +572,19 @@ struct SIMDNativeOps static forcedinline __m128i JUCE_VECTOR_CALLTYPE mul (__m128i a, __m128i b) noexcept { - #if defined(__SSE4_1__) + #if defined (__SSE4_1__) return _mm_mullo_epi32 (a, b); #else __m128i even = _mm_mul_epu32 (a,b); __m128i odd = _mm_mul_epu32 (_mm_srli_si128 (a,4), _mm_srli_si128 (b,4)); - return _mm_unpacklo_epi32 (_mm_shuffle_epi32(even, _MM_SHUFFLE (0,0,2,0)), - _mm_shuffle_epi32(odd, _MM_SHUFFLE (0,0,2,0))); + return _mm_unpacklo_epi32 (_mm_shuffle_epi32 (even, _MM_SHUFFLE (0,0,2,0)), + _mm_shuffle_epi32 (odd, _MM_SHUFFLE (0,0,2,0))); #endif } static forcedinline __m128i JUCE_VECTOR_CALLTYPE min (__m128i a, __m128i b) noexcept { - #if defined(__SSE4_1__) + #if defined (__SSE4_1__) return _mm_min_epi32 (a, b); #else __m128i lt = greaterThan (b, a); @@ -598,7 +594,7 @@ struct SIMDNativeOps static forcedinline __m128i JUCE_VECTOR_CALLTYPE max (__m128i a, __m128i b) noexcept { - #if defined(__SSE4_1__) + #if defined (__SSE4_1__) return _mm_max_epi32 (a, b); #else __m128i gt = greaterThan (a, b); @@ -646,7 +642,7 @@ struct SIMDNativeOps static forcedinline __m128i JUCE_VECTOR_CALLTYPE equal (__m128i a, __m128i b) noexcept { - #if defined(__SSE4_1__) + #if defined (__SSE4_1__) return _mm_cmpeq_epi64 (a, b); #else __m128i bitmask = _mm_cmpeq_epi32 (a, b); @@ -657,7 +653,7 @@ struct SIMDNativeOps static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThan (__m128i a, __m128i b) noexcept { - #if defined(__SSE4_2__) + #if defined (__SSE4_2__) return _mm_cmpgt_epi64 (a, b); #else return SIMDFallbackOps::greaterThan (a, b); @@ -706,7 +702,7 @@ struct SIMDNativeOps static forcedinline __m128i JUCE_VECTOR_CALLTYPE equal (__m128i a, __m128i b) noexcept { - #if defined(__SSE4_1__) + #if defined (__SSE4_1__) return _mm_cmpeq_epi64 (a, b); #else __m128i bitmask = _mm_cmpeq_epi32 (a, b); @@ -717,7 +713,7 @@ struct SIMDNativeOps static forcedinline __m128i JUCE_VECTOR_CALLTYPE greaterThan (__m128i a, __m128i b) noexcept { - #if defined(__SSE4_2__) + #if defined (__SSE4_2__) return _mm_cmpgt_epi64 (ssign (a), ssign (b)); #else return SIMDFallbackOps::greaterThan (a, b); @@ -727,9 +723,6 @@ struct SIMDNativeOps #endif -#if JUCE_GCC && (__GNUC__ >= 6) - #pragma GCC diagnostic pop -#endif +JUCE_END_IGNORE_WARNINGS_GCC_LIKE -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_avx_SIMDNativeOps.cpp b/JuceLibraryCode/modules/juce_dsp/native/juce_avx_SIMDNativeOps.cpp deleted file mode 100644 index 5a26c00b..00000000 --- a/JuceLibraryCode/modules/juce_dsp/native/juce_avx_SIMDNativeOps.cpp +++ /dev/null @@ -1,59 +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. - - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). - - 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 dsp - { - DEFINE_AVX_SIMD_CONST (int32_t, float, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 }; - DEFINE_AVX_SIMD_CONST (int32_t, float, kEvenHighBit) = { static_cast(0x80000000), 0, static_cast(0x80000000), 0, static_cast(0x80000000), 0, static_cast(0x80000000), 0 }; - DEFINE_AVX_SIMD_CONST (float, float, kOne) = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; - - DEFINE_AVX_SIMD_CONST (int64_t, double, kAllBitsSet) = { -1, -1, -1, -1 }; - DEFINE_AVX_SIMD_CONST (int64_t, double, kEvenHighBit) = { static_cast (0x8000000000000000), 0, static_cast (0x8000000000000000), 0 }; - DEFINE_AVX_SIMD_CONST (double, double, kOne) = { 1.0, 1.0, 1.0, 1.0 }; - - DEFINE_AVX_SIMD_CONST (int8_t, int8_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - - DEFINE_AVX_SIMD_CONST (uint8_t, uint8_t, kAllBitsSet) = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - DEFINE_AVX_SIMD_CONST (uint8_t, uint8_t, kHighBit) = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }; - - DEFINE_AVX_SIMD_CONST (int16_t, int16_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - - DEFINE_AVX_SIMD_CONST (uint16_t, uint16_t, kAllBitsSet) = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; - DEFINE_AVX_SIMD_CONST (uint16_t, uint16_t, kHighBit) = { 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000 }; - - DEFINE_AVX_SIMD_CONST (int32_t, int32_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 }; - - DEFINE_AVX_SIMD_CONST (uint32_t, uint32_t, kAllBitsSet) = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; - DEFINE_AVX_SIMD_CONST (uint32_t, uint32_t, kHighBit) = { 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000, 0x80000000 }; - - DEFINE_AVX_SIMD_CONST (int64_t, int64_t, kAllBitsSet) = { -1LL, -1LL, -1LL, -1LL }; - - DEFINE_AVX_SIMD_CONST (uint64_t, uint64_t, kAllBitsSet) = { 0xffffffffffffffffULL, 0xffffffffffffffffULL, 0xffffffffffffffffULL, 0xffffffffffffffffULL }; - DEFINE_AVX_SIMD_CONST (uint64_t, uint64_t, kHighBit) = { 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL }; - } -} diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_neon_SIMDNativeOps.cpp b/JuceLibraryCode/modules/juce_dsp/native/juce_neon_SIMDNativeOps.cpp deleted file mode 100644 index beaabdc5..00000000 --- a/JuceLibraryCode/modules/juce_dsp/native/juce_neon_SIMDNativeOps.cpp +++ /dev/null @@ -1,44 +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. - - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). - - 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 dsp - { - DEFINE_NEON_SIMD_CONST (int32_t, float, kAllBitsSet) = { -1, -1, -1, -1 }; - DEFINE_NEON_SIMD_CONST (int32_t, float, kEvenHighBit) = { static_cast(0x80000000), 0, static_cast(0x80000000), 0 }; - DEFINE_NEON_SIMD_CONST (float, float, kOne) = { 1.0f, 1.0f, 1.0f, 1.0f }; - - DEFINE_NEON_SIMD_CONST (int8_t, int8_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - DEFINE_NEON_SIMD_CONST (uint8_t, uint8_t, kAllBitsSet) = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - DEFINE_NEON_SIMD_CONST (int16_t, int16_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 }; - DEFINE_NEON_SIMD_CONST (uint16_t, uint16_t, kAllBitsSet) = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; - DEFINE_NEON_SIMD_CONST (int32_t, int32_t, kAllBitsSet) = { -1, -1, -1, -1 }; - DEFINE_NEON_SIMD_CONST (uint32_t, uint32_t, kAllBitsSet) = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; - DEFINE_NEON_SIMD_CONST (int64_t, int64_t, kAllBitsSet) = { -1, -1 }; - DEFINE_NEON_SIMD_CONST (uint64_t, uint64_t, kAllBitsSet) = { 0xffffffffffffffff, 0xffffffffffffffff }; - } -} diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_sse_SIMDNativeOps.cpp b/JuceLibraryCode/modules/juce_dsp/native/juce_sse_SIMDNativeOps.cpp deleted file mode 100644 index fc729c30..00000000 --- a/JuceLibraryCode/modules/juce_dsp/native/juce_sse_SIMDNativeOps.cpp +++ /dev/null @@ -1,59 +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. - - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). - - 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 dsp - { - DEFINE_SSE_SIMD_CONST (int32_t, float, kAllBitsSet) = { -1, -1, -1, -1 }; - DEFINE_SSE_SIMD_CONST (int32_t, float, kEvenHighBit) = { static_cast(0x80000000), 0, static_cast(0x80000000), 0 }; - DEFINE_SSE_SIMD_CONST (float, float, kOne) = { 1.0f, 1.0f, 1.0f, 1.0f }; - - DEFINE_SSE_SIMD_CONST (int64_t, double, kAllBitsSet) = { -1LL, -1LL }; - DEFINE_SSE_SIMD_CONST (int64_t, double, kEvenHighBit) = { static_cast(0x8000000000000000), 0 }; - DEFINE_SSE_SIMD_CONST (double, double, kOne) = { 1.0, 1.0 }; - - DEFINE_SSE_SIMD_CONST (int8_t, int8_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - - DEFINE_SSE_SIMD_CONST (uint8_t, uint8_t, kAllBitsSet) = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - DEFINE_SSE_SIMD_CONST (uint8_t, uint8_t, kHighBit) = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }; - - DEFINE_SSE_SIMD_CONST (int16_t, int16_t, kAllBitsSet) = { -1, -1, -1, -1, -1, -1, -1, -1 }; - - DEFINE_SSE_SIMD_CONST (uint16_t, uint16_t, kAllBitsSet) = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; - DEFINE_SSE_SIMD_CONST (uint16_t, uint16_t, kHighBit) = { 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000 }; - - DEFINE_SSE_SIMD_CONST (int32_t, int32_t, kAllBitsSet) = { -1, -1, -1, -1 }; - - DEFINE_SSE_SIMD_CONST (uint32_t, uint32_t, kAllBitsSet) = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; - DEFINE_SSE_SIMD_CONST (uint32_t, uint32_t, kHighBit) = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }; - - DEFINE_SSE_SIMD_CONST (int64_t, int64_t, kAllBitsSet) = { -1, -1 }; - - DEFINE_SSE_SIMD_CONST (uint64_t, uint64_t, kAllBitsSet) = { 0xffffffffffffffff, 0xffffffffffffffff }; - DEFINE_SSE_SIMD_CONST (uint64_t, uint64_t, kHighBit) = { 0x8000000000000000, 0x8000000000000000 }; - } -} diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_BallisticsFilter.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_BallisticsFilter.cpp new file mode 100644 index 00000000..6e9ac2f8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_BallisticsFilter.cpp @@ -0,0 +1,127 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +BallisticsFilter::BallisticsFilter() +{ + setAttackTime (attackTime); + setReleaseTime (releaseTime); +} + +template +void BallisticsFilter::setAttackTime (SampleType attackTimeMs) +{ + attackTime = attackTimeMs; + cteAT = calculateLimitedCte (static_cast (attackTime)); +} + +template +void BallisticsFilter::setReleaseTime (SampleType releaseTimeMs) +{ + releaseTime = releaseTimeMs; + cteRL = calculateLimitedCte (static_cast (releaseTime)); +} + +template +void BallisticsFilter::setLevelCalculationType (LevelCalculationType newLevelType) +{ + levelType = newLevelType; + reset(); +} + +template +void BallisticsFilter::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + expFactor = -2.0 * MathConstants::pi * 1000.0 / sampleRate; + + setAttackTime (attackTime); + setReleaseTime (releaseTime); + + yold.resize (spec.numChannels); + + reset(); +} + +template +void BallisticsFilter::reset() +{ + reset (0); +} + +template +void BallisticsFilter::reset (SampleType initialValue) +{ + for (auto& old : yold) + old = initialValue; +} + +template +SampleType BallisticsFilter::processSample (int channel, SampleType inputValue) +{ + jassert (isPositiveAndBelow (channel, yold.size())); + + if (levelType == LevelCalculationType::RMS) + inputValue *= inputValue; + else + inputValue = std::abs (inputValue); + + SampleType cte = (inputValue > yold[(size_t) channel] ? cteAT : cteRL); + + SampleType result = inputValue + cte * (yold[(size_t) channel] - inputValue); + yold[(size_t) channel] = result; + + if (levelType == LevelCalculationType::RMS) + return std::sqrt (result); + + return result; +} + +template +void BallisticsFilter::snapToZero() noexcept +{ + for (auto& old : yold) + util::snapToZero (old); +} + +template +SampleType BallisticsFilter::calculateLimitedCte (SampleType timeMs) const noexcept +{ + return timeMs < static_cast (1.0e-3) ? 0 + : static_cast (std::exp (expFactor / timeMs)); +} + +//============================================================================== +template class BallisticsFilter; +template class BallisticsFilter; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_BallisticsFilter.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_BallisticsFilter.h new file mode 100644 index 00000000..5c1606d1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_BallisticsFilter.h @@ -0,0 +1,147 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +enum class BallisticsFilterLevelCalculationType +{ + peak, + RMS +}; + +/** + A processor to apply standard attack / release ballistics to an input signal. + This is useful in dynamics processors, envelope followers, modulated audio + effects and for smoothing animation in data visualisation. + + @tags{DSP} +*/ +template +class BallisticsFilter +{ +public: + //============================================================================== + using LevelCalculationType = BallisticsFilterLevelCalculationType; + + //============================================================================== + /** Constructor. */ + BallisticsFilter(); + + //============================================================================== + /** Sets the attack time in ms. + + Attack times less than 0.001 ms will be snapped to zero and very long attack + times will eventually saturate depending on the numerical precision used. + */ + void setAttackTime (SampleType attackTimeMs); + + /** Sets the release time in ms. + + Release times less than 0.001 ms will be snapped to zero and very long + release times will eventually saturate depending on the numerical precision + used. + */ + void setReleaseTime (SampleType releaseTimeMs); + + /** Sets how the filter levels are calculated. + + Level calculation in digital envelope followers is usually performed using + peak detection with a rectifier function (like std::abs) and filtering, + which returns an envelope dependant on the peak or maximum values of the + signal amplitude. + + To perform an estimation of the average value of the signal you can use + an RMS (root mean squared) implementation of the ballistics filter instead. + This is useful in some compressor and noise-gate designs, or in specific + types of volume meters. + */ + void setLevelCalculationType (LevelCalculationType newCalculationType); + + //============================================================================== + /** Initialises the filter. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the filter. */ + void reset(); + + /** Resets the internal state variables of the filter to the given initial value. */ + void reset (SampleType initialValue); + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + const auto numChannels = outputBlock.getNumChannels(); + const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumChannels() <= yold.size()); + jassert (inputBlock.getNumChannels() == numChannels); + jassert (inputBlock.getNumSamples() == numSamples); + + if (context.isBypassed) + { + outputBlock.copyFrom (inputBlock); + return; + } + + for (size_t channel = 0; channel < numChannels; ++channel) + { + auto* inputSamples = inputBlock .getChannelPointer (channel); + auto* outputSamples = outputBlock.getChannelPointer (channel); + + for (size_t i = 0; i < numSamples; ++i) + outputSamples[i] = processSample ((int) channel, inputSamples[i]); + } + + #if JUCE_DSP_ENABLE_SNAP_TO_ZERO + snapToZero(); + #endif + } + + /** Processes one sample at a time on a given channel. */ + SampleType processSample (int channel, SampleType inputValue); + + /** Ensure that the state variables are rounded to zero if the state + variables are denormals. This is only needed if you are doing + sample by sample processing. + */ + void snapToZero() noexcept; + +private: + //============================================================================== + SampleType calculateLimitedCte (SampleType) const noexcept; + + //============================================================================== + std::vector yold; + double sampleRate = 44100.0, expFactor = -0.142; + SampleType attackTime = 1.0, releaseTime = 100.0, cteAT = 0.0, cteRL = 0.0; + LevelCalculationType levelType = LevelCalculationType::peak; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.cpp new file mode 100644 index 00000000..ef556534 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.cpp @@ -0,0 +1,135 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +DelayLine::DelayLine() + : DelayLine (0) +{ +} + +template +DelayLine::DelayLine (int maximumDelayInSamples) +{ + jassert (maximumDelayInSamples >= 0); + + sampleRate = 44100.0; + + setMaximumDelayInSamples (maximumDelayInSamples); +} + +//============================================================================== +template +void DelayLine::setDelay (SampleType newDelayInSamples) +{ + auto upperLimit = (SampleType) getMaximumDelayInSamples(); + jassert (isPositiveAndNotGreaterThan (newDelayInSamples, upperLimit)); + + delay = jlimit ((SampleType) 0, upperLimit, newDelayInSamples); + delayInt = static_cast (std::floor (delay)); + delayFrac = delay - (SampleType) delayInt; + + updateInternalVariables(); +} + +template +SampleType DelayLine::getDelay() const +{ + return delay; +} + +//============================================================================== +template +void DelayLine::prepare (const ProcessSpec& spec) +{ + jassert (spec.numChannels > 0); + + bufferData.setSize ((int) spec.numChannels, totalSize, false, false, true); + + writePos.resize (spec.numChannels); + readPos.resize (spec.numChannels); + + v.resize (spec.numChannels); + sampleRate = spec.sampleRate; + + reset(); +} + +template +void DelayLine::setMaximumDelayInSamples (int maxDelayInSamples) +{ + jassert (maxDelayInSamples >= 0); + totalSize = jmax (4, maxDelayInSamples + 2); + bufferData.setSize ((int) bufferData.getNumChannels(), totalSize, false, false, true); + reset(); +} + +template +void DelayLine::reset() +{ + for (auto vec : { &writePos, &readPos }) + std::fill (vec->begin(), vec->end(), 0); + + std::fill (v.begin(), v.end(), static_cast (0)); + + bufferData.clear(); +} + +//============================================================================== +template +void DelayLine::pushSample (int channel, SampleType sample) +{ + bufferData.setSample (channel, writePos[(size_t) channel], sample); + writePos[(size_t) channel] = (writePos[(size_t) channel] + totalSize - 1) % totalSize; +} + +template +SampleType DelayLine::popSample (int channel, SampleType delayInSamples, bool updateReadPointer) +{ + if (delayInSamples >= 0) + setDelay (delayInSamples); + + auto result = interpolateSample (channel); + + if (updateReadPointer) + readPos[(size_t) channel] = (readPos[(size_t) channel] + totalSize - 1) % totalSize; + + return result; +} + +//============================================================================== +template class DelayLine; +template class DelayLine; +template class DelayLine; +template class DelayLine; +template class DelayLine; +template class DelayLine; +template class DelayLine; +template class DelayLine; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.h new file mode 100644 index 00000000..11b92a38 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.h @@ -0,0 +1,314 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +/** + A collection of structs to pass as the template argument when setting the + interpolation type for the DelayLine class. +*/ +namespace DelayLineInterpolationTypes +{ + /** + No interpolation between successive samples in the delay line will be + performed. This is useful when the delay is a constant integer or to + create lo-fi audio effects. + + @tags{DSP} + */ + struct None {}; + + /** + Successive samples in the delay line will be linearly interpolated. This + type of interpolation has a low computational cost where the delay can be + modulated in real time, but it also introduces a low-pass filtering effect + into your audio signal. + + @tags{DSP} + */ + struct Linear {}; + + /** + Successive samples in the delay line will be interpolated using a 3rd order + Lagrange interpolator. This method incurs more computational overhead than + linear interpolation but reduces the low-pass filtering effect whilst + remaining amenable to real time delay modulation. + + @tags{DSP} + */ + struct Lagrange3rd {}; + + /** + Successive samples in the delay line will be interpolated using 1st order + Thiran interpolation. This method is very efficient, and features a flat + amplitude frequency response in exchange for less accuracy in the phase + response. This interpolation method is stateful so is unsuitable for + applications requiring fast delay modulation. + + @tags{DSP} + */ + struct Thiran {}; +} + +//============================================================================== +/** + A delay line processor featuring several algorithms for the fractional delay + calculation, block processing, and sample-by-sample processing useful when + modulating the delay in real time or creating a standard delay effect with + feedback. + + Note: If you intend to change the delay in real time, you may want to smooth + changes to the delay systematically using either a ramp or a low-pass filter. + + @see SmoothedValue, FirstOrderTPTFilter + + @tags{DSP} +*/ +template +class DelayLine +{ +public: + //============================================================================== + /** Default constructor. */ + DelayLine(); + + /** Constructor. */ + explicit DelayLine (int maximumDelayInSamples); + + //============================================================================== + /** Sets the delay in samples. */ + void setDelay (SampleType newDelayInSamples); + + /** Returns the current delay in samples. */ + SampleType getDelay() const; + + //============================================================================== + /** Initialises the processor. */ + void prepare (const ProcessSpec& spec); + + /** Sets a new maximum delay in samples. + + Also clears the delay line. + + This may allocate internally, so you should never call it from the audio thread. + */ + void setMaximumDelayInSamples (int maxDelayInSamples); + + /** Gets the maximum possible delay in samples. + + For very short delay times, the result of getMaximumDelayInSamples() may + differ from the last value passed to setMaximumDelayInSamples(). + */ + int getMaximumDelayInSamples() const noexcept { return totalSize - 2; } + + /** Resets the internal state variables of the processor. */ + void reset(); + + //============================================================================== + /** Pushes a single sample into one channel of the delay line. + + Use this function and popSample instead of process if you need to modulate + the delay in real time instead of using a fixed delay value, or if you want + to code a delay effect with a feedback loop. + + @see setDelay, popSample, process + */ + void pushSample (int channel, SampleType sample); + + /** Pops a single sample from one channel of the delay line. + + Use this function to modulate the delay in real time or implement standard + delay effects with feedback. + + @param channel the target channel for the delay line. + + @param delayInSamples sets the wanted fractional delay in samples, or -1 + to use the value being used before or set with + setDelay function. + + @param updateReadPointer should be set to true if you use the function + once for each sample, or false if you need + multi-tap delay capabilities. + + @see setDelay, pushSample, process + */ + SampleType popSample (int channel, SampleType delayInSamples = -1, bool updateReadPointer = true); + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. + + Can be used for block processing when the delay is not going to change + during processing. The delay must first be set by calling setDelay. + + @see setDelay + */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + const auto numChannels = outputBlock.getNumChannels(); + const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumChannels() == numChannels); + jassert (inputBlock.getNumChannels() == writePos.size()); + jassert (inputBlock.getNumSamples() == numSamples); + + if (context.isBypassed) + { + outputBlock.copyFrom (inputBlock); + return; + } + + for (size_t channel = 0; channel < numChannels; ++channel) + { + auto* inputSamples = inputBlock.getChannelPointer (channel); + auto* outputSamples = outputBlock.getChannelPointer (channel); + + for (size_t i = 0; i < numSamples; ++i) + { + pushSample ((int) channel, inputSamples[i]); + outputSamples[i] = popSample ((int) channel); + } + } + } + +private: + //============================================================================== + SampleType interpolateSample (int channel) + { + if constexpr (std::is_same_v) + { + auto index = (readPos[(size_t) channel] + delayInt) % totalSize; + return bufferData.getSample (channel, index); + } + else if constexpr (std::is_same_v) + { + auto index1 = readPos[(size_t) channel] + delayInt; + auto index2 = index1 + 1; + + if (index2 >= totalSize) + { + index1 %= totalSize; + index2 %= totalSize; + } + + auto value1 = bufferData.getSample (channel, index1); + auto value2 = bufferData.getSample (channel, index2); + + return value1 + delayFrac * (value2 - value1); + } + else if constexpr (std::is_same_v) + { + auto index1 = readPos[(size_t) channel] + delayInt; + auto index2 = index1 + 1; + auto index3 = index2 + 1; + auto index4 = index3 + 1; + + if (index4 >= totalSize) + { + index1 %= totalSize; + index2 %= totalSize; + index3 %= totalSize; + index4 %= totalSize; + } + + auto* samples = bufferData.getReadPointer (channel); + + auto value1 = samples[index1]; + auto value2 = samples[index2]; + auto value3 = samples[index3]; + auto value4 = samples[index4]; + + auto d1 = delayFrac - 1.f; + auto d2 = delayFrac - 2.f; + auto d3 = delayFrac - 3.f; + + auto c1 = -d1 * d2 * d3 / 6.f; + auto c2 = d2 * d3 * 0.5f; + auto c3 = -d1 * d3 * 0.5f; + auto c4 = d1 * d2 / 6.f; + + return value1 * c1 + delayFrac * (value2 * c2 + value3 * c3 + value4 * c4); + } + else if constexpr (std::is_same_v) + { + auto index1 = readPos[(size_t) channel] + delayInt; + auto index2 = index1 + 1; + + if (index2 >= totalSize) + { + index1 %= totalSize; + index2 %= totalSize; + } + + auto value1 = bufferData.getSample (channel, index1); + auto value2 = bufferData.getSample (channel, index2); + + auto output = approximatelyEqual (delayFrac, (SampleType) 0) ? value1 : value2 + alpha * (value1 - v[(size_t) channel]); + v[(size_t) channel] = output; + + return output; + } + } + + //============================================================================== + void updateInternalVariables() + { + if constexpr (std::is_same_v) + { + if (delayFrac < (SampleType) 2.0 && delayInt >= 1) + { + delayFrac++; + delayInt--; + } + } + else if constexpr (std::is_same_v) + { + if (delayFrac < (SampleType) 0.618 && delayInt >= 1) + { + delayFrac++; + delayInt--; + } + + alpha = (1 - delayFrac) / (1 + delayFrac); + } + } + + //============================================================================== + double sampleRate; + + //============================================================================== + AudioBuffer bufferData; + std::vector v; + std::vector writePos, readPos; + SampleType delay = 0.0, delayFrac = 0.0; + int delayInt = 0, totalSize = 4; + SampleType alpha = 0.0; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_DryWetMixer.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_DryWetMixer.cpp new file mode 100644 index 00000000..41a3c5ae --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_DryWetMixer.cpp @@ -0,0 +1,368 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +DryWetMixer::DryWetMixer() + : DryWetMixer (0) +{ +} + +template +DryWetMixer::DryWetMixer (int maximumWetLatencyInSamplesIn) + : dryDelayLine (maximumWetLatencyInSamplesIn), + maximumWetLatencyInSamples (maximumWetLatencyInSamplesIn) +{ + dryDelayLine.setDelay (0); + + update(); + reset(); +} + +//============================================================================== +template +void DryWetMixer::setMixingRule (MixingRule newRule) +{ + currentMixingRule = newRule; + update(); +} + +template +void DryWetMixer::setWetMixProportion (SampleType newWetMixProportion) +{ + jassert (isPositiveAndNotGreaterThan (newWetMixProportion, 1.0)); + + mix = jlimit (static_cast (0.0), static_cast (1.0), newWetMixProportion); + update(); +} + +template +void DryWetMixer::setWetLatency (SampleType wetLatencySamples) +{ + dryDelayLine.setDelay (wetLatencySamples); +} + +//============================================================================== +template +void DryWetMixer::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + + dryDelayLine.prepare (spec); + bufferDry.setSize ((int) spec.numChannels, (int) spec.maximumBlockSize, false, false, true); + + update(); + reset(); +} + +template +void DryWetMixer::reset() +{ + dryVolume.reset (sampleRate, 0.05); + wetVolume.reset (sampleRate, 0.05); + + dryDelayLine.reset(); + + fifo = SingleThreadedAbstractFifo (nextPowerOfTwo (bufferDry.getNumSamples())); + bufferDry.setSize (bufferDry.getNumChannels(), fifo.getSize(), false, false, true); +} + +//============================================================================== +template +void DryWetMixer::pushDrySamples (const AudioBlock drySamples) +{ + jassert (drySamples.getNumChannels() <= (size_t) bufferDry.getNumChannels()); + jassert (drySamples.getNumSamples() <= (size_t) fifo.getRemainingSpace()); + + auto offset = 0; + + for (const auto& range : fifo.write ((int) drySamples.getNumSamples())) + { + if (range.getLength() == 0) + continue; + + auto block = AudioBlock (bufferDry).getSubsetChannelBlock (0, drySamples.getNumChannels()) + .getSubBlock ((size_t) range.getStart(), (size_t) range.getLength()); + + auto inputBlock = drySamples.getSubBlock ((size_t) offset, (size_t) range.getLength()); + + if (maximumWetLatencyInSamples == 0) + block.copyFrom (inputBlock); + else + dryDelayLine.process (ProcessContextNonReplacing (inputBlock, block)); + + offset += range.getLength(); + } +} + +template +void DryWetMixer::mixWetSamples (AudioBlock inOutBlock) +{ + inOutBlock.multiplyBy (wetVolume); + + jassert (inOutBlock.getNumSamples() <= (size_t) fifo.getNumReadable()); + + auto offset = 0; + + for (const auto& range : fifo.read ((int) inOutBlock.getNumSamples())) + { + if (range.getLength() == 0) + continue; + + auto block = AudioBlock (bufferDry).getSubsetChannelBlock (0, inOutBlock.getNumChannels()) + .getSubBlock ((size_t) range.getStart(), (size_t) range.getLength()); + block.multiplyBy (dryVolume); + inOutBlock.getSubBlock ((size_t) offset).add (block); + + offset += range.getLength(); + } +} + +//============================================================================== +template +void DryWetMixer::update() +{ + SampleType dryValue, wetValue; + + switch (currentMixingRule) + { + case MixingRule::balanced: + dryValue = static_cast (2.0) * jmin (static_cast (0.5), static_cast (1.0) - mix); + wetValue = static_cast (2.0) * jmin (static_cast (0.5), mix); + break; + + case MixingRule::linear: + dryValue = static_cast (1.0) - mix; + wetValue = mix; + break; + + case MixingRule::sin3dB: + dryValue = static_cast (std::sin (0.5 * MathConstants::pi * (1.0 - mix))); + wetValue = static_cast (std::sin (0.5 * MathConstants::pi * mix)); + break; + + case MixingRule::sin4p5dB: + dryValue = static_cast (std::pow (std::sin (0.5 * MathConstants::pi * (1.0 - mix)), 1.5)); + wetValue = static_cast (std::pow (std::sin (0.5 * MathConstants::pi * mix), 1.5)); + break; + + case MixingRule::sin6dB: + dryValue = static_cast (std::pow (std::sin (0.5 * MathConstants::pi * (1.0 - mix)), 2.0)); + wetValue = static_cast (std::pow (std::sin (0.5 * MathConstants::pi * mix), 2.0)); + break; + + case MixingRule::squareRoot3dB: + dryValue = std::sqrt (static_cast (1.0) - mix); + wetValue = std::sqrt (mix); + break; + + case MixingRule::squareRoot4p5dB: + dryValue = static_cast (std::pow (std::sqrt (1.0 - mix), 1.5)); + wetValue = static_cast (std::pow (std::sqrt (mix), 1.5)); + break; + + default: + dryValue = jmin (static_cast (0.5), static_cast (1.0) - mix); + wetValue = jmin (static_cast (0.5), mix); + break; + } + + dryVolume.setTargetValue (dryValue); + wetVolume.setTargetValue (wetValue); +} + +//============================================================================== +template class DryWetMixer; +template class DryWetMixer; + + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +struct DryWetMixerTests final : public UnitTest +{ + DryWetMixerTests() : UnitTest ("DryWetMixer", UnitTestCategories::dsp) {} + + enum class Kind { down, up }; + + static auto getRampBuffer (ProcessSpec spec, Kind kind) + { + AudioBuffer buffer ((int) spec.numChannels, (int) spec.maximumBlockSize); + + for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample) + { + for (uint32_t channel = 0; channel < spec.numChannels; ++channel) + { + const auto ramp = kind == Kind::up ? sample : spec.maximumBlockSize - sample; + + buffer.setSample ((int) channel, + (int) sample, + jmap ((float) ramp, 0.0f, (float) spec.maximumBlockSize, 0.0f, 1.0f)); + } + } + + return buffer; + } + + void runTest() override + { + constexpr ProcessSpec spec { 44100.0, 512, 2 }; + constexpr auto numBlocks = 5; + + const auto wetBuffer = getRampBuffer (spec, Kind::up); + const auto dryBuffer = getRampBuffer (spec, Kind::down); + + for (auto maxLatency : { 0, 100, 200, 512 }) + { + beginTest ("Mixer can push multiple small buffers"); + { + DryWetMixer mixer (maxLatency); + mixer.setWetMixProportion (0.5f); + mixer.prepare (spec); + + for (auto block = 0; block < numBlocks; ++block) + { + // Push samples one-by-one + for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample) + mixer.pushDrySamples (AudioBlock (dryBuffer).getSubBlock (sample, 1)); + + // Mix wet samples in one go + auto outputBlock = wetBuffer; + mixer.mixWetSamples ({ outputBlock }); + + // The output block should contain the wet and dry samples averaged + for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample) + { + for (uint32_t channel = 0; channel < spec.numChannels; ++channel) + { + const auto outputValue = outputBlock.getSample ((int) channel, (int) sample); + expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f); + } + } + } + } + + beginTest ("Mixer can pop multiple small buffers"); + { + DryWetMixer mixer (maxLatency); + mixer.setWetMixProportion (0.5f); + mixer.prepare (spec); + + for (auto block = 0; block < numBlocks; ++block) + { + // Push samples in one go + mixer.pushDrySamples ({ dryBuffer }); + + // Process wet samples one-by-one + for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample) + { + AudioBuffer outputBlock ((int) spec.numChannels, 1); + AudioBlock (wetBuffer).getSubBlock (sample, 1).copyTo (outputBlock); + mixer.mixWetSamples ({ outputBlock }); + + // The output block should contain the wet and dry samples averaged + for (uint32_t channel = 0; channel < spec.numChannels; ++channel) + { + const auto outputValue = outputBlock.getSample ((int) channel, 0); + expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f); + } + } + } + } + + beginTest ("Mixer can push and pop multiple small buffers"); + { + DryWetMixer mixer (maxLatency); + mixer.setWetMixProportion (0.5f); + mixer.prepare (spec); + + for (auto block = 0; block < numBlocks; ++block) + { + // Push dry samples and process wet samples one-by-one + for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample) + { + mixer.pushDrySamples (AudioBlock (dryBuffer).getSubBlock (sample, 1)); + + AudioBuffer outputBlock ((int) spec.numChannels, 1); + AudioBlock (wetBuffer).getSubBlock (sample, 1).copyTo (outputBlock); + mixer.mixWetSamples ({ outputBlock }); + + // The output block should contain the wet and dry samples averaged + for (uint32_t channel = 0; channel < spec.numChannels; ++channel) + { + const auto outputValue = outputBlock.getSample ((int) channel, 0); + expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f); + } + } + } + } + + beginTest ("Mixer can push and pop full-sized blocks after encountering a shorter block"); + { + DryWetMixer mixer (maxLatency); + mixer.setWetMixProportion (0.5f); + mixer.prepare (spec); + + constexpr auto shortBlockLength = spec.maximumBlockSize / 2; + AudioBuffer shortBlock (spec.numChannels, shortBlockLength); + mixer.pushDrySamples (AudioBlock (dryBuffer).getSubBlock (shortBlockLength)); + mixer.mixWetSamples ({ shortBlock }); + + for (auto block = 0; block < numBlocks; ++block) + { + // Push a full block of dry samples + mixer.pushDrySamples ({ dryBuffer }); + + // Mix a full block of wet samples + auto outputBlock = wetBuffer; + mixer.mixWetSamples ({ outputBlock }); + + // The output block should contain the wet and dry samples averaged + for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample) + { + for (uint32_t channel = 0; channel < spec.numChannels; ++channel) + { + const auto outputValue = outputBlock.getSample ((int) channel, (int) sample); + expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f); + } + } + } + } + } + } +}; + +static const DryWetMixerTests dryWetMixerTests; + +#endif + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_DryWetMixer.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_DryWetMixer.h new file mode 100644 index 00000000..93f41f7c --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_DryWetMixer.h @@ -0,0 +1,117 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +enum class DryWetMixingRule +{ + linear, // dry volume is equal to 1 - wet volume + balanced, // both dry and wet are 1 when mix is 0.5, with dry decreasing to 0 + // above this value and wet decreasing to 0 below it + sin3dB, // alternate dry/wet mixing rule using the 3 dB sine panning rule + sin4p5dB, // alternate dry/wet mixing rule using the 4.5 dB sine panning rule + sin6dB, // alternate dry/wet mixing rule using the 6 dB sine panning rule + squareRoot3dB, // alternate dry/wet mixing rule using the regular 3 dB panning rule + squareRoot4p5dB // alternate dry/wet mixing rule using the regular 4.5 dB panning rule +}; + +/** + A processor to handle dry/wet mixing of two audio signals, where the wet signal + may have additional latency. + + Once a DryWetMixer object is configured, push the dry samples using pushDrySamples + and mix into the fully wet samples using mixWetSamples. + + @tags{DSP} +*/ +template +class DryWetMixer +{ +public: + //============================================================================== + using MixingRule = DryWetMixingRule; + + //============================================================================== + /** Default constructor. */ + DryWetMixer(); + + /** Constructor. */ + explicit DryWetMixer (int maximumWetLatencyInSamples); + + //============================================================================== + /** Sets the mix rule. */ + void setMixingRule (MixingRule newRule); + + /** Sets the current dry/wet mix proportion, with 0.0 being full dry and 1.0 + being fully wet. + */ + void setWetMixProportion (SampleType newWetMixProportion); + + /** Sets the relative latency of the wet signal path compared to the dry signal + path, and thus the amount of latency compensation that will be added to the + dry samples in this processor. + */ + void setWetLatency (SampleType wetLatencyInSamples); + + //============================================================================== + /** Initialises the processor. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the processor. */ + void reset(); + + //============================================================================== + /** Copies the dry path samples into an internal delay line. */ + void pushDrySamples (const AudioBlock drySamples); + + /** Mixes the supplied wet samples with the latency-compensated dry samples from + pushDrySamples. + + @param wetSamples Input: The AudioBlock references fully wet samples. + Output: The AudioBlock references the wet samples mixed + with the latency compensated dry samples. + + @see pushDrySamples + */ + void mixWetSamples (AudioBlock wetSamples); + +private: + //============================================================================== + void update(); + + //============================================================================== + SmoothedValue dryVolume, wetVolume; + DelayLine dryDelayLine; + AudioBuffer bufferDry; + + SingleThreadedAbstractFifo fifo; + SampleType mix = 1.0; + MixingRule currentMixingRule = MixingRule::linear; + double sampleRate = 44100.0; + int maximumWetLatencyInSamples = 0; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter.cpp index 7493ecbd..e524ef4c 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter.cpp +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { template @@ -158,5 +155,4 @@ void FIR::Coefficients::Coefficients::normalise() noexcept template struct FIR::Coefficients; template struct FIR::Coefficients; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter.h index 5521a60d..809b5a02 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,15 +23,10 @@ ============================================================================== */ -namespace juce -{ -namespace dsp -{ - /** Classes for FIR filter processing. */ -namespace FIR +namespace juce::dsp::FIR { template struct Coefficients; @@ -77,12 +71,11 @@ namespace FIR //============================================================================== /** Prepare this filter for processing. */ - inline void prepare (const ProcessSpec& spec) noexcept + inline void prepare ([[maybe_unused]] const ProcessSpec& spec) noexcept { // This class can only process mono signals. Use the ProcessorDuplicator class // to apply this filter on a multi-channel audio stream. jassert (spec.numChannels == 1); - ignoreUnused (spec); reset(); } @@ -124,7 +117,7 @@ namespace FIR template void process (const ProcessContext& context) noexcept { - static_assert (std::is_same::value, + static_assert (std::is_same_v, "The sample-type of the FIR filter must match the sample-type supplied to this process callback"); check(); @@ -281,7 +274,5 @@ namespace FIR */ Array coefficients; }; -} -} // namespace dsp -} // namespace juce +} // namespace juce::dsp::FIR diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter_test.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter_test.cpp index 5cda6a7c..58c6714e 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter_test.cpp +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_FIRFilter_test.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,12 +23,10 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { -class FIRFilterTest : public UnitTest +class FIRFilterTest final : public UnitTest { template struct Helpers @@ -219,5 +216,4 @@ class FIRFilterTest : public UnitTest static FIRFilterTest firFilterUnitTest; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_FirstOrderTPTFilter.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_FirstOrderTPTFilter.cpp new file mode 100644 index 00000000..1543c840 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_FirstOrderTPTFilter.cpp @@ -0,0 +1,119 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +FirstOrderTPTFilter::FirstOrderTPTFilter() +{ + update(); +} + +//============================================================================== +template +void FirstOrderTPTFilter::setType (Type newValue) +{ + filterType = newValue; +} + +template +void FirstOrderTPTFilter::setCutoffFrequency (SampleType newValue) +{ + jassert (isPositiveAndBelow (newValue, static_cast (sampleRate * 0.5))); + + cutoffFrequency = newValue; + update(); +} + +//============================================================================== +template +void FirstOrderTPTFilter::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + s1.resize (spec.numChannels); + + update(); + reset(); +} + +template +void FirstOrderTPTFilter::reset() +{ + reset (static_cast (0)); +} + +template +void FirstOrderTPTFilter::reset (SampleType newValue) +{ + std::fill (s1.begin(), s1.end(), newValue); +} + +//============================================================================== +template +SampleType FirstOrderTPTFilter::processSample (int channel, SampleType inputValue) +{ + auto& s = s1[(size_t) channel]; + + auto v = G * (inputValue - s); + auto y = v + s; + s = y + v; + + switch (filterType) + { + case Type::lowpass: return y; + case Type::highpass: return inputValue - y; + case Type::allpass: return 2 * y - inputValue; + default: break; + } + + jassertfalse; + return y; +} + +template +void FirstOrderTPTFilter::snapToZero() noexcept +{ + for (auto& s : s1) + util::snapToZero (s); +} + +//============================================================================== +template +void FirstOrderTPTFilter::update() +{ + auto g = SampleType (std::tan (juce::MathConstants::pi * cutoffFrequency / sampleRate)); + G = g / (1 + g); +} + +//============================================================================== +template class FirstOrderTPTFilter; +template class FirstOrderTPTFilter; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_FirstOrderTPTFilter.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_FirstOrderTPTFilter.h new file mode 100644 index 00000000..1d210062 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_FirstOrderTPTFilter.h @@ -0,0 +1,148 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +enum class FirstOrderTPTFilterType +{ + lowpass, + highpass, + allpass +}; + +//============================================================================== +/** + A first order filter class using the TPT (Topology-Preserving Transform) structure. + + This filter can be modulated at high rates without producing audio artefacts. See + Vadim Zavalishin's documentation about TPT structures for more information. + + Note: Using this class prevents some loud audio artefacts commonly encountered when + changing the cutoff frequency using of other filter simulation structures and IIR + filter classes. However, this class may still require additional smoothing for + cutoff frequency changes. + + see StateVariableFilter, IIRFilter, SmoothedValue + + @tags{DSP} +*/ +template +class FirstOrderTPTFilter +{ +public: + //============================================================================== + using Type = FirstOrderTPTFilterType; + + //============================================================================== + /** Constructor. */ + FirstOrderTPTFilter(); + + //============================================================================== + /** Sets the filter type. */ + void setType (Type newType); + + /** Sets the cutoff frequency of the filter. + + @param newFrequencyHz cutoff frequency in Hz. + */ + void setCutoffFrequency (SampleType newFrequencyHz); + + //============================================================================== + /** Returns the type of the filter. */ + Type getType() const noexcept { return filterType; } + + /** Returns the cutoff frequency of the filter. */ + SampleType getCutoffFrequency() const noexcept { return cutoffFrequency; } + + //============================================================================== + /** Initialises the filter. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the filter. */ + void reset(); + + /** Resets the internal state variables of the filter to a given value. */ + void reset (SampleType newValue); + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + const auto numChannels = outputBlock.getNumChannels(); + const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumChannels() <= s1.size()); + jassert (inputBlock.getNumChannels() == numChannels); + jassert (inputBlock.getNumSamples() == numSamples); + + if (context.isBypassed) + { + outputBlock.copyFrom (inputBlock); + return; + } + + for (size_t channel = 0; channel < numChannels; ++channel) + { + auto* inputSamples = inputBlock .getChannelPointer (channel); + auto* outputSamples = outputBlock.getChannelPointer (channel); + + for (size_t i = 0; i < numSamples; ++i) + outputSamples[i] = processSample ((int) channel, inputSamples[i]); + } + + #if JUCE_DSP_ENABLE_SNAP_TO_ZERO + snapToZero(); + #endif + } + + //============================================================================== + /** Processes one sample at a time on a given channel. */ + SampleType processSample (int channel, SampleType inputValue); + + /** Ensure that the state variables are rounded to zero if the state + variables are denormals. This is only needed if you are doing + sample by sample processing. + */ + void snapToZero() noexcept; + +private: + //============================================================================== + void update(); + + //============================================================================== + SampleType G = 0; + std::vector s1 { 2 }; + double sampleRate = 44100.0; + + //============================================================================== + Type filterType = Type::lowpass; + SampleType cutoffFrequency = 1000.0; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.cpp index 49bd4e33..dc2353e2 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.cpp +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,323 +23,417 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp::IIR { -template -IIR::Coefficients::Coefficients() - : coefficients ({ NumericType(), - NumericType(), - NumericType(), - NumericType(), - NumericType() }) -{ -} +constexpr auto minimumDecibels = -300.0; template -IIR::Coefficients::Coefficients (NumericType b0, NumericType b1, - NumericType a0, NumericType a1) -{ - jassert (a0 != 0); - - coefficients.clear(); - - auto a0inv = static_cast (1) / a0; - - coefficients.add (b0 * a0inv, - b1 * a0inv, - a1 * a0inv); -} - -template -IIR::Coefficients::Coefficients (NumericType b0, NumericType b1, NumericType b2, - NumericType a0, NumericType a1, NumericType a2) -{ - jassert (a0 != 0); - - coefficients.clear(); - - auto a0inv = static_cast (1) / a0; - - coefficients.add (b0 * a0inv, - b1 * a0inv, - b2 * a0inv, - a1 * a0inv, - a2 * a0inv); -} - -template -IIR::Coefficients::Coefficients (NumericType b0, NumericType b1, NumericType b2, NumericType b3, - NumericType a0, NumericType a1, NumericType a2, NumericType a3) -{ - jassert (a0 != 0); - - coefficients.clear(); - - auto a0inv = static_cast (1) / a0; - - coefficients.add (b0 * a0inv, - b1 * a0inv, - b2 * a0inv, - b3 * a0inv, - a1 * a0inv, - a2 * a0inv, - a3 * a0inv); -} - -template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeFirstOrderLowPass (double sampleRate, - NumericType frequency) +std::array ArrayCoefficients::makeFirstOrderLowPass (double sampleRate, + NumericType frequency) { jassert (sampleRate > 0.0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); - auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + const auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); - return *new Coefficients (n, n, n + 1, n - 1); + return { { n, n, n + 1, n - 1 } }; } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeFirstOrderHighPass (double sampleRate, - NumericType frequency) +std::array ArrayCoefficients::makeFirstOrderHighPass (double sampleRate, + NumericType frequency) { jassert (sampleRate > 0.0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); - auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + const auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); - return *new Coefficients (1, -1, n + 1, n - 1); + return { { 1, -1, n + 1, n - 1 } }; } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeFirstOrderAllPass (double sampleRate, - NumericType frequency) +std::array ArrayCoefficients::makeFirstOrderAllPass (double sampleRate, + NumericType frequency) { jassert (sampleRate > 0.0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); - auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + const auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); - return *new Coefficients (n - 1, n + 1, n + 1, n - 1); + return { { n - 1, n + 1, n + 1, n - 1 } }; } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeLowPass (double sampleRate, - NumericType frequency) +std::array ArrayCoefficients::makeLowPass (double sampleRate, + NumericType frequency) { return makeLowPass (sampleRate, frequency, inverseRootTwo); } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeLowPass (double sampleRate, - NumericType frequency, - NumericType Q) +std::array ArrayCoefficients::makeLowPass (double sampleRate, + NumericType frequency, + NumericType Q) { jassert (sampleRate > 0.0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0.0); - auto n = 1 / std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); - auto nSquared = n * n; - auto invQ = 1 / Q; - auto c1 = 1 / (1 + invQ * n + nSquared); + const auto n = 1 / std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + const auto nSquared = n * n; + const auto invQ = 1 / Q; + const auto c1 = 1 / (1 + invQ * n + nSquared); - return *new Coefficients (c1, c1 * 2, c1, - 1, c1 * 2 * (1 - nSquared), - c1 * (1 - invQ * n + nSquared)); + return { { c1, c1 * 2, c1, + 1, c1 * 2 * (1 - nSquared), + c1 * (1 - invQ * n + nSquared) } }; } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeHighPass (double sampleRate, - NumericType frequency) +std::array ArrayCoefficients::makeHighPass (double sampleRate, + NumericType frequency) { return makeHighPass (sampleRate, frequency, inverseRootTwo); } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeHighPass (double sampleRate, - NumericType frequency, - NumericType Q) +std::array ArrayCoefficients::makeHighPass (double sampleRate, + NumericType frequency, + NumericType Q) { jassert (sampleRate > 0.0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0.0); - auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); - auto nSquared = n * n; - auto invQ = 1 / Q; - auto c1 = 1 / (1 + invQ * n + nSquared); + const auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + const auto nSquared = n * n; + const auto invQ = 1 / Q; + const auto c1 = 1 / (1 + invQ * n + nSquared); - return *new Coefficients (c1, c1 * -2, c1, - 1, c1 * 2 * (nSquared - 1), - c1 * (1 - invQ * n + nSquared)); + return { { c1, c1 * -2, c1, + 1, c1 * 2 * (nSquared - 1), + c1 * (1 - invQ * n + nSquared) } }; } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeBandPass (double sampleRate, - NumericType frequency) +std::array ArrayCoefficients::makeBandPass (double sampleRate, + NumericType frequency) { return makeBandPass (sampleRate, frequency, inverseRootTwo); } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeBandPass (double sampleRate, - NumericType frequency, - NumericType Q) +std::array ArrayCoefficients::makeBandPass (double sampleRate, + NumericType frequency, + NumericType Q) { jassert (sampleRate > 0.0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0.0); - auto n = 1 / std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); - auto nSquared = n * n; - auto invQ = 1 / Q; - auto c1 = 1 / (1 + invQ * n + nSquared); + const auto n = 1 / std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + const auto nSquared = n * n; + const auto invQ = 1 / Q; + const auto c1 = 1 / (1 + invQ * n + nSquared); - return *new Coefficients (c1 * n * invQ, 0, - -c1 * n * invQ, 1, - c1 * 2 * (1 - nSquared), - c1 * (1 - invQ * n + nSquared)); + return { { c1 * n * invQ, 0, + -c1 * n * invQ, 1, + c1 * 2 * (1 - nSquared), + c1 * (1 - invQ * n + nSquared) } }; } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeNotch (double sampleRate, - NumericType frequency) +std::array ArrayCoefficients::makeNotch (double sampleRate, + NumericType frequency) { return makeNotch (sampleRate, frequency, inverseRootTwo); } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeNotch (double sampleRate, - NumericType frequency, - NumericType Q) +std::array ArrayCoefficients::makeNotch (double sampleRate, + NumericType frequency, + NumericType Q) { jassert (sampleRate > 0.0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0.0); - auto n = 1 / std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); - auto nSquared = n * n; - auto invQ = 1 / Q; - auto c1 = 1 / (1 + n * invQ + nSquared); - auto b0 = c1 * (1 + nSquared); - auto b1 = 2 * c1 * (1 - nSquared); + const auto n = 1 / std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + const auto nSquared = n * n; + const auto invQ = 1 / Q; + const auto c1 = 1 / (1 + n * invQ + nSquared); + const auto b0 = c1 * (1 + nSquared); + const auto b1 = 2 * c1 * (1 - nSquared); - return *new Coefficients (b0, b1, b0, 1, b1, c1 * (1 - n * invQ + nSquared)); + return { { b0, b1, b0, 1, b1, c1 * (1 - n * invQ + nSquared) } }; } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeAllPass (double sampleRate, - NumericType frequency) +std::array ArrayCoefficients::makeAllPass (double sampleRate, + NumericType frequency) { return makeAllPass (sampleRate, frequency, inverseRootTwo); } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeAllPass (double sampleRate, - NumericType frequency, - NumericType Q) +std::array ArrayCoefficients::makeAllPass (double sampleRate, + NumericType frequency, + NumericType Q) { jassert (sampleRate > 0); jassert (frequency > 0 && frequency <= sampleRate * 0.5); jassert (Q > 0); - auto n = 1 / std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); - auto nSquared = n * n; - auto invQ = 1 / Q; - auto c1 = 1 / (1 + invQ * n + nSquared); - auto b0 = c1 * (1 - n * invQ + nSquared); - auto b1 = c1 * 2 * (1 - nSquared); + const auto n = 1 / std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + const auto nSquared = n * n; + const auto invQ = 1 / Q; + const auto c1 = 1 / (1 + invQ * n + nSquared); + const auto b0 = c1 * (1 - n * invQ + nSquared); + const auto b1 = c1 * 2 * (1 - nSquared); - return *new Coefficients (b0, b1, 1, 1, b1, b0); + return { { b0, b1, 1, 1, b1, b0 } }; } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeLowShelf (double sampleRate, - NumericType cutOffFrequency, - NumericType Q, - NumericType gainFactor) +std::array ArrayCoefficients::makeLowShelf (double sampleRate, + NumericType cutOffFrequency, + NumericType Q, + NumericType gainFactor) { jassert (sampleRate > 0.0); jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); jassert (Q > 0.0); - auto A = jmax (static_cast (0.0), std::sqrt (gainFactor)); - auto aminus1 = A - 1; - auto aplus1 = A + 1; - auto omega = (2 * MathConstants::pi * jmax (cutOffFrequency, static_cast (2.0))) / static_cast (sampleRate); - auto coso = std::cos (omega); - auto beta = std::sin (omega) * std::sqrt (A) / Q; - auto aminus1TimesCoso = aminus1 * coso; - - return *new Coefficients (A * (aplus1 - aminus1TimesCoso + beta), - A * 2 * (aminus1 - aplus1 * coso), - A * (aplus1 - aminus1TimesCoso - beta), - aplus1 + aminus1TimesCoso + beta, - -2 * (aminus1 + aplus1 * coso), - aplus1 + aminus1TimesCoso - beta); + const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, (NumericType) minimumDecibels)); + const auto aminus1 = A - 1; + const auto aplus1 = A + 1; + const auto omega = (2 * MathConstants::pi * jmax (cutOffFrequency, static_cast (2.0))) / static_cast (sampleRate); + const auto coso = std::cos (omega); + const auto beta = std::sin (omega) * std::sqrt (A) / Q; + const auto aminus1TimesCoso = aminus1 * coso; + + return { { A * (aplus1 - aminus1TimesCoso + beta), + A * 2 * (aminus1 - aplus1 * coso), + A * (aplus1 - aminus1TimesCoso - beta), + aplus1 + aminus1TimesCoso + beta, + -2 * (aminus1 + aplus1 * coso), + aplus1 + aminus1TimesCoso - beta } }; } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makeHighShelf (double sampleRate, - NumericType cutOffFrequency, - NumericType Q, - NumericType gainFactor) +std::array ArrayCoefficients::makeHighShelf (double sampleRate, + NumericType cutOffFrequency, + NumericType Q, + NumericType gainFactor) { jassert (sampleRate > 0); jassert (cutOffFrequency > 0 && cutOffFrequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0); - auto A = jmax (static_cast (0.0), std::sqrt (gainFactor)); - auto aminus1 = A - 1; - auto aplus1 = A + 1; - auto omega = (2 * MathConstants::pi * jmax (cutOffFrequency, static_cast (2.0))) / static_cast (sampleRate); - auto coso = std::cos (omega); - auto beta = std::sin (omega) * std::sqrt (A) / Q; - auto aminus1TimesCoso = aminus1 * coso; - - return *new Coefficients (A * (aplus1 + aminus1TimesCoso + beta), - A * -2 * (aminus1 + aplus1 * coso), - A * (aplus1 + aminus1TimesCoso - beta), - aplus1 - aminus1TimesCoso + beta, - 2 * (aminus1 - aplus1 * coso), - aplus1 - aminus1TimesCoso - beta); + const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, (NumericType) minimumDecibels)); + const auto aminus1 = A - 1; + const auto aplus1 = A + 1; + const auto omega = (2 * MathConstants::pi * jmax (cutOffFrequency, static_cast (2.0))) / static_cast (sampleRate); + const auto coso = std::cos (omega); + const auto beta = std::sin (omega) * std::sqrt (A) / Q; + const auto aminus1TimesCoso = aminus1 * coso; + + return { { A * (aplus1 + aminus1TimesCoso + beta), + A * -2 * (aminus1 + aplus1 * coso), + A * (aplus1 + aminus1TimesCoso - beta), + aplus1 - aminus1TimesCoso + beta, + 2 * (aminus1 - aplus1 * coso), + aplus1 - aminus1TimesCoso - beta } }; } template -typename IIR::Coefficients::Ptr IIR::Coefficients::makePeakFilter (double sampleRate, - NumericType frequency, - NumericType Q, - NumericType gainFactor) +std::array ArrayCoefficients::makePeakFilter (double sampleRate, + NumericType frequency, + NumericType Q, + NumericType gainFactor) { jassert (sampleRate > 0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0); jassert (gainFactor > 0); - auto A = jmax (static_cast (0.0), std::sqrt (gainFactor)); - auto omega = (2 * MathConstants::pi * jmax (frequency, static_cast (2.0))) / static_cast (sampleRate); - auto alpha = std::sin (omega) / (Q * 2); - auto c2 = -2 * std::cos (omega); - auto alphaTimesA = alpha * A; - auto alphaOverA = alpha / A; + const auto A = std::sqrt (Decibels::gainWithLowerBound (gainFactor, (NumericType) minimumDecibels)); + const auto omega = (2 * MathConstants::pi * jmax (frequency, static_cast (2.0))) / static_cast (sampleRate); + const auto alpha = std::sin (omega) / (Q * 2); + const auto c2 = -2 * std::cos (omega); + const auto alphaTimesA = alpha * A; + const auto alphaOverA = alpha / A; + + return { { 1 + alphaTimesA, c2, 1 - alphaTimesA, 1 + alphaOverA, c2, 1 - alphaOverA } }; +} + +template struct ArrayCoefficients; +template struct ArrayCoefficients; - return *new Coefficients (1 + alphaTimesA, c2, - 1 - alphaTimesA, - 1 + alphaOverA, c2, - 1 - alphaOverA); +//============================================================================== +template +Coefficients::Coefficients() +{ + assign ({ NumericType(), NumericType(), NumericType(), + NumericType(), NumericType(), NumericType() }); +} + +template +Coefficients::Coefficients (NumericType b0, NumericType b1, + NumericType a0, NumericType a1) +{ + assign ({ b0, b1, + a0, a1 }); +} + +template +Coefficients::Coefficients (NumericType b0, NumericType b1, NumericType b2, + NumericType a0, NumericType a1, NumericType a2) +{ + assign ({ b0, b1, b2, + a0, a1, a2 }); +} + +template +Coefficients::Coefficients (NumericType b0, NumericType b1, NumericType b2, NumericType b3, + NumericType a0, NumericType a1, NumericType a2, NumericType a3) +{ + assign ({ b0, b1, b2, b3, + a0, a1, a2, a3 }); +} + +template +typename Coefficients::Ptr Coefficients::makeFirstOrderLowPass (double sampleRate, + NumericType frequency) +{ + return *new Coefficients (ArrayCoeffs::makeFirstOrderLowPass (sampleRate, frequency)); +} + +template +typename Coefficients::Ptr Coefficients::makeFirstOrderHighPass (double sampleRate, + NumericType frequency) +{ + return *new Coefficients (ArrayCoeffs::makeFirstOrderHighPass (sampleRate, frequency)); +} + +template +typename Coefficients::Ptr Coefficients::makeFirstOrderAllPass (double sampleRate, + NumericType frequency) +{ + return *new Coefficients (ArrayCoeffs::makeFirstOrderAllPass (sampleRate, frequency)); +} + +template +typename Coefficients::Ptr Coefficients::makeLowPass (double sampleRate, + NumericType frequency) +{ + return *new Coefficients (ArrayCoeffs::makeLowPass (sampleRate, frequency)); +} + +template +typename Coefficients::Ptr Coefficients::makeLowPass (double sampleRate, + NumericType frequency, + NumericType Q) +{ + return *new Coefficients (ArrayCoeffs::makeLowPass (sampleRate, frequency, Q)); +} + +template +typename Coefficients::Ptr Coefficients::makeHighPass (double sampleRate, + NumericType frequency) +{ + return *new Coefficients (ArrayCoeffs::makeHighPass (sampleRate, frequency)); +} + +template +typename Coefficients::Ptr Coefficients::makeHighPass (double sampleRate, + NumericType frequency, + NumericType Q) +{ + return *new Coefficients (ArrayCoeffs::makeHighPass (sampleRate, frequency, Q)); +} + +template +typename Coefficients::Ptr Coefficients::makeBandPass (double sampleRate, + NumericType frequency) +{ + return *new Coefficients (ArrayCoeffs::makeBandPass (sampleRate, frequency)); +} + +template +typename Coefficients::Ptr Coefficients::makeBandPass (double sampleRate, + NumericType frequency, + NumericType Q) +{ + return *new Coefficients (ArrayCoeffs::makeBandPass (sampleRate, frequency, Q)); +} + +template +typename Coefficients::Ptr Coefficients::makeNotch (double sampleRate, + NumericType frequency) +{ + return *new Coefficients (ArrayCoeffs::makeNotch (sampleRate, frequency)); +} + +template +typename Coefficients::Ptr Coefficients::makeNotch (double sampleRate, + NumericType frequency, + NumericType Q) +{ + return *new Coefficients (ArrayCoeffs::makeNotch (sampleRate, frequency, Q)); +} + +template +typename Coefficients::Ptr Coefficients::makeAllPass (double sampleRate, + NumericType frequency) +{ + return *new Coefficients (ArrayCoeffs::makeAllPass (sampleRate, frequency)); +} + +template +typename Coefficients::Ptr Coefficients::makeAllPass (double sampleRate, + NumericType frequency, + NumericType Q) +{ + return *new Coefficients (ArrayCoeffs::makeAllPass (sampleRate, frequency, Q)); +} + +template +typename Coefficients::Ptr Coefficients::makeLowShelf (double sampleRate, + NumericType cutOffFrequency, + NumericType Q, + NumericType gainFactor) +{ + return *new Coefficients (ArrayCoeffs::makeLowShelf (sampleRate, cutOffFrequency, Q, gainFactor)); +} + +template +typename Coefficients::Ptr Coefficients::makeHighShelf (double sampleRate, + NumericType cutOffFrequency, + NumericType Q, + NumericType gainFactor) +{ + return *new Coefficients (ArrayCoeffs::makeHighShelf (sampleRate, cutOffFrequency, Q, gainFactor)); +} + +template +typename Coefficients::Ptr Coefficients::makePeakFilter (double sampleRate, + NumericType frequency, + NumericType Q, + NumericType gainFactor) +{ + return *new Coefficients (ArrayCoeffs::makePeakFilter (sampleRate, frequency, Q, gainFactor)); } template -size_t IIR::Coefficients::getFilterOrder() const noexcept +size_t Coefficients::getFilterOrder() const noexcept { return (static_cast (coefficients.size()) - 1) / 2; } template -double IIR::Coefficients::getMagnitudeForFrequency (double frequency, double sampleRate) const noexcept +double Coefficients::getMagnitudeForFrequency (double frequency, double sampleRate) const noexcept { constexpr Complex j (0, 1); const auto order = getFilterOrder(); @@ -370,8 +463,8 @@ double IIR::Coefficients::getMagnitudeForFrequency (double frequenc } template -void IIR::Coefficients::getMagnitudeForFrequencyArray (const double* frequencies, double* magnitudes, - size_t numSamples, double sampleRate) const noexcept +void Coefficients::getMagnitudeForFrequencyArray (const double* frequencies, double* magnitudes, + size_t numSamples, double sampleRate) const noexcept { constexpr Complex j (0, 1); const auto order = getFilterOrder(); @@ -401,12 +494,12 @@ void IIR::Coefficients::getMagnitudeForFrequencyArray (const double factor *= jw; } - magnitudes[i] = std::abs(numerator / denominator); + magnitudes[i] = std::abs (numerator / denominator); } } template -double IIR::Coefficients::getPhaseForFrequency (double frequency, double sampleRate) const noexcept +double Coefficients::getPhaseForFrequency (double frequency, double sampleRate) const noexcept { constexpr Complex j (0, 1); const auto order = getFilterOrder(); @@ -436,8 +529,8 @@ double IIR::Coefficients::getPhaseForFrequency (double frequency, d } template -void IIR::Coefficients::getPhaseForFrequencyArray (double* frequencies, double* phases, - size_t numSamples, double sampleRate) const noexcept +void Coefficients::getPhaseForFrequencyArray (double* frequencies, double* phases, + size_t numSamples, double sampleRate) const noexcept { jassert (sampleRate > 0); @@ -474,8 +567,7 @@ void IIR::Coefficients::getPhaseForFrequencyArray (double* frequenc } } -template struct IIR::Coefficients; -template struct IIR::Coefficients; +template struct Coefficients; +template struct Coefficients; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp::IIR diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.h index efd7bc3a..14064f53 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,129 +23,96 @@ ============================================================================== */ -namespace juce -{ -namespace dsp -{ - /** Classes for IIR filter processing. */ -namespace IIR +namespace juce::dsp::IIR { - template - struct Coefficients; - - /** - A processing class that can perform IIR filtering on an audio signal, using - the Transposed Direct Form II digital structure. - - If you need a lowpass, bandpass or highpass filter with fast modulation of - its cutoff frequency, you might use the class StateVariableFilter instead, - which is designed to prevent artefacts at parameter changes, instead of the - class Filter. - - @see Filter::Coefficients, FilterAudioSource, StateVariableFilter + /** A set of coefficients for use in an Filter object. @tags{DSP} */ - template - class Filter + template + struct ArrayCoefficients { - public: - /** The NumericType is the underlying primitive type used by the SampleType (which - could be either a primitive or vector) - */ - using NumericType = typename SampleTypeHelpers::ElementType::Type; - - /** A typedef for a ref-counted pointer to the coefficients object */ - using CoefficientsPtr = typename Coefficients::Ptr; + /** Returns the coefficients for a first order low-pass filter. */ + static std::array makeFirstOrderLowPass (double sampleRate, NumericType frequency); - //============================================================================== - /** Creates a filter. + /** Returns the coefficients for a first order high-pass filter. */ + static std::array makeFirstOrderHighPass (double sampleRate, NumericType frequency); - Initially the filter is inactive, so will have no effect on samples that - you process with it. You can modify the coefficients member to turn it into - the type of filter needed. - */ - Filter(); + /** Returns the coefficients for a first order all-pass filter. */ + static std::array makeFirstOrderAllPass (double sampleRate, NumericType frequency); - /** Creates a filter with a given set of coefficients. */ - Filter (CoefficientsPtr coefficientsToUse); + /** Returns the coefficients for a low-pass filter. */ + static std::array makeLowPass (double sampleRate, NumericType frequency); - Filter (const Filter&) = default; - Filter (Filter&&) = default; - Filter& operator= (const Filter&) = default; - Filter& operator= (Filter&&) = default; + /** Returns the coefficients for a low-pass filter with variable Q. */ + static std::array makeLowPass (double sampleRate, NumericType frequency, NumericType Q); - //============================================================================== - /** The coefficients of the IIR filter. It's up to the caller to ensure that - these coefficients are modified in a thread-safe way. + /** Returns the coefficients for a high-pass filter. */ + static std::array makeHighPass (double sampleRate, NumericType frequency); - If you change the order of the coefficients then you must call reset after - modifying them. - */ - CoefficientsPtr coefficients; + /** Returns the coefficients for a high-pass filter with variable Q. */ + static std::array makeHighPass (double sampleRate, NumericType frequency, NumericType Q); - //============================================================================== - /** Resets the filter's processing pipeline, ready to start a new stream of data. + /** Returns the coefficients for a band-pass filter. */ + static std::array makeBandPass (double sampleRate, NumericType frequency); - Note that this clears the processing state, but the type of filter and - its coefficients aren't changed. - */ - void reset() { reset (SampleType {0}); } + /** Returns the coefficients for a band-pass filter with variable Q. */ + static std::array makeBandPass (double sampleRate, NumericType frequency, NumericType Q); - /** Resets the filter's processing pipeline to a specific value. - @see reset - */ - void reset (SampleType resetToValue); + /** Returns the coefficients for a notch filter. */ + static std::array makeNotch (double sampleRate, NumericType frequency); - //============================================================================== - /** Called before processing starts. */ - void prepare (const ProcessSpec&) noexcept; + /** Returns the coefficients for a notch filter with variable Q. */ + static std::array makeNotch (double sampleRate, NumericType frequency, NumericType Q); - /** Processes a block of samples */ - template - void process (const ProcessContext& context) noexcept - { - if (context.isBypassed) - processInternal (context); - else - processInternal (context); - } + /** Returns the coefficients for an all-pass filter. */ + static std::array makeAllPass (double sampleRate, NumericType frequency); - /** Processes a single sample, without any locking. + /** Returns the coefficients for an all-pass filter with variable Q. */ + static std::array makeAllPass (double sampleRate, NumericType frequency, NumericType Q); - Use this if you need processing of a single value. + /** Returns the coefficients for a low-pass shelf filter with variable Q and gain. - Moreover, you might need the function snapToZero after a few calls to avoid - potential denormalisation issues. + The gain is a scale factor that the low frequencies are multiplied by, so values + greater than 1.0 will boost the low frequencies, values less than 1.0 will + attenuate them. */ - SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType sample) noexcept; + static std::array makeLowShelf (double sampleRate, + NumericType cutOffFrequency, + NumericType Q, + NumericType gainFactor); - /** Ensure that the state variables are rounded to zero if the state - variables are denormals. This is only needed if you are doing - sample by sample processing. - */ - void snapToZero() noexcept; + /** Returns the coefficients for a high-pass shelf filter with variable Q and gain. - private: - //============================================================================== - void check(); + The gain is a scale factor that the high frequencies are multiplied by, so values + greater than 1.0 will boost the high frequencies, values less than 1.0 will + attenuate them. + */ + static std::array makeHighShelf (double sampleRate, + NumericType cutOffFrequency, + NumericType Q, + NumericType gainFactor); - /** Processes a block of samples */ - template - void processInternal (const ProcessContext& context) noexcept; + /** Returns the coefficients for a peak filter centred around a + given frequency, with a variable Q and gain. - //============================================================================== - HeapBlock memory; - SampleType* state = nullptr; - size_t order = 0; + The gain is a scale factor that the centre frequencies are multiplied by, so + values greater than 1.0 will boost the centre frequencies, values less than + 1.0 will attenuate them. + */ + static std::array makePeakFilter (double sampleRate, + NumericType centreFrequency, + NumericType Q, + NumericType gainFactor); - JUCE_LEAK_DETECTOR (Filter) + private: + // Unfortunately, std::sqrt is not marked as constexpr just yet in all compilers + static constexpr NumericType inverseRootTwo = static_cast (0.70710678118654752440L); }; - //============================================================================== /** A set of coefficients for use in an Filter object. @see IIR::Filter @@ -177,6 +143,14 @@ namespace IIR Coefficients& operator= (const Coefficients&) = default; Coefficients& operator= (Coefficients&&) = default; + /** Constructs from an array. */ + template + explicit Coefficients (const std::array& values) { assignImpl (values.data()); } + + /** Assigns contents from an array. */ + template + Coefficients& operator= (const std::array& values) { return assignImpl (values.data()); } + /** The Coefficients structure is ref-counted, so this is a handy type that can be used as a pointer to one. */ @@ -295,12 +269,126 @@ namespace IIR Array coefficients; private: - // Unfortunately, std::sqrt is not marked as constexpr just yet in all compilers - static constexpr NumericType inverseRootTwo = static_cast (0.70710678118654752440L); + using ArrayCoeffs = ArrayCoefficients; + + template + Coefficients& assignImpl (const NumericType* values); + + template + Coefficients& assign (const NumericType (& values)[Num]) { return assignImpl (values); } }; -} // namespace IIR -} // namespace dsp -} // namespace juce + //============================================================================== + /** + A processing class that can perform IIR filtering on an audio signal, using + the Transposed Direct Form II digital structure. + + If you need a lowpass, bandpass or highpass filter with fast modulation of + its cutoff frequency, you might use the class StateVariableFilter instead, + which is designed to prevent artefacts at parameter changes, instead of the + class Filter. + + @see Filter::Coefficients, FilterAudioSource, StateVariableFilter -#include "juce_IIRFilter_Impl.h" + @tags{DSP} + */ + template + class Filter + { + public: + /** The NumericType is the underlying primitive type used by the SampleType (which + could be either a primitive or vector) + */ + using NumericType = typename SampleTypeHelpers::ElementType::Type; + + /** A typedef for a ref-counted pointer to the coefficients object */ + using CoefficientsPtr = typename Coefficients::Ptr; + + //============================================================================== + /** Creates a filter. + + Initially the filter is inactive, so will have no effect on samples that + you process with it. You can modify the coefficients member to turn it into + the type of filter needed. + */ + Filter(); + + /** Creates a filter with a given set of coefficients. */ + Filter (CoefficientsPtr coefficientsToUse); + + Filter (const Filter&) = default; + Filter (Filter&&) = default; + Filter& operator= (const Filter&) = default; + Filter& operator= (Filter&&) = default; + + //============================================================================== + /** The coefficients of the IIR filter. It's up to the caller to ensure that + these coefficients are modified in a thread-safe way. + + If you change the order of the coefficients then you must call reset after + modifying them. + */ + CoefficientsPtr coefficients; + + //============================================================================== + /** Resets the filter's processing pipeline, ready to start a new stream of data. + + Note that this clears the processing state, but the type of filter and + its coefficients aren't changed. + */ + void reset() { reset (SampleType {0}); } + + /** Resets the filter's processing pipeline to a specific value. + @see reset + */ + void reset (SampleType resetToValue); + + //============================================================================== + /** Called before processing starts. */ + void prepare (const ProcessSpec&) noexcept; + + /** Processes a block of samples */ + template + void process (const ProcessContext& context) noexcept + { + if (context.isBypassed) + processInternal (context); + else + processInternal (context); + + #if JUCE_DSP_ENABLE_SNAP_TO_ZERO + snapToZero(); + #endif + } + + /** Processes a single sample, without any locking. + + Use this if you need processing of a single value. + + Moreover, you might need the function snapToZero after a few calls to avoid + potential denormalisation issues. + */ + SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType sample) noexcept; + + /** Ensure that the state variables are rounded to zero if the state + variables are denormals. This is only needed if you are doing + sample by sample processing. + */ + void snapToZero() noexcept; + + private: + //============================================================================== + void check(); + + /** Processes a block of samples */ + template + void processInternal (const ProcessContext& context) noexcept; + + //============================================================================== + HeapBlock memory; + SampleType* state = nullptr; + size_t order = 0; + + JUCE_LEAK_DETECTOR (Filter) + }; +} // namespace juce::dsp::IIR diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter_Impl.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter_Impl.h index ce838003..2bf202f8 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter_Impl.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter_Impl.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,15 +23,32 @@ ============================================================================== */ -namespace juce -{ -namespace dsp -{ -namespace IIR +namespace juce::dsp::IIR { #ifndef DOXYGEN +template +template +Coefficients& Coefficients::assignImpl (const NumericType* values) +{ + static_assert (Num % 2 == 0, "Must supply an even number of coefficients"); + const auto a0Index = Num / 2; + const auto a0 = values[a0Index]; + const auto a0Inv = ! approximatelyEqual (a0, NumericType()) + ? static_cast (1) / values[a0Index] + : NumericType(); + + coefficients.clearQuick(); + coefficients.ensureStorageAllocated ((int) jmax ((size_t) 8, Num)); + + for (size_t i = 0; i < Num; ++i) + if (i != a0Index) + coefficients.add (values[i] * a0Inv); + + return *this; +} + //============================================================================== template Filter::Filter() @@ -70,7 +86,7 @@ template template void Filter::processInternal (const ProcessContext& context) noexcept { - static_assert (std::is_same::value, + static_assert (std::is_same_v, "The sample-type of the IIR filter must match the sample-type supplied to this process callback"); check(); @@ -193,7 +209,7 @@ SampleType JUCE_VECTOR_CALLTYPE Filter::processSample (SampleType sa check(); auto* c = coefficients->getRawCoefficients(); - auto output= (c[0] * sample) + state[0]; + auto output = (c[0] * sample) + state[0]; for (size_t j = 0; j < order - 1; ++j) state[j] = (c[j + 1] * sample) - (c[order + j + 1] * output) + state[j + 1]; @@ -221,6 +237,4 @@ void Filter::check() #endif -} // namespace IIR -} // namespace dsp -} // namespace juce +} // namespace juce::dsp::IIR diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_LadderFilter.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_LadderFilter.cpp deleted file mode 100644 index 255fb1fc..00000000 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_LadderFilter.cpp +++ /dev/null @@ -1,170 +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. - - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). - - 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 dsp -{ - -//============================================================================== -template -LadderFilter::LadderFilter() : state (2) -{ - setSampleRate (Type (1000)); // intentionally setting unrealistic default - // sample rate to catch missing initialisation bugs - setResonance (Type (0)); - setDrive (Type (1.2)); - setMode (Mode::LPF12); -} - -//============================================================================== -template -void LadderFilter::setMode (Mode newValue) noexcept -{ - switch (newValue) - { - case Mode::LPF12: A = {{ Type (0), Type (0), Type (1), Type (0), Type (0) }}; comp = Type (0.5); break; - case Mode::HPF12: A = {{ Type (1), Type (-2), Type (1), Type (0), Type (0) }}; comp = Type (0); break; - case Mode::LPF24: A = {{ Type (0), Type (0), Type (0), Type (0), Type (1) }}; comp = Type (0.5); break; - case Mode::HPF24: A = {{ Type (1), Type (-4), Type (6), Type (-4), Type (1) }}; comp = Type (0); break; - default: jassertfalse; break; - } - - static constexpr auto outputGain = Type (1.2); - - for (auto& a : A) - a *= outputGain; - - mode = newValue; - reset(); -} - -//============================================================================== -template -void LadderFilter::prepare (const juce::dsp::ProcessSpec& spec) -{ - setSampleRate (Type (spec.sampleRate)); - setNumChannels (spec.numChannels); - reset(); -} - -//============================================================================== -template -void LadderFilter::reset() noexcept -{ - for (auto& s : state) - s.fill (Type (0)); - - cutoffTransformSmoother.setCurrentAndTargetValue (cutoffTransformSmoother.getTargetValue()); - scaledResonanceSmoother.setCurrentAndTargetValue (scaledResonanceSmoother.getTargetValue()); -} - -//============================================================================== -template -void LadderFilter::setCutoffFrequencyHz (Type newValue) noexcept -{ - jassert (newValue > Type (0)); - cutoffFreqHz = newValue; - updateCutoffFreq(); -} - -//============================================================================== -template -void LadderFilter::setResonance (Type newValue) noexcept -{ - jassert (newValue >= Type (0) && newValue <= Type (1)); - resonance = newValue; - updateResonance(); -} - -//============================================================================== -template -void LadderFilter::setDrive (Type newValue) noexcept -{ - jassert (newValue >= Type (1)); - - drive = newValue; - gain = std::pow (drive, Type (-2.642)) * Type (0.6103) + Type (0.3903); - drive2 = drive * Type (0.04) + Type (0.96); - gain2 = std::pow (drive2, Type (-2.642)) * Type (0.6103) + Type (0.3903); -} - -//============================================================================== -template -Type LadderFilter::processSample (Type inputValue, size_t channelToUse) noexcept -{ - auto& s = state[channelToUse]; - - const auto a1 = cutoffTransformValue; - const auto g = a1 * Type (-1) + Type (1); - const auto b0 = g * Type (0.76923076923); - const auto b1 = g * Type (0.23076923076); - - const auto dx = gain * saturationLUT (drive * inputValue); - const auto a = dx + scaledResonanceValue * Type (-4) * (gain2 * saturationLUT (drive2 * s[4]) - dx * comp); - - const auto b = b1 * s[0] + a1 * s[1] + b0 * a; - const auto c = b1 * s[1] + a1 * s[2] + b0 * b; - const auto d = b1 * s[2] + a1 * s[3] + b0 * c; - const auto e = b1 * s[3] + a1 * s[4] + b0 * d; - - s[0] = a; - s[1] = b; - s[2] = c; - s[3] = d; - s[4] = e; - - return a * A[0] + b * A[1] + c * A[2] + d * A[3] + e * A[4]; -} - -//============================================================================== -template -void LadderFilter::updateSmoothers() noexcept -{ - cutoffTransformValue = cutoffTransformSmoother.getNextValue(); - scaledResonanceValue = scaledResonanceSmoother.getNextValue(); -} - -//============================================================================== -template -void LadderFilter::setSampleRate (Type newValue) noexcept -{ - jassert (newValue > Type (0)); - cutoffFreqScaler = Type (-2.0 * juce::MathConstants::pi) / newValue; - - static constexpr Type smootherRampTimeSec = Type (0.05); - cutoffTransformSmoother.reset (newValue, smootherRampTimeSec); - scaledResonanceSmoother.reset (newValue, smootherRampTimeSec); - - updateCutoffFreq(); -} - -//============================================================================== -template class LadderFilter; -template class LadderFilter; - -} // namespace dsp -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_LinkwitzRileyFilter.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_LinkwitzRileyFilter.cpp new file mode 100644 index 00000000..ae4b7cdd --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_LinkwitzRileyFilter.cpp @@ -0,0 +1,146 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +LinkwitzRileyFilter::LinkwitzRileyFilter() +{ + update(); +} + +//============================================================================== +template +void LinkwitzRileyFilter::setType (Type newType) +{ + filterType = newType; +} + +template +void LinkwitzRileyFilter::setCutoffFrequency (SampleType newCutoffFrequencyHz) +{ + jassert (isPositiveAndBelow (newCutoffFrequencyHz, static_cast (sampleRate * 0.5))); + + cutoffFrequency = newCutoffFrequencyHz; + update(); +} + +//============================================================================== +template +void LinkwitzRileyFilter::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + update(); + + s1.resize (spec.numChannels); + s2.resize (spec.numChannels); + s3.resize (spec.numChannels); + s4.resize (spec.numChannels); + + reset(); +} + +template +void LinkwitzRileyFilter::reset() +{ + for (auto s : { &s1, &s2, &s3, &s4 }) + std::fill (s->begin(), s->end(), static_cast (0)); +} + +template +void LinkwitzRileyFilter::snapToZero() noexcept +{ + for (auto s : { &s1, &s2, &s3, &s4 }) + for (auto& element : *s) + util::snapToZero (element); +} + +//============================================================================== +template +SampleType LinkwitzRileyFilter::processSample (int channel, SampleType inputValue) +{ + auto yH = (inputValue - (R2 + g) * s1[(size_t) channel] - s2[(size_t) channel]) * h; + + auto yB = g * yH + s1[(size_t) channel]; + s1[(size_t) channel] = g * yH + yB; + + auto yL = g * yB + s2[(size_t) channel]; + s2[(size_t) channel] = g * yB + yL; + + if (filterType == Type::allpass) + return yL - R2 * yB + yH; + + auto yH2 = ((filterType == Type::lowpass ? yL : yH) - (R2 + g) * s3[(size_t) channel] - s4[(size_t) channel]) * h; + + auto yB2 = g * yH2 + s3[(size_t) channel]; + s3[(size_t) channel] = g * yH2 + yB2; + + auto yL2 = g * yB2 + s4[(size_t) channel]; + s4[(size_t) channel] = g * yB2 + yL2; + + return filterType == Type::lowpass ? yL2 : yH2; +} + +template +void LinkwitzRileyFilter::processSample (int channel, SampleType inputValue, SampleType &outputLow, SampleType &outputHigh) +{ + auto yH = (inputValue - (R2 + g) * s1[(size_t) channel] - s2[(size_t) channel]) * h; + + auto yB = g * yH + s1[(size_t) channel]; + s1[(size_t) channel] = g * yH + yB; + + auto yL = g * yB + s2[(size_t) channel]; + s2[(size_t) channel] = g * yB + yL; + + auto yH2 = (yL - (R2 + g) * s3[(size_t) channel] - s4[(size_t) channel]) * h; + + auto yB2 = g * yH2 + s3[(size_t) channel]; + s3[(size_t) channel] = g * yH2 + yB2; + + auto yL2 = g * yB2 + s4[(size_t) channel]; + s4[(size_t) channel] = g * yB2 + yL2; + + outputLow = yL2; + outputHigh = yL - R2 * yB + yH - yL2; +} + +template +void LinkwitzRileyFilter::update() +{ + g = (SampleType) std::tan (MathConstants::pi * cutoffFrequency / sampleRate); + R2 = (SampleType) std::sqrt (2.0); + h = (SampleType) (1.0 / (1.0 + R2 * g + g * g)); +} + +//============================================================================== +template class LinkwitzRileyFilter; +template class LinkwitzRileyFilter; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_LinkwitzRileyFilter.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_LinkwitzRileyFilter.h new file mode 100644 index 00000000..58df0e2b --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_LinkwitzRileyFilter.h @@ -0,0 +1,140 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +enum class LinkwitzRileyFilterType +{ + lowpass, + highpass, + allpass +}; + +/** + A filter class designed to perform multi-band separation using the TPT + (Topology-Preserving Transform) structure. + + Linkwitz-Riley filters are widely used in audio crossovers that have two outputs, + a low-pass and a high-pass, such that their sum is equivalent to an all-pass filter + with a flat magnitude frequency response. The Linkwitz-Riley filters available in + this class are designed to have a -24 dB/octave slope (LR 4th order). + + @tags{DSP} +*/ +template +class LinkwitzRileyFilter +{ +public: + //============================================================================== + using Type = LinkwitzRileyFilterType; + + //============================================================================== + /** Constructor. */ + LinkwitzRileyFilter(); + + //============================================================================== + /** Sets the filter type. */ + void setType (Type newType); + + /** Sets the cutoff frequency of the filter in Hz. */ + void setCutoffFrequency (SampleType newCutoffFrequencyHz); + + //============================================================================== + /** Returns the type of the filter. */ + Type getType() const noexcept { return filterType; } + + /** Returns the cutoff frequency of the filter. */ + SampleType getCutoffFrequency() const noexcept { return cutoffFrequency; } + + //============================================================================== + /** Initialises the filter. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the filter. */ + void reset(); + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + const auto numChannels = outputBlock.getNumChannels(); + const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumChannels() <= s1.size()); + jassert (inputBlock.getNumChannels() == numChannels); + jassert (inputBlock.getNumSamples() == numSamples); + + if (context.isBypassed) + { + outputBlock.copyFrom (inputBlock); + return; + } + + for (size_t channel = 0; channel < numChannels; ++channel) + { + auto* inputSamples = inputBlock.getChannelPointer (channel); + auto* outputSamples = outputBlock.getChannelPointer (channel); + + for (size_t i = 0; i < numSamples; ++i) + outputSamples[i] = processSample ((int) channel, inputSamples[i]); + } + + #if JUCE_DSP_ENABLE_SNAP_TO_ZERO + snapToZero(); + #endif + } + + /** Performs the filter operation on a single sample at a time. */ + SampleType processSample (int channel, SampleType inputValue); + + /** Performs the filter operation on a single sample at a time, and returns both + the low-pass and the high-pass outputs of the TPT structure. + */ + void processSample (int channel, SampleType inputValue, SampleType &outputLow, SampleType &outputHigh); + + /** Ensure that the state variables are rounded to zero if the state + variables are denormals. This is only needed if you are doing + sample by sample processing. + */ + void snapToZero() noexcept; + +private: + //============================================================================== + void update(); + + //============================================================================== + SampleType g, R2, h; + std::vector s1, s2, s3, s4; + + double sampleRate = 44100.0; + SampleType cutoffFrequency = 2000.0; + Type filterType = Type::lowpass; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.cpp index 5833d781..829d26f5 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.cpp +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.cpp @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** Abstract class for the provided oversampling stages used internally in @@ -39,7 +36,7 @@ struct Oversampling::OversamplingStage virtual ~OversamplingStage() {} //============================================================================== - virtual SampleType getLatencyInSamples() = 0; + virtual SampleType getLatencyInSamples() const = 0; virtual void initProcessing (size_t maximumNumberOfSamplesBeforeOversampling) { @@ -71,14 +68,14 @@ struct Oversampling::OversamplingStage signal, which could be equivalent to a "one time" oversampling processing. */ template -struct OversamplingDummy : public Oversampling::OversamplingStage +struct OversamplingDummy final : public Oversampling::OversamplingStage { using ParentType = typename Oversampling::OversamplingStage; OversamplingDummy (size_t numChans) : ParentType (numChans, 1) {} //============================================================================== - SampleType getLatencyInSamples() override + SampleType getLatencyInSamples() const override { return 0; } @@ -111,7 +108,7 @@ struct OversamplingDummy : public Oversampling::OversamplingStage leading to specific processing optimizations. */ template -struct Oversampling2TimesEquirippleFIR : public Oversampling::OversamplingStage +struct Oversampling2TimesEquirippleFIR final : public Oversampling::OversamplingStage { using ParentType = typename Oversampling::OversamplingStage; @@ -139,7 +136,7 @@ struct Oversampling2TimesEquirippleFIR : public Oversampling::Overs } //============================================================================== - SampleType getLatencyInSamples() override + SampleType getLatencyInSamples() const override { return static_cast (coefficientsUp.getFilterOrder() + coefficientsDown.getFilterOrder()) * 0.5f; } @@ -263,7 +260,7 @@ struct Oversampling2TimesEquirippleFIR : public Oversampling::Overs phase, and provided with a method to get the exact resulting latency. */ template -struct Oversampling2TimesPolyphaseIIR : public Oversampling::OversamplingStage +struct Oversampling2TimesPolyphaseIIR final : public Oversampling::OversamplingStage { using ParentType = typename Oversampling::OversamplingStage; @@ -300,7 +297,7 @@ struct Oversampling2TimesPolyphaseIIR : public Oversampling::Oversa } //============================================================================== - SampleType getLatencyInSamples() override + SampleType getLatencyInSamples() const override { return latency; } @@ -364,8 +361,9 @@ struct Oversampling2TimesPolyphaseIIR : public Oversampling::Oversa } } - // Snap To Zero + #if JUCE_DSP_ENABLE_SNAP_TO_ZERO snapToZero (true); + #endif } void processSamplesDown (AudioBlock& outputBlock) override @@ -422,8 +420,9 @@ struct Oversampling2TimesPolyphaseIIR : public Oversampling::Oversa delayDown.setUnchecked (static_cast (channel), delay); } - // Snap To Zero + #if JUCE_DSP_ENABLE_SNAP_TO_ZERO snapToZero (false); + #endif } void snapToZero (bool snapUpProcessing) @@ -506,10 +505,10 @@ struct Oversampling2TimesPolyphaseIIR : public Oversampling::Oversa coeffs.coefficients.clear(); auto inversion = one / denominator[0]; - for (auto i = 0; i <= numerator.getOrder(); ++i) + for (int i = 0; i <= numerator.getOrder(); ++i) coeffs.coefficients.add (numerator[i] * inversion); - for (auto i = 1; i <= denominator.getOrder(); ++i) + for (int i = 1; i <= denominator.getOrder(); ++i) coeffs.coefficients.add (denominator[i] * inversion); return coeffs; @@ -539,8 +538,9 @@ Oversampling::Oversampling (size_t newNumChannels) template Oversampling::Oversampling (size_t newNumChannels, size_t newFactor, - FilterType newType, bool isMaximumQuality) - : numChannels (newNumChannels) + FilterType newType, bool isMaximumQuality, + bool useIntegerLatency) + : numChannels (newNumChannels), shouldUseIntegerLatency (useIntegerLatency) { jassert (isPositiveAndBelow (newFactor, 5) && numChannels > 0); @@ -561,8 +561,8 @@ Oversampling::Oversampling (size_t newNumChannels, size_t newFactor, auto gaindBFactorDown = (isMaximumQuality ? 10.0f : 8.0f); addOversamplingStage (FilterType::filterHalfBandPolyphaseIIR, - twUp, gaindBStartUp + gaindBFactorUp * n, - twDown, gaindBStartDown + gaindBFactorDown * n); + twUp, gaindBStartUp + gaindBFactorUp * (float) n, + twDown, gaindBStartDown + gaindBFactorDown * (float) n); } } else if (newType == FilterType::filterHalfBandFIREquiripple) @@ -578,8 +578,8 @@ Oversampling::Oversampling (size_t newNumChannels, size_t newFactor, auto gaindBFactorDown = (isMaximumQuality ? 10.0f : 8.0f); addOversamplingStage (FilterType::filterHalfBandFIREquiripple, - twUp, gaindBStartUp + gaindBFactorUp * n, - twDown, gaindBStartDown + gaindBFactorDown * n); + twUp, gaindBStartUp + gaindBFactorUp * (float) n, + twDown, gaindBStartDown + gaindBFactorDown * (float) n); } } } @@ -629,7 +629,20 @@ void Oversampling::clearOversamplingStages() //============================================================================== template -SampleType Oversampling::getLatencyInSamples() noexcept +void Oversampling::setUsingIntegerLatency (bool useIntegerLatency) noexcept +{ + shouldUseIntegerLatency = useIntegerLatency; +} + +template +SampleType Oversampling::getLatencyInSamples() const noexcept +{ + auto latency = getUncompensatedLatency(); + return shouldUseIntegerLatency ? latency + fractionalDelay : latency; +} + +template +SampleType Oversampling::getUncompensatedLatency() const noexcept { auto latency = static_cast (0); size_t order = 1; @@ -644,7 +657,7 @@ SampleType Oversampling::getLatencyInSamples() noexcept } template -size_t Oversampling::getOversamplingFactor() noexcept +size_t Oversampling::getOversamplingFactor() const noexcept { return factorOversampling; } @@ -662,6 +675,10 @@ void Oversampling::initProcessing (size_t maximumNumberOfSamplesBefo currentNumSamples *= stage->factor; } + ProcessSpec spec = { 0.0, (uint32) maximumNumberOfSamplesBeforeOversampling, (uint32) numChannels }; + delay.prepare (spec); + updateDelayLine(); + isReady = true; reset(); } @@ -674,6 +691,8 @@ void Oversampling::reset() noexcept if (isReady) for (auto* stage : stages) stage->reset(); + + delay.reset(); } template @@ -708,11 +727,11 @@ void Oversampling::processSamplesDown (AudioBlock& outpu auto currentNumSamples = outputBlock.getNumSamples(); for (int n = 0; n < stages.size() - 1; ++n) - currentNumSamples *= stages.getUnchecked(n)->factor; + currentNumSamples *= stages.getUnchecked (n)->factor; for (int n = stages.size() - 1; n > 0; --n) { - auto& stage = *stages.getUnchecked(n); + auto& stage = *stages.getUnchecked (n); auto audioBlock = stages.getUnchecked (n - 1)->getProcessedSamples (currentNumSamples); stage.processSamplesDown (audioBlock); @@ -720,10 +739,29 @@ void Oversampling::processSamplesDown (AudioBlock& outpu } stages.getFirst()->processSamplesDown (outputBlock); + + if (shouldUseIntegerLatency && fractionalDelay > static_cast (0.0)) + { + auto context = ProcessContextReplacing (outputBlock); + delay.process (context); + } +} + +template +void Oversampling::updateDelayLine() +{ + auto latency = getUncompensatedLatency(); + fractionalDelay = static_cast (1.0) - (latency - std::floor (latency)); + + if (approximatelyEqual (fractionalDelay, static_cast (1.0))) + fractionalDelay = static_cast (0.0); + else if (fractionalDelay < static_cast (0.618)) + fractionalDelay += static_cast (1.0); + + delay.setDelay (fractionalDelay); } template class Oversampling; template class Oversampling; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.h index b4121e15..cd3dee75 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,29 +23,26 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { //============================================================================== /** - A processing class performing multi-channel oversampling. + A processor that performs multi-channel oversampling. - It can be configured to do 2 times, 4 times, 8 times or 16 times oversampling - using a multi-stage approach, either polyphase allpass IIR filters or FIR - filters for the filtering, and reports successfully the latency added by the - filter stages. + This class can be configured to do a factor of 2, 4, 8 or 16 times + oversampling, using multiple stages, with polyphase allpass IIR filters or FIR + filters, and latency compensation. The principle of oversampling is to increase the sample rate of a given - non-linear process, to prevent it from creating aliasing. Oversampling works - by upsampling N times the input signal, processing the upsampled signal - with the increased internal sample rate, and downsampling the result to get - back the original processing sample rate. - - Choose between FIR or IIR filtering depending on your needs in term of - latency and phase distortion. With FIR filters, the phase is linear but the - latency is maximised. With IIR filtering, the phase is compromised around the + non-linear process to prevent it from creating aliasing. Oversampling works + by upsampling the input signal N times, processing the upsampled signal + with the increased internal sample rate, then downsampling the result to get + back to the original sample rate. + + Choose between FIR or IIR filtering depending on your needs in terms of + latency and phase distortion. With FIR filters the phase is linear but the + latency is maximised. With IIR filtering the phase is compromised around the Nyquist frequency but the latency is minimised. @see FilterDesign. @@ -66,49 +62,59 @@ class JUCE_API Oversampling }; //============================================================================== - /** - Constructor of the oversampling class. All the processing parameters must be - provided at the creation of the oversampling object. - - @param numChannels the number of channels to process with this object - @param factor the processing will perform 2 ^ factor times oversampling - @param type the type of filter design employed for filtering during - oversampling - @param isMaxQuality if the oversampling is done using the maximum quality, - the filters will be more efficient, but the CPU load will - increase as well - */ - Oversampling (size_t numChannels, - size_t factor, - FilterType type, - bool isMaxQuality = true); - - /** The default constructor of the oversampling class, which can be used to create an - empty object and then add the appropriate stages. + /** The default constructor. - Note: This creates a "dummy" oversampling stage, which needs to be removed first + Note: This creates a "dummy" oversampling stage, which needs to be removed before adding proper oversampling stages. + @param numChannels the number of channels to process with this object + @see clearOversamplingStages, addOversamplingStage */ explicit Oversampling (size_t numChannels = 1); + /** Constructor. + + @param numChannels the number of channels to process with this object + @param factor the processing will perform 2 ^ factor times oversampling + @param type the type of filter design employed for filtering during + oversampling + @param isMaxQuality if the oversampling is done using the maximum quality, where + the filters will be more efficient but the CPU load will + increase as well + @param useIntegerLatency if true this processor will add some fractional delay at the + end of the signal path to ensure that the overall latency of + the oversampling is an integer + */ + Oversampling (size_t numChannels, + size_t factor, + FilterType type, + bool isMaxQuality = true, + bool useIntegerLatency = false); + /** Destructor. */ ~Oversampling(); //============================================================================== - /** Returns the latency in samples of the whole processing. Use this information - in your main processor to compensate the additional latency involved with - the oversampling, for example with a dry / wet functionality, and to report - the latency to the DAW. + /* Sets if this processor should add some fractional delay at the end of the signal + path to ensure that the overall latency of the oversampling is an integer. + */ + void setUsingIntegerLatency (bool shouldUseIntegerLatency) noexcept; + + /** Returns the latency in samples of the overall processing. You can use this + information in your main processor to compensate the additional latency + involved with the oversampling, for example with a dry / wet mixer, and to + report the latency to the DAW. - Note: The latency might not be integer, so you might need to round its value - or to compensate it properly in your processing code. + Note: If you have not opted to use an integer latency then the latency may not be + integer, so you might need to round its value or to compensate it properly in + your processing code since plug-ins can only report integer latency values in + samples to the DAW. */ - SampleType getLatencyInSamples() noexcept; + SampleType getLatencyInSamples() const noexcept; /** Returns the current oversampling factor. */ - size_t getOversamplingFactor() noexcept; + size_t getOversamplingFactor() const noexcept; //============================================================================== /** Must be called before any processing, to set the buffer sizes of the internal @@ -188,13 +194,18 @@ class JUCE_API Oversampling #endif private: + //============================================================================== + void updateDelayLine(); + SampleType getUncompensatedLatency() const noexcept; + //============================================================================== OwnedArray stages; - bool isReady = false; + bool isReady = false, shouldUseIntegerLatency = false; + DelayLine delay { 8 }; + SampleType fractionalDelay = 0; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling) }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_Panner.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_Panner.cpp new file mode 100644 index 00000000..2d4300b3 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_Panner.cpp @@ -0,0 +1,140 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +Panner::Panner() +{ + update(); + reset(); +} + +//============================================================================== +template +void Panner::setRule (Rule newRule) +{ + currentRule = newRule; + update(); +} + +template +void Panner::setPan (SampleType newPan) +{ + jassert (newPan >= -1.0 && newPan <= 1.0); + + pan = jlimit (static_cast (-1.0), static_cast (1.0), newPan); + update(); +} + +//============================================================================== +template +void Panner::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + + reset(); +} + +template +void Panner::reset() +{ + leftVolume .reset (sampleRate, 0.05); + rightVolume.reset (sampleRate, 0.05); +} + +//============================================================================== +template +void Panner::update() +{ + SampleType leftValue, rightValue, boostValue; + + auto normalisedPan = static_cast (0.5) * (pan + static_cast (1.0)); + + switch (currentRule) + { + case Rule::balanced: + leftValue = jmin (static_cast (0.5), static_cast (1.0) - normalisedPan); + rightValue = jmin (static_cast (0.5), normalisedPan); + boostValue = static_cast (2.0); + break; + + case Rule::linear: + leftValue = static_cast (1.0) - normalisedPan; + rightValue = normalisedPan; + boostValue = static_cast (2.0); + break; + + case Rule::sin3dB: + leftValue = static_cast (std::sin (0.5 * MathConstants::pi * (1.0 - normalisedPan))); + rightValue = static_cast (std::sin (0.5 * MathConstants::pi * normalisedPan)); + boostValue = std::sqrt (static_cast (2.0)); + break; + + case Rule::sin4p5dB: + leftValue = static_cast (std::pow (std::sin (0.5 * MathConstants::pi * (1.0 - normalisedPan)), 1.5)); + rightValue = static_cast (std::pow (std::sin (0.5 * MathConstants::pi * normalisedPan), 1.5)); + boostValue = static_cast (std::pow (2.0, 3.0 / 4.0)); + break; + + case Rule::sin6dB: + leftValue = static_cast (std::pow (std::sin (0.5 * MathConstants::pi * (1.0 - normalisedPan)), 2.0)); + rightValue = static_cast (std::pow (std::sin (0.5 * MathConstants::pi * normalisedPan), 2.0)); + boostValue = static_cast (2.0); + break; + + case Rule::squareRoot3dB: + leftValue = std::sqrt (static_cast (1.0) - normalisedPan); + rightValue = std::sqrt (normalisedPan); + boostValue = std::sqrt (static_cast (2.0)); + break; + + case Rule::squareRoot4p5dB: + leftValue = static_cast (std::pow (std::sqrt (1.0 - normalisedPan), 1.5)); + rightValue = static_cast (std::pow (std::sqrt (normalisedPan), 1.5)); + boostValue = static_cast (std::pow (2.0, 3.0 / 4.0)); + break; + + default: + leftValue = jmin (static_cast (0.5), static_cast (1.0) - normalisedPan); + rightValue = jmin (static_cast (0.5), normalisedPan); + boostValue = static_cast (2.0); + break; + } + + leftVolume .setTargetValue (leftValue * boostValue); + rightVolume.setTargetValue (rightValue * boostValue); +} + +//============================================================================== +template class Panner; +template class Panner; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_Panner.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_Panner.h new file mode 100644 index 00000000..41fc336b --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_Panner.h @@ -0,0 +1,118 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +enum class PannerRule +{ + linear, // regular 6 dB or linear panning rule, allows the panned sound to be + // perceived as having a constant level when summed to mono + balanced, // both left and right are 1 when pan value is 0, with left decreasing + // to 0 above this value and right decreasing to 0 below it + sin3dB, // alternate version of the regular 3 dB panning rule with a sine curve + sin4p5dB, // alternate version of the regular 4.5 dB panning rule with a sine curve + sin6dB, // alternate version of the regular 6 dB panning rule with a sine curve + squareRoot3dB, // regular 3 dB or constant power panning rule, allows the panned sound + // to be perceived as having a constant level regardless of the pan position + squareRoot4p5dB // regular 4.5 dB panning rule, a compromise option between 3 dB and 6 dB panning rules +}; + +/** + A processor to perform panning operations on stereo buffers. + + @tags{DSP} +*/ +template +class Panner +{ +public: + //============================================================================== + using Rule = PannerRule; + + //============================================================================== + /** Constructor. */ + Panner(); + + //============================================================================== + /** Sets the panning rule. */ + void setRule (Rule newRule); + + /** Sets the current panning value, between -1 (full left) and 1 (full right). */ + void setPan (SampleType newPan); + + //============================================================================== + /** Initialises the processor. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the processor. */ + void reset(); + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + + const auto numInputChannels = inputBlock.getNumChannels(); + const auto numOutputChannels = outputBlock.getNumChannels(); + [[maybe_unused]] const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumSamples() == numSamples); + + if (numOutputChannels != 2 || numInputChannels == 0 || numInputChannels > 2) + return; + + if (numInputChannels == 2) + { + outputBlock.copyFrom (inputBlock); + } + else + { + outputBlock.getSingleChannelBlock (0).copyFrom (inputBlock); + outputBlock.getSingleChannelBlock (1).copyFrom (inputBlock); + } + + if (context.isBypassed) + return; + + outputBlock.getSingleChannelBlock (0).multiplyBy (leftVolume); + outputBlock.getSingleChannelBlock (1).multiplyBy (rightVolume); + } + +private: + //============================================================================== + void update(); + + //============================================================================== + Rule currentRule = Rule::balanced; + SampleType pan = 0.0; + SmoothedValue leftVolume, rightVolume; + double sampleRate = 44100.0; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessContext.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessContext.h index a99ce803..5a5da59a 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessContext.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessContext.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -47,6 +44,18 @@ struct ProcessSpec uint32 numChannels; }; +constexpr bool operator== (const ProcessSpec& a, const ProcessSpec& b) +{ + const auto tie = [] (const ProcessSpec& p) + { + return std::tie (p.sampleRate, p.maximumBlockSize, p.numChannels); + }; + + return tie (a) == tie (b); +} + +constexpr bool operator!= (const ProcessSpec& a, const ProcessSpec& b) { return ! (a == b); } + //============================================================================== /** This is a handy base class for the state of a processor (such as parameter values) @@ -138,7 +147,7 @@ struct ProcessContextNonReplacing using AudioBlockType = AudioBlock; using ConstAudioBlockType = AudioBlock; - /** Creates a ProcessContextReplacing that uses the given input and output blocks. + /** Creates a ProcessContextNonReplacing that uses the given input and output blocks. Note that the caller must not delete these blocks while they are still in use by this object! */ ProcessContextNonReplacing (const ConstAudioBlockType& input, AudioBlockType& output) noexcept @@ -174,5 +183,4 @@ struct ProcessContextNonReplacing AudioBlockType& outputBlock; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain.h index 388b220f..e0d8c9a2 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,114 +23,157 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { +//============================================================================== #ifndef DOXYGEN -namespace ProcessorHelpers // Internal helper classes used in building the ProcessorChain +/** The contents of this namespace are used to implement ProcessorChain and should + not be used elsewhere. Their interfaces (and existence) are liable to change! +*/ +namespace detail { - template - struct AccessHelper + template + constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple, std::index_sequence) + { + (fn (std::get (tuple), std::integral_constant()), ...); + } + + template + using TupleIndexSequence = std::make_index_sequence>>>; + + template + constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple) { - template - static auto& get (ProcessorType& a) noexcept { return AccessHelper::get (a.processors); } + forEachInTuple (std::forward (fn), std::forward (tuple), TupleIndexSequence{}); + } - template - static const auto& get (const ProcessorType& a) noexcept { return AccessHelper::get (a.processors); } + template + inline constexpr auto useContextDirectly = ! Context::usesSeparateInputAndOutputBlocks() || Ix == 0; +} +#endif - template - static void setBypassed (ProcessorType& a, bool bypassed) { AccessHelper::setBypassed (a.processors, bypassed); } - }; +/** This variadically-templated class lets you join together any number of processor + classes into a single processor which will call process() on them all in sequence. - template <> - struct AccessHelper<0> + @tags{DSP} +*/ +template +class ProcessorChain +{ +public: + /** Get a reference to the processor at index `Index`. */ + template auto& get() noexcept { return std::get (processors); } + + /** Get a reference to the processor at index `Index`. */ + template const auto& get() const noexcept { return std::get (processors); } + + /** Set the processor at index `Index` to be bypassed or enabled. */ + template + void setBypassed (bool b) noexcept { bypassed[(size_t) Index] = b; } + + /** Query whether the processor at index `Index` is bypassed. */ + template + bool isBypassed() const noexcept { return bypassed[(size_t) Index]; } + + /** Prepare all inner processors with the provided `ProcessSpec`. */ + void prepare (const ProcessSpec& spec) { - template - static auto& get (ProcessorType& a) noexcept { return a.getProcessor(); } + detail::forEachInTuple ([&] (auto& proc, auto) { proc.prepare (spec); }, processors); + } - template - static const auto& get (const ProcessorType& a) noexcept { return a.getProcessor(); } + /** Reset all inner processors. */ + void reset() + { + detail::forEachInTuple ([] (auto& proc, auto) { proc.reset(); }, processors); + } - template - static void setBypassed (ProcessorType& a, bool bypassed) { a.isBypassed = bypassed; } - }; + /** Process `context` through all inner processors in sequence. */ + template + void process (const ProcessContext& context) noexcept + { + detail::forEachInTuple ([this, &context] (auto& proc, auto index) noexcept { this->processOne (context, proc, index); }, + processors); + } - //============================================================================== - template - struct ChainElement +private: + template + void processOne (const Context& context, Proc& proc, std::integral_constant) noexcept { - void prepare (const ProcessSpec& spec) + if constexpr (detail::useContextDirectly) { - processor.prepare (spec); - } + auto contextCopy = context; + contextCopy.isBypassed = (bypassed[Ix] || context.isBypassed); - template - void process (const ProcessContext& context) noexcept - { - if (context.usesSeparateInputAndOutputBlocks() && ! isFirst) - { - jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels()); - ProcessContextReplacing replacingContext (context.getOutputBlock()); - replacingContext.isBypassed = (isBypassed || context.isBypassed); - - processor.process (replacingContext); - } - else - { - ProcessContext contextCopy (context); - contextCopy.isBypassed = (isBypassed || context.isBypassed); - - processor.process (contextCopy); - } + proc.process (contextCopy); } - - void reset() + else { - processor.reset(); + jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels()); + ProcessContextReplacing replacingContext (context.getOutputBlock()); + replacingContext.isBypassed = (bypassed[Ix] || context.isBypassed); + + proc.process (replacingContext); } + } - bool isBypassed = false; - Processor processor; + std::tuple processors; + std::array bypassed { {} }; +}; - Processor& getProcessor() noexcept { return processor; } - const Processor& getProcessor() const noexcept { return processor; } - Subclass& getThis() noexcept { return *static_cast (this); } - const Subclass& getThis() const noexcept { return *static_cast (this); } +/** Non-member equivalent of ProcessorChain::get which avoids awkward + member template syntax. +*/ +template +inline auto& get (ProcessorChain& chain) noexcept +{ + return chain.template get(); +} - template auto& get() noexcept { return AccessHelper::get (getThis()); } - template const auto& get() const noexcept { return AccessHelper::get (getThis()); } - template void setBypassed (bool bypassed) noexcept { AccessHelper::setBypassed (getThis(), bypassed); } - }; +/** Non-member equivalent of ProcessorChain::get which avoids awkward + member template syntax. +*/ +template +inline auto& get (const ProcessorChain& chain) noexcept +{ + return chain.template get(); +} - //============================================================================== - template - struct ChainBase : public ChainElement> - { - using Base = ChainElement>; +/** Non-member equivalent of ProcessorChain::setBypassed which avoids awkward + member template syntax. +*/ +template +inline void setBypassed (ProcessorChain& chain, bool bypassed) noexcept +{ + chain.template setBypassed (bypassed); +} - template - void process (const ProcessContext& context) noexcept { Base::process (context); processors.process (context); } - void prepare (const ProcessSpec& spec) { Base::prepare (spec); processors.prepare (spec); } - void reset() { Base::reset(); processors.reset(); } +/** Non-member equivalent of ProcessorChain::isBypassed which avoids awkward + member template syntax. +*/ +template +inline bool isBypassed (const ProcessorChain& chain) noexcept +{ + return chain.template isBypassed(); +} - ChainBase processors; - }; +} // namespace juce::dsp - template - struct ChainBase : public ChainElement> {}; -} -#endif +#ifndef DOXYGEN +namespace std +{ +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmismatched-tags") -//============================================================================== -/** - This variadically-templated class lets you join together any number of processor - classes into a single processor which will call process() on them all in sequence. -*/ +/** Adds support for C++17 structured bindings. */ template -using ProcessorChain = ProcessorHelpers::ChainBase; +struct tuple_size<::juce::dsp::ProcessorChain> : integral_constant {}; + +/** Adds support for C++17 structured bindings. */ +template +struct tuple_element> : tuple_element> {}; -} // namespace dsp -} // namespace juce +JUCE_END_IGNORE_WARNINGS_GCC_LIKE + +} // namespace std +#endif diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp new file mode 100644 index 00000000..bb2c168c --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp @@ -0,0 +1,164 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +class ProcessorChainTest final : public UnitTest +{ + template + struct MockProcessor + { + void prepare (const ProcessSpec&) noexcept { isPrepared = true; } + void reset() noexcept { isReset = true; } + + template + void process (const Context& context) noexcept + { + bufferWasClear = approximatelyEqual (context.getInputBlock().getSample (0, 0), 0.0f); + + if (! context.isBypassed) + context.getOutputBlock().add (AddValue); + } + + bool isPrepared = false; + bool isReset = false; + bool bufferWasClear = false; + }; + +public: + ProcessorChainTest() + : UnitTest ("ProcessorChain", UnitTestCategories::dsp) {} + + void runTest() override + { + beginTest ("After calling setBypass, processor is bypassed"); + { + ProcessorChain, MockProcessor<2>> chain; + + setBypassed<0> (chain, true); + expect (isBypassed<0> (chain)); + setBypassed<0> (chain, false); + expect (! isBypassed<0> (chain)); + + setBypassed<1> (chain, true); + expect (isBypassed<1> (chain)); + setBypassed<1> (chain, false); + expect (! isBypassed<1> (chain)); + } + + beginTest ("After calling prepare, all processors are prepared"); + { + ProcessorChain, MockProcessor<2>> chain; + + expect (! get<0> (chain).isPrepared); + expect (! get<1> (chain).isPrepared); + + chain.prepare (ProcessSpec{}); + + expect (get<0> (chain).isPrepared); + expect (get<1> (chain).isPrepared); + } + + beginTest ("After calling reset, all processors are reset"); + { + ProcessorChain, MockProcessor<2>> chain; + + expect (! get<0> (chain).isReset); + expect (! get<1> (chain).isReset); + + chain.reset(); + + expect (get<0> (chain).isReset); + expect (get<1> (chain).isReset); + } + + beginTest ("After calling process, all processors contribute to processing"); + { + ProcessorChain, MockProcessor<2>> chain; + + AudioBuffer buffer (1, 1); + AudioBlock block (buffer); + ProcessContextReplacing context (block); + + block.clear(); + chain.process (context); + expectEquals (buffer.getSample (0, 0), 3.0f); + expect (get<0> (chain).bufferWasClear); + expect (! get<1> (chain).bufferWasClear); + + setBypassed<0> (chain, true); + block.clear(); + chain.process (context); + expectEquals (buffer.getSample (0, 0), 2.0f); + expect (get<0> (chain).bufferWasClear); + expect (get<1> (chain).bufferWasClear); + + setBypassed<1> (chain, true); + block.clear(); + chain.process (context); + expectEquals (buffer.getSample (0, 0), 0.0f); + expect (get<0> (chain).bufferWasClear); + expect (get<1> (chain).bufferWasClear); + + setBypassed<0> (chain, false); + block.clear(); + chain.process (context); + expectEquals (buffer.getSample (0, 0), 1.0f); + expect (get<0> (chain).bufferWasClear); + expect (! get<1> (chain).bufferWasClear); + } + + beginTest ("Chains with trailing items that only support replacing contexts can be built"); + { + AudioBuffer inBuf (1, 1), outBuf (1, 1); + juce::dsp::AudioBlock in (inBuf), out (outBuf); + + struct OnlyReplacing + { + void prepare (const juce::dsp::ProcessSpec&) {} + void process (const juce::dsp::ProcessContextReplacing& c) + { + c.getOutputBlock().multiplyBy (2.0f); + } + void reset() {} + }; + + { + juce::dsp::ProcessorChain, OnlyReplacing, OnlyReplacing> c; + juce::dsp::ProcessContextNonReplacing context (in, out); + get<0> (c).setGainLinear (1.0f); + c.prepare (ProcessSpec{}); + inBuf.setSample (0, 0, 1.0f); + c.process (context); + expectEquals (outBuf.getSample (0, 0), 4.0f); + } + } + } +}; + +static ProcessorChainTest processorChainUnitTest; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorDuplicator.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorDuplicator.h index 191112be..7057fe2f 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorDuplicator.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorDuplicator.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -64,7 +61,7 @@ struct ProcessorDuplicator void reset() noexcept { for (auto* p : processors) p->reset(); } - template + template void process (const ProcessContext& context) noexcept { jassert ((int) context.getInputBlock().getNumChannels() <= processors.size()); @@ -96,5 +93,4 @@ struct ProcessorDuplicator juce::OwnedArray processors; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorWrapper.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorWrapper.h index d950f176..f6d08e1e 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorWrapper.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorWrapper.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -78,5 +75,4 @@ struct ProcessorWrapper : public ProcessorBase ProcessorType processor; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_StateVariableFilter.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_StateVariableFilter.h index bd4dd941..a9b06d43 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_StateVariableFilter.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_StateVariableFilter.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,29 +23,31 @@ ============================================================================== */ -namespace juce -{ -namespace dsp -{ - /** Classes for state variable filter processing. */ -namespace StateVariableFilter +namespace juce::dsp::StateVariableFilter { template struct Parameters; /** An IIR filter that can perform low, band and high-pass filtering on an audio - signal, with 12 dB of attenuation / octave, using a TPT structure, designed + signal, with 12 dB of attenuation per octave, using a TPT structure, designed for fast modulation (see Vadim Zavalishin's documentation about TPT structures for more information). Its behaviour is based on the analog state variable filter circuit. - Note: The bandpass here is not the one in the RBJ CookBook, its gain can be + Note: The bandpass here is not the one in the RBJ CookBook as its gain can be higher than 0 dB. For the classic 0 dB bandpass, we need to multiply the - result with R2 + result by R2. + + Note 2: Using this class prevents some loud audio artefacts commonly encountered when + changing the cutoff frequency using other filter simulation structures and IIR + filter classes. However, this class may still require additional smoothing for + cutoff frequency changes. + + see IIRFilter, SmoothedValue @tags{DSP} */ @@ -64,10 +65,17 @@ namespace StateVariableFilter using ParametersPtr = typename Parameters::Ptr; //============================================================================== + #ifndef DOXYGEN /** Creates a filter with default parameters. */ - Filter() : parameters (new Parameters) { reset(); } + [[deprecated ("The classes in the StateVariableFilter namespace are deprecated. you should " + "use the equivalent functionality in the StateVariableTPTFilter class.")]] + Filter() : parameters (new Parameters) { reset(); } + /** Creates a filter using some parameters. */ + [[deprecated ("The classes in the StateVariableFilter namespace are deprecated. you should " + "use the equivalent functionality in the StateVariableTPTFilter class.")]] Filter (ParametersPtr parametersToUse) : parameters (std::move (parametersToUse)) { reset(); } + #endif /** Creates a copy of another filter. */ Filter (const Filter&) = default; @@ -97,7 +105,7 @@ namespace StateVariableFilter template void process (const ProcessContext& context) noexcept { - static_assert (std::is_same::value, + static_assert (std::is_same_v, "The sample-type of the filter must match the sample-type supplied to this process callback"); if (context.isBypassed) @@ -145,7 +153,10 @@ namespace StateVariableFilter for (size_t i = 0 ; i < n; ++i) output[i] = processLoop (input[i], state); + #if JUCE_DSP_ENABLE_SNAP_TO_ZERO snapToZero(); + #endif + *parameters = state; } @@ -181,6 +192,13 @@ namespace StateVariableFilter JUCE_LEAK_DETECTOR (Filter) }; + enum class StateVariableFilterType + { + lowPass, + bandPass, + highPass + }; + //============================================================================== /** Structure used for the state variable filter parameters. @@ -191,12 +209,7 @@ namespace StateVariableFilter struct Parameters : public ProcessorState { //============================================================================== - enum class Type - { - lowPass, - bandPass, - highPass - }; + using Type = StateVariableFilterType; //============================================================================== /** The type of the IIR filter */ @@ -206,7 +219,7 @@ namespace StateVariableFilter Note: The bandwidth of the resonance increases with the value of the parameter. To have a standard 12 dB/octave filter, the value must be set - at 1 / sqrt(2). + at 1 / sqrt (2). */ void setCutOffFrequency (double sampleRate, NumericType frequency, NumericType resonance = static_cast (1.0 / MathConstants::sqrt2)) noexcept @@ -236,7 +249,5 @@ namespace StateVariableFilter NumericType R2 = static_cast (MathConstants::sqrt2); NumericType h = static_cast (1.0 / (1.0 + R2 * g + g * g)); }; -} -} // namespace dsp -} // namespace juce +} // namespace juce::dsp::StateVariableFilter diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_StateVariableTPTFilter.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_StateVariableTPTFilter.cpp new file mode 100644 index 00000000..14f550cf --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_StateVariableTPTFilter.cpp @@ -0,0 +1,134 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +StateVariableTPTFilter::StateVariableTPTFilter() +{ + update(); +} + +template +void StateVariableTPTFilter::setType (Type newValue) +{ + filterType = newValue; +} + +template +void StateVariableTPTFilter::setCutoffFrequency (SampleType newCutoffFrequencyHz) +{ + jassert (isPositiveAndBelow (newCutoffFrequencyHz, static_cast (sampleRate * 0.5))); + + cutoffFrequency = newCutoffFrequencyHz; + update(); +} + +template +void StateVariableTPTFilter::setResonance (SampleType newResonance) +{ + jassert (newResonance > static_cast (0)); + + resonance = newResonance; + update(); +} + +//============================================================================== +template +void StateVariableTPTFilter::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + + s1.resize (spec.numChannels); + s2.resize (spec.numChannels); + + reset(); + update(); +} + +template +void StateVariableTPTFilter::reset() +{ + reset (static_cast (0)); +} + +template +void StateVariableTPTFilter::reset (SampleType newValue) +{ + for (auto v : { &s1, &s2 }) + std::fill (v->begin(), v->end(), newValue); +} + +template +void StateVariableTPTFilter::snapToZero() noexcept +{ + for (auto v : { &s1, &s2 }) + for (auto& element : *v) + util::snapToZero (element); +} + +//============================================================================== +template +SampleType StateVariableTPTFilter::processSample (int channel, SampleType inputValue) +{ + auto& ls1 = s1[(size_t) channel]; + auto& ls2 = s2[(size_t) channel]; + + auto yHP = h * (inputValue - ls1 * (g + R2) - ls2); + + auto yBP = yHP * g + ls1; + ls1 = yHP * g + yBP; + + auto yLP = yBP * g + ls2; + ls2 = yBP * g + yLP; + + switch (filterType) + { + case Type::lowpass: return yLP; + case Type::bandpass: return yBP; + case Type::highpass: return yHP; + default: return yLP; + } +} + +//============================================================================== +template +void StateVariableTPTFilter::update() +{ + g = static_cast (std::tan (juce::MathConstants::pi * cutoffFrequency / sampleRate)); + R2 = static_cast (1.0 / resonance); + h = static_cast (1.0 / (1.0 + R2 * g + g * g)); +} + +//============================================================================== +template class StateVariableTPTFilter; +template class StateVariableTPTFilter; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_StateVariableTPTFilter.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_StateVariableTPTFilter.h new file mode 100644 index 00000000..23f47387 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_StateVariableTPTFilter.h @@ -0,0 +1,163 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +enum class StateVariableTPTFilterType +{ + lowpass, + bandpass, + highpass +}; + +//============================================================================== +/** An IIR filter that can perform low, band and high-pass filtering on an audio + signal, with 12 dB of attenuation per octave, using a TPT structure, designed + for fast modulation (see Vadim Zavalishin's documentation about TPT + structures for more information). Its behaviour is based on the analog + state variable filter circuit. + + Note: The bandpass here is not the one in the RBJ CookBook as its gain can be + higher than 0 dB. For the classic 0 dB bandpass, we need to multiply the + result by R2. + + Note 2: Using this class prevents some loud audio artefacts commonly encountered when + changing the cutoff frequency using other filter simulation structures and IIR + filter classes. However, this class may still require additional smoothing for + cutoff frequency changes. + + see IIRFilter, SmoothedValue + + @tags{DSP} +*/ +template +class StateVariableTPTFilter +{ +public: + //============================================================================== + using Type = StateVariableTPTFilterType; + + //============================================================================== + /** Constructor. */ + StateVariableTPTFilter(); + + //============================================================================== + /** Sets the filter type. */ + void setType (Type newType); + + /** Sets the cutoff frequency of the filter. + + @param newFrequencyHz the new cutoff frequency in Hz. + */ + void setCutoffFrequency (SampleType newFrequencyHz); + + /** Sets the resonance of the filter. + + Note: The bandwidth of the resonance increases with the value of the + parameter. To have a standard 12 dB / octave filter, the value must be set + at 1 / sqrt (2). + */ + void setResonance (SampleType newResonance); + + //============================================================================== + /** Returns the type of the filter. */ + Type getType() const noexcept { return filterType; } + + /** Returns the cutoff frequency of the filter. */ + SampleType getCutoffFrequency() const noexcept { return cutoffFrequency; } + + /** Returns the resonance of the filter. */ + SampleType getResonance() const noexcept { return resonance; } + + //============================================================================== + /** Initialises the filter. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the filter. */ + void reset(); + + /** Resets the internal state variables of the filter to a given value. */ + void reset (SampleType newValue); + + /** Ensure that the state variables are rounded to zero if the state + variables are denormals. This is only needed if you are doing + sample by sample processing. + */ + void snapToZero() noexcept; + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + const auto numChannels = outputBlock.getNumChannels(); + const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumChannels() <= s1.size()); + jassert (inputBlock.getNumChannels() == numChannels); + jassert (inputBlock.getNumSamples() == numSamples); + + if (context.isBypassed) + { + outputBlock.copyFrom (inputBlock); + return; + } + + for (size_t channel = 0; channel < numChannels; ++channel) + { + auto* inputSamples = inputBlock .getChannelPointer (channel); + auto* outputSamples = outputBlock.getChannelPointer (channel); + + for (size_t i = 0; i < numSamples; ++i) + outputSamples[i] = processSample ((int) channel, inputSamples[i]); + } + + #if JUCE_DSP_ENABLE_SNAP_TO_ZERO + snapToZero(); + #endif + } + + //============================================================================== + /** Processes one sample at a time on a given channel. */ + SampleType processSample (int channel, SampleType inputValue); + +private: + //============================================================================== + void update(); + + //============================================================================== + SampleType g, h, R2; + std::vector s1 { 2 }, s2 { 2 }; + + double sampleRate = 44100.0; + Type filterType = Type::lowpass; + SampleType cutoffFrequency = static_cast (1000.0), + resonance = static_cast (1.0 / std::sqrt (2.0)); +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_Bias.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Bias.h similarity index 87% rename from JuceLibraryCode/modules/juce_dsp/processors/juce_Bias.h rename to JuceLibraryCode/modules/juce_dsp/widgets/juce_Bias.h index 64bbe59d..a65e7760 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_Bias.h +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Bias.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -65,7 +62,7 @@ class Bias /** Sets the length of the ramp used for smoothing gain changes. */ void setRampDurationSeconds (double newDurationSeconds) noexcept { - if (rampDurationSeconds != newDurationSeconds) + if (! approximatelyEqual (rampDurationSeconds, newDurationSeconds)) { rampDurationSeconds = newDurationSeconds; updateRamp(); @@ -90,21 +87,21 @@ class Bias //============================================================================== /** Returns the result of processing a single sample. */ template - SampleType processSample (SampleType inputSample) const noexcept + SampleType processSample (SampleType inputSample) noexcept { return inputSample + bias.getNextValue(); } //============================================================================== /** Processes the input and output buffers supplied in the processing context. */ - template + template void process (const ProcessContext& context) noexcept { auto&& inBlock = context.getInputBlock(); auto&& outBlock = context.getOutputBlock(); jassert (inBlock.getNumChannels() == outBlock.getNumChannels()); - jassert (inBlock.getNumSamples() == outBlock.getNumSamples()); + jassert (inBlock.getNumSamples() == outBlock.getNumSamples()); auto len = inBlock.getNumSamples(); auto numChannels = inBlock.getNumChannels(); @@ -129,6 +126,7 @@ class Bias } else { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6386) auto* biases = static_cast (alloca (sizeof (FloatType) * len)); for (size_t i = 0; i < len; ++i) @@ -138,6 +136,7 @@ class Bias FloatVectorOperations::add (outBlock.getChannelPointer (chan), inBlock.getChannelPointer (chan), biases, static_cast (len)); + JUCE_END_IGNORE_WARNINGS_MSVC } } @@ -154,5 +153,4 @@ class Bias } }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Chorus.cpp b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Chorus.cpp new file mode 100644 index 00000000..8b448e13 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Chorus.cpp @@ -0,0 +1,138 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +Chorus::Chorus() +{ + auto oscFunction = [] (SampleType x) { return std::sin (x); }; + osc.initialise (oscFunction); + + dryWet.setMixingRule (DryWetMixingRule::linear); +} + +template +void Chorus::setRate (SampleType newRateHz) +{ + jassert (isPositiveAndBelow (newRateHz, static_cast (100.0))); + + rate = newRateHz; + update(); +} + +template +void Chorus::setDepth (SampleType newDepth) +{ + jassert (isPositiveAndNotGreaterThan (newDepth, maxDepth)); + + depth = newDepth; + update(); +} + +template +void Chorus::setCentreDelay (SampleType newDelayMs) +{ + jassert (isPositiveAndBelow (newDelayMs, maxCentreDelayMs)); + + centreDelay = jlimit (static_cast (1.0), maxCentreDelayMs, newDelayMs); +} + +template +void Chorus::setFeedback (SampleType newFeedback) +{ + jassert (newFeedback >= static_cast (-1.0) && newFeedback <= static_cast (1.0)); + + feedback = newFeedback; + update(); +} + +template +void Chorus::setMix (SampleType newMix) +{ + jassert (isPositiveAndNotGreaterThan (newMix, static_cast (1.0))); + + mix = newMix; + update(); +} + +//============================================================================== +template +void Chorus::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + + const auto maxPossibleDelay = std::ceil ((maximumDelayModulation * maxDepth * oscVolumeMultiplier + maxCentreDelayMs) + * sampleRate / 1000.0); + delay = DelayLine{ static_cast (maxPossibleDelay) }; + delay.prepare (spec); + + dryWet.prepare (spec); + feedbackVolume.resize (spec.numChannels); + lastOutput.resize (spec.numChannels); + + osc.prepare (spec); + bufferDelayTimes.setSize (1, (int) spec.maximumBlockSize, false, false, true); + + update(); + reset(); +} + +template +void Chorus::reset() +{ + std::fill (lastOutput.begin(), lastOutput.end(), static_cast (0)); + + delay.reset(); + osc.reset(); + dryWet.reset(); + + oscVolume.reset (sampleRate, 0.05); + + for (auto& vol : feedbackVolume) + vol.reset (sampleRate, 0.05); +} + +template +void Chorus::update() +{ + osc.setFrequency (rate); + oscVolume.setTargetValue (depth * oscVolumeMultiplier); + dryWet.setWetMixProportion (mix); + + for (auto& vol : feedbackVolume) + vol.setTargetValue (feedback); +} + +//============================================================================== +template class Chorus; +template class Chorus; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Chorus.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Chorus.h new file mode 100644 index 00000000..fb5368ef --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Chorus.h @@ -0,0 +1,166 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +/** + A simple chorus DSP widget that modulates the delay of a delay line in order to + create sweeping notches in the magnitude frequency response. + + This audio effect can be controlled via the speed and depth of the LFO controlling + the frequency response, a mix control, a feedback control, and the centre delay + of the modulation. + + Note: To get classic chorus sounds try to use a centre delay time around 7-8 ms + with a low feedback volume and a low depth. This effect can also be used as a + flanger with a lower centre delay time and a lot of feedback, and as a vibrato + effect if the mix value is 1. + + @tags{DSP} +*/ +template +class Chorus +{ +public: + //============================================================================== + /** Constructor. */ + Chorus(); + + //============================================================================== + /** Sets the rate (in Hz) of the LFO modulating the chorus delay line. This rate + must be lower than 100 Hz. + */ + void setRate (SampleType newRateHz); + + /** Sets the volume of the LFO modulating the chorus delay line (between 0 and 1). + */ + void setDepth (SampleType newDepth); + + /** Sets the centre delay in milliseconds of the chorus delay line modulation. + This delay must be between 1 and 100 ms. + */ + void setCentreDelay (SampleType newDelayMs); + + /** Sets the feedback volume (between -1 and 1) of the chorus delay line. + Negative values can be used to get specific chorus sounds. + */ + void setFeedback (SampleType newFeedback); + + /** Sets the amount of dry and wet signal in the output of the chorus (between 0 + for full dry and 1 for full wet). + */ + void setMix (SampleType newMix); + + //============================================================================== + /** Initialises the processor. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the processor. */ + void reset(); + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + const auto numChannels = outputBlock.getNumChannels(); + const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumChannels() == numChannels); + jassert (inputBlock.getNumChannels() == lastOutput.size()); + jassert (inputBlock.getNumSamples() == numSamples); + + if (context.isBypassed) + { + outputBlock.copyFrom (inputBlock); + return; + } + + auto delayValuesBlock = AudioBlock (bufferDelayTimes).getSubBlock (0, numSamples); + auto contextDelay = ProcessContextReplacing (delayValuesBlock); + delayValuesBlock.clear(); + + osc.process (contextDelay); + delayValuesBlock.multiplyBy (oscVolume); + + auto* delaySamples = bufferDelayTimes.getWritePointer (0); + + for (size_t i = 0; i < numSamples; ++i) + { + auto lfo = jmax (static_cast (1.0), maximumDelayModulation * delaySamples[i] + centreDelay); + delaySamples[i] = static_cast (lfo * sampleRate / 1000.0); + } + + dryWet.pushDrySamples (inputBlock); + + for (size_t channel = 0; channel < numChannels; ++channel) + { + auto* inputSamples = inputBlock .getChannelPointer (channel); + auto* outputSamples = outputBlock.getChannelPointer (channel); + + for (size_t i = 0; i < numSamples; ++i) + { + auto input = inputSamples[i]; + auto output = input - lastOutput[channel]; + + delay.pushSample ((int) channel, output); + delay.setDelay (delaySamples[i]); + output = delay.popSample ((int) channel); + + outputSamples[i] = output; + lastOutput[channel] = output * feedbackVolume[channel].getNextValue(); + } + } + + dryWet.mixWetSamples (outputBlock); + } + +private: + //============================================================================== + void update(); + + //============================================================================== + Oscillator osc; + DelayLine delay; + SmoothedValue oscVolume; + std::vector> feedbackVolume { 2 }; + DryWetMixer dryWet; + std::vector lastOutput { 2 }; + AudioBuffer bufferDelayTimes; + + double sampleRate = 44100.0; + SampleType rate = 1.0, depth = 0.25, feedback = 0.0, mix = 0.5, + centreDelay = 7.0; + + static constexpr SampleType maxDepth = 1.0, + maxCentreDelayMs = 100.0, + oscVolumeMultiplier = 0.5, + maximumDelayModulation = 20.0; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Compressor.cpp b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Compressor.cpp new file mode 100644 index 00000000..57c3379d --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Compressor.cpp @@ -0,0 +1,118 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +Compressor::Compressor() +{ + update(); +} + +//============================================================================== +template +void Compressor::setThreshold (SampleType newThreshold) +{ + thresholddB = newThreshold; + update(); +} + +template +void Compressor::setRatio (SampleType newRatio) +{ + jassert (newRatio >= static_cast (1.0)); + + ratio = newRatio; + update(); +} + +template +void Compressor::setAttack (SampleType newAttack) +{ + attackTime = newAttack; + update(); +} + +template +void Compressor::setRelease (SampleType newRelease) +{ + releaseTime = newRelease; + update(); +} + +//============================================================================== +template +void Compressor::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + + envelopeFilter.prepare (spec); + + update(); + reset(); +} + +template +void Compressor::reset() +{ + envelopeFilter.reset(); +} + +//============================================================================== +template +SampleType Compressor::processSample (int channel, SampleType inputValue) +{ + // Ballistics filter with peak rectifier + auto env = envelopeFilter.processSample (channel, inputValue); + + // VCA + auto gain = (env < threshold) ? static_cast (1.0) + : std::pow (env * thresholdInverse, ratioInverse - static_cast (1.0)); + + // Output + return gain * inputValue; +} + +template +void Compressor::update() +{ + threshold = Decibels::decibelsToGain (thresholddB, static_cast (-200.0)); + thresholdInverse = static_cast (1.0) / threshold; + ratioInverse = static_cast (1.0) / ratio; + + envelopeFilter.setAttackTime (attackTime); + envelopeFilter.setReleaseTime (releaseTime); +} + +//============================================================================== +template class Compressor; +template class Compressor; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Compressor.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Compressor.h new file mode 100644 index 00000000..e6114777 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Compressor.h @@ -0,0 +1,107 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +/** + A simple compressor with standard threshold, ratio, attack time and release time + controls. + + @tags{DSP} +*/ +template +class Compressor +{ +public: + //============================================================================== + /** Constructor. */ + Compressor(); + + //============================================================================== + /** Sets the threshold in dB of the compressor.*/ + void setThreshold (SampleType newThreshold); + + /** Sets the ratio of the compressor (must be higher or equal to 1).*/ + void setRatio (SampleType newRatio); + + /** Sets the attack time in milliseconds of the compressor.*/ + void setAttack (SampleType newAttack); + + /** Sets the release time in milliseconds of the compressor.*/ + void setRelease (SampleType newRelease); + + //============================================================================== + /** Initialises the processor. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the processor. */ + void reset(); + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + const auto numChannels = outputBlock.getNumChannels(); + const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumChannels() == numChannels); + jassert (inputBlock.getNumSamples() == numSamples); + + if (context.isBypassed) + { + outputBlock.copyFrom (inputBlock); + return; + } + + for (size_t channel = 0; channel < numChannels; ++channel) + { + auto* inputSamples = inputBlock .getChannelPointer (channel); + auto* outputSamples = outputBlock.getChannelPointer (channel); + + for (size_t i = 0; i < numSamples; ++i) + outputSamples[i] = processSample ((int) channel, inputSamples[i]); + } + } + + /** Performs the processing operation on a single sample at a time. */ + SampleType processSample (int channel, SampleType inputValue); + +private: + //============================================================================== + void update(); + + //============================================================================== + SampleType threshold, thresholdInverse, ratioInverse; + BallisticsFilter envelopeFilter; + + double sampleRate = 44100.0; + SampleType thresholddB = 0.0, ratio = 1.0, attackTime = 1.0, releaseTime = 100.0; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_Gain.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Gain.h similarity index 90% rename from JuceLibraryCode/modules/juce_dsp/processors/juce_Gain.h rename to JuceLibraryCode/modules/juce_dsp/widgets/juce_Gain.h index aec485fa..73ecf634 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_Gain.h +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Gain.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -56,7 +53,7 @@ class Gain /** Sets the length of the ramp used for smoothing gain changes. */ void setRampDurationSeconds (double newDurationSeconds) noexcept { - if (rampDurationSeconds != newDurationSeconds) + if (! approximatelyEqual (rampDurationSeconds, newDurationSeconds)) { rampDurationSeconds = newDurationSeconds; reset(); @@ -125,10 +122,12 @@ class Gain } else { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6386) auto* gains = static_cast (alloca (sizeof (FloatType) * len)); for (size_t i = 0; i < len; ++i) gains[i] = gain.getNextValue(); + JUCE_END_IGNORE_WARNINGS_MSVC for (size_t chan = 0; chan < numChannels; ++chan) FloatVectorOperations::multiply (outBlock.getChannelPointer (chan), @@ -143,5 +142,4 @@ class Gain double sampleRate = 0, rampDurationSeconds = 0; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_LadderFilter.cpp b/JuceLibraryCode/modules/juce_dsp/widgets/juce_LadderFilter.cpp new file mode 100644 index 00000000..bda863d4 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_LadderFilter.cpp @@ -0,0 +1,173 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +LadderFilter::LadderFilter() : state (2) +{ + setSampleRate (SampleType (1000)); // intentionally setting unrealistic default + // sample rate to catch missing initialisation bugs + setResonance (SampleType (0)); + setDrive (SampleType (1.2)); + + mode = Mode::LPF24; + setMode (Mode::LPF12); +} + +//============================================================================== +template +void LadderFilter::setMode (Mode newMode) noexcept +{ + if (newMode == mode) + return; + + switch (newMode) + { + case Mode::LPF12: A = {{ SampleType (0), SampleType (0), SampleType (1), SampleType (0), SampleType (0) }}; comp = SampleType (0.5); break; + case Mode::HPF12: A = {{ SampleType (1), SampleType (-2), SampleType (1), SampleType (0), SampleType (0) }}; comp = SampleType (0); break; + case Mode::BPF12: A = {{ SampleType (0), SampleType (0), SampleType (-1), SampleType (1), SampleType (0) }}; comp = SampleType (0.5); break; + case Mode::LPF24: A = {{ SampleType (0), SampleType (0), SampleType (0), SampleType (0), SampleType (1) }}; comp = SampleType (0.5); break; + case Mode::HPF24: A = {{ SampleType (1), SampleType (-4), SampleType (6), SampleType (-4), SampleType (1) }}; comp = SampleType (0); break; + case Mode::BPF24: A = {{ SampleType (0), SampleType (0), SampleType (1), SampleType (-2), SampleType (1) }}; comp = SampleType (0.5); break; + default: jassertfalse; break; + } + + static constexpr auto outputGain = SampleType (1.2); + + for (auto& a : A) + a *= outputGain; + + mode = newMode; + reset(); +} + +//============================================================================== +template +void LadderFilter::prepare (const ProcessSpec& spec) +{ + setSampleRate (SampleType (spec.sampleRate)); + setNumChannels (spec.numChannels); + reset(); +} + +//============================================================================== +template +void LadderFilter::reset() noexcept +{ + for (auto& s : state) + s.fill (SampleType (0)); + + cutoffTransformSmoother.setCurrentAndTargetValue (cutoffTransformSmoother.getTargetValue()); + scaledResonanceSmoother.setCurrentAndTargetValue (scaledResonanceSmoother.getTargetValue()); +} + +//============================================================================== +template +void LadderFilter::setCutoffFrequencyHz (SampleType newCutoff) noexcept +{ + jassert (newCutoff > SampleType (0)); + cutoffFreqHz = newCutoff; + updateCutoffFreq(); +} + +//============================================================================== +template +void LadderFilter::setResonance (SampleType newResonance) noexcept +{ + jassert (newResonance >= SampleType (0) && newResonance <= SampleType (1)); + resonance = newResonance; + updateResonance(); +} + +//============================================================================== +template +void LadderFilter::setDrive (SampleType newDrive) noexcept +{ + jassert (newDrive >= SampleType (1)); + + drive = newDrive; + gain = std::pow (drive, SampleType (-2.642)) * SampleType (0.6103) + SampleType (0.3903); + drive2 = drive * SampleType (0.04) + SampleType (0.96); + gain2 = std::pow (drive2, SampleType (-2.642)) * SampleType (0.6103) + SampleType (0.3903); +} + +//============================================================================== +template +SampleType LadderFilter::processSample (SampleType inputValue, size_t channelToUse) noexcept +{ + auto& s = state[channelToUse]; + + const auto a1 = cutoffTransformValue; + const auto g = a1 * SampleType (-1) + SampleType (1); + const auto b0 = g * SampleType (0.76923076923); + const auto b1 = g * SampleType (0.23076923076); + + const auto dx = gain * saturationLUT (drive * inputValue); + const auto a = dx + scaledResonanceValue * SampleType (-4) * (gain2 * saturationLUT (drive2 * s[4]) - dx * comp); + + const auto b = b1 * s[0] + a1 * s[1] + b0 * a; + const auto c = b1 * s[1] + a1 * s[2] + b0 * b; + const auto d = b1 * s[2] + a1 * s[3] + b0 * c; + const auto e = b1 * s[3] + a1 * s[4] + b0 * d; + + s[0] = a; + s[1] = b; + s[2] = c; + s[3] = d; + s[4] = e; + + return a * A[0] + b * A[1] + c * A[2] + d * A[3] + e * A[4]; +} + +//============================================================================== +template +void LadderFilter::updateSmoothers() noexcept +{ + cutoffTransformValue = cutoffTransformSmoother.getNextValue(); + scaledResonanceValue = scaledResonanceSmoother.getNextValue(); +} + +//============================================================================== +template +void LadderFilter::setSampleRate (SampleType newValue) noexcept +{ + jassert (newValue > SampleType (0)); + cutoffFreqScaler = SampleType (-2.0 * juce::MathConstants::pi) / newValue; + + static constexpr SampleType smootherRampTimeSec = SampleType (0.05); + cutoffTransformSmoother.reset (newValue, smootherRampTimeSec); + scaledResonanceSmoother.reset (newValue, smootherRampTimeSec); + + updateCutoffFreq(); +} + +//============================================================================== +template class LadderFilter; +template class LadderFilter; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_LadderFilter.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_LadderFilter.h similarity index 56% rename from JuceLibraryCode/modules/juce_dsp/processors/juce_LadderFilter.h rename to JuceLibraryCode/modules/juce_dsp/widgets/juce_LadderFilter.h index b8d14ff3..4ba62a8f 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_LadderFilter.h +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_LadderFilter.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,67 +23,76 @@ ============================================================================== */ -namespace juce +namespace juce::dsp { -namespace dsp + +enum class LadderFilterMode { + LPF12, // low-pass 12 dB/octave + HPF12, // high-pass 12 dB/octave + BPF12, // band-pass 12 dB/octave + LPF24, // low-pass 24 dB/octave + HPF24, // high-pass 24 dB/octave + BPF24 // band-pass 24 dB/octave +}; /** Multi-mode filter based on the Moog ladder filter. @tags{DSP} */ -template +template class LadderFilter { public: - enum class Mode - { - LPF12, // low-pass 12 dB/octave - HPF12, // high-pass 12 dB/octave - LPF24, // low-pass 24 dB/octave - HPF24 // high-pass 24 dB/octave - }; + //============================================================================== + using Mode = LadderFilterMode; //============================================================================== /** Creates an uninitialised filter. Call prepare() before first use. */ LadderFilter(); /** Enables or disables the filter. If disabled it will simply pass through the input signal. */ - void setEnabled (bool newValue) noexcept { enabled = newValue; } + void setEnabled (bool isEnabled) noexcept { enabled = isEnabled; } /** Sets filter mode. */ - void setMode (Mode newValue) noexcept; + void setMode (Mode newMode) noexcept; /** Initialises the filter. */ - void prepare (const juce::dsp::ProcessSpec& spec); + void prepare (const ProcessSpec& spec); /** Returns the current number of channels. */ - size_t getNumChannels() const noexcept { return state.size(); } + size_t getNumChannels() const noexcept { return state.size(); } /** Resets the internal state variables of the filter. */ void reset() noexcept; /** Sets the cutoff frequency of the filter. - @param newValue cutoff frequency in Hz */ - void setCutoffFrequencyHz (Type newValue) noexcept; + + @param newCutoff cutoff frequency in Hz + */ + void setCutoffFrequencyHz (SampleType newCutoff) noexcept; /** Sets the resonance of the filter. - @param newValue a value between 0 and 1; higher values increase the resonance and can result in self oscillation! */ - void setResonance (Type newValue) noexcept; + + @param newResonance a value between 0 and 1; higher values increase the resonance and can result in self oscillation! + */ + void setResonance (SampleType newResonance) noexcept; /** Sets the amount of saturation in the filter. - @param newValue saturation amount; it can be any number greater than or equal to one. Higher values result in more distortion.*/ - void setDrive (Type newValue) noexcept; + + @param newDrive saturation amount; it can be any number greater than or equal to one. Higher values result in more distortion. + */ + void setDrive (SampleType newDrive) noexcept; //============================================================================== template void process (const ProcessContext& context) noexcept { const auto& inputBlock = context.getInputBlock(); - auto& outputBlock = context.getOutputBlock(); + auto& outputBlock = context.getOutputBlock(); const auto numChannels = outputBlock.getNumChannels(); - const auto numSamples = outputBlock.getNumSamples(); + const auto numSamples = outputBlock.getNumSamples(); jassert (inputBlock.getNumChannels() <= getNumChannels()); jassert (inputBlock.getNumChannels() == numChannels); @@ -107,36 +115,36 @@ class LadderFilter protected: //============================================================================== - Type processSample (Type inputValue, size_t channelToUse) noexcept; + SampleType processSample (SampleType inputValue, size_t channelToUse) noexcept; void updateSmoothers() noexcept; private: //============================================================================== - Type drive, drive2, gain, gain2, comp; + void setSampleRate (SampleType newValue) noexcept; + void setNumChannels (size_t newValue) { state.resize (newValue); } + void updateCutoffFreq() noexcept { cutoffTransformSmoother.setTargetValue (std::exp (cutoffFreqHz * cutoffFreqScaler)); } + void updateResonance() noexcept { scaledResonanceSmoother.setTargetValue (jmap (resonance, SampleType (0.1), SampleType (1.0))); } + + //============================================================================== + SampleType drive, drive2, gain, gain2, comp; static constexpr size_t numStates = 5; - std::vector> state; - std::array A; + std::vector> state; + std::array A; - SmoothedValue cutoffTransformSmoother, scaledResonanceSmoother; - Type cutoffTransformValue, scaledResonanceValue; + SmoothedValue cutoffTransformSmoother, scaledResonanceSmoother; + SampleType cutoffTransformValue, scaledResonanceValue; - LookupTableTransform saturationLUT { [] (Type x) { return std::tanh (x); }, Type (-5), Type (5), 128 }; + LookupTableTransform saturationLUT { [] (SampleType x) { return std::tanh (x); }, + SampleType (-5), SampleType (5), 128 }; - Type cutoffFreqHz { Type (200) }; - Type resonance; + SampleType cutoffFreqHz { SampleType (200) }; + SampleType resonance; - Type cutoffFreqScaler; + SampleType cutoffFreqScaler; Mode mode; bool enabled = true; - - //============================================================================== - void setSampleRate (Type newValue) noexcept; - void setNumChannels (size_t newValue) { state.resize (newValue); } - void updateCutoffFreq() noexcept { cutoffTransformSmoother.setTargetValue (std::exp (cutoffFreqHz * cutoffFreqScaler)); } - void updateResonance() noexcept { scaledResonanceSmoother.setTargetValue (jmap (resonance, Type (0.1), Type (1.0))); } }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Limiter.cpp b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Limiter.cpp new file mode 100644 index 00000000..355e098e --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Limiter.cpp @@ -0,0 +1,95 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +void Limiter::setThreshold (SampleType newThreshold) +{ + thresholddB = newThreshold; + update(); +} + +template +void Limiter::setRelease (SampleType newRelease) +{ + releaseTime = newRelease; + update(); +} + +//============================================================================== +template +void Limiter::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + + firstStageCompressor.prepare (spec); + secondStageCompressor.prepare (spec); + + update(); + reset(); +} + +template +void Limiter::reset() +{ + firstStageCompressor.reset(); + secondStageCompressor.reset(); + + outputVolume.reset (sampleRate, 0.001); +} + +//============================================================================== +template +void Limiter::update() +{ + firstStageCompressor.setThreshold ((SampleType) -10.0); + firstStageCompressor.setRatio ((SampleType) 4.0); + firstStageCompressor.setAttack ((SampleType) 2.0); + firstStageCompressor.setRelease ((SampleType) 200.0); + + secondStageCompressor.setThreshold (thresholddB); + secondStageCompressor.setRatio ((SampleType) 1000.0); + secondStageCompressor.setAttack ((SampleType) 0.001); + secondStageCompressor.setRelease (releaseTime); + + auto ratioInverse = (SampleType) (1.0 / 4.0); + + auto gain = (SampleType) std::pow (10.0, 10.0 * (1.0 - ratioInverse) / 40.0); + gain *= Decibels::decibelsToGain (-thresholddB, (SampleType) -100.0); + + outputVolume.setTargetValue (gain); +} + +//============================================================================== +template class Limiter; +template class Limiter; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Limiter.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Limiter.h new file mode 100644 index 00000000..48ca85c5 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Limiter.h @@ -0,0 +1,102 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +/** + A simple limiter with standard threshold and release time controls, featuring + two compressors and a hard clipper at 0 dB. + + @tags{DSP} +*/ +template +class Limiter +{ +public: + //============================================================================== + /** Constructor. */ + Limiter() = default; + + //============================================================================== + /** Sets the threshold in dB of the limiter.*/ + void setThreshold (SampleType newThreshold); + + /** Sets the release time in milliseconds of the limiter.*/ + void setRelease (SampleType newRelease); + + //============================================================================== + /** Initialises the processor. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the processor. */ + void reset(); + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + const auto numChannels = outputBlock.getNumChannels(); + const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumChannels() == numChannels); + jassert (inputBlock.getNumSamples() == numSamples); + + if (context.isBypassed) + { + outputBlock.copyFrom (inputBlock); + return; + } + + firstStageCompressor.process (context); + + auto secondContext = ProcessContextReplacing (outputBlock); + secondStageCompressor.process (secondContext); + + outputBlock.multiplyBy (outputVolume); + + for (size_t channel = 0; channel < numChannels; ++channel) + { + FloatVectorOperations::clip (outputBlock.getChannelPointer (channel), outputBlock.getChannelPointer (channel), + (SampleType) -1.0, (SampleType) 1.0, (int) numSamples); + } + } + +private: + //============================================================================== + void update(); + + //============================================================================== + Compressor firstStageCompressor, secondStageCompressor; + SmoothedValue outputVolume; + + double sampleRate = 44100.0; + SampleType thresholddB = -10.0, releaseTime = 100.0; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_NoiseGate.cpp b/JuceLibraryCode/modules/juce_dsp/widgets/juce_NoiseGate.cpp new file mode 100644 index 00000000..010e9e81 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_NoiseGate.cpp @@ -0,0 +1,126 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +NoiseGate::NoiseGate() +{ + update(); + + RMSFilter.setLevelCalculationType (BallisticsFilterLevelCalculationType::RMS); + RMSFilter.setAttackTime (static_cast (0.0)); + RMSFilter.setReleaseTime (static_cast (50.0)); +} + +template +void NoiseGate::setThreshold (SampleType newValue) +{ + thresholddB = newValue; + update(); +} + +template +void NoiseGate::setRatio (SampleType newRatio) +{ + jassert (newRatio >= static_cast (1.0)); + + ratio = newRatio; + update(); +} + +template +void NoiseGate::setAttack (SampleType newAttack) +{ + attackTime = newAttack; + update(); +} + +template +void NoiseGate::setRelease (SampleType newRelease) +{ + releaseTime = newRelease; + update(); +} + +//============================================================================== +template +void NoiseGate::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + + RMSFilter.prepare (spec); + envelopeFilter.prepare (spec); + + update(); + reset(); +} + +template +void NoiseGate::reset() +{ + RMSFilter.reset(); + envelopeFilter.reset(); +} + +//============================================================================== +template +SampleType NoiseGate::processSample (int channel, SampleType sample) +{ + // RMS ballistics filter + auto env = RMSFilter.processSample (channel, sample); + + // Ballistics filter + env = envelopeFilter.processSample (channel, env); + + // VCA + auto gain = (env > threshold) ? static_cast (1.0) + : std::pow (env * thresholdInverse, currentRatio - static_cast (1.0)); + + // Output + return gain * sample; +} + +template +void NoiseGate::update() +{ + threshold = Decibels::decibelsToGain (thresholddB, static_cast (-200.0)); + thresholdInverse = static_cast (1.0) / threshold; + currentRatio = ratio; + + envelopeFilter.setAttackTime (attackTime); + envelopeFilter.setReleaseTime (releaseTime); +} + +//============================================================================== +template class NoiseGate; +template class NoiseGate; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_NoiseGate.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_NoiseGate.h new file mode 100644 index 00000000..f5506647 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_NoiseGate.h @@ -0,0 +1,107 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +/** + A simple noise gate with standard threshold, ratio, attack time and + release time controls. Can be used as an expander if the ratio is low. + + @tags{DSP} +*/ +template +class NoiseGate +{ +public: + //============================================================================== + /** Constructor. */ + NoiseGate(); + + //============================================================================== + /** Sets the threshold in dB of the noise-gate.*/ + void setThreshold (SampleType newThreshold); + + /** Sets the ratio of the noise-gate (must be higher or equal to 1).*/ + void setRatio (SampleType newRatio); + + /** Sets the attack time in milliseconds of the noise-gate.*/ + void setAttack (SampleType newAttack); + + /** Sets the release time in milliseconds of the noise-gate.*/ + void setRelease (SampleType newRelease); + + //============================================================================== + /** Initialises the processor. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the processor. */ + void reset(); + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + const auto numChannels = outputBlock.getNumChannels(); + const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumChannels() == numChannels); + jassert (inputBlock.getNumSamples() == numSamples); + + if (context.isBypassed) + { + outputBlock.copyFrom (inputBlock); + return; + } + + for (size_t channel = 0; channel < numChannels; ++channel) + { + auto* inputSamples = inputBlock .getChannelPointer (channel); + auto* outputSamples = outputBlock.getChannelPointer (channel); + + for (size_t i = 0; i < numSamples; ++i) + outputSamples[i] = processSample ((int) channel, inputSamples[i]); + } + } + + /** Performs the processing operation on a single sample at a time. */ + SampleType processSample (int channel, SampleType inputValue); + +private: + //============================================================================== + void update(); + + //============================================================================== + SampleType threshold, thresholdInverse, currentRatio; + BallisticsFilter envelopeFilter, RMSFilter; + + double sampleRate = 44100.0; + SampleType thresholddB = -100, ratio = 10.0, attackTime = 1.0, releaseTime = 100.0; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_Oscillator.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Oscillator.h similarity index 93% rename from JuceLibraryCode/modules/juce_dsp/processors/juce_Oscillator.h rename to JuceLibraryCode/modules/juce_dsp/widgets/juce_Oscillator.h index cb42ba67..512ef80c 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_Oscillator.h +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Oscillator.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -51,7 +48,7 @@ class Oscillator If lookup table is not zero, then the function will be approximated with a lookup table. */ - Oscillator (const std::function& function, + Oscillator (const std::function& function, size_t lookupTableNumPoints = 0) { initialise (function, lookupTableNumPoints); @@ -61,7 +58,7 @@ class Oscillator bool isInitialised() const noexcept { return static_cast (generator); } /** Initialises the oscillator with a waveform. */ - void initialise (const std::function& function, + void initialise (const std::function& function, size_t lookupTableNumPoints = 0) { if (lookupTableNumPoints != 0) @@ -240,7 +237,7 @@ class Oscillator private: //============================================================================== - std::function generator; + std::function generator; std::unique_ptr> lookupTable; Array rampBuffer; SmoothedValue frequency { static_cast (440.0) }; @@ -248,5 +245,4 @@ class Oscillator Phase phase; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Phaser.cpp b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Phaser.cpp new file mode 100644 index 00000000..e85f5830 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Phaser.cpp @@ -0,0 +1,151 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +//============================================================================== +template +Phaser::Phaser() +{ + auto oscFunction = [] (SampleType x) { return std::sin (x); }; + osc.initialise (oscFunction); + + for (auto n = 0; n < numStages; ++n) + { + filters.add (new FirstOrderTPTFilter()); + filters[n]->setType (FirstOrderTPTFilterType::allpass); + } + + dryWet.setMixingRule (DryWetMixingRule::linear); +} + +template +void Phaser::setRate (SampleType newRateHz) +{ + jassert (isPositiveAndBelow (newRateHz, static_cast (100.0))); + + rate = newRateHz; + update(); +} + +template +void Phaser::setDepth (SampleType newDepth) +{ + jassert (isPositiveAndNotGreaterThan (newDepth, static_cast (1.0))); + + depth = newDepth; + update(); +} + +template +void Phaser::setCentreFrequency (SampleType newCentreHz) +{ + jassert (isPositiveAndBelow (newCentreHz, static_cast (sampleRate * 0.5))); + + centreFrequency = newCentreHz; + normCentreFrequency = mapFromLog10 (centreFrequency, static_cast (20.0), static_cast (jmin (20000.0, 0.49 * sampleRate))); +} + +template +void Phaser::setFeedback (SampleType newFeedback) +{ + jassert (newFeedback >= static_cast (-1.0) && newFeedback <= static_cast (1.0)); + + feedback = newFeedback; + update(); +} + +template +void Phaser::setMix (SampleType newMix) +{ + jassert (isPositiveAndNotGreaterThan (newMix, static_cast (1.0))); + + mix = newMix; + update(); +} + +//============================================================================== +template +void Phaser::prepare (const ProcessSpec& spec) +{ + jassert (spec.sampleRate > 0); + jassert (spec.numChannels > 0); + + sampleRate = spec.sampleRate; + + for (auto n = 0; n < numStages; ++n) + filters[n]->prepare (spec); + + dryWet.prepare (spec); + feedbackVolume.resize (spec.numChannels); + lastOutput.resize (spec.numChannels); + + auto specDown = spec; + specDown.sampleRate /= (double) maxUpdateCounter; + specDown.maximumBlockSize = specDown.maximumBlockSize / (uint32) maxUpdateCounter + 1; + + osc.prepare (specDown); + bufferFrequency.setSize (1, (int) specDown.maximumBlockSize, false, false, true); + + update(); + reset(); +} + +template +void Phaser::reset() +{ + std::fill (lastOutput.begin(), lastOutput.end(), static_cast (0)); + + for (auto n = 0; n < numStages; ++n) + filters[n]->reset(); + + osc.reset(); + dryWet.reset(); + + oscVolume.reset (sampleRate / (double) maxUpdateCounter, 0.05); + + for (auto& vol : feedbackVolume) + vol.reset (sampleRate, 0.05); + + updateCounter = 0; +} + +template +void Phaser::update() +{ + osc.setFrequency (rate); + oscVolume.setTargetValue (depth * (SampleType) 0.5); + dryWet.setWetMixProportion (mix); + + for (auto& vol : feedbackVolume) + vol.setTargetValue (feedback); +} + +//============================================================================== +template class Phaser; +template class Phaser; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Phaser.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Phaser.h new file mode 100644 index 00000000..ea4a00f3 --- /dev/null +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Phaser.h @@ -0,0 +1,203 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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::dsp +{ + +/** + A 6 stage phaser that modulates first order all-pass filters to create sweeping + notches in the magnitude frequency response. + + This audio effect can be controlled with standard phaser parameters: the speed + and depth of the LFO controlling the frequency response, a mix control, a + feedback control, and the centre frequency of the modulation. + + @tags{DSP} +*/ +template +class Phaser +{ +public: + //============================================================================== + /** Constructor. */ + Phaser(); + + //============================================================================== + /** Sets the rate (in Hz) of the LFO modulating the phaser all-pass filters. This + rate must be lower than 100 Hz. + */ + void setRate (SampleType newRateHz); + + /** Sets the volume (between 0 and 1) of the LFO modulating the phaser all-pass + filters. + */ + void setDepth (SampleType newDepth); + + /** Sets the centre frequency (in Hz) of the phaser all-pass filters modulation. + */ + void setCentreFrequency (SampleType newCentreHz); + + /** Sets the feedback volume (between -1 and 1) of the phaser. Negative can be + used to get specific phaser sounds. + */ + void setFeedback (SampleType newFeedback); + + /** Sets the amount of dry and wet signal in the output of the phaser (between 0 + for full dry and 1 for full wet). + */ + void setMix (SampleType newMix); + + //============================================================================== + /** Initialises the processor. */ + void prepare (const ProcessSpec& spec); + + /** Resets the internal state variables of the processor. */ + void reset(); + + //============================================================================== + /** Processes the input and output samples supplied in the processing context. */ + template + void process (const ProcessContext& context) noexcept + { + const auto& inputBlock = context.getInputBlock(); + auto& outputBlock = context.getOutputBlock(); + const auto numChannels = outputBlock.getNumChannels(); + const auto numSamples = outputBlock.getNumSamples(); + + jassert (inputBlock.getNumChannels() == numChannels); + jassert (inputBlock.getNumChannels() == lastOutput.size()); + jassert (inputBlock.getNumSamples() == numSamples); + + if (context.isBypassed) + { + outputBlock.copyFrom (inputBlock); + return; + } + + int numSamplesDown = 0; + auto counter = updateCounter; + + for (size_t i = 0; i < numSamples; ++i) + { + if (counter == 0) + numSamplesDown++; + + counter++; + + if (counter == maxUpdateCounter) + counter = 0; + } + + if (numSamplesDown > 0) + { + auto freqBlock = AudioBlock (bufferFrequency).getSubBlock (0, (size_t) numSamplesDown); + auto contextFreq = ProcessContextReplacing (freqBlock); + freqBlock.clear(); + + osc.process (contextFreq); + freqBlock.multiplyBy (oscVolume); + } + + auto* freqSamples = bufferFrequency.getWritePointer (0); + + for (int i = 0; i < numSamplesDown; ++i) + { + auto lfo = jlimit (static_cast (0.0), + static_cast (1.0), + freqSamples[i] + normCentreFrequency); + + freqSamples[i] = mapToLog10 (lfo, static_cast (20.0), + static_cast (jmin (20000.0, 0.49 * sampleRate))); + } + + auto currentFrequency = filters[0]->getCutoffFrequency(); + dryWet.pushDrySamples (inputBlock); + + for (size_t channel = 0; channel < numChannels; ++channel) + { + counter = updateCounter; + int k = 0; + + auto* inputSamples = inputBlock .getChannelPointer (channel); + auto* outputSamples = outputBlock.getChannelPointer (channel); + + for (size_t i = 0; i < numSamples; ++i) + { + auto input = inputSamples[i]; + auto output = input - lastOutput[channel]; + + if (i == 0 && counter != 0) + for (int n = 0; n < numStages; ++n) + filters[n]->setCutoffFrequency (currentFrequency); + + if (counter == 0) + { + for (int n = 0; n < numStages; ++n) + filters[n]->setCutoffFrequency (freqSamples[k]); + + k++; + } + + for (int n = 0; n < numStages; ++n) + output = filters[n]->processSample ((int) channel, output); + + outputSamples[i] = output; + lastOutput[channel] = output * feedbackVolume[channel].getNextValue(); + + counter++; + + if (counter == maxUpdateCounter) + counter = 0; + } + } + + dryWet.mixWetSamples (outputBlock); + updateCounter = (updateCounter + (int) numSamples) % maxUpdateCounter; + } + +private: + //============================================================================== + void update(); + + //============================================================================== + Oscillator osc; + OwnedArray> filters; + SmoothedValue oscVolume; + std::vector> feedbackVolume { 2 }; + DryWetMixer dryWet; + std::vector lastOutput { 2 }; + AudioBuffer bufferFrequency; + SampleType normCentreFrequency = 0.5; + double sampleRate = 44100.0; + + int updateCounter = 0; + static constexpr int maxUpdateCounter = 4; + + SampleType rate = 1.0, depth = 0.5, feedback = 0.0, mix = 0.5; + SampleType centreFrequency = 1300.0; + static constexpr int numStages = 6; +}; + +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_Reverb.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Reverb.h similarity index 88% rename from JuceLibraryCode/modules/juce_dsp/processors/juce_Reverb.h rename to JuceLibraryCode/modules/juce_dsp/widgets/juce_Reverb.h index d1c625e1..c80f4c0e 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_Reverb.h +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Reverb.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -61,7 +58,7 @@ class Reverb //============================================================================== /** Initialises the reverb. */ - void prepare (const juce::dsp::ProcessSpec& spec) + void prepare (const ProcessSpec& spec) { reverb.setSampleRate (spec.sampleRate); } @@ -112,5 +109,4 @@ class Reverb bool enabled = true; }; -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_WaveShaper.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_WaveShaper.h similarity index 80% rename from JuceLibraryCode/modules/juce_dsp/processors/juce_WaveShaper.h rename to JuceLibraryCode/modules/juce_dsp/widgets/juce_WaveShaper.h index bb061f43..116c3e9d 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_WaveShaper.h +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_WaveShaper.h @@ -2,17 +2,16 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). @@ -24,9 +23,7 @@ ============================================================================== */ -namespace juce -{ -namespace dsp +namespace juce::dsp { /** @@ -72,15 +69,12 @@ struct WaveShaper }; //============================================================================== -// Although clang supports C++17, their standard library still has no invoke_result -// support. Remove the "|| JUCE_CLANG" once clang supports this properly! -#if (! JUCE_CXX17_IS_AVAILABLE) || JUCE_CLANG +#if ! ((JUCE_MAC || JUCE_IOS) && JUCE_CLANG && __clang_major__ < 10) template -static WaveShaper, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; } +static WaveShaper, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; } #else template -static WaveShaper, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; } +static WaveShaper, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; } #endif -} // namespace dsp -} // namespace juce +} // namespace juce::dsp diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp index d6013a1e..5e8c1786 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,7 +23,7 @@ namespace juce { -class ActionBroadcaster::ActionMessage : public MessageManager::MessageBase +class ActionBroadcaster::ActionMessage final : public MessageManager::MessageBase { public: ActionMessage (const ActionBroadcaster* ab, @@ -86,7 +86,7 @@ void ActionBroadcaster::sendActionMessage (const String& message) const const ScopedLock sl (actionListenerLock); for (int i = actionListeners.size(); --i >= 0;) - (new ActionMessage (this, message, actionListeners.getUnchecked(i)))->post(); + (new ActionMessage (this, message, actionListeners.getUnchecked (i)))->post(); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.h index 3ddfcf79..13ee090e 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionListener.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionListener.h index bbb2cf37..1ccc69c0 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionListener.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionListener.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp b/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp index 1b5d050d..823fa9d0 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,7 +23,7 @@ namespace juce { -class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage +class AsyncUpdater::AsyncUpdaterMessage final : public CallbackMessage { public: AsyncUpdaterMessage (AsyncUpdater& au) : owner (au) {} diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.h index ac99a459..bf34dea8 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp index 151f6fef..ff6839b2 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h index e55660e5..8d9e1726 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -86,6 +86,7 @@ class JUCE_API ChangeBroadcaster { public: ChangeBroadcasterCallback(); + ~ChangeBroadcasterCallback() override { cancelPendingUpdate(); } void handleAsyncUpdate() override; ChangeBroadcaster* owner; diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h index c77d9928..53d95e56 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.cpp b/JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.cpp new file mode 100644 index 00000000..87c440b6 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.cpp @@ -0,0 +1,133 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 LockingAsyncUpdater::Impl : public CallbackMessage +{ +public: + explicit Impl (std::function cb) + : callback (std::move (cb)) {} + + void clear() + { + const ScopedLock lock (mutex); + deliver = false; + callback = nullptr; + } + + void trigger() + { + { + const ScopedLock lock (mutex); + + if (deliver) + return; + + deliver = true; + } + + if (! post()) + cancel(); + } + + void cancel() + { + const ScopedLock lock (mutex); + deliver = false; + } + + bool isPending() + { + const ScopedLock lock (mutex); + return deliver; + } + + void messageCallback() override + { + const ScopedLock lock (mutex); + + if (std::exchange (deliver, false)) + NullCheckedInvocation::invoke (callback); + } + +private: + CriticalSection mutex; + std::function callback; + bool deliver = false; +}; + +//============================================================================== +LockingAsyncUpdater::LockingAsyncUpdater (std::function callbackToUse) + : impl (new Impl (std::move (callbackToUse))) {} + +LockingAsyncUpdater::LockingAsyncUpdater (LockingAsyncUpdater&& other) noexcept + : impl (std::exchange (other.impl, nullptr)) {} + +LockingAsyncUpdater& LockingAsyncUpdater::operator= (LockingAsyncUpdater&& other) noexcept +{ + LockingAsyncUpdater temp { std::move (other) }; + std::swap (temp.impl, impl); + return *this; +} + +LockingAsyncUpdater::~LockingAsyncUpdater() +{ + if (impl != nullptr) + impl->clear(); +} + +void LockingAsyncUpdater::triggerAsyncUpdate() +{ + if (impl != nullptr) + impl->trigger(); + else + jassertfalse; // moved-from! +} + +void LockingAsyncUpdater::cancelPendingUpdate() noexcept +{ + if (impl != nullptr) + impl->cancel(); + else + jassertfalse; // moved-from! +} + +void LockingAsyncUpdater::handleUpdateNowIfNeeded() +{ + if (impl != nullptr) + impl->messageCallback(); + else + jassertfalse; // moved-from! +} + +bool LockingAsyncUpdater::isUpdatePending() const noexcept +{ + if (impl != nullptr) + return impl->isPending(); + + jassertfalse; // moved-from! + return false; +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.h new file mode 100644 index 00000000..49cb91d1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.h @@ -0,0 +1,113 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +//============================================================================== +/** + A bit like an AsyncUpdater, but guarantees that after cancelPendingUpdate() returns, + the async function will never be called until triggerAsyncUpdate() is called again. + This is an important guarantee for writing classes with async behaviour that can + still be destroyed safely from a background thread. + + Note that all of the member functions of this type have a chance of blocking, so + this class is unsuitable for broadcasting changes from a realtime thread. + + @tags{Events} +*/ +class JUCE_API LockingAsyncUpdater final +{ +public: + //============================================================================== + /** Creates a LockingAsyncUpdater object that will call the provided callback + on the main thread when triggered. + + Note that the LockingAsyncUpdater takes an internal mutex before calling + the provided callback. Therefore, in order to avoid deadlocks, you should + (ideally) ensure that no locks are taken inside the callbackToUse. If you + do need to take a lock inside the callback, make sure that you do not + hold the same lock while calling any of the LockingAsyncUpdater member + functions. + */ + explicit LockingAsyncUpdater (std::function callbackToUse); + + /** Move constructor. */ + LockingAsyncUpdater (LockingAsyncUpdater&& other) noexcept; + + /** Move assignment operator. */ + LockingAsyncUpdater& operator= (LockingAsyncUpdater&& other) noexcept; + + /** Destructor. + If there are any pending callbacks when the object is deleted, these are lost. + The async callback is guaranteed not to be called again once the destructor has + completed. + */ + ~LockingAsyncUpdater(); + + //============================================================================== + /** Causes the callback to be triggered at a later time. + + This method returns quickly, after which a callback to the + handleAsyncUpdate() method will be made by the impl thread as + soon as possible. + + If an update callback is already pending but hasn't started yet, calling + this method will have no effect. + + It's thread-safe to call this method from any thread, BUT beware of calling + it from a real-time (e.g. audio) thread, because it unconditionally locks + a mutex. This may block, e.g. if this is called from a background thread + while the async callback is in progress on the main thread. + */ + void triggerAsyncUpdate(); + + /** This will stop any pending updates from happening. + + If a callback is already in progress on another thread, this will block until + the callback has finished before returning. + */ + void cancelPendingUpdate() noexcept; + + /** If an update has been triggered and is pending, this will invoke it + synchronously. + + Use this as a kind of "flush" operation - if an update is pending, the + handleAsyncUpdate() method will be called immediately; if no update is + pending, then nothing will be done. + + Because this may invoke the callback, this method must only be called on + the main event thread. + */ + void handleUpdateNowIfNeeded(); + + /** Returns true if there's an update callback in the pipeline. */ + bool isUpdatePending() const noexcept; + +private: + class Impl; + ReferenceCountedObjectPtr impl; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LockingAsyncUpdater) +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_ChildProcessManager.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_ChildProcessManager.cpp new file mode 100644 index 00000000..69b52acb --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_ChildProcessManager.cpp @@ -0,0 +1,52 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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 +{ + +void ChildProcessManager::checkProcesses() +{ + for (auto it = processes.begin(); it != processes.end();) + { + auto processPtr = *it; + + if (! processPtr->isRunning()) + { + listeners.call (processPtr.get()); + it = processes.erase (it); + } + else + { + ++it; + } + } + + if (processes.empty()) + timer.stopTimer(); +} + +JUCE_IMPLEMENT_SINGLETON (ChildProcessManager) + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_ChildProcessManager.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_ChildProcessManager.h new file mode 100644 index 00000000..1414ab23 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_ChildProcessManager.h @@ -0,0 +1,104 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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 +{ + /** Manages a set of ChildProcesses and periodically checks their return value. Upon completion + it calls listeners added with addChildProcessExitedListener(). + + This class is mostly aimed for usage on Linux, where terminated child processes are only + cleaned up if their return code is read after termination. In order to ensure this one needs + to call ChildProcess::isFinished() until it returns false or + ChildProcess::waitForProcessToFinish() until it returns true. + + This class will keep querying the return code on a Timer thread until the process + terminates. This can be handy if one wants to start and stop multiple ChildProcesses on + Linux that could take a long time to complete. + + Since this class uses a Timer to check subprocess status, it's generally only safe to + access the returned ChildProcesses from the message thread. + + @see ChildProcessManagerSingleton + + @tags{Events} + */ + class JUCE_API ChildProcessManager final : private DeletedAtShutdown + { + public: + #ifndef DOXYGEN + JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (ChildProcessManager) + #endif + + /** Creates a new ChildProcess and starts it with the provided arguments. + + The arguments are the same as the overloads to ChildProcess::start(). + + The manager will keep the returned ChildProcess object alive until it terminates and its + return value has been queried. Calling ChildProcess::kill() on the returned object will + eventually cause its removal from the ChildProcessManager after it terminates. + */ + template + std::shared_ptr createAndStartManagedChildProcess (Args&&... args) + { + auto p = std::make_shared(); + + if (! p->start (std::forward (args)...)) + return nullptr; + + processes.insert (p); + timer.startTimer (1000); + + return p; + } + + /** Registers a callback function that is called for every ChildProcess that terminated. + + This registration is deleted when the returned ErasedScopedGuard is deleted. + */ + auto addChildProcessExitedListener (std::function listener) + { + return listeners.addListener (std::move (listener)); + } + + /** Returns true if the ChildProcessManager contains any running ChildProcesses that it's + monitoring. + */ + auto hasRunningProcess() const + { + return timer.isTimerRunning(); + } + + private: + ChildProcessManager() = default; + ~ChildProcessManager() override { clearSingletonInstance(); } + + void checkProcesses(); + + std::set> processes; + detail::CallbackListenerList listeners; + TimedCallback timer { [this] { checkProcesses(); } }; + }; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp index 36a17d7a..8a11ffba 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,14 +23,14 @@ namespace juce { -enum { magicMastSlaveConnectionHeader = 0x712baf04 }; +enum { magicCoordWorkerConnectionHeader = 0x712baf04 }; static const char* startMessage = "__ipc_st"; static const char* killMessage = "__ipc_k_"; static const char* pingMessage = "__ipc_p_"; enum { specialMessageSize = 8, defaultTimeoutMs = 8000 }; -static inline bool isMessageType (const MemoryBlock& mb, const char* messageType) noexcept +static bool isMessageType (const MemoryBlock& mb, const char* messageType) noexcept { return mb.matches (messageType, (size_t) specialMessageSize); } @@ -43,14 +43,16 @@ static String getCommandLinePrefix (const String& commandLineUniqueID) //============================================================================== // This thread sends and receives ping messages every second, so that it // can find out if the other process has stopped running. -struct ChildProcessPingThread : public Thread, - private AsyncUpdater +struct ChildProcessPingThread : public Thread, + private AsyncUpdater { ChildProcessPingThread (int timeout) : Thread ("IPC ping"), timeoutMs (timeout) { pingReceived(); } + void startPinging() { startThread (Priority::low); } + void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; } void triggerConnectionLostMessage() { triggerAsyncUpdate(); } @@ -59,6 +61,8 @@ struct ChildProcessPingThread : public Thread, int timeoutMs; + using AsyncUpdater::cancelPendingUpdate; + private: Atomic countdown; @@ -82,28 +86,30 @@ struct ChildProcessPingThread : public Thread, }; //============================================================================== -struct ChildProcessMaster::Connection : public InterprocessConnection, - private ChildProcessPingThread +struct ChildProcessCoordinator::Connection final : public InterprocessConnection, + private ChildProcessPingThread { - Connection (ChildProcessMaster& m, const String& pipeName, int timeout) - : InterprocessConnection (false, magicMastSlaveConnectionHeader), + Connection (ChildProcessCoordinator& m, const String& pipeName, int timeout) + : InterprocessConnection (false, magicCoordWorkerConnectionHeader), ChildProcessPingThread (timeout), owner (m) { - if (createPipe (pipeName, timeoutMs)) - startThread (4); + createPipe (pipeName, timeoutMs); } ~Connection() override { + cancelPendingUpdate(); stopThread (10000); } + using ChildProcessPingThread::startPinging; + private: void connectionMade() override {} void connectionLost() override { owner.handleConnectionLost(); } - bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToSlave (m); } + bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToWorker (m); } void pingFailed() override { connectionLost(); } void messageReceived (const MemoryBlock& m) override @@ -111,25 +117,34 @@ struct ChildProcessMaster::Connection : public InterprocessConnection, pingReceived(); if (m.getSize() != specialMessageSize || ! isMessageType (m, pingMessage)) - owner.handleMessageFromSlave (m); + owner.handleMessageFromWorker (m); } - ChildProcessMaster& owner; + ChildProcessCoordinator& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) }; //============================================================================== -ChildProcessMaster::ChildProcessMaster() {} +ChildProcessCoordinator::ChildProcessCoordinator() = default; -ChildProcessMaster::~ChildProcessMaster() +ChildProcessCoordinator::~ChildProcessCoordinator() { - killSlaveProcess(); + killWorkerProcess(); } -void ChildProcessMaster::handleConnectionLost() {} +void ChildProcessCoordinator::handleConnectionLost() {} -bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) +void ChildProcessCoordinator::handleMessageFromWorker (const MemoryBlock& mb) +{ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + handleMessageFromSlave (mb); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_WARNINGS_MSVC +} + +bool ChildProcessCoordinator::sendMessageToWorker (const MemoryBlock& mb) { if (connection != nullptr) return connection->sendMessage (mb); @@ -138,10 +153,10 @@ bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) return false; } -bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID, - int timeoutMs, int streamFlags) +bool ChildProcessCoordinator::launchWorkerProcess (const File& executable, const String& commandLineUniqueID, + int timeoutMs, int streamFlags) { - killSlaveProcess(); + killWorkerProcess(); auto pipeName = "p" + String::toHexString (Random().nextInt64()); @@ -149,15 +164,27 @@ bool ChildProcessMaster::launchSlaveProcess (const File& executable, const Strin args.add (executable.getFullPathName()); args.add (getCommandLinePrefix (commandLineUniqueID) + pipeName); - childProcess.reset (new ChildProcess()); + childProcess = [&]() -> std::shared_ptr + { + if ((SystemStats::getOperatingSystemType() & SystemStats::Linux) != 0) + return ChildProcessManager::getInstance()->createAndStartManagedChildProcess (args, streamFlags); + + auto p = std::make_shared(); + + if (p->start (args, streamFlags)) + return p; - if (childProcess->start (args, streamFlags)) + return nullptr; + }(); + + if (childProcess != nullptr) { connection.reset (new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs)); if (connection->isConnected()) { - sendMessageToSlave ({ startMessage, specialMessageSize }); + connection->startPinging(); + sendMessageToWorker ({ startMessage, specialMessageSize }); return true; } @@ -167,11 +194,11 @@ bool ChildProcessMaster::launchSlaveProcess (const File& executable, const Strin return false; } -void ChildProcessMaster::killSlaveProcess() +void ChildProcessCoordinator::killWorkerProcess() { if (connection != nullptr) { - sendMessageToSlave ({ killMessage, specialMessageSize }); + sendMessageToWorker ({ killMessage, specialMessageSize }); connection->disconnect(); connection.reset(); } @@ -180,30 +207,33 @@ void ChildProcessMaster::killSlaveProcess() } //============================================================================== -struct ChildProcessSlave::Connection : public InterprocessConnection, - private ChildProcessPingThread +struct ChildProcessWorker::Connection final : public InterprocessConnection, + private ChildProcessPingThread { - Connection (ChildProcessSlave& p, const String& pipeName, int timeout) - : InterprocessConnection (false, magicMastSlaveConnectionHeader), + Connection (ChildProcessWorker& p, const String& pipeName, int timeout) + : InterprocessConnection (false, magicCoordWorkerConnectionHeader), ChildProcessPingThread (timeout), owner (p) { connectToPipe (pipeName, timeoutMs); - startThread (4); } ~Connection() override { + cancelPendingUpdate(); stopThread (10000); + disconnect(); } + using ChildProcessPingThread::startPinging; + private: - ChildProcessSlave& owner; + ChildProcessWorker& owner; void connectionMade() override {} void connectionLost() override { owner.handleConnectionLost(); } - bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToMaster (m); } + bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToCoordinator (m); } void pingFailed() override { connectionLost(); } void messageReceived (const MemoryBlock& m) override @@ -219,20 +249,29 @@ struct ChildProcessSlave::Connection : public InterprocessConnection, if (isMessageType (m, startMessage)) return owner.handleConnectionMade(); - owner.handleMessageFromMaster (m); + owner.handleMessageFromCoordinator (m); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) }; //============================================================================== -ChildProcessSlave::ChildProcessSlave() {} -ChildProcessSlave::~ChildProcessSlave() {} +ChildProcessWorker::ChildProcessWorker() = default; +ChildProcessWorker::~ChildProcessWorker() = default; -void ChildProcessSlave::handleConnectionMade() {} -void ChildProcessSlave::handleConnectionLost() {} +void ChildProcessWorker::handleConnectionMade() {} +void ChildProcessWorker::handleConnectionLost() {} + +void ChildProcessWorker::handleMessageFromCoordinator (const MemoryBlock& mb) +{ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + handleMessageFromMaster (mb); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_WARNINGS_MSVC +} -bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) +bool ChildProcessWorker::sendMessageToCoordinator (const MemoryBlock& mb) { if (connection != nullptr) return connection->sendMessage (mb); @@ -241,9 +280,9 @@ bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) return false; } -bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, - const String& commandLineUniqueID, - int timeoutMs) +bool ChildProcessWorker::initialiseFromCommandLine (const String& commandLine, + const String& commandLineUniqueID, + int timeoutMs) { auto prefix = getCommandLinePrefix (commandLineUniqueID); @@ -256,7 +295,9 @@ bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, { connection.reset (new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs)); - if (! connection->isConnected()) + if (connection->isConnected()) + connection->startPinging(); + else connection.reset(); } } diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h index 6f94b07f..4fbfdb9d 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,47 +25,47 @@ namespace juce //============================================================================== /** - Acts as the slave end of a master/slave pair of connected processes. + Acts as the worker end of a coordinator/worker pair of connected processes. - The ChildProcessSlave and ChildProcessMaster classes make it easy for an app + The ChildProcessWorker and ChildProcessCoordinator classes make it easy for an app to spawn a child process, and to manage a 2-way messaging connection to control it. - To use the system, you need to create subclasses of both ChildProcessSlave and - ChildProcessMaster. To instantiate the ChildProcessSlave object, you must + To use the system, you need to create subclasses of both ChildProcessWorker and + ChildProcessCoordinator. To instantiate the ChildProcessWorker object, you must add some code to your main() or JUCEApplication::initialise() function that calls the initialiseFromCommandLine() method to check the app's command-line parameters to see whether it's being launched as a child process. If this returns - true then the slave process can be allowed to run, and its handleMessageFromMaster() + true then the worker process can be allowed to run, and its handleMessageFromCoordinator() method will be called whenever a message arrives. The juce demo app has a good example of this class in action. - @see ChildProcessMaster, InterprocessConnection, ChildProcess + @see ChildProcessCoordinator, InterprocessConnection, ChildProcess @tags{Events} */ -class JUCE_API ChildProcessSlave +class JUCE_API ChildProcessWorker { public: - /** Creates a non-connected slave process. - Use initialiseFromCommandLine to connect to a master process. + /** Creates a non-connected worker process. + Use initialiseFromCommandLine to connect to a coordinator process. */ - ChildProcessSlave(); + ChildProcessWorker(); /** Destructor. */ - virtual ~ChildProcessSlave(); + virtual ~ChildProcessWorker(); /** This checks some command-line parameters to see whether they were generated by - ChildProcessMaster::launchSlaveProcess(), and if so, connects to that master process. + ChildProcessCoordinator::launchWorkerProcess(), and if so, connects to that coordinator process. In an exe that can be used as a child process, you should add some code to your main() or JUCEApplication::initialise() that calls this method. The commandLineUniqueID should be a short alphanumeric identifier (no spaces!) - that matches the string passed to ChildProcessMaster::launchSlaveProcess(). + that matches the string passed to ChildProcessCoordinator::launchWorkerProcess(). The timeoutMs parameter lets you specify how long the child process is allowed - to run without receiving a ping from the master before the master is considered to + to run without receiving a ping from the coordinator before the coordinator is considered to have died, and handleConnectionLost() will be called. Passing <= 0 for this timeout makes it use a default value. @@ -76,78 +76,86 @@ class JUCE_API ChildProcessSlave int timeoutMs = 0); //============================================================================== - /** This will be called to deliver messages from the master process. + /** This will be called to deliver messages from the coordinator process. The call will probably be made on a background thread, so be careful with your thread-safety! You may want to respond by sending back a message with - sendMessageToMaster() + sendMessageToCoordinator() */ - virtual void handleMessageFromMaster (const MemoryBlock&) = 0; + virtual void handleMessageFromCoordinator (const MemoryBlock& mb); - /** This will be called when the master process finishes connecting to this slave. + [[deprecated ("Replaced by handleMessageFromCoordinator.")]] + virtual void handleMessageFromMaster (const MemoryBlock&) {} + + /** This will be called when the coordinator process finishes connecting to this worker. The call will probably be made on a background thread, so be careful with your thread-safety! */ virtual void handleConnectionMade(); - /** This will be called when the connection to the master process is lost. + /** This will be called when the connection to the coordinator process is lost. The call may be made from any thread (including the message thread). - Typically, if your process only exists to act as a slave, you should probably exit + Typically, if your process only exists to act as a worker, you should probably exit when this happens. */ virtual void handleConnectionLost(); - /** Tries to send a message to the master process. + /** Tries to send a message to the coordinator process. This returns true if the message was sent, but doesn't check that it actually gets delivered at the other end. If successful, the data will emerge in a call to your - ChildProcessMaster::handleMessageFromSlave(). + ChildProcessCoordinator::handleMessageFromWorker(). */ - bool sendMessageToMaster (const MemoryBlock&); + bool sendMessageToCoordinator (const MemoryBlock&); + + [[deprecated ("Replaced by sendMessageToCoordinator.")]] + bool sendMessageToMaster (const MemoryBlock& mb) { return sendMessageToCoordinator (mb); } private: struct Connection; std::unique_ptr connection; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessWorker) }; +using ChildProcessSlave [[deprecated ("Replaced by ChildProcessWorker.")]] = ChildProcessWorker; + //============================================================================== /** - Acts as the master in a master/slave pair of connected processes. + Acts as the coordinator in a coordinator/worker pair of connected processes. - The ChildProcessSlave and ChildProcessMaster classes make it easy for an app + The ChildProcessWorker and ChildProcessCoordinator classes make it easy for an app to spawn a child process, and to manage a 2-way messaging connection to control it. - To use the system, you need to create subclasses of both ChildProcessSlave and - ChildProcessMaster. When you want your master process to launch the slave, you - just call launchSlaveProcess(), and it'll attempt to launch the executable that + To use the system, you need to create subclasses of both ChildProcessWorker and + ChildProcessCoordinator. When you want your coordinator process to launch the worker, you + just call launchWorkerProcess(), and it'll attempt to launch the executable that you specify (which may be the same exe), and assuming it has been set-up to - correctly parse the command-line parameters (see ChildProcessSlave) then a + correctly parse the command-line parameters (see ChildProcessWorker) then a two-way connection will be created. The juce demo app has a good example of this class in action. - @see ChildProcessSlave, InterprocessConnection, ChildProcess + @see ChildProcessWorker, InterprocessConnection, ChildProcess @tags{Events} */ -class JUCE_API ChildProcessMaster +class JUCE_API ChildProcessCoordinator { public: - /** Creates an uninitialised master process object. - Use launchSlaveProcess to launch and connect to a child process. + /** Creates an uninitialised coordinator process object. + Use launchWorkerProcess to launch and connect to a child process. */ - ChildProcessMaster(); + ChildProcessCoordinator(); /** Destructor. - Note that the destructor calls killSlaveProcess(), but doesn't wait for + Note that the destructor calls killWorkerProcess(), but doesn't wait for the child process to finish terminating. */ - virtual ~ChildProcessMaster(); + virtual ~ChildProcessCoordinator(); - /** Attempts to launch and connect to a slave process. + /** Attempts to launch and connect to a worker process. This will start the given executable, passing it a special command-line parameter based around the commandLineUniqueID string, which must be a short alphanumeric string (no spaces!) that identifies your app. The exe - that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine() + that gets launched must respond by calling ChildProcessWorker::initialiseFromCommandLine() in its startup code, and must use a matching ID to commandLineUniqueID. The timeoutMs parameter lets you specify how long the child process is allowed @@ -156,45 +164,65 @@ class JUCE_API ChildProcessMaster it use a default value. If this all works, the method returns true, and you can begin sending and - receiving messages with the slave process. + receiving messages with the worker process. - If a child process is already running, this will call killSlaveProcess() and + If a child process is already running, this will call killWorkerProcess() and start a new one. */ + bool launchWorkerProcess (const File& executableToLaunch, + const String& commandLineUniqueID, + int timeoutMs = 0, + int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr); + + [[deprecated ("Replaced by launchWorkerProcess.")]] bool launchSlaveProcess (const File& executableToLaunch, const String& commandLineUniqueID, int timeoutMs = 0, - int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr); + int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr) + { + return launchWorkerProcess (executableToLaunch, commandLineUniqueID, timeoutMs, streamFlags); + } - /** Sends a kill message to the slave, and disconnects from it. + /** Sends a kill message to the worker, and disconnects from it. Note that this won't wait for it to terminate. */ - void killSlaveProcess(); + void killWorkerProcess(); - /** This will be called to deliver a message from the slave process. + [[deprecated ("Replaced by killWorkerProcess.")]] + void killSlaveProcess() { killWorkerProcess(); } + + /** This will be called to deliver a message from the worker process. The call will probably be made on a background thread, so be careful with your thread-safety! */ - virtual void handleMessageFromSlave (const MemoryBlock&) = 0; + virtual void handleMessageFromWorker (const MemoryBlock&); + + [[deprecated ("Replaced by handleMessageFromWorker")]] + virtual void handleMessageFromSlave (const MemoryBlock&) {} - /** This will be called when the slave process dies or is somehow disconnected. + /** This will be called when the worker process dies or is somehow disconnected. The call will probably be made on a background thread, so be careful with your thread-safety! */ virtual void handleConnectionLost(); - /** Attempts to send a message to the slave process. + /** Attempts to send a message to the worker process. This returns true if the message was dispatched, but doesn't check that it actually gets delivered at the other end. If successful, the data will emerge in a call to - your ChildProcessSlave::handleMessageFromMaster(). + your ChildProcessWorker::handleMessageFromCoordinator(). */ - bool sendMessageToSlave (const MemoryBlock&); + bool sendMessageToWorker (const MemoryBlock&); + + [[deprecated ("Replaced by sendMessageToWorker.")]] + bool sendMessageToSlave (const MemoryBlock& mb) { return sendMessageToWorker (mb); } private: - std::unique_ptr childProcess; + std::shared_ptr childProcess; struct Connection; std::unique_ptr connection; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessCoordinator) }; +using ChildProcessMaster [[deprecated ("Replaced by ChildProcessCoordinator.")]] = ChildProcessCoordinator; + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp index dd7ce10e..a0123ff0 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,7 +23,7 @@ namespace juce { -struct InterprocessConnection::ConnectionThread : public Thread +struct InterprocessConnection::ConnectionThread final : public Thread { ConnectionThread (InterprocessConnection& c) : Thread ("JUCE IPC"), owner (c) {} void run() override { owner.runThread(); } @@ -32,19 +32,64 @@ struct InterprocessConnection::ConnectionThread : public Thread JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionThread) }; +class SafeActionImpl +{ +public: + explicit SafeActionImpl (InterprocessConnection& p) + : ref (p) {} + + template + void ifSafe (Fn&& fn) + { + const ScopedLock lock (mutex); + + if (safe) + fn (ref); + } + + void setSafe (bool s) + { + const ScopedLock lock (mutex); + safe = s; + } + + bool isSafe() + { + const ScopedLock lock (mutex); + return safe; + } + +private: + CriticalSection mutex; + InterprocessConnection& ref; + bool safe = false; +}; + +class InterprocessConnection::SafeAction final : public SafeActionImpl +{ + using SafeActionImpl::SafeActionImpl; +}; + //============================================================================== InterprocessConnection::InterprocessConnection (bool callbacksOnMessageThread, uint32 magicMessageHeaderNumber) : useMessageThread (callbacksOnMessageThread), - magicMessageHeader (magicMessageHeaderNumber) + magicMessageHeader (magicMessageHeaderNumber), + safeAction (std::make_shared (*this)) { thread.reset (new ConnectionThread (*this)); } InterprocessConnection::~InterprocessConnection() { + // You *must* call `disconnect` in the destructor of your derived class to ensure + // that any pending messages are not delivered. If the messages were delivered after + // destroying the derived class, we'd end up calling the pure virtual implementations + // of `messageReceived`, `connectionMade` and `connectionLost` which is definitely + // not a good idea! + jassert (! safeAction->isSafe()); + callbackConnectionState = false; - disconnect(); - masterReference.clear(); + disconnect (4000, Notify::no); thread.reset(); } @@ -54,18 +99,15 @@ bool InterprocessConnection::connectToSocket (const String& hostName, { disconnect(); - const ScopedLock sl (pipeAndSocketLock); - socket.reset (new StreamingSocket()); + auto s = std::make_unique(); - if (socket->connect (hostName, portNumber, timeOutMillisecs)) + if (s->connect (hostName, portNumber, timeOutMillisecs)) { - threadIsRunning = true; - connectionMadeInt(); - thread->startThread(); + const ScopedWriteLock sl (pipeAndSocketLock); + initialiseWithSocket (std::move (s)); return true; } - socket.reset(); return false; } @@ -73,13 +115,13 @@ bool InterprocessConnection::connectToPipe (const String& pipeName, int timeoutM { disconnect(); - std::unique_ptr newPipe (new NamedPipe()); + auto newPipe = std::make_unique(); if (newPipe->openExisting (pipeName)) { - const ScopedLock sl (pipeAndSocketLock); + const ScopedWriteLock sl (pipeAndSocketLock); pipeReceiveMessageTimeout = timeoutMs; - initialiseWithPipe (newPipe.release()); + initialiseWithPipe (std::move (newPipe)); return true; } @@ -90,44 +132,49 @@ bool InterprocessConnection::createPipe (const String& pipeName, int timeoutMs, { disconnect(); - std::unique_ptr newPipe (new NamedPipe()); + auto newPipe = std::make_unique(); if (newPipe->createNewPipe (pipeName, mustNotExist)) { - const ScopedLock sl (pipeAndSocketLock); + const ScopedWriteLock sl (pipeAndSocketLock); pipeReceiveMessageTimeout = timeoutMs; - initialiseWithPipe (newPipe.release()); + initialiseWithPipe (std::move (newPipe)); return true; } return false; } -void InterprocessConnection::disconnect() +void InterprocessConnection::disconnect (int timeoutMs, Notify notify) { thread->signalThreadShouldExit(); { - const ScopedLock sl (pipeAndSocketLock); + const ScopedReadLock sl (pipeAndSocketLock); if (socket != nullptr) socket->close(); if (pipe != nullptr) pipe->close(); } - thread->stopThread (4000); + thread->stopThread (timeoutMs); deletePipeAndSocket(); - connectionLostInt(); + + if (notify == Notify::yes) + connectionLostInt(); + + callbackConnectionState = false; + safeAction->setSafe (false); } void InterprocessConnection::deletePipeAndSocket() { - const ScopedLock sl (pipeAndSocketLock); + const ScopedWriteLock sl (pipeAndSocketLock); socket.reset(); pipe.reset(); } bool InterprocessConnection::isConnected() const { - const ScopedLock sl (pipeAndSocketLock); + const ScopedReadLock sl (pipeAndSocketLock); return ((socket != nullptr && socket->isConnected()) || (pipe != nullptr && pipe->isOpen())) @@ -137,7 +184,7 @@ bool InterprocessConnection::isConnected() const String InterprocessConnection::getConnectedHostName() const { { - const ScopedLock sl (pipeAndSocketLock); + const ScopedReadLock sl (pipeAndSocketLock); if (pipe == nullptr && socket == nullptr) return {}; @@ -164,7 +211,7 @@ bool InterprocessConnection::sendMessage (const MemoryBlock& message) int InterprocessConnection::writeData (void* data, int dataSize) { - const ScopedLock sl (pipeAndSocketLock); + const ScopedReadLock sl (pipeAndSocketLock); if (socket != nullptr) return socket->write (data, dataSize); @@ -176,45 +223,47 @@ int InterprocessConnection::writeData (void* data, int dataSize) } //============================================================================== -void InterprocessConnection::initialiseWithSocket (StreamingSocket* newSocket) +void InterprocessConnection::initialise() { - jassert (socket == nullptr && pipe == nullptr); - socket.reset (newSocket); - + safeAction->setSafe (true); threadIsRunning = true; connectionMadeInt(); thread->startThread(); } -void InterprocessConnection::initialiseWithPipe (NamedPipe* newPipe) +void InterprocessConnection::initialiseWithSocket (std::unique_ptr newSocket) { jassert (socket == nullptr && pipe == nullptr); - pipe.reset (newPipe); + socket = std::move (newSocket); + initialise(); +} - threadIsRunning = true; - connectionMadeInt(); - thread->startThread(); +void InterprocessConnection::initialiseWithPipe (std::unique_ptr newPipe) +{ + jassert (socket == nullptr && pipe == nullptr); + pipe = std::move (newPipe); + initialise(); } //============================================================================== -struct ConnectionStateMessage : public MessageManager::MessageBase +struct ConnectionStateMessage final : public MessageManager::MessageBase { - ConnectionStateMessage (InterprocessConnection* ipc, bool connected) noexcept - : owner (ipc), connectionMade (connected) + ConnectionStateMessage (std::shared_ptr ipc, bool connected) noexcept + : safeAction (ipc), connectionMade (connected) {} void messageCallback() override { - if (auto* ipc = owner.get()) + safeAction->ifSafe ([this] (InterprocessConnection& owner) { if (connectionMade) - ipc->connectionMade(); + owner.connectionMade(); else - ipc->connectionLost(); - } + owner.connectionLost(); + }); } - WeakReference owner; + std::shared_ptr safeAction; bool connectionMade; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionStateMessage) @@ -227,7 +276,7 @@ void InterprocessConnection::connectionMadeInt() callbackConnectionState = true; if (useMessageThread) - (new ConnectionStateMessage (this, true))->post(); + (new ConnectionStateMessage (safeAction, true))->post(); else connectionMade(); } @@ -240,25 +289,27 @@ void InterprocessConnection::connectionLostInt() callbackConnectionState = false; if (useMessageThread) - (new ConnectionStateMessage (this, false))->post(); + (new ConnectionStateMessage (safeAction, false))->post(); else connectionLost(); } } -struct DataDeliveryMessage : public Message +struct DataDeliveryMessage final : public Message { - DataDeliveryMessage (InterprocessConnection* ipc, const MemoryBlock& d) - : owner (ipc), data (d) + DataDeliveryMessage (std::shared_ptr ipc, const MemoryBlock& d) + : safeAction (ipc), data (d) {} void messageCallback() override { - if (auto* ipc = owner.get()) - ipc->messageReceived (data); + safeAction->ifSafe ([this] (InterprocessConnection& owner) + { + owner.messageReceived (data); + }); } - WeakReference owner; + std::shared_ptr safeAction; MemoryBlock data; }; @@ -267,7 +318,7 @@ void InterprocessConnection::deliverDataInt (const MemoryBlock& data) jassert (callbackConnectionState); if (useMessageThread) - (new DataDeliveryMessage (this, data))->post(); + (new DataDeliveryMessage (safeAction, data))->post(); else messageReceived (data); } @@ -275,6 +326,8 @@ void InterprocessConnection::deliverDataInt (const MemoryBlock& data) //============================================================================== int InterprocessConnection::readData (void* data, int num) { + const ScopedReadLock sl (pipeAndSocketLock); + if (socket != nullptr) return socket->read (data, num, true); diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h index d3950fcc..859ee49a 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -43,6 +43,9 @@ class MemoryBlock; To act as a socket server and create connections for one or more client, see the InterprocessConnectionServer class. + IMPORTANT NOTE: Your derived Connection class *must* call `disconnect` in its destructor + in order to cancel any pending messages before the class is destroyed. + @see InterprocessConnectionServer, Socket, NamedPipe @tags{Events} @@ -117,8 +120,18 @@ class JUCE_API InterprocessConnection */ bool createPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs, bool mustNotExist = false); - /** Disconnects and closes any currently-open sockets or pipes. */ - void disconnect(); + /** Whether the disconnect call should trigger callbacks. */ + enum class Notify { no, yes }; + + /** Disconnects and closes any currently-open sockets or pipes. + + Derived classes *must* call this in their destructors in order to avoid undefined + behaviour. + + @param timeoutMs the time in ms to wait before killing the thread by force + @param notify whether or not to call `connectionLost` + */ + void disconnect (int timeoutMs = -1, Notify notify = Notify::yes); /** True if a socket or pipe is currently active. */ bool isConnected() const; @@ -178,7 +191,7 @@ class JUCE_API InterprocessConnection private: //============================================================================== - CriticalSection pipeAndSocketLock; + ReadWriteLock pipeAndSocketLock; std::unique_ptr socket; std::unique_ptr pipe; bool callbackConnectionState = false; @@ -187,8 +200,9 @@ class JUCE_API InterprocessConnection int pipeReceiveMessageTimeout = -1; friend class InterprocessConnectionServer; - void initialiseWithSocket (StreamingSocket*); - void initialiseWithPipe (NamedPipe*); + void initialise(); + void initialiseWithSocket (std::unique_ptr); + void initialiseWithPipe (std::unique_ptr); void deletePipeAndSocket(); void connectionMadeInt(); void connectionLostInt(); @@ -200,10 +214,12 @@ class JUCE_API InterprocessConnection std::unique_ptr thread; std::atomic threadIsRunning { false }; + class SafeAction; + std::shared_ptr safeAction; + void runThread(); int writeData (void*, int); - JUCE_DECLARE_WEAK_REFERENCEABLE (InterprocessConnection) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnection) }; diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp index 679fe085..c202f662 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -73,7 +73,7 @@ void InterprocessConnectionServer::run() if (clientSocket != nullptr) if (auto* newConnection = createConnectionObject()) - newConnection->initialiseWithSocket (clientSocket.release()); + newConnection->initialiseWithSocket (std::move (clientSocket)); } } diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h index ee7ac207..d6a5265d 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.cpp index 28a08025..a8a0b92d 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.cpp +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -41,7 +41,7 @@ NetworkServiceDiscovery::Advertiser::Advertiser (const String& serviceTypeUID, message.setAttribute ("address", String()); message.setAttribute ("port", connectionPort); - startThread (2); + startThread (Priority::background); } NetworkServiceDiscovery::Advertiser::~Advertiser() @@ -67,11 +67,20 @@ void NetworkServiceDiscovery::Advertiser::run() void NetworkServiceDiscovery::Advertiser::sendBroadcast() { - auto localAddress = IPAddress::getLocalAddress(); - message.setAttribute ("address", localAddress.toString()); - auto broadcastAddress = IPAddress::getInterfaceBroadcastAddress (localAddress); - auto data = message.toString (XmlElement::TextFormat().singleLine().withoutHeader()); - socket.write (broadcastAddress.toString(), broadcastPort, data.toRawUTF8(), (int) data.getNumBytesAsUTF8()); + static IPAddress local = IPAddress::local(); + + for (auto& address : IPAddress::getAllAddresses()) + { + if (address == local) + continue; + + message.setAttribute ("address", address.toString()); + + auto broadcastAddress = IPAddress::getInterfaceBroadcastAddress (address); + auto data = message.toString (XmlElement::TextFormat().singleLine().withoutHeader()); + + socket.write (broadcastAddress.toString(), broadcastPort, data.toRawUTF8(), (int) data.getNumBytesAsUTF8()); + } } //============================================================================== @@ -83,7 +92,7 @@ NetworkServiceDiscovery::AvailableServiceList::AvailableServiceList (const Strin #endif socket.bindToPort (broadcastPort); - startThread (2); + startThread (Priority::background); } NetworkServiceDiscovery::AvailableServiceList::~AvailableServiceList() @@ -125,8 +134,7 @@ std::vector NetworkServiceDiscovery::Available void NetworkServiceDiscovery::AvailableServiceList::handleAsyncUpdate() { - if (onChange != nullptr) - onChange(); + NullCheckedInvocation::invoke (onChange); } void NetworkServiceDiscovery::AvailableServiceList::handleMessage (const XmlElement& xml) diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h index 62430660..535b3913 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_NetworkServiceDiscovery.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/juce_events.cpp b/JuceLibraryCode/modules/juce_events/juce_events.cpp index e19d6d8a..bb4af965 100644 --- a/JuceLibraryCode/modules/juce_events/juce_events.cpp +++ b/JuceLibraryCode/modules/juce_events/juce_events.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -49,7 +49,7 @@ #import #import -#elif JUCE_LINUX +#elif JUCE_LINUX || JUCE_BSD #include #endif @@ -60,44 +60,40 @@ #include "messages/juce_MessageManager.cpp" #include "broadcasters/juce_ActionBroadcaster.cpp" #include "broadcasters/juce_AsyncUpdater.cpp" +#include "broadcasters/juce_LockingAsyncUpdater.cpp" #include "broadcasters/juce_ChangeBroadcaster.cpp" #include "timers/juce_MultiTimer.cpp" #include "timers/juce_Timer.cpp" +#include "interprocess/juce_ChildProcessManager.cpp" #include "interprocess/juce_InterprocessConnection.cpp" #include "interprocess/juce_InterprocessConnectionServer.cpp" #include "interprocess/juce_ConnectedChildProcess.cpp" #include "interprocess/juce_NetworkServiceDiscovery.cpp" +#include "native/juce_ScopedLowPowerModeDisabler.cpp" //============================================================================== #if JUCE_MAC || JUCE_IOS - #include "native/juce_osx_MessageQueue.h" - - #if JUCE_CLANG - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wundeclared-selector" - #endif + #include "native/juce_MessageQueue_mac.h" #if JUCE_MAC - #include "native/juce_mac_MessageManager.mm" + #include "native/juce_MessageManager_mac.mm" #else - #include "native/juce_ios_MessageManager.mm" - #endif - - #if JUCE_CLANG - #pragma clang diagnostic pop + #include "native/juce_MessageManager_ios.mm" #endif #elif JUCE_WINDOWS - #include "native/juce_win32_Messaging.cpp" + #include "native/juce_RunningInUnity.h" + #include "native/juce_Messaging_windows.cpp" #if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER - #include "native/juce_win32_WinRTWrapper.cpp" + #include "native/juce_WinRTWrapper_windows.cpp" #endif -#elif JUCE_LINUX - #include "native/juce_linux_Messaging.cpp" +#elif JUCE_LINUX || JUCE_BSD + #include "native/juce_EventLoopInternal_linux.h" + #include "native/juce_Messaging_linux.cpp" #elif JUCE_ANDROID - #include "native/juce_android_Messaging.cpp" + #include "native/juce_Messaging_android.cpp" #endif diff --git a/JuceLibraryCode/modules/juce_events/juce_events.h b/JuceLibraryCode/modules/juce_events/juce_events.h index 623a78d5..d482f696 100644 --- a/JuceLibraryCode/modules/juce_events/juce_events.h +++ b/JuceLibraryCode/modules/juce_events/juce_events.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,18 +25,19 @@ The block below describes the properties of this module, and is read by the Projucer to automatically generate project code that uses it. For details about the syntax and how to create or use a module, see the - JUCE Module Format.txt file. + JUCE Module Format.md file. BEGIN_JUCE_MODULE_DECLARATION ID: juce_events vendor: juce - version: 5.4.7 + version: 7.0.10 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 + minimumCppStandard: 17 dependencies: juce_core @@ -59,7 +60,7 @@ #define JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK 0 #endif -#if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER && JUCE_WINDOWS +#if JUCE_WINDOWS && JUCE_EVENTS_INCLUDE_WINRT_WRAPPER // If this header file is missing then you are probably attempting to use WinRT // functionality without the WinRT libraries installed on your system. Try installing // the latest Windows Standalone SDK and maybe also adding the path to the WinRT @@ -81,24 +82,28 @@ #include "broadcasters/juce_ActionBroadcaster.h" #include "broadcasters/juce_ActionListener.h" #include "broadcasters/juce_AsyncUpdater.h" +#include "broadcasters/juce_LockingAsyncUpdater.h" #include "broadcasters/juce_ChangeListener.h" #include "broadcasters/juce_ChangeBroadcaster.h" #include "timers/juce_Timer.h" +#include "timers/juce_TimedCallback.h" #include "timers/juce_MultiTimer.h" +#include "interprocess/juce_ChildProcessManager.h" #include "interprocess/juce_InterprocessConnection.h" #include "interprocess/juce_InterprocessConnectionServer.h" #include "interprocess/juce_ConnectedChildProcess.h" #include "interprocess/juce_NetworkServiceDiscovery.h" +#include "native/juce_ScopedLowPowerModeDisabler.h" -#if JUCE_LINUX - #include "native/juce_linux_EventLoop.h" +#if JUCE_LINUX || JUCE_BSD + #include "native/juce_EventLoop_linux.h" #endif #if JUCE_WINDOWS #if JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW - #include "native/juce_win32_HiddenMessageWindow.h" + #include "native/juce_HiddenMessageWindow_windows.h" #endif #if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER - #include "native/juce_win32_WinRTWrapper.h" + #include "native/juce_WinRTWrapper_windows.h" #endif #endif diff --git a/JuceLibraryCode/modules/juce_events/juce_events.mm b/JuceLibraryCode/modules/juce_events/juce_events.mm index e6b7946f..84fb1e46 100644 --- a/JuceLibraryCode/modules/juce_events/juce_events.mm +++ b/JuceLibraryCode/modules/juce_events/juce_events.mm @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp index 33a9a0a0..3293612a 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -91,7 +91,7 @@ void JUCEApplicationBase::sendUnhandledException (const std::exception* const e, #endif #if JUCE_HANDLE_MULTIPLE_INSTANCES -struct JUCEApplicationBase::MultipleInstanceHandler : public ActionListener +struct JUCEApplicationBase::MultipleInstanceHandler final : public ActionListener { MultipleInstanceHandler (const String& appName) : appLock ("juceAppLock_" + appName) @@ -175,7 +175,7 @@ StringArray JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameterArray() #else -#if JUCE_IOS +#if JUCE_IOS && JUCE_MODULE_AVAILABLE_juce_gui_basics extern int juce_iOSMain (int argc, const char* argv[], void* classPtr); #endif @@ -183,8 +183,8 @@ StringArray JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameterArray() extern void initialiseNSApplication(); #endif -#if JUCE_LINUX && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined(JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER) - extern int juce_gtkWebkitMain (int argc, const char* argv[]); +#if (JUCE_LINUX || JUCE_BSD) && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined (JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER) + extern "C" int juce_gtkWebkitMain (int argc, const char* const* argv); #endif #if JUCE_WINDOWS @@ -199,14 +199,12 @@ String JUCEApplicationBase::getCommandLineParameters() { String argString; - for (int i = 1; i < juce_argc; ++i) + for (const auto& arg : getCommandLineParameterArray()) { - String arg (juce_argv[i]); - - if (arg.containsChar (' ') && ! arg.isQuotedString()) - arg = arg.quoted ('"'); - - argString << arg << ' '; + const auto withQuotes = arg.containsChar (' ') && ! arg.isQuotedString() + ? arg.quoted ('"') + : arg; + argString << withQuotes << ' '; } return argString.trim(); @@ -214,7 +212,12 @@ String JUCEApplicationBase::getCommandLineParameters() StringArray JUCEApplicationBase::getCommandLineParameterArray() { - return StringArray (juce_argv + 1, juce_argc - 1); + StringArray result; + + for (int i = 1; i < juce_argc; ++i) + result.add (CharPointer_UTF8 (juce_argv[i])); + + return result; } int JUCEApplicationBase::main (int argc, const char* argv[]) @@ -228,12 +231,12 @@ int JUCEApplicationBase::main (int argc, const char* argv[]) initialiseNSApplication(); #endif - #if JUCE_LINUX && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined(JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER) + #if (JUCE_LINUX || JUCE_BSD) && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined (JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER) if (argc >= 2 && String (argv[1]) == "--juce-gtkwebkitfork-child") return juce_gtkWebkitMain (argc, argv); #endif - #if JUCE_IOS + #if JUCE_IOS && JUCE_MODULE_AVAILABLE_juce_gui_basics return juce_iOSMain (argc, argv, iOSCustomDelegate); #else @@ -279,17 +282,17 @@ bool JUCEApplicationBase::initialiseApp() } #endif - #if JUCE_WINDOWS && JUCE_STANDALONE_APPLICATION && (! defined (_CONSOLE)) && (! JUCE_MINGW) - if (AttachConsole (ATTACH_PARENT_PROCESS) != 0) + #if JUCE_WINDOWS && (! defined (_CONSOLE)) && (! JUCE_MINGW) + if (isStandaloneApp() && AttachConsole (ATTACH_PARENT_PROCESS) != 0) { // if we've launched a GUI app from cmd.exe or PowerShell, we need this to enable printf etc. // However, only reassign stdout, stderr, stdin if they have not been already opened by // a redirect or similar. FILE* ignore; - if (_fileno(stdout) < 0) freopen_s (&ignore, "CONOUT$", "w", stdout); - if (_fileno(stderr) < 0) freopen_s (&ignore, "CONOUT$", "w", stderr); - if (_fileno(stdin) < 0) freopen_s (&ignore, "CONIN$", "r", stdin); + if (_fileno (stdout) < 0) freopen_s (&ignore, "CONOUT$", "w", stdout); + if (_fileno (stderr) < 0) freopen_s (&ignore, "CONOUT$", "w", stderr); + if (_fileno (stdin) < 0) freopen_s (&ignore, "CONIN$", "r", stdin); } #endif diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h index 29237ce7..c7378a23 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -309,7 +309,7 @@ class JUCE_API JUCEApplicationBase //============================================================================== -#if JUCE_CATCH_UNHANDLED_EXCEPTIONS || defined (DOXYGEN) +#if JUCE_CATCH_UNHANDLED_EXCEPTIONS || DOXYGEN /** The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to the JUCEApplicationBase::sendUnhandledException() method. diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_CallbackMessage.h b/JuceLibraryCode/modules/juce_events/messages/juce_CallbackMessage.h index 9796c14a..98121b6e 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_CallbackMessage.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_CallbackMessage.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -63,7 +63,7 @@ class JUCE_API CallbackMessage : public MessageManager::MessageBase Note that like all other messages, this object will be deleted immediately after this method has been invoked. */ - virtual void messageCallback() override = 0; + void messageCallback() override = 0; private: // Avoid the leak-detector because for plugins, the host can unload our DLL with undelivered diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.cpp index 8ed76fea..f8e5ebbf 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -43,12 +43,9 @@ DeletedAtShutdown::~DeletedAtShutdown() getDeletedAtShutdownObjects().removeFirstMatchingValue (this); } -#if JUCE_MSVC - // Disable unreachable code warning, in case the compiler manages to figure out that - // you have no classes of DeletedAtShutdown that could throw an exception in their destructor. - #pragma warning (push) - #pragma warning (disable: 4702) -#endif +// Disable unreachable code warning, in case the compiler manages to figure out that +// you have no classes of DeletedAtShutdown that could throw an exception in their destructor. +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4702) void DeletedAtShutdown::deleteAll() { @@ -65,7 +62,7 @@ void DeletedAtShutdown::deleteAll() { JUCE_TRY { - auto* deletee = localCopy.getUnchecked(i); + auto* deletee = localCopy.getUnchecked (i); // double-check that it's not already been deleted during another object's destructor. { @@ -87,8 +84,6 @@ void DeletedAtShutdown::deleteAll() getDeletedAtShutdownObjects().clear(); // just to make sure the array doesn't have any memory still allocated } -#if JUCE_MSVC - #pragma warning (pop) -#endif +JUCE_END_IGNORE_WARNINGS_MSVC } // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.h b/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.h index 221c1b1c..8fafba49 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h b/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h index 12bdb639..e6bee61f 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -74,6 +74,9 @@ class JUCE_API ScopedJuceInitialiser_GUI final /** The destructor simply calls shutdownJuce_GUI(). */ ~ScopedJuceInitialiser_GUI(); + + JUCE_DECLARE_NON_COPYABLE (ScopedJuceInitialiser_GUI) + JUCE_DECLARE_NON_MOVEABLE (ScopedJuceInitialiser_GUI) }; @@ -84,11 +87,14 @@ class JUCE_API ScopedJuceInitialiser_GUI final See the JUCEApplication and JUCEApplicationBase class documentation for more details. */ -#ifdef DOXYGEN +#if DOXYGEN #define START_JUCE_APPLICATION(AppClass) #else #if JUCE_WINDOWS && ! defined (_CONSOLE) - #define JUCE_MAIN_FUNCTION int __stdcall WinMain (struct HINSTANCE__*, struct HINSTANCE__*, char*, int) + #define JUCE_MAIN_FUNCTION \ + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (28251) \ + int __stdcall WinMain (struct HINSTANCE__*, struct HINSTANCE__*, char*, int) \ + JUCE_END_IGNORE_WARNINGS_MSVC #define JUCE_MAIN_FUNCTION_ARGS #else #define JUCE_MAIN_FUNCTION int main (int argc, char* argv[]) @@ -98,12 +104,16 @@ class JUCE_API ScopedJuceInitialiser_GUI final #if JUCE_IOS #define JUCE_CREATE_APPLICATION_DEFINE(AppClass) \ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") \ juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \ - void* juce_GetIOSCustomDelegateClass() { return nullptr; } + void* juce_GetIOSCustomDelegateClass() { return nullptr; } \ + JUCE_END_IGNORE_WARNINGS_GCC_LIKE #define JUCE_CREATE_APPLICATION_DEFINE_CUSTOM_DELEGATE(AppClass, DelegateClass) \ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") \ juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \ - void* juce_GetIOSCustomDelegateClass() { return [DelegateClass class]; } + void* juce_GetIOSCustomDelegateClass() { return [DelegateClass class]; } \ + JUCE_END_IGNORE_WARNINGS_GCC_LIKE #define JUCE_MAIN_FUNCTION_DEFINITION \ extern "C" JUCE_MAIN_FUNCTION \ @@ -150,8 +160,10 @@ class JUCE_API ScopedJuceInitialiser_GUI final #else #define START_JUCE_APPLICATION(AppClass) \ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes") \ JUCE_CREATE_APPLICATION_DEFINE(AppClass) \ - JUCE_MAIN_FUNCTION_DEFINITION + JUCE_MAIN_FUNCTION_DEFINITION \ + JUCE_END_IGNORE_WARNINGS_GCC_LIKE #if JUCE_IOS /** diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_Message.h b/JuceLibraryCode/modules/juce_events/messages/juce_Message.h index 5465fcb6..9527adb7 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_Message.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_Message.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp index 95985ca9..b05df858 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.h b/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.h index f25734e9..bfcc9e88 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp index fa45378d..8eab0dcc 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -26,6 +26,8 @@ namespace juce MessageManager::MessageManager() noexcept : messageThreadId (Thread::getCurrentThreadId()) { + JUCE_VERSION_ID + if (JUCEApplicationBase::isStandaloneApp()) Thread::setCurrentThreadName ("JUCE Message Thread"); } @@ -78,32 +80,14 @@ bool MessageManager::MessageBase::post() } //============================================================================== -#if JUCE_MODAL_LOOPS_PERMITTED && ! (JUCE_MAC || JUCE_IOS) -bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) +#if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID) +// implemented in platform-specific code (juce_Messaging_linux.cpp and juce_Messaging_windows.cpp) +namespace detail { - jassert (isThisTheMessageThread()); // must only be called by the message thread - - auto endTime = Time::currentTimeMillis() + millisecondsToRunFor; - - while (quitMessageReceived.get() == 0) - { - JUCE_TRY - { - if (! dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0)) - Thread::sleep (1); - } - JUCE_CATCH_EXCEPTION - - if (millisecondsToRunFor >= 0 && Time::currentTimeMillis() >= endTime) - break; - } - - return quitMessageReceived.get() == 0; -} -#endif +bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); +} // namespace detail -#if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID) -class MessageManager::QuitMessage : public MessageManager::MessageBase +class MessageManager::QuitMessage final : public MessageManager::MessageBase { public: QuitMessage() {} @@ -125,7 +109,7 @@ void MessageManager::runDispatchLoop() { JUCE_TRY { - if (! dispatchNextMessageOnSystemQueue (false)) + if (! detail::dispatchNextMessageOnSystemQueue (false)) Thread::sleep (1); } JUCE_CATCH_EXCEPTION @@ -138,10 +122,34 @@ void MessageManager::stopDispatchLoop() quitMessagePosted = true; } +#if JUCE_MODAL_LOOPS_PERMITTED +bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) +{ + jassert (isThisTheMessageThread()); // must only be called by the message thread + + auto endTime = Time::currentTimeMillis() + millisecondsToRunFor; + + while (quitMessageReceived.get() == 0) + { + JUCE_TRY + { + if (! detail::dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0)) + Thread::sleep (1); + } + JUCE_CATCH_EXCEPTION + + if (millisecondsToRunFor >= 0 && Time::currentTimeMillis() >= endTime) + break; + } + + return quitMessageReceived.get() == 0; +} +#endif + #endif //============================================================================== -class AsyncFunctionCallback : public MessageManager::MessageBase +class AsyncFunctionCallback final : public MessageManager::MessageBase { public: AsyncFunctionCallback (MessageCallbackFunction* const f, void* const param) @@ -186,7 +194,7 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func bool MessageManager::callAsync (std::function fn) { - struct AsyncCallInvoker : public MessageBase + struct AsyncCallInvoker final : public MessageBase { AsyncCallInvoker (std::function f) : callback (std::move (f)) {} void messageCallback() override { callback(); } @@ -220,6 +228,8 @@ void MessageManager::deregisterBroadcastListener (ActionListener* const listener //============================================================================== bool MessageManager::isThisTheMessageThread() const noexcept { + const std::lock_guard lock { messageThreadIdMutex }; + return Thread::getCurrentThreadId() == messageThreadId; } @@ -227,13 +237,15 @@ void MessageManager::setCurrentThreadAsMessageThread() { auto thisThread = Thread::getCurrentThreadId(); - if (messageThreadId != thisThread) - { - messageThreadId = thisThread; + const std::lock_guard lock { messageThreadIdMutex }; + if (std::exchange (messageThreadId, thisThread) != thisThread) + { + #if JUCE_WINDOWS // This is needed on windows to make sure the message window is created by this thread doPlatformSpecificShutdown(); doPlatformSpecificInitialisation(); + #endif } } @@ -270,27 +282,33 @@ bool MessageManager::existsAndIsCurrentThread() noexcept accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens in Cocoa). */ -struct MessageManager::Lock::BlockingMessage : public MessageManager::MessageBase +struct MessageManager::Lock::BlockingMessage final : public MessageManager::MessageBase { - BlockingMessage (const MessageManager::Lock* parent) noexcept - : owner (parent) - {} + explicit BlockingMessage (const MessageManager::Lock* parent) noexcept + : owner (parent) {} void messageCallback() override { - { - ScopedLock lock (ownerCriticalSection); + std::unique_lock lock { mutex }; - if (auto* o = owner.get()) - o->messageCallback(); - } + if (owner != nullptr) + owner->setAcquired (true); - releaseEvent.wait(); + condvar.wait (lock, [&] { return owner == nullptr; }); } - CriticalSection ownerCriticalSection; - Atomic owner; - WaitableEvent releaseEvent; + void stopWaiting() + { + const ScopeGuard scope { [&] { condvar.notify_one(); } }; + const std::scoped_lock lock { mutex }; + owner = nullptr; + } + +private: + std::mutex mutex; + std::condition_variable condvar; + + const MessageManager::Lock* owner = nullptr; JUCE_DECLARE_NON_COPYABLE (BlockingMessage) }; @@ -298,8 +316,23 @@ struct MessageManager::Lock::BlockingMessage : public MessageManager::MessageB //============================================================================== MessageManager::Lock::Lock() {} MessageManager::Lock::~Lock() { exit(); } -void MessageManager::Lock::enter() const noexcept { tryAcquire (true); } -bool MessageManager::Lock::tryEnter() const noexcept { return tryAcquire (false); } +void MessageManager::Lock::enter() const noexcept { exclusiveTryAcquire (true); } +bool MessageManager::Lock::tryEnter() const noexcept { return exclusiveTryAcquire (false); } + +bool MessageManager::Lock::exclusiveTryAcquire (bool lockIsMandatory) const noexcept +{ + if (lockIsMandatory) + entryMutex.enter(); + else if (! entryMutex.tryEnter()) + return false; + + const auto result = tryAcquire (lockIsMandatory); + + if (! result) + entryMutex.exit(); + + return result; +} bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept { @@ -311,9 +344,12 @@ bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept return false; } - if (! lockIsMandatory && (abortWait.get() != 0)) + if (! lockIsMandatory && [&] + { + const std::scoped_lock lock { mutex }; + return std::exchange (abortWait, false); + }()) { - abortWait.set (0); return false; } @@ -338,65 +374,68 @@ bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept return false; } - do + for (;;) { - while (abortWait.get() == 0) - lockedEvent.wait (-1); - - abortWait.set (0); + { + std::unique_lock lock { mutex }; + condvar.wait (lock, [&] { return std::exchange (abortWait, false); }); + } - if (lockGained.get() != 0) + if (acquired) { mm->threadWithLock = Thread::getCurrentThreadId(); return true; } - } while (lockIsMandatory); + if (! lockIsMandatory) + break; + } // we didn't get the lock - blockingMessage->releaseEvent.signal(); - - { - ScopedLock lock (blockingMessage->ownerCriticalSection); - - lockGained.set (0); - blockingMessage->owner.set (nullptr); - } + blockingMessage->stopWaiting(); blockingMessage = nullptr; return false; } void MessageManager::Lock::exit() const noexcept { - if (lockGained.compareAndSetBool (false, true)) + const auto wasAcquired = [&] { - auto* mm = MessageManager::instance; + const std::scoped_lock lock { mutex }; + return acquired; + }(); - jassert (mm == nullptr || mm->currentThreadHasLockedMessageManager()); - lockGained.set (0); + if (! wasAcquired) + return; - if (mm != nullptr) - mm->threadWithLock = {}; + const ScopeGuard unlocker { [&] { entryMutex.exit(); } }; - if (blockingMessage != nullptr) - { - blockingMessage->releaseEvent.signal(); - blockingMessage = nullptr; - } + if (blockingMessage == nullptr) + return; + + if (auto* mm = MessageManager::instance) + { + jassert (mm->currentThreadHasLockedMessageManager()); + mm->threadWithLock = {}; } + + blockingMessage->stopWaiting(); + blockingMessage = nullptr; + acquired = false; } -void MessageManager::Lock::messageCallback() const +void MessageManager::Lock::abort() const noexcept { - lockGained.set (1); - abort(); + setAcquired (false); } -void MessageManager::Lock::abort() const noexcept +void MessageManager::Lock::setAcquired (bool x) const noexcept { - abortWait.set (1); - lockedEvent.signal(); + const ScopeGuard scope { [&] { condvar.notify_one(); } }; + const std::scoped_lock lock { mutex }; + abortWait = true; + acquired = x; } //============================================================================== @@ -453,7 +492,6 @@ void MessageManagerLock::exitSignalSent() } //============================================================================== -JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI(); JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI() { JUCE_AUTORELEASEPOOL @@ -462,7 +500,6 @@ JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI() } } -JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI(); JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI() { JUCE_AUTORELEASEPOOL diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h index 94ec2d17..dbfe87f0 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -28,11 +28,6 @@ class ThreadPoolJob; class ActionListener; class ActionBroadcaster; -//============================================================================== -#if JUCE_MODULE_AVAILABLE_juce_opengl -class OpenGLContext; -#endif - //============================================================================== /** See MessageManager::callFunctionOnMessageThread() for use of this function type. */ using MessageCallbackFunction = void* (void* userData); @@ -84,7 +79,7 @@ class JUCE_API MessageManager final */ bool hasStopMessageBeenSent() const noexcept { return quitMessagePosted.get() != 0; } - #if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN + #if JUCE_MODAL_LOOPS_PERMITTED /** Synchronously dispatches messages until a given time has elapsed. Returns false if a quit message has been posted by a call to stopDispatchLoop(), @@ -208,7 +203,7 @@ class JUCE_API MessageManager final Creates a new critical section to exclusively access methods which can only be called when the message manager is locked. - Unlike CrititcalSection, multiple instances of this lock class provide + Unlike CriticalSection, multiple instances of this lock class provide exclusive access to a single resource - the MessageManager. */ Lock(); @@ -302,13 +297,23 @@ class JUCE_API MessageManager final struct BlockingMessage; friend class ReferenceCountedObjectPtr; + bool exclusiveTryAcquire (bool) const noexcept; bool tryAcquire (bool) const noexcept; - void messageCallback() const; + + void setAcquired (bool success) const noexcept; //============================================================================== + // This mutex is used to make this lock type behave like a normal mutex. + // If multiple threads call enter() simultaneously, only one will succeed in gaining + // this mutex. The mutex is released again in exit(). + mutable CriticalSection entryMutex; + + // This mutex protects the other data members of the lock from concurrent access, which + // happens when the BlockingMessage calls setAcquired to indicate that the lock was gained. + mutable std::mutex mutex; mutable ReferenceCountedObjectPtr blockingMessage; - WaitableEvent lockedEvent; - mutable Atomic abortWait, lockGained; + mutable std::condition_variable condvar; + mutable bool abortWait = false, acquired = false; }; //============================================================================== @@ -333,12 +338,12 @@ class JUCE_API MessageManager final Atomic quitMessagePosted { 0 }, quitMessageReceived { 0 }; Thread::ThreadID messageThreadId; Atomic threadWithLock; + mutable std::mutex messageThreadIdMutex; static bool postMessageToSystemQueue (MessageBase*); static void* exitModalLoopCallback (void*); static void doPlatformSpecificInitialisation(); static void doPlatformSpecificShutdown(); - static bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageManager) }; diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h b/JuceLibraryCode/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h index 510429b8..b59eb4ea 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,7 +23,7 @@ namespace juce { -#if JUCE_MAC || JUCE_WINDOWS || defined (DOXYGEN) +#if JUCE_MAC || JUCE_WINDOWS || DOXYGEN //============================================================================== /** diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_NotificationType.h b/JuceLibraryCode/modules/juce_events/messages/juce_NotificationType.h index bfaae65b..870018db 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_NotificationType.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_NotificationType.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/native/juce_EventLoopInternal_linux.h b/JuceLibraryCode/modules/juce_events/native/juce_EventLoopInternal_linux.h new file mode 100644 index 00000000..6f863530 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/native/juce_EventLoopInternal_linux.h @@ -0,0 +1,55 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +struct LinuxEventLoopInternal +{ + /** + @internal + + Receives notifications when a fd callback is added or removed. + + This is useful for VST3 plugins that host other VST3 plugins. In this scenario, the 'inner' + plugin may want to register additional file descriptors on top of those registered by the + 'outer' plugin. In order for this to work, the outer plugin must in turn pass this request + on to the host, to indicate that the outer plugin wants to receive FD callbacks for both the + inner and outer plugin. + */ + struct Listener + { + virtual ~Listener() = default; + virtual void fdCallbacksChanged() = 0; + }; + + /** @internal */ + static void registerLinuxEventLoopListener (Listener&); + /** @internal */ + static void deregisterLinuxEventLoopListener (Listener&); + /** @internal */ + static void invokeEventLoopCallbackForFd (int); + /** @internal */ + static std::vector getRegisteredFds(); +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoop.h b/JuceLibraryCode/modules/juce_events/native/juce_EventLoop_linux.h similarity index 88% rename from JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoop.h rename to JuceLibraryCode/modules/juce_events/native/juce_EventLoop_linux.h index ee6c8984..e098bcbf 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoop.h +++ b/JuceLibraryCode/modules/juce_events/native/juce_EventLoop_linux.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -20,11 +20,9 @@ ============================================================================== */ -namespace juce +namespace juce::LinuxEventLoop { -namespace LinuxEventLoop -{ /** Registers a callback that will be called when a file descriptor is ready for I/O. This will add the given file descriptor to the internal set of file descriptors @@ -38,13 +36,12 @@ namespace LinuxEventLoop file descriptor. The possible values for this are defined in */ - void registerFdCallback (int fd, std::function readCallback, short eventMask = 1 /*POLLIN*/); + void registerFdCallback (int fd, std::function readCallback, short eventMask = 1 /*POLLIN*/); /** Unregisters a previously registered file descriptor. @see registerFdCallback */ void unregisterFdCallback (int fd); -} -} // namespace juce +} // namespace juce::LinuxEventLoop diff --git a/JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h b/JuceLibraryCode/modules/juce_events/native/juce_HiddenMessageWindow_windows.h similarity index 88% rename from JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h rename to JuceLibraryCode/modules/juce_events/native/juce_HiddenMessageWindow_windows.h index e3c3241a..95cdcf04 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h +++ b/JuceLibraryCode/modules/juce_events/native/juce_HiddenMessageWindow_windows.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -34,7 +34,7 @@ class HiddenMessageWindow HMODULE moduleHandle = (HMODULE) Process::getCurrentModuleInstanceHandle(); - WNDCLASSEX wc = { 0 }; + WNDCLASSEX wc = {}; wc.cbSize = sizeof (wc); wc.lpfnWndProc = wndProc; wc.cbWndExtra = 4; @@ -45,14 +45,15 @@ class HiddenMessageWindow jassert (atom != 0); hwnd = CreateWindow (getClassNameFromAtom(), messageWindowName, - 0, 0, 0, 0, 0, 0, 0, moduleHandle, 0); - jassert (hwnd != 0); + 0, 0, 0, 0, 0, + nullptr, nullptr, moduleHandle, nullptr); + jassert (hwnd != nullptr); } ~HiddenMessageWindow() { DestroyWindow (hwnd); - UnregisterClass (getClassNameFromAtom(), 0); + UnregisterClass (getClassNameFromAtom(), nullptr); } inline HWND getHWND() const noexcept { return hwnd; } @@ -90,16 +91,13 @@ class JuceWindowIdentifier class DeviceChangeDetector : private Timer { public: - DeviceChangeDetector (const wchar_t* const name) - : messageWindow (name, (WNDPROC) deviceChangeEventCallback) + DeviceChangeDetector (const wchar_t* const name, std::function onChangeIn) + : messageWindow (name, (WNDPROC) deviceChangeEventCallback), + onChange (std::move (onChangeIn)) { SetWindowLongPtr (messageWindow.getHWND(), GWLP_USERDATA, (LONG_PTR) this); } - virtual ~DeviceChangeDetector() {} - - virtual void systemDeviceChanged() = 0; - void triggerAsyncDeviceChangeCallback() { // We'll pause before sending a message, because on device removal, the OS hasn't always updated @@ -109,6 +107,7 @@ class DeviceChangeDetector : private Timer private: HiddenMessageWindow messageWindow; + std::function onChange; static LRESULT CALLBACK deviceChangeEventCallback (HWND h, const UINT message, const WPARAM wParam, const LPARAM lParam) @@ -128,7 +127,7 @@ class DeviceChangeDetector : private Timer void timerCallback() override { stopTimer(); - systemDeviceChanged(); + NullCheckedInvocation::invoke (onChange); } }; diff --git a/JuceLibraryCode/modules/juce_events/native/juce_ios_MessageManager.mm b/JuceLibraryCode/modules/juce_events/native/juce_MessageManager_ios.mm similarity index 98% rename from JuceLibraryCode/modules/juce_events/native/juce_ios_MessageManager.mm rename to JuceLibraryCode/modules/juce_events/native/juce_MessageManager_ios.mm index 81c6df49..293f07b4 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_ios_MessageManager.mm +++ b/JuceLibraryCode/modules/juce_events/native/juce_MessageManager_ios.mm @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm b/JuceLibraryCode/modules/juce_events/native/juce_MessageManager_mac.mm similarity index 64% rename from JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm rename to JuceLibraryCode/modules/juce_events/native/juce_MessageManager_mac.mm index bfe03ebb..46b86baf 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm +++ b/JuceLibraryCode/modules/juce_events/native/juce_MessageManager_mac.mm @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -33,126 +33,21 @@ MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr; //============================================================================== -struct AppDelegate +struct AppDelegateClass final : public ObjCClass { -public: - AppDelegate() - { - static AppDelegateClass cls; - delegate = [cls.createInstance() init]; - - NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; - - [center addObserver: delegate selector: @selector (mainMenuTrackingBegan:) - name: NSMenuDidBeginTrackingNotification object: nil]; - [center addObserver: delegate selector: @selector (mainMenuTrackingEnded:) - name: NSMenuDidEndTrackingNotification object: nil]; - - if (JUCEApplicationBase::isStandaloneApp()) - { - [NSApp setDelegate: delegate]; - - [[NSDistributedNotificationCenter defaultCenter] addObserver: delegate - selector: @selector (broadcastMessageCallback:) - name: getBroadcastEventName() - object: nil - suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately]; - } - else - { - [center addObserver: delegate selector: @selector (applicationDidResignActive:) - name: NSApplicationDidResignActiveNotification object: NSApp]; - - [center addObserver: delegate selector: @selector (applicationDidBecomeActive:) - name: NSApplicationDidBecomeActiveNotification object: NSApp]; - - [center addObserver: delegate selector: @selector (applicationWillUnhide:) - name: NSApplicationWillUnhideNotification object: NSApp]; - } - } - - ~AppDelegate() - { - [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: delegate]; - [[NSNotificationCenter defaultCenter] removeObserver: delegate]; - - if (JUCEApplicationBase::isStandaloneApp()) - { - [NSApp setDelegate: nil]; - - [[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate - name: getBroadcastEventName() - object: nil]; - } - - [delegate release]; - } - - static NSString* getBroadcastEventName() - { - return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64())); - } - - MessageQueue messageQueue; - id delegate; - -private: - //============================================================================== - struct AppDelegateClass : public ObjCClass + AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") { - AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") - { - addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@"); - addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@"); - addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@"); - addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@"); - addMethod (@selector (application:openFile:), application_openFile, "c@:@@"); - addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@"); - addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@"); - addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@"); - addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@"); - addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@"); - addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@"); - addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@"); - addMethod (@selector (dummyMethod), dummyMethod, "v@:"); - - #if JUCE_PUSH_NOTIFICATIONS - //============================================================================== - addIvar*> ("pushNotificationsDelegate"); - - addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@"); - addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate, "v@:@"); - addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications, "v@:@@"); - addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications, "v@:@@"); - addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification, "v@:@@"); - #endif - - registerClass(); - } - - private: - static void applicationWillFinishLaunching (id self, SEL, NSNotification*) + addMethod (@selector (applicationWillFinishLaunching:), [] (id self, SEL, NSNotification*) { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") [[NSAppleEventManager sharedAppleEventManager] setEventHandler: self andSelector: @selector (getUrl:withReplyEvent:) forEventClass: kInternetEventClass andEventID: kAEGetURL]; - } - - #if JUCE_PUSH_NOTIFICATIONS - static void applicationDidFinishLaunching (id self, SEL, NSNotification* notification) - { - if (notification.userInfo != nil) - { - NSUserNotification* userNotification = [notification.userInfo objectForKey: nsStringLiteral ("NSApplicationLaunchUserNotificationKey")]; - - if (userNotification != nil && userNotification.userInfo != nil) - didReceiveRemoteNotification (self, nil, [NSApplication sharedApplication], userNotification.userInfo); - } - } - #endif + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + }); - static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*) + addMethod (@selector (applicationShouldTerminate:), [] (id /*self*/, SEL, NSApplication*) { if (auto* app = JUCEApplicationBase::getInstance()) { @@ -163,14 +58,14 @@ static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, } return NSTerminateNow; - } + }); - static void applicationWillTerminate (id /*self*/, SEL, NSNotification*) + addMethod (@selector (applicationWillTerminate:), [] (id /*self*/, SEL, NSNotification*) { JUCEApplicationBase::appWillTerminateByForce(); - } + }); - static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename) + addMethod (@selector (application:openFile:), [] (id /*self*/, SEL, NSApplication*, NSString* filename) { if (auto* app = JUCEApplicationBase::getInstance()) { @@ -179,9 +74,9 @@ static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* fi } return NO; - } + }); - static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames) + addMethod (@selector (application:openFiles:), [] (id /*self*/, SEL, NSApplication*, NSArray* filenames) { if (auto* app = JUCEApplicationBase::getInstance()) { @@ -193,73 +88,73 @@ static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* fi if (files.size() > 0) app->anotherInstanceStarted (files.joinIntoString (" ")); } - } + }); - static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } - static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } - static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); } + addMethod (@selector (applicationDidBecomeActive:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); }); + addMethod (@selector (applicationDidResignActive:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); }); + addMethod (@selector (applicationWillUnhide:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); }); - static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n) + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + addMethod (@selector (getUrl:withReplyEvent:), [] (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*) + { + if (auto* app = JUCEApplicationBase::getInstance()) + app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue])); + }); + + addMethod (@selector (broadcastMessageCallback:), [] (id /*self*/, SEL, NSNotification* n) { NSDictionary* dict = (NSDictionary*) [n userInfo]; auto messageString = nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]); MessageManager::getInstance()->deliverBroadcastMessage (messageString); - } + }); - static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*) + addMethod (@selector (mainMenuTrackingBegan:), [] (id /*self*/, SEL, NSNotification*) { - if (menuTrackingChangedCallback != nullptr) - (*menuTrackingChangedCallback) (true); - } + NullCheckedInvocation::invoke (menuTrackingChangedCallback, true); + }); - static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*) + addMethod (@selector (mainMenuTrackingEnded:), [] (id /*self*/, SEL, NSNotification*) { - if (menuTrackingChangedCallback != nullptr) - (*menuTrackingChangedCallback) (false); - } - - static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread) + NullCheckedInvocation::invoke (menuTrackingChangedCallback, false); + }); - static void focusChanged() - { - if (appFocusChangeCallback != nullptr) - (*appFocusChangeCallback)(); - } + // (used as a way of running a dummy thread) + addMethod (@selector (dummyMethod), [] (id /*self*/, SEL) {}); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE - static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*) - { - if (auto* app = JUCEApplicationBase::getInstance()) - app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue])); - } + #if JUCE_PUSH_NOTIFICATIONS + //============================================================================== + addIvar*> ("pushNotificationsDelegate"); - static String quotedIfContainsSpaces (NSString* file) + addMethod (@selector (applicationDidFinishLaunching:), [] (id self, SEL, NSNotification* notification) { - String s (nsStringToJuce (file)); - s = s.unquoted().replace ("\"", "\\\""); - - if (s.containsChar (' ')) - s = s.quoted(); + if (notification.userInfo != nil) + { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + // NSUserNotification is deprecated from macOS 11, but there doesn't seem to be a + // replacement for NSApplicationLaunchUserNotificationKey returning a non-deprecated type + NSUserNotification* userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE - return s; - } + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullable-to-nonnull-conversion") + if (userNotification != nil && userNotification.userInfo != nil) + [self application: [NSApplication sharedApplication] didReceiveRemoteNotification: userNotification.userInfo]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } + }); - #if JUCE_PUSH_NOTIFICATIONS - //============================================================================== - static void setPushNotificationsDelegate (id self, SEL, NSObject* delegate) + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + addMethod (@selector (setPushNotificationsDelegate:), [] (id self, SEL, NSObject* delegate) { object_setInstanceVariable (self, "pushNotificationsDelegate", delegate); - } + }); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE - static NSObject* getPushNotificationsDelegate (id self) - { - return getIvar*> (self, "pushNotificationsDelegate"); - } - - static void registeredForRemoteNotifications (id self, SEL, NSApplication* application, NSData* deviceToken) + addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), [] (id self, SEL, NSApplication* application, NSData* deviceToken) { auto* delegate = getPushNotificationsDelegate (self); - SEL selector = NSSelectorFromString (@"application:didRegisterForRemoteNotificationsWithDeviceToken:"); + SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:); if (delegate != nil && [delegate respondsToSelector: selector]) { @@ -271,13 +166,13 @@ static void registeredForRemoteNotifications (id self, SEL, NSApplication* appli [invocation invoke]; } - } + }); - static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* application, NSError* error) + addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), [] (id self, SEL, NSApplication* application, NSError* error) { auto* delegate = getPushNotificationsDelegate (self); - SEL selector = NSSelectorFromString (@"application:didFailToRegisterForRemoteNotificationsWithError:"); + SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:); if (delegate != nil && [delegate respondsToSelector: selector]) { @@ -289,13 +184,13 @@ static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* [invocation invoke]; } - } + }); - static void didReceiveRemoteNotification (id self, SEL, NSApplication* application, NSDictionary* userInfo) + addMethod (@selector (application:didReceiveRemoteNotification:), [] (id self, SEL, NSApplication* application, NSDictionary* userInfo) { auto* delegate = getPushNotificationsDelegate (self); - SEL selector = NSSelectorFromString (@"application:didReceiveRemoteNotification:"); + SEL selector = @selector (application:didReceiveRemoteNotification:); if (delegate != nil && [delegate respondsToSelector: selector]) { @@ -307,9 +202,109 @@ static void didReceiveRemoteNotification (id self, SEL, NSApplication* applicati [invocation invoke]; } - } + }); #endif - }; + + registerClass(); + } + +private: + static void focusChanged() + { + if (appFocusChangeCallback != nullptr) + (*appFocusChangeCallback)(); + } + + static String quotedIfContainsSpaces (NSString* file) + { + String s (nsStringToJuce (file)); + s = s.unquoted().replace ("\"", "\\\""); + + if (s.containsChar (' ')) + s = s.quoted(); + + return s; + } + + //============================================================================== + #if JUCE_PUSH_NOTIFICATIONS + static NSObject* getPushNotificationsDelegate (id self) + { + return getIvar*> (self, "pushNotificationsDelegate"); + } + #endif +}; + +// This is declared at file scope, so that it's guaranteed to be +// constructed before and destructed after `appDelegate` (below) +static AppDelegateClass appDelegateClass; + +//============================================================================== +struct AppDelegate +{ +public: + AppDelegate() + { + delegate = [appDelegateClass.createInstance() init]; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + [center addObserver: delegate selector: @selector (mainMenuTrackingBegan:) + name: NSMenuDidBeginTrackingNotification object: nil]; + [center addObserver: delegate selector: @selector (mainMenuTrackingEnded:) + name: NSMenuDidEndTrackingNotification object: nil]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + if (JUCEApplicationBase::isStandaloneApp()) + { + [NSApp setDelegate: delegate]; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + [[NSDistributedNotificationCenter defaultCenter] addObserver: delegate + selector: @selector (broadcastMessageCallback:) + name: getBroadcastEventName() + object: nil + suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } + else + { + [center addObserver: delegate selector: @selector (applicationDidResignActive:) + name: NSApplicationDidResignActiveNotification object: NSApp]; + + [center addObserver: delegate selector: @selector (applicationDidBecomeActive:) + name: NSApplicationDidBecomeActiveNotification object: NSApp]; + + [center addObserver: delegate selector: @selector (applicationWillUnhide:) + name: NSApplicationWillUnhideNotification object: NSApp]; + } + } + + ~AppDelegate() + { + [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: delegate]; + [[NSNotificationCenter defaultCenter] removeObserver: delegate]; + + if (JUCEApplicationBase::isStandaloneApp()) + { + [NSApp setDelegate: nil]; + + [[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate + name: getBroadcastEventName() + object: nil]; + } + + [delegate release]; + } + + static NSString* getBroadcastEventName() + { + return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64())); + } + + MessageQueue messageQueue; + id delegate; }; //============================================================================== @@ -322,9 +317,6 @@ static void didReceiveRemoteNotification (id self, SEL, NSApplication* applicati // must only be called by the message thread! jassert (isThisTheMessageThread()); - #if JUCE_PROJUCER_LIVE_BUILD - runDispatchLoopUntil (std::numeric_limits::max()); - #else #if JUCE_CATCH_UNHANDLED_EXCEPTIONS @try { @@ -342,7 +334,6 @@ static void didReceiveRemoteNotification (id self, SEL, NSApplication* applicati #else [NSApp run]; #endif - #endif } } } @@ -355,10 +346,6 @@ static void shutdownNSApp() void MessageManager::stopDispatchLoop() { - #if JUCE_PROJUCER_LIVE_BUILD - quitMessagePosted = true; - #else - if (isThisTheMessageThread()) { quitMessagePosted = true; @@ -366,7 +353,7 @@ static void shutdownNSApp() } else { - struct QuitCallback : public CallbackMessage + struct QuitCallback final : public CallbackMessage { QuitCallback() {} void messageCallback() override { MessageManager::getInstance()->stopDispatchLoop(); } @@ -374,7 +361,6 @@ static void shutdownNSApp() (new QuitCallback())->post(); } - #endif } #if JUCE_MODAL_LOOPS_PERMITTED @@ -419,17 +405,16 @@ void initialiseNSApplication() } } -static AppDelegate* appDelegate = nullptr; +static std::unique_ptr appDelegate; void MessageManager::doPlatformSpecificInitialisation() { if (appDelegate == nil) - appDelegate = new AppDelegate(); + appDelegate.reset (new AppDelegate()); } void MessageManager::doPlatformSpecificShutdown() { - delete appDelegate; appDelegate = nullptr; } @@ -450,27 +435,6 @@ void initialiseNSApplication() userInfo: info]; } -// Special function used by some plugin classes to re-post carbon events -void __attribute__ ((visibility("default"))) repostCurrentNSEvent(); -void __attribute__ ((visibility("default"))) repostCurrentNSEvent() -{ - struct EventReposter : public CallbackMessage - { - EventReposter() : e ([[NSApp currentEvent] retain]) {} - ~EventReposter() override { [e release]; } - - void messageCallback() override - { - [NSApp postEvent: e atStart: YES]; - } - - NSEvent* e; - }; - - (new EventReposter())->post(); -} - - //============================================================================== #if JUCE_MAC struct MountedVolumeListChangeDetector::Pimpl @@ -483,8 +447,10 @@ void messageCallback() override NSNotificationCenter* nc = [[NSWorkspace sharedWorkspace] notificationCenter]; + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") [nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidMountNotification object: nil]; [nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidUnmountNotification object: nil]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } ~Pimpl() @@ -497,12 +463,16 @@ void messageCallback() override MountedVolumeListChangeDetector& owner; id delegate; - struct ObserverClass : public ObjCClass + struct ObserverClass final : public ObjCClass { ObserverClass() : ObjCClass ("JUCEDriveObserver_") { addIvar ("owner"); - addMethod (@selector (changed:), changed, "v@:@"); + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + addMethod (@selector (changed:), changed); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + addProtocol (@protocol (NSTextInput)); registerClass(); } diff --git a/JuceLibraryCode/modules/juce_events/native/juce_osx_MessageQueue.h b/JuceLibraryCode/modules/juce_events/native/juce_MessageQueue_mac.h similarity index 84% rename from JuceLibraryCode/modules/juce_events/native/juce_osx_MessageQueue.h rename to JuceLibraryCode/modules/juce_events/native/juce_MessageQueue_mac.h index b461282a..ff883086 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_osx_MessageQueue.h +++ b/JuceLibraryCode/modules/juce_events/native/juce_MessageQueue_mac.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -40,15 +40,14 @@ class MessageQueue zerostruct (sourceContext); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) sourceContext.info = this; sourceContext.perform = runLoopSourceCallback; - runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); - CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); + runLoopSource.reset (CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext)); + CFRunLoopAddSource (runLoop, runLoopSource.get(), kCFRunLoopCommonModes); } ~MessageQueue() noexcept { - CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes); - CFRunLoopSourceInvalidate (runLoopSource); - CFRelease (runLoopSource); + CFRunLoopRemoveSource (runLoop, runLoopSource.get(), kCFRunLoopCommonModes); + CFRunLoopSourceInvalidate (runLoopSource.get()); } void post (MessageManager::MessageBase* const message) @@ -60,11 +59,11 @@ class MessageQueue private: ReferenceCountedArray messages; CFRunLoopRef runLoop; - CFRunLoopSourceRef runLoopSource; + CFUniquePtr runLoopSource; void wakeUp() noexcept { - CFRunLoopSourceSignal (runLoopSource); + CFRunLoopSourceSignal (runLoopSource.get()); CFRunLoopWakeUp (runLoop); } diff --git a/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp b/JuceLibraryCode/modules/juce_events/native/juce_Messaging_android.cpp similarity index 93% rename from JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp rename to JuceLibraryCode/modules/juce_events/native/juce_Messaging_android.cpp index 550fa0c3..5aed46e2 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_Messaging_android.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -26,7 +26,7 @@ namespace juce //============================================================================== namespace Android { - class Runnable : public juce::AndroidInterfaceImplementer + class Runnable : public juce::AndroidInterfaceImplementer { public: virtual void run() = 0; @@ -67,7 +67,7 @@ namespace Android } //============================================================================== -struct AndroidMessageQueue : private Android::Runnable +struct AndroidMessageQueue final : private Android::Runnable { JUCE_DECLARE_SINGLETON_SINGLETHREADED (AndroidMessageQueue, true) @@ -118,15 +118,6 @@ JUCE_IMPLEMENT_SINGLETON (AndroidMessageQueue) void MessageManager::doPlatformSpecificInitialisation() { AndroidMessageQueue::getInstance(); } void MessageManager::doPlatformSpecificShutdown() { AndroidMessageQueue::deleteInstance(); } -//============================================================================== -bool MessageManager::dispatchNextMessageOnSystemQueue (const bool) -{ - Logger::outputDebugString ("*** Modal loops are not possible in Android!! Exiting..."); - exit (1); - - return true; -} - bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) { return AndroidMessageQueue::getInstance()->post (message); @@ -143,7 +134,7 @@ void MessageManager::runDispatchLoop() void MessageManager::stopDispatchLoop() { - struct QuitCallback : public CallbackMessage + struct QuitCallback final : public CallbackMessage { QuitCallback() {} @@ -178,7 +169,7 @@ void MessageManager::stopDispatchLoop() } //============================================================================== -class JuceAppLifecycle : public ActivityLifecycleCallbacks +class JuceAppLifecycle final : public ActivityLifecycleCallbacks { public: JuceAppLifecycle (juce::JUCEApplicationBase* (*initSymbolAddr)()) @@ -297,6 +288,7 @@ class JuceAppLifecycle : public ActivityLifecycleCallbacks //============================================================================== File juce_getExecutableFile(); +void juce_juceEventsAndroidStartApp(); void juce_juceEventsAndroidStartApp() { auto dllPath = juce_getExecutableFile().getFullPathName(); diff --git a/JuceLibraryCode/modules/juce_events/native/juce_Messaging_linux.cpp b/JuceLibraryCode/modules/juce_events/native/juce_Messaging_linux.cpp new file mode 100644 index 00000000..67600aa6 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/native/juce_Messaging_linux.cpp @@ -0,0 +1,396 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 InternalMessageQueue +{ +public: + InternalMessageQueue() + { + [[maybe_unused]] auto err = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, msgpipe); + jassert (err == 0); + + 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()); + + clearSingletonInstance(); + } + + //============================================================================== + void postMessage (MessageManager::MessageBase* const msg) noexcept + { + ScopedLock sl (lock); + queue.add (msg); + + if (bytesInSocket < maxBytesInSocketQueue) + { + bytesInSocket++; + + ScopedUnlock ul (lock); + unsigned char x = 0xff; + [[maybe_unused]] auto numBytes = write (getWriteHandle(), &x, 1); + } + } + + //============================================================================== + JUCE_DECLARE_SINGLETON (InternalMessageQueue, false) + +private: + CriticalSection lock; + ReferenceCountedArray queue; + + int msgpipe[2]; + int bytesInSocket = 0; + static constexpr int maxBytesInSocketQueue = 128; + + int getWriteHandle() const noexcept { return msgpipe[0]; } + int getReadHandle() const noexcept { return msgpipe[1]; } + + MessageManager::MessageBase::Ptr popNextMessage (int fd) noexcept + { + const ScopedLock sl (lock); + + if (bytesInSocket > 0) + { + --bytesInSocket; + + ScopedUnlock ul (lock); + unsigned char x; + [[maybe_unused]] auto numBytes = read (fd, &x, 1); + } + + return queue.removeAndReturn (0); + } +}; + +JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue) + +//============================================================================== +/* + Stores callbacks associated with file descriptors (FD). + + The callback for a particular FD should be called whenever that file has data to read. + + For standalone apps, the main thread will call poll to wait for new data on any FD, and then + call the associated callbacks for any FDs that changed. + + For plugins, the host (generally) provides some kind of run loop mechanism instead. + - In VST2 plugins, the host should call effEditIdle at regular intervals, and plugins can + dispatch all pending events inside this callback. The host doesn't know about any of the + plugin's FDs, so it's possible there will be a bit of latency between an FD becoming ready, + and its associated callback being called. + - In VST3 plugins, it's possible to register each FD individually with the host. In this case, + the facilities in LinuxEventLoopInternal can be used to observe added/removed FD callbacks, + and the host can be notified whenever the set of FDs changes. The host will call onFDIsSet + whenever a particular FD has data ready. This call should be forwarded through to + InternalRunLoop::dispatchEvent. +*/ +struct InternalRunLoop +{ +public: + InternalRunLoop() = default; + + void registerFdCallback (int fd, std::function&& cb, short eventMask) + { + { + const ScopedLock sl (lock); + + callbacks.emplace (fd, std::make_shared> (std::move (cb))); + + const auto iter = getPollfd (fd); + + if (iter == pfds.end() || iter->fd != fd) + pfds.insert (iter, { fd, eventMask, 0 }); + else + jassertfalse; + + jassert (pfdsAreSorted()); + } + + listeners.call ([] (auto& l) { l.fdCallbacksChanged(); }); + } + + void unregisterFdCallback (int fd) + { + { + const ScopedLock sl (lock); + + callbacks.erase (fd); + + const auto iter = getPollfd (fd); + + if (iter != pfds.end() && iter->fd == fd) + pfds.erase (iter); + else + jassertfalse; + + jassert (pfdsAreSorted()); + } + + listeners.call ([] (auto& l) { l.fdCallbacksChanged(); }); + } + + bool dispatchPendingEvents() + { + callbackStorage.clear(); + getFunctionsToCallThisTime (callbackStorage); + + // CriticalSection should be available during the callback + for (auto& fn : callbackStorage) + (*fn)(); + + return ! callbackStorage.empty(); + } + + void dispatchEvent (int fd) const + { + const auto fn = [&] + { + const ScopedLock sl (lock); + const auto iter = callbacks.find (fd); + return iter != callbacks.end() ? iter->second : nullptr; + }(); + + // CriticalSection should be available during the callback + if (auto* callback = fn.get()) + (*callback)(); + } + + bool sleepUntilNextEvent (int timeoutMs) + { + const ScopedLock sl (lock); + return poll (pfds.data(), static_cast (pfds.size()), timeoutMs) != 0; + } + + std::vector getRegisteredFds() + { + const ScopedLock sl (lock); + std::vector result; + result.reserve (callbacks.size()); + std::transform (callbacks.begin(), + callbacks.end(), + std::back_inserter (result), + [] (const auto& pair) { return pair.first; }); + return result; + } + + void addListener (LinuxEventLoopInternal::Listener& listener) { listeners.add (&listener); } + void removeListener (LinuxEventLoopInternal::Listener& listener) { listeners.remove (&listener); } + + //============================================================================== + JUCE_DECLARE_SINGLETON (InternalRunLoop, false) + +private: + using SharedCallback = std::shared_ptr>; + + /* Appends any functions that need to be called to the passed-in vector. + + We take a copy of each shared function so that the functions can be called without + locking or racing in the event that the function attempts to register/deregister a + new FD callback. + */ + void getFunctionsToCallThisTime (std::vector& functions) + { + const ScopedLock sl (lock); + + if (! sleepUntilNextEvent (0)) + return; + + for (auto& pfd : pfds) + { + if (std::exchange (pfd.revents, 0) != 0) + { + const auto iter = callbacks.find (pfd.fd); + + if (iter != callbacks.end()) + functions.emplace_back (iter->second); + } + } + } + + std::vector::iterator getPollfd (int fd) + { + return std::lower_bound (pfds.begin(), pfds.end(), fd, [] (auto descriptor, auto toFind) + { + return descriptor.fd < toFind; + }); + } + + bool pfdsAreSorted() const + { + return std::is_sorted (pfds.begin(), pfds.end(), [] (auto a, auto b) { return a.fd < b.fd; }); + } + + CriticalSection lock; + + std::map callbacks; + std::vector callbackStorage; + std::vector pfds; + + ListenerList listeners; +}; + +JUCE_IMPLEMENT_SINGLETON (InternalRunLoop) + +//============================================================================== +namespace LinuxErrorHandling +{ + static bool keyboardBreakOccurred = false; + + static void keyboardBreakSignalHandler (int sig) + { + if (sig == SIGINT) + keyboardBreakOccurred = true; + } + + static void installKeyboardBreakHandler() + { + struct sigaction saction; + sigset_t maskSet; + sigemptyset (&maskSet); + saction.sa_handler = keyboardBreakSignalHandler; + saction.sa_mask = maskSet; + saction.sa_flags = 0; + sigaction (SIGINT, &saction, nullptr); + } +} + +//============================================================================== +void MessageManager::doPlatformSpecificInitialisation() +{ + if (JUCEApplicationBase::isStandaloneApp()) + LinuxErrorHandling::installKeyboardBreakHandler(); + + InternalRunLoop::getInstance(); + InternalMessageQueue::getInstance(); +} + +void MessageManager::doPlatformSpecificShutdown() +{ + InternalMessageQueue::deleteInstance(); + InternalRunLoop::deleteInstance(); +} + +bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) +{ + if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) + { + queue->postMessage (message); + return true; + } + + return false; +} + +void MessageManager::broadcastMessage (const String&) +{ + // TODO +} + +namespace detail +{ +// this function expects that it will NEVER be called simultaneously for two concurrent threads +bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) +{ + for (;;) + { + if (LinuxErrorHandling::keyboardBreakOccurred) + JUCEApplicationBase::quit(); + + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + { + if (runLoop->dispatchPendingEvents()) + break; + + if (returnIfNoPendingMessages) + return false; + + runLoop->sleepUntilNextEvent (2000); + } + } + + return true; +} +} // namespace detail + +//============================================================================== +void LinuxEventLoop::registerFdCallback (int fd, std::function readCallback, short eventMask) +{ + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + runLoop->registerFdCallback (fd, [cb = std::move (readCallback), fd] { cb (fd); }, eventMask); +} + +void LinuxEventLoop::unregisterFdCallback (int fd) +{ + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + runLoop->unregisterFdCallback (fd); +} + +//============================================================================== +void LinuxEventLoopInternal::registerLinuxEventLoopListener (LinuxEventLoopInternal::Listener& listener) +{ + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + runLoop->addListener (listener); +} + +void LinuxEventLoopInternal::deregisterLinuxEventLoopListener (LinuxEventLoopInternal::Listener& listener) +{ + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + runLoop->removeListener (listener); +} + +void LinuxEventLoopInternal::invokeEventLoopCallbackForFd (int fd) +{ + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + runLoop->dispatchEvent (fd); +} + +std::vector LinuxEventLoopInternal::getRegisteredFds() +{ + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + return runLoop->getRegisteredFds(); + + return {}; +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp b/JuceLibraryCode/modules/juce_events/native/juce_Messaging_windows.cpp similarity index 88% rename from JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp rename to JuceLibraryCode/modules/juce_events/native/juce_Messaging_windows.cpp index d9e4bb8e..9ae2c012 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_Messaging_windows.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -25,10 +25,6 @@ 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 @@ -51,7 +47,7 @@ class InternalMessageQueue ~InternalMessageQueue() { - juce_messageWindowHandle = 0; + juce_messageWindowHandle = nullptr; clearSingletonInstance(); } @@ -69,7 +65,7 @@ class InternalMessageQueue { COPYDATASTRUCT data; data.dwData = broadcastMessageMagicNumber; - data.cbData = (localCopy.length() + 1) * sizeof (CharPointer_UTF32::CharType); + data.cbData = (DWORD) (((size_t) localCopy.length() + 1) * sizeof (CharPointer_UTF32::CharType)); data.lpData = (void*) localCopy.toUTF32().getAddress(); DWORD_PTR result; @@ -94,13 +90,11 @@ class InternalMessageQueue if (! shouldTriggerMessageQueueDispatch) return; - #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity - if (juce_isRunningInUnity()) + if (detail::RunningInUnity::state) { SendNotifyMessage (juce_messageWindowHandle, customMessageID, 0, 0); return; } - #endif PostMessage (juce_messageWindowHandle, customMessageID, 0, 0); } @@ -109,10 +103,10 @@ class InternalMessageQueue { MSG m; - if (returnIfNoPendingMessages && ! PeekMessage (&m, (HWND) 0, 0, 0, PM_NOREMOVE)) + if (returnIfNoPendingMessages && ! PeekMessage (&m, nullptr, 0, 0, PM_NOREMOVE)) return false; - if (GetMessage (&m, (HWND) 0, 0, 0) >= 0) + if (GetMessage (&m, nullptr, 0, 0) >= 0) { #if JUCE_MODULE_AVAILABLE_juce_gui_extra if (juce_offerEventToActiveXControl (m) != S_FALSE) @@ -137,7 +131,7 @@ class InternalMessageQueue // currently on a juce window, pass the kb focus over.. auto currentFocus = GetFocus(); - if (currentFocus == 0 || JuceWindowIdentifier::isJUCEWindow (currentFocus)) + if (currentFocus == nullptr || JuceWindowIdentifier::isJUCEWindow (currentFocus)) SetFocus (m.hwnd); } @@ -170,8 +164,7 @@ class InternalMessageQueue } if (message == WM_SETTINGCHANGE) - if (settingChangeCallback != nullptr) - settingChangeCallback(); + NullCheckedInvocation::invoke (settingChangeCallback); } return DefWindowProc (h, message, wParam, lParam); @@ -206,7 +199,7 @@ class InternalMessageQueue { if (data != nullptr && data->dwData == broadcastMessageMagicNumber) { - struct BroadcastMessage : public CallbackMessage + struct BroadcastMessage final : public CallbackMessage { BroadcastMessage (CharPointer_UTF32 text, size_t length) : message (text, length) {} void messageCallback() override { MessageManager::getInstance()->deliverBroadcastMessage (message); } @@ -260,7 +253,10 @@ JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue) const TCHAR InternalMessageQueue::messageWindowName[] = _T("JUCEWindow"); //============================================================================== -bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) +namespace detail +{ + +bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) { if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) return queue->dispatchNextMessage (returnIfNoPendingMessages); @@ -268,6 +264,8 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMes return false; } +} // namespace detail + bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) { if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) @@ -288,7 +286,7 @@ void MessageManager::broadcastMessage (const String& value) //============================================================================== void MessageManager::doPlatformSpecificInitialisation() { - OleInitialize (0); + [[maybe_unused]] const auto result = OleInitialize (nullptr); InternalMessageQueue::getInstance(); } @@ -299,25 +297,24 @@ void MessageManager::doPlatformSpecificShutdown() } //============================================================================== -struct MountedVolumeListChangeDetector::Pimpl : private DeviceChangeDetector +struct MountedVolumeListChangeDetector::Pimpl { - Pimpl (MountedVolumeListChangeDetector& d) : DeviceChangeDetector (L"MountedVolumeList"), owner (d) + explicit Pimpl (MountedVolumeListChangeDetector& d) + : owner (d) { File::findFileSystemRoots (lastVolumeList); } - void systemDeviceChanged() override + void systemDeviceChanged() { Array newList; File::findFileSystemRoots (newList); - if (lastVolumeList != newList) - { - lastVolumeList = newList; + if (std::exchange (lastVolumeList, newList) != newList) owner.mountedVolumeListChanged(); - } } + DeviceChangeDetector detector { L"MountedVolumeList", [this] { systemDeviceChanged(); } }; MountedVolumeListChangeDetector& owner; Array lastVolumeList; }; diff --git a/JuceLibraryCode/modules/juce_events/native/juce_RunningInUnity.h b/JuceLibraryCode/modules/juce_events/native/juce_RunningInUnity.h new file mode 100644 index 00000000..41fc2cb0 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/native/juce_RunningInUnity.h @@ -0,0 +1,35 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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. + + ============================================================================== +*/ + +#pragma once + +namespace juce::detail +{ + +class RunningInUnity +{ +public: + /* @internal */ + static inline bool state = false; +}; + +} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.cpp b/JuceLibraryCode/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.cpp new file mode 100644 index 00000000..35fd51b0 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.cpp @@ -0,0 +1,63 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +#if JUCE_MAC + +class ScopedLowPowerModeDisabler::Pimpl +{ +public: + Pimpl() + { + if (@available (macOS 10.9, *)) + activity = [[NSProcessInfo processInfo] beginActivityWithOptions: NSActivityUserInitiatedAllowingIdleSystemSleep + reason: @"App must remain in high-power mode"]; + } + + ~Pimpl() + { + if (@available (macOS 10.9, *)) + [[NSProcessInfo processInfo] endActivity: activity]; + } + +private: + id activity; + + JUCE_DECLARE_NON_COPYABLE (Pimpl) + JUCE_DECLARE_NON_MOVEABLE (Pimpl) +}; + +#else + +class ScopedLowPowerModeDisabler::Pimpl {}; + +#endif + +//============================================================================== +ScopedLowPowerModeDisabler::ScopedLowPowerModeDisabler() + : pimpl (std::make_unique()) {} + +ScopedLowPowerModeDisabler::~ScopedLowPowerModeDisabler() = default; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.h b/JuceLibraryCode/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.h new file mode 100644 index 00000000..5cd6db81 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/native/juce_ScopedLowPowerModeDisabler.h @@ -0,0 +1,49 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + 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 +{ + +//============================================================================== +/** + Disables low-power-mode for the duration of an instance's lifetime. + + Currently this is only implemented on macOS, where it will disable the + "App Nap" power-saving feature. + + @tags{Events} +*/ +class ScopedLowPowerModeDisabler +{ +public: + ScopedLowPowerModeDisabler(); + ~ScopedLowPowerModeDisabler(); + +private: + class Pimpl; + std::unique_ptr pimpl; + + JUCE_DECLARE_NON_COPYABLE (ScopedLowPowerModeDisabler) + JUCE_DECLARE_NON_MOVEABLE (ScopedLowPowerModeDisabler) +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/native/juce_win32_WinRTWrapper.cpp b/JuceLibraryCode/modules/juce_events/native/juce_WinRTWrapper_windows.cpp similarity index 97% rename from JuceLibraryCode/modules/juce_events/native/juce_win32_WinRTWrapper.cpp rename to JuceLibraryCode/modules/juce_events/native/juce_WinRTWrapper_windows.cpp index 9269140c..f416ccfc 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_win32_WinRTWrapper.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_WinRTWrapper_windows.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,6 +23,36 @@ namespace juce { +WinRTWrapper::WinRTWrapper() +{ + winRTHandle = ::LoadLibraryA ("api-ms-win-core-winrt-l1-1-0"); + + if (winRTHandle == nullptr) + return; + + roInitialize = (RoInitializeFuncPtr) ::GetProcAddress (winRTHandle, "RoInitialize"); + createHString = (WindowsCreateStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsCreateString"); + deleteHString = (WindowsDeleteStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsDeleteString"); + getHStringRawBuffer = (WindowsGetStringRawBufferFuncPtr) ::GetProcAddress (winRTHandle, "WindowsGetStringRawBuffer"); + roActivateInstance = (RoActivateInstanceFuncPtr) ::GetProcAddress (winRTHandle, "RoActivateInstance"); + roGetActivationFactory = (RoGetActivationFactoryFuncPtr) ::GetProcAddress (winRTHandle, "RoGetActivationFactory"); + + if (roInitialize == nullptr || createHString == nullptr || deleteHString == nullptr + || getHStringRawBuffer == nullptr || roActivateInstance == nullptr || roGetActivationFactory == nullptr) + return; + + HRESULT status = roInitialize (1); + initialised = ! (status != S_OK && status != S_FALSE && status != (HRESULT) 0x80010106L); +} + +WinRTWrapper::~WinRTWrapper() +{ + if (winRTHandle != nullptr) + ::FreeLibrary (winRTHandle); + + clearSingletonInstance(); +} + WinRTWrapper::ScopedHString::ScopedHString (String str) { if (WinRTWrapper::getInstance()->isInitialised()) @@ -37,14 +67,6 @@ WinRTWrapper::ScopedHString::~ScopedHString() WinRTWrapper::getInstance()->deleteHString (hstr); } -WinRTWrapper::~WinRTWrapper() -{ - if (winRTHandle != nullptr) - ::FreeLibrary (winRTHandle); - - clearSingletonInstance(); -} - String WinRTWrapper::hStringToString (HSTRING hstr) { if (isInitialised()) @@ -54,27 +76,6 @@ String WinRTWrapper::hStringToString (HSTRING hstr) return {}; } -WinRTWrapper::WinRTWrapper() -{ - winRTHandle = ::LoadLibraryA ("api-ms-win-core-winrt-l1-1-0"); - - if (winRTHandle == nullptr) - return; - - roInitialize = (RoInitializeFuncPtr) ::GetProcAddress (winRTHandle, "RoInitialize"); - createHString = (WindowsCreateStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsCreateString"); - deleteHString = (WindowsDeleteStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsDeleteString"); - getHStringRawBuffer = (WindowsGetStringRawBufferFuncPtr) ::GetProcAddress (winRTHandle, "WindowsGetStringRawBuffer"); - roActivateInstance = (RoActivateInstanceFuncPtr) ::GetProcAddress (winRTHandle, "RoActivateInstance"); - roGetActivationFactory = (RoGetActivationFactoryFuncPtr) ::GetProcAddress (winRTHandle, "RoGetActivationFactory"); - - if (roInitialize == nullptr || createHString == nullptr || deleteHString == nullptr - || getHStringRawBuffer == nullptr || roActivateInstance == nullptr || roGetActivationFactory == nullptr) - return; - - HRESULT status = roInitialize (1); - initialised = ! (status != S_OK && status != S_FALSE && status != 0x80010106L); -} JUCE_IMPLEMENT_SINGLETON (WinRTWrapper) diff --git a/JuceLibraryCode/modules/juce_events/native/juce_win32_WinRTWrapper.h b/JuceLibraryCode/modules/juce_events/native/juce_WinRTWrapper_windows.h similarity index 66% rename from JuceLibraryCode/modules/juce_events/native/juce_win32_WinRTWrapper.h rename to JuceLibraryCode/modules/juce_events/native/juce_WinRTWrapper_windows.h index f8ddbee0..58d002e1 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_win32_WinRTWrapper.h +++ b/JuceLibraryCode/modules/juce_events/native/juce_WinRTWrapper_windows.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -26,77 +26,21 @@ namespace juce class WinRTWrapper : public DeletedAtShutdown { public: - class ScopedHString - { - public: - ScopedHString (String); - - ~ScopedHString(); - - HSTRING get() const noexcept { return hstr; } - - private: - HSTRING hstr = nullptr; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedHString) - }; - - template - class ComPtr - { - public: - ComPtr() noexcept {} - ComPtr (ComClass* obj) : p (obj) { if (p) p->AddRef(); } - ComPtr (const ComPtr& other) : p (other.p) { if (p) p->AddRef(); } - ~ComPtr() { release(); } - - operator ComClass*() const noexcept { return p; } - ComClass& operator*() const noexcept { return *p; } - ComClass* operator->() const noexcept { return p; } - - ComPtr& operator= (ComClass* const newP) - { - if (newP != nullptr) - newP->AddRef(); - - release(); - p = newP; - return *this; - } - - ComPtr& operator= (const ComPtr& newP) { return operator= (newP.p); } - - ComClass** resetAndGetPointerAddress() - { - release(); - p = nullptr; - return &p; - } - - private: - ComClass* p = nullptr; - - void release() { if (p != nullptr) p->Release(); } - - ComClass** operator&() noexcept; // private to avoid it being used accidentally - }; - - JUCE_DECLARE_SINGLETON (WinRTWrapper, true) - + //============================================================================== ~WinRTWrapper(); + bool isInitialised() const noexcept { return initialised; } - String hStringToString (HSTRING); - - bool isInitialised() const noexcept { return initialised; } + JUCE_DECLARE_SINGLETON (WinRTWrapper, false) + //============================================================================== template - ComPtr activateInstance (const wchar_t* runtimeClassID, REFCLSID classUUID) + ComSmartPtr activateInstance (const wchar_t* runtimeClassID, REFCLSID classUUID) { - ComPtr result; + ComSmartPtr result; if (isInitialised()) { - ComPtr inspectable; + ComSmartPtr inspectable; ScopedHString runtimeClass (runtimeClassID); auto hr = roActivateInstance (runtimeClass.get(), inspectable.resetAndGetPointerAddress()); @@ -108,9 +52,9 @@ class WinRTWrapper : public DeletedAtShutdown } template - ComPtr getWRLFactory (const wchar_t* runtimeClassID) + ComSmartPtr getWRLFactory (const wchar_t* runtimeClassID) { - ComPtr comPtr; + ComSmartPtr comPtr; if (isInitialised()) { @@ -123,9 +67,27 @@ class WinRTWrapper : public DeletedAtShutdown return comPtr; } + //============================================================================== + class ScopedHString + { + public: + ScopedHString (String); + ~ScopedHString(); + + HSTRING get() const noexcept { return hstr; } + + private: + HSTRING hstr = nullptr; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedHString) + }; + + String hStringToString (HSTRING); + private: WinRTWrapper(); + //============================================================================== HMODULE winRTHandle = nullptr; bool initialised = false; @@ -142,6 +104,9 @@ class WinRTWrapper : public DeletedAtShutdown WindowsGetStringRawBufferFuncPtr getHStringRawBuffer = nullptr; RoActivateInstanceFuncPtr roActivateInstance = nullptr; RoGetActivationFactoryFuncPtr roGetActivationFactory = nullptr; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WinRTWrapper) }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp b/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp deleted file mode 100644 index b24016dd..00000000 --- a/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp +++ /dev/null @@ -1,286 +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 InternalMessageQueue -{ -public: - InternalMessageQueue() - { - auto err = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, msgpipe); - jassert (err == 0); - ignoreUnused (err); - - 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()); - - clearSingletonInstance(); - } - - //============================================================================== - void postMessage (MessageManager::MessageBase* const msg) noexcept - { - ScopedLock sl (lock); - queue.add (msg); - - if (bytesInSocket < maxBytesInSocketQueue) - { - bytesInSocket++; - - ScopedUnlock ul (lock); - unsigned char x = 0xff; - auto numBytes = write (getWriteHandle(), &x, 1); - ignoreUnused (numBytes); - } - } - - //============================================================================== - JUCE_DECLARE_SINGLETON (InternalMessageQueue, false) - -private: - CriticalSection lock; - ReferenceCountedArray queue; - - int msgpipe[2]; - int bytesInSocket = 0; - static constexpr int maxBytesInSocketQueue = 128; - - int getWriteHandle() const noexcept { return msgpipe[0]; } - int getReadHandle() const noexcept { return msgpipe[1]; } - - MessageManager::MessageBase::Ptr popNextMessage (int fd) noexcept - { - const ScopedLock sl (lock); - - if (bytesInSocket > 0) - { - --bytesInSocket; - - ScopedUnlock ul (lock); - unsigned char x; - auto numBytes = read (fd, &x, 1); - ignoreUnused (numBytes); - } - - return queue.removeAndReturn (0); - } -}; - -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) - keyboardBreakOccurred = true; - } - - void installKeyboardBreakHandler() - { - struct sigaction saction; - sigset_t maskSet; - sigemptyset (&maskSet); - saction.sa_handler = keyboardBreakSignalHandler; - saction.sa_mask = maskSet; - saction.sa_flags = 0; - sigaction (SIGINT, &saction, nullptr); - } -} - -//============================================================================== -void MessageManager::doPlatformSpecificInitialisation() -{ - if (JUCEApplicationBase::isStandaloneApp()) - LinuxErrorHandling::installKeyboardBreakHandler(); - - InternalRunLoop::getInstance(); - InternalMessageQueue::getInstance(); -} - -void MessageManager::doPlatformSpecificShutdown() -{ - InternalMessageQueue::deleteInstance(); - InternalRunLoop::deleteInstance(); -} - -bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) -{ - if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) - { - queue->postMessage (message); - return true; - } - - return false; -} - -void MessageManager::broadcastMessage (const String&) -{ - // TODO -} - -// this function expects that it will NEVER be called simultaneously for two concurrent threads -bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) -{ - for (;;) - { - if (LinuxErrorHandling::keyboardBreakOccurred) - JUCEApplicationBase::quit(); - - if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) - { - if (runLoop->dispatchPendingEvents()) - break; - - if (returnIfNoPendingMessages) - return false; - - runLoop->sleepUntilNextEvent (2000); - } - } - - return true; -} - -//============================================================================== -void LinuxEventLoop::registerFdCallback (int fd, std::function readCallback, short eventMask) -{ - if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) - runLoop->registerFdCallback (fd, std::move (readCallback), eventMask); -} - -void LinuxEventLoop::unregisterFdCallback (int fd) -{ - if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) - runLoop->unregisterFdCallback (fd); -} - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.cpp b/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.cpp index dc3d0e6b..633870bf 100644 --- a/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.cpp +++ b/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,7 +23,7 @@ namespace juce { -struct MultiTimerCallback : public Timer +struct MultiTimerCallback final : public Timer { MultiTimerCallback (const int tid, MultiTimer& mt) noexcept : owner (mt), timerID (tid) @@ -56,7 +56,7 @@ Timer* MultiTimer::getCallback (int timerID) const noexcept { for (int i = timers.size(); --i >= 0;) { - MultiTimerCallback* const t = static_cast (timers.getUnchecked(i)); + MultiTimerCallback* const t = static_cast (timers.getUnchecked (i)); if (t->timerID == timerID) return t; diff --git a/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.h b/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.h index c7c12a0e..1e5c3c93 100644 --- a/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.h +++ b/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. diff --git a/JuceLibraryCode/modules/juce_events/timers/juce_TimedCallback.h b/JuceLibraryCode/modules/juce_events/timers/juce_TimedCallback.h new file mode 100644 index 00000000..72cba988 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/timers/juce_TimedCallback.h @@ -0,0 +1,64 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 7 End-User License + Agreement and JUCE Privacy Policy. + + End User License Agreement: www.juce.com/juce-7-licence + Privacy Policy: www.juce.com/juce-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + 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 +{ + +/** Utility class wrapping a single non-null callback called by a Timer. + + You can use the usual Timer functions to start and stop the TimedCallback. Deleting the + TimedCallback will automatically stop the underlying Timer. + + With this class you can use the Timer facility without inheritance. + + @see Timer + @tags{Events} +*/ +class TimedCallback final : private Timer +{ +public: + /** Constructor. The passed in callback must be non-null. */ + explicit TimedCallback (std::function callbackIn) + : callback (std::move (callbackIn)) + { + jassert (callback); + } + + /** Destructor. */ + ~TimedCallback() noexcept override { stopTimer(); } + + using Timer::startTimer; + using Timer::startTimerHz; + using Timer::stopTimer; + using Timer::isTimerRunning; + using Timer::getTimerInterval; + +private: + void timerCallback() override { callback(); } + + std::function callback; +}; + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp b/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp index dcaf49a0..2a70c3ed 100644 --- a/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp +++ b/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -23,9 +23,8 @@ namespace juce { -class Timer::TimerThread : private Thread, - private DeletedAtShutdown, - private AsyncUpdater +class Timer::TimerThread final : private Thread, + private AsyncUpdater { public: using LockType = CriticalSection; // (mysteriously, using a SpinLock here causes problems on some XP machines..) @@ -38,13 +37,10 @@ class Timer::TimerThread : private Thread, ~TimerThread() override { + cancelPendingUpdate(); signalThreadShouldExit(); callbackArrived.signal(); - stopThread (4000); - jassert (instance == this || instance == nullptr); - - if (instance == this) - instance = nullptr; + stopThread (-1); } void run() override @@ -136,58 +132,14 @@ class Timer::TimerThread : private Thread, callTimers(); } - static inline void add (Timer* tim) noexcept - { - if (instance == nullptr) - instance = new TimerThread(); - - instance->addTimer (tim); - } - - static inline void remove (Timer* tim) noexcept - { - if (instance != nullptr) - instance->removeTimer (tim); - } - - static inline void resetCounter (Timer* tim) noexcept - { - if (instance != nullptr) - instance->resetTimerCounter (tim); - } - - static TimerThread* instance; - static LockType lock; - -private: - struct TimerCountdown - { - Timer* timer; - int countdownMs; - }; - - std::vector timers; - - WaitableEvent callbackArrived; - - struct CallTimersMessage : public MessageManager::MessageBase - { - CallTimersMessage() {} - - void messageCallback() override - { - if (instance != nullptr) - instance->callTimers(); - } - }; - - //============================================================================== void addTimer (Timer* t) { + const LockType::ScopedLockType sl (lock); + // Trying to add a timer that's already here - shouldn't get to this point, // so if you get this assertion, let me know! - jassert (std::find_if (timers.begin(), timers.end(), - [t](TimerCountdown i) { return i.timer == t; }) == timers.end()); + jassert (std::none_of (timers.begin(), timers.end(), + [t] (TimerCountdown i) { return i.timer == t; })); auto pos = timers.size(); @@ -199,6 +151,8 @@ class Timer::TimerThread : private Thread, void removeTimer (Timer* t) { + const LockType::ScopedLockType sl (lock); + auto pos = t->positionInQueue; auto lastIndex = timers.size() - 1; @@ -216,6 +170,8 @@ class Timer::TimerThread : private Thread, void resetTimerCounter (Timer* t) noexcept { + const LockType::ScopedLockType sl (lock); + auto pos = t->positionInQueue; jassert (pos < timers.size()); @@ -237,6 +193,31 @@ class Timer::TimerThread : private Thread, } } +private: + LockType lock; + + struct TimerCountdown + { + Timer* timer; + int countdownMs; + }; + + std::vector timers; + + WaitableEvent callbackArrived; + + struct CallTimersMessage final : public MessageManager::MessageBase + { + CallTimersMessage() = default; + + void messageCallback() override + { + if (auto instance = SharedResourcePointer::getSharedObjectWithoutCreating()) + (*instance)->callTimers(); + } + }; + + //============================================================================== void shuffleTimerBackInQueue (size_t pos) { auto numTimers = timers.size(); @@ -302,21 +283,26 @@ class Timer::TimerThread : private Thread, void handleAsyncUpdate() override { - startThread (7); + startThread (Priority::high); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread) }; -Timer::TimerThread* Timer::TimerThread::instance = nullptr; -Timer::TimerThread::LockType Timer::TimerThread::lock; - //============================================================================== Timer::Timer() noexcept {} Timer::Timer (const Timer&) noexcept {} Timer::~Timer() { + // If you're destroying a timer on a background thread, make sure the timer has + // been stopped before execution reaches this point. A simple way to achieve this + // is to add a call to `stopTimer()` to the destructor of your class which inherits + // from Timer. + jassert (! isTimerRunning() + || MessageManager::getInstanceWithoutCreating() == nullptr + || MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager()); + stopTimer(); } @@ -326,15 +312,13 @@ void Timer::startTimer (int interval) noexcept // running, then you're not going to get any timer callbacks! JUCE_ASSERT_MESSAGE_MANAGER_EXISTS - const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - bool wasStopped = (timerPeriodMs == 0); timerPeriodMs = jmax (1, interval); if (wasStopped) - TimerThread::add (this); + timerThread->addTimer (this); else - TimerThread::resetCounter (this); + timerThread->resetTimerCounter (this); } void Timer::startTimerHz (int timerFrequencyHz) noexcept @@ -347,33 +331,37 @@ void Timer::startTimerHz (int timerFrequencyHz) noexcept void Timer::stopTimer() noexcept { - const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - if (timerPeriodMs > 0) { - TimerThread::remove (this); + timerThread->removeTimer (this); timerPeriodMs = 0; } } void JUCE_CALLTYPE Timer::callPendingTimersSynchronously() { - if (TimerThread::instance != nullptr) - TimerThread::instance->callTimersSynchronously(); + if (auto instance = SharedResourcePointer::getSharedObjectWithoutCreating()) + (*instance)->callTimersSynchronously(); } -struct LambdaInvoker : private Timer +struct LambdaInvoker final : private Timer, + private DeletedAtShutdown { - LambdaInvoker (int milliseconds, std::function f) : function (f) + LambdaInvoker (int milliseconds, std::function f) + : function (std::move (f)) { startTimer (milliseconds); } - void timerCallback() override + ~LambdaInvoker() final + { + stopTimer(); + } + + void timerCallback() final { - auto f = function; + NullCheckedInvocation::invoke (function); delete this; - f(); } std::function function; @@ -383,7 +371,7 @@ struct LambdaInvoker : private Timer void JUCE_CALLTYPE Timer::callAfterDelay (int milliseconds, std::function f) { - new LambdaInvoker (milliseconds, f); + new LambdaInvoker (milliseconds, std::move (f)); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_events/timers/juce_Timer.h b/JuceLibraryCode/modules/juce_events/timers/juce_Timer.h index f3a752bd..a33a9243 100644 --- a/JuceLibraryCode/modules/juce_events/timers/juce_Timer.h +++ b/JuceLibraryCode/modules/juce_events/timers/juce_Timer.h @@ -2,7 +2,7 @@ ============================================================================== This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. + Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. @@ -127,9 +127,9 @@ class JUCE_API Timer private: class TimerThread; - friend class TimerThread; size_t positionInQueue = (size_t) -1; int timerPeriodMs = 0; + SharedResourcePointer timerThread; Timer& operator= (const Timer&) = delete; }; diff --git a/OpenShotLibrary.jucer b/OpenShotLibrary.jucer index 63de2194..b62423f5 100644 --- a/OpenShotLibrary.jucer +++ b/OpenShotLibrary.jucer @@ -1,10 +1,10 @@ + bundleIdentifier="org.openshot.libopenshot-audio" companyName="OpenShot Studios, LLC" + includeBinaryInAppConfig="0" companyWebsite="http://www.openshot.org/" + companyEmail="sales@openshot.org" projectLineFeed=" " reportAppUsage="0" + displaySplashScreen="1" jucerFormatVersion="1"> @@ -39,7 +39,7 @@ + JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES="1" JUCE_USE_CURL="0" JUCE_ASIO="1"/> diff --git a/include/AppConfig.h.in b/include/AppConfig.h.in index 84e5f18c..5f4944d0 100644 --- a/include/AppConfig.h.in +++ b/include/AppConfig.h.in @@ -45,8 +45,8 @@ // END SECTION A -#define JUCE_USE_DARK_SPLASH_SCREEN 1 -#define JUCE_PROJUCER_VERSION 0x50407 +#define JUCE_USE_DARK_SPLASH_SCREEN 0 +#define JUCE_PROJUCER_VERSION 0x7000a //============================================================================== #define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 diff --git a/src/Main.cpp b/src/Main.cpp index 834dac04..4f66574b 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -53,10 +53,9 @@ class TestAudioDeviceManager : public juce::AudioDeviceManager { void createAudioDeviceTypes (juce::OwnedArray& list) override { - /* - addIfNotNull (list, juce::AudioIODeviceType::createAudioIODeviceType_WASAPI (false)); - addIfNotNull (list, juce::AudioIODeviceType::createAudioIODeviceType_WASAPI (true)); - */ + addIfNotNull (list, juce::AudioIODeviceType::createAudioIODeviceType_WASAPI (juce::WASAPIDeviceMode::shared)); + addIfNotNull (list, juce::AudioIODeviceType::createAudioIODeviceType_WASAPI (juce::WASAPIDeviceMode::exclusive)); + addIfNotNull (list, juce::AudioIODeviceType::createAudioIODeviceType_WASAPI (juce::WASAPIDeviceMode::sharedLowLatency)); addIfNotNull (list, juce::AudioIODeviceType::createAudioIODeviceType_DirectSound()); addIfNotNull (list, juce::AudioIODeviceType::createAudioIODeviceType_ASIO()); addIfNotNull (list, juce::AudioIODeviceType::createAudioIODeviceType_CoreAudio());