From 3e3be68438a65b8f11807f8ee4feb774432b5e41 Mon Sep 17 00:00:00 2001 From: David Braun <2096055+DBraun@users.noreply.github.com> Date: Sun, 17 Sep 2023 16:11:09 -0400 Subject: [PATCH] revert to JUCE 7.0.5 --- Builds/LinuxMakefile/Makefile | 81 +- .../DawDreamer.xcodeproj/project.pbxproj | 4 - .../DawDreamer_DynamicLibrary.vcxproj | 421 +- .../DawDreamer_DynamicLibrary.vcxproj.filters | 628 +-- DawDreamer.jucer | 7 +- JuceLibraryCode/AppConfig.h | 2 +- .../buffers/juce_AudioChannelSet.cpp | 143 +- .../buffers/juce_AudioChannelSet.h | 80 +- .../buffers/juce_AudioDataConverters.cpp | 4 +- .../buffers/juce_AudioProcessLoadMeasurer.cpp | 2 +- .../buffers/juce_AudioSampleBuffer.h | 55 +- .../buffers/juce_FloatVectorOperations.cpp | 2 +- .../buffers/juce_FloatVectorOperations.h | 2 +- .../juce_audio_basics/juce_audio_basics.cpp | 1 - .../juce_audio_basics/juce_audio_basics.h | 2 +- .../juce_audio_basics/midi/juce_MidiFile.cpp | 6 +- .../midi/juce_MidiMessageSequence.cpp | 4 +- .../juce_audio_basics/midi/juce_MidiRPN.cpp | 364 +- .../juce_audio_basics/midi/juce_MidiRPN.h | 37 +- .../juce_audio_basics/midi/ump/juce_UMP.h | 2 + .../midi/ump/juce_UMPConversion.h | 126 +- .../midi/ump/juce_UMPConverters.h | 53 +- .../midi/ump/juce_UMPDispatcher.h | 2 +- .../midi/ump/juce_UMPFactory.h | 10 +- .../ump/juce_UMPMidi1ToBytestreamTranslator.h | 15 +- .../juce_UMPMidi1ToMidi2DefaultTranslator.cpp | 54 +- .../juce_UMPMidi1ToMidi2DefaultTranslator.h | 22 +- .../midi/ump/juce_UMPSysEx7.cpp | 12 +- .../midi/ump/juce_UMPSysEx7.h | 2 +- .../midi/ump/juce_UMPUtils.h | 7 +- .../midi/ump/juce_UMP_test.cpp | 137 +- .../mpe/juce_MPESynthesiserBase.cpp | 4 +- .../mpe/juce_MPEZoneLayout.cpp | 9 +- .../mpe/juce_MPEZoneLayout.h | 2 - ...outs_mac.h => juce_mac_CoreAudioLayouts.h} | 8 +- ....h => juce_mac_CoreAudioTimeConversions.h} | 0 .../sources/juce_BufferingAudioSource.cpp | 2 +- .../sources/juce_MemoryAudioSource.cpp | 24 +- .../sources/juce_ResamplingAudioSource.cpp | 2 +- .../sources/juce_ToneGeneratorAudioSource.cpp | 2 +- .../synthesisers/juce_Synthesiser.cpp | 4 +- .../utilities/juce_Decibels.h | 14 - .../utilities/juce_IIRFilter.cpp | 74 +- .../utilities/juce_Interpolators.h | 2 +- .../utilities/juce_SmoothedValue.h | 8 +- .../audio_io/juce_AudioDeviceManager.cpp | 85 +- .../audio_io/juce_AudioDeviceManager.h | 6 - .../audio_io/juce_AudioIODevice.h | 6 +- .../audio_io/juce_AudioIODeviceType.h | 2 +- .../juce_audio_devices/juce_audio_devices.cpp | 39 +- .../juce_audio_devices/juce_audio_devices.h | 4 +- .../midi_io/juce_MidiDevices.cpp | 75 - .../midi_io/juce_MidiDevices.h | 86 +- .../midi_io/juce_MidiMessageCollector.cpp | 2 +- .../midi_io/ump/juce_UMPU32InputHandler.h | 4 +- .../app/com/rmsl/juce/JuceMidiSupport.java | 57 +- .../native/juce_Midi_android.cpp | 1171 ------ ...dio_android.cpp => juce_android_Audio.cpp} | 0 ...uce_android_HighPerformanceAudioHelpers.h} | 2 +- .../native/juce_android_Midi.cpp | 701 ++++ ...Oboe_android.cpp => juce_android_Oboe.cpp} | 41 +- ...SL_android.cpp => juce_android_OpenSL.cpp} | 0 ...{juce_Audio_ios.cpp => juce_ios_Audio.cpp} | 8 +- .../{juce_Audio_ios.h => juce_ios_Audio.h} | 0 ...uce_ALSA_linux.cpp => juce_linux_ALSA.cpp} | 0 ...uce_Bela_linux.cpp => juce_linux_Bela.cpp} | 0 ...dio_linux.cpp => juce_linux_JackAudio.cpp} | 0 ...uce_Midi_linux.cpp => juce_linux_Midi.cpp} | 284 +- ...reAudio_mac.cpp => juce_mac_CoreAudio.cpp} | 95 +- ...e_CoreMidi_mac.mm => juce_mac_CoreMidi.mm} | 113 +- ...e_ASIO_windows.cpp => juce_win32_ASIO.cpp} | 0 ...windows.cpp => juce_win32_DirectSound.cpp} | 31 +- ...e_Midi_windows.cpp => juce_win32_Midi.cpp} | 12 +- ...SAPI_windows.cpp => juce_win32_WASAPI.cpp} | 95 +- .../sources/juce_AudioTransportSource.h | 2 +- .../codecs/juce_CoreAudioFormat.cpp | 4 +- .../codecs/juce_FlacAudioFormat.cpp | 13 +- .../codecs/juce_OggVorbisAudioFormat.cpp | 23 +- .../codecs/juce_WavAudioFormat.cpp | 2 +- .../format/juce_ARAAudioReaders.cpp | 10 +- .../format/juce_AudioFormatReaderSource.cpp | 10 +- .../juce_BufferingAudioFormatReader.cpp | 97 +- .../juce_audio_formats/juce_audio_formats.h | 2 +- .../format_types/VST3_SDK/JUCE_README.md | 5 +- .../format_types/VST3_SDK/LICENSE.txt | 5 +- .../format_types/VST3_SDK/README.md | 267 +- .../VST3_SDK/VST3_License_Agreement.pdf | Bin 135822 -> 160002 bytes .../VST3_SDK/VST3_Usage_Guidelines.pdf | Bin 307448 -> 196713 bytes .../format_types/VST3_SDK/base/LICENSE.txt | 2 +- .../format_types/VST3_SDK/base/README.md | 5 +- .../VST3_SDK/base/source/baseiids.cpp | 2 +- .../base/source/classfactoryhelpers.h | 2 +- .../VST3_SDK/base/source/fbuffer.cpp | 14 +- .../VST3_SDK/base/source/fbuffer.h | 2 +- .../VST3_SDK/base/source/fcommandline.h | 366 -- .../VST3_SDK/base/source/fdebug.cpp | 25 +- .../VST3_SDK/base/source/fdebug.h | 7 +- .../VST3_SDK/base/source/fobject.cpp | 233 +- .../VST3_SDK/base/source/fobject.h | 25 +- .../VST3_SDK/base/source/fstreamer.cpp | 48 +- .../VST3_SDK/base/source/fstreamer.h | 10 +- .../VST3_SDK/base/source/fstring.cpp | 478 +-- .../VST3_SDK/base/source/fstring.h | 25 +- .../VST3_SDK/base/source/updatehandler.cpp | 19 +- .../VST3_SDK/base/source/updatehandler.h | 27 +- .../VST3_SDK/base/thread/include/flock.h | 4 +- .../VST3_SDK/base/thread/source/flock.cpp | 2 +- .../format_types/VST3_SDK/helper.manifest | 10 - .../VST3_SDK/pluginterfaces/LICENSE.txt | 5 +- .../VST3_SDK/pluginterfaces/README.md | 4 +- .../pluginterfaces/base/conststringtable.cpp | 2 +- .../pluginterfaces/base/conststringtable.h | 2 +- .../VST3_SDK/pluginterfaces/base/falignpush.h | 4 - .../VST3_SDK/pluginterfaces/base/fplatform.h | 145 +- .../VST3_SDK/pluginterfaces/base/fstrdefs.h | 104 +- .../VST3_SDK/pluginterfaces/base/ftypes.h | 54 +- .../VST3_SDK/pluginterfaces/base/funknown.cpp | 28 +- .../VST3_SDK/pluginterfaces/base/funknown.h | 40 +- .../VST3_SDK/pluginterfaces/base/futils.h | 11 +- .../pluginterfaces/base/ipluginbase.h | 179 +- .../base/iplugincompatibility.h | 122 - .../gui/iplugviewcontentscalesupport.h | 20 +- .../pluginterfaces/vst/ivstattributes.h | 4 +- .../pluginterfaces/vst/ivstaudioprocessor.h | 84 +- .../pluginterfaces/vst/ivsteditcontroller.h | 9 +- .../pluginterfaces/vst/ivstparameterchanges.h | 2 +- .../vst/ivstparameterfunctionname.h | 76 +- .../pluginterfaces/vst/ivsttestplugprovider.h | 2 +- .../VST3_SDK/pluginterfaces/vst/vstspeaker.h | 601 +-- .../VST3_SDK/pluginterfaces/vst/vsttypes.h | 47 +- .../VST3_SDK/public.sdk/LICENSE.txt | 2 +- .../VST3_SDK/public.sdk/README.md | 14 +- .../moduleinfotool/source/main.cpp | 439 --- .../public.sdk/source/common/memorystream.cpp | 4 +- .../public.sdk/source/common/memorystream.h | 2 +- .../public.sdk/source/common/pluginview.cpp | 3 +- .../public.sdk/source/common/pluginview.h | 6 +- .../public.sdk/source/common/readfile.cpp | 76 - .../public.sdk/source/common/readfile.h | 55 - .../source/vst/hosting/hostclasses.cpp | 177 +- .../source/vst/hosting/hostclasses.h | 91 +- .../public.sdk/source/vst/hosting/module.cpp | 340 -- .../public.sdk/source/vst/hosting/module.h | 214 -- .../source/vst/hosting/module_linux.cpp | 369 -- .../source/vst/hosting/module_mac.mm | 390 -- .../source/vst/hosting/module_win32.cpp | 647 ---- .../vst/hosting/pluginterfacesupport.cpp | 19 +- .../source/vst/hosting/pluginterfacesupport.h | 14 +- .../source/vst/moduleinfo/ReadMe.md | 43 - .../public.sdk/source/vst/moduleinfo/json.h | 3403 ----------------- .../source/vst/moduleinfo/jsoncxx.h | 427 --- .../source/vst/moduleinfo/moduleinfo.h | 100 - .../vst/moduleinfo/moduleinfocreator.cpp | 309 -- .../source/vst/moduleinfo/moduleinfocreator.h | 67 - .../vst/moduleinfo/moduleinfoparser.cpp | 537 --- .../source/vst/moduleinfo/moduleinfoparser.h | 68 - .../public.sdk/source/vst/utility/optional.h | 135 - .../source/vst/utility/stringconvert.cpp | 148 - .../source/vst/utility/stringconvert.h | 147 - .../public.sdk/source/vst/utility/uid.h | 294 -- .../VST3_SDK/public.sdk/source/vst/vstbus.cpp | 5 +- .../VST3_SDK/public.sdk/source/vst/vstbus.h | 16 +- .../public.sdk/source/vst/vstcomponent.cpp | 4 +- .../public.sdk/source/vst/vstcomponent.h | 3 +- .../source/vst/vstcomponentbase.cpp | 4 +- .../public.sdk/source/vst/vstcomponentbase.h | 2 +- .../source/vst/vsteditcontroller.cpp | 60 +- .../public.sdk/source/vst/vsteditcontroller.h | 20 +- .../public.sdk/source/vst/vstinitiids.cpp | 106 +- .../public.sdk/source/vst/vstparameters.cpp | 22 +- .../public.sdk/source/vst/vstparameters.h | 12 +- .../public.sdk/source/vst/vstpresetfile.cpp | 42 +- .../public.sdk/source/vst/vstpresetfile.h | 6 +- .../format_types/juce_ARAHosting.h | 6 - .../format_types/juce_AU_Shared.h | 19 +- .../juce_AudioUnitPluginFormat.mm | 58 +- .../format_types/juce_LADSPAPluginFormat.cpp | 2 +- .../format_types/juce_LV2PluginFormat.cpp | 23 +- .../format_types/juce_LV2SupportLibs.cpp | 5 - .../juce_LegacyAudioParameter.cpp | 10 +- .../format_types/juce_VST3Common.h | 188 +- .../format_types/juce_VST3Headers.h | 90 +- .../format_types/juce_VST3PluginFormat.cpp | 382 +- .../format_types/juce_VST3PluginFormat.h | 1 - .../juce_VST3PluginFormat_test.cpp | 23 +- .../format_types/juce_VSTPluginFormat.cpp | 15 +- .../juce_audio_processors.cpp | 5 +- .../juce_audio_processors.h | 5 +- .../processors/juce_AudioProcessor.cpp | 124 +- .../processors/juce_AudioProcessor.h | 54 +- .../processors/juce_AudioProcessorEditor.cpp | 12 - .../processors/juce_AudioProcessorEditor.h | 17 - .../processors/juce_AudioProcessorGraph.cpp | 566 +-- .../processors/juce_AudioProcessorGraph.h | 17 +- .../juce_GenericAudioProcessorEditor.cpp | 4 +- .../scanning/juce_PluginListComponent.cpp | 91 +- .../scanning/juce_PluginListComponent.h | 2 - .../ARA/juce_ARADocumentController.cpp | 18 - .../ARA/juce_ARADocumentController.h | 9 - .../utilities/ARA/juce_ARAModelObjects.cpp | 2 +- .../utilities/ARA/juce_ARA_utils.h | 3 +- .../utilities/juce_AAXClientExtensions.cpp | 299 -- .../utilities/juce_AAXClientExtensions.h | 83 - .../utilities/juce_AudioParameterBool.h | 2 - .../utilities/juce_AudioParameterChoice.h | 2 - .../utilities/juce_AudioParameterFloat.cpp | 4 +- .../utilities/juce_AudioParameterFloat.h | 2 - .../utilities/juce_AudioParameterInt.h | 2 - .../juce_AudioProcessorParameterWithID.h | 4 - .../juce_AudioProcessorValueTreeState.cpp | 18 +- .../juce_AudioProcessorValueTreeState.h | 2 - .../juce_NativeScaleFactorNotifier.h | 2 - .../utilities/juce_ParameterAttachments.cpp | 2 +- .../utilities/juce_PluginHostType.cpp | 3 - .../utilities/juce_PluginHostType.h | 3 - .../utilities/juce_RangedAudioParameter.h | 2 - .../utilities/juce_VST3ClientExtensions.h | 21 +- ...nsions.cpp => juce_VSTCallbackHandler.cpp} | 10 +- ...Extensions.h => juce_VSTCallbackHandler.h} | 27 +- .../modules/juce_core/containers/juce_Array.h | 4 +- .../juce_core/containers/juce_ArrayBase.h | 2 +- .../juce_core/containers/juce_ListenerList.h | 3 - .../juce_core/containers/juce_Optional.h | 5 +- .../modules/juce_core/containers/juce_Span.h | 150 - .../juce_core/containers/juce_Variant.cpp | 4 +- .../modules/juce_core/files/juce_File.cpp | 2 +- .../juce_core/files/juce_FileSearchPath.cpp | 86 +- .../juce_core/files/juce_FileSearchPath.h | 13 +- .../juce_core/files/juce_common_MimeTypes.cpp | 1 - .../juce_core/files/juce_common_MimeTypes.h | 9 +- .../juce_core/javascript/juce_Javascript.cpp | 10 +- .../modules/juce_core/juce_core.cpp | 91 +- JuceLibraryCode/modules/juce_core/juce_core.h | 14 +- .../juce_core/maths/juce_MathsFunctions.h | 372 +- .../maths/juce_MathsFunctions_test.cpp | 543 --- .../juce_core/maths/juce_NormalisableRange.h | 8 +- .../modules/juce_core/maths/juce_Random.cpp | 2 +- .../modules/juce_core/maths/juce_Range.h | 11 +- .../modules/juce_core/memory/juce_Reservoir.h | 2 - .../modules/juce_core/misc/juce_EnumHelpers.h | 103 - .../juce_core/misc/juce_EnumHelpers_test.cpp | 94 - .../modules/juce_core/misc/juce_Functional.h | 4 +- .../native/juce_BasicNativeHeaders.h | 8 +- .../native/juce_PlatformTimerListener.h | 32 - .../native/juce_PlatformTimer_generic.cpp | 149 - .../native/juce_PlatformTimer_windows.cpp | 68 - ...d.cpp => juce_android_AndroidDocument.cpp} | 0 ...les_android.cpp => juce_android_Files.cpp} | 0 ...ndroid.cpp => juce_android_JNIHelpers.cpp} | 48 +- ...rs_android.h => juce_android_JNIHelpers.h} | 46 +- ...Misc_android.cpp => juce_android_Misc.cpp} | 0 ...k_android.cpp => juce_android_Network.cpp} | 0 ...pp => juce_android_RuntimePermissions.cpp} | 6 +- ...droid.cpp => juce_android_SystemStats.cpp} | 0 ...s_android.cpp => juce_android_Threads.cpp} | 2 + ...Network_curl.cpp => juce_curl_Network.cpp} | 15 +- ...edCode_intel.h => juce_intel_SharedCode.h} | 0 ...le_linux.cpp => juce_linux_CommonFile.cpp} | 0 ...e_Files_linux.cpp => juce_linux_Files.cpp} | 6 +- ...twork_linux.cpp => juce_linux_Network.cpp} | 2 +- ...s_linux.cpp => juce_linux_SystemStats.cpp} | 6 +- ...reads_linux.cpp => juce_linux_Threads.cpp} | 0 ...e_CFHelpers_mac.h => juce_mac_CFHelpers.h} | 0 .../{juce_Files_mac.mm => juce_mac_Files.mm} | 0 ...uce_Network_mac.mm => juce_mac_Network.mm} | 5 - ...jCHelpers_mac.h => juce_mac_ObjCHelpers.h} | 50 +- ...uce_Strings_mac.mm => juce_mac_Strings.mm} | 0 ...emStats_mac.mm => juce_mac_SystemStats.mm} | 93 +- ...uce_Threads_mac.mm => juce_mac_Threads.mm} | 109 +- ...ative.h => juce_native_ThreadPriorities.h} | 0 ...Address_posix.h => juce_posix_IPAddress.h} | 0 ...ipe_posix.cpp => juce_posix_NamedPipe.cpp} | 0 ...edCode_posix.h => juce_posix_SharedCode.h} | 22 +- ...ats_wasm.cpp => juce_wasm_SystemStats.cpp} | 0 ...Ptr_windows.h => juce_win32_ComSmartPtr.h} | 0 ...Files_windows.cpp => juce_win32_Files.cpp} | 0 ...ork_windows.cpp => juce_win32_Network.cpp} | 0 ...ry_windows.cpp => juce_win32_Registry.cpp} | 0 ...windows.cpp => juce_win32_SystemStats.cpp} | 221 +- ...ads_windows.cpp => juce_win32_Threads.cpp} | 0 .../juce_core/network/juce_WebInputStream.cpp | 5 +- .../streams/juce_MemoryInputStream.cpp | 4 +- .../juce_core/system/juce_CompilerWarnings.h | 23 +- .../juce_core/system/juce_StandardHeader.h | 3 +- .../juce_core/system/juce_SystemStats.cpp | 81 +- .../juce_core/system/juce_SystemStats.h | 41 +- .../juce_core/system/juce_TargetPlatform.h | 2 +- .../text/juce_CharacterFunctions.cpp | 2 +- .../modules/juce_core/text/juce_String.cpp | 14 +- .../modules/juce_core/text/juce_String.h | 2 +- .../threads/juce_HighResolutionTimer.cpp | 419 +- .../threads/juce_HighResolutionTimer.h | 32 +- .../modules/juce_core/threads/juce_Thread.cpp | 20 +- .../modules/juce_core/threads/juce_Thread.h | 121 +- .../juce_core/threads/juce_ThreadPool.cpp | 27 +- .../juce_core/threads/juce_ThreadPool.h | 83 +- .../juce_core/threads/juce_WaitableEvent.cpp | 6 +- .../juce_core/threads/juce_WaitableEvent.h | 2 +- .../juce_core/time/juce_RelativeTime.cpp | 12 +- .../juce_core/unit_tests/juce_UnitTest.h | 4 +- .../juce_cryptography/juce_cryptography.h | 2 +- .../juce_data_structures.h | 2 +- .../values/juce_CachedValue.h | 11 +- .../containers/juce_AudioBlock_test.cpp | 173 +- .../containers/juce_SIMDRegister_test.cpp | 17 +- .../filter_design/juce_FilterDesign.cpp | 11 +- .../juce_dsp/frequency/juce_Convolution.cpp | 2 +- .../frequency/juce_Convolution_test.cpp | 6 +- JuceLibraryCode/modules/juce_dsp/juce_dsp.cpp | 6 +- JuceLibraryCode/modules/juce_dsp/juce_dsp.h | 10 +- .../juce_dsp/maths/juce_LogRampedValue.h | 2 +- .../modules/juce_dsp/maths/juce_Matrix.cpp | 10 +- ...Ops_avx.cpp => juce_avx_SIMDNativeOps.cpp} | 0 ...tiveOps_avx.h => juce_avx_SIMDNativeOps.h} | 0 ...llback.h => juce_fallback_SIMDNativeOps.h} | 6 +- ...s_neon.cpp => juce_neon_SIMDNativeOps.cpp} | 0 ...veOps_neon.h => juce_neon_SIMDNativeOps.h} | 0 ...Ops_sse.cpp => juce_sse_SIMDNativeOps.cpp} | 0 ...tiveOps_sse.h => juce_sse_SIMDNativeOps.h} | 0 .../juce_dsp/processors/juce_DelayLine.cpp | 2 +- .../juce_dsp/processors/juce_DelayLine.h | 6 +- .../juce_dsp/processors/juce_IIRFilter.cpp | 96 +- .../juce_dsp/processors/juce_IIRFilter_Impl.h | 5 +- .../juce_dsp/processors/juce_Oversampling.cpp | 2 +- .../juce_dsp/processors/juce_Oversampling.h | 18 +- .../juce_dsp/processors/juce_ProcessContext.h | 9 +- .../processors/juce_ProcessorChain_test.cpp | 2 +- .../modules/juce_dsp/widgets/juce_Bias.h | 2 +- .../modules/juce_dsp/widgets/juce_Gain.h | 2 +- .../broadcasters/juce_LockingAsyncUpdater.cpp | 133 - .../broadcasters/juce_LockingAsyncUpdater.h | 113 - .../modules/juce_events/juce_events.cpp | 18 +- .../modules/juce_events/juce_events.h | 9 +- .../messages/juce_ApplicationBase.cpp | 4 +- .../messages/juce_MessageManager.cpp | 113 +- .../messages/juce_MessageManager.h | 6 +- .../juce_events/native/juce_RunningInUnity.h | 35 - ...android.cpp => juce_android_Messaging.cpp} | 0 ...ager_ios.mm => juce_ios_MessageManager.mm} | 0 ...entLoop_linux.h => juce_linux_EventLoop.h} | 0 ...linux.h => juce_linux_EventLoopInternal.h} | 0 ...ing_linux.cpp => juce_linux_Messaging.cpp} | 3 - ...ager_mac.mm => juce_mac_MessageManager.mm} | 337 +- ...ageQueue_mac.h => juce_osx_MessageQueue.h} | 0 ...ows.h => juce_win32_HiddenMessageWindow.h} | 10 +- ...g_windows.cpp => juce_win32_Messaging.cpp} | 26 +- ...indows.cpp => juce_win32_WinRTWrapper.cpp} | 0 ...er_windows.h => juce_win32_WinRTWrapper.h} | 0 .../juce_graphics/colour/juce_Colour.cpp | 9 +- .../juce_graphics/colour/juce_Colour.h | 6 +- .../colour/juce_ColourGradient.cpp | 38 +- .../colour/juce_ColourGradient.h | 11 - .../contexts/juce_GraphicsContext.cpp | 4 +- .../fonts/juce_CustomTypeface.cpp | 2 +- .../modules/juce_graphics/fonts/juce_Font.cpp | 22 +- .../fonts/juce_GlyphArrangement.cpp | 8 +- .../juce_graphics/fonts/juce_TextLayout.cpp | 2 +- .../juce_graphics/fonts/juce_Typeface.cpp | 2 +- .../geometry/juce_AffineTransform.cpp | 29 +- .../juce_graphics/geometry/juce_EdgeTable.cpp | 29 +- .../juce_graphics/geometry/juce_Line.h | 14 +- .../juce_graphics/geometry/juce_Path.cpp | 19 +- .../juce_graphics/geometry/juce_Path.h | 10 +- .../geometry/juce_PathIterator.cpp | 20 +- .../geometry/juce_PathStrokeType.cpp | 191 +- .../juce_graphics/geometry/juce_Point.h | 13 +- .../juce_graphics/geometry/juce_Rectangle.h | 8 +- .../image_formats/juce_GIFLoader.cpp | 4 +- .../image_formats/juce_PNGLoader.cpp | 11 +- .../juce_graphics/images/juce_ImageCache.cpp | 7 +- .../juce_graphics/images/juce_ScaledImage.h | 2 - .../modules/juce_graphics/juce_graphics.cpp | 28 +- .../modules/juce_graphics/juce_graphics.h | 8 +- .../native/juce_RenderingHelpers.h | 6 +- ...nts_android.cpp => juce_android_Fonts.cpp} | 0 ...d.cpp => juce_android_GraphicsContext.cpp} | 0 ...droid.cpp => juce_android_IconHelpers.cpp} | 0 ...s_freetype.cpp => juce_freetype_Fonts.cpp} | 0 ...e_Fonts_linux.cpp => juce_linux_Fonts.cpp} | 0 ...s_linux.cpp => juce_linux_IconHelpers.cpp} | 0 ...t_mac.h => juce_mac_CoreGraphicsContext.h} | 5 +- ...mac.mm => juce_mac_CoreGraphicsContext.mm} | 178 +- ...s_mac.h => juce_mac_CoreGraphicsHelpers.h} | 0 .../{juce_Fonts_mac.mm => juce_mac_Fonts.mm} | 2 +- ...lpers_mac.cpp => juce_mac_IconHelpers.cpp} | 0 ...=> juce_win32_Direct2DGraphicsContext.cpp} | 0 ...h => juce_win32_Direct2DGraphicsContext.h} | 0 ...p => juce_win32_DirectWriteTypeLayout.cpp} | 2 +- ...cpp => juce_win32_DirectWriteTypeface.cpp} | 0 ...Fonts_windows.cpp => juce_win32_Fonts.cpp} | 0 ...windows.cpp => juce_win32_IconHelpers.cpp} | 0 .../placement/juce_RectanglePlacement.cpp | 2 +- .../enums/juce_AccessibilityEvent.h | 2 +- .../enums/juce_AccessibilityRole.h | 2 +- .../juce_AccessibilityTableInterface.h | 1 - .../juce_AccessibilityHandler.cpp | 110 +- .../accessibility/juce_AccessibilityHandler.h | 20 - .../application/juce_Application.cpp | 6 +- .../juce_gui_basics/buttons/juce_Button.cpp | 93 +- .../juce_gui_basics/buttons/juce_Button.h | 3 +- .../buttons/juce_HyperlinkButton.cpp | 2 +- .../buttons/juce_HyperlinkButton.h | 5 +- .../buttons/juce_ToggleButton.cpp | 2 +- .../buttons/juce_ToggleButton.h | 5 +- .../juce_ApplicationCommandManager.cpp | 2 +- .../components/juce_Component.cpp | 471 ++- .../components/juce_Component.h | 98 +- .../components/juce_FocusTraverser.cpp | 114 +- .../components/juce_ModalComponentManager.cpp | 2 +- .../juce_gui_basics/desktop/juce_Desktop.cpp | 4 +- .../juce_gui_basics/desktop/juce_Desktop.h | 6 +- .../juce_gui_basics/desktop/juce_Displays.cpp | 26 +- .../detail/juce_AccessibilityHelpers.cpp | 33 - .../detail/juce_AccessibilityHelpers.h | 70 - .../detail/juce_AlertWindowHelpers.h | 115 - .../detail/juce_ButtonAccessibilityHandler.h | 125 - .../detail/juce_ComponentHelpers.h | 255 -- .../detail/juce_CustomMouseCursorInfo.h | 35 - .../detail/juce_FocusHelpers.h | 117 - .../detail/juce_FocusRestorer.h | 46 - .../detail/juce_LookAndFeelHelpers.h | 62 - .../detail/juce_MouseInputSourceImpl.h | 588 --- .../detail/juce_MouseInputSourceList.h | 154 - .../detail/juce_ScalingHelpers.h | 123 - .../detail/juce_ScopedContentSharerImpl.h | 98 - .../juce_ScopedContentSharerInterface.h | 211 - .../detail/juce_ScopedMessageBoxImpl.h | 132 - .../detail/juce_ScopedMessageBoxInterface.h | 60 - ...e_ToolbarItemDragAndDropOverlayComponent.h | 118 - .../detail/juce_TopLevelWindowManager.h | 136 - .../detail/juce_ViewportHelpers.h | 49 - .../detail/juce_WindowingHelpers.h | 52 - .../drawables/juce_DrawableImage.h | 3 +- .../drawables/juce_DrawableText.cpp | 4 +- .../drawables/juce_DrawableText.h | 3 +- .../drawables/juce_SVGParser.cpp | 4 +- .../filebrowser/juce_ContentSharer.cpp | 252 +- .../filebrowser/juce_ContentSharer.h | 106 +- .../filebrowser/juce_FileBrowserComponent.cpp | 3 +- .../filebrowser/juce_FileBrowserComponent.h | 3 +- .../filebrowser/juce_FileChooser.cpp | 2 +- .../filebrowser/juce_FileChooserDialogBox.cpp | 41 +- .../filebrowser/juce_FileChooserDialogBox.h | 3 +- .../filebrowser/juce_FileListComponent.cpp | 11 +- .../juce_FileSearchPathListComponent.cpp | 6 +- .../filebrowser/juce_FileTreeComponent.cpp | 618 +-- .../filebrowser/juce_FileTreeComponent.h | 3 - .../filebrowser/juce_ImagePreviewComponent.h | 3 +- .../juce_gui_basics/juce_gui_basics.cpp | 402 +- .../modules/juce_gui_basics/juce_gui_basics.h | 33 +- .../keyboard/juce_KeyboardFocusTraverser.cpp | 16 +- .../layout/juce_AnimatedPosition.h | 2 +- .../layout/juce_AnimatedPositionBehaviours.h | 2 +- ...uce_BorderedComponentBoundsConstrainer.cpp | 77 - .../juce_BorderedComponentBoundsConstrainer.h | 69 - .../layout/juce_ComponentAnimator.cpp | 4 +- .../layout/juce_ConcertinaPanel.h | 4 +- .../juce_gui_basics/layout/juce_FlexBox.cpp | 16 +- .../juce_gui_basics/layout/juce_Grid.cpp | 1721 ++++----- .../juce_gui_basics/layout/juce_Grid.h | 8 +- .../layout/juce_GroupComponent.h | 4 +- .../layout/juce_ResizableBorderComponent.cpp | 4 - .../layout/juce_ResizableBorderComponent.h | 2 +- .../layout/juce_ResizableCornerComponent.cpp | 9 +- .../layout/juce_ResizableEdgeComponent.cpp | 21 +- .../juce_gui_basics/layout/juce_ScrollBar.h | 3 +- .../juce_gui_basics/layout/juce_SidePanel.h | 3 +- .../layout/juce_StretchableLayoutManager.cpp | 2 +- .../layout/juce_TabbedButtonBar.h | 3 +- .../layout/juce_TabbedComponent.h | 3 +- .../juce_gui_basics/layout/juce_Viewport.cpp | 21 +- .../juce_gui_basics/layout/juce_Viewport.h | 1 - .../lookandfeel/juce_LookAndFeel_V1.cpp | 3 - .../lookandfeel/juce_LookAndFeel_V2.cpp | 98 +- .../lookandfeel/juce_LookAndFeel_V2.h | 7 - .../lookandfeel/juce_LookAndFeel_V3.cpp | 2 - .../lookandfeel/juce_LookAndFeel_V4.cpp | 68 +- .../lookandfeel/juce_LookAndFeel_V4.h | 8 +- .../menus/juce_BurgerMenuComponent.h | 3 +- .../menus/juce_MenuBarComponent.h | 3 +- .../juce_gui_basics/menus/juce_PopupMenu.cpp | 27 +- .../misc/juce_BubbleComponent.cpp | 9 +- .../misc/juce_BubbleComponent.h | 22 +- .../misc/juce_DropShadower.cpp | 26 +- .../misc/juce_JUCESplashScreen.h | 4 +- .../mouse/juce_DragAndDropContainer.cpp | 24 +- .../mouse/juce_MouseCursor.cpp | 2 +- .../juce_gui_basics/mouse/juce_MouseCursor.h | 2 +- .../mouse/juce_MouseInputSource.cpp | 655 +++- .../mouse/juce_MouseInputSource.h | 9 +- .../{detail => mouse}/juce_PointerState.h | 9 +- .../accessibility/juce_Accessibility.cpp | 33 - ...oid.cpp => juce_android_Accessibility.cpp} | 92 +- ...ility_ios.mm => juce_ios_Accessibility.mm} | 35 +- ...ility_mac.mm => juce_mac_Accessibility.mm} | 19 +- ...mm => juce_mac_AccessibilitySharedCode.mm} | 10 +- ...ndows.cpp => juce_win32_Accessibility.cpp} | 22 +- ...pp => juce_win32_AccessibilityElement.cpp} | 31 +- ...ws.h => juce_win32_AccessibilityElement.h} | 5 +- ...s_windows.h => juce_win32_ComInterfaces.h} | 8 - ...=> juce_win32_UIAExpandCollapseProvider.h} | 0 ...ows.h => juce_win32_UIAGridItemProvider.h} | 6 +- ...windows.h => juce_win32_UIAGridProvider.h} | 6 +- ...pers_windows.h => juce_win32_UIAHelpers.h} | 0 ...ndows.h => juce_win32_UIAInvokeProvider.h} | 0 ...windows.h => juce_win32_UIAProviderBase.h} | 0 ...rs_windows.h => juce_win32_UIAProviders.h} | 28 +- ...s.h => juce_win32_UIARangeValueProvider.h} | 0 ...ws.h => juce_win32_UIASelectionProvider.h} | 0 ...windows.h => juce_win32_UIATextProvider.h} | 2 +- ...ndows.h => juce_win32_UIAToggleProvider.h} | 0 ...ws.h => juce_win32_UIATransformProvider.h} | 0 ...indows.h => juce_win32_UIAValueProvider.h} | 0 ...ndows.h => juce_win32_UIAWindowProvider.h} | 0 ...ndows.h => juce_win32_WindowsUIAWrapper.h} | 0 .../app/com/rmsl/juce/ComponentPeerView.java | 18 +- .../javaopt/app/com/rmsl/juce/Receiver.java | 42 - .../native/juce_ContentSharer_ios.cpp | 125 - .../native/juce_NativeMessageBox_android.cpp | 115 - .../native/juce_NativeMessageBox_ios.mm | 110 - .../native/juce_NativeMessageBox_linux.cpp | 69 - .../native/juce_NativeMessageBox_mac.mm | 134 - .../native/juce_NativeMessageBox_windows.cpp | 348 -- .../juce_NativeModalWrapperComponent_ios.h | 132 - .../juce_ScopedDPIAwarenessDisabler.cpp | 34 - .../native/juce_WindowUtils_android.cpp | 34 - .../native/juce_WindowUtils_ios.mm | 34 - .../native/juce_WindowUtils_linux.cpp | 39 - .../native/juce_WindowUtils_mac.mm | 38 - .../native/juce_WindowUtils_windows.cpp | 59 - .../native/juce_WindowsHooks_windows.cpp | 105 - .../native/juce_WindowsHooks_windows.h | 46 - ...oid.cpp => juce_android_ContentSharer.cpp} | 921 +++-- ...droid.cpp => juce_android_FileChooser.cpp} | 0 ...android.cpp => juce_android_Windowing.cpp} | 1811 ++++----- .../native/juce_ios_ContentSharer.cpp | 204 + ...Chooser_ios.mm => juce_ios_FileChooser.mm} | 127 +- ...ios.mm => juce_ios_UIViewComponentPeer.mm} | 172 +- ...Windowing_ios.mm => juce_ios_Windowing.mm} | 213 +- ...r_linux.cpp => juce_linux_FileChooser.cpp} | 17 +- ...ing_linux.cpp => juce_linux_Windowing.cpp} | 155 +- ..._mac.h => juce_mac_CGMetalLayerRenderer.h} | 92 +- ...Chooser_mac.mm => juce_mac_FileChooser.mm} | 32 +- ...e_MainMenu_mac.mm => juce_mac_MainMenu.mm} | 17 +- ...eCursor_mac.mm => juce_mac_MouseCursor.mm} | 6 +- ...mac.mm => juce_mac_NSViewComponentPeer.mm} | 367 +- ...mac.h => juce_mac_PerScreenDisplayLinks.h} | 0 ...Windowing_mac.mm => juce_mac_Windowing.mm} | 343 +- ...windows.cpp => juce_win32_DragAndDrop.cpp} | 2 +- ...windows.cpp => juce_win32_FileChooser.cpp} | 87 +- ...ce_win32_ScopedThreadDPIAwarenessSetter.h} | 0 ...g_windows.cpp => juce_win32_Windowing.cpp} | 527 ++- .../juce_linux_ScopedWindowAssociation.h} | 0 .../juce_linux_X11_DragAndDrop.cpp} | 4 +- .../juce_linux_X11_Symbols.cpp} | 2 +- .../juce_linux_X11_Symbols.h} | 2 +- .../juce_linux_XWindowSystem.cpp} | 133 +- .../juce_linux_XWindowSystem.h} | 6 - .../juce_SliderPropertyComponent.cpp | 2 +- .../juce_gui_basics/widgets/juce_ComboBox.cpp | 22 +- .../juce_gui_basics/widgets/juce_ComboBox.h | 3 +- .../widgets/juce_ImageComponent.h | 4 +- .../juce_gui_basics/widgets/juce_Label.cpp | 2 +- .../juce_gui_basics/widgets/juce_Label.h | 4 +- .../juce_gui_basics/widgets/juce_ListBox.cpp | 214 +- .../juce_gui_basics/widgets/juce_ListBox.h | 18 +- .../widgets/juce_ProgressBar.cpp | 33 +- .../widgets/juce_ProgressBar.h | 88 +- .../juce_gui_basics/widgets/juce_Slider.cpp | 41 +- .../juce_gui_basics/widgets/juce_Slider.h | 11 +- .../widgets/juce_TableHeaderComponent.h | 3 +- .../widgets/juce_TableListBox.cpp | 123 +- .../widgets/juce_TableListBox.h | 14 +- .../widgets/juce_TextEditor.cpp | 7 +- .../juce_gui_basics/widgets/juce_TextEditor.h | 7 +- .../juce_gui_basics/widgets/juce_Toolbar.cpp | 2 +- .../juce_gui_basics/widgets/juce_Toolbar.h | 3 +- .../widgets/juce_ToolbarItemComponent.cpp | 95 +- .../widgets/juce_ToolbarItemComponent.h | 7 +- .../widgets/juce_ToolbarItemPalette.h | 3 +- .../juce_gui_basics/widgets/juce_TreeView.cpp | 2 +- .../juce_gui_basics/widgets/juce_TreeView.h | 3 +- .../windows/juce_AlertWindow.cpp | 215 +- .../windows/juce_AlertWindow.h | 92 +- .../windows/juce_CallOutBox.cpp | 5 +- .../juce_gui_basics/windows/juce_CallOutBox.h | 3 +- .../windows/juce_ComponentPeer.cpp | 6 +- .../windows/juce_ComponentPeer.h | 10 - .../windows/juce_DialogWindow.cpp | 2 +- .../windows/juce_DialogWindow.h | 5 +- .../windows/juce_MessageBoxOptions.cpp | 93 - .../windows/juce_MessageBoxOptions.h | 74 +- .../windows/juce_NativeMessageBox.cpp | 158 - .../windows/juce_NativeMessageBox.h | 37 - .../windows/juce_ScopedMessageBox.cpp | 58 - .../windows/juce_ScopedMessageBox.h | 69 - .../windows/juce_TooltipWindow.cpp | 6 +- .../windows/juce_TooltipWindow.h | 3 +- .../windows/juce_TopLevelWindow.cpp | 116 +- .../windows/juce_TopLevelWindow.h | 5 +- ...achment.cpp => juce_VBlankAttachement.cpp} | 0 ...kAttachment.h => juce_VBlankAttachement.h} | 9 +- .../windows/juce_WindowUtils.h | 42 - .../code_editor/juce_CodeEditorComponent.cpp | 10 +- .../code_editor/juce_CodeEditorComponent.h | 9 +- .../documents/juce_FileBasedDocument.cpp | 141 +- .../embedding/juce_AndroidViewComponent.h | 2 - .../embedding/juce_NSViewComponent.h | 2 - .../embedding/juce_UIViewComponent.h | 2 - .../modules/juce_gui_extra/juce_gui_extra.cpp | 36 +- .../modules/juce_gui_extra/juce_gui_extra.h | 2 +- .../misc/juce_ColourSelector.cpp | 6 +- .../misc/juce_KeyMappingEditorComponent.cpp | 55 +- .../misc/juce_KeyMappingEditorComponent.h | 1 - .../misc/juce_WebBrowserComponent.h | 12 +- .../native/juce_AndroidViewComponent.cpp | 16 +- ...cpp => juce_android_PushNotifications.cpp} | 0 ...p => juce_android_WebBrowserComponent.cpp} | 10 +- ...ios.cpp => juce_ios_PushNotifications.cpp} | 0 ...ent_ios.mm => juce_ios_UIViewComponent.mm} | 12 +- ....cpp => juce_linux_X11_SystemTrayIcon.cpp} | 0 ...=> juce_linux_X11_WebBrowserComponent.cpp} | 116 +- ...nux.cpp => juce_linux_XEmbedComponent.cpp} | 0 ...eRemote_mac.mm => juce_mac_AppleRemote.mm} | 0 ...ent_mac.mm => juce_mac_NSViewComponent.mm} | 33 +- ...er_mac.h => juce_mac_NSViewFrameWatcher.h} | 0 ...mac.cpp => juce_mac_PushNotifications.cpp} | 0 ...on_mac.cpp => juce_mac_SystemTrayIcon.cpp} | 0 ...mac.mm => juce_mac_WebBrowserComponent.mm} | 2 +- ...ws.cpp => juce_win32_ActiveXComponent.cpp} | 0 ...ndows.cpp => juce_win32_HWNDComponent.cpp} | 0 ...dows.cpp => juce_win32_SystemTrayIcon.cpp} | 0 ...cpp => juce_win32_WebBrowserComponent.cpp} | 213 +- .../modules/juce_opengl/juce_opengl.cpp | 24 +- .../modules/juce_opengl/juce_opengl.h | 4 +- ...OpenGL_linux.h => juce_OpenGL_linux_X11.h} | 119 +- .../{juce_OpenGL_mac.h => juce_OpenGL_osx.h} | 64 +- ...e_OpenGL_windows.h => juce_OpenGL_win32.h} | 0 .../juce_opengl/opengl/juce_OpenGLContext.cpp | 168 +- .../juce_opengl/opengl/juce_OpenGLContext.h | 17 +- .../opengl/juce_OpenGLFrameBuffer.cpp | 4 +- .../opengl/juce_OpenGLGraphicsContext.cpp | 130 +- .../juce_video/capture/juce_CameraDevice.cpp | 8 +- .../modules/juce_video/juce_video.cpp | 2 +- .../modules/juce_video/juce_video.h | 2 +- ..._android.h => juce_android_CameraDevice.h} | 0 ...e_Video_android.h => juce_android_Video.h} | 2 +- ...raDevice_ios.h => juce_ios_CameraDevice.h} | 2 - ...raDevice_mac.h => juce_mac_CameraDevice.h} | 55 +- .../{juce_Video_mac.h => juce_mac_Video.h} | 10 +- ...ce_windows.h => juce_win32_CameraDevice.h} | 0 ...mTypes_windows.h => juce_win32_ComTypes.h} | 0 ...uce_Video_windows.h => juce_win32_Video.h} | 0 .../playback/juce_VideoComponent.cpp | 6 +- Source/Sampler | 2 +- 655 files changed, 12254 insertions(+), 30770 deletions(-) rename JuceLibraryCode/modules/juce_audio_basics/native/{juce_CoreAudioLayouts_mac.h => juce_mac_CoreAudioLayouts.h} (98%) rename JuceLibraryCode/modules/juce_audio_basics/native/{juce_CoreAudioTimeConversions_mac.h => juce_mac_CoreAudioTimeConversions.h} (100%) delete mode 100644 JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_android.cpp rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_Audio_android.cpp => juce_android_Audio.cpp} (100%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_HighPerformanceAudioHelpers_android.h => juce_android_HighPerformanceAudioHelpers.h} (98%) create mode 100644 JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_Oboe_android.cpp => juce_android_Oboe.cpp} (98%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_OpenSL_android.cpp => juce_android_OpenSL.cpp} (100%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_Audio_ios.cpp => juce_ios_Audio.cpp} (99%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_Audio_ios.h => juce_ios_Audio.h} (100%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_ALSA_linux.cpp => juce_linux_ALSA.cpp} (100%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_Bela_linux.cpp => juce_linux_Bela.cpp} (100%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_JackAudio_linux.cpp => juce_linux_JackAudio.cpp} (100%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_Midi_linux.cpp => juce_linux_Midi.cpp} (69%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_CoreAudio_mac.cpp => juce_mac_CoreAudio.cpp} (97%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_CoreMidi_mac.mm => juce_mac_CoreMidi.mm} (94%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_ASIO_windows.cpp => juce_win32_ASIO.cpp} (100%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_DirectSound_windows.cpp => juce_win32_DirectSound.cpp} (98%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_Midi_windows.cpp => juce_win32_Midi.cpp} (99%) rename JuceLibraryCode/modules/juce_audio_devices/native/{juce_WASAPI_windows.cpp => juce_win32_WASAPI.cpp} (96%) delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fcommandline.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/helper.manifest delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/iplugincompatibility.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/samples/vst-utilities/moduleinfotool/source/main.cpp delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/readfile.cpp delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/readfile.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.cpp delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_linux.cpp delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_mac.mm delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_win32.cpp delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/ReadMe.md delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/json.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/jsoncxx.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfo.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/optional.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AAXClientExtensions.cpp delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AAXClientExtensions.h rename JuceLibraryCode/modules/juce_audio_processors/utilities/{juce_VST2ClientExtensions.cpp => juce_VSTCallbackHandler.cpp} (64%) rename JuceLibraryCode/modules/juce_audio_processors/utilities/{juce_VST2ClientExtensions.h => juce_VSTCallbackHandler.h} (70%) delete mode 100644 JuceLibraryCode/modules/juce_core/containers/juce_Span.h delete mode 100644 JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions_test.cpp delete mode 100644 JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers.h delete mode 100644 JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers_test.cpp delete mode 100644 JuceLibraryCode/modules/juce_core/native/juce_PlatformTimerListener.h delete mode 100644 JuceLibraryCode/modules/juce_core/native/juce_PlatformTimer_generic.cpp delete mode 100644 JuceLibraryCode/modules/juce_core/native/juce_PlatformTimer_windows.cpp rename JuceLibraryCode/modules/juce_core/native/{juce_AndroidDocument_android.cpp => juce_android_AndroidDocument.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_Files_android.cpp => juce_android_Files.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_JNIHelpers_android.cpp => juce_android_JNIHelpers.cpp} (96%) rename JuceLibraryCode/modules/juce_core/native/{juce_JNIHelpers_android.h => juce_android_JNIHelpers.h} (96%) rename JuceLibraryCode/modules/juce_core/native/{juce_Misc_android.cpp => juce_android_Misc.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_Network_android.cpp => juce_android_Network.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_RuntimePermissions_android.cpp => juce_android_RuntimePermissions.cpp} (97%) rename JuceLibraryCode/modules/juce_core/native/{juce_SystemStats_android.cpp => juce_android_SystemStats.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_Threads_android.cpp => juce_android_Threads.cpp} (99%) rename JuceLibraryCode/modules/juce_core/native/{juce_Network_curl.cpp => juce_curl_Network.cpp} (97%) rename JuceLibraryCode/modules/juce_core/native/{juce_SharedCode_intel.h => juce_intel_SharedCode.h} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_CommonFile_linux.cpp => juce_linux_CommonFile.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_Files_linux.cpp => juce_linux_Files.cpp} (98%) rename JuceLibraryCode/modules/juce_core/native/{juce_Network_linux.cpp => juce_linux_Network.cpp} (99%) rename JuceLibraryCode/modules/juce_core/native/{juce_SystemStats_linux.cpp => juce_linux_SystemStats.cpp} (98%) rename JuceLibraryCode/modules/juce_core/native/{juce_Threads_linux.cpp => juce_linux_Threads.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_CFHelpers_mac.h => juce_mac_CFHelpers.h} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_Files_mac.mm => juce_mac_Files.mm} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_Network_mac.mm => juce_mac_Network.mm} (99%) rename JuceLibraryCode/modules/juce_core/native/{juce_ObjCHelpers_mac.h => juce_mac_ObjCHelpers.h} (92%) rename JuceLibraryCode/modules/juce_core/native/{juce_Strings_mac.mm => juce_mac_Strings.mm} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_SystemStats_mac.mm => juce_mac_SystemStats.mm} (80%) rename JuceLibraryCode/modules/juce_core/native/{juce_Threads_mac.mm => juce_mac_Threads.mm} (63%) rename JuceLibraryCode/modules/juce_core/native/{juce_ThreadPriorities_native.h => juce_native_ThreadPriorities.h} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_IPAddress_posix.h => juce_posix_IPAddress.h} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_NamedPipe_posix.cpp => juce_posix_NamedPipe.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_SharedCode_posix.h => juce_posix_SharedCode.h} (98%) rename JuceLibraryCode/modules/juce_core/native/{juce_SystemStats_wasm.cpp => juce_wasm_SystemStats.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_ComSmartPtr_windows.h => juce_win32_ComSmartPtr.h} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_Files_windows.cpp => juce_win32_Files.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_Network_windows.cpp => juce_win32_Network.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_Registry_windows.cpp => juce_win32_Registry.cpp} (100%) rename JuceLibraryCode/modules/juce_core/native/{juce_SystemStats_windows.cpp => juce_win32_SystemStats.cpp} (73%) rename JuceLibraryCode/modules/juce_core/native/{juce_Threads_windows.cpp => juce_win32_Threads.cpp} (100%) rename JuceLibraryCode/modules/juce_dsp/native/{juce_SIMDNativeOps_avx.cpp => juce_avx_SIMDNativeOps.cpp} (100%) rename JuceLibraryCode/modules/juce_dsp/native/{juce_SIMDNativeOps_avx.h => juce_avx_SIMDNativeOps.h} (100%) rename JuceLibraryCode/modules/juce_dsp/native/{juce_SIMDNativeOps_fallback.h => juce_fallback_SIMDNativeOps.h} (98%) rename JuceLibraryCode/modules/juce_dsp/native/{juce_SIMDNativeOps_neon.cpp => juce_neon_SIMDNativeOps.cpp} (100%) rename JuceLibraryCode/modules/juce_dsp/native/{juce_SIMDNativeOps_neon.h => juce_neon_SIMDNativeOps.h} (100%) rename JuceLibraryCode/modules/juce_dsp/native/{juce_SIMDNativeOps_sse.cpp => juce_sse_SIMDNativeOps.cpp} (100%) rename JuceLibraryCode/modules/juce_dsp/native/{juce_SIMDNativeOps_sse.h => juce_sse_SIMDNativeOps.h} (100%) delete mode 100644 JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.cpp delete mode 100644 JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.h delete mode 100644 JuceLibraryCode/modules/juce_events/native/juce_RunningInUnity.h rename JuceLibraryCode/modules/juce_events/native/{juce_Messaging_android.cpp => juce_android_Messaging.cpp} (100%) rename JuceLibraryCode/modules/juce_events/native/{juce_MessageManager_ios.mm => juce_ios_MessageManager.mm} (100%) rename JuceLibraryCode/modules/juce_events/native/{juce_EventLoop_linux.h => juce_linux_EventLoop.h} (100%) rename JuceLibraryCode/modules/juce_events/native/{juce_EventLoopInternal_linux.h => juce_linux_EventLoopInternal.h} (100%) rename JuceLibraryCode/modules/juce_events/native/{juce_Messaging_linux.cpp => juce_linux_Messaging.cpp} (99%) rename JuceLibraryCode/modules/juce_events/native/{juce_MessageManager_mac.mm => juce_mac_MessageManager.mm} (58%) rename JuceLibraryCode/modules/juce_events/native/{juce_MessageQueue_mac.h => juce_osx_MessageQueue.h} (100%) rename JuceLibraryCode/modules/juce_events/native/{juce_HiddenMessageWindow_windows.h => juce_win32_HiddenMessageWindow.h} (94%) rename JuceLibraryCode/modules/juce_events/native/{juce_Messaging_windows.cpp => juce_win32_Messaging.cpp} (94%) rename JuceLibraryCode/modules/juce_events/native/{juce_WinRTWrapper_windows.cpp => juce_win32_WinRTWrapper.cpp} (100%) rename JuceLibraryCode/modules/juce_events/native/{juce_WinRTWrapper_windows.h => juce_win32_WinRTWrapper.h} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_Fonts_android.cpp => juce_android_Fonts.cpp} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_GraphicsContext_android.cpp => juce_android_GraphicsContext.cpp} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_IconHelpers_android.cpp => juce_android_IconHelpers.cpp} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_Fonts_freetype.cpp => juce_freetype_Fonts.cpp} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_Fonts_linux.cpp => juce_linux_Fonts.cpp} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_IconHelpers_linux.cpp => juce_linux_IconHelpers.cpp} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_CoreGraphicsContext_mac.h => juce_mac_CoreGraphicsContext.h} (97%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_CoreGraphicsContext_mac.mm => juce_mac_CoreGraphicsContext.mm} (89%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_CoreGraphicsHelpers_mac.h => juce_mac_CoreGraphicsHelpers.h} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_Fonts_mac.mm => juce_mac_Fonts.mm} (99%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_IconHelpers_mac.cpp => juce_mac_IconHelpers.cpp} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_Direct2DGraphicsContext_windows.cpp => juce_win32_Direct2DGraphicsContext.cpp} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_Direct2DGraphicsContext_windows.h => juce_win32_Direct2DGraphicsContext.h} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_DirectWriteTypeLayout_windows.cpp => juce_win32_DirectWriteTypeLayout.cpp} (99%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_DirectWriteTypeface_windows.cpp => juce_win32_DirectWriteTypeface.cpp} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_Fonts_windows.cpp => juce_win32_Fonts.cpp} (100%) rename JuceLibraryCode/modules/juce_graphics/native/{juce_IconHelpers_windows.cpp => juce_win32_IconHelpers.cpp} (100%) delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_AccessibilityHelpers.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_AccessibilityHelpers.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_AlertWindowHelpers.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_ButtonAccessibilityHandler.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_ComponentHelpers.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_CustomMouseCursorInfo.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_FocusHelpers.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_FocusRestorer.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_LookAndFeelHelpers.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_MouseInputSourceImpl.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_MouseInputSourceList.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScalingHelpers.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedContentSharerImpl.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_ViewportHelpers.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/detail/juce_WindowingHelpers.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/layout/juce_BorderedComponentBoundsConstrainer.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/layout/juce_BorderedComponentBoundsConstrainer.h rename JuceLibraryCode/modules/juce_gui_basics/{detail => mouse}/juce_PointerState.h (98%) delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/accessibility/juce_Accessibility.cpp rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_Accessibility_android.cpp => juce_android_Accessibility.cpp} (94%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_Accessibility_ios.mm => juce_ios_Accessibility.mm} (95%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_Accessibility_mac.mm => juce_mac_Accessibility.mm} (97%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_AccessibilitySharedCode_mac.mm => juce_mac_AccessibilitySharedCode.mm} (93%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_Accessibility_windows.cpp => juce_win32_Accessibility.cpp} (91%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_AccessibilityElement_windows.cpp => juce_win32_AccessibilityElement.cpp} (94%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_AccessibilityElement_windows.h => juce_win32_AccessibilityElement.h} (93%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_ComInterfaces_windows.h => juce_win32_ComInterfaces.h} (97%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIAExpandCollapseProvider_windows.h => juce_win32_UIAExpandCollapseProvider.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIAGridItemProvider_windows.h => juce_win32_UIAGridItemProvider.h} (94%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIAGridProvider_windows.h => juce_win32_UIAGridProvider.h} (94%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIAHelpers_windows.h => juce_win32_UIAHelpers.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIAInvokeProvider_windows.h => juce_win32_UIAInvokeProvider.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIAProviderBase_windows.h => juce_win32_UIAProviderBase.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIAProviders_windows.h => juce_win32_UIAProviders.h} (65%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIARangeValueProvider_windows.h => juce_win32_UIARangeValueProvider.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIASelectionProvider_windows.h => juce_win32_UIASelectionProvider.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIATextProvider_windows.h => juce_win32_UIATextProvider.h} (99%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIAToggleProvider_windows.h => juce_win32_UIAToggleProvider.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIATransformProvider_windows.h => juce_win32_UIATransformProvider.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIAValueProvider_windows.h => juce_win32_UIAValueProvider.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_UIAWindowProvider_windows.h => juce_win32_UIAWindowProvider.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/accessibility/{juce_WindowsUIAWrapper_windows.h => juce_win32_WindowsUIAWrapper.h} (100%) delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/javaopt/app/com/rmsl/juce/Receiver.java delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_ContentSharer_ios.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_NativeMessageBox_android.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_NativeMessageBox_ios.mm delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_NativeMessageBox_linux.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_NativeMessageBox_mac.mm delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_NativeMessageBox_windows.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_NativeModalWrapperComponent_ios.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_ScopedDPIAwarenessDisabler.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_WindowUtils_android.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_WindowUtils_ios.mm delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_WindowUtils_linux.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_WindowUtils_mac.mm delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_WindowUtils_windows.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_WindowsHooks_windows.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_WindowsHooks_windows.h rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_ContentSharer_android.cpp => juce_android_ContentSharer.cpp} (53%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_FileChooser_android.cpp => juce_android_FileChooser.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_Windowing_android.cpp => juce_android_Windowing.cpp} (55%) create mode 100644 JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_ContentSharer.cpp rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_FileChooser_ios.mm => juce_ios_FileChooser.mm} (77%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_UIViewComponentPeer_ios.mm => juce_ios_UIViewComponentPeer.mm} (93%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_Windowing_ios.mm => juce_ios_Windowing.mm} (77%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_FileChooser_linux.cpp => juce_linux_FileChooser.cpp} (91%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_Windowing_linux.cpp => juce_linux_Windowing.cpp} (82%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_CGMetalLayerRenderer_mac.h => juce_mac_CGMetalLayerRenderer.h} (86%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_FileChooser_mac.mm => juce_mac_FileChooser.mm} (92%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_MainMenu_mac.mm => juce_mac_MainMenu.mm} (98%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_MouseCursor_mac.mm => juce_mac_MouseCursor.mm} (96%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_NSViewComponentPeer_mac.mm => juce_mac_NSViewComponentPeer.mm} (90%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_PerScreenDisplayLinks_mac.h => juce_mac_PerScreenDisplayLinks.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_Windowing_mac.mm => juce_mac_Windowing.mm} (63%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_DragAndDrop_windows.cpp => juce_win32_DragAndDrop.cpp} (99%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_FileChooser_windows.cpp => juce_win32_FileChooser.cpp} (90%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_ScopedThreadDPIAwarenessSetter_windows.h => juce_win32_ScopedThreadDPIAwarenessSetter.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_Windowing_windows.cpp => juce_win32_Windowing.cpp} (92%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_ScopedWindowAssociation_linux.h => x11/juce_linux_ScopedWindowAssociation.h} (100%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_DragAndDrop_linux.cpp => x11/juce_linux_X11_DragAndDrop.cpp} (98%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_XSymbols_linux.cpp => x11/juce_linux_X11_Symbols.cpp} (99%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_XSymbols_linux.h => x11/juce_linux_X11_Symbols.h} (99%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_XWindowSystem_linux.cpp => x11/juce_linux_XWindowSystem.cpp} (97%) rename JuceLibraryCode/modules/juce_gui_basics/native/{juce_XWindowSystem_linux.h => x11/juce_linux_XWindowSystem.h} (98%) delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/windows/juce_MessageBoxOptions.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/windows/juce_NativeMessageBox.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/windows/juce_ScopedMessageBox.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/windows/juce_ScopedMessageBox.h rename JuceLibraryCode/modules/juce_gui_basics/windows/{juce_VBlankAttachment.cpp => juce_VBlankAttachement.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_basics/windows/{juce_VBlankAttachment.h => juce_VBlankAttachement.h} (93%) delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/windows/juce_WindowUtils.h rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_PushNotifications_android.cpp => juce_android_PushNotifications.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_WebBrowserComponent_android.cpp => juce_android_WebBrowserComponent.cpp} (98%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_PushNotifications_ios.cpp => juce_ios_PushNotifications.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_UIViewComponent_ios.mm => juce_ios_UIViewComponent.mm} (90%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_SystemTrayIcon_linux.cpp => juce_linux_X11_SystemTrayIcon.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_WebBrowserComponent_linux.cpp => juce_linux_X11_WebBrowserComponent.cpp} (91%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_XEmbedComponent_linux.cpp => juce_linux_XEmbedComponent.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_AppleRemote_mac.mm => juce_mac_AppleRemote.mm} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_NSViewComponent_mac.mm => juce_mac_NSViewComponent.mm} (83%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_NSViewFrameWatcher_mac.h => juce_mac_NSViewFrameWatcher.h} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_PushNotifications_mac.cpp => juce_mac_PushNotifications.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_SystemTrayIcon_mac.cpp => juce_mac_SystemTrayIcon.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_WebBrowserComponent_mac.mm => juce_mac_WebBrowserComponent.mm} (99%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_ActiveXComponent_windows.cpp => juce_win32_ActiveXComponent.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_HWNDComponent_windows.cpp => juce_win32_HWNDComponent.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_SystemTrayIcon_windows.cpp => juce_win32_SystemTrayIcon.cpp} (100%) rename JuceLibraryCode/modules/juce_gui_extra/native/{juce_WebBrowserComponent_windows.cpp => juce_win32_WebBrowserComponent.cpp} (78%) rename JuceLibraryCode/modules/juce_opengl/native/{juce_OpenGL_linux.h => juce_OpenGL_linux_X11.h} (78%) rename JuceLibraryCode/modules/juce_opengl/native/{juce_OpenGL_mac.h => juce_OpenGL_osx.h} (87%) rename JuceLibraryCode/modules/juce_opengl/native/{juce_OpenGL_windows.h => juce_OpenGL_win32.h} (100%) rename JuceLibraryCode/modules/juce_video/native/{juce_CameraDevice_android.h => juce_android_CameraDevice.h} (100%) rename JuceLibraryCode/modules/juce_video/native/{juce_Video_android.h => juce_android_Video.h} (99%) rename JuceLibraryCode/modules/juce_video/native/{juce_CameraDevice_ios.h => juce_ios_CameraDevice.h} (99%) rename JuceLibraryCode/modules/juce_video/native/{juce_CameraDevice_mac.h => juce_mac_CameraDevice.h} (92%) rename JuceLibraryCode/modules/juce_video/native/{juce_Video_mac.h => juce_mac_Video.h} (98%) rename JuceLibraryCode/modules/juce_video/native/{juce_CameraDevice_windows.h => juce_win32_CameraDevice.h} (100%) rename JuceLibraryCode/modules/juce_video/native/{juce_ComTypes_windows.h => juce_win32_ComTypes.h} (100%) rename JuceLibraryCode/modules/juce_video/native/{juce_Video_windows.h => juce_win32_Video.h} (100%) diff --git a/Builds/LinuxMakefile/Makefile b/Builds/LinuxMakefile/Makefile index 83d9920e..fbdc6434 100644 --- a/Builds/LinuxMakefile/Makefile +++ b/Builds/LinuxMakefile/Makefile @@ -39,7 +39,7 @@ ifeq ($(CONFIG),Debug) TARGET_ARCH := endif - JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DDEBUG=1" "-D_DEBUG=1" "-DPIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==" "-DSAMPLER_SKIP_UI" "-DJUCE_MODAL_LOOPS_PERMITTED" "-DHAVE_LIBSAMPLERATE" "-DUSE_BUILTIN_FFT" "-DUSE_PTHREADS" "-DBUILD_DAWDREAMER_FAUST" "-DBUILD_DAWDREAMER_RUBBERBAND" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=0.7.2" "-DJUCE_APP_VERSION_HEX=0x702" $(shell $(PKG_CONFIG) --cflags alsa freetype2 gl) -pthread -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK -I../../thirdparty/JUCE/modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../JuceLibraryCode/modules -I../../thirdparty -I../../thirdparty/pybind11/include -I../../thirdparty/faust/architecture -I../../thirdparty/faust/compiler -I../../thirdparty/faust/compiler/boxes -I../../thirdparty/faust/compiler/documentator -I../../thirdparty/faust/compiler/draw -I../../thirdparty/faust/compiler/draw/device -I../../thirdparty/faust/compiler/draw/schema -I../../thirdparty/faust/compiler/errors -I../../thirdparty/faust/compiler/evaluate -I../../thirdparty/faust/compiler/extended -I../../thirdparty/faust/compiler/generator -I../../thirdparty/faust/compiler/generator/interpreter -I../../thirdparty/faust/compiler/normalize -I../../thirdparty/faust/compiler/parallelize -I../../thirdparty/faust/compiler/parser -I../../thirdparty/faust/compiler/patternmatcher -I../../thirdparty/faust/compiler/propagate -I../../thirdparty/faust/compiler/signals -I../../thirdparty/faust/compiler/tlib -I../../thirdparty/faust/compiler/transform -I../../thirdparty/faust/compiler/utils -I../../thirdparty/libsamplerate/src -I../../thirdparty/libsamplerate/include $(CPPFLAGS) + JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DDEBUG=1" "-D_DEBUG=1" "-DPIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==" "-DSAMPLER_SKIP_UI" "-DJUCE_MODAL_LOOPS_PERMITTED" "-DHAVE_LIBSAMPLERATE" "-DUSE_BUILTIN_FFT" "-DUSE_PTHREADS" "-DBUILD_DAWDREAMER_FAUST" "-DBUILD_DAWDREAMER_RUBBERBAND" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=0.7.2" "-DJUCE_APP_VERSION_HEX=0x702" $(shell $(PKG_CONFIG) --cflags alsa freetype2) -pthread -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK -I../../thirdparty/JUCE/modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../JuceLibraryCode/modules -I../../thirdparty -I../../thirdparty/pybind11/include -I../../thirdparty/faust/architecture -I../../thirdparty/faust/compiler -I../../thirdparty/faust/compiler/boxes -I../../thirdparty/faust/compiler/documentator -I../../thirdparty/faust/compiler/draw -I../../thirdparty/faust/compiler/draw/device -I../../thirdparty/faust/compiler/draw/schema -I../../thirdparty/faust/compiler/errors -I../../thirdparty/faust/compiler/evaluate -I../../thirdparty/faust/compiler/extended -I../../thirdparty/faust/compiler/generator -I../../thirdparty/faust/compiler/generator/interpreter -I../../thirdparty/faust/compiler/normalize -I../../thirdparty/faust/compiler/parallelize -I../../thirdparty/faust/compiler/parser -I../../thirdparty/faust/compiler/patternmatcher -I../../thirdparty/faust/compiler/propagate -I../../thirdparty/faust/compiler/signals -I../../thirdparty/faust/compiler/tlib -I../../thirdparty/faust/compiler/transform -I../../thirdparty/faust/compiler/utils -I../../thirdparty/libsamplerate/src -I../../thirdparty/libsamplerate/include $(CPPFLAGS) JUCE_CPPFLAGS_DYNAMIC_LIBRARY := "-DJucePlugin_Build_VST=0" "-DJucePlugin_Build_VST3=0" "-DJucePlugin_Build_AU=0" "-DJucePlugin_Build_AUv3=0" "-DJucePlugin_Build_AAX=0" "-DJucePlugin_Build_Standalone=0" "-DJucePlugin_Build_Unity=0" "-DJucePlugin_Build_LV2=0" JUCE_CFLAGS_DYNAMIC_LIBRARY := -fPIC -fvisibility=hidden JUCE_LDFLAGS_DYNAMIC_LIBRARY := -shared @@ -47,7 +47,7 @@ ifeq ($(CONFIG),Debug) JUCE_CFLAGS += $(JUCE_CPPFLAGS) $(TARGET_ARCH) -fPIC -g -ggdb -O0 -fPIC $(CFLAGS) JUCE_CXXFLAGS += $(JUCE_CFLAGS) -std=c++17 $(CXXFLAGS) - JUCE_LDFLAGS += $(TARGET_ARCH) -L$(JUCE_BINDIR) -L$(JUCE_LIBDIR) -L/usr/local/lib -L../../thirdparty/libfaust/ubuntu-x86_64/Debug/lib -L../../thirdparty/libsamplerate/build_release/src $(shell $(PKG_CONFIG) --libs alsa freetype2 gl) -fvisibility=hidden -lrt -ldl -lpthread -lsamplerate -lfaustwithllvm $(LDFLAGS) + JUCE_LDFLAGS += $(TARGET_ARCH) -L$(JUCE_BINDIR) -L$(JUCE_LIBDIR) -L/usr/local/lib -L../../thirdparty/libfaust/ubuntu-x86_64/Debug/lib -L../../thirdparty/libsamplerate/build_release/src $(shell $(PKG_CONFIG) --libs alsa freetype2) -fvisibility=hidden -lrt -ldl -lpthread -lGL -lsamplerate -lfaustwithllvm $(LDFLAGS) CLEANCMD = rm -rf $(JUCE_OUTDIR)/$(TARGET) $(JUCE_OBJDIR) endif @@ -62,7 +62,7 @@ ifeq ($(CONFIG),Release) TARGET_ARCH := endif - JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DNDEBUG=1" "-DPIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==" "-DSAMPLER_SKIP_UI" "-DJUCE_MODAL_LOOPS_PERMITTED" "-DHAVE_LIBSAMPLERATE" "-DUSE_BUILTIN_FFT" "-DUSE_PTHREADS" "-DBUILD_DAWDREAMER_FAUST" "-DBUILD_DAWDREAMER_RUBBERBAND" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=0.7.2" "-DJUCE_APP_VERSION_HEX=0x702" $(shell $(PKG_CONFIG) --cflags alsa freetype2 gl) -pthread -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK -I../../thirdparty/JUCE/modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../JuceLibraryCode/modules -I../../thirdparty -I../../thirdparty/pybind11/include -I../../thirdparty/faust/architecture -I../../thirdparty/faust/compiler -I../../thirdparty/faust/compiler/boxes -I../../thirdparty/faust/compiler/documentator -I../../thirdparty/faust/compiler/draw -I../../thirdparty/faust/compiler/draw/device -I../../thirdparty/faust/compiler/draw/schema -I../../thirdparty/faust/compiler/errors -I../../thirdparty/faust/compiler/evaluate -I../../thirdparty/faust/compiler/extended -I../../thirdparty/faust/compiler/generator -I../../thirdparty/faust/compiler/generator/interpreter -I../../thirdparty/faust/compiler/normalize -I../../thirdparty/faust/compiler/parallelize -I../../thirdparty/faust/compiler/parser -I../../thirdparty/faust/compiler/patternmatcher -I../../thirdparty/faust/compiler/propagate -I../../thirdparty/faust/compiler/signals -I../../thirdparty/faust/compiler/tlib -I../../thirdparty/faust/compiler/transform -I../../thirdparty/faust/compiler/utils -I../../thirdparty/libsamplerate/src -I../../thirdparty/libsamplerate/include $(CPPFLAGS) + JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DNDEBUG=1" "-DPIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==" "-DSAMPLER_SKIP_UI" "-DJUCE_MODAL_LOOPS_PERMITTED" "-DHAVE_LIBSAMPLERATE" "-DUSE_BUILTIN_FFT" "-DUSE_PTHREADS" "-DBUILD_DAWDREAMER_FAUST" "-DBUILD_DAWDREAMER_RUBBERBAND" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=0.7.2" "-DJUCE_APP_VERSION_HEX=0x702" $(shell $(PKG_CONFIG) --cflags alsa freetype2) -pthread -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK -I../../thirdparty/JUCE/modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../JuceLibraryCode/modules -I../../thirdparty -I../../thirdparty/pybind11/include -I../../thirdparty/faust/architecture -I../../thirdparty/faust/compiler -I../../thirdparty/faust/compiler/boxes -I../../thirdparty/faust/compiler/documentator -I../../thirdparty/faust/compiler/draw -I../../thirdparty/faust/compiler/draw/device -I../../thirdparty/faust/compiler/draw/schema -I../../thirdparty/faust/compiler/errors -I../../thirdparty/faust/compiler/evaluate -I../../thirdparty/faust/compiler/extended -I../../thirdparty/faust/compiler/generator -I../../thirdparty/faust/compiler/generator/interpreter -I../../thirdparty/faust/compiler/normalize -I../../thirdparty/faust/compiler/parallelize -I../../thirdparty/faust/compiler/parser -I../../thirdparty/faust/compiler/patternmatcher -I../../thirdparty/faust/compiler/propagate -I../../thirdparty/faust/compiler/signals -I../../thirdparty/faust/compiler/tlib -I../../thirdparty/faust/compiler/transform -I../../thirdparty/faust/compiler/utils -I../../thirdparty/libsamplerate/src -I../../thirdparty/libsamplerate/include $(CPPFLAGS) JUCE_CPPFLAGS_DYNAMIC_LIBRARY := "-DJucePlugin_Build_VST=0" "-DJucePlugin_Build_VST3=0" "-DJucePlugin_Build_AU=0" "-DJucePlugin_Build_AUv3=0" "-DJucePlugin_Build_AAX=0" "-DJucePlugin_Build_Standalone=0" "-DJucePlugin_Build_Unity=0" "-DJucePlugin_Build_LV2=0" JUCE_CFLAGS_DYNAMIC_LIBRARY := -fPIC -fvisibility=hidden JUCE_LDFLAGS_DYNAMIC_LIBRARY := -shared @@ -70,7 +70,7 @@ ifeq ($(CONFIG),Release) JUCE_CFLAGS += $(JUCE_CPPFLAGS) $(TARGET_ARCH) -fPIC -O3 -fPIC $(CFLAGS) JUCE_CXXFLAGS += $(JUCE_CFLAGS) -std=c++17 $(CXXFLAGS) - JUCE_LDFLAGS += $(TARGET_ARCH) -L$(JUCE_BINDIR) -L$(JUCE_LIBDIR) -L/usr/local/lib -L../../thirdparty/libfaust/ubuntu-x86_64/Release/lib -L../../thirdparty/libsamplerate/build_release/src $(shell $(PKG_CONFIG) --libs alsa freetype2 gl) -fvisibility=hidden -lrt -ldl -lpthread -lsamplerate -lfaustwithllvm $(LDFLAGS) + JUCE_LDFLAGS += $(TARGET_ARCH) -L$(JUCE_BINDIR) -L$(JUCE_LIBDIR) -L/usr/local/lib -L../../thirdparty/libfaust/ubuntu-x86_64/Release/lib -L../../thirdparty/libsamplerate/build_release/src $(shell $(PKG_CONFIG) --libs alsa freetype2) -fvisibility=hidden -lrt -ldl -lpthread -lGL -lsamplerate -lfaustwithllvm $(LDFLAGS) CLEANCMD = rm -rf $(JUCE_OUTDIR)/$(TARGET) $(JUCE_OBJDIR) endif @@ -112,175 +112,170 @@ OBJECTS_DYNAMIC_LIBRARY := \ all : $(JUCE_OUTDIR)/$(JUCE_TARGET_DYNAMIC_LIBRARY) -$(JUCE_OUTDIR)/$(JUCE_TARGET_DYNAMIC_LIBRARY) : $(OBJECTS_DYNAMIC_LIBRARY) $(JUCE_OBJDIR)/execinfo.cmd $(RESOURCES) +$(JUCE_OUTDIR)/$(JUCE_TARGET_DYNAMIC_LIBRARY) : $(OBJECTS_DYNAMIC_LIBRARY) $(RESOURCES) @command -v $(PKG_CONFIG) >/dev/null 2>&1 || { echo >&2 "pkg-config not installed. Please, install it."; exit 1; } - @$(PKG_CONFIG) --print-errors alsa freetype2 gl + @$(PKG_CONFIG) --print-errors alsa freetype2 @echo Linking "DawDreamer - Dynamic Library" -$(V_AT)mkdir -p $(JUCE_BINDIR) -$(V_AT)mkdir -p $(JUCE_LIBDIR) -$(V_AT)mkdir -p $(JUCE_OUTDIR) - $(V_AT)$(CXX) -o $(JUCE_OUTDIR)/$(JUCE_TARGET_DYNAMIC_LIBRARY) $(OBJECTS_DYNAMIC_LIBRARY) $(JUCE_LDFLAGS) $(shell cat $(JUCE_OBJDIR)/execinfo.cmd) $(JUCE_LDFLAGS_DYNAMIC_LIBRARY) $(RESOURCES) $(TARGET_ARCH) + $(V_AT)$(CXX) -o $(JUCE_OUTDIR)/$(JUCE_TARGET_DYNAMIC_LIBRARY) $(OBJECTS_DYNAMIC_LIBRARY) $(JUCE_LDFLAGS) $(JUCE_LDFLAGS_DYNAMIC_LIBRARY) $(RESOURCES) $(TARGET_ARCH) $(JUCE_OBJDIR)/DataModel_e101b2.o: ../../Source/Sampler/Source/DataModels/DataModel.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling DataModel.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/MPESettingsDataModel_aeb1f3d1.o: ../../Source/Sampler/Source/DataModels/MPESettingsDataModel.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling MPESettingsDataModel.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/SamplerAudioProcessor_298eea40.o: ../../Source/Sampler/Source/SamplerAudioProcessor.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling SamplerAudioProcessor.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/SamplerAudioProcessorEditor_39db550d.o: ../../Source/Sampler/Source/SamplerAudioProcessorEditor.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling SamplerAudioProcessorEditor.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/FaustProcessor_2056a3e0.o: ../../Source/FaustProcessor.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling FaustProcessor.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/FaustBoxAPI_5f675c8b.o: ../../Source/FaustBoxAPI.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling FaustBoxAPI.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/FaustSignalAPI_ca49a5a0.o: ../../Source/FaustSignalAPI.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling FaustSignalAPI.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/FilterProcessor_8755f5b.o: ../../Source/FilterProcessor.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling FilterProcessor.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/PlaybackWarpProcessor_eedba470.o: ../../Source/PlaybackWarpProcessor.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling PlaybackWarpProcessor.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/PluginProcessor_a059e380.o: ../../Source/PluginProcessor.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling PluginProcessor.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/ProcessorBase_65bf9464.o: ../../Source/ProcessorBase.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling ProcessorBase.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/CustomParameters_5970e144.o: ../../Source/CustomParameters.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling CustomParameters.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/custom_pybind_wrappers_ac32b5b4.o: ../../Source/custom_pybind_wrappers.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling custom_pybind_wrappers.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/RenderEngine_d1c4d401.o: ../../Source/RenderEngine.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling RenderEngine.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/source_704c2604.o: ../../Source/source.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling source.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_audio_basics_8a4e984a.o: ../../JuceLibraryCode/include_juce_audio_basics.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_audio_basics.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_audio_devices_63111d02.o: ../../JuceLibraryCode/include_juce_audio_devices.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_audio_devices.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_audio_formats_15f82001.o: ../../JuceLibraryCode/include_juce_audio_formats.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_audio_formats.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_audio_processors_10c03666.o: ../../JuceLibraryCode/include_juce_audio_processors.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_audio_processors.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_audio_processors_ara_2a4c6ef7.o: ../../JuceLibraryCode/include_juce_audio_processors_ara.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_audio_processors_ara.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_audio_processors_lv2_libs_12bdca08.o: ../../JuceLibraryCode/include_juce_audio_processors_lv2_libs.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_audio_processors_lv2_libs.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_core_f26d17db.o: ../../JuceLibraryCode/include_juce_core.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_core.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_cryptography_8cb807a8.o: ../../JuceLibraryCode/include_juce_cryptography.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_cryptography.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_data_structures_7471b1e3.o: ../../JuceLibraryCode/include_juce_data_structures.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_data_structures.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_dsp_aeb2060f.o: ../../JuceLibraryCode/include_juce_dsp.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_dsp.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_events_fd7d695.o: ../../JuceLibraryCode/include_juce_events.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_events.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_graphics_f817e147.o: ../../JuceLibraryCode/include_juce_graphics.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_graphics.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_gui_basics_e3f79785.o: ../../JuceLibraryCode/include_juce_gui_basics.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_gui_basics.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_gui_extra_6dee1c1a.o: ../../JuceLibraryCode/include_juce_gui_extra.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_gui_extra.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_opengl_a8a032b.o: ../../JuceLibraryCode/include_juce_opengl.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_opengl.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" $(JUCE_OBJDIR)/include_juce_video_be78589.o: ../../JuceLibraryCode/include_juce_video.cpp - -$(V_AT)mkdir -p $(@D) + -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling include_juce_video.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_DYNAMIC_LIBRARY) $(JUCE_CFLAGS_DYNAMIC_LIBRARY) -o "$@" -c "$<" -$(JUCE_OBJDIR)/execinfo.cmd: - -$(V_AT)mkdir -p $(@D) - -@if [ -z "$(V_AT)" ]; then echo "Checking if we need to link libexecinfo"; fi - $(V_AT)printf "int main() { return 0; }" | $(CXX) -x c++ -o $(@D)/execinfo.x -lexecinfo - >/dev/null 2>&1 && printf -- "-lexecinfo" > "$@" || touch "$@" - clean: @echo Cleaning DawDreamer $(V_AT)$(CLEANCMD) diff --git a/Builds/MacOSX/DawDreamer.xcodeproj/project.pbxproj b/Builds/MacOSX/DawDreamer.xcodeproj/project.pbxproj index 53232763..a5bbe8a0 100644 --- a/Builds/MacOSX/DawDreamer.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/DawDreamer.xcodeproj/project.pbxproj @@ -48,7 +48,6 @@ C9200467702F9E5502DCC9F0 /* include_juce_video.mm */ = {isa = PBXBuildFile; fileRef = C519012CDDE2CA92FF58903A; }; D8E2F775028097109496AAA1 /* Cocoa.framework */ = {isa = PBXBuildFile; fileRef = 7EC708BB8670BC4B971F031E; }; D915E61C72D98F0B97F31690 /* StandalonePluginWindow.h */ = {isa = PBXBuildFile; fileRef = 44A26898F037A3E6B43EB911; }; - DF5374E67F8812D79694279E /* Security.framework */ = {isa = PBXBuildFile; fileRef = B243F8F5ADFF280683E55EE1; }; DFE780784ED99EACC1B654A6 /* include_juce_gui_extra.mm */ = {isa = PBXBuildFile; fileRef = 06AE4EC72C9D2D0775EF879E; }; E358C00D8D92D35AFBC9944C /* FaustProcessor.cpp */ = {isa = PBXBuildFile; fileRef = BFD142EBB8AFBADC8AB04A8A; }; E3F94999130BC1FFAB7F9829 /* FaustSignalAPI.cpp */ = {isa = PBXBuildFile; fileRef = DA5D0A9D3542DCB5ACFC8FB8; }; @@ -142,7 +141,6 @@ AB39C0017B2EE44B9E510D89 /* PlaybackWarpProcessor.cpp */ /* PlaybackWarpProcessor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PlaybackWarpProcessor.cpp; path = ../../Source/PlaybackWarpProcessor.cpp; sourceTree = SOURCE_ROOT; }; AD248A6B37034D215326CB7F /* juce_audio_processors */ /* juce_audio_processors */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_processors; path = ../../JuceLibraryCode/modules/juce_audio_processors; sourceTree = SOURCE_ROOT; }; B1FAEBBC0B8073BAE84C5665 /* PlaybackProcessor.h */ /* PlaybackProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PlaybackProcessor.h; path = ../../Source/PlaybackProcessor.h; sourceTree = SOURCE_ROOT; }; - B243F8F5ADFF280683E55EE1 /* Security.framework */ /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; B2CCBF7C3614EE8B865D41C0 /* include_juce_graphics.mm */ /* include_juce_graphics.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_graphics.mm; path = ../../JuceLibraryCode/include_juce_graphics.mm; sourceTree = SOURCE_ROOT; }; B9149690533DD3D5D70543AA /* WaveformEditor.h */ /* WaveformEditor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WaveformEditor.h; path = ../../Source/Sampler/Source/Components/WaveformEditor.h; sourceTree = SOURCE_ROOT; }; BFD142EBB8AFBADC8AB04A8A /* FaustProcessor.cpp */ /* FaustProcessor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FaustProcessor.cpp; path = ../../Source/FaustProcessor.cpp; sourceTree = SOURCE_ROOT; }; @@ -196,7 +194,6 @@ C0D03D32EA25F7011FECF9F6, 29782FB7B1C181EA8900F409, 8CB86F722980F4262C64B7D7, - DF5374E67F8812D79694279E, 31D395DB17463EFA64FFF6EE, ); runOnlyForDeploymentPostprocessing = 0; @@ -317,7 +314,6 @@ E0756912D4AE6F8AF5A16B05, 1B24D4A69BF5E24D9E0DB1E5, D2350C56CE13419F5D6CF40E, - B243F8F5ADFF280683E55EE1, 7FC28740B3DC997941439C94, ); name = Frameworks; diff --git a/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj b/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj index a5db7124..e3bc4b1a 100644 --- a/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj +++ b/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj @@ -486,46 +486,46 @@ true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true @@ -870,42 +870,18 @@ true - - true - true true - - true - true - - true - - - true - - - true - true - - true - - - true - - - true - true @@ -1005,9 +981,6 @@ true - - true - true @@ -1038,7 +1011,7 @@ true - + true @@ -1134,9 +1107,6 @@ true - - true - true @@ -1149,9 +1119,6 @@ true - - true - true @@ -1161,73 +1128,67 @@ true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - - true - - - true - - + true @@ -1479,13 +1440,13 @@ true - + true - + true - + true @@ -1554,9 +1515,6 @@ true - - true - true @@ -1581,19 +1539,19 @@ true - + true - + true - + true - + true - + true @@ -1872,40 +1830,40 @@ true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true @@ -1977,9 +1935,6 @@ true - - true - true @@ -2052,9 +2007,6 @@ true - - true - true @@ -2178,79 +2130,52 @@ true - - true - - - true - - - true - - - true - true - - true - - - true - - - true - - - true - - - true - - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true @@ -2352,18 +2277,9 @@ true - - true - - - true - true - - true - true @@ -2373,7 +2289,7 @@ true - + true @@ -2430,43 +2346,43 @@ true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true - + true @@ -2623,8 +2539,8 @@ - - + + @@ -2719,8 +2635,8 @@ - - + + @@ -2897,7 +2813,6 @@ - @@ -2917,7 +2832,6 @@ - @@ -2954,18 +2868,8 @@ - - - - - - - - - - @@ -3006,7 +2910,6 @@ - @@ -3019,8 +2922,8 @@ - + @@ -3039,7 +2942,6 @@ - @@ -3082,22 +2984,20 @@ - + - - - - - - - - - + + + + + + + @@ -3205,10 +3105,10 @@ - - - - + + + + @@ -3242,7 +3142,6 @@ - @@ -3256,13 +3155,12 @@ - - - - - + + + - + + @@ -3319,10 +3217,10 @@ - - - + + + @@ -3358,26 +3256,6 @@ - - - - - - - - - - - - - - - - - - - - @@ -3408,7 +3286,6 @@ - @@ -3454,37 +3331,36 @@ + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + @@ -3522,12 +3398,10 @@ - - - + @@ -3555,7 +3429,7 @@ - + @@ -3563,9 +3437,9 @@ - - - + + + @@ -3583,14 +3457,14 @@ - - - - - - - - + + + + + + + + @@ -3607,7 +3481,6 @@ - diff --git a/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj.filters b/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj.filters index d188c42b..f98ece44 100644 --- a/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj.filters +++ b/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj.filters @@ -305,30 +305,12 @@ {DAF30656-5915-0E45-C4E4-54439617D525} - - {600076D4-829D-CE7A-272C-832A4BBC40AB} - - - {C02D05C7-CD20-9901-2F02-95A9BD7FA797} - - - {47771136-6D29-90C7-2C6E-1728E7D1C485} - - - {3E938566-9812-78C0-9E81-75858F44C51F} - {9266EA90-6A0A-5DDB-9CB7-966BEF03BA5C} {9C713CBA-A9E2-5F4E-F83C-2CAB8533913C} - - {D5B5DC1F-B81B-0449-5E26-15D1367B0C8C} - - - {2741675A-628F-4473-FF8D-45CD2C214CDA} - {63571A07-9AA3-5BB0-1103-0B42A2E6BC9E} @@ -542,9 +524,6 @@ {B331BC33-9770-3DB5-73F2-BC2469ECCF7F} - - {3B09E947-B78C-1758-E072-7FD67F8DCB00} - {46A17AC9-0BFF-B5CE-26D6-B9D1992C88AC} @@ -572,6 +551,9 @@ {C0E5DD5D-F8F1-DD25-67D7-291946AB3828} + + {FE7E6CD5-C7A0-DB20-4E7E-D6E7F08C4578} + {895C2D33-E08D-B1BA-BB36-FC4CA65090C8} @@ -1006,49 +988,49 @@ JUCE Modules\juce_audio_devices\native\oboe\src\opensles - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native @@ -1399,45 +1381,18 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\pluginterfaces\base - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\samples\vst-utilities\moduleinfotool\source - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\common JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\common - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\common - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\moduleinfo - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\moduleinfo - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -1540,9 +1495,6 @@ JUCE Modules\juce_audio_processors\utilities\ARA - - JUCE Modules\juce_audio_processors\utilities - JUCE Modules\juce_audio_processors\utilities @@ -1573,7 +1525,7 @@ JUCE Modules\juce_audio_processors\utilities - + JUCE Modules\juce_audio_processors\utilities @@ -1672,9 +1624,6 @@ JUCE Modules\juce_core\maths - - JUCE Modules\juce_core\maths - JUCE Modules\juce_core\maths @@ -1687,9 +1636,6 @@ JUCE Modules\juce_core\misc - - JUCE Modules\juce_core\misc - JUCE Modules\juce_core\misc @@ -1699,88 +1645,82 @@ JUCE Modules\juce_core\misc - - JUCE Modules\juce_core\native - - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - - JUCE Modules\juce_core\native - - + JUCE Modules\juce_core\native @@ -2041,13 +1981,13 @@ JUCE Modules\juce_dsp\maths - + JUCE Modules\juce_dsp\native - + JUCE Modules\juce_dsp\native - + JUCE Modules\juce_dsp\native @@ -2119,9 +2059,6 @@ JUCE Modules\juce_events\broadcasters - - JUCE Modules\juce_events\broadcasters - JUCE Modules\juce_events\interprocess @@ -2146,25 +2083,25 @@ JUCE Modules\juce_events\messages - + JUCE Modules\juce_events\native - + JUCE Modules\juce_events\native - + JUCE Modules\juce_events\native - + JUCE Modules\juce_events\native - + JUCE Modules\juce_events\native - + JUCE Modules\juce_events\native - + JUCE Modules\juce_events\native @@ -2446,46 +2383,46 @@ JUCE Modules\juce_graphics\images - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native @@ -2560,9 +2497,6 @@ JUCE Modules\juce_gui_basics\desktop - - JUCE Modules\juce_gui_basics\detail - JUCE Modules\juce_gui_basics\drawables @@ -2635,9 +2569,6 @@ JUCE Modules\juce_gui_basics\keyboard - - JUCE Modules\juce_gui_basics\layout - JUCE Modules\juce_gui_basics\layout @@ -2761,124 +2692,85 @@ JUCE Modules\juce_gui_basics\mouse - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - - JUCE Modules\juce_gui_basics\native\accessibility - - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native + + JUCE Modules\juce_gui_basics\native\x11 - - JUCE Modules\juce_gui_basics\native + + JUCE Modules\juce_gui_basics\native\x11 - - JUCE Modules\juce_gui_basics\native + + JUCE Modules\juce_gui_basics\native\x11 - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - - + JUCE Modules\juce_gui_basics\native @@ -2980,18 +2872,9 @@ JUCE Modules\juce_gui_basics\windows - - JUCE Modules\juce_gui_basics\windows - - - JUCE Modules\juce_gui_basics\windows - JUCE Modules\juce_gui_basics\windows - - JUCE Modules\juce_gui_basics\windows - JUCE Modules\juce_gui_basics\windows @@ -3001,7 +2884,7 @@ JUCE Modules\juce_gui_basics\windows - + JUCE Modules\juce_gui_basics\windows @@ -3061,55 +2944,55 @@ JUCE Modules\juce_gui_extra\misc - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native - + JUCE Modules\juce_gui_extra\native @@ -3471,10 +3354,10 @@ JUCE Modules\juce_audio_basics\mpe - + JUCE Modules\juce_audio_basics\native - + JUCE Modules\juce_audio_basics\native @@ -3759,10 +3642,10 @@ JUCE Modules\juce_audio_devices\native\oboe\src\opensles - + JUCE Modules\juce_audio_devices\native - + JUCE Modules\juce_audio_devices\native @@ -4293,9 +4176,6 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\base\source - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\base\source - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\base\source @@ -4353,9 +4233,6 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\pluginterfaces\base - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\pluginterfaces\base - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\pluginterfaces\base @@ -4464,42 +4341,12 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\common - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\common - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\moduleinfo - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\moduleinfo - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\moduleinfo - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\moduleinfo - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\moduleinfo - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility - - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -4620,9 +4467,6 @@ JUCE Modules\juce_audio_processors\utilities\ARA - - JUCE Modules\juce_audio_processors\utilities - JUCE Modules\juce_audio_processors\utilities @@ -4659,10 +4503,10 @@ JUCE Modules\juce_audio_processors\utilities - + JUCE Modules\juce_audio_processors\utilities - + JUCE Modules\juce_audio_processors\utilities @@ -4719,9 +4563,6 @@ JUCE Modules\juce_core\containers - - JUCE Modules\juce_core\containers - JUCE Modules\juce_core\containers @@ -4848,9 +4689,6 @@ JUCE Modules\juce_core\misc - - JUCE Modules\juce_core\misc - JUCE Modules\juce_core\misc @@ -4866,34 +4704,31 @@ JUCE Modules\juce_core\misc - - JUCE Modules\juce_core\native - - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native - + JUCE Modules\juce_core\native @@ -5217,16 +5052,16 @@ JUCE Modules\juce_dsp\maths - + JUCE Modules\juce_dsp\native - + JUCE Modules\juce_dsp\native - + JUCE Modules\juce_dsp\native - + JUCE Modules\juce_dsp\native @@ -5328,9 +5163,6 @@ JUCE Modules\juce_events\broadcasters - - JUCE Modules\juce_events\broadcasters - JUCE Modules\juce_events\interprocess @@ -5370,25 +5202,22 @@ JUCE Modules\juce_events\messages - + JUCE Modules\juce_events\native - + JUCE Modules\juce_events\native - + JUCE Modules\juce_events\native - + JUCE Modules\juce_events\native - + JUCE Modules\juce_events\native - - JUCE Modules\juce_events\native - - + JUCE Modules\juce_events\native @@ -5559,16 +5388,16 @@ JUCE Modules\juce_graphics\images - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native - + JUCE Modules\juce_graphics\native @@ -5676,66 +5505,6 @@ JUCE Modules\juce_gui_basics\desktop - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - - - JUCE Modules\juce_gui_basics\detail - JUCE Modules\juce_gui_basics\drawables @@ -5826,9 +5595,6 @@ JUCE Modules\juce_gui_basics\layout - - JUCE Modules\juce_gui_basics\layout - JUCE Modules\juce_gui_basics\layout @@ -5964,6 +5730,9 @@ JUCE Modules\juce_gui_basics\mouse + + JUCE Modules\juce_gui_basics\mouse + JUCE Modules\juce_gui_basics\mouse @@ -5973,88 +5742,82 @@ JUCE Modules\juce_gui_basics\mouse - - JUCE Modules\juce_gui_basics\native\accessibility - JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - + JUCE Modules\juce_gui_basics\native\accessibility - - JUCE Modules\juce_gui_basics\native + + JUCE Modules\juce_gui_basics\native\accessibility - - JUCE Modules\juce_gui_basics\native + + JUCE Modules\juce_gui_basics\native\x11 - - JUCE Modules\juce_gui_basics\native + + JUCE Modules\juce_gui_basics\native\x11 - - JUCE Modules\juce_gui_basics\native + + JUCE Modules\juce_gui_basics\native\x11 - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native - + JUCE Modules\juce_gui_basics\native @@ -6168,9 +5931,6 @@ JUCE Modules\juce_gui_basics\windows - - JUCE Modules\juce_gui_basics\windows - JUCE Modules\juce_gui_basics\windows @@ -6180,10 +5940,7 @@ JUCE Modules\juce_gui_basics\windows - - JUCE Modules\juce_gui_basics\windows - - + JUCE Modules\juce_gui_basics\windows @@ -6267,7 +6024,7 @@ JUCE Modules\juce_gui_extra\misc - + JUCE Modules\juce_gui_extra\native @@ -6291,13 +6048,13 @@ JUCE Modules\juce_opengl\native - + JUCE Modules\juce_opengl\native - + JUCE Modules\juce_opengl\native - + JUCE Modules\juce_opengl\native @@ -6351,28 +6108,28 @@ JUCE Modules\juce_video\capture - + JUCE Modules\juce_video\native - + JUCE Modules\juce_video\native - + JUCE Modules\juce_video\native - + JUCE Modules\juce_video\native - + JUCE Modules\juce_video\native - + JUCE Modules\juce_video\native - + JUCE Modules\juce_video\native - + JUCE Modules\juce_video\native @@ -6419,9 +6176,6 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\pluginterfaces - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\moduleinfo - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk diff --git a/DawDreamer.jucer b/DawDreamer.jucer index 0e21bac9..a95a67bc 100644 --- a/DawDreamer.jucer +++ b/DawDreamer.jucer @@ -88,10 +88,10 @@ - + - @@ -173,7 +173,8 @@ - + diff --git a/JuceLibraryCode/AppConfig.h b/JuceLibraryCode/AppConfig.h index fab33c1e..b6baecc9 100644 --- a/JuceLibraryCode/AppConfig.h +++ b/JuceLibraryCode/AppConfig.h @@ -43,7 +43,7 @@ #define JUCE_USE_DARK_SPLASH_SCREEN 1 -#define JUCE_PROJUCER_VERSION 0x70007 +#define JUCE_PROJUCER_VERSION 0x70005 //============================================================================== #define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp index 7f053d31..2a359f3b 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp @@ -107,34 +107,6 @@ String AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type) 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"); @@ -219,34 +191,6 @@ String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelT 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"; @@ -264,6 +208,9 @@ String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelT default: break; } + if (type >= ambisonicACN4 && type <= ambisonicACN35) + return "ACN" + String (type - ambisonicACN4 + 4); + return {}; } @@ -336,34 +283,6 @@ AudioChannelSet::ChannelType AudioChannelSet::getChannelTypeFromAbbreviation (co 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; @@ -419,8 +338,6 @@ 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"; @@ -434,13 +351,9 @@ String AudioChannelSet::getDescription() const 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"; @@ -473,11 +386,11 @@ String AudioChannelSet::getDescription() const bool AudioChannelSet::isDiscreteLayout() const noexcept { - const auto channelTypes = getChannelTypes(); + for (auto& speaker : getChannelTypes()) + if (speaker <= ambisonicACN35) + return false; - return std::none_of (std::begin (channelTypes), - std::end (channelTypes), - [] (const auto& t) { return t < discreteChannel0; }); + return true; } int AudioChannelSet::size() const noexcept @@ -554,44 +467,26 @@ AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet 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, 8)); - - static constexpr std::pair continuousRanges[] { { ambisonicACN0, ambisonicACN3 }, - { ambisonicACN4, ambisonicACN35 }, - { ambisonicACN36, ambisonicACN63 } }; + jassert (isPositiveAndBelow (order, 6)); - AudioChannelSet set; + if (order == 0) + return AudioChannelSet ((uint32) (1 << ambisonicACN0)); - 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; - }; + AudioChannelSet set ((1u << ambisonicACN0) | (1u << ambisonicACN1) | (1u << ambisonicACN2) | (1u << ambisonicACN3)); - const auto numAmbisonicChannels = square (order + 1); - - for (int rangeIdx = 0, bitsSet = 0; bitsSet < numAmbisonicChannels; ++rangeIdx) - { - bitsSet += setBits (continuousRanges[rangeIdx], numAmbisonicChannels - bitsSet); - } + auto numAmbisonicChannels = (order + 1) * (order + 1); + set.channels.setRange (ambisonicACN4, numAmbisonicChannels - 4, true); return set; } @@ -736,13 +631,15 @@ int32 AudioChannelSet::getWaveChannelMask() const noexcept } //============================================================================== -int AudioChannelSet::getAmbisonicOrderForNumChannels (int numChannels, int maxOrderToCheck) +int JUCE_CALLTYPE AudioChannelSet::getAmbisonicOrderForNumChannels (int numChannels) { - for (auto i = 0; i <= maxOrderToCheck; ++i) - if (numChannels == square (i + 1)) - return i; + auto sqrtMinusOne = std::sqrt (static_cast (numChannels)) - 1.0f; + auto ambisonicOrder = jmax (0, static_cast (std::floor (sqrtMinusOne))); - return -1; + if (ambisonicOrder > 5) + return -1; + + return (static_cast (ambisonicOrder) == sqrtMinusOne ? ambisonicOrder : -1); } diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h index 989669dd..3c96befa 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h @@ -196,24 +196,12 @@ 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). @@ -244,36 +232,12 @@ class JUCE_API AudioChannelSet */ 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 @@ -455,40 +419,6 @@ 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. */ }; @@ -505,7 +435,7 @@ class JUCE_API AudioChannelSet //============================================================================== enum { - maxChannelsOfNamedLayout = 64 + maxChannelsOfNamedLayout = 36 }; /** Adds a channel to the set. */ @@ -575,12 +505,6 @@ 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; @@ -594,6 +518,8 @@ class JUCE_API AudioChannelSet explicit AudioChannelSet (uint32); 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 f5e9c668..c72e3e3c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp @@ -599,7 +599,7 @@ class AudioConversionTests : public UnitTest 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)); + expect (destBuffer.getSample (0, ch + (i * numChannels)) == sourceBuffer.getSample (ch, i)); } beginTest ("Deinterleaving"); @@ -620,7 +620,7 @@ class AudioConversionTests : public UnitTest 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)); + expect (sourceBuffer.getSample (0, ch + (i * numChannels)) == destBuffer.getSample (ch, i)); } } }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp index 98ce1a59..2144fa66 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp @@ -60,7 +60,7 @@ void AudioProcessLoadMeasurer::registerRenderTime (double milliseconds, int numS void AudioProcessLoadMeasurer::registerRenderTimeLocked (double milliseconds, int numSamples) { - if (approximatelyEqual (msPerSample, 0.0)) + if (msPerSample == 0) return; const auto maxMilliseconds = numSamples * msPerSample; diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h index a1d18595..d583a593 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h @@ -686,11 +686,11 @@ class AudioBuffer jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - if (! approximatelyEqual (gain, Type (1)) && ! isClear) + if (gain != Type (1) && ! isClear) { auto* d = channels[channel] + startSample; - if (approximatelyEqual (gain, Type())) + if (gain == Type()) FloatVectorOperations::clear (d, numSamples); else FloatVectorOperations::multiply (d, gain, numSamples); @@ -728,7 +728,7 @@ class AudioBuffer { if (! isClear) { - if (approximatelyEqual (startGain, endGain)) + if (startGain == endGain) { applyGain (channel, startSample, numSamples, startGain); } @@ -798,7 +798,7 @@ class AudioBuffer jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); - if (! approximatelyEqual (gainToApplyToSource, (Type) 0) && numSamples > 0 && ! source.isClear) + if (gainToApplyToSource != 0 && numSamples > 0 && ! source.isClear) { auto* d = channels[destChannel] + destStartSample; auto* s = source.channels[sourceChannel] + sourceStartSample; @@ -809,14 +809,14 @@ class AudioBuffer { isClear = false; - if (! approximatelyEqual (gainToApplyToSource, Type (1))) + if (gainToApplyToSource != Type (1)) FloatVectorOperations::copyWithMultiply (d, s, gainToApplyToSource, numSamples); else FloatVectorOperations::copy (d, s, numSamples); } else { - if (! approximatelyEqual (gainToApplyToSource, Type (1))) + if (gainToApplyToSource != Type (1)) FloatVectorOperations::addWithMultiply (d, s, gainToApplyToSource, numSamples); else FloatVectorOperations::add (d, s, numSamples); @@ -850,7 +850,7 @@ class AudioBuffer jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); jassert (source != nullptr); - if (! approximatelyEqual (gainToApplyToSource, Type()) && numSamples > 0) + if (gainToApplyToSource != 0 && numSamples > 0) { auto* d = channels[destChannel] + destStartSample; @@ -858,14 +858,14 @@ class AudioBuffer { isClear = false; - if (! approximatelyEqual (gainToApplyToSource, Type (1))) + if (gainToApplyToSource != Type (1)) FloatVectorOperations::copyWithMultiply (d, source, gainToApplyToSource, numSamples); else FloatVectorOperations::copy (d, source, numSamples); } else { - if (! approximatelyEqual (gainToApplyToSource, Type (1))) + if (gainToApplyToSource != Type (1)) FloatVectorOperations::addWithMultiply (d, source, gainToApplyToSource, numSamples); else FloatVectorOperations::add (d, source, numSamples); @@ -899,7 +899,7 @@ class AudioBuffer Type startGain, Type endGain) noexcept { - if (approximatelyEqual (startGain, endGain)) + if (startGain == endGain) { addFrom (destChannel, destStartSample, source, numSamples, startGain); } @@ -912,7 +912,7 @@ class AudioBuffer if (numSamples > 0) { isClear = false; - const auto increment = (endGain - startGain) / (Type) numSamples; + const auto increment = (endGain - startGain) / numSamples; auto* d = channels[destChannel] + destStartSample; while (--numSamples >= 0) @@ -1023,9 +1023,9 @@ class AudioBuffer { auto* d = channels[destChannel] + destStartSample; - if (! approximatelyEqual (gain, Type (1))) + if (gain != Type (1)) { - if (approximatelyEqual (gain, Type())) + if (gain == Type()) { if (! isClear) FloatVectorOperations::clear (d, numSamples); @@ -1071,7 +1071,7 @@ class AudioBuffer Type startGain, Type endGain) noexcept { - if (approximatelyEqual (startGain, endGain)) + if (startGain == endGain) { copyFrom (destChannel, destStartSample, source, numSamples, startGain); } @@ -1084,7 +1084,7 @@ class AudioBuffer if (numSamples > 0) { isClear = false; - const auto increment = (endGain - startGain) / (Type) numSamples; + const auto increment = (endGain - startGain) / numSamples; auto* d = channels[destChannel] + destStartSample; while (--numSamples >= 0) @@ -1280,31 +1280,6 @@ class AudioBuffer 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 1079e872..2a576dc9 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -1676,7 +1676,7 @@ class FloatVectorOperationsTests : public UnitTest static bool areAllValuesEqual (const ValueType* d, int num, ValueType target) { while (--num >= 0) - if (! exactlyEqual (*d++, target)) + if (*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 ce342b63..9c6ed499 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h @@ -98,7 +98,7 @@ struct FloatVectorOperationsBase /** Multiplies the destination values by the source values. */ static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src, CountType numValues) noexcept; - /** Multiplies each source1 value by the corresponding source2 value, then stores it in the destination array. */ + /** Multiplies each source1 value by the correspinding 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. */ diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp index 260635fa..57097476 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp @@ -88,7 +88,6 @@ #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" diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h index 6a8c999d..85c0c437 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h @@ -32,7 +32,7 @@ ID: juce_audio_basics vendor: juce - version: 7.0.7 + version: 7.0.5 name: JUCE audio and MIDI data classes description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. website: http://www.juce.com/juce diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp index 51bfcdda..67795b45 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp @@ -160,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) @@ -174,9 +174,9 @@ namespace MidiFileHelpers while (i + 1 < numEvents) { - auto& m2 = tempoEvents.getEventPointer (i + 1)->message; + auto& m2 = tempoEvents.getEventPointer(i + 1)->message; - if (! approximatelyEqual (m2.getTimeStamp(), eventTime)) + if (m2.getTimeStamp() != eventTime) break; if (m2.isTempoMetaEvent()) diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp index 95d01222..129ad9e0 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp @@ -266,7 +266,7 @@ void MidiMessageSequence::updateMatchedPairs() noexcept void MidiMessageSequence::addTimeToMessages (double delta) noexcept { - if (! approximatelyEqual (delta, 0.0)) + if (delta != 0) for (auto* m : list) m->message.addToTimeStamp (delta); } @@ -554,7 +554,7 @@ struct MidiMessageSequenceTest : public UnitTest { const auto isEqual = [this] (const ControlValue& cv, const MidiMessage& msg) { - return exactlyEqual (msg.getTimeStamp(), time) + return msg.getTimeStamp() == time && msg.isController() && msg.getChannel() == channel && msg.getControllerNumber() == cv.control diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp index f16e73a7..ae5fc500 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.cpp @@ -23,46 +23,42 @@ namespace juce { -bool MidiRPNDetector::parseControllerMessage (int midiChannel, - int controllerNumber, - int controllerValue, - MidiRPNMessage& result) noexcept +MidiRPNDetector::MidiRPNDetector() noexcept { - auto parsed = tryParse (midiChannel, controllerNumber, controllerValue); - - if (! parsed.has_value()) - return false; +} - result = *parsed; - return true; +MidiRPNDetector::~MidiRPNDetector() noexcept +{ } -std::optional MidiRPNDetector::tryParse (int midiChannel, - int controllerNumber, - int controllerValue) +bool MidiRPNDetector::parseControllerMessage (int midiChannel, + int controllerNumber, + int controllerValue, + MidiRPNMessage& result) noexcept { jassert (midiChannel > 0 && midiChannel <= 16); jassert (controllerNumber >= 0 && controllerNumber < 128); jassert (controllerValue >= 0 && controllerValue < 128); - return states[midiChannel - 1].handleController (midiChannel, controllerNumber, controllerValue); + return states[midiChannel - 1].handleController (midiChannel, controllerNumber, controllerValue, result); } void MidiRPNDetector::reset() noexcept { - for (auto& state : states) + for (int i = 0; i < 16; ++i) { - state.parameterMSB = 0xff; - state.parameterLSB = 0xff; - state.resetValue(); - state.isNRPN = false; + states[i].parameterMSB = 0xff; + states[i].parameterLSB = 0xff; + states[i].resetValue(); + states[i].isNRPN = false; } } //============================================================================== -std::optional MidiRPNDetector::ChannelState::handleController (int channel, - int controllerNumber, - int value) noexcept +bool MidiRPNDetector::ChannelState::handleController (int channel, + int controllerNumber, + int value, + MidiRPNMessage& result) noexcept { switch (controllerNumber) { @@ -72,11 +68,13 @@ std::optional MidiRPNDetector::ChannelState::handleController (i case 0x64: parameterLSB = uint8 (value); resetValue(); isNRPN = false; break; case 0x65: parameterMSB = uint8 (value); resetValue(); isNRPN = false; break; - case 0x06: valueMSB = uint8 (value); valueLSB = 0xff; return sendIfReady (channel); - case 0x26: valueLSB = uint8 (value); return sendIfReady (channel); + case 0x06: valueMSB = uint8 (value); return sendIfReady (channel, result); + case 0x26: valueLSB = uint8 (value); break; + + default: break; } - return {}; + return false; } void MidiRPNDetector::ChannelState::resetValue() noexcept @@ -86,28 +84,32 @@ void MidiRPNDetector::ChannelState::resetValue() noexcept } //============================================================================== -std::optional MidiRPNDetector::ChannelState::sendIfReady (int channel) noexcept +bool MidiRPNDetector::ChannelState::sendIfReady (int channel, MidiRPNMessage& result) noexcept { - if (parameterMSB >= 0x80 || parameterLSB >= 0x80 || valueMSB >= 0x80) - return {}; + if (parameterMSB < 0x80 && parameterLSB < 0x80) + { + if (valueMSB < 0x80) + { + result.channel = channel; + result.parameterNumber = (parameterMSB << 7) + parameterLSB; + result.isNRPN = isNRPN; - MidiRPNMessage result{}; - result.channel = channel; - result.parameterNumber = (parameterMSB << 7) + parameterLSB; - result.isNRPN = isNRPN; + if (valueLSB < 0x80) + { + result.value = (valueMSB << 7) + valueLSB; + result.is14BitValue = true; + } + else + { + result.value = valueMSB; + result.is14BitValue = false; + } - if (valueLSB < 0x80) - { - result.value = (valueMSB << 7) + valueLSB; - result.is14BitValue = true; - } - else - { - result.value = valueMSB; - result.is14BitValue = false; + return true; + } } - return result; + return false; } //============================================================================== @@ -130,26 +132,25 @@ MidiBuffer MidiRPNGenerator::generate (int midiChannel, jassert (parameterNumber >= 0 && parameterNumber < 16384); jassert (value >= 0 && value < (use14BitValue ? 16384 : 128)); - auto parameterLSB = uint8 (parameterNumber & 0x0000007f); - auto parameterMSB = uint8 (parameterNumber >> 7); + uint8 parameterLSB = uint8 (parameterNumber & 0x0000007f); + uint8 parameterMSB = uint8 (parameterNumber >> 7); uint8 valueLSB = use14BitValue ? uint8 (value & 0x0000007f) : 0x00; uint8 valueMSB = use14BitValue ? uint8 (value >> 7) : uint8 (value); - auto channelByte = uint8 (0xb0 + midiChannel - 1); + uint8 channelByte = uint8 (0xb0 + midiChannel - 1); MidiBuffer buffer; buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x62 : 0x64, parameterLSB), 0); buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x63 : 0x65, parameterMSB), 0); - 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. + // sending the value LSB is optional, but must come before sending the value MSB: if (use14BitValue) buffer.addEvent (MidiMessage (channelByte, 0x26, valueLSB), 0); + buffer.addEvent (MidiMessage (channelByte, 0x06, valueMSB), 0); + return buffer; } @@ -167,196 +168,134 @@ class MidiRPNDetectorTests : public UnitTest void runTest() override { - // 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"); + beginTest ("7-bit RPN"); { MidiRPNDetector detector; - expect (! detector.tryParse (2, 101, 0)); - expect (! detector.tryParse (2, 100, 7)); - expect (! detector.tryParse (2, 38, 42)); + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (2, 101, 0, rpn)); + expect (! detector.parseControllerMessage (2, 100, 7, rpn)); + expect (detector.parseControllerMessage (2, 6, 42, rpn)); + + expectEquals (rpn.channel, 2); + expectEquals (rpn.parameterNumber, 7); + expectEquals (rpn.value, 42); + expect (! rpn.isNRPN); + expect (! rpn.is14BitValue); } - beginTest ("LSB following MSB is parsed as 14-bit"); + beginTest ("14-bit RPN"); { MidiRPNDetector detector; - 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); + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (1, 100, 44, rpn)); + expect (! detector.parseControllerMessage (1, 101, 2, rpn)); + expect (! detector.parseControllerMessage (1, 38, 94, rpn)); + expect (detector.parseControllerMessage (1, 6, 1, rpn)); + + expectEquals (rpn.channel, 1); + expectEquals (rpn.parameterNumber, 300); + expectEquals (rpn.value, 222); + expect (! rpn.isNRPN); + expect (rpn.is14BitValue); } beginTest ("RPNs on multiple channels simultaneously"); { MidiRPNDetector detector; - 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); + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (1, 100, 44, rpn)); + expect (! detector.parseControllerMessage (2, 101, 0, rpn)); + expect (! detector.parseControllerMessage (1, 101, 2, rpn)); + expect (! detector.parseControllerMessage (2, 100, 7, rpn)); + expect (! detector.parseControllerMessage (1, 38, 94, rpn)); + expect (detector.parseControllerMessage (2, 6, 42, rpn)); + + expectEquals (rpn.channel, 2); + expectEquals (rpn.parameterNumber, 7); + expectEquals (rpn.value, 42); + expect (! rpn.isNRPN); + expect (! rpn.is14BitValue); + + expect (detector.parseControllerMessage (1, 6, 1, rpn)); + + expectEquals (rpn.channel, 1); + expectEquals (rpn.parameterNumber, 300); + expectEquals (rpn.value, 222); + expect (! rpn.isNRPN); + expect (rpn.is14BitValue); } beginTest ("14-bit RPN with value within 7-bit range"); { MidiRPNDetector detector; - 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); + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (16, 100, 0 , rpn)); + expect (! detector.parseControllerMessage (16, 101, 0, rpn)); + expect (! detector.parseControllerMessage (16, 38, 3, rpn)); + expect (detector.parseControllerMessage (16, 6, 0, rpn)); + + expectEquals (rpn.channel, 16); + expectEquals (rpn.parameterNumber, 0); + expectEquals (rpn.value, 3); + expect (! rpn.isNRPN); + expect (rpn.is14BitValue); } beginTest ("invalid RPN (wrong order)"); { MidiRPNDetector detector; - expect (! detector.tryParse (2, 6, 42)); - expect (! detector.tryParse (2, 101, 0)); - expect (! detector.tryParse (2, 100, 7)); + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (2, 6, 42, rpn)); + expect (! detector.parseControllerMessage (2, 101, 0, rpn)); + expect (! detector.parseControllerMessage (2, 100, 7, rpn)); } beginTest ("14-bit RPN interspersed with unrelated CC messages"); { MidiRPNDetector detector; - 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); + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (16, 3, 80, rpn)); + expect (! detector.parseControllerMessage (16, 100, 0 , rpn)); + expect (! detector.parseControllerMessage (16, 4, 81, rpn)); + expect (! detector.parseControllerMessage (16, 101, 0, rpn)); + expect (! detector.parseControllerMessage (16, 5, 82, rpn)); + expect (! detector.parseControllerMessage (16, 5, 83, rpn)); + expect (! detector.parseControllerMessage (16, 38, 3, rpn)); + expect (! detector.parseControllerMessage (16, 4, 84, rpn)); + expect (! detector.parseControllerMessage (16, 3, 85, rpn)); + expect (detector.parseControllerMessage (16, 6, 0, rpn)); + + expectEquals (rpn.channel, 16); + expectEquals (rpn.parameterNumber, 0); + expectEquals (rpn.value, 3); + expect (! rpn.isNRPN); + expect (rpn.is14BitValue); } beginTest ("14-bit NRPN"); { MidiRPNDetector detector; - 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); + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (1, 98, 44, rpn)); + expect (! detector.parseControllerMessage (1, 99 , 2, rpn)); + expect (! detector.parseControllerMessage (1, 38, 94, rpn)); + expect (detector.parseControllerMessage (1, 6, 1, rpn)); + + expectEquals (rpn.channel, 1); + expectEquals (rpn.parameterNumber, 300); + expectEquals (rpn.value, 222); + expect (rpn.isNRPN); + expect (rpn.is14BitValue); } beginTest ("reset"); { MidiRPNDetector detector; - expect (! detector.tryParse (2, 101, 0)); + MidiRPNMessage rpn; + expect (! detector.parseControllerMessage (2, 101, 0, rpn)); detector.reset(); - expect (! detector.tryParse (2, 100, 7)); - expect (! detector.tryParse (2, 6, 42)); + expect (! detector.parseControllerMessage (2, 100, 7, rpn)); + expect (! detector.parseControllerMessage (2, 6, 42, rpn)); } } }; @@ -407,24 +346,25 @@ class MidiRPNGeneratorTests : public UnitTest //============================================================================== void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected) { - std::optional result; + MidiRPNMessage result = MidiRPNMessage(); MidiRPNDetector detector; for (const auto metadata : midiBuffer) { const auto midiMessage = metadata.getMessage(); - result = detector.tryParse (midiMessage.getChannel(), - midiMessage.getControllerNumber(), - midiMessage.getControllerValue()); + if (detector.parseControllerMessage (midiMessage.getChannel(), + midiMessage.getControllerNumber(), + midiMessage.getControllerValue(), + result)) + break; } - 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); + 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 1f4fcf3c..f1fb9ff0 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiRPN.h @@ -68,10 +68,10 @@ class JUCE_API MidiRPNDetector { public: /** Constructor. */ - MidiRPNDetector() noexcept = default; + MidiRPNDetector() noexcept; /** Destructor. */ - ~MidiRPNDetector() noexcept = default; + ~MidiRPNDetector() noexcept; /** Resets the RPN detector's internal state, so that it forgets about previously received MIDI CC messages. @@ -79,39 +79,26 @@ class JUCE_API MidiRPNDetector void reset() noexcept; //============================================================================== - /** @see tryParse() */ - [[deprecated ("Use tryParse() instead")]] + /** Takes the next in a stream of incoming MIDI CC messages and returns true + if it forms the last of a sequence that makes an RPN or NPRN. + + If this returns true, then the RPNMessage object supplied will be + filled-out with the message's details. + (If it returns false then the RPNMessage object will be unchanged). + */ bool parseControllerMessage (int midiChannel, int controllerNumber, int controllerValue, MidiRPNMessage& result) noexcept; - /** 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 { - std::optional handleController (int channel, int controllerNumber, - int value) noexcept; + bool handleController (int channel, int controllerNumber, + int value, MidiRPNMessage&) noexcept; void resetValue() noexcept; - std::optional sendIfReady (int channel) noexcept; + bool sendIfReady (int channel, MidiRPNMessage&) noexcept; uint8 parameterMSB = 0xff, parameterLSB = 0xff, valueMSB = 0xff, valueLSB = 0xff; bool isNRPN = false; diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h index ea656a65..0fd724ae 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP.h @@ -20,6 +20,8 @@ ============================================================================== */ +#include "../juce_MidiDataConcatenator.h" + #include "juce_UMPProtocols.h" #include "juce_UMPUtils.h" #include "juce_UMPacket.h" diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h index f28cc765..8980e1e7 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h @@ -27,44 +27,6 @@ namespace juce namespace 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. @@ -78,17 +40,13 @@ struct Conversion `callback` is a function which accepts a single View argument. */ template - static void toMidi1 (const BytestreamMidiView& m, PacketCallbackFunction&& callback) + static void toMidi1 (const MidiMessage& m, PacketCallbackFunction&& callback) { - const auto size = m.bytes.size(); - - if (size <= 0) - return; - - const auto* data = m.bytes.data(); + const auto* data = m.getRawData(); const auto firstByte = data[0]; + const auto size = m.getRawDataSize(); - if (firstByte != std::byte { 0xf0 }) + if (firstByte != 0xf0) { const auto mask = [size]() -> uint32_t { @@ -103,15 +61,15 @@ struct Conversion return 0x00000000; }(); - const auto extraByte = ((((firstByte & std::byte { 0xf0 }) == std::byte { 0xf0 }) ? std::byte { 0x1 } : std::byte { 0x2 }) << 0x4); + const auto extraByte = (uint8_t) ((((firstByte & 0xf0) == 0xf0) ? 0x1 : 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 numSysExBytes = m.getSysExDataSize(); const auto numMessages = SysEx7::getNumPacketsRequiredForDataSize ((uint32_t) numSysExBytes); - auto* dataOffset = data + 1; + auto* dataOffset = m.getSysExData(); if (numMessages <= 1) { @@ -120,9 +78,9 @@ struct Conversion return; } - constexpr ssize_t byteIncrement = 6; + constexpr auto byteIncrement = 6; - for (auto i = static_cast (numSysExBytes); i > 0; i -= byteIncrement, dataOffset += byteIncrement) + for (auto i = numSysExBytes; i > 0; i -= byteIncrement, dataOffset += byteIncrement) { const auto func = [&] { @@ -141,6 +99,20 @@ struct Conversion } } + /** Converts a MidiMessage to one or more messages in UMP format, using + the MIDI 1.0 Protocol. + + `packets` is an out-param to allow the caller to control + allocation/deallocation. Returning a new Packets object would + require every call to toMidi1 to allocate. With this version, no + allocations will occur, provided that `packets` has adequate reserved + space. + */ + static void toMidi1 (const MidiMessage& m, Packets& packets) + { + toMidi1 (m, [&] (const View& view) { packets.add (view); }); + } + /** Widens a 7-bit MIDI 1.0 value to a 8-bit MIDI 2.0 value. */ static uint8_t scaleTo8 (uint8_t word7Bit) { @@ -226,7 +198,7 @@ struct Conversion } const auto status = Utils::getStatus (firstWord); - const auto typeAndGroup = ((std::byte { 0x2 } << 0x4) | std::byte { Utils::getGroup (firstWord) }); + const auto typeAndGroup = (uint8_t) ((0x2 << 0x4) | Utils::getGroup (firstWord)); switch (status) { @@ -235,18 +207,18 @@ struct Conversion 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]) }; + const auto statusAndChannel = (uint8_t) ((firstWord >> 0x10) & 0xff); + const auto byte2 = (uint8_t) ((firstWord >> 0x08) & 0xff); + const auto byte3 = 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 needsCorrection = status == 0x9 && byte3 == 0; + const auto correctedByte = (uint8_t) (needsCorrection ? 1 : byte3); const auto shouldIgnore = status == 0xb && [&] { - switch (uint8_t (byte2)) + switch (byte2) { case 0: case 6: @@ -275,13 +247,13 @@ struct Conversion case 0xd: // channel pressure { - const auto statusAndChannel = std::byte ((firstWord >> 0x10) & 0xff); - const auto byte2 = std::byte { scaleTo7 (v[1]) }; + const auto statusAndChannel = (uint8_t) ((firstWord >> 0x10) & 0xff); + const auto byte2 = scaleTo7 (v[1]); const PacketX1 packet { Utils::bytesToWord (typeAndGroup, statusAndChannel, byte2, - std::byte { 0 }) }; + 0) }; callback (View (packet.data())); return; } @@ -289,17 +261,17 @@ struct Conversion 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 ccX = (uint8_t) (status == 0x2 ? 101 : 99); + const auto ccY = (uint8_t) (status == 0x2 ? 100 : 98); + const auto statusAndChannel = (uint8_t) ((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)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, ccX, (uint8_t) ((firstWord >> 0x8) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, ccY, (uint8_t) ((firstWord >> 0x0) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, 6, (uint8_t) ((data >> 0x7) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, 38, (uint8_t) ((data >> 0x0) & 0x7f)) }, }; for (const auto& packet : packets) @@ -312,24 +284,24 @@ struct Conversion { if (firstWord & 1) { - const auto statusAndChannel = std::byte ((0xb << 0x4) | Utils::getChannel (firstWord)); + const auto statusAndChannel = (uint8_t) ((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)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, 0, (uint8_t) ((secondWord >> 0x8) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, 32, (uint8_t) ((secondWord >> 0x0) & 0x7f)) }, }; for (const auto& packet : packets) callback (View (packet.data())); } - const auto statusAndChannel = std::byte ((0xc << 0x4) | Utils::getChannel (firstWord)); + const auto statusAndChannel = (uint8_t) ((0xc << 0x4) | Utils::getChannel (firstWord)); const PacketX1 packet { Utils::bytesToWord (typeAndGroup, statusAndChannel, - std::byte ((v[1] >> 0x18) & 0x7f), - std::byte { 0 }) }; + (uint8_t) ((v[1] >> 0x18) & 0x7f), + 0) }; callback (View (packet.data())); return; } @@ -337,11 +309,11 @@ struct Conversion case 0xe: // pitch bend { const auto data = scaleTo14 (v[1]); - const auto statusAndChannel = std::byte ((firstWord >> 0x10) & 0xff); + const auto statusAndChannel = (uint8_t) ((firstWord >> 0x10) & 0xff); const PacketX1 packet { Utils::bytesToWord (typeAndGroup, statusAndChannel, - std::byte (data & 0x7f), - std::byte ((data >> 7) & 0x7f)) }; + (uint8_t) (data & 0x7f), + (uint8_t) ((data >> 7) & 0x7f)) }; callback (View (packet.data())); return; } diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h index 8f51f13a..3639cceb 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h @@ -35,7 +35,7 @@ namespace universal_midi_packets struct ToUMP1Converter { template - void convert (const BytestreamMidiView& m, Fn&& fn) + void convert (const MidiMessage& m, Fn&& fn) { Conversion::toMidi1 (m, std::forward (fn)); } @@ -56,7 +56,7 @@ namespace universal_midi_packets struct ToUMP2Converter { template - void convert (const BytestreamMidiView& m, Fn&& fn) + void convert (const MidiMessage& m, Fn&& fn) { Conversion::toMidi1 (m, [&] (const View& v) { @@ -88,15 +88,6 @@ namespace universal_midi_packets */ 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) {} @@ -106,43 +97,33 @@ namespace universal_midi_packets 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) + void convert (const MidiMessage& m, Fn&& fn) { - visit (*this, m, std::forward (fn)); + switch (mode) + { + case PacketProtocol::MIDI_1_0: return std::get<0> (converters).convert (m, std::forward (fn)); + case PacketProtocol::MIDI_2_0: return std::get<1> (converters).convert (m, std::forward (fn)); + } } template void convert (const View& v, Fn&& fn) { - visit (*this, v, std::forward (fn)); + switch (mode) + { + case PacketProtocol::MIDI_1_0: return std::get<0> (converters).convert (v, std::forward (fn)); + case PacketProtocol::MIDI_2_0: return std::get<1> (converters).convert (v, std::forward (fn)); + } } template void convert (Iterator begin, Iterator end, Fn&& fn) { - visit (*this, begin, end, std::forward (fn)); + std::for_each (begin, end, [&] (const View& v) + { + convert (v, fn); + }); } PacketProtocol getProtocol() const noexcept { return mode; } diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h index f5eb8873..b2512d2f 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h @@ -123,7 +123,7 @@ class BytestreamToUMPDispatcher void handleIncomingMidiMessage (void*, const MidiMessage& msg) const { - Conversion::toMidi1 (BytestreamMidiView (&msg), [&] (const View& view) + Conversion::toMidi1 (msg, [&] (const View& view) { dispatch.converter.convert (view, *callbackPtr); }); diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h index 9f1e2656..788d34a4 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h @@ -45,7 +45,7 @@ struct Factory static PacketX2 makeSysEx (uint8_t group, uint8_t status, uint8_t numBytes, - const std::byte* data) + const uint8_t* data) { jassert (numBytes <= 6); @@ -250,28 +250,28 @@ struct Factory static PacketX2 makeSysExIn1Packet (uint8_t group, uint8_t numBytes, - const std::byte* data) + const uint8_t* data) { return Detail::makeSysEx (group, 0x0, numBytes, data); } static PacketX2 makeSysExStart (uint8_t group, uint8_t numBytes, - const std::byte* data) + const uint8_t* data) { return Detail::makeSysEx (group, 0x1, numBytes, data); } static PacketX2 makeSysExContinue (uint8_t group, uint8_t numBytes, - const std::byte* data) + const uint8_t* data) { return Detail::makeSysEx (group, 0x2, numBytes, data); } static PacketX2 makeSysExEnd (uint8_t group, uint8_t numBytes, - const std::byte* data) + const uint8_t* data) { return Detail::makeSysEx (group, 0x3, numBytes, data); } diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h index e1b268b5..f3d4e01c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h @@ -73,10 +73,7 @@ class Midi1ToBytestreamTranslator { // Utility messages don't translate to bytestream format if (Utils::getMessageType (firstWord) != 0x00) - { - const auto message = fromUmp (PacketX1 { firstWord }, time); - callback (BytestreamMidiView (&message)); - } + callback (fromUmp (PacketX1 { firstWord }, time)); break; } @@ -165,14 +162,16 @@ class Midi1ToBytestreamTranslator void startSysExMessage (double time) { pendingSysExTime = time; - pendingSysExData.push_back (std::byte { 0xf0 }); + pendingSysExData.push_back (0xf0); } template void terminateSysExMessage (MessageCallback&& callback) { - pendingSysExData.push_back (std::byte { 0xf7 }); - callback (BytestreamMidiView (pendingSysExData, pendingSysExTime)); + pendingSysExData.push_back (0xf7); + callback (MidiMessage (pendingSysExData.data(), + int (pendingSysExData.size()), + pendingSysExTime)); pendingSysExData.clear(); } @@ -207,7 +206,7 @@ class Midi1ToBytestreamTranslator return Utils::getMessageType (word) == 0x1 && ((word >> 0x10) & 0xff) >= 0xf8; } - std::vector pendingSysExData; + std::vector pendingSysExData; double pendingSysExTime = 0.0; }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp index 69648092..d4c5e275 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp @@ -28,14 +28,14 @@ namespace 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 })) + const auto needsConversion = (helpers.byte0 >> 0x4) == 0x9 && velocity == 0; + const auto firstByte = needsConversion ? (uint8_t) ((0x8 << 0x4) | (helpers.byte0 & 0xf)) : helpers.byte0; return PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, firstByte, helpers.byte1, std::byte { 0 }), - (uint32_t) (Conversion::scaleTo16 (uint8_t (velocity)) << 0x10) + Utils::bytesToWord (helpers.typeAndGroup, firstByte, helpers.byte1, 0), + (uint32_t) (Conversion::scaleTo16 (velocity) << 0x10) }; } @@ -43,8 +43,8 @@ PacketX2 Midi1ToMidi2DefaultTranslator::processPolyPressure (const HelperValues { return PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, helpers.byte1, std::byte { 0 }), - Conversion::scaleTo32 (uint8_t (helpers.byte2)) + Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, helpers.byte1, 0), + Conversion::scaleTo32 (helpers.byte2) }; } @@ -52,7 +52,7 @@ bool Midi1ToMidi2DefaultTranslator::processControlChange (const HelperValues hel PacketX2& packet) { const auto statusAndChannel = helpers.byte0; - const auto cc = uint8_t (helpers.byte1); + const auto cc = helpers.byte1; const auto shouldAccumulate = [&] { @@ -70,8 +70,8 @@ bool Midi1ToMidi2DefaultTranslator::processControlChange (const HelperValues hel return false; }(); - const auto group = (uint8_t) (helpers.typeAndGroup & std::byte { 0xf }); - const auto channel = (uint8_t) (statusAndChannel & std::byte { 0xf }); + const auto group = (uint8_t) (helpers.typeAndGroup & 0xf); + const auto channel = (uint8_t) (statusAndChannel & 0xf); const auto byte = helpers.byte2; if (shouldAccumulate) @@ -86,13 +86,13 @@ bool Midi1ToMidi2DefaultTranslator::processControlChange (const HelperValues hel 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 value = (uint16_t) (((msb & 0x7f) << 7) | (lsb & 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), + Utils::bytesToWord (helpers.typeAndGroup, (uint8_t) ((newStatus << 0x4) | channel), bank, index), Conversion::scaleTo32 (value) }; return true; @@ -103,41 +103,35 @@ bool Midi1ToMidi2DefaultTranslator::processControlChange (const HelperValues hel if (cc == 0) { - groupBanks[group][channel].setMsb (uint8_t (byte)); + groupBanks[group][channel].setMsb (byte); return false; } if (cc == 32) { - groupBanks[group][channel].setLsb (uint8_t (byte)); + groupBanks[group][channel].setLsb (byte); return false; } packet = PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, statusAndChannel, std::byte { cc }, std::byte { 0 }), - Conversion::scaleTo32 (uint8_t (helpers.byte2)) + Utils::bytesToWord (helpers.typeAndGroup, statusAndChannel, cc, 0), + Conversion::scaleTo32 (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 group = (uint8_t) (helpers.typeAndGroup & 0xf); + const auto channel = (uint8_t) (helpers.byte0 & 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 }) + Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, 0, valid ? 1 : 0), + Utils::bytesToWord (helpers.byte1, 0, valid ? bank.getMsb() : 0, valid ? bank.getLsb() : 0) }; } @@ -145,8 +139,8 @@ PacketX2 Midi1ToMidi2DefaultTranslator::processChannelPressure (const HelperValu { return PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, std::byte { 0 }, std::byte { 0 }), - Conversion::scaleTo32 (uint8_t (helpers.byte1)) + Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, 0, 0), + Conversion::scaleTo32 (helpers.byte1) }; } @@ -154,16 +148,16 @@ PacketX2 Midi1ToMidi2DefaultTranslator::processPitchBend (const HelperValues hel { const auto lsb = helpers.byte1; const auto msb = helpers.byte2; - const auto value = uint16_t (uint16_t (msb) << 7 | uint16_t (lsb)); + const auto value = (uint16_t) (msb << 7 | lsb); return PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, std::byte { 0 }, std::byte { 0 }), + Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, 0, 0), Conversion::scaleTo32 (value) }; } -bool Midi1ToMidi2DefaultTranslator::PnAccumulator::addByte (uint8_t cc, std::byte byte) +bool Midi1ToMidi2DefaultTranslator::PnAccumulator::addByte (uint8_t cc, uint8_t byte) { const auto isStart = cc == 99 || cc == 101; diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h index b644b839..b2a47836 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h @@ -61,10 +61,10 @@ class Midi1ToMidi2DefaultTranslator 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), + (uint8_t) ((0x4 << 0x4) | Utils::getGroup (firstWord)), + (uint8_t) ((firstWord >> 0x10) & 0xff), + (uint8_t) ((firstWord >> 0x08) & 0x7f), + (uint8_t) ((firstWord >> 0x00) & 0x7f), }; switch (Utils::getStatus (firstWord)) @@ -128,10 +128,10 @@ class Midi1ToMidi2DefaultTranslator struct HelperValues { - std::byte typeAndGroup; - std::byte byte0; - std::byte byte1; - std::byte byte2; + uint8_t typeAndGroup; + uint8_t byte0; + uint8_t byte1; + uint8_t byte2; }; static PacketX2 processNoteOnOrOff (const HelperValues helpers); @@ -147,13 +147,13 @@ class Midi1ToMidi2DefaultTranslator class PnAccumulator { public: - bool addByte (uint8_t cc, std::byte byte); + bool addByte (uint8_t cc, uint8_t byte); - const std::array& getBytes() const noexcept { return bytes; } + const std::array& getBytes() const noexcept { return bytes; } PnKind getKind() const noexcept { return kind; } private: - std::array bytes; + std::array bytes; uint8_t index = 0; PnKind kind = PnKind::nrpn; }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp index eaea29f1..9f8e2ef8 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp @@ -39,12 +39,12 @@ SysEx7::PacketBytes SysEx7::getDataBytes (const PacketX2& packet) 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>() } } }, + { { packet.getU8<2>(), + packet.getU8<3>(), + packet.getU8<4>(), + packet.getU8<5>(), + packet.getU8<6>(), + packet.getU8<7>() } }, jmin (numBytes, maxBytes) }; } diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h index c08313c0..0d6e8001 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h @@ -63,7 +63,7 @@ struct SysEx7 /** Holds the bytes from a single SysEx-7 packet. */ struct PacketBytes { - std::array data; + std::array data; uint8_t size; }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h index 53b097b9..2b86a71f 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h @@ -35,12 +35,9 @@ namespace universal_midi_packets 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) + static constexpr uint32_t bytesToWord (uint8_t a, uint8_t b, uint8_t c, uint8_t d) { - return uint32_t (a) << 0x18 - | uint32_t (b) << 0x10 - | uint32_t (c) << 0x08 - | uint32_t (d) << 0x00; + return uint32_t (a << 0x18 | b << 0x10 | c << 0x08 | d << 0x00); } /** Returns the expected number of 32-bit words in a Universal MIDI Packet, given 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 index b34ed0fb..fd52554b 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp @@ -48,18 +48,18 @@ class UniversalMidiPacketTests : public UnitTest forEachNonSysExTestMessage (random, [&] (const MidiMessage& m) { - const auto packets = toMidi1 (m); + Packets packets; + Conversion::toMidi1 (m, packets); 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)); + expect (Utils::getMessageType (packets.data()[0]) == ((m.getRawData()[0] >> 0x4) == 0xf ? 0x1 : 0x2)); translator.dispatch (View {packets.data() }, 0, - [&] (const BytestreamMidiView& roundTripped) + [&] (const MidiMessage& roundTripped) { - expect (equal (m, roundTripped.getMessage())); + expect (equal (m, roundTripped)); }); }); } @@ -68,7 +68,8 @@ class UniversalMidiPacketTests : public UnitTest { { // Zero length message - const auto packets = toMidi1 (createRandomSysEx (random, 0)); + Packets packets; + Conversion::toMidi1 (createRandomSysEx (random, 0), packets); expect (packets.size() == 2); expect (packets.data()[0] == 0x30000000); @@ -77,50 +78,51 @@ class UniversalMidiPacketTests : public UnitTest { const auto message = createRandomSysEx (random, 1); - const auto packets = toMidi1 (message); + Packets packets; + Conversion::toMidi1 (message, packets); 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()[0] == Utils::bytesToWord (0x30, 0x01, sysEx[0], 0)); expect (packets.data()[1] == 0x00000000); } { const auto message = createRandomSysEx (random, 6); - const auto packets = toMidi1 (message); + Packets packets; + Conversion::toMidi1 (message, packets); 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] })); + expect (packets.data()[0] == Utils::bytesToWord (0x30, 0x06, sysEx[0], sysEx[1])); + expect (packets.data()[1] == Utils::bytesToWord (sysEx[2], sysEx[3], sysEx[4], sysEx[5])); } { const auto message = createRandomSysEx (random, 12); - const auto packets = toMidi1 (message); + Packets packets; + Conversion::toMidi1 (message, packets); 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] })); + expect (packets.data()[0] == Utils::bytesToWord (0x30, 0x16, sysEx[0], sysEx[1])); + expect (packets.data()[1] == Utils::bytesToWord (sysEx[2], sysEx[3], sysEx[4], sysEx[5])); + expect (packets.data()[2] == Utils::bytesToWord (0x30, 0x36, sysEx[6], sysEx[7])); + expect (packets.data()[3] == Utils::bytesToWord (sysEx[8], sysEx[9], sysEx[10], sysEx[11])); } { const auto message = createRandomSysEx (random, 13); - const auto packets = toMidi1 (message); + Packets packets; + Conversion::toMidi1 (message, packets); 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()[0] == Utils::bytesToWord (0x30, 0x16, sysEx[0], sysEx[1])); + expect (packets.data()[1] == Utils::bytesToWord (sysEx[2], sysEx[3], sysEx[4], sysEx[5])); + expect (packets.data()[2] == Utils::bytesToWord (0x30, 0x26, sysEx[6], sysEx[7])); + expect (packets.data()[3] == Utils::bytesToWord (sysEx[8], sysEx[9], sysEx[10], sysEx[11])); + expect (packets.data()[4] == Utils::bytesToWord (0x30, 0x31, sysEx[12], 0)); expect (packets.data()[5] == 0x00000000); } } @@ -131,15 +133,15 @@ class UniversalMidiPacketTests : public UnitTest const auto checkRoundTrip = [&] (const MidiBuffer& expected) { for (const auto meta : expected) - Conversion::toMidi1 (ump::BytestreamMidiView (meta), [&] (const auto p) { packets.add (p); }); + Conversion::toMidi1 (meta.getMessage(), packets); MidiBuffer output; converter.dispatch (packets.data(), packets.data() + packets.size(), 0, - [&] (const BytestreamMidiView& roundTripped) + [&] (const MidiMessage& roundTripped) { - output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); + output.addEvent (roundTripped, int (roundTripped.getTimeStamp())); }); packets.clear(); @@ -159,7 +161,8 @@ class UniversalMidiPacketTests : public UnitTest beginTest ("UMP SysEx7 messages interspersed with utility messages convert to bytestream"); { const auto sysEx = createRandomSysEx (random, 100); - const auto originalPackets = toMidi1 (sysEx); + Packets originalPackets; + Conversion::toMidi1 (sysEx, originalPackets); Packets modifiedPackets; @@ -180,9 +183,9 @@ class UniversalMidiPacketTests : public UnitTest converter.dispatch (modifiedPackets.data(), modifiedPackets.data() + modifiedPackets.size(), 0, - [&] (const BytestreamMidiView& roundTripped) + [&] (const MidiMessage& roundTripped) { - output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); + output.addEvent (roundTripped, int (roundTripped.getTimeStamp())); }); // All Utility messages should have been ignored @@ -195,7 +198,8 @@ class UniversalMidiPacketTests : public UnitTest beginTest ("UMP SysEx7 messages interspersed with System Realtime messages convert to bytestream"); { const auto sysEx = createRandomSysEx (random, 200); - const auto originalPackets = toMidi1 (sysEx); + Packets originalPackets; + Conversion::toMidi1 (sysEx, originalPackets); Packets modifiedPackets; MidiBuffer realtimeMessages; @@ -218,9 +222,9 @@ class UniversalMidiPacketTests : public UnitTest converter.dispatch (modifiedPackets.data(), modifiedPackets.data() + modifiedPackets.size(), 0, - [&] (const BytestreamMidiView& roundTripped) + [&] (const MidiMessage& roundTripped) { - output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); + output.addEvent (roundTripped, int (roundTripped.getTimeStamp())); }); const auto numOutputs = output.getNumEvents(); @@ -254,7 +258,8 @@ class UniversalMidiPacketTests : public UnitTest 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 originalPackets; + Conversion::toMidi1 (sysEx, originalPackets); Packets modifiedPackets; MidiBuffer realtimeMessages; @@ -285,9 +290,9 @@ class UniversalMidiPacketTests : public UnitTest converter.dispatch (modifiedPackets.data(), modifiedPackets.data() + modifiedPackets.size(), 0, - [&] (const BytestreamMidiView& roundTripped) + [&] (const MidiMessage& roundTripped) { - output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); + output.addEvent (roundTripped, int (roundTripped.getTimeStamp())); }); const auto numOutputs = output.getNumEvents(); @@ -331,14 +336,19 @@ class UniversalMidiPacketTests : public UnitTest Packets p; for (const auto meta : noteOn) - Conversion::toMidi1 (ump::BytestreamMidiView (meta), [&] (const auto packet) { p.add (packet); }); + Conversion::toMidi1 (meta.getMessage(), p); return p; }(); const auto sysEx = createRandomSysEx (random, 300); - const auto originalPackets = toMidi1 (sysEx); + const auto originalPackets = [&] + { + Packets p; + Conversion::toMidi1 (sysEx, p); + return p; + }(); const auto modifiedPackets = [&] { @@ -368,9 +378,9 @@ class UniversalMidiPacketTests : public UnitTest converter.dispatch (p.data(), p.data() + p.size(), 0, - [&] (const BytestreamMidiView& roundTripped) + [&] (const MidiMessage& roundTripped) { - output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); + output.addEvent (roundTripped, int (roundTripped.getTimeStamp())); }); }; @@ -380,7 +390,8 @@ class UniversalMidiPacketTests : public UnitTest expect (equal (output, noteOn)); const auto newSysEx = createRandomSysEx (random, 300); - const auto newSysExPackets = toMidi1 (newSysEx); + Packets newSysExPackets; + Conversion::toMidi1 (newSysEx, newSysExPackets); // If we push another midi event without interrupting it, // it should get through without being modified, @@ -667,17 +678,12 @@ class UniversalMidiPacketTests : public UnitTest 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 } }; + const uint8_t opcodes[] { 0x0, 0x1, 0x4, 0x5, 0x6, 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 }); + midi2.add (PacketX2 { Utils::bytesToWord (0x40, (uint8_t) (opcode << 0x4), 0, 0), 0x0 }); checkMidi2ToMidi1Conversion (midi2, {}); } } @@ -779,7 +785,7 @@ class UniversalMidiPacketTests : public UnitTest for (const auto cc : CCs) { Packets midi1; - midi1.add (PacketX1 { Utils::bytesToWord (std::byte { 0x20 }, std::byte { 0xb0 }, std::byte { cc }, std::byte { 0x00 }) }); + midi1.add (PacketX1 { Utils::bytesToWord (0x20, 0xb0, cc, 0x00) }); checkMidi1ToMidi2Conversion (midi1, {}); } @@ -868,13 +874,6 @@ class UniversalMidiPacketTests : public UnitTest } 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; @@ -935,10 +934,10 @@ class UniversalMidiPacketTests : public UnitTest { 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))) }; + return PacketX1 { Utils::bytesToWord (0, + uint8_t (status << 0x4), + uint8_t (status == 0 ? 0 : random.nextInt (0x100)), + uint8_t (status == 0 ? 0 : random.nextInt (0x100))) }; } PacketX1 createRandomRealtimeUMP (Random& random) @@ -947,19 +946,19 @@ class UniversalMidiPacketTests : public UnitTest { 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 }; + case 0: return 0xf8; + case 1: return 0xfa; + case 2: return 0xfb; + case 3: return 0xfc; + case 4: return 0xfe; + case 5: return 0xff; } jassertfalse; - return std::byte { 0x00 }; + return 0x00; }(); - return PacketX1 { Utils::bytesToWord (std::byte { 0x10 }, status, std::byte { 0x00 }, std::byte { 0x00 }) }; + return PacketX1 { Utils::bytesToWord (0x10, uint8_t (status), 0x00, 0x00) }; } template diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp index 45555558..4f1c2afc 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp @@ -107,7 +107,7 @@ void MPESynthesiserBase::renderNextBlock (AudioBuffer& outputAudio, int numSamples) { // you must set the sample rate before using this! - jassert (! approximatelyEqual (sampleRate, 0.0)); + jassert (sampleRate != 0); const ScopedLock sl (noteStateLock); @@ -144,7 +144,7 @@ template void MPESynthesiserBase::renderNextBlock (AudioBuffer&, //============================================================================== void MPESynthesiserBase::setCurrentPlaybackSampleRate (const double newRate) { - if (! approximatelyEqual (sampleRate, newRate)) + if (sampleRate != newRate) { const ScopedLock sl (noteStateLock); instrument.releaseAllNotes(); diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp index b86e179b..e51f314d 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp @@ -108,11 +108,14 @@ void MPEZoneLayout::processNextMidiEvent (const MidiMessage& message) if (! message.isController()) return; - if (auto parsed = rpnDetector.tryParse (message.getChannel(), + MidiRPNMessage rpn; + + if (rpnDetector.parseControllerMessage (message.getChannel(), message.getControllerNumber(), - message.getControllerValue())) + message.getControllerValue(), + rpn)) { - processRpnMessage (*parsed); + processRpnMessage (rpn); } } diff --git a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h index dc89dcc2..a9703ac7 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h +++ b/JuceLibraryCode/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h @@ -35,8 +35,6 @@ namespace juce It also defines a pitchbend range (in semitones) to be applied for per-note pitchbends and master pitchbends, respectively. - - @tags{Audio} */ struct MPEZone { diff --git a/JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioLayouts_mac.h b/JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h similarity index 98% rename from JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioLayouts_mac.h rename to JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h index e13dab20..16c4471c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioLayouts_mac.h +++ b/JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h @@ -263,13 +263,13 @@ struct CoreAudioLayouts } } - const auto numChannels = tag & 0xffff; - + auto numChannels = tag & 0xffff; if (tag >= coreAudioHOASN3DLayoutTag && tag <= (coreAudioHOASN3DLayoutTag | 0xffff)) { - const auto ambisonicOrder = AudioChannelSet::getAmbisonicOrderForNumChannels (static_cast (numChannels)); + auto sqrtMinusOne = std::sqrt (static_cast (numChannels)) - 1.0f; + auto ambisonicOrder = jmax (0, static_cast (std::floor (sqrtMinusOne))); - if (ambisonicOrder != -1) + if (static_cast (ambisonicOrder) == sqrtMinusOne) return AudioChannelSet::ambisonic (ambisonicOrder).getChannelTypes(); } diff --git a/JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioTimeConversions_mac.h b/JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioTimeConversions.h similarity index 100% rename from JuceLibraryCode/modules/juce_audio_basics/native/juce_CoreAudioTimeConversions_mac.h rename to JuceLibraryCode/modules/juce_audio_basics/native/juce_mac_CoreAudioTimeConversions.h diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp index cda04184..b0593bb3 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp @@ -51,7 +51,7 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double ne { auto bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer); - if (! approximatelyEqual (newSampleRate, sampleRate) + if (newSampleRate != sampleRate || bufferSizeNeeded != buffer.getNumSamples() || ! isPrepared) { diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp index db30a341..2c8387f5 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp @@ -108,6 +108,26 @@ void MemoryAudioSource::setLooping (bool shouldLoop) //============================================================================== #if JUCE_UNIT_TESTS +static bool operator== (const AudioBuffer& a, const AudioBuffer& b) +{ + if (a.getNumChannels() != b.getNumChannels()) + return false; + + for (int channel = 0; channel < a.getNumChannels(); ++channel) + { + auto* aPtr = a.getReadPointer (channel); + auto* bPtr = b.getReadPointer (channel); + + if (std::vector (aPtr, aPtr + a.getNumSamples()) + != std::vector (bPtr, bPtr + b.getNumSamples())) + { + return false; + } + } + + return true; +} + struct MemoryAudioSourceTests : public UnitTest { MemoryAudioSourceTests() : UnitTest ("MemoryAudioSource", UnitTestCategories::audio) {} @@ -164,7 +184,7 @@ struct MemoryAudioSourceTests : public UnitTest play (source, channelInfo); for (int sample = 0; sample < buffer.getNumSamples(); ++sample) - expectEquals (bufferToFill.getSample (0, sample + buffer.getNumSamples()), buffer.getSample (0, sample)); + expect (bufferToFill.getSample (0, sample + buffer.getNumSamples()) == buffer.getSample (0, sample)); expect (! isSilent (bufferToFill)); } @@ -199,7 +219,7 @@ struct MemoryAudioSourceTests : public UnitTest for (int i = 0; i < 100; ++i) { play (source, channelInfo); - expectEquals (bufferToFill.getSample (0, 0), buffer.getSample (0, (i * blockSize) % buffer.getNumSamples())); + expect (bufferToFill.getSample (0, 0) == buffer.getSample (0, (i * blockSize) % buffer.getNumSamples())); } } } diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp index e936ae2a..e4887270 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp @@ -88,7 +88,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf localRatio = ratio; } - if (! approximatelyEqual (lastRatio, localRatio)) + if (lastRatio != localRatio) { createLowPass (localRatio); lastRatio = localRatio; diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp index 4b9aef4a..0ad3e0b9 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp @@ -62,7 +62,7 @@ void ToneGeneratorAudioSource::releaseResources() void ToneGeneratorAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { - if (approximatelyEqual (phasePerSample, 0.0)) + if (phasePerSample == 0.0) phasePerSample = MathConstants::twoPi / (sampleRate / frequency); for (int i = 0; i < info.numSamples; ++i) diff --git a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index f6e611e1..f9aaff23 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -153,7 +153,7 @@ void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples, bool shoul //============================================================================== void Synthesiser::setCurrentPlaybackSampleRate (const double newRate) { - if (! approximatelyEqual (sampleRate, newRate)) + if (sampleRate != newRate) { const ScopedLock sl (lock); allNotesOff (0, false); @@ -171,7 +171,7 @@ void Synthesiser::processNextBlock (AudioBuffer& outputAudio, int numSamples) { // must set the sample rate before using this! - jassert (! exactlyEqual (sampleRate, 0.0)); + jassert (sampleRate != 0); const int targetChannels = outputAudio.getNumChannels(); auto midiIterator = midiData.findNextSamplePosition (startSample); diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Decibels.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Decibels.h index 937cf0a6..e5eb8026 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Decibels.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Decibels.h @@ -60,20 +60,6 @@ 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_IIRFilter.cpp b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.cpp index c1e32254..fedeb759 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_IIRFilter.cpp @@ -23,8 +23,6 @@ namespace juce { -constexpr auto minimumDecibels = -300.0f; - IIRCoefficients::IIRCoefficients() noexcept { zeromem (coefficients, sizeof (coefficients)); @@ -46,7 +44,7 @@ IIRCoefficients& IIRCoefficients::operator= (const IIRCoefficients& other) noexc IIRCoefficients::IIRCoefficients (double c1, double c2, double c3, double c4, double c5, double c6) noexcept { - const auto a = 1.0 / c4; + auto a = 1.0 / c4; coefficients[0] = (float) (c1 * a); coefficients[1] = (float) (c2 * a); @@ -69,9 +67,9 @@ IIRCoefficients IIRCoefficients::makeLowPass (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - 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); + 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); return IIRCoefficients (c1, c1 * 2.0, @@ -95,9 +93,9 @@ IIRCoefficients IIRCoefficients::makeHighPass (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - 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); + auto n = std::tan (MathConstants::pi * frequency / sampleRate); + auto nSquared = n * n; + auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); return IIRCoefficients (c1, c1 * -2.0, @@ -121,9 +119,9 @@ IIRCoefficients IIRCoefficients::makeBandPass (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - 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); + 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); return IIRCoefficients (c1 * n / Q, 0.0, @@ -147,9 +145,9 @@ IIRCoefficients IIRCoefficients::makeNotchFilter (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - 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); + auto n = 1.0 / std::tan (MathConstants::pi * frequency / sampleRate); + auto nSquared = n * n; + auto c1 = 1.0 / (1.0 + n / Q + nSquared); return IIRCoefficients (c1 * (1.0 + nSquared), 2.0 * c1 * (1.0 - nSquared), @@ -173,9 +171,9 @@ IIRCoefficients IIRCoefficients::makeAllPass (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - 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); + 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); return IIRCoefficients (c1 * (1.0 - n / Q + nSquared), c1 * 2.0 * (1.0 - nSquared), @@ -194,13 +192,13 @@ IIRCoefficients IIRCoefficients::makeLowShelf (double sampleRate, jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); jassert (Q > 0.0); - 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; + 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; return IIRCoefficients (A * (aplus1 - aminus1TimesCoso + beta), A * 2.0 * (aminus1 - aplus1 * coso), @@ -219,13 +217,13 @@ IIRCoefficients IIRCoefficients::makeHighShelf (double sampleRate, jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); jassert (Q > 0.0); - 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; + 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; return IIRCoefficients (A * (aplus1 + aminus1TimesCoso + beta), A * -2.0 * (aminus1 + aplus1 * coso), @@ -244,12 +242,12 @@ IIRCoefficients IIRCoefficients::makePeakFilter (double sampleRate, jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); jassert (Q > 0.0); - 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; + 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; return IIRCoefficients (1.0 + alphaTimesA, c2, diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Interpolators.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Interpolators.h index ccee0fcd..0b2d454c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Interpolators.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_Interpolators.h @@ -73,7 +73,7 @@ class Interpolators sign = (sincPosition < 0 ? -1 : 1); } - if (approximatelyEqual (sincPosition, 0.0f)) + if (sincPosition == 0.0f) result += inputs[samplePosition]; else if (sincPosition < floatCrossings && sincPosition > -floatCrossings) result += inputs[samplePosition] * windowedSinc (firstFrac, index); diff --git a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h index 4c0b2119..e210705e 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h +++ b/JuceLibraryCode/modules/juce_audio_basics/utilities/juce_SmoothedValue.h @@ -237,8 +237,7 @@ class SmoothedValue : public SmoothedValueBase - && approximatelyEqual (initialValue, (FloatType) 0))); + jassert (! (std::is_same_v && initialValue == 0)); // Visual Studio can't handle base class initialisation with CRTP this->currentValue = initialValue; @@ -271,7 +270,7 @@ class SmoothedValue : public SmoothedValueBase target)) + if (newValue == this->target) return; if (stepsToTarget <= 0) @@ -281,8 +280,7 @@ class SmoothedValue : public SmoothedValueBase - && approximatelyEqual (newValue, (FloatType) 0))); + jassert (! (std::is_same_v && newValue == 0)); this->target = newValue; this->countdown = stepsToTarget; 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 4a1a9aed..e2645ef0 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -220,12 +220,6 @@ void AudioDeviceManager::audioDeviceListChanged() sendChangeMessage(); } -void AudioDeviceManager::midiDeviceListChanged() -{ - openLastRequestedMidiDevices (midiDeviceInfosFromXml, defaultMidiOutputDeviceInfo); - sendChangeMessage(); -} - //============================================================================== static void addIfNotNull (OwnedArray& list, AudioIODeviceType* const device) { @@ -436,62 +430,65 @@ String AudioDeviceManager::initialiseFromXML (const XmlElement& xml, if (error.isNotEmpty() && selectDefaultDeviceOnFailure) error = initialise (numInputChansNeeded, numOutputChansNeeded, nullptr, false, preferredDefaultDeviceName); + midiDeviceInfosFromXml.clear(); enabledMidiInputs.clear(); - const auto midiInputs = [&] - { - Array result; + for (auto* c : xml.getChildWithTagNameIterator ("MIDIINPUT")) + midiDeviceInfosFromXml.add ({ c->getStringAttribute ("name"), c->getStringAttribute ("identifier") }); - for (auto* c : xml.getChildWithTagNameIterator ("MIDIINPUT")) - result.add ({ c->getStringAttribute ("name"), c->getStringAttribute ("identifier") }); + auto isIdentifierAvailable = [] (const Array& available, const String& identifier) + { + for (auto& device : available) + if (device.identifier == identifier) + return true; - return result; - }(); + return false; + }; - const MidiDeviceInfo defaultOutputDeviceInfo (xml.getStringAttribute ("defaultMidiOutput"), - xml.getStringAttribute ("defaultMidiOutputDevice")); + auto getUpdatedIdentifierForName = [&] (const Array& available, const String& name) -> String + { + for (auto& device : available) + if (device.name == name) + return device.identifier; - openLastRequestedMidiDevices (midiInputs, defaultOutputDeviceInfo); + return {}; + }; - return error; -} + auto inputs = MidiInput::getAvailableDevices(); -void AudioDeviceManager::openLastRequestedMidiDevices (const Array& desiredInputs, const MidiDeviceInfo& defaultOutput) -{ - const auto openDeviceIfAvailable = [&] (const Array& devices, - const MidiDeviceInfo& deviceToOpen, - auto&& doOpen) + for (auto& info : midiDeviceInfosFromXml) { - const auto iterWithMatchingIdentifier = std::find_if (devices.begin(), devices.end(), [&] (const auto& x) + if (isIdentifierAvailable (inputs, info.identifier)) { - return x.identifier == deviceToOpen.identifier; - }); - - if (iterWithMatchingIdentifier != devices.end()) - { - doOpen (deviceToOpen.identifier); - return; + setMidiInputDeviceEnabled (info.identifier, true); } - - const auto iterWithMatchingName = std::find_if (devices.begin(), devices.end(), [&] (const auto& x) + else { - return x.name == deviceToOpen.name; - }); + auto identifier = getUpdatedIdentifierForName (inputs, info.name); - if (iterWithMatchingName != devices.end()) - doOpen (iterWithMatchingName->identifier); - }; + if (identifier.isNotEmpty()) + setMidiInputDeviceEnabled (identifier, true); + } + } - midiDeviceInfosFromXml = desiredInputs; + MidiDeviceInfo defaultOutputDeviceInfo (xml.getStringAttribute ("defaultMidiOutput"), + xml.getStringAttribute ("defaultMidiOutputDevice")); - const auto inputs = MidiInput::getAvailableDevices(); + auto outputs = MidiOutput::getAvailableDevices(); - for (const auto& info : midiDeviceInfosFromXml) - openDeviceIfAvailable (inputs, info, [&] (const auto identifier) { setMidiInputDeviceEnabled (identifier, true); }); + if (isIdentifierAvailable (outputs, defaultOutputDeviceInfo.identifier)) + { + setDefaultMidiOutputDevice (defaultOutputDeviceInfo.identifier); + } + else + { + auto identifier = getUpdatedIdentifierForName (outputs, defaultOutputDeviceInfo.name); - const auto outputs = MidiOutput::getAvailableDevices(); + if (identifier.isNotEmpty()) + setDefaultMidiOutputDevice (identifier); + } - openDeviceIfAvailable (outputs, defaultOutput, [&] (const auto identifier) { setDefaultMidiOutputDevice (identifier); }); + return error; } String AudioDeviceManager::initialiseWithDefaultDevices (int numInputChannelsNeeded, 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 b3fd6e81..c655254e 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h @@ -499,10 +499,6 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster std::unique_ptr lastExplicitSettings; mutable bool listNeedsScanning = true; AudioBuffer tempBuffer; - MidiDeviceListConnection midiDeviceListConnection = MidiDeviceListConnection::make ([this] - { - midiDeviceListChanged(); - }); struct MidiCallbackInfo { @@ -541,7 +537,6 @@ class JUCE_API AudioDeviceManager : public ChangeBroadcaster 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); @@ -559,7 +554,6 @@ 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); 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 da5bfed5..1405f7e2 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h @@ -25,11 +25,7 @@ namespace juce class AudioIODevice; -/** - Additional information that may be passed to the AudioIODeviceCallback. - - @tags{Audio} -*/ +/** Additional information that may be passed to the AudioIODeviceCallback. */ struct AudioIODeviceCallbackContext { /** If the host provides this information, this field will be set to point to 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 d4cc1739..aa08cece 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h @@ -116,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. diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp index ba60b96e..5449282b 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp @@ -46,11 +46,9 @@ #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 "midi_io/ump/juce_UMPBytestreamInputHandler.h" #include "midi_io/ump/juce_UMPU32InputHandler.h" @@ -65,8 +63,8 @@ #undef Point #undef Component - #include "native/juce_CoreAudio_mac.cpp" - #include "native/juce_CoreMidi_mac.mm" + #include "native/juce_mac_CoreAudio.cpp" + #include "native/juce_mac_CoreMidi.mm" #elif JUCE_IOS #import @@ -77,18 +75,18 @@ #import #endif - #include "native/juce_Audio_ios.cpp" - #include "native/juce_CoreMidi_mac.mm" + #include "native/juce_ios_Audio.cpp" + #include "native/juce_mac_CoreMidi.mm" //============================================================================== #elif JUCE_WINDOWS #if JUCE_WASAPI #include - #include "native/juce_WASAPI_windows.cpp" + #include "native/juce_win32_WASAPI.cpp" #endif #if JUCE_DIRECTSOUND - #include "native/juce_DirectSound_windows.cpp" + #include "native/juce_win32_DirectSound.cpp" #endif #if JUCE_USE_WINRT_MIDI && (JUCE_MSVC || JUCE_CLANG) @@ -115,7 +113,7 @@ #endif #include - #include "native/juce_Midi_windows.cpp" + #include "native/juce_win32_Midi.cpp" #if JUCE_ASIO /* This is very frustrating - we only need to use a handful of definitions from @@ -138,7 +136,7 @@ needed - so to simplify things, you could just copy these into your JUCE directory). */ #include - #include "native/juce_ASIO_windows.cpp" + #include "native/juce_win32_ASIO.cpp" #endif //============================================================================== @@ -152,10 +150,8 @@ 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" + #include "native/juce_linux_ALSA.cpp" #endif #if JUCE_JACK @@ -168,7 +164,7 @@ JUCE with low latency audio support, just set the JUCE_JACK flag to 0. */ #include - #include "native/juce_JackAudio_linux.cpp" + #include "native/juce_linux_JackAudio.cpp" #endif #if (JUCE_LINUX && JUCE_BELA) @@ -179,14 +175,14 @@ #include #include #include - #include "native/juce_Bela_linux.cpp" + #include "native/juce_linux_Bela.cpp" #endif #undef SIZEOF #if ! JUCE_BELA #include - #include "native/juce_Midi_linux.cpp" + #include "native/juce_linux_Midi.cpp" #endif //============================================================================== @@ -198,19 +194,19 @@ namespace juce RealtimeThreadFactory getAndroidRealtimeThreadFactory(); } // namespace juce -#include "native/juce_Audio_android.cpp" +#include "native/juce_android_Audio.cpp" #include - #include "native/juce_Midi_android.cpp" + #include "native/juce_android_Midi.cpp" #if JUCE_USE_ANDROID_OPENSLES || JUCE_USE_ANDROID_OBOE - #include "native/juce_HighPerformanceAudioHelpers_android.h" + #include "native/juce_android_HighPerformanceAudioHelpers.h" #if JUCE_USE_ANDROID_OPENSLES #include #include #include - #include "native/juce_OpenSL_android.cpp" + #include "native/juce_android_OpenSL.cpp" #endif #if JUCE_USE_ANDROID_OBOE @@ -226,7 +222,7 @@ namespace juce #include JUCE_END_IGNORE_WARNINGS_GCC_LIKE - #include "native/juce_Oboe_android.cpp" + #include "native/juce_android_Oboe.cpp" #endif #else // No audio library, so no way to create realtime threads. @@ -253,5 +249,6 @@ namespace juce #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" diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h index 9e0e9a5d..b2685820 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h @@ -32,7 +32,7 @@ ID: juce_audio_devices vendor: juce - version: 7.0.7 + version: 7.0.5 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 @@ -186,5 +186,5 @@ namespace juce #include "audio_io/juce_AudioDeviceManager.h" #if JUCE_IOS - #include "native/juce_Audio_ios.h" + #include "native/juce_ios_Audio.h" #endif 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 e96a9d63..b2baf8c1 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.cpp @@ -23,81 +23,6 @@ namespace juce { -class MidiDeviceListConnectionBroadcaster : 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, 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 ab90cf0c..5dab3f92 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiDevices.h @@ -22,87 +22,6 @@ 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. @@ -142,9 +61,8 @@ struct MidiDeviceInfo String identifier; //============================================================================== - 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(); } + bool operator== (const MidiDeviceInfo& other) const noexcept { return name == other.name && identifier == other.identifier; } + bool operator!= (const MidiDeviceInfo& other) const noexcept { return ! operator== (other); } }; class MidiInputCallback; 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 2fea9724..8eced815 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp @@ -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 (! approximatelyEqual (message.getTimeStamp(), 0.0)); + jassert (message.getTimeStamp() != 0); auto sampleNumber = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate); 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 index 15ee58d3..2733f053 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h @@ -82,9 +82,9 @@ struct U32ToBytestreamHandler : public U32InputHandler void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) override { - dispatcher.dispatch (begin, end, time, [this] (const BytestreamMidiView& m) + dispatcher.dispatch (begin, end, time, [this] (const MidiMessage& m) { - callback.handleIncomingMidiMessage (&input, m.getMessage()); + callback.handleIncomingMidiMessage (&input, m); }); } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/java/app/com/rmsl/juce/JuceMidiSupport.java b/JuceLibraryCode/modules/juce_audio_devices/native/java/app/com/rmsl/juce/JuceMidiSupport.java index 337119b0..0fd83c65 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/java/app/com/rmsl/juce/JuceMidiSupport.java +++ b/JuceLibraryCode/modules/juce_audio_devices/native/java/app/com/rmsl/juce/JuceMidiSupport.java @@ -24,7 +24,6 @@ import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; @@ -44,7 +43,6 @@ 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; @@ -58,7 +56,6 @@ import java.util.List; import static android.content.Context.MIDI_SERVICE; -import static android.content.Context.BLUETOOTH_SERVICE; public class JuceMidiSupport { @@ -80,18 +77,10 @@ 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 BluetoothMidiManager extends ScanCallback + public static class BluetoothManager extends ScanCallback { - BluetoothMidiManager (Context contextToUse) + BluetoothManager (Context contextToUse) { appContext = contextToUse; } @@ -103,7 +92,7 @@ public String[] getMidiBluetoothAddresses () public String getHumanReadableStringForBluetoothAddress (String address) { - BluetoothDevice btDevice = getDefaultBluetoothAdapter (appContext).getRemoteDevice (address); + BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter ().getRemoteDevice (address); return btDevice.getName (); } @@ -114,11 +103,11 @@ public int getBluetoothDeviceStatus (String address) public void startStopScan (boolean shouldStart) { - BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter (appContext); + BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter (); if (bluetoothAdapter == null) { - Log.d ("JUCE", "BluetoothMidiManager error: could not get default Bluetooth adapter"); + Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter"); return; } @@ -126,7 +115,7 @@ public void startStopScan (boolean shouldStart) if (bluetoothLeScanner == null) { - Log.d ("JUCE", "BluetoothMidiManager error: could not get Bluetooth LE scanner"); + Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner"); return; } @@ -151,7 +140,7 @@ public void startStopScan (boolean shouldStart) public boolean pairBluetoothMidiDevice (String address) { - BluetoothDevice btDevice = getDefaultBluetoothAdapter (appContext).getRemoteDevice (address); + BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter ().getRemoteDevice (address); if (btDevice == null) { @@ -554,8 +543,12 @@ public MidiDeviceManager (Context contextToUse) return; } - MidiDeviceInfo[] foundDevices = manager.getDevices (); + openPorts = new HashMap> (); + midiDevices = new ArrayList> (); + openTasks = new HashMap (); + btDevicesPairing = new HashMap (); + MidiDeviceInfo[] foundDevices = manager.getDevices (); for (MidiDeviceInfo info : foundDevices) onDeviceAdded (info); @@ -817,7 +810,6 @@ public void removePort (MidiPortPath path) openPorts.remove (path); } - @Override public void onDeviceAdded (MidiDeviceInfo info) { // only add standard midi devices @@ -827,7 +819,6 @@ public void onDeviceAdded (MidiDeviceInfo info) manager.openDevice (info, this, null); } - @Override public void onDeviceRemoved (MidiDeviceInfo info) { synchronized (MidiDeviceManager.class) @@ -865,11 +856,8 @@ public void onDeviceRemoved (MidiDeviceInfo info) midiDevices.remove (devicePair); } } - - handleDevicesChanged(); } - @Override public void onDeviceStatusChanged (MidiDeviceStatus status) { } @@ -945,7 +933,6 @@ public void onDeviceOpenedDelayed (MidiDevice theDevice) BluetoothGatt gatt = openTasks.get (deviceID).getGatt (); openTasks.remove (deviceID); midiDevices.add (new Pair (theDevice, gatt)); - handleDevicesChanged(); } } else { @@ -986,6 +973,7 @@ public String getPortName (MidiPortPath path) { for (MidiDeviceInfo info : deviceInfos) { + int localIndex = 0; if (info.getId () == path.deviceId) { for (MidiDeviceInfo.PortInfo portInfo : info.getPorts ()) @@ -1060,11 +1048,11 @@ private Pair getMidiDevicePairForId (int deviceId) } private MidiManager manager; - private HashMap btDevicesPairing = new HashMap(); - private HashMap openTasks = new HashMap(); - private ArrayList> midiDevices = new ArrayList>(); + private HashMap btDevicesPairing; + private HashMap openTasks; + private ArrayList> midiDevices; private MidiDeviceInfo[] deviceInfos; - private HashMap> openPorts = new HashMap>(); + private HashMap> openPorts; private Context appContext = null; } @@ -1082,9 +1070,9 @@ public static MidiDeviceManager getAndroidMidiDeviceManager (Context context) return midiDeviceManager; } - public static BluetoothMidiManager getAndroidBluetoothManager (Context context) + public static BluetoothManager getAndroidBluetoothManager (Context context) { - BluetoothAdapter adapter = getDefaultBluetoothAdapter (context); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter (); if (adapter == null) return null; @@ -1095,15 +1083,12 @@ public static BluetoothMidiManager getAndroidBluetoothManager (Context context) synchronized (JuceMidiSupport.class) { if (bluetoothManager == null) - bluetoothManager = new BluetoothMidiManager (context); + bluetoothManager = new BluetoothManager (context); } return bluetoothManager; } - // To be called when devices become (un)available - private native static void handleDevicesChanged(); - private static MidiDeviceManager midiDeviceManager = null; - private static BluetoothMidiManager bluetoothManager = null; + private static BluetoothManager bluetoothManager = null; } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_android.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_android.cpp deleted file mode 100644 index ad5e87b7..00000000 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_android.cpp +++ /dev/null @@ -1,1171 +0,0 @@ -/* - ============================================================================== - - 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_Audio_android.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_android.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_HighPerformanceAudioHelpers_android.h b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_HighPerformanceAudioHelpers.h similarity index 98% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_HighPerformanceAudioHelpers_android.h rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_android_HighPerformanceAudioHelpers.h index 0ba3b10c..b8a6195a 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_HighPerformanceAudioHelpers_android.h +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_HighPerformanceAudioHelpers.h @@ -65,7 +65,7 @@ namespace AndroidHighPerformanceAudioHelpers static bool canUseHighPerformanceAudioPath (int nativeBufferSize, int requestedBufferSize, int requestedSampleRate) { return ((requestedBufferSize % nativeBufferSize) == 0) - && approximatelyEqual ((double) requestedSampleRate, getNativeSampleRate()) + && (requestedSampleRate == getNativeSampleRate()) && isProAudioDevice(); } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp new file mode 100644 index 00000000..dbcae830 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp @@ -0,0 +1,701 @@ +/* + ============================================================================== + + 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. +static const uint8 javaMidiByteCode[] = +{31,139,8,8,43,113,161,94,0,3,106,97,118,97,77,105,100,105,66,121,116,101,67,111,100,101,46,100,101,120,0,149,124,11,124,220, +69,181,255,153,223,99,119,179,217,36,155,77,218,164,105,178,217,164,73,179,165,205,171,233,35,109,146,182,121,180,77,218,164,45, +201,182,72,195,5,183,201,182,217,146,236,134,236,166,180,114,189,20,244,210,162,168,40,80,65,177,162,2,242,18,81,65,80,17,81, +80,81,81,122,149,63,214,39,138,112,69,69,64,20,17,229,218,255,247,204,204,110,126,109,3,213,246,243,221,51,191,51,103,206,204, +156,57,115,230,204,111,183,29,141,237,247,54,181,44,167,234,195,55,252,236,231,159,31,184,160,244,71,71,143,188,248,212,248, +199,142,188,62,189,243,225,179,11,151,53,157,77,52,73,68,251,119,44,11,144,254,243,246,109,68,231,10,197,95,15,60,105,19,157,3, +250,188,139,40,4,250,134,151,232,179,76,115,137,114,64,211,133,68,55,174,33,186,22,26,254,86,79,244,119,224,255,0,163,129,200, +6,22,3,13,64,43,176,14,232,1,54,1,219,128,93,192,81,224,105,224,31,192,63,1,163,145,200,13,132,129,173,192,32,240,54,224,66, +224,82,224,125,192,167,128,91,129,59,128,207,2,247,2,95,6,30,2,30,1,190,3,188,4,20,55,17,173,4,118,1,215,0,15,3,127,0,252,205, +68,109,192,249,192,101,192,221,192,143,128,23,129,130,165,68,29,192,46,224,74,224,51,192,47,129,146,22,162,85,192,249,192,101, +192,17,224,46,224,155,192,79,128,63,2,198,50,216,14,120,47,240,16,240,10,16,90,78,148,0,238,5,126,11,204,89,65,180,2,216,9,188, +3,248,24,240,32,112,28,120,9,48,86,162,47,96,49,176,22,216,1,164,129,107,128,219,129,135,1,187,149,168,9,232,1,222,6,76,0,151, +1,71,128,187,128,175,2,79,0,214,42,244,7,132,129,181,192,32,112,29,112,59,112,63,240,75,224,215,192,115,192,239,129,151,129,191, +1,111,0,98,53,214,1,200,7,138,128,82,32,8,212,0,139,129,165,192,10,96,21,208,1,116,2,235,129,56,112,61,240,16,240,35,224,121, +224,85,64,180,17,121,129,2,160,20,88,8,180,0,107,129,141,192,185,192,52,112,37,240,81,224,115,192,35,192,15,128,227,192,239, +129,87,128,215,1,119,59,244,0,149,192,66,160,30,88,1,116,3,3,192,78,96,4,184,8,216,15,92,2,28,2,62,0,220,0,124,10,248,60,240,53, +224,7,192,79,129,231,128,63,1,175,3,86,7,244,3,107,129,62,96,28,120,31,112,45,240,73,224,110,224,94,224,107,192,163,192,49,224, +23,192,235,192,92,236,133,122,160,11,56,15,72,1,239,4,174,6,62,10,220,9,220,15,60,12,252,1,120,5,120,29,48,214,98,46,192,86, +224,0,112,45,112,23,112,31,240,13,224,127,128,223,0,127,4,222,0,242,214,97,254,64,24,88,9,172,3,54,1,219,128,97,224,124,96,4, +136,3,147,192,37,192,97,224,58,224,40,240,105,224,94,224,33,224,49,224,41,224,103,192,111,128,151,128,191,1,255,4,188,157,68, +21,64,61,176,26,88,3,116,3,189,192,22,96,59,112,1,16,3,246,2,83,192,21,192,199,129,251,128,199,129,231,128,215,0,111,23,209,60, +96,17,176,14,56,27,24,3,46,2,46,5,62,8,220,9,124,9,248,54,240,4,240,28,240,119,192,213,13,95,6,26,128,94,224,60,96,28,184,24, +184,12,184,10,184,6,248,56,112,43,240,21,224,199,192,203,192,95,129,55,0,119,15,81,33,48,31,88,12,44,7,122,128,45,192,249,64, +12,216,11,92,14,188,7,56,2,124,10,120,8,248,22,240,4,240,99,224,23,192,51,192,171,128,23,65,178,8,168,0,106,129,197,192,70,224, +108,96,23,144,4,46,5,174,2,62,4,220,0,220,12,124,1,248,10,240,117,224,123,192,83,192,207,129,103,129,151,128,191,1,198,6,216, +11,88,6,108,1,182,3,5,136,185,197,64,53,176,0,168,1,106,129,133,64,29,16,6,22,1,103,1,139,129,37,0,194,49,33,180,18,66,34,33, +252,17,194,28,33,164,17,66,22,33,68,17,194,18,33,244,16,66,11,33,108,16,182,63,97,203,18,182,26,97,59,16,220,154,224,114,132, +37,36,44,5,193,148,212,163,207,7,12,137,54,2,189,64,31,176,9,216,12,244,3,3,192,22,96,43,128,99,133,112,220,208,32,48,4,68,128, +29,192,219,128,97,224,63,128,11,248,252,1,118,1,163,64,12,216,13,140,3,255,9,92,10,28,4,46,3,46,7,222,5,188,155,148,77,50,127, +252,154,78,98,226,133,186,188,31,229,50,80,67,63,115,217,212,229,74,93,158,212,50,150,230,87,233,242,65,205,247,56,228,113,4, +210,85,154,159,171,249,243,129,60,224,90,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,82,151,25,55,106,153,106,45,83,161,203,55,47,81,178,92,190,75,203,215,56,218,214,234,182,220,15,251, +208,3,122,12,13,142,113,54,58,198,214,228,24,27,151,31,94,162,242,2,46,63,182,100,134,159,177,103,179,67,79,179,99,252,92,62, +230,40,103,230,184,204,209,87,171,163,47,246,201,227,154,191,90,243,217,47,58,116,121,66,151,185,109,66,151,127,133,114,82,151, +159,95,162,114,26,46,255,5,229,139,116,217,194,230,216,175,203,62,148,167,116,185,20,229,148,46,135,80,222,167,203,75,80,190, +88,151,151,57,202,235,234,103,116,246,59,202,55,58,250,138,56,248,231,57,250,29,117,240,39,29,229,253,142,126,15,58,248,135,29, +109,175,70,249,64,166,47,135,252,109,40,191,67,151,239,113,180,61,230,24,15,175,93,70,254,49,7,127,210,81,126,208,209,215,163, +40,79,103,244,160,124,137,46,31,119,216,234,87,40,167,117,249,133,122,181,111,215,232,53,122,167,46,243,26,253,151,46,179,253, +51,229,135,29,252,140,255,116,234,182,92,238,114,248,67,183,195,31,122,52,127,190,46,95,43,125,190,137,238,39,69,215,10,110, +83,64,87,201,182,205,244,1,73,87,210,135,36,245,80,135,96,31,46,165,247,242,90,163,247,231,37,21,244,71,73,107,169,74,214,47,164, +197,130,227,66,177,148,171,210,252,42,205,95,160,159,153,110,19,188,199,44,250,48,49,245,211,95,36,85,245,53,186,190,86,143,167, +22,145,247,136,164,93,116,167,164,37,244,138,164,203,232,53,93,95,46,20,13,10,181,71,111,39,166,107,232,247,164,227,190,224, +216,95,73,31,228,50,36,95,37,142,117,30,122,84,82,147,190,37,169,77,63,37,142,117,110,250,184,164,213,244,85,77,159,228,117, +192,137,241,49,77,63,43,169,69,223,150,116,11,45,135,126,27,124,55,113,28,236,165,62,193,116,5,13,8,190,3,40,190,55,75,189,116, +189,164,57,180,30,245,62,173,39,79,215,231,129,115,189,164,185,212,45,20,237,17,28,35,243,232,235,196,180,138,126,70,28,199, +213,120,252,136,164,63,144,180,128,74,4,83,63,205,23,28,219,213,184,57,198,63,165,233,207,73,197,215,239,75,58,72,199,37,45,164, +159,104,62,215,23,107,189,197,56,165,214,65,207,28,61,174,18,156,74,223,145,180,137,230,8,166,171,105,174,164,29,212,44,105, +59,237,16,28,167,85,251,82,216,255,168,166,108,175,121,90,79,25,198,255,32,113,60,13,208,151,136,227,176,65,183,72,63,92,47,235, +217,239,20,21,244,136,164,181,244,61,73,183,211,15,37,221,72,66,250,235,98,42,148,116,9,5,36,61,155,106,36,221,68,155,36,221, +64,219,165,95,174,147,250,66,122,92,76,239,149,84,217,39,132,72,254,11,73,7,232,15,186,62,79,182,235,167,34,73,55,83,151,80,252, +94,77,251,165,95,175,149,122,171,180,222,42,173,183,74,235,173,210,250,170,116,251,42,221,190,74,183,175,214,237,170,181,124, +181,150,175,214,242,213,90,190,90,203,47,192,78,231,254,22,32,43,49,228,243,50,50,53,181,36,93,74,182,164,203,201,165,169,91, +243,243,53,45,144,180,153,252,154,22,203,253,214,37,245,214,160,255,143,72,90,77,223,144,212,69,223,37,117,22,62,46,233,89, +180,90,238,51,181,62,181,122,190,181,240,148,251,36,157,71,95,148,116,33,61,36,169,90,191,90,248,205,99,146,238,160,39,36,221,78, +199,52,253,31,73,139,232,71,146,214,208,255,147,116,62,253,88,210,85,228,145,253,181,82,142,166,94,161,248,185,146,182,145,79, +168,120,80,42,233,92,154,39,105,41,149,73,186,149,170,37,109,164,5,146,118,83,139,164,27,40,34,227,68,189,156,199,66,100,94, +247,232,56,241,180,140,15,103,97,230,138,186,37,157,67,95,147,180,140,30,38,62,235,23,75,126,163,150,135,118,26,18,76,43,232, +109,130,207,118,213,174,73,219,167,9,158,254,77,226,51,92,245,211,12,59,255,142,56,183,236,145,114,45,240,124,222,15,203,116, +187,101,144,59,172,159,111,212,207,55,73,90,71,47,232,231,165,66,229,1,27,37,141,208,160,224,28,53,76,239,35,206,83,149,158, +21,186,253,10,200,127,66,210,74,217,207,10,100,191,47,75,26,162,38,161,248,172,111,165,110,183,82,247,191,82,247,179,82,247,179, +82,247,211,138,241,255,146,152,6,233,159,196,121,135,26,215,106,77,219,180,158,54,100,187,107,4,231,199,234,185,93,251,23,159, +77,96,203,119,35,36,227,2,206,50,36,226,55,32,17,62,178,69,229,97,194,53,147,71,113,253,213,168,127,98,139,122,14,233,246,204, +127,251,18,69,111,66,253,31,116,125,149,174,111,114,212,63,128,250,186,173,170,126,129,214,107,59,244,31,67,253,144,174,175,209, +252,118,71,253,175,80,255,30,93,95,171,245,207,1,198,180,254,151,81,255,57,93,191,80,183,115,142,127,29,228,22,109,83,207, +117,142,241,101,234,183,161,190,91,215,115,14,30,197,197,96,108,64,201,165,52,189,124,96,166,238,26,71,249,227,186,254,14,7, +239,11,186,252,16,232,55,29,229,99,3,42,151,103,153,159,1,255,171,219,254,73,83,99,139,162,69,154,134,53,237,208,52,162,105,108, +203,76,95,251,53,239,93,91,88,183,33,203,231,111,80,247,140,73,127,30,158,171,225,59,147,254,79,226,121,216,111,33,234,15,251, +13,26,14,24,56,183,88,158,245,36,55,168,123,66,4,53,23,249,175,32,62,21,19,161,113,172,181,87,222,13,44,45,183,111,131,186,67, +92,36,123,241,137,68,200,192,126,130,172,223,150,207,124,30,152,168,99,217,119,109,80,103,94,36,100,81,164,202,130,204,45,168, +241,138,5,184,224,38,66,183,98,124,62,248,98,143,148,177,101,22,128,188,17,109,230,130,78,249,111,71,159,62,49,229,255,52,183, +49,90,141,60,240,110,67,153,219,248,40,16,72,52,173,130,39,133,95,206,215,35,35,58,186,65,217,129,239,53,46,57,51,220,179,55, +168,123,99,160,112,105,177,77,129,170,150,226,66,140,163,16,253,249,176,127,114,41,210,204,227,226,91,148,207,72,132,62,5,223, +13,116,182,20,87,34,126,205,161,50,227,66,186,40,212,12,222,76,139,192,41,45,110,150,181,150,182,69,59,34,105,190,156,11,247, +253,205,13,234,94,227,180,85,39,180,32,122,106,253,95,208,250,3,162,64,68,154,149,229,133,148,252,79,105,169,240,171,94,104,98, +237,79,109,80,239,56,3,37,1,151,214,7,61,94,42,179,160,199,206,147,122,34,232,59,33,47,151,62,177,74,100,234,124,186,46,252, +74,107,206,114,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,207,193,45,215,103,5,106,185,20,161,5,233,117,232,161,0,173,125,246,128,109,185,46,242,127,68,181,247,23,161,149,207,78, +172,203,165,206,255,14,127,53,17,242,225,6,28,254,18,101,253,203,187,81,221,73,79,246,175,75,225,95,249,200,211,92,202,231,55, +170,123,232,164,191,1,109,134,23,228,208,112,141,139,134,107,189,180,115,161,7,150,63,47,228,150,107,107,75,255,18,84,183,81, +197,146,128,25,233,116,81,171,112,19,211,132,127,49,234,34,157,57,224,228,72,26,233,242,162,175,255,130,157,135,187,161,179,219, +5,45,121,122,5,2,122,5,194,207,171,120,196,186,133,168,199,241,37,228,152,218,209,7,199,206,132,159,51,254,164,255,42,237,95, +25,31,239,217,168,226,104,36,132,126,170,184,159,41,233,215,133,50,142,8,233,151,155,54,170,189,26,128,229,132,230,109,219,56, +227,171,249,152,63,223,221,119,108,84,123,171,35,215,71,67,151,121,200,125,208,125,141,184,89,220,103,125,107,159,167,83,203, +90,250,246,191,219,209,222,208,99,153,218,168,98,98,196,159,163,60,213,15,171,64,98,187,223,45,125,133,159,19,161,37,24,95,192, +127,158,223,125,82,219,75,222,162,109,107,182,109,61,183,165,76,91,30,11,143,225,221,122,109,39,253,124,235,24,22,62,26,54,114, +105,24,222,148,159,93,171,107,29,107,149,171,215,42,23,86,93,32,215,202,167,215,202,135,181,202,203,174,21,244,116,231,254, +27,107,117,123,118,173,250,102,93,171,207,101,215,10,253,84,229,205,186,86,247,103,214,10,158,232,214,51,252,42,120,69,220, +174,89,143,28,52,177,174,134,58,99,51,99,235,20,122,108,175,169,119,50,122,108,222,204,122,63,233,88,175,12,239,167,14,158,41,71, +137,115,110,163,122,143,51,44,10,96,79,182,234,176,145,47,227,180,122,27,244,130,163,77,198,23,254,58,11,207,232,117,198,66, +75,206,41,191,87,197,227,64,168,197,46,128,15,36,224,179,38,172,193,81,195,195,251,23,113,231,157,242,54,51,163,167,162,247,116, +221,139,102,225,45,159,133,215,217,123,242,252,248,207,192,44,188,115,28,60,91,90,14,231,90,47,71,8,182,67,17,236,240,25,105, +7,156,91,102,33,13,91,60,66,75,90,209,164,169,94,245,30,168,220,168,161,10,35,32,134,155,3,88,249,187,81,3,159,109,246,99,189, +10,36,77,192,103,133,46,113,180,97,73,63,158,11,33,225,149,52,225,47,213,252,66,10,26,184,235,137,160,81,39,242,68,248,53,158, +205,60,212,85,202,241,153,50,255,112,73,159,90,116,105,253,146,69,186,108,208,7,122,85,78,90,110,98,44,102,100,16,186,141,5, +196,52,225,159,199,49,83,36,252,213,242,52,11,44,107,233,154,11,110,21,71,123,163,204,186,6,190,216,136,8,204,103,155,137,253, +230,201,158,48,65,179,16,40,195,242,133,223,200,67,169,206,80,249,195,66,180,172,215,190,36,112,255,202,248,239,103,122,85,125, +196,239,207,250,53,215,124,161,55,115,190,171,209,200,248,235,87,111,10,213,249,174,246,233,151,122,249,30,137,57,8,204,65,68, +154,2,24,77,30,69,154,10,249,14,64,252,28,89,10,13,161,125,136,220,65,193,254,31,20,117,164,250,44,146,125,21,99,223,168,179, +252,209,94,245,14,53,96,77,250,107,97,185,225,72,49,13,239,40,134,69,138,169,204,252,51,180,148,224,70,228,51,42,141,18,26,30, +44,1,191,132,86,225,124,42,51,176,163,204,49,185,219,113,115,194,153,181,2,62,240,81,62,19,6,231,225,105,25,158,62,36,159,74, +79,170,43,59,233,105,142,212,151,240,47,100,203,83,149,25,48,150,47,245,210,6,76,51,17,74,35,214,29,55,12,172,173,148,105,90, +68,131,86,248,113,143,62,171,254,212,171,114,207,200,72,41,218,31,225,157,97,113,30,98,145,215,108,53,155,100,30,98,201,113,7, +41,130,160,86,105,250,165,77,205,153,21,54,3,203,91,134,126,123,66,175,176,89,102,171,21,30,202,174,240,79,78,232,21,54,19,161, +247,227,172,101,205,79,156,40,52,2,70,248,159,46,61,142,185,125,234,253,120,164,171,12,250,63,206,243,48,166,252,119,104,250, +105,80,110,53,95,142,199,144,154,27,41,210,13,217,208,81,57,150,74,172,97,194,63,34,71,192,189,12,73,249,103,79,20,138,128,8, +255,147,227,140,242,158,230,62,245,174,189,220,174,165,10,59,146,230,89,95,207,179,181,22,116,117,241,89,144,86,118,192,156,93, +66,102,100,46,212,181,90,115,101,207,46,104,175,52,131,116,28,241,46,130,43,81,165,165,172,193,121,193,128,101,8,33,194,191,13, +218,133,70,158,21,180,235,44,246,139,6,217,107,163,244,19,254,59,214,167,222,209,151,91,232,31,190,50,193,185,150,57,60,52, +151,130,214,204,106,39,66,23,82,140,103,162,86,196,228,113,152,114,28,65,57,14,53,227,133,50,170,207,145,185,203,53,216,81,9, +255,229,188,30,214,69,254,43,117,166,195,220,240,51,121,102,208,170,51,121,158,108,197,5,77,156,145,222,192,86,196,92,144,145, +154,60,78,142,24,43,179,123,108,5,185,244,110,186,70,175,75,185,129,241,26,145,78,101,29,49,51,38,209,42,74,51,99,194,170,4, +229,109,183,82,40,187,200,252,206,95,41,243,187,150,179,159,63,17,52,56,222,4,40,252,127,42,226,168,189,84,39,123,10,19,231,157, +220,239,93,125,234,59,136,114,47,250,244,182,250,139,41,48,47,209,148,162,27,243,124,88,211,45,20,249,250,60,204,224,99,200, +197,185,119,55,86,45,232,45,164,192,162,240,139,131,205,243,105,50,116,17,238,192,62,119,171,123,21,69,246,97,44,46,68,70,87,11, +164,90,225,175,131,205,21,104,59,31,121,183,15,121,118,14,110,11,33,114,127,221,122,118,159,139,223,144,182,98,6,172,189,218, +106,129,158,171,97,197,68,211,77,212,108,5,189,225,99,24,177,183,78,168,246,101,220,222,211,234,249,221,137,106,156,131,147,235, +122,233,225,214,240,51,65,47,102,246,32,201,251,124,55,102,196,223,189,168,44,104,163,244,63,94,255,122,48,249,253,98,192,21, +73,97,55,134,206,194,126,80,190,16,73,205,129,205,110,228,83,42,165,246,128,90,249,59,100,182,201,214,182,165,79,151,72,107,219, +210,3,26,149,44,246,64,145,92,77,222,3,187,32,31,254,157,90,243,128,57,140,250,32,116,207,59,85,163,99,151,151,57,118,249,66, +146,178,208,184,64,106,108,65,187,15,202,118,149,102,53,228,182,177,246,223,12,239,155,7,185,217,34,70,117,70,151,242,5,83,80, +43,34,6,211,74,211,146,39,147,233,120,114,201,167,76,84,41,101,221,63,83,122,175,69,153,179,26,206,190,56,143,89,4,251,45,211, +249,141,192,109,182,64,223,21,70,54,169,247,211,51,123,39,242,212,92,170,218,19,48,197,242,229,211,45,216,107,46,59,225,15,113, +244,241,84,93,9,238,178,229,215,214,32,50,122,144,199,7,101,244,106,249,205,92,170,246,46,204,156,16,185,129,162,150,53,240, +166,162,132,191,130,235,125,147,235,14,210,151,31,225,189,244,73,58,110,90,66,44,11,63,29,48,195,127,58,110,218,66,44,15,255,160, +208,176,245,222,190,114,147,218,43,129,80,185,192,138,194,243,63,192,86,49,86,25,33,120,83,28,62,193,62,135,155,153,63,200,39, +245,210,57,210,139,187,228,141,207,141,179,37,252,87,117,194,76,134,246,106,89,139,185,127,8,138,128,204,3,121,190,243,49,38, +158,77,142,180,67,69,54,167,61,186,73,249,93,192,63,25,74,200,155,99,97,182,238,230,76,93,104,166,46,160,199,124,199,38,245,253, +158,7,188,237,158,114,106,245,108,193,189,75,237,60,15,242,163,72,46,172,249,96,192,35,174,92,254,157,77,176,91,110,78,192, +208,59,218,195,109,6,243,230,83,203,241,85,196,54,134,230,188,170,95,6,60,203,159,111,166,13,86,158,135,45,140,249,187,90,246, +177,117,43,164,135,200,54,5,21,212,242,231,114,240,202,217,83,108,182,19,124,27,251,41,63,147,39,120,202,114,254,33,79,145,235, +81,95,105,55,243,14,183,3,117,225,123,143,123,60,34,124,236,184,39,71,136,43,195,247,7,189,101,72,142,195,127,206,243,96,111, +122,56,131,28,68,235,183,103,227,216,5,217,187,241,156,205,234,59,56,142,172,221,50,42,201,40,230,184,115,23,56,238,220,11,101, +44,133,55,24,45,131,47,159,96,107,241,25,98,105,159,59,107,179,186,187,241,249,199,185,87,160,185,197,111,194,79,35,56,227,3, +34,177,46,76,77,129,240,235,252,78,219,144,57,212,50,200,223,196,241,44,15,86,205,75,139,121,188,91,61,108,69,15,172,19,112,7, +114,248,198,23,241,149,225,182,248,30,62,99,242,217,47,94,128,239,173,242,157,47,123,129,156,47,176,166,229,133,38,142,132,176, +144,135,124,190,178,124,117,206,190,32,45,132,115,214,86,183,115,68,6,75,233,83,254,213,143,182,173,190,118,154,225,29,5,207, +231,174,116,91,14,222,39,192,171,206,173,145,28,15,252,205,99,32,23,61,39,74,185,85,167,141,13,17,241,133,220,234,252,54,236, +183,59,49,235,214,156,38,42,242,29,115,95,246,176,237,231,156,17,59,121,221,93,244,144,63,152,151,159,25,143,143,117,240,14,120, +152,124,222,86,47,188,234,46,65,238,135,43,97,77,55,226,180,71,198,5,183,140,7,110,74,227,60,41,162,96,94,248,215,121,190,96, +94,157,175,200,55,74,225,31,230,249,194,175,241,89,49,141,21,226,239,215,216,155,142,100,115,185,235,197,165,245,71,197,245,130, +243,62,67,230,218,15,108,86,223,195,149,187,97,115,55,159,69,94,156,232,108,115,156,231,70,228,136,154,143,193,235,0,27,93,129, +117,104,117,33,154,30,65,236,9,93,71,183,226,121,149,171,150,78,150,59,10,57,159,171,210,197,81,118,84,230,2,39,215,127,2,245, +172,161,218,19,164,201,166,165,116,155,201,81,227,10,10,186,243,73,143,192,226,213,227,220,162,204,163,86,239,10,25,199,177,122, +162,92,90,75,200,92,85,82,23,91,44,142,213,109,181,225,91,67,42,102,182,154,30,29,69,85,244,228,168,233,165,240,19,121,174, +240,247,243,92,65,119,157,75,157,169,156,87,76,234,120,121,177,220,15,136,82,151,214,167,247,209,62,105,35,142,13,158,126,245, +61,127,57,102,90,33,109,227,37,175,205,231,231,185,242,253,207,64,54,54,71,4,122,23,22,5,138,3,115,90,144,248,4,172,200,117, +234,68,177,229,217,116,135,166,242,140,194,138,190,120,66,159,81,242,68,25,172,155,79,90,187,167,229,208,139,39,100,91,88,179, +86,122,174,58,97,108,89,86,39,12,34,100,113,248,241,86,225,209,55,25,117,139,137,92,199,107,243,97,212,6,93,200,197,237,160, +171,206,86,115,61,87,238,245,157,217,119,99,155,251,79,190,15,242,253,121,168,63,27,31,183,93,66,75,35,62,153,109,24,178,238, +63,250,213,61,148,223,197,5,140,201,193,75,168,219,207,245,57,114,191,27,20,235,87,191,169,8,20,113,236,227,93,195,107,196,223, +90,114,70,102,82,129,161,78,112,126,135,232,134,5,91,109,68,115,43,252,42,238,48,86,157,17,153,154,67,186,149,29,112,241,238, +25,116,69,166,138,193,155,43,179,219,192,156,106,215,2,248,203,14,218,231,78,172,195,9,24,243,33,171,193,138,59,218,201,86,34, +104,137,165,225,239,242,189,245,16,133,255,174,222,121,122,49,83,254,158,117,177,180,65,105,54,70,189,191,95,189,31,200,196, +164,58,196,36,117,15,85,86,250,176,182,71,196,63,79,238,126,126,55,165,252,194,162,155,250,213,111,63,120,142,94,185,51,56,186, +169,157,20,57,162,162,202,173,146,143,123,204,17,21,81,110,149,107,13,255,52,212,170,25,114,213,12,93,255,9,212,243,169,118, +187,244,230,0,234,98,236,41,50,91,177,179,187,140,178,187,136,61,31,178,176,46,124,175,251,164,91,135,43,17,186,24,187,66,101, +11,65,119,248,94,229,245,121,162,204,133,155,148,167,72,222,164,174,192,153,160,222,211,244,192,42,91,244,253,251,108,29,47, +206,209,249,174,65,67,151,214,239,24,202,190,219,249,75,191,243,221,206,185,98,62,157,103,148,211,185,102,133,124,107,165,110, +157,214,128,122,39,31,16,171,16,115,11,112,142,188,79,102,78,76,57,147,111,89,250,218,9,247,54,62,65,6,187,42,104,16,109,91, +150,190,120,98,123,87,57,109,55,203,81,126,254,196,96,215,124,240,113,106,46,125,230,68,160,48,252,116,230,29,31,206,168,1,245, +125,68,53,238,11,131,157,243,233,135,129,131,160,21,84,100,30,164,229,205,69,178,124,79,213,100,232,16,199,88,255,97,121,62, +109,239,196,153,141,157,18,248,211,231,171,10,68,145,184,148,194,47,65,235,223,213,13,25,121,211,128,246,123,216,172,18,8,98,46, +153,119,72,77,3,42,167,80,243,205,215,177,212,160,229,3,250,253,149,152,121,251,106,82,161,208,239,91,113,86,254,227,68,185, +81,135,251,192,46,17,164,85,2,89,180,168,196,154,181,32,35,143,131,19,148,252,240,243,153,28,191,80,234,16,250,187,18,33,51,24, +83,247,181,117,64,125,71,83,70,234,142,108,200,222,44,249,61,113,57,114,161,10,177,11,245,171,136,51,245,90,244,177,7,109,121, +38,65,201,15,191,152,185,147,231,233,62,42,178,125,148,203,223,192,10,34,202,216,130,231,122,83,64,125,199,116,27,232,61,217, +95,217,170,63,15,234,231,140,60,255,198,234,49,240,142,157,194,223,164,249,63,63,165,253,243,167,60,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,152,214,27,7,173,193,243,133,160,75,138,212,111,63,150, +129,182,23,157,172,191,247,148,103,65,250,251,44,82,177,140,41,255,150,199,144,180,89,198,202,2,100,203,182,174,43,119,204,73, +144,202,105,12,253,100,106,186,134,102,126,167,149,249,30,201,144,180,73,62,47,212,252,238,172,156,79,183,85,191,50,81,125,173, +205,182,103,152,89,204,149,177,203,208,54,50,29,182,82,60,151,230,185,36,79,149,221,89,93,57,154,250,53,13,104,153,128,214,43, +223,179,211,204,247,103,114,143,201,12,90,217,158,101,249,251,228,133,186,95,254,46,119,161,222,139,181,248,107,105,234,215, +241,97,149,110,179,74,223,73,88,174,93,239,165,53,186,110,173,30,191,149,45,203,89,135,201,12,247,225,14,179,136,106,154,90,186, +90,155,214,47,239,172,95,223,179,190,181,126,89,87,75,75,125,231,202,229,205,245,43,186,215,183,44,91,223,189,172,123,101, +19,76,139,124,173,125,100,60,158,136,167,215,144,171,93,81,99,77,27,89,107,218,22,237,224,79,148,253,93,227,211,177,116,50,153, +30,27,136,38,162,123,98,83,180,250,84,78,40,54,53,149,156,90,29,26,73,78,143,143,134,18,201,116,104,79,44,29,202,74,133,250, +215,135,82,35,209,68,2,109,215,254,107,109,71,99,187,163,211,227,78,29,209,209,232,100,26,10,202,122,166,39,38,14,100,249,27, +163,233,116,119,116,124,124,87,116,228,66,18,125,100,244,245,147,217,215,223,79,149,125,91,67,235,247,143,196,38,211,241,36,130, +249,88,124,60,22,26,25,79,166,226,137,61,161,201,228,84,154,106,251,182,190,89,253,68,124,52,142,33,236,139,143,196,72,108,34, +107,211,246,238,245,84,184,105,122,36,54,128,154,190,196,228,116,122,27,171,8,100,88,91,167,211,25,158,47,195,147,79,197,153, +167,161,233,73,238,181,97,111,116,95,148,68,63,25,253,125,100,246,247,201,15,244,128,15,100,22,24,182,217,143,15,171,191,127, +103,63,213,244,71,19,163,83,201,248,104,227,174,204,108,27,179,243,238,84,230,104,163,5,111,37,213,35,231,208,70,85,111,37,196, +38,108,163,69,103,18,201,88,185,141,26,207,40,58,22,157,138,142,96,120,241,84,58,62,210,70,139,207,212,160,39,150,26,153,138, +79,166,147,83,179,15,100,60,54,35,223,31,27,82,190,52,251,220,33,202,245,51,163,125,19,125,44,180,33,62,142,65,214,116,77,199, +199,71,89,223,108,102,58,73,244,45,69,6,99,41,184,236,236,179,213,34,67,177,116,26,14,150,154,233,242,45,166,144,17,110,163, +121,89,161,145,100,34,29,75,164,27,187,153,238,71,103,149,217,170,137,216,104,60,218,200,174,219,200,14,151,89,250,37,111,45,208, +151,216,157,172,97,87,229,130,115,56,111,42,221,70,181,111,45,52,148,142,166,167,49,234,234,55,19,203,110,32,167,43,157,34,163, +163,67,141,82,57,179,154,43,207,212,96,107,66,53,217,58,25,75,196,70,251,225,129,49,233,43,161,51,52,124,139,185,207,236,110, +231,250,159,34,52,24,27,137,197,247,177,158,162,172,72,50,213,216,53,157,24,29,199,50,20,59,153,189,81,102,66,180,196,201,221, +22,157,26,137,141,111,159,142,143,182,81,32,91,49,157,142,143,55,246,39,247,156,198,219,22,141,79,57,250,202,242,218,104,251, +233,204,246,51,184,201,25,227,3,14,130,166,254,145,228,68,227,212,68,106,188,113,47,162,90,227,41,161,173,230,212,200,222,70, +205,103,104,113,90,68,109,163,165,255,98,19,231,154,44,249,23,219,40,233,254,51,72,207,88,37,235,131,111,122,226,180,81,207,191, +173,109,134,195,46,26,137,166,46,60,179,161,78,211,114,230,73,103,38,188,45,154,30,227,48,241,150,210,188,89,71,163,227,251, +226,23,54,34,180,38,177,129,113,40,54,174,79,232,3,177,123,60,154,194,134,14,206,34,211,199,145,88,215,87,205,82,63,16,155,216, +165,5,98,16,169,152,69,100,40,190,39,129,136,49,133,93,82,54,75,117,100,108,42,121,49,154,206,233,231,179,179,49,158,108,116, +28,220,109,84,168,216,227,209,196,158,70,61,142,34,7,171,15,113,82,218,43,224,96,110,221,181,55,54,146,62,153,55,148,158,194, +76,179,221,72,158,236,58,186,139,247,111,185,131,61,21,219,221,120,78,44,122,225,96,108,119,108,42,150,64,146,80,241,86,181, +188,249,101,181,220,141,157,83,83,209,3,28,150,50,61,157,204,109,163,238,217,216,237,255,206,106,175,225,67,111,86,37,167,77,119, +77,214,8,51,162,169,147,121,189,209,20,118,244,100,198,170,78,222,233,130,56,179,78,19,4,239,100,19,244,225,36,141,202,179,190, +192,193,149,54,241,159,194,104,163,150,83,56,237,103,60,128,215,156,172,87,118,95,232,96,68,226,19,236,16,115,78,101,169,173, +88,120,218,94,163,206,211,88,179,39,173,142,211,36,148,58,128,131,103,34,148,138,77,201,44,50,112,250,174,39,159,115,209,168, +214,121,228,55,116,119,246,247,119,117,118,111,190,32,114,238,182,245,23,12,116,70,186,123,47,232,223,58,20,33,177,131,140,29, +200,26,119,32,207,181,118,244,237,236,35,215,142,77,200,35,55,129,141,236,113,7,210,74,107,7,231,149,246,14,201,5,71,126,176, +116,191,170,68,217,230,207,77,138,32,23,221,177,147,4,210,79,40,51,144,119,26,195,93,84,61,124,230,84,168,126,248,223,74,45,106, +254,5,113,236,221,225,89,246,233,73,204,204,70,205,141,142,140,196,82,169,13,227,209,61,41,242,34,221,156,142,142,203,156,219, +157,185,42,152,209,209,81,126,26,157,130,28,249,116,239,125,137,209,216,126,180,86,79,178,133,55,58,57,169,51,42,114,69,83,202, +19,119,157,146,106,83,89,150,211,191,94,238,61,181,182,219,183,247,245,80,96,215,105,233,169,67,67,198,145,138,103,56,217,105, +167,28,114,23,232,59,71,206,174,116,167,30,181,103,87,90,201,65,76,151,82,124,160,195,4,228,218,149,230,195,136,236,93,156,77, +146,111,68,159,74,145,3,147,49,114,97,20,72,39,40,127,228,164,100,156,236,145,241,88,116,138,73,50,21,35,55,18,202,4,108,76, +185,186,32,21,122,56,205,140,198,19,41,201,150,165,205,177,3,82,88,218,200,167,11,145,228,118,232,176,177,11,18,105,18,163,228, +29,205,230,241,228,210,115,241,40,10,27,101,74,163,148,151,41,41,5,185,163,89,7,72,101,234,50,38,243,170,71,153,236,228,140,198, +167,48,68,132,125,176,227,169,204,208,93,177,139,176,244,41,202,145,155,178,59,57,10,3,198,50,7,4,53,236,142,226,106,55,26, +74,39,67,35,83,177,104,58,22,218,53,61,174,239,148,74,119,104,247,84,114,34,148,113,19,207,238,120,34,58,30,127,71,140,170,80, +26,157,89,168,13,201,41,199,237,75,9,87,178,72,102,67,207,38,96,239,142,79,193,153,124,187,97,162,209,204,130,123,185,67,229, +198,100,237,97,131,231,240,167,50,134,137,72,66,94,124,100,84,228,114,121,92,186,118,138,202,248,65,121,238,105,215,242,249, +51,117,167,199,176,57,92,57,57,57,30,31,145,167,106,198,219,139,192,62,109,208,165,78,166,51,167,151,90,78,191,136,145,7,108,121, +246,82,33,74,61,234,238,158,217,54,57,146,37,125,33,63,91,84,107,237,205,62,167,200,141,178,116,190,69,40,244,78,79,112,56,199, +70,198,225,171,44,53,171,117,33,10,199,146,100,84,106,96,189,84,141,2,31,144,167,25,99,75,116,130,153,125,61,41,170,59,93,70, +102,161,167,9,134,79,23,84,185,231,105,146,243,32,201,213,167,14,19,147,155,171,171,122,178,206,140,233,232,33,179,6,185,200, +188,194,242,33,47,243,48,205,185,19,249,245,35,31,19,220,172,71,218,91,249,131,20,157,74,78,198,166,210,113,244,83,128,199,193, +216,68,50,29,203,4,13,48,134,228,81,164,163,149,236,82,6,136,188,49,121,11,209,247,22,114,143,69,83,91,216,37,60,40,140,201, +93,100,141,37,225,187,57,252,169,124,83,196,201,140,143,238,39,43,206,102,182,227,114,17,115,226,217,247,33,185,241,84,118,242, +252,208,173,118,104,12,19,141,167,214,79,76,166,15,112,65,218,153,171,103,94,164,120,226,58,37,32,15,167,55,189,220,175,111,175, +243,69,138,121,33,2,144,11,31,156,97,120,199,147,136,117,42,144,187,39,180,135,91,124,158,144,119,34,107,102,42,156,56,109, +27,228,79,156,180,10,148,59,225,8,196,198,196,4,153,19,169,61,248,72,79,147,149,224,181,176,249,19,81,33,17,187,152,247,0,140, +146,96,35,153,201,93,123,201,149,220,189,59,133,225,4,146,137,174,104,122,100,108,38,7,73,81,9,246,216,73,129,23,79,137,61,176, +68,241,169,21,236,230,52,231,84,238,57,83,48,137,212,162,108,136,61,43,251,87,106,200,159,76,204,188,51,145,26,10,157,28,213, +58,47,169,239,194,112,68,244,156,159,60,233,106,204,125,58,159,123,98,227,209,3,96,23,100,216,236,72,251,156,114,42,8,100,38, +226,78,38,54,140,79,167,198,200,151,76,12,164,167,51,108,140,140,199,163,188,112,48,149,138,83,41,115,198,227,188,149,229,184, +186,147,19,147,136,192,144,69,75,153,80,200,8,157,121,82,22,132,113,145,13,37,164,189,180,235,166,122,56,230,227,138,13,217, +34,184,124,226,148,24,69,94,102,234,114,30,151,103,28,172,132,31,79,186,106,158,19,79,143,97,43,149,102,42,102,46,148,186,38, +144,169,113,240,242,153,231,120,217,151,195,207,106,39,122,146,153,188,46,39,83,66,128,194,224,248,16,75,206,52,177,147,23,115, +200,44,154,132,251,157,58,129,178,89,152,67,233,216,100,228,226,36,149,156,84,55,19,76,200,154,228,244,209,146,239,52,115,38, +101,186,197,251,194,51,169,51,47,85,146,129,37,63,83,210,17,75,214,200,236,51,47,83,82,27,93,86,200,40,145,159,41,69,146,27,112, +214,145,61,41,103,107,242,22,158,59,21,219,195,239,87,166,78,126,73,67,174,41,233,57,228,85,84,133,6,85,86,249,214,188,41,28, +217,177,84,122,198,183,183,77,197,147,240,141,3,220,86,46,191,123,74,111,36,48,210,251,162,227,100,77,177,47,153,83,211,9,42, +76,101,179,80,253,30,141,138,82,142,236,57,195,116,103,94,58,123,82,35,99,177,81,28,251,228,74,197,144,54,140,146,149,98,223, +42,227,79,245,182,119,44,58,26,234,219,26,154,201,27,60,92,199,102,166,2,236,241,110,103,106,149,11,6,123,234,0,7,201,124,126, +208,153,224,116,124,20,149,99,124,41,192,94,193,68,173,20,39,18,118,74,62,228,72,194,13,41,79,21,211,201,73,249,232,74,169,227, +213,74,129,131,158,51,252,28,120,79,102,149,211,99,113,24,131,63,107,154,80,129,11,11,26,77,76,146,59,157,148,183,54,242,164, +147,58,167,152,51,157,152,205,187,230,157,194,118,248,80,233,116,226,77,214,210,134,237,167,113,58,72,178,117,55,69,196,205, +194,157,111,188,76,109,251,141,171,47,109,171,167,152,120,15,24,116,129,36,135,76,58,34,44,218,73,224,124,85,8,178,172,7,140,213, +163,238,252,19,38,221,111,228,237,180,137,62,33,196,231,89,254,22,97,124,68,220,111,184,243,47,236,55,233,54,97,213,95,111,83, +199,254,126,23,53,28,121,7,196,62,32,164,190,195,82,95,195,254,16,237,21,223,54,220,75,32,251,1,97,54,24,21,23,27,123,42,250, +77,241,65,145,211,240,158,134,157,166,241,160,145,251,225,157,166,249,85,35,127,243,206,142,71,250,182,218,134,109,210,141,74, +201,17,250,190,176,94,23,135,196,103,140,103,240,216,94,143,63,237,244,186,32,119,197,150,183,111,62,80,95,111,76,87,84,154, +244,5,209,64,199,193,204,111,111,167,163,6,207,224,105,126,162,107,101,249,253,134,245,119,113,153,113,139,248,33,198,92,127, +11,221,96,152,234,25,117,207,177,220,163,59,59,232,165,76,225,78,195,84,29,170,238,232,167,198,44,157,221,107,168,206,94,146, +29,124,69,126,126,111,70,237,150,11,140,75,50,162,95,151,149,223,146,159,55,153,6,6,143,65,180,215,211,189,166,241,21,113,35, +143,225,30,211,228,210,227,232,145,62,239,40,127,154,203,143,27,255,132,76,199,230,15,211,23,249,241,211,170,234,1,71,249,33, +46,255,67,149,31,228,242,151,13,89,254,50,119,32,75,119,103,75,223,54,45,250,140,184,77,60,8,157,59,121,118,199,76,140,171,163, +29,139,243,37,99,109,255,206,225,53,91,206,95,83,111,147,177,191,205,69,244,146,172,236,143,155,226,57,81,116,224,17,185,160, +245,231,219,100,139,249,149,171,232,160,197,83,122,183,252,124,15,127,118,28,218,31,44,167,235,45,118,179,10,227,176,213,102, +188,126,201,146,250,71,251,141,252,139,141,125,21,251,247,239,63,16,71,55,162,91,233,91,189,198,22,244,126,151,92,102,17,240, +91,198,171,162,178,243,144,179,171,111,115,79,182,65,71,181,208,92,191,73,183,139,38,200,28,53,106,111,227,74,186,220,195,253, +30,54,141,255,21,221,65,147,158,21,66,184,109,50,5,10,143,88,38,107,20,134,45,92,36,114,109,114,137,74,219,172,151,26,127,105, +137,187,97,142,142,120,229,176,101,220,105,44,31,22,197,126,83,220,110,212,237,55,14,124,131,37,214,187,12,140,245,255,196,122, +186,218,35,238,224,5,16,129,2,139,88,225,175,67,54,205,175,60,139,254,100,153,183,139,95,139,63,112,101,187,153,243,89,67,244, +155,38,84,52,30,50,106,150,24,219,43,108,211,206,89,238,50,93,57,123,45,247,221,104,215,176,217,116,221,38,10,27,224,22,47,137, +5,13,123,77,227,19,198,188,122,12,143,118,217,6,246,206,173,205,152,145,105,187,108,183,113,17,140,143,150,46,151,123,175,233, +249,173,152,35,165,132,233,34,195,87,1,33,136,216,158,74,186,7,54,175,24,238,24,22,115,11,48,118,209,244,77,91,172,110,175,228, +39,227,101,81,7,147,218,70,3,27,22,91,180,178,179,127,255,59,174,180,105,184,157,94,112,203,169,99,222,247,24,203,119,30,49, +177,3,138,238,229,185,7,226,183,236,91,213,191,200,22,219,176,135,239,182,217,228,21,29,123,227,43,140,253,21,135,154,91,227, +13,149,116,92,46,245,143,228,231,119,221,226,86,168,57,96,186,49,241,142,251,228,178,5,77,227,21,33,110,189,213,180,160,13,179, +189,219,16,152,180,121,155,33,246,110,54,237,59,140,165,113,225,181,237,6,151,93,203,38,198,92,45,219,182,93,198,72,133,237,94, +238,18,46,195,101,241,148,141,75,218,80,225,50,74,54,177,20,252,236,117,183,113,183,113,59,59,64,113,129,73,159,54,234,46,196, +0,239,113,161,71,119,254,37,229,244,172,75,60,193,107,185,217,244,96,12,232,246,118,33,130,166,251,43,194,174,52,115,158,23, +107,159,57,16,124,196,180,57,20,109,54,173,235,196,210,91,133,199,118,215,163,159,242,139,121,0,247,154,185,240,166,251,68,161, +223,206,13,26,163,21,24,132,125,139,229,125,141,87,108,111,67,251,218,117,182,119,133,26,40,91,221,206,89,197,99,116,121,92,57, +174,92,35,221,102,231,178,60,125,204,173,198,0,71,68,239,143,30,62,193,219,208,62,98,4,111,182,205,160,177,187,2,61,99,108, +143,152,66,13,131,48,140,67,150,45,251,232,119,153,13,237,93,114,233,237,205,77,210,48,22,61,160,102,215,113,216,34,237,58,240, +229,194,1,83,200,89,89,247,25,107,59,130,188,29,120,161,15,139,194,130,96,53,214,186,125,205,48,154,195,170,193,114,24,232, +105,151,218,241,159,178,13,118,83,148,110,179,5,91,145,224,224,71,132,123,137,26,134,49,103,204,72,84,116,196,141,188,37,198, +190,182,91,132,223,47,59,105,248,72,195,60,250,133,20,204,143,211,29,46,185,216,59,233,25,233,22,249,70,120,204,24,174,232,56, +124,136,227,138,141,142,207,179,13,172,48,236,200,147,131,5,218,113,34,108,182,172,215,102,198,126,73,167,109,194,139,97,72, +172,47,124,27,86,171,52,5,199,199,191,27,230,157,226,179,226,189,58,188,211,251,77,113,208,112,87,180,204,251,98,189,73,55,25, +117,116,136,163,16,182,159,73,191,17,77,159,183,169,29,145,233,81,236,251,138,122,106,103,87,252,130,71,124,10,190,138,16,118, +88,152,134,111,137,113,113,197,133,29,155,247,195,115,110,145,53,249,135,106,141,119,84,40,142,17,22,63,54,74,235,140,69,198, +127,11,43,231,31,162,36,215,88,12,78,185,85,114,126,137,93,50,84,226,81,143,118,137,40,137,130,177,164,36,71,138,214,228,184, +32,91,58,211,108,174,177,132,229,68,233,210,25,94,137,82,94,171,56,6,56,249,51,197,248,140,220,30,227,44,110,107,148,214,148, +46,200,116,119,158,236,189,102,166,127,102,156,93,178,33,195,112,151,156,3,198,26,160,9,82,222,12,147,165,122,75,182,227,179, +39,195,244,96,232,67,37,102,86,54,171,209,144,42,122,51,12,23,24,51,50,114,112,94,12,110,175,26,156,171,180,182,116,97,105,85, +105,168,180,178,180,90,148,88,194,20,57,102,153,129,63,194,104,57,120,208,58,182,112,153,56,88,39,196,109,192,147,192,225,48, +220,30,56,14,60,185,72,136,155,206,18,130,255,145,50,185,182,93,230,33,49,11,200,54,114,92,250,135,56,130,58,46,59,104,189,123, +137,117,185,65,107,196,189,75,132,117,125,189,16,15,212,27,226,231,160,47,3,239,110,16,226,30,224,81,224,96,35,2,188,59,87,182, +235,69,187,7,27,251,196,175,26,133,245,104,147,16,207,2,135,155,133,184,17,248,21,240,151,102,50,132,55,223,16,87,135,118,64, +244,240,210,115,196,109,75,133,120,16,56,6,60,11,92,223,34,196,93,192,195,192,147,192,243,192,27,45,100,9,219,143,201,10,110, +26,69,211,171,151,237,18,15,44,195,8,150,11,241,216,10,104,7,14,175,36,183,59,80,172,196,244,223,61,144,125,114,165,97,92,181, +74,24,215,174,54,141,55,86,11,227,141,54,211,184,171,195,107,220,180,102,204,122,99,173,41,30,239,130,165,186,77,241,100,15, +102,215,99,136,171,214,99,164,27,48,132,141,120,6,158,221,4,221,3,232,99,11,248,192,213,91,13,113,207,86,240,183,193,18,103,195, +186,103,195,2,198,124,193,127,14,10,116,120,253,208,101,152,212,16,38,19,225,159,231,5,189,194,251,46,113,248,160,245,66,132, +107,15,111,23,57,55,1,87,237,200,252,255,74,206,223,244,100,254,239,64,254,173,74,230,255,15,228,223,169,100,254,15,65,254,157, +74,136,212,255,35,200,191,213,201,252,95,130,46,154,249,255,4,77,191,250,29,141,252,61,85,72,253,63,82,219,192,112,133,148,12, +255,123,122,225,87,191,125,231,127,3,111,132,84,191,252,255,15,154,90,158,255,141,182,21,82,191,75,226,127,199,109,135,212,248, +248,223,224,147,214,195,255,38,159,127,204,195,124,254,127,15,255,63,171,27,97,244,48,81,0,0,0,0}; + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ + STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$MidiDeviceManager;") \ + STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$BluetoothManager;") + +DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/rmsl/juce/JuceMidiSupport", 23, 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/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 {}; + } + + 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 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 (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() + : 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 {}; + } + + 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(); + } + + 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)); + + 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.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); + } +} + +} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Oboe_android.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp similarity index 98% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_Oboe_android.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp index 38152cb8..194ec074 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Oboe_android.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Oboe.cpp @@ -478,7 +478,7 @@ class OboeAudioIODevice : public AudioIODevice auto nextState = oboe::StreamState::Started; int64 timeoutNanos = 1000 * oboe::kNanosPerMillisecond; - [[maybe_unused]] auto startResult = stream->requestStart(); + auto startResult = stream->requestStart(); JUCE_OBOE_LOG ("Requested Oboe stream start with result: " + getOboeString (startResult)); startResult = stream->waitForStateChange (expectedState, &nextState, timeoutNanos); @@ -751,6 +751,8 @@ class OboeAudioIODevice : public AudioIODevice void start() override { + audioCallbackGuard.set (0); + if (inputStream != nullptr) inputStream->start(); @@ -762,10 +764,13 @@ class OboeAudioIODevice : public AudioIODevice void stop() override { - const SpinLock::ScopedLockType lock { audioCallbackMutex }; + while (! audioCallbackGuard.compareAndSetBool (1, 0)) + Thread::sleep (1); inputStream = nullptr; outputStream = nullptr; + + audioCallbackGuard.set (0); } int getOutputLatencyInSamples() override { return outputLatency; } @@ -783,9 +788,7 @@ class OboeAudioIODevice : public AudioIODevice oboe::DataCallbackResult onAudioReady (oboe::AudioStream* stream, void* audioData, int32_t numFrames) override { - const SpinLock::ScopedTryLockType lock { audioCallbackMutex }; - - if (lock.isLocked()) + if (audioCallbackGuard.compareAndSetBool (1, 0)) { if (stream == nullptr) return oboe::DataCallbackResult::Stop; @@ -794,9 +797,8 @@ class OboeAudioIODevice : public AudioIODevice jassert (stream->getDirection() == oboe::Direction::Output && stream == outputStream->getNativeStream()); // Read input from Oboe - const auto expandedBufferSize = jmax (inputStreamNativeBuffer.size(), - static_cast (numInputChannels * jmax (bufferSize, numFrames))); - inputStreamNativeBuffer.resize (expandedBufferSize); + inputStreamSampleBuffer.clear(); + inputStreamNativeBuffer.calloc (static_cast (numInputChannels * bufferSize)); if (inputStream != nullptr) { @@ -809,17 +811,17 @@ class OboeAudioIODevice : public AudioIODevice return oboe::DataCallbackResult::Continue; } - auto result = inputStream->getNativeStream()->read (inputStreamNativeBuffer.data(), numFrames, 0); + auto result = inputStream->getNativeStream()->read (inputStreamNativeBuffer.getData(), numFrames, 0); if (result) { auto referringDirectlyToOboeData = OboeAudioIODeviceBufferHelpers - ::referAudioBufferDirectlyToOboeIfPossible (inputStreamNativeBuffer.data(), + ::referAudioBufferDirectlyToOboeIfPossible (inputStreamNativeBuffer.get(), inputStreamSampleBuffer, result.value()); if (! referringDirectlyToOboeData) - OboeAudioIODeviceBufferHelpers::convertFromOboe (inputStreamNativeBuffer.data(), inputStreamSampleBuffer, result.value()); + OboeAudioIODeviceBufferHelpers::convertFromOboe (inputStreamNativeBuffer.get(), inputStreamSampleBuffer, result.value()); } else { @@ -851,6 +853,8 @@ class OboeAudioIODevice : public AudioIODevice if (isOutputLatencyDetectionSupported) outputLatency = getLatencyFor (*outputStream); + + audioCallbackGuard.set (0); } return oboe::DataCallbackResult::Continue; @@ -940,14 +944,13 @@ class OboeAudioIODevice : public AudioIODevice if (error == oboe::Result::ErrorDisconnected) { - const SpinLock::ScopedTryLockType streamRestartLock { streamRestartMutex }; - - if (streamRestartLock.isLocked()) + if (streamRestartGuard.compareAndSetBool (1, 0)) { // 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). - const SpinLock::ScopedLockType audioCallbackLock { audioCallbackMutex }; + while (! audioCallbackGuard.compareAndSetBool (1, 0)) + Thread::sleep (1); outputStream = nullptr; outputStream.reset (new OboeStream (oboe::kUnspecified, @@ -960,14 +963,18 @@ class OboeAudioIODevice : public AudioIODevice this)); outputStream->start(); + + audioCallbackGuard.set (0); + streamRestartGuard.set (0); } } } - std::vector inputStreamNativeBuffer; + HeapBlock inputStreamNativeBuffer; AudioBuffer inputStreamSampleBuffer, outputStreamSampleBuffer; - SpinLock audioCallbackMutex, streamRestartMutex; + Atomic audioCallbackGuard { 0 }, + streamRestartGuard { 0 }; bool isInputLatencyDetectionSupported = false; int inputLatency = -1; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_OpenSL_android.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_OpenSL_android.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp similarity index 99% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp index 4f328b05..b3b58ae8 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp @@ -20,7 +20,7 @@ ============================================================================== */ -#include +#include namespace juce { @@ -196,7 +196,7 @@ JUCE_END_IGNORE_WARNINGS_GCC_LIKE //============================================================================== #if JUCE_MODULE_AVAILABLE_juce_graphics - #include + #include #endif namespace juce { @@ -938,7 +938,7 @@ struct iOSAudioIODevice::Pimpl : public AsyncUpdater { if (! firstHostTime) { - if (! approximatelyEqual ((time->mSampleTime - lastSampleTime), (double) lastNumFrames)) + if ((time->mSampleTime - lastSampleTime) != lastNumFrames) xrun++; } else @@ -1159,7 +1159,7 @@ struct iOSAudioIODevice::Pimpl : public AsyncUpdater &desc, &dataSize); - if (! approximatelyEqual (desc.mSampleRate, 0.0) && ! approximatelyEqual (desc.mSampleRate, sampleRate)) + if (desc.mSampleRate != 0 && desc.mSampleRate != sampleRate) { JUCE_IOS_AUDIO_LOG ("Stream format has changed: Sample rate " << desc.mSampleRate); triggerAsyncUpdate(); diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.h b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.h similarity index 100% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_Audio_ios.h rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.h diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ALSA_linux.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_ALSA_linux.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Bela_linux.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Bela.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_Bela_linux.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Bela.cpp diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_JackAudio_linux.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_JackAudio_linux.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_linux.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp similarity index 69% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_linux.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp index 3883bfa1..07f318ba 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_linux.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp @@ -26,32 +26,38 @@ namespace juce #if JUCE_ALSA //============================================================================== -class AlsaClient +class AlsaClient : public ReferenceCountedObject { - auto lowerBound (int portId) const +public: + AlsaClient() { - const auto comparator = [] (const auto& port, const auto& id) { return port->getPortId() < id; }; - return std::lower_bound (ports.begin(), ports.end(), portId, comparator); - } + jassert (instance == nullptr); - auto findPortIterator (int portId) const - { - const auto iter = lowerBound (portId); - return (iter == ports.end() || (*iter)->getPortId() != portId) ? ports.end() : iter; + 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.ensureStorageAllocated (32); + } } -public: ~AlsaClient() { - inputThread.reset(); + jassert (instance != nullptr); + instance = nullptr; 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() @@ -66,12 +72,15 @@ class AlsaClient #endif } + using Ptr = ReferenceCountedObjectPtr; + //============================================================================== // represents an input or output port of the supplied AlsaClient struct Port { - explicit Port (bool forInput) noexcept - : isInput (forInput) {} + Port (AlsaClient& c, bool forInput) noexcept + : client (c), isInput (forInput) + {} ~Port() { @@ -82,21 +91,21 @@ class AlsaClient 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) @@ -114,7 +123,15 @@ class AlsaClient void enableCallback (bool enable) { - callbackEnabled = enable; + const auto oldValue = callbackEnabled.exchange (enable); + + if (oldValue != enable) + { + if (enable) + client.registerCallback(); + else + client.unregisterCallback(); + } } bool sendMessageNow (const MidiMessage& message) @@ -132,7 +149,7 @@ class AlsaClient auto numBytes = (long) message.getRawDataSize(); auto* data = message.getRawData(); - auto seqHandle = client->get(); + auto seqHandle = client.get(); bool success = true; while (numBytes > 0) @@ -171,7 +188,7 @@ class AlsaClient 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)) @@ -200,7 +217,7 @@ class AlsaClient const String& getPortName() const { return portName; } private: - const std::shared_ptr client = AlsaClient::getInstance(); + AlsaClient& client; MidiInputCallback* callback = nullptr; snd_midi_event_t* midiParser = nullptr; @@ -213,23 +230,36 @@ class AlsaClient bool isInput = false; }; - static std::shared_ptr getInstance() + static Ptr getInstance() + { + if (instance == nullptr) + instance = new AlsaClient(); + + return instance; + } + + void registerCallback() { - static std::weak_ptr ptr; + if (inputThread == nullptr) + inputThread.reset (new MidiInputThread (*this)); + + if (++activeCallbacks == 1) + inputThread->startThread(); + } - if (auto locked = ptr.lock()) - return locked; + void unregisterCallback() + { + jassert (activeCallbacks.get() > 0); - std::shared_ptr result (new AlsaClient()); - ptr = result; - return result; + if (--activeCallbacks == 0 && inputThread->isThreadRunning()) + inputThread->signalThreadShouldExit(); } void handleIncomingMidiMessage (snd_seq_event* event, const MidiMessage& message) { const ScopedLock sl (callbackLock); - if (auto* port = findPort (event->dest.port)) + if (auto* port = ports[event->dest.port]) port->handleIncomingMidiMessage (message); } @@ -237,7 +267,7 @@ class AlsaClient { const ScopedLock sl (callbackLock); - if (auto* port = findPort (event->dest.port)) + if (auto* port = ports[event->dest.port]) port->handlePartialSysexMessage (messageData, numBytesSoFar, timeStamp); } @@ -248,13 +278,10 @@ class AlsaClient { const ScopedLock sl (callbackLock); - auto port = new Port (forInput); + auto port = new Port (*this, forInput); port->createPort (name, enableSubscription); - - const auto iter = lowerBound (port->getPortId()); - jassert (iter == ports.end() || port->getPortId() < (*iter)->getPortId()); - ports.insert (iter, rawToUniquePtr (port)); - + ports.set (port->getPortId(), port); + incReferenceCount(); return port; } @@ -262,96 +289,31 @@ class AlsaClient { const ScopedLock sl (callbackLock); - if (const auto iter = findPortIterator (port->getPortId()); iter != ports.end()) - ports.erase (iter); + ports.set (port->getPortId(), nullptr); + decReferenceCount(); } 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; - int announcementsIn = 0; - std::vector> ports; + OwnedArray ports; Atomic activeCallbacks; CriticalSection callbackLock; + static AlsaClient* instance; + //============================================================================== - class SequencerThread + class MidiInputThread : public Thread { public: - explicit SequencerThread (AlsaClient& c) - : client (c) - { - } - - ~SequencerThread() noexcept + MidiInputThread (AlsaClient& c) + : Thread ("JUCE MIDI Input"), client (c) { - shouldStop = true; - thread.join(); + jassert (client.get() != nullptr); } - 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 : private AsyncUpdater + void run() override { - 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; @@ -359,20 +321,17 @@ class AlsaClient if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) { - const ScopeGuard freeMidiEvent { [&] { snd_midi_event_free (midiParser); } }; + auto numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); + HeapBlock pfd (numPfds); + snd_seq_poll_descriptors (seqHandle, pfd, (unsigned int) numPfds, POLLIN); - 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 (! shouldStop) + while (! threadShouldExit()) { - // 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 (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 { - if (shouldStop) + if (threadShouldExit()) break; do @@ -381,60 +340,44 @@ class AlsaClient 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? - const auto numBytes = snd_midi_event_decode (midiParser, - buffer.data(), - maxEventSize, - inputEvent); + auto numBytes = snd_midi_event_decode (midiParser, buffer, + maxEventSize, inputEvent); snd_midi_event_reset_decode (midiParser); - concatenator.pushMidiData (buffer.data(), (int) numBytes, + concatenator.pushMidiData (buffer, (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::optional inputThread; + std::unique_ptr inputThread; }; +AlsaClient* AlsaClient::instance = nullptr; + //============================================================================== static String getFormattedPortIdentifier (int clientId, int portId) { return String (clientId) + "-" + String (portId); } -static AlsaClient::Port* iterateMidiClient (AlsaClient& client, +static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client, snd_seq_client_info_t* clientInfo, bool forInput, Array& devices, @@ -442,7 +385,7 @@ static AlsaClient::Port* iterateMidiClient (AlsaClient& 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); @@ -469,7 +412,7 @@ static AlsaClient::Port* iterateMidiClient (AlsaClient& client, { if (portID != -1) { - port = client.createPort (portName, forInput, false); + port = client->createPort (portName, forInput, false); jassert (port->isValid()); port->connectWith (sourceClient, portID); break; @@ -507,11 +450,8 @@ 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; @@ -719,18 +659,6 @@ void MidiOutput::sendMessageNow (const MidiMessage& 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 @@ -765,12 +693,6 @@ 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_CoreAudio_mac.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp similarity index 97% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreAudio_mac.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index bc470fc3..785ee259 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreAudio_mac.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -20,7 +20,7 @@ ============================================================================== */ -#include +#include namespace juce { @@ -966,7 +966,7 @@ class CoreAudioInternal : private Timer, jassert (timestamp == nullptr || (((timestamp->mFlags & kAudioTimeStampSampleTimeValid) != 0) && ((timestamp->mFlags & kAudioTimeStampHostTimeValid) != 0))); - if (exactlyEqual (previousSampleTime, invalidSampleTime)) + if (previousSampleTime == invalidSampleTime) previousSampleTime = timestamp != nullptr ? timestamp->mSampleTime : 0.0; if (timestamp != nullptr && std::fabs (previousSampleTime - timestamp->mSampleTime) >= 1.0) @@ -1091,7 +1091,7 @@ class CoreAudioInternal : private Timer, if (! updateDetailsFromDevice()) owner.stopInternal(); - else if ((oldBufferSize != bufferSize || ! approximatelyEqual (oldSampleRate, sampleRate)) && owner.shouldRestartDevice()) + else if ((oldBufferSize != bufferSize || oldSampleRate != sampleRate) && owner.shouldRestartDevice()) owner.restart(); } @@ -1113,51 +1113,39 @@ class CoreAudioInternal : private Timer, return noErr; } - static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, - UInt32 numAddresses, - const AudioObjectPropertyAddress* pa, - void* inClientData) + static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, + const AudioObjectPropertyAddress* pa, void* inClientData) { - auto& intern = *static_cast (inClientData); + auto intern = static_cast (inClientData); - const auto xruns = std::count_if (pa, pa + numAddresses, [] (const AudioObjectPropertyAddress& x) + switch (pa->mSelector) { - 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, - }; + case kAudioDeviceProcessorOverload: + intern->xruns++; + break; - return std::find (std::begin (selectors), std::end (selectors), x.mSelector) != std::end (selectors); - }); + case kAudioDevicePropertyBufferSize: + case kAudioDevicePropertyBufferFrameSize: + case kAudioDevicePropertyNominalSampleRate: + case kAudioDevicePropertyStreamFormat: + case kAudioDevicePropertyDeviceIsAlive: + case kAudioStreamPropertyPhysicalFormat: + intern->deviceDetailsChanged(); + break; - if (detailsChanged) - intern.deviceDetailsChanged(); + case kAudioDevicePropertyDeviceHasChanged: + case kAudioObjectPropertyOwnedObjects: + intern->deviceRequestedRestart(); + break; - if (requestedRestart) - intern.deviceRequestedRestart(); + case kAudioDevicePropertyBufferSizeRange: + case kAudioDevicePropertyVolumeScalar: + case kAudioDevicePropertyMute: + case kAudioDevicePropertyPlayThru: + case kAudioDevicePropertyDataSource: + case kAudioDevicePropertyDeviceIsRunning: + break; + } return noErr; } @@ -1390,18 +1378,19 @@ class CoreAudioIODevice : public AudioIODevice, start (previousCallback); } - static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, - UInt32 numAddresses, - const AudioObjectPropertyAddress* pa, - void* inClientData) + static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) { - const auto detailsChanged = std::any_of (pa, pa + numAddresses, [] (const AudioObjectPropertyAddress& x) + switch (pa->mSelector) { - return x.mSelector == kAudioHardwarePropertyDevices; - }); + case kAudioHardwarePropertyDevices: + static_cast (inClientData)->deviceDetailsChanged(); + break; - if (detailsChanged) - static_cast (inClientData)->deviceDetailsChanged(); + case kAudioHardwarePropertyDefaultOutputDevice: + case kAudioHardwarePropertyDefaultInputDevice: + case kAudioHardwarePropertyDefaultSystemOutputDevice: + break; + } return noErr; } @@ -1588,7 +1577,7 @@ class AudioIODeviceCombiner : public AudioIODevice, { auto deviceSampleRate = d->getCurrentSampleRate(); - if (! approximatelyEqual (deviceSampleRate, sampleRateRequested)) + if (deviceSampleRate != sampleRateRequested) { if (! getAvailableSampleRates().contains (deviceSampleRate)) return; @@ -1923,7 +1912,7 @@ class AudioIODeviceCombiner : public AudioIODevice, for (auto& d : getDeviceWrappers()) { - if (! approximatelyEqual (d->getCurrentSampleRate(), currentSampleRate)) + if (d->getCurrentSampleRate() != currentSampleRate) { d->setCurrentSampleRate (currentSampleRate); anySampleRateChanges = true; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreMidi_mac.mm b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm similarity index 94% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreMidi_mac.mm rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm index aa65c856..93ec5e39 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_CoreMidi_mac.mm +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm @@ -71,8 +71,10 @@ static bool checkError (OSStatus err, [[maybe_unused]] int lineNum) { virtual ~SenderBase() noexcept = default; - virtual void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) = 0; + virtual void send (MIDIPortRef port, MIDIEndpointRef endpoint, const MidiMessage& m) = 0; virtual void send (MIDIPortRef port, MIDIEndpointRef endpoint, ump::Iterator b, ump::Iterator e) = 0; + + virtual ump::MidiProtocol getProtocol() const noexcept = 0; }; template @@ -82,7 +84,11 @@ static bool checkError (OSStatus err, [[maybe_unused]] int lineNum) template <> struct API_AVAILABLE (macos (11.0), ios (14.0)) Sender : public SenderBase { - void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) override + explicit Sender (MIDIEndpointRef ep) + : umpConverter (getProtocolForEndpoint (ep)) + {} + + void send (MIDIPortRef port, MIDIEndpointRef endpoint, const MidiMessage& m) override { newSendImpl (port, endpoint, m); } @@ -92,8 +98,14 @@ void send (MIDIPortRef port, MIDIEndpointRef endpoint, ump::Iterator b, ump::Ite newSendImpl (port, endpoint, b, e); } + ump::MidiProtocol getProtocol() const noexcept override + { + return umpConverter.getProtocol() == ump::PacketProtocol::MIDI_2_0 ? ump::MidiProtocol::UMP_MIDI_2_0 + : ump::MidiProtocol::UMP_MIDI_1_0; + } + private: - ump::ToUMP1Converter umpConverter; + ump::GenericUMPConverter umpConverter; static ump::PacketProtocol getProtocolForEndpoint (MIDIEndpointRef ep) noexcept { @@ -107,6 +119,9 @@ void send (MIDIPortRef port, MIDIEndpointRef endpoint, ump::Iterator b, ump::Ite template void newSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, Params&&... params) { + // The converter protocol got out-of-sync with the device protocol + jassert (getProtocolForEndpoint (endpoint) == umpConverter.getProtocol()); + #if JUCE_IOS const MIDITimeStamp timeStamp = mach_absolute_time(); #else @@ -118,10 +133,9 @@ void newSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, Params&&... params 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); + end = MIDIEventListInit (&stackList, + umpConverter.getProtocol() == ump::PacketProtocol::MIDI_2_0 ? kMIDIProtocol_2_0 + : kMIDIProtocol_1_0); }; const auto send = [&] @@ -145,19 +159,16 @@ void newSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, Params&&... params init(); - ump::GenericUMPConverter::convertImpl (umpConverter, params..., [&] (const auto& v) + umpConverter.convert (params..., [&] (const ump::View& view) { - umpConverter.convert (v, [&] (const ump::View& view) - { - add (view); + add (view); - if (end != nullptr) - return; + if (end != nullptr) + return; - send(); - init(); - add (view); - }); + send(); + init(); + add (view); }); send(); @@ -169,7 +180,9 @@ void newSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, Params&&... params template <> struct Sender : public SenderBase { - void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) override + explicit Sender (MIDIEndpointRef) {} + + void send (MIDIPortRef port, MIDIEndpointRef endpoint, const MidiMessage& m) override { oldSendImpl (port, endpoint, m); } @@ -178,17 +191,22 @@ void send (MIDIPortRef port, MIDIEndpointRef endpoint, ump::Iterator b, ump::Ite { std::for_each (b, e, [&] (const ump::View& v) { - bytestreamConverter.convert (v, 0.0, [&] (const ump::BytestreamMidiView& m) + bytestreamConverter.convert (v, 0.0, [&] (const MidiMessage& m) { send (port, endpoint, m); }); }); } + ump::MidiProtocol getProtocol() const noexcept override + { + return ump::MidiProtocol::bytestream; + } + private: ump::ToBytestreamConverter bytestreamConverter { 2048 }; - void oldSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& message) + void oldSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, const MidiMessage& message) { #if JUCE_IOS const MIDITimeStamp timeStamp = mach_absolute_time(); @@ -199,7 +217,7 @@ void oldSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::Bytestr HeapBlock allocatedPackets; MIDIPacketList stackPacket; auto* packetToSend = &stackPacket; - auto dataSize = message.bytes.size(); + auto dataSize = (size_t) message.getRawDataSize(); if (message.isSysEx()) { @@ -216,7 +234,7 @@ void oldSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::Bytestr { p->timeStamp = timeStamp; p->length = (UInt16) jmin (maxPacketSize, bytesLeft); - memcpy (p->data, message.bytes.data() + pos, p->length); + memcpy (p->data, message.getRawData() + pos, p->length); pos += p->length; bytesLeft -= p->length; p = MIDIPacketNext (p); @@ -236,7 +254,7 @@ void oldSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::Bytestr auto& p = *(packetToSend->packet); p.timeStamp = timeStamp; p.length = (UInt16) dataSize; - memcpy (p.data, message.bytes.data(), dataSize); + memcpy (p.data, message.getRawData(), dataSize); } else { @@ -256,11 +274,11 @@ void oldSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::Bytestr template <> struct Sender { - Sender() - : sender (makeImpl()) + explicit Sender (MIDIEndpointRef ep) + : sender (makeImpl (ep)) {} - void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) + void send (MIDIPortRef port, MIDIEndpointRef endpoint, const MidiMessage& m) { sender->send (port, endpoint, m); } @@ -270,13 +288,18 @@ void send (MIDIPortRef port, MIDIEndpointRef endpoint, ump::Iterator b, ump::Ite sender->send (port, endpoint, b, e); } + ump::MidiProtocol getProtocol() const noexcept + { + return sender->getProtocol(); + } + private: - static std::unique_ptr makeImpl() + static std::unique_ptr makeImpl (MIDIEndpointRef ep) { if (@available (macOS 11, iOS 14, *)) - return std::make_unique>(); + return std::make_unique> (ep); - return std::make_unique>(); + return std::make_unique> (ep); } std::unique_ptr sender; @@ -346,7 +369,7 @@ Resource release() noexcept { public: MidiPortAndEndpoint (ScopedPortRef p, ScopedEndpointRef ep) noexcept - : port (std::move (p)), endpoint (std::move (ep)) + : port (std::move (p)), endpoint (std::move (ep)), sender (*endpoint) {} ~MidiPortAndEndpoint() noexcept @@ -356,7 +379,7 @@ Resource release() noexcept endpoint.release(); } - void send (const ump::BytestreamMidiView& m) + void send (const MidiMessage& m) { sender.send (*port, *endpoint, m); } @@ -369,6 +392,11 @@ void send (ump::Iterator b, ump::Iterator e) bool canStop() const noexcept { return *port != 0; } void stop() const { CHECK_ERROR (MIDIPortDisconnectSource (*port, *endpoint)); } + ump::MidiProtocol getProtocol() const noexcept + { + return sender.getProtocol(); + } + private: ScopedPortRef port; ScopedEndpointRef endpoint; @@ -551,10 +579,9 @@ static void enableSimulatorMidiSession() #endif } - static void globalSystemChangeCallback (const MIDINotification* notification, void*) + static void globalSystemChangeCallback (const MIDINotification*, void*) { - if (notification != nullptr && notification->messageID == kMIDIMsgSetupChanged) - MidiDeviceListConnectionBroadcaster::get().notify(); + // TODO.. Should pass-on this notification.. } static String getGlobalMidiClientName() @@ -567,7 +594,9 @@ static String getGlobalMidiClientName() static MIDIClientRef getGlobalMidiClient() { - static const auto globalMidiClient = [&] + static MIDIClientRef globalMidiClient = 0; + + if (globalMidiClient == 0) { // Since OSX 10.6, the MIDIClientCreate function will only work // correctly when called from the message thread! @@ -576,10 +605,8 @@ static MIDIClientRef getGlobalMidiClient() enableSimulatorMidiSession(); CFUniquePtr name (getGlobalMidiClientName().toCFString()); - MIDIClientRef result{}; - CHECK_ERROR (MIDIClientCreate (name.get(), globalSystemChangeCallback, nullptr, &result)); - return result; - }(); + CHECK_ERROR (MIDIClientCreate (name.get(), &globalSystemChangeCallback, nullptr, &globalMidiClient)); + } return globalMidiClient; } @@ -1270,13 +1297,7 @@ static CreatorFunctionPointers getCreatorFunctionPointers() 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)) }; + internal->send (message); } #undef CHECK_ERROR diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ASIO_windows.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_ASIO_windows.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_DirectSound_windows.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp similarity index 98% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_DirectSound_windows.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp index bd113144..68826fd2 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_DirectSound_windows.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp @@ -1035,14 +1035,8 @@ class DSoundAudioIODevice : public AudioIODevice, }; //============================================================================== -class DSoundDeviceList +struct DSoundDeviceList { - auto tie() const - { - return std::tie (outputDeviceNames, inputDeviceNames, outputGuids, inputGuids); - } - -public: StringArray outputDeviceNames, inputDeviceNames; Array outputGuids, inputGuids; @@ -1060,8 +1054,13 @@ class DSoundDeviceList } } - bool operator== (const DSoundDeviceList& other) const noexcept { return tie() == other.tie(); } - bool operator!= (const DSoundDeviceList& other) const noexcept { return tie() != other.tie(); } + bool operator!= (const DSoundDeviceList& other) const noexcept + { + return outputDeviceNames != other.outputDeviceNames + || inputDeviceNames != other.inputDeviceNames + || outputGuids != other.outputGuids + || inputGuids != other.inputGuids; + } private: static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array& guids) @@ -1214,11 +1213,13 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, } //============================================================================== -class DSoundAudioIODeviceType : public AudioIODeviceType +class DSoundAudioIODeviceType : public AudioIODeviceType, + private DeviceChangeDetector { public: DSoundAudioIODeviceType() - : AudioIODeviceType ("DirectSound") + : AudioIODeviceType ("DirectSound"), + DeviceChangeDetector (L"DirectSound") { initialiseDSoundFunctions(); } @@ -1273,17 +1274,19 @@ class DSoundAudioIODeviceType : public AudioIODeviceType } private: - DeviceChangeDetector detector { L"DirectSound", [this] { systemDeviceChanged(); } }; DSoundDeviceList deviceList; bool hasScanned = false; - void systemDeviceChanged() + void systemDeviceChanged() override { DSoundDeviceList newList; newList.scan(); - if (std::exchange (deviceList, newList) != newList) + if (newList != deviceList) + { + deviceList = newList; callDeviceChangeListeners(); + } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType) diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_windows.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp similarity index 99% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_windows.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp index 3119769c..e0c6fae0 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_Midi_windows.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp @@ -103,7 +103,7 @@ struct MidiServiceType struct Win32MidiService : public MidiServiceType, private Timer { - Win32MidiService() = default; + Win32MidiService() {} Array getAvailableDevices (bool isInput) override { @@ -1871,10 +1871,6 @@ struct MidiService : public DeletedAtShutdown private: std::unique_ptr internal; - DeviceChangeDetector detector { L"JuceMidiDeviceDetector_", [] - { - MidiDeviceListConnectionBroadcaster::get().notify(); - } }; }; JUCE_IMPLEMENT_SINGLETON (MidiService) @@ -2017,10 +2013,4 @@ void MidiOutput::sendMessageNow (const MidiMessage& 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_WASAPI_windows.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp similarity index 96% rename from JuceLibraryCode/modules/juce_audio_devices/native/juce_WASAPI_windows.cpp rename to JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index a224e196..2addd3c1 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_WASAPI_windows.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -1694,11 +1694,13 @@ class WASAPIAudioIODevice : public AudioIODevice, //============================================================================== -class WASAPIAudioIODeviceType : public AudioIODeviceType +class WASAPIAudioIODeviceType : public AudioIODeviceType, + private DeviceChangeDetector { public: - explicit WASAPIAudioIODeviceType (WASAPIDeviceMode mode) + WASAPIAudioIODeviceType (WASAPIDeviceMode mode) : AudioIODeviceType (getDeviceTypename (mode)), + DeviceChangeDetector (L"Windows Audio"), deviceMode (mode) { } @@ -1713,15 +1715,22 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType void scanForDevices() override { hasScanned = true; - devices = scan(); + + outputDeviceNames.clear(); + inputDeviceNames.clear(); + outputDeviceIds.clear(); + inputDeviceIds.clear(); + + scan (outputDeviceNames, inputDeviceNames, + outputDeviceIds, inputDeviceIds); } StringArray getDeviceNames (bool wantInputNames) const override { jassert (hasScanned); // need to call scanForDevices() before doing this - return wantInputNames ? devices.inputDeviceNames - : devices.outputDeviceNames; + return wantInputNames ? inputDeviceNames + : outputDeviceNames; } int getDefaultDeviceIndex (bool /*forInput*/) const override @@ -1735,8 +1744,8 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType jassert (hasScanned); // need to call scanForDevices() before doing this if (auto d = dynamic_cast (device)) - return asInput ? devices.inputDeviceIds .indexOf (d->inputDeviceId) - : devices.outputDeviceIds.indexOf (d->outputDeviceId); + return asInput ? inputDeviceIds.indexOf (d->inputDeviceId) + : outputDeviceIds.indexOf (d->outputDeviceId); return -1; } @@ -1750,16 +1759,16 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType std::unique_ptr device; - auto outputIndex = devices.outputDeviceNames.indexOf (outputDeviceName); - auto inputIndex = devices.inputDeviceNames .indexOf (inputDeviceName); + auto outputIndex = outputDeviceNames.indexOf (outputDeviceName); + auto inputIndex = inputDeviceNames.indexOf (inputDeviceName); if (outputIndex >= 0 || inputIndex >= 0) { device.reset (new WASAPIAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName : inputDeviceName, getTypeName(), - devices.outputDeviceIds[outputIndex], - devices.inputDeviceIds [inputIndex], + outputDeviceIds [outputIndex], + inputDeviceIds [inputIndex], deviceMode)); if (! device->initialise()) @@ -1770,24 +1779,10 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType } //============================================================================== - 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; + StringArray outputDeviceNames, outputDeviceIds; + StringArray inputDeviceNames, inputDeviceIds; private: - DeviceChangeDetector deviceChangeDetector { L"Windows Audio", [this] { systemDeviceChanged(); } }; WASAPIDeviceMode deviceMode; bool hasScanned = false; ComSmartPtr enumerator; @@ -1796,7 +1791,7 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType class ChangeNotificationClient : public ComBaseClassHelper { public: - explicit ChangeNotificationClient (WASAPIAudioIODeviceType* d) + ChangeNotificationClient (WASAPIAudioIODeviceType* d) : ComBaseClassHelper (0), device (d) {} JUCE_COMRESULT OnDeviceAdded (LPCWSTR) { return notify(); } @@ -1811,7 +1806,7 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType HRESULT notify() { if (device != nullptr) - device->deviceChangeDetector.triggerAsyncDeviceChangeCallback(); + device->triggerAsyncDeviceChangeCallback(); return S_OK; } @@ -1845,12 +1840,15 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType } //============================================================================== - Devices scan() + void scan (StringArray& outDeviceNames, + StringArray& inDeviceNames, + StringArray& outDeviceIds, + StringArray& inDeviceIds) { if (enumerator == nullptr) { if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) - return {}; + return; notifyClient = new ChangeNotificationClient (this); enumerator->RegisterEndpointNotificationCallback (notifyClient); @@ -1864,9 +1862,7 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())) && check (deviceCollection->GetCount (&numDevices)))) - return {}; - - Devices result; + return; for (UINT32 i = 0; i < numDevices; ++i) { @@ -1906,33 +1902,40 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType if (flow == eRender) { const int index = (deviceId == defaultRenderer) ? 0 : -1; - result.outputDeviceIds.insert (index, deviceId); - result.outputDeviceNames.insert (index, name); + outDeviceIds.insert (index, deviceId); + outDeviceNames.insert (index, name); } else if (flow == eCapture) { const int index = (deviceId == defaultCapture) ? 0 : -1; - result.inputDeviceIds.insert (index, deviceId); - result.inputDeviceNames.insert (index, name); + inDeviceIds.insert (index, deviceId); + inDeviceNames.insert (index, name); } } - result.inputDeviceNames .appendNumbersToDuplicates (false, false); - result.outputDeviceNames.appendNumbersToDuplicates (false, false); - - return result; + inDeviceNames.appendNumbersToDuplicates (false, false); + outDeviceNames.appendNumbersToDuplicates (false, false); } //============================================================================== - void systemDeviceChanged() + void systemDeviceChanged() override { - const auto newDevices = scan(); + StringArray newOutNames, newInNames, newOutIds, newInIds; + scan (newOutNames, newInNames, newOutIds, newInIds); - if (std::exchange (devices, newDevices) != newDevices) + if (newOutNames != outputDeviceNames + || newInNames != inputDeviceNames + || newOutIds != outputDeviceIds + || newInIds != inputDeviceIds) { hasScanned = true; - callDeviceChangeListeners(); + outputDeviceNames = newOutNames; + inputDeviceNames = newInNames; + outputDeviceIds = newOutIds; + inputDeviceIds = newInIds; } + + callDeviceChangeListeners(); } //============================================================================== diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h index b45bdcac..3d82788c 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h @@ -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; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp index 034dcbcd..079df3f3 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp @@ -25,8 +25,8 @@ #if JUCE_MAC || JUCE_IOS -#include -#include +#include +#include namespace juce { diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp index 3fed5337..e743ae66 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp @@ -111,16 +111,15 @@ namespace FlacNamespace #endif JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wconversion", + "-Wshadow", "-Wdeprecated-register", - "-Wfloat-equal", + "-Wswitch-enum", + "-Wswitch-default", "-Wimplicit-fallthrough", - "-Wlanguage-extension-token", - "-Wredundant-decls", - "-Wshadow", + "-Wzero-as-null-pointer-constant", "-Wsign-conversion", - "-Wswitch-default", - "-Wswitch-enum", - "-Wzero-as-null-pointer-constant") + "-Wredundant-decls", + "-Wlanguage-extension-token") #if JUCE_INTEL #if JUCE_32BIT diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp index fb133988..cf105e9b 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp @@ -37,21 +37,20 @@ namespace OggVorbisNamespace #if JUCE_INCLUDE_OGGVORBIS_CODE || ! defined (JUCE_INCLUDE_OGGVORBIS_CODE) 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", + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wconversion", "-Wshadow", + "-Wfloat-conversion", + "-Wdeprecated-register", + "-Wdeprecated-declarations", + "-Wswitch-enum", + "-Wzero-as-null-pointer-constant", "-Wsign-conversion", "-Wswitch-default", - "-Wswitch-enum", - "-Wzero-as-null-pointer-constant") + "-Wredundant-decls", + "-Wmisleading-indentation", + "-Wmissing-prototypes", + "-Wcast-align", + "-Wmaybe-uninitialized") JUCE_BEGIN_NO_SANITIZE ("undefined") #include "oggvorbis/vorbisenc.h" diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 0105a2c3..c562882c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -948,7 +948,7 @@ namespace WavFileHelpers } }; - //============================================================================== + //============================================================================= namespace IXMLChunk { static const std::unordered_set aswgMetadataKeys diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp index a285635e..a8c974d2 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp @@ -67,7 +67,7 @@ void ARAAudioSourceReader::willUpdateAudioSourceProperties (ARAAudioSource* audi ARAAudioSource::PropertiesPtr newProperties) { if (audioSource->getSampleCount() != newProperties->sampleCount - || ! exactlyEqual (audioSource->getSampleRate(), newProperties->sampleRate) + || audioSource->getSampleRate() != newProperties->sampleRate || audioSource->getChannelCount() != newProperties->channelCount) { invalidate(); @@ -277,10 +277,10 @@ void ARAPlaybackRegionReader::willUpdatePlaybackRegionProperties (ARAPlaybackReg { 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) + if ((playbackRegion->getStartInAudioModificationTime() != newProperties->startInModificationTime) + || (playbackRegion->getDurationInAudioModificationTime() != newProperties->durationInModificationTime) + || (playbackRegion->getStartInPlaybackTime() != newProperties->startInPlaybackTime) + || (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)) diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp index 96f2d5ba..7c47f86a 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp @@ -81,14 +81,8 @@ void AudioFormatReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& i } else { - 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)); - + reader->read (info.buffer, info.startSample, + info.numSamples, start, true, true); nextPlayPos += info.numSamples; } } diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp index 23018867..95da52c9 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp @@ -176,6 +176,26 @@ bool BufferingAudioReader::readNextBufferChunk() //============================================================================== #if JUCE_UNIT_TESTS +static bool operator== (const AudioBuffer& a, const AudioBuffer& b) +{ + if (a.getNumChannels() != b.getNumChannels() || a.getNumSamples() != b.getNumSamples()) + return false; + + for (int channel = 0; channel < a.getNumChannels(); ++channel) + { + auto* aPtr = a.getReadPointer (channel); + auto* bPtr = b.getReadPointer (channel); + + if (std::vector (aPtr, aPtr + a.getNumSamples()) + != std::vector (bPtr, bPtr + b.getNumSamples())) + { + return false; + } + } + + return true; +} + static bool isSilent (const AudioBuffer& b) { for (int channel = 0; channel < b.getNumChannels(); ++channel) @@ -187,16 +207,15 @@ static bool isSilent (const AudioBuffer& b) struct TestAudioFormatReader : public AudioFormatReader { - explicit TestAudioFormatReader (const AudioBuffer* b) + explicit TestAudioFormatReader (AudioBuffer& b) : AudioFormatReader (nullptr, {}), buffer (b) { - jassert (buffer != nullptr); sampleRate = 44100.0f; bitsPerSample = 32; usesFloatingPointData = true; - lengthInSamples = buffer->getNumSamples(); - numChannels = (unsigned int) buffer->getNumChannels(); + lengthInSamples = buffer.getNumSamples(); + numChannels = (unsigned int) buffer.getNumChannels(); } bool readSamples (int* const* destChannels, int numDestChannels, int startOffsetInDestBuffer, @@ -215,7 +234,7 @@ struct TestAudioFormatReader : public AudioFormatReader dest += startOffsetInDestBuffer; if (j < (int) numChannels) - FloatVectorOperations::copy (dest, buffer->getReadPointer (j, (int) startSampleInFile), numSamples); + FloatVectorOperations::copy (dest, buffer.getReadPointer (j, (int) startSampleInFile), numSamples); else FloatVectorOperations::clear (dest, numSamples); } @@ -224,20 +243,9 @@ struct TestAudioFormatReader : public AudioFormatReader return true; } - const AudioBuffer* buffer; + 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 : public UnitTest { public: @@ -250,54 +258,44 @@ class BufferingAudioReaderTests : public UnitTest beginTest ("Timeout"); { - struct BlockingReader : public TestAudioFormatReader + struct BlockingReader : public AudioFormatReader { - explicit BlockingReader (const AudioBuffer* b) - : TestAudioFormatReader (b) + BlockingReader() + : AudioFormatReader (nullptr, {}) { + sampleRate = 44100.0f; + bitsPerSample = 32; + usesFloatingPointData = true; + lengthInSamples = 1024; + numChannels = 2; } - bool readSamples (int* const* destChannels, - int numDestChannels, - int startOffsetInDestBuffer, - int64 startSampleInFile, - int numSamples) override + bool readSamples (int* const*, int, int, int64, int) override { Thread::sleep (100); - return TestAudioFormatReader::readSamples (destChannels, numDestChannels, startOffsetInDestBuffer, startSampleInFile, numSamples); + return true; } }; - Random random { getRandom() }; - - const auto blockingBuffer = generateTestBuffer (random, 1024); - expect (! isSilent (blockingBuffer)); - - BufferingAudioReader bufferingReader (new BlockingReader (&blockingBuffer), timeSlice, 64); + BufferingAudioReader bufferingReader (new BlockingReader(), timeSlice, 64); bufferingReader.setReadTimeout (10); - const auto originalBuffer = generateTestBuffer (random, 1024); - expect (! isSilent (originalBuffer)); - expect (originalBuffer != blockingBuffer); - - auto readBuffer = originalBuffer; - expect (readBuffer == originalBuffer); + AudioBuffer readBuffer { 2, 1024 }; + readBuffer.clear(); read (bufferingReader, readBuffer); - expect (readBuffer != originalBuffer); + expect (isSilent (readBuffer)); } beginTest ("Read samples"); { - Random random { getRandom() }; - for (auto i = 4; i < 18; ++i) { const auto backgroundBufferSize = 1 << i; - const auto buffer = generateTestBuffer (random, backgroundBufferSize); + auto buffer = generateTestBuffer (backgroundBufferSize); - BufferingAudioReader bufferingReader (new TestAudioFormatReader (&buffer), timeSlice, backgroundBufferSize); + BufferingAudioReader bufferingReader (new TestAudioFormatReader (buffer), timeSlice, backgroundBufferSize); bufferingReader.setReadTimeout (-1); AudioBuffer readBuffer { buffer.getNumChannels(), buffer.getNumSamples() }; @@ -309,6 +307,19 @@ class BufferingAudioReaderTests : public UnitTest } private: + AudioBuffer generateTestBuffer (int bufferSize) const + { + auto random = getRandom(); + + AudioBuffer buffer { 2, random.nextInt ({ bufferSize, bufferSize * 10 }) }; + + for (int channel = 0; channel < buffer.getNumChannels(); ++channel) + for (int sample = 0; sample < buffer.getNumSamples(); ++sample) + buffer.setSample (channel, sample, random.nextFloat()); + + return buffer; + } + void read (BufferingAudioReader& reader, AudioBuffer& readBuffer) { constexpr int blockSize = 1024; diff --git a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h index fd4e5431..aa26e9fa 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h +++ b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h @@ -35,7 +35,7 @@ ID: juce_audio_formats vendor: juce - version: 7.0.7 + version: 7.0.5 name: JUCE audio file format codecs description: Classes for reading and writing various audio file formats. website: http://www.juce.com/juce diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/JUCE_README.md b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/JUCE_README.md index f3e3689c..d337f051 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/JUCE_README.md +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/JUCE_README.md @@ -1,5 +1,6 @@ This list details modifications made to the VST3 SDK in order to facilitate inclusion in JUCE. -- The main.cpp of moduleinfotool was updated to include information exported - by the plugin's IPluginCompatibility object, if present. +- `#warning` directives were removed from fstring.cpp, as these cannot be + silenced with a `pragma GCC diagnostic ignored "-Wcpp"` when building with + g++. diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/LICENSE.txt b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/LICENSE.txt index 6daa072e..5d521d2a 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/LICENSE.txt +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/LICENSE.txt @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- This license applies only to files referencing this license, for other files of the Software Development Kit the respective embedded license text @@ -38,7 +38,4 @@ OF THE POSSIBILITY OF SUCH DAMAGE. b) General Public License (GPL) Version 3 Details of these licenses can be found at: www.gnu.org/licenses/gpl-3.0.html -Please refer to the Steinberg VST usage guidelines for the use of VST, VST logo and VST -compatible logos: -https://steinbergmedia.github.io/vst3_dev_portal/pages/VST+3+Licensing/Usage+guidelines.html //---------------------------------------------------------------------------------- diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/README.md b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/README.md index 191fe799..b4f95807 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/README.md +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/README.md @@ -1,267 +1,10 @@ -
-VST 3 SDK
+# Welcome to VST 3 SDK Interfaces -# Welcome to VST SDK 3.7.x - -## Table Of Contents - -1. [The VST SDK package](#100) -1. [System requirements](#200) -1. [About VST plug-ins in general](#300) -1. [About VST 3](#400) -1. [How to build VST 3](#500) -1. [Contributing](#600) -1. [License & Usage guidelines](#700) - -
- -## The VST SDK package contains - -- VST 3 API -- VST 3 Implementation Helper Classes -- AAX, AU, AUv3 and VST 2 wrappers -- VST 3 plug-ins Examples - -The full VST 3 SDK is available [here!](https://www.steinberg.net/en/company/developers.html). It contains : - -- VST 3 plug-in Test Host Application/Validator, -- the **Steinberg VST 3 Plug-In SDK Licensing Agreement** that you have to sign if you want to develop or host **VST 3** plug-ins. - -
- -## System requirements - -Supported Platforms: - -| Operating System |Architecture |Compiler | Notes| -| :------------------------------------ | :-----------------------: | :-------------------------------: | :-----------: | -|Windows 10/11 |x86, x86_64, arm64 |MSVC 2022, MSVC 2019 | | -|Windows 8.1 |x86, x86_64 |MSVC 2019, MSVC 2017 | | -|macOS 10.13, 10.14, 10.15, 11, 12, 13 |x86, x86_64, Apple Silicon |Xcode 10 - 14 | | -|iOS 13 - iOS 16 |arm64 |Xcode 11 - 14 | | -|Linux - Raspberry Pi OS (Buster) |arm32 |GCC 8.3 and higher |Visual Studio Code| -|Linux - Ubuntu 18.04 LTS |x86, x86_64 |GCC 8.3 and higher |Visual Studio Code, Qt Creator| -|Linux - Ubuntu 20.04 LTS |x86, x86_64 |GCC 8.3 and higher |Visual Studio Code, Qt Creator| - ---- -
- -## About VST plug-ins in general - -A VST plug-in is an audio processing component that is utilized within a host application. This host application provides the audio or/and event streams that are processed by the plug-in's code. Generally speaking, a VST plug-in can take a stream of audio data, apply a process to the audio, and return the result to the host application. A VST plug-in performs its process normally using the processor of the computer. The audio stream is broken down into a series of blocks. The host supplies the blocks in sequence. The host and its current environment control the block-size. The VST plug-in maintains the status of all its own parameters relating to the running process: The host does not maintain any information about what the plug-in did with the last block of data it processed. - -From the host application's point of view, a VST plug-in is a black box with an arbitrary number of inputs, outputs (Event (MIDI) or Audio), and associated parameters. The host needs no implicit knowledge of the plug-in's process to be able to use it. The plug-in process can use whatever parameters it wishes, internally to the process, but depending on the capabilities of the host, it can allow the changes to user parameters to be automated by the host. - -The source code of a VST plug-in is platform independent, but the delivery system depends on the platform architecture: - -- On **Windows**, a VST plug-in is a multi-threaded DLL (Dynamic Link Library), recently packaged into a folder structure. -- On **Mac OS X**, a VST plug-in is a Mach-O Bundle -- On **Linux**, a VST plug-in is a package - -To learn more about VST you can: - -- subscribe to the [VST Developer Forum](https://sdk.steinberg.net) -- check the 3rd Party Developer Support section at [www.steinberg.net](https://www.steinberg.net/en/company/developers.html) -- check the VST 3 SDK online documentation under: [steinbergmedia.github.io/vst3_dev_portal](https://steinbergmedia.github.io/vst3_dev_portal/pages/index.html) -- check the online documentation under: [steinbergmedia.github.io/vst3_doc](https://steinbergmedia.github.io/vst3_doc) - - --- -
- -## About VST 3 - -VST 3 is a general rework of the long-serving VST plug-in interface. It is not compatible with the older VST versions, but it includes some new features and possibilities. We have redesigned the API to make it not only far easier and more reliable for developers to work with, but have also provided completely new possibilities for plug-ins. These include: - -### 1. Improved Performance with the Silence Flag - -Processing can optionally be applied to plug-ins only when audio signals are present on their respective inputs, so VST 3 plug-ins can apply their processing economically and only when it is needed. - -### 2. Multiple Dynamic I/Os - -VST 3 plug-ins are no longer limited to a fixed number of inputs and outputs, and their I/O configuration can dynamically adapt to the channel configuration. Side-chains are also very easily realizable. This includes the possibility to deactivate unused busses after loading and even reactivate those when needed. This cleans up the mixer and further helps to reduce CPU load. - -### 3. Sample-accurate Automation - -VST 3 also features vastly improved parameter automation with sample accuracy and support for ramped automation data, allowing completely accurate and rapid parameter automation changes. - -### 4. Logical Parameter Organization - -The VST 3 plug-in parameters are displayed in a tree structure. Parameters are grouped into sections which represent the structure of the plug-in. Plug-ins can communicate their internal structure for the purpose of overview, but also for some associated functionality (eg. program-lists). - -### 5. Resizeable UI Editor - -VST 3 defines a way to allow resizing of the plug-in editor by a user. - -### 6. Mouse Over Support - -The host could ask the plug-in which parameter is under the mouse. - -### 7. Context Menu Support - -VST 3 defines a way to allow the host to add its own entries in the plug-in context menu of a specific parameter. - -### 8. Channel Context Information - -A VST 3 plug-in could access some channel information where it is instantiated: name, color, ... - -### 9. Note Expression - -VST 3 defines with Note Expression a new way of event controller editing. The plug-in is able to break free from the limitations of MIDI controller events by providing access to new VST 3 controller events that circumvent the laws of MIDI and provide articulation information for each individual note (event) in a polyphonic arrangement according to its noteId. - -### 10. 3D Support - -VST 3 supports new speaker configurations like Ambisonic, Atmos, Auro 3D or 22.2. - -### 11. Factory Concept - -VST 3 plug-in library could export multiple plug-ins and in this way replaces the shell concept of VST 2 (kPlugCategShell). - -### 12. Support Remote control Representation - -VST 3 plug-in can deliver a specific parameter mapping for remote controls like Nuage. - -### 13. Others - -While designing VST 3, we performed a careful analysis of the existing functionality of VST and rewrote the interfaces from scratch. In doing so, we focused a lot on providing clear interfaces and their documentation in order to avoid usage errors from the deepest possible layer. -Some more features implemented specifically for developers include: - -- More stable technical host/plug-in environment -- Advanced technical definition of the standard -- Modular approach -- Separation of UI and processing -- Advanced Preset System -- Multiple plug-ins per Library -- Test Host included -- Automated Testing Environment -- Validator (small command line Test Host) and plug-in examples code included - ---- -
- -## How to build VST3 - -### Get the source code from GitHub - -```c -git clone --recursive https://github.com/steinbergmedia/vst3sdk.git -``` - -### Adding VST2 version - -The **VST 2 SDK** is not part anymore of the **VST 3 SDK**, you have to use an older version of the SDK and copy the vst2sdk folder into the VST_SDK folder. -In order to build a VST2 version of the plug-in and a VST3 at the same time, you need to copy the VST2 folder into the VST3 folder, simply run the following commands: - -- for macOS: - -```c -cd TheFolderWhereYouDownloadTheSDK -./copy_vst2_to_vst3_sdk.sh -``` - -- for Windows: - -```c -cd TheFolderWhereYouDownloadTheSDK -copy_vst2_to_vst3_sdk.bat -``` - -### Build the examples on Windows - -- Create a folder for the build and move to this folder (using cd): - -```c -mkdir build -cd build -``` - -- Generate the Solution/Projects: provide the path of the Project where CMakeLists.txt is located: - -```c -// examples: -cmake.exe -G "Visual Studio 17 2022" -A x64 ..\vst3sdk -// or without symbolic links -cmake.exe -G "Visual Studio 17 2022" -A x64 ..\vst3sdk -DSMTG_CREATE_PLUGIN_LINK=0 -// or by using the local user program folder (FOLDERID_UserProgramFilesCommon) as VST3 folder -cmake.exe -G "Visual Studio 17 2022" -A x64 -DSMTG_PLUGIN_TARGET_USER_PROGRAM_FILES_COMMON=1 -``` - -- Now you can build the plug-in (you can use Visual Studio too): - -```c -msbuild.exe vstsdk.sln -// (or alternatively for example for release) -cmake --build . --config Release -``` - -### Build the examples on macOS - -- Create a folder for the build and move to this folder (using cd): - -```c -mkdir build -cd build -``` - -- Generate the Solution/Projects: provide the path of the Project where CMakeLists.txt is located: - -```c -// For XCode: -cmake -GXcode ../vst3sdk -// Without XCode (here debug variant): -cmake -DCMAKE_BUILD_TYPE=Debug ../ -``` - -- Now you can build the plug-in (you can use XCode too): - -```c -xcodebuild -// (or alternatively for example for release) -cmake --build . --config Release -``` - -### Build the examples on Linux - -- Install the required packages [Package Requirements](https://steinbergmedia.github.io/vst3_dev_portal/pages/Getting+Started/How+to+setup+my+system.html#for-linux) -- Create a folder for the build and move to this folder (using cd): - -```c -mkdir build -cd build -``` - -- Generate the Solution/Projects: provide the path of the Project where CMakeLists.txt is located: - -```c -cmake ../vst3sdk -``` - -- Now you can build the plug-in: - -```c -make -// (or alternatively for example for release) -cmake --build . --config Release -``` - -### Build using cmake-gui - -- start the cmake-gui Application -- **Browse Source...**: select the folder vst3sdk -- **Browse Build...**: select a folder where the outputs (projects/...) will be created. Typically, a folder named "build" -- you can check the SMTG Options -- Press **Configure** -- Press **Generate** and the project will be created - ---- -
- -## Contributing - -For bug reports and features requests, please visit the [VST Developer Forum](https://sdk.steinberg.net) - ---- -
+Here are located all VST interfaces definitions (including VST Component/Controller, UI, Test). ## License & Usage guidelines More details are found at [www.steinberg.net/sdklicenses_vst3](http://www.steinberg.net/sdklicenses_vst3) + +---- +Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) \ No newline at end of file diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/VST3_License_Agreement.pdf b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/VST3_License_Agreement.pdf index ad8c399b48ea9a2c29ff06d8457a76abe6c5fdfc..04349f443ac0ef319815279f33081a172b677075 100644 GIT binary patch literal 160002 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 z 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
j|dIpGhIGy4z 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 z�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%0Aq+AJhypz(-mP0^L6lxLUo9(;osVUH0# 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%0D*C5lY09 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

vcchxN8i9H{Qx<*2SfZe z281*!Iz)2xbdFniDYdct=}#>=TzhqOeVA&3eb_wjhCfc&GPwPl(hw{?xc06)2*}38 zmSGp9z(r<7L@taXIvJeBM)m3jFHaTe6K1FQ7cvJ`_7p&}2 zU^4EO82_Pu@5@glXP|m~ebDZU#=A^9TVd6ikOK#5gHK53qu-_Z4=auP@G9W1HKk=NjgLVZYS2C zY;+O3D~fQK$=kb7GNsBjM4M8Z;M6c9kC?xCP83(EkUX)H0Jq^KVp$1=5sSC$p+Clv z;PRRdBx@1}{$z|5F)60Vb`1Ax$m=bH7cR+v(W+J{t$Op`>Y8EA5z-MUhQ_@{M6pYR z9k36~swrFqgWkS7|32PKhh=Xa{LEA3WMQS?{T(6;>9o~r5h3;Cfe+jsmJ5c2Y%So# zD_}mF!*Ws#H?i+db~8k!NlOT+u{B=%fAv${(d&lnKf+DL;vS>^^8%KcQs_k04 zk|OWF32uv1_{>w&cz=$qW(=pEvtcYJdE?CsUie3Xd+ipe_sk$z$gR@u#aR^{|E7t2 z7ygdJrbvs-pWkr;fwB(d*cj4}{7$w8JrS}1)sllh~G%EgA2b?H+zhGWfTZ3uzg%?i#(I&gIo|m@lg_k+o=kfC4R&1C&sy7 z4BU&e_e3=6o+$x$Xtq9KKszRtf5N$)OEXt{t@0_06EVRT?-xxp;)rN-zIcBjw&2vS z^3QLo6CKds$;e#PzTu--I^x<-I5(|RSDE~_E*LcRLy>Q?Nlb|g&ma{UiYPO3!b*U& zEEZ+^ppjlF;Yk)Ie0?J1ubM4i($?TezM%yg`Ib-&Go)irPQ#)udq{<$dd81B4S1Kd zaa@FRLKT-$GoJ%wTUp>o-4dEDKr@pFwgQhz0T?klg4l)C>zK^0ufhG&IP zimY(ALB=Osx_n!Aj4-`K(va^z@HYX7XM9=vlnW@#EebPlKvx8o<}a>Sfy7#W&@P2W zC#f(DMlE=KTF&>m6Rgm>#8)mRS3f%bf>g z9Y8G4V$H4a=X36&k@cc5q( zo%1j%ss&-AuCZra^>ILj=LKp)<41_lCR+X-&Clyy_Dy|vr^9gvwlrW{ZGV|E|Bu2` zzkt(zLzy(>1Lg2xA9<<*t%!vNlz3mm6Krc_Romrv&ABMDC{WczXb0%)G`{RQf3ycP zEwn6KQI0)vZU8p5A7v!i0TrW2N@0PwC=9PLKYFu%nOtNO=J00(9Y`u;aZ`0jhs3X% z8f6trSyU@hT1rXvP^!emB$Gz)s8@k%e!A1edR5@{!Lkuu+#^9@v0X#sF&n9!!^NIl zo#EP2eel<7lM)TZSnZZ(RR@nDz9wAvkG@Fq0N7<;@dh@|Dzr8AR>O&VL#$R^ zO?_2+*>_8Gd!KnyI?kUv114?#P41+SdIcvfza2EW0&REvNjcAL2 zK680if7%2qQn;o6-gs`UL!F6>I8ccmwe0yiwB`Kp=g$b&v$0#!D_t#A9jOZ?(3L-F z_}0HdS{7wFpEbiGYbn2485^f|oFG`_3r5#Rw4$a4YnD@_^MilFmfN4I@mCP{MQUZ{ zfd-)ZEqw%lbJe-lvr@?p8KI$4iam|yiR+;u$5Fqj?a+BB~+H$&&+RWgD9F}VXhCo(7itbj8s zYtFAgR9Bz|DSV$YD2PC~d@Z(J~a)j3twDaw&Ha~vKqJ*@~QI7A*X+pyR->g=|k6!Sf>|4WhF?SCr z!AOO1V*Cxvoy=GXp_?0C{ZYd2EFqQCGt&&x(GSr|M}zBT_-l+=l)PqW_fq_Q2O7Ao zK}~5>>Wqo0s*A%~P6VaWmat!vR4|T7A*I+H9%PKtQZ;Q*Vi{yLH|C^_D9n5{Smn-` zO-Im&)oVM1dq2lqo2e1`RiF3u&eZ*T*N7~F_B+)VkT?1l?U3=jxX2|a z%YSuylAxsbWj!(V*i<(_k##;;j*BW(PF65oyz9U_&(k6QRn1_07gO1E3}0#uS}4+0 z(>q&{qYSDneTj_JFe@xuV>D5 z1Z%P9UA8Vkq@GC+Zb|9+fv%uiTl`-QdXQ>j=#!@ijHA5L6F$s4&}m*jdDwO5&HXoO zc1Ps0FQ(%E*qfp3!43a%Inli@bz|&M6XWF5%m`;+u+evxiv9X`ox+51qN^PO^XW-v z$`RhSHe8D{cj6`~xoNJMgb{hriXD77i*9>cIp$PFkAD)pt$Z;Q)AJlx-{!efeVelg zYgAH6NNYNA#ZF1a4cmDE!%a0#1@y3=E?k9~4p(?N_s7=PAix9>@=gUKA> zVn~OxqqS3M7>`8d#O(2N&484?}$36@*AAS~=uD zCf9WTIrSs?#>))KiRb1Ai$Wjg{4l3HG~34OY&zDadt9CiH7BvtNQwR49f03}{6Zv+ zx|!W!=g0|rO%F#*{RZAGnFvRU?m-s=)@CW*x?%K}K8tL3<6<$D0QnO0(?V7|{1J`kmHdU|JhYLBzB z<0@C$W^li8ooijN?9zQC%PI_2C&(p0Xtx$nc1bML7vF4{+q5{pH*;g>%SXJR^C29s zq%HG(s|4lg?H1C>#S}gn8qFYYV!!;8H^=Q0TSY0s#qISg@? zRb~WF6CAFJR9bE|WvKdC(G>sX&Dhpx-_LH}efGhpsvVwijG*J%(+lz)kDaY>FVw`y z=kLra)DX}5D%BWKZJl;r@bXG>IN-G56r7l9kew;kM9Izf+Q{Nf)ZMa#M@=P7dZ2Vt znZ{hujhyS*lLg;jjL@)Y^jNffnpH8VJK#QKSNGuMTvncOPo&t)WM7K%4aaLwyVgz6 zIX-0-Osu7NACZ?8ja6i+S~*4(z=QTU!dqsTDt9EtZc7RJo8cB0MG_T=5%@viDCQjL#luvVW#* z>DiuXz|r_xT$y%zT(crV*};LTiwb2}2B4>@8B&+ePVaat`M820*>ESf1*$_V@BJ6+ z5m5=Rq+fU&6+LoZF`NzCd^k+Pbmz^H(amH6eV@ep}3~y zyF8(l!};mfMmWz5ZZhYqt_Z9eRnuXoXa<#g$cfjq=MOu)d4^%vDcWWF?FVcRI{(VZ zb&OBpzY3C`q)hgAQL^bt7qbQ(7zkd+T0N7fxKVC6d5eq5AB7on&OIrO^0*5u&?U-!9C8FwO-V3jSd4!{IN`@D|wM_*Ei!d$Uaa&T;9_ukP&f8XgbK&g8&F zb3!3~5+*&Lw<*1%mvMOw1p~wnaD@A{sZIC|l}cZrU%z0AK6&J&c}BVn=Ve;pfDAnN zoPKBIDC2;05S~;P0J>YwzdpjhR^eZ<@P9ZXkd~#1FK%?~Vrz(If2Hd-#K3TCId<$S zJAitEa{1#wfBEOt`!@f#z5G-8sRe>Z!@|wY#t42b{Aa?zBlpP7Rm;UWp`3EQDq5G{TK&{;fuFCl9w5m=d3{tnEr*oUneH| zo$SmT4Tgk-T>AMlU}kT{r={V+b5RiI1@xHy454*{O&5oym*>mUO9=S;M!9rWO#eCI zzhC$=H!f2P+U-bR?n~og!QY~xXTSNfA9LOsgrk`%vdapW&1!z~qg+vJ(CWqaMjtN^ z50Be_zeywQYy5cda*_V>fbBy{c{ERe}B3EAAXT7rE6kh zVrXcnqoZ^0-oe)N=X0vPy}hTdt`(2FySr61HM=gcFPxq|dv@X1vW$a+6CNHu+vI0v zYI-r>hEZbA%uO%tTU1!6qON|P=xk|awXPM3IUKW$Ws002t2^XjW5aU8_`w5o>b9O8 z(Is3(auO1mUrw`)k>TN97w3q3_eK}HqPNGLL5vW8zGYm>C?Z1PgL>(;IU#gDOD*g; zb;+ZdF7YZfG&Cy8_jJ!jP7b*d+bpz{^n_kIFry9A-vt%zh2Dq9GVio*I|c-lSwDP8 zC;se7fwP39B%#=s@H@Q7i1fns=Irju*?I&qCFSW{O8^cz_sQ8Aortsfw;c@Y z+*VUXUGwjziCMOXUi|s}d~>48b@@wOU0q5_iZriA06Jb}Rn_6a!4C0BmhYc4JW77l zG`oq)NBh49j2b)-J0q#5r>7+)B<`k5P*PA(5E1#uGTpp+lZy+_3RQ^-7xk6`t~_=2 z@ca9mgzk^VOK5xL*(IeJ28rGAy9rkLbZl}5hM1`s16Q}VuYVX!6R%cFC-3s@e;*BIpKPr-$&u(JUY6%XkU>;$Xp5T5!VU_l#@<<6!p9CE@rtg zxR13bp&ddn6)8)MhmSu~W)YB`w zmHqzx`@Fnwkf-wU^18aZc9)4}z8|Zr&C0ENN}Db`7usJnH#e7+l_e!59UQo++oBHo zOG~2_mdwlJonW*~VOX?xL7eige=IALnYZV{LZoX#sCA&m)jr!y`TSX&WW4f`F6AF~ zd|qm$7J2G9Lp8V?`dRC<}4n+vshUffS~s{KXI|Pz9B(LPoI&L6dISC zpPxUz10SKE3bpT+tf3*5)U}+2_$0921#4$WAduf(>~_(gd|_6OdNSMS1JO&c5yL1S zllRtH*q>Dl9KAUz`5?9BypqI)zXFzyP0k za~A>XF1=CA@+m4JeU7HZDXuMX3A8C@5Jgy}fsi*7fh-589wY zX~~igc~ETltJT3cD+t>Mto(7=;dQc=Nd zlok+(PkSCkf%MK-R<;EqiHV8H+s7x7+hn5LM$Iwa`=8(N1o>^&%VMKy$M0npxKwn- z!w|w99X~gIRFvtuy9JP!&XVcx&zz_I85 zw}1F5EG(?QU){{ijFpvDB-w@V$B!Q-O}-6VGhvDT(e%>!d3n>duC{Ok@F9pAj&HF4 zlN3t|N=i!3&Smf3v6nkOBt@;wIz@PTj&b(*p?~s&(1#t!Tp;5z{$@m< z@{3omu;Na|DkV&P888V633147+5ISIsSxh^XW4l%gk32(YC(ef;z(>eVYo zG@ty!!tTk*$?xBjewZ+K@7KSS(BxHo^k}s&kq35wdwUZRf=_$4H7#E9;e(9%lY!pe zI`{Q4r=~x_a{~PFOn-}S3yEH-X&RqZ@J8^v1gN$yCVFUStVy6y9^54A<+_1K_&5-gj zJg*3|ww`N#sj8}4|6}2Wfz((`eEj0_G9-`myMTYvPfp1^0dg;bQXsf0K;RY^m%N$KrJwhN@k=_6N$9~*}AS2oyoo3H|;H^xbM&Cc4kgT7cS{{es@0iwR>u+E8jkhhmmr|3?{yQ&7gJr_U#db6ep+s#`ljtX9uw><5;R# zn3ys4#7u+4<<|z6mzJ*TZvB)9tQ$1nY9`2zdy0x^<4EQK!cH^#`uZx?tLS)C6x7r_ z#x=Z>lC;_rutm?U^gk>^bD6Am;_2HrmZQO^lVBUa+1|K3>@nnMMl5uLm6h4Xa^vY3 zx^MV8mD;rlRc&o;clWCJ_^;I_ZT>MEiL0%xB{{PDUN*nf6OV#|Ldeqo z^~aA|DB6}6T!?gylVwCd6Tl}uLZOYRTH?ePANvfbPq<3#Zy~|{4G#~uO3Y>}h6AW# zp4U`Yx8{10%22h8bPMmB{CM};736W865+dykr=#rzJH!3{@k$IaljIjluBN{jYaJZ z?=w+*tJRS_<8ggt-;2}b7cZ_^q?7Zytqc*f-hKV%&7Q|;95)i zbU>F+$_#@QSjHUVHx<&C$@c0o+jk}GX6jGt5&KazqH%BE_VwxJSszI7^71B0`$^@d zB_=Na{K@jA+GiDEWApQ-M#}lYS53%QGBPqr&x0~RA0IzbN_n3^nRw#h09TTBS!jnm z(|FhN<;&gvVTHR%DJiWcx&?PTjL|P&lv^+fJnT=3Wmd+gzWrnTVr&F9a3RahUlPltxIXlZH3X5MK#54p^lc5>S~h=_~3npnBWqO3!|=?Evc zKdgTN^&vPgu)V7*F(G01R(0a^e&+oT56HQVyZih52M3?KxfLDmBM&zD{Qd;hKgI&; zG=pp9H$346641m96vBeP3T+zz?6FZvzZC^hd)n_Z`JoMXMjp*Pw>-o7x?FHtZUWDfq|(~6m= zW0GbHJrzMB!C2y=NYXM?LGMig7&caHbi6(5w>HY|L5g?t)-7Dpz;bBqu(aAX_v*8= z{XbL^Pd^|y-Pm|Sg0)MYE7{%aj0Npl+t=C=_G{$pSNXV{ckg;a_hGXmwzc*2l*dr7 zTe#A8IeXyc!YHI4@ z!k2CG{8+UaO>U3bY6nf`LCp%Jvb^&8=)3sPCwJZ)Tdb!pE4~<`ASOl;XB4O9HNUn; zuYxs@^kZW8`#zIQqQbEPYX>gQ;v?7yN?i8E=D-}~=-~cX+)xUMS-kl3rxV z(&e(0C^j}WL2k6VzgAZ4PK@1(V$heCmkSCCth^O&vrO}3OMl6brr-AmF#2uURh8mE z0vS2a{H}?Pjz8AxP*$6zQzu!$p)N)i!S_&x1oY?5R!=pBrfK42kKV?`J^k66Fzfpp z&(*tjMl}ZC$Y`=Hrs`0d86Dv-L`elHaS)`^!nW@D=T^Rf1jK{ zWoF{zBQ#4e4KSyJe3KNETbkM0+JbCrmYG{x3T(%!z&cM23JOY2P98;(+`F%?#LM}T zmGy3FjZNPh;4*&>$IbjFM^*OL+b6szC8N0mRQZ0r{q^e?tV&z`yTU?DQst$F`uZ^} z3kXmhP5+ZM_~fn@Dp(E_#Vf$#!pXSg)}hVB@80!oI6T+OYykLf4?U{4w^s*4m#&%m za6VI~`O^HBv@_lZl$VHUtV}@cEeopzi!g zT|W-JGY&q%pG9k9U-!Qr4#a6 z*09nJNpI(gdkC+V#?nhkuOpH_=Xb9?rB;6eU5SpEI6gL(keb?SccBA%@ALJsf#&9G zwJbUe`1Dmd*Z?4dCHzi*CJww6W-i9V#`eFt+8evOa5=5w3Wg?<%pw1Lci%4JQvSAXrso0Yg6rO3EOLS77s zXy=bKNRG);KAyjiHWfqAkoB~+vGDLn&wGGNMnptFigkWbjHODNotQZ5j%8*;+_{5Y zU~xh1;^GqLNtb92XamarvUT%&z4S|U2ufeyMz;`VtB4r;A8V=0NWUfq(Lj>N&u{jjMJ zd97d>tsclQBcla`DhfI-fpj(FAP!0`Hb0r9q@)s6@Ti@gU4MqOw1hhN;uD5itQP$-TTNf;Y=jZJ{{nVXe-EYOkXcH~7q~SOnNmt}vaG$`$cnL)lvMWi{5bKDY(!+_=H}*gItdSe zY>+BYqXAPf3d9~XJaSXLLXS;BFG=w9iKFD(I` zR5{cuF@AMV8nm-hRX}aUn$KWU1A|D2RiQuLu9hIh*gMy2j-Q#CVR{z&)Ws#%9?4@4 zfus_zMW-6A_AnI*1)U*_i<5J)1&;Q`1Fq5C_mNk*h3B<7|8 zNQv~E4ogT(R*xTxS+t_|#|=3og#6e{{ z+pM0wn<|1-&C0^kvgCh{KC4sO3Ug;?N5957D>il*AlG*%>MFVPOYRjyfJ7ILFMIZh zkg6V9TQ^I$c4~}K@L8gsJ)FKT$*&4Dmyk3tDqY%-dtrv3h(ho>=ud{nZHx*-01w~; zWoN+FGDV)!n98P?GT^W``Ab}y@X9zrK7B^JIjD9Ain4-2J8W8wb=9QKZGMU)$;Xuz z>(#rGl9?*|MChlXp1_|Jm6gMr45_(j9*+zUYgaC!tZ`EaLXhYc>AQfKR{5;zo=l$DngeVU{b+vaIH z_g|0F47;rIKTJ-tO%rjkgqVntkwjQL@MQ>*eINZQ0s0NY;B2)M{e-u%F%1c;$aKVq z@%--nRyH=aoui|fJ-kF7vp4J8dn5Z9Sl6$Ue|+-_jev!eG@-d!=F9P?;)G}+21|g9 zB-+f-4e06V4bsJ%rB!5iIjb6R@2b3nKMf6kBB_Prz;aiYA)l@%c(RGG7UJgS22_9R z_Qz7$%(J7-a9QJBUC-Q=FJHb)OiaucV-j_PVhBO3(;3dK|0W|j8L>LTV$BcPTuxJ? z};`-Fu=0R%*^lK_1}!6#4MBI`Ffc;zrQt=my^?G$@!Z~NJt0+BV=mI z810Ae(&u~k?g2+KGBm{YD$FV=k%Aoc{smRB{E|j4HX(tOkZ^u`TiSlCNHzd{*b%EA zdEVa6E&@b+z$HjXNSN-jvPj~TQV7%Gx$0%BogW@SFt^2~(d`>BN78nu=O2Fy< zGV_lZiD z+Ryp<7E7bw_`qI(A{!VOz-M=YA_{6!*mvu90GhAB#nu?--ZkCe)>b(MEa|o76WqWX9y4ysjo+&U84UVgSQ-5F?4h>v z9X6Y9uC8Kj0CrZQ$n#tJ#+{0JT`W zhp2zrcz*Nt?N46ETdiXs?H@ggq!Hy86ntGJe!GfEq=lE8dkZLo*U?4`g&?qf7<6`2AxfQpb0;Ju!20<|Iy4#GR%wJ9UhOy?XXlE7 z-(vpy?OSTlE=Wi!L7BSeFp{eZ(-^31x6B^UU6anS@kAhG6Gk?lXkLBz0*Lb`JtEF? zGLB!Rmonz(=QrqyULq7g1XkyM5|Rv}BrcuANuP=HIzduFdq>A~J>qLehYe_56q=J& z_65bo8b*b$uAiaht3W%t^wvpI_!}wf2z9mCuO&v+Q5|E)LMm?^o;-oE)DQ2QoFYi5 zGsK_7OcWJuK*xkySwyPp3nV3{sjIzxs>$yX)==e;6}p`FiL<)(z5DkK8mHW2K})n~ zf9;m&b+xTdl04U@=^t^0;OUr{_<+a)OlxBbg?wr@iPyrvY>$ndeRX|Z+Ven#Z@Z@t zc`fRmw4mStKre_!OmQ6_6O(C>>>0lm3_ z>uY)P_=~#!f^rmE>Us5X2PH3IV%V>mB?XNLx`0KDJva{9+$YCr z!|$}zxm6&y`(B)jUd91`rS0qOjhVN#wg#p94CX}Gn3y{U2kpGV=FQivtgLd+LP@I& z;@B}m+xC9;S)lV-sa@@A?J}CV5!*KS&QfHZk6c5rKIn)-&~*o14eP#4tpg zl^Qi)LTOMr1R6UH8cbatv-zL1Lre4+2)`JJjofG5d9fdzJw`L3#z#d(&7tEjzUKi6 z0te@<;G?**&%^+UVe|rf#rr$lkA$FQQeWk8e}8|tm9M215NAe4Mn%OwXlJ;1cu>=$ zU1|*~;MJQEUHr6SZX%#+0wXF`TkqKqLq}W)KoN!Vw)pJ#Ye3R~A_aU)FCalczb4rI z3_=<(%Ui0A58i+Hu(-H*1$SUb#3&y;`k{dwh{4HMP3dVUq%}4AZm+xl`Hd^Bg;7C4 zXdyhB@7}%BNE15*H3oMK2eAsRRk5D^g|(E3NK%!Mv^#A?@b+v|7b-6o*XFkmde9F* z!2z5m2rB!>JZF?TbRzUzyli;|Sm5q`GyjYAwYA4KHXWAn=hS!Z-0}4EgpjqqQ(LI* z=2i*e7)rkhX@2KUSW=I-f`Y=mdn8gk-{-YlZqF1SUeu=9hxJs>ghAi%;n zlF+#^-PhjIa$#$W+p|~opDSq_8hV1rKu%6hX7qEMK|lhi%kl>Xp_nolVSpB(DB%W> zi*ZC?*WKaq;|Y-DfgM^%0B_R;Z3fWz<$7rod_0(-{DBr0lb&1pZ3hK+0MklU73+Nl zFmrP9=P+JAhy{|!XzX1V50JrZn2ZJb)Rlyi@45Vi*2l+(jQZEC-B{6&GK)5A=~=wi zWZ(~Ft_^;LKc0bNr=LytQu=g{8iIbusqXnUyza)q)>hc2_w|noTks}0mzy=nX`7g& zK_3Ps;Ab!C!D~Pc6O)tIDR?tMlyvv8*8l`|WF%M1=kDF8#6AsP(j^4D&J;OO z*S_>9E(@TDn1jA`>KYx;=E-?XQz6e_aP&)-ii#@Y69a+|+7f^=%n#&Hs~85uuhnir zR=BD7D$KvD#nrHG+H!($Z*4%H5s^wjW41J3BYaus5aA zoG9GCPs;7`=?$Z5`c14=m*Or6q6lG%M|Hdnml<0FH{6NU#h{!rto#h(>jWyC;e zO&%;3WH|8y9H@(cmdf42E1Xyh>W z{q>k5(oS4N1g4{*HF!D(2B%Q$0f-aMTG`vTxc3nKqB)sWEciSCivhV9X4VMq`UCX?UaKU%e7tN+byOD2y^8&K|4K8XA% zB$LMeMla^JGRZ8?$-x2o?s9*!m7U$m^l}GCMkkt}759Jn@`evzC&CTj6pUCvVdTGX zYe1{Ws;{qCj;71H&e83k3iWl^^Q{;v!jwsC7J?K6!R$XN1qlmj3x02aQrs7YMoKPAO_hWmton3%aJ^`E6A{Q2{z3S=m) zqQv@ZPDtG?m``j@R;%5)BQ^Fy^$*0b(WfWuPu{%L?rCd7F^P_SyEK9@g$R@F8yrmR z+B!SL2;2f<3ixvI+3^N&HP9<{+fgI`=6W8(?c2{l>8IJ!@i!1S`(2RY!YU;-0|S!p ztlBEf%sF6+$Y0|=B*uP?`ZVTGr?%uWY!YPpRJz?%o?7D4k~Lw_jjNJ{P$i_Pq#`BF zD=6qPZTlk*X%m2P9sm0E%l$!VNu7ATx5=JU?461qFy6QCzLwX(r^aCm4V9e#&Gy@7BF znm?55y4qScbLVA@!kPAVYcO~~A*n1hW#Tn3g3#6_&wc=d&+Ykn#tS+2za_;My6bxw z7kYVlkxlMBml>P@&yBTp?)hVmXjP z-$)}4kY(HpE!U-WGrvD97g;xS7_JX8+kxg;<8|x^Op)cN%0Jh?Dx@qp zI2eXwagBR2f0G(l$JDeDY7(%u^6%M~*OyHE^|F@?l}|Unzst!HaGa`{^}D3yih05l z0ZJ8ckzVm+Jsq8q(a}_4CsRASLXv-r!MIhCl0x+sLw>G$^xHSESt&f>>LA&h zNk+i542BM%Kmcq>d~UMF1W+0zxu-rvfh7*+=Dfb=9)b^tw;Qkn{>DmjUR|9S3k#Z0 zE)dM3s>kPV8X9~+o)xj5u?`HPJ(&-IHU*9=-3OPXHH{(H>EAr(FD)vHIKh!%pJrkB zn^~POKUWaX5U|Cc5EbWirlFw$RG2G1Zlt5XBjFz$+cz{s@TnG%>Ic2jl^BB-LX=Q+ zeurV@9u>S4&R<#3iNswNP;nXhEcGh=pp@Vb@|nRd26!FsUfZirZB8Y>W1aPGQ4Z>K z*#n+8nxiA%zNPV6$XQx4iRUD>#-*fq?##(DGc%tC8SZ!o#NybVFBRkHbGYHDhBHeHJ^Y~Hy!vU%{kzMmxwRlv5Ltnod)i}cm3O7ka(5L!(nuh+)P9y*2!6C9UIX&If(6ILj1<+6KXyowg`O+BI>8#6? z++iMKEGtDuNx3>V*SwK+V~DnqV7JsgM89)>Y#-V8d{^1s{Sd}7pi28det}p3%(DK+ zAEI6j4UNOSy%bT`hrr{1x|HSvI|VODmo+g0xq`_h4JemVg5{1kNz1+&^0Kh~n7W$U2N;1t=&J0^HhAfgi;Hlx zC~aO%CaHx>ppHVb+pFo2gc&+;l-I9Gl<Zus1Jj-{hf|bDNfq&;vA%PnDm~37DJHY zFHY|mI0LGd-{qfUK%YM*CO$p-8vKJ=XyrX17~-4i&hG9FPk&=xw)g@NC55* z3kmj7>#JFNJMIc}4&XU^GgNYd&bNA<0S?8pYC<>70dAHQQTYCSt6D#LmPK2z3fA1S zdo)e@Fwpc4*rlOsX?JsnD8=LtHWV%1OvLX$1k(hb3)t)?(DXyT3a~b0$_BvffpG!%`KkxxNse-0;RzZZMmueUrQm{# z?5KhSw%t^sGa>nbhr*js|2=HW_Q>PoyMFU20@L5VRy6ku4*pT*7H|XPr8j+e@45RS z^cjqyxQ$!0 zP1hAcfnd``I`gNY1D1V4{Hf*5cPUB9KgY9vA=h|J>RzX&M#y*qbOxj!gIff{IQ<&h ztj{x=1X#d&s{zTe&A@!3KZ%bS2F?z;XBCgW%$#Bm3IDu6JHt$6E{dvXdk0~Smu3Ys z6+ju_s3L4z+@pNY(wRh^10_dA7JS$ucqknZBKhK7d=`0OoV8QMuj z16HSAGDxCh9Mt12yaxzQ)@r=6q%{muT`|<|`&s_G<>{#@0BgZ>r@$x)IzYU1BgZ5jPwYJ+)+^^Fdqz*D}Z@{2N2>N=?oQU zW?Woq02iH_p5A4J{c)qIkr^rq2tWybBJ_BaZM<5x_B2oA%1$ByNek$eM)WCa8cx_T zpclhYf))I4L_Qe>{w;nlzk-cbxRaI*&#f!ir|&*bCU^4Nw~JKsL{_gublb3C7s+kK zz*PdNgt!o9i`SGoU4qYcv!RR*(YWw(E0ZTOe^y2ch9HVDN3t)G1bC+R!MKNcM9`a` zo2x?4zPJIEm%yHJW#W^Ak(L%gVDuMc7W8l?4twY$B&-c9OD{oN0FdMlyA}L&c(qv? zN*FV&;DyZ6lS%V`Uvh-&Q9XPZ&KVHNvO|ECuvvn4oQ9Sb#(sP%)m2qo+g|KMvTZGjrI1iC9nwmIZJ(2K+oD5O;+`5E zy5bAaUl|#ZmXnf`4Q49jd-fM#F5(GWu%itgn z7*4_44B7|yY+GVBq2iL7+2t)E-2b^GhU_XV#VZ}s(=QD5B&4K4S#^9A3+5lmIHMgjtH0!M?~P_M*05U6h1%$tMdvzjIDvU zpDad8D=8~G!AOmX3HhU72^fvKXA{6(n74O{j)8I6)C5jhd28#Xn`qMKJ8iJPv2(*Y z%3#fKKic>KHR{S=1sgSV0+fYL+V>ph0g$0N35-@CYAZ+$M>DrkHD2*Qo_RHLx>wo*FEJcf8wB`unm`$j;~_E&oG=Q*pU+u z426W0mO8<>o5e3Wx{#u`Q#O#HQy_B;f3KeJMcBQ56qM6DCku441fC%hPB**>T8EOE zFy-*UZ|={qgoj9n<0Lcx`r^N>LF7BHMj(H}!pGIKzOdt%5GlC0=jZ!_z{w@OPj&!w z0P*)~et`r$K_nLPzto`O7wCNe{=fA1w@3Yaod;6jm6;i^Eenecz%77bP=fy~g!2UB z)2_|Wzr1z0J$vQ+ZVki&H{Ptz@iZhtiD|~l}=buF3g4g9rF-NP&My~^lAz?4svT+B)`TZyWJKS3x1@`s zMQYI1s_ncWmOrSirIp!dpr-J-vyeT13n3^>;R{|lE~8IHOh_0Q7*0;xz&6agp7s*G zr(CLVUB2rk2$lZc+dL3=0KrOea9{@+=;&nkWqtknwdwTN+n}}3SFeVdb~iSz#HyUn z_ufi*>ab&FpLTV2zSq!*Q4A-88te%3ohzFaV*h5Z@%DSDo!g51FV;DKS~8XlZJ`QT z!dw`9ke<5>L3>$*Ju<`oli(yIx_qFw01ZS>R?6OA1K0yKTqlX&b7B~ z2QW<1O`mc`^&l>;w2z1VGR?On4atyfEv&4F>FA=gjMr~vy=~rtv5;r1gv^as&b!d_ zU{Fi-Qz3g9n%giuag~Pm>`js%iYHwaa-Oj5msF6NBEQ^ChA|C5AG6JumX?-a8)^pw zeE5!VM>q0)LfQ{X^GMX?frn1z(@wO6Sx9Ka6Ei!qmPzoa&}x#6A}|c824do zfDaXS-O927ISVX8q<>Nl`c)3Rp%qB|InX8LDnv_=wYup4t?u3fi`6bvess7@S=k>v^)ByFsAB%1S* zB@pHLnrHa*QV}UPSpXBP4<_-+dyiowOf4_qSO-{*7*mxVfRNL;5?^=|(G2VLMe zk6E1?memp98Wrkg|1(!OW+wG*vPGxfEAsvm1&|A3=ZKhdL<(nG9tb#!u5Zv~4&ew2Uw7@JhSm0qfy zgHCn9fqn(8K%}4e(33|@P0jh|j_jf95Qgio{$Vx84nE&OI6R7EaRh$E4o`Ks+9b7Nvz>13am5sKBvf9A6 zNKoD6c^H;}kGy*2VIbde1qP@8P`|;m%A=3#etlHP)-`TR8>|CB)e;gkE}uPg(9qD> zcCd(VhH4TAdGp@Y_CnDe+ON^6A}u_J^MdkATD|eTa7yyZEcmmiUYy7}vdrz+8*ZKu zJ8V13S3p{tUe?OlnfHB2W=IQ=VG!|d%tW=>ed*{>bJV(WAtN*1>sjbKGaxBTL^{4 zrt>|Vh<1*h+YS>ar>9Uf)E2(eXz;VLVl0njE8?`E4x@rbe5C~w$(}4NF1Ga&QK2!# z1NB@jcr}N?ohIq^3<`*TPqvrb%kTo`6@=SkkhqlvJ2YK8BPeyKe#7NYe#04s{)q`g z-VdaUr~F!~;1(1ICehM3+1iS1(D74iGAH5nYZ^(<*M45uJ(hOCz7Kdm)QnM>sr0{R zNNCy53$wUYQqx^={Q%jnL&ubw5lT+17oB`lXej2G^L4^sFmeakT<8vQ)-)j2t_aGu zx=;~&OkHQQtv7veBt!rB8`wStpDwBBD_UDKsz9X#^R4xl-h_q*Nn@_`qRi!ifdOdg zq4Uzx8UXj%<7o(Z_m4Y<65QeCB$8(DJpzRI`QwBdL9bC9FB;n~d;-j5;W^n!(C5(GH`Udst0wj6B8P8UR zWPg@I+@|o`gq>W|O6^@lyqG`DRgFh=MH_6BSUTNnwv)WxAoKoe4lt0Xu-HMNwI0+2 znk8ZUhd|pCvwOE;Ms}j?d>;BMA^@DcX1r=AwAo`evRs0xXgFkLBh`P8coEi8_wPv{ z?}3JARh0EKnwIts2*4^{$~kTK=dPO$wK@JM4>}BNWk?U<8otE-vMz(L%)G%(V7vuQ zLR?(@!$vq$>!V*ke`b9rCL#jo(}3oc8F`|i$?VO3@8apzaYv}qyn1kMMut)Ck<=1G zgU@g)>-6#L_rsvyq3&NyEZ-xQW z(3VQ2%m69_mDs>`b9zoi(iVyT;e4!Cs<0{+`0$Clj!(G2+ z-q7WjiHV7G{VXtZJLkT@N4ADpOdCD^_lb#YqNkIYY+yd?)SLwq3;ec}Uk^@)61w&a zoxm*TUrWVj8Q89H6&RK2%7b)7L?d94Z+UuKSXkLN_f}CaGRoEIS5R>=P*FkN$~nur z^C3+HtnGbu2Phc$5E2Drr`itz{uSkb1Q}L;gPUm3*4B1=`g7u?-hn++A107G4=5&3 z@3nudhtT`VNby~n?&%F{-o5J!w&ITnx_4k# zPPy;4AW*I)0=uxh9dwQE0sG_zjLQ9A8R_fW5l%h5M>l9zP-ThxuUmV7;mU^=2g49%qs7I=b05K=(dKED`12Hm80XKJ zO1i+zNN@GSWC5D*UUkUb`Iz%_@0>jeZUQAvf2tM_)L_D03GW)~9-@n=mAPKW3Fpj^ zV%-9KKMT|+Sh|mnF-_m3U+$2vu<*T^DziYbUoa+BOi=Plyr`o@nCeE1?Ze^5A~`r! zn^jO?D@vP)#xV~g&XcceB0zuKybY9+q1hc!73sY&s`i0#Zd7 z?dhca9j(BR2q|XffT~QI^KCHI^UtDN@wu6v(^_m-Rto;)vM2`FgDJ&!ytk+GS^Xo! z&9zg|9N|DQpHk?`$_i|HNO17N+0n|dyf+tqk~OH5RB0(KlF|?RDMl$5+36ro zlO5s@s)2q`1omwJFHgm|!>>+&int)Z3^9L0CG^-0%qy^*csl^%ig;#PFK=a(NknVK zzs-XvrMobL$qj136c{(*u@j^L|Kgo2^^xhTUVz_+*U_|+_#8g8`Gn=;4Z*Q*}-8Aie%Ng?|K@v^}<577G?MQ zB&Irq{@l$fD-ZNbr?TBsrTt8umESB|h&p0fy3yM$o+Gl(4|!ulfQIa3G&AW<1=8QJ zlSmK2#lWMwkwo%B9cHWg!uJ;z7Eo^~g0=>Opo^SkjT^N2;yF(hJQHE4nx)|) zAU78l-Yhp19|0hDqf_d8acbk!bmJx;?|u8iS^1_Zx?6hO{gkI3VBcI z2{?3SiELXMj4tPdQ-NQ?3rJ^>o}XK3Si|i~j*tJWc>TviN1Z#A!T*?e_B?}lyz-4i zz^I+1OtF+bDDNEgb;=diaid@%5s$jSK-k^Mx_T!6e@z#&$^QBHB zpr(UBj-@3_XKHD)p5Mq7!s0j0R+n>O&TjYWBQE_uH5H-ursGh4tM#U=Y`h@0^jI#} z>k>3mD-pg+Ckv1sGi`xwadB!e_vq8!mL1LergTIX)GqpWV}lC#^CD^(9ew(W$)V^2 zK4(ke4rZN2qdzuQ_JkcBzQ$KNz6Bg!&9(>7^qSgbDffiT z-8&IefHZI&Hex$h=^;hT%2=PtUu7B=o`>>WUv=mItwD}M>?`beY;NyQJmr=S zUmMM9x^8@DkGv=A?cLZqabHRVemsLvX|2PzGRmXWA0iqrSC0TPW1k;ugAM}C?Z}1g z*~S=FxNKMK$iBp&|2+L6(pmXo_p$lH)?T28MTqo`?A5@=}WW)WyXS zv7-;Sm%)UkL6nr32xj01Wkm2aS|VwfzCs5M_zCRX+l7zQyy^Az^}V)Nv`d_FcywkF z(zQWIOwNeDe)?e{dZR4CmiC# z3@)HLBBC;9z20fQe$U!DjIOaC9>yd-el5od_!sy2Q$}_pgh!V2i=$hO{G?_7cR^oo zfvnZw*QKl1v<<5cP0AXHf`w0~ZF5tYK-U%A>3{O4LHum*E9@{>^Dd#k^tFPveIYc( z^N7Z?v_pKhM0YEBu@E!>Zv2Z_7Rd7`o>q0!J8?$h*QKyiGKc`_SQovQ3NInsg){h( zr*y2_FCZ7@TcwE+Z*JHzD#DX@XDM*jxNYyxjNTIU8GOcgf}YWGEgP<66OdM6<~dgl zS=lf^I62mrTCZ0A<8pI8k6!Tj1`K7Vc#z|Ip4ZgO%=?KpxB;POo=uq{%(;*hR*F{? zr$Laf&IRAHguJoGd|Jh$0wRs&o)~s$CRPr@OZY9a3*7T)!|pyYeD zOnZI~4rL3sC=u!$U6c7N|J}SCOrAn{X|xVGN#jKBPbrGk>vh)S=Ds2!0oE>JgXPgB0VcaA3ri!x+Byzh^LcO67W z>`|Ap9zCywcRc7wAYhp2u5j`{1x5dt<_o}HY3kmu2*Du9oQ@-Symk~9d;FM7GP?_+ z8{O9W{Gia#Zg8E#pi*v+6La|iH@!pELgXr?#OKtYswr19>UP%0TZGKNr#?M*@w^5k z*tq4Jms3Pd4in{6fx3cv3pi~usSa$`z5$S>aa!M8eLp~NdJK-LaB)ee?NPV)0eY@z z9ob7rft^ogOl}hQ#sL5z&<4I6(zMP_R zZf!mGPdcZPkoN{^p}Zl!jI~2gTwFY%m4TgI(N*&9_1~a>5J>p69z;$~4l5x(s&ECM z2tr(3619+3q_Nz&Eun@b%nh13GWmbDK2^YornPqp)v)2|472qp#SaPjDn12;`2IDd zw%0i8#F1r0#XgDP|31mc0HAMlrm3+b$>txRT!Aztza>I?bk=Yc1Qy_3{yknw=Geo0 z#Y@}i+~kJ!*w`2-U>GUK^fa!yNAy8U%}GiMJ~Z^iP$^ryfHO%?!Any9Y9C21Z8s&Mt#F zU-YBBUYGnK^{6D^B;=_H6I{nCD4_ElMx)Vh%hX;Ayt>G#$FsWu;0}qJO4lx>U2##- zxts@VpE{ek5#BT!cZADXj)Uv3d}!qj7=HUcxOHtXgCQXN@@W0rw+ulqq0Bqaw~9j5 zwhZ}#%6=3c2v$Uj%3pIWpS%z8=JR>tHGKj91wtK_TcwG8;57=Gd_r0=5k|&q?4Q*! znoc16qzNS|UFpc#%B-OtUvueAv#_=?QK+o1j7KpE@Ja)^a1JS?Xs`zKOKg2yj?7oO2ho&CK%;AyG=Ky$ofA~PB? zdI@Nsih*N6h%$-){CEh1t@qnnON+#XiJO0Zk$1cdnS$R4G3PZNR|)=O0Vk*(Wr7C@l(`%0xr)z>BTXwrJR3 zz=_DATmE?H;$;3J);P}zI>h*-gAL z3ie~dk4kKW>Rr2rnDIQt)?u~fyqV9?FHD(1xdR&UiumSryBMAK zz%J_nn)auZe&vr}l)UWPJn5Xm{UA5pu6I@*-}SkEy?=0U-0cimac|gzasqyJ)F-fE zh0OZ!SV`|R_EYjIHa*NIFjTa&W94MGF3)3&7wO2uP%gRRpP|h1m&choeXqkcq2M5b zG8-KkNlGB6;h{XR3)~Pdf}`3A1nI^VCjG<@9AX!wZVHfryckLfiGKomlv)tt)1W($ zo*2OXlO7LuQ;XiM3&@+=d_ENkEKux20*tDH!l-jiGH@i?aX?!q`2Kr+r4!ISh^ds? zxh<2=afM%l7y)OF50tjFzbwMtd}uo9!3{d0MdLZ~aqx@O zDyZHF-ce4L8!+rRC5Zg}^CyN||5$Mu1GI3#Yj4W@9c*36Y+4xff@cnZ=SuyWCD43) zEVqdhj9sfXZ@=>J;lpi~GR+qe@i#u^KvAigptWh=@q?F~gcEmj2lTl>o8zNcs&w8> z04EX^8F}sRZ8&IivQ7ku5(@&gYocbws&_q~jhVGC1aDGOyzo+iCeO~N)iCBW2b#jU zIU3R5FzXhS-!#H>Wo6XvrF|(bSVi7|wRVE1nJzKB2yz%oo(YFY!<6*=_ml;g1HL)7 z5YzA1fB0~|`RT7~tl=!jM8~e3F}MSQerSP|eUW>j27XjYWx1u?b6Rrp0;MHZSpj0* zr=HKB$3;Y#|1rW9b+NI5=2#E^vmg#}c(XrX#vTuwGr8#SO9wxY|A7)Oj|SW!aQpP1 zTF?)d7y_*QC`C!?c~Y0CF;dZQz?{lYg@C7mKkB_Y2Tl73O#_G(v>H zck;`bARxe}!Kb5E&yU@}Y@8sKx4nBMv~P!<`=JljM66-p_@%2gAY3-#9Rm5ReEBKm zW?t2;a+Wiau&F(Lbf*2>;7TVCz z?`rSXBQ|_;-U6=+A(8E*PG;|IRHW0@9bxskr`A>SjeH?+n*sY3WzH~AbFigh+#W$x ze;$96@k@-4fAQpj%iNtWtL{1~xZHqqBV~!|VLCfJ^fnOzF}hj{!}ibL*L7TQ@ot67IUG)DI9j2-PyC+bAm&T;ndRy*I4h1#^SJbW#UKE%Yl_OSLEUWwS_9lM!O0P!bcnrpY0djMo_75E5Z03>BHV(h>Hj~TD@gQ zcJ%^Y!^4}cl2Wpl7#a)wnmUG7l~hzPAu$PL$%3_>E4?Jw-W_7e#FR0$UgMA;4vFpU z*@fKM5W4?@T#^4}x5oc3H}?PjA3B`>?=^TEg$A830Gk?*A+eY8J_a2|?Sjt13+Qk! zuRm)4eHAMF2TmuLW@1@zewH4yLLz{?Fd5w*F} zx!EsBqXT6!SS?tv4&|U@0L-1D^$q|&z}%=6fYii>wP)2A6=GbX^X{et^Uq@N8s+Wg z(+FS?-8W%x`*1!yAj1lsSC<%i{ea_qomU^QObtSFqVe#^;m1^flu%C1&7H%%2{_t1 z45@)v#|K}$cwrWB#DqX#EOY&VlI=Hc3#`L@0azr=3lt_A{d@K7)K;1lkJ0T#!@#`dx>92lYbOYLKj5}Tfx zc><&*3}|U*u?t?74KnU&4w$B&qOHDD?A$Jp~jtdQdiq}DX zz#0M*H~U5Gclu)H$H^SfBreVm+CY>G>I0kg=925dv%p~Q07wR{iSJdc0zEKgth&4~ zhenEUldo)SGy(P%k~Si5YB~or6+7QmcFOnY%d(<49pH4pj9Kk%`Bl>Aqi?_{roR6O z+Y$ET;1#I9uM-pbeD>_XUmTr@!Ai5UV`ASzswl;!$t%=P^c4uZrhK1)PC0``cf*@4&W@ic9K|PJxyJdi0 zB(=GEdV(Vede>#V%V&~gOfU#^{T*Je>K%i7`^3_c*Wv4Pi*ylIBke-~K}*y=vD}3e zL9HTXi7!u2yWfRkNsWsb5!Yr00JVl;8_}9`EuI zyx`DkyDvCqPXIZEIUBG;NyJ!~6D*mhpaW>nbO-a?`dtV|6aq5i+zj57%P|GT#czp6 z*zA&xq8VIOM)h6&b#iVIfByFEA3#BXPe|c99(ZCx!@x%jy1qa2ZEbV!i_PFzsw)KA z|3G_|>ezGb-tez9m}?*s1>ABl?>E!O4RrL1#+my9!o-V|mcDjgv7;v~*2|uM21^x( z@6~Kfx+bg&Ktg|3BP$L&&VtZe*X#?uyP|Ox-I#1mdY#rdY1hWaM)NE$*x&)SOF$pN zWr(#9g>0xK5WQ-zxEWA3VTf`lbP2h+0mrY*&Ok5{DxSwX`Z_uA3u|dX!8J5AYMy}C z5P$KK1KfogHAjKpx_5iI!YJ+7?08_6zx~IEYJIk8SN!5=`0(%$lB66TBI88NRNKrMWv8EuO(_O~a0+vWvp{(Sn|dNLwK zRM?4`=&AS^KkPykGIi^wNX|WJj^Uz)VY$NdW1qPRlpQvHdrZ{@IS!Xu>nSLo19pcV zetUlI`>Ud%XEM;UBO4;-toVq;4(V32kZT!DW>5KJz@dKzlI|N6`NvD+T55%3PvS%c z?HRTJ1FhpRtds&!gS}C--U~0^_glkj0MUIF z?>hqFaiQ5PMF37XaG(Kb?Qt@Z;vZx#d+kT7Lmr3rdTQy=Zr&oc5L>=dBSU z9opx!ZiDrg&~d^)S-&^JA56oU=2+PjW~ue4H{Sc^s!?O&#QtA}bCimuK-iyQW<;wz>X7bQQM`669zwUU*ypg2o@a@h3f>-4FeNSc+gc zJ!nGb!24!k(4F-#pRna@eQL_Uq4Qo;L8(s|BL7pFcJ|JO_=mwc@$s_r8BMz(3_3Br+*^TJqVbB6@-zoqdtO z3xUOR(EG4F-m{M>ZOgz&iX!jw*II)((fujVA&Rr!dIHYxcr8Y=Dy5J({&c{BGozVi zd_+5UvY*cxFrqc~vfXPwZvAQAGQ)?IycBFfe+Zw6r7VH`Rv^_L5{EETaP#pqQacO` z?t>*LU#c9;>YOi%x$jMs>HGrqwVtK15B^(gC&NgBFZUlYP0QclGa=UP@W4CC z^zja+sMEe=^0As*v@|CwuOKYZoWlEzoIAb_()Uh^rDmYJie}Ky-6|a@}2$!@#Pfb1b68eP{$W<2O7dTJ-eTRde-rM~dc^#gjq1kb%9ee@o?x$J^Ec35 zuDUN|-f(GL#P-h10Hup?@0Hc8oT#X0!)lyL{y{(!of zh)W_kY$%d+ke!m9WFV5NBy#0s_z2RVv)p>9nst`PkOXQmZHkPK|(E} zyA;HVWVF_IQIf&nLVFP`z~dK5Alq(Qd5hdR8FmD>`xAW70SaiF@_p{mdqyDg-aO{w zE;Zyy-0kiB;T<1Jhl^FlXV)i@RM)-OBLOIz6Fr@f7`VD1vGdTy-EEMTl3oPdc$z+( zP421K<0x!mPHv7X=%+P(IJW0rsiZu1j}wV2Z-kl~?#F#8c@c%SY^2Qg#A0|j=gr{N zx1D?`KGvK*`$h#$UDTq8&Tk#=68zhHwC9-xOSow06_nu`~>7WZ!LiC6h#(aE^!y!!x`prj~;BSn61s^dA}R)?)vnEzN*@43pY&Boff}J_2}FqVAABdb<3Off2VhEY}S2MwdqxO z>U?UGDIH?pYHoH7=U)0R3WTwatLeI41K)@7q%6B9`waI;0a+yt2x|SGYpwD2F(|aYG@UmE$>Hywydw>l`g~dVX4? z)LDGM&OS(^<`6V(>s9$D)!r0*t_k#F+*0a7MzLhe^&=vO3}GuHath6tkg>x~PP&aZ z;Ab~KaH1R1aEP_bV*1Ni7{yRi)S(UP~qW+!vjWHBCA?y?$%asIu#V*681`g9(U zQ#5tCU5eJSwl7w%2UqoN`AR+BONIGo6s%fVFu#lRg391gz;_k8B7Jrr2nK&@uwc}I zd}h6VcoyGL7-9ZUMoNsr%)D5~tt$scC4R)Z=hq~5e7rsh>>}D8o5bz3`KxHAdyASm z=gTa3?g*Y6ksZQj)#ef+b`~TLB{GK!S`(#+%CWM^+qOfsghTHBNgBwT(QUoR;^8>( zNvbngbhkD*D&i+{(Eh2rSMc!?`(eK!S9qpts^y%j7DZNCacDBbKf+m$VbMv}+OPB{ zX(;<#pG};l4xPR9&!Jf8PA~HZ8k)1~|^ok$v#&JlN+6Zwc40Dp3G zU8GD>bRwf4kh#iR>%xAHpGx)DMJ9_VEzVo_XU=QJ5rU?BjZ6dtS(E&tu2Q(RW2WEe zG!Jo9wHy|kb~Jrb%bBltJ|Mo96xb8<4*{(4FLo|8g_(PfP(kY1@{ZVeR8uM0#=ODy z*KW&eS_O~&d$_FCcdortb(>?CCVr*ZNRSwDfp=_Y&(P7sl+_?nx=3qA zl)OcvrF?^r$W7u>%=|X%>n)YdK_Mp6zHNBY>+~xVJ_aT)HLo+&+|NpWj50gr;>wd_ zwfN|^&LWtmkeTSjC0G!>n?g)=R|z`CgDoPS)BT!eLkUo5}sHmOoi_mbVto+G>yDxG5 z?lt^Fo^BPx+k2VXdl(bEs#?7v#~i z$lV1_o5Bdjb$Me#_E#!gxsu#XE2JY)ze-`Q5pCYNEui_QSUJN|OK|{sJSd8F0{&lX49pvZ){@FAIm8l%iQY%NOx#g6Fng^sMk4dZ#{fz$gmiRfIt0~{0n$CPv z+@MD6A-!qWwMJ1}R9o*yog=Qz+N#v7wJXh0pEbi%Ypjs`_^0W)c!4t=R|Q8i8x zLC@p+gPmin)5*yqHTYIpRUr`_KOOs(xtZlzs^w)RhC{2WHNc_uV>()9@x@09HM|BM z1o~y@WcZBL_O@$O)8WG>N_qh0XZbvS=bkm}SSQg)+Sa>z3V&ZrPg7Pn9hOodf~aj# zg+$~&^Wgn2cRWUAZjGv)3U#Hrw?eN6+41lIQo&?1nsu$ei4HPW02&3D@(5 zU?SA22!63c{IJR(`%<&Nf)1;K9xfUgX&6mi7PZ@Y@!`ca6fB~Fs&`@XN5oiX@w`&U zG%Mv{7~F{D4r3LTW>Am*Hc!JzHH}M=9KY*@D&4i0i)q?bVszY59wE@6O2uySm_~m? zZWuj)sy_ZK)4rJZ zK$*F=V>Q|K1+CP85mkYkjqy366Dx-H4%(~HrKv0K&fmu6*e!1A;ejJC7Be24>{yea z0nMKQHV34D>3Mxl8z-v#D7|0+;-m^o1(fc9(GA7i1Rw&v??M}U5R5dB9AU%5%CWA% zL#3t6U%+GfDIL#|XYn0p!ZY}`H)p|Xeqcg}-H4WkDYEM8nqtq9@ zXXnC(mhzm^=60>PwpNTKkih|$UJlOGs@9@#1SJfY3!Redxs1o5_ z18~uZ?^I1PsB22dbf z3l}k5G4%&9pD$}6*7gW9h>OPhm!fA1r)Q^Y--*|n=Vlv3I=l>O{+b%=OoA3a zUVqzs#)~LQue%U)o%?u0XyKBNDG+Y(=5+EB_;ld1RNpMpBt^XobZOr1F@9Q7OL&*w z9T!o>YeBGoTkj8>d87fmSd;%DNiNr_AKfYVPFZI0$-bjo-kLn-L+QnV-~T0*lqeC` z$rrM)(n)P>3!=dpU3%HM%8tJZ_tTqQ5brcm*X@6Ntj~&lu2?%`YPrj>l=ciC2{Y8ai;Z<0IAF@GI3KxTu=q8jtY zaydQ5&v>jLlmvkH1q7QFPh|k7 zb+bj1ciHb>FNxfp_H$6ds|Ywk=5IFPy#C-*+!ve%(h+ULr1;I^@I4=V0WfaL)xC-) z%$vPL)zlA$N7I#|2lUj71Ms0A;F;m19W?Nx{#JM15V|vG$9RW9KWof3YgiON=&-vu zH^2c^)_v$!PRok!JSYI|(kIY@C&kf*R0^P*PKSYCy9+1;C@gk3ZkT?S@?e6`h2y{D zrW>N_ph*KbflF|pvyuW&V)YTVbK^Z1y=H7)4c26AHZ}y>@mra7$1Hn*c(BHZY^Y`O zp^uA9zCh8-t+@}dDRj@XlnJ8vy>wCnlTL0kEs7BAj2sR)J3MM|&;0W)X7>$OyJ?-BM&kX{ zkX@(<_m9R8bcG(-g*?|~titD>aYr!>syzI0?F!uElL?QU;E@-&EOH+Z=#7{6GgDr= z2eK9Y6dI<(S^|`?-=f3+NYicZv9T|dotv2_n^lS)Vn=;VP#2Z6g;U%h74YL>vKUT_G5L_*9wyv+!Bc$uHZi?a#ds3%@nunjRES@S^u8>pXp?{(TAluISmQl z=m@e`TBfzVvgTn(JTli2anY(W6%~Smr2J2@e@lPJ*;UNpr5a@vtmsH|YyBRAs4#8$ zo?5CoG)=SW&z+z`PdSix)O?0>?cm4r+#kayjjB2;yDlk8n@U-fS6RkrKH__rHh`M+ zOAJ7`#Uhm}Sr74&Q2uQOjq#)&=YZR&n?0ugqR{fC9Exvr8zm|ZP$XbEni|GoZG2l} z0?u?f_iA3Wf3i=h2~_SsUfWr=8o-bpR-O*Um&S!$Dz7%%K6zRl@gT^fvXqr&-g`Nw zY)xeK#SxjBh#LTvtIyI0j-UZ^nS9&>=rQtLLG6xF?z4W~A|aPROwwRRX=B^LoNxGF zg^l96tcUSbOM(o2CZiJ`t>veh_oPS_X#x%x>2sfTBKJ>WXNVQD zgdPAGX_f^3{29^FDV*jv!jLkoRMzQ2lM=Zb-@(%{yv<0DA0%e0VUqt1lXx=Eo4C~D zLbbG`XXgLFilXxfV8Wg-@T7|97`?K)?7Xcc#KpX3DjbiDH2+$b& zi^%uDy-_(FVl~|RLRiBw^O2p_b>ru-7sQ<8eYJ2$7I__3)xTH zGb&d6AU`66Ww*GY@!b_tW1aT&ey+QE7kmp4{vvkcGUkHG=dTcaeWF?$(LB$x+C6gb zkWX?v=4ivEX+7rQT<;g4t!Nln6B0dWggef(MB;Mq@Mb`lqLxXlp3bqJB*4wJ{yAhABQnd@0u_F%EYnCC5x}MGmT{fdk5{8KqneDwV>Dkz;oLPp@rJWFciz0osu_EJha-10N;e=8+*(bU_9(3hQ2?b*&_ z{$7EK&cE;#R;~hOMdhVklGqvW<1db0o6?_iv-tf(}>p;PCTH0+cnCTuaR_ ze@7LJo=%rxh5fF%gr*8ht-BQ<6#PNwCe&`^N`hQ8&7AM!dzQM5KIKxvA+1|iqgb+B zuM8O@S5x@>xR;a3(cKY%#G>EQ$!v2QsGo*Bx@Gr@07?#z;*{JV$=!5;$*@wM0U7_-Cnf`QI_*cWi_Sfpd747ahf=78UzrKE8lW(2#iATbHa?Fo$y}C`i6BR8fu z$TD^<38=So?mv(Phno6`h1xf)s304{TPHAG% zttc>;zUC(=3UFerV(qaVdQHLdq$Zu{j{+O;v|Q*>O+$`6(x#nftzfjil+d?j$(6!# z`ubp1%;hUYc@7QchAl7YQE?OA@CjBl;(WX2-!wdw?*lip!i1U$TbXHc(@(mO^zR#5 z{x5h2XBf8U6AV%D=@$^K8n3Pq~yWxcH8+EhW@;uRBAO*kXMBjyN$VLHsgIJjiO#L zaF!Y>4$Y<_;HbO+GydO(r?p^asCxQ+)cn<|7uAgbV`444^MbSjMFu$HAcs4=jxK5L6gpu zvtZUi-%+yqxl6!qW)%0wh-{drhdj9hrQ+ZO6Y|Clwh_lm%(852&#|A3a(#bu`R$Nz z+|;Q`DGbl$u|wLMGJA^HUza;D+@@cjcF|B-6!JQ=@z!kfJfBk6EJ1;A>iGsDfXv&} zVyY;)Zt%poFiu_XO4f7yg4tw`F$I=w;{^JHgh@J80b+jhO5Fqpo9{u|up|u{=VvyM z88Q4AWBwcZN&iwmG2R^l&O5O(kCPukO}sqB?dXJ)N>@j5SQBhK4aWaU2iWFg z6_u1$FB^)CgsQ)Ct%4vG>&Zl(eo~n!6%nC%QGEDR9@E()(Z56oB?(?AN=6D~uE%?; zNAh~Y3Zhm@S1sRZMhq3B@Y<|h-n*M|+DQNUMoo9}Xj<{>6PfEY-sZesEXP%^RCYt{!Pu>(|o2bA% zBh81h)wEA2XYYYSA2^mHf8nq_Yz_p07+g-S^LMNnR*4D}3Oz^4>|Eh@+U-o?`*oPXPE?xww!3dBrE*dU*r{U#LkCwIF`wBU_8w*BK7{$enz+?Ue;d}(Q+$1lNe=PRyO8opNRuLM^I^L>Ae;9A1BWjM`aekOd)>Lm65 z??(ZoVDkJ&J+LTJVFVfQbO&A>1j3=4u7EB>Ol0FqqSF3%$Ev@7s4Mqjy2I$asRheH z;56gIos#zB*-QoOrn>h51~XEJLmDCtCm;_O8eV7rDqLXH8R1Yx8ql!09w-DPUTlk< z{MOn*Y1MvJeUuHPd1quiWaok~v@x=16b>4OQujUR%IhkcqqM-IG#GE;kfLIkCX?Sk zWqGf%!Xkdu2e;E7Ks-$16a+nBIv(-S6EH_y>RxykE0g62utFQ$Zlo>+p+>Vd2|Di4 zW+NsU4|B^m0|(|W&Y!PLgE&Cx!NpiVR6ArR;lp$^aOxi_(3;4jZ&d*gpyf)V20(SLNS}OiT1I^>Z4{?*?@eY?oFs7ocR}_ug6<;QmA) zt6Q$W4Zzg#b8s{jn#hz|eOeKrttu%bcKzWs`Sf8H+CZ~DjR6T>A*tGXzarwqp<2L1 z?@6J;uzY$TXO#j5(2PCO)y>uu=eeN;>wmCH%tB}1g2`q2r(y6{3U>%0<+mOBdbfp#H<32w8@4^_Yv@A{7bW(}GKpcO$-bOA~uN>fy#RG~>4#>hhzB z#7|wRykOCN4jY(e4Aa9WHYN;B@Nk0rNlVfDXK69G8B_0wuO@5#grf<{+_ZKAvv%Qx zx(cJ2h^M}VI65m?P*CbmI?lKnX2&W`9_dbk1|yL5i5YU0LgD6*?BEHvpO#7 z>x_|03)VkmmG_J0I%Iz`TWWP-5}*^8vl626UQC1U?Qkb8E?ENvf2+V$6G1(2bq&A! zOlWUJ>K3Q|3+!ik?!lxs1a|OzY*OeMYiy`~AVTp6!>JpF-LOt9%)`&G&rjvMhP?%u zDVP|?zJR|4^f9SxRCOSc`o?c*dNe&8 z@a%Y_7{brTR(JCl?TDRThb>9fVQ*iWr??loM4K$5lkFVdmjz7y)^{ZN!V2dxBi zd$nq+AAI`Y?>fisl&i!0q?@fvXWSjc!GH;;N;aSF48R}pg~oS~Amv-)OZXGydyamh zn}fbbjL$*d$4{#JRUsU^MeQBa`kP*dAba67ZJyEw9gQ{x*;FgS#Uq>B-gX6|LKOWK zXUI=LA)M>;vvd)DN+SLPm|!Q^a{`VxFal6;s1@GvQGM|K7Hsx2kWFs`O<^3a0H%AU z`0-Jp8}d%g)+JAY7;e^M~>5dFt}!4D>x9rtti9bb@4IzpQ4 zsK3BDf{qC^5y zdXExT`smd~B6oU<7Uwy7^9pE;02K9}`1!Ip043-+%%3jcFuo4eD`impa3L7^lzb|+>hXHY^!3r@@3x6B|B^m(Nr zJl;Hb<#6^4!@_Cc*<>J7dqapO3Htf8^(~#Sb*)0PqFy1hv#f@^M)pt?*S&XIw;wt? zeT7UtlZ&-gk>ESx0e(G4Tz}XjsD8S_`&3&$+Yw`iAtM5y1VM0BFU{+RF6W=-&zvFC z4^CU~gPIB&(*2A6LeTQpobXdpZD255+%9^gF9>6`K&i(fjyYj$ZXZa{6?0hX(;IGN zZ?=J|?adKBPqTWf+fE@x)7EJ<;RP{A0!~xvEVqnZFAF)kpmbpqV0p*N7s8a43qCR0^R4UO zZSFM{RqkjSz5G1%aNhXEE>!)u_WH>N(?J*sR)caB+NEQ{j-lhd0EOFJV1<09and=w zKmg{wf1}({h7s=cYxum+ns=;fxdD}i|32{yuk&V4_q(?O{0*OB5qfn0FPSAS`oYNI zApfwHBy4l78t#8m@4$#F#z$86LI3MW&f?ig+l8-bAW_ib^}C+LTyA)uIz6=-ZVw24 zL2~yJM{2TE#B;o?RT3ah;iJRSS2c5Rsb~)V1#PbAXVt^oVz1y!G_(i#QD& z0SyGoRCJrctzMCxUItqKy#%e07bpFz`BB_E_Clfl9p}I!tf8_`*5Ae*KL=*%v;kC@ zZs_wFC_YBcO-xNYYQ(R6yzCIHLusHxY=h%MKt%Qoyk)YZbT?RW(XhN&FNd&G{RLzY zO^$NRbgv57@;tX*2k)O^bmzlBkWm@pM_;C4=7)pE;OJ9K^Dxh^yu)V|Vf6^f*{6OW>a`(Gjs#pc5BU{Xdu^Lr8~7`lk5;2ssHJeak7ECOBs_ ztbGKW>zYa_3g4BcUE1mp(vtA58`s&5f=L82GTEQ4h!H91r6Rj2^k$(N-`oMLXV*Dz zcISx1uqUP-M8HcG4Rf!@qMb8hsyhNpzjv{QL}BF@$*$~+T zygrW1RgtNVEBg00yp8R{`erPVZw1fwYprlC(>q~;c;Vrh~s8g6d%3*DF4vbUF}7N)Qf|*i@PR&)>3W$VF~eE8B6>vd66!qcPhUO ztu|=KKysGS$TgF>BJ`bPzE+o=$U%4q@3Xz<=5REjpCe<%rc+Hm01z9$_CBe zHEr!HAfM&y!VD#c`K}Hcu*cYl>|-)S_BdnJYcu#s1&^P@~VY zqOky#%~|^ETd50cY&!e3Zog5R_gP;onz9PJui@AXXM@6D>3H$hB_`hc6axaZUuguL zDCOR8Bgw_|@!y%t-`o6hw%n{mDdj!h-c;MZC0!F5MP8cba{5AOIE+YiPhGW)ES4^~ zVm-5au@AY?knT)&FDNwEg!?9aC^1f>;3F#~<*2bY3d(5wzsdOZj4c#vB2Q=BD%oWR zIHsCJ$KD&u8)Vn12@OwQeg-U-BI$jTm_DJ5l2S#3qC-MpV+b$rr0Hf%Z-u}(YHHh zYai{uGTS4waQ?t36~Vqf5^R?_fYkjC$tl<_ImtXq4Q!q=;RIn8#nD5i3uAzvci%1G zwxs%d$>WhEg^c^$xngCBd`Ku(Mh+tVURyO@-&U&WPDR9Sc~t#p@-27T+apZ`R1tR6 z@ql7d9DIdbK6XortzTWR7O6~k%awHe9RKI`l1xaALXwfWT0du%D8x$A_(4hnHp-~} zo+@IMyw#-P$^a+)GNDd70}BqIy5Gt+iQ2w&xXB&p}xGBReB$?zL{V%vC>A1)*VUN<>MS={F(zD=t?qyUA@8I((Xij_g-`^O0Sd zd%)UjPYNNT8-qP-npI3uqp6)g*A13l``E2q?>eIMraXp}7PB-~S6bc6s}XdnO55Fp zdpFz|L-(#^p_>O8VsrLcS0bC45$Ucu#Y!j3j(49;>-Iy^fymKH(ox#}tawgS4DknL zBYg#sW=N&tbj)M53#q20O#UqodSdA9zIChZ5Gw%N`eq+;tt*8T6<&2s(xBPTAa^hL zNB^Z=_nZbr?RAw*Z!yV&Evs`kKgMxq@|@nBBu{*z4C|8O$i-6_vnE#M+&}@ChxhnJ>56Zl^~FN-kK&6E zc?iBsefZssJ%+5eHqg~nof4E!dGANx*FYZb!U{fzicK~!IRq!16` z8@0jag&_Q;91KroyPxq9!R?RQ~tvXFKQ13iHz0u7SMQ!q1FhBCad5M;)yiz1kv&i{K|61=u;_d`fchKqzgCOw&-KBOLKOM!}E?VWPD~iu} zUB#qbZ<3l3lD)sv@cEG5W11*_HyLA7oqK9lP?f4R`lsSb7Li`Bj7~u$8A4VFLgro1 z5HtXQ!)e3{f9^9qgHh9-g}XE%DHs<$Bu7PR^r-o3wK|QhPonT;R)uJFSLBrqA9Wyg&PY2xBU5}q{g#A{ z!FZH-T0)eBU-9I=&P>jkoO#`7h`Xhc(Njs{Eb3^?h&H$@b_&j%Xbnt0uqka~-<5|7 zyNWVdY4^Y5r&dWgQHrjS}Lf!Jo&t1)Ual~z8^7kC0|*yo>VW{3*#Bl05TvEj9~yu2*^ewu5+<90oBn1xZPKsQE* zGfLMy^!WD^-{^|`3bxNk1Zf(O3VPaprSFOR81vW=n(4a)51!ji<2yD;^d*GsCTBP} zA@ZyHAgJRZ0w*!3e!odAM6nX+dEW^GTP6zsh2|bG5IhuDtjAEZgcUT+fbMA08r+gA z5kMoN+U`f5Fg|6Z8Kkga)PA|U&-y_a!O~l;d8};0MM9fG>E3bJBvA~{VGE0l^Xyb$ zLa{uGV~lDLfzBd}i=fssMKa-|s|%N!CBEUU=c2MB*p6@bM^ztLT_aMXiL?pO@|}2EyKuEH7*kPQdpd1D1Ko>#58M@90rd z>(5tR{1yIvHX~mUY=9BE&q{f@D)5YBb5$%RIHW(xvT``M_|?V{D3+8YenamSeYnEY z*C}7eMOqU-HCg?-^QUfkG+A_{6$hfKtM}{~g(Jztx6CFM#bN!35XSqXpOHBnDk=}K zAyvd!@oo_uWyXL4OiectYbxJ z;-bueN1|}c`BiwE-~IeuNm>Bobu!eKTYrLC-~B)Ay>(Po-S3WHUM`)3`w=10@%(YCesQ#>*gn1HK!t{zj>OC5_~E0>J2VH|}?w!LOYL`TiX_uCg0$|2DX2HlRsj&s5AVFl6iHUrR69?!2ksSyo2ZFHL zMKHpag=9DjDu)}_yAzIVl!4P9+<7Z#vdxkg4 z+ftMCc2$f?G#kg{qI1=ckJPB%F2#HpZ&Qrx**RrXZCtnuC97Mz{}5Ycc6g@@Vl32K z*hVgS!r0xNB^x$+n;Nl2l1x4TVNSpwB`^!lwsvw7GErBTOxEB@32sg#oH)B_DKGyh ztWC@I)(U&;mJ4mmuZbrK_DzcNK8Upt@x_Z5qXpAHAsSagYQJ5KxIzhjx{aT}xVspf z8LY9lA`^gc^j4YhO5o|XhLy;(0XX3N3kySjwR%sXD;(IH9gYU7UX!WE-ed?;1*2g{ zL7;e2=35*3fOZnD%jl8|=cGJw&f8XsPt3@U^J&BaNH>-a$zWf@fjfSA zVNNBgE$8nWM6A-vaJGxv(x=cu#@ZKVn=VU4HPOALG_Fbu8dAt^l%I)+iPr_Z`bx7q zltz^e6l85iS5~O;R5p?pvuWdDKm%0>asmzHNTb-#=!D0{0{I`#Jh1Hsv?Zx9JZEZ9 z2NvsK8?0xF1=BDbB`^WW!55J45m(V`H=Uh9TGCguZba-`~}I{Spg<1SKe@##}puWh)_4nlXH#y_z^v?sGD#AB9`joeEQ)@ zV`HN^#ElYG`D*26=89rUA`y;DrjD2R7v=vC$Do{?m(dUTc8_%_61p< zdQscVNC38&PBzmxNFp@8?M8+m<_sM>Lo!7uvb{yH34(PE_dvY6VMt@N9h zuRmUkJ|BVX6s7&s=(#3WI@QCD0!sM180t4wJz@+2mKR&MKfhhW0%C}^WDEB|t+J3E zVBv4A7;4`I$I1RcP=siUJ|{lzhL=N*8Vk|5!4iDle?3UOCi(S@p97(Gloy$f|< z-d-n$e#=sa!Ek6+gyzP3CY;iDARt&zIFE4aEyDB~>*Pz1$M;2yn?zXsu3Imo&Ig77r${t=|AVS~%|xBrDptiYFe{v;dMpKqP{$ z)-t|6@IIi9+MqTYkl)(^qP3rSja_Zu*8stybE-QYcv8~26(7d#_ALS8qbZd)n8I_E z=~CPw%*8m>mVazseGJ$V*myPGw)v7w8WtATB1pp|_?Y?5OihKYuVw^icWmXp3vzE3 zRug_R2$|v8XD=qcWRU|Lj*qYm8=Z2<(N@JWo_f*!RXK5{kBTphgWWN(Y@1CAhr>X| zeIFRHt$BOOkB=~_Mj3{q#j|0!Rv@U#!#C6K`b3kWmCbeZJ)YRYs+b-SJLJ9T3V(^E z%flImu~nAUW@QRAic>z72yK5c9>Ckj9e{*A3ZKl+uc%Z#!uqrAxNWccC<%_Pajj=t8I;dd_z*ANIXZ$9sc>q3(@ z1!1_J2thU)>rUANq)s|YW*h8vPX5QeV8n%BqL=n{c0S^OUUH581^hQd%^|iMVqez% zYK5|xfPkHPTATuD|F(#gX>fH?7E-{+XRaEUQfo}|Kc*SN$WD|$YK;Q+J|RDNd1Bdy zSc->nHKS&_J}^pianUEvJ;=D{1B!(zKpaq4!rf8XM&3a(B>pk zZ=vCS?pyWiX<4Sd*2H`Mb&(tgDi%!(z1_p7doD*Jz{fXJG53@8f_gM#F33Nd7jN{J zJf=Iy9pu!#J_M-VU=__FIG}?@CKOwqJ&GPgZR><fkn z{_^8N$JdsGF0@;D$(bugKl2{$dYxU;vhyaS_YE#)-AZ0I&fY$akg@>) zYXXD7fD`CoEypnkn>tq?kRLPl&U|=Q`j=`h5G30Xiw{2I?X_dIr4IO$^MAg%?{-9ZgnTa48Td)szYY>qG0;B{f>h#}qKTCgaDCQ2T zWE0gguRr@CK6^xM9L_I9W!)_!Q{*rrc?O6bD(X6Aen2&qJ1TEI>9Vy;*>IE-1bO#(dR&a8 zvfR@3C&{rHQ^tTxQARC`_3rxp{9X}?8>N<@fWV|yewY=HbcwBYP+<2ngb#$%sv7Cf zaS>h(gXE%RwA{BuvZU6>FFp(5jXtFMT3vyc`jGbS2eb-c0q>%IDc_m6`F2%i>MPI3 zUdWFJ%BAGl$w?_LrM*(FG{&ToAH$#a-Aa*F$jHg*xj?GPsk9f4Dfxl&aa82KxC+%) z5$!p}CaP(yfP=uz@YENGp; z!U@vH#GdPZVnmPdzxKG%?j;U}^>UHVn?0KqKuy9QjowU2Njb$U?6v(h0@;n$;#OD{ zz(u?E1A$=jt?D{)ILVq=7_CvE?M!@UPl63+oe}i|6mk5zRyLsqc`ic$p7kF&SYUK%wzUzWBUufZgv)eH9X?ZpSj<81rf`m z4U?a+X85l(QipuY6LIgsu4>B86Y(r}u#k3)wDb#G>N1R{Df5>zbIo0qzVZdQ!Sf%V zg0u6u-XhezdcHDKODDYfLsy2a@3C+G7zeqiIj#Q4^GEWuw9E&f9^_lvw(18M=ItwV zW^yH51P?)T=CsWShwdKPL4tiIl66*#z}J%syOK2|&tf0)nA)$ctTDH1e`Mj=icvCG z0&O9jKlz*Ei2)_P4_yeH7-8W`;E%_-MMfdK4%0){9S(IZT>_Yi{mh;11>xv5E$8naMyxm`xlrpM58K#90%)h;_98G(2-FX>#)dKg~vXL|aH@l8BBt~fo+ zYprWzn-sIxka8Y>`Zq3ditc=iNACk<7}16crNNwhPW8iNT=LjW0&7tg>=fe|qs=u7 zD9l97yJQEs(fVS#Qi`vqp0b27G+U}!_eh{pk(+`n;b=0!@1gk-AAY6*>^UAsdVx;_ zIlW?}twTRajEsyhpV2?@vg^lHX-`-J-Q++z1DOh21_q7qJFXK!yy zx)pz}j?LZMB)ysO8z%4pGjtps-|bpR5zh)L^ggZ4j``m5BqVa*69=l(1DqnDcnm7P z)TuK2Oq6DTPif*RK-G1ZpZmAJ;IG`*?hHbVDq)2*;R|;t1yM2A<^={i;u&sG(vD@7 ztTX;1NKJ1Db;%+rutVC-n*XSd{;wcZu zxbR z_@4-G>nD0W!_PTo!pMa+Po1eBv0Ae=yNQhTI>uZ5Wq zEm|8~tzF&HlC`TCmUrp(SCWiK!vI})D>4J28)cTxonRr3XtptMhmN(|(-ryt@B={Y zQyCov1~9HwVnxh95nM;}An;cjS==6$v7YW+}Gm7vBmo;Z> zX8z>2cC0gc-V)&+BwNb&bd~1Ww61Qn3Np3FAWG2&&|OUo!RLTeWA-+7$E5?r)!Y#Z zXqGmXZG!M{+qorY1{0qrJU%6Hyd`FEL*ss@hhf!4I10y&iQR*7%^o^#R>b|EY*4k@Qh0DP%k3JPb45Owd_<#4Ky)fX* zmgBbAU+&2Jk_&sX{GKtYd-U=X#8c|SEgCuplrOkhLmHWwsTnx;Wh8L9mG!lmhUVr> zkF?C7S{uII3pE9)&`3*Wpl|-{*hY=vWfCIAJt_*S9=!;O*}zXO(lB(tszmIKkLin4 zg(t(9Z|g@0+7wBN_TF9stmz6G2_%ha^Tdk%%0kwWChkVE(L*pDT6-_w%=$6e|~U#-CXe(GKB9jU|)pxtaN2P?lFm-n;7 z!6ok@gX`umZsUp9j-^f0jKFV1r;}QzNE2oM%C?{2sA+b03j7qNq315y@WIbhOojO= zb3m)ZX2#2cvg6F!RGr8q{LY+fo>0V0SU6JsBH#L~Dczw1uQpU2wrTIpXi&jVJ`H$| z0vN5=Cq=F|HY77BzqS1c%p{E9zNKI%c58n+M7Vh)`JT+Ah|%qPXFl$F|A{f$Gh1cD zc~S$7;wD|{EtTi}Cj?~;hTLE0RG+BegrTTL6lMqdBFL;|uZt#{0PcK}GJhD}YcT-J9YjF3sn( zAoOPY{qI{)P4iF{`rV!Bm_dyVRMDUgFtvDQDy87Rd=K;>K^4;hfNKD)Ilws#2Ttz; zu2TXO1_M}P58 zJ`jkZIyJ3^SqLs`lugpVQQQZ?-v?YDeD6ze@lOkuEgjYrR#3^QSEgOQpXz2@q*+#L z@Dq}vRP+-R;r@U9q3-ihAY7YLrp=mSw_nlGaStNN0i*XI8NSF0nn-%x3_2K8B15&V;b!^P@t;4P&FrwydAX^%sQ>v(OpH?-;$q49TFuPP z(%gen%hSx`;kBaUTWd>BJzEP88)|+oK28NoTWcE+YCb_8PHATcXE!aEx8|0dGL~Mp z=9cnqZy}s2KuF%w0Gt;57*TUd*?PFETe?X*JGwYKSvq-83vntsd04u+I6J)cu%xzn z>)>w5DQD{d-r|&dcuU68+}XmC^OdEOHMk@<7muL0I2QVUHQXorU?TX@qerNnTnXP! zAyE*JpX4y zrv6~J)Ze;2*qnzCH90jc-JLz%%q`u4t&?+h@_6{gP0jz$XL3B$!ViCli*w5Qc*tvc z080cum)8=c7X0T6@>)XFLjU>3e{3Z9{2%Mc%SR3VKV0G8mjcwh0{^?0Le#wfb)dSN zv$>X~hXJR$j2x%7rH{wIM@aqqOZwkmibm9+6)Kh%wr{1Jeg3ti{KEVkJk&yb+#CYH zrb>gxgWGba=H>mjZEntw|Zm94LHu~9k#IXAnzqPka2 zPxLV=S2tE?UGim-@AaX^p|r$(Y}RhBK1dcVD52?#L8#mX8m00r-CfrS@l?I5)!kj# zTnkj#l))Ah;S+Khw0$qv?8{Kx45Q1ngd4m(XdDGH*L%3{Rtm}3i_Qx&Jc7n9Cq&bE zTesCSqnRo_W)+G;OlaTWT+wEXBSgIWz{sop=KRPbl(i#aol7KfF8<|=xW!{~e54GQ zIsGI20Dp_uxeVfE4R7D;wDq&6j4wc0j|hBYM2||@@!}0DYA?2%EHp7vE)D||mC6Nw zo-3&|=$Vw(>g^UBG{>}YsJIENhF0Xex&}~K#-wDHNxQxfBz(ZZuT8Z50u?1 zuPx2lICB4(#nOL$6heKDi&J+$R+Eh(dCv8T;_0??Wyt5C@wFG*T^^nLgEg`4Brl8x zYgPvo)TCl*bTq{BTc>qH~moUs%Ds!MQd6Cc;%j)us%qMl}m&4xBe$0MrzKvcBS3(01^T98dIi|Fx ziC>H;`hJmjW5+?vwzXC1^VE}xn&X#`_mB2;9CwJ2;&%;W{l#8>@ri!C{rTX7%l`dm1(C>@Z?H5PjETCLfAAg(eX#$gUe;NW7F6%NxC zN7;Ep7%)&E=%aZ_pQuBC?FpkgdhY|wIO8{j9Pm@rK~T2$ldM-^^({6cdl$g!A3 z7q|Z_B+23OavY&p$MZ#6Ib)dIA8~bq9Q;8BmIN;Lr;7+VFW_AP7v?-#29IW=PQ+6- z;+*LkAe^a96wlQp&1qC+i_uC-f0Fr((qv{ZmYY>JCHMV>CRcyRM{Y{Qt+kC^3P_w= zYH-9*bd6CG`DmXz`&WvH4@IsYi`R@h$&+g)3GzasXbyXaPT@nwa-KrWaUY&2;XB2i z%MS}aEJ-J$UIzQjx5O?bo{9Epdf3(R;8aO0uZZ>vPV2-lAvB&ykD0tmE zLh+TjFi)V3H!&f0blNkq{M9N!;)xt*20*i*Nl?Ca=o@xEz%Yyg1quj_bdV8{E z=&d%$e5%M{>&GeKs?I8m1J6G9S!>XS&GHHal?k-JxJa)9BMw4{hjxE^gE!t zuBfPZfb4gnhEPW^nps(Sfpkb99$?&8L1c)dM*ZBC4+XFINRq|C7m&4-PyYP*;|y4O zVyIC={61*w=XQJ#SSFf#<4Bj&$5$NNNiT*L`e^%HPOQcNYv zm$)+TUk=ithS+fZa}XBte{Ln!L~Qy(+e5c_aV4H12PeO+B$ zkmCjjDuAh1vaoB0el&d77W8Co1&xcoSVyh#aO|C|IZ!c1Nv-k9v-vW*B}iVY5_fOfcl3%}mnY8<5q@{WP%j)Y0lJ6LFw40+@^2s2Gc?9@?0WAGlMurO-plXhY z5(V68n#9D`=)KzCH9kG`10l+Z3zwgdk5CiEiI=*!rW?o7QA zpPp;HmjpVTAOq!B2=q_I{BqOuS^$Kdg`4Z2dkSg#{(UqZ2kTR`nCW430JzYS0~{%3z2 ztgW|-1JKfja)fO0{aR~lPpW%50BGf{xp_fyGUY>G(h5p_G9AsXHddNxwNwUJ9fL+E ztUy72{(~tlAap23ndW1y0{WoY4^Ud2oB*zB`)Pa!2gj#zxc?lE5^rU1Yl}r80tG5t z@!i%I7T^vN6zJD<2D&e(gvQm;aPkB#226>fEFByS*^Iosyp&W_DuHO;^XKT4d;YzodeDC?R%fm^PLVy)1YEnN)zv0@WFmm7 z8k|^N9TyijU*Vq%#xqWUN@bT5Pzu`_4t(z)jP|J3zn1<7BuE z2uxPqgK5dI1OuFLlcRntQlGeJXg<}}y6x0jI?fG9Ok+LFv_i%B!9otB8&Cs;9%k4P zU7fXpn${Vpn5B)Jo zFGN0CYdMtrlqduUp}dHRiP>u}aUI;--X7mM1^@aOBmlP(NShE66ZiM@bjm}c{&OH8 z!>{f!85tQ8h3tO&{vFbGWNUbW7GF!tz{o-E51Y3|)pIy)cTgoFUu82}{tUk?SiqF^#0pr}Z%xmbpV zY8P7tw1Z!D1iX|1ILvW?&jF?8Ky#ak!pI$HeBzhgc}izz=V)na0xI_jP{a0LCqnz2 zcLOvQY@jbR2ctv{C|`)2<^q)zfU2^zuu!151`{19-|X)Ep=459+1^f1PS)1coa_h; z{I3&4GAg#?Nge@M9tP1ch+sNzzq|Ci15omR=AoUm>tF{2R=f>&SJ(l()(5MrFN>xE zOm#mN6K<`e*=yw=C!!ywn@$cXx9Wy8LnlXz|3T zk*ax2LpL@a_;%WD{+TE$W}QKI_ezJU&CLyum8Y*M%b@PC6Ekyj8Y3K3)YlCTKqBJV zP0i=ev@F82x?wwOMn;Q3^xDnM4U~=3n|yniF7b?y`vB9Pr?0Q?>ltXX`zY>j3Moa# zq_(!UTrmi3y**e)#GBZio%3#PZia`4n=yKSqW;_GGqbZsksau>p4uLKsC7#he0-__ z07#?Rat8{Gr9OTA^fEn9ag!Vy4J{bsCqRbm`RgkY{qtZ{r0DSv7V6o6$P)LlGX_SK zuXT#8!U-r+XJ==xsH$QV@k7R;{H-32v$D6>;{ZkE7))PMK@hQ|QKKSNap8*tL2 zhl#nKSh%>jEaLHCDdx)1LLl`Ov%DQX$De&mq6E3Qg&)ea(>NbO-}%(SRNl9k3~_#D zlD}q}km=3MKbE1Tn6bVKzDSC`e+ZeUrs}#M&7#Enqr^Nl7QP~Qd%8UlAFrpYn=9^K ze8^`RGiIb!XDu!v5eW2s>r*LKVer4VvSWGO6+mu7onAFr>@)W_r$y2LmN^lT=4N%& zp8Lvzg35whY@UoB3!cKzUJG=%Sb{n=xMX*UT>#omCl=;u3ezvw3r1m zE!%p@M@Q>hg2vBf5v-oLZ_(DfxVZu8X>0{bKJ0r(8=Kd#KVa5-?aw4mYJRr9GdZT1 z>E=(nf4}~xMC}Mv*|md#0Ps8@-~5jyZwQy8AF?2 zGaTv(XHwNW-ycXNAX0z$vE`PIiJYuYsg4tdjEsP)+;BLNC(c8;a7_!S}NF8xuArasJCW5~XH7>}RVhv}f+H02I;7f;$z## zVrWJtKB>QYw&_8X)V1;TJ2??Xt(rS8GI;HX}3hi{W?Hd zQzf%584aLl@jT8Ghny_T)=fV*3D}+C{+vumd|yGTq^!)@(~D;ixzvc$pqy;{_ysjS zj^H#-@i;lQGQAT*1aBZs9?tX=P5-E${AhzI8fl!0>#J^c2PKs;`#+XmybHsk{E(RT z9>|WiL14&}C-wG$@z)6q2m`Bu3txlNLoQdl!$(I|KZb`V%Ig!xci|6y`SdARxSL$l z2phVGDPpmM@OW(Q=VX11y8(`1;Qi=J|LbTH0gug7+aZ8{7#q!z$EIqBz<}8Mx9HQ; zFVAf@tu{6`iJ)7=Pl;07KsO`K0=IE zAQ7fa#CZplHsfo|k&$pvF)_Ed=i31d^jG^>Y%ksTD-3wPWpN?ohZT2`twfHKT#>p5 zz2ws!Mf8NLj*hK-l~$QY2NWDxNG4mGyS0|)(C+ICvRS0$)`h?D8#?*e!op&y2R3jB zSf&OB2ccJ6;nl=KE@I*x0MYp1a$TL;8XGSKKe-0fxRoU(B|bjPkiw1eave6I+YeF$ zR!9pXJIyWjSpjYD0z2G#+h4Tg5kD&5d0TBP9Ziu85<5#Hr6W#q4M(ef`HYSr> zY75>$!+G+jaOu^rvoh|FNX2`yZbh#hm;SD!aJn!BATCD4qSQ$`5|yg;}5{@X$w)n2nT`F>2L;z^=kO>Me^p5-(5+{CJN@aC-OK!vdsV8x#4#Y7G7SMqN=Kf243M$rNO~fWn(>Eq7I+O3=*82TgU7A z4D9^;3=Bwj7tHKfZYTQHMB?wx*Wm7#4&N=vb7&Jf`-ha2-%dwxp&F}-eSYpGkeW)Hpzv4q&QRc+_X?+gfbOU& z_Fc{mG8VbZ&c(9H&4r*bC%E$1EUULw`*(7YF>MY zIB+5h&A4 z%=g2KLcdnF7|dKAw|MWX%VgE6YZ&L|qsPDh!yraT3c^`|5{EG7@8>mI}U&&W0W=`w7iR(rgKI@2)`*ab)cDiFl=*U5ET`b zkTBU1RP5?*3Alrde0;ht)rXCZeYicpG(R7;jpqETZ?ZHW!a$6D z8+w@=88OpkQ%g%lNs0XR`O@88j(niF+uDa?`&7$(>RTpeh-&0GKux$*^k94XEnQNY2I# zUjbXEji*n?U$HSW54;=0uz8f4*3GbDGI%*y&>t%zBqZ6ee7SIP5)5t>?FKPACVJRL zn=z>?1=D2fSm?A59PD&24;h~2jOk!hU#Y98^fT)a=l$8El0TaK1sv>-SOag~8 z8l<;%UcY{=uC89mWSjy#A`P6x{Tzfc(Wu@>R8mf2ZZqR5WC9Cq?pqKI4O!Vx%~wce zcz*MT6ryr5LEb(z+^{=1R*V@vT$&9vn&)i`ZXSWr^I`<5*G`+%2Pj4C4J6#hqtMt@??6P_fu zvIhO??&(1+M1La^K9oZAISRNqWuph0>L(H9!7%buRl$Oq^tJ2q{Du7Xwyl5{Tc?9b zIH24DtUyYxgnSg8sWV2!S9n-- zo6p_NuWWKl9c8VTgZ8Y<5E)qo(sHncRz$6AYn*Q4l%k#;{W+|innwLj!hQF5rcQ;( zRYT)fT(|EI;&B)mbu6Z4X1`CYw0{5j6Dsofk94B5oZ$P@-2QdRTgkspRU9qzH;Cpef;?8%jpl@eq8)JJx$aFVZ1Z)UUgF;X{ zfsiKzSxYezJgZ|3V`k3rcDJ&#k7UAKhtYJ0?}qFpSH`4-x7d$SE*6|0va>$do{!N_ zZY>T;luyP$O_eL+dA7O2hLK>Z@VB!vOHg8P(009IUkG6tPWr}P+8O|am&$?T#5<{6b5Wqk-mn9RK?>clMK7FDI zN8*b3J|}qz4c^i}0{5>DZV$J-1`+JaJ zEnMfjLwQ*lzO{?pU<3*@eo&hg>BCsUmjym%AJd)?xSE?=a)s}|{X&R{=`2^^#-Jw5 zuHUqB*Y-|{Pfg|L(9eLmW?8X}M`@KM74r+%mAXK7?$hiKi*y1QQ$kSBj%nk!!qBK)TTlCPAW4AQ4{lL~ma zoCe?a49~D`Y$q(YlZa;aTL!LWhy{bPugF)$zjnw4l&M#cJdQ~3MyA1jHRVLyBkW(Z zKrYG15>TAdjj(a|4N^zDo0{|C^70LDvGk$Rr}ThK{}WHZ;>}Nv%2ns1!LB?pdNA)^ zTeneR{BMV3qDO?v@NBxxcr{GLOO`vT8~~1~quF|lT3wr$+k1Pqty?=D$MjID58bP; zUq_Hr>{otyzJK8O9_9Sr^5PKec8hg6X&@M%B)(@;c88hj>;Rz^&w+N;#Umutq$O`sWf6nV zraVzIGgCapP6HuhQ|^duuXQF?)>3x)E195BI9jKdlPD;_v!=|5*`wLwBCOAvb&%b! z6VMsohjD30#W}{=7mb}I^zx~t{tQ%SD9zH^`j41!6gq}HCe@~?^U#*-$PS->OxCY_ zMzF^y;sF7VMA+qVV-ziduSerIT(1*YOgdF=UfQ(w;Jb#i*W`FK*Io(5Zm`W|KxLx2 zRaT)oNJdKf+YpxkL!-&Cp-3UWnN752VM$)^($A@p;|}mMS8jYZ@j4{OCm&7~+vJ48 zw6$&7gFhEvybs<}qm(#Y{}Y=Ig&bk1%VcK;OW5P$kSaWuKfO3M~T6v#OF%JFZ|FGkEZH}ep)KW zQHh3GZpBj2g=5#_qWz(UV;{6#+|`!MdcR3^+cw42j;c#CZ%ag}<>8f#>H0TJ`r(5G{hsSl0b!O#^z?rKt^+hggb~kKz_v-g$2|IO?VnO;S=4C4EwS)8?1R z#LUS#_DZd>q4qI^h86ohL3(lJBMf)eiwB?qUYo+mObr6y*Ra|Z@28wZOG*5#>F zSCnY5XtTB0N%{bs!swwarcYE zl*>zR5XJ_YjUZ&bcof;;d*%wuF#;h7pyFvnI^6F64#-lT-cR)POaJYOg^EECN@AjK z^;p-m;{D>Bi~?VO=Q;e%qJB$-hSpo2k<`HOgk`G!(x)^S>>{F{asQB_2lu|3Mp+jq z;Q&0=k`bfFXGwq*A;x~1`iHrEtYm$Ykl>7wRuW);N={43dExH6vnGpu-f*Ayr;fyr zL!N#`>y?U*c1^h)-XH-z6GP>7K+I*A1&1iD*bQ*GV_GxfTPy2R8uePl{e80(xB}un zB_zDW>eOiFzjQly;OYpO_^G0#gpR1rOWD*j9ZeLHc8D4&L+iyZ?72jI$-sqyrC=8pD&to88BQFj}JMQk!)@_d5ZeH`-=R3^TZoGEb z-&hXh92nDEE6*=Sc6&cLIXMr|l|sI!F0Y1z+uluA-UiPUSSqIhRzQatdD{%GfQ13P z?oY@eSLaT9hkaWW>wOiKm7ezj{%;F_KH`^W!vLlkA@pf~_B%}7gD)9awQtGEwUe4f zdfKwGy-C^GFzYeO7j72wQ{4WGXod~$4V@QP&Xvz6p9%)CVq=Y_Cyw^)~ z`GK@HT4^BKaNo-1XMB|V7!1{(uM7i!Gcer;4tA`p_(;#zcN_H#PvYyB?zeLDoKZ@# zF1_!UOvv7_oiBE)(>^97iuzNiZath0fuC3+6{KWAp%$q5Wq*b?+#DPnK=gPX2(8al zOan;`;0S}FOP9;PtIL7+w^Nn#8|Y(jUCvB)2+tsi10((klAlxMu^aiITH>V+IwA;K zJ?d~Jwk}DK4uAgU8>*(L&hG}z0mzMyhL6xq$d^iX_U9kQMZzw-=6enmti0bgLTQr0 zewCE<1ABZAXGmXY(Li4xYpL3i<+6W;=~K(X66twIO&gnK z4(=s2{gor*Tt~2uhs6~Z7Y?-3aFNiTgL3-k&!4yA5(}E!rThHVj7d^y{h8?VK_YL^ z^qb!*8wVsr8pS-H()8lR!XoKTnYxAk`#6o0vq=!tw?PdGrnv{13Q7?V1t1xFFz=mA zQfZ}ZEH57}!^8ND4E(&*RU>2#{b9!Q`gw8u7#(_EKf{#hd#ZP2#3iZ$6g2ho-_75x znFO-bl&AC-4=8YnxnAQ~Aa!BD4KIsY=ZVGRy5B~5|Mma$Yozd6m9P}El<1Eq_4FJ$ z;S4#nQAW%m)#on5JB1V5fL!9D&68sP;70VC-KZ9NUYS*$*^aH@rio9#3o;>|dy})j zx)%YDxlpZcDy_9Vip}`h-miDt`76S89fn-a#um`9($XjDp9>0NsYHFRewW!FAD>im z>FDUBdgo*Az|e|dWaha;h&IM03C z`4I|J&-i$;W#0hZL#j$A)Y7WieTMm&Yk0>)qy`l`N&%LSc(zmTH;6xg_UD%~BZg_> zmaMEaG7^Drc+<#mDXtg>t#$h{F>-gb+&*BRf_y-x9x$ zd>S7q-TX@T9QhI#JJ_CVOC^DfQrw67-G;WV?ox~URtdX7>pVp22};#82t_!)SZMa% zo9r+dFGM3~2brdWO#YQ@u)?+a!rj)W0-@!bR*$oJxvwuc;pg*%NsQ;)OC$3Y6nHrD zwaF>!o#Bi8+%#mQ5uAah*QrUXY_yP?C1s^56SbK}=VhRgM}BY+X@tGM%PnX>=M`-*mH7E8Ie#z#*{JjY{8Ivw9=9HGNiQCTWf_ss zy;f2ZO(1fcfj+9E;}_|^Xh0V?4sU9ba9$XArKUIk!K%L07se>c3!56r>W`zCuX}e9 zZL5WRjuS#1nIP;UOyIo|m8H)o5`$bq9;4*AWJ>@|@{^U7)zzK)_>?UqB;?Ys{dC?m zJRgb9kyB7}B}WBIr;fpd0E8k+X8>cbBtv(Kb}UF#c+!YK^8Hm#XWnZA4;&zC z-K$jELe!?$v$=ZKjCVUA z&DreVkDcr%9VGkd59S<=ZX+h;I41+kj^!_Tf98bkCn$#54zWtn?b^R5F^{nuHf7_ALeM z691GX<>AM*ko&DAq5#6dW)P%)JAeoqs()wwDit#9RX<9-&aIdJACqn_UcjmL|A6FmXs9fR93RZqSt~2Bs-NU3 z=BDNd+Y=4c9p^n#YF8XD)jr-9e_U-qe2=06(U#uFE$rgpMuV??fJw@D11$(qnwqU|7hQh#!#IXw)(;aVSK0&k4!$)YX z-h4RSQB;lH9WBB+8I!ob^r@$_>iMk6yaL~>B7;1d=OS5qn`9&c=dY~;BTCSD&c>1@ z@l=WzE*1;CZ9BKxIlF6fCSmM0VdnUcNe4|i5NIhfQ54)N*dVekG>LPWL0YKk9ms1)3`>=3D)fbX%s4(5^1*e zby9~X!mVYIkv>Aq;rw2wrEF;&{NF%uTI`vyUwTd@Rw-H)D&a?#tlprjqq!p35BO>z z6XVZX!if^adc=WK_dGH9vUA75OoQW}hbTJmKX4uf*IeHl*1#jL+$SMdqqn}JWAgzb z?wcM!`rQRY|)I4L5!9EaVW>@uPY#7b|x2XDq^VbL^H4Y;U*(cxr2L@#?6Udin)vcQ1$1jIt@vh%@2`T~K>q z7>ZYUT^euK`%OB$RB}A;YLe2juBT(jlxb<@u&A&V0~VisGRh`@hk__mWpI7{-Mz)@ z==|)=@i1vaA|Gl$0n%3>AnyyZ=R+~=?YWYZV`%r3629N^-!O5v_-=g6$r+LNCeUSG z!BfiM_Ee&K=Y29$Gf2=!;QrD86{*Mi)5zzNlK!o>At|Hzrfd>Rlwf$9T|llt*Lf6h)$evS-Pkg+QnzwCkS zE6RDd2#GQZUT6Wh%|10Ho*54byIPpDhh>>08R%9=Ap@Htt(`0cj|%1TRu5TTbRN|;aGCs zQJ=wPO3V|atHQAhmg|N#JO&5^@)P&Pix(~%u#W{jB`Tc^Ds6;iYpe%+pGmm*ye7Pd zCul*6DV{MR5HJzX<+PZfWBr?2s=7)+t&B9UsCaf_t58yABAxg9WZiM7I$&V`Hquih zlVY6Rap4<3n=buzTwXzeC3sI5D>o@W(j5#r}0z7}1sbifT z%jd37Yl83&1hg-7w6xrA;Bvyk6#b!#CFWN00=Rrf-Rft;f~c2=mnB(Qi_M>B6_k`0 ze>vsk<)H^Cz2J{=`rZu?017g)SUHC5*Ttk|Gy9efQ~hOYcTg91yQK%v^Mn2BZqgyi zEN|rPuBDKW(b0EHWrqB8XINgs6i$cD9?lxL{^CJ7w$|1~4SBZjPB_UT;_obC+fV;^ z1m4X^FeLMTxEV4xK!%MBQz6})Gg4ax6@YtVF}pbD`8I@9RQ_V2HjDz38awlBxxFCf zd4PmSzz9$NfIH25J-t-<7lGu<&4-F^<(2z$?2gBG*UB zM`=VU@VN~^yKWK7@r(;SG(krkn~jZxK!MhJZ-IeC!&GB01jUQv#WaCeRE7JfB))hK zy^o~-qG zzT-AVu6wG?KwSgMQ`1T!%A##@M~?QKpyDYZWd*?P@}Q2Y z`uck%DaWi+Kbytg;a7F-o}7TbLV=Wi9+ zu;=={EM~$8G{R0xT}zjLyRp{S*KZDdX~&tNxyGeH>C|ugQyTRM!kJv?e%$2H-8mqF@n~CN=?K#J0ue z`&;P`dLHz2$-cTJy!xS%^fQ2hHaoj~;5$u=sEKL0dTBNH3Sd_VLqUxeZ+uEN&0lI} z-R9-}_-WMm%KpfBu&7rhUM%&O4mLZx5}N(Lw`3It5&NM!ZA)@yO=2)x$gHn_c=7i35UJ$~Ql(@= z?}Ng}m9AV#7sH3~)n9Tk`2{ovja*MBdVdey!r$a=i%^wsYm3(kn!LQceQs`*-5BNo zWBcaItqV^d;v>4TV`Ecg++QN2&5<;3iVBLlrKCWo$KX7r#!B?2gqz=fK3VctO=UG> zTt9ubSg3G20j{995|U8Wt!vj&nMH5!BXl*uiXobxmfNUFw$;lW=K^7 zBC0P)Ax$sFo=Uci2FWJSzNZ`V?+vFkX-j51ssVj4VQ>)2(<3O*=()jWMwh_kb-gNR zbi;Ubn)yqL`6~escX0Qbw+x%Fu8fg;S{K=@%*{~~%>Hh3FvUsdd^rm~E=X3z5zyX@ zHgEqR6Qx^Y)<60@h})nk*=MTFYJQjBc;fM9fSa%<>h%0LQC9LO1FDa!$TRVJ$Pc_I zD_Z&`bfe&!+I;+BhLF>Z#gV>0?nZZF_R7O8Z@eJ$2G>^|ucZVAXeSVaBhb;YzaGzw z<;QpJK)iP18&laYR>Hc8s66HiBlGg|I1|}NzA4rah6Qx}eN%BY*|mhXA(`2GM#Jl= zaWYxDIg%zW?mV7VRdqJFJvNZYb$sR;M2aOeV)|m=s!E8T5JdKmm0$=y6aEcPa^RZ= z8KzSGcN#jD1s@lCQ&JY5i@J10eFH(akgisUJjlz1RvG$cULUQi3%(9#DVmFGYPxpu zrA?I2;@!%&FM3jm!Q?+|Zenp!um1`^AIpU(2K@I0)0>NQKA*c9BOgJJ<+kfei^IHp z|K;b8l*E2Bf70h_gK(~kGzXpz4kSg%d8ex_(#8mwlSlRm(3@JU^fXCw1U$gjZqC)Z zfBFlp15lSXw_ORTLP?rB$ljrB_x!1ijg3f#1bdTu{`x>7Ytb|ytvG7RD zM!7hbnt=dg+>iq0K9tnJyFizG6_w1J zbVVk*6TuDzt%VfKe{qy02@XGtQV&dl2hWaM1uWj%kGlgghIj8q>nkm8F87f#iTe@` zNbHu?^P>B0UtyIjZ47-$Nb~v)JG1mUDN}tzWcnC`J*2R9T%Me+y;^xjD@nx;rH6ePayUmD4`(V314Bc+*ctj)^G z%FRU%hb5~tOMQ7X%JQNVbS+RB$p{$0A?cO~6lD4rJqRFI!+wvL{tfASm04rueRAdG zeK`=N6IaF7wxkkHgN_)d*Z3C`Gm30OqDB9p&iPu!ofw`DJ6@HN_Vi5E;=H~>FnJYL z*e$e0^Ng#I2s2LHqTSb(JgS$G!0xG`kg_ta-^0V`8?Q)eLDNMb-CyH%8Pgf0=CJTv zvq@$2j|bpQ!;jaH?!1p>(eHFeT>lq6Kl%hZI$Z>1871w*-jC%cCAGdB|7|y$E^LHD zj@*gkR30x;Gd9WV0|bm{ox4b^Wlxt=1?*XhI-A48Kgr1jjAE5__o=6iW0W5W+P-Si zAxor=)$Z;KYCXO@#o3)UkuzOgK|%`+3j+`fMqK06`Z^A532>%Pjc}idm5j{IO*v=% zF9R7%Wqb}c-dN?JAtz1syiIKJ+LQ^W;??CK!90%J&^=Z_vmwED7KuXY>|ORlaF?Vn2K(P_9PScM zR;(4QbdoYL*{di&tX&_T=5!kAM(qgpPOS~5DG;?vm$LK)(FQD6`7H4P4 zZ6SZ4GHBbw`~9f;+4pN*7&Hzl^m|PaIC7_F^jYRzFTO8lwSfc5t{ckD$=VOF#km20 zozJB$Gni8^ibGT!`ATw4DypiwRHjU&MK%Qb)|P`>9~}1mmoGy?<$;}%+m)lCArM7+ z$J&yNC++k0Xxh(0zt%h=KZv5DywI#phTdV2&oQQVbxBwEOE#jBYwBd_Uo~~&Jb?** zWBznem!6R+3T);=NySNzQBYFMu;AZ&|J~+dw9ib4C|so(E;CGWpTR z$xG4W`zs6NX{JkGMM4JiC_k!sNRRsR77%n#^?D5+trYv*u(I=xsSekAZe-qG3X~eD zKtz%{-&MVi`j?uQ|E1>0tho;%`344Xq}D6Q`fRmn_XBeSl6i07Nao1BNcM+Yw<|xQ zpMmxDvoUiswy$(@2`b7hqU=#rGFyJK_Pm_7avZy|Qh&3suzcK2ZxZ2Wfe}M$N=6*W zvxE?)OiyA`v0m>;2#SlK#}7FSq*4~@ z!zOyZP~mOGy>ymtbmv)i)+9Fpf6Ux!R5KP`>@{Yx-tb7f#e z!>xJ+L$T7!BqpMqt}U~0=Qwyl#Q04y7*cQ(A416ccT62DUHI(Jt}7$~f92S_-I|XI zBr3NTsU}WNqi=GA+$=Y@SEPfRA`aqYc5$|-H#RnoOo2q&ij=r^`%CmwkFG?tm>J5A z*AFcsL#Zg|TO@*(%by*XlxC|;Is;ucm+wEIISl;XIc(t^ymDh;M&rk!owu=pNy)+^ z$QVu%baY?2IY&2-K!v=&#ft4V{K(2O-Uuh_V&bTe%s7?&ZObh4es#WHJ`tB>FmO`3r4T3ogsQp!M(#p=CAPzRzc1!e;1JQqlywd65fb$4wrz%#-$_0Y_C|RVV;|_jU(Lc{(L!t||>jS|g^A3O!(rC>k)D;t!T_XNVf*prOkSrH_wut)!dKes)Og zH)9z=$ZzQD+{dL6dPYnp7u;0e;DvdPWqPm&8#oS?BuvoKADfDB>*WlrFlVnxbnfPLDLi3&vVc7HtgZkFZB|G$w z+y~F&o%hM#;mD+n$CKFYU?#FH41<%^sqk~P7zqc{j_+f~6*w25Kl6b?>!XsG2eoq5 z3YGZF*#3(@wx_$N#{PZ_z;0}-2WJ;&og3ja7{_Kx{&;>#0>LPx^*c(&P3Y_4wav9= z^K)yd3%cOPk`&%=Xae21zRrSX&in61cNk8H9Ttg*h$LMP;5$1UBp7?)B3&s5v|1-G z+^Ncs>}0WpsLXYAY)(Qvu9SURL<~AQzru(G1n5>Kmwa1c*J^WbWK+dPtQLL+R?d%# zf0b;>@RMv2GB-C@L`8C?4!;&hYW0XsvM87spYwfO_@RkaKaa1cZNfuD)9(_YVh*L=Z*uJKqvnF%QBdNB3E;0LTjTK|p)R(G-*XIOV@BX-9 zH@u0%bx7}7DX;L5@Q}q(!MjwMs|m>zvFp6~WaWOqf5Wu&K1bNP|cqiMCFWkcKK-k zJKn`&c&qWyXL;H0zH>2*cWSa{<3qkYmW#{FnaHDgY7tu&orh2&z;Jc^9e^?P3h;K-8V64O0UZuj661#5h3RZxa7ULpI?w- zdojh)kzfXY6mZujeO4U>T5K~sgG@1g`sehJdHqpmrDjl9IA@9aV{mpo*96_+1`X0s z4j^a)OlhjcfF<#_UvJ=O(Dc-E#PKRDyW6q9d7-ax!D@}qT(ym?J_iIns<)!1NN-r{ zT=D_v(y5%B7^6xeH)pG{{HEf^k2o4OSIP0yO~L`TDEq!(;Pn}8O|;)!nc=5s}z>fpXC>nAHhQiLH-P|Q#4~>)<3~LSbCJMXeJf? zWNn!`Nic{Pr*30>v{S%*g8V*;#vNyOZ-+jpB4o&9ZpMmJqXh#ghb~0&`)ENIqCrym zFaXEd%UExs7d-%BLE2L&Mx(Z8{@}lKTm3~CGWYAfPNasfXy#gPWb;ea+~4!_^WsP? zZEGPWg4T>Yh3+of7w@NJ@Mzd>61=vzL2-&R_Ou+E%(a1hVC`G*9RMQui?u%4F5xZ( ztn-p#6NV|5JzwSo^fV_OjtZTvot=4p$lXn~jb@9w_sMJC)7o@lj4;Fo)pJg-|D_SA z=4V43ZwePv{1c0wk*m!I@3&MM^X2viW|E}sXcR4P8~Z1imSTPjhBW0_pr8=;M0B!0iNmWQZuygYj`~ur&&^FI%d4rBl$mMq=GGyiG3r~c ze(S|h0EYZ`&3~nBbYC(0Gi_rGtnxGZUn+0sp6!JB%XJM=2y_U!pB$gqu5LCuj2wpg z5R`0PZI9_zW4HfOauVdjlxjwsl7wS8yEse3-f(1v(2!&$cm5bH=*!R!yqx3mn1>`+ zdtVL_`KL{Nj`5s{?RI7kA}0*O|G&?~C2>d`po{G)HENtx9oqyWi9d5Ax94FLkhHOBg^uff@o!e=-mnMx{;U0d2V4CG@`EnK*IoEE!?n9L)6X z@exc84l-6Bqc7S4AAk(XTrWUTdAxuS&z!QE*nMPd`%Z9d_7bpYRM^gJ1}#K9t8Bb} zVXFu|>8#}cjjf3%PH&{WXQhGRb`!k2yLNtGk|HQLb8|3OeOy#xI~cqIdP{M*I1OJ_ zN;Tt28z?tXVfaXE&&@|%X22q&?50h3$9$T6_L0Xl$>4&p`x#7PCu*%%^l&bSXU)#m zmgqg!e-UbJY+};h8OdTwZ6C+1+~;|xGjfqYkU>mTVpy*huwJ>$OXgZ^y=ebQDvbW| z(03ndTlu;3yP7p#>{r@J40lFmcEOV$2Eo{e_y!#>C^CAp**~@XchhSll}BWcgdH0r zmg?19d0%pU2xdPAYp0>1p@5bFTWqD-K&Kg}(Gyev*vl=Y+1tjgLhk z3x`(wUE|W7fa%BaYhHEsQdM@q3b(k7q?$g)6iE^Lhp7UjgZJX0Wf;Ly5@>~8@_vuf{@ugFiPQHOhvO9`RT{fWs)1GG z<5D3Z0XTS%LUMh?i z_Gzn~oP1PV5{blBGkuAOM-QKZeQ8-SeoHSX*cV)m8n!XgSi1Ih5_G)b2V_!vu?6;> zg#Vp$8x#r(0vM@U$TN2rFNRWvbRM*EoIuQ-yxOcL-H_#XkoWn8OLQiIW;I;-`||u zc%06~`Pz~FoqvCT?vUPT9@7sO`o!?xFI4)3=0{#0V@x&S;Ar8JPfn(60sull4L}$c z=xFpRwKPm-EUHiY1mGOSN!-1XGL4P(N5=`64++Jdy9esy-Px3?AmPEao_y1CZ?!QHf3z}OWf99O|y4x>h+;*lN#AHIDb)AiYsk3uqMa&MG z_?@A5WBDqr_XooJuYy?pBn`u!78e?S!bF*-wgeEZ+_U%|@3)&&5(6k5HscMWdL<$rf{T_4zUb@@rA-y~9K!7)HFL;VI;_h}%p>-rSH zws^Ly%v}*G^MxV3W-l!@FrQu3yn1zR^8)QFGkJo1ZSCf1`3FR`kk@TX4$2*S_qP`! zLnBiS-1hcv7b6q`K9~Cf&df4(R66K9b)ON8-lQ8DWSUML7z9d5&6e7wPq+Gt`nUhl zYD|763;%>LeR_mqGJG2O3xH4wiSd>dqMK$Gmmvoz+DzYDcRVLLo=LXEf>eCRtRi-F zdAJ~Kaj~~mK>d@;x#LOCkQY}=mJ&lp54J{n3)wks%>Q}>V?>xyLP338&Rf~aWhDxM z#gntr_Ato%ZK* zGo$-f9{dH^s8nCg2T|KbDjFK=Ypn5K?l(I4E|#x=ng8rU2{#)@fZP&bB?NVK-`sfk zb*v2>!U|9a1^9jWJhFh$096@SXG?v9`M zD`2kNHmDn`92O7stW`4f@SBjuMKF zXG8v{YpioYEs26-jl7^@sx$fW7h!8jeWu-<;u_qu{DfYXBX;K+}gnkV3r zM;!tq&e+(@ygnWI2ilOmFl{5vbPu~L(JUqb2+Zw`2Nc>cupSwS`_dL=&7a6uKY023 zQPj)AI!`+66LKGgwmA82+?N3t~1-d44G*!pPLYVy|6v0R@k=$n7m%*@P085MN@i=^RH+n!EeFU(j> zvF(3jFLDFYz2D1E2c{8o6P*mqYx-r`UNG7&k4fI1XGYVoHz#spIk_hFF+y<$&smj zY_OXgyjn5Bvzcp1ND30hcax?ksSvcatr2Hp-)3vh$@#Fh`c?bGT>wU<69EB%+u>q0 zK-Nw-NAdyjYh%56ya>xrxgE8oqy#8vPzgGloo);PJ&!=>-QI(n>C)#Dk_K0s*4i>7wo8w3mb%2*=#~5^A zh!$TyNa?@INLCZeQ?s#~S!liYx&EENS2ypg;HDf&P2Ee|mXGWb9qx5mB>L&2pvRUf z7!;iyecZ-jIJxoDsl~^<&iaJ4G13bBZHq1kMS*enYo8`$Jk<|2A96O~Q=f>TR{)Ik zC^vq92xwcpXxWPJio-lka2UQtRHs}l;7$9YSY4s(p}>4uVt$qL}OxbOYJ)g%0R zjTIG^xel*PR_sAb<{(Mx^lRB{ZG+p65-M5vjj&?K_(|{18|KZH$cNm(o%kr_T#x zW8PDd`_Ss4FkYHBq2a+tXO8|VsoiGNMHIS{gaEqJF4IrpGlAgSBFs_!4CAe-udlDJ z<`0y}9xb>yqDe5du)zAQB3Vj@n}i|C0s^@>Gs=DJGZVf^x|h`X6&n}HsqVE10>>hM zaopt3_ous)rMH)Rf*^f^@g|sHWJ6#_PkLypTfEL$VGn6_!r(mOihF}ylLi#FYe0n= zPC2~~CmU;*e5C&&3X$3+p%X{&NM6Sq&b)%iB9GuAL6UbhFv%9N#3rQ~&=^kmzerMN zV}K+K*5m{7gsi)c)?^}CV$#9rk_uA3B+kURS1ZKm^oKs65AEvKTNrw>HmJym@|EEa zm#7gd*wcKEX+AeYj!YA5O1|s&nT?l15m^)5&WaSsLh*z2ych_#415 zrJ#B3y!F({ouzZtG+RWKzN0fM_8%JLt=z=57jo2eq*I_ojpnj#QF)cDAWHLqYc8%U zO7ah1jP85RhrFjbm`r+-z8?AF{)P?XU98jPLGX&v+D2ORz^Gh_hbFD$;zT#_qq+d? zfVvt}BpK3d_v3PXe$LI)-bgcoiX6oqIIT;xe6H!c2;2zyP*6{S@#0)yO({onjwklx z4*P%4e7UoDzZND2CV5WLayz;-khSo>egrQ-GiWm}j2F>+8{IdBP5=$q0ILl`3)C$( zq@+-t{77wXqpxz_39;u_PM7vEh8fg6lssFmNEv%sYK0Q8g(p=56H-5$Wb;fp%CkS0 zx&4{d^_z*VPa(@dn*1V*5_X^M^nsxNTYJ`r{E?%GCp-@*S2P~_(Tb?=>aI9GAGyZL zcfBl>Wb!fs%gn>h%+Cgn)CN8fh$4yNwQ539N@N)WqSzA)NospU=f7dplHAprQeL5< zQuT0vMRF`GmsO{7=9j&HIxjpg4;D;KO;PC{6f`vAfu`I~qZSc>yFV={DGC1bOH~@1 z%G2?1k?rHgcwv{7Ym1oihddLUy|BSj8^d(!wEVvqDcoh@U&0S z2~k5Jh<;+pJhE_nbmWczar!7R+NVs|1W;r|KOU3+AO194E)(Ht+^y(CpY0!~X`Pk2NYMHMZ1 zWaD>OOe+{^^Fsm~4Ch8P$cUpng;Vq@OmX-3_kdT?VmaCYwm#tI1OH;P zm$s^EbVf{t-CRvTKmhPD5E2%)X%>l~5CG~obaZqpD+aN#vAEO_kH?4m@2jgj=TI=S zkGq2b|0v(PnB>>3K)%lB!RM$k>)LvWitBIwD{zr}Kn&Blus%fRnDBf&y||c!Z)-5K zumlgR`@r1mK1ojf4D~Ujj~Txq$Rb2VMYYcJT<|*kY)?2}k`fdY1RRcL_Yp^?^_?1l zN1k+Cm^OXVK(=@F$#UoxpcRf~(QcW4o2pSBH;O^yonBb*28K^oR#p?58W?3ptzYqh z9@M*|8h-oiEP`Y7K488fq{Yaw6yr80-z{@fl5_JefT=s4s?{c;_b8>QQ zyfHF;$QyuhcB#gpm3aGke7sVsfJ*8s;sA0Ho%7iOz&&fVo%^xHWi0P!R_HXthE4Gb zTc})%S|49fP|#-bXQ2HLsNJq)Ga>XUtz92M510HBpsd!dj^%k_p`lu3%GRUb^MC-| z*J|sTUs5m_>~{o(^;FRE;7ldhQ?|GNX?=#QDM=k1m5Jb=vy&9);4*cv0E*~p!}kvK zk1uGOc$js8+=i`fi5P9cuEOoJ6vd0>_P6V`daXWhU@zslzJQA&11yGKpAdWSQVD-5 zvS|eEV)PB;i7^I9)m#0>eOBw$q{O5SOBIAI_DV&_yk9_o;qz&I+BLucq{$CG;TJ%Z z$VN$n=o%0sKJCwHo)2`4E!06P%L@u7*(m@%ZtRwrnu;}MQc&u-L4QXQ?p@u|$*x`2 z-POfXk4peu-C> z15K)iuh1D(XFu zn@cZ(Z*QkK9a8HsH5C6%VAs}kFVcPo_j?{vs38gZ-uI|}4yl}g9n+;I(0D&NNDUEi-?dig?qTF1thrK=(klMlWTt*C#PsJ)1v?(2Ft>Yd1ll>NKv1mHgzcYZB88mKeZ58}Z4PrK^vy2H%nh5P9 zSn~?U#-3J)HQsQo|870Xw;Nu%aQVO1&ZL2f3V%-yRxFo+E*QKG3a^D!-)@4T` zz4IPfOyRZAdXc)~^u^!beSZ<&rD671M94si%p75!mz7y z@V)fmD{t!@kLh)ULx0y8`QG17a<1>o>Z2`6xBA>`6k^j7g)IMK6JaWM-Hs*Uw6@20 za43t(k^V7X_s*tY`-td>{Pgrxv%X{SJ?~qIZ@w^hgRr~rQei~*^f01-WYts#R;zO$ zr>c1jOXahQWh?!l6BAVSjDt_}Th65OUDxr|6`7k`k@ENIz~itceXgj8dx9$EckG?7 zv2dI~s|8f#+txfhJ+)@spU21=kNbrH;~SuT$SH+N!GbjZ@ppWGOUrM!Kx7GV0P>ui zCY+n_m>Vmm@Y`sO{SY4>p1z)pDl@jSlFJ$W#%D!9MwXKN`Vrhn@`-0FnZ}{`G&DxV zFN|{f9?P0tqKa8}De6-COAA6&`dIn{3Y^G7$zC?0N}U8B$Lqyo)(3pgb!r_<9&d{4 zDUhiL!`_!%gkupBc@z!d8VxbYp3bS+m-(k6oBMCPNzZAvJ~^QmtL8 z+bwI7(RtUETO3ZbEQe3o6y-bjtd13I#$?>rBTyqBsT$|Mjfc#Cl%#V2qA}wd^6W}1 z5z#NuZ|j-UJWkoTpdPP*@m=C3)IYzqvXXbL3G|kyau!{|D@)$Bamk28KIm=1mk&TA z^qHOQ)e~aARb>Tg$n@<1_etJ0HRDL0Hn%^3n*PKh+RxXuGpAp&|5K#}P!n6mgO_AK zqo~i_)a~V=1r`teYC+6sKCrV}i!%o0!dEUqo1&_jgu*R;_V*pD<`4*B^kfme+!K-z)l~J55AMk&cOJ1JqAlHxe4- zPSO+tZ?u#yC)z8Gt ztk5(;isaiWQHxpd>@GPFkhLu-Iq>bT9y>Z<&%9$H9aVgU}jlc&r0;{QHxJh6bSF0GF3m6c=xQLP&`I%uERi^e+AUL`P!- zEz@4Z$l$$`lQofxhZr4e)Ne#$4%78O<3Y-8sG)ft?ACOB4Fyiy;Y7i)L1?`|eAQ!f z7^sMLO5r75NRI4zr=6Xh z^-Ge=+>W7xLqpj+Nhv9E5WK`h!fS!$mA?cGOThZDyj)e+n&o;!JcUuztS{PYP@11v zB_p2uBM`$CZuIT&o_+27RY6~0EjA_wpOh4h{pso2?ykaMKk%axQw560&NL+r4W1wy z5)cwvDalw{TPvpvb$d*x#6u`^Qs-jh5iep>O{ss;G!Qit6?a zSLE%OS4>mN3{L?vzPi`Ls+9BZPZy3kPdAwj7-1%UX!oUhWZz9FxD6X56_+|Y-$sry zcV)p>q}Sqre403T&)_Bez#)C27HAtqBWifib$)(M7#GXN#$Nf$1=PT=?7&{#M-=kI zv0n5q)~0+1J3I2&J3ACDQW=6?HE7$Tw1nTKNvmG<0Le>#RGja23N{im|I@(mDg5~} z@#+?AqXKL_X>`{S$nDOL1KAp1Ol%G6e*e4>{>a*Epah{MjS3MWO9e|a_@eYc%+z+i zt^(kCrR0mb_+u(6DnayY>h=_41Po=vFaso%a20wMD9StC{m8SgDcP)py@Ce(aZ}5T|l*=O?6cjd^jo?yk-#7 z>2Bj-u>(lL=GiI4!9X!jw^PXPmaFHQ742ER<__^)GD~BhcNoxm6NrPDYwzVxLl*0 zbbjqKJMl68KHtSAXcZ1wEHLkS^SfTju-Wq*-mh+&u>$|x@~g6v@fDx|UA2#l*lnw= z$23(`90UKd;6k~CJJBCX!rmL$PIJj)`Q63m`*W9C(i>Q6gk=Cx9mzbTRsY(Ju6F|B z4nRl>O3iY%3QwdMA&|7q=&AqSbf;icL`0s!`|_dXcK>Uo*UFV2PS5A%5c#9LLK^ycdiml0b|{U;+uK`u*RyruL8-26Z;w(J zZz(H_77Oc_ELrT~e0^nH9O1F1=y4-zz~xk|dL5EmJI?y@a+XFc4UpdSLqZEKd;>I+ z&8^wGR=RLjs;+Ks2$;i496XXkr}k#6i)(91kNvOKc6QWXyf~Ry)_8ZKdMCu|_Xlg3 zO>#S&j3!IsTXb~WV-~pY*xA@Y-!mh_No;4gzma*a68=|S1a9=`sj4gARFe1cBeP1n z^~T*Zvh(b9G@UA0`Bayb&z=aSSpA^TvUIS07V9u^B$qV3=!GL!1QOy?e>sh+l~yqK z*qy1&Kap)wN`0kQW1km6je#OeOZ!Syl*qV<60DquHz27HW%&}qOIDM1VT-(i913+rkMu?yec8Z>?r6n$FyqCuSXr5`P+ zOfSmu@o^VbKj$?#&iK7?;bCF^x4QI�LlS{9$2XK`ANiWxFug-@jw^?0orf5K3Ec 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

#)*Z>5_7r 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 fsOsm7*{yJB05rS(}}fIw5)X-&bAMiAINJs74~ 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 diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/LICENSE.txt b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/LICENSE.txt index f9c531e7..fd2eb70b 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/LICENSE.txt +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/LICENSE.txt @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/README.md b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/README.md index ab5a7a6a..9600761b 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/README.md +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/README.md @@ -1,10 +1,9 @@ # Welcome to VST SDK 3 base -Here you can find some helper classes useful for developing **VST 3** plug-ins. +Here you can find some helper classes useful for developing VST3 Plug-Ins. ## License & Usage guidelines - More details are found at [www.steinberg.net/sdklicenses_vst3](http://www.steinberg.net/sdklicenses_vst3) ---- -Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) +Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) \ No newline at end of file diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/baseiids.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/baseiids.cpp index 7d12c713..d7b1f7b5 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/baseiids.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/baseiids.cpp @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/classfactoryhelpers.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/classfactoryhelpers.h index 5a531e93..075c0a73 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/classfactoryhelpers.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/classfactoryhelpers.h @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fbuffer.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fbuffer.cpp index 83866ea9..a67ad569 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fbuffer.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fbuffer.cpp @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -529,10 +529,12 @@ int8* Buffer::operator + (uint32 i) { if (i < memSize) return buffer + i; - - static int8 eof; - eof = 0; - return &eof; + else + { + static int8 eof; + eof = 0; + return &eof; + } } //------------------------------------------------------------------------------------- @@ -603,7 +605,7 @@ bool Buffer::toWideString (int32 sourceCodePage) endString8 (); Buffer dest (getFillSize () * sizeof (char16)); - int32 result = String::multiByteToWideString (dest.str16 (), str8 (), dest.getFree () / sizeof (char16), sourceCodePage); + int32 result = String::multiByteToWideString (dest.str16 (), buffer, dest.getFree () / sizeof (char16), sourceCodePage); if (result > 0) { dest.setFillSize ((result - 1) * sizeof (char16)); diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fbuffer.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fbuffer.h index 0a8c1ff6..d0e2f394 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fbuffer.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fbuffer.h @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fcommandline.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fcommandline.h deleted file mode 100644 index 653fb7c6..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fcommandline.h +++ /dev/null @@ -1,366 +0,0 @@ -//------------------------------------------------------------------------ -// Project : SDK Base -// Version : 1.0 -// -// Category : Helpers -// Filename : base/source/fcommandline.h -// Created by : Steinberg, 2007 -// Description : Very simple command-line parser. -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -//------------------------------------------------------------------------ -/** @file base/source/fcommandline.h - Very simple command-line parser. - @see Steinberg::CommandLine */ -//------------------------------------------------------------------------ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Steinberg { -//------------------------------------------------------------------------ -/** Very simple command-line parser. - -Parses the command-line into a CommandLine::VariablesMap.\n -The command-line parser uses CommandLine::Descriptions to define the available options. - -@b Example: -\code -#include "base/source/fcommandline.h" -#include - -int main (int argc, char* argv[]) -{ - using namespace std; - - CommandLine::Descriptions desc; - CommandLine::VariablesMap valueMap; - - desc.addOptions ("myTool") - ("help", "produce help message") - ("opt1", string(), "option 1") - ("opt2", string(), "option 2") - ; - CommandLine::parse (argc, argv, desc, valueMap); - - if (valueMap.hasError () || valueMap.count ("help")) - { - cout << desc << "\n"; - return 1; - } - if (valueMap.count ("opt1")) - { - cout << "Value of option 1 " << valueMap["opt1"] << "\n"; - } - if (valueMap.count ("opt2")) - { - cout << "Value of option 2 " << valueMap["opt2"] << "\n"; - } - return 0; -} -\endcode -@note -This is a "header only" implementation.\n -If you need the declarations in more than one cpp file, you have to define -@c SMTG_NO_IMPLEMENTATION in all but one file. - -*/ -//------------------------------------------------------------------------ -namespace CommandLine { - - //------------------------------------------------------------------------ - /** Command-line parsing result. - - This is the result of the parser.\n - - Use hasError() to check for errors.\n - - To test if a option was specified on the command-line use: count()\n - - To retrieve the value of an options, use operator [](const VariablesMapContainer::key_type k)\n - */ - //------------------------------------------------------------------------ - class VariablesMap - { - bool mParaError; - using VariablesMapContainer = std::map; - VariablesMapContainer mVariablesMapContainer; - public: - VariablesMap () : mParaError (false) {} ///< Constructor. Creates a empty VariablesMap. - bool hasError () const { return mParaError; } ///< Returns @c true when an error has occurred. - void setError () { mParaError = true; } ///< Sets the error state to @c true. - std::string& operator [](const VariablesMapContainer::key_type k); ///< Retrieve the value of option @c k. - const std::string& operator [](const VariablesMapContainer::key_type k) const; ///< Retrieve the value of option @c k. - VariablesMapContainer::size_type count (const VariablesMapContainer::key_type k) const; ///< Returns @c != @c 0 if command-line contains option @c k. - }; - - //! type of the list of elements on the command line that are not handled by options parsing - using FilesVector = std::vector; - - //------------------------------------------------------------------------ - /** The description of one single command-line option. - - Normally you rarely use a Description directly.\n - In most cases you will use the Descriptions::addOptions (const std::string&) method to create and add descriptions. - */ - //------------------------------------------------------------------------ - class Description : public std::string - { - public: - Description (const std::string& name, const std::string& help, const std::string& valueType ); ///< Construct a Description - std::string mHelp; ///< The help string for this option. - std::string mType; ///< The type of this option (kBool, kString). - static const std::string kBool; - static const std::string kString; - }; - //------------------------------------------------------------------------ - /** List of command-line option descriptions. - - Use addOptions(const std::string&) to add Descriptions. - */ - //------------------------------------------------------------------------ - class Descriptions - { - using DescriptionsList = std::deque; - DescriptionsList mDescriptions; - std::string mCaption; - public: - /** Sets the command-line tool caption and starts adding Descriptions. */ - Descriptions& addOptions (const std::string& caption = "", - std::initializer_list&& options = {}); - /** Parse the command-line. */ - bool parse (int ac, char* av[], VariablesMap& result, FilesVector* files = nullptr) const; - /** Print a brief description for the command-line tool into the stream @c os. */ - void print (std::ostream& os) const; - /** Add a new switch. Only */ - Descriptions& operator() (const std::string& name, const std::string& help); - /** Add a new option of type @c inType. Currently only std::string is supported. */ - template - Descriptions& operator () (const std::string& name, const Type& inType, std::string help); - }; - -//------------------------------------------------------------------------ -// If you need the declarations in more than one cpp file you have to define -// SMTG_NO_IMPLEMENTATION in all but one file. -//------------------------------------------------------------------------ -#ifndef SMTG_NO_IMPLEMENTATION - - //------------------------------------------------------------------------ - /*! If command-line contains option @c k more than once, only the last value will survive. */ - //------------------------------------------------------------------------ - std::string& VariablesMap::operator [](const VariablesMapContainer::key_type k) - { - return mVariablesMapContainer[k]; - } - - //------------------------------------------------------------------------ - /*! If command-line contains option @c k more than once, only the last value will survive. */ - //------------------------------------------------------------------------ - const std::string& VariablesMap::operator [](const VariablesMapContainer::key_type k) const - { - return (*const_cast(this))[k]; - } - - //------------------------------------------------------------------------ - VariablesMap::VariablesMapContainer::size_type VariablesMap::count (const VariablesMapContainer::key_type k) const - { - return mVariablesMapContainer.count (k); - } - - //------------------------------------------------------------------------ - /** Add a new option with a string as parameter. */ - //------------------------------------------------------------------------ - template <> Descriptions& Descriptions::operator() (const std::string& name, const std::string& inType, std::string help) - { - mDescriptions.emplace_back (name, help, inType); - return *this; - } - bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result, FilesVector* files = nullptr); ///< Parse the command-line. - std::ostream& operator<< (std::ostream& os, const Descriptions& desc); ///< Make Descriptions stream able. - - const std::string Description::kBool = "bool"; - const std::string Description::kString = "string"; - - //------------------------------------------------------------------------ - /*! In most cases you will use the Descriptions::addOptions (const std::string&) method to create and add descriptions. - - @param[in] name of the option. - @param[in] help a help description for this option. - @param[out] valueType Description::kBool or Description::kString. - */ - Description::Description (const std::string& name, const std::string& help, const std::string& valueType) - : std::string (name) - , mHelp (help) - , mType (valueType) - { - } - -//------------------------------------------------------------------------ - /*! Returning a reference to *this, enables chaining of calls to operator()(const std::string&, - const std::string&). - @param[in] name of the added option. - @param[in] help a help description for this option. - @return a reference to *this. - */ - Descriptions& Descriptions::operator () (const std::string& name, const std::string& help) - { - mDescriptions.emplace_back (name, help, Description::kBool); - return *this; - } - -//------------------------------------------------------------------------ - /*! Usage example: - @code - CommandLine::Descriptions desc; - desc.addOptions ("myTool") // Set caption to "myTool" - ("help", "produce help message") // add switch -help - ("opt1", string(), "option 1") // add string option -opt1 - ("opt2", string(), "option 2") // add string option -opt2 - ; - @endcode - @note - The operator() is used for every additional option. - - Or with initializer list : - @code - CommandLine::Descriptions desc; - desc.addOptions ("myTool", // Set caption to "myTool" - {{"help", "produce help message", Description::kBool}, // add switch -help - {"opt1", "option 1", Description::kString}, // add string option -opt1 - {"opt2", "option 2", Description::kString}} // add string option -opt2 - ); - @endcode - @param[in] caption the caption of the command-line tool. - @param[in] options initializer list with options - @return a reverense to *this. - */ - Descriptions& Descriptions::addOptions (const std::string& caption, - std::initializer_list&& options) - { - mCaption = caption; - std::move (options.begin (), options.end (), std::back_inserter (mDescriptions)); - return *this; - } - - //------------------------------------------------------------------------ - /*! @param[in] ac count of command-line parameters - @param[in] av command-line as array of strings - @param[out] result the parsing result - @param[out] files optional list of elements on the command line that are not handled by options parsing - */ - bool Descriptions::parse (int ac, char* av[], VariablesMap& result, FilesVector* files) const - { - using namespace std; - - for (int i = 1; i < ac; i++) - { - string current = av[i]; - if (current[0] == '-') - { - int pos = current[1] == '-' ? 2 : 1; - current = current.substr (pos, string::npos); - - DescriptionsList::const_iterator found = - find (mDescriptions.begin (), mDescriptions.end (), current); - if (found != mDescriptions.end ()) - { - result[*found] = "true"; - if (found->mType != Description::kBool) - { - if (((i + 1) < ac) && *av[i + 1] != '-') - { - result[*found] = av[++i]; - } - else - { - result[*found] = "error!"; - result.setError (); - return false; - } - } - } - else - { - result.setError (); - return false; - } - } - else if (files) - files->push_back (av[i]); - } - return true; - } - -//------------------------------------------------------------------------ - /*! The description includes the help strings for all options. */ - //------------------------------------------------------------------------ - void Descriptions::print (std::ostream& os) const - { - if (!mCaption.empty ()) - os << mCaption << ":\n"; - - size_t maxLength = 0u; - std::for_each (mDescriptions.begin (), mDescriptions.end (), - [&] (const Description& d) { maxLength = std::max (maxLength, d.size ()); }); - - for (const Description& opt : mDescriptions) - { - os << "-" << opt; - for (auto s = opt.size (); s < maxLength; ++s) - os << " "; - os << " | " << opt.mHelp << "\n"; - } - } - -//------------------------------------------------------------------------ - std::ostream& operator<< (std::ostream& os, const Descriptions& desc) - { - desc.print (os); - return os; - } - - //------------------------------------------------------------------------ - /*! @param[in] ac count of command-line parameters - @param[in] av command-line as array of strings - @param[in] desc Descriptions including all allowed options - @param[out] result the parsing result - @param[out] files optional list of elements on the command line that are not handled by options parsing - */ - bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result, FilesVector* files) - { - return desc.parse (ac, av, result, files); - } -#endif - -} //namespace CommandLine -} //namespace Steinberg diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fdebug.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fdebug.cpp index 9fe27d85..6355feba 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fdebug.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fdebug.cpp @@ -11,7 +11,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -107,8 +107,6 @@ bool AmIBeingDebugged () #include #include #include -#include -#include #if SMTG_OS_WINDOWS #ifndef _WIN32_WINNT @@ -143,20 +141,6 @@ DebugPrintLogger gDebugPrintLogger = nullptr; static const int kDebugPrintfBufferSize = 10000; static bool neverDebugger = false; // so I can switch it off in the debugger... -static std::once_flag neverDebuggerEnvCheckFlag {}; - -//-------------------------------------------------------------------------- -static void initNeverDebugger () -{ - std::call_once (neverDebuggerEnvCheckFlag, [] () { - // add this environment variable to not stop in the debugger on ASSERT - if (std::getenv ("SMTG_DEBUG_IGNORE_ASSERT")) - { - neverDebugger = true; - } - }); -} - //-------------------------------------------------------------------------- static void printDebugString (const char* string) { @@ -209,7 +193,6 @@ void FDebugBreak (const char* format, ...) gPreAssertionHook (string); } - initNeverDebugger (); if (neverDebugger) return; if (AmIBeingDebugged ()) @@ -245,7 +228,7 @@ void FDebugBreak (const char* format, ...) void FPrintLastError (const char* file, int line) { #if SMTG_OS_WINDOWS - LPVOID lpMessageBuffer = nullptr; + LPVOID lpMessageBuffer; FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMessageBuffer, 0, nullptr); @@ -308,16 +291,12 @@ void* operator new[] (size_t size, int, const char* file, int line) //------------------------------------------------------------------------ void operator delete (void* p, int, const char* file, int line) { - (void)file; - (void)line; ::operator delete (p); } //------------------------------------------------------------------------ void operator delete[] (void* p, int, const char* file, int line) { - (void)file; - (void)line; ::operator delete[] (p); } diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fdebug.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fdebug.h index 3b376ae6..a5d9923f 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fdebug.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fdebug.h @@ -11,7 +11,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -97,10 +97,6 @@ bool AmIBeingDebugged (); if (!(f)) \ FDebugBreak ("%s(%d) : Assert failed: %s\n", __FILE__, __LINE__, #f); -#define SMTG_ASSERT_MSG(f, msg) \ - if (!(f)) \ - FDebugBreak ("%s(%d) : Assert failed: [%s] [%s]\n", __FILE__, __LINE__, #f, msg); - /** Send "comment" string to the debugger for display. */ #define SMTG_WARNING(comment) FDebugPrint ("%s(%d) : %s\n", __FILE__, __LINE__, comment); @@ -198,7 +194,6 @@ void* operator new (size_t, int, const char*, int); #else /** if DEVELOPMENT is not set, these macros will do nothing. */ #define SMTG_ASSERT(f) -#define SMTG_ASSERT_MSG(f, msg) #define SMTG_WARNING(s) #define SMTG_PRINTSYSERROR #define SMTG_DEBUGSTR(s) diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fobject.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fobject.cpp index a061556b..e085a343 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fobject.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fobject.cpp @@ -1,5 +1,4 @@ //------------------------------------------------------------------------ -// Flags : clang-format SMTGSequencer // Project : SDK Base // Version : 1.0 // @@ -10,28 +9,28 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // 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, +// +// * 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 +// this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the Steinberg Media Technologies nor the names of its -// contributors may be used to endorse or promote products derived from this +// 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 COPYRIGHT OWNER 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 +// 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 COPYRIGHT OWNER 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. //----------------------------------------------------------------------------- @@ -40,12 +39,6 @@ #include "base/thread/include/flock.h" #include -#define SMTG_VALIDATE_DEPENDENCY_COUNT DEVELOPMENT // validating dependencyCount - -#if SMTG_DEPENDENCY_COUNT -#include "base/source/updatehandler.h" -#define SMTG_DEPENDENCY_CHECK_LEVEL 1 // 1 => minimal assert, 2 => full assert -#endif // SMTG_DEPENDENCY_COUNT namespace Steinberg { @@ -61,132 +54,52 @@ struct FObjectIIDInitializer // only can cast to their own objects // this initializer must be after the definition of FObject::iid, otherwise // the default constructor of FUID will clear the generated iid - FObjectIIDInitializer () { const_cast (FObject::iid).generate (); } -} gFObjectIidInitializer; - -//------------------------------------------------------------------------ -FObject::~FObject () -{ -#if SMTG_DEPENDENCY_COUNT && DEVELOPMENT - static bool localNeverDebugger = false; -#endif - -#if DEVELOPMENT - if (refCount > 1) - FDebugPrint ("Refcount is %d when trying to delete %s\n", refCount, isA ()); -#endif - -#if SMTG_DEPENDENCY_COUNT -#if SMTG_DEPENDENCY_CHECK_LEVEL >= 1 - if (gUpdateHandler) - { -#if DEVELOPMENT - SMTG_ASSERT (dependencyCount == 0 || localNeverDebugger); -#endif // DEVELOPMENT + FObjectIIDInitializer () + { + const_cast (FObject::iid).generate (); } -#endif -#endif // SMTG_DEPENDENCY_COUNT - -#if SMTG_VALIDATE_DEPENDENCY_COUNT - if (!gUpdateHandler || gUpdateHandler != UpdateHandler::instance (false)) - return; - - auto updateHandler = UpdateHandler::instance (); - if (!updateHandler || updateHandler == this) - return; - - SMTG_ASSERT ((updateHandler->checkDeferred (this) == false || localNeverDebugger) && - "'this' has scheduled a deferUpdate that was not yet delivered"); - - if (updateHandler->hasDependencies (this)) - { - SMTG_ASSERT ( - (false || localNeverDebugger) && - "Another object is still dependent on 'this'. This leads to zombie entries in the dependency map that can later crash."); - FDebugPrint ("Object still has dependencies %x %s\n", this, this->isA ()); - updateHandler->printForObject (this); - } -#endif // SMTG_VALIDATE_DEPENDENCY_COUNT -} +} gFObjectIidInitializer; //------------------------------------------------------------------------ -uint32 PLUGIN_API FObject::addRef () -{ +uint32 PLUGIN_API FObject::addRef () +{ return FUnknownPrivate::atomicAdd (refCount, 1); -} +} //------------------------------------------------------------------------ -uint32 PLUGIN_API FObject::release () +uint32 PLUGIN_API FObject::release () { if (FUnknownPrivate::atomicAdd (refCount, -1) == 0) { refCount = -1000; delete this; return 0; - } - return refCount; + } + return refCount; } //------------------------------------------------------------------------ tresult PLUGIN_API FObject::queryInterface (const TUID _iid, void** obj) { - QUERY_INTERFACE (_iid, obj, FUnknown::iid, FUnknown) - QUERY_INTERFACE (_iid, obj, IDependent::iid, IDependent) - QUERY_INTERFACE (_iid, obj, FObject::iid, FObject) + QUERY_INTERFACE (_iid, obj, FUnknown::iid, FUnknown) + QUERY_INTERFACE (_iid, obj, IDependent::iid, IDependent) + QUERY_INTERFACE (_iid, obj, FObject::iid, FObject) *obj = nullptr; - return kNoInterface; + return kNoInterface; } //------------------------------------------------------------------------ void FObject::addDependent (IDependent* dep) { - if (!gUpdateHandler) - return; - - gUpdateHandler->addDependent (unknownCast (), dep); -#if SMTG_DEPENDENCY_COUNT - dependencyCount++; -#endif + if (gUpdateHandler) + gUpdateHandler->addDependent (unknownCast (), dep); } //------------------------------------------------------------------------ void FObject::removeDependent (IDependent* dep) { -#if SMTG_DEPENDENCY_COUNT && DEVELOPMENT - static bool localNeverDebugger = false; -#endif - - if (!gUpdateHandler) - return; - -#if SMTG_DEPENDENCY_COUNT - if (gUpdateHandler != UpdateHandler::instance (false)) - { + if (gUpdateHandler) gUpdateHandler->removeDependent (unknownCast (), dep); - dependencyCount--; - return; - } -#if SMTG_DEPENDENCY_CHECK_LEVEL > 1 - SMTG_ASSERT ((dependencyCount > 0 || localNeverDebugger) && - "All dependencies have already been removed - mmichaelis 7/2021"); -#endif - size_t removeCount; - UpdateHandler::instance ()->removeDependent (unknownCast (), dep, removeCount); - if (removeCount == 0) - { -#if SMTG_DEPENDENCY_CHECK_LEVEL > 1 - SMTG_ASSERT (localNeverDebugger && "No dependency to remove - ygrabit 8/2021"); -#endif - } - else - { - SMTG_ASSERT ((removeCount == 1 || localNeverDebugger) && - "Duplicated dependencies established - mmichaelis 7/2021"); - } - dependencyCount -= (int16)removeCount; -#else - gUpdateHandler->removeDependent (unknownCast (), dep); -#endif // SMTG_DEPENDENCY_COUNT } //------------------------------------------------------------------------ @@ -210,61 +123,61 @@ void FObject::deferUpdate (int32 msg) //------------------------------------------------------------------------ /** Automatic creation and destruction of singleton instances. */ //------------------------------------------------------------------------ -namespace Singleton { -using ObjectVector = std::vector; -ObjectVector* singletonInstances = nullptr; -bool singletonsTerminated = false; -Steinberg::Base::Thread::FLock* singletonsLock; - -bool isTerminated () +namespace Singleton { - return singletonsTerminated; -} + using ObjectVector = std::vector; + ObjectVector* singletonInstances = nullptr; + bool singletonsTerminated = false; + Steinberg::Base::Thread::FLock* singletonsLock; -void lockRegister () -{ - if (!singletonsLock) // assume first call not from multiple threads - singletonsLock = NEW Steinberg::Base::Thread::FLock; - singletonsLock->lock (); -} + bool isTerminated () {return singletonsTerminated;} -void unlockRegister () -{ - singletonsLock->unlock (); -} + void lockRegister () + { + if (!singletonsLock) // assume first call not from multiple threads + singletonsLock = NEW Steinberg::Base::Thread::FLock; + singletonsLock->lock (); + } + void unlockRegister () + { + singletonsLock->unlock (); + } -void registerInstance (FObject** o) -{ - SMTG_ASSERT (singletonsTerminated == false) - if (singletonsTerminated == false) + void registerInstance (FObject** o) { - if (singletonInstances == nullptr) - singletonInstances = NEW std::vector; - singletonInstances->push_back (o); + SMTG_ASSERT (singletonsTerminated == false) + if (singletonsTerminated == false) + { + if (singletonInstances == nullptr) + singletonInstances = NEW std::vector; + singletonInstances->push_back (o); + } } -} -struct Deleter -{ - ~Deleter () + struct Deleter { - singletonsTerminated = true; - if (singletonInstances) + ~Deleter () { - for (Steinberg::FObject** obj : *singletonInstances) + singletonsTerminated = true; + if (singletonInstances) { - (*obj)->release (); - *obj = nullptr; - obj = nullptr; + for (ObjectVector::iterator it = singletonInstances->begin (), + end = singletonInstances->end (); + it != end; ++it) + { + FObject** obj = (*it); + (*obj)->release (); + *obj = nullptr; + obj = nullptr; + } + + delete singletonInstances; + singletonInstances = nullptr; } - - delete singletonInstances; - singletonInstances = nullptr; + delete singletonsLock; + singletonsLock = nullptr; } - delete singletonsLock; - singletonsLock = nullptr; - } -} deleter; + } deleter; } //------------------------------------------------------------------------ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fobject.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fobject.h index 4165a7b4..9bcf4435 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fobject.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fobject.h @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -45,8 +45,6 @@ #include "pluginterfaces/base/iupdatehandler.h" #include "base/source/fdebug.h" // use of NEW -#define SMTG_DEPENDENCY_COUNT DEVELOPMENT - namespace Steinberg { //---------------------------------- @@ -84,15 +82,10 @@ class FObject : public IDependent { public: //------------------------------------------------------------------------ - FObject () = default; ///< default constructor... - FObject (const FObject&) ///< overloaded constructor... - : refCount (1) -#if SMTG_DEPENDENCY_COUNT - , dependencyCount (0) -#endif - {} - FObject& operator= (const FObject&) { return *this; } ///< overloads operator "=" as the reference assignment - virtual ~FObject (); ///< destructor... + FObject () : refCount (1) {} ///< default constructor... + FObject (const FObject&) : refCount (1) {} ///< overloaded constructor... + virtual ~FObject () {} ///< destructor... + FObject& operator = (const FObject&) { return *this; } ///< overloads operator "=" as the reference assignment // OBJECT_METHODS static inline FClassID getFClassID () {return "FObject";} ///< return Class ID as an ASCII string (statically) @@ -131,10 +124,8 @@ class FObject : public IDependent //------------------------------------------------------------------------ protected: - int32 refCount = 1; ///< COM-model local reference count -#if SMTG_DEPENDENCY_COUNT - int16 dependencyCount = 0; -#endif + int32 refCount; ///< COM-model local reference count + static IUpdateHandler* gUpdateHandler; }; @@ -350,7 +341,7 @@ namespace Singleton { virtual Steinberg::FClassID isA () const SMTG_OVERRIDE {return className::getFClassID ();} \ virtual bool isA (Steinberg::FClassID s) const SMTG_OVERRIDE {return isTypeOf (s, false);} \ virtual bool isTypeOf (Steinberg::FClassID s, bool askBaseClass = true) const SMTG_OVERRIDE \ - { return (FObject::classIDsEqual (s, #className) ? true : (askBaseClass ? baseClass::isTypeOf (s, true) : false)); } + { return (classIDsEqual (s, #className) ? true : (askBaseClass ? baseClass::isTypeOf (s, true) : false)); } //------------------------------------------------------------------------ /** Delegate refcount functions to BaseClass. diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstreamer.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstreamer.cpp index 524956b9..b9af91e0 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstreamer.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstreamer.cpp @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -187,30 +187,6 @@ bool FStreamer::readChar16 (char16& c) return false; } -//------------------------------------------------------------------------ -bool FStreamer::writeInt8 (int8 c) -{ - return writeRaw ((void*)&c, sizeof (int8)) == sizeof (int8); -} - -//------------------------------------------------------------------------ -bool FStreamer::readInt8 (int8& c) -{ - return readRaw ((void*)&c, sizeof (int8)) == sizeof (int8); -} - -//------------------------------------------------------------------------ -bool FStreamer::writeInt8u (uint8 c) -{ - return writeRaw ((void*)&c, sizeof (uint8)) == sizeof (uint8); -} - -//------------------------------------------------------------------------ -bool FStreamer::readInt8u (uint8& c) -{ - return readRaw ((void*)&c, sizeof (uint8)) == sizeof (uint8); -} - // int16 ----------------------------------------------------------------- //------------------------------------------------------------------------ bool FStreamer::writeInt16 (int16 i) @@ -587,9 +563,6 @@ TSize FStreamer::writeString8 (const char8* ptr, bool terminate) //------------------------------------------------------------------------ TSize FStreamer::readString8 (char8* ptr, TSize size) { - if (size < 1 || ptr == nullptr) - return 0; - TSize i = 0; char8 c = 0; while (i < size) @@ -597,19 +570,18 @@ TSize FStreamer::readString8 (char8* ptr, TSize size) if (readRaw ((void*)&c, sizeof (char)) != sizeof (char)) break; ptr[i] = c; + i++; if (c == '\n' || c == '\0') break; - i++; } - // remove at end \n (LF) or \r\n (CR+LF) - if (c == '\n') - { - if (i > 0 && ptr[i - 1] == '\r') - i--; - } - ptr[i] = 0; + if (c == '\n' && ptr[i - 2] == '\r') + ptr[i - 2] = 0; + if (i < size) + ptr[i] = 0; + else + ptr[size - 1] = 0; - return i; + return strlen (ptr); } //------------------------------------------------------------------------ @@ -658,7 +630,7 @@ int32 FStreamer::readStringUtf8 (tchar* ptr, int32 nChars) break; } - char8* source = tmp.str8 (); + char8* source = tmp.int8Ptr (); uint32 codePage = kCP_Default; // for legacy take default page if no utf8 bom is present... if (tmp.getFillSize () > 2) { diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstreamer.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstreamer.h index 8b97bb6c..750e5e18 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstreamer.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstreamer.h @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -84,10 +84,10 @@ class FStreamer bool writeChar16 (char16 c); bool readChar16 (char16& c); - bool writeInt8 (int8 c); - bool readInt8 (int8& c); - bool writeInt8u (uint8 c); - bool readInt8u (uint8& c); + bool writeInt8 (int8 c){return writeChar8 (c);} + bool readInt8 (int8& c){return readChar8 (c);} + bool writeInt8u (uint8 c){return writeUChar8 (c);} + bool readInt8u (uint8& c){return readUChar8 (c);} ///@} /** @name read and write int16. */ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.cpp index 907baa73..7fae6b0c 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.cpp @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -45,15 +45,8 @@ #include #include #include -#include -#include -#include -#include #if SMTG_OS_WINDOWS -#ifndef NOMINMAX -#define NOMINMAX -#endif #include #ifdef _MSC_VER #pragma warning (disable : 4244) @@ -209,32 +202,34 @@ static bool fromCFStringRef (Steinberg::char8* dest, Steinberg::int32 destSize, #endif // SMTG_OS_MACOS #if SMTG_OS_WINDOWS -//----------------------------------------------------------------------------- -static inline int stricmp16 (const Steinberg::tchar* s1, const Steinberg::tchar* s2) -{ - return wcsicmp (Steinberg::wscast (s1), Steinberg::wscast (s2)); -} - -//----------------------------------------------------------------------------- -static inline int strnicmp16 (const Steinberg::tchar* s1, const Steinberg::tchar* s2, size_t l) -{ - return wcsnicmp (Steinberg::wscast (s1), Steinberg::wscast (s2), l); -} - -//----------------------------------------------------------------------------- -static inline int vsnwprintf (Steinberg::char16* buffer, size_t bufferSize, - const Steinberg::char16* format, va_list args) -{ - return _vsnwprintf (Steinberg::wscast (buffer), bufferSize, Steinberg::wscast (format), args); -} - -//----------------------------------------------------------------------------- -static inline Steinberg::int32 sprintf16 (Steinberg::char16* str, const Steinberg::char16* format, ...) -{ - va_list marker; - va_start (marker, format); - return vsnwprintf (str, -1, format, marker); -} +#define stricmp16 wcsicmp +#define strnicmp16 wcsnicmp +#define strrchr16 wcsrchr +#define sprintf16 swprintf +#define snprintf16 snwprintf +#define vsnprintf16 vsnwprintf +#define vsprintf16 wvsprintf +#define vfprintf16 vfwprintf +#define sscanf16 swscanf +#define toupper16 towupper +#define tolower16 towlower +#define isupper16 iswupper +#define islower16 iswlower +#define isspace16 iswspace +#define isalpha16 iswalpha +#define isdigit16 iswdigit +#define isalnum16 iswalnum + +#define stricmp _stricmp +#define strnicmp _strnicmp +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define snwprintf _snwprintf +#define vsnwprintf _vsnwprintf + +#define wtoi _wtoi +#define wtol _wtol +#define wtof _wtof #elif SMTG_OS_LINUX #include @@ -293,7 +288,7 @@ static inline int strnicmp16 (const Steinberg::char16* s1, const Steinberg::char //----------------------------------------------------------------------------- static inline int sprintf16 (Steinberg::char16* wcs, const Steinberg::char16* format, ...) { - assert (false && "DEPRECATED No Linux implementation"); + assert(false && "DEPRECATED No Linux implementation"); return 0; } @@ -316,7 +311,7 @@ static inline int vsnwprintf (Steinberg::char16* wcs, size_t maxlen, //----------------------------------------------------------------------------- static inline Steinberg::char16* strrchr16 (const Steinberg::char16* str, Steinberg::char16 c) { - assert (false && "DEPRECATED No Linux implementation"); + assert(false && "DEPRECATED No Linux implementation"); return nullptr; } @@ -538,9 +533,6 @@ bool ConstString::testChar16 (uint32 index, char16 c) const //----------------------------------------------------------------------------- bool ConstString::extract (String& result, uint32 idx, int32 n) const { - // AddressSanitizer : when extracting part of "this" on itself, it can lead to heap-use-after-free. - SMTG_ASSERT (this != static_cast (&result)) - if (len == 0|| idx >= len) return false; @@ -667,7 +659,7 @@ int32 ConstString::compare (const ConstString& str, int32 n, CompareMode mode) c return 0; return 1; } - if (isEmpty ()) + else if (isEmpty ()) return -1; if (!isWide && !str.isWide) @@ -676,23 +668,33 @@ int32 ConstString::compare (const ConstString& str, int32 n, CompareMode mode) c { if (isCaseSensitive (mode)) return strcmp (*this, str); - return stricmp (*this, str); + else + return stricmp (*this, str); + } + else + { + if (isCaseSensitive (mode)) + return strncmp (*this, str, n); + else + return strnicmp (*this, str, n); } - if (isCaseSensitive (mode)) - return strncmp (*this, str, n); - return strnicmp (*this, str, n); } - if (isWide && str.isWide) + else if (isWide && str.isWide) { if (n < 0) { if (isCaseSensitive (mode)) return strcmp16 (*this, str); - return stricmp16 (*this, str); + else + return stricmp16 (*this, str); + } + else + { + if (isCaseSensitive (mode)) + return strncmp16 (*this, str, n); + else + return strnicmp16 (*this, str, n); } - if (isCaseSensitive (mode)) - return strncmp16 (*this, str, n); - return strnicmp16 (*this, str, n); } return compareAt (0, str, n, mode); } @@ -715,7 +717,7 @@ int32 ConstString::compareAt (uint32 index, const ConstString& str, int32 n, Com return 0; return 1; } - if (isEmpty ()) + else if (isEmpty ()) return -1; if (!isWide && !str.isWide) @@ -736,13 +738,18 @@ int32 ConstString::compareAt (uint32 index, const ConstString& str, int32 n, Com { if (isCaseSensitive (mode)) return strcmp (toCompare, str); - return stricmp (toCompare, str); + else + return stricmp (toCompare, str); + } + else + { + if (isCaseSensitive (mode)) + return strncmp (toCompare, str, n); + else + return strnicmp (toCompare, str, n); } - if (isCaseSensitive (mode)) - return strncmp (toCompare, str, n); - return strnicmp (toCompare, str, n); } - if (isWide && str.isWide) + else if (isWide && str.isWide) { char16* toCompare = buffer16; if (index > 0) @@ -760,25 +767,34 @@ int32 ConstString::compareAt (uint32 index, const ConstString& str, int32 n, Com { if (isCaseSensitive (mode)) return strcmp16 (toCompare, str.text16 ()); - return stricmp16 (toCompare, str.text16 ()); + else + return stricmp16 (toCompare, str.text16 ()); + } + else + { + if (isCaseSensitive (mode)) + return strncmp16 (toCompare, str.text16 (), n); + else + return strnicmp16 (toCompare, str.text16 (), n); } - if (isCaseSensitive (mode)) - return strncmp16 (toCompare, str.text16 (), n); - return strnicmp16 (toCompare, str.text16 (), n); } - - if (isWide) + else { - String tmp (str.text8 ()); - if (tmp.toWideString () == false) - return -1; - return compareAt (index, tmp, n, mode); + if (isWide) + { + String tmp (str.text8 ()); + if (tmp.toWideString () == false) + return -1; + return compareAt (index, tmp, n, mode); + } + else + { + String tmp (text8 ()); + if (tmp.toWideString () == false) + return 1; + return tmp.compareAt (index, str, n, mode); + } } - - String tmp (text8 ()); - if (tmp.toWideString () == false) - return 1; - return tmp.compareAt (index, str, n, mode); } //------------------------------------------------------------------------ @@ -790,23 +806,28 @@ Steinberg::int32 ConstString::naturalCompare (const ConstString& str, CompareMod return 0; return 1; } - if (isEmpty ()) + else if (isEmpty ()) return -1; if (!isWide && !str.isWide) return strnatcmp8 (buffer8, str.text8 (), isCaseSensitive (mode)); - if (isWide && str.isWide) + else if (isWide && str.isWide) return strnatcmp16 (buffer16, str.text16 (), isCaseSensitive (mode)); - - if (isWide) + else { - String tmp (str.text8 ()); - tmp.toWideString (); - return strnatcmp16 (buffer16, tmp.text16 (), isCaseSensitive (mode)); + if (isWide) + { + String tmp (str.text8 ()); + tmp.toWideString (); + return strnatcmp16 (buffer16, tmp.text16 (), isCaseSensitive (mode)); + } + else + { + String tmp (text8 ()); + tmp.toWideString (); + return strnatcmp16 (tmp.text16 (), str.text16 (), isCaseSensitive (mode)); + } } - String tmp (text8 ()); - tmp.toWideString (); - return strnatcmp16 (tmp.text16 (), str.text16 (), isCaseSensitive (mode)); } //----------------------------------------------------------------------------- @@ -816,7 +837,7 @@ bool ConstString::startsWith (const ConstString& str, CompareMode mode /*= kCase { return isEmpty (); } - if (isEmpty ()) + else if (isEmpty ()) { return false; } @@ -830,13 +851,13 @@ bool ConstString::startsWith (const ConstString& str, CompareMode mode /*= kCase return strncmp (buffer8, str.buffer8, str.length ()) == 0; return strnicmp (buffer8, str.buffer8, str.length ()) == 0; } - if (isWide && str.isWide) + else if (isWide && str.isWide) { if (isCaseSensitive (mode)) return strncmp16 (buffer16, str.buffer16, str.length ()) == 0; return strnicmp16 (buffer16, str.buffer16, str.length ()) == 0; } - if (isWide) + else if (isWide) { String tmp (str.text8 ()); tmp.toWideString (); @@ -846,13 +867,16 @@ bool ConstString::startsWith (const ConstString& str, CompareMode mode /*= kCase return strncmp16 (buffer16, tmp.buffer16, tmp.length ()) == 0; return strnicmp16 (buffer16, tmp.buffer16, tmp.length ()) == 0; } - String tmp (text8 ()); - tmp.toWideString (); - if (str.length () > tmp.length ()) - return false; - if (isCaseSensitive (mode)) - return strncmp16 (tmp.buffer16, str.buffer16, str.length ()) == 0; - return strnicmp16 (tmp.buffer16, str.buffer16, str.length ()) == 0; + else + { + String tmp (text8 ()); + tmp.toWideString (); + if (str.length () > tmp.length ()) + return false; + if (isCaseSensitive (mode)) + return strncmp16 (tmp.buffer16, str.buffer16, str.length ()) == 0; + return strnicmp16 (tmp.buffer16, str.buffer16, str.length ()) == 0; + } } //----------------------------------------------------------------------------- @@ -862,7 +886,7 @@ bool ConstString::endsWith (const ConstString& str, CompareMode mode /*= kCaseSe { return isEmpty (); } - if (isEmpty ()) + else if (isEmpty ()) { return false; } @@ -876,13 +900,13 @@ bool ConstString::endsWith (const ConstString& str, CompareMode mode /*= kCaseSe return strncmp (buffer8 + (length () - str.length ()), str.buffer8, str.length ()) == 0; return strnicmp (buffer8 + (length () - str.length ()), str.buffer8, str.length ()) == 0; } - if (isWide && str.isWide) + else if (isWide && str.isWide) { if (isCaseSensitive (mode)) return strncmp16 (buffer16 + (length () - str.length ()), str.buffer16, str.length ()) == 0; return strnicmp16 (buffer16 + (length () - str.length ()), str.buffer16, str.length ()) == 0; } - if (isWide) + else if (isWide) { String tmp (str.text8 ()); tmp.toWideString (); @@ -892,13 +916,16 @@ bool ConstString::endsWith (const ConstString& str, CompareMode mode /*= kCaseSe return strncmp16 (buffer16 + (length () - tmp.length ()), tmp.buffer16, tmp.length ()) == 0; return strnicmp16 (buffer16 + (length () - tmp.length ()), tmp.buffer16, tmp.length ()) == 0; } - String tmp (text8 ()); - tmp.toWideString (); - if (str.length () > tmp.length ()) - return false; - if (isCaseSensitive (mode)) - return strncmp16 (tmp.buffer16 + (tmp.length () - str.length ()), str.buffer16, str.length ()) == 0; - return strnicmp16 (tmp.buffer16 + (tmp.length () - str.length ()), str.buffer16, str.length ()) == 0; + else + { + String tmp (text8 ()); + tmp.toWideString (); + if (str.length () > tmp.length ()) + return false; + if (isCaseSensitive (mode)) + return strncmp16 (tmp.buffer16 + (tmp.length () - str.length ()), str.buffer16, str.length ()) == 0; + return strnicmp16 (tmp.buffer16 + (tmp.length () - str.length ()), str.buffer16, str.length ()) == 0; + } } //----------------------------------------------------------------------------- @@ -941,7 +968,7 @@ int32 ConstString::findNext (int32 startIndex, const ConstString& str, int32 n, } return -1; } - if (!isWide && !str.isWide) + else if (!isWide && !str.isWide) { uint32 stringLength = str.length (); n = n < 0 ? stringLength : Min (n, stringLength); @@ -1172,7 +1199,7 @@ int32 ConstString::findPrev (int32 startIndex, const ConstString& str, int32 n, } return -1; } - if (!isWide && !str.isWide) + else if (!isWide && !str.isWide) { uint32 stringLength = str.length (); n = n < 0 ? stringLength : Min (n, stringLength); @@ -1278,11 +1305,13 @@ int32 ConstString::getFirstDifferent (const ConstString& str, CompareMode mode) return -1; return getFirstDifferent (tmp, mode); } - - String tmp (text8 ()); - if (tmp.toWideString () == false) - return -1; - return tmp.getFirstDifferent (str, mode); + else + { + String tmp (text8 ()); + if (tmp.toWideString () == false) + return -1; + return tmp.getFirstDifferent (str, mode); + } } uint32 len1 = len; @@ -1338,7 +1367,8 @@ bool ConstString::scanInt64 (int64& value, uint32 offset, bool scanToEnd) const if (isWide) return scanInt64_16 (buffer16 + offset, value, scanToEnd); - return scanInt64_8 (buffer8 + offset, value, scanToEnd); + else + return scanInt64_8 (buffer8 + offset, value, scanToEnd); } //----------------------------------------------------------------------------- @@ -1349,7 +1379,8 @@ bool ConstString::scanUInt64 (uint64& value, uint32 offset, bool scanToEnd) cons if (isWide) return scanUInt64_16 (buffer16 + offset, value, scanToEnd); - return scanUInt64_8 (buffer8 + offset, value, scanToEnd); + else + return scanUInt64_8 (buffer8 + offset, value, scanToEnd); } //----------------------------------------------------------------------------- @@ -1360,7 +1391,8 @@ bool ConstString::scanHex (uint8& value, uint32 offset, bool scanToEnd) const if (isWide) return scanHex_16 (buffer16 + offset, value, scanToEnd); - return scanHex_8 (buffer8 + offset, value, scanToEnd); + else + return scanHex_8 (buffer8 + offset, value, scanToEnd); } //----------------------------------------------------------------------------- @@ -1371,7 +1403,8 @@ bool ConstString::scanInt32 (int32& value, uint32 offset, bool scanToEnd) const if (isWide) return scanInt32_16 (buffer16 + offset, value, scanToEnd); - return scanInt32_8 (buffer8 + offset, value, scanToEnd); + else + return scanInt32_8 (buffer8 + offset, value, scanToEnd); } //----------------------------------------------------------------------------- @@ -1382,7 +1415,8 @@ bool ConstString::scanUInt32 (uint32& value, uint32 offset, bool scanToEnd) cons if (isWide) return scanUInt32_16 (buffer16 + offset, value, scanToEnd); - return scanUInt32_8 (buffer8 + offset, value, scanToEnd); + else + return scanUInt32_8 (buffer8 + offset, value, scanToEnd); } //----------------------------------------------------------------------------- @@ -1392,7 +1426,7 @@ bool ConstString::scanInt64_8 (const char8* text, int64& value, bool scanToEnd) { if (sscanf (text, "%" FORMAT_INT64A, &value) == 1) return true; - if (scanToEnd == false) + else if (scanToEnd == false) return false; text++; } @@ -1418,7 +1452,7 @@ bool ConstString::scanUInt64_8 (const char8* text, uint64& value, bool scanToEnd { if (sscanf (text, "%" FORMAT_UINT64A, &value) == 1) return true; - if (scanToEnd == false) + else if (scanToEnd == false) return false; text++; } @@ -1468,7 +1502,7 @@ bool ConstString::scanHex_8 (const char8* text, uint8& value, bool scanToEnd) value = (uint8)v; return true; } - if (scanToEnd == false) + else if (scanToEnd == false) return false; text++; } @@ -1523,7 +1557,7 @@ bool ConstString::scanFloat (double& value, uint32 offset, bool scanToEnd) const { if (sscanf (txt, "%lf", &value) == 1) return true; - if (scanToEnd == false) + else if (scanToEnd == false) return false; txt++; } @@ -1551,7 +1585,7 @@ char16 ConstString::toLower (char16 c) } return c; #elif SMTG_OS_LINUX - assert (false && "DEPRECATED No Linux implementation"); + assert(false && "DEPRECATED No Linux implementation"); return c; #else return towlower (c); @@ -1579,7 +1613,7 @@ char16 ConstString::toUpper (char16 c) } return c; #elif SMTG_OS_LINUX - assert (false && "DEPRECATED No Linux implementation"); + assert(false && "DEPRECATED No Linux implementation"); return c; #else return towupper (c); @@ -1725,7 +1759,8 @@ bool ConstString::isDigit (uint32 index) const if (isWide) return ConstString::isCharDigit (buffer16[index]); - return ConstString::isCharDigit (buffer8[index]); + else + return ConstString::isCharDigit (buffer8[index]); } //----------------------------------------------------------------------------- @@ -1832,7 +1867,7 @@ int32 ConstString::multiByteToWideString (char16* dest, const char8* source, int } int32 result = 0; #if SMTG_OS_WINDOWS - result = MultiByteToWideChar (sourceCodePage, MB_ERR_INVALID_CHARS, source, -1, wscast (dest), charCount); + result = MultiByteToWideChar (sourceCodePage, MB_ERR_INVALID_CHARS, source, -1, dest, charCount); #endif #if SMTG_OS_MACOS @@ -1876,7 +1911,7 @@ int32 ConstString::multiByteToWideString (char16* dest, const char8* source, int } else { - assert (false && "DEPRECATED No Linux implementation"); + assert(false && "DEPRECATED No Linux implementation"); } #endif @@ -1889,7 +1924,7 @@ int32 ConstString::multiByteToWideString (char16* dest, const char8* source, int int32 ConstString::wideStringToMultiByte (char8* dest, const char16* wideString, int32 charCount, uint32 destCodePage) { #if SMTG_OS_WINDOWS - return WideCharToMultiByte (destCodePage, 0, wscast (wideString), -1, dest, charCount, nullptr, nullptr); + return WideCharToMultiByte (destCodePage, 0, wideString, -1, dest, charCount, nullptr, nullptr); #elif SMTG_OS_MACOS int32 result = 0; @@ -1956,12 +1991,13 @@ int32 ConstString::wideStringToMultiByte (char8* dest, const char16* wideString, } else { - assert (false && "DEPRECATED No Linux implementation"); + assert(false && "DEPRECATED No Linux implementation"); } return result; #else - assert (false && "DEPRECATED No Linux implementation"); +#warning DEPRECATED No Linux implementation + assert(false && "DEPRECATED No Linux implementation"); return 0; #endif @@ -1977,7 +2013,7 @@ bool ConstString::isNormalized (UnicodeNormalization n) #ifdef UNICODE if (n != kUnicodeNormC) return false; - uint32 normCharCount = static_cast (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, nullptr, 0)); + uint32 normCharCount = static_cast (FoldString (MAP_PRECOMPOSED, buffer16, len, nullptr, 0)); return (normCharCount == len); #else return false; @@ -2007,27 +2043,11 @@ String::String () //----------------------------------------------------------------------------- String::String (const char8* str, MBCodePage codePage, int32 n, bool isTerminated) { - isWide = false; + isWide = 0; if (str) { - if (isTerminated && n >= 0 && str[n] != 0) - { - // isTerminated is not always set correctly - isTerminated = false; - } - - if (!isTerminated) - { - assign (str, n, isTerminated); - toWideString (codePage); - } - else - { - if (n < 0) - n = static_cast (strlen (str)); - if (n > 0) - _toWideString (str, n, codePage); - } + assign (str, n, isTerminated); + toWideString (codePage); } } @@ -2119,33 +2139,22 @@ void String::updateLength () //----------------------------------------------------------------------------- bool String::toWideString (uint32 sourceCodePage) -{ - if (!isWide && buffer8 && len > 0) - return _toWideString (buffer8, len, sourceCodePage); - isWide = true; - return true; -} - -//----------------------------------------------------------------------------- -bool String::_toWideString (const char8* src, int32 length, uint32 sourceCodePage) { if (!isWide) { - if (src && length > 0) + if (buffer8 && len > 0) { - int32 bytesNeeded = multiByteToWideString (nullptr, src, 0, sourceCodePage) * sizeof (char16); + int32 bytesNeeded = multiByteToWideString (nullptr, buffer8, 0, sourceCodePage) * sizeof (char16); if (bytesNeeded) { bytesNeeded += sizeof (char16); - char16* newStr = (char16*)malloc (bytesNeeded); - if (multiByteToWideString (newStr, src, length + 1, sourceCodePage) < 0) + char16* newStr = (char16*) malloc (bytesNeeded); + if (multiByteToWideString (newStr, buffer8, len + 1, sourceCodePage) <= 0) { free (newStr); return false; } - if (buffer8) - free (buffer8); - + free (buffer8); buffer16 = newStr; isWide = true; updateLength (); @@ -2245,8 +2254,8 @@ bool String::toMultiByte (uint32 destCodePage) //----------------------------------------------------------------------------- void String::fromUTF8 (const char8* utf8String) { - resize (0, false); - _toWideString (utf8String, static_cast (strlen (utf8String)), kCP_Utf8); + assign (utf8String); + toWideString (kCP_Utf8); } //----------------------------------------------------------------------------- @@ -2263,12 +2272,12 @@ bool String::normalize (UnicodeNormalization n) if (n != kUnicodeNormC) return false; - uint32 normCharCount = static_cast (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, nullptr, 0)); + uint32 normCharCount = static_cast (FoldString (MAP_PRECOMPOSED, buffer16, len, nullptr, 0)); if (normCharCount == len) return true; char16* newString = (char16*)malloc ((normCharCount + 1) * sizeof (char16)); - uint32 converterCount = static_cast (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, wscast (newString), normCharCount + 1)); + uint32 converterCount = static_cast (FoldString (MAP_PRECOMPOSED, buffer16, len, newString, normCharCount + 1)); if (converterCount != normCharCount) { free (newString); @@ -2401,10 +2410,12 @@ bool String::setChar8 (uint32 index, char8 c) len = index; return true; } - - if (resize (index + 1, isWide, true) == false) - return false; - len = index + 1; + else + { + if (resize (index + 1, isWide, true) == false) + return false; + len = index + 1; + } } if (index < len && buffer) @@ -2451,9 +2462,12 @@ bool String::setChar16 (uint32 index, char16 c) len = index; return true; } - if (resize (index + 1, isWide, true) == false) - return false; - len = index + 1; + else + { + if (resize (index + 1, isWide, true) == false) + return false; + len = index + 1; + } } if (index < len && buffer) @@ -2487,7 +2501,8 @@ String& String::assign (const ConstString& str, int32 n) { if (str.isWideString ()) return assign (str.text16 (), n < 0 ? str.length () : n); - return assign (str.text8 (), n < 0 ? str.length () : n); + else + return assign (str.text8 (), n < 0 ? str.length () : n); } //----------------------------------------------------------------------------- @@ -2583,7 +2598,8 @@ String& String::append (const ConstString& str, int32 n) { if (str.isWideString ()) return append (str.text16 (), n); - return append (str.text8 (), n); + else + return append (str.text8 (), n); } //----------------------------------------------------------------------------- @@ -2667,7 +2683,7 @@ String& String::append (const char8 c, int32 n) { return append (str, 1); } - if (n > 1) + else if (n > 1) { if (isWide) { @@ -2701,7 +2717,7 @@ String& String::append (const char16 c, int32 n) char16 str[] = {c, 0}; return append (str, 1); } - if (n > 1) + else if (n > 1) { if (!isWide) { @@ -2730,7 +2746,8 @@ String& String::insertAt (uint32 idx, const ConstString& str, int32 n) { if (str.isWideString ()) return insertAt (idx, str.text16 (), n); - return insertAt (idx, str.text8 (), n); + else + return insertAt (idx, str.text8 (), n); } //----------------------------------------------------------------------------- @@ -2808,7 +2825,8 @@ String& String::replace (uint32 idx, int32 n1, const ConstString& str, int32 n2) { if (str.isWideString ()) return replace (idx, n1, str.text16 (), n2); - return replace (idx, n1, str.text8 (), n2); + else + return replace (idx, n1, str.text8 (), n2); } // "replace" replaces n1 number of characters at the specified index with @@ -3351,44 +3369,55 @@ String& String::printInt64 (int64 value) } //----------------------------------------------------------------------------- -String& String::printFloat (double value, uint32 maxPrecision) +String& String::printFloat (double value) { - static constexpr auto kMaxAfterCommaResolution = 16; - // escape point for integer values, avoid unnecessary complexity later on - const bool withinInt64Boundaries = value <= std::numeric_limits::max () && value >= std::numeric_limits::lowest (); - if (withinInt64Boundaries && (maxPrecision == 0 || std::round (value) == value)) - return printInt64 (value); - - const auto absValue = std::abs (value); - const uint32 valueExponent = absValue >= 1 ? std::log10 (absValue) : -std::log10 (absValue) + 1; - - maxPrecision = std::min (kMaxAfterCommaResolution - valueExponent, maxPrecision); - if (isWide) - printf (STR ("%s%dlf"), STR ("%."), maxPrecision); - else - printf ("%s%dlf", "%.", maxPrecision); + { + char16 string[kPrintfBufferSize]; + sprintf16 (string, STR16 ("%lf"), value); - if (isWide) - printf (text16 (), value); + char16* pointPtr = strrchr16 (string, STR ('.')); + if (pointPtr) + { + pointPtr++; // keep 1st digit after point + int32 index = strlen16 (string) - 1; + char16 zero = STR16 ('0'); + while (pointPtr < (string + index)) + { + if (string[index] == zero) + { + string[index] = 0; + index--; + } + else + break; + } + } + return assign (string); + } else - printf (text8 (), value); - - // trim trail zeros - for (int32 i = length () - 1; i >= 0; i--) { - if (isWide && testChar16 (i, '0') || testChar8 (i, '0')) - remove (i); - else if (isWide && testChar16(i,'.') || testChar8(i, '.')) + char8 string[kPrintfBufferSize]; + sprintf (string, "%lf", value); + + char8* pointPtr = strrchr (string, '.'); + if (pointPtr) { - remove(i); - break; + pointPtr++; // keep 1st digit after point + int32 index = (int32) (strlen (string) - 1); + while (pointPtr < (string + index)) + { + if (string[index] == '0') + { + string[index] = 0; + index--; + } + else + break; + } } - else - break; + return assign (string); } - - return *this; } //----------------------------------------------------------------------------- @@ -3432,19 +3461,17 @@ bool String::incrementTrailingNumber (uint32 width, tchar separator, uint32 minN } else { - static constexpr auto kFormatSize = 64u; - static constexpr auto kTrailSize = 64u; - char format[kFormatSize]; - char trail[kTrailSize]; + char format[64]; + char trail[128]; if (separator && isEmpty () == false) { - snprintf (format, kFormatSize, "%%c%%0%uu", width); - snprintf (trail, kTrailSize, format, separator, (uint32) number); + sprintf (format, "%%c%%0%uu", width); + sprintf (trail, format, separator, (uint32) number); } else { - snprintf (format, kFormatSize, "%%0%uu", width); - snprintf (trail, kTrailSize, format, (uint32) number); + sprintf (format, "%%0%uu", width); + sprintf (trail, format, (uint32) number); } append (trail); } @@ -3696,9 +3723,11 @@ unsigned char* String::toPascalString (unsigned char* buf) } return buf; } - - *buf = 0; - return buf; + else + { + *buf = 0; + return buf; + } } //----------------------------------------------------------------------------- @@ -3797,7 +3826,7 @@ void* ConstString::toCFStringRef (uint32 encoding, bool mutableCFString) const return (void*)CFStringCreateWithCString (kCFAllocator, "", encoding); } } - return nullptr; + return 0; } #endif @@ -3831,9 +3860,9 @@ template int32 tstrnatcmp (const T* s1, const T* s2, bool caseSensitiv { if (s1 == nullptr && s2 == nullptr) return 0; - if (s1 == nullptr) + else if (s1 == nullptr) return -1; - if (s2 == nullptr) + else if (s2 == nullptr) return 1; while (*s1 && *s2) @@ -3894,11 +3923,12 @@ template int32 tstrnatcmp (const T* s1, const T* s2, bool caseSensitiv if (*s1 == 0 && *s2 == 0) return 0; - if (*s1 == 0) + else if (*s1 == 0) return -1; - if (*s2 == 0) + else if (*s2 == 0) return 1; - return (int32)(*s1 - *s2); + else + return (int32)(*s1 - *s2); } //------------------------------------------------------------------------ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.h index 7fc758aa..80ff82ac 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/fstring.h @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -278,11 +278,6 @@ class ConstString bool isNormalized (UnicodeNormalization = kUnicodeNormC); ///< On PC only kUnicodeNormC is working -#if SMTG_OS_WINDOWS - ConstString (const wchar_t* str, int32 length = -1) : ConstString (wscast (str), length) {} - operator const wchar_t* () const { return wscast (text16 ());} -#endif - #if SMTG_OS_MACOS virtual void* toCFStringRef (uint32 encoding = 0xFFFF, bool mutableCFString = false) const; ///< CFString conversion #endif @@ -322,7 +317,7 @@ class String : public ConstString String (const ConstString& str, int32 n = -1); ///< assign n characters of str (-1: all) String (const FVariant& var); ///< assign from FVariant String (IString* str); ///< assign from IString - ~String () SMTG_OVERRIDE; + ~String (); #if SMTG_CPP11_STDLIBSUPPORT String (String&& str); @@ -421,15 +416,7 @@ class String : public ConstString // numbers----------------------------------------------------------------- String& printInt64 (int64 value); - - /** - * @brief print a float into a string, trailing zeros will be trimmed - * @param value the floating value to be printed - * @param maxPrecision (optional) the max precision allowed for this, num of significant digits after the comma - * For instance printFloat (1.234, 2) => 1.23 - * @return the resulting string. - */ - String& printFloat (double value, uint32 maxPrecision = 6); + String& printFloat (double value); /** Increment the trailing number if present else start with minNumber, width specifies the string width format (width 2 for number 3 is 03), applyOnlyFormat set to true will only format the string to the given width without incrementing the founded trailing number */ bool incrementTrailingNumber (uint32 width = 2, tchar separator = STR (' '), uint32 minNumber = 1, bool applyOnlyFormat = false); @@ -461,11 +448,6 @@ class String : public ConstString void fromUTF8 (const char8* utf8String); ///< Assigns from UTF8 string bool normalize (UnicodeNormalization = kUnicodeNormC); ///< On PC only kUnicodeNormC is working -#if SMTG_OS_WINDOWS - String (const wchar_t* str, int32 length = -1, bool isTerminated = true) : String (wscast (str), length, isTerminated) {} - String& operator= (const wchar_t* str) {return String::operator= (wscast (str)); } -#endif - #if SMTG_OS_MACOS virtual bool fromCFStringRef (const void*, uint32 encoding = 0xFFFF); ///< CFString conversion #endif @@ -476,7 +458,6 @@ class String : public ConstString bool resize (uint32 newSize, bool wide, bool fill = false); private: - bool _toWideString (const char8* src, int32 length, uint32 sourceCodePage = kCP_Default); void tryFreeBuffer (); bool checkToMultiByte (uint32 destCodePage = kCP_Default) const; // to remove debug code from inline - const_cast inside!!! }; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/updatehandler.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/updatehandler.cpp index f4ac80cc..9213ebc9 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/updatehandler.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/updatehandler.cpp @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -56,6 +56,7 @@ using Steinberg::Base::Thread::FGuard; namespace Steinberg { DEF_CLASS_IID (IUpdateManager) +bool UpdateHandler::lockUpdates = false; namespace Update { const uint32 kHashSize = (1 << 8); // must be power of 2 (16 bytes * 256 == 4096) @@ -242,17 +243,10 @@ tresult PLUGIN_API UpdateHandler::addDependent (FUnknown* u, IDependent* _depend return kResultTrue; } -//------------------------------------------------------------------------ -tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent) -{ - size_t eraseCount; - return removeDependent (u, dependent, eraseCount); -} //------------------------------------------------------------------------ -tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent, size_t& eraseCount) +tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent) { - eraseCount = 0; IPtr unknown = Update::getUnknownBase (u); if (unknown == nullptr && dependent == nullptr) return kResultFalse; @@ -293,16 +287,13 @@ tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* depe if ((*iterList) == dependent) #endif { - eraseCount = list.size (); if (list.size () == 1u) { listIsEmpty = true; break; } else - { iterList = list.erase (iterList); - } } else { @@ -331,11 +322,11 @@ tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* depe { if (dependent == nullptr) // Remove all dependents of object { - eraseCount = iterList->second.size (); map.erase (iterList); } else // Remove one dependent { + int32 eraseCount = 0; Update::DependentList& dependentlist = (*iterList).second; Update::DependentListIter iterDependentlist = dependentlist.begin (); while (iterDependentlist != dependentlist.end ()) @@ -372,6 +363,8 @@ tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* depe //------------------------------------------------------------------------ tresult UpdateHandler::doTriggerUpdates (FUnknown* u, int32 message, bool suppressUpdateDone) { + if (lockUpdates) + return kResultFalse; IPtr unknown = Update::getUnknownBase (u); if (!unknown) return kResultFalse; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/updatehandler.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/updatehandler.h index e85a9bbc..538022e6 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/updatehandler.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/source/updatehandler.h @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -78,22 +78,18 @@ class UpdateHandler : public FObject, public IUpdateHandler, public IUpdateManag public: //------------------------------------------------------------------------------ UpdateHandler (); - ~UpdateHandler () SMTG_OVERRIDE; + ~UpdateHandler (); using FObject::addDependent; using FObject::removeDependent; using FObject::deferUpdate; // IUpdateHandler -//private: - friend class FObject; /** register \param dependent to get messages from \param object */ tresult PLUGIN_API addDependent (FUnknown* object, IDependent* dependent) SMTG_OVERRIDE; /** unregister \param dependent to get no messages from \param object */ - tresult PLUGIN_API removeDependent (FUnknown* object, IDependent* dependent, size_t& earseCount); tresult PLUGIN_API removeDependent (FUnknown* object, IDependent* dependent) SMTG_OVERRIDE; -public: /** send \param message to all dependents of \param object immediately */ tresult PLUGIN_API triggerUpdates (FUnknown* object, int32 message) SMTG_OVERRIDE; /** send \param message to all dependents of \param object when idle */ @@ -107,24 +103,15 @@ class UpdateHandler : public FObject, public IUpdateHandler, public IUpdateManag /// @cond ignore // obsolete functions kept for compatibility - void checkUpdates (FObject* object = nullptr) - { - triggerDeferedUpdates (object ? object->unknownCast () : nullptr); - } - void flushUpdates (FObject* object) - { - if (object) - cancelUpdates (object->unknownCast ()); - } + void checkUpdates (FObject* object = nullptr) { triggerDeferedUpdates (object->unknownCast ()); } + void flushUpdates (FObject* object) { cancelUpdates (object->unknownCast ()); } void deferUpdate (FObject* object, int32 message) { - if (object) - deferUpdates (object->unknownCast (), message); + deferUpdates (object->unknownCast (), message); } void signalChange (FObject* object, int32 message, bool suppressUpdateDone = false) { - if (object) - doTriggerUpdates (object->unknownCast (), message, suppressUpdateDone); + doTriggerUpdates (object->unknownCast (), message, suppressUpdateDone); } #if DEVELOPMENT bool checkDeferred (FUnknown* object); @@ -143,6 +130,8 @@ class UpdateHandler : public FObject, public IUpdateHandler, public IUpdateManag Steinberg::Base::Thread::FLock lock; Update::Table* table = nullptr; + friend struct LockUpdateDependencies; + static bool lockUpdates; }; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/thread/include/flock.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/thread/include/flock.h index 2ef35ba8..bc452502 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/thread/include/flock.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/thread/include/flock.h @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -99,7 +99,7 @@ class FLock : public ILock FLock (const char8* name = "FLock"); /** Lock destructor. */ - ~FLock () SMTG_OVERRIDE; + ~FLock (); //-- ILock ----------------------------------------------------------- void lock () SMTG_OVERRIDE; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/thread/source/flock.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/thread/source/flock.cpp index e7c154ba..8135cb6c 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/thread/source/flock.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/base/thread/source/flock.cpp @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/helper.manifest b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/helper.manifest deleted file mode 100644 index f4be0f3a..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/helper.manifest +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - UTF-8 - - - - diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/LICENSE.txt b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/LICENSE.txt index 6daa072e..5d521d2a 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/LICENSE.txt +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/LICENSE.txt @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- This license applies only to files referencing this license, for other files of the Software Development Kit the respective embedded license text @@ -38,7 +38,4 @@ OF THE POSSIBILITY OF SUCH DAMAGE. b) General Public License (GPL) Version 3 Details of these licenses can be found at: www.gnu.org/licenses/gpl-3.0.html -Please refer to the Steinberg VST usage guidelines for the use of VST, VST logo and VST -compatible logos: -https://steinbergmedia.github.io/vst3_dev_portal/pages/VST+3+Licensing/Usage+guidelines.html //---------------------------------------------------------------------------------- diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/README.md b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/README.md index 8a864b60..b4f95807 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/README.md +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/README.md @@ -1,10 +1,10 @@ # Welcome to VST 3 SDK Interfaces -Here are located all **VST 3** interfaces definitions (including VST Component/Controller, UI, Test). +Here are located all VST interfaces definitions (including VST Component/Controller, UI, Test). ## License & Usage guidelines More details are found at [www.steinberg.net/sdklicenses_vst3](http://www.steinberg.net/sdklicenses_vst3) ---- -Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) +Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) \ No newline at end of file diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/conststringtable.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/conststringtable.cpp index 29e453ae..4891cefa 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/conststringtable.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/conststringtable.cpp @@ -44,7 +44,7 @@ const char16* ConstStringTable::getString (const char8* str) const } //---------------------------------------------------------------------------- -char16 ConstStringTable::getString (const char8 str) const +const char16 ConstStringTable::getString (const char8 str) const { std::map::iterator iter = charMap->find (str); if (iter != charMap->end ()) diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/conststringtable.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/conststringtable.h index f9450222..e19cae65 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/conststringtable.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/conststringtable.h @@ -32,7 +32,7 @@ class ConstStringTable /** Returns a char16 string of a ASCII string literal*/ const char16* getString (const char8* str) const; /** Returns a char16 character of a ASCII character */ - char16 getString (const char8 str) const; + const char16 getString (const char8 str) const; protected: ConstStringTable (); diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/falignpush.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/falignpush.h index 49eb5736..5ff11f22 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/falignpush.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/falignpush.h @@ -26,10 +26,6 @@ #elif defined __BORLANDC__ #pragma -a8 #elif SMTG_OS_WINDOWS - //! @brief warning C4103: alignment changed after including header, may be due to missing #pragma pack(pop) - #ifdef _MSC_VER - #pragma warning(disable : 4103) - #endif #pragma pack(push) #if SMTG_PLATFORM_64 #pragma pack(16) diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/fplatform.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/fplatform.h index 7b4a78d2..dea70d0f 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/fplatform.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/fplatform.h @@ -16,7 +16,6 @@ #pragma once -// values for BYTEORDER according to the used platform #define kLittleEndian 0 #define kBigEndian 1 @@ -39,39 +38,21 @@ //----------------------------------------------------------------------------- #if defined (_WIN32) //----------------------------------------------------------------------------- + // ARM32 AND ARM64 (WINDOWS) + #if (defined(_M_ARM64) || defined(_M_ARM)) + #define SMTG_OS_WINDOWS_ARM 1 + #endif + #define SMTG_OS_LINUX 0 #define SMTG_OS_MACOS 0 #define SMTG_OS_WINDOWS 1 #define SMTG_OS_IOS 0 #define SMTG_OS_OSX 0 - #if defined(_M_IX86) - #define SMTG_CPU_X86 1 - #else - #define SMTG_CPU_X86 0 - #endif - #if defined(_M_AMD64) - #define SMTG_CPU_X86_64 1 - #else - #define SMTG_CPU_X86_64 0 - #endif - #if defined(_M_ARM) - #define SMTG_CPU_ARM 1 - #else - #define SMTG_CPU_ARM 0 - #endif - #if defined(_M_ARM64) - #define SMTG_CPU_ARM_64 1 - #else - #define SMTG_CPU_ARM_64 0 - #endif - #if defined(_M_ARM64EC) - #define SMTG_CPU_ARM_64EC 1 - #else - #define SMTG_CPU_ARM_64EC 0 - #endif - - #define SMTG_OS_WINDOWS_ARM (SMTG_CPU_ARM_64EC || SMTG_CPU_ARM_64 || SMTG_CPU_ARM) + #define SMTG_CPU_X86 _M_IX86 + #define SMTG_CPU_X86_64 _M_AMD64 + #define SMTG_CPU_ARM (_M_ARM && !_M_ARM64) + #define SMTG_CPU_ARM_64 _M_ARM64 #define BYTEORDER kLittleEndian @@ -80,19 +61,18 @@ #define SMTG_PTHREADS 0 #define SMTG_EXPORT_SYMBOL __declspec (dllexport) - #define SMTG_HIDDEN_SYMBOL #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifdef _MSC_VER - #pragma warning (disable : 4244) //! @brief warning C4244: Conversion from 'type1' to 'type2', possible loss of data. - #pragma warning (disable : 4250) //! @brief warning C4250: Inheritance via dominance is allowed - #pragma warning (disable : 4996) //! @brief warning C4996: deprecated functions + #pragma warning (disable : 4244) // Conversion from 'type1' to 'type2', possible loss of data. + #pragma warning (disable : 4250) // Inheritance via dominance is allowed + #pragma warning (disable : 4996) // deprecated functions - #pragma warning (3 : 4189) //! @brief warning C4189: local variable is initialized but not referenced - #pragma warning (3 : 4238) //! @brief warning C4238: nonstandard extension used : class rvalue used as lvalue + #pragma warning (3 : 4189) // local variable is initialized but not referenced + #pragma warning (3 : 4238) // nonstandard extension used : class rvalue used as lvalue #endif #if defined (_WIN64) || defined (_M_ARM64) @@ -106,49 +86,26 @@ #endif #ifdef __cplusplus - #define SMTG_CPP11 (__cplusplus >= 201103L || _MSC_VER > 1600 || SMTG_INTEL_CXX11_MODE) + #define SMTG_CPP11 __cplusplus >= 201103L || _MSC_VER > 1600 || SMTG_INTEL_CXX11_MODE #define SMTG_CPP11_STDLIBSUPPORT SMTG_CPP11 - #define SMTG_CPP14 (__cplusplus >= 201402L || ((_MSC_FULL_VER >= 190024210L) && (_MSVC_LANG >= 201402L))) - #define SMTG_CPP17 (__cplusplus >= 201703L || ((_MSC_FULL_VER >= 190024210L) && (_MSVC_LANG >= 201703L))) - #define SMTG_CPP20 (__cplusplus >= 202002L) - #define SMTG_HAS_NOEXCEPT ((_MSC_FULL_VER >= 190023026L) || (SMTG_INTEL_CXX11_MODE && SMTG_INTEL_COMPILER >= 1300)) - #if ((_MSC_FULL_VER >= 190024210L) || (SMTG_INTEL_CXX11_MODE && SMTG_INTEL_COMPILER >= 1500) || (defined(__MINGW32__) && SMTG_CPP11)) - #define SMTG_HAS_CPP11_CONSTEXPR 1 - #else - #define SMTG_HAS_CPP11_CONSTEXPR 0 - #endif - #if (((_MSC_VER >= 1915L) && (_MSVC_LANG >= 201402L)) || (SMTG_INTEL_CXX11_MODE && SMTG_INTEL_COMPILER > 1700) || (defined(__MINGW32__) && SMTG_CPP14)) - #define SMTG_HAS_CPP14_CONSTEXPR 1 - #else - #define SMTG_HAS_CPP14_CONSTEXPR 0 - #endif - #endif //__cplusplus + #define SMTG_HAS_NOEXCEPT _MSC_VER >= 1900 || (SMTG_INTEL_CXX11_MODE && SMTG_INTEL_COMPILER >= 1300) + #endif #define SMTG_DEPRECATED_ATTRIBUTE(message) __declspec (deprecated ("Is Deprecated: " message)) //----------------------------------------------------------------------------- // LINUX //----------------------------------------------------------------------------- #elif __gnu_linux__ || __linux__ - #define SMTG_OS_LINUX 1 - #define SMTG_OS_MACOS 0 - #define SMTG_OS_WINDOWS 0 - #define SMTG_OS_WINDOWS_ARM 0 - #define SMTG_OS_IOS 0 - #define SMTG_OS_OSX 0 + #define SMTG_OS_LINUX 1 + #define SMTG_OS_MACOS 0 + #define SMTG_OS_WINDOWS 0 + #define SMTG_OS_IOS 0 + #define SMTG_OS_OSX 0 #define SMTG_CPU_X86 __i386__ #define SMTG_CPU_X86_64 __x86_64__ - #if defined(__arm__) - #define SMTG_CPU_ARM __arm__ - #else - #define SMTG_CPU_ARM 0 - #endif - #if defined(__aarch64__) - #define SMTG_CPU_ARM_64 __aarch64__ - #else - #define SMTG_CPU_ARM_64 0 - #endif - #define SMTG_CPU_ARM_64EC 0 + #define SMTG_CPU_ARM __arm__ + #define SMTG_CPU_ARM_64 __aarch64__ #include #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -162,7 +119,6 @@ #define SMTG_PTHREADS 1 #define SMTG_EXPORT_SYMBOL __attribute__ ((visibility ("default"))) - #define SMTG_HIDDEN_SYMBOL __attribute__ ((visibility ("hidden"))) #if __LP64__ #define SMTG_PLATFORM_64 1 @@ -175,35 +131,28 @@ #ifndef SMTG_CPP11 #error unsupported compiler #endif - #define SMTG_CPP14 (__cplusplus >= 201402L) - #define SMTG_CPP17 (__cplusplus >= 201703L) - #define SMTG_CPP20 (__cplusplus >= 202002L) #if defined(__GNUG__) && __GNUG__ < 8 #define SMTG_CPP11_STDLIBSUPPORT 0 #else #define SMTG_CPP11_STDLIBSUPPORT 1 #endif #define SMTG_HAS_NOEXCEPT 1 - #define SMTG_HAS_CPP11_CONSTEXPR SMTG_CPP11 - #define SMTG_HAS_CPP14_CONSTEXPR SMTG_CPP14 - #endif // __cplusplus + #endif //----------------------------------------------------------------------------- // Mac and iOS //----------------------------------------------------------------------------- #elif __APPLE__ #include - #define SMTG_OS_LINUX 0 - #define SMTG_OS_MACOS 1 - #define SMTG_OS_WINDOWS 0 - #define SMTG_OS_WINDOWS_ARM 0 - #define SMTG_OS_IOS TARGET_OS_IPHONE - #define SMTG_OS_OSX TARGET_OS_MAC && !TARGET_OS_IPHONE - - #define SMTG_CPU_X86 TARGET_CPU_X86 - #define SMTG_CPU_X86_64 TARGET_CPU_X86_64 - #define SMTG_CPU_ARM TARGET_CPU_ARM - #define SMTG_CPU_ARM_64 TARGET_CPU_ARM64 - #define SMTG_CPU_ARM_64EC 0 + #define SMTG_OS_LINUX 0 + #define SMTG_OS_MACOS 1 + #define SMTG_OS_WINDOWS 0 + #define SMTG_OS_IOS TARGET_OS_IPHONE + #define SMTG_OS_OSX TARGET_OS_MAC && !TARGET_OS_IPHONE + + #define SMTG_CPU_X86 TARGET_CPU_X86 + #define SMTG_CPU_X86_64 TARGET_CPU_X86_64 + #define SMTG_CPU_ARM TARGET_CPU_ARM + #define SMTG_CPU_ARM_64 TARGET_CPU_ARM64 #if !SMTG_OS_IOS #ifndef __CF_USE_FRAMEWORK_INCLUDES__ @@ -229,7 +178,6 @@ #define SMTG_PTHREADS 1 #define SMTG_EXPORT_SYMBOL __attribute__ ((visibility ("default"))) - #define SMTG_HIDDEN_SYMBOL __attribute__ ((visibility ("hidden"))) #if !defined(__PLIST__) && !defined(SMTG_DISABLE_DEFAULT_DIAGNOSTICS) #ifdef __clang__ @@ -255,9 +203,6 @@ #ifdef __cplusplus #include #define SMTG_CPP11 (__cplusplus >= 201103L || SMTG_INTEL_CXX11_MODE) - #define SMTG_CPP14 (__cplusplus >= 201402L) - #define SMTG_CPP17 (__cplusplus >= 201703L) - #define SMTG_CPP20 (__cplusplus >= 202002L) #if defined (_LIBCPP_VERSION) && SMTG_CPP11 #define SMTG_CPP11_STDLIBSUPPORT 1 #define SMTG_HAS_NOEXCEPT 1 @@ -265,12 +210,7 @@ #define SMTG_CPP11_STDLIBSUPPORT 0 #define SMTG_HAS_NOEXCEPT 0 #endif - #define SMTG_HAS_CPP11_CONSTEXPR SMTG_CPP11 - #define SMTG_HAS_CPP14_CONSTEXPR SMTG_CPP14 - #endif // __cplusplus -//----------------------------------------------------------------------------- -// Unknown Platform -//----------------------------------------------------------------------------- + #endif #else #pragma error unknown platform #endif @@ -296,22 +236,11 @@ //----------------------------------------------------------------------------- #if SMTG_CPP11 #define SMTG_OVERRIDE override -#else -#define SMTG_OVERRIDE -#endif - -#if SMTG_HAS_CPP11_CONSTEXPR #define SMTG_CONSTEXPR constexpr #else +#define SMTG_OVERRIDE #define SMTG_CONSTEXPR #endif - -#if SMTG_HAS_CPP14_CONSTEXPR -#define SMTG_CONSTEXPR14 constexpr -#else -#define SMTG_CONSTEXPR14 -#endif - #if SMTG_HAS_NOEXCEPT #define SMTG_NOEXCEPT noexcept #else diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/fstrdefs.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/fstrdefs.h index fea4522c..00eaa1de 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/fstrdefs.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/fstrdefs.h @@ -25,7 +25,11 @@ // 16 bit string operations #if SMTG_CPP11 // if c++11 unicode string literals #define SMTG_CPP11_CAT_PRIVATE_DONT_USE(a,b) a ## b - #define STR16(x) SMTG_CPP11_CAT_PRIVATE_DONT_USE(u,x) + #if SMTG_OS_WINDOWS + #define STR16(x) SMTG_CPP11_CAT_PRIVATE_DONT_USE(L,x) + #else + #define STR16(x) SMTG_CPP11_CAT_PRIVATE_DONT_USE(u,x) + #endif #else #include "conststringtable.h" #define STR16(x) Steinberg::ConstStringTable::instance ()->getString (x) @@ -95,19 +99,19 @@ namespace Steinberg { //---------------------------------------------------------------------------- -static SMTG_CONSTEXPR const tchar kEmptyString[] = { 0 }; -static SMTG_CONSTEXPR const char8 kEmptyString8[] = { 0 }; -static SMTG_CONSTEXPR const char16 kEmptyString16[] = { 0 }; +static const tchar kEmptyString[] = { 0 }; +static const char8 kEmptyString8[] = { 0 }; +static const char16 kEmptyString16[] = { 0 }; #ifdef UNICODE -static SMTG_CONSTEXPR const tchar kInfiniteSymbol[] = { 0x221E, 0 }; +static const tchar kInfiniteSymbol[] = { 0x221E, 0 }; #else -static SMTG_CONSTEXPR const tchar* const kInfiniteSymbol = STR ("oo"); +static const tchar* const kInfiniteSymbol = STR ("oo"); #endif //---------------------------------------------------------------------------- template -inline SMTG_CONSTEXPR14 int32 _tstrlen (const T* wcs) +inline int32 _tstrlen (const T* wcs) { const T* eos = wcs; @@ -117,13 +121,13 @@ inline SMTG_CONSTEXPR14 int32 _tstrlen (const T* wcs) return (int32) (eos - wcs - 1); } -inline SMTG_CONSTEXPR14 int32 tstrlen (const tchar* str) {return _tstrlen (str);} -inline SMTG_CONSTEXPR14 int32 strlen8 (const char8* str) {return _tstrlen (str);} -inline SMTG_CONSTEXPR14 int32 strlen16 (const char16* str) {return _tstrlen (str);} +inline int32 tstrlen (const tchar* str) {return _tstrlen (str);} +inline int32 strlen8 (const char8* str) {return _tstrlen (str);} +inline int32 strlen16 (const char16* str) {return _tstrlen (str);} //---------------------------------------------------------------------------- template -inline SMTG_CONSTEXPR14 int32 _tstrcmp (const T* src, const T* dst) +inline int32 _tstrcmp (const T* src, const T* dst) { while (*src == *dst && *dst) { @@ -133,29 +137,30 @@ inline SMTG_CONSTEXPR14 int32 _tstrcmp (const T* src, const T* dst) if (*src == 0 && *dst == 0) return 0; - if (*src == 0) + else if (*src == 0) return -1; - if (*dst == 0) + else if (*dst == 0) return 1; - return (int32) (*src - *dst); + else + return (int32) (*src - *dst); } -inline SMTG_CONSTEXPR14 int32 tstrcmp (const tchar* src, const tchar* dst) {return _tstrcmp (src, dst);} -inline SMTG_CONSTEXPR14 int32 strcmp8 (const char8* src, const char8* dst) {return _tstrcmp (src, dst);} -inline SMTG_CONSTEXPR14 int32 strcmp16 (const char16* src, const char16* dst) {return _tstrcmp (src, dst);} +inline int32 tstrcmp (const tchar* src, const tchar* dst) {return _tstrcmp (src, dst);} +inline int32 strcmp8 (const char8* src, const char8* dst) {return _tstrcmp (src, dst);} +inline int32 strcmp16 (const char16* src, const char16* dst) {return _tstrcmp (src, dst);} template -inline SMTG_CONSTEXPR14 int32 strcmpT (const T* first, const T* last); +inline int32 strcmpT (const T* first, const T* last); template <> -inline SMTG_CONSTEXPR14 int32 strcmpT (const char8* first, const char8* last) { return _tstrcmp (first, last); } +inline int32 strcmpT (const char8* first, const char8* last) { return _tstrcmp (first, last); } template <> -inline SMTG_CONSTEXPR14 int32 strcmpT (const char16* first, const char16* last) { return _tstrcmp (first, last); } +inline int32 strcmpT (const char16* first, const char16* last) { return _tstrcmp (first, last); } //---------------------------------------------------------------------------- template -inline SMTG_CONSTEXPR14 int32 _tstrncmp (const T* first, const T* last, uint32 count) +inline int32 _tstrncmp (const T* first, const T* last, uint32 count) { if (count == 0) return 0; @@ -168,42 +173,43 @@ inline SMTG_CONSTEXPR14 int32 _tstrncmp (const T* first, const T* last, uint32 c if (*first == 0 && *last == 0) return 0; - if (*first == 0) + else if (*first == 0) return -1; - if (*last == 0) + else if (*last == 0) return 1; - return (int32) (*first - *last); + else + return (int32) (*first - *last); } -inline SMTG_CONSTEXPR14 int32 tstrncmp (const tchar* first, const tchar* last, uint32 count) {return _tstrncmp (first, last, count);} -inline SMTG_CONSTEXPR14 int32 strncmp8 (const char8* first, const char8* last, uint32 count) {return _tstrncmp (first, last, count);} -inline SMTG_CONSTEXPR14 int32 strncmp16 (const char16* first, const char16* last, uint32 count) {return _tstrncmp (first, last, count);} +inline int32 tstrncmp (const tchar* first, const tchar* last, uint32 count) {return _tstrncmp (first, last, count);} +inline int32 strncmp8 (const char8* first, const char8* last, uint32 count) {return _tstrncmp (first, last, count);} +inline int32 strncmp16 (const char16* first, const char16* last, uint32 count) {return _tstrncmp (first, last, count);} template -inline SMTG_CONSTEXPR14 int32 strncmpT (const T* first, const T* last, uint32 count); +inline int32 strncmpT (const T* first, const T* last, uint32 count); template <> -inline SMTG_CONSTEXPR14 int32 strncmpT (const char8* first, const char8* last, uint32 count) { return _tstrncmp (first, last, count); } +inline int32 strncmpT (const char8* first, const char8* last, uint32 count) { return _tstrncmp (first, last, count); } template <> -inline SMTG_CONSTEXPR14 int32 strncmpT (const char16* first, const char16* last, uint32 count) {return _tstrncmp (first, last, count); } +inline int32 strncmpT (const char16* first, const char16* last, uint32 count) {return _tstrncmp (first, last, count); } //---------------------------------------------------------------------------- template -inline SMTG_CONSTEXPR14 T* _tstrcpy (T* dst, const T* src) +inline T* _tstrcpy (T* dst, const T* src) { T* cp = dst; while ((*cp++ = *src++) != 0) // copy string ; return dst; } -inline SMTG_CONSTEXPR14 tchar* tstrcpy (tchar* dst, const tchar* src) {return _tstrcpy (dst, src);} -inline SMTG_CONSTEXPR14 char8* strcpy8 (char8* dst, const char8* src) {return _tstrcpy (dst, src);} -inline SMTG_CONSTEXPR14 char16* strcpy16 (char16* dst, const char16* src) {return _tstrcpy (dst, src);} +inline tchar* tstrcpy (tchar* dst, const tchar* src) {return _tstrcpy (dst, src);} +inline char8* strcpy8 (char8* dst, const char8* src) {return _tstrcpy (dst, src);} +inline char16* strcpy16 (char16* dst, const char16* src) {return _tstrcpy (dst, src);} //---------------------------------------------------------------------------- template -inline SMTG_CONSTEXPR14 T* _tstrncpy (T* dest, const T* source, uint32 count) +inline T* _tstrncpy (T* dest, const T* source, uint32 count) { T* start = dest; while (count && (*dest++ = *source++) != 0) // copy string @@ -217,13 +223,13 @@ inline SMTG_CONSTEXPR14 T* _tstrncpy (T* dest, const T* source, uint32 count) return start; } -inline SMTG_CONSTEXPR14 tchar* tstrncpy (tchar* dest, const tchar* source, uint32 count) {return _tstrncpy (dest, source, count);} -inline SMTG_CONSTEXPR14 char8* strncpy8 (char8* dest, const char8* source, uint32 count) {return _tstrncpy (dest, source, count);} -inline SMTG_CONSTEXPR14 char16* strncpy16 (char16* dest, const char16* source, uint32 count) {return _tstrncpy (dest, source, count);} +inline tchar* tstrncpy (tchar* dest, const tchar* source, uint32 count) {return _tstrncpy (dest, source, count);} +inline char8* strncpy8 (char8* dest, const char8* source, uint32 count) {return _tstrncpy (dest, source, count);} +inline char16* strncpy16 (char16* dest, const char16* source, uint32 count) {return _tstrncpy (dest, source, count);} //---------------------------------------------------------------------------- template -inline SMTG_CONSTEXPR14 T* _tstrcat (T* dst, const T* src) +inline T* _tstrcat (T* dst, const T* src) { T* cp = dst; @@ -236,12 +242,12 @@ inline SMTG_CONSTEXPR14 T* _tstrcat (T* dst, const T* src) return dst; } -inline SMTG_CONSTEXPR14 tchar* tstrcat (tchar* dst, const tchar* src) {return _tstrcat (dst, src); } -inline SMTG_CONSTEXPR14 char8* strcat8 (char8* dst, const char8* src) {return _tstrcat (dst, src); } -inline SMTG_CONSTEXPR14 char16* strcat16 (char16* dst, const char16* src) {return _tstrcat (dst, src); } +inline tchar* tstrcat (tchar* dst, const tchar* src) {return _tstrcat (dst, src); } +inline char8* strcat8 (char8* dst, const char8* src) {return _tstrcat (dst, src); } +inline char16* strcat16 (char16* dst, const char16* src) {return _tstrcat (dst, src); } //---------------------------------------------------------------------------- -inline SMTG_CONSTEXPR14 void str8ToStr16 (char16* dst, const char8* src, int32 n = -1) +inline void str8ToStr16 (char16* dst, const char8* src, int32 n = -1) { int32 i = 0; for (;;) @@ -272,20 +278,12 @@ inline SMTG_CONSTEXPR14 void str8ToStr16 (char16* dst, const char8* src, int32 n } //------------------------------------------------------------------------ -inline SMTG_CONSTEXPR14 bool FIDStringsEqual (FIDString id1, FIDString id2) +inline bool FIDStringsEqual (FIDString id1, FIDString id2) { return (id1 && id2) ? (strcmp8 (id1, id2) == 0) : false; } -static SMTG_CONSTEXPR const uint32 kPrintfBufferSize = 4096; - -#if SMTG_OS_WINDOWS -/* cast between wchar_t and char16 */ -inline wchar_t* wscast (char16* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast (s); } -inline char16* wscast (wchar_t* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast (s);} -inline const wchar_t* wscast (const char16* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast (s); } -inline const char16* wscast (const wchar_t* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast (s); } -#endif +static const uint32 kPrintfBufferSize = 4096; //------------------------------------------------------------------------ } // namespace Steinberg diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/ftypes.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/ftypes.h index 133dbba3..1f95bd11 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/ftypes.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/ftypes.h @@ -18,8 +18,6 @@ #include "fplatform.h" -#include - //#define UNICODE_OFF // disable / enable unicode #ifdef UNICODE_OFF @@ -39,26 +37,38 @@ namespace Steinberg //----------------------------------------------------------------- // Integral Types typedef char int8; - typedef uint8_t uint8; + typedef unsigned char uint8; typedef unsigned char uchar; - typedef int16_t int16; - typedef uint16_t uint16; - - typedef int32_t int32; - typedef uint32_t uint32; + typedef short int16; + typedef unsigned short uint16; - static const int32 kMaxInt32 = INT32_MAX; - static const int32 kMinInt32 = INT32_MIN; - static const int32 kMaxLong = kMaxInt32; - static const int32 kMinLong = kMinInt32; - static const uint32 kMaxInt32u = UINT32_MAX; +#if SMTG_OS_WINDOWS && !defined(__GNUC__) + typedef long int32; + typedef unsigned long uint32; +#else + typedef int int32; + typedef unsigned int uint32; +#endif - typedef int64_t int64; - typedef uint64_t uint64; - static const int64 kMaxInt64 = INT64_MAX; - static const int64 kMinInt64 = INT64_MIN; - static const uint64 kMaxInt64u = UINT64_MAX; + static const int32 kMaxLong = 0x7fffffff; + static const int32 kMinLong = (-0x7fffffff - 1); + static const int32 kMaxInt32 = kMaxLong; + static const int32 kMinInt32 = kMinLong; + static const uint32 kMaxInt32u = 0xffffffff; + +#if SMTG_OS_WINDOWS && !defined(__GNUC__) + typedef __int64 int64; + typedef unsigned __int64 uint64; + static const int64 kMaxInt64 = 9223372036854775807i64; + static const int64 kMinInt64 = (-9223372036854775807i64 - 1); +#else + typedef long long int64; + typedef unsigned long long uint64; + static const int64 kMaxInt64 = 0x7fffffffffffffffLL; + static const int64 kMinInt64 = (-0x7fffffffffffffffLL-1); +#endif + static const uint64 kMaxInt64u = uint64 (0xffffffff) | (uint64 (0xffffffff) << 32); //----------------------------------------------------------------- // other Semantic Types @@ -81,7 +91,15 @@ namespace Steinberg //------------------------------------------------------------------ // Char / Strings typedef char char8; +#ifdef _NATIVE_WCHAR_T_DEFINED + typedef __wchar_t char16; +#elif defined(__MINGW32__) + typedef wchar_t char16; +#elif SMTG_CPP11 typedef char16_t char16; +#else + typedef int16 char16; +#endif #ifdef UNICODE typedef char16 tchar; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.cpp index b88fc5bf..23ca64e6 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.cpp @@ -406,23 +406,17 @@ void FUID::toRegistryString (char8* string) const char8 s5[13]; Steinberg::toString8 (s5, data, 10, 16); - snprintf (string, 40, "{%s-%s-%s-%s-%s}", s1, s2, s3, s4, s5); + sprintf (string, "{%s-%s-%s-%s-%s}", s1, s2, s3, s4, s5); #endif } //------------------------------------------------------------------------ void FUID::print (char8* string, int32 style) const { - print (style, string, 62); -} - -//------------------------------------------------------------------------ -void FUID::print (int32 style, char8* string, size_t stringBufferSize) const -{ - if (!string || stringBufferSize == 0) // no string: debug output + if (!string) // no string: debug output { char8 str[128]; - print (style, str, 128); + print (str, style); #if SMTG_OS_WINDOWS OutputDebugStringA (str); @@ -439,25 +433,21 @@ void FUID::print (int32 style, char8* string, size_t stringBufferSize) const switch (style) { case kINLINE_UID: - snprintf (string, stringBufferSize, "INLINE_UID (0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1, - l2, l3, l4); + sprintf (string, "INLINE_UID (0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1, l2, l3, l4); break; case kDECLARE_UID: - snprintf (string, stringBufferSize, "DECLARE_UID (0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1, - l2, l3, l4); + sprintf (string, "DECLARE_UID (0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1, l2, l3, l4); break; case kFUID: - snprintf (string, stringBufferSize, "FUID (0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1, l2, l3, - l4); + sprintf (string, "FUID (0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1, l2, l3, l4); break; case kCLASS_UID: default: - snprintf (string, stringBufferSize, - "DECLARE_CLASS_IID (Interface, 0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1, l2, l3, - l4); + sprintf (string, "DECLARE_CLASS_IID (Interface, 0x%08X, 0x%08X, 0x%08X, 0x%08X)", l1, + l2, l3, l4); break; } } @@ -477,7 +467,7 @@ static void toString8 (char8* string, const char* data, int32 i1, int32 i2) for (int32 i = i1; i < i2; i++) { char8 s[3]; - snprintf (s, 3, "%02X", (uint8)data[i]); + sprintf (s, "%02X", (uint8)data[i]); strcat (string, s); } } diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.h index 56cf292c..d5a5cfc6 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.h @@ -60,18 +60,19 @@ #endif //------------------------------------------------------------------------ -#define DECLARE_UID(name, l1, l2, l3, l4) SMTG_CONSTEXPR14 ::Steinberg::TUID name = INLINE_UID (l1, l2, l3, l4); +#define DECLARE_UID(name, l1, l2, l3, l4) ::Steinberg::TUID name = INLINE_UID (l1, l2, l3, l4); //------------------------------------------------------------------------ #define EXTERN_UID(name) extern const ::Steinberg::TUID name; #ifdef INIT_CLASS_IID -#define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4) \ - static SMTG_CONSTEXPR14 const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4); \ - const ::Steinberg::FUID ClassName::iid (ClassName##_iid); +#define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4) \ + static const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4); \ + \ +const ::Steinberg::FUID ClassName::iid (ClassName##_iid); #else #define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4) \ - static SMTG_CONSTEXPR14 const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4); + static const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4); #endif #define DEF_CLASS_IID(ClassName) const ::Steinberg::FUID ClassName::iid (ClassName##_iid); @@ -206,16 +207,7 @@ typedef int64 LARGE_INT; // obsolete //------------------------------------------------------------------------ // FUID class declaration //------------------------------------------------------------------------ -typedef char TUID[16]; ///< plain UID type - -#if SMTG_CPP14 -//------------------------------------------------------------------------ -inline SMTG_CONSTEXPR14 void copyTUID (char* dst, const char* src) -{ - for (auto i = 0; i < 16; ++i) - dst[i] = src[i]; -} -#endif +typedef int8 TUID[16]; ///< plain UID type //------------------------------------------------------------------------ /* FUnknown private */ @@ -309,18 +301,12 @@ class FUID kCLASS_UID ///< "DECLARE_CLASS_IID (Interface, 0x00000000, 0x00000000, 0x00000000, 0x00000000)" }; /** Prints the UID to a string (or debug output if string is NULL). - \param style can be chosen from the FUID::UIDPrintStyle enumeration. - \param string is the output string if not NULL. - \param stringBufferSize is the size of the output string */ - void print (int32 style, char8* string = nullptr, size_t stringBufferSize = 0) const; - -#if SMTG_CPP17 - [[deprecated ("Use the print method with the buffer size")]] -#endif + \param string is the output string if not NULL. + \param style can be chosen from the FUID::UIDPrintStyle enumeration. */ void print (char8* string = nullptr, int32 style = kINLINE_UID) const; template - inline explicit FUID (const char (&uid)[N]) + inline explicit FUID (const int8 (&uid)[N]) { #if SMTG_CPP11_STDLIBSUPPORT static_assert (N == sizeof (TUID), "only TUID allowed"); @@ -456,7 +442,7 @@ using VoidT = typename Void::Type; //------------------------------------------------------------------------ /** * This type trait detects if a class has an @c iid member variable. It is used to detect if - * the FUID and DECLARE_CLASS_IID method or the U::UID method is used. + * the FUID and DECLARE_CLASS_IID method or the SKI::UID method is used. */ template struct HasIIDType : std::false_type @@ -473,7 +459,7 @@ struct HasIIDType> : std::true_type } // FUnknownPrivate //------------------------------------------------------------------------ -/** @return the TUID for an interface which uses the U::UID method. */ +/** @return the TUID for a SKI interface which uses the SKI::UID method. */ template ::value>::type* = nullptr> const TUID& getTUID () @@ -482,7 +468,7 @@ const TUID& getTUID () } //------------------------------------------------------------------------ -/** @return the TUID for an interface which uses the FUID and DECLARE_CLASS_IID method. */ +/** @return the TUID for a SKI interface which uses the FUID and DECLARE_CLASS_IID method. */ template ::value>::type* = nullptr> const TUID& getTUID () diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/futils.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/futils.h index e5d8a5f3..5ce9e295 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/futils.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/futils.h @@ -27,39 +27,34 @@ inline const T& Min (const T& a, const T& b) return b < a ? b : a; } -//---------------------------------------------------------------------------- template inline const T& Max (const T& a, const T& b) { return a < b ? b : a; } -//---------------------------------------------------------------------------- template inline T Abs (const T& value) { return (value >= (T)0) ? value : -value; } -//---------------------------------------------------------------------------- template inline T Sign (const T& value) { return (value == (T)0) ? 0 : ((value >= (T)0) ? 1 : -1); } -//---------------------------------------------------------------------------- template inline T Bound (T minval, T maxval, T x) { if (x < minval) return minval; - if (x > maxval) + else if (x > maxval) return maxval; return x; } -//---------------------------------------------------------------------------- template void Swap (T& t1, T& t2) { @@ -68,7 +63,6 @@ void Swap (T& t1, T& t2) t2 = tmp; } -//---------------------------------------------------------------------------- template bool IsApproximateEqual (T t1, T t2, T epsilon) { @@ -82,21 +76,18 @@ bool IsApproximateEqual (T t1, T t2, T epsilon) return false; } -//---------------------------------------------------------------------------- template inline T ToNormalized (const T& value, const int32 numSteps) { return value / T (numSteps); } -//---------------------------------------------------------------------------- template inline int32 FromNormalized (const T& norm, const int32 numSteps) { return Min (numSteps, int32 (norm * (numSteps + 1))); } -//---------------------------------------------------------------------------- // Four character constant #ifndef CCONST #define CCONST(a, b, c, d) \ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/ipluginbase.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/ipluginbase.h index 36bc263f..10325fe8 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/ipluginbase.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/ipluginbase.h @@ -38,10 +38,8 @@ class IPluginBase: public FUnknown public: //------------------------------------------------------------------------ /** The host passes a number of interfaces as context to initialize the plug-in class. - \param context, passed by the host, is mandatory and should implement IHostApplication - @note Extensive memory allocations etc. should be performed in this method rather than in - the class' constructor! If the method does NOT return kResultOk, the object is released - immediately. In this case terminate is not called! */ + @note Extensive memory allocations etc. should be performed in this method rather than in the class' constructor! + If the method does NOT return kResultOk, the object is released immediately. In this case terminate is not called! */ virtual tresult PLUGIN_API initialize (FUnknown* context) = 0; /** This function is called before the plug-in is unloaded and can be used for @@ -64,23 +62,11 @@ struct PFactoryInfo //------------------------------------------------------------------------ enum FactoryFlags { - /** Nothing */ - kNoFlags = 0, - - /** The number of exported classes can change each time the Module is loaded. If this flag - is set, the host does not cache class information. This leads to a longer startup time - because the host always has to load the Module to get the current class information. */ - kClassesDiscardable = 1 << 0, - - /** This flag is deprecated, do not use anymore, resp. it will get ignored from - Cubase/Nuendo 12 and later. */ - kLicenseCheck = 1 << 1, - - /** Component will not be unloaded until process exit */ - kComponentNonDiscardable = 1 << 3, - - /** Components have entirely unicode encoded strings (True for VST 3 plug-ins so far). */ - kUnicode = 1 << 4 + kNoFlags = 0, ///< Nothing + kClassesDiscardable = 1 << 0, ///< The number of exported classes can change each time the Module is loaded. If this flag is set, the host does not cache class information. This leads to a longer startup time because the host always has to load the Module to get the current class information. + kLicenseCheck = 1 << 1, ///< Class IDs of components are interpreted as Syncrosoft-License (LICENCE_UID). Loaded in a Steinberg host, the module will not be loaded when the license is not valid + kComponentNonDiscardable = 1 << 3, ///< Component will not be unloaded until process exit + kUnicode = 1 << 4 ///< Components have entirely unicode encoded strings. (True for VST 3 plug-ins so far) }; enum @@ -91,16 +77,12 @@ struct PFactoryInfo }; //------------------------------------------------------------------------ - char8 vendor[kNameSize]; ///< e.g. "Steinberg Media Technologies" - char8 url[kURLSize]; ///< e.g. "http://www.steinberg.de" - char8 email[kEmailSize]; ///< e.g. "info@steinberg.de" - int32 flags; ///< (see FactoryFlags above) + char8 vendor[kNameSize]; ///< e.g. "Steinberg Media Technologies" + char8 url[kURLSize]; ///< e.g. "http://www.steinberg.de" + char8 email[kEmailSize]; ///< e.g. "info@steinberg.de" + int32 flags; ///< (see above) //------------------------------------------------------------------------ - SMTG_CONSTEXPR14 PFactoryInfo (const char8* _vendor, const char8* _url, const char8* _email, - int32 _flags) -#if SMTG_CPP14 - : vendor (), url (), email (), flags () -#endif + PFactoryInfo (const char8* _vendor, const char8* _url, const char8* _email, int32 _flags) { strncpy8 (vendor, _vendor, kNameSize); strncpy8 (url, _url, kURLSize); @@ -135,32 +117,16 @@ struct PClassInfo kNameSize = 64 }; //------------------------------------------------------------------------ - /** Class ID 16 Byte class GUID */ - TUID cid; - - /** Cardinality of the class, set to kManyInstances (see \ref PClassInfo::ClassCardinality) */ - int32 cardinality; - - /** Class category, host uses this to categorize interfaces */ - char8 category[kCategorySize]; - - /** Class name, visible to the user */ - char8 name[kNameSize]; - + TUID cid; ///< Class ID 16 Byte class GUID + int32 cardinality; ///< cardinality of the class, set to kManyInstances (see \ref ClassCardinality) + char8 category[kCategorySize]; ///< class category, host uses this to categorize interfaces + char8 name[kNameSize]; ///< class name, visible to the user //------------------------------------------------------------------------ - SMTG_CONSTEXPR14 PClassInfo (const TUID _cid, int32 _cardinality, const char8* _category, - const char8* _name) -#if SMTG_CPP14 - : cid (), cardinality (), category (), name () -#endif + PClassInfo (const TUID _cid, int32 _cardinality, const char8* _category, const char8* _name) { -#if SMTG_CPP14 - copyTUID (cid, _cid); -#else memset (this, 0, sizeof (PClassInfo)); memcpy (cid, _cid, sizeof (TUID)); -#endif if (_category) strncpy8 (category, _category, kCategorySize); if (_name) @@ -196,9 +162,8 @@ class IPluginFactory : public FUnknown /** Fill a PFactoryInfo structure with information about the plug-in vendor. */ virtual tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) = 0; - /** Returns the number of exported classes by this factory. If you are using the CPluginFactory - * implementation provided by the SDK, it returns the number of classes you registered with - * CPluginFactory::registerClass. */ + /** Returns the number of exported classes by this factory. + If you are using the CPluginFactory implementation provided by the SDK, it returns the number of classes you registered with CPluginFactory::registerClass. */ virtual int32 PLUGIN_API countClasses () = 0; /** Fill a PClassInfo structure with information about the class at the specified index. */ @@ -221,17 +186,10 @@ DECLARE_CLASS_IID (IPluginFactory, 0x7A4D811C, 0x52114A1F, 0xAED9D2EE, 0x0B43BF9 struct PClassInfo2 { //------------------------------------------------------------------------ - /** Class ID 16 Byte class GUID */ - TUID cid; - - /** Cardinality of the class, set to kManyInstances (see \ref PClassInfo::ClassCardinality) */ - int32 cardinality; - - /** Class category, host uses this to categorize interfaces */ - char8 category[PClassInfo::kCategorySize]; - - /** Class name, visible to the user */ - char8 name[PClassInfo::kNameSize]; + TUID cid; ///< Class ID 16 Byte class GUID + int32 cardinality; ///< cardinality of the class, set to kManyInstances (see \ref PClassInfo::ClassCardinality) + char8 category[PClassInfo::kCategorySize]; ///< class category, host uses this to categorize interfaces + char8 name[PClassInfo::kNameSize]; ///< class name, visible to the user enum { kVendorSize = 64, @@ -239,44 +197,20 @@ struct PClassInfo2 kSubCategoriesSize = 128 }; - /** flags used for a specific category, must be defined where category is defined */ - uint32 classFlags; + uint32 classFlags; ///< flags used for a specific category, must be defined where category is defined + char8 subCategories[kSubCategoriesSize]; ///< module specific subcategories, can be more than one, logically added by the \c OR operator + char8 vendor[kVendorSize]; ///< overwrite vendor information from factory info + char8 version[kVersionSize]; ///< Version string (e.g. "1.0.0.512" with Major.Minor.Subversion.Build) + char8 sdkVersion[kVersionSize]; ///< SDK version used to build this class (e.g. "VST 3.0") - /** module specific subcategories, can be more than one, logically added by the OR operator */ - char8 subCategories[kSubCategoriesSize]; - - /** overwrite vendor information from factory info */ - char8 vendor[kVendorSize]; - - /** Version string (e.g. "1.0.0.512" with Major.Minor.Subversion.Build) */ - char8 version[kVersionSize]; - - /** SDK version used to build this class (e.g. "VST 3.0") */ - char8 sdkVersion[kVersionSize]; //------------------------------------------------------------------------ - SMTG_CONSTEXPR14 PClassInfo2 (const TUID _cid, int32 _cardinality, const char8* _category, - const char8* _name, int32 _classFlags, - const char8* _subCategories, const char8* _vendor, - const char8* _version, const char8* _sdkVersion) -#if SMTG_CPP14 - : cid () - , cardinality () - , category () - , name () - , classFlags () - , subCategories () - , vendor () - , version () - , sdkVersion () -#endif + PClassInfo2 (const TUID _cid, int32 _cardinality, const char8* _category, const char8* _name, + int32 _classFlags, const char8* _subCategories, const char8* _vendor, const char8* _version, + const char8* _sdkVersion) { -#if SMTG_CPP14 - copyTUID (cid, _cid); -#else memset (this, 0, sizeof (PClassInfo2)); memcpy (cid, _cid, sizeof (TUID)); -#endif cardinality = _cardinality; if (_category) strncpy8 (category, _category, PClassInfo::kCategorySize); @@ -347,44 +281,19 @@ struct PClassInfoW kSubCategoriesSize = 128 }; - /** flags used for a specific category, must be defined where category is defined */ - uint32 classFlags; - - /** module specific subcategories, can be more than one, logically added by the OR operator */ - char8 subCategories[kSubCategoriesSize]; - - /** overwrite vendor information from factory info */ - char16 vendor[kVendorSize]; - - /** Version string (e.g. "1.0.0.512" with Major.Minor.Subversion.Build) */ - char16 version[kVersionSize]; - - /** SDK version used to build this class (e.g. "VST 3.0") */ - char16 sdkVersion[kVersionSize]; - -//------------------------------------------------------------------------ - SMTG_CONSTEXPR14 PClassInfoW (const TUID _cid, int32 _cardinality, const char8* _category, - const char16* _name, int32 _classFlags, - const char8* _subCategories, const char16* _vendor, - const char16* _version, const char16* _sdkVersion) -#if SMTG_CPP14 - : cid () - , cardinality () - , category () - , name () - , classFlags () - , subCategories () - , vendor () - , version () - , sdkVersion () -#endif + uint32 classFlags; ///< flags used for a specific category, must be defined where category is defined + char8 subCategories[kSubCategoriesSize];///< module specific subcategories, can be more than one, logically added by the \c OR operator + char16 vendor[kVendorSize]; ///< overwrite vendor information from factory info + char16 version[kVersionSize]; ///< Version string (e.g. "1.0.0.512" with Major.Minor.Subversion.Build) + char16 sdkVersion[kVersionSize]; ///< SDK version used to build this class (e.g. "VST 3.0") + +//------------------------------------------------------------------------ + PClassInfoW (const TUID _cid, int32 _cardinality, const char8* _category, const char16* _name, + int32 _classFlags, const char8* _subCategories, const char16* _vendor, const char16* _version, + const char16* _sdkVersion) { -#if SMTG_CPP14 - copyTUID (cid, _cid); -#else memset (this, 0, sizeof (PClassInfoW)); memcpy (cid, _cid, sizeof (TUID)); -#endif cardinality = _cardinality; if (_category) strncpy8 (category, _category, PClassInfo::kCategorySize); @@ -417,13 +326,9 @@ struct PClassInfoW PClassInfoW () { memset (this, 0, sizeof (PClassInfoW)); } #endif - SMTG_CONSTEXPR14 void fromAscii (const PClassInfo2& ci2) + void fromAscii (const PClassInfo2& ci2) { -#if SMTG_CPP14 - copyTUID (cid, ci2.cid); -#else memcpy (cid, ci2.cid, sizeof (TUID)); -#endif cardinality = ci2.cardinality; strncpy8 (category, ci2.category, PClassInfo::kCategorySize); str8ToStr16 (name, ci2.name, PClassInfo::kNameSize); diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/iplugincompatibility.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/iplugincompatibility.h deleted file mode 100644 index d3f67e9b..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/iplugincompatibility.h +++ /dev/null @@ -1,122 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : SDK Core -// -// Category : SDK Core Interfaces -// Filename : pluginterfaces/base/iplugincompatibility.h -// Created by : Steinberg, 02/2022 -// Description : Basic Plug-in Interfaces -// -//----------------------------------------------------------------------------- -// This file is part of a Steinberg SDK. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this distribution -// and at www.steinberg.net/sdklicenses. -// No part of the SDK, including this file, may be copied, modified, propagated, -// or distributed except according to the terms contained in the LICENSE file. -//----------------------------------------------------------------------------- - -#pragma once - -#include "ibstream.h" - -//------------------------------------------------------------------------ -namespace Steinberg { - -//------------------------------------------------------------------------ -/** moduleinfo.json - -The moduleinfo.json describes the contents of the plug-in in a JSON5 compatible format (See https://json5.org/). -It contains the factory info (see PFactoryInfo), the contained classes (see PClassInfo), the -included snapshots and a list of compatibility of the included classes. - -An example moduleinfo.json: - -\code -{ - "Name": "", - "Version": "1.0", - "Factory Info": { - "Vendor": "Steinberg Media Technologies", - "URL": "http://www.steinberg.net", - "E-Mail": "mailto:info@steinberg.de", - "Flags": { - "Unicode": true, - "Classes Discardable": false, - "Component Non Discardable": false, - }, - }, - "Compatibility": [ - { - "New": "B9F9ADE1CD9C4B6DA57E61E3123535FD", - "Old": [ - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", // just an example - "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", // another example - ], - }, - ], - "Classes": [ - { - "CID": "B9F9ADE1CD9C4B6DA57E61E3123535FD", - "Category": "Audio Module Class", - "Name": "AGainSimple VST3", - "Vendor": "Steinberg Media Technologies", - "Version": "1.3.0.1", - "SDKVersion": "VST 3.7.4", - "Sub Categories": [ - "Fx", - ], - "Class Flags": 0, - "Cardinality": 2147483647, - "Snapshots": [ - ], - }, - ], -} -\endcode - -*/ - -#define kPluginCompatibilityClass "Plugin Compatibility Class" - -//------------------------------------------------------------------------ -/** optional interface to query the compatibility of the plug-ins classes -- [plug imp] - -A plug-in can add a class with this interface to its class factory if it cannot provide a -moduleinfo.json file in its plug-in package/bundle where the compatibility is normally part of. - -If the module contains a moduleinfo.json the host will ignore this class. - -The class must write into the stream an UTF-8 encoded json description of the compatibility of -the other classes in the factory. - -It is expected that the JSON5 written starts with an array: -\code -[ - { - "New": "B9F9ADE1CD9C4B6DA57E61E3123535FD", - "Old": [ - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", // just an example - "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", // another example - ], - }, -] -\endcode -*/ -class IPluginCompatibility : public FUnknown -{ -public: - /** get the compatibility stream - * @param stream the stream the plug-in must write the UTF8 encoded JSON5 compatibility - * string. - * @return kResultTrue on success - */ - virtual tresult PLUGIN_API getCompatibilityJSON (IBStream* stream) = 0; - -//------------------------------------------------------------------------ - static const FUID iid; -}; - -DECLARE_CLASS_IID (IPluginCompatibility, 0x4AFD4B6A, 0x35D7C240, 0xA5C31414, 0xFB7D15E6) - -//------------------------------------------------------------------------ -} // Steinberg diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/gui/iplugviewcontentscalesupport.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/gui/iplugviewcontentscalesupport.h index f529031f..532c9410 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/gui/iplugviewcontentscalesupport.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/gui/iplugviewcontentscalesupport.h @@ -36,24 +36,16 @@ namespace Steinberg { This interface communicates the content scale factor from the host to the plug-in view on systems where plug-ins cannot get this information directly like Microsoft Windows. -The host calls setContentScaleFactor directly before or after the plug-in view is attached and when -the scale factor changes while the view is attached (system change or window moved to another screen -with different scaling settings). - -The host may call setContentScaleFactor in a different context, for example: scaling the plug-in -editor for better readability. - +The host calls setContentScaleFactor directly after the plug-in view is attached and when the scale +factor changes (system change or window moved to another screen with different scaling settings). +The host could call setContentScaleFactor in a different context, for example: scaling the +plug-in editor for better readability. When a plug-in handles this (by returning kResultTrue), it needs to scale the width and height of -its view by the scale factor and inform the host via a IPlugFrame::resizeView(). The host will then +its view by the scale factor and inform the host via a IPlugFrame::resizeView(), the host will then call IPlugView::onSize(). Note that the host is allowed to call setContentScaleFactor() at any time the IPlugView is valid. -If this happens before the IPlugFrame object is set on your view, make sure that when the host calls -IPlugView::getSize() afterwards you return the size of your view for that new scale factor. - -It is recommended to implement this interface on Microsoft Windows to let the host know that the -plug-in is able to render in different scalings. -*/ + */ class IPlugViewContentScaleSupport : public FUnknown { public: diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstattributes.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstattributes.h index 7d4e68fb..fb63b30c 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstattributes.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstattributes.h @@ -55,7 +55,7 @@ class IAttributeList : public FUnknown /** Gets float value. */ virtual tresult PLUGIN_API getFloat (AttrID id, double& value) = 0; - /** Sets string value (UTF16) (must be null-terminated!). */ + /** Sets string value (UTF16) (should be null-terminated!). */ virtual tresult PLUGIN_API setString (AttrID id, const TChar* string) = 0; /** Gets string value (UTF16). Note that Size is in Byte, not the string Length! @@ -106,7 +106,7 @@ tresult PLUGIN_API MyPlugin::setState (IBStream* state) UString128 tmp (string); char ascii[128]; tmp.toAscii (ascii, 128); - if (strncmp (ascii, StateType::kProject, strlen (StateType::kProject)) == 0) + if (!strncmp (ascii, StateType::kProject, strlen (StateType::kProject))) { // we are in project loading context... } diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstaudioprocessor.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstaudioprocessor.h index 82d550e3..f2dd5e7d 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstaudioprocessor.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstaudioprocessor.h @@ -46,48 +46,48 @@ namespace PlugType \defgroup plugType Plug-in Type used for subCategories */ /*@{*/ //------------------------------------------------------------------------ -SMTG_CONSTEXPR const CString kFx = "Fx"; ///< others type (not categorized) -SMTG_CONSTEXPR const CString kFxAnalyzer = "Fx|Analyzer"; ///< Scope, FFT-Display, Loudness Processing... -SMTG_CONSTEXPR const CString kFxDelay = "Fx|Delay"; ///< Delay, Multi-tap Delay, Ping-Pong Delay... -SMTG_CONSTEXPR const CString kFxDistortion = "Fx|Distortion"; ///< Amp Simulator, Sub-Harmonic, SoftClipper... -SMTG_CONSTEXPR const CString kFxDynamics = "Fx|Dynamics"; ///< Compressor, Expander, Gate, Limiter, Maximizer, Tape Simulator, EnvelopeShaper... -SMTG_CONSTEXPR const CString kFxEQ = "Fx|EQ"; ///< Equalization, Graphical EQ... -SMTG_CONSTEXPR const CString kFxFilter = "Fx|Filter"; ///< WahWah, ToneBooster, Specific Filter,... -SMTG_CONSTEXPR const CString kFxGenerator = "Fx|Generator"; ///< Tone Generator, Noise Generator... -SMTG_CONSTEXPR const CString kFxInstrument = "Fx|Instrument"; ///< Fx which could be loaded as Instrument too -SMTG_CONSTEXPR const CString kFxInstrumentExternal = "Fx|Instrument|External"; ///< Fx which could be loaded as Instrument too and is external (wrapped Hardware) -SMTG_CONSTEXPR const CString kFxMastering = "Fx|Mastering"; ///< Dither, Noise Shaping,... -SMTG_CONSTEXPR const CString kFxModulation = "Fx|Modulation"; ///< Phaser, Flanger, Chorus, Tremolo, Vibrato, AutoPan, Rotary, Cloner... -SMTG_CONSTEXPR const CString kFxNetwork = "Fx|Network"; ///< using Network -SMTG_CONSTEXPR const CString kFxPitchShift = "Fx|Pitch Shift"; ///< Pitch Processing, Pitch Correction, Vocal Tuning... -SMTG_CONSTEXPR const CString kFxRestoration = "Fx|Restoration"; ///< Denoiser, Declicker,... -SMTG_CONSTEXPR const CString kFxReverb = "Fx|Reverb"; ///< Reverberation, Room Simulation, Convolution Reverb... -SMTG_CONSTEXPR const CString kFxSpatial = "Fx|Spatial"; ///< MonoToStereo, StereoEnhancer,... -SMTG_CONSTEXPR const CString kFxSurround = "Fx|Surround"; ///< dedicated to surround processing: LFE Splitter, Bass Manager... -SMTG_CONSTEXPR const CString kFxTools = "Fx|Tools"; ///< Volume, Mixer, Tuner... -SMTG_CONSTEXPR const CString kFxVocals = "Fx|Vocals"; ///< Tools dedicated to vocals - -SMTG_CONSTEXPR const CString kInstrument = "Instrument"; ///< Effect used as instrument (sound generator), not as insert -SMTG_CONSTEXPR const CString kInstrumentDrum = "Instrument|Drum"; ///< Instrument for Drum sounds -SMTG_CONSTEXPR const CString kInstrumentExternal = "Instrument|External";///< External Instrument (wrapped Hardware) -SMTG_CONSTEXPR const CString kInstrumentPiano = "Instrument|Piano"; ///< Instrument for Piano sounds -SMTG_CONSTEXPR const CString kInstrumentSampler = "Instrument|Sampler"; ///< Instrument based on Samples -SMTG_CONSTEXPR const CString kInstrumentSynth = "Instrument|Synth"; ///< Instrument based on Synthesis -SMTG_CONSTEXPR const CString kInstrumentSynthSampler = "Instrument|Synth|Sampler"; ///< Instrument based on Synthesis and Samples - -SMTG_CONSTEXPR const CString kAmbisonics = "Ambisonics"; ///< used for Ambisonics channel (FX or Panner/Mixconverter/Up-Mixer/Down-Mixer when combined with other category) -SMTG_CONSTEXPR const CString kAnalyzer = "Analyzer"; ///< Meter, Scope, FFT-Display, not selectable as insert plug-in -SMTG_CONSTEXPR const CString kNoOfflineProcess = "NoOfflineProcess"; ///< will be NOT used for plug-in offline processing (will work as normal insert plug-in) -SMTG_CONSTEXPR const CString kOnlyARA = "OnlyARA"; ///< used for plug-ins that require ARA to operate (will not work as normal insert plug-in) -SMTG_CONSTEXPR const CString kOnlyOfflineProcess = "OnlyOfflineProcess"; ///< used for plug-in offline processing (will not work as normal insert plug-in) -SMTG_CONSTEXPR const CString kOnlyRealTime = "OnlyRT"; ///< indicates that it supports only realtime process call, no processing faster than realtime -SMTG_CONSTEXPR const CString kSpatial = "Spatial"; ///< used for SurroundPanner -SMTG_CONSTEXPR const CString kSpatialFx = "Spatial|Fx"; ///< used for SurroundPanner and as insert effect -SMTG_CONSTEXPR const CString kUpDownMix = "Up-Downmix"; ///< used for Mixconverter/Up-Mixer/Down-Mixer - -SMTG_CONSTEXPR const CString kMono = "Mono"; ///< used for Mono only plug-in [optional] -SMTG_CONSTEXPR const CString kStereo = "Stereo"; ///< used for Stereo only plug-in [optional] -SMTG_CONSTEXPR const CString kSurround = "Surround"; ///< used for Surround only plug-in [optional] +const CString kFxAnalyzer = "Fx|Analyzer"; ///< Scope, FFT-Display, Loudness Processing... +const CString kFxDelay = "Fx|Delay"; ///< Delay, Multi-tap Delay, Ping-Pong Delay... +const CString kFxDistortion = "Fx|Distortion"; ///< Amp Simulator, Sub-Harmonic, SoftClipper... +const CString kFxDynamics = "Fx|Dynamics"; ///< Compressor, Expander, Gate, Limiter, Maximizer, Tape Simulator, EnvelopeShaper... +const CString kFxEQ = "Fx|EQ"; ///< Equalization, Graphical EQ... +const CString kFxFilter = "Fx|Filter"; ///< WahWah, ToneBooster, Specific Filter,... +const CString kFx = "Fx"; ///< others type (not categorized) +const CString kFxInstrument = "Fx|Instrument"; ///< Fx which could be loaded as Instrument too +const CString kFxInstrumentExternal = "Fx|Instrument|External"; ///< Fx which could be loaded as Instrument too and is external (wrapped Hardware) +const CString kFxSpatial = "Fx|Spatial"; ///< MonoToStereo, StereoEnhancer,... +const CString kFxGenerator = "Fx|Generator"; ///< Tone Generator, Noise Generator... +const CString kFxMastering = "Fx|Mastering"; ///< Dither, Noise Shaping,... +const CString kFxModulation = "Fx|Modulation"; ///< Phaser, Flanger, Chorus, Tremolo, Vibrato, AutoPan, Rotary, Cloner... +const CString kFxPitchShift = "Fx|Pitch Shift"; ///< Pitch Processing, Pitch Correction, Vocal Tuning... +const CString kFxRestoration = "Fx|Restoration"; ///< Denoiser, Declicker,... +const CString kFxReverb = "Fx|Reverb"; ///< Reverberation, Room Simulation, Convolution Reverb... +const CString kFxSurround = "Fx|Surround"; ///< dedicated to surround processing: LFE Splitter, Bass Manager... +const CString kFxTools = "Fx|Tools"; ///< Volume, Mixer, Tuner... +const CString kFxNetwork = "Fx|Network"; ///< using Network + +const CString kInstrument = "Instrument"; ///< Effect used as instrument (sound generator), not as insert +const CString kInstrumentDrum = "Instrument|Drum"; ///< Instrument for Drum sounds +const CString kInstrumentExternal = "Instrument|External";///< External Instrument (wrapped Hardware) +const CString kInstrumentPiano = "Instrument|Piano"; ///< Instrument for Piano sounds +const CString kInstrumentSampler = "Instrument|Sampler"; ///< Instrument based on Samples +const CString kInstrumentSynth = "Instrument|Synth"; ///< Instrument based on Synthesis +const CString kInstrumentSynthSampler = "Instrument|Synth|Sampler"; ///< Instrument based on Synthesis and Samples + +const CString kSpatial = "Spatial"; ///< used for SurroundPanner +const CString kSpatialFx = "Spatial|Fx"; ///< used for SurroundPanner and as insert effect +const CString kOnlyRealTime = "OnlyRT"; ///< indicates that it supports only realtime process call, no processing faster than realtime +const CString kOnlyOfflineProcess = "OnlyOfflineProcess"; ///< used for plug-in offline processing (will not work as normal insert plug-in) +const CString kOnlyARA = "OnlyARA"; ///< used for plug-ins that require ARA to operate (will not work as normal insert plug-in) + +const CString kNoOfflineProcess = "NoOfflineProcess"; ///< will be NOT used for plug-in offline processing (will work as normal insert plug-in) +const CString kUpDownMix = "Up-Downmix"; ///< used for Mixconverter/Up-Mixer/Down-Mixer +const CString kAnalyzer = "Analyzer"; ///< Meter, Scope, FFT-Display, not selectable as insert plug-in +const CString kAmbisonics = "Ambisonics"; ///< used for Ambisonics channel (FX or Panner/Mixconverter/Up-Mixer/Down-Mixer when combined with other category) + +const CString kMono = "Mono"; ///< used for Mono only plug-in [optional] +const CString kStereo = "Stereo"; ///< used for Stereo only plug-in [optional] +const CString kSurround = "Surround"; ///< used for Surround only plug-in [optional] //------------------------------------------------------------------------ /*@}*/ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivsteditcontroller.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivsteditcontroller.h index d7c8925c..7bd1a487 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivsteditcontroller.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivsteditcontroller.h @@ -146,14 +146,7 @@ enum RestartFlags * The host ask the plug-in for the new routing with IComponent::getRoutingInfo, \ref vst3Routing * see IComponent * [SDK 3.6.6] */ - kRoutingInfoChanged = 1 << 9, - - /** Key switches has changed (info, count) - * Either the Key switches info, the count of Key switches has changed. - * The host invalidates all caches of Key switches infos and asks the edit controller (IKeyswitchController) for the current ones. - * See IKeyswitchController - * [SDK 3.7.3] */ - kKeyswitchChanged = 1 << 10 + kRoutingInfoChanged = 1 << 9 }; //------------------------------------------------------------------------ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstparameterchanges.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstparameterchanges.h index 1d216c1a..ce9dec06 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstparameterchanges.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstparameterchanges.h @@ -128,7 +128,7 @@ class IParameterChanges : public FUnknown /** Adds a new parameter queue with a given ID at the end of the list, returns it and its index in the parameter changes list. */ - virtual IParamValueQueue* PLUGIN_API addParameterData (const ParamID& id, int32& index /*out*/) = 0; + virtual IParamValueQueue* PLUGIN_API addParameterData (const Vst::ParamID& id, int32& index /*out*/) = 0; //------------------------------------------------------------------------ static const FUID iid; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstparameterfunctionname.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstparameterfunctionname.h index ca2f2a50..8e9a5ab9 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstparameterfunctionname.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivstparameterfunctionname.h @@ -26,6 +26,7 @@ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { + namespace FunctionNameType { //-------------------------------------------------------------------- const CString kCompGainReduction = "Comp:GainReduction"; /** */ @@ -44,12 +45,6 @@ namespace FunctionNameType { const CString kRandomize = "Randomize"; /** Allow to assign some randomized values to some parameters in a controlled way*/ - /// Panner Type - const CString kPanPosCenterX = "PanPosCenterX"; ///< Gravity point X-axis [0, 1]=>[L-R] (for stereo: middle between left and right) - const CString kPanPosCenterY = "PanPosCenterY"; ///< Gravity point Y-axis [0, 1]=>[Front-Rear] - const CString kPanPosCenterZ = "PanPosCenterZ"; ///< Gravity point Z-axis [0, 1]=>[Bottom-Top] - - } // FunctionNameType //------------------------------------------------------------------------ @@ -60,11 +55,10 @@ namespace FunctionNameType { - [released: 3.7.0] - [optional] -This interface allows the host to get a parameter associated to a specific meaning (a functionName) -for a given unit. The host can use this information, for example, for drawing a Gain Reduction meter -in its own UI. In order to get the plain value of this parameter, the host should use the -IEditController::normalizedParamToPlain. The host can automatically map parameters to dedicated UI -controls, such as the wet-dry mix knob or Randomize button. +This interface allows the host to get a parameter associated to a specific meaning (a functionName) for a given unit. +The host can use this information, for example, for drawing a Gain Reduction meter in its own UI. +In order to get the plain value of this parameter, the host should use the IEditController::normalizedParamToPlain. +The host can automatically map parameters to dedicated UI controls, such as the wet-dry mix knob or Randomize button. \section IParameterFunctionNameExample Example @@ -76,39 +70,38 @@ controls, such as the wet-dry mix knob or Randomize button. in MyController class declaration class MyController : public Vst::EditController, public Vst::IParameterFunctionName { - ... - tresult PLUGIN_API getParameterIDFromFunctionName (UnitID unitID, FIDString functionName, - Vst::ParamID& paramID) override; - ... - - OBJ_METHODS (MyController, Vst::EditController) - DEFINE_INTERFACES - ... - DEF_INTERFACE (Vst::IParameterFunctionName) - END_DEFINE_INTERFACES (Vst::EditController) - DELEGATE_REFCOUNT (Vst::EditController) - ... + ... + tresult PLUGIN_API getParameterIDFromFunctionName (UnitID unitID, FIDString functionName, + Vst::ParamID& paramID) override; + ... + + OBJ_METHODS (MyController, Vst::EditController) + DEFINE_INTERFACES + ... + DEF_INTERFACE (Vst::IParameterFunctionName) + END_DEFINE_INTERFACES (Vst::EditController) + ... } #include "ivstparameterfunctionname.h" namespace Steinberg { - namespace Vst { - DEF_CLASS_IID (IParameterFunctionName) - } + namespace Vst { + DEF_CLASS_IID (IParameterFunctionName) + } } //------------------------------------------------------------------------ -tresult PLUGIN_API MyController::getParameterIDFromFunctionName (UnitID unitID, FIDString -functionName, Vst::ParamID& paramID) +tresult PLUGIN_API MyController::getParameterIDFromFunctionName (UnitID unitID, FIDString functionName, + Vst::ParamID& paramID) { - using namespace Vst; + using namespace Vst; - paramID = kNoParamId; + paramID = kNoParamId; - if (unitID == kRootUnitId && FIDStringsEqual (functionName, kCompGainReduction)) - paramID = kMyGainReductionId; + if (unitID == kRootUnitId && FIDStringsEqual (functionName, kCompGainReduction)) + paramID = kMyGainReductionId; - return (paramID != kNoParamId) ? kResultOk : kResultFalse; + return (paramID != kNoParamId) ? kResultOk : kResultFalse; } //--- a host implementation example: -------------------- @@ -116,15 +109,14 @@ functionName, Vst::ParamID& paramID) FUnknownPtr functionName (mEditController->getIEditController ()); if (functionName) { - Vst::ParamID paramID; - if (functionName->getParameterIDFromFunctionName (kRootUnitId, - Vst::FunctionNameType::kCompGainReduction, paramID) == kResultTrue) - { - // paramID could be cached for performance issue - ParamValue norm = mEditController->getIEditController ()->getParamNormalized (paramID); - ParamValue plain = mEditController->getIEditController ()->normalizedParamToPlain (paramID, norm); - // plain is something like -6 (-6dB) - } + Vst::ParamID paramID; + if (functionName->getParameterIDFromFunctionName (Vst::FunctionNameType::kCompGainReduction, paramID) == kResultTrue) + { + // paramID could be cached for performance issue + ParamValue norm = mEditController->getIEditController ()->getParamNormalized (paramID); + ParamValue plain = mEditController->getIEditController ()->normalizedParamToPlain (paramID, norm); + // plain is something like -6 (-6dB) + } } \endcode */ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivsttestplugprovider.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivsttestplugprovider.h index ffbcec49..eb9923f5 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivsttestplugprovider.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/ivsttestplugprovider.h @@ -9,7 +9,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2020, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/vstspeaker.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/vstspeaker.h index 0d21684f..0daa490a 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/vstspeaker.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/vstspeaker.h @@ -76,15 +76,6 @@ const Speaker kSpeakerACN12 = (Speaker)1 << 46; ///< Ambisonic ACN 12 const Speaker kSpeakerACN13 = (Speaker)1 << 47; ///< Ambisonic ACN 13 const Speaker kSpeakerACN14 = (Speaker)1 << 48; ///< Ambisonic ACN 14 const Speaker kSpeakerACN15 = (Speaker)1 << 49; ///< Ambisonic ACN 15 -const Speaker kSpeakerACN16 = (Speaker)1 << 50; ///< Ambisonic ACN 16 -const Speaker kSpeakerACN17 = (Speaker)1 << 51; ///< Ambisonic ACN 17 -const Speaker kSpeakerACN18 = (Speaker)1 << 52; ///< Ambisonic ACN 18 -const Speaker kSpeakerACN19 = (Speaker)1 << 53; ///< Ambisonic ACN 19 -const Speaker kSpeakerACN20 = (Speaker)1 << 54; ///< Ambisonic ACN 20 -const Speaker kSpeakerACN21 = (Speaker)1 << 55; ///< Ambisonic ACN 21 -const Speaker kSpeakerACN22 = (Speaker)1 << 56; ///< Ambisonic ACN 22 -const Speaker kSpeakerACN23 = (Speaker)1 << 57; ///< Ambisonic ACN 23 -const Speaker kSpeakerACN24 = (Speaker)1 << 58; ///< Ambisonic ACN 24 const Speaker kSpeakerTsl = (Speaker)1 << 24; ///< Top Side Left (Tsl) const Speaker kSpeakerTsr = (Speaker)1 << 25; ///< Top Side Right (Tsr) @@ -112,7 +103,6 @@ namespace SpeakerArr { //------------------------------------------------------------------------ /** Speaker Arrangement Definitions. -* for example: 5.0.5.3 for 5x Middle + 0x LFE + 5x Top + 3x Bottom \ingroup speakerArrangements */ /*@{*/ const SpeakerArrangement kEmpty = 0; ///< empty arrangement @@ -126,46 +116,45 @@ const SpeakerArrangement kStereoTF = kSpeakerTfl | kSpeakerTfr; ///< Tfl Tfr const SpeakerArrangement kStereoTS = kSpeakerTsl | kSpeakerTsr; ///< Tsl Tsr const SpeakerArrangement kStereoTR = kSpeakerTrl | kSpeakerTrr; ///< Trl Trr const SpeakerArrangement kStereoBF = kSpeakerBfl | kSpeakerBfr; ///< Bfl Bfr -/** L R C Lc Rc */ -const SpeakerArrangement kCineFront = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLc | kSpeakerRc; +const SpeakerArrangement kCineFront = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLc | kSpeakerRc; ///< L R C Lc Rc -/** L R C */ // 3.0 +/** L R C */ const SpeakerArrangement k30Cine = kSpeakerL | kSpeakerR | kSpeakerC; -/** L R C Lfe */ // 3.1 -const SpeakerArrangement k31Cine = k30Cine | kSpeakerLfe; +/** L R C Lfe */ +const SpeakerArrangement k31Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe; /** L R S */ const SpeakerArrangement k30Music = kSpeakerL | kSpeakerR | kSpeakerCs; /** L R Lfe S */ -const SpeakerArrangement k31Music = k30Music | kSpeakerLfe; -/** L R C S */ // LCRS +const SpeakerArrangement k31Music = kSpeakerL | kSpeakerR | kSpeakerLfe | kSpeakerCs; +/** L R C S (LCRS) */ const SpeakerArrangement k40Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerCs; -/** L R C Lfe S */ // LCRS+Lfe -const SpeakerArrangement k41Cine = k40Cine | kSpeakerLfe; -/** L R Ls Rs */ // 4.0 (Quadro) +/** L R C Lfe S (LCRS+Lfe) */ +const SpeakerArrangement k41Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerCs; +/** L R Ls Rs (Quadro) */ const SpeakerArrangement k40Music = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs; -/** L R Lfe Ls Rs */ // 4.1 (Quadro+Lfe) -const SpeakerArrangement k41Music = k40Music | kSpeakerLfe; -/** L R C Ls Rs */ // 5.0 (ITU 0+5+0.0 Sound System B) +/** L R Lfe Ls Rs (Quadro+Lfe) */ +const SpeakerArrangement k41Music = kSpeakerL | kSpeakerR | kSpeakerLfe | kSpeakerLs | kSpeakerRs; +/** L R C Ls Rs */ const SpeakerArrangement k50 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs; -/** L R C Lfe Ls Rs */ // 5.1 (ITU 0+5+0.1 Sound System B) -const SpeakerArrangement k51 = k50 | kSpeakerLfe; +/** L R C Lfe Ls Rs */ +const SpeakerArrangement k51 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs; /** L R C Ls Rs Cs */ const SpeakerArrangement k60Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerCs; /** L R C Lfe Ls Rs Cs */ -const SpeakerArrangement k61Cine = k60Cine | kSpeakerLfe; +const SpeakerArrangement k61Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerCs; /** L R Ls Rs Sl Sr */ const SpeakerArrangement k60Music = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr; /** L R Lfe Ls Rs Sl Sr */ -const SpeakerArrangement k61Music = k60Music | kSpeakerLfe; +const SpeakerArrangement k61Music = kSpeakerL | kSpeakerR | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr; /** L R C Ls Rs Lc Rc */ const SpeakerArrangement k70Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc; /** L R C Lfe Ls Rs Lc Rc */ -const SpeakerArrangement k71Cine = k70Cine | kSpeakerLfe; +const SpeakerArrangement k71Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc; const SpeakerArrangement k71CineFullFront = k71Cine; -/** L R C Ls Rs Sl Sr */ // (ITU 0+7+0.0 Sound System I) +/** L R C Ls Rs Sl Sr */ const SpeakerArrangement k70Music = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr; -/** L R C Lfe Ls Rs Sl Sr */ // (ITU 0+7+0.1 Sound System I) -const SpeakerArrangement k71Music = k70Music | kSpeakerLfe; +/** L R C Lfe Ls Rs Sl Sr */ +const SpeakerArrangement k71Music = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr; /** L R C Lfe Ls Rs Lcs Rcs */ const SpeakerArrangement k71CineFullRear = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerLcs | kSpeakerRcs; @@ -176,110 +165,76 @@ const SpeakerArrangement k71Proximity = kSpeakerL | kSpeakerR | kSpeakerC | /** L R C Ls Rs Lc Rc Cs */ const SpeakerArrangement k80Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerCs; /** L R C Lfe Ls Rs Lc Rc Cs */ -const SpeakerArrangement k81Cine = k80Cine | kSpeakerLfe; +const SpeakerArrangement k81Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerCs; /** L R C Ls Rs Cs Sl Sr */ const SpeakerArrangement k80Music = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerCs | kSpeakerSl | kSpeakerSr; /** L R C Lfe Ls Rs Cs Sl Sr */ -const SpeakerArrangement k81Music = k80Music | kSpeakerLfe; +const SpeakerArrangement k81Music = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerCs | kSpeakerSl | kSpeakerSr; /** L R C Ls Rs Lc Rc Sl Sr */ const SpeakerArrangement k90Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerSl | kSpeakerSr; /** L R C Lfe Ls Rs Lc Rc Sl Sr */ -const SpeakerArrangement k91Cine = k90Cine | kSpeakerLfe; +const SpeakerArrangement k91Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | + kSpeakerSl | kSpeakerSr; /** L R C Ls Rs Lc Rc Cs Sl Sr */ const SpeakerArrangement k100Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerCs | kSpeakerSl | kSpeakerSr; /** L R C Lfe Ls Rs Lc Rc Cs Sl Sr */ -const SpeakerArrangement k101Cine = k100Cine | kSpeakerLfe; +const SpeakerArrangement k101Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerCs | + kSpeakerSl | kSpeakerSr; -/** First-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (4 channels) */ +/** First-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization */ const SpeakerArrangement kAmbi1stOrderACN = kSpeakerACN0 | kSpeakerACN1 | kSpeakerACN2 | kSpeakerACN3; -/** Second-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (9 channels) */ +/** Second-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization */ const SpeakerArrangement kAmbi2cdOrderACN = kAmbi1stOrderACN | kSpeakerACN4 | kSpeakerACN5 | kSpeakerACN6 | kSpeakerACN7 | kSpeakerACN8; -/** Third-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (16 channels) */ +/** Third-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization */ const SpeakerArrangement kAmbi3rdOrderACN = kAmbi2cdOrderACN | kSpeakerACN9 | kSpeakerACN10 | kSpeakerACN11 | kSpeakerACN12 | kSpeakerACN13 | kSpeakerACN14 | kSpeakerACN15; -/** Fourth-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (25 channels) */ -const SpeakerArrangement kAmbi4thOrderACN = kAmbi3rdOrderACN | kSpeakerACN16 | kSpeakerACN17 | kSpeakerACN18 | kSpeakerACN19 | kSpeakerACN20 | - kSpeakerACN21 | kSpeakerACN22 | kSpeakerACN23 | kSpeakerACN24; -/** Fifth-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (36 channels) */ -const SpeakerArrangement kAmbi5thOrderACN = 0x000FFFFFFFFF; -/** Sixth-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (49 channels) */ -const SpeakerArrangement kAmbi6thOrderACN = 0x0001FFFFFFFFFFFF; -/** Seventh-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (64 channels) */ -const SpeakerArrangement kAmbi7thOrderACN = 0xFFFFFFFFFFFFFFFF; + /*-----------*/ /* 3D formats */ /*-----------*/ /** L R Ls Rs Tfl Tfr Trl Trr */ // 4.0.4 const SpeakerArrangement k80Cube = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | kSpeakerTfl| kSpeakerTfr| kSpeakerTrl | kSpeakerTrr; -const SpeakerArrangement k40_4 = k80Cube; - /** L R C Lfe Ls Rs Cs Tc */ // 6.1.1 const SpeakerArrangement k71CineTopCenter = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerCs | kSpeakerTc; - /** L R C Lfe Ls Rs Cs Tfc */ // 6.1.1 const SpeakerArrangement k71CineCenterHigh = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerCs | kSpeakerTfc; +/** L R C Lfe Ls Rs Tfl Tfr */ // 5.1.2 +const SpeakerArrangement k71CineFrontHigh = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfr; +const SpeakerArrangement k71MPEG3D = k71CineFrontHigh; +/** L R C Lfe Ls Rs Tsl Tsr */ // 5.1.2 +const SpeakerArrangement k71CineSideHigh = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerTsl | kSpeakerTsr; -/** L R C Ls Rs Tfl Tfr */ // 5.0.2 (ITU 2+5+0.0 Sound System C) -const SpeakerArrangement k70CineFrontHigh = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfr; -const SpeakerArrangement k70MPEG3D = k70CineFrontHigh; -const SpeakerArrangement k50_2 = k70CineFrontHigh; - -/** L R C Lfe Ls Rs Tfl Tfr */ // 5.1.2 (ITU 2+5+0.1 Sound System C) -const SpeakerArrangement k71CineFrontHigh = k70CineFrontHigh | kSpeakerLfe; -const SpeakerArrangement k71MPEG3D = k71CineFrontHigh; -const SpeakerArrangement k51_2 = k71CineFrontHigh; - -/** L R C Ls Rs Tsl Tsr */ // 5.0.2 (Side) -const SpeakerArrangement k70CineSideHigh = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTsl | kSpeakerTsr; -const SpeakerArrangement k50_2_TS = k70CineSideHigh; - -/** L R C Lfe Ls Rs Tsl Tsr */ // 5.1.2 (Side) -const SpeakerArrangement k71CineSideHigh = k70CineSideHigh | kSpeakerLfe; -const SpeakerArrangement k51_2_TS = k71CineSideHigh; - -/** L R Lfe Ls Rs Tfl Tfc Tfr Bfc */ // 4.1.3.1 +/** L R Lfe Ls Rs Tfl Tfc Tfr Bfc */ // 4.1.4 const SpeakerArrangement k81MPEG3D = kSpeakerL | kSpeakerR | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerBfc; -const SpeakerArrangement k41_4_1 = k81MPEG3D; -/** L R C Ls Rs Tfl Tfr Trl Trr */ // 5.0.4 (ITU 4+5+0.0 Sound System D) +/** L R C Ls Rs Tfl Tfr Trl Trr */ // 5.0.4 const SpeakerArrangement k90 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTfl| kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; const SpeakerArrangement k50_4 = k90; - -/** L R C Lfe Ls Rs Tfl Tfr Trl Trr */ // 5.1.4 (ITU 4+5+0.1 Sound System D) -const SpeakerArrangement k91 = k90 | kSpeakerLfe; +/** L R C Lfe Ls Rs Tfl Tfr Trl Trr */ // 5.1.4 +const SpeakerArrangement k91 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | + kSpeakerTfl| kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; const SpeakerArrangement k51_4 = k91; -/** L R C Ls Rs Tfl Tfr Trl Trr Bfc */ // 5.0.4.1 (ITU 4+5+1.0 Sound System E) -const SpeakerArrangement k50_4_1 = k50_4 | kSpeakerBfc; - -/** L R C Lfe Ls Rs Tfl Tfr Trl Trr Bfc */ // 5.1.4.1 (ITU 4+5+1.1 Sound System E) -const SpeakerArrangement k51_4_1 = k50_4_1 | kSpeakerLfe; - /** L R C Ls Rs Sl Sr Tsl Tsr */ // 7.0.2 const SpeakerArrangement k70_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | kSpeakerTsl | kSpeakerTsr; /** L R C Lfe Ls Rs Sl Sr Tsl Tsr */ // 7.1.2 -const SpeakerArrangement k71_2 = k70_2 | kSpeakerLfe; +const SpeakerArrangement k71_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | + kSpeakerSl | kSpeakerSr | kSpeakerTsl | kSpeakerTsr; const SpeakerArrangement k91Atmos = k71_2; // 9.1 Dolby Atmos (3D) -/** L R C Ls Rs Sl Sr Tfl Tfr Trc */ // 7.0.3 (ITU 3+7+0.0 Sound System F) -const SpeakerArrangement k70_3 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | - kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrc; - -/** L R C Lfe Ls Rs Sl Sr Tfl Tfr Trc Lfe2 */ // 7.2.3 (ITU 3+7+0.2 Sound System F) -const SpeakerArrangement k72_3 = k70_3 | kSpeakerLfe | kSpeakerLfe2; - -/** L R C Ls Rs Sl Sr Tfl Tfr Trl Trr */ // 7.0.4 (ITU 4+7+0.0 Sound System J) +/** L R C Ls Rs Sl Sr Tfl Tfr Trl Trr */ // 7.0.4 const SpeakerArrangement k70_4 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; -/** L R C Lfe Ls Rs Sl Sr Tfl Tfr Trl Trr */ // 7.1.4 (ITU 4+7+0.1 Sound System J) -const SpeakerArrangement k71_4 = k70_4 | kSpeakerLfe; +/** L R C Lfe Ls Rs Sl Sr Tfl Tfr Trl Trr */ // 7.1.4 +const SpeakerArrangement k71_4 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | + kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; const SpeakerArrangement k111MPEG3D = k71_4; /** L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr */ // 7.0.6 @@ -288,15 +243,19 @@ const SpeakerArrangement k70_6 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerTsl | kSpeakerTsr; /** L R C Lfe Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr */ // 7.1.6 -const SpeakerArrangement k71_6 = k70_6 | kSpeakerLfe; +const SpeakerArrangement k71_6 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | + kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | + kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerTsl | kSpeakerTsr; -/** L R C Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr */ // 9.0.4 (ITU 4+9+0.0 Sound System G) +/** L R C Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr */ // 9.0.4 const SpeakerArrangement k90_4 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; -/** L R C Lfe Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr */ // 9.1.4 (ITU 4+9+0.1 Sound System G) -const SpeakerArrangement k91_4 = k90_4 | kSpeakerLfe; +/** L R C Lfe Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr */ // 9.1.4 +const SpeakerArrangement k91_4 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | + kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerSl | kSpeakerSr | + kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; /** L R C Lfe Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr Tsl Tsr */ // 9.0.6 const SpeakerArrangement k90_6 = kSpeakerL | kSpeakerR | kSpeakerC | @@ -304,118 +263,53 @@ const SpeakerArrangement k90_6 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerTsl | kSpeakerTsr; /** L R C Lfe Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr Tsl Tsr */ // 9.1.6 -const SpeakerArrangement k91_6 = k90_6 | kSpeakerLfe; +const SpeakerArrangement k91_6 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | + kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerSl | kSpeakerSr | + kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerTsl | kSpeakerTsr; -/** L R C Ls Rs Tc Tfl Tfr Trl Trr */ // 5.0.5 (10.0 Auro-3D) +/** L R C Ls Rs Tc Tfl Tfr Trl Trr */ // 5.0.5 const SpeakerArrangement k100 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTc | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; -const SpeakerArrangement k50_5 = k100; - -/** L R C Lfe Ls Rs Tc Tfl Tfr Trl Trr */ // 5.1.5 (10.1 Auro-3D) -const SpeakerArrangement k101 = k50_5 | kSpeakerLfe; +/** L R C Lfe Ls Rs Tc Tfl Tfr Trl Trr */ // 5.1.5 +const SpeakerArrangement k101 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | + kSpeakerTc | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; const SpeakerArrangement k101MPEG3D = k101; -const SpeakerArrangement k51_5 = k101; /** L R C Lfe Ls Rs Tfl Tfc Tfr Trl Trr Lfe2 */ // 5.2.5 const SpeakerArrangement k102 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerTfl| kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerLfe2; -const SpeakerArrangement k52_5 = k102; -/** L R C Ls Rs Tc Tfl Tfc Tfr Trl Trr */ // 5.0.6 (11.0 Auro-3D) +/** L R C Ls Rs Tc Tfl Tfc Tfr Trl Trr */ // 5.0.6 const SpeakerArrangement k110 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTc | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; -const SpeakerArrangement k50_6 = k110; - -/** L R C Lfe Ls Rs Tc Tfl Tfc Tfr Trl Trr */ // 5.1.6 (11.1 Auro-3D) -const SpeakerArrangement k111 = k110 | kSpeakerLfe; -const SpeakerArrangement k51_6 = k111; +/** L R C Lfe Ls Rs Tc Tfl Tfc Tfr Trl Trr */ // 5.1.6 +const SpeakerArrangement k111 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | + kSpeakerTc | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; /** L R C Lfe Ls Rs Lc Rc Tfl Tfc Tfr Trl Trr Lfe2 */ // 7.2.5 const SpeakerArrangement k122 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerTfl| kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerLfe2; -const SpeakerArrangement k72_5 = k122; - -/** L R C Ls Rs Sl Sr Tc Tfl Tfc Tfr Trl Trr */ // 7.0.6 (13.0 Auro-3D) +/** L R C Ls Rs Sl Sr Tc Tfl Tfc Tfr Trl Trr */ // 7.0.6 const SpeakerArrangement k130 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | kSpeakerTc | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; +/** L R C Lfe Ls Rs Sl Sr Tc Tfl Tfc Tfr Trl Trr */ // 7.1.6 +const SpeakerArrangement k131 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | + kSpeakerTc | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; -/** L R C Lfe Ls Rs Sl Sr Tc Tfl Tfc Tfr Trl Trr */ // 7.1.6 (13.1 Auro-3D) -const SpeakerArrangement k131 = k130 | kSpeakerLfe; - -/** L R Ls Rs Sl Sr Tfl Tfr Trl Trr Bfl Bfr Brl Brr */ // 6.0.4.4 -const SpeakerArrangement k140 = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | +/** L R Ls Rs Sl Sr Tfl Tfr Trl Trr Bfl Bfr Brl Brr */ // 6.0.4.4 +const SpeakerArrangement k140 = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerBfl | kSpeakerBfr | kSpeakerBrl | kSpeakerBrr; -const SpeakerArrangement k60_4_4 = k140; -/** L R C Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr Tsl Tsr Bfl Bfc Bfr */ // 10.0.9.3 (ITU 9+10+3.0 Sound System H) +/** L R C Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr Tsl Tsr Bfl Bfc Bfr */ // 10.0.9.3 const SpeakerArrangement k220 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerCs | kSpeakerSl | kSpeakerSr | kSpeakerTc | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrc | kSpeakerTrr | kSpeakerTsl | kSpeakerTsr | kSpeakerBfl| kSpeakerBfc | kSpeakerBfr; -const SpeakerArrangement k100_9_3 = k220; -/** L R C Lfe Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr Lfe2 Tsl Tsr Bfl Bfc Bfr */ // 10.2.9.3 (ITU 9+10+3.2 Sound System H) +/** L R C Lfe Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr Lfe2 Tsl Tsr Bfl Bfc Bfr */ // 10.2.9.3 const SpeakerArrangement k222 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerCs | kSpeakerSl | kSpeakerSr | kSpeakerTc | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrc | kSpeakerTrr | kSpeakerLfe2 | kSpeakerTsl | kSpeakerTsr | kSpeakerBfl| kSpeakerBfc | kSpeakerBfr; -const SpeakerArrangement k102_9_3 = k222; - -/** L R C Ls Rs Tfl Tfc Tfr Trl Trr Bfl Bfc Bfr */ // 5.0.5.3 -const SpeakerArrangement k50_5_3 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | - kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | - kSpeakerBfl | kSpeakerBfc | kSpeakerBfr; - -/** L R C Lfe Ls Rs Tfl Tfc Tfr Trl Trr Bfl Bfc Bfr */ // 5.1.5.3 -const SpeakerArrangement k51_5_3 = k50_5_3 | kSpeakerLfe; - -/** L R C Ls Rs Tsl Tsr Bfl Bfr */ // 5.0.2.2 -const SpeakerArrangement k50_2_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | - kSpeakerTsl | kSpeakerTsr | - kSpeakerBfl | kSpeakerBfr; - -/** L R C Ls Rs Tfl Tfr Trl Trr Bfl Bfr */ // 5.0.4.2 -const SpeakerArrangement k50_4_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | - kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | - kSpeakerBfl | kSpeakerBfr; - -/** L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Bfl Bfr */ // 7.0.4.2 -const SpeakerArrangement k70_4_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | - kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | - kSpeakerBfl | kSpeakerBfr; - -/** L R C Ls Rs Tfl Tfc Tfr Trl Trr */ // 5.0.5.0 (Sony 360RA) -const SpeakerArrangement k50_5_Sony = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | - kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; - -/** C Sl Sr Cs Tsl Tsr Bsl Bsr */ // 4.0.2.2 (Sony 360RA) -const SpeakerArrangement k40_2_2 = kSpeakerC | kSpeakerSl | kSpeakerSr | kSpeakerCs | - kSpeakerTsl | kSpeakerTsr | - kSpeakerBsl | kSpeakerBsr; - -/** L R Ls Rs Tfl Tfr Trl Trr Bfl Bfr */ // 4.0.4.2 (Sony 360RA) -const SpeakerArrangement k40_4_2 = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | - kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | - kSpeakerBfl | kSpeakerBfr; - -/** L R C Ls Rs Tfl Tfc Tfr Bfl Bfr */ // 5.0.3.2 (Sony 360RA) -const SpeakerArrangement k50_3_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | - kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | - kSpeakerBfl | kSpeakerBfr; - -/** L R C Tfl Tfc Tfr Trl Trr Bfl Bfr */ // 3.0.5.2 (Sony 360RA) -const SpeakerArrangement k30_5_2 = kSpeakerL | kSpeakerR | kSpeakerC | - kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | - kSpeakerBfl | kSpeakerBfr; - -/** L R Ls Rs Tfl Tfr Trl Trr Bfl Bfr Brl Brr */ // 4.0.4.4 (Sony 360RA) -const SpeakerArrangement k40_4_4 = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | - kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | - kSpeakerBfl | kSpeakerBfr | kSpeakerBrl | kSpeakerBrr; - -/** L R C Ls Rs Tfl Tfr Trl Trr Bfl Bfr Brl Brr */ // 5.0.4.4 (Sony 360RA) -const SpeakerArrangement k50_4_4 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | - kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | - kSpeakerBfl | kSpeakerBfr | kSpeakerBrl | kSpeakerBrr; //------------------------------------------------------------------------ /** Speaker Arrangement String Representation. @@ -458,69 +352,47 @@ const CString kString71Music = "7.1"; const CString kString71MusicOld = "7.1 Music (Dolby)"; const CString kString71CineTopCenter = "7.1 Cine Top Center"; const CString kString71CineCenterHigh = "7.1 Cine Center High"; -const CString kString71CineFullRear = "7.1 Cine Full Rear"; -const CString kString51_2 = "5.1.2"; -const CString kString50_2 = "5.0.2"; -const CString kString50_2TopSide = "5.0.2 Top Side"; -const CString kString51_2TopSide = "5.1.2 Top Side"; -const CString kString71Proximity = "7.1 Proximity"; +const CString kString71CineFrontHigh = "7.1 Cine Front High"; +const CString kString71CineSideHigh = "7.1 Cine Side High"; +const CString kString71CineFullRear = "7.1 Cine Full Rear"; +const CString kString71Proximity = "7.1 Proximity"; const CString kString80Cine = "8.0 Cine"; const CString kString80Music = "8.0 Music"; -const CString kString40_4 = "8.0 Cube"; +const CString kString80Cube = "8.0 Cube"; const CString kString81Cine = "8.1 Cine"; const CString kString81Music = "8.1 Music"; const CString kString90Cine = "9.0 Cine"; const CString kString91Cine = "9.1 Cine"; const CString kString100Cine = "10.0 Cine"; const CString kString101Cine = "10.1 Cine"; -const CString kString52_5 = "5.2.5"; -const CString kString72_5 = "12.2"; +const CString kString102 = "10.2 Experimental"; +const CString kString122 = "12.2"; const CString kString50_4 = "5.0.4"; const CString kString51_4 = "5.1.4"; -const CString kString50_4_1 = "5.0.4.1"; -const CString kString51_4_1 = "5.1.4.1"; -const CString kString70_2 = "7.0.2"; +const CString kString70_2 = "7.0.2"; const CString kString71_2 = "7.1.2"; -const CString kString70_3 = "7.0.3"; -const CString kString72_3 = "7.2.3"; const CString kString70_4 = "7.0.4"; const CString kString71_4 = "7.1.4"; -const CString kString70_6 = "7.0.6"; +const CString kString70_6 = "7.0.6"; const CString kString71_6 = "7.1.6"; -const CString kString90_4 = "9.0.4"; +const CString kString90_4 = "9.0.4"; const CString kString91_4 = "9.1.4"; -const CString kString90_6 = "9.0.6"; +const CString kString90_6 = "9.0.6"; const CString kString91_6 = "9.1.6"; -const CString kString50_5 = "10.0 Auro-3D"; -const CString kString51_5 = "10.1 Auro-3D"; -const CString kString50_6 = "11.0 Auro-3D"; -const CString kString51_6 = "11.1 Auro-3D"; +const CString kString100 = "10.0 Auro-3D"; +const CString kString101 = "10.1 Auro-3D"; +const CString kString110 = "11.0 Auro-3D"; +const CString kString111 = "11.1 Auro-3D"; const CString kString130 = "13.0 Auro-3D"; const CString kString131 = "13.1 Auro-3D"; -const CString kString41_4_1 = "8.1 MPEG"; -const CString kString60_4_4 = "14.0"; -const CString kString220 = "22.0"; +const CString kString81MPEG = "8.1 MPEG"; +const CString kString140 = "14.0"; const CString kString222 = "22.2"; -const CString kString50_5_3 = "5.0.5.3"; -const CString kString51_5_3 = "5.1.5.3"; -const CString kString50_2_2 = "5.0.2.2"; -const CString kString50_4_2 = "5.0.4.2"; -const CString kString70_4_2 = "7.0.4.2"; -const CString kString50_5_Sony = "5.0.5 Sony"; -const CString kString40_2_2 = "4.0.3.2"; -const CString kString40_4_2 = "4.0.4.2"; -const CString kString50_3_2 = "5.0.3.2"; -const CString kString30_5_2 = "3.0.5.2"; -const CString kString40_4_4 = "4.0.4.4"; -const CString kString50_4_4 = "5.0.4.4"; - +const CString kString220 = "22.0"; const CString kStringAmbi1stOrder = "1st Order Ambisonics"; const CString kStringAmbi2cdOrder = "2nd Order Ambisonics"; const CString kStringAmbi3rdOrder = "3rd Order Ambisonics"; -const CString kStringAmbi4thOrder = "4th Order Ambisonics"; -const CString kStringAmbi5thOrder = "5th Order Ambisonics"; -const CString kStringAmbi6thOrder = "6th Order Ambisonics"; -const CString kStringAmbi7thOrder = "7th Order Ambisonics"; + /*@}*/ //------------------------------------------------------------------------ @@ -560,27 +432,21 @@ const CString kString80CineS = "L R C Ls Rs Lc Rc Cs"; const CString kString80MusicS = "L R C Ls Rs Cs Sl Sr"; const CString kString81CineS = "L R C LFE Ls Rs Lc Rc Cs"; const CString kString81MusicS = "L R C LFE Ls Rs Cs Sl Sr"; -const CString kString40_4S = "L R Ls Rs Tfl Tfr Trl Trr"; +const CString kString80CubeS = "L R Ls Rs Tfl Tfr Trl Trr"; const CString kString71CineTopCenterS = "L R C LFE Ls Rs Cs Tc"; const CString kString71CineCenterHighS = "L R C LFE Ls Rs Cs Tfc"; -const CString kString71CineFullRearS = "L R C LFE Ls Rs Lcs Rcs"; -const CString kString50_2S = "L R C Ls Rs Tfl Tfr"; -const CString kString51_2S = "L R C LFE Ls Rs Tfl Tfr"; -const CString kString50_2TopSideS = "L R C Ls Rs Tsl Tsr"; -const CString kString51_2TopSideS = "L R C LFE Ls Rs Tsl Tsr"; -const CString kString71ProximityS = "L R C LFE Ls Rs Pl Pr"; +const CString kString71CineFrontHighS = "L R C LFE Ls Rs Tfl Tfl"; +const CString kString71CineSideHighS = "L R C LFE Ls Rs Tsl Tsl"; +const CString kString71CineFullRearS = "L R C LFE Ls Rs Lcs Rcs"; +const CString kString71ProximityS = "L R C LFE Ls Rs Pl Pr"; const CString kString90CineS = "L R C Ls Rs Lc Rc Sl Sr"; -const CString kString91CineS = "L R C LFE Ls Rs Lc Rc Sl Sr"; +const CString kString91CineS = "L R C Lfe Ls Rs Lc Rc Sl Sr"; const CString kString100CineS = "L R C Ls Rs Lc Rc Cs Sl Sr"; -const CString kString101CineS = "L R C LFE Ls Rs Lc Rc Cs Sl Sr"; +const CString kString101CineS = "L R C Lfe Ls Rs Lc Rc Cs Sl Sr"; const CString kString50_4S = "L R C Ls Rs Tfl Tfr Trl Trr"; -const CString kString51_4S = "L R C LFE Ls Rs Tfl Tfr Trl Trr"; -const CString kString50_4_1S = "L R C Ls Rs Tfl Tfr Trl Trr Bfc"; -const CString kString51_4_1S = "L R C LFE Ls Rs Tfl Tfr Trl Trr Bfc"; +const CString kString51_4S = "L R C LFE Ls Rs Tfl Tfr Trl Trr"; const CString kString70_2S = "L R C Ls Rs Sl Sr Tsl Tsr"; const CString kString71_2S = "L R C LFE Ls Rs Sl Sr Tsl Tsr"; -const CString kString70_3S = "L R C Ls Rs Sl Sr Tfl Tfr Trc"; -const CString kString72_3S = "L R C LFE Ls Rs Sl Sr Tfl Tfr Trc LFE2"; const CString kString70_4S = "L R C Ls Rs Sl Sr Tfl Tfr Trl Trr"; const CString kString71_4S = "L R C LFE Ls Rs Sl Sr Tfl Tfr Trl Trr"; const CString kString70_6S = "L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr"; @@ -589,38 +455,22 @@ const CString kString90_4S = "L R C Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr"; const CString kString91_4S = "L R C LFE Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr"; const CString kString90_6S = "L R C Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr Tsl Tsr"; const CString kString91_6S = "L R C LFE Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr Tsl Tsr"; -const CString kString50_5S = "L R C Ls Rs Tc Tfl Tfr Trl Trr"; -const CString kString51_5S = "L R C LFE Ls Rs Tc Tfl Tfr Trl Trr"; -const CString kString50_5_SonyS = "L R C Ls Rs Tfl Tfc Tfr Trl Trr"; -const CString kString50_6S = "L R C Ls Rs Tc Tfl Tfc Tfr Trl Trr"; -const CString kString51_6S = "L R C LFE Ls Rs Tc Tfl Tfc Tfr Trl Trr"; +const CString kString100S = "L R C Ls Rs Tc Tfl Tfr Trl Trr"; +const CString kString101S = "L R C LFE Ls Rs Tc Tfl Tfr Trl Trr"; +const CString kString110S = "L R C Ls Rs Tc Tfl Tfc Tfr Trl Trr"; +const CString kString111S = "L R C LFE Ls Rs Tc Tfl Tfc Tfr Trl Trr"; const CString kString130S = "L R C Ls Rs Sl Sr Tc Tfl Tfc Tfr Trl Trr"; const CString kString131S = "L R C LFE Ls Rs Sl Sr Tc Tfl Tfc Tfr Trl Trr"; -const CString kString52_5S = "L R C LFE Ls Rs Tfl Tfc Tfr Trl Trr LFE2"; -const CString kString72_5S = "L R C LFE Ls Rs Lc Rc Tfl Tfc Tfr Trl Trr LFE2"; -const CString kString41_4_1S = "L R LFE Ls Rs Tfl Tfc Tfr Bfc"; -const CString kString30_5_2S = "L R C Tfl Tfc Tfr Trl Trr Bfl Bfr"; -const CString kString40_2_2S = "C Sl Sr Cs Tfc Tsl Tsr Trc"; -const CString kString40_4_2S = "L R Ls Rs Tfl Tfr Trl Trr Bfl Bfr"; -const CString kString40_4_4S = "L R Ls Rs Tfl Tfr Trl Trr Bfl Bfr Brl Brr"; -const CString kString50_4_4S = "L R C Ls Rs Tfl Tfr Trl Trr Bfl Bfr Brl Brr"; -const CString kString60_4_4S = "L R Ls Rs Sl Sr Tfl Tfr Trl Trr Bfl Bfr Brl Brr"; -const CString kString50_5_3S = "L R C Ls Rs Tfl Tfc Tfr Trl Trr Bfl Bfc Bfr"; -const CString kString51_5_3S = "L R C LFE Ls Rs Tfl Tfc Tfr Trl Trr Bfl Bfc Bfr"; -const CString kString50_2_2S = "L R C Ls Rs Tsl Tsr Bfl Bfr"; -const CString kString50_3_2S = "L R C Ls Rs Tfl Tfc Tfr Bfl Bfr"; -const CString kString50_4_2S = "L R C Ls Rs Tfl Tfr Trl Trr Bfl Bfr"; -const CString kString70_4_2S = "L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Bfl Bfr"; -const CString kString222S = "L R C LFE Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr LFE2 Tsl Tsr Bfl Bfc Bfr"; -const CString kString220S = "L R C Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr Tsl Tsr Bfl Bfc Bfr"; - -const CString kStringAmbi1stOrderS = "0 1 2 3"; -const CString kStringAmbi2cdOrderS = "0 1 2 3 4 5 6 7 8"; -const CString kStringAmbi3rdOrderS = "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"; -const CString kStringAmbi4thOrderS = "0..24"; -const CString kStringAmbi5thOrderS = "0..35"; -const CString kStringAmbi6thOrderS = "0..48"; -const CString kStringAmbi7thOrderS = "0..63"; +const CString kString102S = "L R C LFE Ls Rs Tfl Tfc Tfr Trl Trr LFE2"; +const CString kString122S = "L R C LFE Ls Rs Lc Rc Tfl Tfc Tfr Trl Trr LFE2"; +const CString kString81MPEGS = "L R LFE Ls Rs Tfl Tfc Tfr Bfc"; +const CString kString140S = "L R Ls Rs Sl Sr Tfl Tfr Trl Trr Bfl Bfr Brl Brr"; +const CString kString222S = "L R C LFE Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr LFE2 Tsl Tsr Bfl Bfc Bfr"; +const CString kString220S = "L R C Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr Tsl Tsr Bfl Bfc Bfr"; + +const CString kStringAmbi1stOrderS = "0 1 2 3"; +const CString kStringAmbi2cdOrderS = "0 1 2 3 4 5 6 7 8"; +const CString kStringAmbi3rdOrderS = "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"; /*@}*/ //------------------------------------------------------------------------ @@ -716,9 +566,8 @@ inline bool hasTopSpeakers (const SpeakerArrangement& arr) /** Returns true if arrangement contains bottom (lower layer) speakers */ inline bool hasBottomSpeakers (const SpeakerArrangement& arr) { - if (arr & kSpeakerBfl || arr & kSpeakerBfc || arr & kSpeakerBfr || - arr & kSpeakerBsl || arr & kSpeakerBsr || - arr & kSpeakerBrr || arr & kSpeakerBrl || arr & kSpeakerBrc) + if (arr & kSpeakerBfl || arr & kSpeakerBfc || arr & kSpeakerBfl || arr & kSpeakerBfc || + arr & kSpeakerBfr) return true; return false; } @@ -758,39 +607,16 @@ inline bool is3D (const SpeakerArrangement& arr) } //------------------------------------------------------------------------ -/** Returns true if arrangement is a Ambisonic configuration. */ +/** Returns true if arrangement is a Auro configuration. */ inline bool isAmbisonics (const SpeakerArrangement& arr) { - if (arr == kAmbi1stOrderACN || arr == kAmbi2cdOrderACN || arr == kAmbi3rdOrderACN || - arr == kAmbi4thOrderACN || arr == kAmbi5thOrderACN || arr == kAmbi6thOrderACN || - arr == kAmbi7thOrderACN) + if (arr == kAmbi1stOrderACN || arr == kAmbi2cdOrderACN || arr == kAmbi3rdOrderACN) { return true; } return false; } - -//------------------------------------------------------------------------ -/** Converts a speaker of a Ambisonic order 1 to 4 to a Ambisonic order 7 (5 to 7) (return 0 when out of range).*/ -inline Speaker convertSpeaker_Ambi_1234Order_to_Ambi567Order (Speaker speaker_1234_order) -{ - int32 idx = getSpeakerIndex (speaker_1234_order, kAmbi4thOrderACN); - if (idx < 0) - return 0; - return (Speaker)1 << idx; -} - -//------------------------------------------------------------------------ -/** Converts a speaker of a Ambisonic order 5 to 7 to a Ambisonic order 4 (1 to 4) (return 0 when out of range).*/ -inline Speaker convertSpeaker_Ambi_567Order_to_Ambi1234Order (Speaker speaker_567_order) -{ - int32 idx = getSpeakerIndex (speaker_567_order, kAmbi7thOrderACN); - if (idx < 0) - return 0; - return getSpeaker (kAmbi4thOrderACN, idx); -} - //------------------------------------------------------------------------ /** Returns the speaker arrangement associated to a string representation. Returns kEmpty if no associated arrangement is known. */ @@ -864,24 +690,20 @@ inline SpeakerArrangement getSpeakerArrangementFromString (CString arrStr) return k81Cine; if (!strcmp8 (arrStr, kString81Music)) return k81Music; - if (!strcmp8 (arrStr, kString52_5)) - return k52_5; - if (!strcmp8 (arrStr, kString72_5)) - return k72_5; - if (!strcmp8 (arrStr, kString40_4)) - return k40_4; + if (!strcmp8 (arrStr, kString102)) + return k102; + if (!strcmp8 (arrStr, kString122)) + return k122; + if (!strcmp8 (arrStr, kString80Cube)) + return k80Cube; if (!strcmp8 (arrStr, kString71CineTopCenter)) return k71CineTopCenter; if (!strcmp8 (arrStr, kString71CineCenterHigh)) return k71CineCenterHigh; - if (!strcmp8 (arrStr, kString50_2)) - return k50_2; - if (!strcmp8 (arrStr, kString51_2)) - return k51_2; - if (!strcmp8 (arrStr, kString50_2TopSide)) - return k50_2_TS; - if (!strcmp8 (arrStr, kString51_2TopSide)) - return k51_2_TS; + if (!strcmp8 (arrStr, kString71CineFrontHigh)) + return k71CineFrontHigh; + if (!strcmp8 (arrStr, kString71CineSideHigh)) + return k71CineSideHigh; if (!strcmp8 (arrStr, kString71CineFullRear)) return k71CineFullRear; if (!strcmp8 (arrStr, kString90Cine)) @@ -896,20 +718,12 @@ inline SpeakerArrangement getSpeakerArrangementFromString (CString arrStr) return k50_4; if (!strcmp8 (arrStr, kString51_4)) return k51_4; - if (!strcmp8 (arrStr, kString50_4_1)) - return k50_4_1; - if (!strcmp8 (arrStr, kString51_4_1)) - return k51_4_1; - if (!strcmp8 (arrStr, kString41_4_1)) - return k41_4_1; + if (!strcmp8 (arrStr, kString81MPEG)) + return k81MPEG3D; if (!strcmp8 (arrStr, kString70_2)) return k70_2; if (!strcmp8 (arrStr, kString71_2)) return k71_2; - if (!strcmp8 (arrStr, kString70_3)) - return k70_3; - if (!strcmp8 (arrStr, kString72_3)) - return k72_3; if (!strcmp8 (arrStr, kString70_4)) return k70_4; if (!strcmp8 (arrStr, kString71_4)) @@ -926,64 +740,30 @@ inline SpeakerArrangement getSpeakerArrangementFromString (CString arrStr) return k90_6; if (!strcmp8 (arrStr, kString91_6)) return k91_6; - if (!strcmp8 (arrStr, kString50_5)) - return k50_5; - if (!strcmp8 (arrStr, kString51_5)) - return k51_5; - if (!strcmp8 (arrStr, kString50_6)) - return k50_6; - if (!strcmp8 (arrStr, kString51_6)) - return k51_6; + if (!strcmp8 (arrStr, kString100)) + return k100; + if (!strcmp8 (arrStr, kString101)) + return k101; + if (!strcmp8 (arrStr, kString110)) + return k110; + if (!strcmp8 (arrStr, kString111)) + return k111; if (!strcmp8 (arrStr, kString130)) return k130; if (!strcmp8 (arrStr, kString131)) return k131; - if (!strcmp8 (arrStr, kString60_4_4)) - return k60_4_4; + if (!strcmp8 (arrStr, kString140)) + return k140; if (!strcmp8 (arrStr, kString222)) return k222; if (!strcmp8 (arrStr, kString220)) return k220; - if (!strcmp8 (arrStr, kString50_5_3)) - return k50_5_3; - if (!strcmp8 (arrStr, kString51_5_3)) - return k51_5_3; - if (!strcmp8 (arrStr, kString50_2_2)) - return k50_2_2; - if (!strcmp8 (arrStr, kString50_4_2)) - return k50_4_2; - if (!strcmp8 (arrStr, kString70_4_2)) - return k70_4_2; - - if (!strcmp8 (arrStr, kString50_5_Sony)) - return k50_5_Sony; - if (!strcmp8 (arrStr, kString40_2_2)) - return k40_2_2; - if (!strcmp8 (arrStr, kString40_4_2)) - return k40_4_2; - if (!strcmp8 (arrStr, kString50_3_2)) - return k50_3_2; - if (!strcmp8 (arrStr, kString30_5_2)) - return k30_5_2; - if (!strcmp8 (arrStr, kString40_4_4)) - return k40_4_4; - if (!strcmp8 (arrStr, kString50_4_4)) - return k50_4_4; - if (!strcmp8 (arrStr, kStringAmbi1stOrder)) return kAmbi1stOrderACN; if (!strcmp8 (arrStr, kStringAmbi2cdOrder)) return kAmbi2cdOrderACN; if (!strcmp8 (arrStr, kStringAmbi3rdOrder)) return kAmbi3rdOrderACN; - if (!strcmp8 (arrStr, kStringAmbi4thOrder)) - return kAmbi4thOrderACN; - if (!strcmp8 (arrStr, kStringAmbi5thOrder)) - return kAmbi5thOrderACN; - if (!strcmp8 (arrStr, kStringAmbi6thOrder)) - return kAmbi6thOrderACN; - if (!strcmp8 (arrStr, kStringAmbi7thOrder)) - return kAmbi7thOrderACN; return kEmpty; } @@ -995,7 +775,6 @@ inline CString getSpeakerArrangementString (SpeakerArrangement arr, bool withSpe switch (arr) { case kMono: return withSpeakersName ? kStringMonoS : kStringMono; - //--- Stereo pairs--- case kStereo: return withSpeakersName ? kStringStereoS : kStringStereo; case kStereoSurround: return withSpeakersName ? kStringStereoRS : kStringStereoR; case kStereoCenter: return withSpeakersName ? kStringStereoCS : kStringStereoC; @@ -1005,61 +784,54 @@ inline CString getSpeakerArrangementString (SpeakerArrangement arr, bool withSpe case kStereoTS: return withSpeakersName ? kStringStereoTSS : kStringStereoTS; case kStereoTR: return withSpeakersName ? kStringStereoTRS : kStringStereoTR; case kStereoBF: return withSpeakersName ? kStringStereoBFS : kStringStereoBF; - - //--- --- case kCineFront: return withSpeakersName ? kStringCineFrontS : kStringCineFront; case k30Cine: return withSpeakersName ? kString30CineS : kString30Cine; - case k31Cine: return withSpeakersName ? kString31CineS : kString31Cine; case k30Music: return withSpeakersName ? kString30MusicS : kString30Music; + case k31Cine: return withSpeakersName ? kString31CineS : kString31Cine; case k31Music: return withSpeakersName ? kString31MusicS : kString31Music; case k40Cine: return withSpeakersName ? kString40CineS : kString40Cine; - case k41Cine: return withSpeakersName ? kString41CineS : kString41Cine; case k40Music: return withSpeakersName ? kString40MusicS : kString40Music; + case k41Cine: return withSpeakersName ? kString41CineS : kString41Cine; case k41Music: return withSpeakersName ? kString41MusicS : kString41Music; case k50: return withSpeakersName ? kString50S : kString50; case k51: return withSpeakersName ? kString51S : kString51; case k60Cine: return withSpeakersName ? kString60CineS : kString60Cine; - case k61Cine: return withSpeakersName ? kString61CineS : kString61Cine; case k60Music: return withSpeakersName ? kString60MusicS : kString60Music; + case k61Cine: return withSpeakersName ? kString61CineS : kString61Cine; case k61Music: return withSpeakersName ? kString61MusicS : kString61Music; case k70Cine: return withSpeakersName ? kString70CineS : kString70Cine; - case k71Cine: return withSpeakersName ? kString71CineS : kString71Cine; case k70Music: return withSpeakersName ? kString70MusicS : kString70Music; + case k71Cine: return withSpeakersName ? kString71CineS : kString71Cine; case k71Music: return withSpeakersName ? kString71MusicS : kString71Music; case k71Proximity: return withSpeakersName ? kString71ProximityS : kString71Proximity; case k80Cine: return withSpeakersName ? kString80CineS : kString80Cine; - case k81Cine: return withSpeakersName ? kString81CineS : kString81Cine; case k80Music: return withSpeakersName ? kString80MusicS : kString80Music; + case k81Cine: return withSpeakersName ? kString81CineS : kString81Cine; case k81Music: return withSpeakersName ? kString81MusicS : kString81Music; - case k71CineFullRear: return withSpeakersName ? kString71CineFullRearS : kString71CineFullRear; - case k90Cine: return withSpeakersName ? kString90CineS : kString90Cine; + case k81MPEG3D: return withSpeakersName ? kString81MPEGS : kString81MPEG; + case k102: return withSpeakersName ? kString102S : kString102; + case k122: return withSpeakersName ? kString122S : kString122; + case k80Cube: return withSpeakersName ? kString80CubeS : kString80Cube; + case k71CineTopCenter: return withSpeakersName ? kString71CineTopCenterS : kString71CineTopCenter; + case k71CineCenterHigh: return withSpeakersName ? kString71CineCenterHighS : kString71CineCenterHigh; + case k71CineFrontHigh: return withSpeakersName ? kString71CineFrontHighS : kString71CineFrontHigh; + case k71CineSideHigh: return withSpeakersName ? kString71CineSideHighS : kString71CineSideHigh; + case k71CineFullRear: return withSpeakersName ? kString71CineFullRearS : kString71CineFullRear; + case k90Cine: return withSpeakersName ? kString90CineS : kString90Cine; case k91Cine: return withSpeakersName ? kString91CineS : kString91Cine; - case k100Cine: return withSpeakersName ? kString100CineS : kString100Cine; + case k100Cine: return withSpeakersName ? kString100CineS : kString100Cine; case k101Cine: return withSpeakersName ? kString101CineS : kString101Cine; + case k100: return withSpeakersName ? kString100S : kString100; + case k101: return withSpeakersName ? kString101S : kString101; + case k110: return withSpeakersName ? kString110S : kString110; + case k111: return withSpeakersName ? kString111S : kString111; - //---With Tops --- - case k71CineTopCenter: return withSpeakersName ? kString71CineTopCenterS : kString71CineTopCenter; - case k71CineCenterHigh: return withSpeakersName ? kString71CineCenterHighS : kString71CineCenterHigh; - case k50_2_TS: return withSpeakersName ? kString50_2TopSideS : kString50_2TopSide; - case k51_2_TS: return withSpeakersName ? kString51_2TopSideS : kString51_2TopSide; - - case k40_4: return withSpeakersName ? kString40_4S : kString40_4; - case k50_2: return withSpeakersName ? kString50_2S : kString50_2; - case k51_2: return withSpeakersName ? kString51_2S : kString51_2; case k50_4: return withSpeakersName ? kString50_4S : kString50_4; case k51_4: return withSpeakersName ? kString51_4S : kString51_4; - case k50_5: return withSpeakersName ? kString50_5S : kString50_5; - case k51_5: return withSpeakersName ? kString51_5S : kString51_5; - case k52_5: return withSpeakersName ? kString52_5S : kString52_5; - case k50_6: return withSpeakersName ? kString50_6S : kString50_6; - case k51_6: return withSpeakersName ? kString51_6S : kString51_6; case k70_2: return withSpeakersName ? kString70_2S : kString70_2; case k71_2: return withSpeakersName ? kString71_2S : kString71_2; - case k70_3: return withSpeakersName ? kString70_3S : kString70_3; - case k72_3: return withSpeakersName ? kString72_3S : kString72_3; case k70_4: return withSpeakersName ? kString70_4S : kString70_4; case k71_4: return withSpeakersName ? kString71_4S : kString71_4; - case k72_5: return withSpeakersName ? kString72_5S : kString72_5; case k70_6: return withSpeakersName ? kString70_6S : kString70_6; case k71_6: return withSpeakersName ? kString71_6S : kString71_6; case k90_4: return withSpeakersName ? kString90_4S : kString90_4; @@ -1068,44 +840,19 @@ inline CString getSpeakerArrangementString (SpeakerArrangement arr, bool withSpe case k91_6: return withSpeakersName ? kString91_6S : kString91_6; case k130: return withSpeakersName ? kString130S : kString130; case k131: return withSpeakersName ? kString131S : kString131; - - //--- With Tops and Bottoms --- - case k41_4_1: return withSpeakersName ? kString41_4_1S : kString41_4_1; - case k50_4_1: return withSpeakersName ? kString50_4_1S : kString50_4_1; - case k51_4_1: return withSpeakersName ? kString51_4_1S : kString51_4_1; - case k50_5_3: return withSpeakersName ? kString50_5_3S : kString50_5_3; - case k51_5_3: return withSpeakersName ? kString51_5_3S : kString51_5_3; - case k50_2_2: return withSpeakersName ? kString50_2_2S : kString50_2_2; - case k50_4_2: return withSpeakersName ? kString50_4_2S : kString50_4_2; - case k60_4_4: return withSpeakersName ? kString60_4_4S : kString60_4_4; - case k70_4_2: return withSpeakersName ? kString70_4_2S : kString70_4_2; - - case k50_5_Sony: return withSpeakersName ? kString50_5_SonyS : kString50_5_Sony; - case k40_2_2: return withSpeakersName ? kString40_2_2S : kString40_2_2; - case k40_4_2: return withSpeakersName ? kString40_4_2S : kString40_4_2; - case k50_3_2: return withSpeakersName ? kString50_3_2S : kString50_3_2; - case k30_5_2: return withSpeakersName ? kString30_5_2S : kString30_5_2; - case k40_4_4: return withSpeakersName ? kString40_4_4S : kString40_4_4; - case k50_4_4: return withSpeakersName ? kString50_4_4S : kString50_4_4; - - case k220: return withSpeakersName ? kString220S : kString220; + case k140: return withSpeakersName ? kString140S : kString140; case k222: return withSpeakersName ? kString222S : kString222; + case k220: return withSpeakersName ? kString220S : kString220; + break; } - //--- Ambisonics --- + if (arr == kAmbi1stOrderACN) return withSpeakersName ? kStringAmbi1stOrderS : kStringAmbi1stOrder; if (arr == kAmbi2cdOrderACN) return withSpeakersName ? kStringAmbi2cdOrderS : kStringAmbi2cdOrder; if (arr == kAmbi3rdOrderACN) return withSpeakersName ? kStringAmbi3rdOrderS : kStringAmbi3rdOrder; - if (arr == kAmbi4thOrderACN) - return withSpeakersName ? kStringAmbi4thOrderS : kStringAmbi4thOrder; - if (arr == kAmbi5thOrderACN) - return withSpeakersName ? kStringAmbi5thOrderS : kStringAmbi5thOrder; - if (arr == kAmbi6thOrderACN) - return withSpeakersName ? kStringAmbi6thOrderS : kStringAmbi6thOrder; - if (arr == kAmbi7thOrderACN) - return withSpeakersName ? kStringAmbi7thOrderS : kStringAmbi7thOrder; + return kStringEmpty; } @@ -1208,24 +955,6 @@ inline CString getSpeakerShortName (const SpeakerArrangement& arr, int32 index) return "14"; if (speaker == kSpeakerACN15) return "15"; - if (speaker == kSpeakerACN16) - return "16"; - if (speaker == kSpeakerACN17) - return "17"; - if (speaker == kSpeakerACN18) - return "18"; - if (speaker == kSpeakerACN19) - return "19"; - if (speaker == kSpeakerACN20) - return "20"; - if (speaker == kSpeakerACN21) - return "21"; - if (speaker == kSpeakerACN22) - return "22"; - if (speaker == kSpeakerACN23) - return "23"; - if (speaker == kSpeakerACN24) - return "24"; if (speaker == kSpeakerTsl) return "Tsl"; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/vsttypes.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/vsttypes.h index 081177fa..15c1dbe7 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/vsttypes.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/pluginterfaces/vst/vsttypes.h @@ -22,25 +22,19 @@ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ -/** VST 3 SDK Version */ +/** VST3 SDK Version */ #ifndef kVstVersionString -#define kVstVersionString "VST 3.7.8" ///< SDK version for PClassInfo2 +#define kVstVersionString "VST 3.7.2" ///< SDK version for PClassInfo2 #endif #define kVstVersionMajor 3 #define kVstVersionMinor 7 -#define kVstVersionSub 8 +#define kVstVersionSub 2 #define VST_VERSION ((kVstVersionMajor << 16) | (kVstVersionMinor << 8) | kVstVersionSub) // Versions History which allows to write such code: // #if VST_VERSION >= VST_3_6_5_VERSION -#define VST_3_7_8_VERSION 0x030708 -#define VST_3_7_7_VERSION 0x030707 -#define VST_3_7_6_VERSION 0x030706 -#define VST_3_7_5_VERSION 0x030705 -#define VST_3_7_4_VERSION 0x030704 -#define VST_3_7_3_VERSION 0x030703 #define VST_3_7_2_VERSION 0x030702 #define VST_3_7_1_VERSION 0x030701 #define VST_3_7_0_VERSION 0x030700 @@ -106,41 +100,6 @@ typedef uint64 Speaker; ///< Bit for one speaker /*@}*/ -static SMTG_CONSTEXPR const FIDString SDKVersionString = kVstVersionString; - -static SMTG_CONSTEXPR const uint32 SDKVersionMajor = kVstVersionMajor; -static SMTG_CONSTEXPR const uint32 SDKVersionMinor = kVstVersionMinor; -static SMTG_CONSTEXPR const uint32 SDKVersionSub = kVstVersionSub; - -static SMTG_CONSTEXPR const uint32 SDKVersion = - ((SDKVersionMajor << 16) | (SDKVersionMinor << 8) | SDKVersionSub); - -// Versions History which allows to write such code: -// if constexpr (SDKVersion >= SDKVersion_3_6_5) { ... } -static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_7 = VST_3_7_7_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_6 = VST_3_7_6_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_5 = VST_3_7_5_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_4 = VST_3_7_4_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_3 = VST_3_7_3_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_2 = VST_3_7_2_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_1 = VST_3_7_1_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_0 = VST_3_7_0_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_14 = VST_3_6_14_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_13 = VST_3_6_13_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_12 = VST_3_6_12_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_11 = VST_3_6_11_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_10 = VST_3_6_10_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_9 = VST_3_6_9_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_8 = VST_3_6_8_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_7 = VST_3_6_7_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_6 = VST_3_6_6_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_5 = VST_3_6_5_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_0 = VST_3_6_0_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_5_0 = VST_3_5_0_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_1_0 = VST_3_1_0_VERSION; -static SMTG_CONSTEXPR const uint32 SDKVersion_3_0_0 = VST_3_0_0_VERSION; - //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg - diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/LICENSE.txt b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/LICENSE.txt index f9c531e7..fd2eb70b 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/LICENSE.txt +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/LICENSE.txt @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/README.md b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/README.md index e7c8a937..acf33ef7 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/README.md +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/README.md @@ -2,12 +2,12 @@ Here are located: -- helper classes implementing **VST 3** Interfaces -- samples of **VST 3** Hosting and **VST 3** plug-ins -- **AAX** Wrapper -- **AU** Wrapper -- **AUv3** Wrapper -- **VST 2** Wrapper +- helper classes implementing VST3 Interfaces +- samples of VST3 Hosting and VST3 Plug-Ins +- AAX Wrapper +- AU Wrapper +- AUv3 Wrapper +- VST2 Wrapper - InterAppAudio ## License & Usage guidelines @@ -15,4 +15,4 @@ Here are located: More details are found at [VST 3 SDK public_sdk License](https://forums.steinberg.net/t/vst-3-sdk-public-sdk-license/695592) ---- -Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) +Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) \ No newline at end of file diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/samples/vst-utilities/moduleinfotool/source/main.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/samples/vst-utilities/moduleinfotool/source/main.cpp deleted file mode 100644 index 2d568ae8..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/samples/vst-utilities/moduleinfotool/source/main.cpp +++ /dev/null @@ -1,439 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// Flags : clang-format SMTGSequencer -// -// Category : moduleinfotool -// Filename : public.sdk/samples/vst-utilities/moduleinfotool/main.cpp -// Created by : Steinberg, 12/2021 -// Description : main entry point -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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 "base/source/fcommandline.h" -#include "pluginterfaces/base/fplatform.h" -#include "pluginterfaces/base/iplugincompatibility.h" -#include "pluginterfaces/vst/vsttypes.h" -#include "public.sdk/source/common/memorystream.h" -#include "public.sdk/source/common/readfile.h" -#include "public.sdk/source/vst/hosting/module.h" -#include "public.sdk/source/vst/moduleinfo/moduleinfocreator.h" -#include "public.sdk/source/vst/moduleinfo/moduleinfoparser.h" -#include "public.sdk/source/vst/utility/stringconvert.h" -#include -#include -#include - -//------------------------------------------------------------------------ -namespace Steinberg { -namespace ModuleInfoTool { -namespace { - -//------------------------------------------------------------------------ -constexpr auto BUILD_INFO = "moduleinfotool 1.0.0 [Built " __DATE__ "]"; - -//------------------------------------------------------------------------ -//-- Options -constexpr auto optHelp = "help"; -constexpr auto optCreate = "create"; -constexpr auto optValidate = "validate"; -constexpr auto optModuleVersion = "version"; -constexpr auto optModulePath = "path"; -constexpr auto optInfoPath = "infopath"; -constexpr auto optModuleCompatPath = "compat"; -constexpr auto optOutputPath = "output"; - -//------------------------------------------------------------------------ -void printUsage (std::ostream& s) -{ - s << "Usage:\n"; - s << " moduleinfotool -create -version VERSION -path MODULE_PATH [-compat PATH -output PATH]\n"; - s << " moduleinfotool -validate -path MODULE_PATH [-infopath PATH]\n"; -} - -//------------------------------------------------------------------------ -std::optional openAndParseCompatJSON (const std::string& path) -{ - auto data = readFile (path); - if (data.empty ()) - { - std::cout << "Can not read '" << path << "'\n"; - printUsage (std::cout); - return {}; - } - std::stringstream error; - auto result = ModuleInfoLib::parseCompatibilityJson (data, &error); - if (!result) - { - std::cout << "Can not parse '" << path << "'\n"; - std::cout << error.str (); - printUsage (std::cout); - return {}; - } - return result; -} - -//------------------------------------------------------------------------ -std::optional loadCompatibilityFromModule (const VST3::Hosting::Module& module) -{ - const auto& factory = module.getFactory(); - const auto& infos = factory.classInfos(); - - const auto iter = std::find_if (infos.begin(), infos.end(), [&] (const auto& info) - { - return info.category() == kPluginCompatibilityClass; - }); - - if (iter == infos.end()) - return {}; - - const auto compatibility = factory.createInstance (iter->ID()); - - if (compatibility == nullptr) - return {}; - - Steinberg::MemoryStream stream; - - if (compatibility->getCompatibilityJSON (&stream) != kResultOk) - return {}; - - const std::string_view streamView (stream.getData(), stream.getSize()); - - return ModuleInfoLib::parseCompatibilityJson (streamView, nullptr); -} - -//------------------------------------------------------------------------ -int createJSON (const std::optional& compat, - const std::string& modulePath, const std::string& moduleVersion, - std::ostream& outStream) -{ - std::string errorStr; - auto module = VST3::Hosting::Module::create (modulePath, errorStr); - if (!module) - { - std::cout << errorStr; - return 1; - } - auto moduleInfo = ModuleInfoLib::createModuleInfo (*module, false); - if (compat) - moduleInfo.compatibility = *compat; - else if (auto loaded = loadCompatibilityFromModule (*module)) - moduleInfo.compatibility = *loaded; - - moduleInfo.version = moduleVersion; - - std::stringstream output; - ModuleInfoLib::outputJson (moduleInfo, output); - auto str = output.str (); - outStream << str; - return 0; -} - -//------------------------------------------------------------------------ -struct validate_error : std::exception -{ - validate_error (const std::string& str) : str (str) {} - const char* what () const noexcept override { return str.data (); } - -private: - std::string str; -}; - -//------------------------------------------------------------------------ -void validate (const ModuleInfo& moduleInfo, const VST3::Hosting::Module& module) -{ - auto factory = module.getFactory (); - auto factoryInfo = factory.info (); - auto classInfoList = factory.classInfos (); - auto snapshotList = module.getSnapshots (module.getPath ()); - - if (factoryInfo.vendor () != moduleInfo.factoryInfo.vendor) - throw validate_error ("factoryInfo.vendor different: " + moduleInfo.factoryInfo.vendor); - if (factoryInfo.url () != moduleInfo.factoryInfo.url) - throw validate_error ("factoryInfo.url different: " + moduleInfo.factoryInfo.url); - if (factoryInfo.email () != moduleInfo.factoryInfo.email) - throw validate_error ("factoryInfo.email different: " + moduleInfo.factoryInfo.email); - if (factoryInfo.flags () != moduleInfo.factoryInfo.flags) - throw validate_error ("factoryInfo.flags different: " + - std::to_string (moduleInfo.factoryInfo.flags)); - - for (const auto& ci : moduleInfo.classes) - { - auto cid = VST3::UID::fromString (ci.cid); - if (!cid) - throw validate_error ("could not parse class UID: " + ci.cid); - auto it = std::find_if (classInfoList.begin (), classInfoList.end (), - [&] (const auto& el) { return el.ID () == *cid; }); - if (it == classInfoList.end ()) - throw validate_error ("cannot find CID in class list: " + ci.cid); - if (it->name () != ci.name) - throw validate_error ("class name different: " + ci.name); - if (it->category () != ci.category) - throw validate_error ("class category different: " + ci.category); - if (it->vendor () != ci.vendor) - throw validate_error ("class vendor different: " + ci.vendor); - if (it->version () != ci.version) - throw validate_error ("class version different: " + ci.version); - if (it->sdkVersion () != ci.sdkVersion) - throw validate_error ("class sdkVersion different: " + ci.sdkVersion); - if (it->subCategories () != ci.subCategories) - throw validate_error ("class subCategories different: " /* + ci.subCategories*/); - if (it->cardinality () != ci.cardinality) - throw validate_error ("class cardinality different: " + - std::to_string (ci.cardinality)); - if (it->classFlags () != ci.flags) - throw validate_error ("class flags different: " + std::to_string (ci.flags)); - classInfoList.erase (it); - - auto snapshotListIt = std::find_if (snapshotList.begin (), snapshotList.end (), - [&] (const auto& el) { return el.uid == *cid; }); - if (snapshotListIt == snapshotList.end () && !ci.snapshots.empty ()) - throw validate_error ("cannot find snapshots for: " + ci.cid); - for (const auto& snapshot : ci.snapshots) - { - auto snapshotIt = std::find_if ( - snapshotListIt->images.begin (), snapshotListIt->images.end (), - [&] (const auto& el) { return el.scaleFactor == snapshot.scaleFactor; }); - if (snapshotIt == snapshotListIt->images.end ()) - throw validate_error ("cannot find snapshots for scale factor: " + - std::to_string (snapshot.scaleFactor)); - std::string_view path (snapshotIt->path); - if (path.find (module.getPath ()) == 0) - path.remove_prefix (module.getPath ().size () + 1); - if (path != snapshot.path) - throw validate_error ("cannot find snapshots with path: " + snapshot.path); - snapshotListIt->images.erase (snapshotIt); - } - if (snapshotListIt != snapshotList.end () && !snapshotListIt->images.empty ()) - { - std::string errorStr = "Missing Snapshots in moduleinfo:\n"; - for (const auto& s : snapshotListIt->images) - { - errorStr += s.path + '\n'; - } - throw validate_error (errorStr); - } - if (snapshotListIt != snapshotList.end ()) - snapshotList.erase (snapshotListIt); - } - if (!classInfoList.empty ()) - throw validate_error ("Missing classes in moduleinfo"); - if (!snapshotList.empty ()) - throw validate_error ("Missing snapshots in moduleinfo"); -} - -//------------------------------------------------------------------------ -int validate (const std::string& modulePath, std::string infoJsonPath) -{ - if (infoJsonPath.empty ()) - { - auto path = VST3::Hosting::Module::getModuleInfoPath (modulePath); - if (!path) - { - std::cerr << "Module does not contain a moduleinfo.json: '" << modulePath << "'" - << '\n'; - return 1; - } - infoJsonPath = *path; - } - - auto data = readFile (infoJsonPath); - if (data.empty ()) - { - std::cerr << "Empty or non existing file: '" << infoJsonPath << "'" << '\n'; - printUsage (std::cout); - return 1; - } - auto moduleInfo = ModuleInfoLib::parseJson (data, &std::cerr); - if (moduleInfo) - { - std::string errorStr; - auto module = VST3::Hosting::Module::create (modulePath, errorStr); - if (!module) - { - std::cerr << errorStr; - printUsage (std::cout); - return 1; - } - try - { - validate (*moduleInfo, *module); - } - catch (const std::exception& exc) - { - std::cerr << "Error:\n" << exc.what () << '\n'; - printUsage (std::cout); - return 1; - } - return 0; - } - printUsage (std::cout); - return 1; -} - -//------------------------------------------------------------------------ -} // anonymous - -//------------------------------------------------------------------------ -int run (int argc, char* argv[]) -{ - // parse command line - CommandLine::Descriptions desc; - CommandLine::VariablesMap valueMap; - CommandLine::FilesVector files; - - using Description = CommandLine::Description; - desc.addOptions ( - BUILD_INFO, - { - {optCreate, "Create moduleinfo", Description::kBool}, - {optValidate, "Validate moduleinfo", Description::kBool}, - {optModuleVersion, "Module version", Description::kString}, - {optModulePath, "Path to module", Description::kString}, - {optInfoPath, "Path to moduleinfo.json", Description::kString}, - {optModuleCompatPath, "Path to compatibility.json", Description::kString}, - {optOutputPath, "Write json to file instead of stdout", Description::kString}, - {optHelp, "Print help", Description::kBool}, - }); - CommandLine::parse (argc, argv, desc, valueMap, &files); - - bool isCreate = valueMap.count (optCreate) != 0 && valueMap.count (optModuleVersion) != 0 && - valueMap.count (optModulePath) != 0; - bool isValidate = valueMap.count (optValidate) && valueMap.count (optModulePath) != 0; - - if (valueMap.hasError () || valueMap.count (optHelp) || !(isCreate || isValidate)) - { - std::cout << '\n' << desc << '\n'; - printUsage (std::cout); - return 1; - } - - int result = 1; - - const auto& modulePath = valueMap[optModulePath]; - if (isCreate) - { - auto* outputStream = &std::cout; - std::optional compat; - if (valueMap.count (optModuleCompatPath) != 0) - { - const auto& compatPath = valueMap[optModuleCompatPath]; - compat = openAndParseCompatJSON (compatPath); - if (!compat) - return 1; - } - bool writeToFile = false; - if (valueMap.count (optOutputPath) != 0) - { - writeToFile = true; -#if SMTG_OS_WINDOWS - auto tmp = VST3::StringConvert::convert (valueMap[optOutputPath]); - auto outputFile = reinterpret_cast (tmp.data ()); -#else - auto outputFile = valueMap[optOutputPath]; -#endif - auto ostream = new std::ofstream (outputFile); - - if (ostream->is_open ()) - outputStream = ostream; - else - { - std::cout << "Cannot create output file: " << valueMap[optOutputPath] << '\n'; - return result; - } - } - const auto& moduleVersion = valueMap[optModuleVersion]; - result = createJSON (compat, modulePath, moduleVersion, *outputStream); - if (writeToFile) - delete outputStream; - } - else if (isValidate) - { - std::string moduleInfoJsonPath; - if (valueMap.count (optInfoPath) != 0) - moduleInfoJsonPath = valueMap[optInfoPath]; - result = validate (modulePath, moduleInfoJsonPath); - } - return result; -} - -//------------------------------------------------------------------------ -} // ModuleInfoTool -} // Steinberg - -//------------------------------------------------------------------------ -#if SMTG_OS_WINDOWS -//------------------------------------------------------------------------ -#include -#include - -//------------------------------------------------------------------------ -using Utf8String = std::string; - -//------------------------------------------------------------------------ -using Utf8Args = std::vector; -Utf8Args toUtf8Args (int argc, wchar_t* wargv[]) -{ - Utf8Args utf8Args; - for (int i = 0; i < argc; i++) - { - auto str = reinterpret_cast(wargv[i]); - utf8Args.push_back (VST3::StringConvert::convert (str)); - } - - return utf8Args; -} - -//------------------------------------------------------------------------ -using Utf8ArgPtrs = std::vector; -Utf8ArgPtrs toUtf8ArgPtrs (Utf8Args& utf8Args) -{ - Utf8ArgPtrs utf8ArgPtrs; - for (auto& el : utf8Args) - { - utf8ArgPtrs.push_back (el.data ()); - } - - return utf8ArgPtrs; -} - -//------------------------------------------------------------------------ -int wmain (int argc, wchar_t* wargv[]) -{ - Utf8Args utf8Args = toUtf8Args (argc, wargv); - Utf8ArgPtrs utf8ArgPtrs = toUtf8ArgPtrs (utf8Args); - - char** argv = &(utf8ArgPtrs.at (0)); - return Steinberg::ModuleInfoTool::run (argc, argv); -} -#else -int main (int argc, char* argv[]) -{ - return Steinberg::ModuleInfoTool::run (argc, argv); -} -#endif diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/memorystream.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/memorystream.cpp index 5b6c245e..92e82fab 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/memorystream.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/memorystream.cpp @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -36,7 +36,7 @@ #include "memorystream.h" #include "pluginterfaces/base/futils.h" -#include +#include namespace Steinberg { diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/memorystream.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/memorystream.h index 489ace5f..e080c3fa 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/memorystream.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/memorystream.h @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/pluginview.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/pluginview.cpp index 02dd9fd2..1a5ff335 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/pluginview.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/pluginview.cpp @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -51,7 +51,6 @@ CPluginView::CPluginView (const ViewRect* _rect) //------------------------------------------------------------------------ CPluginView::~CPluginView () { - setFrame (nullptr); } //------------------------------------------------------------------------ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/pluginview.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/pluginview.h index ce131b94..417c5327 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/pluginview.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/pluginview.h @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -51,7 +51,7 @@ class CPluginView : public FObject, public IPlugView public: //------------------------------------------------------------------------ CPluginView (const ViewRect* rect = nullptr); - ~CPluginView () SMTG_OVERRIDE; + virtual ~CPluginView (); /** Returns its current frame rectangle. */ const ViewRect& getRect () const { return rect; } @@ -109,7 +109,7 @@ class CPluginView : public FObject, public IPlugView protected: ViewRect rect; void* systemWindow {nullptr}; - IPtr plugFrame; + IPlugFrame* plugFrame {nullptr}; }; } // namespace diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/readfile.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/readfile.cpp deleted file mode 100644 index 5f604577..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/readfile.cpp +++ /dev/null @@ -1,76 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// Flags : clang-format SMTGSequencer -// -// Category : readfile -// Filename : public.sdk/source/common/readfile.cpp -// Created by : Steinberg, 3/2023 -// Description : read file routine -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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 "readfile.h" -#include "public.sdk/source/vst/utility/stringconvert.h" -#include "pluginterfaces/base/fplatform.h" -#include -#include - -namespace Steinberg { - -//------------------------------------------------------------------------ -std::string readFile (const std::string& path) -{ -#if SMTG_OS_WINDOWS - auto u16Path = VST3::StringConvert::convert (path); - std::ifstream file (reinterpret_cast (u16Path.data ()), - std::ios_base::in | std::ios_base::binary); -#else - std::ifstream file (path, std::ios_base::in | std::ios_base::binary); -#endif - if (!file.is_open ()) - return {}; - -#if SMTG_CPP17 - auto size = file.seekg (0, std::ios_base::end).tellg (); - file.seekg (0, std::ios_base::beg); - std::string data; - data.resize (size); - file.read (data.data (), data.size ()); - if (file.bad ()) - return {}; - return data; -#else - std::stringstream buffer; - buffer << file.rdbuf (); - return buffer.str (); -#endif -} - -//------------------------------------------------------------------------ -} // namespace Steinberg diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/readfile.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/readfile.h deleted file mode 100644 index fdc7105e..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/readfile.h +++ /dev/null @@ -1,55 +0,0 @@ -//----------------------------------------------------------------------------- -// Flags : clang-format SMTGSequencer -// Project : VST SDK -// -// Category : readfile -// Filename : public.sdk/source/common/readfile.h -// Created by : Steinberg, 3/2023 -// Description : read file routine -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -#pragma once - -#include - -namespace Steinberg { - -//------------------------------------------------------------------------ -/** Reads entire file content -\ingroup sdkBase - -Returns entire file content at the given path -\endcode -*/ -std::string readFile (const std::string& path); - -//------------------------------------------------------------------------ - -} // namespace Steinberg diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/hostclasses.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/hostclasses.cpp index e04987e1..30a2e31c 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/hostclasses.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/hostclasses.cpp @@ -4,38 +4,37 @@ // Category : Helpers // Filename : public.sdk/source/vst/hosting/hostclasses.cpp // Created by : Steinberg, 03/05/2008. -// Description : VST 3 hostclasses, example impl. for IHostApplication, IAttributeList and IMessage +// Description : VST 3 hostclasses, example implementations for IHostApplication, IAttributeList and IMessage // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // 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, +// +// * 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 +// this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the Steinberg Media Technologies nor the names of its -// contributors may be used to endorse or promote products derived from this +// 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 COPYRIGHT OWNER 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 +// 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 COPYRIGHT OWNER 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 "hostclasses.h" -#include "public.sdk/source/vst/utility/stringconvert.h" #include @@ -47,34 +46,31 @@ HostApplication::HostApplication () { FUNKNOWN_CTOR - mPlugInterfaceSupport = owned (new PlugInterfaceSupport); + mPlugInterfaceSupport = owned (NEW PlugInterfaceSupport); } //----------------------------------------------------------------------------- tresult PLUGIN_API HostApplication::getName (String128 name) { - return VST3::StringConvert::convert ("My VST3 HostApplication", name) ? kResultTrue : - kInternalError; + String str ("My VST3 HostApplication"); + str.copyTo16 (name, 0, 127); + return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostApplication::createInstance (TUID cid, TUID _iid, void** obj) { - if (FUnknownPrivate::iidEqual (cid, IMessage::iid) && - FUnknownPrivate::iidEqual (_iid, IMessage::iid)) + FUID classID (FUID::fromTUID (cid)); + FUID interfaceID (FUID::fromTUID (_iid)); + if (classID == IMessage::iid && interfaceID == IMessage::iid) { *obj = new HostMessage; return kResultTrue; } - if (FUnknownPrivate::iidEqual (cid, IAttributeList::iid) && - FUnknownPrivate::iidEqual (_iid, IAttributeList::iid)) + else if (classID == IAttributeList::iid && interfaceID == IAttributeList::iid) { - if (auto al = HostAttributeList::make ()) - { - *obj = al.take (); - return kResultTrue; - } - return kOutOfMemory; + *obj = new HostAttributeList; + return kResultTrue; } *obj = nullptr; return kResultFalse; @@ -110,12 +106,17 @@ uint32 PLUGIN_API HostApplication::release () //----------------------------------------------------------------------------- IMPLEMENT_FUNKNOWN_METHODS (HostMessage, IMessage, IMessage::iid) //----------------------------------------------------------------------------- -HostMessage::HostMessage () {FUNKNOWN_CTOR} +HostMessage::HostMessage () : messageId (nullptr), attributeList (nullptr) +{ + FUNKNOWN_CTOR +} //----------------------------------------------------------------------------- -HostMessage::~HostMessage () noexcept +HostMessage::~HostMessage () { setMessageID (nullptr); + if (attributeList) + attributeList->release (); FUNKNOWN_DTOR } @@ -143,51 +144,38 @@ void PLUGIN_API HostMessage::setMessageID (const char* mid) IAttributeList* PLUGIN_API HostMessage::getAttributes () { if (!attributeList) - attributeList = HostAttributeList::make (); + attributeList = new HostAttributeList; return attributeList; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -struct HostAttributeList::Attribute +class HostAttribute { - enum class Type +public: + enum Type { - kUninitialized, kInteger, kFloat, kString, kBinary }; - Attribute () = default; - Attribute (int64 value) : type (Type::kInteger) { v.intValue = value; } - Attribute (double value) : type (Type::kFloat) { v.floatValue = value; } - /* size is in code unit (count of TChar) */ - Attribute (const TChar* value, uint32 sizeInCodeUnit) - : size (sizeInCodeUnit), type (Type::kString) + HostAttribute (int64 value) : size (0), type (kInteger) { v.intValue = value; } + HostAttribute (double value) : size (0), type (kFloat) { v.floatValue = value; } + /** size is in code unit (count of TChar) */ + HostAttribute (const TChar* value, uint32 sizeInCodeUnit) : size (sizeInCodeUnit), type (kString) { v.stringValue = new TChar[sizeInCodeUnit]; memcpy (v.stringValue, value, sizeInCodeUnit * sizeof (TChar)); } - Attribute (const void* value, uint32 sizeInBytes) : size (sizeInBytes), type (Type::kBinary) + HostAttribute (const void* value, uint32 sizeInBytes) : size (sizeInBytes), type (kBinary) { v.binaryValue = new char[sizeInBytes]; memcpy (v.binaryValue, value, sizeInBytes); } - Attribute (Attribute&& o) { *this = std::move (o); } - Attribute& operator= (Attribute&& o) - { - v = o.v; - size = o.size; - type = o.type; - o.size = 0; - o.type = Type::kUninitialized; - o.v = {}; - return *this; - } - ~Attribute () noexcept + ~HostAttribute () { if (size) delete[] v.binaryValue; @@ -195,7 +183,7 @@ struct HostAttributeList::Attribute int64 intValue () const { return v.intValue; } double floatValue () const { return v.floatValue; } - /* sizeInCodeUnit is in code unit (count of TChar) */ + /** sizeInCodeUnit is in code unit (count of TChar) */ const TChar* stringValue (uint32& sizeInCodeUnit) { sizeInCodeUnit = size; @@ -209,7 +197,7 @@ struct HostAttributeList::Attribute Type getType () const { return type; } -private: +protected: union v { int64 intValue; @@ -217,48 +205,58 @@ struct HostAttributeList::Attribute TChar* stringValue; char* binaryValue; } v; - uint32 size {0}; - Type type {Type::kUninitialized}; + uint32 size; + Type type; }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- IMPLEMENT_FUNKNOWN_METHODS (HostAttributeList, IAttributeList, IAttributeList::iid) - //----------------------------------------------------------------------------- -IPtr HostAttributeList::make () +HostAttributeList::HostAttributeList () { - return owned (new HostAttributeList); + FUNKNOWN_CTOR } //----------------------------------------------------------------------------- -HostAttributeList::HostAttributeList () {FUNKNOWN_CTOR} +HostAttributeList::~HostAttributeList () +{ + auto it = list.rbegin (); + while (it != list.rend ()) + { + delete it->second; + it++; + } + FUNKNOWN_DTOR +} //----------------------------------------------------------------------------- -HostAttributeList::~HostAttributeList () noexcept +void HostAttributeList::removeAttrID (AttrID aid) { - FUNKNOWN_DTOR + auto it = list.find (aid); + if (it != list.end ()) + { + delete it->second; + list.erase (it); + } } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::setInt (AttrID aid, int64 value) { - if (!aid) - return kInvalidArgument; - list[aid] = Attribute (value); + removeAttrID (aid); + list[aid] = new HostAttribute (value); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::getInt (AttrID aid, int64& value) { - if (!aid) - return kInvalidArgument; auto it = list.find (aid); - if (it != list.end () && it->second.getType () == Attribute::Type::kInteger) + if (it != list.end () && it->second) { - value = it->second.intValue (); + value = it->second->intValue (); return kResultTrue; } return kResultFalse; @@ -267,21 +265,18 @@ tresult PLUGIN_API HostAttributeList::getInt (AttrID aid, int64& value) //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::setFloat (AttrID aid, double value) { - if (!aid) - return kInvalidArgument; - list[aid] = Attribute (value); + removeAttrID (aid); + list[aid] = new HostAttribute (value); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::getFloat (AttrID aid, double& value) { - if (!aid) - return kInvalidArgument; auto it = list.find (aid); - if (it != list.end () && it->second.getType () == Attribute::Type::kFloat) + if (it != list.end () && it->second) { - value = it->second.floatValue (); + value = it->second->floatValue (); return kResultTrue; } return kResultFalse; @@ -290,24 +285,20 @@ tresult PLUGIN_API HostAttributeList::getFloat (AttrID aid, double& value) //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::setString (AttrID aid, const TChar* string) { - if (!aid) - return kInvalidArgument; + removeAttrID (aid); // + 1 for the null-terminate - auto length = tstrlen (string) + 1; - list[aid] = Attribute (string, length); + list[aid] = new HostAttribute (string, String (string).length () + 1); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::getString (AttrID aid, TChar* string, uint32 sizeInBytes) { - if (!aid) - return kInvalidArgument; auto it = list.find (aid); - if (it != list.end () && it->second.getType () == Attribute::Type::kString) + if (it != list.end () && it->second) { uint32 sizeInCodeUnit = 0; - const TChar* _string = it->second.stringValue (sizeInCodeUnit); + const TChar* _string = it->second->stringValue (sizeInCodeUnit); memcpy (string, _string, std::min (sizeInCodeUnit * sizeof (TChar), sizeInBytes)); return kResultTrue; } @@ -317,26 +308,22 @@ tresult PLUGIN_API HostAttributeList::getString (AttrID aid, TChar* string, uint //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::setBinary (AttrID aid, const void* data, uint32 sizeInBytes) { - if (!aid) - return kInvalidArgument; - list[aid] = Attribute (data, sizeInBytes); + removeAttrID (aid); + list[aid] = new HostAttribute (data, sizeInBytes); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::getBinary (AttrID aid, const void*& data, uint32& sizeInBytes) { - if (!aid) - return kInvalidArgument; auto it = list.find (aid); - if (it != list.end () && it->second.getType () == Attribute::Type::kBinary) + if (it != list.end () && it->second) { - data = it->second.binaryValue (sizeInBytes); + data = it->second->binaryValue (sizeInBytes); return kResultTrue; } sizeInBytes = 0; return kResultFalse; } - -} // Vst -} // Steinberg +} +} // namespace diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/hostclasses.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/hostclasses.h index 4a254cac..7b105a20 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/hostclasses.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/hostclasses.h @@ -4,32 +4,32 @@ // Category : Helpers // Filename : public.sdk/source/vst/hosting/hostclasses.h // Created by : Steinberg, 03/05/2008. -// Description : VST 3 hostclasses, example impl. for IHostApplication, IAttributeList and IMessage +// Description : VST 3 hostclasses, example implementations for IHostApplication, IAttributeList and IMessage // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // 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, +// +// * 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 +// this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the Steinberg Media Technologies nor the names of its -// contributors may be used to endorse or promote products derived from this +// 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 COPYRIGHT OWNER 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 +// 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 COPYRIGHT OWNER 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. //----------------------------------------------------------------------------- @@ -37,10 +37,9 @@ #pragma once #include "public.sdk/source/vst/hosting/pluginterfacesupport.h" +#include "base/source/fstring.h" #include "pluginterfaces/vst/ivsthostapplication.h" #include -#include -#include namespace Steinberg { namespace Vst { @@ -53,66 +52,64 @@ class HostApplication : public IHostApplication { public: HostApplication (); - virtual ~HostApplication () noexcept {FUNKNOWN_DTOR} + virtual ~HostApplication () { FUNKNOWN_DTOR } //--- IHostApplication --------------- - tresult PLUGIN_API getName (String128 name) override; - tresult PLUGIN_API createInstance (TUID cid, TUID _iid, void** obj) override; + tresult PLUGIN_API getName (String128 name) SMTG_OVERRIDE; + tresult PLUGIN_API createInstance (TUID cid, TUID _iid, void** obj) SMTG_OVERRIDE; DECLARE_FUNKNOWN_METHODS PlugInterfaceSupport* getPlugInterfaceSupport () const { return mPlugInterfaceSupport; } -private: +protected: IPtr mPlugInterfaceSupport; }; +class HostAttribute; //------------------------------------------------------------------------ -/** Example, ready to use implementation of IAttributeList. +/** Implementation's example of IAttributeList. \ingroup hostingBase */ -class HostAttributeList final : public IAttributeList +class HostAttributeList : public IAttributeList { public: - /** make a new attribute list instance */ - static IPtr make (); + HostAttributeList (); + virtual ~HostAttributeList (); - tresult PLUGIN_API setInt (AttrID aid, int64 value) override; - tresult PLUGIN_API getInt (AttrID aid, int64& value) override; - tresult PLUGIN_API setFloat (AttrID aid, double value) override; - tresult PLUGIN_API getFloat (AttrID aid, double& value) override; - tresult PLUGIN_API setString (AttrID aid, const TChar* string) override; - tresult PLUGIN_API getString (AttrID aid, TChar* string, uint32 sizeInBytes) override; - tresult PLUGIN_API setBinary (AttrID aid, const void* data, uint32 sizeInBytes) override; - tresult PLUGIN_API getBinary (AttrID aid, const void*& data, uint32& sizeInBytes) override; + tresult PLUGIN_API setInt (AttrID aid, int64 value) SMTG_OVERRIDE; + tresult PLUGIN_API getInt (AttrID aid, int64& value) SMTG_OVERRIDE; + tresult PLUGIN_API setFloat (AttrID aid, double value) SMTG_OVERRIDE; + tresult PLUGIN_API getFloat (AttrID aid, double& value) SMTG_OVERRIDE; + tresult PLUGIN_API setString (AttrID aid, const TChar* string) SMTG_OVERRIDE; + tresult PLUGIN_API getString (AttrID aid, TChar* string, uint32 sizeInBytes) SMTG_OVERRIDE; + tresult PLUGIN_API setBinary (AttrID aid, const void* data, uint32 sizeInBytes) SMTG_OVERRIDE; + tresult PLUGIN_API getBinary (AttrID aid, const void*& data, uint32& sizeInBytes) SMTG_OVERRIDE; - virtual ~HostAttributeList () noexcept; DECLARE_FUNKNOWN_METHODS -private: - HostAttributeList (); - - struct Attribute; - std::map list; +protected: + void removeAttrID (AttrID aid); + std::map list; }; //------------------------------------------------------------------------ -/** Example implementation of IMessage. +/** Implementation's example of IMessage. \ingroup hostingBase */ -class HostMessage final : public IMessage +class HostMessage : public IMessage { public: HostMessage (); - virtual ~HostMessage () noexcept; + virtual ~HostMessage (); - const char* PLUGIN_API getMessageID () override; - void PLUGIN_API setMessageID (const char* messageID) override; - IAttributeList* PLUGIN_API getAttributes () override; + const char* PLUGIN_API getMessageID () SMTG_OVERRIDE; + void PLUGIN_API setMessageID (const char* messageID) SMTG_OVERRIDE; + IAttributeList* PLUGIN_API getAttributes () SMTG_OVERRIDE; DECLARE_FUNKNOWN_METHODS -private: - char* messageId {nullptr}; - IPtr attributeList; +protected: + char* messageId; + HostAttributeList* attributeList; }; //------------------------------------------------------------------------ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.cpp deleted file mode 100644 index 9ce5b575..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.cpp +++ /dev/null @@ -1,340 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// -// Category : Helpers -// Filename : public.sdk/source/vst/hosting/module.cpp -// Created by : Steinberg, 08/2016 -// Description : hosting module classes -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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 "module.h" -#include "public.sdk/source/vst/utility/stringconvert.h" -#include "public.sdk/source/vst/utility/optional.h" -#include -#include - -//------------------------------------------------------------------------ -namespace VST3 { -namespace Hosting { - -//------------------------------------------------------------------------ -FactoryInfo::FactoryInfo (PFactoryInfo&& other) noexcept -{ - *this = std::move (other); -} - -//------------------------------------------------------------------------ -FactoryInfo& FactoryInfo::operator= (FactoryInfo&& other) noexcept -{ - info = std::move (other.info); - other.info = {}; - return *this; -} - -//------------------------------------------------------------------------ -FactoryInfo& FactoryInfo::operator= (PFactoryInfo&& other) noexcept -{ - info = std::move (other); - other = {}; - return *this; -} - -//------------------------------------------------------------------------ -std::string FactoryInfo::vendor () const noexcept -{ - return StringConvert::convert (info.vendor, PFactoryInfo::kNameSize); -} - -//------------------------------------------------------------------------ -std::string FactoryInfo::url () const noexcept -{ - return StringConvert::convert (info.url, PFactoryInfo::kURLSize); -} - -//------------------------------------------------------------------------ -std::string FactoryInfo::email () const noexcept -{ - return StringConvert::convert (info.email, PFactoryInfo::kEmailSize); -} - -//------------------------------------------------------------------------ -Steinberg::int32 FactoryInfo::flags () const noexcept -{ - return info.flags; -} - -//------------------------------------------------------------------------ -bool FactoryInfo::classesDiscardable () const noexcept -{ - return (info.flags & PFactoryInfo::kClassesDiscardable) != 0; -} - -//------------------------------------------------------------------------ -bool FactoryInfo::licenseCheck () const noexcept -{ - return (info.flags & PFactoryInfo::kLicenseCheck) != 0; -} - -//------------------------------------------------------------------------ -bool FactoryInfo::componentNonDiscardable () const noexcept -{ - return (info.flags & PFactoryInfo::kComponentNonDiscardable) != 0; -} - -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -PluginFactory::PluginFactory (const PluginFactoryPtr& factory) noexcept : factory (factory) -{ -} - -//------------------------------------------------------------------------ -void PluginFactory::setHostContext (Steinberg::FUnknown* context) const noexcept -{ - if (auto f = Steinberg::FUnknownPtr (factory)) - f->setHostContext (context); -} - -//------------------------------------------------------------------------ -FactoryInfo PluginFactory::info () const noexcept -{ - Steinberg::PFactoryInfo i; - factory->getFactoryInfo (&i); - return FactoryInfo (std::move (i)); -} - -//------------------------------------------------------------------------ -uint32_t PluginFactory::classCount () const noexcept -{ - auto count = factory->countClasses (); - assert (count >= 0); - return static_cast (count); -} - -//------------------------------------------------------------------------ -PluginFactory::ClassInfos PluginFactory::classInfos () const noexcept -{ - auto count = classCount (); - Optional factoryInfo; - ClassInfos classes; - classes.reserve (count); - auto f3 = Steinberg::FUnknownPtr (factory); - auto f2 = Steinberg::FUnknownPtr (factory); - Steinberg::PClassInfo ci; - Steinberg::PClassInfo2 ci2; - Steinberg::PClassInfoW ci3; - for (uint32_t i = 0; i < count; ++i) - { - if (f3 && f3->getClassInfoUnicode (i, &ci3) == Steinberg::kResultTrue) - classes.emplace_back (ci3); - else if (f2 && f2->getClassInfo2 (i, &ci2) == Steinberg::kResultTrue) - classes.emplace_back (ci2); - else if (factory->getClassInfo (i, &ci) == Steinberg::kResultTrue) - classes.emplace_back (ci); - auto& classInfo = classes.back (); - if (classInfo.vendor ().empty ()) - { - if (!factoryInfo) - factoryInfo = Optional (info ()); - classInfo.get ().vendor = factoryInfo->vendor (); - } - } - return classes; -} - -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -const UID& ClassInfo::ID () const noexcept -{ - return data.classID; -} - -//------------------------------------------------------------------------ -int32_t ClassInfo::cardinality () const noexcept -{ - return data.cardinality; -} - -//------------------------------------------------------------------------ -const std::string& ClassInfo::category () const noexcept -{ - return data.category; -} - -//------------------------------------------------------------------------ -const std::string& ClassInfo::name () const noexcept -{ - return data.name; -} - -//------------------------------------------------------------------------ -const std::string& ClassInfo::vendor () const noexcept -{ - return data.vendor; -} - -//------------------------------------------------------------------------ -const std::string& ClassInfo::version () const noexcept -{ - return data.version; -} - -//------------------------------------------------------------------------ -const std::string& ClassInfo::sdkVersion () const noexcept -{ - return data.sdkVersion; -} - -//------------------------------------------------------------------------ -const ClassInfo::SubCategories& ClassInfo::subCategories () const noexcept -{ - return data.subCategories; -} - -//------------------------------------------------------------------------ -Steinberg::uint32 ClassInfo::classFlags () const noexcept -{ - return data.classFlags; -} - -//------------------------------------------------------------------------ -ClassInfo::ClassInfo (const PClassInfo& info) noexcept -{ - data.classID = info.cid; - data.cardinality = info.cardinality; - data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize); - data.name = StringConvert::convert (info.name, PClassInfo::kNameSize); -} - -//------------------------------------------------------------------------ -ClassInfo::ClassInfo (const PClassInfo2& info) noexcept -{ - data.classID = info.cid; - data.cardinality = info.cardinality; - data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize); - data.name = StringConvert::convert (info.name, PClassInfo::kNameSize); - data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize); - data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize); - data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize); - parseSubCategories ( - StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize)); - data.classFlags = info.classFlags; -} - -//------------------------------------------------------------------------ -ClassInfo::ClassInfo (const PClassInfoW& info) noexcept -{ - data.classID = info.cid; - data.cardinality = info.cardinality; - data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize); - data.name = StringConvert::convert (info.name, PClassInfo::kNameSize); - data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize); - data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize); - data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize); - parseSubCategories ( - StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize)); - data.classFlags = info.classFlags; -} - -//------------------------------------------------------------------------ -void ClassInfo::parseSubCategories (const std::string& str) noexcept -{ - std::stringstream stream (str); - std::string item; - while (std::getline (stream, item, '|')) - data.subCategories.emplace_back (std::move (item)); -} - -//------------------------------------------------------------------------ -std::string ClassInfo::subCategoriesString () const noexcept -{ - std::string result; - if (data.subCategories.empty ()) - return result; - result = data.subCategories[0]; - for (auto index = 1u; index < data.subCategories.size (); ++index) - result += "|" + data.subCategories[index]; - return result; -} - -//------------------------------------------------------------------------ -namespace { - -//------------------------------------------------------------------------ -std::pair rangeOfScaleFactor (const std::string& name) -{ - auto result = std::make_pair (std::string::npos, std::string::npos); - size_t xIndex = name.find_last_of ('x'); - if (xIndex == std::string::npos) - return result; - - size_t indicatorIndex = name.find_last_of ('_'); - if (indicatorIndex == std::string::npos) - return result; - if (xIndex < indicatorIndex) - return result; - result.first = indicatorIndex + 1; - result.second = xIndex; - return result; -} - -//------------------------------------------------------------------------ -} // anonymous - -//------------------------------------------------------------------------ -Optional Module::Snapshot::decodeScaleFactor (const std::string& name) -{ - auto range = rangeOfScaleFactor (name); - if (range.first == std::string::npos || range.second == std::string::npos) - return {}; - std::string tmp (name.data () + range.first, range.second - range.first); - std::istringstream sstream (tmp); - sstream.imbue (std::locale::classic ()); - sstream.precision (static_cast (3)); - double result; - sstream >> result; - return Optional (result); -} - -//------------------------------------------------------------------------ -Optional Module::Snapshot::decodeUID (const std::string& filename) -{ - if (filename.size () < 45) - return {}; - if (filename.find ("_snapshot") != 32) - return {}; - auto uidStr = filename.substr (0, 32); - return UID::fromString (uidStr); -} - -//------------------------------------------------------------------------ -} // Hosting -} // VST3 diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.h deleted file mode 100644 index 36b3afad..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.h +++ /dev/null @@ -1,214 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// -// Category : Helpers -// Filename : public.sdk/source/vst/hosting/module.h -// Created by : Steinberg, 08/2016 -// Description : hosting module classes -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -#pragma once - -#include "../utility/uid.h" -#include "pluginterfaces/base/ipluginbase.h" -#include -#include - -//------------------------------------------------------------------------ -namespace VST3 { -namespace Hosting { - -//------------------------------------------------------------------------ -class FactoryInfo -{ -public: -//------------------------------------------------------------------------ - using PFactoryInfo = Steinberg::PFactoryInfo; - - FactoryInfo () noexcept {} - ~FactoryInfo () noexcept {} - FactoryInfo (const FactoryInfo&) noexcept = default; - FactoryInfo (PFactoryInfo&&) noexcept; - FactoryInfo (FactoryInfo&&) noexcept = default; - FactoryInfo& operator= (const FactoryInfo&) noexcept = default; - FactoryInfo& operator= (FactoryInfo&&) noexcept; - FactoryInfo& operator= (PFactoryInfo&&) noexcept; - - std::string vendor () const noexcept; - std::string url () const noexcept; - std::string email () const noexcept; - Steinberg::int32 flags () const noexcept; - bool classesDiscardable () const noexcept; - bool licenseCheck () const noexcept; - bool componentNonDiscardable () const noexcept; - - PFactoryInfo& get () noexcept { return info; } -//------------------------------------------------------------------------ -private: - PFactoryInfo info {}; -}; - -//------------------------------------------------------------------------ -class ClassInfo -{ -public: -//------------------------------------------------------------------------ - using SubCategories = std::vector; - using PClassInfo = Steinberg::PClassInfo; - using PClassInfo2 = Steinberg::PClassInfo2; - using PClassInfoW = Steinberg::PClassInfoW; - -//------------------------------------------------------------------------ - ClassInfo () noexcept {} - explicit ClassInfo (const PClassInfo& info) noexcept; - explicit ClassInfo (const PClassInfo2& info) noexcept; - explicit ClassInfo (const PClassInfoW& info) noexcept; - ClassInfo (const ClassInfo&) = default; - ClassInfo& operator= (const ClassInfo&) = default; - ClassInfo (ClassInfo&&) = default; - ClassInfo& operator= (ClassInfo&&) = default; - - const UID& ID () const noexcept; - int32_t cardinality () const noexcept; - const std::string& category () const noexcept; - const std::string& name () const noexcept; - const std::string& vendor () const noexcept; - const std::string& version () const noexcept; - const std::string& sdkVersion () const noexcept; - const SubCategories& subCategories () const noexcept; - std::string subCategoriesString () const noexcept; - Steinberg::uint32 classFlags () const noexcept; - - struct Data - { - UID classID; - int32_t cardinality; - std::string category; - std::string name; - std::string vendor; - std::string version; - std::string sdkVersion; - SubCategories subCategories; - Steinberg::uint32 classFlags = 0; - }; - - Data& get () noexcept { return data; } -//------------------------------------------------------------------------ -private: - void parseSubCategories (const std::string& str) noexcept; - Data data {}; -}; - -//------------------------------------------------------------------------ -class PluginFactory -{ -public: -//------------------------------------------------------------------------ - using ClassInfos = std::vector; - using PluginFactoryPtr = Steinberg::IPtr; - -//------------------------------------------------------------------------ - explicit PluginFactory (const PluginFactoryPtr& factory) noexcept; - - void setHostContext (Steinberg::FUnknown* context) const noexcept; - - FactoryInfo info () const noexcept; - uint32_t classCount () const noexcept; - ClassInfos classInfos () const noexcept; - - template - Steinberg::IPtr createInstance (const UID& classID) const noexcept; - - const PluginFactoryPtr& get () const noexcept { return factory; } -//------------------------------------------------------------------------ -private: - PluginFactoryPtr factory; -}; - -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -class Module -{ -public: -//------------------------------------------------------------------------ - struct Snapshot - { - struct ImageDesc - { - double scaleFactor {1.}; - std::string path; - }; - UID uid; - std::vector images; - - static Optional decodeScaleFactor (const std::string& path); - static Optional decodeUID (const std::string& filename); - }; - - using Ptr = std::shared_ptr; - using PathList = std::vector; - using SnapshotList = std::vector; - -//------------------------------------------------------------------------ - static Ptr create (const std::string& path, std::string& errorDescription); - static PathList getModulePaths (); - static SnapshotList getSnapshots (const std::string& modulePath); - /** get the path to the module info json file if it exists */ - static Optional getModuleInfoPath (const std::string& modulePath); - - const std::string& getName () const noexcept { return name; } - const std::string& getPath () const noexcept { return path; } - const PluginFactory& getFactory () const noexcept { return factory; } - bool isBundle () const noexcept { return hasBundleStructure; } -//------------------------------------------------------------------------ -protected: - virtual ~Module () noexcept = default; - virtual bool load (const std::string& path, std::string& errorDescription) = 0; - - PluginFactory factory {nullptr}; - std::string name; - std::string path; - bool hasBundleStructure {true}; -}; - -//------------------------------------------------------------------------ -template -inline Steinberg::IPtr PluginFactory::createInstance (const UID& classID) const noexcept -{ - T* obj = nullptr; - if (factory->createInstance (classID.data (), T::iid, reinterpret_cast (&obj)) == - Steinberg::kResultTrue) - return Steinberg::owned (obj); - return nullptr; -} - -//------------------------------------------------------------------------ -} // Hosting -} // VST3 diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_linux.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_linux.cpp deleted file mode 100644 index 404054c8..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_linux.cpp +++ /dev/null @@ -1,369 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// -// Category : Helpers -// Filename : public.sdk/source/vst/hosting/module_linux.cpp -// Created by : Steinberg, 08/2016 -// Description : hosting module classes (linux implementation) -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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 "module.h" -#include "../utility/optional.h" -#include "../utility/stringconvert.h" -#include -#include -#include -#include -#include - -#if SMTG_CPP17 - -#if __has_include() -#define USE_EXPERIMENTAL_FS 0 -#elif __has_include() -#define USE_EXPERIMENTAL_FS 1 -#endif - -#else // !SMTG_CPP17 - -#define USE_EXPERIMENTAL_FS 1 - -#endif // SMTG_CPP17 - -#if USE_EXPERIMENTAL_FS == 1 - -#include -namespace filesystem = std::experimental::filesystem; - -#else // USE_FILESYSTEM == 0 - -#include -namespace filesystem = std::filesystem; - -#endif // USE_FILESYSTEM - -//------------------------------------------------------------------------ -extern "C" { -using ModuleEntryFunc = bool (PLUGIN_API*) (void*); -using ModuleExitFunc = bool (PLUGIN_API*) (); -} - -//------------------------------------------------------------------------ -namespace VST3 { -namespace Hosting { - -using Path = filesystem::path; - -//------------------------------------------------------------------------ -namespace { - -//------------------------------------------------------------------------ -Optional getCurrentMachineName () -{ - struct utsname unameData; - - int res = uname (&unameData); - if (res != 0) - return {}; - - return {unameData.machine}; -} - -//------------------------------------------------------------------------ -Optional getApplicationPath () -{ - std::string appPath = ""; - - pid_t pid = getpid (); - char buf[10]; - sprintf (buf, "%d", pid); - std::string _link = "/proc/"; - _link.append (buf); - _link.append ("/exe"); - char proc[1024]; - int ch = readlink (_link.c_str (), proc, 1024); - if (ch == -1) - return {}; - - proc[ch] = 0; - appPath = proc; - std::string::size_type t = appPath.find_last_of ("/"); - appPath = appPath.substr (0, t); - - return Path {appPath}; -} - -//------------------------------------------------------------------------ -class LinuxModule : public Module -{ -public: - template - T getFunctionPointer (const char* name) - { - return reinterpret_cast (dlsym (mModule, name)); - } - - ~LinuxModule () override - { - factory = PluginFactory (nullptr); - - if (mModule) - { - if (auto moduleExit = getFunctionPointer ("ModuleExit")) - moduleExit (); - - dlclose (mModule); - } - } - - static Optional getSOPath (const std::string& inPath) - { - Path modulePath {inPath}; - if (!filesystem::is_directory (modulePath)) - return {}; - - auto stem = modulePath.stem (); - - modulePath /= "Contents"; - if (!filesystem::is_directory (modulePath)) - return {}; - - // use the Machine Hardware Name (from uname cmd-line) as prefix for "-linux" - auto machine = getCurrentMachineName (); - if (!machine) - return {}; - - modulePath /= *machine + "-linux"; - if (!filesystem::is_directory (modulePath)) - return {}; - - stem.replace_extension (".so"); - modulePath /= stem; - return Optional (std::move (modulePath)); - } - - bool load (const std::string& inPath, std::string& errorDescription) override - { - auto modulePath = getSOPath (inPath); - if (!modulePath) - { - errorDescription = inPath + " is not a module directory."; - return false; - } - - mModule = dlopen (reinterpret_cast (modulePath->generic_string ().data ()), - RTLD_LAZY); - if (!mModule) - { - errorDescription = "dlopen failed.\n"; - errorDescription += dlerror (); - return false; - } - // ModuleEntry is mandatory - auto moduleEntry = getFunctionPointer ("ModuleEntry"); - if (!moduleEntry) - { - errorDescription = - "The shared library does not export the required 'ModuleEntry' function"; - return false; - } - // ModuleExit is mandatory - auto moduleExit = getFunctionPointer ("ModuleExit"); - if (!moduleExit) - { - errorDescription = - "The shared library does not export the required 'ModuleExit' function"; - return false; - } - auto factoryProc = getFunctionPointer ("GetPluginFactory"); - if (!factoryProc) - { - errorDescription = - "The shared library does not export the required 'GetPluginFactory' function"; - return false; - } - - if (!moduleEntry (mModule)) - { - errorDescription = "Calling 'ModuleEntry' failed"; - return false; - } - auto f = Steinberg::FUnknownPtr (owned (factoryProc ())); - if (!f) - { - errorDescription = "Calling 'GetPluginFactory' returned nullptr"; - return false; - } - factory = PluginFactory (f); - return true; - } - - void* mModule {nullptr}; -}; - -//------------------------------------------------------------------------ -void findFilesWithExt (const std::string& path, const std::string& ext, Module::PathList& pathList, - bool recursive = true) -{ - try - { - for (auto& p : filesystem::directory_iterator (path)) - { - if (p.path ().extension () == ext) - { - pathList.push_back (p.path ().generic_string ()); - } - else if (recursive && p.status ().type () == filesystem::file_type::directory) - { - findFilesWithExt (p.path (), ext, pathList); - } - } - } - catch (...) - { - } -} - -//------------------------------------------------------------------------ -void findModules (const std::string& path, Module::PathList& pathList) -{ - findFilesWithExt (path, ".vst3", pathList); -} - -//------------------------------------------------------------------------ -} // anonymous - -//------------------------------------------------------------------------ -Module::Ptr Module::create (const std::string& path, std::string& errorDescription) -{ - auto _module = std::make_shared (); - if (_module->load (path, errorDescription)) - { - _module->path = path; - auto it = std::find_if (path.rbegin (), path.rend (), - [] (const std::string::value_type& c) { return c == '/'; }); - if (it != path.rend ()) - _module->name = {it.base (), path.end ()}; - return _module; - } - return nullptr; -} - -//------------------------------------------------------------------------ -Module::PathList Module::getModulePaths () -{ - /* VST3 component locations on linux : - * User privately installed : $HOME/.vst3/ - * Distribution installed : /usr/lib/vst3/ - * Locally installed : /usr/local/lib/vst3/ - * Application : /$APPFOLDER/vst3/ - */ - - const auto systemPaths = {"/usr/lib/vst3/", "/usr/local/lib/vst3/"}; - - PathList list; - if (auto homeDir = getenv ("HOME")) - { - filesystem::path homePath (homeDir); - homePath /= ".vst3"; - findModules (homePath.generic_string (), list); - } - for (auto path : systemPaths) - findModules (path, list); - - // application level - auto appPath = getApplicationPath (); - if (appPath) - { - *appPath /= "vst3"; - findModules (appPath->generic_string (), list); - } - - return list; -} - -//------------------------------------------------------------------------ -Module::SnapshotList Module::getSnapshots (const std::string& modulePath) -{ - SnapshotList result; - filesystem::path path (modulePath); - path /= "Contents"; - path /= "Resources"; - path /= "Snapshots"; - PathList pngList; - findFilesWithExt (path, ".png", pngList, false); - for (auto& png : pngList) - { - filesystem::path p (png); - auto filename = p.filename ().generic_string (); - auto uid = Snapshot::decodeUID (filename); - if (!uid) - continue; - auto scaleFactor = 1.; - if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename)) - scaleFactor = *decodedScaleFactor; - - Module::Snapshot::ImageDesc desc; - desc.scaleFactor = scaleFactor; - desc.path = std::move (png); - bool found = false; - for (auto& entry : result) - { - if (entry.uid != *uid) - continue; - found = true; - entry.images.emplace_back (std::move (desc)); - break; - } - if (found) - continue; - Module::Snapshot snapshot; - snapshot.uid = *uid; - snapshot.images.emplace_back (std::move (desc)); - result.emplace_back (std::move (snapshot)); - } - return result; -} - -//------------------------------------------------------------------------ -Optional Module::getModuleInfoPath (const std::string& modulePath) -{ - filesystem::path path (modulePath); - path /= "Contents"; - path /= "Resources"; - path /= "moduleinfo.json"; - if (filesystem::exists (path)) - return {path.generic_string ()}; - return {}; -} - -//------------------------------------------------------------------------ -} // Hosting -} // VST3 diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_mac.mm b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_mac.mm deleted file mode 100644 index 49a71555..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_mac.mm +++ /dev/null @@ -1,390 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// -// Category : Helpers -// Filename : public.sdk/source/vst/hosting/module_mac.mm -// Created by : Steinberg, 08/2016 -// Description : hosting module classes (macOS implementation) -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -#import "module.h" - -#import -#import - -#if !__has_feature(objc_arc) -#error this file needs to be compiled with automatic reference counting enabled -#endif - -//------------------------------------------------------------------------ -extern "C" { -typedef bool (*BundleEntryFunc) (CFBundleRef); -typedef bool (*BundleExitFunc) (); -} - -//------------------------------------------------------------------------ -namespace VST3 { -namespace Hosting { - -//------------------------------------------------------------------------ -namespace { - -//------------------------------------------------------------------------ -template -class CFPtr -{ -public: - inline CFPtr (const T& obj = nullptr) : obj (obj) {} - inline CFPtr (CFPtr&& other) { *this = other; } - inline ~CFPtr () - { - if (obj) - CFRelease (obj); - } - - inline CFPtr& operator= (CFPtr&& other) - { - obj = other.obj; - other.obj = nullptr; - return *this; - } - inline CFPtr& operator= (const T& o) - { - if (obj) - CFRelease (obj); - obj = o; - return *this; - } - inline operator T () const { return obj; } // act as T -private: - CFPtr (const CFPtr& other) = delete; - CFPtr& operator= (const CFPtr& other) = delete; - - T obj = nullptr; -}; - -//------------------------------------------------------------------------ -class MacModule : public Module -{ -public: - template - T getFunctionPointer (const char* name) - { - assert (bundle); - CFPtr functionName ( - CFStringCreateWithCString (kCFAllocatorDefault, name, kCFStringEncodingASCII)); - return reinterpret_cast (CFBundleGetFunctionPointerForName (bundle, functionName)); - } - - bool loadInternal (const std::string& path, std::string& errorDescription) - { - CFPtr url (CFURLCreateFromFileSystemRepresentation ( - kCFAllocatorDefault, reinterpret_cast (path.data ()), path.length (), - true)); - if (!url) - return false; - bundle = CFBundleCreate (kCFAllocatorDefault, url); - CFErrorRef error = nullptr; - if (!bundle || !CFBundleLoadExecutableAndReturnError (bundle, &error)) - { - if (error) - { - CFPtr errorString (CFErrorCopyDescription (error)); - if (errorString) - { - auto stringLength = CFStringGetLength (errorString); - auto maxSize = - CFStringGetMaximumSizeForEncoding (stringLength, kCFStringEncodingUTF8); - auto buffer = std::make_unique (maxSize); - if (CFStringGetCString (errorString, buffer.get (), maxSize, - kCFStringEncodingUTF8)) - errorDescription = buffer.get (); - CFRelease (error); - } - } - else - { - errorDescription = "Could not create Bundle for path: " + path; - } - return false; - } - // bundleEntry is mandatory - auto bundleEntry = getFunctionPointer ("bundleEntry"); - if (!bundleEntry) - { - errorDescription = "Bundle does not export the required 'bundleEntry' function"; - return false; - } - // bundleExit is mandatory - auto bundleExit = getFunctionPointer ("bundleExit"); - if (!bundleExit) - { - errorDescription = "Bundle does not export the required 'bundleExit' function"; - return false; - } - auto factoryProc = getFunctionPointer ("GetPluginFactory"); - if (!factoryProc) - { - errorDescription = "Bundle does not export the required 'GetPluginFactory' function"; - return false; - } - if (!bundleEntry (bundle)) - { - errorDescription = "Calling 'bundleEntry' failed"; - return false; - } - auto f = owned (factoryProc ()); - if (!f) - { - errorDescription = "Calling 'GetPluginFactory' returned nullptr"; - return false; - } - factory = PluginFactory (f); - return true; - } - - bool load (const std::string& path, std::string& errorDescription) override - { - if (!path.empty () && path[0] != '/') - { - auto buffer = std::make_unique (PATH_MAX); - auto workDir = getcwd (buffer.get (), PATH_MAX); - if (workDir) - { - std::string wd (workDir); - wd += "/"; - return loadInternal (wd + path, errorDescription); - } - } - return loadInternal (path, errorDescription); - } - - ~MacModule () override - { - factory = PluginFactory (nullptr); - - if (bundle) - { - if (auto bundleExit = getFunctionPointer ("bundleExit")) - bundleExit (); - } - } - - CFPtr bundle; -}; - -//------------------------------------------------------------------------ -void findModulesInDirectory (NSURL* dirUrl, Module::PathList& result) -{ - dirUrl = [dirUrl URLByResolvingSymlinksInPath]; - if (!dirUrl) - return; - NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] - enumeratorAtURL: dirUrl - includingPropertiesForKeys:nil - options:NSDirectoryEnumerationSkipsPackageDescendants - errorHandler:nil]; - for (NSURL* url in enumerator) - { - if ([[[url lastPathComponent] pathExtension] isEqualToString:@"vst3"]) - { - CFPtr archs ( - CFBundleCopyExecutableArchitecturesForURL (static_cast (url))); - if (archs) - result.emplace_back ([url.path UTF8String]); - } - else - { - id resValue; - if (![url getResourceValue:&resValue forKey:NSURLIsSymbolicLinkKey error:nil]) - continue; - if (!static_cast (resValue).boolValue) - continue; - auto resolvedUrl = [url URLByResolvingSymlinksInPath]; - if (![resolvedUrl getResourceValue:&resValue forKey:NSURLIsDirectoryKey error:nil]) - continue; - if (!static_cast (resValue).boolValue) - continue; - findModulesInDirectory (resolvedUrl, result); - } - } -} - -//------------------------------------------------------------------------ -void getModules (NSSearchPathDomainMask domain, Module::PathList& result) -{ - NSURL* libraryUrl = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory - inDomain:domain - appropriateForURL:nil - create:NO - error:nil]; - if (libraryUrl == nil) - return; - NSURL* audioUrl = [libraryUrl URLByAppendingPathComponent:@"Audio"]; - if (audioUrl == nil) - return; - NSURL* plugInsUrl = [audioUrl URLByAppendingPathComponent:@"Plug-Ins"]; - if (plugInsUrl == nil) - return; - NSURL* vst3Url = - [[plugInsUrl URLByAppendingPathComponent:@"VST3"] URLByResolvingSymlinksInPath]; - if (vst3Url == nil) - return; - findModulesInDirectory (vst3Url, result); -} - -//------------------------------------------------------------------------ -void getApplicationModules (Module::PathList& result) -{ - auto bundle = CFBundleGetMainBundle (); - if (!bundle) - return; - auto bundleUrl = static_cast (CFBridgingRelease (CFBundleCopyBundleURL (bundle))); - if (!bundleUrl) - return; - auto resUrl = [bundleUrl URLByAppendingPathComponent:@"Contents"]; - if (!resUrl) - return; - auto vst3Url = [resUrl URLByAppendingPathComponent:@"VST3"]; - if (!vst3Url) - return; - findModulesInDirectory (vst3Url, result); -} - -//------------------------------------------------------------------------ -void getModuleSnapshots (const std::string& path, Module::SnapshotList& result) -{ - auto nsString = [NSString stringWithUTF8String:path.data ()]; - if (!nsString) - return; - auto bundleUrl = [NSURL fileURLWithPath:nsString]; - if (!bundleUrl) - return; - auto urls = [NSBundle URLsForResourcesWithExtension:@"png" - subdirectory:@"Snapshots" - inBundleWithURL:bundleUrl]; - if (!urls || [urls count] == 0) - return; - - for (NSURL* url in urls) - { - std::string fullpath ([[url path] UTF8String]); - std::string filename ([[[url path] lastPathComponent] UTF8String]); - auto uid = Module::Snapshot::decodeUID (filename); - if (!uid) - continue; - - auto scaleFactor = 1.; - if (auto decodedScaleFactor = Module::Snapshot::decodeScaleFactor (filename)) - scaleFactor = *decodedScaleFactor; - - Module::Snapshot::ImageDesc desc; - desc.scaleFactor = scaleFactor; - desc.path = std::move (fullpath); - bool found = false; - for (auto& entry : result) - { - if (entry.uid != *uid) - continue; - found = true; - entry.images.emplace_back (std::move (desc)); - break; - } - if (found) - continue; - Module::Snapshot snapshot; - snapshot.uid = *uid; - snapshot.images.emplace_back (std::move (desc)); - result.emplace_back (std::move (snapshot)); - } -} - -//------------------------------------------------------------------------ -} // anonymous - -//------------------------------------------------------------------------ -Module::Ptr Module::create (const std::string& path, std::string& errorDescription) -{ - auto module = std::make_shared (); - if (module->load (path, errorDescription)) - { - module->path = path; - auto it = std::find_if (path.rbegin (), path.rend (), - [] (const std::string::value_type& c) { return c == '/'; }); - if (it != path.rend ()) - module->name = {it.base (), path.end ()}; - return std::move (module); - } - return nullptr; -} - -//------------------------------------------------------------------------ -Module::PathList Module::getModulePaths () -{ - PathList list; - getModules (NSUserDomainMask, list); - getModules (NSLocalDomainMask, list); - // TODO getModules (NSNetworkDomainMask, list); - getApplicationModules (list); - return list; -} - -//------------------------------------------------------------------------ -Module::SnapshotList Module::getSnapshots (const std::string& modulePath) -{ - SnapshotList list; - getModuleSnapshots (modulePath, list); - return list; -} - -//------------------------------------------------------------------------ -Optional Module::getModuleInfoPath (const std::string& modulePath) -{ - auto nsString = [NSString stringWithUTF8String:modulePath.data ()]; - if (!nsString) - return {}; - auto bundleUrl = [NSURL fileURLWithPath:nsString]; - if (!bundleUrl) - return {}; - auto moduleInfoUrl = [NSBundle URLForResource:@"moduleinfo" - withExtension:@"json" - subdirectory:nullptr - inBundleWithURL:bundleUrl]; - if (!moduleInfoUrl) - return {}; - NSError* error = nil; - if ([moduleInfoUrl checkResourceIsReachableAndReturnError:&error]) - return {std::string (moduleInfoUrl.fileSystemRepresentation)}; - return {}; -} - -//------------------------------------------------------------------------ -} // Hosting -} // VST3 diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_win32.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_win32.cpp deleted file mode 100644 index 77990fdc..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_win32.cpp +++ /dev/null @@ -1,647 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// -// Category : Helpers -// Filename : public.sdk/source/vst/hosting/module_win32.cpp -// Created by : Steinberg, 08/2016 -// Description : hosting module classes (win32 implementation) -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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 "../utility/optional.h" -#include "../utility/stringconvert.h" -#include "module.h" - -#include -#include - -#include -#include - -#if SMTG_CPP17 - -#if __has_include() -#define USE_FILESYSTEM 1 -#elif __has_include() -#define USE_FILESYSTEM 0 -#endif - -#else // !SMTG_CPP17 - -#define USE_FILESYSTEM 0 - -#endif // SMTG_CPP17 - -#if USE_FILESYSTEM == 1 - -#include -namespace filesystem = std::filesystem; - -#else // USE_FILESYSTEM == 0 - -// The header is deprecated. It is superseded by the C++17 -// header. You can define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING to silence the -// warning, otherwise the build will fail in VS2019 16.3.0 -#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING -#include -namespace filesystem = std::experimental::filesystem; - -#endif // USE_FILESYSTEM - -#pragma comment(lib, "Shell32") - -//------------------------------------------------------------------------ -extern "C" { -using InitModuleFunc = bool (PLUGIN_API*) (); -using ExitModuleFunc = bool (PLUGIN_API*) (); -} - -//------------------------------------------------------------------------ -namespace VST3 { -namespace Hosting { - -constexpr unsigned long kIPPathNameMax = 1024; - -//------------------------------------------------------------------------ -namespace { - -#define USE_OLE !USE_FILESYSTEM - -// for testing only -#if 0 // DEVELOPMENT -#define LOG_ENABLE 1 -#else -#define LOG_ENABLE 0 -#endif - -#if SMTG_PLATFORM_64 - -#if SMTG_OS_WINDOWS_ARM - -#if SMTG_CPU_ARM_64EC -constexpr auto architectureString = "arm64ec-win"; -constexpr auto architectureX64String = "x86_64-win"; -#else // !SMTG_CPU_ARM_64EC -constexpr auto architectureString = "arm64-win"; -#endif // SMTG_CPU_ARM_64EC - -constexpr auto architectureArm64XString = "arm64x-win"; - -#else // !SMTG_OS_WINDOWS_ARM -constexpr auto architectureString = "x86_64-win"; -#endif // SMTG_OS_WINDOWS_ARM - -#else // !SMTG_PLATFORM_64 - -#if SMTG_OS_WINDOWS_ARM -constexpr auto architectureString = "arm-win"; -#else // !SMTG_OS_WINDOWS_ARM -constexpr auto architectureString = "x86-win"; -#endif // SMTG_OS_WINDOWS_ARM - -#endif // SMTG_PLATFORM_64 - -#if USE_OLE -//------------------------------------------------------------------------ -struct Ole -{ - static Ole& instance () - { - static Ole gInstance; - return gInstance; - } - -private: - Ole () { OleInitialize (nullptr); } - ~Ole () { OleUninitialize (); } -}; -#endif // USE_OLE - -//------------------------------------------------------------------------ -class Win32Module : public Module -{ -public: - template - T getFunctionPointer (const char* name) - { - return reinterpret_cast (GetProcAddress (mModule, name)); - } - - ~Win32Module () override - { - factory = PluginFactory (nullptr); - - if (mModule) - { - // ExitDll is optional - if (auto dllExit = getFunctionPointer ("ExitDll")) - dllExit (); - - FreeLibrary ((HMODULE)mModule); - } - } - - //--- ----------------------------------------------------------------------- - HINSTANCE loadAsPackage (const std::string& inPath, const char* archString = architectureString) - { - filesystem::path p (inPath); - auto filename = p.filename (); - p /= "Contents"; - p /= archString; - p /= filename; - auto wideStr = StringConvert::convert (p.string ()); - HINSTANCE instance = LoadLibraryW (reinterpret_cast (wideStr.data ())); -#if SMTG_CPU_ARM_64EC - if (instance == nullptr) - instance = loadAsPackage (inPath, architectureArm64XString); - if (instance == nullptr) - instance = loadAsPackage (inPath, architectureX64String); -#endif - return instance; - } - - //--- ----------------------------------------------------------------------- - HINSTANCE loadAsDll (const std::string& inPath, std::string& errorDescription) - { - auto wideStr = StringConvert::convert (inPath); - HINSTANCE instance = LoadLibraryW (reinterpret_cast (wideStr.data ())); - if (instance == nullptr) - { - auto lastError = GetLastError (); - LPVOID lpMessageBuffer {nullptr}; - if (FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - nullptr, lastError, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&lpMessageBuffer, 0, nullptr) > 0) - { - errorDescription = "LoadLibray failed: " + std::string ((char*)lpMessageBuffer); - LocalFree (lpMessageBuffer); - } - else - { - errorDescription = - "LoadLibrary failed with error number : " + std::to_string (lastError); - } - } - else - { - hasBundleStructure = false; - } - return instance; - } - - //--- ----------------------------------------------------------------------- - bool load (const std::string& inPath, std::string& errorDescription) override - { - // filesystem::u8path is deprecated in C++20 -#if SMTG_CPP20 - const filesystem::path tmp (inPath); -#else - const filesystem::path tmp = filesystem::u8path (inPath); -#endif - if (filesystem::is_directory (tmp)) - { - // try as package (bundle) - mModule = loadAsPackage (inPath); - } - else - { - // try old definition without package - mModule = loadAsDll (inPath, errorDescription); - } - if (mModule == nullptr) - return false; - - auto factoryProc = getFunctionPointer ("GetPluginFactory"); - if (!factoryProc) - { - errorDescription = "The dll does not export the required 'GetPluginFactory' function"; - return false; - } - // InitDll is optional - auto dllEntry = getFunctionPointer ("InitDll"); - if (dllEntry && !dllEntry ()) - { - errorDescription = "Calling 'InitDll' failed"; - return false; - } - auto f = Steinberg::FUnknownPtr (owned (factoryProc ())); - if (!f) - { - errorDescription = "Calling 'GetPluginFactory' returned nullptr"; - return false; - } - factory = PluginFactory (f); - return true; - } - - HINSTANCE mModule {nullptr}; -}; - -//------------------------------------------------------------------------ -bool openVST3Package (const filesystem::path& p, const char* archString, - filesystem::path* result = nullptr) -{ - auto path = p; - path /= "Contents"; - path /= archString; - path /= p.filename (); - auto hFile = CreateFileW (reinterpret_cast (path.c_str ()), GENERIC_READ, - FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); - if (hFile != INVALID_HANDLE_VALUE) - { - CloseHandle (hFile); - if (result) - *result = path; - return true; - } - return false; -} - -//------------------------------------------------------------------------ -bool checkVST3Package (const filesystem::path& p, filesystem::path* result = nullptr, - const char* archString = architectureString) -{ - if (openVST3Package (p, archString, result)) - return true; - -#if SMTG_CPU_ARM_64EC - if (openVST3Package (p, architectureArm64XString, result)) - return true; - if (openVST3Package (p, architectureX64String, result)) - return true; -#endif - return false; -} - -//------------------------------------------------------------------------ -bool isFolderSymbolicLink (const filesystem::path& p) -{ -#if USE_FILESYSTEM - if (/*filesystem::exists (p) &&*/ filesystem::is_symlink (p)) - return true; -#else - std::wstring wString = p.generic_wstring (); - auto attrib = GetFileAttributesW (reinterpret_cast (wString.c_str ())); - if (attrib & FILE_ATTRIBUTE_REPARSE_POINT) - { - auto hFile = CreateFileW (reinterpret_cast (wString.c_str ()), GENERIC_READ, - FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); - if (hFile == INVALID_HANDLE_VALUE) - return true; - CloseHandle (hFile); - } -#endif - return false; -} - -//------------------------------------------------------------------------ -Optional getKnownFolder (REFKNOWNFOLDERID folderID) -{ - PWSTR wideStr {}; - if (FAILED (SHGetKnownFolderPath (folderID, 0, nullptr, &wideStr))) - return {}; - return StringConvert::convert (Steinberg::wscast (wideStr)); -} - -//------------------------------------------------------------------------ -VST3::Optional resolveShellLink (const filesystem::path& p) -{ -#if USE_FILESYSTEM - return {filesystem::read_symlink (p).lexically_normal ()}; -#elif USE_OLE - Ole::instance (); - - IShellLink* shellLink = nullptr; - if (!SUCCEEDED (CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, - IID_IShellLink, reinterpret_cast (&shellLink)))) - return {}; - - IPersistFile* persistFile = nullptr; - if (!SUCCEEDED ( - shellLink->QueryInterface (IID_IPersistFile, reinterpret_cast (&persistFile)))) - return {}; - - if (!SUCCEEDED (persistFile->Load (p.wstring ().data (), STGM_READ))) - return {}; - - if (!SUCCEEDED (shellLink->Resolve (nullptr, MAKELONG (SLR_NO_UI, 500)))) - return {}; - - WCHAR resolvedPath[kIPPathNameMax]; - if (!SUCCEEDED (shellLink->GetPath (resolvedPath, kIPPathNameMax, nullptr, SLGP_SHORTPATH))) - return {}; - - std::wstring longPath; - longPath.resize (kIPPathNameMax); - auto numChars = - GetLongPathNameW (resolvedPath, const_cast (longPath.data ()), kIPPathNameMax); - if (!numChars) - return {}; - longPath.resize (numChars); - - persistFile->Release (); - shellLink->Release (); - - return {filesystem::path (longPath)}; -#else - return {}; -#endif -} - -//------------------------------------------------------------------------ -void addToPathList (Module::PathList& pathList, const std::string& toAdd) -{ -#if LOG_ENABLE - std::cout << "=> add: " << toAdd << "\n"; -#endif - - pathList.push_back (toAdd); -} - -//------------------------------------------------------------------------ -void findFilesWithExt (const filesystem::path& path, const std::string& ext, - Module::PathList& pathList, bool recursive = true) -{ - for (auto& p : filesystem::directory_iterator (path)) - { -#if USE_FILESYSTEM - filesystem::path finalPath (p); - if (isFolderSymbolicLink (p)) - { - if (auto res = resolveShellLink (p)) - { - finalPath = *res; - if (!filesystem::exists (finalPath)) - continue; - } - else - continue; - } - const auto& cpExt = finalPath.extension (); - if (cpExt == ext) - { - filesystem::path result; - if (checkVST3Package (finalPath, &result)) - { - addToPathList (pathList, result.generic_string ()); - continue; - } - } - - if (filesystem::is_directory (finalPath)) - { - if (recursive) - findFilesWithExt (finalPath, ext, pathList, recursive); - } - else if (cpExt == ext) - addToPathList (pathList, finalPath.generic_string ()); -#else - const auto& cp = p.path (); - const auto& cpExt = cp.extension (); - if (cpExt == ext) - { - if ((p.status ().type () == filesystem::file_type::directory) || - isFolderSymbolicLink (p)) - { - filesystem::path result; - if (checkVST3Package (p, &result)) - { - addToPathList (pathList, result.generic_u8string ()); - continue; - } - findFilesWithExt (cp, ext, pathList, recursive); - } - else - addToPathList (pathList, cp.generic_u8string ()); - } - else if (recursive) - { - if (p.status ().type () == filesystem::file_type::directory) - { - findFilesWithExt (cp, ext, pathList, recursive); - } - else if (cpExt == ".lnk") - { - if (auto resolvedLink = resolveShellLink (cp)) - { - if (resolvedLink->extension () == ext) - { - if (filesystem::is_directory (*resolvedLink) || - isFolderSymbolicLink (*resolvedLink)) - { - filesystem::path result; - if (checkVST3Package (*resolvedLink, &result)) - { - addToPathList (pathList, result.generic_u8string ()); - continue; - } - findFilesWithExt (*resolvedLink, ext, pathList, recursive); - } - else - addToPathList (pathList, resolvedLink->generic_u8string ()); - } - else if (filesystem::is_directory (*resolvedLink)) - { - const auto& str = resolvedLink->generic_u8string (); - if (cp.generic_u8string ().compare (0, str.size (), str.data (), - str.size ()) != 0) - findFilesWithExt (*resolvedLink, ext, pathList, recursive); - } - } - } - } -#endif - } -} - -//------------------------------------------------------------------------ -void findModules (const filesystem::path& path, Module::PathList& pathList) -{ - if (filesystem::exists (path)) - findFilesWithExt (path, ".vst3", pathList); -} - -//------------------------------------------------------------------------ -Optional getContentsDirectoryFromModuleExecutablePath ( - const std::string& modulePath) -{ - filesystem::path path (modulePath); - - path = path.parent_path (); - if (path.filename () != architectureString) - return {}; - path = path.parent_path (); - if (path.filename () != "Contents") - return {}; - - return Optional {std::move (path)}; -} - -//------------------------------------------------------------------------ -} // anonymous - -//------------------------------------------------------------------------ -Module::Ptr Module::create (const std::string& path, std::string& errorDescription) -{ - auto _module = std::make_shared (); - if (_module->load (path, errorDescription)) - { - _module->path = path; - auto it = std::find_if (path.rbegin (), path.rend (), - [] (const std::string::value_type& c) { return c == '/'; }); - if (it != path.rend ()) - _module->name = {it.base (), path.end ()}; - return _module; - } - return nullptr; -} - -//------------------------------------------------------------------------ -Module::PathList Module::getModulePaths () -{ - // find plug-ins located in common/VST3 - PathList list; - if (auto knownFolder = getKnownFolder (FOLDERID_UserProgramFilesCommon)) - { - filesystem::path path (*knownFolder); - path.append ("VST3"); -#if LOG_ENABLE - std::cout << "Check folder: " << path << "\n"; -#endif - findModules (path, list); - } - - if (auto knownFolder = getKnownFolder (FOLDERID_ProgramFilesCommon)) - { - filesystem::path path (*knownFolder); - path.append ("VST3"); -#if LOG_ENABLE - std::cout << "Check folder: " << path << "\n"; -#endif - findModules (path, list); - } - - // find plug-ins located in VST3 (application folder) - WCHAR modulePath[kIPPathNameMax]; - GetModuleFileNameW (nullptr, modulePath, kIPPathNameMax); - auto appPath = StringConvert::convert (Steinberg::wscast (modulePath)); - filesystem::path path (appPath); - path = path.parent_path (); - path = path.append ("VST3"); -#if LOG_ENABLE - std::cout << "Check folder: " << path << "\n"; -#endif - findModules (path, list); - - return list; -} - -//------------------------------------------------------------------------ -Optional Module::getModuleInfoPath (const std::string& modulePath) -{ - auto path = getContentsDirectoryFromModuleExecutablePath (modulePath); - if (!path) - { - filesystem::path p; - if (!checkVST3Package ({modulePath}, &p)) - return {}; - p = p.parent_path (); - p = p.parent_path (); - path = Optional {p}; - } - - *path /= "Resources"; - *path /= "moduleinfo.json"; - - if (filesystem::exists (*path)) - { - return {path->generic_string ()}; - } - return {}; -} - -//------------------------------------------------------------------------ -Module::SnapshotList Module::getSnapshots (const std::string& modulePath) -{ - SnapshotList result; - auto path = getContentsDirectoryFromModuleExecutablePath (modulePath); - if (!path) - { - filesystem::path p; - if (!checkVST3Package ({modulePath}, &p)) - return result; - p = p.parent_path (); - p = p.parent_path (); - path = Optional (p); - } - - *path /= "Resources"; - *path /= "Snapshots"; - - if (filesystem::exists (*path) == false) - return result; - - PathList pngList; - findFilesWithExt (*path, ".png", pngList, false); - for (auto& png : pngList) - { - filesystem::path p (png); - auto filename = p.filename ().generic_string (); - auto uid = Snapshot::decodeUID (filename); - if (!uid) - continue; - auto scaleFactor = 1.; - if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename)) - scaleFactor = *decodedScaleFactor; - - Module::Snapshot::ImageDesc desc; - desc.scaleFactor = scaleFactor; - desc.path = std::move (png); - bool found = false; - for (auto& entry : result) - { - if (entry.uid != *uid) - continue; - found = true; - entry.images.emplace_back (std::move (desc)); - break; - } - if (found) - continue; - Module::Snapshot snapshot; - snapshot.uid = *uid; - snapshot.images.emplace_back (std::move (desc)); - result.emplace_back (std::move (snapshot)); - } - return result; -} - -//------------------------------------------------------------------------ -} // Hosting -} // VST3 diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/pluginterfacesupport.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/pluginterfacesupport.cpp index adabdb90..c4d2a665 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/pluginterfacesupport.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/pluginterfacesupport.cpp @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -43,14 +43,12 @@ #include -//----------------------------------------------------------------------------- namespace Steinberg { namespace Vst { //----------------------------------------------------------------------------- PlugInterfaceSupport::PlugInterfaceSupport () { - FUNKNOWN_CTOR // add minimum set //---VST 3.0.0-------------------------------- @@ -119,16 +117,9 @@ void PlugInterfaceSupport::addPlugInterfaceSupported (const TUID _iid) //----------------------------------------------------------------------------- bool PlugInterfaceSupport::removePlugInterfaceSupported (const TUID _iid) { - auto uid = FUID::fromTUID (_iid); - auto it = std::find (mFUIDArray.begin (), mFUIDArray.end (), uid); - if (it == mFUIDArray.end ()) - return false; - mFUIDArray.erase (it); - return true; + return std::remove (mFUIDArray.begin (), mFUIDArray.end (), FUID::fromTUID (_iid)) != + mFUIDArray.end (); } -IMPLEMENT_FUNKNOWN_METHODS (PlugInterfaceSupport, IPlugInterfaceSupport, IPlugInterfaceSupport::iid) - -//----------------------------------------------------------------------------- -} // Vst -} // Steinberg +} +} // namespace diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/pluginterfacesupport.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/pluginterfacesupport.h index a88cbf68..ad204b78 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/pluginterfacesupport.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/pluginterfacesupport.h @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -39,19 +39,19 @@ #include "pluginterfaces/vst/ivstpluginterfacesupport.h" #include +#include "base/source/fobject.h" namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ -/** Example implementation of IPlugInterfaceSupport. +/** Implementation's example of IPlugInterfaceSupport. \ingroup hostingBase */ -class PlugInterfaceSupport : public IPlugInterfaceSupport +class PlugInterfaceSupport : public FObject, public IPlugInterfaceSupport { public: PlugInterfaceSupport (); - virtual ~PlugInterfaceSupport () = default; //--- IPlugInterfaceSupport --------- tresult PLUGIN_API isPlugInterfaceSupported (const TUID _iid) SMTG_OVERRIDE; @@ -59,7 +59,11 @@ class PlugInterfaceSupport : public IPlugInterfaceSupport void addPlugInterfaceSupported (const TUID _iid); bool removePlugInterfaceSupported (const TUID _iid); - DECLARE_FUNKNOWN_METHODS + OBJ_METHODS (PlugInterfaceSupport, FObject) + REFCOUNT_METHODS (FObject) + DEFINE_INTERFACES + DEF_INTERFACE (IPlugInterfaceSupport) + END_DEFINE_INTERFACES (FObject) private: std::vector mFUIDArray; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/ReadMe.md b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/ReadMe.md deleted file mode 100644 index 6c938720..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/ReadMe.md +++ /dev/null @@ -1,43 +0,0 @@ - -# ModuleInfoLib - -This is a c++17 library to parse and create the Steinberg moduleinfo.json files. - -## Parsing - -To parse a moduleinfo.json file you need to include the following files to your project: - -* moduleinfoparser.cpp -* moduleinfoparser.h -* moduleinfo.h -* json.h -* jsoncxx.h - -And add a header search path to the root folder of the VST SDK. - -Now to parse a moduleinfo.json file in code you need to read the moduleinfo.json into a memory buffer and call - -``` c++ -auto moduleInfo = ModuleInfoLib::parseCompatibilityJson (std::string_view (buffer, bufferSize), &std::cerr); -``` - -Afterwards if parsing succeeded the moduleInfo optional has a value containing the ModuleInfo. - -## Creating - -The VST3 SDK contains the moduleinfotool utility that can create moduleinfo.json files from VST3 modules. - -To add this capability to your own project you need to link to the sdk_hosting library from the SDK and include the following files to your project: - -* moduleinfocreator.cpp -* moduleinfocreator.h -* moduleinfo.h - -Additionally you need to add the module platform implementation from the hosting directory (module_win32.cpp, module_mac.mm or module_linux.cpp). - -Now you can use the two methods in moduleinfocreator.h to create a moduleinfo.json file: - -``` c++ -auto moduleInfo = ModuleInfoLib::createModuleInfo (module, false); -ModuleInfoLib::outputJson (moduleInfo, std::cout); -``` diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/json.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/json.h deleted file mode 100644 index da8a6ad7..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/json.h +++ /dev/null @@ -1,3403 +0,0 @@ -/* - The latest version of this library is available on GitHub; - https://github.com/sheredom/json.h. -*/ - -/* - This is free and unencumbered software released into the public domain. - - Anyone is free to copy, modify, publish, use, compile, sell, or - distribute this software, either in source code form or as a compiled - binary, for any purpose, commercial or non-commercial, and by any - means. - - In jurisdictions that recognize copyright laws, the author or authors - of this software dedicate any and all copyright interest in the - software to the public domain. We make this dedication for the benefit - of the public at large and to the detriment of our heirs and - successors. We intend this dedication to be an overt act of - relinquishment in perpetuity of all present and future rights to this - software under copyright law. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - - For more information, please refer to . -*/ - -#ifndef SHEREDOM_JSON_H_INCLUDED -#define SHEREDOM_JSON_H_INCLUDED - -#if defined(_MSC_VER) -#pragma warning(push) - -/* disable warning: no function prototype given: converting '()' to '(void)' */ -#pragma warning(disable : 4255) - -/* disable warning: '__cplusplus' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */ -#pragma warning(disable : 4668) - -/* disable warning: 'bytes padding added after construct' */ -#pragma warning(disable : 4820) -#endif - -#include -#include - -#if defined(_MSC_VER) || defined(__MINGW32__) -#define json_weak __inline -#elif defined(__clang__) || defined(__GNUC__) -#define json_weak __attribute__((weak)) -#else -#error Non clang, non gcc, non MSVC compiler found! -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -struct json_value_s; -struct json_parse_result_s; - -enum json_parse_flags_e { - json_parse_flags_default = 0, - - /* allow trailing commas in objects and arrays. For example, both [true,] and - {"a" : null,} would be allowed with this option on. */ - json_parse_flags_allow_trailing_comma = 0x1, - - /* allow unquoted keys for objects. For example, {a : null} would be allowed - with this option on. */ - json_parse_flags_allow_unquoted_keys = 0x2, - - /* allow a global unbracketed object. For example, a : null, b : true, c : {} - would be allowed with this option on. */ - json_parse_flags_allow_global_object = 0x4, - - /* allow objects to use '=' instead of ':' between key/value pairs. For - example, a = null, b : true would be allowed with this option on. */ - json_parse_flags_allow_equals_in_object = 0x8, - - /* allow that objects don't have to have comma separators between key/value - pairs. */ - json_parse_flags_allow_no_commas = 0x10, - - /* allow c-style comments (either variants) to be ignored in the input JSON - file. */ - json_parse_flags_allow_c_style_comments = 0x20, - - /* deprecated flag, unused. */ - json_parse_flags_deprecated = 0x40, - - /* record location information for each value. */ - json_parse_flags_allow_location_information = 0x80, - - /* allow strings to be 'single quoted'. */ - json_parse_flags_allow_single_quoted_strings = 0x100, - - /* allow numbers to be hexadecimal. */ - json_parse_flags_allow_hexadecimal_numbers = 0x200, - - /* allow numbers like +123 to be parsed. */ - json_parse_flags_allow_leading_plus_sign = 0x400, - - /* allow numbers like .0123 or 123. to be parsed. */ - json_parse_flags_allow_leading_or_trailing_decimal_point = 0x800, - - /* allow Infinity, -Infinity, NaN, -NaN. */ - json_parse_flags_allow_inf_and_nan = 0x1000, - - /* allow multi line string values. */ - json_parse_flags_allow_multi_line_strings = 0x2000, - - /* allow simplified JSON to be parsed. Simplified JSON is an enabling of a set - of other parsing options. */ - json_parse_flags_allow_simplified_json = - (json_parse_flags_allow_trailing_comma | - json_parse_flags_allow_unquoted_keys | - json_parse_flags_allow_global_object | - json_parse_flags_allow_equals_in_object | - json_parse_flags_allow_no_commas), - - /* allow JSON5 to be parsed. JSON5 is an enabling of a set of other parsing - options. */ - json_parse_flags_allow_json5 = - (json_parse_flags_allow_trailing_comma | - json_parse_flags_allow_unquoted_keys | - json_parse_flags_allow_c_style_comments | - json_parse_flags_allow_single_quoted_strings | - json_parse_flags_allow_hexadecimal_numbers | - json_parse_flags_allow_leading_plus_sign | - json_parse_flags_allow_leading_or_trailing_decimal_point | - json_parse_flags_allow_inf_and_nan | - json_parse_flags_allow_multi_line_strings) -}; - -/* Parse a JSON text file, returning a pointer to the root of the JSON - * structure. json_parse performs 1 call to malloc for the entire encoding. - * Returns 0 if an error occurred (malformed JSON input, or malloc failed). */ -json_weak struct json_value_s *json_parse(const void *src, size_t src_size); - -/* Parse a JSON text file, returning a pointer to the root of the JSON - * structure. json_parse performs 1 call to alloc_func_ptr for the entire - * encoding. Returns 0 if an error occurred (malformed JSON input, or malloc - * failed). If an error occurred, the result struct (if not NULL) will explain - * the type of error, and the location in the input it occurred. If - * alloc_func_ptr is null then malloc is used. */ -json_weak struct json_value_s * -json_parse_ex(const void *src, size_t src_size, size_t flags_bitset, - void *(*alloc_func_ptr)(void *, size_t), void *user_data, - struct json_parse_result_s *result); - -/* Extracts a value and all the data that makes it up into a newly created - * value. json_extract_value performs 1 call to malloc for the entire encoding. - */ -json_weak struct json_value_s * -json_extract_value(const struct json_value_s *value); - -/* Extracts a value and all the data that makes it up into a newly created - * value. json_extract_value performs 1 call to alloc_func_ptr for the entire - * encoding. If alloc_func_ptr is null then malloc is used. */ -json_weak struct json_value_s * -json_extract_value_ex(const struct json_value_s *value, - void *(*alloc_func_ptr)(void *, size_t), void *user_data); - -/* Write out a minified JSON utf-8 string. This string is an encoding of the - * minimal string characters required to still encode the same data. - * json_write_minified performs 1 call to malloc for the entire encoding. Return - * 0 if an error occurred (malformed JSON input, or malloc failed). The out_size - * parameter is optional as the utf-8 string is null terminated. */ -json_weak void *json_write_minified(const struct json_value_s *value, - size_t *out_size); - -/* Write out a pretty JSON utf-8 string. This string is encoded such that the - * resultant JSON is pretty in that it is easily human readable. The indent and - * newline parameters allow a user to specify what kind of indentation and - * newline they want (two spaces / three spaces / tabs? \r, \n, \r\n ?). Both - * indent and newline can be NULL, indent defaults to two spaces (" "), and - * newline defaults to linux newlines ('\n' as the newline character). - * json_write_pretty performs 1 call to malloc for the entire encoding. Return 0 - * if an error occurred (malformed JSON input, or malloc failed). The out_size - * parameter is optional as the utf-8 string is null terminated. */ -json_weak void *json_write_pretty(const struct json_value_s *value, - const char *indent, const char *newline, - size_t *out_size); - -/* Reinterpret a JSON value as a string. Returns null is the value was not a - * string. */ -json_weak struct json_string_s * -json_value_as_string(struct json_value_s *const value); - -/* Reinterpret a JSON value as a number. Returns null is the value was not a - * number. */ -json_weak struct json_number_s * -json_value_as_number(struct json_value_s *const value); - -/* Reinterpret a JSON value as an object. Returns null is the value was not an - * object. */ -json_weak struct json_object_s * -json_value_as_object(struct json_value_s *const value); - -/* Reinterpret a JSON value as an array. Returns null is the value was not an - * array. */ -json_weak struct json_array_s * -json_value_as_array(struct json_value_s *const value); - -/* Whether the value is true. */ -json_weak int json_value_is_true(const struct json_value_s *const value); - -/* Whether the value is false. */ -json_weak int json_value_is_false(const struct json_value_s *const value); - -/* Whether the value is null. */ -json_weak int json_value_is_null(const struct json_value_s *const value); - -/* The various types JSON values can be. Used to identify what a value is. */ -enum json_type_e { - json_type_string, - json_type_number, - json_type_object, - json_type_array, - json_type_true, - json_type_false, - json_type_null -}; - -/* A JSON string value. */ -struct json_string_s { - /* utf-8 string */ - const char *string; - /* The size (in bytes) of the string */ - size_t string_size; -}; - -/* A JSON string value (extended). */ -struct json_string_ex_s { - /* The JSON string this extends. */ - struct json_string_s string; - - /* The character offset for the value in the JSON input. */ - size_t offset; - - /* The line number for the value in the JSON input. */ - size_t line_no; - - /* The row number for the value in the JSON input, in bytes. */ - size_t row_no; -}; - -/* A JSON number value. */ -struct json_number_s { - /* ASCII string containing representation of the number. */ - const char *number; - /* the size (in bytes) of the number. */ - size_t number_size; -}; - -/* an element of a JSON object. */ -struct json_object_element_s { - /* the name of this element. */ - struct json_string_s *name; - /* the value of this element. */ - struct json_value_s *value; - /* the next object element (can be NULL if the last element in the object). */ - struct json_object_element_s *next; -}; - -/* a JSON object value. */ -struct json_object_s { - /* a linked list of the elements in the object. */ - struct json_object_element_s *start; - /* the number of elements in the object. */ - size_t length; -}; - -/* an element of a JSON array. */ -struct json_array_element_s { - /* the value of this element. */ - struct json_value_s *value; - /* the next array element (can be NULL if the last element in the array). */ - struct json_array_element_s *next; -}; - -/* a JSON array value. */ -struct json_array_s { - /* a linked list of the elements in the array. */ - struct json_array_element_s *start; - /* the number of elements in the array. */ - size_t length; -}; - -/* a JSON value. */ -struct json_value_s { - /* a pointer to either a json_string_s, json_number_s, json_object_s, or. */ - /* json_array_s. Should be cast to the appropriate struct type based on what. - */ - /* the type of this value is. */ - void *payload; - /* must be one of json_type_e. If type is json_type_true, json_type_false, or. - */ - /* json_type_null, payload will be NULL. */ - size_t type; -}; - -/* a JSON value (extended). */ -struct json_value_ex_s { - /* the JSON value this extends. */ - struct json_value_s value; - - /* the character offset for the value in the JSON input. */ - size_t offset; - - /* the line number for the value in the JSON input. */ - size_t line_no; - - /* the row number for the value in the JSON input, in bytes. */ - size_t row_no; -}; - -/* a parsing error code. */ -enum json_parse_error_e { - /* no error occurred (huzzah!). */ - json_parse_error_none = 0, - - /* expected either a comma or a closing '}' or ']' to close an object or. */ - /* array! */ - json_parse_error_expected_comma_or_closing_bracket, - - /* colon separating name/value pair was missing! */ - json_parse_error_expected_colon, - - /* expected string to begin with '"'! */ - json_parse_error_expected_opening_quote, - - /* invalid escaped sequence in string! */ - json_parse_error_invalid_string_escape_sequence, - - /* invalid number format! */ - json_parse_error_invalid_number_format, - - /* invalid value! */ - json_parse_error_invalid_value, - - /* reached end of buffer before object/array was complete! */ - json_parse_error_premature_end_of_buffer, - - /* string was malformed! */ - json_parse_error_invalid_string, - - /* a call to malloc, or a user provider allocator, failed. */ - json_parse_error_allocator_failed, - - /* the JSON input had unexpected trailing characters that weren't part of the. - */ - /* JSON value. */ - json_parse_error_unexpected_trailing_characters, - - /* catch-all error for everything else that exploded (real bad chi!). */ - json_parse_error_unknown -}; - -/* error report from json_parse_ex(). */ -struct json_parse_result_s { - /* the error code (one of json_parse_error_e). */ - size_t error; - - /* the character offset for the error in the JSON input. */ - size_t error_offset; - - /* the line number for the error in the JSON input. */ - size_t error_line_no; - - /* the row number for the error, in bytes. */ - size_t error_row_no; -}; - -#ifdef __cplusplus -} /* extern "C". */ -#endif - -#include - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - -#if defined(_MSC_VER) && (_MSC_VER < 1920) -#define json_uintmax_t unsigned __int64 -#else -#include -#define json_uintmax_t uintmax_t -#endif - -#if defined(_MSC_VER) -#define json_strtoumax _strtoui64 -#else -#define json_strtoumax strtoumax -#endif - -#if defined(__cplusplus) && (__cplusplus >= 201103L) -#define json_null nullptr -#else -#define json_null 0 -#endif - -#if defined(__clang__) -#pragma clang diagnostic push - -/* we do one big allocation via malloc, then cast aligned slices of this for. */ -/* our structures - we don't have a way to tell the compiler we know what we. */ -/* are doing, so disable the warning instead! */ -#pragma clang diagnostic ignored "-Wcast-align" - -/* We use C style casts everywhere. */ -#pragma clang diagnostic ignored "-Wold-style-cast" - -/* We need long long for strtoull. */ -#pragma clang diagnostic ignored "-Wc++11-long-long" - -/* Who cares if nullptr doesn't work with C++98, we don't use it there! */ -#pragma clang diagnostic ignored "-Wc++98-compat" -#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#elif defined(_MSC_VER) -#pragma warning(push) - -/* disable 'function selected for inline expansion' warning. */ -#pragma warning(disable : 4711) - -/* disable '#pragma warning: there is no warning number' warning. */ -#pragma warning(disable : 4619) - -/* disable 'warning number not a valid compiler warning' warning. */ -#pragma warning(disable : 4616) - -/* disable 'Compiler will insert Spectre mitigation for memory load if - * /Qspectre. */ -/* switch specified' warning. */ -#pragma warning(disable : 5045) -#endif - -struct json_parse_state_s { - const char *src; - size_t size; - size_t offset; - size_t flags_bitset; - char *data; - char *dom; - size_t dom_size; - size_t data_size; - size_t line_no; /* line counter for error reporting. */ - size_t line_offset; /* (offset-line_offset) is the character number (in - bytes). */ - size_t error; -}; - -json_weak int json_hexadecimal_digit(const char c); -int json_hexadecimal_digit(const char c) { - if ('0' <= c && c <= '9') { - return c - '0'; - } - if ('a' <= c && c <= 'f') { - return c - 'a' + 10; - } - if ('A' <= c && c <= 'F') { - return c - 'A' + 10; - } - return -1; -} - -json_weak int json_hexadecimal_value(const char *c, const unsigned long size, - unsigned long *result); -int json_hexadecimal_value(const char *c, const unsigned long size, - unsigned long *result) { - const char *p; - int digit; - - if (size > sizeof(unsigned long) * 2) { - return 0; - } - - *result = 0; - for (p = c; (unsigned long)(p - c) < size; ++p) { - *result <<= 4; - digit = json_hexadecimal_digit(*p); - if (digit < 0 || digit > 15) { - return 0; - } - *result |= (unsigned char)digit; - } - return 1; -} - -json_weak int json_skip_whitespace(struct json_parse_state_s *state); -int json_skip_whitespace(struct json_parse_state_s *state) { - size_t offset = state->offset; - const size_t size = state->size; - const char *const src = state->src; - - /* the only valid whitespace according to ECMA-404 is ' ', '\n', '\r' and - * '\t'. */ - switch (src[offset]) { - default: - return 0; - case ' ': - case '\r': - case '\t': - case '\n': - break; - } - - do { - switch (src[offset]) { - default: - /* Update offset. */ - state->offset = offset; - return 1; - case ' ': - case '\r': - case '\t': - break; - case '\n': - state->line_no++; - state->line_offset = offset; - break; - } - - offset++; - } while (offset < size); - - /* Update offset. */ - state->offset = offset; - return 1; -} - -json_weak int json_skip_c_style_comments(struct json_parse_state_s *state); -int json_skip_c_style_comments(struct json_parse_state_s *state) { - /* do we have a comment?. */ - if ('/' == state->src[state->offset]) { - /* skip '/'. */ - state->offset++; - - if ('/' == state->src[state->offset]) { - /* we had a comment of the form //. */ - - /* skip second '/'. */ - state->offset++; - - while (state->offset < state->size) { - switch (state->src[state->offset]) { - default: - /* skip the character in the comment. */ - state->offset++; - break; - case '\n': - /* if we have a newline, our comment has ended! Skip the newline. */ - state->offset++; - - /* we entered a newline, so move our line info forward. */ - state->line_no++; - state->line_offset = state->offset; - return 1; - } - } - - /* we reached the end of the JSON file! */ - return 1; - } else if ('*' == state->src[state->offset]) { - /* we had a comment in the C-style long form. */ - - /* skip '*'. */ - state->offset++; - - while (state->offset + 1 < state->size) { - if (('*' == state->src[state->offset]) && - ('/' == state->src[state->offset + 1])) { - /* we reached the end of our comment! */ - state->offset += 2; - return 1; - } else if ('\n' == state->src[state->offset]) { - /* we entered a newline, so move our line info forward. */ - state->line_no++; - state->line_offset = state->offset; - } - - /* skip character within comment. */ - state->offset++; - } - - /* Comment wasn't ended correctly which is a failure. */ - return 1; - } - } - - /* we didn't have any comment, which is ok too! */ - return 0; -} - -json_weak int json_skip_all_skippables(struct json_parse_state_s *state); -int json_skip_all_skippables(struct json_parse_state_s *state) { - /* skip all whitespace and other skippables until there are none left. note - * that the previous version suffered from read past errors should. the - * stream end on json_skip_c_style_comments eg. '{"a" ' with comments flag. - */ - - int did_consume = 0; - const size_t size = state->size; - - if (json_parse_flags_allow_c_style_comments & state->flags_bitset) { - do { - if (state->offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - did_consume = json_skip_whitespace(state); - - /* This should really be checked on access, not in front of every call. - */ - if (state->offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - did_consume |= json_skip_c_style_comments(state); - } while (0 != did_consume); - } else { - do { - if (state->offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - did_consume = json_skip_whitespace(state); - } while (0 != did_consume); - } - - if (state->offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - return 0; -} - -json_weak int json_get_value_size(struct json_parse_state_s *state, - int is_global_object); - -json_weak int json_get_string_size(struct json_parse_state_s *state, - size_t is_key); -int json_get_string_size(struct json_parse_state_s *state, size_t is_key) { - size_t offset = state->offset; - const size_t size = state->size; - size_t data_size = 0; - const char *const src = state->src; - const int is_single_quote = '\'' == src[offset]; - const char quote_to_use = is_single_quote ? '\'' : '"'; - const size_t flags_bitset = state->flags_bitset; - unsigned long codepoint; - unsigned long high_surrogate = 0; - - if ((json_parse_flags_allow_location_information & flags_bitset) != 0 && - is_key != 0) { - state->dom_size += sizeof(struct json_string_ex_s); - } else { - state->dom_size += sizeof(struct json_string_s); - } - - if ('"' != src[offset]) { - /* if we are allowed single quoted strings check for that too. */ - if (!((json_parse_flags_allow_single_quoted_strings & flags_bitset) && - is_single_quote)) { - state->error = json_parse_error_expected_opening_quote; - state->offset = offset; - return 1; - } - } - - /* skip leading '"' or '\''. */ - offset++; - - while ((offset < size) && (quote_to_use != src[offset])) { - /* add space for the character. */ - data_size++; - - switch (src[offset]) { - default: - break; - case '\0': - case '\t': - state->error = json_parse_error_invalid_string; - state->offset = offset; - return 1; - } - - if ('\\' == src[offset]) { - /* skip reverse solidus character. */ - offset++; - - if (offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - state->offset = offset; - return 1; - } - - switch (src[offset]) { - default: - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - case '"': - case '\\': - case '/': - case 'b': - case 'f': - case 'n': - case 'r': - case 't': - /* all valid characters! */ - offset++; - break; - case 'u': - if (!(offset + 5 < size)) { - /* invalid escaped unicode sequence! */ - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } - - codepoint = 0; - if (!json_hexadecimal_value(&src[offset + 1], 4, &codepoint)) { - /* escaped unicode sequences must contain 4 hexadecimal digits! */ - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } - - /* Valid sequence! - * see: https://en.wikipedia.org/wiki/UTF-8#Invalid_code_points. - * 1 7 U + 0000 U + 007F 0xxxxxxx. - * 2 11 U + 0080 U + 07FF 110xxxxx - * 10xxxxxx. - * 3 16 U + 0800 U + FFFF 1110xxxx - * 10xxxxxx 10xxxxxx. - * 4 21 U + 10000 U + 10FFFF 11110xxx - * 10xxxxxx 10xxxxxx 10xxxxxx. - * Note: the high and low surrogate halves used by UTF-16 (U+D800 - * through U+DFFF) and code points not encodable by UTF-16 (those after - * U+10FFFF) are not legal Unicode values, and their UTF-8 encoding must - * be treated as an invalid byte sequence. */ - - if (high_surrogate != 0) { - /* we previously read the high half of the \uxxxx\uxxxx pair, so now - * we expect the low half. */ - if (codepoint >= 0xdc00 && - codepoint <= 0xdfff) { /* low surrogate range. */ - data_size += 3; - high_surrogate = 0; - } else { - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } - } else if (codepoint <= 0x7f) { - data_size += 0; - } else if (codepoint <= 0x7ff) { - data_size += 1; - } else if (codepoint >= 0xd800 && - codepoint <= 0xdbff) { /* high surrogate range. */ - /* The codepoint is the first half of a "utf-16 surrogate pair". so we - * need the other half for it to be valid: \uHHHH\uLLLL. */ - if (offset + 11 > size || '\\' != src[offset + 5] || - 'u' != src[offset + 6]) { - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } - high_surrogate = codepoint; - } else if (codepoint >= 0xd800 && - codepoint <= 0xdfff) { /* low surrogate range. */ - /* we did not read the other half before. */ - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } else { - data_size += 2; - } - /* escaped codepoints after 0xffff are supported in json through utf-16 - * surrogate pairs: \uD83D\uDD25 for U+1F525. */ - - offset += 5; - break; - } - } else if (('\r' == src[offset]) || ('\n' == src[offset])) { - if (!(json_parse_flags_allow_multi_line_strings & flags_bitset)) { - /* invalid escaped unicode sequence! */ - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } - - offset++; - } else { - /* skip character (valid part of sequence). */ - offset++; - } - } - - /* If the offset is equal to the size, we had a non-terminated string! */ - if (offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - state->offset = offset - 1; - return 1; - } - - /* skip trailing '"' or '\''. */ - offset++; - - /* add enough space to store the string. */ - state->data_size += data_size; - - /* one more byte for null terminator ending the string! */ - state->data_size++; - - /* update offset. */ - state->offset = offset; - - return 0; -} - -json_weak int is_valid_unquoted_key_char(const char c); -int is_valid_unquoted_key_char(const char c) { - return (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || - ('A' <= c && c <= 'Z') || ('_' == c)); -} - -json_weak int json_get_key_size(struct json_parse_state_s *state); -int json_get_key_size(struct json_parse_state_s *state) { - const size_t flags_bitset = state->flags_bitset; - - if (json_parse_flags_allow_unquoted_keys & flags_bitset) { - size_t offset = state->offset; - const size_t size = state->size; - const char *const src = state->src; - size_t data_size = state->data_size; - - /* if we are allowing unquoted keys, first grok for a quote... */ - if ('"' == src[offset]) { - /* ... if we got a comma, just parse the key as a string as normal. */ - return json_get_string_size(state, 1); - } else if ((json_parse_flags_allow_single_quoted_strings & flags_bitset) && - ('\'' == src[offset])) { - /* ... if we got a comma, just parse the key as a string as normal. */ - return json_get_string_size(state, 1); - } else { - while ((offset < size) && is_valid_unquoted_key_char(src[offset])) { - offset++; - data_size++; - } - - /* one more byte for null terminator ending the string! */ - data_size++; - - if (json_parse_flags_allow_location_information & flags_bitset) { - state->dom_size += sizeof(struct json_string_ex_s); - } else { - state->dom_size += sizeof(struct json_string_s); - } - - /* update offset. */ - state->offset = offset; - - /* update data_size. */ - state->data_size = data_size; - - return 0; - } - } else { - /* we are only allowed to have quoted keys, so just parse a string! */ - return json_get_string_size(state, 1); - } -} - -json_weak int json_get_object_size(struct json_parse_state_s *state, - int is_global_object); -int json_get_object_size(struct json_parse_state_s *state, - int is_global_object) { - const size_t flags_bitset = state->flags_bitset; - const char *const src = state->src; - const size_t size = state->size; - size_t elements = 0; - int allow_comma = 0; - int found_closing_brace = 0; - - if (is_global_object) { - /* if we found an opening '{' of an object, we actually have a normal JSON - * object at the root of the DOM... */ - if (!json_skip_all_skippables(state) && '{' == state->src[state->offset]) { - /* . and we don't actually have a global object after all! */ - is_global_object = 0; - } - } - - if (!is_global_object) { - if ('{' != src[state->offset]) { - state->error = json_parse_error_unknown; - return 1; - } - - /* skip leading '{'. */ - state->offset++; - } - - state->dom_size += sizeof(struct json_object_s); - - if ((state->offset == size) && !is_global_object) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - do { - if (!is_global_object) { - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - if ('}' == src[state->offset]) { - /* skip trailing '}'. */ - state->offset++; - - found_closing_brace = 1; - - /* finished the object! */ - break; - } - } else { - /* we don't require brackets, so that means the object ends when the input - * stream ends! */ - if (json_skip_all_skippables(state)) { - break; - } - } - - /* if we parsed at least once element previously, grok for a comma. */ - if (allow_comma) { - if (',' == src[state->offset]) { - /* skip comma. */ - state->offset++; - allow_comma = 0; - } else if (json_parse_flags_allow_no_commas & flags_bitset) { - /* we don't require a comma, and we didn't find one, which is ok! */ - allow_comma = 0; - } else { - /* otherwise we are required to have a comma, and we found none. */ - state->error = json_parse_error_expected_comma_or_closing_bracket; - return 1; - } - - if (json_parse_flags_allow_trailing_comma & flags_bitset) { - continue; - } else { - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - } - } - - if (json_get_key_size(state)) { - /* key parsing failed! */ - state->error = json_parse_error_invalid_string; - return 1; - } - - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - if (json_parse_flags_allow_equals_in_object & flags_bitset) { - const char current = src[state->offset]; - if ((':' != current) && ('=' != current)) { - state->error = json_parse_error_expected_colon; - return 1; - } - } else { - if (':' != src[state->offset]) { - state->error = json_parse_error_expected_colon; - return 1; - } - } - - /* skip colon. */ - state->offset++; - - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - if (json_get_value_size(state, /* is_global_object = */ 0)) { - /* value parsing failed! */ - return 1; - } - - /* successfully parsed a name/value pair! */ - elements++; - allow_comma = 1; - } while (state->offset < size); - - if ((state->offset == size) && !is_global_object && !found_closing_brace) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - state->dom_size += sizeof(struct json_object_element_s) * elements; - - return 0; -} - -json_weak int json_get_array_size(struct json_parse_state_s *state); -int json_get_array_size(struct json_parse_state_s *state) { - const size_t flags_bitset = state->flags_bitset; - size_t elements = 0; - int allow_comma = 0; - const char *const src = state->src; - const size_t size = state->size; - - if ('[' != src[state->offset]) { - /* expected array to begin with leading '['. */ - state->error = json_parse_error_unknown; - return 1; - } - - /* skip leading '['. */ - state->offset++; - - state->dom_size += sizeof(struct json_array_s); - - while (state->offset < size) { - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - if (']' == src[state->offset]) { - /* skip trailing ']'. */ - state->offset++; - - state->dom_size += sizeof(struct json_array_element_s) * elements; - - /* finished the object! */ - return 0; - } - - /* if we parsed at least once element previously, grok for a comma. */ - if (allow_comma) { - if (',' == src[state->offset]) { - /* skip comma. */ - state->offset++; - allow_comma = 0; - } else if (!(json_parse_flags_allow_no_commas & flags_bitset)) { - state->error = json_parse_error_expected_comma_or_closing_bracket; - return 1; - } - - if (json_parse_flags_allow_trailing_comma & flags_bitset) { - allow_comma = 0; - continue; - } else { - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - } - } - - if (json_get_value_size(state, /* is_global_object = */ 0)) { - /* value parsing failed! */ - return 1; - } - - /* successfully parsed an array element! */ - elements++; - allow_comma = 1; - } - - /* we consumed the entire input before finding the closing ']' of the array! - */ - state->error = json_parse_error_premature_end_of_buffer; - return 1; -} - -json_weak int json_get_number_size(struct json_parse_state_s *state); -int json_get_number_size(struct json_parse_state_s *state) { - const size_t flags_bitset = state->flags_bitset; - size_t offset = state->offset; - const size_t size = state->size; - int had_leading_digits = 0; - const char *const src = state->src; - - state->dom_size += sizeof(struct json_number_s); - - if ((json_parse_flags_allow_hexadecimal_numbers & flags_bitset) && - (offset + 1 < size) && ('0' == src[offset]) && - (('x' == src[offset + 1]) || ('X' == src[offset + 1]))) { - /* skip the leading 0x that identifies a hexadecimal number. */ - offset += 2; - - /* consume hexadecimal digits. */ - while ((offset < size) && (('0' <= src[offset] && src[offset] <= '9') || - ('a' <= src[offset] && src[offset] <= 'f') || - ('A' <= src[offset] && src[offset] <= 'F'))) { - offset++; - } - } else { - int found_sign = 0; - int inf_or_nan = 0; - - if ((offset < size) && - (('-' == src[offset]) || - ((json_parse_flags_allow_leading_plus_sign & flags_bitset) && - ('+' == src[offset])))) { - /* skip valid leading '-' or '+'. */ - offset++; - - found_sign = 1; - } - - if (json_parse_flags_allow_inf_and_nan & flags_bitset) { - const char inf[9] = "Infinity"; - const size_t inf_strlen = sizeof(inf) - 1; - const char nan[4] = "NaN"; - const size_t nan_strlen = sizeof(nan) - 1; - - if (offset + inf_strlen < size) { - int found = 1; - size_t i; - for (i = 0; i < inf_strlen; i++) { - if (inf[i] != src[offset + i]) { - found = 0; - break; - } - } - - if (found) { - /* We found our special 'Infinity' keyword! */ - offset += inf_strlen; - - inf_or_nan = 1; - } - } - - if (offset + nan_strlen < size) { - int found = 1; - size_t i; - for (i = 0; i < nan_strlen; i++) { - if (nan[i] != src[offset + i]) { - found = 0; - break; - } - } - - if (found) { - /* We found our special 'NaN' keyword! */ - offset += nan_strlen; - - inf_or_nan = 1; - } - } - } - - if (found_sign && !inf_or_nan && (offset < size) && - !('0' <= src[offset] && src[offset] <= '9')) { - /* check if we are allowing leading '.'. */ - if (!(json_parse_flags_allow_leading_or_trailing_decimal_point & - flags_bitset) || - ('.' != src[offset])) { - /* a leading '-' must be immediately followed by any digit! */ - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - } - } - - if ((offset < size) && ('0' == src[offset])) { - /* skip valid '0'. */ - offset++; - - /* we need to record whether we had any leading digits for checks later. - */ - had_leading_digits = 1; - - if ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) { - /* a leading '0' must not be immediately followed by any digit! */ - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - } - } - - /* the main digits of our number next. */ - while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) { - offset++; - - /* we need to record whether we had any leading digits for checks later. - */ - had_leading_digits = 1; - } - - if ((offset < size) && ('.' == src[offset])) { - offset++; - - if (!('0' <= src[offset] && src[offset] <= '9')) { - if (!(json_parse_flags_allow_leading_or_trailing_decimal_point & - flags_bitset) || - !had_leading_digits) { - /* a decimal point must be followed by at least one digit. */ - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - } - } - - /* a decimal point can be followed by more digits of course! */ - while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) { - offset++; - } - } - - if ((offset < size) && ('e' == src[offset] || 'E' == src[offset])) { - /* our number has an exponent! Skip 'e' or 'E'. */ - offset++; - - if ((offset < size) && ('-' == src[offset] || '+' == src[offset])) { - /* skip optional '-' or '+'. */ - offset++; - } - - if ((offset < size) && !('0' <= src[offset] && src[offset] <= '9')) { - /* an exponent must have at least one digit! */ - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - } - - /* consume exponent digits. */ - do { - offset++; - } while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')); - } - } - - if (offset < size) { - switch (src[offset]) { - case ' ': - case '\t': - case '\r': - case '\n': - case '}': - case ',': - case ']': - /* all of the above are ok. */ - break; - case '=': - if (json_parse_flags_allow_equals_in_object & flags_bitset) { - break; - } - - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - default: - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - } - } - - state->data_size += offset - state->offset; - - /* one more byte for null terminator ending the number string! */ - state->data_size++; - - /* update offset. */ - state->offset = offset; - - return 0; -} - -json_weak int json_get_value_size(struct json_parse_state_s *state, - int is_global_object); -int json_get_value_size(struct json_parse_state_s *state, - int is_global_object) { - const size_t flags_bitset = state->flags_bitset; - const char *const src = state->src; - size_t offset; - const size_t size = state->size; - - if (json_parse_flags_allow_location_information & flags_bitset) { - state->dom_size += sizeof(struct json_value_ex_s); - } else { - state->dom_size += sizeof(struct json_value_s); - } - - if (is_global_object) { - return json_get_object_size(state, /* is_global_object = */ 1); - } else { - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - /* can cache offset now. */ - offset = state->offset; - - switch (src[offset]) { - case '"': - return json_get_string_size(state, 0); - case '\'': - if (json_parse_flags_allow_single_quoted_strings & flags_bitset) { - return json_get_string_size(state, 0); - } else { - /* invalid value! */ - state->error = json_parse_error_invalid_value; - return 1; - } - case '{': - return json_get_object_size(state, /* is_global_object = */ 0); - case '[': - return json_get_array_size(state); - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return json_get_number_size(state); - case '+': - if (json_parse_flags_allow_leading_plus_sign & flags_bitset) { - return json_get_number_size(state); - } else { - /* invalid value! */ - state->error = json_parse_error_invalid_number_format; - return 1; - } - case '.': - if (json_parse_flags_allow_leading_or_trailing_decimal_point & - flags_bitset) { - return json_get_number_size(state); - } else { - /* invalid value! */ - state->error = json_parse_error_invalid_number_format; - return 1; - } - default: - if ((offset + 4) <= size && 't' == src[offset + 0] && - 'r' == src[offset + 1] && 'u' == src[offset + 2] && - 'e' == src[offset + 3]) { - state->offset += 4; - return 0; - } else if ((offset + 5) <= size && 'f' == src[offset + 0] && - 'a' == src[offset + 1] && 'l' == src[offset + 2] && - 's' == src[offset + 3] && 'e' == src[offset + 4]) { - state->offset += 5; - return 0; - } else if ((offset + 4) <= size && 'n' == state->src[offset + 0] && - 'u' == state->src[offset + 1] && - 'l' == state->src[offset + 2] && - 'l' == state->src[offset + 3]) { - state->offset += 4; - return 0; - } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && - (offset + 3) <= size && 'N' == src[offset + 0] && - 'a' == src[offset + 1] && 'N' == src[offset + 2]) { - return json_get_number_size(state); - } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && - (offset + 8) <= size && 'I' == src[offset + 0] && - 'n' == src[offset + 1] && 'f' == src[offset + 2] && - 'i' == src[offset + 3] && 'n' == src[offset + 4] && - 'i' == src[offset + 5] && 't' == src[offset + 6] && - 'y' == src[offset + 7]) { - return json_get_number_size(state); - } - - /* invalid value! */ - state->error = json_parse_error_invalid_value; - return 1; - } - } -} - -json_weak void json_parse_value(struct json_parse_state_s *state, - int is_global_object, - struct json_value_s *value); - -json_weak void json_parse_string(struct json_parse_state_s *state, - struct json_string_s *string); -void json_parse_string(struct json_parse_state_s *state, - struct json_string_s *string) { - size_t offset = state->offset; - size_t bytes_written = 0; - const char *const src = state->src; - const char quote_to_use = '\'' == src[offset] ? '\'' : '"'; - char *data = state->data; - unsigned long high_surrogate = 0; - unsigned long codepoint; - - string->string = data; - - /* skip leading '"' or '\''. */ - offset++; - - while (quote_to_use != src[offset]) { - if ('\\' == src[offset]) { - /* skip the reverse solidus. */ - offset++; - - switch (src[offset++]) { - default: - return; /* we cannot ever reach here. */ - case 'u': { - codepoint = 0; - if (!json_hexadecimal_value(&src[offset], 4, &codepoint)) { - return; /* this shouldn't happen as the value was already validated. - */ - } - - offset += 4; - - if (codepoint <= 0x7fu) { - data[bytes_written++] = (char)codepoint; /* 0xxxxxxx. */ - } else if (codepoint <= 0x7ffu) { - data[bytes_written++] = - (char)(0xc0u | (codepoint >> 6)); /* 110xxxxx. */ - data[bytes_written++] = - (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */ - } else if (codepoint >= 0xd800 && - codepoint <= 0xdbff) { /* high surrogate. */ - high_surrogate = codepoint; - continue; /* we need the low half to form a complete codepoint. */ - } else if (codepoint >= 0xdc00 && - codepoint <= 0xdfff) { /* low surrogate. */ - /* combine with the previously read half to obtain the complete - * codepoint. */ - const unsigned long surrogate_offset = - 0x10000u - (0xD800u << 10) - 0xDC00u; - codepoint = (high_surrogate << 10) + codepoint + surrogate_offset; - high_surrogate = 0; - data[bytes_written++] = - (char)(0xF0u | (codepoint >> 18)); /* 11110xxx. */ - data[bytes_written++] = - (char)(0x80u | ((codepoint >> 12) & 0x3fu)); /* 10xxxxxx. */ - data[bytes_written++] = - (char)(0x80u | ((codepoint >> 6) & 0x3fu)); /* 10xxxxxx. */ - data[bytes_written++] = - (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */ - } else { - /* we assume the value was validated and thus is within the valid - * range. */ - data[bytes_written++] = - (char)(0xe0u | (codepoint >> 12)); /* 1110xxxx. */ - data[bytes_written++] = - (char)(0x80u | ((codepoint >> 6) & 0x3fu)); /* 10xxxxxx. */ - data[bytes_written++] = - (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */ - } - } break; - case '"': - data[bytes_written++] = '"'; - break; - case '\\': - data[bytes_written++] = '\\'; - break; - case '/': - data[bytes_written++] = '/'; - break; - case 'b': - data[bytes_written++] = '\b'; - break; - case 'f': - data[bytes_written++] = '\f'; - break; - case 'n': - data[bytes_written++] = '\n'; - break; - case 'r': - data[bytes_written++] = '\r'; - break; - case 't': - data[bytes_written++] = '\t'; - break; - case '\r': - data[bytes_written++] = '\r'; - - /* check if we have a "\r\n" sequence. */ - if ('\n' == src[offset]) { - data[bytes_written++] = '\n'; - offset++; - } - - break; - case '\n': - data[bytes_written++] = '\n'; - break; - } - } else { - /* copy the character. */ - data[bytes_written++] = src[offset++]; - } - } - - /* skip trailing '"' or '\''. */ - offset++; - - /* record the size of the string. */ - string->string_size = bytes_written; - - /* add null terminator to string. */ - data[bytes_written++] = '\0'; - - /* move data along. */ - state->data += bytes_written; - - /* update offset. */ - state->offset = offset; -} - -json_weak void json_parse_key(struct json_parse_state_s *state, - struct json_string_s *string); -void json_parse_key(struct json_parse_state_s *state, - struct json_string_s *string) { - if (json_parse_flags_allow_unquoted_keys & state->flags_bitset) { - const char *const src = state->src; - char *const data = state->data; - size_t offset = state->offset; - - /* if we are allowing unquoted keys, check for quoted anyway... */ - if (('"' == src[offset]) || ('\'' == src[offset])) { - /* ... if we got a quote, just parse the key as a string as normal. */ - json_parse_string(state, string); - } else { - size_t size = 0; - - string->string = state->data; - - while (is_valid_unquoted_key_char(src[offset])) { - data[size++] = src[offset++]; - } - - /* add null terminator to string. */ - data[size] = '\0'; - - /* record the size of the string. */ - string->string_size = size++; - - /* move data along. */ - state->data += size; - - /* update offset. */ - state->offset = offset; - } - } else { - /* we are only allowed to have quoted keys, so just parse a string! */ - json_parse_string(state, string); - } -} - -json_weak void json_parse_object(struct json_parse_state_s *state, - int is_global_object, - struct json_object_s *object); -void json_parse_object(struct json_parse_state_s *state, int is_global_object, - struct json_object_s *object) { - const size_t flags_bitset = state->flags_bitset; - const size_t size = state->size; - const char *const src = state->src; - size_t elements = 0; - int allow_comma = 0; - struct json_object_element_s *previous = json_null; - - if (is_global_object) { - /* if we skipped some whitespace, and then found an opening '{' of an. */ - /* object, we actually have a normal JSON object at the root of the DOM... - */ - if ('{' == src[state->offset]) { - /* . and we don't actually have a global object after all! */ - is_global_object = 0; - } - } - - if (!is_global_object) { - /* skip leading '{'. */ - state->offset++; - } - - (void)json_skip_all_skippables(state); - - /* reset elements. */ - elements = 0; - - while (state->offset < size) { - struct json_object_element_s *element = json_null; - struct json_string_s *string = json_null; - struct json_value_s *value = json_null; - - if (!is_global_object) { - (void)json_skip_all_skippables(state); - - if ('}' == src[state->offset]) { - /* skip trailing '}'. */ - state->offset++; - - /* finished the object! */ - break; - } - } else { - if (json_skip_all_skippables(state)) { - /* global object ends when the file ends! */ - break; - } - } - - /* if we parsed at least one element previously, grok for a comma. */ - if (allow_comma) { - if (',' == src[state->offset]) { - /* skip comma. */ - state->offset++; - allow_comma = 0; - continue; - } - } - - element = (struct json_object_element_s *)state->dom; - - state->dom += sizeof(struct json_object_element_s); - - if (json_null == previous) { - /* this is our first element, so record it in our object. */ - object->start = element; - } else { - previous->next = element; - } - - previous = element; - - if (json_parse_flags_allow_location_information & flags_bitset) { - struct json_string_ex_s *string_ex = - (struct json_string_ex_s *)state->dom; - state->dom += sizeof(struct json_string_ex_s); - - string_ex->offset = state->offset; - string_ex->line_no = state->line_no; - string_ex->row_no = state->offset - state->line_offset; - - string = &(string_ex->string); - } else { - string = (struct json_string_s *)state->dom; - state->dom += sizeof(struct json_string_s); - } - - element->name = string; - - (void)json_parse_key(state, string); - - (void)json_skip_all_skippables(state); - - /* skip colon or equals. */ - state->offset++; - - (void)json_skip_all_skippables(state); - - if (json_parse_flags_allow_location_information & flags_bitset) { - struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom; - state->dom += sizeof(struct json_value_ex_s); - - value_ex->offset = state->offset; - value_ex->line_no = state->line_no; - value_ex->row_no = state->offset - state->line_offset; - - value = &(value_ex->value); - } else { - value = (struct json_value_s *)state->dom; - state->dom += sizeof(struct json_value_s); - } - - element->value = value; - - json_parse_value(state, /* is_global_object = */ 0, value); - - /* successfully parsed a name/value pair! */ - elements++; - allow_comma = 1; - } - - /* if we had at least one element, end the linked list. */ - if (previous) { - previous->next = json_null; - } - - if (0 == elements) { - object->start = json_null; - } - - object->length = elements; -} - -json_weak void json_parse_array(struct json_parse_state_s *state, - struct json_array_s *array); -void json_parse_array(struct json_parse_state_s *state, - struct json_array_s *array) { - const char *const src = state->src; - const size_t size = state->size; - size_t elements = 0; - int allow_comma = 0; - struct json_array_element_s *previous = json_null; - - /* skip leading '['. */ - state->offset++; - - (void)json_skip_all_skippables(state); - - /* reset elements. */ - elements = 0; - - do { - struct json_array_element_s *element = json_null; - struct json_value_s *value = json_null; - - (void)json_skip_all_skippables(state); - - if (']' == src[state->offset]) { - /* skip trailing ']'. */ - state->offset++; - - /* finished the array! */ - break; - } - - /* if we parsed at least one element previously, grok for a comma. */ - if (allow_comma) { - if (',' == src[state->offset]) { - /* skip comma. */ - state->offset++; - allow_comma = 0; - continue; - } - } - - element = (struct json_array_element_s *)state->dom; - - state->dom += sizeof(struct json_array_element_s); - - if (json_null == previous) { - /* this is our first element, so record it in our array. */ - array->start = element; - } else { - previous->next = element; - } - - previous = element; - - if (json_parse_flags_allow_location_information & state->flags_bitset) { - struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom; - state->dom += sizeof(struct json_value_ex_s); - - value_ex->offset = state->offset; - value_ex->line_no = state->line_no; - value_ex->row_no = state->offset - state->line_offset; - - value = &(value_ex->value); - } else { - value = (struct json_value_s *)state->dom; - state->dom += sizeof(struct json_value_s); - } - - element->value = value; - - json_parse_value(state, /* is_global_object = */ 0, value); - - /* successfully parsed an array element! */ - elements++; - allow_comma = 1; - } while (state->offset < size); - - /* end the linked list. */ - if (previous) { - previous->next = json_null; - } - - if (0 == elements) { - array->start = json_null; - } - - array->length = elements; -} - -json_weak void json_parse_number(struct json_parse_state_s *state, - struct json_number_s *number); -void json_parse_number(struct json_parse_state_s *state, - struct json_number_s *number) { - const size_t flags_bitset = state->flags_bitset; - size_t offset = state->offset; - const size_t size = state->size; - size_t bytes_written = 0; - const char *const src = state->src; - char *data = state->data; - - number->number = data; - - if (json_parse_flags_allow_hexadecimal_numbers & flags_bitset) { - if (('0' == src[offset]) && - (('x' == src[offset + 1]) || ('X' == src[offset + 1]))) { - /* consume hexadecimal digits. */ - while ((offset < size) && - (('0' <= src[offset] && src[offset] <= '9') || - ('a' <= src[offset] && src[offset] <= 'f') || - ('A' <= src[offset] && src[offset] <= 'F') || - ('x' == src[offset]) || ('X' == src[offset]))) { - data[bytes_written++] = src[offset++]; - } - } - } - - while (offset < size) { - int end = 0; - - switch (src[offset]) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '.': - case 'e': - case 'E': - case '+': - case '-': - data[bytes_written++] = src[offset++]; - break; - default: - end = 1; - break; - } - - if (0 != end) { - break; - } - } - - if (json_parse_flags_allow_inf_and_nan & flags_bitset) { - const size_t inf_strlen = 8; /* = strlen("Infinity");. */ - const size_t nan_strlen = 3; /* = strlen("NaN");. */ - - if (offset + inf_strlen < size) { - if ('I' == src[offset]) { - size_t i; - /* We found our special 'Infinity' keyword! */ - for (i = 0; i < inf_strlen; i++) { - data[bytes_written++] = src[offset++]; - } - } - } - - if (offset + nan_strlen < size) { - if ('N' == src[offset]) { - size_t i; - /* We found our special 'NaN' keyword! */ - for (i = 0; i < nan_strlen; i++) { - data[bytes_written++] = src[offset++]; - } - } - } - } - - /* record the size of the number. */ - number->number_size = bytes_written; - /* add null terminator to number string. */ - data[bytes_written++] = '\0'; - /* move data along. */ - state->data += bytes_written; - /* update offset. */ - state->offset = offset; -} - -json_weak void json_parse_value(struct json_parse_state_s *state, - int is_global_object, - struct json_value_s *value); -void json_parse_value(struct json_parse_state_s *state, int is_global_object, - struct json_value_s *value) { - const size_t flags_bitset = state->flags_bitset; - const char *const src = state->src; - const size_t size = state->size; - size_t offset; - - (void)json_skip_all_skippables(state); - - /* cache offset now. */ - offset = state->offset; - - if (is_global_object) { - value->type = json_type_object; - value->payload = state->dom; - state->dom += sizeof(struct json_object_s); - json_parse_object(state, /* is_global_object = */ 1, - (struct json_object_s *)value->payload); - } else { - switch (src[offset]) { - case '"': - case '\'': - value->type = json_type_string; - value->payload = state->dom; - state->dom += sizeof(struct json_string_s); - json_parse_string(state, (struct json_string_s *)value->payload); - break; - case '{': - value->type = json_type_object; - value->payload = state->dom; - state->dom += sizeof(struct json_object_s); - json_parse_object(state, /* is_global_object = */ 0, - (struct json_object_s *)value->payload); - break; - case '[': - value->type = json_type_array; - value->payload = state->dom; - state->dom += sizeof(struct json_array_s); - json_parse_array(state, (struct json_array_s *)value->payload); - break; - case '-': - case '+': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '.': - value->type = json_type_number; - value->payload = state->dom; - state->dom += sizeof(struct json_number_s); - json_parse_number(state, (struct json_number_s *)value->payload); - break; - default: - if ((offset + 4) <= size && 't' == src[offset + 0] && - 'r' == src[offset + 1] && 'u' == src[offset + 2] && - 'e' == src[offset + 3]) { - value->type = json_type_true; - value->payload = json_null; - state->offset += 4; - } else if ((offset + 5) <= size && 'f' == src[offset + 0] && - 'a' == src[offset + 1] && 'l' == src[offset + 2] && - 's' == src[offset + 3] && 'e' == src[offset + 4]) { - value->type = json_type_false; - value->payload = json_null; - state->offset += 5; - } else if ((offset + 4) <= size && 'n' == src[offset + 0] && - 'u' == src[offset + 1] && 'l' == src[offset + 2] && - 'l' == src[offset + 3]) { - value->type = json_type_null; - value->payload = json_null; - state->offset += 4; - } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && - (offset + 3) <= size && 'N' == src[offset + 0] && - 'a' == src[offset + 1] && 'N' == src[offset + 2]) { - value->type = json_type_number; - value->payload = state->dom; - state->dom += sizeof(struct json_number_s); - json_parse_number(state, (struct json_number_s *)value->payload); - } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && - (offset + 8) <= size && 'I' == src[offset + 0] && - 'n' == src[offset + 1] && 'f' == src[offset + 2] && - 'i' == src[offset + 3] && 'n' == src[offset + 4] && - 'i' == src[offset + 5] && 't' == src[offset + 6] && - 'y' == src[offset + 7]) { - value->type = json_type_number; - value->payload = state->dom; - state->dom += sizeof(struct json_number_s); - json_parse_number(state, (struct json_number_s *)value->payload); - } - break; - } - } -} - -struct json_value_s * -json_parse_ex(const void *src, size_t src_size, size_t flags_bitset, - void *(*alloc_func_ptr)(void *user_data, size_t size), - void *user_data, struct json_parse_result_s *result) { - struct json_parse_state_s state; - void *allocation; - struct json_value_s *value; - size_t total_size; - int input_error; - - if (result) { - result->error = json_parse_error_none; - result->error_offset = 0; - result->error_line_no = 0; - result->error_row_no = 0; - } - - if (json_null == src) { - /* invalid src pointer was null! */ - return json_null; - } - - state.src = (const char *)src; - state.size = src_size; - state.offset = 0; - state.line_no = 1; - state.line_offset = 0; - state.error = json_parse_error_none; - state.dom_size = 0; - state.data_size = 0; - state.flags_bitset = flags_bitset; - - input_error = json_get_value_size( - &state, (int)(json_parse_flags_allow_global_object & state.flags_bitset)); - - if (0 == input_error) { - json_skip_all_skippables(&state); - - if (state.offset != state.size) { - /* our parsing didn't have an error, but there are characters remaining in - * the input that weren't part of the JSON! */ - - state.error = json_parse_error_unexpected_trailing_characters; - input_error = 1; - } - } - - if (input_error) { - /* parsing value's size failed (most likely an invalid JSON DOM!). */ - if (result) { - result->error = state.error; - result->error_offset = state.offset; - result->error_line_no = state.line_no; - result->error_row_no = state.offset - state.line_offset; - } - return json_null; - } - - /* our total allocation is the combination of the dom and data sizes (we. */ - /* first encode the structure of the JSON, and then the data referenced by. */ - /* the JSON values). */ - total_size = state.dom_size + state.data_size; - - if (json_null == alloc_func_ptr) { - allocation = malloc(total_size); - } else { - allocation = alloc_func_ptr(user_data, total_size); - } - - if (json_null == allocation) { - /* malloc failed! */ - if (result) { - result->error = json_parse_error_allocator_failed; - result->error_offset = 0; - result->error_line_no = 0; - result->error_row_no = 0; - } - - return json_null; - } - - /* reset offset so we can reuse it. */ - state.offset = 0; - - /* reset the line information so we can reuse it. */ - state.line_no = 1; - state.line_offset = 0; - - state.dom = (char *)allocation; - state.data = state.dom + state.dom_size; - - if (json_parse_flags_allow_location_information & state.flags_bitset) { - struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state.dom; - state.dom += sizeof(struct json_value_ex_s); - - value_ex->offset = state.offset; - value_ex->line_no = state.line_no; - value_ex->row_no = state.offset - state.line_offset; - - value = &(value_ex->value); - } else { - value = (struct json_value_s *)state.dom; - state.dom += sizeof(struct json_value_s); - } - - json_parse_value( - &state, (int)(json_parse_flags_allow_global_object & state.flags_bitset), - value); - - return (struct json_value_s *)allocation; -} - -struct json_value_s *json_parse(const void *src, size_t src_size) { - return json_parse_ex(src, src_size, json_parse_flags_default, json_null, - json_null, json_null); -} - -struct json_extract_result_s { - size_t dom_size; - size_t data_size; -}; - -struct json_value_s *json_extract_value(const struct json_value_s *value) { - return json_extract_value_ex(value, json_null, json_null); -} - -json_weak struct json_extract_result_s -json_extract_get_number_size(const struct json_number_s *const number); -json_weak struct json_extract_result_s -json_extract_get_string_size(const struct json_string_s *const string); -json_weak struct json_extract_result_s -json_extract_get_object_size(const struct json_object_s *const object); -json_weak struct json_extract_result_s -json_extract_get_array_size(const struct json_array_s *const array); -json_weak struct json_extract_result_s -json_extract_get_value_size(const struct json_value_s *const value); - -struct json_extract_result_s -json_extract_get_number_size(const struct json_number_s *const number) { - struct json_extract_result_s result; - result.dom_size = sizeof(struct json_number_s); - result.data_size = number->number_size; - return result; -} - -struct json_extract_result_s -json_extract_get_string_size(const struct json_string_s *const string) { - struct json_extract_result_s result; - result.dom_size = sizeof(struct json_string_s); - result.data_size = string->string_size + 1; - return result; -} - -struct json_extract_result_s -json_extract_get_object_size(const struct json_object_s *const object) { - struct json_extract_result_s result; - size_t i; - const struct json_object_element_s *element = object->start; - - result.dom_size = sizeof(struct json_object_s) + - (sizeof(struct json_object_element_s) * object->length); - result.data_size = 0; - - for (i = 0; i < object->length; i++) { - const struct json_extract_result_s string_result = - json_extract_get_string_size(element->name); - const struct json_extract_result_s value_result = - json_extract_get_value_size(element->value); - - result.dom_size += string_result.dom_size; - result.data_size += string_result.data_size; - - result.dom_size += value_result.dom_size; - result.data_size += value_result.data_size; - - element = element->next; - } - - return result; -} - -struct json_extract_result_s -json_extract_get_array_size(const struct json_array_s *const array) { - struct json_extract_result_s result; - size_t i; - const struct json_array_element_s *element = array->start; - - result.dom_size = sizeof(struct json_array_s) + - (sizeof(struct json_array_element_s) * array->length); - result.data_size = 0; - - for (i = 0; i < array->length; i++) { - const struct json_extract_result_s value_result = - json_extract_get_value_size(element->value); - - result.dom_size += value_result.dom_size; - result.data_size += value_result.data_size; - - element = element->next; - } - - return result; -} - -struct json_extract_result_s -json_extract_get_value_size(const struct json_value_s *const value) { - struct json_extract_result_s result = {0, 0}; - - switch (value->type) { - default: - break; - case json_type_object: - result = json_extract_get_object_size( - (const struct json_object_s *)value->payload); - break; - case json_type_array: - result = json_extract_get_array_size( - (const struct json_array_s *)value->payload); - break; - case json_type_number: - result = json_extract_get_number_size( - (const struct json_number_s *)value->payload); - break; - case json_type_string: - result = json_extract_get_string_size( - (const struct json_string_s *)value->payload); - break; - } - - result.dom_size += sizeof(struct json_value_s); - - return result; -} - -struct json_extract_state_s { - char *dom; - char *data; -}; - -json_weak void json_extract_copy_value(struct json_extract_state_s *const state, - const struct json_value_s *const value); -void json_extract_copy_value(struct json_extract_state_s *const state, - const struct json_value_s *const value) { - struct json_string_s *string; - struct json_number_s *number; - struct json_object_s *object; - struct json_array_s *array; - struct json_value_s *new_value; - - memcpy(state->dom, value, sizeof(struct json_value_s)); - new_value = (struct json_value_s *)state->dom; - state->dom += sizeof(struct json_value_s); - new_value->payload = state->dom; - - if (json_type_string == value->type) { - memcpy(state->dom, value->payload, sizeof(struct json_string_s)); - string = (struct json_string_s *)state->dom; - state->dom += sizeof(struct json_string_s); - - memcpy(state->data, string->string, string->string_size + 1); - string->string = state->data; - state->data += string->string_size + 1; - } else if (json_type_number == value->type) { - memcpy(state->dom, value->payload, sizeof(struct json_number_s)); - number = (struct json_number_s *)state->dom; - state->dom += sizeof(struct json_number_s); - - memcpy(state->data, number->number, number->number_size); - number->number = state->data; - state->data += number->number_size; - } else if (json_type_object == value->type) { - struct json_object_element_s *element; - size_t i; - - memcpy(state->dom, value->payload, sizeof(struct json_object_s)); - object = (struct json_object_s *)state->dom; - state->dom += sizeof(struct json_object_s); - - element = object->start; - object->start = (struct json_object_element_s *)state->dom; - - for (i = 0; i < object->length; i++) { - struct json_value_s *previous_value; - struct json_object_element_s *previous_element; - - memcpy(state->dom, element, sizeof(struct json_object_element_s)); - element = (struct json_object_element_s *)state->dom; - state->dom += sizeof(struct json_object_element_s); - - string = element->name; - memcpy(state->dom, string, sizeof(struct json_string_s)); - string = (struct json_string_s *)state->dom; - state->dom += sizeof(struct json_string_s); - element->name = string; - - memcpy(state->data, string->string, string->string_size + 1); - string->string = state->data; - state->data += string->string_size + 1; - - previous_value = element->value; - element->value = (struct json_value_s *)state->dom; - json_extract_copy_value(state, previous_value); - - previous_element = element; - element = element->next; - - if (element) { - previous_element->next = (struct json_object_element_s *)state->dom; - } - } - } else if (json_type_array == value->type) { - struct json_array_element_s *element; - size_t i; - - memcpy(state->dom, value->payload, sizeof(struct json_array_s)); - array = (struct json_array_s *)state->dom; - state->dom += sizeof(struct json_array_s); - - element = array->start; - array->start = (struct json_array_element_s *)state->dom; - - for (i = 0; i < array->length; i++) { - struct json_value_s *previous_value; - struct json_array_element_s *previous_element; - - memcpy(state->dom, element, sizeof(struct json_array_element_s)); - element = (struct json_array_element_s *)state->dom; - state->dom += sizeof(struct json_array_element_s); - - previous_value = element->value; - element->value = (struct json_value_s *)state->dom; - json_extract_copy_value(state, previous_value); - - previous_element = element; - element = element->next; - - if (element) { - previous_element->next = (struct json_array_element_s *)state->dom; - } - } - } -} - -struct json_value_s *json_extract_value_ex(const struct json_value_s *value, - void *(*alloc_func_ptr)(void *, - size_t), - void *user_data) { - void *allocation; - struct json_extract_result_s result; - struct json_extract_state_s state; - size_t total_size; - - if (json_null == value) { - /* invalid value was null! */ - return json_null; - } - - result = json_extract_get_value_size(value); - total_size = result.dom_size + result.data_size; - - if (json_null == alloc_func_ptr) { - allocation = malloc(total_size); - } else { - allocation = alloc_func_ptr(user_data, total_size); - } - - state.dom = (char *)allocation; - state.data = state.dom + result.dom_size; - - json_extract_copy_value(&state, value); - - return (struct json_value_s *)allocation; -} - -struct json_string_s *json_value_as_string(struct json_value_s *const value) { - if (value->type != json_type_string) { - return json_null; - } - - return (struct json_string_s *)value->payload; -} - -struct json_number_s *json_value_as_number(struct json_value_s *const value) { - if (value->type != json_type_number) { - return json_null; - } - - return (struct json_number_s *)value->payload; -} - -struct json_object_s *json_value_as_object(struct json_value_s *const value) { - if (value->type != json_type_object) { - return json_null; - } - - return (struct json_object_s *)value->payload; -} - -struct json_array_s *json_value_as_array(struct json_value_s *const value) { - if (value->type != json_type_array) { - return json_null; - } - - return (struct json_array_s *)value->payload; -} - -int json_value_is_true(const struct json_value_s *const value) { - return value->type == json_type_true; -} - -int json_value_is_false(const struct json_value_s *const value) { - return value->type == json_type_false; -} - -int json_value_is_null(const struct json_value_s *const value) { - return value->type == json_type_null; -} - -json_weak int -json_write_minified_get_value_size(const struct json_value_s *value, - size_t *size); - -json_weak int json_write_get_number_size(const struct json_number_s *number, - size_t *size); -int json_write_get_number_size(const struct json_number_s *number, - size_t *size) { - json_uintmax_t parsed_number; - size_t i; - - if (number->number_size >= 2) { - switch (number->number[1]) { - default: - break; - case 'x': - case 'X': - /* the number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal - * so we have to do extra work to convert it to a non-hexadecimal for JSON - * output. */ - parsed_number = json_strtoumax(number->number, json_null, 0); - - i = 0; - - while (0 != parsed_number) { - parsed_number /= 10; - i++; - } - - *size += i; - return 0; - } - } - - /* check to see if the number has leading/trailing decimal point. */ - i = 0; - - /* skip any leading '+' or '-'. */ - if ((i < number->number_size) && - (('+' == number->number[i]) || ('-' == number->number[i]))) { - i++; - } - - /* check if we have infinity. */ - if ((i < number->number_size) && ('I' == number->number[i])) { - const char *inf = "Infinity"; - size_t k; - - for (k = i; k < number->number_size; k++) { - const char c = *inf++; - - /* Check if we found the Infinity string! */ - if ('\0' == c) { - break; - } else if (c != number->number[k]) { - break; - } - } - - if ('\0' == *inf) { - /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */ - *size += 22; - - /* if we had a leading '-' we need to record it in the JSON output. */ - if ('-' == number->number[0]) { - *size += 1; - } - } - - return 0; - } - - /* check if we have nan. */ - if ((i < number->number_size) && ('N' == number->number[i])) { - const char *nan = "NaN"; - size_t k; - - for (k = i; k < number->number_size; k++) { - const char c = *nan++; - - /* Check if we found the NaN string! */ - if ('\0' == c) { - break; - } else if (c != number->number[k]) { - break; - } - } - - if ('\0' == *nan) { - /* NaN becomes 1 because JSON can't support it. */ - *size += 1; - - return 0; - } - } - - /* if we had a leading decimal point. */ - if ((i < number->number_size) && ('.' == number->number[i])) { - /* 1 + because we had a leading decimal point. */ - *size += 1; - goto cleanup; - } - - for (; i < number->number_size; i++) { - const char c = number->number[i]; - if (!('0' <= c && c <= '9')) { - break; - } - } - - /* if we had a trailing decimal point. */ - if ((i + 1 == number->number_size) && ('.' == number->number[i])) { - /* 1 + because we had a trailing decimal point. */ - *size += 1; - goto cleanup; - } - -cleanup: - *size += number->number_size; /* the actual string of the number. */ - - /* if we had a leading '+' we don't record it in the JSON output. */ - if ('+' == number->number[0]) { - *size -= 1; - } - - return 0; -} - -json_weak int json_write_get_string_size(const struct json_string_s *string, - size_t *size); -int json_write_get_string_size(const struct json_string_s *string, - size_t *size) { - size_t i; - for (i = 0; i < string->string_size; i++) { - switch (string->string[i]) { - case '"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - *size += 2; - break; - default: - *size += 1; - break; - } - } - - *size += 2; /* need to encode the surrounding '"' characters. */ - - return 0; -} - -json_weak int -json_write_minified_get_array_size(const struct json_array_s *array, - size_t *size); -int json_write_minified_get_array_size(const struct json_array_s *array, - size_t *size) { - struct json_array_element_s *element; - - *size += 2; /* '[' and ']'. */ - - if (1 < array->length) { - *size += array->length - 1; /* ','s seperate each element. */ - } - - for (element = array->start; json_null != element; element = element->next) { - if (json_write_minified_get_value_size(element->value, size)) { - /* value was malformed! */ - return 1; - } - } - - return 0; -} - -json_weak int -json_write_minified_get_object_size(const struct json_object_s *object, - size_t *size); -int json_write_minified_get_object_size(const struct json_object_s *object, - size_t *size) { - struct json_object_element_s *element; - - *size += 2; /* '{' and '}'. */ - - *size += object->length; /* ':'s seperate each name/value pair. */ - - if (1 < object->length) { - *size += object->length - 1; /* ','s seperate each element. */ - } - - for (element = object->start; json_null != element; element = element->next) { - if (json_write_get_string_size(element->name, size)) { - /* string was malformed! */ - return 1; - } - - if (json_write_minified_get_value_size(element->value, size)) { - /* value was malformed! */ - return 1; - } - } - - return 0; -} - -json_weak int -json_write_minified_get_value_size(const struct json_value_s *value, - size_t *size); -int json_write_minified_get_value_size(const struct json_value_s *value, - size_t *size) { - switch (value->type) { - default: - /* unknown value type found! */ - return 1; - case json_type_number: - return json_write_get_number_size((struct json_number_s *)value->payload, - size); - case json_type_string: - return json_write_get_string_size((struct json_string_s *)value->payload, - size); - case json_type_array: - return json_write_minified_get_array_size( - (struct json_array_s *)value->payload, size); - case json_type_object: - return json_write_minified_get_object_size( - (struct json_object_s *)value->payload, size); - case json_type_true: - *size += 4; /* the string "true". */ - return 0; - case json_type_false: - *size += 5; /* the string "false". */ - return 0; - case json_type_null: - *size += 4; /* the string "null". */ - return 0; - } -} - -json_weak char *json_write_minified_value(const struct json_value_s *value, - char *data); - -json_weak char *json_write_number(const struct json_number_s *number, - char *data); -char *json_write_number(const struct json_number_s *number, char *data) { - json_uintmax_t parsed_number, backup; - size_t i; - - if (number->number_size >= 2) { - switch (number->number[1]) { - default: - break; - case 'x': - case 'X': - /* The number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal - * so we have to do extra work to convert it to a non-hexadecimal for JSON - * output. */ - parsed_number = json_strtoumax(number->number, json_null, 0); - - /* We need a copy of parsed number twice, so take a backup of it. */ - backup = parsed_number; - - i = 0; - - while (0 != parsed_number) { - parsed_number /= 10; - i++; - } - - /* Restore parsed_number to its original value stored in the backup. */ - parsed_number = backup; - - /* Now use backup to take a copy of i, or the length of the string. */ - backup = i; - - do { - *(data + i - 1) = '0' + (char)(parsed_number % 10); - parsed_number /= 10; - i--; - } while (0 != parsed_number); - - data += backup; - - return data; - } - } - - /* check to see if the number has leading/trailing decimal point. */ - i = 0; - - /* skip any leading '-'. */ - if ((i < number->number_size) && - (('+' == number->number[i]) || ('-' == number->number[i]))) { - i++; - } - - /* check if we have infinity. */ - if ((i < number->number_size) && ('I' == number->number[i])) { - const char *inf = "Infinity"; - size_t k; - - for (k = i; k < number->number_size; k++) { - const char c = *inf++; - - /* Check if we found the Infinity string! */ - if ('\0' == c) { - break; - } else if (c != number->number[k]) { - break; - } - } - - if ('\0' == *inf++) { - const char *dbl_max; - - /* if we had a leading '-' we need to record it in the JSON output. */ - if ('-' == number->number[0]) { - *data++ = '-'; - } - - /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */ - for (dbl_max = "1.7976931348623158e308"; '\0' != *dbl_max; dbl_max++) { - *data++ = *dbl_max; - } - - return data; - } - } - - /* check if we have nan. */ - if ((i < number->number_size) && ('N' == number->number[i])) { - const char *nan = "NaN"; - size_t k; - - for (k = i; k < number->number_size; k++) { - const char c = *nan++; - - /* Check if we found the NaN string! */ - if ('\0' == c) { - break; - } else if (c != number->number[k]) { - break; - } - } - - if ('\0' == *nan++) { - /* NaN becomes 0 because JSON can't support it. */ - *data++ = '0'; - return data; - } - } - - /* if we had a leading decimal point. */ - if ((i < number->number_size) && ('.' == number->number[i])) { - i = 0; - - /* skip any leading '+'. */ - if ('+' == number->number[i]) { - i++; - } - - /* output the leading '-' if we had one. */ - if ('-' == number->number[i]) { - *data++ = '-'; - i++; - } - - /* insert a '0' to fix the leading decimal point for JSON output. */ - *data++ = '0'; - - /* and output the rest of the number as normal. */ - for (; i < number->number_size; i++) { - *data++ = number->number[i]; - } - - return data; - } - - for (; i < number->number_size; i++) { - const char c = number->number[i]; - if (!('0' <= c && c <= '9')) { - break; - } - } - - /* if we had a trailing decimal point. */ - if ((i + 1 == number->number_size) && ('.' == number->number[i])) { - i = 0; - - /* skip any leading '+'. */ - if ('+' == number->number[i]) { - i++; - } - - /* output the leading '-' if we had one. */ - if ('-' == number->number[i]) { - *data++ = '-'; - i++; - } - - /* and output the rest of the number as normal. */ - for (; i < number->number_size; i++) { - *data++ = number->number[i]; - } - - /* insert a '0' to fix the trailing decimal point for JSON output. */ - *data++ = '0'; - - return data; - } - - i = 0; - - /* skip any leading '+'. */ - if ('+' == number->number[i]) { - i++; - } - - for (; i < number->number_size; i++) { - *data++ = number->number[i]; - } - - return data; -} - -json_weak char *json_write_string(const struct json_string_s *string, - char *data); -char *json_write_string(const struct json_string_s *string, char *data) { - size_t i; - - *data++ = '"'; /* open the string. */ - - for (i = 0; i < string->string_size; i++) { - switch (string->string[i]) { - case '"': - *data++ = '\\'; /* escape the control character. */ - *data++ = '"'; - break; - case '\\': - *data++ = '\\'; /* escape the control character. */ - *data++ = '\\'; - break; - case '\b': - *data++ = '\\'; /* escape the control character. */ - *data++ = 'b'; - break; - case '\f': - *data++ = '\\'; /* escape the control character. */ - *data++ = 'f'; - break; - case '\n': - *data++ = '\\'; /* escape the control character. */ - *data++ = 'n'; - break; - case '\r': - *data++ = '\\'; /* escape the control character. */ - *data++ = 'r'; - break; - case '\t': - *data++ = '\\'; /* escape the control character. */ - *data++ = 't'; - break; - default: - *data++ = string->string[i]; - break; - } - } - - *data++ = '"'; /* close the string. */ - - return data; -} - -json_weak char *json_write_minified_array(const struct json_array_s *array, - char *data); -char *json_write_minified_array(const struct json_array_s *array, char *data) { - struct json_array_element_s *element = json_null; - - *data++ = '['; /* open the array. */ - - for (element = array->start; json_null != element; element = element->next) { - if (element != array->start) { - *data++ = ','; /* ','s seperate each element. */ - } - - data = json_write_minified_value(element->value, data); - - if (json_null == data) { - /* value was malformed! */ - return json_null; - } - } - - *data++ = ']'; /* close the array. */ - - return data; -} - -json_weak char *json_write_minified_object(const struct json_object_s *object, - char *data); -char *json_write_minified_object(const struct json_object_s *object, - char *data) { - struct json_object_element_s *element = json_null; - - *data++ = '{'; /* open the object. */ - - for (element = object->start; json_null != element; element = element->next) { - if (element != object->start) { - *data++ = ','; /* ','s seperate each element. */ - } - - data = json_write_string(element->name, data); - - if (json_null == data) { - /* string was malformed! */ - return json_null; - } - - *data++ = ':'; /* ':'s seperate each name/value pair. */ - - data = json_write_minified_value(element->value, data); - - if (json_null == data) { - /* value was malformed! */ - return json_null; - } - } - - *data++ = '}'; /* close the object. */ - - return data; -} - -json_weak char *json_write_minified_value(const struct json_value_s *value, - char *data); -char *json_write_minified_value(const struct json_value_s *value, char *data) { - switch (value->type) { - default: - /* unknown value type found! */ - return json_null; - case json_type_number: - return json_write_number((struct json_number_s *)value->payload, data); - case json_type_string: - return json_write_string((struct json_string_s *)value->payload, data); - case json_type_array: - return json_write_minified_array((struct json_array_s *)value->payload, - data); - case json_type_object: - return json_write_minified_object((struct json_object_s *)value->payload, - data); - case json_type_true: - data[0] = 't'; - data[1] = 'r'; - data[2] = 'u'; - data[3] = 'e'; - return data + 4; - case json_type_false: - data[0] = 'f'; - data[1] = 'a'; - data[2] = 'l'; - data[3] = 's'; - data[4] = 'e'; - return data + 5; - case json_type_null: - data[0] = 'n'; - data[1] = 'u'; - data[2] = 'l'; - data[3] = 'l'; - return data + 4; - } -} - -void *json_write_minified(const struct json_value_s *value, size_t *out_size) { - size_t size = 0; - char *data = json_null; - char *data_end = json_null; - - if (json_null == value) { - return json_null; - } - - if (json_write_minified_get_value_size(value, &size)) { - /* value was malformed! */ - return json_null; - } - - size += 1; /* for the '\0' null terminating character. */ - - data = (char *)malloc(size); - - if (json_null == data) { - /* malloc failed! */ - return json_null; - } - - data_end = json_write_minified_value(value, data); - - if (json_null == data_end) { - /* bad chi occurred! */ - free(data); - return json_null; - } - - /* null terminated the string. */ - *data_end = '\0'; - - if (json_null != out_size) { - *out_size = size; - } - - return data; -} - -json_weak int json_write_pretty_get_value_size(const struct json_value_s *value, - size_t depth, size_t indent_size, - size_t newline_size, - size_t *size); - -json_weak int json_write_pretty_get_array_size(const struct json_array_s *array, - size_t depth, size_t indent_size, - size_t newline_size, - size_t *size); -int json_write_pretty_get_array_size(const struct json_array_s *array, - size_t depth, size_t indent_size, - size_t newline_size, size_t *size) { - struct json_array_element_s *element; - - *size += 1; /* '['. */ - - if (0 < array->length) { - /* if we have any elements we need to add a newline after our '['. */ - *size += newline_size; - - *size += array->length - 1; /* ','s seperate each element. */ - - for (element = array->start; json_null != element; - element = element->next) { - /* each element gets an indent. */ - *size += (depth + 1) * indent_size; - - if (json_write_pretty_get_value_size(element->value, depth + 1, - indent_size, newline_size, size)) { - /* value was malformed! */ - return 1; - } - - /* each element gets a newline too. */ - *size += newline_size; - } - - /* since we wrote out some elements, need to add a newline and indentation. - */ - /* to the trailing ']'. */ - *size += depth * indent_size; - } - - *size += 1; /* ']'. */ - - return 0; -} - -json_weak int -json_write_pretty_get_object_size(const struct json_object_s *object, - size_t depth, size_t indent_size, - size_t newline_size, size_t *size); -int json_write_pretty_get_object_size(const struct json_object_s *object, - size_t depth, size_t indent_size, - size_t newline_size, size_t *size) { - struct json_object_element_s *element; - - *size += 1; /* '{'. */ - - if (0 < object->length) { - *size += newline_size; /* need a newline next. */ - - *size += object->length - 1; /* ','s seperate each element. */ - - for (element = object->start; json_null != element; - element = element->next) { - /* each element gets an indent and newline. */ - *size += (depth + 1) * indent_size; - *size += newline_size; - - if (json_write_get_string_size(element->name, size)) { - /* string was malformed! */ - return 1; - } - - *size += 3; /* seperate each name/value pair with " : ". */ - - if (json_write_pretty_get_value_size(element->value, depth + 1, - indent_size, newline_size, size)) { - /* value was malformed! */ - return 1; - } - } - - *size += depth * indent_size; - } - - *size += 1; /* '}'. */ - - return 0; -} - -json_weak int json_write_pretty_get_value_size(const struct json_value_s *value, - size_t depth, size_t indent_size, - size_t newline_size, - size_t *size); -int json_write_pretty_get_value_size(const struct json_value_s *value, - size_t depth, size_t indent_size, - size_t newline_size, size_t *size) { - switch (value->type) { - default: - /* unknown value type found! */ - return 1; - case json_type_number: - return json_write_get_number_size((struct json_number_s *)value->payload, - size); - case json_type_string: - return json_write_get_string_size((struct json_string_s *)value->payload, - size); - case json_type_array: - return json_write_pretty_get_array_size( - (struct json_array_s *)value->payload, depth, indent_size, newline_size, - size); - case json_type_object: - return json_write_pretty_get_object_size( - (struct json_object_s *)value->payload, depth, indent_size, - newline_size, size); - case json_type_true: - *size += 4; /* the string "true". */ - return 0; - case json_type_false: - *size += 5; /* the string "false". */ - return 0; - case json_type_null: - *size += 4; /* the string "null". */ - return 0; - } -} - -json_weak char *json_write_pretty_value(const struct json_value_s *value, - size_t depth, const char *indent, - const char *newline, char *data); - -json_weak char *json_write_pretty_array(const struct json_array_s *array, - size_t depth, const char *indent, - const char *newline, char *data); -char *json_write_pretty_array(const struct json_array_s *array, size_t depth, - const char *indent, const char *newline, - char *data) { - size_t k, m; - struct json_array_element_s *element; - - *data++ = '['; /* open the array. */ - - if (0 < array->length) { - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - - for (element = array->start; json_null != element; - element = element->next) { - if (element != array->start) { - *data++ = ','; /* ','s seperate each element. */ - - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - } - - for (k = 0; k < depth + 1; k++) { - for (m = 0; '\0' != indent[m]; m++) { - *data++ = indent[m]; - } - } - - data = json_write_pretty_value(element->value, depth + 1, indent, newline, - data); - - if (json_null == data) { - /* value was malformed! */ - return json_null; - } - } - - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - - for (k = 0; k < depth; k++) { - for (m = 0; '\0' != indent[m]; m++) { - *data++ = indent[m]; - } - } - } - - *data++ = ']'; /* close the array. */ - - return data; -} - -json_weak char *json_write_pretty_object(const struct json_object_s *object, - size_t depth, const char *indent, - const char *newline, char *data); -char *json_write_pretty_object(const struct json_object_s *object, size_t depth, - const char *indent, const char *newline, - char *data) { - size_t k, m; - struct json_object_element_s *element; - - *data++ = '{'; /* open the object. */ - - if (0 < object->length) { - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - - for (element = object->start; json_null != element; - element = element->next) { - if (element != object->start) { - *data++ = ','; /* ','s seperate each element. */ - - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - } - - for (k = 0; k < depth + 1; k++) { - for (m = 0; '\0' != indent[m]; m++) { - *data++ = indent[m]; - } - } - - data = json_write_string(element->name, data); - - if (json_null == data) { - /* string was malformed! */ - return json_null; - } - - /* " : "s seperate each name/value pair. */ - *data++ = ' '; - *data++ = ':'; - *data++ = ' '; - - data = json_write_pretty_value(element->value, depth + 1, indent, newline, - data); - - if (json_null == data) { - /* value was malformed! */ - return json_null; - } - } - - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - - for (k = 0; k < depth; k++) { - for (m = 0; '\0' != indent[m]; m++) { - *data++ = indent[m]; - } - } - } - - *data++ = '}'; /* close the object. */ - - return data; -} - -json_weak char *json_write_pretty_value(const struct json_value_s *value, - size_t depth, const char *indent, - const char *newline, char *data); -char *json_write_pretty_value(const struct json_value_s *value, size_t depth, - const char *indent, const char *newline, - char *data) { - switch (value->type) { - default: - /* unknown value type found! */ - return json_null; - case json_type_number: - return json_write_number((struct json_number_s *)value->payload, data); - case json_type_string: - return json_write_string((struct json_string_s *)value->payload, data); - case json_type_array: - return json_write_pretty_array((struct json_array_s *)value->payload, depth, - indent, newline, data); - case json_type_object: - return json_write_pretty_object((struct json_object_s *)value->payload, - depth, indent, newline, data); - case json_type_true: - data[0] = 't'; - data[1] = 'r'; - data[2] = 'u'; - data[3] = 'e'; - return data + 4; - case json_type_false: - data[0] = 'f'; - data[1] = 'a'; - data[2] = 'l'; - data[3] = 's'; - data[4] = 'e'; - return data + 5; - case json_type_null: - data[0] = 'n'; - data[1] = 'u'; - data[2] = 'l'; - data[3] = 'l'; - return data + 4; - } -} - -void *json_write_pretty(const struct json_value_s *value, const char *indent, - const char *newline, size_t *out_size) { - size_t size = 0; - size_t indent_size = 0; - size_t newline_size = 0; - char *data = json_null; - char *data_end = json_null; - - if (json_null == value) { - return json_null; - } - - if (json_null == indent) { - indent = " "; /* default to two spaces. */ - } - - if (json_null == newline) { - newline = "\n"; /* default to linux newlines. */ - } - - while ('\0' != indent[indent_size]) { - ++indent_size; /* skip non-null terminating characters. */ - } - - while ('\0' != newline[newline_size]) { - ++newline_size; /* skip non-null terminating characters. */ - } - - if (json_write_pretty_get_value_size(value, 0, indent_size, newline_size, - &size)) { - /* value was malformed! */ - return json_null; - } - - size += 1; /* for the '\0' null terminating character. */ - - data = (char *)malloc(size); - - if (json_null == data) { - /* malloc failed! */ - return json_null; - } - - data_end = json_write_pretty_value(value, 0, indent, newline, data); - - if (json_null == data_end) { - /* bad chi occurred! */ - free(data); - return json_null; - } - - /* null terminated the string. */ - *data_end = '\0'; - - if (json_null != out_size) { - *out_size = size; - } - - return data; -} - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(_MSC_VER) -#pragma warning(pop) -#endif - -#endif /* SHEREDOM_JSON_H_INCLUDED. */ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/jsoncxx.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/jsoncxx.h deleted file mode 100644 index 979c2f85..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/jsoncxx.h +++ /dev/null @@ -1,427 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// Flags : clang-format SMTGSequencer -// -// Category : -// Filename : public.sdk/source/vst/moduleinfo/jsoncxx.h -// Created by : Steinberg, 12/2021 -// Description : -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -#pragma once - -#include "json.h" -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) || __has_include() -#include -#define SMTG_HAS_CHARCONV -#endif - -//------------------------------------------------------------------------ -namespace JSON { -namespace Detail { - -//------------------------------------------------------------------------ -template -struct Base -{ - explicit Base (JsonT* o) : object_ (o) {} - explicit Base (const Base& o) : object_ (o.object_) {} - - Base& operator= (const Base& o) = default; - - operator JsonT* () const { return object_; } - JsonT* jsonValue () const { return object_; } - -protected: - Base () : object_ (nullptr) {} - - JsonT* object_; -}; - -//------------------------------------------------------------------------ -template -struct Iterator -{ - explicit Iterator (JsonElement el) : el (el) {} - - bool operator== (const Iterator& other) const { return other.el == el; } - bool operator!= (const Iterator& other) const { return other.el != el; } - - const JsonElement& operator* () const { return el; } - const JsonElement& operator-> () const { return el; } - - Iterator& operator++ () - { - if (el) - el = el.next (); - return *this; - } - - Iterator operator++ (int) - { - auto it = Iterator (el); - operator++ (); - return it; - } - -private: - JsonElement el; -}; - -//------------------------------------------------------------------------ -} // Detail - -struct Object; -struct Array; -struct String; -struct Number; -struct Boolean; - -//------------------------------------------------------------------------ -enum class Type -{ - Object, - Array, - String, - Number, - True, - False, - Null, -}; - -//------------------------------------------------------------------------ -struct SourceLocation -{ - size_t offset; - size_t line; - size_t row; -}; - -//------------------------------------------------------------------------ -struct Value : Detail::Base -{ - using Detail::Base::Base; - using VariantT = std::variant; - - std::optional asObject () const; - std::optional asArray () const; - std::optional asString () const; - std::optional asNumber () const; - std::optional asBoolean () const; - std::optional asNull () const; - - VariantT asVariant () const; - Type type () const; - - SourceLocation getSourceLocation () const; -}; - -//------------------------------------------------------------------------ -struct Boolean -{ - Boolean (size_t type) : value (type == json_type_true) {} - - operator bool () const { return value; } - -private: - bool value; -}; - -//------------------------------------------------------------------------ -struct String : Detail::Base -{ - using Detail::Base::Base; - - std::string_view text () const { return {jsonValue ()->string, jsonValue ()->string_size}; } - - SourceLocation getSourceLocation () const; -}; - -//------------------------------------------------------------------------ -struct Number : Detail::Base -{ - using Detail::Base::Base; - - std::string_view text () const { return {jsonValue ()->number, jsonValue ()->number_size}; } - - std::optional getInteger () const; - std::optional getDouble () const; -}; - -//------------------------------------------------------------------------ -struct ObjectElement : Detail::Base -{ - using Detail::Base::Base; - - String name () const { return String (jsonValue ()->name); } - Value value () const { return Value (jsonValue ()->value); } - - ObjectElement next () const { return ObjectElement (jsonValue ()->next); } -}; - -//------------------------------------------------------------------------ -struct Object : Detail::Base -{ - using Detail::Base::Base; - using Iterator = Detail::Iterator; - - size_t size () const { return jsonValue ()->length; } - - Iterator begin () const { return Iterator (ObjectElement (jsonValue ()->start)); } - Iterator end () const { return Iterator (ObjectElement (nullptr)); } -}; - -//------------------------------------------------------------------------ -struct ArrayElement : Detail::Base -{ - using Detail::Base::Base; - - Value value () const { return Value (jsonValue ()->value); } - - ArrayElement next () const { return ArrayElement (jsonValue ()->next); } -}; - -//------------------------------------------------------------------------ -struct Array : Detail::Base -{ - using Detail::Base::Base; - using Iterator = Detail::Iterator; - - size_t size () const { return jsonValue ()->length; } - - Iterator begin () const { return Iterator (ArrayElement (jsonValue ()->start)); } - Iterator end () const { return Iterator (ArrayElement (nullptr)); } -}; - -//------------------------------------------------------------------------ -struct Document : Value -{ - static std::variant parse (std::string_view data) - { - auto allocate = [] (void*, size_t allocSize) { return std::malloc (allocSize); }; - json_parse_result_s parse_result {}; - auto value = json_parse_ex (data.data (), data.size (), - json_parse_flags_allow_json5 | - json_parse_flags_allow_location_information, - allocate, nullptr, &parse_result); - if (value) - return Document (value); - return parse_result; - } - ~Document () noexcept - { - if (object_) - std::free (object_); - } - - Document (Document&& doc) noexcept { *this = std::move (doc); } - Document& operator= (Document&& doc) noexcept - { - std::swap (object_, doc.object_); - return *this; - } - -private: - using Value::Value; -}; - -//------------------------------------------------------------------------ -inline std::optional Value::asObject () const -{ - if (type () != Type::Object) - return {}; - return Object (json_value_as_object (jsonValue ())); -} - -//------------------------------------------------------------------------ -inline std::optional Value::asArray () const -{ - if (type () != Type::Array) - return {}; - return Array (json_value_as_array (jsonValue ())); -} - -//------------------------------------------------------------------------ -inline std::optional Value::asString () const -{ - if (type () != Type::String) - return {}; - return String (json_value_as_string (jsonValue ())); -} - -//------------------------------------------------------------------------ -inline std::optional Value::asNumber () const -{ - if (type () != Type::Number) - return {}; - return Number (json_value_as_number (jsonValue ())); -} - -//------------------------------------------------------------------------ -inline std::optional Value::asBoolean () const -{ - if (type () == Type::True || type () == Type::False) - return Boolean (jsonValue ()->type); - return {}; -} - -//------------------------------------------------------------------------ -inline std::optional Value::asNull () const -{ - if (type () != Type::Null) - return {}; - return nullptr; -} - -//------------------------------------------------------------------------ -inline Type Value::type () const -{ - switch (jsonValue ()->type) - { - case json_type_string: return Type::String; - case json_type_number: return Type::Number; - case json_type_object: return Type::Object; - case json_type_array: return Type::Array; - case json_type_true: return Type::True; - case json_type_false: return Type::False; - case json_type_null: return Type::Null; - } - assert (false); - return Type::Null; -} - -//------------------------------------------------------------------------ -inline Value::VariantT Value::asVariant () const -{ - switch (type ()) - { - case Type::String: return *asString (); - case Type::Number: return *asNumber (); - case Type::Object: return *asObject (); - case Type::Array: return *asArray (); - case Type::True: return *asBoolean (); - case Type::False: return *asBoolean (); - case Type::Null: return *asNull (); - } - assert (false); - return nullptr; -} - -//------------------------------------------------------------------------ -inline SourceLocation Value::getSourceLocation () const -{ - auto exValue = reinterpret_cast (jsonValue ()); - return {exValue->offset, exValue->line_no, exValue->row_no}; -} - -//------------------------------------------------------------------------ -inline SourceLocation String::getSourceLocation () const -{ - auto exValue = reinterpret_cast (jsonValue ()); - return {exValue->offset, exValue->line_no, exValue->row_no}; -} - -//------------------------------------------------------------------------ -inline std::optional Number::getInteger () const -{ -#if defined(SMTG_HAS_CHARCONV) - int64_t result {0}; - auto res = std::from_chars (jsonValue ()->number, - jsonValue ()->number + jsonValue ()->number_size, result); - if (res.ec == std::errc ()) - return result; - return {}; -#else - int64_t result {0}; - std::string str (jsonValue ()->number, jsonValue ()->number + jsonValue ()->number_size); - if (std::sscanf (str.data (), "%lld", &result) != 1) - return {}; - return result; -#endif -} - -//------------------------------------------------------------------------ -inline std::optional Number::getDouble () const -{ -#if 1 // clang still has no floting point from_chars version - size_t ctrl {0}; - auto result = std::stod (std::string (jsonValue ()->number, jsonValue ()->number_size), &ctrl); - if (ctrl > 0) - return result; -#else - double result {0.}; - auto res = std::from_chars (jsonValue ()->number, - jsonValue ()->number + jsonValue ()->number_size, result); - if (res.ec == std::errc ()) - return result; -#endif - return {}; -} - -//------------------------------------------------------------------------ -inline std::string_view errorToString (json_parse_error_e error) -{ - switch (error) - { - case json_parse_error_e::json_parse_error_none: return {}; - case json_parse_error_e::json_parse_error_expected_comma_or_closing_bracket: - return "json_parse_error_expected_comma_or_closing_bracket"; - case json_parse_error_e::json_parse_error_expected_colon: - return "json_parse_error_expected_colon"; - case json_parse_error_e::json_parse_error_expected_opening_quote: - return "json_parse_error_expected_opening_quote"; - case json_parse_error_e::json_parse_error_invalid_string_escape_sequence: - return "json_parse_error_invalid_string_escape_sequence"; - case json_parse_error_e::json_parse_error_invalid_number_format: - return "json_parse_error_invalid_number_format"; - case json_parse_error_e::json_parse_error_invalid_value: - return "json_parse_error_invalid_value"; - case json_parse_error_e::json_parse_error_premature_end_of_buffer: - return "json_parse_error_premature_end_of_buffer"; - case json_parse_error_e::json_parse_error_invalid_string: - return "json_parse_error_invalid_string"; - case json_parse_error_e::json_parse_error_allocator_failed: - return "json_parse_error_allocator_failed"; - case json_parse_error_e::json_parse_error_unexpected_trailing_characters: - return "json_parse_error_unexpected_trailing_characters"; - case json_parse_error_e::json_parse_error_unknown: return "json_parse_error_unknown"; - } - return {}; -} - -//------------------------------------------------------------------------ -} // JSON diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfo.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfo.h deleted file mode 100644 index 81b93f74..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfo.h +++ /dev/null @@ -1,100 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// Flags : clang-format SMTGSequencer -// -// Category : moduleinfo -// Filename : public.sdk/source/vst/moduleinfo/moduleinfo.h -// Created by : Steinberg, 12/2021 -// Description : -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -#pragma once - -#include -#include -#include - -//------------------------------------------------------------------------ -namespace Steinberg { - -//------------------------------------------------------------------------ -struct ModuleInfo -{ -//------------------------------------------------------------------------ - struct FactoryInfo - { - std::string vendor; - std::string url; - std::string email; - int32_t flags {0}; - }; - -//------------------------------------------------------------------------ - struct Snapshot - { - double scaleFactor {1.}; - std::string path; - }; - using SnapshotList = std::vector; - -//------------------------------------------------------------------------ - struct ClassInfo - { - std::string cid; - std::string category; - std::string name; - std::string vendor; - std::string version; - std::string sdkVersion; - std::vector subCategories; - SnapshotList snapshots; - int32_t cardinality {0x7FFFFFFF}; - uint32_t flags {0}; - }; - -//------------------------------------------------------------------------ - struct Compatibility - { - std::string newCID; - std::vector oldCID; - }; - - using ClassList = std::vector; - using CompatibilityList = std::vector; - - std::string name; - std::string version; - FactoryInfo factoryInfo; - ClassList classes; - CompatibilityList compatibility; -}; - -//------------------------------------------------------------------------ -} // Steinberg diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp deleted file mode 100644 index 8f12829c..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp +++ /dev/null @@ -1,309 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// Flags : clang-format SMTGSequencer -// -// Category : moduleinfo -// Filename : public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp -// Created by : Steinberg, 12/2021 -// Description : utility functions to create moduleinfo json files -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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 "moduleinfocreator.h" -#include "jsoncxx.h" -#include -#include -#include - -//------------------------------------------------------------------------ -namespace Steinberg::ModuleInfoLib { -using namespace VST3; -namespace { - -//------------------------------------------------------------------------ -struct JSON5Writer -{ -private: - std::ostream& stream; - bool beautify; - bool lastIsComma {false}; - int32_t intend {0}; - - void doBeautify () - { - if (beautify) - { - stream << '\n'; - for (int i = 0; i < intend; ++i) - stream << " "; - } - } - - void writeComma () - { - if (lastIsComma) - return; - stream << ","; - lastIsComma = true; - } - void startObject () - { - stream << "{"; - ++intend; - lastIsComma = false; - } - void endObject () - { - --intend; - doBeautify (); - stream << "}"; - lastIsComma = false; - } - void startArray () - { - stream << "["; - ++intend; - lastIsComma = false; - } - void endArray () - { - --intend; - doBeautify (); - stream << "]"; - lastIsComma = false; - } - -public: - JSON5Writer (std::ostream& stream, bool beautify = true) : stream (stream), beautify (beautify) - { - } - - void string (std::string_view str) - { - stream << "\"" << str << "\""; - lastIsComma = false; - } - - void boolean (bool val) - { - stream << (val ? "true" : "false"); - lastIsComma = false; - } - - template - void value (ValueT val) - { - stream << val; - lastIsComma = false; - } - - template - void object (Proc proc) - { - startObject (); - proc (); - endObject (); - } - - template - void array (Iterator begin, Iterator end, Proc proc) - { - startArray (); - while (begin != end) - { - doBeautify (); - proc (begin); - ++begin; - writeComma (); - } - endArray (); - } - - template - void keyValue (std::string_view key, Proc proc) - { - doBeautify (); - string (key); - stream << ": "; - proc (); - writeComma (); - } -}; - -//------------------------------------------------------------------------ -void writeSnapshots (const ModuleInfo::SnapshotList& snapshots, JSON5Writer& w) -{ - w.keyValue ("Snapshots", [&] () { - w.array (snapshots.begin (), snapshots.end (), [&] (const auto& el) { - w.object ([&] () { - w.keyValue ("Scale Factor", [&] () { w.value (el->scaleFactor); }); - w.keyValue ("Path", [&] () { w.string (el->path); }); - }); - }); - }); -} - -//------------------------------------------------------------------------ -void writeClassInfo (const ModuleInfo::ClassInfo& cls, JSON5Writer& w) -{ - w.keyValue ("CID", [&] () { w.string (cls.cid); }); - w.keyValue ("Category", [&] () { w.string (cls.category); }); - w.keyValue ("Name", [&] () { w.string (cls.name); }); - w.keyValue ("Vendor", [&] () { w.string (cls.vendor); }); - w.keyValue ("Version", [&] () { w.string (cls.version); }); - w.keyValue ("SDKVersion", [&] () { w.string (cls.sdkVersion); }); - const auto& sc = cls.subCategories; - if (!sc.empty ()) - { - w.keyValue ("Sub Categories", [&] () { - w.array (sc.begin (), sc.end (), [&] (const auto& cat) { w.string (*cat); }); - }); - } - w.keyValue ("Class Flags", [&] () { w.value (cls.flags); }); - w.keyValue ("Cardinality", [&] () { w.value (cls.cardinality); }); - writeSnapshots (cls.snapshots, w); -} - -//------------------------------------------------------------------------ -void writePluginCompatibility (const ModuleInfo::CompatibilityList& compat, JSON5Writer& w) -{ - if (compat.empty ()) - return; - w.keyValue ("Compatibility", [&] () { - w.array (compat.begin (), compat.end (), [&] (auto& el) { - w.object ([&] () { - w.keyValue ("New", [&] () { w.string (el->newCID); }); - w.keyValue ("Old", [&] () { - w.array (el->oldCID.begin (), el->oldCID.end (), - [&] (auto& oldEl) { w.string (*oldEl); }); - }); - }); - }); - }); -} - -//------------------------------------------------------------------------ -void writeFactoryInfo (const ModuleInfo::FactoryInfo& fi, JSON5Writer& w) -{ - w.keyValue ("Factory Info", [&] () { - w.object ([&] () { - w.keyValue ("Vendor", [&] () { w.string (fi.vendor); }); - w.keyValue ("URL", [&] () { w.string (fi.url); }); - w.keyValue ("E-Mail", [&] () { w.string (fi.email); }); - w.keyValue ("Flags", [&] () { - w.object ([&] () { - w.keyValue ("Unicode", - [&] () { w.boolean (fi.flags & PFactoryInfo::kUnicode); }); - w.keyValue ("Classes Discardable", [&] () { - w.boolean (fi.flags & PFactoryInfo::kClassesDiscardable); - }); - w.keyValue ("Component Non Discardable", [&] () { - w.boolean (fi.flags & PFactoryInfo::kComponentNonDiscardable); - }); - }); - }); - }); - }); -} - -//------------------------------------------------------------------------ -} // anonymous - -//------------------------------------------------------------------------ -ModuleInfo createModuleInfo (const VST3::Hosting::Module& module, bool includeDiscardableClasses) -{ - ModuleInfo info; - - const auto& factory = module.getFactory (); - auto factoryInfo = factory.info (); - - info.name = module.getName (); - auto pos = info.name.find_last_of ('.'); - if (pos != std::string::npos) - info.name.erase (pos); - - info.factoryInfo.vendor = factoryInfo.vendor (); - info.factoryInfo.url = factoryInfo.url (); - info.factoryInfo.email = factoryInfo.email (); - info.factoryInfo.flags = factoryInfo.flags (); - - if (factoryInfo.classesDiscardable () == false || - (factoryInfo.classesDiscardable () && includeDiscardableClasses)) - { - auto snapshots = VST3::Hosting::Module::getSnapshots (module.getPath ()); - for (const auto& ci : factory.classInfos ()) - { - ModuleInfo::ClassInfo classInfo; - classInfo.cid = ci.ID ().toString (); - classInfo.category = ci.category (); - classInfo.name = ci.name (); - classInfo.vendor = ci.vendor (); - classInfo.version = ci.version (); - classInfo.sdkVersion = ci.sdkVersion (); - classInfo.subCategories = ci.subCategories (); - classInfo.cardinality = ci.cardinality (); - classInfo.flags = ci.classFlags (); - auto snapshotIt = std::find_if (snapshots.begin (), snapshots.end (), - [&] (const auto& el) { return el.uid == ci.ID (); }); - if (snapshotIt != snapshots.end ()) - { - for (auto& s : snapshotIt->images) - { - std::string_view path (s.path); - if (path.find (module.getPath ()) == 0) - path.remove_prefix (module.getPath ().size () + 1); - classInfo.snapshots.emplace_back ( - ModuleInfo::Snapshot {s.scaleFactor, {path.data (), path.size ()}}); - } - snapshots.erase (snapshotIt); - } - info.classes.emplace_back (std::move (classInfo)); - } - } - return info; -} - -//------------------------------------------------------------------------ -void outputJson (const ModuleInfo& info, std::ostream& output) -{ - JSON5Writer w (output); - w.object ([&] () { - w.keyValue ("Name", [&] () { w.string (info.name); }); - w.keyValue ("Version", [&] () { w.string (info.version); }); - writeFactoryInfo (info.factoryInfo, w); - writePluginCompatibility (info.compatibility, w); - w.keyValue ("Classes", [&] () { - w.array (info.classes.begin (), info.classes.end (), - [&] (const auto& cls) { w.object ([&] () { writeClassInfo (*cls, w); }); }); - }); - }); -} - -//------------------------------------------------------------------------ -} // Steinberg::ModuleInfoLib diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.h deleted file mode 100644 index f28716ae..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.h +++ /dev/null @@ -1,67 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// Flags : clang-format SMTGSequencer -// -// Category : moduleinfo -// Filename : public.sdk/source/vst/moduleinfo/moduleinfocreator.h -// Created by : Steinberg, 12/2021 -// Description : utility functions to create moduleinfo json files -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -#pragma once - -#include "moduleinfo.h" -#include "public.sdk/source/vst/hosting/module.h" -#include -#include -#include - -//------------------------------------------------------------------------ -namespace Steinberg::ModuleInfoLib { - -//------------------------------------------------------------------------ -/** create a ModuleInfo from a module - * - * @param module module to create the module info from - * @param includeDiscardableClasses if true adds the current available classes to the module info - * @return a ModuleInfo struct with the classes and factory info of the module - */ -ModuleInfo createModuleInfo (const VST3::Hosting::Module& module, bool includeDiscardableClasses); - -//------------------------------------------------------------------------ -/** output the ModuleInfo as json to the stream - * - * @param info module info - * @param output output stream - */ -void outputJson (const ModuleInfo& info, std::ostream& output); - -//------------------------------------------------------------------------ -} // Steinberg::ModuelInfoLib diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp deleted file mode 100644 index 0d09d2bb..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp +++ /dev/null @@ -1,537 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// Flags : clang-format SMTGSequencer -// -// Category : moduleinfo -// Filename : public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp -// Created by : Steinberg, 01/2022 -// Description : utility functions to parse moduleinfo json files -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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 "moduleinfoparser.h" -#include "jsoncxx.h" -#include "pluginterfaces/base/ipluginbase.h" -#include -#include - -//------------------------------------------------------------------------ -namespace Steinberg::ModuleInfoLib { -namespace { - -//------------------------------------------------------------------------ -void printJsonParseError (json_parse_result_s& parseResult, std::ostream& errorOut) -{ - errorOut << "error : " - << JSON::errorToString (static_cast (parseResult.error)) << '\n'; - errorOut << "offset : " << parseResult.error_offset << '\n'; - errorOut << "line no: " << parseResult.error_line_no << '\n'; - errorOut << "row no : " << parseResult.error_row_no << '\n'; -} - -//------------------------------------------------------------------------ -struct parse_error : std::exception -{ - parse_error (const std::string& str, const JSON::Value& value) - : str (str), location (value.getSourceLocation ()) - { - addLocation (location); - } - parse_error (const std::string& str, const JSON::String& value) - : str (str), location (value.getSourceLocation ()) - { - addLocation (location); - } - const char* what () const noexcept override { return str.data (); } - -private: - void addLocation (const JSON::SourceLocation& loc) - { - str += '\n'; - str += "offset:"; - str += std::to_string (loc.offset); - str += '\n'; - str += "line:"; - str += std::to_string (loc.line); - str += '\n'; - str += "row:"; - str += std::to_string (loc.row); - str += '\n'; - } - - std::string str; - JSON::SourceLocation location; -}; - -//------------------------------------------------------------------------ -struct ModuleInfoJsonParser -{ - ModuleInfoJsonParser () = default; - - std::string_view getText (const JSON::Value& value) const - { - if (auto str = value.asString ()) - return str->text (); - throw parse_error ("Expect a String here", value); - } - - template - T getInteger (const JSON::Value& value) const - { - if (auto number = value.asNumber ()) - { - if (auto result = number->getInteger ()) - { - if (result > static_cast (std::numeric_limits::max ()) || - result < static_cast (std::numeric_limits::min ())) - throw parse_error ("Value is out of range here", value); - return static_cast (*result); - } - throw parse_error ("Expect an Integer here", value); - } - throw parse_error ("Expect a Number here", value); - } - - double getDouble (const JSON::Value& value) const - { - if (auto number = value.asNumber ()) - { - if (auto result = number->getDouble ()) - return *result; - throw parse_error ("Expect a Double here", value); - } - throw parse_error ("Expect a Number here", value); - } - - void parseFactoryInfo (const JSON::Value& value) - { - enum ParsedBits - { - Vendor = 1 << 0, - URL = 1 << 1, - EMail = 1 << 2, - Flags = 1 << 3, - }; - uint32_t parsed {0}; - if (auto obj = value.asObject ()) - { - for (const auto& el : *obj) - { - auto elementName = el.name ().text (); - if (elementName == "Vendor") - { - if (parsed & ParsedBits::Vendor) - throw parse_error ("Only one 'Vendor' key allowed", el.name ()); - parsed |= ParsedBits::Vendor; - info.factoryInfo.vendor = getText (el.value ()); - } - else if (elementName == "URL") - { - if (parsed & ParsedBits::URL) - throw parse_error ("Only one 'URL' key allowed", el.name ()); - parsed |= ParsedBits::URL; - info.factoryInfo.url = getText (el.value ()); - } - else if (elementName == "E-Mail") - { - if (parsed & ParsedBits::EMail) - throw parse_error ("Only one 'E-Mail' key allowed", el.name ()); - parsed |= ParsedBits::EMail; - info.factoryInfo.email = getText (el.value ()); - } - else if (elementName == "Flags") - { - if (parsed & ParsedBits::Flags) - throw parse_error ("Only one 'Flags' key allowed", el.name ()); - auto flags = el.value ().asObject (); - if (!flags) - throw parse_error ("Expect 'Flags' to be a JSON Object", el.name ()); - for (const auto& flag : *flags) - { - auto flagName = flag.name ().text (); - auto flagValue = flag.value ().asBoolean (); - if (!flagValue) - throw parse_error ("Flag must be a boolean", flag.value ()); - if (flagName == "Classes Discardable") - { - if (*flagValue) - info.factoryInfo.flags |= PFactoryInfo::kClassesDiscardable; - } - else if (flagName == "Component Non Discardable") - { - if (*flagValue) - info.factoryInfo.flags |= PFactoryInfo::kComponentNonDiscardable; - } - else if (flagName == "Unicode") - { - if (*flagValue) - info.factoryInfo.flags |= PFactoryInfo::kUnicode; - } - else - throw parse_error ("Unknown flag", flag.name ()); - } - parsed |= ParsedBits::Flags; - } - } - } - if (!(parsed & ParsedBits::Vendor)) - throw std::logic_error ("Missing 'Vendor' in Factory Info"); - if (!(parsed & ParsedBits::URL)) - throw std::logic_error ("Missing 'URL' in Factory Info"); - if (!(parsed & ParsedBits::EMail)) - throw std::logic_error ("Missing 'EMail' in Factory Info"); - if (!(parsed & ParsedBits::Flags)) - throw std::logic_error ("Missing 'Flags' in Factory Info"); - } - - void parseClasses (const JSON::Value& value) - { - enum ParsedBits - { - CID = 1 << 0, - Category = 1 << 1, - Name = 1 << 2, - Vendor = 1 << 3, - Version = 1 << 4, - SDKVersion = 1 << 5, - SubCategories = 1 << 6, - ClassFlags = 1 << 7, - Snapshots = 1 << 8, - Cardinality = 1 << 9, - }; - - auto array = value.asArray (); - if (!array) - throw parse_error ("Expect Classes Array", value); - for (const auto& classInfoEl : *array) - { - auto classInfo = classInfoEl.value ().asObject (); - if (!classInfo) - throw parse_error ("Expect Class Object", classInfoEl.value ()); - - ModuleInfo::ClassInfo ci {}; - - uint32_t parsed {0}; - - for (const auto& el : *classInfo) - { - auto elementName = el.name ().text (); - if (elementName == "CID") - { - if (parsed & ParsedBits::CID) - throw parse_error ("Only one 'CID' key allowed", el.name ()); - ci.cid = getText (el.value ()); - parsed |= ParsedBits::CID; - } - else if (elementName == "Category") - { - if (parsed & ParsedBits::Category) - throw parse_error ("Only one 'Category' key allowed", el.name ()); - ci.category = getText (el.value ()); - parsed |= ParsedBits::Category; - } - else if (elementName == "Name") - { - if (parsed & ParsedBits::Name) - throw parse_error ("Only one 'Name' key allowed", el.name ()); - ci.name = getText (el.value ()); - parsed |= ParsedBits::Name; - } - else if (elementName == "Vendor") - { - if (parsed & ParsedBits::Vendor) - throw parse_error ("Only one 'Vendor' key allowed", el.name ()); - ci.vendor = getText (el.value ()); - parsed |= ParsedBits::Vendor; - } - else if (elementName == "Version") - { - if (parsed & ParsedBits::Version) - throw parse_error ("Only one 'Version' key allowed", el.name ()); - ci.version = getText (el.value ()); - parsed |= ParsedBits::Version; - } - else if (elementName == "SDKVersion") - { - if (parsed & ParsedBits::SDKVersion) - throw parse_error ("Only one 'SDKVersion' key allowed", el.name ()); - ci.sdkVersion = getText (el.value ()); - parsed |= ParsedBits::SDKVersion; - } - else if (elementName == "Sub Categories") - { - if (parsed & ParsedBits::SubCategories) - throw parse_error ("Only one 'Sub Categories' key allowed", el.name ()); - auto subCatArr = el.value ().asArray (); - if (!subCatArr) - throw parse_error ("Expect Array here", el.value ()); - for (const auto& catEl : *subCatArr) - { - auto cat = getText (catEl.value ()); - ci.subCategories.emplace_back (cat); - } - parsed |= ParsedBits::SubCategories; - } - else if (elementName == "Class Flags") - { - if (parsed & ParsedBits::ClassFlags) - throw parse_error ("Only one 'Class Flags' key allowed", el.name ()); - ci.flags = getInteger (el.value ()); - parsed |= ParsedBits::ClassFlags; - } - else if (elementName == "Cardinality") - { - if (parsed & ParsedBits::Cardinality) - throw parse_error ("Only one 'Cardinality' key allowed", el.name ()); - ci.cardinality = getInteger (el.value ()); - parsed |= ParsedBits::Cardinality; - } - else if (elementName == "Snapshots") - { - if (parsed & ParsedBits::Snapshots) - throw parse_error ("Only one 'Snapshots' key allowed", el.name ()); - auto snapArr = el.value ().asArray (); - if (!snapArr) - throw parse_error ("Expect Array here", el.value ()); - for (const auto& snapEl : *snapArr) - { - auto snap = snapEl.value ().asObject (); - if (!snap) - throw parse_error ("Expect Object here", snapEl.value ()); - ModuleInfo::Snapshot snapshot; - for (const auto& spEl : *snap) - { - auto spElName = spEl.name ().text (); - if (spElName == "Path") - snapshot.path = getText (spEl.value ()); - else if (spElName == "Scale Factor") - snapshot.scaleFactor = getDouble (spEl.value ()); - else - throw parse_error ("Unexpected key", spEl.name ()); - } - if (snapshot.scaleFactor == 0. || snapshot.path.empty ()) - throw parse_error ("Missing Snapshot keys", snapEl.value ()); - ci.snapshots.emplace_back (std::move (snapshot)); - } - parsed |= ParsedBits::Snapshots; - } - else - throw parse_error ("Unexpected key", el.name ()); - } - if (!(parsed & ParsedBits::CID)) - throw parse_error ("'CID' key missing", classInfoEl.value ()); - if (!(parsed & ParsedBits::Category)) - throw parse_error ("'Category' key missing", classInfoEl.value ()); - if (!(parsed & ParsedBits::Name)) - throw parse_error ("'Name' key missing", classInfoEl.value ()); - if (!(parsed & ParsedBits::Vendor)) - throw parse_error ("'Vendor' key missing", classInfoEl.value ()); - if (!(parsed & ParsedBits::Version)) - throw parse_error ("'Version' key missing", classInfoEl.value ()); - if (!(parsed & ParsedBits::SDKVersion)) - throw parse_error ("'SDK Version' key missing", classInfoEl.value ()); - if (!(parsed & ParsedBits::ClassFlags)) - throw parse_error ("'Class Flags' key missing", classInfoEl.value ()); - if (!(parsed & ParsedBits::Cardinality)) - throw parse_error ("'Cardinality' key missing", classInfoEl.value ()); - info.classes.emplace_back (std::move (ci)); - } - } - - void parseCompatibility (const JSON::Value& value) - { - auto arr = value.asArray (); - if (!arr) - throw parse_error ("Expect Array here", value); - for (const auto& el : *arr) - { - auto obj = el.value ().asObject (); - if (!obj) - throw parse_error ("Expect Object here", el.value ()); - - ModuleInfo::Compatibility compat; - for (const auto& objEl : *obj) - { - auto elementName = objEl.name ().text (); - if (elementName == "New") - compat.newCID = getText (objEl.value ()); - else if (elementName == "Old") - { - auto oldElArr = objEl.value ().asArray (); - if (!oldElArr) - throw parse_error ("Expect Array here", objEl.value ()); - for (const auto& old : *oldElArr) - { - compat.oldCID.emplace_back (getText (old.value ())); - } - } - } - if (compat.newCID.empty ()) - throw parse_error ("Expect New CID here", el.value ()); - if (compat.oldCID.empty ()) - throw parse_error ("Expect Old CID here", el.value ()); - info.compatibility.emplace_back (std::move (compat)); - } - } - - void parse (const JSON::Document& doc) - { - auto docObj = doc.asObject (); - if (!docObj) - throw parse_error ("Unexpected", doc); - - enum ParsedBits - { - Name = 1 << 0, - Version = 1 << 1, - FactoryInfo = 1 << 2, - Compatibility = 1 << 3, - Classes = 1 << 4, - }; - - uint32_t parsed {0}; - for (const auto& el : *docObj) - { - auto elementName = el.name ().text (); - if (elementName == "Name") - { - if (parsed & ParsedBits::Name) - throw parse_error ("Only one 'Name' key allowed", el.name ()); - parsed |= ParsedBits::Name; - info.name = getText (el.value ()); - } - else if (elementName == "Version") - { - if (parsed & ParsedBits::Version) - throw parse_error ("Only one 'Version' key allowed", el.name ()); - parsed |= ParsedBits::Version; - info.version = getText (el.value ()); - } - else if (elementName == "Factory Info") - { - if (parsed & ParsedBits::FactoryInfo) - throw parse_error ("Only one 'Factory Info' key allowed", el.name ()); - parseFactoryInfo (el.value ()); - parsed |= ParsedBits::FactoryInfo; - } - else if (elementName == "Compatibility") - { - if (parsed & ParsedBits::Compatibility) - throw parse_error ("Only one 'Compatibility' key allowed", el.name ()); - parseCompatibility (el.value ()); - parsed |= ParsedBits::Compatibility; - } - else if (elementName == "Classes") - { - if (parsed & ParsedBits::Classes) - throw parse_error ("Only one 'Classes' key allowed", el.name ()); - parseClasses (el.value ()); - parsed |= ParsedBits::Classes; - } - else - { - throw parse_error ("Unexpected JSON Token", el.name ()); - } - } - if (!(parsed & ParsedBits::Name)) - throw std::logic_error ("'Name' key missing"); - if (!(parsed & ParsedBits::Version)) - throw std::logic_error ("'Version' key missing"); - if (!(parsed & ParsedBits::FactoryInfo)) - throw std::logic_error ("'Factory Info' key missing"); - if (!(parsed & ParsedBits::Classes)) - throw std::logic_error ("'Classes' key missing"); - } - - ModuleInfo&& takeInfo () { return std::move (info); } - -private: - ModuleInfo info; -}; - -//------------------------------------------------------------------------ -} // anonymous - -//------------------------------------------------------------------------ -std::optional parseJson (std::string_view jsonData, std::ostream* optErrorOutput) -{ - auto docVar = JSON::Document::parse (jsonData); - if (auto res = std::get_if (&docVar)) - { - if (optErrorOutput) - printJsonParseError (*res, *optErrorOutput); - return {}; - } - auto doc = std::get_if (&docVar); - assert (doc); - try - { - ModuleInfoJsonParser parser; - parser.parse (*doc); - return parser.takeInfo (); - } - catch (std::exception& error) - { - if (optErrorOutput) - *optErrorOutput << error.what () << '\n'; - return {}; - } - // unreachable -} - -//------------------------------------------------------------------------ -std::optional parseCompatibilityJson (std::string_view jsonData, - std::ostream* optErrorOutput) -{ - auto docVar = JSON::Document::parse (jsonData); - if (auto res = std::get_if (&docVar)) - { - if (optErrorOutput) - printJsonParseError (*res, *optErrorOutput); - return {}; - } - auto doc = std::get_if (&docVar); - assert (doc); - try - { - ModuleInfoJsonParser parser; - parser.parseCompatibility (*doc); - return parser.takeInfo ().compatibility; - } - catch (std::exception& error) - { - if (optErrorOutput) - *optErrorOutput << error.what () << '\n'; - return {}; - } - // unreachable -} - -//------------------------------------------------------------------------ -} // Steinberg::ModuelInfoLib diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.h deleted file mode 100644 index 9b426da3..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.h +++ /dev/null @@ -1,68 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// Flags : clang-format SMTGSequencer -// -// Category : moduleinfo -// Filename : public.sdk/source/vst/moduleinfo/moduleinfoparser.h -// Created by : Steinberg, 01/2022 -// Description : utility functions to parse moduleinfo json files -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -#pragma once - -#include "moduleinfo.h" -#include -#include -#include - -//------------------------------------------------------------------------ -namespace Steinberg::ModuleInfoLib { - -//------------------------------------------------------------------------ -/** parse a json formatted string to a ModuleInfo struct - * - * @param jsonData a string view to a json formatted string - * @param optErrorOutput optional error output stream where to print parse error - * @return ModuleInfo if parsing succeeded - */ -std::optional parseJson (std::string_view jsonData, std::ostream* optErrorOutput); - -//------------------------------------------------------------------------ -/** parse a json formatted string to a ModuleInfo::CompatibilityList - * - * @param jsonData a string view to a json formatted string - * @param optErrorOutput optional error output stream where to print parse error - * @return ModuleInfo::CompatibilityList if parsing succeeded - */ -std::optional parseCompatibilityJson (std::string_view jsonData, - std::ostream* optErrorOutput); - -//------------------------------------------------------------------------ -} // Steinberg::ModuelInfoLib diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/optional.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/optional.h deleted file mode 100644 index 7ea6a3c3..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/optional.h +++ /dev/null @@ -1,135 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// -// Category : Helpers -// Filename : public.sdk/source/vst/utility/optional.h -// Created by : Steinberg, 08/2016 -// Description : optional helper -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -#pragma once - -#include -#include -#include - -//------------------------------------------------------------------------ -namespace VST3 { - -//------------------------------------------------------------------------ -template -struct Optional -{ - Optional () noexcept : valid (false) {} - explicit Optional (const T& v) noexcept : _value (v), valid (true) {} - Optional (T&& v) noexcept : _value (std::move (v)), valid (true) {} - - Optional (Optional&& other) noexcept { *this = std::move (other); } - Optional& operator= (Optional&& other) noexcept - { - valid = other.valid; - _value = std::move (other._value); - return *this; - } - - explicit operator bool () const noexcept - { - setValidationChecked (); - return valid; - } - - const T& operator* () const noexcept - { - checkValid (); - return _value; - } - - const T* operator-> () const noexcept - { - checkValid (); - return &_value; - } - - T& operator* () noexcept - { - checkValid (); - return _value; - } - - T* operator-> () noexcept - { - checkValid (); - return &_value; - } - - T&& value () noexcept - { - checkValid (); - return move (_value); - } - - const T& value () const noexcept - { - checkValid (); - return _value; - } - - void swap (T& other) noexcept - { - checkValid (); - auto tmp = std::move (other); - other = std::move (_value); - _value = std::move (tmp); - } - -private: - T _value {}; - bool valid; - -#if !defined(NDEBUG) - mutable bool validationChecked {false}; -#endif - - void setValidationChecked () const - { -#if !defined(NDEBUG) - validationChecked = true; -#endif - } - void checkValid () const - { -#if !defined(NDEBUG) - assert (validationChecked); -#endif - } -}; - -//------------------------------------------------------------------------ -} diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp deleted file mode 100644 index 6bc7f3aa..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp +++ /dev/null @@ -1,148 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// -// Category : Helpers -// Filename : public.sdk/source/vst/utility/stringconvert.cpp -// Created by : Steinberg, 11/2014 -// Description : c++11 unicode string convert functions -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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 "stringconvert.h" -#include -#include -#include - -//------------------------------------------------------------------------ -namespace VST3 { -namespace StringConvert { - -//------------------------------------------------------------------------ -namespace { - -#if defined(_MSC_VER) && _MSC_VER >= 1900 -#define USE_WCHAR_AS_UTF16TYPE -using UTF16Type = wchar_t; -#else -using UTF16Type = char16_t; -#endif - -using Converter = std::wstring_convert, UTF16Type>; - -//------------------------------------------------------------------------ -Converter& converter () -{ - static Converter conv; - return conv; -} - -//------------------------------------------------------------------------ -} // anonymous - -//------------------------------------------------------------------------ -std::u16string convert (const std::string& utf8Str) -{ -#if defined(USE_WCHAR_AS_UTF16TYPE) - auto wstr = converter ().from_bytes (utf8Str); - return {wstr.data (), wstr.data () + wstr.size ()}; -#else - return converter ().from_bytes (utf8Str); -#endif -} - -//------------------------------------------------------------------------ -bool convert (const std::string& utf8Str, Steinberg::Vst::String128 str) -{ - return convert (utf8Str, str, 128); -} - -//------------------------------------------------------------------------ -bool convert (const std::string& utf8Str, Steinberg::Vst::TChar* str, uint32_t maxCharacters) -{ - auto ucs2 = convert (utf8Str); - if (ucs2.length () < maxCharacters) - { - ucs2.copy (reinterpret_cast (str), ucs2.length ()); - str[ucs2.length ()] = 0; - return true; - } - return false; -} - -//------------------------------------------------------------------------ -std::string convert (const Steinberg::Vst::TChar* str) -{ - return converter ().to_bytes (reinterpret_cast (str)); -} - -//------------------------------------------------------------------------ -std::string convert (const Steinberg::Vst::TChar* str, uint32_t max) -{ - std::string result; - if (str) - { - Steinberg::Vst::TChar tmp[2] {}; - for (uint32_t i = 0; i < max; ++i, ++str) - { - tmp[0] = *str; - if (tmp[0] == 0) - break; - result += convert (tmp); - } - } - return result; -} - -//------------------------------------------------------------------------ -std::string convert (const std::u16string& str) -{ - return converter ().to_bytes (reinterpret_cast (str.data ()), - reinterpret_cast (str.data () + str.size ())); -} - -//------------------------------------------------------------------------ -std::string convert (const char* str, uint32_t max) -{ - std::string result; - if (str) - { - result.reserve (max); - for (uint32_t i = 0; i < max; ++i, ++str) - { - if (*str == 0) - break; - result += *str; - } - } - return result; -} - -//------------------------------------------------------------------------ -} // String -} // VST3 diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h deleted file mode 100644 index d2d4678a..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h +++ /dev/null @@ -1,147 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// -// Category : Helpers -// Filename : public.sdk/source/vst/utility/stringconvert.h -// Created by : Steinberg, 11/2014 -// Description : c++11 unicode string convert functions -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -#pragma once - -#include "pluginterfaces/vst/vsttypes.h" -#include - -//------------------------------------------------------------------------ -namespace VST3 { -namespace StringConvert { - -//------------------------------------------------------------------------ -/** - * convert an UTF-8 string to an UTF-16 string - * - * @param utf8Str UTF-8 string - * - * @return UTF-16 string - */ -extern std::u16string convert (const std::string& utf8Str); - -//------------------------------------------------------------------------ -/** - * convert an UTF-8 string to an UTF-16 string buffer with max 127 characters - * - * @param utf8Str UTF-8 string - * @param str UTF-16 string buffer - * - * @return true on success - */ -extern bool convert (const std::string& utf8Str, Steinberg::Vst::String128 str); - -//------------------------------------------------------------------------ -/** - * convert an UTF-8 string to an UTF-16 string buffer - * - * @param utf8Str UTF-8 string - * @param str UTF-16 string buffer - * @param maxCharacters max characters that fit into str - * - * @return true on success - */ -extern bool convert (const std::string& utf8Str, Steinberg::Vst::TChar* str, - uint32_t maxCharacters); - -//------------------------------------------------------------------------ -/** - * convert an UTF-16 string buffer to an UTF-8 string - * - * @param str UTF-16 string buffer - * - * @return UTF-8 string - */ -extern std::string convert (const Steinberg::Vst::TChar* str); - -//------------------------------------------------------------------------ -/** - * convert an UTF-16 string buffer to an UTF-8 string - * - * @param str UTF-16 string buffer - * @param max maximum characters in str - * - * @return UTF-8 string - */ -extern std::string convert (const Steinberg::Vst::TChar* str, uint32_t max); - -//------------------------------------------------------------------------ -/** - * convert an UTF-16 string to an UTF-8 string - * - * @param str UTF-16 string - * - * @return UTF-8 string - */ -extern std::string convert (const std::u16string& str); - -//------------------------------------------------------------------------ -/** - * convert a ASCII string buffer to an UTF-8 string - * - * @param str ASCII string buffer - * @param max maximum characters in str - * - * @return UTF-8 string - */ -extern std::string convert (const char* str, uint32_t max); - -//------------------------------------------------------------------------ -} // StringConvert - -//------------------------------------------------------------------------ -inline const Steinberg::Vst::TChar* toTChar (const std::u16string& str) -{ - return reinterpret_cast (str.data ()); -} - -//------------------------------------------------------------------------ -/** - * convert an number to an UTF-16 string - * - * @param value number - * - * @return UTF-16 string - */ -template -std::u16string toString (NumberT value) -{ - auto u8str = std::to_string (value); - return StringConvert::convert (u8str); -} - -//------------------------------------------------------------------------ -} // VST3 diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h deleted file mode 100644 index 2f8ed8b3..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h +++ /dev/null @@ -1,294 +0,0 @@ -//----------------------------------------------------------------------------- -// Project : VST SDK -// -// Category : Helpers -// Filename : public.sdk/source/vst/utility/uid.h -// Created by : Steinberg, 08/2016 -// Description : UID -// -//----------------------------------------------------------------------------- -// LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved -//----------------------------------------------------------------------------- -// 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 Steinberg Media Technologies 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 COPYRIGHT OWNER 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. -//----------------------------------------------------------------------------- - -#pragma once - -#include "optional.h" -#include "pluginterfaces/base/funknown.h" -#include - -//------------------------------------------------------------------------ -namespace VST3 { - -//------------------------------------------------------------------------ -struct UID -{ -#if defined(SMTG_OS_WINDOWS) && SMTG_OS_WINDOWS == 1 - static constexpr bool defaultComFormat = true; -#else - static constexpr bool defaultComFormat = false; -#endif - - using TUID = Steinberg::TUID; - - constexpr UID () noexcept = default; - UID (uint32_t l1, uint32_t l2, uint32_t l3, uint32_t l4, bool comFormat = defaultComFormat) - noexcept; - UID (const TUID& uid) noexcept; - UID (const UID& uid) noexcept; - UID& operator= (const UID& uid) noexcept; - UID& operator= (const TUID& uid) noexcept; - - constexpr const TUID& data () const noexcept; - constexpr size_t size () const noexcept; - - std::string toString (bool comFormat = defaultComFormat) const noexcept; - - template - static Optional fromString (const StringT& str, - bool comFormat = defaultComFormat) noexcept; - - static UID fromTUID (const TUID _uid) noexcept; -//------------------------------------------------------------------------ -private: - Steinberg::TUID _data {}; - - struct GUID - { - uint32_t Data1; - uint16_t Data2; - uint16_t Data3; - uint8_t Data4[8]; - }; -}; - -//------------------------------------------------------------------------ -inline bool operator== (const UID& uid1, const UID& uid2) -{ - const uint64_t* p1 = reinterpret_cast (uid1.data ()); - const uint64_t* p2 = reinterpret_cast (uid2.data ()); - return p1[0] == p2[0] && p1[1] == p2[1]; -} - -//------------------------------------------------------------------------ -inline bool operator!= (const UID& uid1, const UID& uid2) -{ - return !(uid1 == uid2); -} - -//------------------------------------------------------------------------ -inline bool operator< (const UID& uid1, const UID& uid2) -{ - const uint64_t* p1 = reinterpret_cast (uid1.data ()); - const uint64_t* p2 = reinterpret_cast (uid2.data ()); - return (p1[0] < p2[0]) && (p1[1] < p2[1]); -} - -//------------------------------------------------------------------------ -inline UID::UID (uint32_t l1, uint32_t l2, uint32_t l3, uint32_t l4, bool comFormat) noexcept -{ - if (comFormat) - { - _data[0] = static_cast ((l1 & 0x000000FF)); - _data[1] = static_cast ((l1 & 0x0000FF00) >> 8); - _data[2] = static_cast ((l1 & 0x00FF0000) >> 16); - _data[3] = static_cast ((l1 & 0xFF000000) >> 24); - _data[4] = static_cast ((l2 & 0x00FF0000) >> 16); - _data[5] = static_cast ((l2 & 0xFF000000) >> 24); - _data[6] = static_cast ((l2 & 0x000000FF)); - _data[7] = static_cast ((l2 & 0x0000FF00) >> 8); - _data[8] = static_cast ((l3 & 0xFF000000) >> 24); - _data[9] = static_cast ((l3 & 0x00FF0000) >> 16); - _data[10] = static_cast ((l3 & 0x0000FF00) >> 8); - _data[11] = static_cast ((l3 & 0x000000FF)); - _data[12] = static_cast ((l4 & 0xFF000000) >> 24); - _data[13] = static_cast ((l4 & 0x00FF0000) >> 16); - _data[14] = static_cast ((l4 & 0x0000FF00) >> 8); - _data[15] = static_cast ((l4 & 0x000000FF)); - } - else - { - _data[0] = static_cast ((l1 & 0xFF000000) >> 24); - _data[1] = static_cast ((l1 & 0x00FF0000) >> 16); - _data[2] = static_cast ((l1 & 0x0000FF00) >> 8); - _data[3] = static_cast ((l1 & 0x000000FF)); - _data[4] = static_cast ((l2 & 0xFF000000) >> 24); - _data[5] = static_cast ((l2 & 0x00FF0000) >> 16); - _data[6] = static_cast ((l2 & 0x0000FF00) >> 8); - _data[7] = static_cast ((l2 & 0x000000FF)); - _data[8] = static_cast ((l3 & 0xFF000000) >> 24); - _data[9] = static_cast ((l3 & 0x00FF0000) >> 16); - _data[10] = static_cast ((l3 & 0x0000FF00) >> 8); - _data[11] = static_cast ((l3 & 0x000000FF)); - _data[12] = static_cast ((l4 & 0xFF000000) >> 24); - _data[13] = static_cast ((l4 & 0x00FF0000) >> 16); - _data[14] = static_cast ((l4 & 0x0000FF00) >> 8); - _data[15] = static_cast ((l4 & 0x000000FF)); - } -} - -//------------------------------------------------------------------------ -inline UID::UID (const TUID& uid) noexcept -{ - *this = uid; -} - -//------------------------------------------------------------------------ -inline UID::UID (const UID& uid) noexcept -{ - *this = uid; -} - -//------------------------------------------------------------------------ -inline UID& UID::operator= (const UID& uid) noexcept -{ - *this = uid.data (); - return *this; -} - -//------------------------------------------------------------------------ -inline UID& UID::operator= (const TUID& uid) noexcept -{ - uint64_t* p1 = reinterpret_cast (_data); - const uint64_t* p2 = reinterpret_cast (uid); - p1[0] = p2[0]; - p1[1] = p2[1]; - return *this; -} - -//------------------------------------------------------------------------ -inline constexpr auto UID::data () const noexcept -> const TUID& -{ - return _data; -} - -//------------------------------------------------------------------------ -inline constexpr size_t UID::size () const noexcept -{ - return sizeof (TUID); -} - -//------------------------------------------------------------------------ -inline std::string UID::toString (bool comFormat) const noexcept -{ - std::string result; - result.reserve (32); - if (comFormat) - { - const auto& g = reinterpret_cast (_data); - - char tmp[21] {}; - snprintf (tmp, 21, "%08X%04X%04X", g->Data1, g->Data2, g->Data3); - result = tmp; - - for (uint32_t i = 0; i < 8; ++i) - { - char s[3] {}; - snprintf (s, 3, "%02X", g->Data4[i]); - result += s; - } - } - else - { - for (uint32_t i = 0; i < 16; ++i) - { - char s[3] {}; - snprintf (s, 3, "%02X", static_cast (_data[i])); - result += s; - } - } - return result; -} - -//------------------------------------------------------------------------ -template -inline Optional UID::fromString (const StringT& str, bool comFormat) noexcept -{ - if (str.length () != 32) - return {}; - // TODO: this is a copy from FUID. there are no input validation checks !!! - if (comFormat) - { - TUID uid {}; - GUID g; - char s[33]; - - strcpy (s, str.data ()); - s[8] = 0; - sscanf (s, "%x", &g.Data1); - strcpy (s, str.data () + 8); - s[4] = 0; - sscanf (s, "%hx", &g.Data2); - strcpy (s, str.data () + 12); - s[4] = 0; - sscanf (s, "%hx", &g.Data3); - - memcpy (uid, &g, 8); - - for (uint32_t i = 8; i < 16; ++i) - { - char s2[3] {}; - s2[0] = str[i * 2]; - s2[1] = str[i * 2 + 1]; - - int32_t d = 0; - sscanf (s2, "%2x", &d); - uid[i] = static_cast (d); - } - return {uid}; - } - else - { - TUID uid {}; - for (uint32_t i = 0; i < 16; ++i) - { - char s[3] {}; - s[0] = str[i * 2]; - s[1] = str[i * 2 + 1]; - - int32_t d = 0; - sscanf (s, "%2x", &d); - uid[i] = static_cast (d); - } - return {uid}; - } -} - -//------------------------------------------------------------------------ -inline UID UID::fromTUID (const TUID _uid) noexcept -{ - UID result; - - uint64_t* p1 = reinterpret_cast (result._data); - const uint64_t* p2 = reinterpret_cast (_uid); - p1[0] = p2[0]; - p1[1] = p2[1]; - - return result; -} - -//------------------------------------------------------------------------ -} // VST3 diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp index 8f6ff44f..2615b875 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -50,8 +50,7 @@ Bus::Bus (const TChar* _name, BusType _busType, int32 _flags) //------------------------------------------------------------------------ bool Bus::getInfo (BusInfo& info) { - memset (info.name, 0, sizeof (String128)); - name.copy (info.name, 128); + name.copyTo16 (info.name, 0, str16BufferSize (info.name) - 1); info.busType = busType; info.flags = flags; return true; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h index f1e7ad4b..c3c3c217 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -37,13 +37,10 @@ #pragma once #include "base/source/fobject.h" +#include "base/source/fstring.h" #include "pluginterfaces/vst/ivstcomponent.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" -#include -#if SMTG_CPP_17 -#include -#endif #include //------------------------------------------------------------------------ @@ -67,13 +64,8 @@ class Bus: public FObject /** Activates the bus. */ void setActive (TBool state) { active = state; } -#if SMTG_CPP_17 /** Sets a new name for this bus. */ - void setName (std::u16string_view newName) { name = newName; } -#else - /** Sets a new name for this bus. */ - void setName (const std::u16string& newName) { name = newName; } -#endif + void setName (String newName) { name = newName; } /** Sets a new busType for this bus. */ void setBusType (BusType newBusType) { busType = newBusType; } @@ -87,7 +79,7 @@ class Bus: public FObject OBJ_METHODS (Vst::Bus, FObject) //------------------------------------------------------------------------ protected: - std::u16string name; ///< name + String name; ///< name BusType busType; ///< kMain or kAux, see \ref BusTypes int32 flags; ///< flags, see \ref BusInfo::BusFlags TBool active; ///< activation state diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp index 57e03d08..0c46feb3 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -70,7 +70,7 @@ BusList* Component::getBusList (MediaType type, BusDirection dir) { if (type == kAudio) return dir == kInput ? &audioInputs : &audioOutputs; - if (type == kEvent) + else if (type == kEvent) return dir == kInput ? &eventInputs : &eventOutputs; return nullptr; } diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.h index a1009d48..7994534a 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.h @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -59,7 +59,6 @@ class Component : public ComponentBase, public IComponent //---Internal Methods------- /** Sets the controller Class ID associated to its component. */ void setControllerClass (const FUID& cid) { controllerClass = cid; } - void setControllerClass (const TUID& cid) { controllerClass = FUID::fromTUID (cid); } /** Removes all Audio Busses. */ tresult removeAudioBusses (); diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponentbase.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponentbase.cpp index c82ac39c..4d2443c9 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponentbase.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponentbase.cpp @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -159,7 +159,7 @@ tresult ComponentBase::sendTextMessage (const char8* text) const } //------------------------------------------------------------------------ -tresult ComponentBase::sendMessageID (const char8* messageID) const +tresult ComponentBase::sendMessageID (const char* messageID) const { if (auto msg = owned (allocateMessage ())) { diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponentbase.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponentbase.h index e71b857b..64474bdd 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponentbase.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponentbase.h @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vsteditcontroller.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vsteditcontroller.cpp index 4342c7b2..a88483c0 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vsteditcontroller.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vsteditcontroller.cpp @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -48,7 +48,7 @@ KnobMode EditController::hostKnobMode = kCircularMode; //------------------------------------------------------------------------ // EditController Implementation //------------------------------------------------------------------------ -EditController::EditController () +EditController::EditController () : componentHandler (nullptr), componentHandler2 (nullptr) { } @@ -63,8 +63,17 @@ tresult PLUGIN_API EditController::terminate () { parameters.removeAll (); - componentHandler.reset (); - componentHandler2.reset (); + if (componentHandler) + { + componentHandler->release (); + componentHandler = nullptr; + } + + if (componentHandler2) + { + componentHandler2->release (); + componentHandler2 = nullptr; + } return ComponentBase::terminate (); } @@ -180,11 +189,25 @@ tresult PLUGIN_API EditController::setComponentHandler (IComponentHandler* newHa return kResultTrue; } + if (componentHandler) + { + componentHandler->release (); + } + componentHandler = newHandler; - componentHandler2.reset (); + if (componentHandler) + { + componentHandler->addRef (); + } - // try to get the extended version - if (newHandler) + // try to get the extended version + if (componentHandler2) + { + componentHandler2->release (); + componentHandler2 = nullptr; + } + + if (newHandler) { newHandler->queryInterface (IComponentHandler2::iid, (void**)&componentHandler2); } @@ -279,6 +302,10 @@ tresult EditController::requestOpenEditor (FIDString name) EditorView::EditorView (EditController* _controller, ViewRect* size) : CPluginView (size), controller (_controller) { + if (controller) + { + controller->addRef (); + } } //------------------------------------------------------------------------ @@ -287,7 +314,7 @@ EditorView::~EditorView () if (controller) { controller->editorDestroyed (this); - controller = nullptr; + controller->release (); } } @@ -313,7 +340,7 @@ void EditorView::removedFromParent () //------------------------------------------------------------------------ // EditControllerEx1 implementation //------------------------------------------------------------------------ -EditControllerEx1::EditControllerEx1 () +EditControllerEx1::EditControllerEx1 () : selectedUnit (kRootUnitId) { UpdateHandler::instance (); } @@ -349,8 +376,6 @@ bool EditControllerEx1::addUnit (Unit* unit) //------------------------------------------------------------------------ tresult PLUGIN_API EditControllerEx1::getUnitInfo (int32 unitIndex, UnitInfo& info /*out*/) { - if (unitIndex < 0 || unitIndex >= static_cast (units.size ())) - return kResultFalse; if (Unit* unit = units.at (unitIndex)) { info = unit->getInfo (); @@ -561,10 +586,9 @@ tresult ProgramList::getProgramInfo (int32 programIndex, CString attributeId, StringMap::const_iterator it = programInfos[programIndex].find (attributeId); if (it != programInfos[programIndex].end ()) { - if (!it->second.empty ()) + if (!it->second.isEmpty ()) { - memset (value, 0, sizeof (String128)); - it->second.copy (value, 128); + it->second.copyTo16 (value, 0, 128); return kResultTrue; } } @@ -577,8 +601,7 @@ tresult ProgramList::getProgramName (int32 programIndex, String128 name /*out*/) { if (programIndex >= 0 && programIndex < static_cast (programNames.size ())) { - memset (name, 0, sizeof (String128)); - programNames.at (programIndex).copy (name, 128); + programNames.at (programIndex).copyTo16 (name, 0, 128); return kResultTrue; } return kResultFalse; @@ -610,7 +633,7 @@ Parameter* ProgramList::getParameter () unitId); for (const auto& programName : programNames) { - listParameter->appendString (programName.data ()); + listParameter->appendString (programName); } parameter = listParameter; } @@ -688,8 +711,7 @@ tresult ProgramListWithPitchNames::getPitchName (int32 programIndex, int16 midiP PitchNameMap::const_iterator it = pitchNames[programIndex].find (midiPitch); if (it != pitchNames[programIndex].end ()) { - memset (name, 0, sizeof (String128)); - it->second.copy (name, 128); + it->second.copyTo16 (name, 0, 128); return kResultTrue; } } diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vsteditcontroller.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vsteditcontroller.h index 9fcb0e69..d6727842 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vsteditcontroller.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vsteditcontroller.h @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -39,13 +39,13 @@ #include "public.sdk/source/vst/vstcomponentbase.h" #include "public.sdk/source/vst/vstparameters.h" #include "public.sdk/source/common/pluginview.h" +#include "base/source/fstring.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstunits.h" -#include -#include #include +#include //------------------------------------------------------------------------ namespace Steinberg { @@ -125,8 +125,8 @@ class EditController : public ComponentBase, public IEditController, public IEdi REFCOUNT_METHODS (ComponentBase) //------------------------------------------------------------------------ protected: - IPtr componentHandler; - IPtr componentHandler2; + IComponentHandler* componentHandler; + IComponentHandler2* componentHandler2; ParameterContainer parameters; @@ -153,7 +153,7 @@ class EditorView : public CPluginView //------------------------------------------------------------------------ protected: - IPtr controller; + EditController* controller; }; //------------------------------------------------------------------------ @@ -241,8 +241,8 @@ class ProgramList : public FObject OBJ_METHODS (ProgramList, FObject) //------------------------------------------------------------------------ protected: - using StringMap = std::map; - using StringVector = std::vector; + using StringMap = std::map; + using StringVector = std::vector; using ProgramInfoVector = std::vector; ProgramListInfo info; UnitID unitId; @@ -275,7 +275,7 @@ class ProgramListWithPitchNames : public ProgramList OBJ_METHODS (ProgramListWithPitchNames, ProgramList) protected: - using PitchNameMap = std::map; + using PitchNameMap = std::map; using PitchNamesVector = std::vector; PitchNamesVector pitchNames; }; @@ -366,7 +366,7 @@ class EditControllerEx1 : public EditController, public IUnitInfo UnitVector units; ProgramListVector programLists; ProgramIndexMap programIndexMap; - UnitID selectedUnit {kRootUnitId}; + UnitID selectedUnit; }; //------------------------------------------------------------------------ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstinitiids.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstinitiids.cpp index c5ad8b08..aa065471 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstinitiids.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstinitiids.cpp @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -36,7 +36,6 @@ #include "pluginterfaces/base/funknown.h" -#include "pluginterfaces/base/iplugincompatibility.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivstautomationstate.h" #include "pluginterfaces/vst/ivstchannelcontextinfo.h" @@ -59,87 +58,86 @@ //------------------------------------------------------------------------ namespace Steinberg { +namespace Vst { //----VST 3.0-------------------------------- -DEF_CLASS_IID (Vst::IComponent) -DEF_CLASS_IID (Vst::IAudioProcessor) -DEF_CLASS_IID (Vst::IUnitData) -DEF_CLASS_IID (Vst::IProgramListData) +DEF_CLASS_IID (IComponent) +DEF_CLASS_IID (IAudioProcessor) +DEF_CLASS_IID (IUnitData) +DEF_CLASS_IID (IProgramListData) -DEF_CLASS_IID (Vst::IEditController) -DEF_CLASS_IID (Vst::IUnitInfo) +DEF_CLASS_IID (IEditController) +DEF_CLASS_IID (IUnitInfo) -DEF_CLASS_IID (Vst::IConnectionPoint) +DEF_CLASS_IID (IConnectionPoint) -DEF_CLASS_IID (Vst::IComponentHandler) -DEF_CLASS_IID (Vst::IUnitHandler) +DEF_CLASS_IID (IComponentHandler) +DEF_CLASS_IID (IUnitHandler) -DEF_CLASS_IID (Vst::IParamValueQueue) -DEF_CLASS_IID (Vst::IParameterChanges) +DEF_CLASS_IID (IParamValueQueue) +DEF_CLASS_IID (IParameterChanges) -DEF_CLASS_IID (Vst::IEventList) -DEF_CLASS_IID (Vst::IMessage) +DEF_CLASS_IID (IEventList) +DEF_CLASS_IID (IMessage) -DEF_CLASS_IID (Vst::IHostApplication) -DEF_CLASS_IID (Vst::IAttributeList) +DEF_CLASS_IID (IHostApplication) +DEF_CLASS_IID (IAttributeList) //----VST 3.0.1-------------------------------- -DEF_CLASS_IID (Vst::IMidiMapping) +DEF_CLASS_IID (IMidiMapping) //----VST 3.0.2-------------------------------- -DEF_CLASS_IID (Vst::IParameterFinder) +DEF_CLASS_IID (IParameterFinder) //----VST 3.1---------------------------------- -DEF_CLASS_IID (Vst::IComponentHandler2) -DEF_CLASS_IID (Vst::IEditController2) -DEF_CLASS_IID (Vst::IAudioPresentationLatency) -DEF_CLASS_IID (Vst::IVst3ToVst2Wrapper) -DEF_CLASS_IID (Vst::IVst3ToAUWrapper) +DEF_CLASS_IID (IComponentHandler2) +DEF_CLASS_IID (IEditController2) +DEF_CLASS_IID (IAudioPresentationLatency) +DEF_CLASS_IID (IVst3ToVst2Wrapper) +DEF_CLASS_IID (IVst3ToAUWrapper) //----VST 3.5---------------------------------- -DEF_CLASS_IID (Vst::INoteExpressionController) -DEF_CLASS_IID (Vst::IKeyswitchController) -DEF_CLASS_IID (Vst::IContextMenuTarget) -DEF_CLASS_IID (Vst::IContextMenu) -DEF_CLASS_IID (Vst::IComponentHandler3) -DEF_CLASS_IID (Vst::IEditControllerHostEditing) -DEF_CLASS_IID (Vst::IXmlRepresentationController) +DEF_CLASS_IID (INoteExpressionController) +DEF_CLASS_IID (IKeyswitchController) +DEF_CLASS_IID (IContextMenuTarget) +DEF_CLASS_IID (IContextMenu) +DEF_CLASS_IID (IComponentHandler3) +DEF_CLASS_IID (IEditControllerHostEditing) +DEF_CLASS_IID (IXmlRepresentationController) //----VST 3.6---------------------------------- -DEF_CLASS_IID (Vst::IInterAppAudioHost) -DEF_CLASS_IID (Vst::IInterAppAudioConnectionNotification) -DEF_CLASS_IID (Vst::IInterAppAudioPresetManager) -DEF_CLASS_IID (Vst::IStreamAttributes) +DEF_CLASS_IID (IInterAppAudioHost) +DEF_CLASS_IID (IInterAppAudioConnectionNotification) +DEF_CLASS_IID (IInterAppAudioPresetManager) +DEF_CLASS_IID (IStreamAttributes) //----VST 3.6.5-------------------------------- -DEF_CLASS_IID (Vst::ChannelContext::IInfoListener) -DEF_CLASS_IID (Vst::IPrefetchableSupport) -DEF_CLASS_IID (Vst::IUnitHandler2) -DEF_CLASS_IID (Vst::IAutomationState) +DEF_CLASS_IID (ChannelContext::IInfoListener) +DEF_CLASS_IID (IPrefetchableSupport) +DEF_CLASS_IID (IUnitHandler2) +DEF_CLASS_IID (IAutomationState) //----VST 3.6.8-------------------------------- -DEF_CLASS_IID (Vst::IComponentHandlerBusActivation) -DEF_CLASS_IID (Vst::IVst3ToAAXWrapper) +DEF_CLASS_IID (IComponentHandlerBusActivation) +DEF_CLASS_IID (IVst3ToAAXWrapper) //----VST 3.6.11-------------------------------- -DEF_CLASS_IID (Vst::INoteExpressionPhysicalUIMapping) +DEF_CLASS_IID (INoteExpressionPhysicalUIMapping) //----VST 3.6.12-------------------------------- -DEF_CLASS_IID (Vst::IMidiLearn) -DEF_CLASS_IID (Vst::IPlugInterfaceSupport) -DEF_CLASS_IID (Vst::IVst3WrapperMPESupport) +DEF_CLASS_IID (IMidiLearn) +DEF_CLASS_IID (IPlugInterfaceSupport) +DEF_CLASS_IID (IVst3WrapperMPESupport) //----VST 3.6.13-------------------------------- -DEF_CLASS_IID (Vst::ITestPlugProvider) +DEF_CLASS_IID (ITestPlugProvider) //----VST 3.7----------------------------------- -DEF_CLASS_IID (Vst::IParameterFunctionName) -DEF_CLASS_IID (Vst::IProcessContextRequirements) -DEF_CLASS_IID (Vst::IProgress) -DEF_CLASS_IID (Vst::ITestPlugProvider2) - - -//----VST 3.7.5--------------------------------- -DEF_CLASS_IID (IPluginCompatibility) +DEF_CLASS_IID (IParameterFunctionName) +DEF_CLASS_IID (IProcessContextRequirements) +DEF_CLASS_IID (IProgress) +DEF_CLASS_IID (ITestPlugProvider2) +//------------------------------------------------------------------------ +} // Vst } // Steinberg diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstparameters.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstparameters.cpp index dd083516..400f6e35 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstparameters.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstparameters.cpp @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -45,13 +45,14 @@ namespace Vst { //------------------------------------------------------------------------ // Parameter Implementation //------------------------------------------------------------------------ -Parameter::Parameter () +Parameter::Parameter () : valueNormalized (0.), precision (4) { + info = {}; } //------------------------------------------------------------------------ Parameter::Parameter (const ParameterInfo& info) -: info (info), valueNormalized (info.defaultNormalizedValue) +: info (info), valueNormalized (info.defaultNormalizedValue), precision (4) { } @@ -59,7 +60,10 @@ Parameter::Parameter (const ParameterInfo& info) Parameter::Parameter (const TChar* title, ParamID tag, const TChar* units, ParamValue defaultValueNormalized, int32 stepCount, int32 flags, UnitID unitID, const TChar* shortTitle) +: precision (4) { + info = {}; + UString (info.title, str16BufferSize (String128)).assign (title); if (units) UString (info.units, str16BufferSize (String128)).assign (units); @@ -298,7 +302,7 @@ bool StringListParameter::replaceString (int32 index, const String128 string) //------------------------------------------------------------------------ void StringListParameter::toString (ParamValue _valueNormalized, String128 string) const { - int32 index = static_cast (toPlain (_valueNormalized)); + int32 index = (int32)toPlain (_valueNormalized); if (const TChar* valueString = strings.at (index)) { UString (string, str16BufferSize (String128)).assign (valueString); @@ -341,7 +345,7 @@ ParamValue StringListParameter::toNormalized (ParamValue plainValue) const //------------------------------------------------------------------------ // ParameterContainer Implementation //------------------------------------------------------------------------ -ParameterContainer::ParameterContainer () +ParameterContainer::ParameterContainer () : params (nullptr) { } @@ -385,14 +389,6 @@ Parameter* ParameterContainer::addParameter (const ParameterInfo& info) return nullptr; } -//------------------------------------------------------------------------ -Parameter* ParameterContainer::getParameterByIndex (int32 index) const -{ - if (!params || index < 0 || index >= static_cast (params->size ())) - return nullptr; - return params->at (index); -} - //------------------------------------------------------------------------ Parameter* ParameterContainer::getParameter (ParamID tag) const { diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstparameters.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstparameters.h index 88742e20..51d1b4ab 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstparameters.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstparameters.h @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -98,9 +98,9 @@ class Parameter : public FObject OBJ_METHODS (Parameter, FObject) //------------------------------------------------------------------------ protected: - ParameterInfo info {0}; - ParamValue valueNormalized {0.}; - int32 precision {4}; + ParameterInfo info; + ParamValue valueNormalized; + int32 precision; }; //------------------------------------------------------------------------ @@ -212,7 +212,7 @@ class ParameterContainer int32 getParameterCount () const { return params ? static_cast (params->size ()) : 0; } /** Gets parameter by index. */ - Parameter* getParameterByIndex (int32 index) const; + Parameter* getParameterByIndex (int32 index) const { return params ? params->at (index) : nullptr; } /** Removes all parameters. */ void removeAll () @@ -231,7 +231,7 @@ class ParameterContainer protected: using ParameterPtrVector = std::vector>; using IndexMap = std::map; - ParameterPtrVector* params {nullptr}; + ParameterPtrVector* params; IndexMap id2index; }; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstpresetfile.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstpresetfile.cpp index 3a47c175..1adb8e49 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstpresetfile.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstpresetfile.cpp @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -35,8 +35,10 @@ //----------------------------------------------------------------------------- #include "vstpresetfile.h" + #include + namespace Steinberg { namespace Vst { @@ -176,7 +178,7 @@ bool PresetFile::loadPreset (IBStream* stream, const FUID& classID, IComponent* } //------------------------------------------------------------------------ -PresetFile::PresetFile (IBStream* stream) : stream (stream) +PresetFile::PresetFile (IBStream* stream) : stream (stream), entryCount (0) { memset (entries, 0, sizeof (entries)); @@ -488,12 +490,13 @@ bool PresetFile::storeComponentState (IBStream* componentStream) bool PresetFile::restoreComponentState (IComponent* component) { const Entry* e = getEntry (kComponentState); - if (!e) - return false; - - auto readOnlyBStream = owned (new ReadOnlyBStream (stream, e->offset, e->size)); - return verify (component->setState (readOnlyBStream)); - + if (e) + { + auto* readOnlyBStream = new ReadOnlyBStream (stream, e->offset, e->size); + FReleaser readOnlyBStreamReleaser (readOnlyBStream); + return verify (component->setState (readOnlyBStream)); + } + return false; } //------------------------------------------------------------------------ @@ -502,7 +505,8 @@ bool PresetFile::restoreComponentState (IEditController* editController) const Entry* e = getEntry (kComponentState); if (e) { - auto readOnlyBStream = owned (new ReadOnlyBStream (stream, e->offset, e->size)); + auto* readOnlyBStream = new ReadOnlyBStream (stream, e->offset, e->size); + FReleaser readOnlyBStreamReleaser (readOnlyBStream); return verify (editController->setComponentState (readOnlyBStream)); } return false; @@ -542,7 +546,8 @@ bool PresetFile::restoreControllerState (IEditController* editController) const Entry* e = getEntry (kControllerState); if (e) { - auto readOnlyBStream = owned (new ReadOnlyBStream (stream, e->offset, e->size)); + auto* readOnlyBStream = new ReadOnlyBStream (stream, e->offset, e->size); + FReleaser readOnlyBStreamReleaser (readOnlyBStream); return verify (editController->setState (readOnlyBStream)); } return false; @@ -598,8 +603,9 @@ bool PresetFile::restoreProgramData (IProgramListData* programListData, return false; int32 alreadyRead = sizeof (int32); - auto readOnlyBStream = owned ( - new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead)); + auto* readOnlyBStream = + new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead); + FReleaser readOnlyBStreamReleaser (readOnlyBStream); return programListData && verify (programListData->setProgramData ( savedProgramListID, programIndex, readOnlyBStream)); } @@ -633,8 +639,9 @@ bool PresetFile::restoreProgramData (IUnitData* unitData, UnitID* unitId) return false; int32 alreadyRead = sizeof (int32); - auto readOnlyBStream = owned ( - new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead)); + auto* readOnlyBStream = + new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead); + FReleaser readOnlyStreamReleaser (readOnlyBStream); return (unitData && verify (unitData->setUnitData (savedUnitID, readOnlyBStream))); } } @@ -655,8 +662,9 @@ bool PresetFile::restoreProgramData (IUnitInfo* unitInfo, int32 unitProgramListI return false; int32 alreadyRead = sizeof (int32); - auto readOnlyBStream = owned ( - new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead)); + auto* readOnlyBStream = + new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead); + FReleaser readOnlyStreamReleaser (readOnlyBStream); return (unitInfo && unitInfo->setUnitProgramData (unitProgramListID, programIndex, readOnlyBStream)); } @@ -780,7 +788,7 @@ tresult PLUGIN_API ReadOnlyBStream::read (void* buffer, int32 numBytes, int32* n if (!sourceStream) return kNotInitialized; - int32 maxBytesToRead = static_cast (sectionSize - seekPosition); + int32 maxBytesToRead = (int32) (sectionSize - seekPosition); if (numBytes > maxBytesToRead) numBytes = maxBytesToRead; if (numBytes <= 0) diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstpresetfile.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstpresetfile.h index 2a9f83fc..c68da251 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstpresetfile.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstpresetfile.h @@ -8,7 +8,7 @@ // //----------------------------------------------------------------------------- // LICENSE -// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved +// (c) 2021, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: @@ -124,7 +124,7 @@ class PresetFile TSize size; }; - IBStream* getStream () const { return stream; } ///< Returns the associated stream. + IBStream* getStream () { return stream; } ///< Returns the associated stream. const FUID& getClassID () const { return classID; } ///< Returns the associated classID (component ID: Processor part (not the controller!)). void setClassID (const FUID& uid) { classID = uid; }///< Sets the associated classID (component ID: Processor part (not the controller!)). @@ -223,7 +223,7 @@ class PresetFile FUID classID; ///< classID is the FUID of the component (processor) part enum { kMaxEntries = 128 }; Entry entries[kMaxEntries]; - int32 entryCount {0}; + int32 entryCount; }; //------------------------------------------------------------------------ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_ARAHosting.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_ARAHosting.h index 9da806df..08862331 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_ARAHosting.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_ARAHosting.h @@ -107,8 +107,6 @@ struct ConversionFunctions when the lifetime of the helper class object ends. You shouldn't use this class directly but instead inherit from the helper classes. - - @tags{ARA} */ template class ManagedARAHandle @@ -281,8 +279,6 @@ class AudioModification : public ManagedARAHandle= __IPHONE_15_0) \ - || (JUCE_MAC && defined (MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) - #define JUCE_APPLE_MIDI_EVENT_LIST_SUPPORTED 1 -#else - #define JUCE_APPLE_MIDI_EVENT_LIST_SUPPORTED 0 -#endif - -#include - -#if JUCE_APPLE_MIDI_EVENT_LIST_SUPPORTED - #include -#endif - namespace juce { @@ -373,10 +359,7 @@ struct AudioUnitHelpers } auto layout = processor.getBusesLayout(); - - // The 'standard' layout with the most channels defined is AudioChannelSet::create9point1point6(). - // This value should be updated if larger standard channel layouts are added in the future. - constexpr auto maxNumChanToCheckFor = 16; + auto maxNumChanToCheckFor = 9; auto defaultInputs = processor.getChannelCountOfBus (true, 0); auto defaultOutputs = processor.getChannelCountOfBus (false, 0); diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index 6540a882..ef7b30ad 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -42,8 +42,9 @@ #include -#include -#include +#include +#include +#include #include "juce_AU_Shared.h" namespace juce @@ -506,16 +507,40 @@ static bool hasARAExtension ([[maybe_unused]] AudioUnit audioUnit) static void createAudioUnit (VersionedAudioComponent versionedComponent, AudioUnitCreationCallback callback) { + struct AUAsyncInitializationCallback + { + typedef void (^AUCompletionCallbackBlock)(AudioComponentInstance, OSStatus); + + explicit AUAsyncInitializationCallback (AudioUnitCreationCallback inOriginalCallback) + : originalCallback (std::move (inOriginalCallback)) + { + block = CreateObjCBlock (this, &AUAsyncInitializationCallback::completion); + } + + AUCompletionCallbackBlock getBlock() noexcept { return block; } + + void completion (AudioComponentInstance audioUnit, OSStatus err) + { + originalCallback (audioUnit, err); + + delete this; + } + + double sampleRate; + int framesPerBuffer; + AudioUnitCreationCallback originalCallback; + + ObjCBlock block; + }; + + auto callbackBlock = new AUAsyncInitializationCallback (std::move (callback)); + if (versionedComponent.isAUv3) { if (@available (macOS 10.11, *)) { - AudioComponentInstantiate (versionedComponent.audioComponent, - kAudioComponentInstantiation_LoadOutOfProcess, - ^(AudioComponentInstance audioUnit, OSStatus err) - { - callback (audioUnit, err); - }); + AudioComponentInstantiate (versionedComponent.audioComponent, kAudioComponentInstantiation_LoadOutOfProcess, + callbackBlock->getBlock()); return; } @@ -523,7 +548,7 @@ static void createAudioUnit (VersionedAudioComponent versionedComponent, AudioUn AudioComponentInstance audioUnit; auto err = AudioComponentInstanceNew (versionedComponent.audioComponent, &audioUnit); - callback (err != noErr ? nullptr : audioUnit, err); + callbackBlock->completion (err != noErr ? nullptr : audioUnit, err); } struct AudioComponentResult @@ -1247,7 +1272,7 @@ void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, scope, static_cast (i), &sampleRate, &sampleRateSize); - if (! approximatelyEqual (sampleRate, sr)) + if (sampleRate != sr) { if (isAUv3) // setting kAudioUnitProperty_SampleRate fails on AUv3s { @@ -2591,6 +2616,9 @@ void updateBypass (bool processBlockBypassedCalled) { addAndMakeVisible (wrapper); + viewControllerCallback = + CreateObjCBlock (this, &AudioUnitPluginWindowCocoa::requestViewControllerCallback); + setOpaque (true); setVisible (true); setSize (100, 100); @@ -2646,6 +2674,7 @@ void childBoundsChanged (Component*) override AudioUnitFormatHelpers::AutoResizingNSViewComponent wrapper; typedef void (^ViewControllerCallbackBlock)(AUViewControllerBase *); + ObjCBlock viewControllerCallback; bool waitingForViewCallback = false; @@ -2699,9 +2728,12 @@ bool createView ([[maybe_unused]] bool createGenericViewIfNeeded) && dataSize == sizeof (ViewControllerCallbackBlock)) { waitingForViewCallback = true; - auto callback = ^(AUViewControllerBase* controller) { this->requestViewControllerCallback (controller); }; + ViewControllerCallbackBlock callback; + callback = viewControllerCallback; + + ViewControllerCallbackBlock* info = &callback; - if (noErr == AudioUnitSetProperty (plugin.audioUnit, kAudioUnitProperty_RequestViewController, kAudioUnitScope_Global, 0, &callback, dataSize)) + if (noErr == AudioUnitSetProperty (plugin.audioUnit, kAudioUnitProperty_RequestViewController, kAudioUnitScope_Global, 0, info, dataSize)) return true; waitingForViewCallback = false; @@ -2739,7 +2771,7 @@ void requestViewControllerCallback (AUViewControllerBase* controller) if (@available (macOS 10.11, *)) size = [controller preferredContentSize]; - if (approximatelyEqual (size.width, 0.0) || approximatelyEqual (size.height, 0.0)) + if (size.width == 0 || size.height == 0) size = controller.view.frame.size; return CGSizeMake (jmax ((CGFloat) 20.0f, size.width), diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp index 275f2be8..6121dd84 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp @@ -457,7 +457,7 @@ class LADSPAPluginInstance final : public AudioPluginInstance { const ScopedLock sl (pluginInstance.lock); - if (! approximatelyEqual (paramValue.unscaled, newValue)) + if (paramValue.unscaled != newValue) paramValue = ParameterValue (getNewParamScaled (interface->PortRangeHints [paramID], newValue), newValue); } } diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LV2PluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LV2PluginFormat.cpp index b4f35881..4ef7f4b3 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LV2PluginFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LV2PluginFormat.cpp @@ -28,7 +28,7 @@ #include "juce_LV2Common.h" #include "juce_LV2Resources.h" -#include +#include #include @@ -2579,8 +2579,15 @@ class UiInstanceArgs File bundlePath; URL pluginUri; - auto withBundlePath (File v) const noexcept { return withMember (*this, &UiInstanceArgs::bundlePath, std::move (v)); } - auto withPluginUri (URL v) const noexcept { return withMember (*this, &UiInstanceArgs::pluginUri, std::move (v)); } + auto withBundlePath (File v) const noexcept { return with (&UiInstanceArgs::bundlePath, std::move (v)); } + auto withPluginUri (URL v) const noexcept { return with (&UiInstanceArgs::pluginUri, std::move (v)); } + +private: + template + UiInstanceArgs with (Member UiInstanceArgs::* member, Member value) const noexcept + { + return juce::lv2_host::with (*this, member, std::move (value)); + } }; static File bundlePathFromUri (const char* uri) @@ -2607,7 +2614,7 @@ class UiInstance mLV2_UI__floatProtocol (map.map (LV2_UI__floatProtocol)), mLV2_ATOM__atomTransfer (map.map (LV2_ATOM__atomTransfer)), mLV2_ATOM__eventTransfer (map.map (LV2_ATOM__eventTransfer)), - instance (makeInstance (args, features)), + instance (makeInstance (args.pluginUri, args.bundlePath, features)), idleCallback (getExtensionData (world, LV2_UI__idleInterface)) { jassert (descriptor != nullptr); @@ -2675,14 +2682,14 @@ class UiInstance using Instance = std::unique_ptr; using Idle = int (*) (LV2UI_Handle); - Instance makeInstance (const UiInstanceArgs& args, const LV2_Feature* const* features) + Instance makeInstance (const URL& pluginUri, const File& bundlePath, const LV2_Feature* const* features) { if (descriptor->get() == nullptr) return { nullptr, [] (LV2UI_Handle) {} }; return Instance { descriptor->get()->instantiate (descriptor->get(), - args.pluginUri.toString (true).toRawUTF8(), - File::addTrailingSeparator (args.bundlePath.getFullPathName()).toRawUTF8(), + pluginUri.toString (false).toRawUTF8(), + File::addTrailingSeparator (bundlePath.getFullPathName()).toRawUTF8(), writeFunction, this, &widget, @@ -3280,7 +3287,7 @@ class ConfiguredEditorComponent : public Component, { if (auto* r = ref.getComponent()) { - if (approximatelyEqual (std::exchange (r->nativeScaleFactor, platformScale), platformScale)) + if (std::exchange (r->nativeScaleFactor, platformScale) == platformScale) return; r->nativeScaleFactor = platformScale; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LV2SupportLibs.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LV2SupportLibs.cpp index 4c63da5c..bc81cdb6 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LV2SupportLibs.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LV2SupportLibs.cpp @@ -31,8 +31,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wc99-extensions", "-Wdeprecated-declarations", "-Wextra-semi", "-Wfloat-conversion", - "-Wfloat-equal", - "-Wformat-overflow", "-Wimplicit-float-conversion", "-Wimplicit-int-conversion", "-Wmicrosoft-include", @@ -116,6 +114,3 @@ using namespace Utils; #pragma pop_macro ("nil") } // extern "C" - -JUCE_END_IGNORE_WARNINGS_MSVC -JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp index a65a0d59..90efcc58 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp @@ -29,7 +29,7 @@ namespace juce JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) -class LegacyAudioParameter : public HostedAudioProcessorParameter +class LegacyAudioParameter : public AudioProcessorParameter { public: LegacyAudioParameter (AudioProcessor& audioProcessorToUse, int audioParameterIndex) @@ -54,7 +54,7 @@ class LegacyAudioParameter : public HostedAudioProcessorParameter bool isMetaParameter() const override { return processor->isMetaParameter (parameterIndex); } Category getCategory() const override { return processor->getParameterCategory (parameterIndex); } String getCurrentValueAsText() const override { return processor->getParameterText (parameterIndex); } - String getParameterID() const override { return processor->getParameterID (parameterIndex); } + String getParamID() const { return processor->getParameterID (parameterIndex); } //============================================================================== float getValueForText (const String&) const override @@ -101,12 +101,12 @@ class LegacyAudioParameter : public HostedAudioProcessorParameter static String getParamID (const AudioProcessorParameter* param, bool forceLegacyParamIDs) noexcept { if (auto* legacy = dynamic_cast (param)) - return forceLegacyParamIDs ? String (legacy->parameterIndex) : legacy->getParameterID(); + return forceLegacyParamIDs ? String (legacy->parameterIndex) : legacy->getParamID(); - if (auto* paramWithID = dynamic_cast (param)) + if (auto* paramWithID = dynamic_cast (param)) { if (! forceLegacyParamIDs) - return paramWithID->getParameterID(); + return paramWithID->paramID; } if (param != nullptr) diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h index 9043a0d7..cf7f2a81 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -197,7 +197,7 @@ static inline Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg return arrangement; } -static std::optional getSpeakerType (const AudioChannelSet& set, AudioChannelSet::ChannelType type) noexcept +static Steinberg::Vst::Speaker getSpeakerType (const AudioChannelSet& set, AudioChannelSet::ChannelType type) noexcept { switch (type) { @@ -241,15 +241,6 @@ static std::optional getSpeakerType (const AudioChannel case AudioChannelSet::ambisonicACN13: return Steinberg::Vst::kSpeakerACN13; case AudioChannelSet::ambisonicACN14: return Steinberg::Vst::kSpeakerACN14; case AudioChannelSet::ambisonicACN15: return Steinberg::Vst::kSpeakerACN15; - case AudioChannelSet::ambisonicACN16: return Steinberg::Vst::kSpeakerACN16; - case AudioChannelSet::ambisonicACN17: return Steinberg::Vst::kSpeakerACN17; - case AudioChannelSet::ambisonicACN18: return Steinberg::Vst::kSpeakerACN18; - case AudioChannelSet::ambisonicACN19: return Steinberg::Vst::kSpeakerACN19; - case AudioChannelSet::ambisonicACN20: return Steinberg::Vst::kSpeakerACN20; - case AudioChannelSet::ambisonicACN21: return Steinberg::Vst::kSpeakerACN21; - case AudioChannelSet::ambisonicACN22: return Steinberg::Vst::kSpeakerACN22; - case AudioChannelSet::ambisonicACN23: return Steinberg::Vst::kSpeakerACN23; - case AudioChannelSet::ambisonicACN24: return Steinberg::Vst::kSpeakerACN24; case AudioChannelSet::topSideLeft: return Steinberg::Vst::kSpeakerTsl; case AudioChannelSet::topSideRight: return Steinberg::Vst::kSpeakerTsr; case AudioChannelSet::bottomFrontLeft: return Steinberg::Vst::kSpeakerBfl; @@ -263,6 +254,15 @@ static std::optional getSpeakerType (const AudioChannel case AudioChannelSet::discreteChannel0: return Steinberg::Vst::kSpeakerM; + case AudioChannelSet::ambisonicACN16: + case AudioChannelSet::ambisonicACN17: + case AudioChannelSet::ambisonicACN18: + case AudioChannelSet::ambisonicACN19: + case AudioChannelSet::ambisonicACN20: + case AudioChannelSet::ambisonicACN21: + case AudioChannelSet::ambisonicACN22: + case AudioChannelSet::ambisonicACN23: + case AudioChannelSet::ambisonicACN24: case AudioChannelSet::ambisonicACN25: case AudioChannelSet::ambisonicACN26: case AudioChannelSet::ambisonicACN27: @@ -274,44 +274,17 @@ static std::optional getSpeakerType (const AudioChannel case AudioChannelSet::ambisonicACN33: case AudioChannelSet::ambisonicACN34: case AudioChannelSet::ambisonicACN35: - case AudioChannelSet::ambisonicACN36: - case AudioChannelSet::ambisonicACN37: - case AudioChannelSet::ambisonicACN38: - case AudioChannelSet::ambisonicACN39: - case AudioChannelSet::ambisonicACN40: - case AudioChannelSet::ambisonicACN41: - case AudioChannelSet::ambisonicACN42: - case AudioChannelSet::ambisonicACN43: - case AudioChannelSet::ambisonicACN44: - case AudioChannelSet::ambisonicACN45: - case AudioChannelSet::ambisonicACN46: - case AudioChannelSet::ambisonicACN47: - case AudioChannelSet::ambisonicACN48: - case AudioChannelSet::ambisonicACN49: - case AudioChannelSet::ambisonicACN50: - case AudioChannelSet::ambisonicACN51: - case AudioChannelSet::ambisonicACN52: - case AudioChannelSet::ambisonicACN53: - case AudioChannelSet::ambisonicACN54: - case AudioChannelSet::ambisonicACN55: - case AudioChannelSet::ambisonicACN56: - case AudioChannelSet::ambisonicACN57: - case AudioChannelSet::ambisonicACN58: - case AudioChannelSet::ambisonicACN59: - case AudioChannelSet::ambisonicACN60: - case AudioChannelSet::ambisonicACN61: - case AudioChannelSet::ambisonicACN62: - case AudioChannelSet::ambisonicACN63: case AudioChannelSet::wideLeft: case AudioChannelSet::wideRight: case AudioChannelSet::unknown: break; } - return {}; + auto channelIndex = static_cast (type) - (static_cast (AudioChannelSet::discreteChannel0) + 6ull); + return (1ull << (channelIndex + 33ull /* last speaker in vst layout + 1 */)); } -static std::optional getChannelType (Steinberg::Vst::SpeakerArrangement arr, Steinberg::Vst::Speaker type) noexcept +static AudioChannelSet::ChannelType getChannelType (Steinberg::Vst::SpeakerArrangement arr, Steinberg::Vst::Speaker type) noexcept { switch (type) { @@ -351,15 +324,6 @@ static std::optional getChannelType (Steinberg::Vs case Steinberg::Vst::kSpeakerACN13: return AudioChannelSet::ambisonicACN13; case Steinberg::Vst::kSpeakerACN14: return AudioChannelSet::ambisonicACN14; case Steinberg::Vst::kSpeakerACN15: return AudioChannelSet::ambisonicACN15; - case Steinberg::Vst::kSpeakerACN16: return AudioChannelSet::ambisonicACN16; - case Steinberg::Vst::kSpeakerACN17: return AudioChannelSet::ambisonicACN17; - case Steinberg::Vst::kSpeakerACN18: return AudioChannelSet::ambisonicACN18; - case Steinberg::Vst::kSpeakerACN19: return AudioChannelSet::ambisonicACN19; - case Steinberg::Vst::kSpeakerACN20: return AudioChannelSet::ambisonicACN20; - case Steinberg::Vst::kSpeakerACN21: return AudioChannelSet::ambisonicACN21; - case Steinberg::Vst::kSpeakerACN22: return AudioChannelSet::ambisonicACN22; - case Steinberg::Vst::kSpeakerACN23: return AudioChannelSet::ambisonicACN23; - case Steinberg::Vst::kSpeakerACN24: return AudioChannelSet::ambisonicACN24; case Steinberg::Vst::kSpeakerTsl: return AudioChannelSet::topSideLeft; case Steinberg::Vst::kSpeakerTsr: return AudioChannelSet::topSideRight; case Steinberg::Vst::kSpeakerLcs: return AudioChannelSet::leftSurroundRear; @@ -376,7 +340,12 @@ static std::optional getChannelType (Steinberg::Vs case Steinberg::Vst::kSpeakerBrr: return AudioChannelSet::bottomRearRight; } - return {}; + auto channelType = BigInteger (static_cast (type)).findNextSetBit (0); + + // VST3 <-> JUCE layout conversion error: report this bug to the JUCE forum + jassert (channelType >= 33); + + return static_cast (static_cast (AudioChannelSet::discreteChannel0) + 6 + (channelType - 33)); } namespace detail @@ -425,8 +394,6 @@ namespace detail { k70_6, { X::left, X::right, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } }, // The VST3 layout uses 'left/right' and 'left-of-center/right-of-center', but the JUCE layout uses 'left/right' and 'wide-left/wide-right'. - { k91_4, { X::wideLeft, X::wideRight, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::left, X::right, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight } }, - { k90_4, { X::wideLeft, X::wideRight, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::left, X::right, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight } }, { k91_6, { X::wideLeft, X::wideRight, X::centre, X::LFE, X::leftSurroundRear, X::rightSurroundRear, X::left, X::right, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } }, { k90_6, { X::wideLeft, X::wideRight, X::centre, X::leftSurroundRear, X::rightSurroundRear, X::left, X::right, X::leftSurroundSide, X::rightSurroundSide, X::topFrontLeft, X::topFrontRight, X::topRearLeft, X::topRearRight, X::topSideLeft, X::topSideRight } }, }; @@ -439,7 +406,7 @@ namespace detail inline bool isLayoutTableValid() { for (const auto& item : detail::layoutTable) - if ((size_t) countNumberOfBits ((uint64) item.arrangement) != item.channelOrder.size()) + if ((size_t) countNumberOfBits (item.arrangement) != item.channelOrder.size()) return false; std::set arrangements; @@ -456,7 +423,7 @@ inline bool isLayoutTableValid() }); } -static std::optional> getSpeakerOrder (Steinberg::Vst::SpeakerArrangement arr) +static Array getSpeakerOrder (Steinberg::Vst::SpeakerArrangement arr) { using namespace Steinberg::Vst; using namespace Steinberg::Vst::SpeakerArr; @@ -478,34 +445,12 @@ static std::optional> getSpeakerOrder (Stein result.ensureStorageAllocated (channels); for (auto i = 0; i < channels; ++i) - if (const auto t = getChannelType (arr, getSpeaker (arr, i))) - result.add (*t); - - if (getChannelCount (arr) == result.size()) - return result; + result.add (getChannelType (arr, getSpeaker (arr, i))); - return {}; + return result; } -struct Ambisonics -{ - struct Mapping - { - Steinberg::Vst::SpeakerArrangement arrangement; - AudioChannelSet channelSet; - }; - - inline static const Mapping mappings[] - { - { Steinberg::Vst::SpeakerArr::kAmbi5thOrderACN, AudioChannelSet::ambisonic (5) }, - { Steinberg::Vst::SpeakerArr::kAmbi6thOrderACN, AudioChannelSet::ambisonic (6) }, - { Steinberg::Vst::SpeakerArr::kAmbi7thOrderACN, AudioChannelSet::ambisonic (7) }, - }; - - Ambisonics() = delete; -}; - -static std::optional getVst3SpeakerArrangement (const AudioChannelSet& channels) noexcept +static Steinberg::Vst::SpeakerArrangement getVst3SpeakerArrangement (const AudioChannelSet& channels) noexcept { using namespace Steinberg::Vst::SpeakerArr; @@ -513,10 +458,6 @@ static std::optional getVst3SpeakerArrangeme std::call_once (detail::layoutTableCheckedFlag, [] { jassert (isLayoutTableValid()); }); #endif - for (const auto& mapping : Ambisonics::mappings) - if (channels == mapping.channelSet) - return mapping.arrangement; - const auto channelSetMatches = [&channels] (const auto& layoutPair) { return AudioChannelSet::channelSetWithChannels (layoutPair.channelOrder) == channels; @@ -529,28 +470,21 @@ static std::optional getVst3SpeakerArrangeme Steinberg::Vst::SpeakerArrangement result = 0; for (const auto& type : channels.getChannelTypes()) - if (const auto t = getSpeakerType (channels, type)) - result |= *t; + result |= getSpeakerType (channels, type); - if (getChannelCount (result) == channels.size()) - return result; - - return {}; + return result; } -inline std::optional getChannelSetForSpeakerArrangement (Steinberg::Vst::SpeakerArrangement arr) noexcept +inline AudioChannelSet getChannelSetForSpeakerArrangement (Steinberg::Vst::SpeakerArrangement arr) noexcept { using namespace Steinberg::Vst::SpeakerArr; - for (const auto& mapping : Ambisonics::mappings) - if (arr == mapping.arrangement) - return mapping.channelSet; - - if (const auto order = getSpeakerOrder (arr)) - return AudioChannelSet::channelSetWithChannels (*order); + const auto result = AudioChannelSet::channelSetWithChannels (getSpeakerOrder (arr)); // VST3 <-> JUCE layout conversion error: report this bug to the JUCE forum - return {}; + jassert (result.size() == getChannelCount (arr)); + + return result; } //============================================================================== @@ -588,21 +522,7 @@ struct ChannelMapping */ static std::vector makeChannelIndices (const AudioChannelSet& juceArrangement) { - const auto order = [&] - { - const auto fallback = juceArrangement.getChannelTypes(); - const auto vst3Arrangement = getVst3SpeakerArrangement (juceArrangement); - - if (! vst3Arrangement.has_value()) - return fallback; - - const auto reordered = getSpeakerOrder (*vst3Arrangement); - - if (! reordered.has_value() || AudioChannelSet::channelSetWithChannels (*reordered) != juceArrangement) - return fallback; - - return *reordered; - }(); + const auto order = getSpeakerOrder (getVst3SpeakerArrangement (juceArrangement)); std::vector result; @@ -693,9 +613,7 @@ static int countValidBuses (Steinberg::Vst::AudioBusBuffers* buffers, int32 num) })); } -enum class Direction { input, output }; - -template +template static bool validateLayouts (Iterator first, Iterator last, const std::vector& map) { if ((size_t) std::distance (first, last) > map.size()) @@ -705,24 +623,12 @@ static bool validateLayouts (Iterator first, Iterator last, const std::vector{}, bus); - const auto expectedJuceChannels = (int) mapIterator->size(); - const auto actualVstChannels = (int) bus.numChannels; - const auto limit = jmin (expectedJuceChannels, actualVstChannels); - const auto anyChannelIsNull = std::any_of (busPtr, busPtr + limit, [] (auto* ptr) { return ptr == nullptr; }); - constexpr auto isInput = direction == Direction::input; - - const auto channelCountIsUsable = isInput ? expectedJuceChannels <= actualVstChannels - : actualVstChannels <= expectedJuceChannels; + auto** busPtr = getAudioBusPointer (detail::Tag{}, *it); + const auto anyChannelIsNull = std::any_of (busPtr, busPtr + it->numChannels, [] (auto* ptr) { return ptr == nullptr; }); // Null channels are allowed if the bus is inactive - if (mapIterator->isHostActive() && (anyChannelIsNull || ! channelCountIsUsable)) + if (mapIterator->isHostActive() && (anyChannelIsNull || (int) mapIterator->size() != it->numChannels)) return false; - - // If this is hit, the destination bus has fewer channels than the source bus. - // As a result, some channels will 'go missing', and channel layouts may be invalid. - jassert (actualVstChannels == expectedJuceChannels); } // If the host didn't provide the full complement of buses, it must be because the other @@ -763,10 +669,10 @@ class ClientBufferMapperData // WaveLab workaround: This host may report the wrong number of inputs/outputs so re-count here const auto vstInputs = countValidBuses (data.inputs, data.numInputs); - if (! validateLayouts (data.inputs, data.inputs + vstInputs, inputMap)) + if (! validateLayouts (data.inputs, data.inputs + vstInputs, inputMap)) return getBlankBuffer (usedChannels, (int) data.numSamples); - setUpInputChannels (data, (size_t) vstInputs, scratchBuffer, inputMap, channels); + setUpInputChannels (data, (size_t) vstInputs, scratchBuffer, inputMap, channels); setUpOutputChannels (scratchBuffer, outputMap, channels); const auto channelPtr = channels.empty() ? scratchBuffer.getArrayOfWritePointers() @@ -784,7 +690,7 @@ class ClientBufferMapperData { for (size_t busIndex = 0; busIndex < map.size(); ++busIndex) { - const auto& mapping = map[busIndex]; + const auto mapping = map[busIndex]; if (! mapping.isClientActive()) continue; @@ -796,12 +702,7 @@ class ClientBufferMapperData if (mapping.isHostActive() && busIndex < vstInputs) { - auto& bus = data.inputs[busIndex]; - - // Every JUCE channel must have a VST3 channel counterpart - jassert (mapping.size() <= static_cast (bus.numChannels)); - - auto** busPtr = getAudioBusPointer (detail::Tag{}, bus); + auto** busPtr = getAudioBusPointer (detail::Tag{}, data.inputs[busIndex]); for (size_t channelIndex = 0; channelIndex < mapping.size(); ++channelIndex) { @@ -1020,7 +921,7 @@ class ClientRemappedBuffer // WaveLab workaround: This host may report the wrong number of inputs/outputs so re-count here const auto vstOutputs = (size_t) countValidBuses (data.outputs, data.numOutputs); - if (validateLayouts (data.outputs, data.outputs + vstOutputs, *outputMap)) + if (validateLayouts (data.outputs, data.outputs + vstOutputs, *outputMap)) copyToHostOutputBuses (vstOutputs); else clearHostOutputBuses (vstOutputs); @@ -1039,12 +940,9 @@ class ClientRemappedBuffer { auto& bus = data.outputs[i]; - // Every VST3 channel must have a JUCE channel counterpart - jassert (static_cast (bus.numChannels) <= mapping.size()); - if (mapping.isClientActive()) { - for (size_t j = 0; j < static_cast (bus.numChannels); ++j) + for (size_t j = 0; j < mapping.size(); ++j) { auto* hostChannel = getAudioBusPointer (detail::Tag{}, bus)[j]; const auto juceChannel = juceBusOffset + (size_t) mapping.getJuceChannelForVst3Channel ((int) j); @@ -1053,7 +951,7 @@ class ClientRemappedBuffer } else { - for (size_t j = 0; j < static_cast (bus.numChannels); ++j) + for (size_t j = 0; j < mapping.size(); ++j) { auto* hostChannel = getAudioBusPointer (detail::Tag{}, bus)[j]; FloatVectorOperations::clear (hostChannel, (size_t) data.numSamples); diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h index eb715c60..cd757c39 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h @@ -32,49 +32,44 @@ // Wow, those Steinberg guys really don't worry too much about compiler warnings. JUCE_BEGIN_IGNORE_WARNINGS_LEVEL_MSVC (0, 4505 4702 6011 6031 6221 6386 6387 6330 6001 28199) -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings", - "-Wcast-align", - "-Wclass-memaccess", - "-Wcomma", +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-copy-dtor", + "-Wnon-virtual-dtor", + "-Wdeprecated", + "-Wreorder", + "-Wunsequenced", + "-Wint-to-pointer-cast", + "-Wunused-parameter", "-Wconversion", - "-Wcpp", + "-Woverloaded-virtual", + "-Wshadow", + "-Wdeprecated-register", + "-Wunused-function", + "-Wsign-conversion", + "-Wsign-compare", "-Wdelete-non-virtual-dtor", - "-Wdeprecated", - "-Wdeprecated-copy-dtor", "-Wdeprecated-declarations", - "-Wdeprecated-register", - "-Wextra", "-Wextra-semi", - "-Wfloat-equal", - "-Wformat", - "-Wformat-truncation=", - "-Wformat=", - "-Wignored-qualifiers", - "-Winconsistent-missing-destructor-override", - "-Wint-to-pointer-cast", - "-Wlogical-op-parentheses", - "-Wmaybe-uninitialized", "-Wmissing-braces", + "-Wswitch-default", + "-Wshadow-field", + "-Wpragma-pack", + "-Wcomma", + "-Wzero-as-null-pointer-constant", + "-Winconsistent-missing-destructor-override", + "-Wcast-align", + "-Wignored-qualifiers", "-Wmissing-field-initializers", - "-Wmissing-prototypes", - "-Wnon-virtual-dtor", - "-Woverloaded-virtual", - "-Wparentheses", + "-Wformat=", + "-Wformat", "-Wpedantic", - "-Wpragma-pack", - "-Wredundant-decls", - "-Wreorder", - "-Wshadow", - "-Wshadow-field", - "-Wsign-compare", - "-Wsign-conversion", - "-Wswitch-default", + "-Wextra", + "-Wclass-memaccess", + "-Wmissing-prototypes", "-Wtype-limits", - "-Wunsequenced", - "-Wunused-but-set-variable", - "-Wunused-function", - "-Wunused-parameter", - "-Wzero-as-null-pointer-constant") + "-Wcpp", + "-W#warnings", + "-Wmaybe-uninitialized", + "-Wunused-but-set-variable") #undef DEVELOPMENT #define DEVELOPMENT 0 // This avoids a Clang warning in Steinberg code about unused values @@ -92,7 +87,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings", #include #include #include - #include #include #include #include @@ -113,7 +107,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings", #include #include #include - #include #include #include @@ -122,10 +115,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings", // needed for VST_VERSION #include - #ifndef NOMINMAX - #define NOMINMAX // Some of the steinberg sources don't set this before including windows.h - #endif - #include #include #include @@ -148,11 +137,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings", #include #endif -#pragma push_macro ("True") -#undef True -#pragma push_macro ("False") -#undef False - #include #include #include @@ -160,24 +144,18 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings", #include #include #include - #include #include + #include #include #include - #include - #include - #include - #include + #include #include + #include #include #include - #include - #include #include #include - -#pragma pop_macro ("True") -#pragma pop_macro ("False") + #include #if VST_VERSION >= 0x03060c // 3.6.12 #include diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index e049cc9d..7865d50b 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -94,6 +94,7 @@ static int warnOnFailureIfImplemented (int result) noexcept #define warnOnFailureIfImplemented(x) x #endif +enum class Direction { input, output }; enum class MediaKind { audio, event }; static Vst::MediaType toVstType (MediaKind x) { return x == MediaKind::audio ? Vst::kAudio : Vst::kEvent; } @@ -188,66 +189,6 @@ static void fillDescriptionWith (PluginDescription& description, ObjectType& obj description.manufacturerName = toString (object.vendor).trim(); } -static std::vector createPluginDescriptions (const File& pluginFile, const Steinberg::ModuleInfo& info) -{ - std::vector result; - - const auto araMainFactoryClassNames = [&] - { - std::unordered_set factories; - - #if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX) - for (const auto& c : info.classes) - if (c.category == kARAMainFactoryClass) - factories.insert (CharPointer_UTF8 (c.name.c_str())); - #endif - - return factories; - }(); - - for (const auto& c : info.classes) - { - if (c.category != kVstAudioEffectClass) - continue; - - PluginDescription description; - - description.fileOrIdentifier = pluginFile.getFullPathName(); - description.lastFileModTime = pluginFile.getLastModificationTime(); - description.lastInfoUpdateTime = Time::getCurrentTime(); - description.manufacturerName = CharPointer_UTF8 (info.factoryInfo.vendor.c_str()); - description.name = CharPointer_UTF8 (info.name.c_str()); - description.descriptiveName = CharPointer_UTF8 (info.name.c_str()); - description.pluginFormatName = "VST3"; - description.numInputChannels = 0; - description.numOutputChannels = 0; - description.hasARAExtension = araMainFactoryClassNames.find (description.name) != araMainFactoryClassNames.end(); - - const auto uid = VST3::UID::fromString (c.cid); - - if (! uid) - continue; - - description.deprecatedUid = getHashForRange (uid->data()); - description.uniqueId = getHashForRange (getNormalisedTUID (uid->data())); - - StringArray categories; - - for (const auto& category : c.subCategories) - categories.add (CharPointer_UTF8 (category.c_str())); - - description.category = categories.joinIntoString ("|"); - - description.isInstrument = std::any_of (c.subCategories.begin(), - c.subCategories.end(), - [] (const auto& subcategory) { return subcategory == "Instrument"; }); - - result.push_back (description); - } - - return result; -} - static void createPluginDescription (PluginDescription& description, const File& pluginFile, const String& company, const String& name, const PClassInfo& info, PClassInfo2* info2, PClassInfoW* infoW, @@ -436,7 +377,7 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0 //============================================================================== tresult PLUGIN_API requestOpenEditor ([[maybe_unused]] FIDString name) override { - // This request cannot currently be surfaced in the JUCE public API + jassertfalse; return kResultFalse; } @@ -873,49 +814,26 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0 }; //============================================================================== -struct DescriptionLister +struct DescriptionFactory { - static std::vector tryLoadFast (const File& file, const File& moduleinfo) + DescriptionFactory (VST3HostContext* host, IPluginFactory* pluginFactory) + : vst3HostContext (host), factory (pluginFactory) { - if (! moduleinfo.existsAsFile()) - return {}; - - MemoryBlock mb; - - if (! moduleinfo.loadFileAsData (mb)) - return {}; - - const std::string_view blockAsStringView (static_cast (mb.getData()), mb.getSize()); - const auto parsed = Steinberg::ModuleInfoLib::parseJson (blockAsStringView, nullptr); - - if (! parsed) - return {}; - - return createPluginDescriptions (file, *parsed); + jassert (pluginFactory != nullptr); } - static std::vector findDescriptionsFast (const File& file) - { - const auto moduleinfoNewLocation = file.getChildFile ("Contents").getChildFile ("Resources").getChildFile ("moduleinfo.json"); - - if (const auto loaded = tryLoadFast (file, moduleinfoNewLocation); ! loaded.empty()) - return loaded; - - return tryLoadFast (file, file.getChildFile ("Contents").getChildFile ("moduleinfo.json")); - } + virtual ~DescriptionFactory() {} - static std::vector findDescriptionsSlow (VST3HostContext& host, - IPluginFactory& factory, - const File& file) + Result findDescriptionsAndPerform (const File& file) { - std::vector result; - StringArray foundNames; PFactoryInfo factoryInfo; - factory.getFactoryInfo (&factoryInfo); + factory->getFactoryInfo (&factoryInfo); auto companyName = toString (factoryInfo.vendor).trim(); - auto numClasses = factory.countClasses(); + Result result (Result::ok()); + + auto numClasses = factory->countClasses(); // Every ARA::IMainFactory must have a matching Steinberg::IComponent. // The match is determined by the two classes having the same name. @@ -925,7 +843,7 @@ struct DescriptionLister for (Steinberg::int32 i = 0; i < numClasses; ++i) { PClassInfo info; - factory.getClassInfo (i, &info); + factory->getClassInfo (i, &info); if (std::strcmp (info.category, kARAMainFactoryClass) == 0) araMainFactoryClassNames.insert (info.name); } @@ -934,7 +852,7 @@ struct DescriptionLister for (Steinberg::int32 i = 0; i < numClasses; ++i) { PClassInfo info; - factory.getClassInfo (i, &info); + factory->getClassInfo (i, &info); if (std::strcmp (info.category, kVstAudioEffectClass) != 0) continue; @@ -951,13 +869,13 @@ struct DescriptionLister VSTComSmartPtr pf2; VSTComSmartPtr pf3; - if (pf2.loadFrom (&factory)) + if (pf2.loadFrom (factory)) { info2.reset (new PClassInfo2()); pf2->getClassInfo2 (i, info2.get()); } - if (pf3.loadFrom (&factory)) + if (pf3.loadFrom (factory)) { infoW.reset (new PClassInfoW()); pf3->getClassInfoUnicode (i, infoW.get()); @@ -971,9 +889,9 @@ struct DescriptionLister { VSTComSmartPtr component; - if (component.loadFrom (&factory, info.cid)) + if (component.loadFrom (factory, info.cid)) { - if (component->initialize (host.getFUnknown()) == kResultOk) + if (component->initialize (vst3HostContext->getFUnknown()) == kResultOk) { auto numInputs = getNumSingleDirectionChannelsFor (component, Direction::input); auto numOutputs = getNumSingleDirectionChannelsFor (component, Direction::output); @@ -998,11 +916,41 @@ struct DescriptionLister desc.hasARAExtension = true; if (desc.uniqueId != 0) - result.push_back (desc); + result = performOnDescription (desc); + + if (result.failed()) + break; } return result; } + + virtual Result performOnDescription (PluginDescription&) = 0; + +private: + VSTComSmartPtr vst3HostContext; + VSTComSmartPtr factory; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DescriptionFactory) +}; + +struct DescriptionLister : public DescriptionFactory +{ + DescriptionLister (VST3HostContext* host, IPluginFactory* pluginFactory) + : DescriptionFactory (host, pluginFactory) + { + } + + Result performOnDescription (PluginDescription& desc) + { + list.add (new PluginDescription (desc)); + return Result::ok(); + } + + OwnedArray list; + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DescriptionLister) }; //============================================================================== @@ -1409,11 +1357,9 @@ struct VST3ModuleHandle : public ReferenceCountedObject if (std::strcmp (info.category, kVstAudioEffectClass) != 0) continue; - const auto uniqueId = getHashForRange (getNormalisedTUID (info.cid)); - const auto deprecatedUid = getHashForRange (info.cid); - if (toString (info.name).trim() == description.name - && (uniqueId == description.uniqueId || deprecatedUid == description.deprecatedUid)) + && (getHashForRange (getNormalisedTUID (info.cid)) == description.uniqueId + || getHashForRange (info.cid) == description.deprecatedUid)) { name = description.name; return true; @@ -1496,10 +1442,9 @@ static std::shared_ptr getARAFactory (VST3ModuleHandle& m } //============================================================================== -struct VST3PluginWindow final : public AudioProcessorEditor, - private ComponentMovementWatcher, - private ComponentBoundsConstrainer, - private IPlugFrame +struct VST3PluginWindow : public AudioProcessorEditor, + private ComponentMovementWatcher, + private IPlugFrame { VST3PluginWindow (AudioPluginInstance* owner, IPlugView* pluginView) : AudioProcessorEditor (owner), @@ -1512,15 +1457,12 @@ struct VST3PluginWindow final : public AudioProcessorEditor, setSize (10, 10); setOpaque (true); setVisible (true); - setConstrainer (this); warnOnFailure (view->setFrame (this)); view->queryInterface (Steinberg::IPlugViewContentScaleSupport::iid, (void**) &scaleInterface); setContentScaleFactor(); resizeToFit(); - - setResizable (view->canResize() == kResultTrue, false); } ~VST3PluginWindow() override @@ -1586,19 +1528,6 @@ struct VST3PluginWindow final : public AudioProcessorEditor, bool keyPressed (const KeyPress& /*key*/) override { return true; } private: - void checkBounds (Rectangle& bounds, - const Rectangle&, - const Rectangle&, - bool, - bool, - bool, - bool) override - { - auto rect = componentToVST3Rect (bounds); - view->checkSizeConstraint (&rect); - bounds = vst3ToComponentRect (rect); - } - //============================================================================== void componentPeerChanged() override {} @@ -2244,7 +2173,7 @@ class ParameterChanges : public Vst::IParameterChanges for (const auto* item : queues) { auto* ptr = item->ptr.get(); - callback (ptr->getParameterIndex(), ptr->getParameterId(), ptr->get()); + callback (ptr->getParameterIndex(), ptr->get()); } } @@ -2530,33 +2459,34 @@ class VST3PluginInstance final : public AudioPluginInstance return module != nullptr ? module->getName() : String(); } - std::vector getActualArrangements (bool isInput) const + void repopulateArrangements (Array& inputArrangements, Array& outputArrangements) const { - std::vector result; + inputArrangements.clearQuick(); + outputArrangements.clearQuick(); - const auto numBuses = getBusCount (isInput); + auto numInputAudioBuses = getBusCount (true); + auto numOutputAudioBuses = getBusCount (false); - for (auto i = 0; i < numBuses; ++i) - result.push_back (getArrangementForBus (processor, isInput, i)); + for (int i = 0; i < numInputAudioBuses; ++i) + inputArrangements.add (getArrangementForBus (processor, true, i)); - return result; + for (int i = 0; i < numOutputAudioBuses; ++i) + outputArrangements.add (getArrangementForBus (processor, false, i)); } - std::optional> busLayoutsToArrangements (bool isInput) const + void processorLayoutsToArrangements (Array& inputArrangements, Array& outputArrangements) { - std::vector result; + inputArrangements.clearQuick(); + outputArrangements.clearQuick(); - const auto numBuses = getBusCount (isInput); + auto numInputBuses = getBusCount (true); + auto numOutputBuses = getBusCount (false); - for (auto i = 0; i < numBuses; ++i) - { - if (const auto arr = getVst3SpeakerArrangement (getBus (isInput, i)->getLastEnabledLayout())) - result.push_back (*arr); - else - return {}; - } + for (int i = 0; i < numInputBuses; ++i) + inputArrangements.add (getVst3SpeakerArrangement (getBus (true, i)->getLastEnabledLayout())); - return result; + for (int i = 0; i < numOutputBuses; ++i) + outputArrangements.add (getVst3SpeakerArrangement (getBus (false, i)->getLastEnabledLayout())); } void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override @@ -2570,7 +2500,7 @@ class VST3PluginInstance final : public AudioPluginInstance // Avoid redundantly calling things like setActive, which can be a heavy-duty call for some plugins: if (isActive - && approximatelyEqual (getSampleRate(), newSampleRate) + && getSampleRate() == newSampleRate && getBlockSize() == estimatedSamplesPerBlock) return; @@ -2591,21 +2521,21 @@ class VST3PluginInstance final : public AudioPluginInstance holder->initialise(); - auto inArrangements = busLayoutsToArrangements (true) .value_or (std::vector{}); - auto outArrangements = busLayoutsToArrangements (false).value_or (std::vector{}); + Array inputArrangements, outputArrangements; + processorLayoutsToArrangements (inputArrangements, outputArrangements); // Some plug-ins will crash if you pass a nullptr to setBusArrangements! SpeakerArrangement nullArrangement = {}; - auto* inData = inArrangements .empty() ? &nullArrangement : inArrangements .data(); - auto* outData = outArrangements.empty() ? &nullArrangement : outArrangements.data(); + auto* inputArrangementData = inputArrangements.isEmpty() ? &nullArrangement : inputArrangements.getRawDataPointer(); + auto* outputArrangementData = outputArrangements.isEmpty() ? &nullArrangement : outputArrangements.getRawDataPointer(); - warnOnFailure (processor->setBusArrangements (inData, static_cast (inArrangements .size()), - outData, static_cast (outArrangements.size()))); + warnOnFailure (processor->setBusArrangements (inputArrangementData, inputArrangements.size(), + outputArrangementData, outputArrangements.size())); - const auto inArrActual = getActualArrangements (true); - const auto outArrActual = getActualArrangements (false); + Array actualInArr, actualOutArr; + repopulateArrangements (actualInArr, actualOutArr); - jassert (inArrActual == inArrangements && outArrActual == outArrangements); + jassert (actualInArr == inputArrangements && actualOutArr == outputArrangements); // Needed for having the same sample rate in processBlock(); some plugins need this! setRateAndBufferSizeDetails (newSampleRate, estimatedSamplesPerBlock); @@ -2753,12 +2683,9 @@ class VST3PluginInstance final : public AudioPluginInstance processor->process (data); - outputParameterChanges->forEach ([&] (Steinberg::int32 index, Vst::ParamID id, float value) + outputParameterChanges->forEach ([&] (Steinberg::int32 index, float value) { - cachedParamValues.setWithoutNotifying (index, value); - - if (auto* param = getParameterForID (id)) - param->setValueWithoutUpdatingProcessor (value); + parameterDispatcher.push (index, value); }); midiMessages.clear(); @@ -2780,8 +2707,9 @@ class VST3PluginInstance final : public AudioPluginInstance // not much we can do to check the layout while the audio processor is running // Let's at least check if it is a VST3 compatible layout - for (const auto isInput : { true, false }) + for (int dir = 0; dir < 2; ++dir) { + bool isInput = (dir == 0); auto n = getBusCount (isInput); for (int i = 0; i < n; ++i) @@ -2794,8 +2722,9 @@ class VST3PluginInstance final : public AudioPluginInstance bool syncBusLayouts (const BusesLayout& layouts) const { - for (const auto isInput : { true, false }) + for (int dir = 0; dir < 2; ++dir) { + bool isInput = (dir == 0); auto n = getBusCount (isInput); const Vst::BusDirection vstDir = (isInput ? Vst::kInput : Vst::kOutput); @@ -2808,49 +2737,34 @@ class VST3PluginInstance final : public AudioPluginInstance } } - const auto getPotentialArrangements = [&] (bool isInput) -> std::optional> - { - std::vector result; - - for (int i = 0; i < layouts.getBuses (isInput).size(); ++i) - { - const auto& requested = layouts.getChannelSet (isInput, i); - - if (const auto arr = getVst3SpeakerArrangement (requested.isDisabled() ? getBus (isInput, i)->getLastEnabledLayout() : requested)) - result.push_back (*arr); - else - return {}; - } + Array inputArrangements, outputArrangements; - return result; - }; - - auto inArrangements = getPotentialArrangements (true); - auto outArrangements = getPotentialArrangements (false); - - if (! inArrangements.has_value() || ! outArrangements.has_value()) + for (int i = 0; i < layouts.inputBuses.size(); ++i) { - // This bus layout can't be represented as a VST3 speaker arrangement - return false; + const auto& requested = layouts.getChannelSet (true, i); + inputArrangements.add (getVst3SpeakerArrangement (requested.isDisabled() ? getBus (true, i)->getLastEnabledLayout() : requested)); } - auto& inputArrangements = *inArrangements; - auto& outputArrangements = *outArrangements; + for (int i = 0; i < layouts.outputBuses.size(); ++i) + { + const auto& requested = layouts.getChannelSet (false, i); + outputArrangements.add (getVst3SpeakerArrangement (requested.isDisabled() ? getBus (false, i)->getLastEnabledLayout() : requested)); + } // Some plug-ins will crash if you pass a nullptr to setBusArrangements! Vst::SpeakerArrangement nullArrangement = {}; - auto* inputArrangementData = inputArrangements .empty() ? &nullArrangement : inputArrangements .data(); - auto* outputArrangementData = outputArrangements.empty() ? &nullArrangement : outputArrangements.data(); + auto* inputArrangementData = inputArrangements.isEmpty() ? &nullArrangement : inputArrangements.getRawDataPointer(); + auto* outputArrangementData = outputArrangements.isEmpty() ? &nullArrangement : outputArrangements.getRawDataPointer(); - if (processor->setBusArrangements (inputArrangementData, static_cast (inputArrangements .size()), - outputArrangementData, static_cast (outputArrangements.size())) != kResultTrue) + if (processor->setBusArrangements (inputArrangementData, inputArrangements.size(), + outputArrangementData, outputArrangements.size()) != kResultTrue) return false; // check if the layout matches the request - const auto inArrActual = getActualArrangements (true); - const auto outArrActual = getActualArrangements (false); + Array actualIn, actualOut; + repopulateArrangements (actualIn, actualOut); - return (inArrActual == inputArrangements && outArrActual == outputArrangements); + return (actualIn == inputArrangements && actualOut == outputArrangements); } bool canApplyBusesLayout (const BusesLayout& layouts) const override @@ -3093,7 +3007,7 @@ class VST3PluginInstance final : public AudioPluginInstance { if (componentStream != nullptr) { - Steinberg::int64 result; + int64 result; componentStream->seek (0, IBStream::kIBSeekSet, &result); setComponentStateAndResetParameters (*componentStream); } @@ -3454,8 +3368,9 @@ class VST3PluginInstance final : public AudioPluginInstance VSTComSmartPtr processor; processor.loadFrom (component.get()); - for (const auto isInput : { true, false }) + for (int dirIdx = 0; dirIdx < 2; ++dirIdx) { + const bool isInput = (dirIdx == 0); const Vst::BusDirection dir = (isInput ? Vst::kInput : Vst::kOutput); const int numBuses = component->getBusCount (Vst::kAudio, dir); @@ -3471,8 +3386,7 @@ class VST3PluginInstance final : public AudioPluginInstance Vst::SpeakerArrangement arr; if (processor != nullptr && processor->getBusArrangement (dir, i, arr) == kResultOk) - if (const auto set = getChannelSetForSpeakerArrangement (arr)) - layout = *set; + layout = getChannelSetForSpeakerArrangement (arr); busProperties.addBus (isInput, toString (info.name), layout, (info.flags & Vst::BusInfo::kDefaultActive) != 0); @@ -3504,7 +3418,7 @@ class VST3PluginInstance final : public AudioPluginInstance // call was processBlockBypassed, otherwise do nothing if (processBlockBypassedCalled) { - if (bypassParam != nullptr && (approximatelyEqual (bypassParam->getValue(), 0.0f) || ! lastProcessBlockCallWasBypass)) + if (bypassParam != nullptr && (bypassParam->getValue() == 0.0f || ! lastProcessBlockCallWasBypass)) bypassParam->setValue (1.0f); } else @@ -3690,7 +3604,7 @@ tresult VST3HostContext::performEdit (Vst::ParamID paramID, Vst::ParamValue valu param->setValueNotifyingHost ((float) valueNormalised); // did the plug-in already update the parameter internally - if (! approximatelyEqual (plugin->editController->getParamNormalized (paramID), valueNormalised)) + if (plugin->editController->getParamNormalized (paramID) != (float) valueNormalised) return plugin->editController->setParamNormalized (paramID, valueNormalised); return kResultTrue; @@ -3863,18 +3777,7 @@ bool VST3PluginFormat::setStateFromVSTPresetFile (AudioPluginInstance* api, cons void VST3PluginFormat::findAllTypesForFile (OwnedArray& results, const String& fileOrIdentifier) { - if (! fileMightContainThisPluginType (fileOrIdentifier)) - return; - - if (const auto fast = DescriptionLister::findDescriptionsFast (File (fileOrIdentifier)); ! fast.empty()) - { - for (const auto& d : fast) - results.add (new PluginDescription (d)); - - return; - } - - for (const auto& file : getLibraryPaths (fileOrIdentifier)) + if (fileMightContainThisPluginType (fileOrIdentifier)) { /** Since there is no apparent indication if a VST3 plugin is a shell or not, @@ -3882,16 +3785,21 @@ void VST3PluginFormat::findAllTypesForFile (OwnedArray& resul for every housed plugin. */ - VSTComSmartPtr pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (file) - .getPluginFactory()); - - if (pluginFactory == nullptr) - continue; + VSTComSmartPtr pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (fileOrIdentifier) + .getPluginFactory()); - VSTComSmartPtr host (new VST3HostContext()); + if (pluginFactory != nullptr) + { + VSTComSmartPtr host (new VST3HostContext()); + DescriptionLister lister (host, pluginFactory); + lister.findDescriptionsAndPerform (File (fileOrIdentifier)); - for (const auto& d : DescriptionLister::findDescriptionsSlow (*host, *pluginFactory, File (file))) - results.add (new PluginDescription (d)); + results.addCopiesOf (lister.list); + } + else + { + jassertfalse; + } } } @@ -3912,12 +3820,13 @@ void VST3PluginFormat::createARAFactoryAsync (const PluginDescription& descripti } static std::unique_ptr createVST3Instance (VST3PluginFormat& format, - const PluginDescription& description, - const File& file) + const PluginDescription& description) { if (! format.fileMightContainThisPluginType (description.fileOrIdentifier)) return nullptr; + const File file { description.fileOrIdentifier }; + struct ScopedWorkingDirectory { ~ScopedWorkingDirectory() { previousWorkingDirectory.setAsCurrentWorkingDirectory(); } @@ -3945,33 +3854,15 @@ static std::unique_ptr createVST3Instance (VST3PluginFormat return instance; } -StringArray VST3PluginFormat::getLibraryPaths (const String& fileOrIdentifier) -{ - #if JUCE_WINDOWS - if (! File (fileOrIdentifier).existsAsFile()) - { - StringArray files; - recursiveFileSearch (files, fileOrIdentifier, true); - return files; - } - #endif - - return { fileOrIdentifier }; -} - void VST3PluginFormat::createPluginInstance (const PluginDescription& description, double, int, PluginCreationCallback callback) { - for (const auto& file : getLibraryPaths (description.fileOrIdentifier)) - { - if (auto result = createVST3Instance (*this, description, file)) - { - callback (std::move (result), {}); - return; - } - } + auto result = createVST3Instance (*this, description); + + const auto errorMsg = result == nullptr ? TRANS ("Unable to load XXX plug-in file").replace ("XXX", "VST-3") + : String(); - callback (nullptr, TRANS ("Unable to load XXX plug-in file").replace ("XXX", "VST-3")); + callback (std::move (result), errorMsg); } bool VST3PluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const @@ -3983,7 +3874,12 @@ bool VST3PluginFormat::fileMightContainThisPluginType (const String& fileOrIdent { auto f = File::createFileWithoutCheckingPath (fileOrIdentifier); - return f.hasFileExtension (".vst3") && f.exists(); + return f.hasFileExtension (".vst3") + #if JUCE_MAC || JUCE_LINUX || JUCE_BSD + && f.exists(); + #else + && f.existsAsFile(); + #endif } String VST3PluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h index d76a15cf..d192029e 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h @@ -76,7 +76,6 @@ class JUCE_API VST3PluginFormat : public AudioPluginFormat int initialBufferSize, PluginCreationCallback) override; bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const override; void recursiveFileSearch (StringArray&, const File&, bool recursive); - StringArray getLibraryPaths (const String&); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginFormat) }; diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat_test.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat_test.cpp index 27c10a2d..305a705c 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat_test.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat_test.cpp @@ -550,23 +550,6 @@ class VST3PluginFormatTests : public UnitTest expect (clientBuffers[3].channelBuffers64[0] == nullptr); } } - - beginTest ("Speaker layout conversions"); - { - using namespace Steinberg::Vst::SpeakerArr; - - for (const auto& [channelSet, arr] : { std::tuple (AudioChannelSet::ambisonic (1), kAmbi1stOrderACN), - std::tuple (AudioChannelSet::ambisonic (2), kAmbi2cdOrderACN), - std::tuple (AudioChannelSet::ambisonic (3), kAmbi3rdOrderACN), - std::tuple (AudioChannelSet::ambisonic (4), kAmbi4thOrderACN), - std::tuple (AudioChannelSet::ambisonic (5), kAmbi5thOrderACN), - std::tuple (AudioChannelSet::ambisonic (6), kAmbi6thOrderACN), - std::tuple (AudioChannelSet::ambisonic (7), kAmbi7thOrderACN), }) - { - expect (getVst3SpeakerArrangement (channelSet) == arr); - expect (channelSet == getChannelSetForSpeakerArrangement (arr)); - } - } } private: @@ -601,7 +584,7 @@ class VST3PluginFormatTests : public UnitTest bool allMatch (int channel, float value) const { const auto& buf = buffers[(size_t) channel]; - return std::all_of (buf.begin(), buf.end(), [&] (auto x) { return exactlyEqual (x, value); }); + return std::all_of (buf.begin(), buf.end(), [&] (auto x) { return x == value; }); } bool isClear (int channel) const @@ -624,13 +607,13 @@ class VST3PluginFormatTests : public UnitTest static bool channelStartsWithValue (Steinberg::Vst::AudioBusBuffers& bus, size_t index, float value) { - return exactlyEqual (bus.channelBuffers32[index][0], value); + return bus.channelBuffers32[index][0] == value; } static bool allMatch (const AudioBuffer& buf, int index, float value) { const auto* ptr = buf.getReadPointer (index); - return std::all_of (ptr, ptr + buf.getNumSamples(), [&] (auto x) { return exactlyEqual (x, value); }); + return std::all_of (ptr, ptr + buf.getNumSamples(), [&] (auto x) { return x == value; }); } struct MultiBusBuffers diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index 893d244f..1f38a2a9 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -904,7 +904,7 @@ struct VSTPluginInstance final : public AudioPluginInstance, { const ScopedLock sl (pluginInstance.lock); - if (! approximatelyEqual (effect->getParameter (effect, getParameterIndex()), newValue)) + if (effect->getParameter (effect, getParameterIndex()) != newValue) effect->setParameter (effect, getParameterIndex(), newValue); } } @@ -2004,7 +2004,7 @@ struct VSTPluginInstance final : public AudioPluginInstance, void setValue (float newValue) override { - currentValue = (! approximatelyEqual (newValue, 0.0f)); + currentValue = (newValue != 0.0f); if (parent.vstSupportsBypass) parent.dispatch (Vst2::effSetBypass, 0, currentValue ? 1 : 0, nullptr, 0.0f); @@ -2028,7 +2028,7 @@ struct VSTPluginInstance final : public AudioPluginInstance, float getValue() const override { return currentValue; } float getDefaultValue() const override { return 0.0f; } String getName (int /*maximumStringLength*/) const override { return "Bypass"; } - String getText (float value, int) const override { return (! approximatelyEqual (value, 0.0f) ? TRANS("On") : TRANS("Off")); } + String getText (float value, int) const override { return (value != 0.0f ? TRANS("On") : TRANS("Off")); } bool isAutomatable() const override { return true; } bool isDiscrete() const override { return true; } bool isBoolean() const override { return true; } @@ -2740,7 +2740,7 @@ struct VSTPluginInstance final : public AudioPluginInstance, { if (processBlockBypassedCalled) { - if (approximatelyEqual (bypassParam->getValue(), 0.0f) || ! lastProcessBlockCallWasBypass) + if (bypassParam->getValue() == 0.0f || ! lastProcessBlockCallWasBypass) bypassParam->setValue (1.0f); } else @@ -2805,14 +2805,13 @@ struct VSTPluginWindow : public AudioProcessorEditor, ~VSTPluginWindow() override { - activeVSTWindows.removeFirstMatchingValue (this); - closePluginWindow(); #if JUCE_MAC cocoaWrapper.reset(); #endif + activeVSTWindows.removeFirstMatchingValue (this); plugin.editorBeingDeleted (this); } @@ -3038,8 +3037,8 @@ struct VSTPluginWindow : public AudioProcessorEditor, void broughtToFront() override { - if (activeVSTWindows.removeFirstMatchingValue (this) != -1) - activeVSTWindows.add (this); + activeVSTWindows.removeFirstMatchingValue (this); + activeVSTWindows.add (this); #if JUCE_MAC dispatch (Vst2::effEditTop, 0, 0, nullptr, 0); diff --git a/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.cpp b/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.cpp index 27ed7c32..8bbc3a36 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.cpp @@ -43,9 +43,7 @@ //============================================================================== #if (JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_VST3) && (JUCE_LINUX || JUCE_BSD) - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wvariadic-macros") #include - JUCE_END_IGNORE_WARNINGS_GCC_LIKE #include #include #undef KeyPress @@ -222,8 +220,7 @@ struct NSViewComponentWithParent : public NSViewComponent, #include "utilities/juce_AudioProcessorValueTreeState.cpp" #include "utilities/juce_PluginHostType.cpp" #include "utilities/juce_NativeScaleFactorNotifier.cpp" -#include "utilities/juce_AAXClientExtensions.cpp" -#include "utilities/juce_VST2ClientExtensions.cpp" +#include "utilities/juce_VSTCallbackHandler.cpp" #include "utilities/ARA/juce_ARA_utils.cpp" #include "format_types/juce_LV2PluginFormat.cpp" diff --git a/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h b/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h index 87d4c7be..aa618eeb 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h +++ b/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h @@ -35,7 +35,7 @@ ID: juce_audio_processors vendor: juce - version: 7.0.7 + version: 7.0.5 name: JUCE audio processor classes description: Classes for loading and playing VST, AU, LADSPA, or internally-generated audio processors. website: http://www.juce.com/juce @@ -130,8 +130,7 @@ #endif //============================================================================== -#include "utilities/juce_AAXClientExtensions.h" -#include "utilities/juce_VST2ClientExtensions.h" +#include "utilities/juce_VSTCallbackHandler.h" #include "utilities/juce_VST3ClientExtensions.h" #include "utilities/juce_NativeScaleFactorNotifier.h" #include "format_types/juce_ARACommon.h" diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 7ea8d23b..f5815241 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -437,20 +437,18 @@ void AudioProcessor::validateParameter (AudioProcessorParameter* param) See the documentation for AudioProcessorParameter(int) for more information. */ #if JucePlugin_Build_AU - static std::once_flag flag; - if (wrapperType != wrapperType_Undefined && param->getVersionHint() == 0) - std::call_once (flag, [] { jassertfalse; }); + jassert (wrapperType == wrapperType_Undefined || param->getVersionHint() != 0); #endif } void AudioProcessor::checkForDuplicateTrimmedParamID ([[maybe_unused]] AudioProcessorParameter* param) { #if JUCE_DEBUG && ! JUCE_DISABLE_CAUTIOUS_PARAMETER_ID_CHECKING - if (auto* withID = dynamic_cast (param)) + if (auto* withID = dynamic_cast (param)) { constexpr auto maximumSafeAAXParameterIdLength = 31; - const auto paramID = withID->getParameterID(); + const auto paramID = withID->paramID; // If you hit this assertion, a parameter name is too long to be supported // by the AAX plugin format. @@ -478,9 +476,9 @@ void AudioProcessor::checkForDuplicateTrimmedParamID ([[maybe_unused]] AudioProc void AudioProcessor::checkForDuplicateParamID ([[maybe_unused]] AudioProcessorParameter* param) { #if JUCE_DEBUG - if (auto* withID = dynamic_cast (param)) + if (auto* withID = dynamic_cast (param)) { - auto insertResult = paramIDs.insert (withID->getParameterID()); + auto insertResult = paramIDs.insert (withID->paramID); // If you hit this assertion then the parameter ID is not unique jassert (insertResult.second); @@ -1154,7 +1152,7 @@ void AudioProcessor::Bus::updateChannelCount() noexcept void AudioProcessor::BusesProperties::addBus (bool isInput, const String& name, const AudioChannelSet& dfltLayout, bool isActivatedByDefault) { - jassert (! dfltLayout.isDisabled()); + jassert (dfltLayout.size() != 0); BusProperties props; @@ -1183,6 +1181,55 @@ AudioProcessor::BusesProperties AudioProcessor::BusesProperties::withOutput (con return retval; } +//============================================================================== +int32 AudioProcessor::getAAXPluginIDForMainBusConfig (const AudioChannelSet& mainInputLayout, + const AudioChannelSet& mainOutputLayout, + const bool idForAudioSuite) const +{ + int uniqueFormatId = 0; + + for (int dir = 0; dir < 2; ++dir) + { + const bool isInput = (dir == 0); + auto& set = (isInput ? mainInputLayout : mainOutputLayout); + int aaxFormatIndex = 0; + + const AudioChannelSet sets[] + { + AudioChannelSet::disabled(), + AudioChannelSet::mono(), + AudioChannelSet::stereo(), + AudioChannelSet::createLCR(), + AudioChannelSet::createLCRS(), + AudioChannelSet::quadraphonic(), + AudioChannelSet::create5point0(), + AudioChannelSet::create5point1(), + AudioChannelSet::create6point0(), + AudioChannelSet::create6point1(), + AudioChannelSet::create7point0(), + AudioChannelSet::create7point1(), + AudioChannelSet::create7point0SDDS(), + AudioChannelSet::create7point1SDDS(), + AudioChannelSet::create7point0point2(), + AudioChannelSet::create7point1point2(), + AudioChannelSet::ambisonic (1), + AudioChannelSet::ambisonic (2), + AudioChannelSet::ambisonic (3) + }; + + const auto index = (int) std::distance (std::begin (sets), std::find (std::begin (sets), std::end (sets), set)); + + if (index != numElementsInArray (sets)) + aaxFormatIndex = index; + else + jassertfalse; + + uniqueFormatId = (uniqueFormatId << 8) | aaxFormatIndex; + } + + return (idForAudioSuite ? 0x6a796161 /* 'jyaa' */ : 0x6a636161 /* 'jcaa' */) + uniqueFormatId; +} + //============================================================================== const char* AudioProcessor::getWrapperTypeDescription (AudioProcessor::WrapperType type) noexcept { @@ -1201,63 +1248,6 @@ const char* AudioProcessor::getWrapperTypeDescription (AudioProcessor::WrapperTy } } -//============================================================================== -VST2ClientExtensions* AudioProcessor::getVST2ClientExtensions() -{ - if (auto* extensions = dynamic_cast (this)) - { - // To silence this jassert there are two options: - // - // 1. - Override AudioProcessor::getVST2ClientExtensions() and - // return the "this" pointer. - // - // - This option has the advantage of being quick and easy, - // and avoids the above dynamic_cast. - // - // 2. - Create a new object that inherits from VST2ClientExtensions. - // - // - Port your existing functionality from the AudioProcessor - // to the new object. - // - // - Return a pointer to the object in AudioProcessor::getVST2ClientExtensions(). - // - // - This option has the advantage of allowing you to break - // up your AudioProcessor into smaller composable objects. - jassertfalse; - return extensions; - } - - return nullptr; -} - -VST3ClientExtensions* AudioProcessor::getVST3ClientExtensions() -{ - if (auto* extensions = dynamic_cast (this)) - { - // To silence this jassert there are two options: - // - // 1. - Override AudioProcessor::getVST3ClientExtensions() and - // return the "this" pointer. - // - // - This option has the advantage of being quick and easy, - // and avoids the above dynamic_cast. - // - // 2. - Create a new object that inherits from VST3ClientExtensions. - // - // - Port your existing functionality from the AudioProcessor - // to the new object. - // - // - Return a pointer to the object in AudioProcessor::getVST3ClientExtensions(). - // - // - This option has the advantage of allowing you to break - // up your AudioProcessor into smaller composable objects. - jassertfalse; - return extensions; - } - - return nullptr; -} - //============================================================================== JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) @@ -1422,8 +1412,8 @@ const String AudioProcessor::getParameterName (int index) String AudioProcessor::getParameterID (int index) { // Don't use getParamChecked here, as this must also work for legacy plug-ins - if (auto* p = dynamic_cast (getParameters()[index])) - return p->getParameterID(); + if (auto* p = dynamic_cast (getParameters()[index])) + return p->paramID; return String (index); } diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.h index db98f02a..b0acefa9 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -40,11 +40,9 @@ namespace juce plugin, you should implement a global function called createPluginFilter() which creates and returns a new instance of your subclass. - @see AAXClientExtensions, VST2ClientExtensions, VST3ClientExtensions - @tags{Audio} */ -class JUCE_API AudioProcessor : private AAXClientExtensions +class JUCE_API AudioProcessor { protected: struct BusesProperties; @@ -311,37 +309,29 @@ class JUCE_API AudioProcessor : private AAXClientExtensions */ struct BusesLayout { - private: - template - static auto& getBuses (This& t, bool isInput) { return isInput ? t.inputBuses : t.outputBuses; } - - public: /** An array containing the list of input buses that this processor supports. */ Array inputBuses; /** An array containing the list of output buses that this processor supports. */ Array outputBuses; - auto& getBuses (bool isInput) const { return getBuses (*this, isInput); } - auto& getBuses (bool isInput) { return getBuses (*this, isInput); } - /** Get the number of channels of a particular bus */ int getNumChannels (bool isInput, int busIndex) const noexcept { - auto& bus = getBuses (isInput); + auto& bus = (isInput ? inputBuses : outputBuses); return isPositiveAndBelow (busIndex, bus.size()) ? bus.getReference (busIndex).size() : 0; } /** Get the channel set of a particular bus */ AudioChannelSet& getChannelSet (bool isInput, int busIndex) noexcept { - return getBuses (isInput).getReference (busIndex); + return (isInput ? inputBuses : outputBuses).getReference (busIndex); } /** Get the channel set of a particular bus */ AudioChannelSet getChannelSet (bool isInput, int busIndex) const noexcept { - return getBuses (isInput)[busIndex]; + return (isInput ? inputBuses : outputBuses)[busIndex]; } /** Get the input channel layout on the main bus. */ @@ -1178,30 +1168,18 @@ class JUCE_API AudioProcessor : private AAXClientExtensions void setRateAndBufferSizeDetails (double sampleRate, int blockSize) noexcept; //============================================================================== - /** Returns a reference to an object that implements AAX specific information regarding - this AudioProcessor. - */ - virtual AAXClientExtensions& getAAXClientExtensions() { return *this; } - - /** Returns a non-owning pointer to an object that implements VST2 specific information - regarding this AudioProcessor. - - By default, for backwards compatibility, this will attempt to dynamic-cast this - AudioProcessor to VST2ClientExtensions. - It is recommended to override this function to return a pointer directly to an object - of the correct type in order to avoid this dynamic cast. - */ - virtual VST2ClientExtensions* getVST2ClientExtensions(); - - /** Returns a non-owning pointer to an object that implements VST3 specific information - regarding this AudioProcessor. - - By default, for backwards compatibility, this will attempt to dynamic-cast this - AudioProcessor to VST3ClientExtensions. - It is recommended to override this function to return a pointer directly to an object - of the correct type in order to avoid this dynamic cast. - */ - virtual VST3ClientExtensions* getVST3ClientExtensions(); + /** AAX plug-ins need to report a unique "plug-in id" for every audio layout + configuration that your AudioProcessor supports on the main bus. Override this + function if you want your AudioProcessor to use a custom "plug-in id" (for example + to stay backward compatible with older versions of JUCE). + + The default implementation will compute a unique integer from the input and output + layout and add this value to the 4 character code 'jcaa' (for native AAX) or 'jyaa' + (for AudioSuite plug-ins). + */ + virtual int32 getAAXPluginIDForMainBusConfig (const AudioChannelSet& mainInputLayout, + const AudioChannelSet& mainOutputLayout, + bool idForAudioSuite) const; //============================================================================== /** Some plug-ins support sharing response curve data with the host so that it can diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp index 0d3104c1..7b20407c 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp @@ -225,16 +225,4 @@ ComponentPeer* AudioProcessorEditor::createNewPeer ([[maybe_unused]] int styleFl return Component::createNewPeer (styleFlags, nativeWindow); } -bool AudioProcessorEditor::wantsLayerBackedView() const -{ - #if JUCE_MODULE_AVAILABLE_juce_opengl && JUCE_MAC - if (@available (macOS 10.14, *)) - return true; - - return false; - #else - return true; - #endif -} - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h index 3bc5e3ac..df43af64 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h @@ -206,23 +206,6 @@ class JUCE_API AudioProcessorEditor : public Component */ std::unique_ptr resizableCorner; - /** The plugin wrapper will call this function to decide whether to use a layer-backed view to - host the editor on macOS and iOS. - - Layer-backed views generally provide better performance, and are recommended in most - situations. However, on older macOS versions (confirmed on 10.12 and 10.13), displaying an - OpenGL context inside a layer-backed view can lead to deadlocks, so it is recommended to - avoid layer-backed views when using OpenGL on these OS versions. - - The default behaviour of this function is to return false if and only if the juce_opengl - module is present and the current platform is macOS 10.13 or earlier. - - You may want to override this behaviour if your plugin has an option to enable and disable - OpenGL rendering. If you know your plugin editor will never use OpenGL rendering, you can - set this function to return true in all situations. - */ - virtual bool wantsLayerBackedView() const; - private: //============================================================================== struct AudioProcessorEditorListener : public ComponentListener diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp index ea9b8968..5e615433 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp @@ -139,15 +139,6 @@ class Connections using Connection = AudioProcessorGraph::Connection; using NodeAndChannel = AudioProcessorGraph::NodeAndChannel; -private: - static auto equalRange (const std::set& pins, const NodeID node) - { - return std::equal_range (pins.cbegin(), pins.cend(), node, ImplicitNode::compare); - } - - using Map = std::map>; - -public: static constexpr auto midiChannelIndex = AudioProcessorGraph::midiChannelIndex; bool addConnection (const Nodes& n, const Connection& c) @@ -188,7 +179,7 @@ class Connections for (auto& pair : sourcesForDestination) { - const auto range = equalRange (pair.second, n); + const auto range = std::equal_range (pair.second.cbegin(), pair.second.cend(), n, ImplicitNode::compare); result |= range.first != range.second; pair.second.erase (range.first, range.second); } @@ -240,8 +231,8 @@ class Connections return std::any_of (matchingDestinations.first, matchingDestinations.second, [srcID] (const auto& pair) { - const auto [begin, end] = equalRange (pair.second, srcID); - return begin != end; + const auto iter = std::lower_bound (pair.second.cbegin(), pair.second.cend(), srcID, ImplicitNode::compare); + return iter != pair.second.cend() && iter->nodeID == srcID; }); } @@ -285,44 +276,9 @@ class Connections bool operator== (const Connections& other) const { return sourcesForDestination == other.sourcesForDestination; } bool operator!= (const Connections& other) const { return sourcesForDestination != other.sourcesForDestination; } - class DestinationsForSources - { - public: - explicit DestinationsForSources (Map m) : map (std::move (m)) {} - - bool isSourceConnectedToDestinationNodeIgnoringChannel (const NodeAndChannel& source, NodeID dest, int channel) const - { - if (const auto destIter = map.find (source); destIter != map.cend()) - { - const auto [begin, end] = equalRange (destIter->second, dest); - return std::any_of (begin, end, [&] (const NodeAndChannel& nodeAndChannel) - { - return nodeAndChannel != NodeAndChannel { dest, channel }; - }); - } - - return false; - } - - private: - Map map; - }; - - /* Reverses the graph, to allow fast lookup by source. - This is expensive, don't call this more than necessary! - */ - auto getDestinationsForSources() const - { - Map destinationsForSources; - - for (const auto& [destination, sources] : sourcesForDestination) - for (const auto& source : sources) - destinationsForSources[source].insert (destination); - - return DestinationsForSources (std::move (destinationsForSources)); - } - private: + using Map = std::map>; + struct SearchState { std::set visited; @@ -395,14 +351,14 @@ class NodeStates /* Called from prepareToPlay and releaseResources with the PrepareSettings that should be used next time the graph is rebuilt. */ - void setState (std::optional newSettings) + void setState (Optional newSettings) { const std::lock_guard lock (mutex); next = newSettings; } /* Call from the audio thread only. */ - std::optional getLastRequestedSettings() const { return next; } + Optional getLastRequestedSettings() const { return next; } /* Call from the main thread only! @@ -419,7 +375,7 @@ class NodeStates Returns the settings that were applied to the nodes. */ - std::optional applySettings (const Nodes& n) + Optional applySettings (const Nodes& n) { const auto settingsChanged = [this] { @@ -455,7 +411,7 @@ class NodeStates preparedNodes.clear(); } - if (current.has_value()) + if (current.hasValue()) { for (const auto& node : n.getNodes()) { @@ -474,24 +430,10 @@ class NodeStates return current; } - /* Call from the main thread to indicate that a node has been removed from the graph. - */ - void removeNode (const NodeID n) - { - preparedNodes.erase (n); - } - - /* Call from the main thread to indicate that all nodes have been removed from the graph. - */ - void clear() - { - preparedNodes.clear(); - } - private: std::mutex mutex; std::set preparedNodes; - std::optional current, next; + Optional current, next; }; //============================================================================== @@ -500,17 +442,8 @@ struct GraphRenderSequence { using Node = AudioProcessorGraph::Node; - struct GlobalIO - { - AudioBuffer& audioIn; - AudioBuffer& audioOut; - MidiBuffer& midiIn; - MidiBuffer& midiOut; - }; - struct Context { - GlobalIO globalIO; AudioPlayHead* audioPlayHead; int numSamples; }; @@ -549,12 +482,7 @@ struct GraphRenderSequence currentMidiOutputBuffer.clear(); { - const Context context { { *currentAudioInputBuffer, - currentAudioOutputBuffer, - *currentMidiInputBuffer, - currentMidiOutputBuffer }, - audioPlayHead, - numSamples }; + const Context context { audioPlayHead, numSamples }; for (const auto& op : renderOps) op->process (context); @@ -762,30 +690,7 @@ struct GraphRenderSequence int totalNumChans, int midiBuffer) { - auto op = [&]() -> std::unique_ptr - { - if (auto* ioNode = dynamic_cast (node->getProcessor())) - { - switch (ioNode->getType()) - { - case AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode: - return std::make_unique (node, audioChannelsUsed, totalNumChans, midiBuffer); - - case AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode: - return std::make_unique (node, audioChannelsUsed, totalNumChans, midiBuffer); - - case AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode: - return std::make_unique (node, audioChannelsUsed, totalNumChans, midiBuffer); - - case AudioProcessorGraph::AudioGraphIOProcessor::midiOutputNode: - return std::make_unique (node, audioChannelsUsed, totalNumChans, midiBuffer); - } - } - - return std::make_unique (node, audioChannelsUsed, totalNumChans, midiBuffer); - }(); - - renderOps.push_back (std::move (op)); + renderOps.push_back (std::make_unique (node, audioChannelsUsed, totalNumChans, midiBuffer)); } void prepareBuffers (int blockSize) @@ -833,12 +738,12 @@ struct GraphRenderSequence virtual void process (const Context&) = 0; }; - struct NodeOp : public RenderOp + struct ProcessOp : public RenderOp { - NodeOp (const Node::Ptr& n, - const Array& audioChannelsUsed, - int totalNumChans, - int midiBufferIndex) + ProcessOp (const Node::Ptr& n, + const Array& audioChannelsUsed, + int totalNumChans, + int midiBufferIndex) : node (n), processor (*n->getProcessor()), audioChannelsToUse (audioChannelsUsed), @@ -849,7 +754,7 @@ struct GraphRenderSequence audioChannelsToUse.add (0); } - void prepare (FloatType* const* renderBuffer, MidiBuffer* buffers) final + void prepare (FloatType* const* renderBuffer, MidiBuffer* buffers) override { for (size_t i = 0; i < audioChannels.size(); ++i) audioChannels[i] = renderBuffer[audioChannelsToUse.getUnchecked ((int) i)]; @@ -857,7 +762,7 @@ struct GraphRenderSequence midiBuffer = buffers + midiBufferToUse; } - void process (const Context& c) final + void process (const Context& c) override { processor.setPlayHead (c.audioPlayHead); @@ -872,140 +777,64 @@ struct GraphRenderSequence AudioBuffer buffer { audioChannels.data(), numAudioChannels, c.numSamples }; + const ScopedLock lock (processor.getCallbackLock()); + if (processor.isSuspended()) - { buffer.clear(); - } else - { - const auto bypass = node->isBypassed() && processor.getBypassParameter() == nullptr; - processWithBuffer (c.globalIO, bypass, buffer, *midiBuffer); - } - } - - virtual void processWithBuffer (const GlobalIO&, bool bypass, AudioBuffer& audio, MidiBuffer& midi) = 0; - - const Node::Ptr node; - AudioProcessor& processor; - MidiBuffer* midiBuffer = nullptr; - - Array audioChannelsToUse; - std::vector audioChannels; - const int midiBufferToUse; - }; - - struct ProcessOp : public NodeOp - { - using NodeOp::NodeOp; - - void processWithBuffer (const GlobalIO&, bool bypass, AudioBuffer& audio, MidiBuffer& midi) final - { - callProcess (bypass, audio, midi); + callProcess (buffer, *midiBuffer); } - void callProcess (bool bypass, AudioBuffer& buffer, MidiBuffer& midi) + void callProcess (AudioBuffer& buffer, MidiBuffer& midi) { - if (this->processor.isUsingDoublePrecision()) + if (processor.isUsingDoublePrecision()) { tempBufferDouble.makeCopyOf (buffer, true); - processImpl (bypass, this->processor, tempBufferDouble, midi); + process (*node, tempBufferDouble, midi); buffer.makeCopyOf (tempBufferDouble, true); } else { - processImpl (bypass, this->processor, buffer, midi); + process (*node, buffer, midi); } } - void callProcess (bool bypass, AudioBuffer& buffer, MidiBuffer& midi) + void callProcess (AudioBuffer& buffer, MidiBuffer& midi) { - if (this->processor.isUsingDoublePrecision()) + if (processor.isUsingDoublePrecision()) { - processImpl (bypass, this->processor, buffer, midi); + process (*node, buffer, midi); } else { tempBufferFloat.makeCopyOf (buffer, true); - processImpl (bypass, this->processor, tempBufferFloat, midi); + process (*node, tempBufferFloat, midi); buffer.makeCopyOf (tempBufferFloat, true); } } template - static void processImpl (bool bypass, AudioProcessor& p, AudioBuffer& audio, MidiBuffer& midi) + static void process (const Node& node, AudioBuffer& audio, MidiBuffer& midi) { - if (bypass) - p.processBlockBypassed (audio, midi); + if (node.isBypassed() && node.getProcessor()->getBypassParameter() == nullptr) + node.getProcessor()->processBlockBypassed (audio, midi); else - p.processBlock (audio, midi); - } - - AudioBuffer tempBufferFloat, tempBufferDouble; - }; - - struct MidiInOp : public NodeOp - { - using NodeOp::NodeOp; - - void processWithBuffer (const GlobalIO& g, bool bypass, AudioBuffer& audio, MidiBuffer& midi) final - { - if (! bypass) - midi.addEvents (g.midiIn, 0, audio.getNumSamples(), 0); - } - }; - - struct MidiOutOp : public NodeOp - { - using NodeOp::NodeOp; - - void processWithBuffer (const GlobalIO& g, bool bypass, AudioBuffer& audio, MidiBuffer& midi) final - { - if (! bypass) - g.midiOut.addEvents (midi, 0, audio.getNumSamples(), 0); - } - }; - - struct AudioInOp : public NodeOp - { - using NodeOp::NodeOp; - - void processWithBuffer (const GlobalIO& g, bool bypass, AudioBuffer& audio, MidiBuffer&) final - { - if (bypass) - return; - - for (int i = jmin (g.audioIn.getNumChannels(), audio.getNumChannels()); --i >= 0;) - audio.copyFrom (i, 0, g.audioIn, i, 0, audio.getNumSamples()); + node.getProcessor()->processBlock (audio, midi); } - }; - - struct AudioOutOp : public NodeOp - { - using NodeOp::NodeOp; - void processWithBuffer (const GlobalIO& g, bool bypass, AudioBuffer& audio, MidiBuffer&) final - { - if (bypass) - return; + const Node::Ptr node; + AudioProcessor& processor; + MidiBuffer* midiBuffer = nullptr; - for (int i = jmin (g.audioOut.getNumChannels(), audio.getNumChannels()); --i >= 0;) - g.audioOut.addFrom (i, 0, audio, i, 0, audio.getNumSamples()); - } + Array audioChannelsToUse; + std::vector audioChannels; + AudioBuffer tempBufferFloat, tempBufferDouble; + const int midiBufferToUse; }; std::vector> renderOps; }; -//============================================================================== -struct SequenceAndLatency -{ - using RenderSequenceVariant = std::variant, - GraphRenderSequence>; - - RenderSequenceVariant sequence; - int latencySamples = 0; -}; - //============================================================================== class RenderSequenceBuilder { @@ -1017,12 +846,19 @@ class RenderSequenceBuilder static constexpr auto midiChannelIndex = AudioProcessorGraph::midiChannelIndex; - template - static SequenceAndLatency build (const Nodes& n, const Connections& c) + template + static auto build (const Nodes& n, const Connections& c) { - GraphRenderSequence sequence; + RenderSequence sequence; const RenderSequenceBuilder builder (n, c, sequence); - return { std::move (sequence), builder.totalLatency }; + + struct SequenceAndLatency + { + RenderSequence sequence; + int latencySamples = 0; + }; + + return SequenceAndLatency { std::move (sequence), builder.totalLatency }; } private: @@ -1125,7 +961,6 @@ class RenderSequenceBuilder //============================================================================== template int findBufferForInputAudioChannel (const Connections& c, - const Connections::DestinationsForSources& reversed, RenderSequence& sequence, Node& node, const int inputChan, @@ -1163,7 +998,7 @@ class RenderSequenceBuilder jassert (bufIndex >= 0); } - if (inputChan < numOuts && isBufferNeededLater (reversed, ourRenderingIndex, inputChan, src)) + if (inputChan < numOuts && isBufferNeededLater (c, ourRenderingIndex, inputChan, src)) { // can't mess up this channel because it's needed later by another node, // so we need to use a copy of it.. @@ -1190,7 +1025,7 @@ class RenderSequenceBuilder { auto sourceBufIndex = getBufferContaining (src); - if (sourceBufIndex >= 0 && ! isBufferNeededLater (reversed, ourRenderingIndex, inputChan, src)) + if (sourceBufIndex >= 0 && ! isBufferNeededLater (c, ourRenderingIndex, inputChan, src)) { // we've found one of our input chans that can be re-used.. reusableInputIndex = i; @@ -1244,7 +1079,7 @@ class RenderSequenceBuilder if (nodeDelay < maxLatency) { - if (! isBufferNeededLater (reversed, ourRenderingIndex, inputChan, src)) + if (! isBufferNeededLater (c, ourRenderingIndex, inputChan, src)) { sequence.addDelayChannelOp (srcIndex, maxLatency - nodeDelay); } @@ -1270,7 +1105,6 @@ class RenderSequenceBuilder template int findBufferForInputMidiChannel (const Connections& c, - const Connections::DestinationsForSources& reversed, RenderSequence& sequence, Node& node, int ourRenderingIndex) @@ -1297,7 +1131,7 @@ class RenderSequenceBuilder if (midiBufferToUse >= 0) { - if (isBufferNeededLater (reversed, ourRenderingIndex, midiChannelIndex, src)) + if (isBufferNeededLater (c, ourRenderingIndex, midiChannelIndex, src)) { // can't mess up this channel because it's needed later by another node, so we // need to use a copy of it.. @@ -1326,7 +1160,7 @@ class RenderSequenceBuilder auto sourceBufIndex = getBufferContaining (src); if (sourceBufIndex >= 0 - && ! isBufferNeededLater (reversed, ourRenderingIndex, midiChannelIndex, src)) + && ! isBufferNeededLater (c, ourRenderingIndex, midiChannelIndex, src)) { // we've found one of our input buffers that can be re-used.. reusableInputIndex = i; @@ -1375,7 +1209,6 @@ class RenderSequenceBuilder template void createRenderingOpsForNode (const Connections& c, - const Connections::DestinationsForSources& reversed, RenderSequence& sequence, Node& node, const int ourRenderingIndex) @@ -1392,7 +1225,6 @@ class RenderSequenceBuilder { // get a list of all the inputs to this node auto index = findBufferForInputAudioChannel (c, - reversed, sequence, node, inputChan, @@ -1415,7 +1247,7 @@ class RenderSequenceBuilder audioBuffers.getReference (index).channel = { node.nodeID, outputChan }; } - auto midiBufferToUse = findBufferForInputMidiChannel (c, reversed, sequence, node, ourRenderingIndex); + auto midiBufferToUse = findBufferForInputMidiChannel (c, sequence, node, ourRenderingIndex); if (processor.producesMidi()) midiBuffers.getReference (midiBufferToUse).channel = { node.nodeID, midiChannelIndex }; @@ -1454,7 +1286,7 @@ class RenderSequenceBuilder return -1; } - void markAnyUnusedBuffersAsFree (const Connections::DestinationsForSources& c, + void markAnyUnusedBuffersAsFree (const Connections& c, Array& buffers, const int stepIndex) { @@ -1463,25 +1295,34 @@ class RenderSequenceBuilder b.setFree(); } - bool isBufferNeededLater (const Connections::DestinationsForSources& c, - const int stepIndexToSearchFrom, - const int inputChannelOfIndexToIgnore, - const NodeAndChannel output) const + bool isBufferNeededLater (const Connections& c, + int stepIndexToSearchFrom, + int inputChannelOfIndexToIgnore, + NodeAndChannel output) const { - if (orderedNodes.size() <= stepIndexToSearchFrom) - return false; - - if (c.isSourceConnectedToDestinationNodeIgnoringChannel (output, - orderedNodes.getUnchecked (stepIndexToSearchFrom)->nodeID, - inputChannelOfIndexToIgnore)) + while (stepIndexToSearchFrom < orderedNodes.size()) { - return true; + auto* node = orderedNodes.getUnchecked (stepIndexToSearchFrom); + + if (output.isMIDI()) + { + if (inputChannelOfIndexToIgnore != midiChannelIndex + && c.isConnected ({ { output.nodeID, midiChannelIndex }, + { node->nodeID, midiChannelIndex } })) + return true; + } + else + { + for (int i = 0; i < node->getProcessor()->getTotalNumInputChannels(); ++i) + if (i != inputChannelOfIndexToIgnore && c.isConnected ({ output, { node->nodeID, i } })) + return true; + } + + inputChannelOfIndexToIgnore = -1; + ++stepIndexToSearchFrom; } - return std::any_of (orderedNodes.begin() + stepIndexToSearchFrom + 1, orderedNodes.end(), [&] (const auto* node) - { - return c.isSourceConnectedToDestinationNodeIgnoringChannel (output, node->nodeID, -1); - }); + return false; } template @@ -1491,13 +1332,11 @@ class RenderSequenceBuilder audioBuffers.add (AssignedBuffer::createReadOnlyEmpty()); // first buffer is read-only zeros midiBuffers .add (AssignedBuffer::createReadOnlyEmpty()); - const auto reversed = c.getDestinationsForSources(); - for (int i = 0; i < orderedNodes.size(); ++i) { - createRenderingOpsForNode (c, reversed, sequence, *orderedNodes.getUnchecked (i), i); - markAnyUnusedBuffersAsFree (reversed, audioBuffers, i); - markAnyUnusedBuffersAsFree (reversed, midiBuffers, i); + createRenderingOpsForNode (c, sequence, *orderedNodes.getUnchecked (i), i); + markAnyUnusedBuffersAsFree (c, audioBuffers, i); + markAnyUnusedBuffersAsFree (c, midiBuffers, i); } sequence.numBuffersNeeded = audioBuffers.size(); @@ -1517,77 +1356,95 @@ class RenderSequence public: using AudioGraphIOProcessor = AudioProcessorGraph::AudioGraphIOProcessor; - RenderSequence (const PrepareSettings s, const Nodes& n, const Connections& c) - : RenderSequence (s, s.precision == AudioProcessor::ProcessingPrecision::singlePrecision - ? RenderSequenceBuilder::build (n, c) - : RenderSequenceBuilder::build (n, c)) + RenderSequence (PrepareSettings s, const Nodes& n, const Connections& c) + : RenderSequence (s, + RenderSequenceBuilder::build> (n, c), + RenderSequenceBuilder::build> (n, c)) { } - template - void process (AudioBuffer& audio, MidiBuffer& midi, AudioPlayHead* playHead) + void process (AudioBuffer& audio, MidiBuffer& midi, AudioPlayHead* playHead) { - if (auto* s = std::get_if> (&sequence.sequence)) - s->perform (audio, midi, playHead); - else - jassertfalse; // Not prepared for this audio format! + renderSequenceF.perform (audio, midi, playHead); } - int getLatencySamples() const { return sequence.latencySamples; } - PrepareSettings getSettings() const { return settings; } + void process (AudioBuffer& audio, MidiBuffer& midi, AudioPlayHead* playHead) + { + renderSequenceD.perform (audio, midi, playHead); + } -private: - template - static void visitRenderSequence (This& t, Callback&& callback) + void processIO (AudioGraphIOProcessor& io, AudioBuffer& audio, MidiBuffer& midi) { - if (auto* sequence = std::get_if> (&t.sequence.sequence)) return callback (*sequence); - if (auto* sequence = std::get_if> (&t.sequence.sequence)) return callback (*sequence); - jassertfalse; + processIOBlock (io, renderSequenceF, audio, midi); } - RenderSequence (const PrepareSettings s, SequenceAndLatency&& built) - : settings (s), sequence (std::move (built)) + void processIO (AudioGraphIOProcessor& io, AudioBuffer& audio, MidiBuffer& midi) { - visitRenderSequence (*this, [&] (auto& seq) { seq.prepareBuffers (settings.blockSize); }); + processIOBlock (io, renderSequenceD, audio, midi); } - PrepareSettings settings; - SequenceAndLatency sequence; -}; + int getLatencySamples() const { return latencySamples; } + PrepareSettings getSettings() const { return settings; } -//============================================================================== -/* Holds information about a particular graph configuration, without sharing ownership of any - graph nodes. Can be checked for equality with other RenderSequenceSignature instances to see - whether two graph configurations match. -*/ -class RenderSequenceSignature -{ - auto tie() const { return std::tie (settings, connections, nodes); } +private: + template + static void processIOBlock (AudioGraphIOProcessor& io, + SequenceType& sequence, + AudioBuffer& buffer, + MidiBuffer& midiMessages) + { + switch (io.getType()) + { + case AudioGraphIOProcessor::audioOutputNode: + { + auto&& currentAudioOutputBuffer = sequence.currentAudioOutputBuffer; -public: - RenderSequenceSignature (const PrepareSettings s, const Nodes& n, const Connections& c) - : settings (s), connections (c), nodes (getNodeMap (n)) {} + for (int i = jmin (currentAudioOutputBuffer.getNumChannels(), buffer.getNumChannels()); --i >= 0;) + currentAudioOutputBuffer.addFrom (i, 0, buffer, i, 0, buffer.getNumSamples()); - bool operator== (const RenderSequenceSignature& other) const { return tie() == other.tie(); } - bool operator!= (const RenderSequenceSignature& other) const { return tie() != other.tie(); } + break; + } -private: - using NodeMap = std::map; + case AudioGraphIOProcessor::audioInputNode: + { + auto* currentInputBuffer = sequence.currentAudioInputBuffer; - static NodeMap getNodeMap (const Nodes& n) - { - const auto& nodeRefs = n.getNodes(); - NodeMap result; + for (int i = jmin (currentInputBuffer->getNumChannels(), buffer.getNumChannels()); --i >= 0;) + buffer.copyFrom (i, 0, *currentInputBuffer, i, 0, buffer.getNumSamples()); - for (const auto& node : nodeRefs) - result.emplace (node->nodeID, node->getProcessor()->getBusesLayout()); + break; + } - return result; + case AudioGraphIOProcessor::midiOutputNode: + sequence.currentMidiOutputBuffer.addEvents (midiMessages, 0, buffer.getNumSamples(), 0); + break; + + case AudioGraphIOProcessor::midiInputNode: + midiMessages.addEvents (*sequence.currentMidiInputBuffer, 0, buffer.getNumSamples(), 0); + break; + + default: + break; + } + } + + template + RenderSequence (PrepareSettings s, Float f, Double d) + : settings (s), + renderSequenceF (std::move (f.sequence)), + renderSequenceD (std::move (d.sequence)), + latencySamples (f.latencySamples) + { + jassert (f.latencySamples == d.latencySamples); + + renderSequenceF.prepareBuffers (settings.blockSize); + renderSequenceD.prepareBuffers (settings.blockSize); } PrepareSettings settings; - Connections connections; - NodeMap nodes; + GraphRenderSequence renderSequenceF; + GraphRenderSequence renderSequenceD; + int latencySamples = 0; }; //============================================================================== @@ -1618,7 +1475,7 @@ class RenderSequenceExchange : private Timer isNew = true; } - /* Call from the audio thread only. */ + /** Call from the audio thread only. */ void updateAudioThreadState() { const SpinLock::ScopedTryLockType lock (mutex); @@ -1631,7 +1488,7 @@ class RenderSequenceExchange : private Timer } } - /* Call from the audio thread only. */ + /** Call from the audio thread only. */ RenderSequence* getAudioThreadState() const { return audioThreadState.get(); } private: @@ -1677,11 +1534,17 @@ bool AudioProcessorGraph::Connection::operator< (const Connection& other) const } //============================================================================== -class AudioProcessorGraph::Pimpl +class AudioProcessorGraph::Pimpl : public AsyncUpdater { public: explicit Pimpl (AudioProcessorGraph& o) : owner (&o) {} + ~Pimpl() override + { + cancelPendingUpdate(); + clear (UpdateKind::sync); + } + const auto& getNodes() const { return nodes.getNodes(); } void clear (UpdateKind updateKind) @@ -1691,7 +1554,6 @@ class AudioProcessorGraph::Pimpl nodes = Nodes{}; connections = Connections{}; - nodeStates.clear(); topologyChanged (updateKind); } @@ -1730,7 +1592,6 @@ class AudioProcessorGraph::Pimpl { connections.disconnectNode (nodeID); auto result = nodes.removeNode (nodeID); - nodeStates.removeNode (nodeID); topologyChanged (updateKind); return result; } @@ -1826,17 +1687,6 @@ class AudioProcessorGraph::Pimpl topologyChanged (UpdateKind::sync); } - void rebuild (UpdateKind updateKind) - { - if (updateKind == UpdateKind::none) - return; - - if (updateKind == UpdateKind::sync && MessageManager::getInstance()->isThisTheMessageThread()) - handleAsyncUpdate(); - else - updater.triggerAsyncUpdate(); - } - void reset() { for (auto* n : getNodes()) @@ -1893,41 +1743,36 @@ class AudioProcessorGraph::Pimpl void topologyChanged (UpdateKind updateKind) { owner->sendChangeMessage(); - rebuild (updateKind); + + if (updateKind == UpdateKind::sync && MessageManager::getInstance()->isThisTheMessageThread()) + handleAsyncUpdate(); + else + triggerAsyncUpdate(); } - void handleAsyncUpdate() + void handleAsyncUpdate() override { if (const auto newSettings = nodeStates.applySettings (nodes)) { for (const auto node : nodes.getNodes()) setParentGraph (node->getProcessor()); - const RenderSequenceSignature newSignature (*newSettings, nodes, connections); - - if (std::exchange (lastBuiltSequence, newSignature) != newSignature) - { - auto sequence = std::make_unique (*newSettings, nodes, connections); - owner->setLatencySamples (sequence->getLatencySamples()); - renderSequenceExchange.set (std::move (sequence)); - } + auto sequence = std::make_unique (*newSettings, nodes, connections); + owner->setLatencySamples (sequence->getLatencySamples()); + renderSequenceExchange.set (std::move (sequence)); } else { - lastBuiltSequence.reset(); renderSequenceExchange.set (nullptr); } } - AudioProcessorGraph* owner = nullptr; Nodes nodes; Connections connections; NodeStates nodeStates; RenderSequenceExchange renderSequenceExchange; NodeID lastNodeID; - std::optional lastBuiltSequence; - LockingAsyncUpdater updater { [this] { handleAsyncUpdate(); } }; }; //============================================================================== @@ -1954,7 +1799,6 @@ AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (NodeID x) const bool AudioProcessorGraph::disconnectNode (NodeID nodeID, UpdateKind updateKind) { return pimpl->disconnectNode (nodeID, updateKind); } void AudioProcessorGraph::releaseResources() { return pimpl->releaseResources(); } bool AudioProcessorGraph::removeIllegalConnections (UpdateKind updateKind) { return pimpl->removeIllegalConnections (updateKind); } -void AudioProcessorGraph::rebuild() { return pimpl->rebuild (UpdateKind::sync); } void AudioProcessorGraph::reset() { return pimpl->reset(); } bool AudioProcessorGraph::canConnect (const Connection& c) const { return pimpl->canConnect (c); } bool AudioProcessorGraph::isConnected (const Connection& c) const noexcept { return pimpl->isConnected (c); } @@ -2048,16 +1892,20 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::supportsDoublePrecisionProcessi return true; } -void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer&, MidiBuffer&) +void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer& buffer, MidiBuffer& midiMessages) { - // The graph should never call this! - jassertfalse; + jassert (graph != nullptr); + + if (auto* state = graph->pimpl->getAudioThreadState()) + state->processIO (*this, buffer, midiMessages); } -void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer&, MidiBuffer&) +void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer& buffer, MidiBuffer& midiMessages) { - // The graph should never call this! - jassertfalse; + jassert (graph != nullptr); + + if (auto* state = graph->pimpl->getAudioThreadState()) + state->processIO (*this, buffer, midiMessages); } double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const @@ -2095,15 +1943,15 @@ void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorG { graph = newGraph; - if (graph == nullptr) - return; - - setPlayConfigDetails (type == audioOutputNode ? newGraph->getTotalNumOutputChannels() : 0, - type == audioInputNode ? newGraph->getTotalNumInputChannels() : 0, - getSampleRate(), - getBlockSize()); + if (graph != nullptr) + { + setPlayConfigDetails (type == audioOutputNode ? graph->getTotalNumOutputChannels() : 0, + type == audioInputNode ? graph->getTotalNumInputChannels() : 0, + getSampleRate(), + getBlockSize()); - updateHostDisplay(); + updateHostDisplay(); + } } //============================================================================== @@ -2210,36 +2058,6 @@ class AudioProcessorGraphTests : public UnitTest expect (graph.isAnInputTo (*nodes[nodes.size() - 1], *node)); } } - - beginTest ("large render sequence can be built"); - { - AudioProcessorGraph graph; - - std::vector nodeIDs; - - constexpr auto numNodes = 1000; - constexpr auto numChannels = 100; - - for (auto i = 0; i < numNodes; ++i) - { - nodeIDs.push_back (graph.addNode (BasicProcessor::make (BasicProcessor::getMultichannelProperties (numChannels), - MidiIn::yes, - MidiOut::yes))->nodeID); - } - - for (auto it = nodeIDs.begin(); it != std::prev (nodeIDs.end()); ++it) - for (auto channel = 0; channel < numChannels; ++channel) - expect (graph.addConnection ({ { it[0], channel }, { it[1], channel } })); - - const auto b = std::chrono::steady_clock::now(); - graph.prepareToPlay (44100.0, 512); - const auto e = std::chrono::steady_clock::now(); - const auto duration = std::chrono::duration_cast (e - b).count(); - - // No test here, but older versions of the graph would take forever to complete building - // this graph, so we just want to make sure that we finish the test without timing out. - logMessage ("render sequence built in " + String (duration) + " ms"); - } } private: @@ -2284,16 +2102,10 @@ class AudioProcessorGraphTests : public UnitTest static BusesProperties getStereoProperties() { - return BusesProperties().withInput ("in", AudioChannelSet::stereo()) + return BusesProperties().withInput ("in", AudioChannelSet::stereo()) .withOutput ("out", AudioChannelSet::stereo()); } - static BusesProperties getMultichannelProperties (int numChannels) - { - return BusesProperties().withInput ("in", AudioChannelSet::discreteChannels (numChannels)) - .withOutput ("out", AudioChannelSet::discreteChannels (numChannels)); - } - private: MidiIn midiIn; MidiOut midiOut; diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h index b6b60546..8190d754 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h @@ -127,7 +127,7 @@ class JUCE_API AudioProcessorGraph : public AudioProcessor, if (processor != nullptr) { if (auto* bypassParam = processor->getBypassParameter()) - return ! approximatelyEqual (bypassParam->getValue(), 0.0f); + return (bypassParam->getValue() != 0.0f); } return bypassed; @@ -210,11 +210,8 @@ class JUCE_API AudioProcessorGraph : public AudioProcessor, */ enum class UpdateKind { - sync, ///< Graph should be rebuilt immediately after modification. - async, ///< Graph rebuild should be delayed. If you make several changes to the graph - ///< inside the same call stack, these changes will be applied in one go. - none ///< Graph should not be rebuilt automatically. Use rebuild() to trigger a graph - ///< rebuild. + sync, ///< Indicates that the graph should be rebuilt immediately after modification. + async ///< Indicates that the graph rebuild should be deferred. }; //============================================================================== @@ -315,14 +312,6 @@ class JUCE_API AudioProcessorGraph : public AudioProcessor, */ bool removeIllegalConnections (UpdateKind = UpdateKind::sync); - /** Rebuilds the graph if necessary. - - This function will only ever rebuild the graph on the main thread. If this function is - called from another thread, the rebuild request will be dispatched asynchronously to the - main thread. - */ - void rebuild(); - //============================================================================== /** A special type of AudioProcessor that can live inside an AudioProcessorGraph in order to use the audio that comes into and out of the graph itself. diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp index 7e32b8b7..d153c3d2 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp @@ -293,7 +293,7 @@ class ChoiceParameterComponent : public ParameterComponent index = roundToInt (getParameter().getValue() * (float) (parameterValues.size() - 1)); } - box.setSelectedItemIndex (index, dontSendNotification); + box.setSelectedItemIndex (index); } void boxChanged() @@ -378,7 +378,7 @@ class SliderParameterComponent : public ParameterComponent { auto newVal = (float) slider.getValue(); - if (! approximatelyEqual (getParameter().getValue(), newVal)) + if (getParameter().getValue() != newVal) { if (! isDragging) getParameter().beginChangeGesture(); diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp index af4e0013..6af4db76 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp @@ -64,7 +64,7 @@ class PluginListComponent::TableModel : public TableListBoxModel if (columnId == nameCol) text = list.getBlacklistedFiles() [row - list.getNumTypes()]; else if (columnId == descCol) - text = TRANS ("Deactivated after failing to initialise correctly"); + text = TRANS("Deactivated after failing to initialise correctly"); } else { @@ -155,11 +155,11 @@ PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, Kno TableHeaderComponent& header = table.getHeader(); - header.addColumn (TRANS ("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards); - header.addColumn (TRANS ("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable); - header.addColumn (TRANS ("Category"), TableModel::categoryCol, 100, 100, 200); - header.addColumn (TRANS ("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300); - header.addColumn (TRANS ("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable); + header.addColumn (TRANS("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards); + header.addColumn (TRANS("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable); + header.addColumn (TRANS("Category"), TableModel::categoryCol, 100, 100, 200); + header.addColumn (TRANS("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300); + header.addColumn (TRANS("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable); table.setHeaderHeight (22); table.setRowHeight (20); @@ -289,7 +289,7 @@ void PluginListComponent::removePluginItem (int index) PopupMenu PluginListComponent::createOptionsMenu() { PopupMenu menu; - menu.addItem (PopupMenu::Item (TRANS ("Clear list")) + menu.addItem (PopupMenu::Item (TRANS("Clear list")) .setAction ([this] { list.clear(); })); menu.addSeparator(); @@ -306,18 +306,18 @@ PopupMenu PluginListComponent::createOptionsMenu() menu.addSeparator(); - menu.addItem (PopupMenu::Item (TRANS ("Remove selected plug-in from list")) + menu.addItem (PopupMenu::Item (TRANS("Remove selected plug-in from list")) .setEnabled (table.getNumSelectedRows() > 0) .setAction ([this] { removeSelectedPlugins(); })); - menu.addItem (PopupMenu::Item (TRANS ("Remove any plug-ins whose files no longer exist")) + menu.addItem (PopupMenu::Item (TRANS("Remove any plug-ins whose files no longer exist")) .setAction ([this] { removeMissingPlugins(); })); menu.addSeparator(); auto selectedRow = table.getSelectedRow(); - menu.addItem (PopupMenu::Item (TRANS ("Show folder containing selected plug-in")) + menu.addItem (PopupMenu::Item (TRANS("Show folder containing selected plug-in")) .setEnabled (canShowFolderForPlugin (list, selectedRow)) .setAction ([this, selectedRow] { showFolderForPlugin (list, selectedRow); })); @@ -337,10 +337,10 @@ PopupMenu PluginListComponent::createMenuForRow (int rowNumber) if (rowNumber >= 0 && rowNumber < tableModel->getNumRows()) { - menu.addItem (PopupMenu::Item (TRANS ("Remove plug-in from list")) + menu.addItem (PopupMenu::Item (TRANS("Remove plug-in from list")) .setAction ([this, rowNumber] { removePluginItem (rowNumber); })); - menu.addItem (PopupMenu::Item (TRANS ("Show folder containing plug-in")) + menu.addItem (PopupMenu::Item (TRANS("Show folder containing plug-in")) .setEnabled (canShowFolderForPlugin (list, rowNumber)) .setAction ([this, rowNumber] { showFolderForPlugin (list, rowNumber); })); } @@ -391,7 +391,7 @@ class PluginListComponent::Scanner : private Timer formatToScan (format), filesOrIdentifiersToScan (filesOrIdentifiers), propertiesToUse (properties), - pathChooserWindow (TRANS ("Select folders to scan..."), String(), MessageBoxIconType::NoIcon), + pathChooserWindow (TRANS("Select folders to scan..."), String(), MessageBoxIconType::NoIcon), progressWindow (title, text, MessageBoxIconType::NoIcon), numThreads (threads), allowAsync (allowPluginsWhichRequireAsynchronousInstantiation) @@ -417,8 +417,8 @@ class PluginListComponent::Scanner : private Timer pathList.setPath (path); pathChooserWindow.addCustomComponent (&pathList); - pathChooserWindow.addButton (TRANS ("Scan"), 1, KeyPress (KeyPress::returnKey)); - pathChooserWindow.addButton (TRANS ("Cancel"), 0, KeyPress (KeyPress::escapeKey)); + pathChooserWindow.addButton (TRANS("Scan"), 1, KeyPress (KeyPress::returnKey)); + pathChooserWindow.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); pathChooserWindow.enterModalState (true, ModalCallbackFunction::forComponent (startScanCallback, @@ -455,7 +455,6 @@ class PluginListComponent::Scanner : private Timer std::atomic finished { false }; std::unique_ptr pool; std::set initiallyBlacklistedFiles; - ScopedMessageBox messageBox; static void startScanCallback (int result, AlertWindow* alert, Scanner* scanner) { @@ -473,27 +472,22 @@ class PluginListComponent::Scanner : private Timer { for (int i = 0; i < pathList.getPath().getNumPaths(); ++i) { - auto f = pathList.getPath().getRawString (i); + auto f = pathList.getPath()[i]; - if (File::isAbsolutePath (f) && isStupidPath (File (f))) + if (isStupidPath (f)) { - auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, - TRANS ("Plugin Scanning"), - TRANS ("If you choose to scan folders that contain non-plugin files, " - "then scanning may take a long time, and can cause crashes when " - "attempting to load unsuitable files.") - + newLine - + TRANS ("Are you sure you want to scan the folder \"XYZ\"?") - .replace ("XYZ", f), - TRANS ("Scan")); - messageBox = AlertWindow::showScopedAsync (options, [this] (int result) - { - if (result != 0) - startScan(); - else - finishedScan(); - }); - + AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, + TRANS("Plugin Scanning"), + TRANS("If you choose to scan folders that contain non-plugin files, " + "then scanning may take a long time, and can cause crashes when " + "attempting to load unsuitable files.") + + newLine + + TRANS ("Are you sure you want to scan the folder \"XYZ\"?") + .replace ("XYZ", f.getFullPathName()), + TRANS ("Scan"), + String(), + nullptr, + ModalCallbackFunction::create (warnAboutStupidPathsCallback, this)); return; } } @@ -530,6 +524,14 @@ class PluginListComponent::Scanner : private Timer return false; } + static void warnAboutStupidPathsCallback (int result, Scanner* scanner) + { + if (result != 0) + scanner->startScan(); + else + scanner->finishedScan(); + } + void startScan() { pathChooserWindow.setVisible (false); @@ -547,13 +549,13 @@ class PluginListComponent::Scanner : private Timer propertiesToUse->saveIfNeeded(); } - progressWindow.addButton (TRANS ("Cancel"), 0, KeyPress (KeyPress::escapeKey)); + progressWindow.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); progressWindow.addProgressBarComponent (progress); progressWindow.enterModalState(); if (numThreads > 0) { - pool.reset (new ThreadPool (ThreadPoolOptions{}.withNumberOfThreads (numThreads))); + pool.reset (new ThreadPool (numThreads)); for (int i = numThreads; --i >= 0;) pool->addJob (new ScanJob (*this), true); @@ -597,7 +599,7 @@ class PluginListComponent::Scanner : private Timer if (finished) finishedScan(); else - progressWindow.setMessage (TRANS ("Testing") + ":\n\n" + pluginBeingScanned); + progressWindow.setMessage (TRANS("Testing") + ":\n\n" + pluginBeingScanned); } bool doNextScan() @@ -637,8 +639,8 @@ void PluginListComponent::scanFor (AudioPluginFormat& format) void PluginListComponent::scanFor (AudioPluginFormat& format, const StringArray& filesOrIdentifiersToScan) { currentScanner.reset (new Scanner (*this, format, filesOrIdentifiersToScan, propertiesToUse, allowAsync, numThreads, - dialogTitle.isNotEmpty() ? dialogTitle : TRANS ("Scanning for plug-ins..."), - dialogText.isNotEmpty() ? dialogText : TRANS ("Searching for all possible plug-in files..."))); + dialogTitle.isNotEmpty() ? dialogTitle : TRANS("Scanning for plug-ins..."), + dialogText.isNotEmpty() ? dialogText : TRANS("Searching for all possible plug-in files..."))); } bool PluginListComponent::isScanning() const noexcept @@ -670,12 +672,9 @@ void PluginListComponent::scanFinished (const StringArray& failedFiles, currentScanner.reset(); // mustn't delete this before using the failed files array if (! warnings.isEmpty()) - { - auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::InfoIcon, - TRANS ("Scan complete"), - warnings.joinIntoString ("\n\n")); - messageBox = AlertWindow::showScopedAsync (options, nullptr); - } + AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon, + TRANS("Scan complete"), + warnings.joinIntoString ("\n\n")); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h index 96c37502..42f7433c 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h @@ -130,8 +130,6 @@ class JUCE_API PluginListComponent : public Component, class Scanner; std::unique_ptr currentScanner; - ScopedMessageBox messageBox; - void scanFinished (const StringArray&, const std::vector&); void updateList(); void removeMissingPlugins(); diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.cpp b/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.cpp index 5c505e1f..6e819cda 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.cpp @@ -114,9 +114,6 @@ class ARADocumentControllerSpecialisation::ARADocumentControllerImpl : public A ARA::PlugIn::ContentReader* doCreatePlaybackRegionContentReader (ARA::PlugIn::PlaybackRegion* playbackRegion, ARA::ARAContentType type, const ARA::ARAContentTimeRange* range) noexcept override; - void doGetPlaybackRegionHeadAndTailTime (const ARA::PlugIn::PlaybackRegion* playbackRegion, - ARA::ARATimeDuration* headTime, - ARA::ARATimeDuration* tailTime) noexcept override; //============================================================================== // ARAAudioSource analysis @@ -662,13 +659,6 @@ ARA::PlugIn::ContentReader* ARADocumentControllerSpecialisation::ARADocumentCont return specialisation->doCreatePlaybackRegionContentReader (playbackRegion, type, range); } -void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doGetPlaybackRegionHeadAndTailTime (const ARA::PlugIn::PlaybackRegion* playbackRegion, - ARA::ARATimeDuration* headTime, - ARA::ARATimeDuration* tailTime) noexcept -{ - specialisation->doGetPlaybackRegionHeadAndTailTime (playbackRegion, headTime, tailTime); -} - bool ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doIsAudioSourceContentAnalysisIncomplete (const ARA::PlugIn::AudioSource* audioSource, ARA::ARAContentType type) noexcept { @@ -925,14 +915,6 @@ ARA::PlugIn::ContentReader* ARADocumentControllerSpecialisation::doCreatePlaybac return nullptr; } -void ARADocumentControllerSpecialisation::doGetPlaybackRegionHeadAndTailTime ([[maybe_unused]] const ARA::PlugIn::PlaybackRegion* playbackRegion, - ARA::ARATimeDuration* headTime, - ARA::ARATimeDuration* tailTime) -{ - *headTime = 0.0; - *tailTime = 0.0; -} - bool ARADocumentControllerSpecialisation::doIsAudioSourceContentAnalysisIncomplete ([[maybe_unused]] const ARA::PlugIn::AudioSource* audioSource, [[maybe_unused]] ARA::ARAContentType type) { diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.h index c9e315f1..8b04fbb0 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.h @@ -282,15 +282,6 @@ class ARADocumentControllerSpecialisation : public ARADocument::Listener, ARA::ARAContentType type, const ARA::ARAContentTimeRange* range); - /** Override to implement getPlaybackRegionHeadAndTailTime(). - - This function is called within - ARA::PlugIn::DocumentControllerDelegate::doGetPlaybackRegionHeadAndTailTime. - */ - virtual void doGetPlaybackRegionHeadAndTailTime (const ARA::PlugIn::PlaybackRegion* playbackRegion, - ARA::ARATimeDuration* headTime, - ARA::ARATimeDuration* tailTime); - //============================================================================== // ARAAudioSource analysis diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARAModelObjects.cpp b/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARAModelObjects.cpp index c2c17501..3db14330 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARAModelObjects.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARAModelObjects.cpp @@ -91,7 +91,7 @@ double ARARegionSequence::getCommonSampleRate() const const auto range = getPlaybackRegions(); const auto sampleRate = range.size() > 0 ? getSampleRate (range.front()) : 0.0; - if (std::any_of (range.begin(), range.end(), [&] (auto& x) { return ! exactlyEqual (getSampleRate (x), sampleRate); })) + if (std::any_of (range.begin(), range.end(), [&] (auto& x) { return getSampleRate (x) != sampleRate; })) return 0.0; return sampleRate; diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARA_utils.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARA_utils.h index 8f2357e5..e30cbbe3 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARA_utils.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/ARA/juce_ARA_utils.h @@ -27,8 +27,7 @@ // Include ARA SDK headers JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wgnu-zero-variadic-macro-arguments", - "-Wunused-parameter", - "-Wfloat-equal") + "-Wunused-parameter") JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6387) #include diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AAXClientExtensions.cpp b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AAXClientExtensions.cpp deleted file mode 100644 index 61f37d63..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AAXClientExtensions.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* - ============================================================================== - - 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 AAXPluginId -{ -public: - static std::optional create (std::array letters) - { - std::array indices{}; - - for (size_t i = 0; i < letters.size(); ++i) - { - if (const auto index = findIndexOfChar (letters[i])) - indices[i] = *index; - else - return std::nullopt; - } - - return AAXPluginId { std::move (indices) }; - } - - std::optional withIncrementedLetter (size_t index, size_t increment) const - { - if (indices.size() <= index) - return std::nullopt; - - auto copy = *this; - copy.indices[index] += increment; - - if ((size_t) std::size (validChars) <= copy.indices[index]) - return std::nullopt; - - return copy; - } - - int32 getPluginId() const - { - int32 result = 0; - - for (size_t i = 0; i < indices.size(); ++i) - result |= static_cast (validChars[indices[i]]) << (8 * (indices.size() - 1 - i)); - - return result; - } - - static std::optional findIndexOfChar (char c) - { - const auto ptr = std::find (std::begin (validChars), std::end (validChars), c); - return ptr != std::end (validChars) ? std::make_optional (std::distance (std::begin (validChars), ptr)) - : std::nullopt; - } - -private: - static inline constexpr char validChars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - - explicit AAXPluginId (std::array indicesIn) - : indices (std::move (indicesIn)) - {} - - std::array indices; -}; - -static const AudioChannelSet channelSets[] { AudioChannelSet::disabled(), - AudioChannelSet::mono(), - AudioChannelSet::stereo(), - AudioChannelSet::createLCR(), - AudioChannelSet::createLCRS(), - AudioChannelSet::quadraphonic(), - AudioChannelSet::create5point0(), - AudioChannelSet::create5point1(), - AudioChannelSet::create6point0(), - AudioChannelSet::create6point1(), - AudioChannelSet::create7point0(), - AudioChannelSet::create7point1(), - AudioChannelSet::create7point0SDDS(), - AudioChannelSet::create7point1SDDS(), - AudioChannelSet::create7point0point2(), - AudioChannelSet::create7point1point2(), - AudioChannelSet::ambisonic (1), - AudioChannelSet::ambisonic (2), - AudioChannelSet::ambisonic (3), - AudioChannelSet::create5point0point2(), - AudioChannelSet::create5point1point2(), - AudioChannelSet::create5point0point4(), - AudioChannelSet::create5point1point4(), - AudioChannelSet::create7point0point4(), - AudioChannelSet::create7point1point4(), - AudioChannelSet::create7point0point6(), - AudioChannelSet::create7point1point6(), - AudioChannelSet::create9point0point4(), - AudioChannelSet::create9point1point4(), - AudioChannelSet::create9point0point6(), - AudioChannelSet::create9point1point6(), - AudioChannelSet::ambisonic (4), - AudioChannelSet::ambisonic (5), - AudioChannelSet::ambisonic (6), - AudioChannelSet::ambisonic (7) }; - -int32 AAXClientExtensions::getPluginIDForMainBusConfig (const AudioChannelSet& mainInputLayout, - const AudioChannelSet& mainOutputLayout, - bool idForAudioSuite) const -{ - auto pluginId = [&] - { - auto opt = idForAudioSuite ? AAXPluginId::create ({ 'j', 'y', 'a', 'a' }) - : AAXPluginId::create ({ 'j', 'c', 'a', 'a' }); - jassert (opt.has_value()); - return *opt; - }(); - - for (const auto& [channelSet, indexToModify] : { std::tuple (&mainInputLayout, (size_t) 2), - std::tuple (&mainOutputLayout, (size_t) 3) }) - { - const auto increment = (size_t) std::distance (std::begin (channelSets), - std::find (std::begin (channelSets), - std::end (channelSets), - *channelSet)); - - if (auto modifiedPluginIdOpt = pluginId.withIncrementedLetter (indexToModify, increment); - increment < (size_t) std::size (channelSets) && modifiedPluginIdOpt.has_value()) - { - pluginId = *modifiedPluginIdOpt; - } - else - { - jassertfalse; - } - } - - return pluginId.getPluginId(); -} - -String AAXClientExtensions::getPageFileName() const -{ - #ifdef JucePlugin_AAXPageTableFile - JUCE_COMPILER_WARNING ("JucePlugin_AAXPageTableFile is deprecated, instead implement AAXClientExtensions::getPageFileName()") - return JucePlugin_AAXPageTableFile; - #else - return {}; - #endif -} - -//============================================================================== -//============================================================================== -#if JUCE_UNIT_TESTS - -static std::array toCharArray (int32 identifier) -{ - std::array result; - - for (size_t i = 0; i < result.size(); ++i) - result[i] = static_cast ((identifier >> (i * 8)) & 0xff); - - return result; -} - -static bool isValidAAXPluginId (int32 pluginId) -{ - const auto chars = toCharArray (pluginId); - - return std::all_of (std::begin (chars), - std::end (chars), - [] (const auto& c) - { - return AAXPluginId::findIndexOfChar (c).has_value(); - }); -} - -static int32 getPluginIDForMainBusConfigJuce705 (const AudioChannelSet& mainInputLayout, - const AudioChannelSet& mainOutputLayout, - bool idForAudioSuite) -{ - int uniqueFormatId = 0; - - for (int dir = 0; dir < 2; ++dir) - { - const bool isInput = (dir == 0); - auto& set = (isInput ? mainInputLayout : mainOutputLayout); - int aaxFormatIndex = 0; - - const AudioChannelSet sets[] - { - AudioChannelSet::disabled(), - AudioChannelSet::mono(), - AudioChannelSet::stereo(), - AudioChannelSet::createLCR(), - AudioChannelSet::createLCRS(), - AudioChannelSet::quadraphonic(), - AudioChannelSet::create5point0(), - AudioChannelSet::create5point1(), - AudioChannelSet::create6point0(), - AudioChannelSet::create6point1(), - AudioChannelSet::create7point0(), - AudioChannelSet::create7point1(), - AudioChannelSet::create7point0SDDS(), - AudioChannelSet::create7point1SDDS(), - AudioChannelSet::create7point0point2(), - AudioChannelSet::create7point1point2(), - AudioChannelSet::ambisonic (1), - AudioChannelSet::ambisonic (2), - AudioChannelSet::ambisonic (3), - AudioChannelSet::create5point0point2(), - AudioChannelSet::create5point1point2(), - AudioChannelSet::create5point0point4(), - AudioChannelSet::create5point1point4(), - AudioChannelSet::create7point0point4(), - AudioChannelSet::create7point1point4(), - AudioChannelSet::create7point0point6(), - AudioChannelSet::create7point1point6(), - AudioChannelSet::create9point0point4(), - AudioChannelSet::create9point1point4(), - AudioChannelSet::create9point0point6(), - AudioChannelSet::create9point1point6(), - AudioChannelSet::ambisonic (4), - AudioChannelSet::ambisonic (5), - AudioChannelSet::ambisonic (6), - AudioChannelSet::ambisonic (7) - }; - - const auto index = (int) std::distance (std::begin (sets), std::find (std::begin (sets), std::end (sets), set)); - - if (index != (int) std::size (sets)) - aaxFormatIndex = index; - else - jassertfalse; - - uniqueFormatId = (uniqueFormatId << 8) | aaxFormatIndex; - } - - return (idForAudioSuite ? 0x6a796161 /* 'jyaa' */ : 0x6a636161 /* 'jcaa' */) + uniqueFormatId; -} - -class AAXClientExtensionsTests : public UnitTest -{ -public: - AAXClientExtensionsTests() - : UnitTest ("AAXClientExtensionsTests", UnitTestCategories::audioProcessors) - {} - - void runTest() override - { - AAXClientExtensions extensions; - - beginTest ("Previously valid PluginIds should be unchanged"); - { - for (const auto& input : channelSets) - for (const auto& output : channelSets) - for (const auto idForAudioSuite : { false, true }) - if (const auto oldId = getPluginIDForMainBusConfigJuce705 (input, output, idForAudioSuite); isValidAAXPluginId (oldId)) - expect (extensions.getPluginIDForMainBusConfig (input, output, idForAudioSuite) == oldId); - } - - beginTest ("Valid, unique PluginIds should be generated for all configurations"); - { - std::set pluginIds; - - for (const auto& input : channelSets) - for (const auto& output : channelSets) - for (const auto idForAudioSuite : { false, true }) - pluginIds.insert (extensions.getPluginIDForMainBusConfig (input, output, idForAudioSuite)); - - for (auto identifier : pluginIds) - expect (isValidAAXPluginId (identifier)); - - expect (pluginIds.size() == square (std::size (channelSets)) * 2); - } - } -}; - -static AAXClientExtensionsTests aaxClientExtensionsTests; - -#endif - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AAXClientExtensions.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AAXClientExtensions.h deleted file mode 100644 index ce83cc5d..00000000 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AAXClientExtensions.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -/** - An interface to allow an AudioProcessor to implement extended AAX-specific functionality. - - To use this class, create an object that inherits from it, implement the methods, then return - a pointer to the object in your AudioProcessor `getAAXClientExtensions()` method. - - @see AudioProcessor, VST2ClientExtensions, VST3ClientExtensions - - @tags{Audio} -*/ -struct AAXClientExtensions -{ - virtual ~AAXClientExtensions() = default; - - /** AAX plug-ins need to report a unique "plug-in id" for every audio layout - configuration that your AudioProcessor supports on the main bus. Override this - function if you want your AudioProcessor to use a custom "plug-in id" (for example - to stay backward compatible with older versions of JUCE). - - The default implementation will compute a unique integer from the input and output - layout and add this value to the 4 character code 'jcaa' (for native AAX) or 'jyaa' - (for AudioSuite plug-ins). - */ - virtual int32 getPluginIDForMainBusConfig (const AudioChannelSet& mainInputLayout, - const AudioChannelSet& mainOutputLayout, - bool idForAudioSuite) const; - - /** Returns an optional filename (including extension) for a page file to be used. - - A page file allows an AAX plugin to specify how its parameters are displayed on - various control surfaces. For more information read the Page Table Guide in the - AAX SDK documentation. - - By default this file will be searched for in `*.aaxplugin/Contents/Resources`. - - @see getPageFileSearchPath - */ - virtual String getPageFileName() const; - - /** Optionally returns a search path for finding a page table file. - - This can be useful for specifying a location outside the plugin bundle so users can - make changes to a page table file without breaking any code signatures. - - If this function returns a default-constructed File, then a default location will be used. - The AAX SDK states this location will be `*.aaxplugin/Contents/Resources`. - - @note The returned path should be an absolute path to a directory. - - @see getPageFileName - */ - virtual File getPageFileSearchPath() const { return {}; } -}; - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h index 6caa4177..f16945f7 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h @@ -29,8 +29,6 @@ namespace juce /** Properties of an AudioParameterBool. @see AudioParameterBool(), RangedAudioParameterAttributes() - - @tags{Audio} */ class AudioParameterBoolAttributes : public RangedAudioParameterAttributes {}; diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h index 6940a57d..f3fdd2d6 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h @@ -29,8 +29,6 @@ namespace juce /** Properties of an AudioParameterChoice. @see AudioParameterChoice(), RangedAudioParameterAttributes() - - @tags{Audio} */ class AudioParameterChoiceAttributes : public RangedAudioParameterAttributes {}; diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.cpp b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.cpp index 5ac42137..a9f84d3a 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.cpp @@ -44,7 +44,7 @@ AudioParameterFloat::AudioParameterFloat (const ParameterID& idToUse, { int numDecimalPlaces = 7; - if (! approximatelyEqual (range.interval, 0.0f)) + if (range.interval != 0.0f) { if (approximatelyEqual (std::abs (range.interval - std::floor (range.interval)), 0.0f)) return 0; @@ -95,7 +95,7 @@ void AudioParameterFloat::valueChanged (float) {} AudioParameterFloat& AudioParameterFloat::operator= (float newValue) { - if (! approximatelyEqual ((float) value, newValue)) + if (value != newValue) setValueNotifyingHost (convertTo0to1 (newValue)); return *this; diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h index c785f340..c6adbe78 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h @@ -29,8 +29,6 @@ namespace juce /** Properties of an AudioParameterFloat. @see AudioParameterFloat(), RangedAudioParameterAttributes() - - @tags{Audio} */ class AudioParameterFloatAttributes : public RangedAudioParameterAttributes {}; diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h index 87036c24..965dac6a 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h @@ -29,8 +29,6 @@ namespace juce /** Properties of an AudioParameterInt. @see AudioParameterInt(), RangedAudioParameterAttributes() - - @tags{Audio} */ class AudioParameterIntAttributes : public RangedAudioParameterAttributes {}; diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h index c5a622d2..15f7882c 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h @@ -28,8 +28,6 @@ namespace juce /** Combines a parameter ID and a version hint. - - @tags{Audio} */ class ParameterID { @@ -64,8 +62,6 @@ class ParameterID /** An instance of this class may be passed to the constructor of an AudioProcessorParameterWithID to set optional characteristics of that parameter. - - @tags{Audio} */ class AudioProcessorParameterWithIDAttributes { diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp index fa9f91ab..7e75bc14 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp @@ -52,11 +52,13 @@ bool AudioProcessorValueTreeState::Parameter::isBoolean() const { return void AudioProcessorValueTreeState::Parameter::valueChanged (float newValue) { - if (approximatelyEqual ((float) lastValue, newValue)) + if (lastValue == newValue) return; lastValue = newValue; - NullCheckedInvocation::invoke (onValueChanged); + + if (onValueChanged != nullptr) + onValueChanged(); } //============================================================================== @@ -91,8 +93,10 @@ class AudioProcessorValueTreeState::ParameterAdapter : private AudioProcessorP void setDenormalisedValue (float value) { - if (! approximatelyEqual (value, (float) unnormalisedValue)) - setNormalisedValue (normalise (value)); + if (value == unnormalisedValue) + return; + + setNormalisedValue (normalise (value)); } float getDenormalisedValueForText (const String& text) const @@ -115,9 +119,9 @@ class AudioProcessorValueTreeState::ParameterAdapter : private AudioProcessorP if (! needsUpdate.compare_exchange_strong (needsUpdateTestValue, false)) return false; - if (auto* valueProperty = tree.getPropertyPointer (key)) + if (auto valueProperty = tree.getPropertyPointer (key)) { - if (! approximatelyEqual ((float) *valueProperty, unnormalisedValue.load())) + if ((float) *valueProperty != unnormalisedValue) { ScopedValueSetter svs (ignoreParameterChangedCallbacks, true); tree.setProperty (key, unnormalisedValue.load(), um); @@ -140,7 +144,7 @@ class AudioProcessorValueTreeState::ParameterAdapter : private AudioProcessorP { const auto newValue = denormalise (parameter.getValue()); - if (! listenersNeedCalling && approximatelyEqual ((float) unnormalisedValue, newValue)) + if (unnormalisedValue == newValue && ! listenersNeedCalling) return; unnormalisedValue = newValue; diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h index 83d93962..a6e0de39 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h @@ -32,8 +32,6 @@ namespace juce AudioParameterFloatAttributes. @see AudioParameterFloatAttributes, RangedAudioParameterAttributes - - @tags{Audio} */ class AudioProcessorValueTreeStateParameterAttributes { diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.h index 597513ee..a755fb2d 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_NativeScaleFactorNotifier.h @@ -31,8 +31,6 @@ namespace juce This is used in the VST and VST3 wrappers to ensure that the editor's scale is kept in sync with the scale of its containing component. - - @tags{GUI} */ class NativeScaleFactorNotifier : private ComponentMovementWatcher, private ComponentPeer::ScaleFactorListener diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_ParameterAttachments.cpp b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_ParameterAttachments.cpp index 3c092a16..73edfe01 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_ParameterAttachments.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_ParameterAttachments.cpp @@ -84,7 +84,7 @@ void ParameterAttachment::callIfParameterValueChanged (float newDenormalisedValu { const auto newValue = normalise (newDenormalisedValue); - if (! approximatelyEqual (parameter.getValue(), newValue)) + if (parameter.getValue() != newValue) callback (newValue); } diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_PluginHostType.cpp b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_PluginHostType.cpp index b74874a6..78f7ea23 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_PluginHostType.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_PluginHostType.cpp @@ -119,7 +119,6 @@ const char* PluginHostType::getHostDescription() const noexcept case pluginval: return "pluginval"; case MergingPyramix: return "Pyramix"; case MuseReceptorGeneric: return "Muse Receptor"; - case Maschine: return "NI Maschine"; case Reaper: return "Reaper"; case Reason: return "Reason"; case Renoise: return "Renoise"; @@ -214,7 +213,6 @@ PluginHostType::HostType PluginHostType::getHostType() if (hostFilename.containsIgnoreCase ("OsxFL")) return FruityLoops; if (hostFilename.containsIgnoreCase ("pluginval")) return pluginval; if (hostFilename.containsIgnoreCase ("AudioPluginHost")) return JUCEPluginHost; - if (hostFilename.containsIgnoreCase ("Maschine")) return Maschine; if (hostFilename.containsIgnoreCase ("Vienna Ensemble Pro")) return ViennaEnsemblePro; if (hostFilename.containsIgnoreCase ("auvaltool")) return AUVal; if (hostFilename.containsIgnoreCase ("com.apple.audio.infohelper")) return AppleInfoHelper; @@ -282,7 +280,6 @@ PluginHostType::HostType PluginHostType::getHostType() if (hostFilename.containsIgnoreCase ("Wavelab")) return SteinbergWavelabGeneric; if (hostFilename.containsIgnoreCase ("TestHost")) return SteinbergTestHost; if (hostFilename.containsIgnoreCase ("rm-host")) return MuseReceptorGeneric; - if (hostFilename.containsIgnoreCase ("Maschine")) return Maschine; if (hostFilename.startsWith ("FL")) return FruityLoops; if (hostFilename.contains ("ilbridge.")) return FruityLoops; if (hostPath.containsIgnoreCase ("Studio One")) return StudioOne; diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_PluginHostType.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_PluginHostType.h index b9366c0c..cf5f0e40 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_PluginHostType.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_PluginHostType.h @@ -78,7 +78,6 @@ class PluginHostType MagixSequoia, /**< Represents Magix Sequoia. */ MergingPyramix, /**< Represents Merging Pyramix. */ MuseReceptorGeneric, /**< Represents Muse Receptor. */ - Maschine, /**< Represents Native Instruments Maschine. */ pluginval, /**< Represents pluginval. */ Reaper, /**< Represents Cockos Reaper. */ Reason, /**< Represents Reason. */ @@ -204,8 +203,6 @@ class PluginHostType bool isWavelab() const noexcept { return isWavelabLegacy() || type == SteinbergWavelab7 || type == SteinbergWavelab8 || type == SteinbergWavelabGeneric; } /** Returns true if the host is Steinberg WaveLab 6 or below. */ bool isWavelabLegacy() const noexcept { return type == SteinbergWavelab5 || type == SteinbergWavelab6; } - /** Returns true if the host is Native Instruments Maschine. */ - bool isMaschine() const noexcept { return type == Maschine; } //============================================================================== /** Returns a human-readable description of the host. */ diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h index 4707e8f5..c24c52b9 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h @@ -36,8 +36,6 @@ namespace juce i.e. the identifiers AudioParameterFloatAttributes and RangedAudioParameterAttributes should not be interchangable because we might need to add float-specific attributes in the future. Users should not refer directly to RangedAudioParameterAttributes. - - @tags{Audio} */ template class RangedAudioParameterAttributes diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h index 665f11f1..9d70828f 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h @@ -37,13 +37,13 @@ namespace Steinberg namespace juce { -/** - An interface to allow an AudioProcessor to implement extended VST3-specific functionality. +/** An interface to allow an AudioProcessor to implement extended VST3-specific + functionality. - To use this class, create an object that inherits from it, implement the methods, then return - a pointer to the object in your AudioProcessor::getVST3ClientExtensions() method. + To use this class, ensure that your AudioProcessor publicly inherits + from VST3ClientExtensions. - @see AudioProcessor, AAXClientExtensions, VST2ClientExtensions + @see VSTCallbackHandler @tags{Audio} */ @@ -94,17 +94,6 @@ struct VST3ClientExtensions All other input buses will always be designated kAux. */ virtual bool getPluginHasMainInput() const { return true; } - - /** This function should return the UIDs of any compatible VST2 plug-ins. - - Each item in the vector should be a 32-character string consisting only - of the characters 0-9 and A-F. - - This information will be used to implement the IPluginCompatibility - interface. Hosts can use this interface to determine whether this VST3 - is capable of replacing a given VST2. - */ - virtual std::vector getCompatibleClasses() const { return {}; } }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VSTCallbackHandler.cpp similarity index 64% rename from JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp rename to JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VSTCallbackHandler.cpp index 8501d3dc..b84f81f2 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VSTCallbackHandler.cpp @@ -26,14 +26,14 @@ namespace juce { -pointer_sized_int VST2ClientExtensions::handleVstPluginCanDo ([[maybe_unused]] int32 index, - [[maybe_unused]] pointer_sized_int value, - [[maybe_unused]] void* ptr, - [[maybe_unused]] float opt) +pointer_sized_int VSTCallbackHandler::handleVstPluginCanDo ([[maybe_unused]] int32 index, + [[maybe_unused]] pointer_sized_int value, + [[maybe_unused]] void* ptr, + [[maybe_unused]] float opt) { return 0; } -void VST2ClientExtensions::handleVstHostCallbackAvailable ([[maybe_unused]] std::function&& callback) {} +void VSTCallbackHandler::handleVstHostCallbackAvailable ([[maybe_unused]] std::function&& callback) {} } // namespace juce diff --git a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VSTCallbackHandler.h similarity index 70% rename from JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h rename to JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VSTCallbackHandler.h index 98bbe0a2..52f861e2 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h +++ b/JuceLibraryCode/modules/juce_audio_processors/utilities/juce_VSTCallbackHandler.h @@ -26,19 +26,19 @@ namespace juce { -/** - An interface to allow an AudioProcessor to implement extended VST2-specific functionality. +/** An interface to allow an AudioProcessor to send and receive VST specific calls from + the host. - To use this class, create an object that inherits from it, implement the methods, then return - a pointer to the object in your AudioProcessor::getVST2ClientExtensions() method. + To use this class, ensure that your AudioProcessor publicly inherits + from VSTCallbackHandler. - @see AudioProcessor, AAXClientExtensions, VST3ClientExtensions + @see VST3ClientExtensions @tags{Audio} */ -struct VST2ClientExtensions +struct VSTCallbackHandler { - virtual ~VST2ClientExtensions() = default; + virtual ~VSTCallbackHandler() = default; /** This is called by the VST plug-in wrapper when it receives unhandled plug-in "can do" calls from the host. @@ -56,12 +56,13 @@ struct VST2ClientExtensions void* ptr, float opt) = 0; + // Note: VS2013 prevents a "using" declaration here /** The host callback function type. */ - using VstHostCallbackType = pointer_sized_int (int32 opcode, - int32 index, - pointer_sized_int value, - void* ptr, - float opt); + typedef pointer_sized_int (VstHostCallbackType) (int32 opcode, + int32 index, + pointer_sized_int value, + void* ptr, + float opt); /** This is called once by the VST plug-in wrapper after its constructor. You can use the supplied function to query the VST host. @@ -69,6 +70,4 @@ struct VST2ClientExtensions virtual void handleVstHostCallbackAvailable (std::function&& callback); }; -using VSTCallbackHandler [[deprecated ("replace with VST2ClientExtensions")]] = VST2ClientExtensions; - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Array.h b/JuceLibraryCode/modules/juce_core/containers/juce_Array.h index 934357ba..316e5452 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Array.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Array.h @@ -386,7 +386,7 @@ class Array auto endPtr = values.end(); for (; e != endPtr; ++e) - if (exactlyEqual (elementToLookFor, *e)) + if (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 (exactlyEqual (elementToLookFor, *e)) + if (elementToLookFor == *e) return true; return false; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h index d24d5621..353fcfae 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayBase.h @@ -123,7 +123,7 @@ class ArrayBase : public TypeOfCriticalSectionToUse auto* e = begin(); for (auto& o : other) - if (! exactlyEqual (*e++, o)) + if (! (*e++ == o)) return false; return true; diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.h b/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.h index 5f657a07..595e3779 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ListenerList.h @@ -325,10 +325,7 @@ class ListenerList WrappedIterator (const ListenerList& listToIterate, WrappedIterator*& listHeadIn) : it (listToIterate), listHead (listHeadIn), next (listHead) { - // GCC 12.2 with O1 and above gets confused here - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdangling-pointer") listHead = this; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE } ~WrappedIterator() diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Optional.h b/JuceLibraryCode/modules/juce_core/containers/juce_Optional.h index bbcce347..b7609278 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Optional.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Optional.h @@ -30,9 +30,7 @@ constexpr auto nullopt = std::nullopt; // 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. @@ -163,7 +161,6 @@ 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; } \ @@ -174,7 +171,7 @@ Optional> makeOptional (Value&& v) JUCE_OPTIONAL_OPERATORS #undef X + #undef JUCE_OPTIONAL_OPERATORS -#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Span.h b/JuceLibraryCode/modules/juce_core/containers/juce_Span.h deleted file mode 100644 index ec642bf0..00000000 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Span.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - ============================================================================== - - 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; - - 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_Variant.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp index 12b1394f..e84baa02 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp @@ -201,7 +201,7 @@ struct var::VariantType 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 doubleToBool (const ValueUnion& data) noexcept { return data.doubleValue != 0.0; } static bool doubleEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { @@ -645,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 exactlyEqual (diff, 0.0) ? 0 : (diff < 0 ? -1 : 1); + return diff == 0 ? 0 : (diff < 0 ? -1 : 1); } bool operator== (const var& v1, const var& v2) { return v1.equals (v2); } diff --git a/JuceLibraryCode/modules/juce_core/files/juce_File.cpp b/JuceLibraryCode/modules/juce_core/files/juce_File.cpp index 43e226e3..d19f14b8 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_File.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_File.cpp @@ -970,7 +970,7 @@ bool File::createSymbolicLink (const File& linkFileToCreate, linkFileToCreate.deleteFile(); } - #if JUCE_MAC || JUCE_LINUX || JUCE_BSD + #if JUCE_MAC || JUCE_LINUX // one common reason for getting an error here is that the file already exists if (symlink (nativePathOfTarget.toRawUTF8(), linkFileToCreate.getFullPathName().toRawUTF8()) == -1) { diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp index 9b786ac0..6b4ad398 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp @@ -63,12 +63,7 @@ int FileSearchPath::getNumPaths() const File FileSearchPath::operator[] (int index) const { - return File (getRawString (index)); -} - -String FileSearchPath::getRawString (int index) const -{ - return directories[index]; + return File (directories[index]); } String FileSearchPath::toString() const @@ -115,30 +110,21 @@ void FileSearchPath::addPath (const FileSearchPath& other) void FileSearchPath::removeRedundantPaths() { - std::vector reduced; - - for (const auto& directory : directories) + for (int i = directories.size(); --i >= 0;) { - const auto checkedIsChildOf = [&] (const auto& a, const auto& b) - { - return File::isAbsolutePath (a) && File::isAbsolutePath (b) && File (a).isAChildOf (b); - }; + const File d1 (directories[i]); - const auto fContainsDirectory = [&] (const auto& f) + for (int j = directories.size(); --j >= 0;) { - return f == directory || checkedIsChildOf (directory, f); - }; - - if (std::find_if (reduced.begin(), reduced.end(), fContainsDirectory) != reduced.end()) - continue; + const File d2 (directories[j]); - 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); + if (i != j && (d1.isAChildOf (d2) || d1 == d2)) + { + directories.remove (i); + break; + } + } } - - directories = StringArray (reduced.data(), (int) reduced.size()); } void FileSearchPath::removeNonExistentPaths() @@ -186,54 +172,4 @@ bool FileSearchPath::isFileInPath (const File& fileToCheck, return false; } -//============================================================================== -//============================================================================== -#if JUCE_UNIT_TESTS - -class FileSearchPathTests : 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 d789cf41..8b48162b 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h @@ -71,21 +71,10 @@ 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, getRawString + @see getNumPaths */ 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; diff --git a/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.cpp b/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.cpp index fe08f71a..42e150e9 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.cpp @@ -206,7 +206,6 @@ class Table { "fdf", "application/vnd.fdf" }, { "fif", "application/fractals" }, { "fif", "image/fif" }, - { "flac", "audio/flac" }, { "fli", "video/fli" }, { "fli", "video/x-fli" }, { "flo", "image/florian" }, diff --git a/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.h b/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.h index 4b6437a2..d3b3e7be 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_common_MimeTypes.h @@ -28,17 +28,18 @@ namespace juce { -/** @internal */ struct MimeTypeTable { -/** @internal */ + +/* @internal */ static void registerCustomMimeTypeForFileExtension (const String& mimeType, const String& fileExtension); -/** @internal */ +/* @internal */ static StringArray getMimeTypesForFileExtension (const String& fileExtension); -/** @internal */ +/* @internal */ static StringArray getFileExtensionsForMimeType (const String& mimeType); + }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp index 3a1f9ba8..1582571c 100644 --- a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp @@ -520,7 +520,7 @@ struct JavascriptEngine::RootObject : public DynamicObject { 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 exactlyEqual (a, b); } + var getWithDoubles (double a, double b) const override { return 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; } @@ -530,7 +530,7 @@ struct JavascriptEngine::RootObject : public DynamicObject { 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 ! exactlyEqual (a, b); } + var getWithDoubles (double a, double b) const override { return 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; } @@ -593,14 +593,14 @@ struct JavascriptEngine::RootObject : public DynamicObject struct DivideOp : 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 exactlyEqual (b, 0.0) ? std::numeric_limits::infinity() : a / b; } + 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 ((double) a / (double) b) : var (std::numeric_limits::infinity()); } }; struct ModuloOp : 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 exactlyEqual (b, 0.0) ? std::numeric_limits::infinity() : fmod (a, b); } + var getWithDoubles (double a, double b) const override { return b != 0 ? fmod (a, b) : std::numeric_limits::infinity(); } var getWithInts (int64 a, int64 b) const override { return b != 0 ? var (a % b) : var (std::numeric_limits::infinity()); } }; @@ -1711,7 +1711,6 @@ 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); @@ -1750,7 +1749,6 @@ 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. diff --git a/JuceLibraryCode/modules/juce_core/juce_core.cpp b/JuceLibraryCode/modules/juce_core/juce_core.cpp index 788c74b0..99884b30 100644 --- a/JuceLibraryCode/modules/juce_core/juce_core.cpp +++ b/JuceLibraryCode/modules/juce_core/juce_core.cpp @@ -41,7 +41,7 @@ #include #include -#if ! (JUCE_ANDROID || JUCE_BSD) +#if ! JUCE_ANDROID #include #include #endif @@ -186,83 +186,67 @@ #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" +#include "native/juce_native_ThreadPriorities.h" //============================================================================== #if ! JUCE_WINDOWS - #include "native/juce_SharedCode_posix.h" - #include "native/juce_NamedPipe_posix.cpp" + #include "native/juce_posix_SharedCode.h" + #include "native/juce_posix_NamedPipe.cpp" #if ! JUCE_ANDROID || __ANDROID_API__ >= 24 - #include "native/juce_IPAddress_posix.h" + #include "native/juce_posix_IPAddress.h" #endif #endif //============================================================================== #if JUCE_MAC || JUCE_IOS - #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_mac_Files.mm" + #include "native/juce_mac_Network.mm" + #include "native/juce_mac_Strings.mm" + #include "native/juce_intel_SharedCode.h" + #include "native/juce_mac_SystemStats.mm" + #include "native/juce_mac_Threads.mm" //============================================================================== #elif JUCE_WINDOWS - #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" + #include "native/juce_win32_Files.cpp" + #include "native/juce_win32_Network.cpp" + #include "native/juce_win32_Registry.cpp" + #include "native/juce_win32_SystemStats.cpp" + #include "native/juce_win32_Threads.cpp" //============================================================================== -#elif JUCE_LINUX - #include "native/juce_CommonFile_linux.cpp" - #include "native/juce_Files_linux.cpp" - #include "native/juce_Network_linux.cpp" +#elif JUCE_LINUX || JUCE_BSD + #include "native/juce_linux_CommonFile.cpp" + #include "native/juce_linux_Files.cpp" + #include "native/juce_linux_Network.cpp" #if JUCE_USE_CURL - #include "native/juce_Network_curl.cpp" + #include "native/juce_curl_Network.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_Network_curl.cpp" + #if JUCE_BSD + #include "native/juce_intel_SharedCode.h" #endif - #include "native/juce_SharedCode_intel.h" - #include "native/juce_SystemStats_linux.cpp" - #include "native/juce_Threads_linux.cpp" - #include "native/juce_PlatformTimer_generic.cpp" + #include "native/juce_linux_SystemStats.cpp" + #include "native/juce_linux_Threads.cpp" //============================================================================== #elif JUCE_ANDROID - #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" + #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" -//============================================================================== #elif JUCE_WASM - #include "native/juce_SystemStats_wasm.cpp" - #include "native/juce_PlatformTimer_generic.cpp" + #include "native/juce_wasm_SystemStats.cpp" + #endif #include "files/juce_common_MimeTypes.h" #include "files/juce_common_MimeTypes.cpp" -#include "native/juce_AndroidDocument_android.cpp" +#include "native/juce_android_AndroidDocument.cpp" #include "threads/juce_HighResolutionTimer.cpp" #include "threads/juce_WaitableEvent.cpp" #include "network/juce_URL.cpp" @@ -276,9 +260,8 @@ //============================================================================== #if JUCE_UNIT_TESTS #include "containers/juce_HashMap_test.cpp" + #include "containers/juce_Optional_test.cpp" - #include "maths/juce_MathsFunctions_test.cpp" - #include "misc/juce_EnumHelpers_test.cpp" #endif //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/juce_core.h b/JuceLibraryCode/modules/juce_core/juce_core.h index f23eaed6..4ce05eb8 100644 --- a/JuceLibraryCode/modules/juce_core/juce_core.h +++ b/JuceLibraryCode/modules/juce_core/juce_core.h @@ -32,7 +32,7 @@ ID: juce_core vendor: juce - version: 7.0.7 + version: 7.0.5 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 @@ -40,7 +40,7 @@ minimumCppStandard: 17 dependencies: - OSXFrameworks: Cocoa Foundation IOKit Security + OSXFrameworks: Cocoa Foundation IOKit iOSFrameworks: Foundation linuxLibs: rt dl pthread mingwLibs: uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm @@ -219,7 +219,6 @@ 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" @@ -277,7 +276,6 @@ JUCE_END_IGNORE_WARNINGS_MSVC #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" @@ -315,12 +313,12 @@ JUCE_END_IGNORE_WARNINGS_MSVC #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" @@ -349,16 +347,16 @@ JUCE_END_IGNORE_WARNINGS_MSVC #include "streams/juce_AndroidDocumentInputSource.h" #if JUCE_CORE_INCLUDE_OBJC_HELPERS && (JUCE_MAC || JUCE_IOS) - #include "native/juce_ObjCHelpers_mac.h" + #include "native/juce_mac_ObjCHelpers.h" #endif #if JUCE_CORE_INCLUDE_COM_SMART_PTR && JUCE_WINDOWS - #include "native/juce_ComSmartPtr_windows.h" + #include "native/juce_win32_ComSmartPtr.h" #endif #if JUCE_CORE_INCLUDE_JNI_HELPERS && JUCE_ANDROID #include - #include "native/juce_JNIHelpers_android.h" + #include "native/juce_android_JNIHelpers.h" #endif #if JUCE_UNIT_TESTS diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h index 30365240..cc61820e 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h @@ -86,250 +86,6 @@ 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 -*/ -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 @@ -370,7 +126,7 @@ constexpr Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax) template Type jmap (Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax) { - jassert (! approximatelyEqual (sourceRangeMax, sourceRangeMin)); // mapping from a range of zero will produce NaN! + jassert (sourceRangeMax != sourceRangeMin); // mapping from a range of zero will produce NaN! return targetRangeMin + ((targetRangeMax - targetRangeMin) * (sourceValue - sourceRangeMin)) / (sourceRangeMax - sourceRangeMin); } @@ -561,6 +317,132 @@ 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 +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) 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) diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions_test.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions_test.cpp deleted file mode 100644 index 4545044c..00000000 --- a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions_test.cpp +++ /dev/null @@ -1,543 +0,0 @@ -/* - ============================================================================== - - 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 d2eafa0b..2909a5dc 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_NormalisableRange.h @@ -128,7 +128,7 @@ class NormalisableRange auto proportion = clampTo0To1 ((v - start) / (end - start)); - if (exactlyEqual (skew, static_cast (1))) + if (skew == static_cast (1)) return proportion; if (! symmetricSkew) @@ -154,7 +154,7 @@ class NormalisableRange if (! symmetricSkew) { - if (! exactlyEqual (skew, static_cast (1)) && proportion > ValueType()) + if (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 (! exactlyEqual (skew, static_cast (1)) && ! exactlyEqual (distanceFromMiddle, static_cast (0))) + if (skew != static_cast (1) && 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 (exactlyEqual (clampedValue, value)); + jassert (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 b98dc5e6..df9b2665 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp @@ -103,7 +103,7 @@ float Random::nextFloat() noexcept { auto result = static_cast (static_cast (nextInt())) / (static_cast (std::numeric_limits::max()) + 1.0f); - return jmin (result, 1.0f - std::numeric_limits::epsilon()); + return result == 1.0f ? 1.0f - std::numeric_limits::epsilon() : result; } double Random::nextDouble() noexcept diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Range.h b/JuceLibraryCode/modules/juce_core/maths/juce_Range.h index f5eb5318..9a711959 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Range.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Range.h @@ -86,7 +86,7 @@ class Range constexpr inline ValueType getEnd() const noexcept { return end; } /** Returns true if the range has a length of zero. */ - constexpr inline bool isEmpty() const noexcept { return exactlyEqual (start, end); } + constexpr inline bool isEmpty() const noexcept { return start == end; } //============================================================================== /** Changes the start position of the range, leaving the end position unchanged. @@ -198,13 +198,8 @@ class Range return Range (start - amountToSubtract, end - amountToSubtract); } - 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); } + constexpr bool operator== (Range other) const noexcept { return start == other.start && end == other.end; } + constexpr bool operator!= (Range other) const noexcept { return start != other.start || end != other.end; } //============================================================================== /** Returns true if the given position lies inside this range. diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h b/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h index 6d7fe94b..3829f73e 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_Reservoir.h @@ -25,8 +25,6 @@ namespace juce /** Helper functions for managing buffered readers. - - @tags{Audio} */ struct Reservoir { diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers.h b/JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers.h deleted file mode 100644 index 5461f3d4..00000000 --- a/JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - ============================================================================== - - 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 deleted file mode 100644 index 50e4555e..00000000 --- a/JuceLibraryCode/modules/juce_core/misc/juce_EnumHelpers_test.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* -============================================================================== - -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 : 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 = {}; - - 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 index 040132f7..e2a8bbfd 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_Functional.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Functional.h @@ -80,7 +80,7 @@ using DisableIfSameOrDerived = std::enable_if_t -[[nodiscard]] Object withMember (Object copy, Member OtherObject::* member, Other&& value) +Object withMember (Object copy, Member OtherObject::* member, Other&& value) { copy.*member = std::forward (value); return copy; @@ -109,8 +109,6 @@ template struct ScopeGuard : Fn { ~ScopeGuard() { Fn::operator()(); } }; template ScopeGuard (Fn) -> ScopeGuard; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h b/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h index 5227ea44..f444f9c0 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h @@ -209,8 +209,6 @@ #include #include #include - #include - #include #include #include @@ -267,13 +265,11 @@ #include #include #include - #include - #include #include #include - // If you are getting include errors here, then you need to re-build - // the Projucer and re-save your .jucer file. + // If you are getting include errors here, then you to re-build the Projucer + // and re-save your .jucer file. #include #endif diff --git a/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimerListener.h b/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimerListener.h deleted file mode 100644 index 21832a15..00000000 --- a/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimerListener.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - ============================================================================== - - 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 PlatformTimerListener -{ - 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 deleted file mode 100644 index 7a2ff45c..00000000 --- a/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimer_generic.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - ============================================================================== - - 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 deleted file mode 100644 index f4cb5168..00000000 --- a/JuceLibraryCode/modules/juce_core/native/juce_PlatformTimer_windows.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - ============================================================================== - - 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); - - const auto callback = [] (UINT, UINT, DWORD_PTR context, DWORD_PTR, DWORD_PTR) - { - reinterpret_cast (context)->onTimerExpired(); - }; - - timerId = timeSetEvent ((UINT) newIntervalMs, 1, callback, (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 }; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PlatformTimer) - JUCE_DECLARE_NON_MOVEABLE (PlatformTimer) -}; - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_AndroidDocument_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_AndroidDocument.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_AndroidDocument_android.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_android_AndroidDocument.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Files_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_Files_android.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.cpp similarity index 96% rename from JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.cpp index 7540a333..0e928959 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.cpp @@ -94,8 +94,8 @@ struct SystemJavaClassComparator if ((! isSysClassA) && (! isSysClassB)) { - return DefaultElementComparator::compareElements (first != nullptr && first->byteCode != nullptr, - second != nullptr && second->byteCode != nullptr); + return DefaultElementComparator::compareElements (first != nullptr ? first->byteCode != nullptr : false, + second != nullptr ? second->byteCode != nullptr : false); } return DefaultElementComparator::compareElements (isSystemClass (first), @@ -631,17 +631,43 @@ jobject FragmentOverlay::getNativeHandle() } //============================================================================== -void startAndroidActivityForResult (const LocalRef& intent, - int requestCode, - std::function)>&& callback) +class ActivityLauncher : public FragmentOverlay { - auto* launcher = new ActivityLauncher (intent, requestCode); - launcher->callback = [launcher, c = std::move (callback)] (auto&&... args) +public: + ActivityLauncher (const LocalRef& intentToUse, + int requestCodeToUse, + std::function)> && callbackToUse) + : intent (intentToUse), requestCode (requestCodeToUse), callback (std::move (callbackToUse)) + {} + + void onStart() override { - NullCheckedInvocation::invoke (c, args...); - delete launcher; - }; - launcher->open(); + if (! std::exchange (activityHasStarted, true)) + getEnv()->CallVoidMethod (getNativeHandle(), AndroidFragment.startActivityForResult, + intent.get(), requestCode); + } + + void onActivityResult (int activityRequestCode, int resultCode, LocalRef data) override + { + if (callback) + callback (activityRequestCode, resultCode, std::move (data)); + + getEnv()->CallVoidMethod (getNativeHandle(), JuceFragmentOverlay.close); + delete this; + } + +private: + GlobalRef intent; + int requestCode; + std::function)> callback; + bool activityHasStarted = false; +}; + +void startAndroidActivityForResult (const LocalRef& intent, int requestCode, + std::function)> && callback) +{ + auto* activityLauncher = new ActivityLauncher (intent, requestCode, std::move (callback)); + activityLauncher->open(); } //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.h b/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h similarity index 96% rename from JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.h rename to JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h index a32a6014..9d5cae17 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_JNIHelpers_android.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h @@ -262,8 +262,7 @@ template constexpr auto numBytes (const T (&) [N]) { retu METHOD (getApplicationInfo, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;") \ METHOD (checkCallingOrSelfPermission, "checkCallingOrSelfPermission", "(Ljava/lang/String;)I") \ 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;") \ + METHOD (getCacheDir, "getCacheDir", "()Ljava/io/File;") DECLARE_JNI_CLASS (AndroidContext, "android/content/Context") #undef JNI_CLASS_MEMBERS @@ -419,7 +418,6 @@ DECLARE_JNI_CLASS (AndroidHandlerThread, "android/os/HandlerThread") 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;") \ @@ -429,12 +427,6 @@ 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") \ @@ -498,8 +490,7 @@ 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;") \ - STATICMETHOD (getBroadcast, "getBroadcast", "(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;") \ - METHOD (getIntentSender, "getIntentSender", "()Landroid/content/IntentSender;") \ + METHOD (getIntentSender, "getIntentSender", "()Landroid/content/IntentSender;") DECLARE_JNI_CLASS (AndroidPendingIntent, "android/app/PendingIntent") #undef JNI_CLASS_MEMBERS @@ -1035,37 +1026,10 @@ class FragmentOverlay //============================================================================== // Allows you to start an activity without requiring to have an activity -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 startAndroidActivityForResult (const LocalRef& intent, int requestCode, + std::function)> && callback); - 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); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Misc_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Misc.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_Misc_android.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_android_Misc.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Network_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_Network_android.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_RuntimePermissions_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_RuntimePermissions.cpp similarity index 97% rename from JuceLibraryCode/modules/juce_core/native/juce_RuntimePermissions_android.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_android_RuntimePermissions.cpp index 52584e19..45636eb1 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_RuntimePermissions_android.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_RuntimePermissions.cpp @@ -43,11 +43,7 @@ static StringArray jucePermissionToAndroidPermissions (RuntimePermissions::Permi "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::writeExternalStorage: return { "android.permission.WRITE_EXTERNAL_STORAGE" }; case RuntimePermissions::camera: return { "android.permission.CAMERA" }; case RuntimePermissions::readExternalStorage: diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_SystemStats.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_SystemStats_android.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_android_SystemStats.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Threads_android.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp similarity index 99% rename from JuceLibraryCode/modules/juce_core/native/juce_Threads_android.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp index 19a0507d..cad1d03f 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_Threads_android.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp @@ -448,4 +448,6 @@ 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_Network_curl.cpp b/JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp similarity index 97% rename from JuceLibraryCode/modules/juce_core/native/juce_Network_curl.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp index a8d07948..9fa4a5a3 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_Network_curl.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_curl_Network.cpp @@ -89,9 +89,7 @@ struct CURLSymbols static DynamicLibrary libcurl; if (libcurl.getNativeHandle() == nullptr) - for (auto libName : { "libcurl.so", - "libcurl.so.4", "libcurl.so.3", - "libcurl-gnutls.so.4", "libcurl-gnutls.so.3" }) + for (auto libName : { "libcurl.so", "libcurl.so.4", "libcurl.so.3" }) if (libcurl.open (libName)) break; @@ -373,18 +371,11 @@ class WebInputStream::Pimpl if (symbols->curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK) statusCode = static_cast (responseCode); - #if LIBCURL_VERSION_MAJOR < 7 || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR < 55) + // get content length size 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; } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SharedCode_intel.h b/JuceLibraryCode/modules/juce_core/native/juce_intel_SharedCode.h similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_SharedCode_intel.h rename to JuceLibraryCode/modules/juce_core/native/juce_intel_SharedCode.h diff --git a/JuceLibraryCode/modules/juce_core/native/juce_CommonFile_linux.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_CommonFile.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_CommonFile_linux.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_linux_CommonFile.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Files_linux.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp similarity index 98% rename from JuceLibraryCode/modules/juce_core/native/juce_Files_linux.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp index 12216a48..b8737c0d 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_Files_linux.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp @@ -20,6 +20,10 @@ ============================================================================== */ +#if JUCE_BSD +extern char** environ; +#endif + namespace juce { @@ -225,7 +229,7 @@ bool Process::openDocument (const String& fileName, const String& parameters) setsid(); // Child process - execv (argv[0], (char**) argv); + execve (argv[0], (char**) argv, environ); exit (0); } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Network_linux.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp similarity index 99% rename from JuceLibraryCode/modules/juce_core/native/juce_Network_linux.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp index ce5a359a..ed9bac57 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_Network_linux.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp @@ -34,7 +34,7 @@ void MACAddress::findAllAddresses (Array& result) { if (i->ifa_addr->sa_family == AF_LINK) { - auto sdl = unalignedPointerCast (i->ifa_addr); + struct sockaddr_dl* sdl = (struct sockaddr_dl*) i->ifa_addr; MACAddress ma ((const uint8*) (sdl->sdl_data + sdl->sdl_nlen)); if (! ma.isNull()) diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_linux.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp similarity index 98% rename from JuceLibraryCode/modules/juce_core/native/juce_SystemStats_linux.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp index 74601fa7..562a37b4 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_linux.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp @@ -153,7 +153,7 @@ int SystemStats::getMemorySizeInMegabytes() 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; + return result == 0 ? (int) (memory / 1e6) : 0; #else struct sysinfo sysi; @@ -204,7 +204,7 @@ String SystemStats::getUserLanguage() return {}; #else - return getLocaleValue (_NL_ADDRESS_LANG_AB); + return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); #endif } @@ -213,7 +213,7 @@ String SystemStats::getUserRegion() #if JUCE_BSD return {}; #else - return getLocaleValue (_NL_ADDRESS_COUNTRY_AB2); + return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); #endif } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Threads_linux.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_Threads_linux.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_CFHelpers_mac.h b/JuceLibraryCode/modules/juce_core/native/juce_mac_CFHelpers.h similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_CFHelpers_mac.h rename to JuceLibraryCode/modules/juce_core/native/juce_mac_CFHelpers.h diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Files_mac.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_Files_mac.mm rename to JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Network_mac.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm similarity index 99% rename from JuceLibraryCode/modules/juce_core/native/juce_Network_mac.mm rename to JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm index 1a089403..a56fbb26 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_Network_mac.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm @@ -686,12 +686,7 @@ 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 (options.usePost) [request setHTTPMethod: @"POST"]; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_ObjCHelpers_mac.h b/JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCHelpers.h similarity index 92% rename from JuceLibraryCode/modules/juce_core/native/juce_ObjCHelpers_mac.h rename to JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCHelpers.h index 22bee6cf..c0c22096 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_ObjCHelpers_mac.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCHelpers.h @@ -20,7 +20,7 @@ ============================================================================== */ -#include "juce_CFHelpers_mac.h" +#include "juce_mac_CFHelpers.h" /* 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. @@ -504,59 +504,29 @@ auto createObjCBlockImpl (Class* object, Fn func, Signature) } } // 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; } + 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() const { return block; } - void swap (ObjCBlock& other) noexcept - { - std::swap (other.block, block); - } - private: - BlockType block = nullptr; + BlockType block; }; //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Strings_mac.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_Strings_mac.mm rename to JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_mac.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm similarity index 80% rename from JuceLibraryCode/modules/juce_core/native/juce_SystemStats_mac.mm rename to JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm index eaa14782..0a5f5fd5 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_mac.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm @@ -209,7 +209,7 @@ static String getOSXVersion() return String (reinterpret_cast (vendor), 12); #else - return "Apple"; + return {}; #endif } @@ -226,25 +226,17 @@ static String getOSXVersion() int SystemStats::getCpuSpeedInMegahertz() { - #ifdef JUCE_INTEL uint64 speedHz = 0; - size_t optSize = sizeof (speedHz); + size_t speedSize = sizeof (speedHz); int mib[] = { CTL_HW, HW_CPU_FREQ }; - 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); + sysctl (mib, 2, &speedHz, &speedSize, nullptr, 0); - return (int) (double (hz * uint64_t (ci.hz)) / 1000000.0); + #if JUCE_BIG_ENDIAN + if (speedSize == 4) + speedHz >>= 32; #endif + + return (int) (speedHz / 1000000); } //============================================================================== @@ -351,63 +343,32 @@ uint32 millisecondsSinceStartup() const noexcept 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 + static const auto deviceId = [] { - if (UIDevice* device = [UIDevice currentDevice]) - if (NSUUID* uuid = [device identifierForVendor]) - return nsStringToJuce ([uuid UUIDString]); - } - #endif + ChildProcess proc; - 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; + if (proc.start ("ioreg -rd1 -c IOPlatformExpertDevice", ChildProcess::wantStdOut)) + { + constexpr const char key[] = "\"IOPlatformUUID\""; + constexpr const auto keyLen = (int) sizeof (key); - const void* flag = nullptr; + auto output = proc.readAllProcessOutput(); + auto index = output.indexOf (key); - if (! CFDictionaryGetValueIfPresent (static_cast (entitlementsDict), @"com.apple.security.app-sandbox", &flag)) - return false; + if (index >= 0) + { + auto start = output.indexOf (index + keyLen, "\""); + auto end = output.indexOf (start + 1, "\""); + return output.substring (start + 1, end).replace("-", ""); + } + } - return static_cast (CFBooleanGetValue (static_cast (flag))); + return String(); }(); - return result; + // Please tell someone at JUCE if this occurs + jassert (deviceId.isNotEmpty()); + return deviceId; } -#endif } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Threads_mac.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm similarity index 63% rename from JuceLibraryCode/modules/juce_core/native/juce_Threads_mac.mm rename to JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm index ed81ea2d..2d4b05b7 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_Threads_mac.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm @@ -64,99 +64,28 @@ static auto getJucePriority (qos_class_t qos) 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 }; + PosixThreadAttribute attr { threadStackSize }; if (@available (macos 10.10, *)) - pthread_attr_set_qos_class_np (attribute.get(), getNativeQOS (priority), 0); + pthread_attr_set_qos_class_np (attr.get(), getNativeQOS (priority), 0); else - PosixSchedulerPriority::getNativeSchedulerAndPriority (realtimeOptions, priority).apply (attribute); - - struct ThreadData - { - Thread& thread; - std::promise started{}; - }; - - ThreadData threadData { *this, {} }; + PosixSchedulerPriority::getNativeSchedulerAndPriority (realtimeOptions, priority).apply (attr); - threadId = threadHandle = makeThreadHandle (attribute, &threadData, [] (void* userData) -> void* + threadId = threadHandle = makeThreadHandle (attr, this, [] (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); + auto* myself = static_cast (userData); JUCE_AUTORELEASEPOOL { - juce_threadEntryPoint (&thread); + juce_threadEntryPoint (myself); } return nullptr; }); - return threadId != nullptr - && threadData.started.get_future().get(); + return threadId != nullptr; } void Thread::killThread() @@ -193,6 +122,30 @@ static bool tryToUpgradeCurrentThreadToRealtime (const Thread::RealtimeOptions& { jassert (Thread::getCurrentThreadId() == getThreadId()); + if (isRealtime()) + { + // macOS/iOS needs to know how much time you need! + jassert (realtimeOptions->workDurationMs > 0); + + mach_timebase_info_data_t timebase; + mach_timebase_info (&timebase); + + const auto periodMs = realtimeOptions->workDurationMs; + const auto ticksPerMs = ((double) timebase.denom * 1000000.0) / (double) timebase.numer; + const auto periodTicks = (uint32_t) jmin ((double) std::numeric_limits::max(), periodMs * ticksPerMs); + + thread_time_constraint_policy_data_t policy; + policy.period = periodTicks; + policy.computation = jmin ((uint32_t) 50000, policy.period); + policy.constraint = policy.period; + policy.preemptible = true; + + return thread_policy_set (pthread_mach_thread_np (pthread_self()), + THREAD_TIME_CONSTRAINT_POLICY, + (thread_policy_t) &policy, + THREAD_TIME_CONSTRAINT_POLICY_COUNT) == KERN_SUCCESS; + } + if (@available (macOS 10.10, *)) return pthread_set_qos_class_self_np (getNativeQOS (priority), 0) == 0; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_ThreadPriorities_native.h b/JuceLibraryCode/modules/juce_core/native/juce_native_ThreadPriorities.h similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_ThreadPriorities_native.h rename to JuceLibraryCode/modules/juce_core/native/juce_native_ThreadPriorities.h diff --git a/JuceLibraryCode/modules/juce_core/native/juce_IPAddress_posix.h b/JuceLibraryCode/modules/juce_core/native/juce_posix_IPAddress.h similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_IPAddress_posix.h rename to JuceLibraryCode/modules/juce_core/native/juce_posix_IPAddress.h diff --git a/JuceLibraryCode/modules/juce_core/native/juce_NamedPipe_posix.cpp b/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_NamedPipe_posix.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SharedCode_posix.h b/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h similarity index 98% rename from JuceLibraryCode/modules/juce_core/native/juce_SharedCode_posix.h rename to JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h index ddddfb96..2a739b64 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_SharedCode_posix.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h @@ -854,7 +854,7 @@ class PosixThreadAttribute public: explicit PosixThreadAttribute (size_t stackSize) { - if (valid && stackSize != 0) + if (valid) pthread_attr_setstacksize (&attr, stackSize); } @@ -894,7 +894,7 @@ class PosixSchedulerPriority const auto min = jmax (0, sched_get_priority_min (SCHED_RR)); const auto max = jmax (1, sched_get_priority_max (SCHED_RR)); - return jmap (rt->getPriority(), 0, 10, min, max); + return jmap (rt->priority, 0, 10, min, max); } // We only use this helper if we're on an old macos/ios platform that might @@ -924,9 +924,9 @@ class PosixSchedulerPriority return 0; }(); - #if JUCE_MAC || JUCE_IOS || JUCE_BSD + #if JUCE_MAC || JUCE_IOS const auto scheduler = SCHED_OTHER; - #elif JUCE_LINUX + #elif JUCE_LINUX || JUCE_BSD const auto backgroundSched = prio == Thread::Priority::background ? SCHED_IDLE : SCHED_OTHER; const auto scheduler = isRealtime ? SCHED_RR : backgroundSched; @@ -959,13 +959,11 @@ class PosixSchedulerPriority int priority; }; -static void* makeThreadHandle (PosixThreadAttribute& attr, void* userData, void* (*threadEntryProc) (void*)) +static void* makeThreadHandle (PosixThreadAttribute& attr, Thread* userData, void* (*threadEntryProc) (void*)) { pthread_t handle = {}; - const auto status = pthread_create (&handle, attr.get(), threadEntryProc, userData); - - if (status != 0) + if (pthread_create (&handle, attr.get(), threadEntryProc, userData) != 0) return nullptr; pthread_detach (handle); @@ -1022,16 +1020,8 @@ void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask ([[maybe_unused]] uint32 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 || JUCE_BSD)) || ((__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2004)) pthread_setaffinity_np (pthread_self(), sizeof (cpu_set_t), &affinity); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_wasm.cpp b/JuceLibraryCode/modules/juce_core/native/juce_wasm_SystemStats.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_SystemStats_wasm.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_wasm_SystemStats.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_ComSmartPtr_windows.h b/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_ComSmartPtr_windows.h rename to JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Files_windows.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_Files_windows.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Network_windows.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_Network_windows.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Registry_windows.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_Registry_windows.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp diff --git a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_windows.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp similarity index 73% rename from JuceLibraryCode/modules/juce_core/native/juce_SystemStats_windows.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp index 0972e83d..bb4020bf 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_SystemStats_windows.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp @@ -659,221 +659,28 @@ String SystemStats::getDisplayLanguage() 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) + #define PROVIDER(string) (DWORD) (string[0] << 24 | string[1] << 16 | string[2] << 8 | string[3]) - if (smbiosBuffer->size() < sizeof (RawSMBIOSData)) - { - // Malformed buffer; not enough room for RawSMBIOSData instance - jassertfalse; - return {}; - } + auto bufLen = GetSystemFirmwareTable (PROVIDER ("RSMB"), PROVIDER ("RSDT"), nullptr, 0); - String uuid; - const auto* asRawSMBIOSData = unalignedPointerCast (smbiosBuffer->data()); + if (bufLen > 0) + { + HeapBlock buffer { bufLen }; + GetSystemFirmwareTable (PROVIDER ("RSMB"), PROVIDER ("RSDT"), (void*) buffer.getData(), bufLen); - if (smbiosBuffer->size() < sizeof (RawSMBIOSData) + static_cast (asRawSMBIOSData->length)) + return [&] { - // Malformed buffer; declared length is longer than the buffer we were given - jassertfalse; - return {}; - } - - Span content (smbiosBuffer->data() + sizeof (RawSMBIOSData), asRawSMBIOSData->length); + uint64_t hash = 0; + const auto start = buffer.getData(); + const auto end = start + jmin (1024, (int) bufLen); - 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); - } + for (auto dataPtr = start; dataPtr != end; ++dataPtr) + hash = hash * (uint64_t) 101 + *dataPtr; - return String (uuid.hashCode64()); + return String (hash); + }(); } // Please tell someone at JUCE if this occurs diff --git a/JuceLibraryCode/modules/juce_core/native/juce_Threads_windows.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_core/native/juce_Threads_windows.cpp rename to JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp diff --git a/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.cpp b/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.cpp index 71b58311..1183c1bc 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_WebInputStream.cpp @@ -61,8 +61,11 @@ StringPairArray WebInputStream::parseHttpHeaders (const String& headerData) StringPairArray headerPairs; auto headerLines = StringArray::fromLines (headerData); - for (const auto& headersEntry : headerLines) + // ignore the first line as this is the status line + for (int i = 1; i < headerLines.size(); ++i) { + const auto& headersEntry = headerLines[i]; + if (headersEntry.isNotEmpty()) { const auto key = headersEntry.upToFirstOccurrenceOf (": ", false, false); diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp index 80064673..5e60dd61 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp @@ -138,8 +138,8 @@ class MemoryStreamTests : public UnitTest expectEquals (mi.readString(), randomString); expect (mi.readInt64() == randomInt64); expect (mi.readInt64BigEndian() == randomInt64); - expectEquals (mi.readDouble(), randomDouble); - expectEquals (mi.readDoubleBigEndian(), randomDouble); + expect (mi.readDouble() == randomDouble); + expect (mi.readDoubleBigEndian() == randomDouble); const MemoryBlock data ("abcdefghijklmnopqrstuvwxyz", 26); MemoryInputStream stream (data, true); diff --git a/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h b/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h index e13af095..05845903 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_CompilerWarnings.h @@ -31,8 +31,7 @@ #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, ...)\ + _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, N, ...)\ N #define JUCE_EACH_00_(FN) @@ -75,30 +74,10 @@ #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_, \ diff --git a/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h b/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h index a7b2d350..7a071434 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h @@ -29,7 +29,7 @@ */ #define JUCE_MAJOR_VERSION 7 #define JUCE_MINOR_VERSION 0 -#define JUCE_BUILDNUMBER 7 +#define JUCE_BUILDNUMBER 5 /** Current JUCE version number. @@ -72,7 +72,6 @@ #include #include #include -#include #include //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp index 5f9b7526..8601bc08 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp @@ -60,64 +60,24 @@ String SystemStats::getJUCEVersion() StringArray SystemStats::getDeviceIdentifiers() { - for (const auto flag : { MachineIdFlags::fileSystemId, MachineIdFlags::macAddresses }) - if (auto ids = getMachineIdentifiers (flag); ! ids.isEmpty()) - return ids; - - jassertfalse; // Failed to create any IDs! - return {}; -} - -String getLegacyUniqueDeviceID(); - -StringArray SystemStats::getMachineIdentifiers (MachineIdFlags flags) -{ - auto macAddressProvider = [] (StringArray& arr) - { - 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) + #if JUCE_WINDOWS + File f (File::getSpecialLocation (File::windowsSystemDirectory)); + #else + File f ("~"); + #endif + if (auto num = f.getFileIdentifier()) { - if (hasBitValueSet (flags, provider.flag)) - provider.func (ids); + ids.add (String::toHexString ((int64) num)); + } + else + { + for (auto& address : MACAddress::getAllAddresses()) + ids.add (address.toString()); } + jassert (! ids.isEmpty()); // Failed to create any IDs! return ids; } @@ -217,7 +177,7 @@ String SystemStats::getStackBacktrace() auto frames = backtrace (stack, numElementsInArray (stack)); char** frameStrings = backtrace_symbols (stack, frames); - for (auto i = (decltype (frames)) 0; i < frames; ++i) + for (int i = 0; i < frames; ++i) result << frameStrings[i] << newLine; ::free (frameStrings); @@ -270,8 +230,13 @@ void SystemStats::setApplicationCrashHandler (CrashHandlerFunction handler) bool SystemStats::isRunningInAppExtensionSandbox() noexcept { #if JUCE_MAC || JUCE_IOS - static bool isRunningInAppSandbox = [&] + static bool firstQuery = true; + static bool isRunningInAppSandbox = false; + + if (firstQuery) { + firstQuery = false; + File bundle = File::getSpecialLocation (File::invokedExecutableFile).getParentDirectory(); #if JUCE_MAC @@ -279,10 +244,8 @@ bool SystemStats::isRunningInAppExtensionSandbox() noexcept #endif if (bundle.isDirectory()) - return bundle.getFileExtension() == ".appex"; - - return false; - }(); + isRunningInAppSandbox = (bundle.getFileExtension() == ".appex"); + } return isRunningInAppSandbox; #else diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h index 777ce65b..a80cc541 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h @@ -153,41 +153,10 @@ class JUCE_API SystemStats final 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. + platforms, or resetting an Android device. */ 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.. @@ -259,7 +228,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. @@ -274,10 +243,6 @@ class JUCE_API SystemStats final */ static bool isRunningInAppExtensionSandbox() noexcept; - #if JUCE_MAC - static bool isAppSandboxEnabled(); - #endif - //============================================================================== #ifndef DOXYGEN [[deprecated ("This method was spelt wrong! Please change your code to use getCpuSpeedInMegahertz instead.")]] @@ -289,6 +254,4 @@ class JUCE_API SystemStats final 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 595027c4..a93c2796 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h @@ -62,7 +62,7 @@ #elif defined (JUCE_ANDROID) #undef JUCE_ANDROID #define JUCE_ANDROID 1 -#elif defined (__FreeBSD__) || defined (__OpenBSD__) +#elif defined (__FreeBSD__) || (__OpenBSD__) #define JUCE_BSD 1 #elif defined (LINUX) || defined (__linux__) #define JUCE_LINUX 1 diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp index 407e70a6..f1b466ff 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp @@ -134,7 +134,7 @@ double CharacterFunctions::mulexp10 (const double value, int exponent) noexcept if (exponent == 0) return value; - if (exactlyEqual (value, 0.0)) + if (value == 0.0) return 0; const bool negative = (exponent < 0); diff --git a/JuceLibraryCode/modules/juce_core/text/juce_String.cpp b/JuceLibraryCode/modules/juce_core/text/juce_String.cpp index 4675f1ec..02c2c8f8 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_String.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_String.cpp @@ -2266,7 +2266,7 @@ static String serialiseDouble (double input) int intInput = (int) input; - if (exactlyEqual ((double) intInput, input)) + if ((double) intInput == input) return { input, 1 }; auto numberOfDecimalPlaces = [absInput] @@ -2567,16 +2567,16 @@ class StringTests : public UnitTest beginTest ("Numeric conversions"); expect (String().getIntValue() == 0); - expectEquals (String().getDoubleValue(), 0.0); - expectEquals (String().getFloatValue(), 0.0f); + expect (String().getDoubleValue() == 0.0); + expect (String().getFloatValue() == 0.0f); expect (s.getIntValue() == 12345678); expect (s.getLargeIntValue() == (int64) 12345678); - expectEquals (s.getDoubleValue(), 12345678.0); - expectEquals (s.getFloatValue(), 12345678.0f); + expect (s.getDoubleValue() == 12345678.0); + expect (s.getFloatValue() == 12345678.0f); expect (String (-1234).getIntValue() == -1234); expect (String ((int64) -1234).getLargeIntValue() == -1234); - expectEquals (String (-1234.56).getDoubleValue(), -1234.56); - expectEquals (String (-1234.56f).getFloatValue(), -1234.56f); + expect (String (-1234.56).getDoubleValue() == -1234.56); + expect (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()); diff --git a/JuceLibraryCode/modules/juce_core/text/juce_String.h b/JuceLibraryCode/modules/juce_core/text/juce_String.h index f7f72906..e24f79ba 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_String.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_String.h @@ -1117,7 +1117,7 @@ class JUCE_API String final { jassert (numberOfSignificantFigures > 0); - if (exactlyEqual (number, DecimalType())) + if (number == 0) { if (numberOfSignificantFigures > 1) { diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp index cec29223..28ca899d 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp @@ -23,411 +23,98 @@ namespace juce { -//============================================================================== -class HighResolutionTimer::Impl : private PlatformTimerListener +class HighResolutionTimer::Pimpl : public Thread { -public: - explicit Impl (HighResolutionTimer& o) - : owner { o } {} + using steady_clock = std::chrono::steady_clock; + using milliseconds = std::chrono::milliseconds; - void startTimer (int newIntervalMs) +public: + explicit Pimpl (HighResolutionTimer& ownerRef) + : Thread ("HighResolutionTimerThread"), + owner (ownerRef) { - shouldCancelCallbacks.store (true); - - const auto shouldWaitForPendingCallbacks = [&] - { - const std::scoped_lock lock { timerMutex }; - - if (timer.getIntervalMs() > 0) - timer.cancelTimer(); + } - jassert (timer.getIntervalMs() == 0); + using Thread::isThreadRunning; - if (newIntervalMs > 0) - timer.startTimer (jmax (0, newIntervalMs)); + void start (int periodMs) + { + { + const std::scoped_lock lk { mutex }; + periodMillis = periodMs; + nextTickTime = steady_clock::now() + milliseconds (periodMillis); + } - return callbackThreadId != std::this_thread::get_id() - && timer.getIntervalMs() <= 0; - }(); + waitEvent.notify_one(); - if (shouldWaitForPendingCallbacks) - std::scoped_lock lock { callbackMutex }; + if (! isThreadRunning()) + startThread (Thread::Priority::high); } - int getIntervalMs() const + void stop() { - const std::scoped_lock lock { timerMutex }; - return timer.getIntervalMs(); + { + const std::scoped_lock lk { mutex }; + periodMillis = 0; + } + + waitEvent.notify_one(); + + if (Thread::getCurrentThreadId() != getThreadId()) + stopThread (-1); } - bool isTimerRunning() const + int getPeriod() const { - return getIntervalMs() > 0; + return periodMillis; } private: - void onTimerExpired() final + void run() override { - callbackThreadId.store (std::this_thread::get_id()); - + for (;;) { - 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; - } + std::unique_lock lk { mutex }; + + if (waitEvent.wait_until (lk, nextTickTime, [this] { return periodMillis == 0; })) + break; + + nextTickTime = steady_clock::now() + milliseconds (periodMillis); } - } - callbackThreadId.store ({}); + owner.hiResTimerCallback(); + } } 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) + std::atomic periodMillis { 0 }; + steady_clock::time_point nextTickTime; + std::mutex mutex; + std::condition_variable waitEvent; }; -//============================================================================== HighResolutionTimer::HighResolutionTimer() - : impl (std::make_unique (*this)) {} + : pimpl (new Pimpl (*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) +void HighResolutionTimer::startTimer (int periodMs) { - impl->startTimer (newIntervalMs); + pimpl->start (jmax (1, periodMs)); } void HighResolutionTimer::stopTimer() { - impl->startTimer (0); + pimpl->stop(); } -int HighResolutionTimer::getTimerInterval() const noexcept -{ - return impl->getIntervalMs(); -} - -bool HighResolutionTimer::isTimerRunning() const noexcept -{ - return impl->isTimerRunning(); -} - -//============================================================================== -#if JUCE_UNIT_TESTS - -class HighResolutionTimerTests : 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 : public HighResolutionTimer - { - public: - explicit Timer (std::function fn) - : callback (std::move (fn)) {} - - ~Timer() override { stopTimer(); } - - void hiResTimerCallback() override { callback(); } - - private: - std::function callback; - }; -}; - -static HighResolutionTimerTests highResolutionTimerTests; - -#endif +bool HighResolutionTimer::isTimerRunning() const noexcept { return getTimerInterval() != 0; } +int HighResolutionTimer::getTimerInterval() const noexcept { return pimpl->getPeriod(); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h index 37bd72d1..2ac6402a 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h @@ -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 - the HighResolutionTimer will use far more resources and require thread - safety considerations. + to start/stop, the HighResolutionTimer will use far more resources, and + starting/stopping it may involve launching and killing threads. @see Timer @@ -57,29 +57,20 @@ 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. 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. + callback to change the subsequent intervals. */ virtual void hiResTimerCallback() = 0; //============================================================================== /** Starts the timer and sets the length of interval required. - 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. + 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. - @param intervalInMilliseconds the interval to use (a value of zero or less will stop the timer) + @param intervalInMilliseconds the interval to use (any values less than 1 will be + rounded up to 1) */ void startTimer (int intervalInMilliseconds); @@ -88,9 +79,6 @@ 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(); @@ -105,8 +93,8 @@ class JUCE_API HighResolutionTimer int getTimerInterval() const noexcept; private: - class Impl; - std::unique_ptr impl; + class Pimpl; + std::unique_ptr pimpl; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HighResolutionTimer) }; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp index 71366088..5b870311 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp @@ -166,7 +166,7 @@ bool Thread::startRealtimeThread (const RealtimeOptions& options) if (threadHandle == nullptr) { - realtimeOptions = std::make_optional (options); + realtimeOptions = makeOptional (options); if (startThreadInternal (Priority::normal)) return true; @@ -276,7 +276,7 @@ void Thread::removeListener (Listener* listener) bool Thread::isRealtime() const { - return realtimeOptions.has_value(); + return realtimeOptions.hasValue(); } void Thread::setAffinityMask (const uint32 newAffinityMask) @@ -285,7 +285,7 @@ void Thread::setAffinityMask (const uint32 newAffinityMask) } //============================================================================== -bool Thread::wait (double timeOutMilliseconds) const +bool Thread::wait (const int timeOutMilliseconds) const { return defaultEvent.wait (timeOutMilliseconds); } @@ -417,7 +417,7 @@ class AtomicTests : public UnitTest class AtomicTester { public: - AtomicTester() = default; + AtomicTester() {} static void testInteger (UnitTest& test) { @@ -460,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 (exactlyEqual (a.get(), (Type) 101)); + test.expect (a.get() == (Type) 101); test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200)); - test.expect (exactlyEqual (a.get(), (Type) 101)); + test.expect (a.get() == (Type) 101); test.expect (a.compareAndSetBool ((Type) 200, a.get())); - test.expect (exactlyEqual (a.get(), (Type) 200)); + test.expect (a.get() == (Type) 200); - test.expect (exactlyEqual (a.exchange ((Type) 300), (Type) 200)); - test.expect (exactlyEqual (a.get(), (Type) 300)); + test.expect (a.exchange ((Type) 300) == (Type) 200); + test.expect (a.get() == (Type) 300); b = a; - test.expect (exactlyEqual (b.get(), a.get())); + test.expect (b.get() == a.get()); } }; }; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h index 94abaea6..97b66ff6 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h @@ -42,9 +42,6 @@ namespace juce class JUCE_API Thread { public: - //============================================================================== - static constexpr size_t osDefaultStackSize { 0 }; - //============================================================================== /** The different runtime priorities of non-realtime threads. @@ -75,114 +72,14 @@ class JUCE_API Thread */ 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 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); - } + /** Linux only: A value with a range of 0-10, where 10 is the highest priority. */ + int priority = 5; - /** 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 + /** iOS/macOS only: A millisecond value representing the estimated time between each + 'Thread::run' call. Your thread may be penalised if you frequently + overrun this. */ - [[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{}; + uint32_t workDurationMs = 0; }; //============================================================================== @@ -198,7 +95,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 = osDefaultStackSize); + explicit Thread (const String& threadName, size_t threadStackSize = 0); /** Destructor. @@ -440,7 +337,7 @@ class JUCE_API Thread @returns true if the event has been signalled, false if the timeout expires. */ - bool wait (double timeOutMilliseconds) const; + bool wait (int timeOutMilliseconds) const; /** Wakes up the thread. @@ -564,7 +461,7 @@ class JUCE_API Thread const String threadName; std::atomic threadHandle { nullptr }; std::atomic threadId { nullptr }; - std::optional realtimeOptions = {}; + Optional realtimeOptions = {}; CriticalSection startStopLock; WaitableEvent startSuspensionEvent, defaultEvent; size_t threadStackSize; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp index 3d6a7891..8639481d 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp @@ -25,9 +25,8 @@ namespace juce struct ThreadPool::ThreadPoolThread : public Thread { - ThreadPoolThread (ThreadPool& p, const Options& options) - : Thread { options.threadName, options.threadStackSizeBytes }, - pool { p } + ThreadPoolThread (ThreadPool& p, size_t stackSize) + : Thread ("Pool", stackSize), pool (p) { } @@ -94,24 +93,18 @@ ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob() } //============================================================================== -ThreadPool::ThreadPool (const Options& options) +ThreadPool::ThreadPool (int numThreads, size_t threadStackSize, Thread::Priority priority) { - // not much point having a pool without any threads! - jassert (options.numberOfThreads > 0); + jassert (numThreads > 0); // not much point having a pool without any threads! - for (int i = jmax (1, options.numberOfThreads); --i >= 0;) - threads.add (new ThreadPoolThread (*this, options)); + for (int i = jmax (1, numThreads); --i >= 0;) + threads.add (new ThreadPoolThread (*this, threadStackSize)); for (auto* t : threads) - t->startThread (options.desiredThreadPriority); + t->startThread (priority); } -ThreadPool::ThreadPool (int numberOfThreads, - size_t threadStackSizeBytes, - Thread::Priority desiredThreadPriority) - : ThreadPool { Options{}.withNumberOfThreads (numberOfThreads) - .withThreadStackSizeBytes (threadStackSizeBytes) - .withDesiredThreadPriority (desiredThreadPriority) } +ThreadPool::ThreadPool() : ThreadPool (SystemStats::getNumCpus(), 0, Thread::Priority::normal) { } @@ -169,13 +162,13 @@ void ThreadPool::addJob (std::function jobToRun) { struct LambdaJobWrapper : public ThreadPoolJob { - LambdaJobWrapper (std::function j) : ThreadPoolJob ("lambda"), job (std::move (j)) {} + LambdaJobWrapper (std::function j) : ThreadPoolJob ("lambda"), job (j) {} JobStatus runJob() override { job(); return ThreadPoolJob::jobHasFinished; } std::function job; }; - addJob (new LambdaJobWrapper (std::move (jobToRun)), true); + addJob (new LambdaJobWrapper (jobToRun), true); } int ThreadPool::getNumJobs() const noexcept diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h index 668c6423..779b1d02 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h @@ -140,51 +140,6 @@ 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 }; -}; - //============================================================================== /** @@ -200,38 +155,26 @@ struct ThreadPoolOptions class JUCE_API ThreadPool { public: - using Options = ThreadPoolOptions; - //============================================================================== - /** Creates a thread pool based on the provided options. - Once you've created a pool, you can give it some jobs by calling addJob(). - - @see ThreadPool::ThreadPoolOptions - */ - explicit ThreadPool (const Options& options); - - /** Creates a thread pool based using the default arguments provided by - ThreadPoolOptions. - + /** Creates a thread pool. Once you've created a pool, you can give it some jobs by calling addJob(). - @see ThreadPoolOptions + @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. + @param priority the desired priority of each thread in the pool. */ - ThreadPool() : ThreadPool { Options{} } {} + ThreadPool (int numberOfThreads, size_t threadStackSize = 0, Thread::Priority priority = Thread::Priority::normal); - /** Creates a thread pool. + /** 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(). - - @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. + 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() */ - ThreadPool (int numberOfThreads, - size_t threadStackSizeBytes = Thread::osDefaultStackSize, - Thread::Priority desiredThreadPriority = Thread::Priority::normal); + ThreadPool(); /** Destructor. diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp index f2b29ab0..14323707 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.cpp @@ -28,19 +28,19 @@ WaitableEvent::WaitableEvent (bool manualReset) noexcept { } -bool WaitableEvent::wait (double timeOutMilliseconds) const +bool WaitableEvent::wait (int timeOutMilliseconds) const { std::unique_lock lock (mutex); if (! triggered) { - if (timeOutMilliseconds < 0.0) + if (timeOutMilliseconds < 0) { condition.wait (lock, [this] { return triggered == true; }); } else { - if (! condition.wait_for (lock, std::chrono::duration { timeOutMilliseconds }, + if (! condition.wait_for (lock, std::chrono::milliseconds (timeOutMilliseconds), [this] { return triggered == true; })) { return false; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h index ca55f904..406ad74c 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h @@ -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 (double timeOutMilliseconds = -1.0) const; + bool wait (int timeOutMilliseconds = -1) const; /** Wakes up any threads that are currently waiting on this object. diff --git a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp index bd8c233d..60b1a1e9 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp @@ -54,16 +54,8 @@ 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 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(); } 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(); } diff --git a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h index 96c5fb88..1685f5bc 100644 --- a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h +++ b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h @@ -150,7 +150,7 @@ class JUCE_API UnitTest template void expectEquals (ValueType actual, ValueType expected, String failureMessage = String()) { - bool result = exactlyEqual (actual, expected); + bool result = 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 = ! exactlyEqual (value, valueToCompareTo); + bool result = value != valueToCompareTo; expectResultAndPrint (value, valueToCompareTo, result, "unequal to", failureMessage); } diff --git a/JuceLibraryCode/modules/juce_cryptography/juce_cryptography.h b/JuceLibraryCode/modules/juce_cryptography/juce_cryptography.h index 807d0c2d..8373b146 100644 --- a/JuceLibraryCode/modules/juce_cryptography/juce_cryptography.h +++ b/JuceLibraryCode/modules/juce_cryptography/juce_cryptography.h @@ -35,7 +35,7 @@ ID: juce_cryptography vendor: juce - version: 7.0.7 + version: 7.0.5 name: JUCE cryptography classes description: Classes for various basic cryptography functions, including RSA, Blowfish, MD5, SHA, etc. website: http://www.juce.com/juce diff --git a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h index a39fb1ea..4f9ebdf0 100644 --- a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h +++ b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h @@ -35,7 +35,7 @@ ID: juce_data_structures vendor: juce - version: 7.0.7 + version: 7.0.5 name: JUCE data model helper classes description: Classes for undo/redo management, and smart data structures. website: http://www.juce.com/juce diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h index 11f870b9..0ce17a86 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_CachedValue.h @@ -116,18 +116,13 @@ class CachedValue : private ValueTree::Listener is equal to other. */ template - bool operator== (const OtherType& other) const - { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") - return cachedValue == other; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - } + 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 ! operator== (other); } + bool operator!= (const OtherType& other) const { return cachedValue != other; } //============================================================================== /** Returns the current property as a Value object. */ @@ -250,7 +245,7 @@ inline CachedValue& CachedValue::operator= (const Type& newValue) template inline void CachedValue::setValue (const Type& newValue, UndoManager* undoManagerToUse) { - if (! exactlyEqual (cachedValue, newValue) || isUsingDefault()) + if (cachedValue != newValue || isUsingDefault()) { cachedValue = newValue; targetTree.setProperty (targetProperty, VariantConverter::toVar (newValue), undoManagerToUse); diff --git a/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock_test.cpp b/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock_test.cpp index bfd2f8aa..17ffad60 100644 --- a/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock_test.cpp +++ b/JuceLibraryCode/modules/juce_dsp/containers/juce_AudioBlock_test.cpp @@ -28,9 +28,6 @@ namespace juce namespace dsp { -template -String& operator<< (String& str, SIMDRegister) { return str; } - template class AudioBlockUnitTests : public UnitTest { @@ -82,25 +79,25 @@ class AudioBlockUnitTests : public UnitTest resetBlocks(); expect (block != otherBlock); - 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)); + 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)); block.swap (otherBlock); expect (block != otherBlock); - 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)); + 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)); block.swap (otherBlock); - 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)); + 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)); } beginTest ("Getters and setters"); @@ -110,49 +107,49 @@ class AudioBlockUnitTests : public UnitTest expectEquals ((int) block.getNumChannels(), (int) data.size()); expectEquals ((int) block.getNumSamples(), numSamples); - expectEquals (block.getChannelPointer (0)[2], SampleType (3.0)); + expect (block.getChannelPointer (0)[2] == SampleType (3.0)); block.getChannelPointer (0)[2] = SampleType (999.0); - expectEquals (block.getChannelPointer (0)[2], SampleType (999.0)); + expect (block.getChannelPointer (0)[2] == SampleType (999.0)); - expectEquals (block.getSample (0, 4), SampleType (5.0)); - expectEquals (block.getSample (1, 4), SampleType (11.0)); + expect (block.getSample (0, 4) == SampleType (5.0)); + expect (block.getSample (1, 4) == SampleType (11.0)); - expectEquals (block.getSingleChannelBlock (1).getSample (0, 3), block.getSample (1, 3)); + expect (block.getSingleChannelBlock (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)); + expect (block.getSubsetChannelBlock (0, 2).getSample (1, 3) == block.getSample (1, 3)); + expect (block.getSubsetChannelBlock (1, 1).getSample (0, 3) == block.getSample (1, 3)); block.setSample (1, 1, SampleType (777.0)); - expectEquals (block.getSample (1, 1), SampleType (777.0)); + expect (block.getSample (1, 1) == SampleType (777.0)); block.addSample (1, 1, SampleType (1.0)); - expectEquals (block.getSample (1, 1), SampleType (778.0)); + expect (block.getSample (1, 1) == SampleType (778.0)); } beginTest ("Basic copying"); { block.clear(); - expectEquals (block.getSample (0, 2), SampleType (0.0)); - expectEquals (block.getSample (1, 4), SampleType (0.0)); + expect (block.getSample (0, 2) == SampleType (0.0)); + expect (block.getSample (1, 4) == SampleType (0.0)); block.fill ((NumericType) 456.0); - expectEquals (block.getSample (0, 2), SampleType (456.0)); - expectEquals (block.getSample (1, 4), SampleType (456.0)); + expect (block.getSample (0, 2) == SampleType (456.0)); + expect (block.getSample (1, 4) == SampleType (456.0)); block.copyFrom (otherBlock); expect (block != otherBlock); - expectEquals (block.getSample (0, 2), otherBlock.getSample (0, 2)); - expectEquals (block.getSample (1, 4), otherBlock.getSample (1, 4)); + expect (block.getSample (0, 2) == otherBlock.getSample (0, 2)); + expect (block.getSample (1, 4) == otherBlock.getSample (1, 4)); resetBlocks(); SampleType testSample1 = block.getSample (0, 2); SampleType testSample2 = block.getSample (1, 3); - expectNotEquals (testSample1, block.getSample (0, 4)); - expectNotEquals (testSample2, block.getSample (1, 5)); + expect (testSample1 != block.getSample (0, 4)); + expect (testSample2 != block.getSample (1, 5)); block.move (0, 2); - expectEquals (block.getSample (0, 4), testSample1); - expectEquals (block.getSample (1, 5), testSample2); + expect (block.getSample (0, 4) == testSample1); + expect (block.getSample (1, 5) == testSample2); } beginTest ("Addition"); @@ -160,22 +157,22 @@ class AudioBlockUnitTests : public UnitTest resetBlocks(); block.add ((NumericType) 15.0); - expectEquals (block.getSample (0, 4), SampleType (20.0)); - expectEquals (block.getSample (1, 4), SampleType (26.0)); + expect (block.getSample (0, 4) == SampleType (20.0)); + expect (block.getSample (1, 4) == SampleType (26.0)); block.add (otherBlock); - expectEquals (block.getSample (0, 4), SampleType (15.0)); - expectEquals (block.getSample (1, 4), SampleType (15.0)); + expect (block.getSample (0, 4) == SampleType (15.0)); + expect (block.getSample (1, 4) == SampleType (15.0)); block.replaceWithSumOf (otherBlock, (NumericType) 9.0); - expectEquals (block.getSample (0, 4), SampleType (4.0)); - expectEquals (block.getSample (1, 4), SampleType (-2.0)); + expect (block.getSample (0, 4) == SampleType (4.0)); + expect (block.getSample (1, 4) == SampleType (-2.0)); resetBlocks(); block.replaceWithSumOf (block, otherBlock); - expectEquals (block.getSample (0, 4), SampleType (0.0)); - expectEquals (block.getSample (1, 4), SampleType (0.0)); + expect (block.getSample (0, 4) == SampleType (0.0)); + expect (block.getSample (1, 4) == SampleType (0.0)); } beginTest ("Subtraction"); @@ -183,22 +180,22 @@ class AudioBlockUnitTests : public UnitTest resetBlocks(); block.subtract ((NumericType) 15.0); - expectEquals (block.getSample (0, 4), SampleType (-10.0)); - expectEquals (block.getSample (1, 4), SampleType (-4.0)); + expect (block.getSample (0, 4) == SampleType (-10.0)); + expect (block.getSample (1, 4) == SampleType (-4.0)); block.subtract (otherBlock); - expectEquals (block.getSample (0, 4), SampleType (-5.0)); - expectEquals (block.getSample (1, 4), SampleType (7.0)); + expect (block.getSample (0, 4) == SampleType (-5.0)); + expect (block.getSample (1, 4) == SampleType (7.0)); block.replaceWithDifferenceOf (otherBlock, (NumericType) 9.0); - expectEquals (block.getSample (0, 4), SampleType (-14.0)); - expectEquals (block.getSample (1, 4), SampleType (-20.0)); + expect (block.getSample (0, 4) == SampleType (-14.0)); + expect (block.getSample (1, 4) == SampleType (-20.0)); resetBlocks(); block.replaceWithDifferenceOf (block, otherBlock); - expectEquals (block.getSample (0, 4), SampleType (10.0)); - expectEquals (block.getSample (1, 4), SampleType (22.0)); + expect (block.getSample (0, 4) == SampleType (10.0)); + expect (block.getSample (1, 4) == SampleType (22.0)); } beginTest ("Multiplication"); @@ -206,22 +203,22 @@ class AudioBlockUnitTests : public UnitTest resetBlocks(); block.multiplyBy ((NumericType) 10.0); - expectEquals (block.getSample (0, 4), SampleType (50.0)); - expectEquals (block.getSample (1, 4), SampleType (110.0)); + expect (block.getSample (0, 4) == SampleType (50.0)); + expect (block.getSample (1, 4) == SampleType (110.0)); block.multiplyBy (otherBlock); - expectEquals (block.getSample (0, 4), SampleType (-250.0)); - expectEquals (block.getSample (1, 4), SampleType (-1210.0)); + expect (block.getSample (0, 4) == SampleType (-250.0)); + expect (block.getSample (1, 4) == SampleType (-1210.0)); block.replaceWithProductOf (otherBlock, (NumericType) 3.0); - expectEquals (block.getSample (0, 4), SampleType (-15.0)); - expectEquals (block.getSample (1, 4), SampleType (-33.0)); + expect (block.getSample (0, 4) == SampleType (-15.0)); + expect (block.getSample (1, 4) == SampleType (-33.0)); resetBlocks(); block.replaceWithProductOf (block, otherBlock); - expectEquals (block.getSample (0, 4), SampleType (-25.0)); - expectEquals (block.getSample (1, 4), SampleType (-121.0)); + expect (block.getSample (0, 4) == SampleType (-25.0)); + expect (block.getSample (1, 4) == SampleType (-121.0)); } beginTest ("Multiply add"); @@ -229,12 +226,12 @@ class AudioBlockUnitTests : public UnitTest resetBlocks(); block.addProductOf (otherBlock, (NumericType) -1.0); - expectEquals (block.getSample (0, 4), SampleType (10.0)); - expectEquals (block.getSample (1, 4), SampleType (22.0)); + expect (block.getSample (0, 4) == SampleType (10.0)); + expect (block.getSample (1, 4) == SampleType (22.0)); block.addProductOf (otherBlock, otherBlock); - expectEquals (block.getSample (0, 4), SampleType (35.0)); - expectEquals (block.getSample (1, 4), SampleType (143.0)); + expect (block.getSample (0, 4) == SampleType (35.0)); + expect (block.getSample (1, 4) == SampleType (143.0)); } beginTest ("Negative abs min max"); @@ -243,68 +240,68 @@ class AudioBlockUnitTests : public UnitTest otherBlock.negate(); block.add (otherBlock); - expectEquals (block.getSample (0, 4), SampleType (10.0)); - expectEquals (block.getSample (1, 4), SampleType (22.0)); + expect (block.getSample (0, 4) == SampleType (10.0)); + expect (block.getSample (1, 4) == SampleType (22.0)); block.replaceWithNegativeOf (otherBlock); - expectEquals (block.getSample (0, 4), SampleType (-5.0)); - expectEquals (block.getSample (1, 4), SampleType (-11.0)); + expect (block.getSample (0, 4) == SampleType (-5.0)); + expect (block.getSample (1, 4) == SampleType (-11.0)); block.clear(); otherBlock.negate(); block.replaceWithAbsoluteValueOf (otherBlock); - expectEquals (block.getSample (0, 4), SampleType (5.0)); - expectEquals (block.getSample (1, 4), SampleType (11.0)); + expect (block.getSample (0, 4) == SampleType (5.0)); + expect (block.getSample (1, 4) == SampleType (11.0)); resetBlocks(); block.replaceWithMinOf (block, otherBlock); - expectEquals (block.getSample (0, 4), SampleType (-5.0)); - expectEquals (block.getSample (1, 4), SampleType (-11.0)); + expect (block.getSample (0, 4) == SampleType (-5.0)); + expect (block.getSample (1, 4) == SampleType (-11.0)); resetBlocks(); block.replaceWithMaxOf (block, otherBlock); - expectEquals (block.getSample (0, 4), SampleType (5.0)); - expectEquals (block.getSample (1, 4), SampleType (11.0)); + expect (block.getSample (0, 4) == SampleType (5.0)); + expect (block.getSample (1, 4) == SampleType (11.0)); resetBlocks(); auto range = block.findMinAndMax(); - expectEquals (SampleType (range.getStart()), SampleType (1.0)); - expectEquals (SampleType (range.getEnd()), SampleType (12.0)); + expect (SampleType (range.getStart()) == SampleType (1.0)); + expect (SampleType (range.getEnd()) == SampleType (12.0)); } beginTest ("Operators"); { resetBlocks(); block += (NumericType) 10.0; - expectEquals (block.getSample (0, 4), SampleType (15.0)); - expectEquals (block.getSample (1, 4), SampleType (21.0)); + expect (block.getSample (0, 4) == SampleType (15.0)); + expect (block.getSample (1, 4) == SampleType (21.0)); block += otherBlock; - expectEquals (block.getSample (0, 4), SampleType (10.0)); - expectEquals (block.getSample (1, 4), SampleType (10.0)); + expect (block.getSample (0, 4) == SampleType (10.0)); + expect (block.getSample (1, 4) == SampleType (10.0)); resetBlocks(); block -= (NumericType) 10.0; - expectEquals (block.getSample (0, 4), SampleType (-5.0)); - expectEquals (block.getSample (1, 4), SampleType (1.0)); + expect (block.getSample (0, 4) == SampleType (-5.0)); + expect (block.getSample (1, 4) == SampleType (1.0)); block -= otherBlock; - expectEquals (block.getSample (0, 4), SampleType (0.0)); - expectEquals (block.getSample (1, 4), SampleType (12.0)); + expect (block.getSample (0, 4) == SampleType (0.0)); + expect (block.getSample (1, 4) == SampleType (12.0)); resetBlocks(); block *= (NumericType) 10.0; - expectEquals (block.getSample (0, 4), SampleType (50.0)); - expectEquals (block.getSample (1, 4), SampleType (110.0)); + expect (block.getSample (0, 4) == SampleType (50.0)); + expect (block.getSample (1, 4) == SampleType (110.0)); block *= otherBlock; - expectEquals (block.getSample (0, 4), SampleType (-250.0)); - expectEquals (block.getSample (1, 4), SampleType (-1210.0)); + expect (block.getSample (0, 4) == SampleType (-250.0)); + expect (block.getSample (1, 4) == SampleType (-1210.0)); } beginTest ("Process"); { resetBlocks(); 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)); + expect (otherBlock.getSample (0, 4) == SampleType (6.0)); + expect (otherBlock.getSample (1, 4) == SampleType (12.0)); } beginTest ("Copying"); diff --git a/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp b/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp index 7b2bcbc5..a8881fcb 100644 --- a/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp +++ b/JuceLibraryCode/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp @@ -117,12 +117,19 @@ class SIMDRegisterUnitTests : public UnitTest template static bool allValuesEqualTo (const SIMDRegister& vec, const type scalar) { - alignas (sizeof (SIMDRegister)) type elements[SIMDRegister::SIMDNumElements]; + #ifdef _MSC_VER + __declspec(align(sizeof (SIMDRegister))) type elements[SIMDRegister::SIMDNumElements]; + #else + type elements[SIMDRegister::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister)))); + #endif vec.copyToRawArray (elements); // as we do not want to rely on the access operator we cast this to a primitive pointer - return std::all_of (std::begin (elements), std::end (elements), [scalar] (const auto x) { return exactlyEqual (x, scalar); }); + for (size_t i = 0; i < SIMDRegister::SIMDNumElements; ++i) + if (elements[i] != scalar) return false; + + return true; } template @@ -300,7 +307,7 @@ class SIMDRegisterUnitTests : public UnitTest const SIMDRegister& b = a; for (size_t i = 0; i < SIMDRegister::SIMDNumElements; ++i) - u.expect (exactlyEqual (b[i], array[i])); + u.expect (b[i] == array[i]); } }; @@ -532,8 +539,8 @@ class SIMDRegisterUnitTests : public UnitTest // do check for (size_t j = 0; j < SIMDRegister::SIMDNumElements; ++j) { - 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_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_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; diff --git a/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.cpp b/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.cpp index 5bd99b79..efea14d2 100644 --- a/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.cpp +++ b/JuceLibraryCode/modules/juce_dsp/filter_design/juce_FilterDesign.cpp @@ -145,11 +145,6 @@ 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 @@ -158,6 +153,9 @@ 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; @@ -193,6 +191,9 @@ 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; diff --git a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.cpp b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.cpp index ef44a2ce..dbcaa64c 100644 --- a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.cpp +++ b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution.cpp @@ -647,7 +647,7 @@ static AudioBuffer resampleImpulseResponse (const AudioBuffer& buf const double srcSampleRate, const double destSampleRate) { - if (approximatelyEqual (srcSampleRate, destSampleRate)) + if (srcSampleRate == destSampleRate) return buf; const auto factorReading = srcSampleRate / destSampleRate; diff --git a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution_test.cpp b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution_test.cpp index e705f9d1..ab676f44 100644 --- a/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution_test.cpp +++ b/JuceLibraryCode/modules/juce_dsp/frequency/juce_Convolution_test.cpp @@ -97,7 +97,7 @@ class ConvolutionTest : public UnitTest expect (std::any_of (channel, channel + block.getNumSamples(), [] (float sample) { - return ! approximatelyEqual (sample, 0.0f); + return sample != 0.0f; })); } } @@ -193,7 +193,7 @@ class ConvolutionTest : public UnitTest processBlocksWithDiracImpulse(); // Check if the impulse response was loaded - if (! approximatelyEqual (block.getSample (0, 1), 0.0f)) + if (block.getSample (0, 1) != 0.0f) break; } } @@ -470,7 +470,7 @@ class ConvolutionTest : public UnitTest Convolution::Stereo::no, Convolution::Trim::yes, Convolution::Normalise::no, - AudioBlock (channels, numElementsInArray (channels), (size_t) length)); + AudioBlock (channels, numElementsInArray (channels), length)); } beginTest ("IRs with extra silence are trimmed appropriately"); diff --git a/JuceLibraryCode/modules/juce_dsp/juce_dsp.cpp b/JuceLibraryCode/modules/juce_dsp/juce_dsp.cpp index f53eccdb..e939a6a8 100644 --- a/JuceLibraryCode/modules/juce_dsp/juce_dsp.cpp +++ b/JuceLibraryCode/modules/juce_dsp/juce_dsp.cpp @@ -81,12 +81,12 @@ #if JUCE_USE_SIMD #if JUCE_INTEL #ifdef __AVX2__ - #include "native/juce_SIMDNativeOps_avx.cpp" + #include "native/juce_avx_SIMDNativeOps.cpp" #else - #include "native/juce_SIMDNativeOps_sse.cpp" + #include "native/juce_sse_SIMDNativeOps.cpp" #endif #elif JUCE_ARM - #include "native/juce_SIMDNativeOps_neon.cpp" + #include "native/juce_neon_SIMDNativeOps.cpp" #else #error "SIMD register support not implemented for this platform" #endif diff --git a/JuceLibraryCode/modules/juce_dsp/juce_dsp.h b/JuceLibraryCode/modules/juce_dsp/juce_dsp.h index cc6e746e..a64e65ad 100644 --- a/JuceLibraryCode/modules/juce_dsp/juce_dsp.h +++ b/JuceLibraryCode/modules/juce_dsp/juce_dsp.h @@ -35,7 +35,7 @@ ID: juce_dsp vendor: juce - version: 7.0.7 + version: 7.0.5 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 @@ -220,17 +220,17 @@ namespace juce //============================================================================== #if JUCE_USE_SIMD - #include "native/juce_SIMDNativeOps_fallback.h" + #include "native/juce_fallback_SIMDNativeOps.h" // include the correct native file for this build target CPU #if defined(__i386__) || defined(__amd64__) || defined(_M_X64) || defined(_X86_) || defined(_M_IX86) #ifdef __AVX2__ - #include "native/juce_SIMDNativeOps_avx.h" + #include "native/juce_avx_SIMDNativeOps.h" #else - #include "native/juce_SIMDNativeOps_sse.h" + #include "native/juce_sse_SIMDNativeOps.h" #endif #elif JUCE_ARM - #include "native/juce_SIMDNativeOps_neon.h" + #include "native/juce_neon_SIMDNativeOps.h" #else #error "SIMD register support not implemented for this platform" #endif diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue.h b/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue.h index 821e07e2..998e2494 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue.h +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_LogRampedValue.h @@ -108,7 +108,7 @@ class LogRampedValue : public SmoothedValueBase > */ void setTargetValue (FloatType newValue) noexcept { - if (approximatelyEqual (newValue, this->target)) + if (newValue == this->target) return; if (stepsToTarget <= 0) diff --git a/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.cpp b/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.cpp index 6d5c8acd..56c460cd 100644 --- a/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.cpp +++ b/JuceLibraryCode/modules/juce_dsp/maths/juce_Matrix.cpp @@ -176,7 +176,7 @@ bool Matrix::solve (Matrix& b) const noexcept { auto denominator = A (0,0); - if (approximatelyEqual (denominator, (ElementType) 0)) + if (denominator == 0) return false; b (0, 0) /= denominator; @@ -187,7 +187,7 @@ bool Matrix::solve (Matrix& b) const noexcept { auto denominator = A (0, 0) * A (1, 1) - A (0, 1) * A (1, 0); - if (approximatelyEqual (denominator, (ElementType) 0)) + if (denominator == 0) return false; auto factor = (1 / denominator); @@ -204,7 +204,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 (approximatelyEqual (denominator, (ElementType) 0)) + if (denominator == 0) return false; auto factor = 1 / denominator; @@ -231,10 +231,10 @@ bool Matrix::solve (Matrix& b) const noexcept for (size_t j = 0; j < n; ++j) { - if (approximatelyEqual (M (j, j), (ElementType) 0)) + if (M (j, j) == 0) { auto i = j; - while (i < n && approximatelyEqual (M (i, j), (ElementType) 0)) + while (i < n && M (i, j) == 0) ++i; if (i == n) diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_avx.cpp b/JuceLibraryCode/modules/juce_dsp/native/juce_avx_SIMDNativeOps.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_avx.cpp rename to JuceLibraryCode/modules/juce_dsp/native/juce_avx_SIMDNativeOps.cpp diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_avx.h b/JuceLibraryCode/modules/juce_dsp/native/juce_avx_SIMDNativeOps.h similarity index 100% rename from JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_avx.h rename to JuceLibraryCode/modules/juce_dsp/native/juce_avx_SIMDNativeOps.h diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_fallback.h b/JuceLibraryCode/modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h similarity index 98% rename from JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_fallback.h rename to JuceLibraryCode/modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h index 6b02f867..956eb7de 100644 --- a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_fallback.h +++ b/JuceLibraryCode/modules/juce_dsp/native/juce_fallback_SIMDNativeOps.h @@ -147,7 +147,7 @@ struct SIMDFallbackOps UnionType a {av}, b {bv}; for (size_t i = 0; i < n; ++i) - if (! exactlyEqual (a.s[i], b.s[i])) + if (a.s[i] != b.s[i]) return false; return true; @@ -181,8 +181,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 exactlyEqual (a, b); } }; - struct ScalarNeq { static forcedinline bool op (ScalarType a, ScalarType b) noexcept { return ! exactlyEqual (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 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); } }; diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_neon.cpp b/JuceLibraryCode/modules/juce_dsp/native/juce_neon_SIMDNativeOps.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_neon.cpp rename to JuceLibraryCode/modules/juce_dsp/native/juce_neon_SIMDNativeOps.cpp diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_neon.h b/JuceLibraryCode/modules/juce_dsp/native/juce_neon_SIMDNativeOps.h similarity index 100% rename from JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_neon.h rename to JuceLibraryCode/modules/juce_dsp/native/juce_neon_SIMDNativeOps.h diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_sse.cpp b/JuceLibraryCode/modules/juce_dsp/native/juce_sse_SIMDNativeOps.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_sse.cpp rename to JuceLibraryCode/modules/juce_dsp/native/juce_sse_SIMDNativeOps.cpp diff --git a/JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_sse.h b/JuceLibraryCode/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h similarity index 100% rename from JuceLibraryCode/modules/juce_dsp/native/juce_SIMDNativeOps_sse.h rename to JuceLibraryCode/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.cpp index a3f020ae..3ea3edbc 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.cpp +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.cpp @@ -86,7 +86,7 @@ template void DelayLine::setMaximumDelayInSamples (int maxDelayInSamples) { jassert (maxDelayInSamples >= 0); - totalSize = jmax (4, maxDelayInSamples + 2); + totalSize = jmax (4, maxDelayInSamples + 1); bufferData.setSize ((int) bufferData.getNumChannels(), totalSize, false, false, true); reset(); } diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.h index 29c53d28..0d922376 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_DelayLine.h @@ -125,7 +125,7 @@ class DelayLine 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; } + int getMaximumDelayInSamples() const noexcept { return totalSize - 1; } /** Resets the internal state variables of the processor. */ void reset(); @@ -271,7 +271,7 @@ class DelayLine 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]); + auto output = delayFrac == 0 ? value1 : value2 + alpha * (value1 - v[(size_t) channel]); v[(size_t) channel] = output; return output; @@ -283,7 +283,7 @@ class DelayLine { if constexpr (std::is_same_v) { - if (delayFrac < (SampleType) 2.0 && delayInt >= 1) + if (delayInt >= 1) { delayFrac++; delayInt--; diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.cpp index 37bed1e8..69f99417 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.cpp +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter.cpp @@ -30,8 +30,6 @@ namespace dsp namespace IIR { -constexpr auto minimumDecibels = -300.0; - template std::array ArrayCoefficients::makeFirstOrderLowPass (double sampleRate, NumericType frequency) @@ -39,7 +37,7 @@ std::array ArrayCoefficients::makeFirstOrderLowPass jassert (sampleRate > 0.0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); - const auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); return { { n, n, n + 1, n - 1 } }; } @@ -51,7 +49,7 @@ std::array ArrayCoefficients::makeFirstOrderHighPas jassert (sampleRate > 0.0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); - const auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); return { { 1, -1, n + 1, n - 1 } }; } @@ -63,7 +61,7 @@ std::array ArrayCoefficients::makeFirstOrderAllPass jassert (sampleRate > 0.0); jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); - const auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); + auto n = std::tan (MathConstants::pi * frequency / static_cast (sampleRate)); return { { n - 1, n + 1, n + 1, n - 1 } }; } @@ -84,10 +82,10 @@ std::array ArrayCoefficients::makeLowPass (double s jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0.0); - 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); + 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); return { { c1, c1 * 2, c1, 1, c1 * 2 * (1 - nSquared), @@ -110,10 +108,10 @@ std::array ArrayCoefficients::makeHighPass (double jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0.0); - 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); + 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); return { { c1, c1 * -2, c1, 1, c1 * 2 * (nSquared - 1), @@ -136,10 +134,10 @@ std::array ArrayCoefficients::makeBandPass (double jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0.0); - 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); + 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); return { { c1 * n * invQ, 0, -c1 * n * invQ, 1, @@ -163,12 +161,12 @@ std::array ArrayCoefficients::makeNotch (double sam jassert (frequency > 0 && frequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0.0); - 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); + 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); return { { b0, b1, b0, 1, b1, c1 * (1 - n * invQ + nSquared) } }; } @@ -189,12 +187,12 @@ std::array ArrayCoefficients::makeAllPass (double s jassert (frequency > 0 && frequency <= sampleRate * 0.5); jassert (Q > 0); - 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); + 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); return { { b0, b1, 1, 1, b1, b0 } }; } @@ -209,13 +207,13 @@ std::array ArrayCoefficients::makeLowShelf (double jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); jassert (Q > 0.0); - 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; + 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 { { A * (aplus1 - aminus1TimesCoso + beta), A * 2 * (aminus1 - aplus1 * coso), @@ -235,13 +233,13 @@ std::array ArrayCoefficients::makeHighShelf (double jassert (cutOffFrequency > 0 && cutOffFrequency <= static_cast (sampleRate * 0.5)); jassert (Q > 0); - 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; + 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 { { A * (aplus1 + aminus1TimesCoso + beta), A * -2 * (aminus1 + aplus1 * coso), @@ -262,12 +260,12 @@ std::array ArrayCoefficients::makePeakFilter (doubl jassert (Q > 0); jassert (gainFactor > 0); - 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; + 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; return { { 1 + alphaTimesA, c2, 1 - alphaTimesA, 1 + alphaOverA, c2, 1 - alphaOverA } }; } diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter_Impl.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter_Impl.h index b30caf7c..150ab429 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter_Impl.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_IIRFilter_Impl.h @@ -39,9 +39,8 @@ Coefficients& Coefficients::assignImpl (const NumericT 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(); + const auto a0Inv = a0 != NumericType() ? static_cast (1) / values[a0Index] + : NumericType(); coefficients.clearQuick(); coefficients.ensureStorageAllocated ((int) jmax ((size_t) 8, Num)); diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.cpp index 8600b18e..f79c4328 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.cpp +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.cpp @@ -755,7 +755,7 @@ void Oversampling::updateDelayLine() auto latency = getUncompensatedLatency(); fractionalDelay = static_cast (1.0) - (latency - std::floor (latency)); - if (approximatelyEqual (fractionalDelay, static_cast (1.0))) + if (fractionalDelay == static_cast (1.0)) fractionalDelay = static_cast (0.0); else if (fractionalDelay < static_cast (0.618)) fractionalDelay += static_cast (1.0); diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.h index 9d5990ae..95ffd987 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_Oversampling.h @@ -28,7 +28,7 @@ namespace juce namespace dsp { -//============================================================================== +//=============================================================================== /** A processor that performs multi-channel oversampling. @@ -63,7 +63,7 @@ class JUCE_API Oversampling numFilterTypes }; - //============================================================================== + //=============================================================================== /** The default constructor. Note: This creates a "dummy" oversampling stage, which needs to be removed @@ -97,7 +97,7 @@ class JUCE_API Oversampling /** Destructor. */ ~Oversampling(); - //============================================================================== + //=============================================================================== /* 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. */ @@ -118,7 +118,7 @@ class JUCE_API Oversampling /** Returns the current oversampling factor. */ size_t getOversamplingFactor() const noexcept; - //============================================================================== + //=============================================================================== /** Must be called before any processing, to set the buffer sizes of the internal buffers of the oversampling processing. */ @@ -143,7 +143,7 @@ class JUCE_API Oversampling */ void processSamplesDown (AudioBlock& outputBlock) noexcept; - //============================================================================== + //=============================================================================== /** Adds a new oversampling stage to the Oversampling class, multiplying the current oversampling factor by two. This is used with the default constructor to create custom oversampling chains, requiring a call to the @@ -187,7 +187,7 @@ class JUCE_API Oversampling */ void clearOversamplingStages(); - //============================================================================== + //=============================================================================== size_t factorOversampling = 1; size_t numChannels = 1; @@ -196,17 +196,17 @@ class JUCE_API Oversampling #endif private: - //============================================================================== + //=============================================================================== void updateDelayLine(); SampleType getUncompensatedLatency() const noexcept; - //============================================================================== + //=============================================================================== OwnedArray stages; bool isReady = false, shouldUseIntegerLatency = false; DelayLine delay { 8 }; SampleType fractionalDelay = 0; - //============================================================================== + //=============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Oversampling) }; diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessContext.h b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessContext.h index 6da17bb7..983dd536 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessContext.h +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessContext.h @@ -48,12 +48,9 @@ struct ProcessSpec 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); + return a.sampleRate == b.sampleRate + && a.maximumBlockSize == b.maximumBlockSize + && a.numChannels == b.numChannels; } constexpr bool operator!= (const ProcessSpec& a, const ProcessSpec& b) { return ! (a == b); } diff --git a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp index 80ff01fe..c8205810 100644 --- a/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp +++ b/JuceLibraryCode/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp @@ -39,7 +39,7 @@ class ProcessorChainTest : public UnitTest template void process (const Context& context) noexcept { - bufferWasClear = approximatelyEqual (context.getInputBlock().getSample (0, 0), 0.0f); + bufferWasClear = context.getInputBlock().getSample (0, 0) == 0; if (! context.isBypassed) context.getOutputBlock().add (AddValue); diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Bias.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Bias.h index cbd68b86..cc255dad 100644 --- a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Bias.h +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Bias.h @@ -64,7 +64,7 @@ class Bias /** Sets the length of the ramp used for smoothing gain changes. */ void setRampDurationSeconds (double newDurationSeconds) noexcept { - if (! approximatelyEqual (rampDurationSeconds, newDurationSeconds)) + if (rampDurationSeconds != newDurationSeconds) { rampDurationSeconds = newDurationSeconds; updateRamp(); diff --git a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Gain.h b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Gain.h index 1e98dc69..c43c240f 100644 --- a/JuceLibraryCode/modules/juce_dsp/widgets/juce_Gain.h +++ b/JuceLibraryCode/modules/juce_dsp/widgets/juce_Gain.h @@ -55,7 +55,7 @@ class Gain /** Sets the length of the ramp used for smoothing gain changes. */ void setRampDurationSeconds (double newDurationSeconds) noexcept { - if (! approximatelyEqual (rampDurationSeconds, newDurationSeconds)) + if (rampDurationSeconds != newDurationSeconds) { rampDurationSeconds = newDurationSeconds; reset(); diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.cpp b/JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.cpp deleted file mode 100644 index 87c440b6..00000000 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - ============================================================================== - - 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 deleted file mode 100644 index 49cb91d1..00000000 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_LockingAsyncUpdater.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - ============================================================================== - - 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/juce_events.cpp b/JuceLibraryCode/modules/juce_events/juce_events.cpp index 67d555a9..c6678d5d 100644 --- a/JuceLibraryCode/modules/juce_events/juce_events.cpp +++ b/JuceLibraryCode/modules/juce_events/juce_events.cpp @@ -60,7 +60,6 @@ #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" @@ -73,26 +72,25 @@ //============================================================================== #if JUCE_MAC || JUCE_IOS - #include "native/juce_MessageQueue_mac.h" + #include "native/juce_osx_MessageQueue.h" #if JUCE_MAC - #include "native/juce_MessageManager_mac.mm" + #include "native/juce_mac_MessageManager.mm" #else - #include "native/juce_MessageManager_ios.mm" + #include "native/juce_ios_MessageManager.mm" #endif #elif JUCE_WINDOWS - #include "native/juce_RunningInUnity.h" - #include "native/juce_Messaging_windows.cpp" + #include "native/juce_win32_Messaging.cpp" #if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER - #include "native/juce_WinRTWrapper_windows.cpp" + #include "native/juce_win32_WinRTWrapper.cpp" #endif #elif JUCE_LINUX || JUCE_BSD - #include "native/juce_EventLoopInternal_linux.h" - #include "native/juce_Messaging_linux.cpp" + #include "native/juce_linux_EventLoopInternal.h" + #include "native/juce_linux_Messaging.cpp" #elif JUCE_ANDROID - #include "native/juce_Messaging_android.cpp" + #include "native/juce_android_Messaging.cpp" #endif diff --git a/JuceLibraryCode/modules/juce_events/juce_events.h b/JuceLibraryCode/modules/juce_events/juce_events.h index e11f371b..b3d60e38 100644 --- a/JuceLibraryCode/modules/juce_events/juce_events.h +++ b/JuceLibraryCode/modules/juce_events/juce_events.h @@ -32,7 +32,7 @@ ID: juce_events vendor: juce - version: 7.0.7 + version: 7.0.5 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 @@ -82,7 +82,6 @@ #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" @@ -94,14 +93,14 @@ #include "native/juce_ScopedLowPowerModeDisabler.h" #if JUCE_LINUX || JUCE_BSD - #include "native/juce_EventLoop_linux.h" + #include "native/juce_linux_EventLoop.h" #endif #if JUCE_WINDOWS #if JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW - #include "native/juce_HiddenMessageWindow_windows.h" + #include "native/juce_win32_HiddenMessageWindow.h" #endif #if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER - #include "native/juce_WinRTWrapper_windows.h" + #include "native/juce_win32_WinRTWrapper.h" #endif #endif diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp index 7b0eb872..04f534be 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp @@ -184,7 +184,7 @@ StringArray JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameterArray() #endif #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); + extern int juce_gtkWebkitMain (int argc, const char* argv[]); #endif #if JUCE_WINDOWS @@ -231,7 +231,7 @@ int JUCEApplicationBase::main (int argc, const char* argv[]) initialiseNSApplication(); #endif - #if (JUCE_LINUX || JUCE_BSD) && 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 diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp index da949d2e..cca2d492 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp @@ -81,11 +81,8 @@ bool MessageManager::MessageBase::post() //============================================================================== #if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID) -// implemented in platform-specific code (juce_Messaging_linux.cpp and juce_Messaging_windows.cpp) -namespace detail -{ +// implemented in platform-specific code (juce_linux_Messaging.cpp and juce_win32_Messaging.cpp) bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); -} // namespace detail class MessageManager::QuitMessage : public MessageManager::MessageBase { @@ -109,7 +106,7 @@ void MessageManager::runDispatchLoop() { JUCE_TRY { - if (! detail::dispatchNextMessageOnSystemQueue (false)) + if (! dispatchNextMessageOnSystemQueue (false)) Thread::sleep (1); } JUCE_CATCH_EXCEPTION @@ -133,7 +130,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { JUCE_TRY { - if (! detail::dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0)) + if (! dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0)) Thread::sleep (1); } JUCE_CATCH_EXCEPTION @@ -284,41 +281,25 @@ bool MessageManager::existsAndIsCurrentThread() noexcept */ struct MessageManager::Lock::BlockingMessage : public MessageManager::MessageBase { - explicit BlockingMessage (const MessageManager::Lock* parent) noexcept - : owner (parent) {} + BlockingMessage (const MessageManager::Lock* parent) noexcept + : owner (parent) + {} void messageCallback() override { - std::unique_lock lock { mutex }; - - if (owner != nullptr) { - owner->abort(); - acquired = true; - } + ScopedLock lock (ownerCriticalSection); - condvar.wait (lock, [&] { return owner == nullptr; }); - } - - void stopWaiting() - { - const ScopeGuard scope { [&] { condvar.notify_one(); } }; - const std::scoped_lock lock { mutex }; - owner = nullptr; - } + if (auto* o = owner.get()) + o->messageCallback(); + } - bool wasAcquired() - { - const std::scoped_lock lock { mutex }; - return acquired; + releaseEvent.wait(); } -private: - std::mutex mutex; - std::condition_variable condvar; - - const MessageManager::Lock* owner = nullptr; - bool acquired = false; + CriticalSection ownerCriticalSection; + Atomic owner; + WaitableEvent releaseEvent; JUCE_DECLARE_NON_COPYABLE (BlockingMessage) }; @@ -339,12 +320,9 @@ bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept return false; } - if (! lockIsMandatory && [&] - { - const std::scoped_lock lock { mutex }; - return std::exchange (abortWait, false); - }()) + if (! lockIsMandatory && (abortWait.get() != 0)) { + abortWait.set (0); return false; } @@ -369,54 +347,65 @@ bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept return false; } - for (;;) + do { - { - std::unique_lock lock { mutex }; - condvar.wait (lock, [&] { return std::exchange (abortWait, false); }); - } + while (abortWait.get() == 0) + lockedEvent.wait (-1); + + abortWait.set (0); - if (blockingMessage->wasAcquired()) + if (lockGained.get() != 0) { mm->threadWithLock = Thread::getCurrentThreadId(); return true; } - if (! lockIsMandatory) - break; - } + } while (lockIsMandatory); // 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 (blockingMessage == nullptr) - return; - - const ScopeGuard scope { [&] { blockingMessage = nullptr; } }; + if (lockGained.compareAndSetBool (false, true)) + { + auto* mm = MessageManager::instance; - blockingMessage->stopWaiting(); + jassert (mm == nullptr || mm->currentThreadHasLockedMessageManager()); + lockGained.set (0); - if (! blockingMessage->wasAcquired()) - return; + if (mm != nullptr) + mm->threadWithLock = {}; - if (auto* mm = MessageManager::instance) - { - jassert (mm->currentThreadHasLockedMessageManager()); - mm->threadWithLock = {}; + if (blockingMessage != nullptr) + { + blockingMessage->releaseEvent.signal(); + blockingMessage = nullptr; + } } } +void MessageManager::Lock::messageCallback() const +{ + lockGained.set (1); + abort(); +} + void MessageManager::Lock::abort() const noexcept { - const ScopeGuard scope { [&] { condvar.notify_one(); } }; - const std::scoped_lock lock { mutex }; - abortWait = true; + abortWait.set (1); + lockedEvent.signal(); } //============================================================================== diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h index 01daff8e..8687a057 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h @@ -298,12 +298,12 @@ class JUCE_API MessageManager final friend class ReferenceCountedObjectPtr; bool tryAcquire (bool) const noexcept; + void messageCallback() const; //============================================================================== mutable ReferenceCountedObjectPtr blockingMessage; - mutable std::mutex mutex; - mutable std::condition_variable condvar; - mutable bool abortWait = false, acquired = false; + WaitableEvent lockedEvent; + mutable Atomic abortWait, lockGained; }; //============================================================================== diff --git a/JuceLibraryCode/modules/juce_events/native/juce_RunningInUnity.h b/JuceLibraryCode/modules/juce_events/native/juce_RunningInUnity.h deleted file mode 100644 index 41fc2cb0..00000000 --- a/JuceLibraryCode/modules/juce_events/native/juce_RunningInUnity.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - ============================================================================== - - 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_Messaging_android.cpp b/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_events/native/juce_Messaging_android.cpp rename to JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp diff --git a/JuceLibraryCode/modules/juce_events/native/juce_MessageManager_ios.mm b/JuceLibraryCode/modules/juce_events/native/juce_ios_MessageManager.mm similarity index 100% rename from JuceLibraryCode/modules/juce_events/native/juce_MessageManager_ios.mm rename to JuceLibraryCode/modules/juce_events/native/juce_ios_MessageManager.mm diff --git a/JuceLibraryCode/modules/juce_events/native/juce_EventLoop_linux.h b/JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoop.h similarity index 100% rename from JuceLibraryCode/modules/juce_events/native/juce_EventLoop_linux.h rename to JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoop.h diff --git a/JuceLibraryCode/modules/juce_events/native/juce_EventLoopInternal_linux.h b/JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoopInternal.h similarity index 100% rename from JuceLibraryCode/modules/juce_events/native/juce_EventLoopInternal_linux.h rename to JuceLibraryCode/modules/juce_events/native/juce_linux_EventLoopInternal.h diff --git a/JuceLibraryCode/modules/juce_events/native/juce_Messaging_linux.cpp b/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp similarity index 99% rename from JuceLibraryCode/modules/juce_events/native/juce_Messaging_linux.cpp rename to JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp index 66575775..40e4ebc0 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_Messaging_linux.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp @@ -327,8 +327,6 @@ 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) { @@ -351,7 +349,6 @@ bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) return true; } -} // namespace detail //============================================================================== void LinuxEventLoop::registerFdCallback (int fd, std::function readCallback, short eventMask) diff --git a/JuceLibraryCode/modules/juce_events/native/juce_MessageManager_mac.mm b/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm similarity index 58% rename from JuceLibraryCode/modules/juce_events/native/juce_MessageManager_mac.mm rename to JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm index 6f9599b4..e26151e4 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_MessageManager_mac.mm +++ b/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm @@ -35,188 +35,151 @@ //============================================================================== struct AppDelegateClass : public ObjCClass { - AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") + AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") { - 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]; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - }); - - addMethod (@selector (applicationShouldTerminate:), [] (id /*self*/, SEL, NSApplication*) - { - if (auto* app = JUCEApplicationBase::getInstance()) - { - app->systemRequestedQuit(); - - if (! MessageManager::getInstance()->hasStopMessageBeenSent()) - return NSTerminateCancel; - } - - return NSTerminateNow; - }); - - addMethod (@selector (applicationWillTerminate:), [] (id /*self*/, SEL, NSNotification*) - { - JUCEApplicationBase::appWillTerminateByForce(); - }); - - addMethod (@selector (application:openFile:), [] (id /*self*/, SEL, NSApplication*, NSString* filename) - { - if (auto* app = JUCEApplicationBase::getInstance()) - { - app->anotherInstanceStarted (quotedIfContainsSpaces (filename)); - return YES; - } - - return NO; - }); + addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching); + addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate); + addMethod (@selector (applicationWillTerminate:), applicationWillTerminate); + addMethod (@selector (application:openFile:), application_openFile); + addMethod (@selector (application:openFiles:), application_openFiles); + addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive); + addMethod (@selector (applicationDidResignActive:), applicationDidResignActive); + addMethod (@selector (applicationWillUnhide:), applicationWillUnhide); - addMethod (@selector (application:openFiles:), [] (id /*self*/, SEL, NSApplication*, NSArray* filenames) - { - if (auto* app = JUCEApplicationBase::getInstance()) - { - StringArray files; - - for (NSString* f in filenames) - files.add (quotedIfContainsSpaces (f)); + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent); + addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback); + addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan); + addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded); + addMethod (@selector (dummyMethod), dummyMethod); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE - if (files.size() > 0) - app->anotherInstanceStarted (files.joinIntoString (" ")); - } - }); + #if JUCE_PUSH_NOTIFICATIONS + //============================================================================== + addIvar*> ("pushNotificationsDelegate"); - addMethod (@selector (applicationDidBecomeActive:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); }); - addMethod (@selector (applicationDidResignActive:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); }); - addMethod (@selector (applicationWillUnhide:), [] (id /*self*/, SEL, NSNotification*) { focusChanged(); }); + addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching); 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); - }); + addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE - addMethod (@selector (mainMenuTrackingBegan:), [] (id /*self*/, SEL, NSNotification*) - { - if (menuTrackingChangedCallback != nullptr) - menuTrackingChangedCallback (true); - }); + addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications); + addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications); + addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification); + #endif - addMethod (@selector (mainMenuTrackingEnded:), [] (id /*self*/, SEL, NSNotification*) - { - if (menuTrackingChangedCallback != nullptr) - menuTrackingChangedCallback (false); - }); + registerClass(); + } - // (used as a way of running a dummy thread) - addMethod (@selector (dummyMethod), [] (id /*self*/, SEL) {}); +private: + static void 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]; JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } - #if JUCE_PUSH_NOTIFICATIONS - //============================================================================== - addIvar*> ("pushNotificationsDelegate"); - - addMethod (@selector (applicationDidFinishLaunching:), [] (id self, SEL, NSNotification* notification) + #if JUCE_PUSH_NOTIFICATIONS + static void applicationDidFinishLaunching (id self, SEL, NSNotification* notification) + { + if (notification.userInfo != nil) { - 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 - - 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 - } - }); + 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 - 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 + if (userNotification != nil && userNotification.userInfo != nil) + didReceiveRemoteNotification (self, nil, [NSApplication sharedApplication], userNotification.userInfo); + } + } + #endif - addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), [] (id self, SEL, NSApplication* application, NSData* deviceToken) + static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*) + { + if (auto* app = JUCEApplicationBase::getInstance()) { - auto* delegate = getPushNotificationsDelegate (self); + app->systemRequestedQuit(); - SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:); + if (! MessageManager::getInstance()->hasStopMessageBeenSent()) + return NSTerminateCancel; + } - if (delegate != nil && [delegate respondsToSelector: selector]) - { - NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; - [invocation setSelector: selector]; - [invocation setTarget: delegate]; - [invocation setArgument: &application atIndex:2]; - [invocation setArgument: &deviceToken atIndex:3]; + return NSTerminateNow; + } - [invocation invoke]; - } - }); + static void applicationWillTerminate (id /*self*/, SEL, NSNotification*) + { + JUCEApplicationBase::appWillTerminateByForce(); + } - addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), [] (id self, SEL, NSApplication* application, NSError* error) + static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename) + { + if (auto* app = JUCEApplicationBase::getInstance()) { - auto* delegate = getPushNotificationsDelegate (self); + app->anotherInstanceStarted (quotedIfContainsSpaces (filename)); + return YES; + } - SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:); + return NO; + } - if (delegate != nil && [delegate respondsToSelector: selector]) - { - NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; - [invocation setSelector: selector]; - [invocation setTarget: delegate]; - [invocation setArgument: &application atIndex:2]; - [invocation setArgument: &error atIndex:3]; + static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames) + { + if (auto* app = JUCEApplicationBase::getInstance()) + { + StringArray files; - [invocation invoke]; - } - }); + for (NSString* f in filenames) + files.add (quotedIfContainsSpaces (f)); - addMethod (@selector (application:didReceiveRemoteNotification:), [] (id self, SEL, NSApplication* application, NSDictionary* userInfo) - { - auto* delegate = getPushNotificationsDelegate (self); + if (files.size() > 0) + app->anotherInstanceStarted (files.joinIntoString (" ")); + } + } - SEL selector = @selector (application:didReceiveRemoteNotification:); + static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } + static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } + static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); } - if (delegate != nil && [delegate respondsToSelector: selector]) - { - NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; - [invocation setSelector: selector]; - [invocation setTarget: delegate]; - [invocation setArgument: &application atIndex:2]; - [invocation setArgument: &userInfo atIndex:3]; + static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n) + { + NSDictionary* dict = (NSDictionary*) [n userInfo]; + auto messageString = nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]); + MessageManager::getInstance()->deliverBroadcastMessage (messageString); + } - [invocation invoke]; - } - }); - #endif + static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*) + { + if (menuTrackingChangedCallback != nullptr) + menuTrackingChangedCallback (true); + } - registerClass(); + static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*) + { + if (menuTrackingChangedCallback != nullptr) + menuTrackingChangedCallback (false); } -private: + static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread) + static void focusChanged() { if (appFocusChangeCallback != nullptr) (*appFocusChangeCallback)(); } + static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*) + { + if (auto* app = JUCEApplicationBase::getInstance()) + app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue])); + } + static String quotedIfContainsSpaces (NSString* file) { String s (nsStringToJuce (file)); @@ -228,12 +191,71 @@ static String quotedIfContainsSpaces (NSString* file) return s; } - //============================================================================== #if JUCE_PUSH_NOTIFICATIONS + //============================================================================== + static void setPushNotificationsDelegate (id self, SEL, NSObject* delegate) + { + object_setInstanceVariable (self, "pushNotificationsDelegate", delegate); + } + static NSObject* getPushNotificationsDelegate (id self) { return getIvar*> (self, "pushNotificationsDelegate"); } + + static void registeredForRemoteNotifications (id self, SEL, NSApplication* application, NSData* deviceToken) + { + auto* delegate = getPushNotificationsDelegate (self); + + SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:); + + if (delegate != nil && [delegate respondsToSelector: selector]) + { + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; + [invocation setSelector: selector]; + [invocation setTarget: delegate]; + [invocation setArgument: &application atIndex:2]; + [invocation setArgument: &deviceToken atIndex:3]; + + [invocation invoke]; + } + } + + static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* application, NSError* error) + { + auto* delegate = getPushNotificationsDelegate (self); + + SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:); + + if (delegate != nil && [delegate respondsToSelector: selector]) + { + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; + [invocation setSelector: selector]; + [invocation setTarget: delegate]; + [invocation setArgument: &application atIndex:2]; + [invocation setArgument: &error atIndex:3]; + + [invocation invoke]; + } + } + + static void didReceiveRemoteNotification (id self, SEL, NSApplication* application, NSDictionary* userInfo) + { + auto* delegate = getPushNotificationsDelegate (self); + + SEL selector = @selector (application:didReceiveRemoteNotification:); + + if (delegate != nil && [delegate respondsToSelector: selector]) + { + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; + [invocation setSelector: selector]; + [invocation setTarget: delegate]; + [invocation setArgument: &application atIndex:2]; + [invocation setArgument: &userInfo atIndex:3]; + + [invocation invoke]; + } + } #endif }; @@ -437,6 +459,27 @@ void initialiseNSApplication() userInfo: info]; } +// Special function used by some plugin classes to re-post carbon events +void repostCurrentNSEvent(); +void 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 diff --git a/JuceLibraryCode/modules/juce_events/native/juce_MessageQueue_mac.h b/JuceLibraryCode/modules/juce_events/native/juce_osx_MessageQueue.h similarity index 100% rename from JuceLibraryCode/modules/juce_events/native/juce_MessageQueue_mac.h rename to JuceLibraryCode/modules/juce_events/native/juce_osx_MessageQueue.h diff --git a/JuceLibraryCode/modules/juce_events/native/juce_HiddenMessageWindow_windows.h b/JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h similarity index 94% rename from JuceLibraryCode/modules/juce_events/native/juce_HiddenMessageWindow_windows.h rename to JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h index 95cdcf04..8dbad81a 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_HiddenMessageWindow_windows.h +++ b/JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h @@ -91,13 +91,14 @@ class JuceWindowIdentifier class DeviceChangeDetector : private Timer { public: - DeviceChangeDetector (const wchar_t* const name, std::function onChangeIn) - : messageWindow (name, (WNDPROC) deviceChangeEventCallback), - onChange (std::move (onChangeIn)) + DeviceChangeDetector (const wchar_t* const name) + : messageWindow (name, (WNDPROC) deviceChangeEventCallback) { SetWindowLongPtr (messageWindow.getHWND(), GWLP_USERDATA, (LONG_PTR) this); } + virtual void systemDeviceChanged() = 0; + void triggerAsyncDeviceChangeCallback() { // We'll pause before sending a message, because on device removal, the OS hasn't always updated @@ -107,7 +108,6 @@ 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) @@ -127,7 +127,7 @@ class DeviceChangeDetector : private Timer void timerCallback() override { stopTimer(); - NullCheckedInvocation::invoke (onChange); + systemDeviceChanged(); } }; diff --git a/JuceLibraryCode/modules/juce_events/native/juce_Messaging_windows.cpp b/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp similarity index 94% rename from JuceLibraryCode/modules/juce_events/native/juce_Messaging_windows.cpp rename to JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp index 4e3e53b9..c6a3d806 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_Messaging_windows.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp @@ -25,6 +25,10 @@ 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 @@ -90,11 +94,13 @@ class InternalMessageQueue if (! shouldTriggerMessageQueueDispatch) return; - if (detail::RunningInUnity::state) + #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client && JucePlugin_Build_Unity + if (juce_isRunningInUnity()) { SendNotifyMessage (juce_messageWindowHandle, customMessageID, 0, 0); return; } + #endif PostMessage (juce_messageWindowHandle, customMessageID, 0, 0); } @@ -254,9 +260,6 @@ JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue) const TCHAR InternalMessageQueue::messageWindowName[] = _T("JUCEWindow"); //============================================================================== -namespace detail -{ - bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) { if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) @@ -265,8 +268,6 @@ bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages) return false; } -} // namespace detail - bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) { if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) @@ -298,24 +299,25 @@ void MessageManager::doPlatformSpecificShutdown() } //============================================================================== -struct MountedVolumeListChangeDetector::Pimpl +struct MountedVolumeListChangeDetector::Pimpl : private DeviceChangeDetector { - explicit Pimpl (MountedVolumeListChangeDetector& d) - : owner (d) + Pimpl (MountedVolumeListChangeDetector& d) : DeviceChangeDetector (L"MountedVolumeList"), owner (d) { File::findFileSystemRoots (lastVolumeList); } - void systemDeviceChanged() + void systemDeviceChanged() override { Array newList; File::findFileSystemRoots (newList); - if (std::exchange (lastVolumeList, newList) != newList) + if (lastVolumeList != newList) + { + lastVolumeList = newList; owner.mountedVolumeListChanged(); + } } - DeviceChangeDetector detector { L"MountedVolumeList", [this] { systemDeviceChanged(); } }; MountedVolumeListChangeDetector& owner; Array lastVolumeList; }; diff --git a/JuceLibraryCode/modules/juce_events/native/juce_WinRTWrapper_windows.cpp b/JuceLibraryCode/modules/juce_events/native/juce_win32_WinRTWrapper.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_events/native/juce_WinRTWrapper_windows.cpp rename to JuceLibraryCode/modules/juce_events/native/juce_win32_WinRTWrapper.cpp diff --git a/JuceLibraryCode/modules/juce_events/native/juce_WinRTWrapper_windows.h b/JuceLibraryCode/modules/juce_events/native/juce_win32_WinRTWrapper.h similarity index 100% rename from JuceLibraryCode/modules/juce_events/native/juce_WinRTWrapper_windows.h rename to JuceLibraryCode/modules/juce_events/native/juce_win32_WinRTWrapper.h diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp index 47c343ec..38d2d8b0 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp @@ -44,7 +44,7 @@ namespace ColourHelpers float hue = 0.0f; - if (hi > 0 && ! exactlyEqual (hi, lo)) + if (hi > 0 && ! approximatelyEqual (hi, lo)) { auto invDiff = 1.0f / (float) (hi - lo); @@ -291,18 +291,13 @@ Colour::Colour (PixelAlpha alpha) noexcept } //============================================================================== -PixelARGB Colour::getPixelARGB() const noexcept +const PixelARGB Colour::getPixelARGB() const noexcept { PixelARGB p (argb); p.premultiply(); return p; } -PixelARGB Colour::getNonPremultipliedPixelARGB() const noexcept -{ - return argb; -} - uint32 Colour::getARGB() const noexcept { return argb.getInARGBMaskOrder(); diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.h b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.h index 2e1e4ee3..3077addd 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.h +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.h @@ -194,11 +194,7 @@ class JUCE_API Colour final /** Returns a premultiplied ARGB pixel object that represents this colour. */ - PixelARGB getPixelARGB() const noexcept; - - /** Returns an ARGB pixel object that represents this colour. - */ - PixelARGB getNonPremultipliedPixelARGB() const noexcept; + const PixelARGB getPixelARGB() const noexcept; /** Returns a 32-bit integer that represents this colour. diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.cpp b/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.cpp index c5bf1342..a9dc3af6 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.cpp +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.cpp @@ -30,7 +30,7 @@ ColourGradient::ColourGradient() noexcept : isRadial (false) { #if JUCE_DEBUG point1.setX (987654.0f); - #define JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED jassert (! exactlyEqual (point1.x, 987654.0f)); + #define JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED jassert (point1.x != 987654.0f); #else #define JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED #endif @@ -174,7 +174,7 @@ void ColourGradient::setColour (int index, Colour newColour) noexcept Colour ColourGradient::getColourAtPosition (double position) const noexcept { - jassert (approximatelyEqual (colours.getReference (0).position, 0.0)); // the first colour specified has to go at position 0 + jassert (colours.getReference(0).position == 0.0); // the first colour specified has to go at position 0 if (position <= 0 || colours.size() <= 1) return colours.getReference(0).colour; @@ -199,30 +199,31 @@ void ColourGradient::createLookupTable (PixelARGB* const lookupTable, const int JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED // Trying to use this object without setting its coordinates? jassert (colours.size() >= 2); jassert (numEntries > 0); - jassert (approximatelyEqual (colours.getReference(0).position, 0.0)); // The first colour specified has to go at position 0 + jassert (colours.getReference(0).position == 0.0); // The first colour specified has to go at position 0 + auto pix1 = colours.getReference (0).colour.getPixelARGB(); int index = 0; - for (int j = 0; j < colours.size() - 1; ++j) + for (int j = 1; j < colours.size(); ++j) { - const auto& o = colours.getReference (j + 0); - const auto& p = colours.getReference (j + 1); - const auto numToDo = roundToInt (p.position * (numEntries - 1)) - index; - const auto pix1 = o.colour.getNonPremultipliedPixelARGB(); - const auto pix2 = p.colour.getNonPremultipliedPixelARGB(); + auto& p = colours.getReference (j); + auto numToDo = roundToInt (p.position * (numEntries - 1)) - index; + auto pix2 = p.colour.getPixelARGB(); - for (auto i = 0; i < numToDo; ++i) + for (int i = 0; i < numToDo; ++i) { - auto blended = pix1; - blended.tween (pix2, (uint32) ((i << 8) / numToDo)); - blended.premultiply(); + jassert (index >= 0 && index < numEntries); - jassert (0 <= index && index < numEntries); - lookupTable[index++] = blended; + lookupTable[index] = pix1; + lookupTable[index].tween (pix2, (uint32) ((i << 8) / numToDo)); + ++index; } + + pix1 = pix2; } - std::fill (lookupTable + index, lookupTable + numEntries, colours.getLast().colour.getPixelARGB()); + while (index < numEntries) + lookupTable [index++] = pix1; } int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlock& lookupTable) const @@ -258,13 +259,12 @@ bool ColourGradient::isInvisible() const noexcept bool ColourGradient::ColourPoint::operator== (ColourPoint other) const noexcept { - const auto tie = [] (const ColourPoint& p) { return std::tie (p.position, p.colour); }; - return tie (*this) == tie (other); + return position == other.position && colour == other.colour; } bool ColourGradient::ColourPoint::operator!= (ColourPoint other) const noexcept { - return ! operator== (other); + return position != other.position || colour != other.colour; } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.h b/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.h index 6a15d95f..c38f5251 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.h +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.h @@ -185,17 +185,6 @@ class JUCE_API ColourGradient final */ void createLookupTable (PixelARGB* resultLookupTable, int numEntries) const noexcept; - /** Creates a set of interpolated premultiplied ARGB values. - This will fill an array of a user-specified size with the gradient, interpolating to fit. - When calling this, the ColourGradient must have at least 2 colour stops specified. - */ - template - void createLookupTable (PixelARGB (&resultLookupTable)[NumEntries]) const noexcept - { - static_assert (NumEntries != 0); - createLookupTable (resultLookupTable, NumEntries); - } - /** Returns true if all colours are opaque. */ bool isOpaque() const noexcept; diff --git a/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp index 225145fd..640ac3b5 100644 --- a/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp +++ b/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp @@ -627,7 +627,7 @@ void Graphics::drawEllipse (Rectangle area, float lineThickness) const { Path p; - if (approximatelyEqual (area.getWidth(), area.getHeight())) + if (area.getWidth() == area.getHeight()) { // For a circle, we can avoid having to generate a stroke p.addEllipse (area.expanded (lineThickness * 0.5f)); @@ -781,7 +781,7 @@ void Graphics::drawDashedLine (Line line, const float* dashLengths, const Line segment (line.getStart() + (delta * lastAlpha).toFloat(), line.getStart() + (delta * jmin (1.0, alpha)).toFloat()); - if (! approximatelyEqual (lineThickness, 1.0f)) + if (lineThickness != 1.0f) drawLine (segment, lineThickness); else context.drawLine (segment); diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp index 8e5ff757..11d37b08 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp @@ -189,7 +189,7 @@ void CustomTypeface::addGlyph (juce_wchar character, const Path& path, float wid void CustomTypeface::addKerningPair (juce_wchar char1, juce_wchar char2, float extraAmount) noexcept { - if (! approximatelyEqual (extraAmount, 0.0f)) + if (extraAmount != 0.0f) { if (auto* g = findGlyph (char1, true)) g->addKerningPair (char2, extraAmount); diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp index 8eb2c9d3..b6a6ec00 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp @@ -291,7 +291,7 @@ class Font::SharedFontInternal : public ReferenceCountedObject { const ScopedLock lock (mutex); - if (approximatelyEqual (ascent, 0.0f)) + if (ascent == 0.0f) ascent = getTypefacePtr (f)->getAscent(); return height * ascent; @@ -569,7 +569,7 @@ void Font::setHeight (float newHeight) { newHeight = FontValues::limitFontHeight (newHeight); - if (! approximatelyEqual (font->getHeight(), newHeight)) + if (font->getHeight() != newHeight) { dupeInternalIfShared(); font->setHeight (newHeight); @@ -581,7 +581,7 @@ void Font::setHeightWithoutChangingWidth (float newHeight) { newHeight = FontValues::limitFontHeight (newHeight); - if (! approximatelyEqual (font->getHeight(), newHeight)) + if (font->getHeight() != newHeight) { dupeInternalIfShared(); font->setHorizontalScale (font->getHorizontalScale() * (font->getHeight() / newHeight)); @@ -626,9 +626,9 @@ void Font::setSizeAndStyle (float newHeight, { newHeight = FontValues::limitFontHeight (newHeight); - if (! approximatelyEqual (font->getHeight(), newHeight) - || ! approximatelyEqual (font->getHorizontalScale(), newHorizontalScale) - || ! approximatelyEqual (font->getKerning(), newKerningAmount)) + if (font->getHeight() != newHeight + || font->getHorizontalScale() != newHorizontalScale + || font->getKerning() != newKerningAmount) { dupeInternalIfShared(); font->setHeight (newHeight); @@ -647,9 +647,9 @@ void Font::setSizeAndStyle (float newHeight, { newHeight = FontValues::limitFontHeight (newHeight); - if (! approximatelyEqual (font->getHeight(), newHeight) - || ! approximatelyEqual (font->getHorizontalScale(), newHorizontalScale) - || ! approximatelyEqual (font->getKerning(), newKerningAmount)) + if (font->getHeight() != newHeight + || font->getHorizontalScale() != newHorizontalScale + || font->getKerning() != newKerningAmount) { dupeInternalIfShared(); font->setHeight (newHeight); @@ -748,7 +748,7 @@ float Font::getStringWidthFloat (const String& text) const { auto w = getTypefacePtr()->getStringWidth (text); - if (! approximatelyEqual (font->getKerning(), 0.0f)) + if (font->getKerning() != 0.0f) w += font->getKerning() * (float) text.length(); return w * font->getHeight() * font->getHorizontalScale(); @@ -763,7 +763,7 @@ void Font::getGlyphPositions (const String& text, Array& glyphs, ArraygetHeight() * font->getHorizontalScale(); auto* x = xOffsets.getRawDataPointer(); - if (! approximatelyEqual (font->getKerning(), 0.0f)) + if (font->getKerning() != 0.0f) { for (int i = 0; i < num; ++i) x[i] = (x[i] + (float) i * font->getKerning()) * scale; diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp index 93571e1d..648acfcc 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp @@ -312,7 +312,7 @@ void GlyphArrangement::addFittedText (const Font& f, const String& text, Justification layout, int maximumLines, float minimumHorizontalScale) { - if (approximatelyEqual (minimumHorizontalScale, 0.0f)) + if (minimumHorizontalScale == 0.0f) minimumHorizontalScale = Font::getDefaultMinimumHorizontalScaleFactor(); // doesn't make much sense if this is outside a sensible range of 0.5 to 1.0 @@ -363,7 +363,7 @@ void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, const float d { jassert (startIndex >= 0); - if (! approximatelyEqual (dx, 0.0f) || ! approximatelyEqual (dy, 0.0f)) + if (dx != 0.0f || dy != 0.0f) { if (num < 0 || startIndex + num > glyphs.size()) num = glyphs.size() - startIndex; @@ -491,7 +491,7 @@ void GlyphArrangement::justifyGlyphs (int startIndex, int num, { auto glyphY = glyphs.getReference (startIndex + i).getBaselineY(); - if (! approximatelyEqual (glyphY, baseY)) + if (glyphY != baseY) { spreadOutLine (startIndex + lineStart, i - lineStart, width); @@ -695,7 +695,7 @@ void GlyphArrangement::drawGlyphUnderline (const Graphics& g, const PositionedGl auto lineThickness = (pg.font.getDescent()) * 0.3f; auto nextX = pg.x + pg.w; - if (i < glyphs.size() - 1 && approximatelyEqual (glyphs.getReference (i + 1).y, pg.y)) + if (i < glyphs.size() - 1 && glyphs.getReference (i + 1).y == pg.y) nextX = glyphs.getReference (i + 1).x; Path p; diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.cpp index da09e575..817d5ec3 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.cpp @@ -294,7 +294,7 @@ void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& te maxWidth -= 10.0f; } - if (! approximatelyEqual (bestWidth, maxWidth)) + if (bestWidth != maxWidth) createLayout (text, bestWidth, maxHeight); } diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp index 5c140437..cc1fef47 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp @@ -148,7 +148,7 @@ struct Typeface::HintingParams void applyVerticalHintingTransform (float fontSize, Path& path) { - if (! approximatelyEqual (cachedSize, fontSize)) + if (cachedSize != fontSize) { cachedSize = fontSize; cachedScale = Scaling (top, middle, bottom, fontSize); diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp index ddc55c29..d2aa599b 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp @@ -35,12 +35,12 @@ AffineTransform::AffineTransform (float m00, float m01, float m02, bool AffineTransform::operator== (const AffineTransform& other) const noexcept { - const auto tie = [] (const AffineTransform& a) - { - return std::tie (a.mat00, a.mat01, a.mat02, a.mat10, a.mat11, a.mat12); - }; - - return tie (*this) == tie (other); + return mat00 == other.mat00 + && mat01 == other.mat01 + && mat02 == other.mat02 + && mat10 == other.mat10 + && mat11 == other.mat11 + && mat12 == other.mat12; } bool AffineTransform::operator!= (const AffineTransform& other) const noexcept @@ -51,7 +51,12 @@ bool AffineTransform::operator!= (const AffineTransform& other) const noexcept //============================================================================== bool AffineTransform::isIdentity() const noexcept { - return operator== (AffineTransform()); + return mat01 == 0.0f + && mat02 == 0.0f + && mat10 == 0.0f + && mat12 == 0.0f + && mat00 == 1.0f + && mat11 == 1.0f; } const AffineTransform AffineTransform::identity (1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); @@ -202,7 +207,7 @@ AffineTransform AffineTransform::inverted() const noexcept bool AffineTransform::isSingularity() const noexcept { - return exactlyEqual (mat00 * mat11 - mat10 * mat01, 0.0f); + return (mat00 * mat11 - mat10 * mat01) == 0.0f; } AffineTransform AffineTransform::fromTargetPoints (float x00, float y00, @@ -224,10 +229,10 @@ AffineTransform AffineTransform::fromTargetPoints (float sx1, float sy1, float t bool AffineTransform::isOnlyTranslation() const noexcept { - return exactlyEqual (mat01, 0.0f) - && exactlyEqual (mat10, 0.0f) - && exactlyEqual (mat00, 1.0f) - && exactlyEqual (mat11, 1.0f); + return mat01 == 0.0f + && mat10 == 0.0f + && mat00 == 1.0f + && mat11 == 1.0f; } float AffineTransform::getDeterminant() const noexcept diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp index c1d9d620..7b660538 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp @@ -46,22 +46,17 @@ EdgeTable::EdgeTable (Rectangle area, const Path& path, const AffineTransfo t += lineStrideElements; } - auto leftLimit = scale * static_cast (bounds.getX()); - auto topLimit = scale * static_cast (bounds.getY()); - auto rightLimit = scale * static_cast (bounds.getRight()); - auto heightLimit = scale * static_cast (bounds.getHeight()); + auto leftLimit = scale * bounds.getX(); + auto topLimit = scale * bounds.getY(); + auto rightLimit = scale * bounds.getRight(); + auto heightLimit = scale * bounds.getHeight(); PathFlatteningIterator iter (path, transform); while (iter.next()) { - const auto scaleIterY = [] (auto y) - { - return static_cast (y * 256.0f + (y >= 0 ? 0.5f : -0.5f)); - }; - - auto y1 = scaleIterY (iter.y1); - auto y2 = scaleIterY (iter.y2); + auto y1 = roundToInt (iter.y1 * 256.0f); + auto y2 = roundToInt (iter.y2 * 256.0f); if (y1 != y2) { @@ -87,15 +82,19 @@ EdgeTable::EdgeTable (Rectangle area, const Path& path, const AffineTransfo { const double startX = 256.0f * iter.x1; const double multiplier = (iter.x2 - iter.x1) / (iter.y2 - iter.y1); - auto stepSize = static_cast (jlimit (1, 256, 256 / (1 + (int) std::abs (multiplier)))); + auto stepSize = jlimit (1, 256, 256 / (1 + (int) std::abs (multiplier))); do { auto step = jmin (stepSize, y2 - y1, 256 - (y1 & 255)); - auto x = static_cast (startX + multiplier * static_cast ((y1 + (step >> 1)) - startY)); - auto clampedX = static_cast (jlimit (leftLimit, rightLimit - 1, x)); + auto x = roundToInt (startX + multiplier * ((y1 + (step >> 1)) - startY)); + + if (x < leftLimit) + x = leftLimit; + else if (x >= rightLimit) + x = rightLimit - 1; - addEdgePoint (clampedX, static_cast (y1 / scale), static_cast (direction * step)); + addEdgePoint (x, y1 / scale, direction * step); y1 += step; } while (y1 < y2); diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Line.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Line.h index 3aac59d4..940b0955 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Line.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Line.h @@ -204,7 +204,7 @@ class Line Point getPointAlongLine (ValueType distanceFromStart) const noexcept { const auto length = getLength(); - return approximatelyEqual (length, (ValueType) 0) ? start : start + (end - start) * (distanceFromStart / length); + return length == 0 ? start : start + (end - start) * (distanceFromStart / length); } /** Returns a point which is a certain distance along and to the side of this line. @@ -391,34 +391,32 @@ class Line auto d2 = p4 - p3; auto divisor = d1.x * d2.y - d2.x * d1.y; - const auto zero = ValueType{}; - - if (approximatelyEqual (divisor, zero)) + if (divisor == 0) { if (! (d1.isOrigin() || d2.isOrigin())) { - if (approximatelyEqual (d1.y, zero) && ! approximatelyEqual (d2.y, zero)) + if (d1.y == 0 && d2.y != 0) { auto along = (p1.y - p3.y) / d2.y; intersection = p1.withX (p3.x + along * d2.x); return isZeroToOne (along); } - if (approximatelyEqual (d2.y, zero) && ! approximatelyEqual (d1.y, zero)) + if (d2.y == 0 && d1.y != 0) { auto along = (p3.y - p1.y) / d1.y; intersection = p3.withX (p1.x + along * d1.x); return isZeroToOne (along); } - if (approximatelyEqual (d1.x, zero) && ! approximatelyEqual (d2.x, zero)) + if (d1.x == 0 && d2.x != 0) { auto along = (p1.x - p3.x) / d2.x; intersection = p1.withY (p3.y + along * d2.y); return isZeroToOne (along); } - if (approximatelyEqual (d2.x, zero) && ! approximatelyEqual (d1.x, zero)) + if (d2.x == 0 && d1.x != 0) { auto along = (p3.x - p1.x) / d1.x; intersection = p3.withY (p1.y + along * d1.y); diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.cpp index a0ab1b31..5026a75d 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.cpp +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.cpp @@ -28,7 +28,7 @@ namespace juce // tests that some coordinates aren't NaNs #define JUCE_CHECK_COORDS_ARE_VALID(x, y) \ - jassert (! std::isnan (x) && ! std::isnan (y)); + jassert (x == x && y == y); //============================================================================== namespace PathHelpers @@ -58,13 +58,18 @@ namespace PathHelpers } //============================================================================== +const float Path::lineMarker = 100001.0f; +const float Path::moveMarker = 100002.0f; +const float Path::quadMarker = 100003.0f; +const float Path::cubicMarker = 100004.0f; +const float Path::closeSubPathMarker = 100005.0f; const float Path::defaultToleranceForTesting = 1.0f; const float Path::defaultToleranceForMeasurement = 0.6f; static bool isMarker (float value, float marker) noexcept { - return exactlyEqual (value, marker); + return value == marker; } //============================================================================== @@ -721,11 +726,10 @@ void Path::addBubble (Rectangle bodyArea, void Path::addPath (const Path& other) { const auto* d = other.data.begin(); - const auto size = other.data.size(); - for (int i = 0; i < size;) + for (int i = 0; i < other.data.size();) { - const auto type = d[i++]; + auto type = d[i++]; if (isMarker (type, moveMarker)) { @@ -763,11 +767,10 @@ void Path::addPath (const Path& other, const AffineTransform& transformToApply) { const auto* d = other.data.begin(); - const auto size = other.data.size(); - for (int i = 0; i < size;) + for (int i = 0; i < other.data.size();) { - const auto type = d[i++]; + auto type = d[i++]; if (isMarker (type, closeSubPathMarker)) { diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.h index 849dad2c..62dd72a1 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.h @@ -827,11 +827,11 @@ class JUCE_API Path final PathBounds bounds; bool useNonZeroWinding = true; - static constexpr float lineMarker = 100001.0f; - static constexpr float moveMarker = 100002.0f; - static constexpr float quadMarker = 100003.0f; - static constexpr float cubicMarker = 100004.0f; - static constexpr float closeSubPathMarker = 100005.0f; + static const float lineMarker; + static const float moveMarker; + static const float quadMarker; + static const float cubicMarker; + static const float closeSubPathMarker; JUCE_LEAK_DETECTOR (Path) }; diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.cpp index e8839142..1239de23 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.cpp +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.cpp @@ -138,9 +138,9 @@ bool PathFlatteningIterator::next() closesSubPath = stackPos == stackBase.get() && source != path.data.end() - && isMarker (*source, Path::closeSubPathMarker) - && approximatelyEqual (x2, subPathCloseX) - && approximatelyEqual (y2, subPathCloseY); + && *source == Path::closeSubPathMarker + && x2 == subPathCloseX + && y2 == subPathCloseY; return true; } @@ -167,8 +167,8 @@ bool PathFlatteningIterator::next() auto errorY = m3y - y2; auto outsideTolerance = errorX * errorX + errorY * errorY > toleranceSquared; - auto canBeSubdivided = (! approximatelyEqual (m3x, m1x) && ! approximatelyEqual (m3x, m2x)) - || (! approximatelyEqual (m3y, m1y) && ! approximatelyEqual (m3y, m2y)); + auto canBeSubdivided = (m3x != m1x && m3x != m2x) + || (m3y != m1y && m3y != m2y); if (outsideTolerance && canBeSubdivided) { @@ -226,10 +226,10 @@ bool PathFlatteningIterator::next() auto outsideTolerance = error1X * error1X + error1Y * error1Y > toleranceSquared || error2X * error2X + error2Y * error2Y > toleranceSquared; - auto canBeSubdivided = (! approximatelyEqual (m4x, m1x) && ! approximatelyEqual (m4x, m2x)) - || (! approximatelyEqual (m4y, m1y) && ! approximatelyEqual (m4y, m2y)) - || (! approximatelyEqual (m5x, m3x) && ! approximatelyEqual (m5x, m2x)) - || (! approximatelyEqual (m5y, m3y) && ! approximatelyEqual (m5y, m2y)); + auto canBeSubdivided = (m4x != m1x && m4x != m2x) + || (m4y != m1y && m4y != m2y) + || (m5x != m3x && m5x != m2x) + || (m5y != m3y && m5y != m2y); if (outsideTolerance && canBeSubdivided) { @@ -266,7 +266,7 @@ bool PathFlatteningIterator::next() } else if (isMarker (type, Path::closeSubPathMarker)) { - if (! approximatelyEqual (x2, subPathCloseX) || ! approximatelyEqual (y2, subPathCloseY)) + if (x2 != subPathCloseX || y2 != subPathCloseY) { x1 = x2; y1 = y2; diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.cpp index 8bfcf9de..fae0391a 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.cpp +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.cpp @@ -57,8 +57,9 @@ PathStrokeType::~PathStrokeType() noexcept bool PathStrokeType::operator== (const PathStrokeType& other) const noexcept { - const auto tie = [] (const PathStrokeType& p) { return std::tie (p.thickness, p.jointStyle, p.endStyle); }; - return tie (*this) == tie (other); + return thickness == other.thickness + && jointStyle == other.jointStyle + && endStyle == other.endStyle; } bool PathStrokeType::operator!= (const PathStrokeType& other) const noexcept @@ -69,128 +70,122 @@ bool PathStrokeType::operator!= (const PathStrokeType& other) const noexcept //============================================================================== namespace PathStrokeHelpers { - struct LineIntersection + static bool lineIntersection (const float x1, const float y1, + const float x2, const float y2, + const float x3, const float y3, + const float x4, const float y4, + float& intersectionX, + float& intersectionY, + float& distanceBeyondLine1EndSquared) noexcept { - Point point; - float distanceBeyondLine1EndSquared; - bool intersects; - }; - - static LineIntersection lineIntersection (const float x1, const float y1, - const float x2, const float y2, - const float x3, const float y3, - const float x4, const float y4) - { - if (! approximatelyEqual (x2, x3) || ! approximatelyEqual (y2, y3)) + if (x2 != x3 || y2 != y3) { - const auto dx1 = x2 - x1; - const auto dy1 = y2 - y1; - const auto dx2 = x4 - x3; - const auto dy2 = y4 - y3; - const auto divisor = dx1 * dy2 - dx2 * dy1; + auto dx1 = x2 - x1; + auto dy1 = y2 - y1; + auto dx2 = x4 - x3; + auto dy2 = y4 - y3; + auto divisor = dx1 * dy2 - dx2 * dy1; - if (approximatelyEqual (divisor, 0.0f)) + if (divisor == 0.0f) { - if (! ((approximatelyEqual (dx1, 0.0f) && approximatelyEqual (dy1, 0.0f)) - || (approximatelyEqual (dx2, 0.0f) && approximatelyEqual (dy2, 0.0f)))) + if (! ((dx1 == 0.0f && dy1 == 0.0f) || (dx2 == 0.0f && dy2 == 0.0f))) { - if (approximatelyEqual (dy1, 0.0f) && ! approximatelyEqual (dy2, 0.0f)) + if (dy1 == 0.0f && dy2 != 0.0f) { - const auto along = (y1 - y3) / dy2; - const auto intersectionX = x3 + along * dx2; - const auto intersectionY = y1; - - const auto distance = square (intersectionX - x2); - const auto distanceBeyondLine1EndSquared = (x2 > x1) == (intersectionX < x2) - ? -distance - : distance; - - return { { intersectionX, intersectionY }, - distanceBeyondLine1EndSquared, - along >= 0 && along <= 1.0f }; + auto along = (y1 - y3) / dy2; + intersectionX = x3 + along * dx2; + intersectionY = y1; + + distanceBeyondLine1EndSquared = intersectionX - x2; + distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; + if ((x2 > x1) == (intersectionX < x2)) + distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; + + return along >= 0 && along <= 1.0f; } - if (approximatelyEqual (dy2, 0.0f) && ! approximatelyEqual (dy1, 0.0f)) + if (dy2 == 0.0f && dy1 != 0.0f) { - const auto along = (y3 - y1) / dy1; - const auto intersectionX = x1 + along * dx1; - const auto intersectionY = y3; + auto along = (y3 - y1) / dy1; + intersectionX = x1 + along * dx1; + intersectionY = y3; - const auto distance = square ((along - 1.0f) * dx1); - const auto distanceBeyondLine1EndSquared = along < 1.0f ? -distance : distance; + distanceBeyondLine1EndSquared = (along - 1.0f) * dx1; + distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; + if (along < 1.0f) + distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; - return { { intersectionX, intersectionY }, - distanceBeyondLine1EndSquared, - along >= 0 && along <= 1.0f }; + return along >= 0 && along <= 1.0f; } - if (approximatelyEqual (dx1, 0.0f) && ! approximatelyEqual (dx2, 0.0f)) + if (dx1 == 0.0f && dx2 != 0.0f) { - const auto along = (x1 - x3) / dx2; - const auto intersectionX = x1; - const auto intersectionY = y3 + along * dy2; - - const auto distance = square (intersectionY - y2); - const auto distanceBeyondLine1EndSquared = (y2 > y1) == (intersectionY < y2) - ? -distance - : distance; - - return { { intersectionX, intersectionY }, - distanceBeyondLine1EndSquared, - along >= 0 && along <= 1.0f }; + auto along = (x1 - x3) / dx2; + intersectionX = x1; + intersectionY = y3 + along * dy2; + + distanceBeyondLine1EndSquared = intersectionY - y2; + distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; + + if ((y2 > y1) == (intersectionY < y2)) + distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; + + return along >= 0 && along <= 1.0f; } - if (approximatelyEqual (dx2, 0.0f) && ! approximatelyEqual (dx1, 0.0f)) + if (dx2 == 0.0f && dx1 != 0.0f) { - const auto along = (x3 - x1) / dx1; - const auto intersectionX = x3; - const auto intersectionY = y1 + along * dy1; + auto along = (x3 - x1) / dx1; + intersectionX = x3; + intersectionY = y1 + along * dy1; - const auto distance = square ((along - 1.0f) * dy1); - const auto distanceBeyondLine1EndSquared = along < 1.0f ? -distance : distance; + distanceBeyondLine1EndSquared = (along - 1.0f) * dy1; + distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; + if (along < 1.0f) + distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; - return { { intersectionX, intersectionY }, - distanceBeyondLine1EndSquared, - along >= 0 && along <= 1.0f }; + return along >= 0 && along <= 1.0f; } } - const auto intersectionX = 0.5f * (x2 + x3); - const auto intersectionY = 0.5f * (y2 + y3); - - const auto distanceBeyondLine1EndSquared = 0.0f; + intersectionX = 0.5f * (x2 + x3); + intersectionY = 0.5f * (y2 + y3); - return { { intersectionX, intersectionY }, - distanceBeyondLine1EndSquared, - false }; + distanceBeyondLine1EndSquared = 0.0f; + return false; } - const auto along = ((y1 - y3) * dx2 - (x1 - x3) * dy2) / divisor; + auto along1 = ((y1 - y3) * dx2 - (x1 - x3) * dy2) / divisor; - const auto intersectionX = x1 + along * dx1; - const auto intersectionY = y1 + along * dy1; + intersectionX = x1 + along1 * dx1; + intersectionY = y1 + along1 * dy1; - if (along >= 0 && along <= 1.0f) + if (along1 >= 0 && along1 <= 1.0f) { - const auto along2 = ((y1 - y3) * dx1 - (x1 - x3) * dy1) / divisor; + auto along2 = ((y1 - y3) * dx1 - (x1 - x3) * dy1) / divisor; if (along2 >= 0 && along2 <= 1.0f) { - return { { intersectionX, intersectionY }, - 0.0f, - true }; + distanceBeyondLine1EndSquared = 0.0f; + return true; } } - const auto distance = square (along - 1.0f) * (dx1 * dx1 + dy1 * dy1); - const auto distanceBeyondLine1EndSquared = along < 1.0f ? -distance : distance; + distanceBeyondLine1EndSquared = along1 - 1.0f; + distanceBeyondLine1EndSquared *= distanceBeyondLine1EndSquared; + distanceBeyondLine1EndSquared *= (dx1 * dx1 + dy1 * dy1); - return { { intersectionX, intersectionY }, - distanceBeyondLine1EndSquared, - false }; + if (along1 < 1.0f) + distanceBeyondLine1EndSquared = -distanceBeyondLine1EndSquared; + + return false; } - return { Point { x2, y2 }, 0.0f, true }; + intersectionX = x2; + intersectionY = y2; + + distanceBeyondLine1EndSquared = 0.0f; + return true; } static void addEdgeAndJoint (Path& destPath, @@ -203,29 +198,31 @@ namespace PathStrokeHelpers const float midX, const float midY) { if (style == PathStrokeType::beveled - || (approximatelyEqual (x3, x4) && approximatelyEqual (y3, y4)) - || (approximatelyEqual (x1, x2) && approximatelyEqual (y1, y2))) + || (x3 == x4 && y3 == y4) + || (x1 == x2 && y1 == y2)) { destPath.lineTo (x2, y2); destPath.lineTo (x3, y3); } else { - const auto intersection = lineIntersection (x1, y1, x2, y2, x3, y3, x4, y4); + float jx, jy, distanceBeyondLine1EndSquared; // if they intersect, use this point.. - if (intersection.intersects) + if (lineIntersection (x1, y1, x2, y2, + x3, y3, x4, y4, + jx, jy, distanceBeyondLine1EndSquared)) { - destPath.lineTo (intersection.point); + destPath.lineTo (jx, jy); } else { if (style == PathStrokeType::mitered) { - if (0.0f < intersection.distanceBeyondLine1EndSquared - && intersection.distanceBeyondLine1EndSquared < maxMiterExtensionSquared) + if (distanceBeyondLine1EndSquared < maxMiterExtensionSquared + && distanceBeyondLine1EndSquared > 0.0f) { - destPath.lineTo (intersection.point); + destPath.lineTo (jx, jy); } else { @@ -304,7 +301,7 @@ namespace PathStrokeHelpers auto dy = y2 - y1; auto len = juce_hypot (dx, dy); - if (approximatelyEqual (len, 0.0f)) + if (len == 0.0f) { offx1 = offx2 = x1; offy1 = offy2 = y1; @@ -613,7 +610,7 @@ namespace PathStrokeHelpers { auto len = std::sqrt (hypotSquared); - if (approximatelyEqual (len, 0.0f)) + if (len == 0.0f) { l.rx1 = l.rx2 = l.lx1 = l.lx2 = l.x1; l.ry1 = l.ry2 = l.ly1 = l.ly2 = l.y1; diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Point.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Point.h index 9e6117f0..55253288 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Point.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Point.h @@ -54,19 +54,14 @@ class Point /** Copies this point from another one. */ Point& operator= (const Point&) = default; - constexpr inline bool operator== (Point other) const noexcept - { - const auto tie = [] (const Point& p) { return std::tie (p.x, p.y); }; - return tie (*this) == tie (other); - } - - constexpr inline bool operator!= (Point other) const noexcept { return ! operator== (other); } + constexpr inline bool operator== (Point other) const noexcept { return x == other.x && y == other.y; } + constexpr inline bool operator!= (Point other) const noexcept { return x != other.x || y != other.y; } /** Returns true if the point is (0, 0). */ - constexpr bool isOrigin() const noexcept { return operator== (Point()); } + constexpr bool isOrigin() const noexcept { return x == ValueType() && y == ValueType(); } /** Returns true if the coordinates are finite values. */ - constexpr inline bool isFinite() const noexcept { return juce_isfinite (x) && juce_isfinite (y); } + constexpr inline bool isFinite() const noexcept { return juce_isfinite(x) && juce_isfinite(y); } /** Returns the point's x coordinate. */ constexpr inline ValueType getX() const noexcept { return x; } diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h index 2e7bf03f..9789f7ee 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h @@ -613,14 +613,10 @@ class Rectangle //============================================================================== /** Returns true if the two rectangles are identical. */ - bool operator== (const Rectangle& other) const noexcept - { - const auto tie = [] (const Rectangle& r) { return std::tie (r.pos, r.w, r.h); }; - return tie (*this) == tie (other); - } + bool operator== (const Rectangle& other) const noexcept { return pos == other.pos && w == other.w && h == other.h; } /** Returns true if the two rectangles are not identical. */ - bool operator!= (const Rectangle& other) const noexcept { return ! operator== (other); } + bool operator!= (const Rectangle& other) const noexcept { return pos != other.pos || w != other.w || h != other.h; } /** Returns true if this coordinate is inside the rectangle. */ bool contains (ValueType xCoord, ValueType yCoord) const noexcept diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/juce_GIFLoader.cpp b/JuceLibraryCode/modules/juce_graphics/image_formats/juce_GIFLoader.cpp index 6b3fb0f2..f2cd0d86 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/juce_GIFLoader.cpp +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/juce_GIFLoader.cpp @@ -324,8 +324,8 @@ class GIFLoader if (finished) return -1; - buffer[0] = buffer [jmax (0, lastByteIndex - 2)]; - buffer[1] = buffer [jmax (0, lastByteIndex - 1)]; + buffer[0] = buffer [lastByteIndex - 2]; + buffer[1] = buffer [lastByteIndex - 1]; const int n = readDataBlock (buffer + 2); diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/juce_PNGLoader.cpp b/JuceLibraryCode/modules/juce_graphics/image_formats/juce_PNGLoader.cpp index fe40b145..137e47ce 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/juce_PNGLoader.cpp +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/juce_PNGLoader.cpp @@ -58,14 +58,13 @@ namespace pnglibNamespace using std::free; #endif - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcomma", - "-Wfloat-equal", + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wsign-conversion", "-Wimplicit-fallthrough", - "-Wmaybe-uninitialized", - "-Wnull-pointer-subtraction", - "-Wsign-conversion", "-Wtautological-constant-out-of-range-compare", - "-Wzero-as-null-pointer-constant") + "-Wzero-as-null-pointer-constant", + "-Wcomma", + "-Wmaybe-uninitialized", + "-Wnull-pointer-subtraction") #undef check using std::abs; diff --git a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp index cdf6cc32..b1731eb5 100644 --- a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp +++ b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp @@ -30,12 +30,7 @@ struct ImageCache::Pimpl : private Timer, private DeletedAtShutdown { Pimpl() = default; - - ~Pimpl() override - { - stopTimer(); - clearSingletonInstance(); - } + ~Pimpl() override { clearSingletonInstance(); } JUCE_DECLARE_SINGLETON (ImageCache::Pimpl, false) diff --git a/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h b/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h index 9dfd7c03..fac21453 100644 --- a/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h +++ b/JuceLibraryCode/modules/juce_graphics/images/juce_ScaledImage.h @@ -40,8 +40,6 @@ namespace juce The ScaledImage class is designed to store an image alongside a scale factor that informs a renderer how to convert between the image's pixels and points. - - @tags{GUI} */ class JUCE_API ScaledImage { diff --git a/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp b/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp index bb8e6d86..d852554b 100644 --- a/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp +++ b/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp @@ -139,32 +139,32 @@ #endif #if JUCE_USE_FREETYPE - #include "native/juce_Fonts_freetype.cpp" + #include "native/juce_freetype_Fonts.cpp" #endif //============================================================================== #if JUCE_MAC || JUCE_IOS - #include "native/juce_Fonts_mac.mm" - #include "native/juce_CoreGraphicsContext_mac.mm" - #include "native/juce_IconHelpers_mac.cpp" + #include "native/juce_mac_Fonts.mm" + #include "native/juce_mac_CoreGraphicsContext.mm" + #include "native/juce_mac_IconHelpers.cpp" #elif JUCE_WINDOWS - #include "native/juce_DirectWriteTypeface_windows.cpp" - #include "native/juce_DirectWriteTypeLayout_windows.cpp" - #include "native/juce_Fonts_windows.cpp" - #include "native/juce_IconHelpers_windows.cpp" + #include "native/juce_win32_DirectWriteTypeface.cpp" + #include "native/juce_win32_DirectWriteTypeLayout.cpp" + #include "native/juce_win32_Fonts.cpp" + #include "native/juce_win32_IconHelpers.cpp" #if JUCE_DIRECT2D - #include "native/juce_Direct2DGraphicsContext_windows.cpp" + #include "native/juce_win32_Direct2DGraphicsContext.cpp" #endif #elif JUCE_LINUX || JUCE_BSD - #include "native/juce_Fonts_linux.cpp" - #include "native/juce_IconHelpers_linux.cpp" + #include "native/juce_linux_Fonts.cpp" + #include "native/juce_linux_IconHelpers.cpp" #elif JUCE_ANDROID - #include "native/juce_GraphicsContext_android.cpp" - #include "native/juce_Fonts_android.cpp" - #include "native/juce_IconHelpers_android.cpp" + #include "native/juce_android_GraphicsContext.cpp" + #include "native/juce_android_Fonts.cpp" + #include "native/juce_android_IconHelpers.cpp" #endif diff --git a/JuceLibraryCode/modules/juce_graphics/juce_graphics.h b/JuceLibraryCode/modules/juce_graphics/juce_graphics.h index 7f35fc74..cb86476d 100644 --- a/JuceLibraryCode/modules/juce_graphics/juce_graphics.h +++ b/JuceLibraryCode/modules/juce_graphics/juce_graphics.h @@ -35,7 +35,7 @@ ID: juce_graphics vendor: juce - version: 7.0.7 + version: 7.0.5 name: JUCE graphics classes description: Classes for 2D vector graphics, image loading/saving, font handling, etc. website: http://www.juce.com/juce @@ -150,10 +150,10 @@ namespace juce #include "effects/juce_GlowEffect.h" #if JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS && (JUCE_MAC || JUCE_IOS) - #include "native/juce_CoreGraphicsHelpers_mac.h" - #include "native/juce_CoreGraphicsContext_mac.h" + #include "native/juce_mac_CoreGraphicsHelpers.h" + #include "native/juce_mac_CoreGraphicsContext.h" #endif #if JUCE_DIRECT2D && JUCE_WINDOWS -#include "native/juce_Direct2DGraphicsContext_windows.h" +#include "native/juce_win32_Direct2DGraphicsContext.h" #endif diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h b/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h index d9fd8c99..716236a7 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -86,10 +86,8 @@ class TranslationOrTransform complexTransform = getTransformWith (t); isOnlyTranslated = false; - isRotated = (! approximatelyEqual (complexTransform.mat01, 0.0f) - || ! approximatelyEqual (complexTransform.mat10, 0.0f) - || complexTransform.mat00 < 0 - || complexTransform.mat11 < 0); + isRotated = (complexTransform.mat01 != 0.0f || complexTransform.mat10 != 0.0f + || complexTransform.mat00 < 0 || complexTransform.mat11 < 0); } float getPhysicalPixelScaleFactor() const noexcept diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_android.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_android.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_GraphicsContext_android.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_android_GraphicsContext.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_GraphicsContext_android.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_android_GraphicsContext.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_IconHelpers_android.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_android_IconHelpers.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_IconHelpers_android.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_android_IconHelpers.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_freetype.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_freetype.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_linux.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_linux.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_IconHelpers_linux.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_linux_IconHelpers.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_IconHelpers_linux.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_linux_IconHelpers.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.h b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h similarity index 97% rename from JuceLibraryCode/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.h rename to JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h index ccf962e8..528c625b 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.h +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h @@ -120,7 +120,8 @@ class CoreGraphicsContext : public LowLevelGraphicsContext detail::ContextPtr context; const CGFloat flipHeight; detail::ColorSpacePtr rgbColourSpace, greyColourSpace; - mutable std::optional> lastClipRect; + mutable Rectangle lastClipRect; + mutable bool lastClipRectIsValid = false; struct SavedState { @@ -141,8 +142,8 @@ class CoreGraphicsContext : public LowLevelGraphicsContext std::unique_ptr state; OwnedArray stateStack; - void setContextClipToPath (const Path&, const AffineTransform&); void drawGradient(); + void createPath (const Path&) const; void createPath (const Path&, const AffineTransform&) const; void flip() const; void applyTransform (const AffineTransform&) const; diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm similarity index 89% rename from JuceLibraryCode/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm rename to JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index d8e7f0cf..5ea3da89 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -221,8 +221,8 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) { CGContextTranslateCTM (context.get(), o.x, -o.y); - if (lastClipRect.has_value()) - lastClipRect->translate (-o.x, -o.y); + if (lastClipRectIsValid) + lastClipRect.translate (-o.x, -o.y); } void CoreGraphicsContext::addTransform (const AffineTransform& transform) @@ -231,7 +231,7 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) .followedBy (transform) .translated (0, (float) -flipHeight) .scaled (1.0f, -1.0f)); - lastClipRect.reset(); + lastClipRectIsValid = false; jassert (getPhysicalPixelScaleFactor() > 0.0f); } @@ -249,14 +249,14 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) CGContextClipToRect (context.get(), CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); - if (lastClipRect.has_value()) + if (lastClipRectIsValid) { // This is actually incorrect, because the actual clip region may be complex, and // clipping its bounds to a rect may not be right... But, removing this shortcut // doesn't actually fix anything because CoreGraphics also ignores complex regions // when calculating the resultant clip bounds, and makes the same mistake! - lastClipRect = lastClipRect->getIntersection (r); - return ! lastClipRect->isEmpty(); + lastClipRect = lastClipRect.getIntersection (r); + return ! lastClipRect.isEmpty(); } return ! isClipEmpty(); @@ -267,6 +267,7 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) if (clipRegion.isEmpty()) { CGContextClipToRect (context.get(), CGRectZero); + lastClipRectIsValid = true; lastClipRect = Rectangle(); return false; } @@ -279,7 +280,7 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) rects[i++] = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()); CGContextClipToRects (context.get(), rects, numRects); - lastClipRect.reset(); + lastClipRectIsValid = false; return true; } @@ -295,7 +296,7 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) clipToRectangleListWithoutTest (remaining); } -void CoreGraphicsContext::setContextClipToPath (const Path& path, const AffineTransform& transform) +void CoreGraphicsContext::clipToPath (const Path& path, const AffineTransform& transform) { createPath (path, transform); @@ -303,12 +304,8 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) CGContextClip (context.get()); else CGContextEOClip (context.get()); -} -void CoreGraphicsContext::clipToPath (const Path& path, const AffineTransform& transform) -{ - setContextClipToPath (path, transform); - lastClipRect.reset(); + lastClipRectIsValid = false; } void CoreGraphicsContext::clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform) @@ -332,7 +329,7 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) applyTransform (t.inverted()); flip(); - lastClipRect.reset(); + lastClipRectIsValid = false; } } @@ -343,17 +340,18 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) Rectangle CoreGraphicsContext::getClipBounds() const { - if (! lastClipRect.has_value()) + if (! lastClipRectIsValid) { auto bounds = CGRectIntegral (CGContextGetClipBoundingBox (context.get())); - lastClipRect = Rectangle (roundToInt (bounds.origin.x), - roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)), - roundToInt (bounds.size.width), - roundToInt (bounds.size.height)); + lastClipRectIsValid = true; + lastClipRect.setBounds (roundToInt (bounds.origin.x), + roundToInt (flipHeight - (bounds.origin.y + bounds.size.height)), + roundToInt (bounds.size.width), + roundToInt (bounds.size.height)); } - return *lastClipRect; + return lastClipRect; } bool CoreGraphicsContext::isClipEmpty() const @@ -378,7 +376,7 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) CGContextSetTextMatrix (context.get(), state->textMatrix); stateStack.removeLast (1, false); - lastClipRect.reset(); + lastClipRectIsValid = false; } else { @@ -472,45 +470,56 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) CGContextSetBlendMode (context.get(), kCGBlendModeCopy); fillCGRect (cgRect, false); CGContextSetBlendMode (context.get(), kCGBlendModeNormal); - return; } - - if (state->fillType.isColour()) + else { - CGContextFillRect (context.get(), cgRect); - return; - } + if (state->fillType.isColour()) + { + CGContextFillRect (context.get(), cgRect); + } + else + { + ScopedCGContextState scopedState (context.get()); - ScopedCGContextState scopedState (context.get()); - CGContextClipToRect (context.get(), cgRect); + CGContextClipToRect (context.get(), cgRect); - if (state->fillType.isGradient()) - drawGradient(); - else - drawImage (state->fillType.image, state->fillType.transform, true); + if (state->fillType.isGradient()) + drawGradient(); + else + drawImage (state->fillType.image, state->fillType.transform, true); + } + } } void CoreGraphicsContext::fillPath (const Path& path, const AffineTransform& transform) { + ScopedCGContextState scopedState (context.get()); + if (state->fillType.isColour()) { - createPath (path, transform); + flip(); + applyTransform (transform); + createPath (path); if (path.isUsingNonZeroWinding()) CGContextFillPath (context.get()); else CGContextEOFillPath (context.get()); - - return; } + else + { + createPath (path, transform); - ScopedCGContextState scopedState (context.get()); - setContextClipToPath (path, transform); + if (path.isUsingNonZeroWinding()) + CGContextClip (context.get()); + else + CGContextEOClip (context.get()); - if (state->fillType.isGradient()) - drawGradient(); - else - drawImage (state->fillType.image, state->fillType.transform, true); + if (state->fillType.isGradient()) + drawGradient(); + else + drawImage (state->fillType.image, state->fillType.transform, true); + } } void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTransform& transform) @@ -540,20 +549,15 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) CGContextDrawTiledImage (context.get(), imageRect, image.get()); #else // There's a bug in CGContextDrawTiledImage that makes it incredibly slow - // if it's doing a transformation - it's quicker to just draw lots of images manually, - // but we might not be able to draw the images ourselves if the clipping region is not - // finite - const auto doCustomTiling = [&] + // if it's doing a transformation - it's quicker to just draw lots of images manually + if (&CGContextDrawTiledImage != nullptr && transform.isOnlyTranslation()) { - if (transform.isOnlyTranslation()) - return false; - - const auto bound = CGContextGetClipBoundingBox (context.get()); - - if (CGRectIsNull (bound)) - return false; - - const auto clip = CGRectIntegral (bound); + CGContextDrawTiledImage (context.get(), imageRect, image.get()); + } + else + { + // Fallback to manually doing a tiled fill + auto clip = CGRectIntegral (CGContextGetClipBoundingBox (context.get())); int x = 0, y = 0; while (x > clip.origin.x) x -= iw; @@ -569,12 +573,7 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) y += ih; } - - return true; - }; - - if (! doCustomTiling()) - CGContextDrawTiledImage (context.get(), imageRect, image.get()); + } #endif } else @@ -593,25 +592,28 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) void CoreGraphicsContext::fillRectList (const RectangleList& list) { - std::vector rects; - rects.reserve ((size_t) list.getNumRectangles()); + HeapBlock rects (list.getNumRectangles()); + + size_t num = 0; for (auto& r : list) - rects.push_back (CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); + rects[num++] = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()); if (state->fillType.isColour()) { - CGContextFillRects (context.get(), rects.data(), rects.size()); - return; + CGContextFillRects (context.get(), rects, num); } + else + { + ScopedCGContextState scopedState (context.get()); - ScopedCGContextState scopedState (context.get()); - CGContextClipToRects (context.get(), rects.data(), rects.size()); + CGContextClipToRects (context.get(), rects, num); - if (state->fillType.isGradient()) - drawGradient(); - else - drawImage (state->fillType.image, state->fillType.transform, true); + if (state->fillType.isGradient()) + drawGradient(); + else + drawImage (state->fillType.image, state->fillType.transform, true); + } } void CoreGraphicsContext::setFont (const Font& newFont) @@ -700,8 +702,10 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) CoreGraphicsContext::SavedState::SavedState (const SavedState& other) : fillType (other.fillType), font (other.font), fontRef (other.fontRef), textMatrix (other.textMatrix), inverseTextMatrix (other.inverseTextMatrix), - gradient (other.gradient.get() != nullptr ? CGGradientRetain (other.gradient.get()) : nullptr) + gradient (other.gradient.get()) { + if (gradient != nullptr) + CGGradientRetain (gradient.get()); } CoreGraphicsContext::SavedState::~SavedState() = default; @@ -715,9 +719,9 @@ static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) static CGGradientRef createGradient (const ColourGradient& g, CGColorSpaceRef colourSpace) { auto numColours = g.getNumColours(); - std::vector data ((size_t) numColours * 5); - auto locations = data.data(); - auto components = locations + numColours; + auto data = (CGFloat*) alloca ((size_t) numColours * 5 * sizeof (CGFloat)); + auto locations = data; + auto components = data + numColours; auto comps = components; for (int i = 0; i < numColours; ++i) @@ -730,8 +734,8 @@ static CGGradientRef createGradient (const ColourGradient& g, CGColorSpaceRef co locations[i] = (CGFloat) g.getColourPosition (i); // There's a bug (?) in the way the CG renderer works where it seems - // to go wrong if you have two colour stops both at position 0. - jassert (i == 0 || ! approximatelyEqual (locations[i], (CGFloat) 0.0)); + // to go wrong if you have two colour stops both at position 0.. + jassert (i == 0 || locations[i] != 0); } return CGGradientCreateWithColorComponents (colourSpace, components, locations, (size_t) numColours); @@ -759,6 +763,24 @@ static CGGradientRef createGradient (const ColourGradient& g, CGColorSpaceRef co kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); } +void CoreGraphicsContext::createPath (const Path& path) const +{ + CGContextBeginPath (context.get()); + + for (Path::Iterator i (path); i.next();) + { + switch (i.elementType) + { + case Path::Iterator::startNewSubPath: CGContextMoveToPoint (context.get(), i.x1, i.y1); break; + case Path::Iterator::lineTo: CGContextAddLineToPoint (context.get(), i.x1, i.y1); break; + case Path::Iterator::quadraticTo: CGContextAddQuadCurveToPoint (context.get(), i.x1, i.y1, i.x2, i.y2); break; + case Path::Iterator::cubicTo: CGContextAddCurveToPoint (context.get(), i.x1, i.y1, i.x2, i.y2, i.x3, i.y3); break; + case Path::Iterator::closePath: CGContextClosePath (context.get()); break; + default: jassertfalse; break; + } + } +} + void CoreGraphicsContext::createPath (const Path& path, const AffineTransform& transform) const { CGContextBeginPath (context.get()); diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_CoreGraphicsHelpers_mac.h b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_CoreGraphicsHelpers_mac.h rename to JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_mac.mm b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm similarity index 99% rename from JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_mac.mm rename to JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm index 93c620d8..864e2dde 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_mac.mm +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -295,7 +295,7 @@ static AttributedStringAndFontMap createCFAttributedString (const AttributedStri auto extraKerning = attr.font.getExtraKerningFactor(); - if (! approximatelyEqual (extraKerning, 0.0f)) + if (extraKerning != 0) { extraKerning *= attr.font.getHeight(); diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_IconHelpers_mac.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_IconHelpers.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_IconHelpers_mac.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_mac_IconHelpers.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.h similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h rename to JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.h diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_DirectWriteTypeLayout_windows.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp similarity index 99% rename from JuceLibraryCode/modules/juce_graphics/native/juce_DirectWriteTypeLayout_windows.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp index bd42ced8..c8ca0912 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_DirectWriteTypeLayout_windows.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp @@ -108,7 +108,7 @@ namespace DirectWriteTypeLayout if (! (baselineOriginY >= -1.0e10f && baselineOriginY <= 1.0e10f)) baselineOriginY = 0; // DirectWrite sometimes sends NaNs in this parameter - if (! approximatelyEqual (baselineOriginY, lastOriginY)) + if (baselineOriginY != lastOriginY) { lastOriginY = baselineOriginY; ++currentLine; diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_windows.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_Fonts_windows.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_IconHelpers_windows.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_IconHelpers.cpp similarity index 100% rename from JuceLibraryCode/modules/juce_graphics/native/juce_IconHelpers_windows.cpp rename to JuceLibraryCode/modules/juce_graphics/native/juce_win32_IconHelpers.cpp diff --git a/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.cpp b/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.cpp index 6a1319ef..9459bfbc 100644 --- a/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.cpp +++ b/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.cpp @@ -39,7 +39,7 @@ bool RectanglePlacement::operator!= (const RectanglePlacement& other) const noex void RectanglePlacement::applyTo (double& x, double& y, double& w, double& h, const double dx, const double dy, const double dw, const double dh) const noexcept { - if (approximatelyEqual (w, 0.0) || approximatelyEqual (h, 0.0)) + if (w == 0.0 || h == 0.0) return; if ((flags & stretchToFit) != 0) diff --git a/JuceLibraryCode/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityEvent.h b/JuceLibraryCode/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityEvent.h index 5da54b64..159f11e0 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityEvent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityEvent.h @@ -78,4 +78,4 @@ enum class AccessibilityEvent rowSelectionChanged }; -} // namespace juce +} diff --git a/JuceLibraryCode/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityRole.h b/JuceLibraryCode/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityRole.h index 76a068ad..d80c5a1e 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityRole.h +++ b/JuceLibraryCode/modules/juce_gui_basics/accessibility/enums/juce_AccessibilityRole.h @@ -68,4 +68,4 @@ enum class AccessibilityRole unspecified }; -} // namespace juce +} diff --git a/JuceLibraryCode/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityTableInterface.h b/JuceLibraryCode/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityTableInterface.h index 2d08cf6a..a50227ed 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityTableInterface.h +++ b/JuceLibraryCode/modules/juce_gui_basics/accessibility/interfaces/juce_AccessibilityTableInterface.h @@ -65,7 +65,6 @@ class JUCE_API AccessibilityTableInterface */ virtual const AccessibilityHandler* getHeaderHandler() const = 0; - /** A simple span of elements. */ struct Span { int begin, num; }; /** Given the handler of one of the cells in the table, returns the rows covered diff --git a/JuceLibraryCode/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp b/JuceLibraryCode/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp index 54a17bc3..2ec62520 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp @@ -28,62 +28,29 @@ namespace juce AccessibilityHandler* AccessibilityHandler::currentlyFocusedHandler = nullptr; -class NativeChildHandler -{ -public: - static NativeChildHandler& getInstance() - { - static NativeChildHandler instance; - return instance; - } - - void* getNativeChild (Component& component) const - { - if (auto it = nativeChildForComponent.find (&component); - it != nativeChildForComponent.end()) - { - return it->second; - } - - return nullptr; - } - - Component* getComponent (void* nativeChild) const - { - if (auto it = componentForNativeChild.find (nativeChild); - it != componentForNativeChild.end()) - { - return it->second; - } - - return nullptr; - } - - void setNativeChild (Component& component, void* nativeChild) - { - clearComponent (component); - - if (nativeChild != nullptr) - { - nativeChildForComponent[&component] = nativeChild; - componentForNativeChild[nativeChild] = &component; - } - } - -private: - NativeChildHandler() = default; +enum class InternalAccessibilityEvent +{ + elementCreated, + elementDestroyed, + elementMovedOrResized, + focusChanged, + windowOpened, + windowClosed +}; - void clearComponent (Component& component) - { - if (auto* nativeChild = getNativeChild (component)) - componentForNativeChild.erase (nativeChild); +void notifyAccessibilityEventInternal (const AccessibilityHandler&, InternalAccessibilityEvent); - nativeChildForComponent.erase (&component); - } +inline String getAccessibleApplicationOrPluginName() +{ + #if defined (JucePlugin_Name) + return JucePlugin_Name; + #else + if (auto* app = JUCEApplicationBase::getInstance()) + return app->getApplicationName(); - std::map componentForNativeChild; - std::map nativeChildForComponent; -}; + return "JUCE Application"; + #endif +} AccessibilityHandler::AccessibilityHandler (Component& comp, AccessibilityRole accessibilityRole, @@ -101,7 +68,7 @@ AccessibilityHandler::AccessibilityHandler (Component& comp, AccessibilityHandler::~AccessibilityHandler() { giveAwayFocus(); - detail::AccessibilityHelpers::notifyAccessibilityEvent (*this, detail::AccessibilityHelpers::Event::elementDestroyed); + notifyAccessibilityEventInternal (*this, InternalAccessibilityEvent::elementDestroyed); } //============================================================================== @@ -353,13 +320,13 @@ void AccessibilityHandler::grabFocusInternal (bool canTryParent) void AccessibilityHandler::giveAwayFocusInternal() const { currentlyFocusedHandler = nullptr; - detail::AccessibilityHelpers::notifyAccessibilityEvent (*this, detail::AccessibilityHelpers::Event::focusChanged); + notifyAccessibilityEventInternal (*this, InternalAccessibilityEvent::focusChanged); } void AccessibilityHandler::takeFocus() { currentlyFocusedHandler = this; - detail::AccessibilityHelpers::notifyAccessibilityEvent (*this, detail::AccessibilityHelpers::Event::focusChanged); + notifyAccessibilityEventInternal (*this, InternalAccessibilityEvent::focusChanged); if ((component.isShowing() || component.isOnDesktop()) && component.getWantsKeyboardFocus() @@ -369,35 +336,4 @@ void AccessibilityHandler::takeFocus() } } -std::unique_ptr AccessibilityHandler::createNativeImpl (AccessibilityHandler& handler) -{ - #if JUCE_NATIVE_ACCESSIBILITY_INCLUDED - return std::make_unique (handler); - #else - ignoreUnused (handler); - return nullptr; - #endif -} - -void* AccessibilityHandler::getNativeChildForComponent (Component& component) -{ - return NativeChildHandler::getInstance().getNativeChild (component); -} - -Component* AccessibilityHandler::getComponentForNativeChild (void* nativeChild) -{ - return NativeChildHandler::getInstance().getComponent (nativeChild); -} - -void AccessibilityHandler::setNativeChildForComponent (Component& component, void* nativeChild) -{ - NativeChildHandler::getInstance().setNativeChild (component, nativeChild); -} - -#if ! JUCE_NATIVE_ACCESSIBILITY_INCLUDED - void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent) const {} - void AccessibilityHandler::postAnnouncement (const String&, AnnouncementPriority) {} - AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const { return nullptr; } -#endif - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.h b/JuceLibraryCode/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.h index a0a9d41a..5cab7a70 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.h +++ b/JuceLibraryCode/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.h @@ -291,26 +291,6 @@ class JUCE_API AccessibilityHandler AccessibilityNativeHandle* getNativeImplementation() const; /** @internal */ std::type_index getTypeIndex() const { return typeIndex; } - /** @internal */ - static void clearCurrentlyFocusedHandler() { currentlyFocusedHandler = nullptr; } - - /** @internal - - The following functions provide the means to associate JUCE Components with OS specific - types that provide their own accessibility mechanisms. This way accessibility navigation - can move from a JUCE Component to a native, embedded window and back. - - These functions assume that the concrete types behind the void* are - - Windows: HWND - - MacOS: NSView* - - iOS: UIView* - - Android: GlobalRef that points to an android.view.View - */ - static void* getNativeChildForComponent (Component& component); - /** @internal */ - static void setNativeChildForComponent (Component& component, void* nativeChild); - /** @internal */ - static Component* getComponentForNativeChild (void* nativeChild); private: //============================================================================== diff --git a/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp b/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp index bd342bdf..5e0844ba 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp @@ -83,12 +83,16 @@ bool JUCEApplication::perform (const InvocationInfo& info) } //============================================================================== +#if JUCE_MAC + extern void juce_initialiseMacMainMenu(); +#endif + bool JUCEApplication::initialiseApp() { if (JUCEApplicationBase::initialiseApp()) { #if JUCE_MAC - initialiseMacMainMenu(); // (needs to get the app's name) + juce_initialiseMacMainMenu(); // (needs to get the app's name) #endif return true; diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp index 0dd5ce9f..b66b88c2 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp @@ -715,9 +715,100 @@ void Button::repeatTimerCallback() } } +//============================================================================== +class ButtonAccessibilityHandler : public AccessibilityHandler +{ +public: + explicit ButtonAccessibilityHandler (Button& buttonToWrap, AccessibilityRole roleIn) + : AccessibilityHandler (buttonToWrap, + isRadioButton (buttonToWrap) ? AccessibilityRole::radioButton : roleIn, + getAccessibilityActions (buttonToWrap), + getAccessibilityInterfaces (buttonToWrap)), + button (buttonToWrap) + { + } + + AccessibleState getCurrentState() const override + { + auto state = AccessibilityHandler::getCurrentState(); + + if (button.isToggleable()) + { + state = state.withCheckable(); + + if (button.getToggleState()) + state = state.withChecked(); + } + + return state; + } + + String getTitle() const override + { + auto title = AccessibilityHandler::getTitle(); + + if (title.isEmpty()) + return button.getButtonText(); + + return title; + } + + String getHelp() const override { return button.getTooltip(); } + +private: + class ButtonValueInterface : public AccessibilityTextValueInterface + { + public: + explicit ButtonValueInterface (Button& buttonToWrap) + : button (buttonToWrap) + { + } + + bool isReadOnly() const override { return true; } + String getCurrentValueAsString() const override { return button.getToggleState() ? "On" : "Off"; } + void setValueAsString (const String&) override {} + + private: + Button& button; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonValueInterface) + }; + + static bool isRadioButton (const Button& button) noexcept + { + return button.getRadioGroupId() != 0; + } + + static AccessibilityActions getAccessibilityActions (Button& button) + { + auto actions = AccessibilityActions().addAction (AccessibilityActionType::press, + [&button] { button.triggerClick(); }); + + if (button.isToggleable()) + actions = actions.addAction (AccessibilityActionType::toggle, + [&button] { button.setToggleState (! button.getToggleState(), sendNotification); }); + + return actions; + } + + static Interfaces getAccessibilityInterfaces (Button& button) + { + if (button.isToggleable()) + return { std::make_unique (button) }; + + return {}; + } + + Button& button; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonAccessibilityHandler) +}; + std::unique_ptr Button::createAccessibilityHandler() { - return std::make_unique (*this, AccessibilityRole::button); + return std::make_unique (*this, AccessibilityRole::button); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h index e9878fb0..ce849023 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h @@ -495,8 +495,6 @@ class JUCE_API Button : public Component, void focusLost (FocusChangeType) override; /** @internal */ void enablementChanged() override; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; private: //============================================================================== @@ -524,6 +522,7 @@ class JUCE_API Button : public Component, bool triggerOnMouseDown = false; bool generateTooltip = false; + std::unique_ptr createAccessibilityHandler() override; void checkToggleableState (bool wasToggleable); void repeatTimerCallback(); diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp index 962e6949..a5f5440f 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp @@ -123,7 +123,7 @@ void HyperlinkButton::paintButton (Graphics& g, std::unique_ptr HyperlinkButton::createAccessibilityHandler() { - return std::make_unique (*this, AccessibilityRole::hyperlink); + return std::make_unique (*this, AccessibilityRole::hyperlink); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h index de346033..553464fa 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h @@ -101,9 +101,6 @@ class JUCE_API HyperlinkButton : public Button /** Returns the type of justification, as set in setJustificationType(). */ Justification getJustificationType() const noexcept { return justification; } - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; - protected: //============================================================================== /** @internal */ @@ -114,6 +111,8 @@ class JUCE_API HyperlinkButton : public Button void paintButton (Graphics&, bool, bool) override; private: + std::unique_ptr createAccessibilityHandler() override; + //============================================================================== using Button::clicked; Font getFontToUse() const; diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp index 74fe6d34..0461ea12 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp @@ -59,7 +59,7 @@ void ToggleButton::colourChanged() std::unique_ptr ToggleButton::createAccessibilityHandler() { - return std::make_unique (*this, AccessibilityRole::toggleButton); + return std::make_unique (*this, AccessibilityRole::toggleButton); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.h index df5ffcc3..d01488c7 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.h @@ -76,9 +76,6 @@ class JUCE_API ToggleButton : public Button tickDisabledColourId = 0x1006503 /**< The colour to use for the disabled tick mark and/or outline. */ }; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; - protected: //============================================================================== /** @internal */ @@ -87,6 +84,8 @@ class JUCE_API ToggleButton : public Button void colourChanged() override; private: + std::unique_ptr createAccessibilityHandler() override; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToggleButton) }; diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp index db6f1bbb..bfa089b3 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp @@ -270,7 +270,7 @@ ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget( // getting a bit desperate now: try all desktop comps.. for (int i = desktop.getNumComponents(); --i >= 0;) if (auto* component = desktop.getComponent (i)) - if (detail::WindowingHelpers::isForegroundOrEmbeddedProcess (component)) + if (isForegroundOrEmbeddedProcess (component)) if (auto* peer = component->getPeer()) if (auto* target = findTargetForComponent (peer->getLastFocusedSubcomponent())) return target; diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp index 9f720c79..9f8d9d74 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp @@ -23,11 +23,6 @@ ============================================================================== */ -#define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN \ - jassert ((MessageManager::getInstanceWithoutCreating() != nullptr \ - && MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager()) \ - || getPeer() == nullptr); - namespace juce { @@ -163,6 +158,343 @@ class Component::MouseListenerList JUCE_DECLARE_NON_COPYABLE (MouseListenerList) }; +//============================================================================== +struct FocusRestorer +{ + FocusRestorer() : lastFocus (Component::getCurrentlyFocusedComponent()) {} + + ~FocusRestorer() + { + if (lastFocus != nullptr + && lastFocus->isShowing() + && ! lastFocus->isCurrentlyBlockedByAnotherModalComponent()) + lastFocus->grabKeyboardFocus(); + } + + WeakReference lastFocus; + + JUCE_DECLARE_NON_COPYABLE (FocusRestorer) +}; + +//============================================================================== +struct ScalingHelpers +{ + template + static PointOrRect unscaledScreenPosToScaled (float scale, PointOrRect pos) noexcept + { + return scale != 1.0f ? pos / scale : pos; + } + + template + static PointOrRect scaledScreenPosToUnscaled (float scale, PointOrRect pos) noexcept + { + return scale != 1.0f ? pos * scale : pos; + } + + // For these, we need to avoid getSmallestIntegerContainer being used, which causes + // judder when moving windows + static Rectangle unscaledScreenPosToScaled (float scale, Rectangle pos) noexcept + { + return scale != 1.0f ? Rectangle (roundToInt ((float) pos.getX() / scale), + roundToInt ((float) pos.getY() / scale), + roundToInt ((float) pos.getWidth() / scale), + roundToInt ((float) pos.getHeight() / scale)) : pos; + } + + static Rectangle scaledScreenPosToUnscaled (float scale, Rectangle pos) noexcept + { + return scale != 1.0f ? Rectangle (roundToInt ((float) pos.getX() * scale), + roundToInt ((float) pos.getY() * scale), + roundToInt ((float) pos.getWidth() * scale), + roundToInt ((float) pos.getHeight() * scale)) : pos; + } + + static Rectangle unscaledScreenPosToScaled (float scale, Rectangle pos) noexcept + { + return scale != 1.0f ? Rectangle (pos.getX() / scale, + pos.getY() / scale, + pos.getWidth() / scale, + pos.getHeight() / scale) : pos; + } + + static Rectangle scaledScreenPosToUnscaled (float scale, Rectangle pos) noexcept + { + return scale != 1.0f ? Rectangle (pos.getX() * scale, + pos.getY() * scale, + pos.getWidth() * scale, + pos.getHeight() * scale) : pos; + } + + template + static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept + { + return unscaledScreenPosToScaled (Desktop::getInstance().getGlobalScaleFactor(), pos); + } + + template + static PointOrRect scaledScreenPosToUnscaled (PointOrRect pos) noexcept + { + return scaledScreenPosToUnscaled (Desktop::getInstance().getGlobalScaleFactor(), pos); + } + + template + static PointOrRect unscaledScreenPosToScaled (const Component& comp, PointOrRect pos) noexcept + { + return unscaledScreenPosToScaled (comp.getDesktopScaleFactor(), pos); + } + + template + static PointOrRect scaledScreenPosToUnscaled (const Component& comp, PointOrRect pos) noexcept + { + return scaledScreenPosToUnscaled (comp.getDesktopScaleFactor(), pos); + } + + static Point addPosition (Point p, const Component& c) noexcept { return p + c.getPosition(); } + static Rectangle addPosition (Rectangle p, const Component& c) noexcept { return p + c.getPosition(); } + static Point addPosition (Point p, const Component& c) noexcept { return p + c.getPosition().toFloat(); } + static Rectangle addPosition (Rectangle p, const Component& c) noexcept { return p + c.getPosition().toFloat(); } + static Point subtractPosition (Point p, const Component& c) noexcept { return p - c.getPosition(); } + static Rectangle subtractPosition (Rectangle p, const Component& c) noexcept { return p - c.getPosition(); } + static Point subtractPosition (Point p, const Component& c) noexcept { return p - c.getPosition().toFloat(); } + static Rectangle subtractPosition (Rectangle p, const Component& c) noexcept { return p - c.getPosition().toFloat(); } + + static Point screenPosToLocalPos (Component& comp, Point pos) + { + if (auto* peer = comp.getPeer()) + { + pos = peer->globalToLocal (pos); + auto& peerComp = peer->getComponent(); + return comp.getLocalPoint (&peerComp, unscaledScreenPosToScaled (peerComp, pos)); + } + + return comp.getLocalPoint (nullptr, unscaledScreenPosToScaled (comp, pos)); + } +}; + +static const char colourPropertyPrefix[] = "jcclr_"; + +//============================================================================== +struct Component::ComponentHelpers +{ + #if JUCE_MODAL_LOOPS_PERMITTED + static void* runModalLoopCallback (void* userData) + { + return (void*) (pointer_sized_int) static_cast (userData)->runModalLoop(); + } + #endif + + static Identifier getColourPropertyID (int colourID) + { + char buffer[32]; + auto* end = buffer + numElementsInArray (buffer) - 1; + auto* t = end; + *t = 0; + + for (auto v = (uint32) colourID;;) + { + *--t = "0123456789abcdef" [v & 15]; + v >>= 4; + + if (v == 0) + break; + } + + for (int i = (int) sizeof (colourPropertyPrefix) - 1; --i >= 0;) + *--t = colourPropertyPrefix[i]; + + return t; + } + + //============================================================================== + static bool hitTest (Component& comp, Point localPoint) + { + const auto intPoint = localPoint.roundToInt(); + return Rectangle { comp.getWidth(), comp.getHeight() }.contains (intPoint) + && comp.hitTest (intPoint.x, intPoint.y); + } + + // converts an unscaled position within a peer to the local position within that peer's component + template + static PointOrRect rawPeerPositionToLocal (const Component& comp, PointOrRect pos) noexcept + { + if (comp.isTransformed()) + pos = pos.transformedBy (comp.getTransform().inverted()); + + return ScalingHelpers::unscaledScreenPosToScaled (comp, pos); + } + + // converts a position within a peer's component to the unscaled position within the peer + template + static PointOrRect localPositionToRawPeerPos (const Component& comp, PointOrRect pos) noexcept + { + if (comp.isTransformed()) + pos = pos.transformedBy (comp.getTransform()); + + return ScalingHelpers::scaledScreenPosToUnscaled (comp, pos); + } + + template + static PointOrRect convertFromParentSpace (const Component& comp, const PointOrRect pointInParentSpace) + { + const auto transformed = comp.affineTransform != nullptr ? pointInParentSpace.transformedBy (comp.affineTransform->inverted()) + : pointInParentSpace; + + if (comp.isOnDesktop()) + { + if (auto* peer = comp.getPeer()) + return ScalingHelpers::unscaledScreenPosToScaled (comp, peer->globalToLocal (ScalingHelpers::scaledScreenPosToUnscaled (transformed))); + + jassertfalse; + return transformed; + } + + if (comp.getParentComponent() == nullptr) + return ScalingHelpers::subtractPosition (ScalingHelpers::unscaledScreenPosToScaled (comp, ScalingHelpers::scaledScreenPosToUnscaled (transformed)), comp); + + return ScalingHelpers::subtractPosition (transformed, comp); + } + + template + static PointOrRect convertToParentSpace (const Component& comp, const PointOrRect pointInLocalSpace) + { + const auto preTransform = [&] + { + if (comp.isOnDesktop()) + { + if (auto* peer = comp.getPeer()) + return ScalingHelpers::unscaledScreenPosToScaled (peer->localToGlobal (ScalingHelpers::scaledScreenPosToUnscaled (comp, pointInLocalSpace))); + + jassertfalse; + return pointInLocalSpace; + } + + if (comp.getParentComponent() == nullptr) + return ScalingHelpers::unscaledScreenPosToScaled (ScalingHelpers::scaledScreenPosToUnscaled (comp, ScalingHelpers::addPosition (pointInLocalSpace, comp))); + + return ScalingHelpers::addPosition (pointInLocalSpace, comp); + }(); + + return comp.affineTransform != nullptr ? preTransform.transformedBy (*comp.affineTransform) + : preTransform; + } + + template + static PointOrRect convertFromDistantParentSpace (const Component* parent, const Component& target, PointOrRect coordInParent) + { + auto* directParent = target.getParentComponent(); + jassert (directParent != nullptr); + + if (directParent == parent) + return convertFromParentSpace (target, coordInParent); + + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011) + return convertFromParentSpace (target, convertFromDistantParentSpace (parent, *directParent, coordInParent)); + JUCE_END_IGNORE_WARNINGS_MSVC + } + + template + static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) + { + while (source != nullptr) + { + if (source == target) + return p; + + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011) + + if (source->isParentOf (target)) + return convertFromDistantParentSpace (source, *target, p); + + JUCE_END_IGNORE_WARNINGS_MSVC + + p = convertToParentSpace (*source, p); + source = source->getParentComponent(); + } + + jassert (source == nullptr); + if (target == nullptr) + return p; + + auto* topLevelComp = target->getTopLevelComponent(); + + p = convertFromParentSpace (*topLevelComp, p); + + if (topLevelComp == target) + return p; + + return convertFromDistantParentSpace (topLevelComp, *target, p); + } + + static bool clipObscuredRegions (const Component& comp, Graphics& g, + const Rectangle clipRect, Point delta) + { + bool wasClipped = false; + + for (int i = comp.childComponentList.size(); --i >= 0;) + { + auto& child = *comp.childComponentList.getUnchecked(i); + + if (child.isVisible() && ! child.isTransformed()) + { + auto newClip = clipRect.getIntersection (child.boundsRelativeToParent); + + if (! newClip.isEmpty()) + { + if (child.isOpaque() && child.componentTransparency == 0) + { + g.excludeClipRegion (newClip + delta); + wasClipped = true; + } + else + { + auto childPos = child.getPosition(); + + if (clipObscuredRegions (child, g, newClip - childPos, childPos + delta)) + wasClipped = true; + } + } + } + } + + return wasClipped; + } + + static Rectangle getParentOrMainMonitorBounds (const Component& comp) + { + if (auto* p = comp.getParentComponent()) + return p->getLocalBounds(); + + return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea; + } + + static void releaseAllCachedImageResources (Component& c) + { + if (auto* cached = c.getCachedComponentImage()) + cached->releaseResources(); + + for (auto* child : c.childComponentList) + releaseAllCachedImageResources (*child); + } + + //============================================================================== + static bool modalWouldBlockComponent (const Component& maybeBlocked, Component* modal) + { + return modal != nullptr + && modal != &maybeBlocked + && ! modal->isParentOf (&maybeBlocked) + && ! modal->canModalEventBeSentToComponent (&maybeBlocked); + } + + template + static void sendMouseEventToComponentsThatAreBlockedByModal (Component& modal, Function&& function) + { + for (auto& ms : Desktop::getInstance().getMouseSources()) + if (auto* c = ms.getComponentUnderMouse()) + if (modalWouldBlockComponent (*c, &modal)) + (c->*function) (ms, ScalingHelpers::screenPosToLocalPos (*c, ms.getScreenPosition()), Time::getCurrentTime()); + } +}; + //============================================================================== Component::Component() noexcept : componentFlags (0) @@ -242,7 +574,7 @@ void Component::setVisible (bool shouldBeVisible) if (! shouldBeVisible) { - detail::ComponentHelpers::releaseAllCachedImageResources (*this); + ComponentHelpers::releaseAllCachedImageResources (*this); if (hasKeyboardFocus (true)) { @@ -332,8 +664,8 @@ void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo) jmax (1, getHeight())); #endif - const auto unscaledPosition = detail::ScalingHelpers::scaledScreenPosToUnscaled (getScreenPosition()); - const auto topLeft = detail::ScalingHelpers::unscaledScreenPosToScaled (*this, unscaledPosition); + const auto unscaledPosition = ScalingHelpers::scaledScreenPosToUnscaled (getScreenPosition()); + const auto topLeft = ScalingHelpers::unscaledScreenPosToScaled (*this, unscaledPosition); bool wasFullscreen = false; bool wasMinimised = false; @@ -415,7 +747,7 @@ void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo) internalHierarchyChanged(); if (auto* handler = getAccessibilityHandler()) - detail::AccessibilityHelpers::notifyAccessibilityEvent (*handler, detail::AccessibilityHelpers::Event::windowOpened); + notifyAccessibilityEventInternal (*handler, InternalAccessibilityEvent::windowOpened); } } } @@ -429,9 +761,9 @@ void Component::removeFromDesktop() if (flags.hasHeavyweightPeerFlag) { if (auto* handler = getAccessibilityHandler()) - detail::AccessibilityHelpers::notifyAccessibilityEvent (*handler, detail::AccessibilityHelpers::Event::windowClosed); + notifyAccessibilityEventInternal (*handler, InternalAccessibilityEvent::windowClosed); - detail::ComponentHelpers::releaseAllCachedImageResources (*this); + ComponentHelpers::releaseAllCachedImageResources (*this); auto* peer = ComponentPeer::getPeerFor (this); jassert (peer != nullptr); @@ -783,15 +1115,15 @@ int Component::getScreenY() const { return getScreenPositi Point Component::getScreenPosition() const { return localPointToGlobal (Point()); } Rectangle Component::getScreenBounds() const { return localAreaToGlobal (getLocalBounds()); } -Point Component::getLocalPoint (const Component* source, Point point) const { return detail::ComponentHelpers::convertCoordinate (this, source, point); } -Point Component::getLocalPoint (const Component* source, Point point) const { return detail::ComponentHelpers::convertCoordinate (this, source, point); } -Rectangle Component::getLocalArea (const Component* source, Rectangle area) const { return detail::ComponentHelpers::convertCoordinate (this, source, area); } -Rectangle Component::getLocalArea (const Component* source, Rectangle area) const { return detail::ComponentHelpers::convertCoordinate (this, source, area); } +Point Component::getLocalPoint (const Component* source, Point point) const { return ComponentHelpers::convertCoordinate (this, source, point); } +Point Component::getLocalPoint (const Component* source, Point point) const { return ComponentHelpers::convertCoordinate (this, source, point); } +Rectangle Component::getLocalArea (const Component* source, Rectangle area) const { return ComponentHelpers::convertCoordinate (this, source, area); } +Rectangle Component::getLocalArea (const Component* source, Rectangle area) const { return ComponentHelpers::convertCoordinate (this, source, area); } -Point Component::localPointToGlobal (Point point) const { return detail::ComponentHelpers::convertCoordinate (nullptr, this, point); } -Point Component::localPointToGlobal (Point point) const { return detail::ComponentHelpers::convertCoordinate (nullptr, this, point); } -Rectangle Component::localAreaToGlobal (Rectangle area) const { return detail::ComponentHelpers::convertCoordinate (nullptr, this, area); } -Rectangle Component::localAreaToGlobal (Rectangle area) const { return detail::ComponentHelpers::convertCoordinate (nullptr, this, area); } +Point Component::localPointToGlobal (Point point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); } +Point Component::localPointToGlobal (Point point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); } +Rectangle Component::localAreaToGlobal (Rectangle area) const { return ComponentHelpers::convertCoordinate (nullptr, this, area); } +Rectangle Component::localAreaToGlobal (Rectangle area) const { return ComponentHelpers::convertCoordinate (nullptr, this, area); } //============================================================================== void Component::setBounds (int x, int y, int w, int h) @@ -906,7 +1238,7 @@ void Component::sendMovedResizedMessages (bool wasMoved, bool wasResized) if ((wasMoved || wasResized) && ! checker.shouldBailOut()) if (auto* handler = getAccessibilityHandler()) - detail::AccessibilityHelpers::notifyAccessibilityEvent (*handler, detail::AccessibilityHelpers::Event::elementMovedOrResized); + notifyAccessibilityEventInternal (*handler, InternalAccessibilityEvent::elementMovedOrResized); } void Component::setSize (int w, int h) { setBounds (getX(), getY(), w, h); } @@ -939,7 +1271,7 @@ void Component::setBoundsRelative (float x, float y, float w, float h) void Component::centreWithSize (int width, int height) { - auto parentArea = detail::ComponentHelpers::getParentOrMainMonitorBounds (*this) + auto parentArea = ComponentHelpers::getParentOrMainMonitorBounds (*this) .transformedBy (getTransform().inverted()); setBounds (parentArea.getCentreX() - width / 2, @@ -949,7 +1281,7 @@ void Component::centreWithSize (int width, int height) void Component::setBoundsInset (BorderSize borders) { - setBounds (borders.subtractedFrom (detail::ComponentHelpers::getParentOrMainMonitorBounds (*this))); + setBounds (borders.subtractedFrom (ComponentHelpers::getParentOrMainMonitorBounds (*this))); } void Component::setBoundsToFit (Rectangle targetArea, Justification justification, bool onlyReduceInSize) @@ -1059,7 +1391,7 @@ bool Component::hitTest (int x, int y) auto& child = *childComponentList.getUnchecked (i); if (child.isVisible() - && detail::ComponentHelpers::hitTest (child, detail::ComponentHelpers::convertFromParentSpace (child, Point (x, y).toFloat()))) + && ComponentHelpers::hitTest (child, ComponentHelpers::convertFromParentSpace (child, Point (x, y).toFloat()))) return true; } } @@ -1088,14 +1420,14 @@ bool Component::contains (Point point) bool Component::contains (Point point) { - if (detail::ComponentHelpers::hitTest (*this, point)) + if (ComponentHelpers::hitTest (*this, point)) { if (parentComponent != nullptr) - return parentComponent->contains (detail::ComponentHelpers::convertToParentSpace (*this, point)); + return parentComponent->contains (ComponentHelpers::convertToParentSpace (*this, point)); if (flags.hasHeavyweightPeerFlag) if (auto* peer = getPeer()) - return peer->contains (detail::ComponentHelpers::localPositionToRawPeerPos (*this, point).roundToInt(), true); + return peer->contains (ComponentHelpers::localPositionToRawPeerPos (*this, point).roundToInt(), true); } return false; @@ -1124,13 +1456,13 @@ Component* Component::getComponentAt (Point position) Component* Component::getComponentAt (Point position) { - if (flags.visibleFlag && detail::ComponentHelpers::hitTest (*this, position)) + if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position)) { for (int i = childComponentList.size(); --i >= 0;) { auto* child = childComponentList.getUnchecked (i); - child = child->getComponentAt (detail::ComponentHelpers::convertFromParentSpace (*child, position)); + child = child->getComponentAt (ComponentHelpers::convertFromParentSpace (*child, position)); if (child != nullptr) return child; @@ -1247,7 +1579,7 @@ Component* Component::removeChildComponent (int index, bool sendParentEvents, bo childComponentList.remove (index); child->parentComponent = nullptr; - detail::ComponentHelpers::releaseAllCachedImageResources (*child); + ComponentHelpers::releaseAllCachedImageResources (*child); // (NB: there are obscure situations where child->isShowing() = false, but it still has the focus) if (child->hasKeyboardFocus (true)) @@ -1400,7 +1732,7 @@ int Component::runModalLoop() { // use a callback so this can be called from non-gui threads return (int) (pointer_sized_int) MessageManager::getInstance() - ->callFunctionOnMessageThread (&detail::ComponentHelpers::runModalLoopCallback, this); + ->callFunctionOnMessageThread (&ComponentHelpers::runModalLoopCallback, this); } if (! isCurrentlyModal (false)) @@ -1426,7 +1758,7 @@ void Component::enterModalState (bool shouldTakeKeyboardFocus, // While this component is in modal state it may block other components from receiving // mouseExit events. To keep mouseEnter and mouseExit calls balanced on these components, // we must manually force the mouse to "leave" blocked components. - detail::ComponentHelpers::sendMouseEventToComponentsThatAreBlockedByModal (*this, &Component::internalMouseExit); + ComponentHelpers::sendMouseEventToComponentsThatAreBlockedByModal (*this, &Component::internalMouseExit); if (safeReference == nullptr) { @@ -1467,7 +1799,7 @@ void Component::exitModalState (int returnValue) // mouseEnter events. To keep mouseEnter and mouseExit calls balanced on these components, // we must manually force the mouse to "enter" blocked components. if (deletionChecker != nullptr) - detail::ComponentHelpers::sendMouseEventToComponentsThatAreBlockedByModal (*deletionChecker, &Component::internalMouseEnter); + ComponentHelpers::sendMouseEventToComponentsThatAreBlockedByModal (*deletionChecker, &Component::internalMouseEnter); } else { @@ -1490,7 +1822,7 @@ bool Component::isCurrentlyModal (bool onlyConsiderForemostModalComponent) const bool Component::isCurrentlyBlockedByAnotherModalComponent() const { - return detail::ComponentHelpers::modalWouldBlockComponent (*this, getCurrentlyModalComponent()); + return ComponentHelpers::modalWouldBlockComponent (*this, getCurrentlyModalComponent()); } int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() noexcept @@ -1591,7 +1923,7 @@ void Component::repaint (Rectangle area) void Component::repaintParent() { if (parentComponent != nullptr) - parentComponent->internalRepaint (detail::ComponentHelpers::convertToParentSpace (*this, getLocalBounds())); + parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, getLocalBounds())); } void Component::internalRepaint (Rectangle area) @@ -1633,7 +1965,7 @@ void Component::internalRepaintUnchecked (Rectangle area, bool isEntireComp else { if (parentComponent != nullptr) - parentComponent->internalRepaint (detail::ComponentHelpers::convertToParentSpace (*this, area)); + parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, area)); } } } @@ -1674,7 +2006,7 @@ void Component::paintComponentAndChildren (Graphics& g) { Graphics::ScopedSaveState ss (g); - if (! (detail::ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, {}) && g.isClipEmpty())) + if (! (ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, {}) && g.isClipEmpty())) paint (g); } @@ -1877,7 +2209,7 @@ void Component::sendLookAndFeelChange() Colour Component::findColour (int colourID, bool inheritFromParent) const { - if (auto* v = properties.getVarPointer (detail::ComponentHelpers::getColourPropertyID (colourID))) + if (auto* v = properties.getVarPointer (ComponentHelpers::getColourPropertyID (colourID))) return Colour ((uint32) static_cast (*v)); if (inheritFromParent && parentComponent != nullptr @@ -1889,18 +2221,18 @@ Colour Component::findColour (int colourID, bool inheritFromParent) const bool Component::isColourSpecified (int colourID) const { - return properties.contains (detail::ComponentHelpers::getColourPropertyID (colourID)); + return properties.contains (ComponentHelpers::getColourPropertyID (colourID)); } void Component::removeColour (int colourID) { - if (properties.remove (detail::ComponentHelpers::getColourPropertyID (colourID))) + if (properties.remove (ComponentHelpers::getColourPropertyID (colourID))) colourChanged(); } void Component::setColour (int colourID, Colour colour) { - if (properties.set (detail::ComponentHelpers::getColourPropertyID (colourID), (int) colour.getARGB())) + if (properties.set (ComponentHelpers::getColourPropertyID (colourID), (int) colour.getARGB())) colourChanged(); } @@ -1912,7 +2244,7 @@ void Component::copyAllExplicitColoursTo (Component& target) const { auto name = properties.getName(i); - if (name.toString().startsWith (detail::colourPropertyPrefix)) + if (name.toString().startsWith (colourPropertyPrefix)) if (target.properties.set (name, properties [name])) changed = true; } @@ -2073,7 +2405,7 @@ void Component::internalMouseEnter (MouseInputSource source, Point relati repaint(); const auto me = makeMouseEvent (source, - detail::PointerState().withPosition (relativePos), + PointerState().withPosition (relativePos), source.getCurrentModifiers(), this, this, @@ -2110,7 +2442,7 @@ void Component::internalMouseExit (MouseInputSource source, Point relativ flags.cachedMouseInsideComponent = false; const auto me = makeMouseEvent (source, - detail::PointerState().withPosition (relativePos), + PointerState().withPosition (relativePos), source.getCurrentModifiers(), this, this, @@ -2130,9 +2462,7 @@ void Component::internalMouseExit (MouseInputSource source, Point relativ MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseExit); } -void Component::internalMouseDown (MouseInputSource source, - const detail::PointerState& relativePointerState, - Time time) +void Component::internalMouseDown (MouseInputSource source, const PointerState& relativePointerState, Time time) { auto& desktop = Desktop::getInstance(); @@ -2182,7 +2512,7 @@ void Component::internalMouseDown (MouseInputSource source, if (! flags.dontFocusOnMouseClickFlag) { - grabKeyboardFocusInternal (focusChangedByMouseClick, true, FocusChangeDirection::unknown); + grabKeyboardFocusInternal (focusChangedByMouseClick, true); if (checker.shouldBailOut()) return; @@ -2201,10 +2531,7 @@ void Component::internalMouseDown (MouseInputSource source, MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseDown); } -void Component::internalMouseUp (MouseInputSource source, - const detail::PointerState& relativePointerState, - Time time, - const ModifierKeys oldModifiers) +void Component::internalMouseUp (MouseInputSource source, const PointerState& relativePointerState, Time time, const ModifierKeys oldModifiers) { if (flags.mouseDownWasBlocked && isCurrentlyBlockedByAnotherModalComponent()) return; @@ -2252,7 +2579,7 @@ void Component::internalMouseUp (MouseInputSource source, } } -void Component::internalMouseDrag (MouseInputSource source, const detail::PointerState& relativePointerState, Time time) +void Component::internalMouseDrag (MouseInputSource source, const PointerState& relativePointerState, Time time) { if (! isCurrentlyBlockedByAnotherModalComponent()) { @@ -2291,7 +2618,7 @@ void Component::internalMouseMove (MouseInputSource source, Point relativ else { const auto me = makeMouseEvent (source, - detail::PointerState().withPosition (relativePos), + PointerState().withPosition (relativePos), source.getCurrentModifiers(), this, this, @@ -2319,7 +2646,7 @@ void Component::internalMouseWheel (MouseInputSource source, Point relati auto& desktop = Desktop::getInstance(); const auto me = makeMouseEvent (source, - detail::PointerState().withPosition (relativePos), + PointerState().withPosition (relativePos), source.getCurrentModifiers(), this, this, @@ -2356,7 +2683,7 @@ void Component::internalMagnifyGesture (MouseInputSource source, Point re auto& desktop = Desktop::getInstance(); const auto me = makeMouseEvent (source, - detail::PointerState().withPosition (relativePos), + PointerState().withPosition (relativePos), source.getCurrentModifiers(), this, this, @@ -2435,20 +2762,17 @@ void Component::internalBroughtToFront() //============================================================================== void Component::focusGained (FocusChangeType) {} -void Component::focusGainedWithDirection (FocusChangeType, FocusChangeDirection) {} void Component::focusLost (FocusChangeType) {} void Component::focusOfChildComponentChanged (FocusChangeType) {} void Component::internalKeyboardFocusGain (FocusChangeType cause) { - internalKeyboardFocusGain (cause, WeakReference (this), FocusChangeDirection::unknown); + internalKeyboardFocusGain (cause, WeakReference (this)); } void Component::internalKeyboardFocusGain (FocusChangeType cause, - const WeakReference& safePointer, - FocusChangeDirection direction) + const WeakReference& safePointer) { - focusGainedWithDirection (cause, direction); focusGained (cause); if (safePointer == nullptr) @@ -2560,16 +2884,16 @@ Component* Component::findKeyboardFocusContainer() const return findContainer (this, &Component::isKeyboardFocusContainer); } -static const Identifier explicitFocusOrderId ("_jexfo"); +static const Identifier juce_explicitFocusOrderId ("_jexfo"); int Component::getExplicitFocusOrder() const { - return properties [explicitFocusOrderId]; + return properties [juce_explicitFocusOrderId]; } void Component::setExplicitFocusOrder (int newFocusOrderIndex) { - properties.set (explicitFocusOrderId, newFocusOrderIndex); + properties.set (juce_explicitFocusOrderId, newFocusOrderIndex); } std::unique_ptr Component::createFocusTraverser() @@ -2588,7 +2912,7 @@ std::unique_ptr Component::createKeyboardFocusTraverser() return parentComponent->createKeyboardFocusTraverser(); } -void Component::takeKeyboardFocus (FocusChangeType cause, FocusChangeDirection direction) +void Component::takeKeyboardFocus (FocusChangeType cause) { if (currentlyFocusedComponent == this) return; @@ -2617,11 +2941,11 @@ void Component::takeKeyboardFocus (FocusChangeType cause, FocusChangeDirection d componentLosingFocus->internalKeyboardFocusLoss (cause); if (currentlyFocusedComponent == this) - internalKeyboardFocusGain (cause, safePointer, direction); + internalKeyboardFocusGain (cause, safePointer); } } -void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryParent, FocusChangeDirection direction) +void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryParent) { if (! isShowing()) return; @@ -2629,7 +2953,7 @@ void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryPar if (flags.wantsKeyboardFocusFlag && (isEnabled() || parentComponent == nullptr)) { - takeKeyboardFocus (cause, direction); + takeKeyboardFocus (cause); return; } @@ -2640,7 +2964,7 @@ void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryPar { if (auto* defaultComp = traverser->getDefaultComponent (this)) { - defaultComp->grabKeyboardFocusInternal (cause, false, direction); + defaultComp->grabKeyboardFocusInternal (cause, false); return; } } @@ -2648,7 +2972,7 @@ void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryPar // if no children want it and we're allowed to try our parent comp, // then pass up to parent, which will try our siblings. if (canTryParent && parentComponent != nullptr) - parentComponent->grabKeyboardFocusInternal (cause, true, direction); + parentComponent->grabKeyboardFocusInternal (cause, true); } void Component::grabKeyboardFocus() @@ -2657,7 +2981,7 @@ void Component::grabKeyboardFocus() // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED - grabKeyboardFocusInternal (focusChangedDirectly, true, FocusChangeDirection::unknown); + grabKeyboardFocusInternal (focusChangedDirectly, true); // A component can only be focused when it's actually on the screen! // If this fails then you're probably trying to grab the focus before you've @@ -2733,10 +3057,7 @@ void Component::moveKeyboardFocusToSibling (bool moveToNext) return; } - nextComp->grabKeyboardFocusInternal (focusChangedByTabKey, - true, - moveToNext ? FocusChangeDirection::forward - : FocusChangeDirection::backward); + nextComp->grabKeyboardFocusInternal (focusChangedByTabKey, true); return; } } @@ -2979,7 +3300,7 @@ AccessibilityHandler* Component::getAccessibilityHandler() // created, the if() predicate above should evaluate to false on recursive calls, // terminating the recursion. if (accessibilityHandler != nullptr) - detail::AccessibilityHelpers::notifyAccessibilityEvent (*accessibilityHandler, detail::AccessibilityHelpers::Event::elementCreated); + notifyAccessibilityEventInternal (*accessibilityHandler, InternalAccessibilityEvent::elementCreated); else jassertfalse; // createAccessibilityHandler must return non-null } diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h index e5ca03d9..9f730496 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h @@ -1168,14 +1168,14 @@ class JUCE_API Component : public MouseListener Calling this method will also invoke the sendLookAndFeelChange() method. - @see getLookAndFeel, lookAndFeelChanged, sendLookAndFeelChange + @see getLookAndFeel, lookAndFeelChanged */ void setLookAndFeel (LookAndFeel* newLookAndFeel); /** Called to let the component react to a change in the look-and-feel setting. - When the look-and-feel is changed for a component, this method, repaint(), and - colourChanged() are called on the original component and all its children recursively. + When the look-and-feel is changed for a component, this will be called in + all its child components, recursively. It can also be triggered manually by the sendLookAndFeelChange() method, in case an application uses a LookAndFeel class that might have changed internally. @@ -1184,8 +1184,10 @@ class JUCE_API Component : public MouseListener */ virtual void lookAndFeelChanged(); - /** Calls the methods repaint(), lookAndFeelChanged(), and colourChanged() in this - component and all its children recursively. + /** Calls the lookAndFeelChanged() method in this component and all its children. + + This will recurse through the children and their children, calling lookAndFeelChanged() + on them all. @see lookAndFeelChanged */ @@ -1891,28 +1893,11 @@ class JUCE_API Component : public MouseListener focusChangedDirectly /**< Means that the focus was changed by a call to grabKeyboardFocus(). */ }; - /** Enumeration used by the focusGainedWithDirection() method. */ - enum class FocusChangeDirection - { - unknown, - forward, - backward - }; - /** Called to indicate that this component has just acquired the keyboard focus. @see focusLost, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus */ virtual void focusGained (FocusChangeType cause); - /** Called to indicate that this component has just acquired the keyboard focus. - - This function is called every time focusGained() is called but it has an additional change - direction parameter. - - @see focusLost, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus - */ - virtual void focusGainedWithDirection (FocusChangeType cause, FocusChangeDirection direction); - /** Called to indicate that this component has just lost the keyboard focus. @see focusGained, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus */ @@ -2128,14 +2113,13 @@ class JUCE_API Component : public MouseListener The callback is an optional object which will receive a callback when the modal component loses its modal status, either by being hidden or when exitModalState() is called. If you pass an object in here, the system will take care of deleting it - later, after making the callback. + later, after making the callback If deleteWhenDismissed is true, then when it is dismissed, the component will be deleted and then the callback will be called. (This will safely handle the situation where the component is deleted before its exitModalState() method is called). - @see exitModalState, runModalLoop, ModalComponentManager::attachCallback, - ModalCallbackFunction + @see exitModalState, runModalLoop, ModalComponentManager::attachCallback */ void enterModalState (bool takeKeyboardFocus = true, ModalComponentManager::Callback* callback = nullptr, @@ -2252,8 +2236,6 @@ class JUCE_API Component : public MouseListener method, which your component can override if it needs to do something when colours are altered. - Note repaint() is not automatically called when a colour is changed. - For more details about colour IDs, see the comments for findColour(). @see findColour, isColourSpecified, colourChanged, LookAndFeel::findColour, LookAndFeel::setColour @@ -2275,11 +2257,8 @@ class JUCE_API Component : public MouseListener */ void copyAllExplicitColoursTo (Component& target) const; - /** This method is called when a colour is changed by the setColour() method, - or when the look-and-feel is changed by the setLookAndFeel() or - sendLookAndFeelChanged() methods. - - @see setColour, findColour, setLookAndFeel, sendLookAndFeelChanged + /** This method is called when a colour is changed by the setColour() method. + @see setColour, findColour */ virtual void colourChanged(); @@ -2500,9 +2479,6 @@ class JUCE_API Component : public MouseListener /** Returns the accessibility handler for this component, or nullptr if this component is not accessible. - To customise the accessibility handler for a component, override - createAccessibilityHandler(). - @see setAccessible */ AccessibilityHandler* getAccessibilityHandler(); @@ -2516,6 +2492,20 @@ class JUCE_API Component : public MouseListener void invalidateAccessibilityHandler(); //============================================================================== + #ifndef DOXYGEN + [[deprecated ("Use the setFocusContainerType that takes a more descriptive enum.")]] + void setFocusContainer (bool shouldBeFocusContainer) noexcept + { + setFocusContainerType (shouldBeFocusContainer ? FocusContainerType::keyboardFocusContainer + : FocusContainerType::none); + } + + [[deprecated ("Use the contains that takes a Point.")]] + void contains (int, int) = delete; + #endif + +private: + //============================================================================== /** Override this method to return a custom AccessibilityHandler for this component. The default implementation creates and returns a AccessibilityHandler object with an @@ -2529,34 +2519,13 @@ class JUCE_API Component : public MouseListener its Component, so it's safe to store and use a reference back to the Component inside the AccessibilityHandler if necessary. - This function should rarely be called directly. If you need to query a component's - accessibility handler, it's normally better to call getAccessibilityHandler(). - The exception to this rule is derived implementations of createAccessibilityHandler(), - which may find it useful to call the base class implementation, and then wrap or - modify the result. - @see getAccessibilityHandler */ virtual std::unique_ptr createAccessibilityHandler(); - //============================================================================== - #ifndef DOXYGEN - [[deprecated ("Use the setFocusContainerType that takes a more descriptive enum.")]] - void setFocusContainer (bool shouldBeFocusContainer) noexcept - { - setFocusContainerType (shouldBeFocusContainer ? FocusContainerType::keyboardFocusContainer - : FocusContainerType::none); - } - - [[deprecated ("Use the contains that takes a Point.")]] - void contains (int, int) = delete; - #endif - -private: - //============================================================================== friend class ComponentPeer; - friend class detail::MouseInputSourceImpl; + friend class MouseInputSourceInternal; #ifndef DOXYGEN static Component* currentlyFocusedComponent; @@ -2625,14 +2594,14 @@ class JUCE_API Component : public MouseListener //============================================================================== void internalMouseEnter (MouseInputSource, Point, Time); void internalMouseExit (MouseInputSource, Point, Time); - void internalMouseDown (MouseInputSource, const detail::PointerState&, Time); - void internalMouseUp (MouseInputSource, const detail::PointerState&, Time, const ModifierKeys oldModifiers); - void internalMouseDrag (MouseInputSource, const detail::PointerState&, Time); + void internalMouseDown (MouseInputSource, const PointerState&, Time); + void internalMouseUp (MouseInputSource, const PointerState&, Time, const ModifierKeys oldModifiers); + void internalMouseDrag (MouseInputSource, const PointerState&, Time); void internalMouseMove (MouseInputSource, Point, Time); void internalMouseWheel (MouseInputSource, Point, Time, const MouseWheelDetails&); void internalMagnifyGesture (MouseInputSource, Point, Time, float); void internalBroughtToFront(); - void internalKeyboardFocusGain (FocusChangeType, const WeakReference&, FocusChangeDirection); + void internalKeyboardFocusGain (FocusChangeType, const WeakReference&); void internalKeyboardFocusGain (FocusChangeType); void internalKeyboardFocusLoss (FocusChangeType); void internalChildKeyboardFocusChange (FocusChangeType, const WeakReference&); @@ -2650,13 +2619,14 @@ class JUCE_API Component : public MouseListener void sendMovedResizedMessagesIfPending(); void repaintParent(); void sendFakeMouseMove() const; - void takeKeyboardFocus (FocusChangeType, FocusChangeDirection); - void grabKeyboardFocusInternal (FocusChangeType, bool canTryParent, FocusChangeDirection); + void takeKeyboardFocus (FocusChangeType); + void grabKeyboardFocusInternal (FocusChangeType, bool canTryParent); void giveAwayKeyboardFocusInternal (bool sendFocusLossEvent); void sendEnablementChangeMessage(); void sendVisibilityChangeMessage(); - friend struct detail::ComponentHelpers; + struct ComponentHelpers; + friend struct ComponentHelpers; /* Components aren't allowed to have copy constructors, as this would mess up parent hierarchies. You might need to give your subclasses a private dummy constructor to avoid compiler warnings. diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_FocusTraverser.cpp b/JuceLibraryCode/modules/juce_gui_basics/components/juce_FocusTraverser.cpp index 34e50936..188c33d6 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_FocusTraverser.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_FocusTraverser.cpp @@ -26,25 +26,111 @@ namespace juce { +namespace FocusHelpers +{ + static int getOrder (const Component* c) + { + auto order = c->getExplicitFocusOrder(); + return order > 0 ? order : std::numeric_limits::max(); + } + + template + static void findAllComponents (Component* parent, + std::vector& components, + FocusContainerFn isFocusContainer) + { + if (parent == nullptr || parent->getNumChildComponents() == 0) + return; + + std::vector localComponents; + + for (auto* c : parent->getChildren()) + if (c->isVisible() && c->isEnabled()) + localComponents.push_back (c); + + const auto compareComponents = [&] (const Component* a, const Component* b) + { + const auto getComponentOrderAttributes = [] (const Component* c) + { + return std::make_tuple (getOrder (c), + c->isAlwaysOnTop() ? 0 : 1, + c->getY(), + c->getX()); + }; + + return getComponentOrderAttributes (a) < getComponentOrderAttributes (b); + }; + + // This will sort so that they are ordered in terms of explicit focus, + // always on top, left-to-right, and then top-to-bottom. + std::stable_sort (localComponents.begin(), localComponents.end(), compareComponents); + + for (auto* c : localComponents) + { + components.push_back (c); + + if (! (c->*isFocusContainer)()) + findAllComponents (c, components, isFocusContainer); + } + } + + enum class NavigationDirection { forwards, backwards }; + + template + static Component* navigateFocus (Component* current, + Component* focusContainer, + NavigationDirection direction, + FocusContainerFn isFocusContainer) + { + if (focusContainer != nullptr) + { + std::vector components; + findAllComponents (focusContainer, components, isFocusContainer); + + const auto iter = std::find (components.cbegin(), components.cend(), current); + + if (iter == components.cend()) + return nullptr; + + switch (direction) + { + case NavigationDirection::forwards: + if (iter != std::prev (components.cend())) + return *std::next (iter); + + break; + + case NavigationDirection::backwards: + if (iter != components.cbegin()) + return *std::prev (iter); + + break; + } + } + + return nullptr; + } +} + //============================================================================== Component* FocusTraverser::getNextComponent (Component* current) { jassert (current != nullptr); - return detail::FocusHelpers::navigateFocus (current, - current->findFocusContainer(), - detail::FocusHelpers::NavigationDirection::forwards, - &Component::isFocusContainer); + return FocusHelpers::navigateFocus (current, + current->findFocusContainer(), + FocusHelpers::NavigationDirection::forwards, + &Component::isFocusContainer); } Component* FocusTraverser::getPreviousComponent (Component* current) { jassert (current != nullptr); - return detail::FocusHelpers::navigateFocus (current, - current->findFocusContainer(), - detail::FocusHelpers::NavigationDirection::backwards, - &Component::isFocusContainer); + return FocusHelpers::navigateFocus (current, + current->findFocusContainer(), + FocusHelpers::NavigationDirection::backwards, + &Component::isFocusContainer); } Component* FocusTraverser::getDefaultComponent (Component* parentComponent) @@ -52,9 +138,9 @@ Component* FocusTraverser::getDefaultComponent (Component* parentComponent) if (parentComponent != nullptr) { std::vector components; - detail::FocusHelpers::findAllComponents (parentComponent, - components, - &Component::isFocusContainer); + FocusHelpers::findAllComponents (parentComponent, + components, + &Component::isFocusContainer); if (! components.empty()) return components.front(); @@ -66,9 +152,9 @@ Component* FocusTraverser::getDefaultComponent (Component* parentComponent) std::vector FocusTraverser::getAllComponents (Component* parentComponent) { std::vector components; - detail::FocusHelpers::findAllComponents (parentComponent, - components, - &Component::isFocusContainer); + FocusHelpers::findAllComponents (parentComponent, + components, + &Component::isFocusContainer); return components; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp b/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp index c0855686..5d1287a7 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp @@ -269,7 +269,7 @@ int ModalComponentManager::runEventLoopForCurrentComponent() if (auto* currentlyModal = getModalComponent (0)) { - detail::FocusRestorer focusRestorer; + FocusRestorer focusRestorer; bool finished = false; attachCallback (currentlyModal, ModalCallbackFunction::create ([&] (int r) { returnValue = r; finished = true; })); diff --git a/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp b/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp index 9c4e7517..6382c52b 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.cpp @@ -27,7 +27,7 @@ namespace juce { Desktop::Desktop() - : mouseSources (new detail::MouseInputSourceList()), + : mouseSources (new MouseInputSource::SourceList()), masterScaleFactor ((float) getDefaultMasterScale()), nativeDarkModeChangeDetectorImpl (createNativeDarkModeChangeDetectorImpl()) { @@ -353,7 +353,7 @@ void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept { JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED - if (! approximatelyEqual (masterScaleFactor, newScaleFactor)) + if (masterScaleFactor != newScaleFactor) { masterScaleFactor = newScaleFactor; displays->refresh(); diff --git a/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h b/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h index 91df79e9..45388b32 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h +++ b/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Desktop.h @@ -410,12 +410,12 @@ class JUCE_API Desktop : private DeletedAtShutdown, friend class Component; friend class ComponentPeer; - friend class detail::MouseInputSourceImpl; + friend class MouseInputSourceInternal; friend class DeletedAtShutdown; - friend class detail::TopLevelWindowManager; + friend class TopLevelWindowManager; friend class Displays; - std::unique_ptr mouseSources; + std::unique_ptr mouseSources; ListenerList mouseListeners; ListenerList focusListeners; diff --git a/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Displays.cpp b/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Displays.cpp index 5b7189f6..a2011434 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Displays.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/desktop/juce_Displays.cpp @@ -26,6 +26,15 @@ namespace juce { +template +auto* getPrimaryDisplayImpl (This& t) +{ + JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED + + const auto iter = std::find_if (t.displays.begin(), t.displays.end(), [] (auto& d) { return d.isMain; }); + return iter != t.displays.end() ? std::addressof (*iter) : nullptr; +} + Displays::Displays (Desktop& desktop) { init (desktop); @@ -162,10 +171,7 @@ Point Displays::logicalToPhysical (Point point, const Disp const Displays::Display* Displays::getPrimaryDisplay() const noexcept { - JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED - - const auto iter = std::find_if (displays.begin(), displays.end(), [] (auto& d) { return d.isMain; }); - return iter != displays.end() ? iter : nullptr; + return getPrimaryDisplayImpl (*this); } RectangleList Displays::getRectangleList (bool userAreasOnly) const @@ -260,10 +266,10 @@ static void processDisplay (DisplayNode* currentNode, Array& allNod Rectangle logicalArea (0.0, 0.0, logicalWidth, logicalHeight); - if (approximatelyEqual (physicalArea.getRight(), physicalParentArea.getX())) logicalArea.setPosition ({ logicalParentArea.getX() - logicalWidth, physicalArea.getY() / parentScale }); // on left - else if (approximatelyEqual (physicalArea.getX(), physicalParentArea.getRight())) logicalArea.setPosition ({ logicalParentArea.getRight(), physicalArea.getY() / parentScale }); // on right - else if (approximatelyEqual (physicalArea.getBottom(), physicalParentArea.getY())) logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getY() - logicalHeight }); // on top - else if (approximatelyEqual (physicalArea.getY(), physicalParentArea.getBottom())) logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getBottom() }); // on bottom + if (physicalArea.getRight() == physicalParentArea.getX()) logicalArea.setPosition ({ logicalParentArea.getX() - logicalWidth, physicalArea.getY() / parentScale }); // on left + else if (physicalArea.getX() == physicalParentArea.getRight()) logicalArea.setPosition ({ logicalParentArea.getRight(), physicalArea.getY() / parentScale }); // on right + else if (physicalArea.getBottom() == physicalParentArea.getY()) logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getY() - logicalHeight }); // on top + else if (physicalArea.getY() == physicalParentArea.getBottom()) logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getBottom() }); // on bottom else jassertfalse; currentNode->logicalArea = logicalArea; @@ -286,8 +292,8 @@ static void processDisplay (DisplayNode* currentNode, Array& allNod const auto otherPhysicalArea = node.display->totalArea.toDouble(); // If the displays are touching on any side - if (approximatelyEqual (otherPhysicalArea.getX(), physicalArea.getRight()) || approximatelyEqual (otherPhysicalArea.getRight(), physicalArea.getX()) - || approximatelyEqual (otherPhysicalArea.getY(), physicalArea.getBottom()) || approximatelyEqual (otherPhysicalArea.getBottom(), physicalArea.getY())) + if (otherPhysicalArea.getX() == physicalArea.getRight() || otherPhysicalArea.getRight() == physicalArea.getX() + || otherPhysicalArea.getY() == physicalArea.getBottom() || otherPhysicalArea.getBottom() == physicalArea.getY()) { node.parent = currentNode; children.add (&node); diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_AccessibilityHelpers.cpp b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_AccessibilityHelpers.cpp deleted file mode 100644 index 2779e3b4..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_AccessibilityHelpers.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -#if ! JUCE_NATIVE_ACCESSIBILITY_INCLUDED - void AccessibilityHelpers::notifyAccessibilityEvent (const AccessibilityHandler&, Event) {} -#endif - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_AccessibilityHelpers.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_AccessibilityHelpers.h deleted file mode 100644 index b0ea244f..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_AccessibilityHelpers.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -struct AccessibilityHelpers -{ - AccessibilityHelpers() = delete; - - enum class Event - { - elementCreated, - elementDestroyed, - elementMovedOrResized, - focusChanged, - windowOpened, - windowClosed - }; - - static void notifyAccessibilityEvent (const AccessibilityHandler&, Event); - - static String getApplicationOrPluginName() - { - #if defined (JucePlugin_Name) - return JucePlugin_Name; - #else - if (auto* app = JUCEApplicationBase::getInstance()) - return app->getApplicationName(); - - return "JUCE Application"; - #endif - } - - template - static const AccessibilityHandler* getEnclosingHandlerWithInterface (const AccessibilityHandler* handler, MemberFn fn) - { - if (handler == nullptr) - return nullptr; - - if ((handler->*fn)() != nullptr) - return handler; - - return getEnclosingHandlerWithInterface (handler->getParent(), fn); - } -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_AlertWindowHelpers.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_AlertWindowHelpers.h deleted file mode 100644 index 7d87824d..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_AlertWindowHelpers.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -struct AlertWindowHelpers -{ - AlertWindowHelpers() = delete; - - static std::unique_ptr create (const MessageBoxOptions& opts) - { - class AlertWindowImpl : public detail::ScopedMessageBoxInterface - { - public: - explicit AlertWindowImpl (const MessageBoxOptions& opts) : options (opts) {} - - void runAsync (std::function recipient) override - { - if (auto* comp = setUpAlert()) - comp->enterModalState (true, ModalCallbackFunction::create (std::move (recipient)), true); - else - NullCheckedInvocation::invoke (recipient, 0); - } - - int runSync() override - { - #if JUCE_MODAL_LOOPS_PERMITTED - if (auto comp = rawToUniquePtr (setUpAlert())) - return comp->runModalLoop(); - #endif - - jassertfalse; - return 0; - } - - void close() override - { - if (alert != nullptr) - if (alert->isCurrentlyModal()) - alert->exitModalState(); - - alert = nullptr; - } - - private: - Component* setUpAlert() - { - auto* component = options.getAssociatedComponent(); - - auto& lf = component != nullptr ? component->getLookAndFeel() - : LookAndFeel::getDefaultLookAndFeel(); - - alert = lf.createAlertWindow (options.getTitle(), - options.getMessage(), - options.getButtonText (0), - options.getButtonText (1), - options.getButtonText (2), - options.getIconType(), - options.getNumButtons(), - component); - - if (alert == nullptr) - { - // You have to return an alert box! - jassertfalse; - return nullptr; - } - - if (auto* parent = options.getParentComponent()) - { - parent->addAndMakeVisible (alert); - - if (options.getAssociatedComponent() == nullptr) - alert->setCentrePosition (parent->getLocalBounds().getCentre()); - } - - alert->setAlwaysOnTop (WindowUtils::areThereAnyAlwaysOnTopWindows()); - - return alert; - } - - const MessageBoxOptions options; - Component::SafePointer alert; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlertWindowImpl) - }; - - return std::make_unique (opts); - } -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ButtonAccessibilityHandler.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ButtonAccessibilityHandler.h deleted file mode 100644 index dd9aa5e5..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ButtonAccessibilityHandler.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -//============================================================================== -class ButtonAccessibilityHandler : public AccessibilityHandler -{ -public: - ButtonAccessibilityHandler (Button& buttonToWrap, AccessibilityRole roleIn) - : AccessibilityHandler (buttonToWrap, - isRadioButton (buttonToWrap) ? AccessibilityRole::radioButton : roleIn, - getAccessibilityActions (buttonToWrap), - getAccessibilityInterfaces (buttonToWrap)), - button (buttonToWrap) - {} - - - AccessibleState getCurrentState() const override - { - auto state = AccessibilityHandler::getCurrentState(); - - if (button.isToggleable()) - { - state = state.withCheckable(); - - if (button.getToggleState()) - state = state.withChecked(); - } - - return state; - } - - - String getTitle() const override - { - auto title = AccessibilityHandler::getTitle(); - - if (title.isEmpty()) - return button.getButtonText(); - - return title; - } - - String getHelp() const override - { - return button.getTooltip(); - } - - -private: - class ButtonValueInterface : public AccessibilityTextValueInterface - { - public: - explicit ButtonValueInterface (Button& buttonToWrap) - : button (buttonToWrap) - { - } - - bool isReadOnly() const override { return true; } - String getCurrentValueAsString() const override { return button.getToggleState() ? "On" : "Off"; } - void setValueAsString (const String&) override {} - - private: - Button& button; - - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonValueInterface) - }; - - static bool isRadioButton (const Button& button) noexcept - { - return button.getRadioGroupId() != 0; - } - - static AccessibilityActions getAccessibilityActions (Button& button) - { - auto actions = AccessibilityActions().addAction (AccessibilityActionType::press, - [&button] { button.triggerClick(); }); - - if (button.isToggleable()) - actions = actions.addAction (AccessibilityActionType::toggle, - [&button] { button.setToggleState (! button.getToggleState(), sendNotification); }); - - return actions; - } - - static Interfaces getAccessibilityInterfaces (Button& button) - { - if (button.isToggleable()) - return { std::make_unique (button) }; - - return {}; - } - - Button& button; - - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonAccessibilityHandler) -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ComponentHelpers.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ComponentHelpers.h deleted file mode 100644 index 41ea70a5..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ComponentHelpers.h +++ /dev/null @@ -1,255 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -constexpr char colourPropertyPrefix[] = "jcclr_"; - -//============================================================================== -struct ComponentHelpers -{ - using SH = ScalingHelpers; - - #if JUCE_MODAL_LOOPS_PERMITTED - static void* runModalLoopCallback (void* userData) - { - return (void*) (pointer_sized_int) static_cast (userData)->runModalLoop(); - } - #endif - - static Identifier getColourPropertyID (int colourID) - { - char buffer[32]; - auto* end = buffer + numElementsInArray (buffer) - 1; - auto* t = end; - *t = 0; - - for (auto v = (uint32) colourID;;) - { - *--t = "0123456789abcdef" [v & 15]; - v >>= 4; - - if (v == 0) - break; - } - - for (int i = (int) sizeof (colourPropertyPrefix) - 1; --i >= 0;) - *--t = colourPropertyPrefix[i]; - - return t; - } - - //============================================================================== - static bool hitTest (Component& comp, Point localPoint) - { - const auto intPoint = localPoint.roundToInt(); - return Rectangle { comp.getWidth(), comp.getHeight() }.contains (intPoint) - && comp.hitTest (intPoint.x, intPoint.y); - } - - // converts an unscaled position within a peer to the local position within that peer's component - template - static PointOrRect rawPeerPositionToLocal (const Component& comp, PointOrRect pos) noexcept - { - if (comp.isTransformed()) - pos = pos.transformedBy (comp.getTransform().inverted()); - - return SH::unscaledScreenPosToScaled (comp, pos); - } - - // converts a position within a peer's component to the unscaled position within the peer - template - static PointOrRect localPositionToRawPeerPos (const Component& comp, PointOrRect pos) noexcept - { - if (comp.isTransformed()) - pos = pos.transformedBy (comp.getTransform()); - - return SH::scaledScreenPosToUnscaled (comp, pos); - } - - template - static PointOrRect convertFromParentSpace (const Component& comp, const PointOrRect pointInParentSpace) - { - const auto transformed = comp.affineTransform != nullptr ? pointInParentSpace.transformedBy (comp.affineTransform->inverted()) - : pointInParentSpace; - - if (comp.isOnDesktop()) - { - if (auto* peer = comp.getPeer()) - return SH::unscaledScreenPosToScaled (comp, peer->globalToLocal (SH::scaledScreenPosToUnscaled (transformed))); - - jassertfalse; - return transformed; - } - - if (comp.getParentComponent() == nullptr) - return SH::subtractPosition (SH::unscaledScreenPosToScaled (comp, SH::scaledScreenPosToUnscaled (transformed)), comp); - - return SH::subtractPosition (transformed, comp); - } - - template - static PointOrRect convertToParentSpace (const Component& comp, const PointOrRect pointInLocalSpace) - { - const auto preTransform = [&] - { - if (comp.isOnDesktop()) - { - if (auto* peer = comp.getPeer()) - return SH::unscaledScreenPosToScaled (peer->localToGlobal (SH::scaledScreenPosToUnscaled (comp, pointInLocalSpace))); - - jassertfalse; - return pointInLocalSpace; - } - - if (comp.getParentComponent() == nullptr) - return SH::unscaledScreenPosToScaled (SH::scaledScreenPosToUnscaled (comp, SH::addPosition (pointInLocalSpace, comp))); - - return SH::addPosition (pointInLocalSpace, comp); - }(); - - return comp.affineTransform != nullptr ? preTransform.transformedBy (*comp.affineTransform) - : preTransform; - } - - template - static PointOrRect convertFromDistantParentSpace (const Component* parent, const Component& target, PointOrRect coordInParent) - { - auto* directParent = target.getParentComponent(); - jassert (directParent != nullptr); - - if (directParent == parent) - return convertFromParentSpace (target, coordInParent); - - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011) - return convertFromParentSpace (target, convertFromDistantParentSpace (parent, *directParent, coordInParent)); - JUCE_END_IGNORE_WARNINGS_MSVC - } - - template - static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) - { - while (source != nullptr) - { - if (source == target) - return p; - - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011) - - if (source->isParentOf (target)) - return convertFromDistantParentSpace (source, *target, p); - - JUCE_END_IGNORE_WARNINGS_MSVC - - p = convertToParentSpace (*source, p); - source = source->getParentComponent(); - } - - jassert (source == nullptr); - if (target == nullptr) - return p; - - auto* topLevelComp = target->getTopLevelComponent(); - - p = convertFromParentSpace (*topLevelComp, p); - - if (topLevelComp == target) - return p; - - return convertFromDistantParentSpace (topLevelComp, *target, p); - } - - static bool clipObscuredRegions (const Component& comp, Graphics& g, - const Rectangle clipRect, Point delta) - { - bool wasClipped = false; - - for (int i = comp.childComponentList.size(); --i >= 0;) - { - auto& child = *comp.childComponentList.getUnchecked(i); - - if (child.isVisible() && ! child.isTransformed()) - { - auto newClip = clipRect.getIntersection (child.boundsRelativeToParent); - - if (! newClip.isEmpty()) - { - if (child.isOpaque() && child.componentTransparency == 0) - { - g.excludeClipRegion (newClip + delta); - wasClipped = true; - } - else - { - auto childPos = child.getPosition(); - - if (clipObscuredRegions (child, g, newClip - childPos, childPos + delta)) - wasClipped = true; - } - } - } - } - - return wasClipped; - } - - static Rectangle getParentOrMainMonitorBounds (const Component& comp) - { - if (auto* p = comp.getParentComponent()) - return p->getLocalBounds(); - - return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea; - } - - static void releaseAllCachedImageResources (Component& c) - { - if (auto* cached = c.getCachedComponentImage()) - cached->releaseResources(); - - for (auto* child : c.childComponentList) - releaseAllCachedImageResources (*child); - } - - //============================================================================== - static bool modalWouldBlockComponent (const Component& maybeBlocked, Component* modal) - { - return modal != nullptr - && modal != &maybeBlocked - && ! modal->isParentOf (&maybeBlocked) - && ! modal->canModalEventBeSentToComponent (&maybeBlocked); - } - - template - static void sendMouseEventToComponentsThatAreBlockedByModal (Component& modal, Function&& function) - { - for (auto& ms : Desktop::getInstance().getMouseSources()) - if (auto* c = ms.getComponentUnderMouse()) - if (modalWouldBlockComponent (*c, &modal)) - (c->*function) (ms, SH::screenPosToLocalPos (*c, ms.getScreenPosition()), Time::getCurrentTime()); - } -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_CustomMouseCursorInfo.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_CustomMouseCursorInfo.h deleted file mode 100644 index 9ec44638..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_CustomMouseCursorInfo.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -struct CustomMouseCursorInfo -{ - ScaledImage image; - Point hotspot; -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_FocusHelpers.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_FocusHelpers.h deleted file mode 100644 index d18c9b73..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_FocusHelpers.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -struct FocusHelpers -{ - FocusHelpers() = delete; - - static int getOrder (const Component* c) - { - auto order = c->getExplicitFocusOrder(); - return order > 0 ? order : std::numeric_limits::max(); - } - - template - static void findAllComponents (Component* parent, - std::vector& components, - FocusContainerFn isFocusContainer) - { - if (parent == nullptr || parent->getNumChildComponents() == 0) - return; - - std::vector localComponents; - - for (auto* c : parent->getChildren()) - if (c->isVisible() && c->isEnabled()) - localComponents.push_back (c); - - const auto compareComponents = [&] (const Component* a, const Component* b) - { - const auto getComponentOrderAttributes = [] (const Component* c) - { - return std::make_tuple (getOrder (c), - c->isAlwaysOnTop() ? 0 : 1, - c->getY(), - c->getX()); - }; - - return getComponentOrderAttributes (a) < getComponentOrderAttributes (b); - }; - - // This will sort so that they are ordered in terms of explicit focus, - // always on top, left-to-right, and then top-to-bottom. - std::stable_sort (localComponents.begin(), localComponents.end(), compareComponents); - - for (auto* c : localComponents) - { - components.push_back (c); - - if (! (c->*isFocusContainer)()) - findAllComponents (c, components, isFocusContainer); - } - } - - enum class NavigationDirection { forwards, backwards }; - - template - static Component* navigateFocus (Component* current, - Component* focusContainer, - NavigationDirection direction, - FocusContainerFn isFocusContainer) - { - if (focusContainer != nullptr) - { - std::vector components; - findAllComponents (focusContainer, components, isFocusContainer); - - const auto iter = std::find (components.cbegin(), components.cend(), current); - - if (iter == components.cend()) - return nullptr; - - switch (direction) - { - case NavigationDirection::forwards: - if (iter != std::prev (components.cend())) - return *std::next (iter); - - break; - - case NavigationDirection::backwards: - if (iter != components.cbegin()) - return *std::prev (iter); - - break; - } - } - - return nullptr; - } -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_FocusRestorer.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_FocusRestorer.h deleted file mode 100644 index d544351e..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_FocusRestorer.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -struct FocusRestorer -{ - FocusRestorer() : lastFocus (Component::getCurrentlyFocusedComponent()) {} - - ~FocusRestorer() - { - if (lastFocus != nullptr - && lastFocus->isShowing() - && ! lastFocus->isCurrentlyBlockedByAnotherModalComponent()) - lastFocus->grabKeyboardFocus(); - } - - WeakReference lastFocus; - - JUCE_DECLARE_NON_COPYABLE (FocusRestorer) -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_LookAndFeelHelpers.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_LookAndFeelHelpers.h deleted file mode 100644 index 35ba8e34..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_LookAndFeelHelpers.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -struct LookAndFeelHelpers -{ - LookAndFeelHelpers() = delete; - - static Colour createBaseColour (Colour buttonColour, - bool hasKeyboardFocus, - bool shouldDrawButtonAsHighlighted, - bool shouldDrawButtonAsDown) noexcept - { - const float sat = hasKeyboardFocus ? 1.3f : 0.9f; - const Colour baseColour (buttonColour.withMultipliedSaturation (sat)); - - if (shouldDrawButtonAsDown) return baseColour.contrasting (0.2f); - if (shouldDrawButtonAsHighlighted) return baseColour.contrasting (0.1f); - - return baseColour; - } - - static TextLayout layoutTooltipText (const String& text, Colour colour) noexcept - { - const float tooltipFontSize = 13.0f; - const int maxToolTipWidth = 400; - - AttributedString s; - s.setJustification (Justification::centred); - s.append (text, Font (tooltipFontSize, Font::bold), colour); - - TextLayout tl; - tl.createLayoutWithBalancedLineLengths (s, (float) maxToolTipWidth); - return tl; - } -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_MouseInputSourceImpl.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_MouseInputSourceImpl.h deleted file mode 100644 index 6aa9a196..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_MouseInputSourceImpl.h +++ /dev/null @@ -1,588 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -class MouseInputSourceImpl : private AsyncUpdater -{ -public: - using SH = ScalingHelpers; - - MouseInputSourceImpl (int i, MouseInputSource::InputSourceType type) - : index (i), - inputType (type) - {} - - //============================================================================== - bool isDragging() const noexcept - { - return buttonState.isAnyMouseButtonDown(); - } - - Component* getComponentUnderMouse() const noexcept - { - return componentUnderMouse.get(); - } - - ModifierKeys getCurrentModifiers() const noexcept - { - return ModifierKeys::currentModifiers - .withoutMouseButtons() - .withFlags (buttonState.getRawFlags()); - } - - ComponentPeer* getPeer() noexcept - { - if (! ComponentPeer::isValidPeer (lastPeer)) - lastPeer = nullptr; - - return lastPeer; - } - - static Component* findComponentAt (Point screenPos, ComponentPeer* peer) - { - if (! ComponentPeer::isValidPeer (peer)) - return nullptr; - - auto relativePos = SH::unscaledScreenPosToScaled (peer->getComponent(), - peer->globalToLocal (screenPos)); - auto& comp = peer->getComponent(); - - // (the contains() call is needed to test for overlapping desktop windows) - if (comp.contains (relativePos)) - return comp.getComponentAt (relativePos); - - return nullptr; - } - - Point getScreenPosition() const noexcept - { - // This needs to return the live position if possible, but it mustn't update the lastScreenPos - // value, because that can cause continuity problems. - return SH::unscaledScreenPosToScaled (getRawScreenPosition()); - } - - Point getRawScreenPosition() const noexcept - { - return unboundedMouseOffset + (inputType != MouseInputSource::InputSourceType::touch ? MouseInputSource::getCurrentRawMousePosition() - : lastPointerState.position); - } - - void setScreenPosition (Point p) - { - MouseInputSource::setRawMousePosition (SH::scaledScreenPosToUnscaled (p)); - } - - //============================================================================== - #if JUCE_DUMP_MOUSE_EVENTS - #define JUCE_MOUSE_EVENT_DBG(desc, screenPos) DBG ("Mouse " << desc << " #" << index \ - << ": " << SH::screenPosToLocalPos (comp, screenPos).toString() \ - << " - Comp: " << String::toHexString ((pointer_sized_int) &comp)); - #else - #define JUCE_MOUSE_EVENT_DBG(desc, screenPos) - #endif - - void sendMouseEnter (Component& comp, const detail::PointerState& pointerState, Time time) - { - JUCE_MOUSE_EVENT_DBG ("enter", pointerState.position) - comp.internalMouseEnter (MouseInputSource (this), - SH::screenPosToLocalPos (comp, pointerState.position), - time); - } - - void sendMouseExit (Component& comp, const detail::PointerState& pointerState, Time time) - { - JUCE_MOUSE_EVENT_DBG ("exit", pointerState.position) - comp.internalMouseExit (MouseInputSource (this), - SH::screenPosToLocalPos (comp, pointerState.position), - time); - } - - void sendMouseMove (Component& comp, const detail::PointerState& pointerState, Time time) - { - JUCE_MOUSE_EVENT_DBG ("move", pointerState.position) - comp.internalMouseMove (MouseInputSource (this), - SH::screenPosToLocalPos (comp, pointerState.position), - time); - } - - void sendMouseDown (Component& comp, const detail::PointerState& pointerState, Time time) - { - JUCE_MOUSE_EVENT_DBG ("down", pointerState.position) - comp.internalMouseDown (MouseInputSource (this), - pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)), - time); - } - - void sendMouseDrag (Component& comp, const detail::PointerState& pointerState, Time time) - { - JUCE_MOUSE_EVENT_DBG ("drag", pointerState.position) - comp.internalMouseDrag (MouseInputSource (this), - pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)), - time); - } - - void sendMouseUp (Component& comp, const detail::PointerState& pointerState, Time time, ModifierKeys oldMods) - { - JUCE_MOUSE_EVENT_DBG ("up", pointerState.position) - comp.internalMouseUp (MouseInputSource (this), - pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)), - time, - oldMods); - } - - void sendMouseWheel (Component& comp, Point screenPos, Time time, const MouseWheelDetails& wheel) - { - JUCE_MOUSE_EVENT_DBG ("wheel", screenPos) - comp.internalMouseWheel (MouseInputSource (this), - SH::screenPosToLocalPos (comp, screenPos), - time, - wheel); - } - - void sendMagnifyGesture (Component& comp, Point screenPos, Time time, float amount) - { - JUCE_MOUSE_EVENT_DBG ("magnify", screenPos) - comp.internalMagnifyGesture (MouseInputSource (this), - SH::screenPosToLocalPos (comp, screenPos), - time, - amount); - } - - #undef JUCE_MOUSE_EVENT_DBG - - //============================================================================== - // (returns true if the button change caused a modal event loop) - bool setButtons (const detail::PointerState& pointerState, Time time, ModifierKeys newButtonState) - { - if (buttonState == newButtonState) - return false; - - // (avoid sending a spurious mouse-drag when we receive a mouse-up) - if (! (isDragging() && ! newButtonState.isAnyMouseButtonDown())) - setPointerState (pointerState, time, false); - - // (ignore secondary clicks when there's already a button down) - if (buttonState.isAnyMouseButtonDown() == newButtonState.isAnyMouseButtonDown()) - { - buttonState = newButtonState; - return false; - } - - auto lastCounter = mouseEventCounter; - - if (buttonState.isAnyMouseButtonDown()) - { - if (auto* current = getComponentUnderMouse()) - { - auto oldMods = getCurrentModifiers(); - buttonState = newButtonState; // must change this before calling sendMouseUp, in case it runs a modal loop - - sendMouseUp (*current, pointerState.withPositionOffset (unboundedMouseOffset), time, oldMods); - - if (lastCounter != mouseEventCounter) - return true; // if a modal loop happened, then newButtonState is no longer valid. - } - - enableUnboundedMouseMovement (false, false); - } - - buttonState = newButtonState; - - if (buttonState.isAnyMouseButtonDown()) - { - Desktop::getInstance().incrementMouseClickCounter(); - - if (auto* current = getComponentUnderMouse()) - { - registerMouseDown (pointerState.position, time, *current, buttonState, - inputType == MouseInputSource::InputSourceType::touch); - sendMouseDown (*current, pointerState, time); - } - } - - return lastCounter != mouseEventCounter; - } - - void setComponentUnderMouse (Component* newComponent, const detail::PointerState& pointerState, Time time) - { - auto* current = getComponentUnderMouse(); - - if (newComponent != current) - { - WeakReference safeNewComp (newComponent); - auto originalButtonState = buttonState; - - if (current != nullptr) - { - WeakReference safeOldComp (current); - setButtons (pointerState, time, ModifierKeys()); - - if (auto oldComp = safeOldComp.get()) - { - componentUnderMouse = safeNewComp; - sendMouseExit (*oldComp, pointerState, time); - } - - buttonState = originalButtonState; - } - - componentUnderMouse = safeNewComp.get(); - current = safeNewComp.get(); - - if (current != nullptr) - sendMouseEnter (*current, pointerState, time); - - revealCursor (false); - setButtons (pointerState, time, originalButtonState); - } - } - - void setPeer (ComponentPeer& newPeer, const detail::PointerState& pointerState, Time time) - { - if (&newPeer != lastPeer && ( findComponentAt (pointerState.position, &newPeer) != nullptr - || findComponentAt (pointerState.position, lastPeer) == nullptr)) - { - setComponentUnderMouse (nullptr, pointerState, time); - lastPeer = &newPeer; - setComponentUnderMouse (findComponentAt (pointerState.position, getPeer()), pointerState, time); - } - } - - void setPointerState (const detail::PointerState& newPointerState, Time time, bool forceUpdate) - { - const auto& newScreenPos = newPointerState.position; - - if (! isDragging()) - setComponentUnderMouse (findComponentAt (newScreenPos, getPeer()), newPointerState, time); - - if ((newPointerState != lastPointerState) || forceUpdate) - { - cancelPendingUpdate(); - - lastPointerState = newPointerState; - - if (auto* current = getComponentUnderMouse()) - { - if (isDragging()) - { - registerMouseDrag (newScreenPos); - sendMouseDrag (*current, newPointerState.withPositionOffset (unboundedMouseOffset), time); - - if (isUnboundedMouseModeOn) - handleUnboundedDrag (*current); - } - else - { - sendMouseMove (*current, newPointerState, time); - } - } - - revealCursor (false); - } - } - - //============================================================================== - void handleEvent (ComponentPeer& newPeer, Point positionWithinPeer, Time time, - const ModifierKeys newMods, float newPressure, float newOrientation, PenDetails pen) - { - lastTime = time; - ++mouseEventCounter; - const auto pointerState = detail::PointerState().withPosition (newPeer.localToGlobal (positionWithinPeer)) - .withPressure (newPressure) - .withOrientation (newOrientation) - .withRotation (MouseInputSource::defaultRotation) - .withTiltX (pen.tiltX) - .withTiltY (pen.tiltY); - - if (isDragging() && newMods.isAnyMouseButtonDown()) - { - setPointerState (pointerState, time, false); - } - else - { - setPeer (newPeer, pointerState, time); - - if (auto* peer = getPeer()) - { - if (setButtons (pointerState, time, newMods)) - return; // some modal events have been dispatched, so the current event is now out-of-date - - peer = getPeer(); - - if (peer != nullptr) - setPointerState (pointerState, time, false); - } - } - } - - Component* getTargetForGesture (ComponentPeer& peer, Point positionWithinPeer, - Time time, Point& screenPos) - { - lastTime = time; - ++mouseEventCounter; - - screenPos = peer.localToGlobal (positionWithinPeer); - const auto pointerState = lastPointerState.withPosition (screenPos); - setPeer (peer, pointerState, time); - setPointerState (pointerState, time, false); - triggerFakeMove(); - - return getComponentUnderMouse(); - } - - void handleWheel (ComponentPeer& peer, Point positionWithinPeer, - Time time, const MouseWheelDetails& wheel) - { - Desktop::getInstance().incrementMouseWheelCounter(); - Point screenPos; - - // This will make sure that when the wheel spins in its inertial phase, any events - // continue to be sent to the last component that the mouse was over when it was being - // actively controlled by the user. This avoids confusion when scrolling through nested - // scrollable components. - if (lastNonInertialWheelTarget == nullptr || ! wheel.isInertial) - lastNonInertialWheelTarget = getTargetForGesture (peer, positionWithinPeer, time, screenPos); - else - screenPos = peer.localToGlobal (positionWithinPeer); - - if (auto target = lastNonInertialWheelTarget.get()) - sendMouseWheel (*target, screenPos, time, wheel); - } - - void handleMagnifyGesture (ComponentPeer& peer, Point positionWithinPeer, - Time time, const float scaleFactor) - { - Point screenPos; - - if (auto* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos)) - sendMagnifyGesture (*current, screenPos, time, scaleFactor); - } - - //============================================================================== - Time getLastMouseDownTime() const noexcept - { - return mouseDowns[0].time; - } - - Point getLastMouseDownPosition() const noexcept - { - return SH::unscaledScreenPosToScaled (mouseDowns[0].position); - } - - int getNumberOfMultipleClicks() const noexcept - { - int numClicks = 1; - - if (! isLongPressOrDrag()) - { - for (int i = 1; i < numElementsInArray (mouseDowns); ++i) - { - if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2))) - ++numClicks; - else - break; - } - } - - return numClicks; - } - - bool isLongPressOrDrag() const noexcept - { - return movedSignificantly || - lastTime > (mouseDowns[0].time + RelativeTime::milliseconds (300)); - } - - bool hasMovedSignificantlySincePressed() const noexcept - { - return movedSignificantly; - } - - // Deprecated method - bool hasMouseMovedSignificantlySincePressed() const noexcept - { - return isLongPressOrDrag(); - } - - //============================================================================== - void triggerFakeMove() - { - triggerAsyncUpdate(); - } - - void handleAsyncUpdate() override - { - setPointerState (lastPointerState, - jmax (lastTime, Time::getCurrentTime()), true); - } - - //============================================================================== - void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen) - { - enable = enable && isDragging(); - isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen; - - if (enable != isUnboundedMouseModeOn) - { - if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin())) - { - // when released, return the mouse to within the component's bounds - if (auto* current = getComponentUnderMouse()) - setScreenPosition (current->getScreenBounds().toFloat() - .getConstrainedPoint (SH::unscaledScreenPosToScaled (lastPointerState.position))); - } - - isUnboundedMouseModeOn = enable; - unboundedMouseOffset = {}; - - revealCursor (true); - } - } - - void handleUnboundedDrag (Component& current) - { - auto componentScreenBounds = SH::scaledScreenPosToUnscaled (current.getParentMonitorArea() - .reduced (2, 2) - .toFloat()); - - if (! componentScreenBounds.contains (lastPointerState.position)) - { - auto componentCentre = current.getScreenBounds().toFloat().getCentre(); - unboundedMouseOffset += (lastPointerState.position - SH::scaledScreenPosToUnscaled (componentCentre)); - setScreenPosition (componentCentre); - } - else if (isCursorVisibleUntilOffscreen - && (! unboundedMouseOffset.isOrigin()) - && componentScreenBounds.contains (lastPointerState.position + unboundedMouseOffset)) - { - MouseInputSource::setRawMousePosition (lastPointerState.position + unboundedMouseOffset); - unboundedMouseOffset = {}; - } - } - - //============================================================================== - void showMouseCursor (MouseCursor cursor, bool forcedUpdate) - { - if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen)) - { - cursor = MouseCursor::NoCursor; - forcedUpdate = true; - } - - if (forcedUpdate || cursor.getHandle() != currentCursorHandle) - { - currentCursorHandle = cursor.getHandle(); - cursor.showInWindow (getPeer()); - } - } - - void hideCursor() - { - showMouseCursor (MouseCursor::NoCursor, true); - } - - void revealCursor (bool forcedUpdate) - { - MouseCursor mc (MouseCursor::NormalCursor); - - if (auto* current = getComponentUnderMouse()) - mc = current->getLookAndFeel().getMouseCursorFor (*current); - - showMouseCursor (mc, forcedUpdate); - } - - //============================================================================== - const int index; - const MouseInputSource::InputSourceType inputType; - Point unboundedMouseOffset; // NB: these are unscaled coords - detail::PointerState lastPointerState; - ModifierKeys buttonState; - - bool isUnboundedMouseModeOn = false, isCursorVisibleUntilOffscreen = false; - -private: - WeakReference componentUnderMouse, lastNonInertialWheelTarget; - ComponentPeer* lastPeer = nullptr; - - void* currentCursorHandle = nullptr; - int mouseEventCounter = 0; - - struct RecentMouseDown - { - RecentMouseDown() = default; - - Point position; - Time time; - ModifierKeys buttons; - uint32 peerID = 0; - bool isTouch = false; - - bool canBePartOfMultipleClickWith (const RecentMouseDown& other, int maxTimeBetweenMs) const noexcept - { - return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs) - && std::abs (position.x - other.position.x) < (float) getPositionToleranceForInputType() - && std::abs (position.y - other.position.y) < (float) getPositionToleranceForInputType() - && buttons == other.buttons - && peerID == other.peerID; - } - - int getPositionToleranceForInputType() const noexcept { return isTouch ? 25 : 8; } - }; - - RecentMouseDown mouseDowns[4]; - Time lastTime; - bool movedSignificantly = false; - - void registerMouseDown (Point screenPos, Time time, Component& component, - const ModifierKeys modifiers, bool isTouchSource) noexcept - { - for (int i = numElementsInArray (mouseDowns); --i > 0;) - mouseDowns[i] = mouseDowns[i - 1]; - - mouseDowns[0].position = screenPos; - mouseDowns[0].time = time; - mouseDowns[0].buttons = modifiers.withOnlyMouseButtons(); - mouseDowns[0].isTouch = isTouchSource; - - if (auto* peer = component.getPeer()) - mouseDowns[0].peerID = peer->getUniqueID(); - else - mouseDowns[0].peerID = 0; - - movedSignificantly = false; - lastNonInertialWheelTarget = nullptr; - } - - void registerMouseDrag (Point screenPos) noexcept - { - movedSignificantly = movedSignificantly || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4; - } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInputSourceImpl) -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_MouseInputSourceList.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_MouseInputSourceList.h deleted file mode 100644 index 4bff8759..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_MouseInputSourceList.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -class MouseInputSourceList : public Timer -{ -public: - MouseInputSourceList() - { - #if JUCE_ANDROID || JUCE_IOS - auto mainMouseInputType = MouseInputSource::InputSourceType::touch; - #else - auto mainMouseInputType = MouseInputSource::InputSourceType::mouse; - #endif - - addSource (0, mainMouseInputType); - } - - MouseInputSource* addSource (int index, MouseInputSource::InputSourceType type) - { - auto* s = new MouseInputSourceImpl (index, type); - sources.add (s); - sourceArray.add (MouseInputSource (s)); - - return &sourceArray.getReference (sourceArray.size() - 1); - } - - MouseInputSource* getMouseSource (int index) noexcept - { - return isPositiveAndBelow (index, sourceArray.size()) ? &sourceArray.getReference (index) - : nullptr; - } - - MouseInputSource* getOrCreateMouseInputSource (MouseInputSource::InputSourceType type, int touchIndex = 0) - { - if (type == MouseInputSource::InputSourceType::mouse - || type == MouseInputSource::InputSourceType::pen) - { - for (auto& m : sourceArray) - if (type == m.getType()) - return &m; - - addSource (0, type); - } - else if (type == MouseInputSource::InputSourceType::touch) - { - jassert (0 <= touchIndex && touchIndex < 100); // sanity-check on number of fingers - - for (auto& m : sourceArray) - if (type == m.getType() && touchIndex == m.getIndex()) - return &m; - - if (canUseTouch()) - return addSource (touchIndex, type); - } - - return nullptr; - } - - int getNumDraggingMouseSources() const noexcept - { - int num = 0; - - for (auto* s : sources) - if (s->isDragging()) - ++num; - - return num; - } - - MouseInputSource* getDraggingMouseSource (int index) noexcept - { - int num = 0; - - for (auto& s : sourceArray) - { - if (s.isDragging()) - { - if (index == num) - return &s; - - ++num; - } - } - - return nullptr; - } - - void beginDragAutoRepeat (int interval) - { - if (interval > 0) - { - if (getTimerInterval() != interval) - startTimer (interval); - } - else - { - stopTimer(); - } - } - - void timerCallback() override - { - bool anyDragging = false; - - for (auto* s : sources) - { - // NB: when doing auto-repeat, we need to force an update of the current position and button state, - // because on some OSes the queue can get overloaded with messages so that mouse-events don't get through.. - if (s->isDragging() && ComponentPeer::getCurrentModifiersRealtime().isAnyMouseButtonDown()) - { - s->lastPointerState.position = s->getRawScreenPosition(); - s->triggerFakeMove(); - anyDragging = true; - } - } - - if (! anyDragging) - stopTimer(); - } - - OwnedArray sources; - Array sourceArray; - -private: - bool addSource(); - bool canUseTouch() const; -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScalingHelpers.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScalingHelpers.h deleted file mode 100644 index 8665c3b2..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScalingHelpers.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -struct ScalingHelpers -{ - template - static PointOrRect unscaledScreenPosToScaled (float scale, PointOrRect pos) noexcept - { - return ! approximatelyEqual (scale, 1.0f) ? pos / scale : pos; - } - - template - static PointOrRect scaledScreenPosToUnscaled (float scale, PointOrRect pos) noexcept - { - return ! approximatelyEqual (scale, 1.0f) ? pos * scale : pos; - } - - // For these, we need to avoid getSmallestIntegerContainer being used, which causes - // judder when moving windows - static Rectangle unscaledScreenPosToScaled (float scale, Rectangle pos) noexcept - { - return ! approximatelyEqual (scale, 1.0f) ? Rectangle (roundToInt ((float) pos.getX() / scale), - roundToInt ((float) pos.getY() / scale), - roundToInt ((float) pos.getWidth() / scale), - roundToInt ((float) pos.getHeight() / scale)) : pos; - } - - static Rectangle scaledScreenPosToUnscaled (float scale, Rectangle pos) noexcept - { - return ! approximatelyEqual (scale, 1.0f) ? Rectangle (roundToInt ((float) pos.getX() * scale), - roundToInt ((float) pos.getY() * scale), - roundToInt ((float) pos.getWidth() * scale), - roundToInt ((float) pos.getHeight() * scale)) : pos; - } - - static Rectangle unscaledScreenPosToScaled (float scale, Rectangle pos) noexcept - { - return ! approximatelyEqual (scale, 1.0f) ? Rectangle (pos.getX() / scale, - pos.getY() / scale, - pos.getWidth() / scale, - pos.getHeight() / scale) : pos; - } - - static Rectangle scaledScreenPosToUnscaled (float scale, Rectangle pos) noexcept - { - return ! approximatelyEqual (scale, 1.0f) ? Rectangle (pos.getX() * scale, - pos.getY() * scale, - pos.getWidth() * scale, - pos.getHeight() * scale) : pos; - } - - template - static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept - { - return unscaledScreenPosToScaled (Desktop::getInstance().getGlobalScaleFactor(), pos); - } - - template - static PointOrRect scaledScreenPosToUnscaled (PointOrRect pos) noexcept - { - return scaledScreenPosToUnscaled (Desktop::getInstance().getGlobalScaleFactor(), pos); - } - - template - static PointOrRect unscaledScreenPosToScaled (const Component& comp, PointOrRect pos) noexcept - { - return unscaledScreenPosToScaled (comp.getDesktopScaleFactor(), pos); - } - - template - static PointOrRect scaledScreenPosToUnscaled (const Component& comp, PointOrRect pos) noexcept - { - return scaledScreenPosToUnscaled (comp.getDesktopScaleFactor(), pos); - } - - static Point addPosition (Point p, const Component& c) noexcept { return p + c.getPosition(); } - static Rectangle addPosition (Rectangle p, const Component& c) noexcept { return p + c.getPosition(); } - static Point addPosition (Point p, const Component& c) noexcept { return p + c.getPosition().toFloat(); } - static Rectangle addPosition (Rectangle p, const Component& c) noexcept { return p + c.getPosition().toFloat(); } - static Point subtractPosition (Point p, const Component& c) noexcept { return p - c.getPosition(); } - static Rectangle subtractPosition (Rectangle p, const Component& c) noexcept { return p - c.getPosition(); } - static Point subtractPosition (Point p, const Component& c) noexcept { return p - c.getPosition().toFloat(); } - static Rectangle subtractPosition (Rectangle p, const Component& c) noexcept { return p - c.getPosition().toFloat(); } - - static Point screenPosToLocalPos (Component& comp, Point pos) - { - if (auto* peer = comp.getPeer()) - { - pos = peer->globalToLocal (pos); - auto& peerComp = peer->getComponent(); - return comp.getLocalPoint (&peerComp, unscaledScreenPosToScaled (peerComp, pos)); - } - - return comp.getLocalPoint (nullptr, unscaledScreenPosToScaled (comp, pos)); - } -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedContentSharerImpl.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedContentSharerImpl.h deleted file mode 100644 index 5315bf9d..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedContentSharerImpl.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -class ConcreteScopedContentSharerImpl : public ScopedMessageBoxImpl, - private AsyncUpdater -{ -public: - static ScopedMessageBox show (std::unique_ptr&& native, - ContentSharer::Callback callback) - { - return ScopedMessageBox (runAsync (std::move (native), std::move (callback))); - } - - ~ConcreteScopedContentSharerImpl() override - { - cancelPendingUpdate(); - } - - void close() override - { - cancelPendingUpdate(); - nativeImplementation->close(); - self.reset(); - } - -private: - static std::shared_ptr runAsync (std::unique_ptr&& p, - ContentSharer::Callback&& c) - { - std::shared_ptr result (new ConcreteScopedContentSharerImpl (std::move (p), std::move (c))); - result->self = result; - result->triggerAsyncUpdate(); - return result; - } - - ConcreteScopedContentSharerImpl (std::unique_ptr&& p, - ContentSharer::Callback&& c) - : callback (std::move (c)), nativeImplementation (std::move (p)) {} - - void handleAsyncUpdate() override - { - nativeImplementation->runAsync ([weakRecipient = std::weak_ptr (self)] (bool result, const String& error) - { - const auto notifyRecipient = [result, error, weakRecipient] - { - if (const auto locked = weakRecipient.lock()) - { - NullCheckedInvocation::invoke (locked->callback, result, error); - locked->self.reset(); - } - }; - - if (MessageManager::getInstance()->isThisTheMessageThread()) - notifyRecipient(); - else - MessageManager::callAsync (notifyRecipient); - }); - } - - ContentSharer::Callback callback; - std::unique_ptr nativeImplementation; - - /* The 'old' native message box API doesn't have a concept of content sharer owners. - Instead, content sharers have to clean up after themselves, once they're done displaying. - To allow this mode of usage, the implementation keeps an owning reference to itself, - which is cleared once the content sharer is closed or asked to quit. To display a content - sharer box without a scoped lifetime, just create a Pimpl instance without using - the ScopedContentSharer wrapper, and the Pimpl will destroy itself after it is dismissed. - */ - std::shared_ptr self; -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h deleted file mode 100644 index f77ad896..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -/* - Instances of this type can show and dismiss a content sharer. - - This is an interface rather than a concrete type so that platforms can pick an implementation at - runtime if necessary. -*/ -struct ScopedContentSharerInterface -{ - virtual ~ScopedContentSharerInterface() = default; - - /* Shows the content sharer. - - When the content sharer exits normally, it should send the result to the passed-in function. - The passed-in function is safe to call from any thread at any time. - */ - virtual void runAsync (ContentSharer::Callback callback) - { - jassertfalse; - NullCheckedInvocation::invoke (callback, false, "Content sharing not available on this platform!"); - } - - /* Forcefully closes the content sharer. - - This will be called when the content sharer handle has fallen out of scope. - If the content sharer has already been closed by the user, this shouldn't do anything. - */ - virtual void close() {} - - /* Implemented differently for each platform. */ - static std::unique_ptr shareFiles (const Array&, Component*); - static std::unique_ptr shareText (const String&, Component*); - - /* Implemented below. */ - static std::unique_ptr shareImages (const Array&, std::unique_ptr, Component*); - static std::unique_ptr shareData (MemoryBlock, Component*); -}; - -class TemporaryFilesDecorator : public ScopedContentSharerInterface, - private AsyncUpdater -{ -public: - explicit TemporaryFilesDecorator (Component* parentIn) - : parent (parentIn) {} - - void runAsync (ContentSharer::Callback cb) override - { - callback = std::move (cb); - - task = std::async (std::launch::async, [this] - { - std::tie (temporaryFiles, error) = prepareTemporaryFiles(); - triggerAsyncUpdate(); - }); - } - - void close() override - { - if (inner != nullptr) - inner->close(); - } - -private: - virtual std::tuple, String> prepareTemporaryFiles() const = 0; - - void handleAsyncUpdate() override - { - if (error.isNotEmpty()) - { - NullCheckedInvocation::invoke (callback, false, error); - return; - } - - inner = shareFiles (temporaryFiles, parent); - - if (inner == nullptr) - { - NullCheckedInvocation::invoke (callback, false, TRANS ("Failed to create file sharer")); - return; - } - - inner->runAsync (callback); - } - - Array temporaryFiles; - String error; - std::unique_ptr inner; - ContentSharer::Callback callback; - std::future task; - Component* parent = nullptr; -}; - -std::unique_ptr ScopedContentSharerInterface::shareImages (const Array& images, - std::unique_ptr format, - Component* parent) -{ - class Decorator : public TemporaryFilesDecorator - { - public: - Decorator (Array imagesIn, std::unique_ptr formatIn, Component* parentIn) - : TemporaryFilesDecorator (parentIn), images (std::move (imagesIn)), format (std::move (formatIn)) {} - - private: - std::tuple, String> prepareTemporaryFiles() const override - { - const auto extension = format->getFormatName().toLowerCase(); - - Array result; - - for (const auto& image : images) - { - File tempFile = File::createTempFile (extension); - - if (! tempFile.create().wasOk()) - return { Array{}, TRANS ("Failed to create temporary file") }; - - std::unique_ptr outputStream (tempFile.createOutputStream()); - - if (outputStream == nullptr) - return { Array{}, TRANS ("Failed to open temporary file for writing") }; - - if (format->writeImageToStream (image, *outputStream)) - result.add (URL (tempFile)); - } - - for (const auto& url : result) - jassertquiet (url.isLocalFile() && url.getLocalFile().existsAsFile()); - - return { std::move (result), String{} }; - } - - Array images; - std::unique_ptr format; - }; - - return std::make_unique (images, - format == nullptr ? std::make_unique() : std::move (format), - parent); -} - -std::unique_ptr ScopedContentSharerInterface::shareData (MemoryBlock mb, Component* parent) -{ - class Decorator : public TemporaryFilesDecorator - { - public: - Decorator (MemoryBlock mbIn, Component* parentIn) - : TemporaryFilesDecorator (parentIn), mb (std::move (mbIn)) {} - - private: - std::tuple, String> prepareTemporaryFiles() const override - { - File tempFile = File::createTempFile ("data"); - - if (! tempFile.create().wasOk()) - return { Array{}, TRANS ("Failed to create temporary file") }; - - std::unique_ptr outputStream (tempFile.createOutputStream()); - - if (outputStream == nullptr) - return { Array{}, TRANS ("Failed to open temporary file for writing") }; - - size_t pos = 0; - size_t totalSize = mb.getSize(); - - while (pos < totalSize) - { - size_t numToWrite = std::min ((size_t) 8192, totalSize - pos); - - if (! outputStream->write (mb.begin() + pos, numToWrite)) - return { Array{}, TRANS ("Failed to write to temporary file") }; - - pos += numToWrite; - } - - return { Array { URL (tempFile) }, String{} }; - } - - MemoryBlock mb; - }; - - return std::make_unique (std::move (mb), parent); -} - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h deleted file mode 100644 index d638060d..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -//============================================================================== -class ScopedMessageBoxImpl -{ -public: - virtual ~ScopedMessageBoxImpl() = default; - virtual void close() = 0; -}; - -//============================================================================== -class ConcreteScopedMessageBoxImpl : public ScopedMessageBoxImpl, - private AsyncUpdater -{ -public: - static ScopedMessageBox show (std::unique_ptr&& native, - std::function callback) - { - return ScopedMessageBox (runAsync (std::move (native), - rawToUniquePtr (ModalCallbackFunction::create (std::move (callback))))); - } - - static int showUnmanaged (std::unique_ptr&& native, - ModalComponentManager::Callback* cb) - { - #if JUCE_MODAL_LOOPS_PERMITTED - if (cb == nullptr) - return runSync (std::move (native)); - #endif - - runAsync (std::move (native), rawToUniquePtr (cb)); - - return 0; - } - - ~ConcreteScopedMessageBoxImpl() override - { - cancelPendingUpdate(); - } - - void close() override - { - cancelPendingUpdate(); - nativeImplementation->close(); - self.reset(); - } - -private: - static std::shared_ptr runAsync (std::unique_ptr&& p, - std::unique_ptr&& c) - { - std::shared_ptr result (new ConcreteScopedMessageBoxImpl (std::move (p), std::move (c))); - result->self = result; - result->triggerAsyncUpdate(); - return result; - } - - static int runSync (std::unique_ptr&& p) - { - auto local = std::move (p); - return local != nullptr ? local->runSync() : 0; - } - - explicit ConcreteScopedMessageBoxImpl (std::unique_ptr&& p) - : ConcreteScopedMessageBoxImpl (std::move (p), nullptr) {} - - ConcreteScopedMessageBoxImpl (std::unique_ptr&& p, - std::unique_ptr&& c) - : callback (std::move (c)), nativeImplementation (std::move (p)) {} - - void handleAsyncUpdate() override - { - nativeImplementation->runAsync ([weakRecipient = std::weak_ptr (self)] (int result) - { - const auto notifyRecipient = [result, weakRecipient] - { - if (const auto locked = weakRecipient.lock()) - { - if (auto* cb = locked->callback.get()) - cb->modalStateFinished (result); - - locked->self.reset(); - } - }; - - if (MessageManager::getInstance()->isThisTheMessageThread()) - notifyRecipient(); - else - MessageManager::callAsync (notifyRecipient); - }); - } - - std::unique_ptr callback; - std::unique_ptr nativeImplementation; - - /* The 'old' native message box API doesn't have a concept of message box owners. - Instead, message boxes have to clean up after themselves, once they're done displaying. - To allow this mode of usage, the implementation keeps an owning reference to itself, - which is cleared once the message box is closed or asked to quit. To display a native - message box without a scoped lifetime, just create a Pimpl instance without using - the ScopedMessageBox wrapper, and the Pimpl will destroy itself after it is dismissed. - */ - std::shared_ptr self; -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h deleted file mode 100644 index a722f050..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -/* - Instances of this type can show and dismiss a message box. - - This is an interface rather than a concrete type so that platforms can pick an implementation at - runtime if necessary. -*/ -struct ScopedMessageBoxInterface -{ - virtual ~ScopedMessageBoxInterface() = default; - - /* Shows the message box. - - When the message box exits normally, it should send the result to the passed-in function. - The passed-in function is safe to call from any thread at any time. - */ - virtual void runAsync (std::function) = 0; - - /* Shows the message box and blocks. */ - virtual int runSync() = 0; - - /* Forcefully closes the message box. - - This will be called when the message box handle has fallen out of scope. - If the message box has already been closed by the user, this shouldn't do anything. - */ - virtual void close() = 0; - - /* Implemented differently for each platform. */ - static std::unique_ptr create (const MessageBoxOptions& options); -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h deleted file mode 100644 index c5359e0d..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -class ToolbarItemDragAndDropOverlayComponent : public Component -{ -public: - ToolbarItemDragAndDropOverlayComponent() - : isDragging (false) - { - setAlwaysOnTop (true); - setRepaintsOnMouseActivity (true); - setMouseCursor (MouseCursor::DraggingHandCursor); - } - - void paint (Graphics& g) override - { - if (ToolbarItemComponent* const tc = getToolbarItemComponent()) - { - if (isMouseOverOrDragging() - && tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar) - { - g.setColour (findColour (Toolbar::editingModeOutlineColourId, true)); - g.drawRect (getLocalBounds(), jmin (2, (getWidth() - 1) / 2, - (getHeight() - 1) / 2)); - } - } - } - - void mouseDown (const MouseEvent& e) override - { - isDragging = false; - - if (ToolbarItemComponent* const tc = getToolbarItemComponent()) - { - tc->dragOffsetX = e.x; - tc->dragOffsetY = e.y; - } - } - - void mouseDrag (const MouseEvent& e) override - { - if (e.mouseWasDraggedSinceMouseDown() && ! isDragging) - { - isDragging = true; - - if (DragAndDropContainer* const dnd = DragAndDropContainer::findParentDragContainerFor (this)) - { - dnd->startDragging (Toolbar::toolbarDragDescriptor, getParentComponent(), ScaledImage(), true, nullptr, &e.source); - - if (ToolbarItemComponent* const tc = getToolbarItemComponent()) - { - tc->isBeingDragged = true; - - if (tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar) - tc->setVisible (false); - } - } - } - } - - void mouseUp (const MouseEvent&) override - { - isDragging = false; - - if (ToolbarItemComponent* const tc = getToolbarItemComponent()) - { - tc->isBeingDragged = false; - - if (Toolbar* const tb = tc->getToolbar()) - tb->updateAllItemPositions (true); - else if (tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar) - delete tc; - } - } - - void parentSizeChanged() override - { - setBounds (0, 0, getParentWidth(), getParentHeight()); - } - -private: - //============================================================================== - bool isDragging; - - ToolbarItemComponent* getToolbarItemComponent() const noexcept - { - return dynamic_cast (getParentComponent()); - } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToolbarItemDragAndDropOverlayComponent) -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h deleted file mode 100644 index 8aef2931..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -/** Keeps track of the active top level window. */ -class TopLevelWindowManager : private Timer, - private DeletedAtShutdown -{ -public: - TopLevelWindowManager() = default; - - ~TopLevelWindowManager() override - { - clearSingletonInstance(); - } - - JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (TopLevelWindowManager) - - static void checkCurrentlyFocusedTopLevelWindow() - { - if (auto* wm = TopLevelWindowManager::getInstanceWithoutCreating()) - wm->checkFocusAsync(); - } - - void checkFocusAsync() - { - startTimer (10); - } - - void checkFocus() - { - startTimer (jmin (1731, getTimerInterval() * 2)); - - auto* newActive = findCurrentlyActiveWindow(); - - if (newActive != currentActive) - { - currentActive = newActive; - - for (int i = windows.size(); --i >= 0;) - if (auto* tlw = windows[i]) - tlw->setWindowActive (isWindowActive (tlw)); - - Desktop::getInstance().triggerFocusCallback(); - } - } - - bool addWindow (TopLevelWindow* const w) - { - windows.add (w); - checkFocusAsync(); - - return isWindowActive (w); - } - - void removeWindow (TopLevelWindow* const w) - { - checkFocusAsync(); - - if (currentActive == w) - currentActive = nullptr; - - windows.removeFirstMatchingValue (w); - - if (windows.isEmpty()) - deleteInstance(); - } - - Array windows; - -private: - TopLevelWindow* currentActive = nullptr; - - void timerCallback() override - { - checkFocus(); - } - - bool isWindowActive (TopLevelWindow* const tlw) const - { - return (tlw == currentActive - || tlw->isParentOf (currentActive) - || tlw->hasKeyboardFocus (true)) - && tlw->isShowing(); - } - - TopLevelWindow* findCurrentlyActiveWindow() const - { - if (Process::isForegroundProcess()) - { - auto* focusedComp = Component::getCurrentlyFocusedComponent(); - auto* w = dynamic_cast (focusedComp); - - if (w == nullptr && focusedComp != nullptr) - w = focusedComp->findParentComponentOfClass(); - - if (w == nullptr) - w = currentActive; - - if (w != nullptr && w->isShowing()) - return w; - } - - return nullptr; - } - - JUCE_DECLARE_NON_COPYABLE (TopLevelWindowManager) -}; - -JUCE_IMPLEMENT_SINGLETON (TopLevelWindowManager) - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ViewportHelpers.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ViewportHelpers.h deleted file mode 100644 index 3db57f17..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_ViewportHelpers.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -struct ViewportHelpers -{ - ViewportHelpers() = delete; - - static bool wouldScrollOnEvent (const Viewport* vp, const MouseInputSource& src) - { - if (vp != nullptr) - { - switch (vp->getScrollOnDragMode()) - { - case Viewport::ScrollOnDragMode::all: return true; - case Viewport::ScrollOnDragMode::nonHover: return ! src.canHover(); - case Viewport::ScrollOnDragMode::never: return false; - } - } - - return false; - } -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_WindowingHelpers.h b/JuceLibraryCode/modules/juce_gui_basics/detail/juce_WindowingHelpers.h deleted file mode 100644 index a26af054..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/detail/juce_WindowingHelpers.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -struct WindowingHelpers -{ - WindowingHelpers() = delete; - - static Image createIconForFile (const File& file); - - #if JUCE_WINDOWS - static bool isEmbeddedInForegroundProcess (Component* c); - static bool isWindowOnCurrentVirtualDesktop (void*); - #else - static bool isEmbeddedInForegroundProcess (Component*) { return false; } - static bool isWindowOnCurrentVirtualDesktop (void*) { return true; } - #endif - - /* Returns true if this process is in the foreground, or if the viewComponent - is embedded into a window owned by the foreground process. - */ - static bool isForegroundOrEmbeddedProcess (Component* viewComponent) - { - return Process::isForegroundProcess() || isEmbeddedInForegroundProcess (viewComponent); - } -}; - -} // namespace juce::detail diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.h b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.h index f56a2d4d..6e1bb162 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.h +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.h @@ -97,11 +97,10 @@ class JUCE_API DrawableImage : public Drawable Rectangle getDrawableBounds() const override; /** @internal */ Path getOutlineAsPath() const override; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; private: //============================================================================== + std::unique_ptr createAccessibilityHandler() override; bool setImageInternal (const Image&); //============================================================================== diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.cpp index 9bf865ae..361aad61 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.cpp @@ -108,7 +108,7 @@ void DrawableText::setBoundingBox (Parallelogram newBounds) void DrawableText::setFontHeight (float newHeight) { - if (! approximatelyEqual (fontHeight, newHeight)) + if (fontHeight != newHeight) { fontHeight = newHeight; refreshBounds(); @@ -117,7 +117,7 @@ void DrawableText::setFontHeight (float newHeight) void DrawableText::setFontHorizontalScale (float newScale) { - if (! approximatelyEqual (fontHScale, newScale)) + if (fontHScale != newScale) { fontHScale = newScale; refreshBounds(); diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.h b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.h index 61c0680c..76ba450d 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.h +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.h @@ -98,8 +98,6 @@ class JUCE_API DrawableText : public Drawable Path getOutlineAsPath() const override; /** @internal */ bool replaceColour (Colour originalColour, Colour replacementColour) override; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; private: //============================================================================== @@ -110,6 +108,7 @@ class JUCE_API DrawableText : public Drawable Colour colour; Justification justification; + std::unique_ptr createAccessibilityHandler() override; void refreshBounds(); Rectangle getTextArea (float width, float height) const; AffineTransform getTextTransform (float width, float height) const; diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index 756e2329..c96a8622 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -189,8 +189,8 @@ class SVGState } else { - if (approximatelyEqual (viewBoxW, 0.0f)) newState.viewBoxW = newState.width; - if (approximatelyEqual (viewBoxH, 0.0f)) newState.viewBoxH = newState.height; + if (viewBoxW == 0.0f) newState.viewBoxW = newState.width; + if (viewBoxH == 0.0f) newState.viewBoxH = newState.height; } newState.parseSubElements (xml, *drawable); diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ContentSharer.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ContentSharer.cpp index 25136a26..eb1c7514 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ContentSharer.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ContentSharer.cpp @@ -26,49 +26,247 @@ namespace juce { -ScopedMessageBox ContentSharer::shareFilesScoped (const Array& files, - Callback callback, - Component* parent) +#if JUCE_CONTENT_SHARING +//============================================================================== +class ContentSharer::PrepareImagesThread : private Thread { - auto impl = detail::ScopedContentSharerInterface::shareFiles (files, parent); - return detail::ConcreteScopedContentSharerImpl::show (std::move (impl), std::move (callback)); -} +public: + PrepareImagesThread (ContentSharer& cs, const Array& imagesToUse, + ImageFileFormat* imageFileFormatToUse) + : Thread ("ContentSharer::PrepareImagesThread"), + owner (cs), + images (imagesToUse), + imageFileFormat (imageFileFormatToUse == nullptr ? new PNGImageFormat() + : imageFileFormatToUse), + extension (imageFileFormat->getFormatName().toLowerCase()) + { + startThread(); + } + + ~PrepareImagesThread() override + { + signalThreadShouldExit(); + waitForThreadToExit (10000); + } + +private: + void run() override + { + for (const auto& image : images) + { + if (threadShouldExit()) + return; + + File tempFile = File::createTempFile (extension); + + if (! tempFile.create().wasOk()) + break; + + std::unique_ptr outputStream (tempFile.createOutputStream()); + + if (outputStream == nullptr) + break; + + if (imageFileFormat->writeImageToStream (image, *outputStream)) + owner.temporaryFiles.add (tempFile); + } + + finish(); + } + + void finish() + { + MessageManager::callAsync ([this]() { owner.filesToSharePrepared(); }); + } + + ContentSharer& owner; + const Array images; + std::unique_ptr imageFileFormat; + String extension; +}; + +//============================================================================== +class ContentSharer::PrepareDataThread : private Thread +{ +public: + PrepareDataThread (ContentSharer& cs, const MemoryBlock& mb) + : Thread ("ContentSharer::PrepareDataThread"), + owner (cs), + data (mb) + { + startThread(); + } + + ~PrepareDataThread() override + { + signalThreadShouldExit(); + waitForThreadToExit (10000); + } + +private: + void run() override + { + File tempFile = File::createTempFile ("data"); + + if (tempFile.create().wasOk()) + { + if (auto outputStream = std::unique_ptr (tempFile.createOutputStream())) + { + size_t pos = 0; + size_t totalSize = data.getSize(); + + while (pos < totalSize) + { + if (threadShouldExit()) + return; + + size_t numToWrite = std::min ((size_t) 8192, totalSize - pos); + + outputStream->write (data.begin() + pos, numToWrite); + + pos += numToWrite; + } + + owner.temporaryFiles.add (tempFile); + } + } + + finish(); + } + + void finish() + { + MessageManager::callAsync ([this]() { owner.filesToSharePrepared(); }); + } + + ContentSharer& owner; + const MemoryBlock data; +}; +#endif + +//============================================================================== +JUCE_IMPLEMENT_SINGLETON (ContentSharer) -ScopedMessageBox ContentSharer::shareTextScoped (const String& text, - Callback callback, - Component* parent) +ContentSharer::ContentSharer() {} +ContentSharer::~ContentSharer() { clearSingletonInstance(); } + +void ContentSharer::shareFiles ([[maybe_unused]] const Array& files, + std::function callbackToUse) { - auto impl = detail::ScopedContentSharerInterface::shareText (text, parent); - return detail::ConcreteScopedContentSharerImpl::show (std::move (impl), std::move (callback)); + #if JUCE_CONTENT_SHARING + startNewShare (callbackToUse); + pimpl->shareFiles (files); + #else + // Content sharing is not available on this platform! + jassertfalse; + + if (callbackToUse) + callbackToUse (false, "Content sharing is not available on this platform!"); + #endif } -ScopedMessageBox ContentSharer::shareImagesScoped (const Array& images, - std::unique_ptr format, - Callback callback, - Component* parent) +#if JUCE_CONTENT_SHARING +void ContentSharer::startNewShare (std::function callbackToUse) { - auto impl = detail::ScopedContentSharerInterface::shareImages (images, std::move (format), parent); - return detail::ConcreteScopedContentSharerImpl::show (std::move (impl), std::move (callback)); + // You should not start another sharing operation before the previous one is finished. + // Forcibly stopping a previous sharing operation is rarely a good idea! + jassert (pimpl == nullptr); + pimpl.reset(); + + prepareDataThread = nullptr; + prepareImagesThread = nullptr; + + deleteTemporaryFiles(); + + // You need to pass a valid callback. + jassert (callbackToUse); + callback = std::move (callbackToUse); + + pimpl.reset (createPimpl()); } +#endif -ScopedMessageBox ContentSharer::shareDataScoped (const MemoryBlock& mb, - Callback callback, - Component* parent) +void ContentSharer::shareText ([[maybe_unused]] const String& text, + std::function callbackToUse) { - auto impl = detail::ScopedContentSharerInterface::shareData (mb, parent); - return detail::ConcreteScopedContentSharerImpl::show (std::move (impl), std::move (callback)); + #if JUCE_CONTENT_SHARING + startNewShare (callbackToUse); + pimpl->shareText (text); + #else + // Content sharing is not available on this platform! + jassertfalse; + + if (callbackToUse) + callbackToUse (false, "Content sharing is not available on this platform!"); + #endif } -#if ! (JUCE_CONTENT_SHARING && (JUCE_IOS || JUCE_ANDROID)) -auto detail::ScopedContentSharerInterface::shareFiles (const Array&, Component*) -> std::unique_ptr +void ContentSharer::shareImages ([[maybe_unused]] const Array& images, + std::function callbackToUse, + [[maybe_unused]] ImageFileFormat* imageFileFormatToUse) { - return std::make_unique(); + #if JUCE_CONTENT_SHARING + startNewShare (callbackToUse); + prepareImagesThread.reset (new PrepareImagesThread (*this, images, imageFileFormatToUse)); + #else + // Content sharing is not available on this platform! + jassertfalse; + + if (callbackToUse) + callbackToUse (false, "Content sharing is not available on this platform!"); + #endif } -auto detail::ScopedContentSharerInterface::shareText (const String&, Component*) -> std::unique_ptr +#if JUCE_CONTENT_SHARING +void ContentSharer::filesToSharePrepared() { - return std::make_unique(); + Array urls; + + for (const auto& tempFile : temporaryFiles) + urls.add (URL (tempFile)); + + prepareImagesThread = nullptr; + prepareDataThread = nullptr; + + pimpl->shareFiles (urls); } #endif +void ContentSharer::shareData ([[maybe_unused]] const MemoryBlock& mb, + std::function callbackToUse) +{ + #if JUCE_CONTENT_SHARING + startNewShare (callbackToUse); + prepareDataThread.reset (new PrepareDataThread (*this, mb)); + #else + if (callbackToUse) + callbackToUse (false, "Content sharing not available on this platform!"); + #endif +} + +void ContentSharer::sharingFinished (bool succeeded, const String& errorDescription) +{ + deleteTemporaryFiles(); + + std::function cb; + std::swap (cb, callback); + + String error (errorDescription); + + #if JUCE_CONTENT_SHARING + pimpl.reset(); + #endif + + if (cb) + cb (succeeded, error); +} + +void ContentSharer::deleteTemporaryFiles() +{ + for (auto& f : temporaryFiles) + f.deleteFile(); + + temporaryFiles.clear(); +} + } // namespace juce diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ContentSharer.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ContentSharer.h index fee6b76f..1edc79f3 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ContentSharer.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ContentSharer.h @@ -23,30 +23,21 @@ ============================================================================== */ +#pragma once + namespace juce { -//============================================================================== -/** - Functions that allow sharing content between apps and devices. +/** A singleton class responsible for sharing content between apps and devices. You can share text, images, files or an arbitrary data block. @tags{GUI} */ -class JUCE_API ContentSharer +class JUCE_API ContentSharer : public DeletedAtShutdown { public: - ContentSharer() = delete; - - /** A callback of this type is passed when starting a content sharing - session. - - When the session ends, the function will receive a flag indicating - whether the session was successful. In the case of failure, the - errorText argument may hold a string describing the problem. - */ - using Callback = std::function; + JUCE_DECLARE_SINGLETON (ContentSharer, false) /** Shares the given files. Each URL should be either a full file path or it should point to a resource within the application bundle. For @@ -59,16 +50,9 @@ class JUCE_API ContentSharer Sadly on Android the returned success flag may be wrong as there is no standard way the sharing targets report if the sharing operation succeeded. Also, the optional error message is always empty on Android. - - @param files the files to share - @param callback a callback that will be called on the main thread - when the sharing session ends - @param parent the component that should be used to host the - sharing view */ - [[nodiscard]] static ScopedMessageBox shareFilesScoped (const Array& files, - Callback callback, - Component* parent = nullptr); + void shareFiles (const Array& files, + std::function callback); /** Shares the given text. @@ -76,16 +60,9 @@ class JUCE_API ContentSharer Sadly on Android the returned success flag may be wrong as there is no standard way the sharing targets report if the sharing operation succeeded. Also, the optional error message is always empty on Android. - - @param text the text to share - @param callback a callback that will be called on the main thread - when the sharing session ends - @param parent the component that should be used to host the - sharing view */ - [[nodiscard]] static ScopedMessageBox shareTextScoped (const String& text, - Callback callback, - Component* parent = nullptr); + void shareText (const String& text, + std::function callback); /** A convenience function to share an image. This is useful when you have images loaded in memory. The images will be written to temporary files first, so if @@ -107,20 +84,10 @@ class JUCE_API ContentSharer Sadly on Android the returned success flag may be wrong as there is no standard way the sharing targets report if the sharing operation succeeded. Also, the optional error message is always empty on Android. - - @param images the images to share - @param format the file format to use when saving the images. - If no format is provided, a sensible default will - be used. - @param callback a callback that will be called on the main thread - when the sharing session ends - @param parent the component that should be used to host the - sharing view */ - [[nodiscard]] static ScopedMessageBox shareImagesScoped (const Array& images, - std::unique_ptr format, - Callback callback, - Component* parent = nullptr); + void shareImages (const Array& images, + std::function callback, + ImageFileFormat* imageFileFormatToUse = nullptr); /** A convenience function to share arbitrary data. The data will be written to a temporary file and then that file will be shared. If you have @@ -130,16 +97,47 @@ class JUCE_API ContentSharer Sadly on Android the returned success flag may be wrong as there is no standard way the sharing targets report if the sharing operation succeeded. Also, the optional error message is always empty on Android. - - @param mb the data to share - @param callback a callback that will be called on the main thread - when the sharing session ends - @param parent the component that should be used to host the - sharing view */ - [[nodiscard]] static ScopedMessageBox shareDataScoped (const MemoryBlock& mb, - Callback callback, - Component* parent = nullptr); + void shareData (const MemoryBlock& mb, + std::function callback); + +private: + ContentSharer(); + ~ContentSharer(); + + Array temporaryFiles; + + std::function callback; + + #if JUCE_CONTENT_SHARING + struct Pimpl + { + virtual ~Pimpl() {} + virtual void shareFiles (const Array& files) = 0; + virtual void shareText (const String& text) = 0; + }; + + std::unique_ptr pimpl; + Pimpl* createPimpl(); + + void startNewShare (std::function); + + class ContentSharerNativeImpl; + friend class ContentSharerNativeImpl; + + class PrepareImagesThread; + friend class PrepareImagesThread; + std::unique_ptr prepareImagesThread; + + class PrepareDataThread; + friend class PrepareDataThread; + std::unique_ptr prepareDataThread; + + void filesToSharePrepared(); + #endif + + void deleteTemporaryFiles(); + void sharingFinished (bool, const String&); }; } // namespace juce diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp index d55d3db3..13e4f9d7 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp @@ -371,6 +371,7 @@ void FileBrowserComponent::lookAndFeelChanged() filenameBox.applyColourToAllText (findColour (filenameBoxTextColourId)); resized(); + repaint(); } //============================================================================== @@ -604,7 +605,7 @@ void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPa void FileBrowserComponent::timerCallback() { - const auto isProcessActive = detail::WindowingHelpers::isForegroundOrEmbeddedProcess (this); + const auto isProcessActive = isForegroundOrEmbeddedProcess (this); if (wasProcessActive != isProcessActive) { diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h index dc4409a7..e70857bc 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h @@ -252,8 +252,6 @@ class JUCE_API FileBrowserComponent : public Component, FilePreviewComponent* getPreviewComponent() const noexcept; /** @internal */ DirectoryContentsDisplayComponent* getDisplayComponent() const noexcept; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; protected: /** Returns a list of names and paths for the default places the user might want to look. @@ -285,6 +283,7 @@ class JUCE_API FileBrowserComponent : public Component, TimeSliceThread thread; bool wasProcessActive; + std::unique_ptr createAccessibilityHandler() override; void timerCallback() override; void sendListenerChangeMessage(); bool isFileOrDirSuitable (const File&) const; diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp index 00b111f0..a85f57ee 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp @@ -165,7 +165,7 @@ bool FileChooser::browseForDirectory() bool FileChooser::showDialog (const int flags, FilePreviewComponent* const previewComp) { - detail::FocusRestorer focusRestorer; + FocusRestorer focusRestorer; pimpl = createPimpl (flags, previewComp); pimpl->runModally(); diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp index 8ec77d0b..814c73ad 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp @@ -115,7 +115,7 @@ FileChooserDialogBox::FileChooserDialogBox (const String& name, if (parentComp != nullptr) parentComp->addAndMakeVisible (this); else - setAlwaysOnTop (WindowUtils::areThereAnyAlwaysOnTopWindows()); + setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows()); } FileChooserDialogBox::~FileChooserDialogBox() @@ -182,26 +182,28 @@ void FileChooserDialogBox::fileDoubleClicked (const File&) void FileChooserDialogBox::fileClicked (const File&, const MouseEvent&) {} void FileChooserDialogBox::browserRootChanged (const File&) {} +void FileChooserDialogBox::okToOverwriteFileCallback (int result, FileChooserDialogBox* box) +{ + if (result != 0 && box != nullptr) + box->exitModalState (1); +} + void FileChooserDialogBox::okButtonPressed() { if (warnAboutOverwritingExistingFiles && content->chooserComponent.isSaveMode() && content->chooserComponent.getSelectedFile(0).exists()) { - auto options = MessageBoxOptions::makeOptionsOkCancel (MessageBoxIconType::WarningIcon, - TRANS ("File already exists"), - TRANS ("There's already a file called: FLNM") - .replace ("FLNM", content->chooserComponent.getSelectedFile(0).getFullPathName()) - + "\n\n" - + TRANS ("Are you sure you want to overwrite it?"), - TRANS ("Overwrite"), - TRANS ("Cancel"), - this); - messageBox = AlertWindow::showScopedAsync (options, [this] (int result) - { - if (result != 0) - exitModalState (1); - }); + AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, + TRANS("File already exists"), + TRANS("There's already a file called: FLNM") + .replace ("FLNM", content->chooserComponent.getSelectedFile(0).getFullPathName()) + + "\n\n" + + TRANS("Are you sure you want to overwrite it?"), + TRANS("Overwrite"), + TRANS("Cancel"), + this, + ModalCallbackFunction::forComponent (okToOverwriteFileCallback, this)); } else { @@ -249,12 +251,9 @@ void FileChooserDialogBox::createNewFolderConfirmed (const String& nameFromDialo auto parent = content->chooserComponent.getRoot(); if (! parent.getChildFile (name).createDirectory()) - { - auto options = MessageBoxOptions::makeOptionsOk (MessageBoxIconType::WarningIcon, - TRANS ("New Folder"), - TRANS ("Couldn't create the folder!")); - messageBox = AlertWindow::showScopedAsync (options, nullptr); - } + AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, + TRANS ("New Folder"), + TRANS ("Couldn't create the folder!")); content->chooserComponent.refresh(); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h index 972d0e5c..bf1e147d 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h @@ -158,10 +158,9 @@ class JUCE_API FileChooserDialogBox : public ResizableWindow, void createNewFolder(); void createNewFolderConfirmed (const String& name); + static void okToOverwriteFileCallback (int result, FileChooserDialogBox*); static void createNewFolderCallback (int result, FileChooserDialogBox*, Component::SafePointer); - ScopedMessageBox messageBox; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox) }; diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp index 853ef484..073feb9e 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp @@ -26,6 +26,9 @@ namespace juce { +Image juce_createIconForFile (const File& file); + + //============================================================================== FileListComponent::FileListComponent (DirectoryContentsList& listToShow) : ListBox ({}, this), @@ -100,7 +103,6 @@ void FileListComponent::changeListenerCallback (ChangeBroadcaster*) //============================================================================== class FileListComponent::ItemComponent : public Component, - public TooltipClient, private TimeSliceClient, private AsyncUpdater { @@ -191,11 +193,6 @@ class FileListComponent::ItemComponent : public Component, repaint(); } - String getTooltip() override - { - return owner.getTooltipForRow (index); - } - private: //============================================================================== FileListComponent& owner; @@ -220,7 +217,7 @@ class FileListComponent::ItemComponent : public Component, if (im.isNull() && ! onlyUpdateIfCached) { - im = detail::WindowingHelpers::createIconForFile (file); + im = juce_createIconForFile (file); if (im.isValid()) ImageCache::addImageToCache (im, hashCode); diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp index 1799e738..65ceb66a 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp @@ -129,7 +129,7 @@ void FileSearchPathListComponent::paintListBoxItem (int rowNumber, Graphics& g, f.setHorizontalScale (0.9f); g.setFont (f); - g.drawText (path.getRawString (rowNumber), + g.drawText (path[rowNumber].getFullPathName(), 4, 0, width - 6, height, Justification::centredLeft, true); } @@ -145,7 +145,7 @@ void FileSearchPathListComponent::deleteKeyPressed (int row) void FileSearchPathListComponent::returnKeyPressed (int row) { - chooser = std::make_unique (TRANS("Change folder..."), path.getRawString (row), "*"); + chooser = std::make_unique (TRANS("Change folder..."), path[row], "*"); auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories; chooser->launchAsync (chooserFlags, [this, row] (const FileChooser& fc) @@ -258,7 +258,7 @@ void FileSearchPathListComponent::moveSelection (int delta) if (currentRow != newRow) { - const auto f = File::createFileWithoutCheckingPath (path.getRawString (currentRow)); + auto f = path[currentRow]; path.remove (currentRow); path.add (f, newRow); listBox.selectRow (newRow); diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp index 3ebc44c6..916193cf 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp @@ -26,83 +26,45 @@ namespace juce { -template -int threeWayCompare (const T& a, const T& b) -{ - if (a < b) return -1; - if (b < a) return 1; - return 0; -} - -int threeWayCompare (const String& a, const String& b); -int threeWayCompare (const String& a, const String& b) -{ - return a.compare (b); -} - -struct ReverseCompareString -{ - String value; -}; - -int threeWayCompare (const ReverseCompareString& a, const ReverseCompareString& b); -int threeWayCompare (const ReverseCompareString& a, const ReverseCompareString& b) -{ - return b.value.compare (a.value); -} - -template -constexpr int threeWayCompareImpl (const std::tuple& a, const std::tuple& b) -{ - if constexpr (position == sizeof... (Ts)) - { - ignoreUnused (a, b); - return 0; - } - else - { - const auto head = threeWayCompare (std::get (a), std::get (b)); - - if (head != 0) - return head; - - return threeWayCompareImpl (a, b); - } -} - -template -constexpr int threeWayCompare (const std::tuple& a, const std::tuple& b) -{ - return threeWayCompareImpl<0> (a, b); -} - //============================================================================== class FileListTreeItem : public TreeViewItem, private TimeSliceClient, - private AsyncUpdater + private AsyncUpdater, + private ChangeListener { public: FileListTreeItem (FileTreeComponent& treeComp, + DirectoryContentsList* parentContents, + int indexInContents, const File& f, TimeSliceThread& t) : file (f), owner (treeComp), + parentContentsList (parentContents), + indexInContentsList (indexInContents), + subContentsList (nullptr, false), thread (t) { - } + DirectoryContentsList::FileInfo fileInfo; - void update (const DirectoryContentsList::FileInfo& fileInfo) - { - fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize); - modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M"); - isDirectory = fileInfo.isDirectory; - repaintItem(); + if (parentContents != nullptr + && parentContents->getFileInfo (indexInContents, fileInfo)) + { + fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize); + modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M"); + isDirectory = fileInfo.isDirectory; + } + else + { + isDirectory = true; + } } ~FileListTreeItem() override { thread.removeTimeSliceClient (this); clearSubItems(); + removeSubContentsList(); } //============================================================================== @@ -114,7 +76,88 @@ class FileListTreeItem : public TreeViewItem, void itemOpennessChanged (bool isNowOpen) override { - NullCheckedInvocation::invoke (onOpennessChanged, file, isNowOpen); + if (isNowOpen) + { + clearSubItems(); + + isDirectory = file.isDirectory(); + + if (isDirectory) + { + if (subContentsList == nullptr && parentContentsList != nullptr) + { + auto l = new DirectoryContentsList (parentContentsList->getFilter(), thread); + + l->setDirectory (file, + parentContentsList->isFindingDirectories(), + parentContentsList->isFindingFiles()); + + setSubContentsList (l, true); + } + + changeListenerCallback (nullptr); + } + } + } + + void removeSubContentsList() + { + if (subContentsList != nullptr) + { + subContentsList->removeChangeListener (this); + subContentsList.reset(); + } + } + + void setSubContentsList (DirectoryContentsList* newList, const bool canDeleteList) + { + removeSubContentsList(); + + subContentsList = OptionalScopedPointer (newList, canDeleteList); + newList->addChangeListener (this); + } + + void selectFile (const File& target) + { + if (file == target) + { + setSelected (true, true); + return; + } + + if (subContentsList != nullptr && subContentsList->isStillLoading()) + { + pendingFileSelection.emplace (*this, target); + return; + } + + pendingFileSelection.reset(); + + if (! target.isAChildOf (file)) + return; + + setOpen (true); + + for (int i = 0; i < getNumSubItems(); ++i) + if (auto* f = dynamic_cast (getSubItem (i))) + f->selectFile (target); + } + + void changeListenerCallback (ChangeBroadcaster*) override + { + rebuildItemsFromContentList(); + } + + void rebuildItemsFromContentList() + { + clearSubItems(); + + if (isOpen() && subContentsList != nullptr) + { + for (int i = 0; i < subContentsList->getNumFiles(); ++i) + addSubItem (new FileListTreeItem (owner, subContentsList, i, + subContentsList->getFile(i), thread)); + } } void paintItem (Graphics& g, int width, int height) override @@ -133,7 +176,7 @@ class FileListTreeItem : public TreeViewItem, file, file.getFileName(), &icon, fileSize, modTime, isDirectory, isSelected(), - getIndexInParent(), owner); + indexInContentsList, owner); } String getAccessibilityName() override @@ -170,11 +213,40 @@ class FileListTreeItem : public TreeViewItem, } const File file; - std::function onOpennessChanged; private: + class PendingFileSelection : private Timer + { + public: + PendingFileSelection (FileListTreeItem& item, const File& f) + : owner (item), fileToSelect (f) + { + startTimer (10); + } + + ~PendingFileSelection() override + { + stopTimer(); + } + + private: + void timerCallback() override + { + // Take a copy of the file here, in case this PendingFileSelection + // object is destroyed during the call to selectFile. + owner.selectFile (File { fileToSelect }); + } + + FileListTreeItem& owner; + File fileToSelect; + }; + + Optional pendingFileSelection; FileTreeComponent& owner; - bool isDirectory = false; + DirectoryContentsList* parentContentsList; + int indexInContentsList; + OptionalScopedPointer subContentsList; + bool isDirectory; TimeSliceThread& thread; CriticalSection iconUpdate; Image icon; @@ -189,7 +261,7 @@ class FileListTreeItem : public TreeViewItem, if (im.isNull() && ! onlyUpdateIfCached) { - im = detail::WindowingHelpers::createIconForFile (file); + im = juce_createIconForFile (file); if (im.isValid()) ImageCache::addImageToCache (im, hashCode); @@ -210,349 +282,11 @@ class FileListTreeItem : public TreeViewItem, JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem) }; -class DirectoryScanner : private ChangeListener -{ -public: - struct Listener - { - virtual ~Listener() = default; - - virtual void rootChanged() = 0; - virtual void directoryChanged (const DirectoryContentsList&) = 0; - }; - - DirectoryScanner (DirectoryContentsList& rootIn, Listener& listenerIn) - : root (rootIn), listener (listenerIn) - { - root.addChangeListener (this); - } - - ~DirectoryScanner() override - { - root.removeChangeListener (this); - } - - void refresh() - { - root.refresh(); - } - - void open (const File& f) - { - auto& contentsList = [&]() -> auto& - { - if (auto it = contentsLists.find (f); it != contentsLists.end()) - return it->second; - - auto insertion = contentsLists.emplace (std::piecewise_construct, - std::forward_as_tuple (f), - std::forward_as_tuple (root.getFilter(), - root.getTimeSliceThread())); - return insertion.first->second; - }(); - - contentsList.addChangeListener (this); - contentsList.setDirectory (f, true, true); - contentsList.refresh(); - } - - void close (const File& f) - { - if (auto it = contentsLists.find (f); it != contentsLists.end()) - contentsLists.erase (it); - } - - File getRootDirectory() const - { - return root.getDirectory(); - } - - bool isStillLoading() const - { - return std::any_of (contentsLists.begin(), - contentsLists.end(), - [] (const auto& it) - { - return it.second.isStillLoading(); - }); - } - -private: - void changeListenerCallback (ChangeBroadcaster* source) override - { - auto* sourceList = static_cast (source); - - if (sourceList == &root) - { - if (std::exchange (lastDirectory, root.getDirectory()) != root.getDirectory()) - { - contentsLists.clear(); - listener.rootChanged(); - } - else - { - for (auto& contentsList : contentsLists) - contentsList.second.refresh(); - } - } - - listener.directoryChanged (*sourceList); - } - - DirectoryContentsList& root; - Listener& listener; - File lastDirectory; - std::map contentsLists; -}; - -struct FileEntry -{ - String path; - bool isDirectory; - - int compareWindows (const FileEntry& other) const - { - const auto toTuple = [] (const auto& x) { return std::tuple (! x.isDirectory, x.path.toLowerCase()); }; - return threeWayCompare (toTuple (*this), toTuple (other)); - } - - int compareLinux (const FileEntry& other) const - { - const auto toTuple = [] (const auto& x) { return std::tuple (x.path.toUpperCase(), ReverseCompareString { x.path }); }; - return threeWayCompare (toTuple (*this), toTuple (other)); - } - - int compareDefault (const FileEntry& other) const - { - return threeWayCompare (path.toLowerCase(), other.path.toLowerCase()); - } -}; - -class OSDependentFileComparisonRules -{ -public: - explicit OSDependentFileComparisonRules (SystemStats::OperatingSystemType systemTypeIn) - : systemType (systemTypeIn) - {} - - int compare (const FileEntry& first, const FileEntry& second) const - { - if ((systemType & SystemStats::OperatingSystemType::Windows) != 0) - return first.compareWindows (second); - - if ((systemType & SystemStats::OperatingSystemType::Linux) != 0) - return first.compareLinux (second); - - return first.compareDefault (second); - } - - bool operator() (const FileEntry& first, const FileEntry& second) const - { - return compare (first, second) < 0; - } - -private: - SystemStats::OperatingSystemType systemType; -}; - -class FileTreeComponent::Controller : private DirectoryScanner::Listener -{ -public: - explicit Controller (FileTreeComponent& ownerIn) - : owner (ownerIn), - scanner (owner.directoryContentsList, *this) - { - refresh(); - } - - ~Controller() override - { - owner.deleteRootItem(); - } - - void refresh() - { - scanner.refresh(); - } - - void selectFile (const File& target) - { - pendingFileSelection.emplace (target); - tryResolvePendingFileSelection(); - } - -private: - template - static void forEachItemRecursive (TreeViewItem* item, ItemCallback&& cb) - { - if (item == nullptr) - return; - - if (auto* fileListItem = dynamic_cast (item)) - cb (fileListItem); - - for (int i = 0; i < item->getNumSubItems(); ++i) - forEachItemRecursive (item->getSubItem (i), cb); - } - - //============================================================================== - void rootChanged() override - { - owner.deleteRootItem(); - treeItemForFile.clear(); - owner.setRootItem (createNewItem (scanner.getRootDirectory()).release()); - } - - void directoryChanged (const DirectoryContentsList& contentsList) override - { - auto* parentItem = [&]() -> FileListTreeItem* - { - if (auto it = treeItemForFile.find (contentsList.getDirectory()); it != treeItemForFile.end()) - return it->second; - - return nullptr; - }(); - - if (parentItem == nullptr) - { - jassertfalse; - return; - } - - for (int i = 0; i < contentsList.getNumFiles(); ++i) - { - auto file = contentsList.getFile (i); - - DirectoryContentsList::FileInfo fileInfo; - contentsList.getFileInfo (i, fileInfo); - - auto* item = [&] - { - if (auto it = treeItemForFile.find (file); it != treeItemForFile.end()) - return it->second; - - auto* newItem = createNewItem (file).release(); - parentItem->addSubItem (newItem); - return newItem; - }(); - - if (item->isOpen() && fileInfo.isDirectory) - scanner.open (item->file); - - item->update (fileInfo); - } - - if (contentsList.isStillLoading()) - return; - - std::set allFiles; - - for (int i = 0; i < contentsList.getNumFiles(); ++i) - allFiles.insert (contentsList.getFile (i)); - - for (int i = 0; i < parentItem->getNumSubItems();) - { - auto* fileItem = dynamic_cast (parentItem->getSubItem (i)); - - if (fileItem != nullptr && allFiles.count (fileItem->file) == 0) - { - forEachItemRecursive (parentItem->getSubItem (i), - [this] (auto* item) - { - scanner.close (item->file); - treeItemForFile.erase (item->file); - }); - - parentItem->removeSubItem (i); - } - else - { - ++i; - } - } - - struct Comparator - { - // The different OSes compare and order files in different ways. This function aims - // to match these different rules of comparison to mimic other FileBrowserComponent - // view modes where we don't need to order the results, and can just rely on the - // ordering of the list provided by the OS. - static int compareElements (TreeViewItem* first, TreeViewItem* second) - { - auto* item1 = dynamic_cast (first); - auto* item2 = dynamic_cast (second); - - if (item1 == nullptr || item2 == nullptr) - return 0; - - static const OSDependentFileComparisonRules comparisonRules { SystemStats::getOperatingSystemType() }; - - return comparisonRules.compare ({ item1->file.getFullPathName(), item1->file.isDirectory() }, - { item2->file.getFullPathName(), item2->file.isDirectory() }); - } - }; - - static Comparator comparator; - parentItem->sortSubItems (comparator); - tryResolvePendingFileSelection(); - } - - std::unique_ptr createNewItem (const File& file) - { - auto newItem = std::make_unique (owner, - file, - owner.directoryContentsList.getTimeSliceThread()); - - newItem->onOpennessChanged = [this, itemPtr = newItem.get()] (const auto& f, auto isOpen) - { - if (isOpen) - { - scanner.open (f); - } - else - { - forEachItemRecursive (itemPtr, - [this] (auto* item) - { - scanner.close (item->file); - }); - } - }; - - treeItemForFile[file] = newItem.get(); - return newItem; - } - - void tryResolvePendingFileSelection() - { - if (! pendingFileSelection.has_value()) - return; - - if (auto item = treeItemForFile.find (*pendingFileSelection); item != treeItemForFile.end()) - { - item->second->setSelected (true, true); - pendingFileSelection.reset(); - return; - } - - if (owner.directoryContentsList.isStillLoading() || scanner.isStillLoading()) - return; - - owner.clearSelectedItems(); - } - - FileTreeComponent& owner; - std::map treeItemForFile; - DirectoryScanner scanner; - std::optional pendingFileSelection; -}; - //============================================================================== FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow) : DirectoryContentsDisplayComponent (listToShow), itemHeight (22) { - controller = std::make_unique (*this); setRootItemVisible (false); refresh(); } @@ -564,7 +298,13 @@ FileTreeComponent::~FileTreeComponent() void FileTreeComponent::refresh() { - controller->refresh(); + deleteRootItem(); + + auto root = new FileListTreeItem (*this, nullptr, 0, directoryContentsList.getDirectory(), + directoryContentsList.getTimeSliceThread()); + + root->setSubContentsList (&directoryContentsList, false); + setRootItem (root); } //============================================================================== @@ -593,7 +333,8 @@ void FileTreeComponent::setDragAndDropDescription (const String& description) void FileTreeComponent::setSelectedFile (const File& target) { - controller->selectFile (target); + if (auto* t = dynamic_cast (getRootItem())) + t->selectFile (target); } void FileTreeComponent::setItemHeight (int newHeight) @@ -607,75 +348,4 @@ void FileTreeComponent::setItemHeight (int newHeight) } } -#if JUCE_UNIT_TESTS - -class FileTreeComponentTests : public UnitTest -{ -public: - //============================================================================== - FileTreeComponentTests() : UnitTest ("FileTreeComponentTests", UnitTestCategories::gui) {} - - void runTest() override - { - const auto checkOrder = [] (const auto& orderedFiles, const std::vector& expected) - { - return std::equal (orderedFiles.begin(), orderedFiles.end(), - expected.begin(), expected.end(), - [] (const auto& entry, const auto& expectedPath) { return entry.path == expectedPath; }); - }; - - const auto doSort = [] (const auto platform, auto& range) - { - std::sort (range.begin(), range.end(), OSDependentFileComparisonRules { platform }); - }; - - beginTest ("Test Linux filename ordering"); - { - std::vector filesToOrder { { "_test", false }, - { "Atest", false }, - { "atest", false } }; - - doSort (SystemStats::OperatingSystemType::Linux, filesToOrder); - - expect (checkOrder (filesToOrder, { "atest", "Atest", "_test" })); - } - - beginTest ("Test Windows filename ordering"); - { - std::vector filesToOrder { { "cmake_install.cmake", false }, - { "CMakeFiles", true }, - { "JUCEConfig.cmake", false }, - { "tools", true }, - { "cmakefiles.cmake", false } }; - - doSort (SystemStats::OperatingSystemType::Windows, filesToOrder); - - expect (checkOrder (filesToOrder, { "CMakeFiles", - "tools", - "cmake_install.cmake", - "cmakefiles.cmake", - "JUCEConfig.cmake" })); - } - - beginTest ("Test MacOS filename ordering"); - { - std::vector filesToOrder { { "cmake_install.cmake", false }, - { "CMakeFiles", true }, - { "tools", true }, - { "JUCEConfig.cmake", false } }; - - doSort (SystemStats::OperatingSystemType::MacOSX, filesToOrder); - - expect (checkOrder (filesToOrder, { "cmake_install.cmake", - "CMakeFiles", - "JUCEConfig.cmake", - "tools" })); - } - } -}; - -static FileTreeComponentTests fileTreeComponentTests; - -#endif - } // namespace juce diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h index 192ce88f..b021bc75 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h @@ -99,9 +99,6 @@ class JUCE_API FileTreeComponent : public TreeView, String dragAndDropDescription; int itemHeight; - class Controller; - std::unique_ptr controller; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileTreeComponent) }; diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.h index 874fb54f..2c354a73 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.h @@ -52,14 +52,13 @@ class JUCE_API ImagePreviewComponent : public FilePreviewComponent, void paint (Graphics&) override; /** @internal */ void timerCallback() override; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; private: File fileToLoad; Image currentThumbnail; String currentDetails; + std::unique_ptr createAccessibilityHandler() override; void getThumbSize (int& w, int& h) const; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePreviewComponent) diff --git a/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp b/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp index 380fe88c..daa71127 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp @@ -107,123 +107,70 @@ #endif //============================================================================== -#include "detail/juce_AccessibilityHelpers.h" -#include "detail/juce_ButtonAccessibilityHandler.h" -#include "detail/juce_ScalingHelpers.h" -#include "detail/juce_ComponentHelpers.h" -#include "detail/juce_FocusHelpers.h" -#include "detail/juce_FocusRestorer.h" -#include "detail/juce_ViewportHelpers.h" -#include "detail/juce_LookAndFeelHelpers.h" -#include "detail/juce_PointerState.h" -#include "detail/juce_CustomMouseCursorInfo.h" -#include "detail/juce_MouseInputSourceImpl.h" -#include "detail/juce_MouseInputSourceList.h" -#include "detail/juce_ToolbarItemDragAndDropOverlayComponent.h" -#include "detail/juce_ScopedMessageBoxInterface.h" -#include "detail/juce_ScopedMessageBoxImpl.h" -#include "detail/juce_ScopedContentSharerInterface.h" -#include "detail/juce_ScopedContentSharerImpl.h" -#include "detail/juce_WindowingHelpers.h" -#include "detail/juce_AlertWindowHelpers.h" -#include "detail/juce_TopLevelWindowManager.h" +#define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN \ + jassert ((MessageManager::getInstanceWithoutCreating() != nullptr \ + && MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager()) \ + || getPeer() == nullptr); -//============================================================================== -#if JUCE_IOS || JUCE_WINDOWS - #include "native/juce_MultiTouchMapper.h" -#endif - -#if JUCE_ANDROID || JUCE_WINDOWS || JUCE_IOS || JUCE_UNIT_TESTS - #include "native/accessibility/juce_AccessibilityTextHelpers.h" -#endif - -#if JUCE_MAC || JUCE_IOS - #include "native/accessibility/juce_AccessibilitySharedCode_mac.mm" - - #if JUCE_IOS - #include "native/juce_UIViewComponentPeer_ios.mm" - #include "native/accessibility/juce_Accessibility_ios.mm" - #include "native/juce_WindowUtils_ios.mm" - #include "native/juce_Windowing_ios.mm" - #include "native/juce_NativeMessageBox_ios.mm" - #include "native/juce_NativeModalWrapperComponent_ios.h" - #include "native/juce_FileChooser_ios.mm" - - #if JUCE_CONTENT_SHARING - #include "native/juce_ContentSharer_ios.cpp" - #endif - - #else - #include "native/accessibility/juce_Accessibility_mac.mm" - #include "native/juce_PerScreenDisplayLinks_mac.h" - #include "native/juce_NSViewComponentPeer_mac.mm" - #include "native/juce_WindowUtils_mac.mm" - #include "native/juce_Windowing_mac.mm" - #include "native/juce_NativeMessageBox_mac.mm" - #include "native/juce_MainMenu_mac.mm" - #include "native/juce_FileChooser_mac.mm" - #endif - - #include "native/juce_MouseCursor_mac.mm" - -#elif JUCE_WINDOWS - #include "native/accessibility/juce_ComInterfaces_windows.h" - #include "native/accessibility/juce_WindowsUIAWrapper_windows.h" - #include "native/accessibility/juce_AccessibilityElement_windows.h" - #include "native/accessibility/juce_UIAHelpers_windows.h" - #include "native/accessibility/juce_UIAProviders_windows.h" - #include "native/accessibility/juce_AccessibilityElement_windows.cpp" - #include "native/accessibility/juce_Accessibility_windows.cpp" - #include "native/juce_WindowsHooks_windows.h" - #include "native/juce_WindowUtils_windows.cpp" - #include "native/juce_Windowing_windows.cpp" - #include "native/juce_WindowsHooks_windows.cpp" - #include "native/juce_NativeMessageBox_windows.cpp" - #include "native/juce_DragAndDrop_windows.cpp" - #include "native/juce_FileChooser_windows.cpp" - -#elif JUCE_LINUX || JUCE_BSD - #include "native/juce_XSymbols_linux.cpp" - #include "native/juce_DragAndDrop_linux.cpp" +namespace juce +{ + bool juce_areThereAnyAlwaysOnTopWindows(); - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant") + bool isEmbeddedInForegroundProcess (Component* c); - #include "native/juce_ScopedWindowAssociation_linux.h" - #include "native/juce_WindowUtils_linux.cpp" - #include "native/juce_Windowing_linux.cpp" - #include "native/juce_NativeMessageBox_linux.cpp" - #include "native/juce_XWindowSystem_linux.cpp" + #if ! JUCE_WINDOWS + bool isEmbeddedInForegroundProcess (Component*) { return false; } + #endif - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + /* Returns true if this process is in the foreground, or if the viewComponent + is embedded into a window owned by the foreground process. + */ + static bool isForegroundOrEmbeddedProcess (Component* viewComponent) + { + return Process::isForegroundProcess() || isEmbeddedInForegroundProcess (viewComponent); + } - #include "native/juce_FileChooser_linux.cpp" + bool isWindowOnCurrentVirtualDesktop (void*); -#elif JUCE_ANDROID + struct CustomMouseCursorInfo + { + ScaledImage image; + Point hotspot; + }; - #include "juce_core/files/juce_common_MimeTypes.h" - #include "native/accessibility/juce_Accessibility_android.cpp" - #include "native/juce_WindowUtils_android.cpp" - #include "native/juce_Windowing_android.cpp" - #include "native/juce_NativeMessageBox_android.cpp" - #include "native/juce_FileChooser_android.cpp" + template + static const AccessibilityHandler* getEnclosingHandlerWithInterface (const AccessibilityHandler* handler, MemberFn fn) + { + if (handler == nullptr) + return nullptr; - #if JUCE_CONTENT_SHARING - #include "native/juce_ContentSharer_android.cpp" - #endif - -#endif + if ((handler->*fn)() != nullptr) + return handler; -//============================================================================== -// Depends on types defined in platform-specific windowing files -#include "mouse/juce_MouseCursor.cpp" + return getEnclosingHandlerWithInterface (handler->getParent(), fn); + } +} // namespace juce -#if JUCE_UNIT_TESTS - #include "native/accessibility/juce_AccessibilityTextHelpers_test.cpp" -#endif +#include "mouse/juce_PointerState.h" -//============================================================================== #include "accessibility/juce_AccessibilityHandler.cpp" -#include "application/juce_Application.cpp" +#include "components/juce_Component.cpp" +#include "components/juce_ComponentListener.cpp" +#include "components/juce_FocusTraverser.cpp" +#include "mouse/juce_MouseInputSource.cpp" +#include "desktop/juce_Displays.cpp" +#include "desktop/juce_Desktop.cpp" +#include "components/juce_ModalComponentManager.cpp" +#include "mouse/juce_ComponentDragger.cpp" +#include "mouse/juce_DragAndDropContainer.cpp" +#include "mouse/juce_MouseEvent.cpp" +#include "mouse/juce_MouseInactivityDetector.cpp" +#include "mouse/juce_MouseListener.cpp" +#include "keyboard/juce_CaretComponent.cpp" +#include "keyboard/juce_KeyboardFocusTraverser.cpp" +#include "keyboard/juce_KeyListener.cpp" +#include "keyboard/juce_KeyPress.cpp" +#include "keyboard/juce_ModifierKeys.cpp" #include "buttons/juce_ArrowButton.cpp" #include "buttons/juce_Button.cpp" #include "buttons/juce_DrawableButton.cpp" @@ -233,17 +180,6 @@ #include "buttons/juce_TextButton.cpp" #include "buttons/juce_ToggleButton.cpp" #include "buttons/juce_ToolbarButton.cpp" -#include "commands/juce_ApplicationCommandInfo.cpp" -#include "commands/juce_ApplicationCommandManager.cpp" -#include "commands/juce_ApplicationCommandTarget.cpp" -#include "commands/juce_KeyPressMappingSet.cpp" -#include "components/juce_Component.cpp" -#include "components/juce_ComponentListener.cpp" -#include "components/juce_FocusTraverser.cpp" -#include "components/juce_ModalComponentManager.cpp" -#include "desktop/juce_Desktop.cpp" -#include "desktop/juce_Displays.cpp" -#include "detail/juce_AccessibilityHelpers.cpp" #include "drawables/juce_Drawable.cpp" #include "drawables/juce_DrawableComposite.cpp" #include "drawables/juce_DrawableImage.cpp" @@ -252,31 +188,22 @@ #include "drawables/juce_DrawableShape.cpp" #include "drawables/juce_DrawableText.cpp" #include "drawables/juce_SVGParser.cpp" -#include "filebrowser/juce_ContentSharer.cpp" #include "filebrowser/juce_DirectoryContentsDisplayComponent.cpp" #include "filebrowser/juce_DirectoryContentsList.cpp" #include "filebrowser/juce_FileBrowserComponent.cpp" #include "filebrowser/juce_FileChooser.cpp" #include "filebrowser/juce_FileChooserDialogBox.cpp" #include "filebrowser/juce_FileListComponent.cpp" +#include "filebrowser/juce_FilenameComponent.cpp" #include "filebrowser/juce_FileSearchPathListComponent.cpp" #include "filebrowser/juce_FileTreeComponent.cpp" -#include "filebrowser/juce_FilenameComponent.cpp" #include "filebrowser/juce_ImagePreviewComponent.cpp" -#include "keyboard/juce_CaretComponent.cpp" -#include "keyboard/juce_KeyListener.cpp" -#include "keyboard/juce_KeyPress.cpp" -#include "keyboard/juce_KeyboardFocusTraverser.cpp" -#include "keyboard/juce_ModifierKeys.cpp" +#include "filebrowser/juce_ContentSharer.cpp" #include "layout/juce_ComponentAnimator.cpp" #include "layout/juce_ComponentBoundsConstrainer.cpp" -#include "layout/juce_BorderedComponentBoundsConstrainer.cpp" #include "layout/juce_ComponentBuilder.cpp" #include "layout/juce_ComponentMovementWatcher.cpp" #include "layout/juce_ConcertinaPanel.cpp" -#include "layout/juce_FlexBox.cpp" -#include "layout/juce_Grid.cpp" -#include "layout/juce_GridItem.cpp" #include "layout/juce_GroupComponent.cpp" #include "layout/juce_MultiDocumentPanel.cpp" #include "layout/juce_ResizableBorderComponent.cpp" @@ -291,26 +218,14 @@ #include "layout/juce_TabbedComponent.cpp" #include "layout/juce_Viewport.cpp" #include "lookandfeel/juce_LookAndFeel.cpp" -#include "lookandfeel/juce_LookAndFeel_V1.cpp" #include "lookandfeel/juce_LookAndFeel_V2.cpp" +#include "lookandfeel/juce_LookAndFeel_V1.cpp" #include "lookandfeel/juce_LookAndFeel_V3.cpp" #include "lookandfeel/juce_LookAndFeel_V4.cpp" -#include "menus/juce_BurgerMenuComponent.cpp" #include "menus/juce_MenuBarComponent.cpp" +#include "menus/juce_BurgerMenuComponent.cpp" #include "menus/juce_MenuBarModel.cpp" #include "menus/juce_PopupMenu.cpp" -#include "misc/juce_BubbleComponent.cpp" -#include "misc/juce_DropShadower.cpp" -#include "misc/juce_FocusOutline.cpp" -#include "misc/juce_JUCESplashScreen.cpp" -#include "mouse/juce_ComponentDragger.cpp" -#include "mouse/juce_DragAndDropContainer.cpp" -#include "mouse/juce_MouseEvent.cpp" -#include "mouse/juce_MouseInactivityDetector.cpp" -#include "mouse/juce_MouseInputSource.cpp" -#include "mouse/juce_MouseListener.cpp" -#include "native/accessibility/juce_Accessibility.cpp" -#include "native/juce_ScopedDPIAwarenessDisabler.cpp" #include "positioning/juce_MarkerList.cpp" #include "positioning/juce_RelativeCoordinate.cpp" #include "positioning/juce_RelativeCoordinatePositioner.cpp" @@ -321,11 +236,11 @@ #include "properties/juce_BooleanPropertyComponent.cpp" #include "properties/juce_ButtonPropertyComponent.cpp" #include "properties/juce_ChoicePropertyComponent.cpp" -#include "properties/juce_MultiChoicePropertyComponent.cpp" #include "properties/juce_PropertyComponent.cpp" #include "properties/juce_PropertyPanel.cpp" #include "properties/juce_SliderPropertyComponent.cpp" #include "properties/juce_TextPropertyComponent.cpp" +#include "properties/juce_MultiChoicePropertyComponent.cpp" #include "widgets/juce_ComboBox.cpp" #include "widgets/juce_ImageComponent.cpp" #include "widgets/juce_Label.cpp" @@ -335,20 +250,213 @@ #include "widgets/juce_TableHeaderComponent.cpp" #include "widgets/juce_TableListBox.cpp" #include "widgets/juce_TextEditor.cpp" -#include "widgets/juce_Toolbar.cpp" #include "widgets/juce_ToolbarItemComponent.cpp" +#include "widgets/juce_Toolbar.cpp" #include "widgets/juce_ToolbarItemPalette.cpp" #include "widgets/juce_TreeView.cpp" -#include "windows/juce_NativeMessageBox.cpp" #include "windows/juce_AlertWindow.cpp" #include "windows/juce_CallOutBox.cpp" #include "windows/juce_ComponentPeer.cpp" #include "windows/juce_DialogWindow.cpp" #include "windows/juce_DocumentWindow.cpp" -#include "windows/juce_MessageBoxOptions.cpp" #include "windows/juce_ResizableWindow.cpp" -#include "windows/juce_ScopedMessageBox.cpp" #include "windows/juce_ThreadWithProgressWindow.cpp" #include "windows/juce_TooltipWindow.cpp" #include "windows/juce_TopLevelWindow.cpp" -#include "windows/juce_VBlankAttachment.cpp" +#include "windows/juce_VBlankAttachement.cpp" +#include "commands/juce_ApplicationCommandInfo.cpp" +#include "commands/juce_ApplicationCommandManager.cpp" +#include "commands/juce_ApplicationCommandTarget.cpp" +#include "commands/juce_KeyPressMappingSet.cpp" +#include "application/juce_Application.cpp" +#include "misc/juce_BubbleComponent.cpp" +#include "misc/juce_DropShadower.cpp" +#include "misc/juce_FocusOutline.cpp" +#include "misc/juce_JUCESplashScreen.cpp" + +#include "layout/juce_FlexBox.cpp" +#include "layout/juce_GridItem.cpp" +#include "layout/juce_Grid.cpp" + +#if JUCE_IOS || JUCE_WINDOWS + #include "native/juce_MultiTouchMapper.h" +#endif + +#if JUCE_ANDROID || JUCE_WINDOWS || JUCE_IOS || JUCE_UNIT_TESTS + #include "native/accessibility/juce_AccessibilityTextHelpers.h" +#endif + +#if JUCE_MAC || JUCE_IOS + #include "native/accessibility/juce_mac_AccessibilitySharedCode.mm" + + #if JUCE_IOS + #include "native/juce_ios_UIViewComponentPeer.mm" + #include "native/accessibility/juce_ios_Accessibility.mm" + #include "native/juce_ios_Windowing.mm" + #include "native/juce_ios_FileChooser.mm" + + #if JUCE_CONTENT_SHARING + #include "native/juce_ios_ContentSharer.cpp" + #endif + + #else + #include "native/accessibility/juce_mac_Accessibility.mm" + #include "native/juce_mac_PerScreenDisplayLinks.h" + #include "native/juce_mac_NSViewComponentPeer.mm" + #include "native/juce_mac_Windowing.mm" + #include "native/juce_mac_MainMenu.mm" + #include "native/juce_mac_FileChooser.mm" + #endif + + #include "native/juce_mac_MouseCursor.mm" + +#elif JUCE_WINDOWS + #include "native/accessibility/juce_win32_ComInterfaces.h" + #include "native/accessibility/juce_win32_WindowsUIAWrapper.h" + #include "native/accessibility/juce_win32_AccessibilityElement.h" + #include "native/accessibility/juce_win32_UIAHelpers.h" + #include "native/accessibility/juce_win32_UIAProviders.h" + #include "native/accessibility/juce_win32_AccessibilityElement.cpp" + #include "native/accessibility/juce_win32_Accessibility.cpp" + #include "native/juce_win32_Windowing.cpp" + #include "native/juce_win32_DragAndDrop.cpp" + #include "native/juce_win32_FileChooser.cpp" + +#elif JUCE_LINUX || JUCE_BSD + #include "native/x11/juce_linux_X11_Symbols.cpp" + #include "native/x11/juce_linux_X11_DragAndDrop.cpp" + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant") + + #include "native/x11/juce_linux_ScopedWindowAssociation.h" + #include "native/juce_linux_Windowing.cpp" + #include "native/x11/juce_linux_XWindowSystem.cpp" + + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + #include "native/juce_linux_FileChooser.cpp" + +#elif JUCE_ANDROID + +namespace juce +{ +static jobject makeAndroidRect (Rectangle r) +{ + return getEnv()->NewObject (AndroidRect, + AndroidRect.constructor, + r.getX(), + r.getY(), + r.getRight(), + r.getBottom()); +} + +static jobject makeAndroidPoint (Point p) +{ + return getEnv()->NewObject (AndroidPoint, + AndroidPoint.create, + p.getX(), + p.getY()); +} +} // namespace juce + + #include "juce_core/files/juce_common_MimeTypes.h" + #include "native/accessibility/juce_android_Accessibility.cpp" + #include "native/juce_android_Windowing.cpp" + #include "native/juce_android_FileChooser.cpp" + + #if JUCE_CONTENT_SHARING + #include "native/juce_android_ContentSharer.cpp" + #endif + +#endif + +namespace juce +{ + #if ! JUCE_NATIVE_ACCESSIBILITY_INCLUDED + class AccessibilityHandler::AccessibilityNativeImpl { public: AccessibilityNativeImpl (AccessibilityHandler&) {} }; + void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent) const {} + void AccessibilityHandler::postAnnouncement (const String&, AnnouncementPriority) {} + AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const { return nullptr; } + void notifyAccessibilityEventInternal (const AccessibilityHandler&, InternalAccessibilityEvent) {} + std::unique_ptr AccessibilityHandler::createNativeImpl (AccessibilityHandler&) + { + return nullptr; + } + #else + std::unique_ptr AccessibilityHandler::createNativeImpl (AccessibilityHandler& handler) + { + return std::make_unique (handler); + } + #endif +} + +//============================================================================== +#if JUCE_WINDOWS +namespace juce +{ + +JUCE_COMCLASS (JuceIVirtualDesktopManager, "a5cd92ff-29be-454c-8d04-d82879fb3f1b") : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE IsWindowOnCurrentVirtualDesktop( + __RPC__in HWND topLevelWindow, + __RPC__out BOOL * onCurrentDesktop) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetWindowDesktopId( + __RPC__in HWND topLevelWindow, + __RPC__out GUID * desktopId) = 0; + + virtual HRESULT STDMETHODCALLTYPE MoveWindowToDesktop( + __RPC__in HWND topLevelWindow, + __RPC__in REFGUID desktopId) = 0; +}; + +JUCE_COMCLASS (JuceVirtualDesktopManager, "aa509086-5ca9-4c25-8f95-589d3c07b48a"); + +} // namespace juce + +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL (juce::JuceIVirtualDesktopManager, 0xa5cd92ff, 0x29be, 0x454c, 0x8d, 0x04, 0xd8, 0x28, 0x79, 0xfb, 0x3f, 0x1b) +__CRT_UUID_DECL (juce::JuceVirtualDesktopManager, 0xaa509086, 0x5ca9, 0x4c25, 0x8f, 0x95, 0x58, 0x9d, 0x3c, 0x07, 0xb4, 0x8a) +#endif + +bool juce::isWindowOnCurrentVirtualDesktop (void* x) +{ + if (x == nullptr) + return false; + + static auto* desktopManager = [] + { + JuceIVirtualDesktopManager* result = nullptr; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + + if (SUCCEEDED (CoCreateInstance (__uuidof (JuceVirtualDesktopManager), nullptr, CLSCTX_ALL, IID_PPV_ARGS (&result)))) + return result; + + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + return static_cast (nullptr); + }(); + + BOOL current = false; + + if (auto* dm = desktopManager) + if (SUCCEEDED (dm->IsWindowOnCurrentVirtualDesktop (static_cast (x), ¤t))) + return current != false; + + return true; +} + +#else + bool juce::isWindowOnCurrentVirtualDesktop (void*) { return true; } + juce::ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { ignoreUnused (previousContext); } + juce::ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() {} +#endif + +// Depends on types defined in platform-specific windowing files +#include "mouse/juce_MouseCursor.cpp" + +#if JUCE_UNIT_TESTS +#include "native/accessibility/juce_AccessibilityTextHelpers_test.cpp" +#endif diff --git a/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h b/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h index 161c03cd..bd29a182 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h +++ b/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h @@ -35,7 +35,7 @@ ID: juce_gui_basics vendor: juce - version: 7.0.7 + version: 7.0.5 name: JUCE GUI core classes description: Basic user-interface components and related classes. website: http://www.juce.com/juce @@ -127,6 +127,7 @@ namespace juce class Component; class LookAndFeel; class MouseInputSource; + class MouseInputSourceInternal; class ComponentPeer; class MouseEvent; struct MouseWheelDetails; @@ -160,27 +161,16 @@ namespace juce class Displays; class AccessibilityHandler; class KeyboardFocusTraverser; + class PointerState; class FlexBox; class Grid; class FocusOutline; - #if JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD + #if JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX Image createSnapshotOfNativeWindow (void* nativeWindowHandle); #endif - - namespace detail - { - struct ComponentHelpers; - class MouseInputSourceImpl; - class MouseInputSourceList; - class PointerState; - class ScopedMessageBoxImpl; - class ToolbarItemDragAndDropOverlayComponent; - class TopLevelWindowManager; - } // namespace detail - -} // namespace juce +} #include "mouse/juce_MouseCursor.h" #include "mouse/juce_MouseListener.h" @@ -199,7 +189,6 @@ namespace juce #include "desktop/juce_Desktop.h" #include "desktop/juce_Displays.h" #include "layout/juce_ComponentBoundsConstrainer.h" -#include "layout/juce_BorderedComponentBoundsConstrainer.h" #include "mouse/juce_ComponentDragger.h" #include "mouse/juce_DragAndDropTarget.h" #include "mouse/juce_DragAndDropContainer.h" @@ -280,7 +269,6 @@ namespace juce #include "widgets/juce_TreeView.h" #include "windows/juce_TopLevelWindow.h" #include "windows/juce_MessageBoxOptions.h" -#include "windows/juce_ScopedMessageBox.h" #include "windows/juce_AlertWindow.h" #include "windows/juce_CallOutBox.h" #include "windows/juce_ComponentPeer.h" @@ -290,8 +278,7 @@ namespace juce #include "windows/juce_NativeMessageBox.h" #include "windows/juce_ThreadWithProgressWindow.h" #include "windows/juce_TooltipWindow.h" -#include "windows/juce_VBlankAttachment.h" -#include "windows/juce_WindowUtils.h" +#include "windows/juce_VBlankAttachement.h" #include "layout/juce_MultiDocumentPanel.h" #include "layout/juce_SidePanel.h" #include "filebrowser/juce_FileBrowserListener.h" @@ -336,9 +323,7 @@ namespace juce #if JUCE_LINUX || JUCE_BSD #if JUCE_GUI_BASICS_INCLUDE_XHEADERS // If you're missing these headers, you need to install the libx11-dev package - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wvariadic-macros") #include - JUCE_END_IGNORE_WARNINGS_GCC_LIKE #include #include #include @@ -378,13 +363,13 @@ namespace juce #undef SIZEOF #undef KeyPress - #include "native/juce_XWindowSystem_linux.h" - #include "native/juce_XSymbols_linux.h" + #include "native/x11/juce_linux_XWindowSystem.h" + #include "native/x11/juce_linux_X11_Symbols.h" #endif #endif #if JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER && JUCE_WINDOWS - #include "native/juce_ScopedThreadDPIAwarenessSetter_windows.h" + #include "native/juce_win32_ScopedThreadDPIAwarenessSetter.h" #endif #include "layout/juce_FlexItem.h" diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp index f0d5e681..0aab730b 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp @@ -35,10 +35,10 @@ namespace KeyboardFocusTraverserHelpers } static Component* traverse (Component* current, Component* container, - detail::FocusHelpers::NavigationDirection direction) + FocusHelpers::NavigationDirection direction) { - if (auto* comp = detail::FocusHelpers::navigateFocus (current, container, direction, - &Component::isKeyboardFocusContainer)) + if (auto* comp = FocusHelpers::navigateFocus (current, container, direction, + &Component::isKeyboardFocusContainer)) { if (isKeyboardFocusable (comp, container)) return comp; @@ -53,13 +53,13 @@ namespace KeyboardFocusTraverserHelpers Component* KeyboardFocusTraverser::getNextComponent (Component* current) { return KeyboardFocusTraverserHelpers::traverse (current, current->findKeyboardFocusContainer(), - detail::FocusHelpers::NavigationDirection::forwards); + FocusHelpers::NavigationDirection::forwards); } Component* KeyboardFocusTraverser::getPreviousComponent (Component* current) { return KeyboardFocusTraverserHelpers::traverse (current, current->findKeyboardFocusContainer(), - detail::FocusHelpers::NavigationDirection::backwards); + FocusHelpers::NavigationDirection::backwards); } Component* KeyboardFocusTraverser::getDefaultComponent (Component* parentComponent) @@ -74,9 +74,9 @@ Component* KeyboardFocusTraverser::getDefaultComponent (Component* parentCompone std::vector KeyboardFocusTraverser::getAllComponents (Component* parentComponent) { std::vector components; - detail::FocusHelpers::findAllComponents (parentComponent, - components, - &Component::isKeyboardFocusContainer); + FocusHelpers::findAllComponents (parentComponent, + components, + &Component::isKeyboardFocusContainer); auto removePredicate = [parentComponent] (const Component* comp) { diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPosition.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPosition.h index 85607807..20d9f9f6 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPosition.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPosition.h @@ -179,7 +179,7 @@ class AnimatedPosition : private Timer { newPosition = range.clipValue (newPosition); - if (! approximatelyEqual (position, newPosition)) + if (position != newPosition) { position = newPosition; listeners.call ([this, newPosition] (Listener& l) { l.positionChanged (*this, newPosition); }); diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h index 5786d780..107138c7 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h @@ -89,7 +89,7 @@ namespace AnimatedPositionBehaviours */ bool isStopped (double /*position*/) const noexcept { - return approximatelyEqual (velocity, 0.0); + return velocity == 0.0; } private: diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_BorderedComponentBoundsConstrainer.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_BorderedComponentBoundsConstrainer.cpp deleted file mode 100644 index e2f5265a..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_BorderedComponentBoundsConstrainer.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - ============================================================================== - - 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 BorderedComponentBoundsConstrainer::checkBounds (Rectangle& bounds, - const Rectangle& previousBounds, - const Rectangle& limits, - bool isStretchingTop, - bool isStretchingLeft, - bool isStretchingBottom, - bool isStretchingRight) -{ - if (auto* decorated = getWrappedConstrainer()) - { - const auto border = getAdditionalBorder(); - const auto requestedBounds = bounds; - - border.subtractFrom (bounds); - decorated->checkBounds (bounds, - border.subtractedFrom (previousBounds), - limits, - isStretchingTop, - isStretchingLeft, - isStretchingBottom, - isStretchingRight); - border.addTo (bounds); - bounds = bounds.withPosition (requestedBounds.getPosition()); - - if (isStretchingTop && ! isStretchingBottom) - bounds = bounds.withBottomY (previousBounds.getBottom()); - - if (! isStretchingTop && isStretchingBottom) - bounds = bounds.withY (previousBounds.getY()); - - if (isStretchingLeft && ! isStretchingRight) - bounds = bounds.withRightX (previousBounds.getRight()); - - if (! isStretchingLeft && isStretchingRight) - bounds = bounds.withX (previousBounds.getX()); - } - else - { - ComponentBoundsConstrainer::checkBounds (bounds, - previousBounds, - limits, - isStretchingTop, - isStretchingLeft, - isStretchingBottom, - isStretchingRight); - } -} - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_BorderedComponentBoundsConstrainer.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_BorderedComponentBoundsConstrainer.h deleted file mode 100644 index 0ed9889d..00000000 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_BorderedComponentBoundsConstrainer.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - ============================================================================== - - 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 -{ - -//============================================================================== -/** - A ComponentBoundsConstrainer that can be used to add a constant border onto another - ComponentBoundsConstrainer. - - This is useful when trying to constrain the size of a resizable window or - other component that wraps a constrained component, such as a plugin - editor. - - @see ResizableCornerComponent, ResizableBorderComponent, ResizableWindow, - ComponentBoundsConstrainer - - @tags{GUI} -*/ -class JUCE_API BorderedComponentBoundsConstrainer : public ComponentBoundsConstrainer -{ -public: - /** Default constructor. */ - BorderedComponentBoundsConstrainer() = default; - - /** Returns a pointer to another constrainer that will be used as the - base for any resizing operations. - */ - virtual ComponentBoundsConstrainer* getWrappedConstrainer() const = 0; - - /** Returns the border that should be applied to the constrained bounds. */ - virtual BorderSize getAdditionalBorder() const = 0; - - /** @internal */ - void checkBounds (Rectangle& bounds, - const Rectangle& previousBounds, - const Rectangle& limits, - bool isStretchingTop, - bool isStretchingLeft, - bool isStretchingBottom, - bool isStretchingRight) override; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BorderedComponentBoundsConstrainer) -}; - -} // namespace juce diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp index 42732fbd..3095bde9 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp @@ -49,7 +49,7 @@ class ComponentAnimator::AnimationTask destAlpha = finalAlpha; isMoving = (finalBounds != component->getBounds()); - isChangingAlpha = ! approximatelyEqual (finalAlpha, component->getAlpha()); + isChangingAlpha = (finalAlpha != component->getAlpha()); left = component->getX(); top = component->getY(); @@ -273,7 +273,7 @@ void ComponentAnimator::fadeOut (Component* component, int millisecondsToTake) void ComponentAnimator::fadeIn (Component* component, int millisecondsToTake) { - if (component != nullptr && ! (component->isVisible() && approximatelyEqual (component->getAlpha(), 1.0f))) + if (component != nullptr && ! (component->isVisible() && component->getAlpha() == 1.0f)) { component->setAlpha (0.0f); component->setVisible (true); diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h index d6aa4a9f..db959f34 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h @@ -119,10 +119,8 @@ class JUCE_API ConcertinaPanel : public Component ConcertinaPanel&, Component&) = 0; }; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; - private: + std::unique_ptr createAccessibilityHandler() override; void resized() override; class PanelHolder; diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexBox.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexBox.cpp index ed5443b6..9a7a2469 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexBox.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_FlexBox.cpp @@ -84,16 +84,8 @@ struct FlexBoxLayoutCalculation ItemWithState& getItem (int x, int y) const noexcept { return *lineItems[y * numItems + x]; } - static bool isAuto (Coord value) noexcept - { - return exactlyEqual (value, static_cast (FlexItem::autoValue)); - } - - static bool isAssigned (Coord value) noexcept - { - return ! exactlyEqual (value, static_cast (FlexItem::notAssigned)); - } - + static bool isAuto (Coord value) noexcept { return value == FlexItem::autoValue; } + static bool isAssigned (Coord value) noexcept { return value != FlexItem::notAssigned; } static Coord getValueOrZeroIfAuto (Coord value) noexcept { return isAuto (value) ? Coord() : value; } //============================================================================== @@ -612,12 +604,12 @@ struct FlexBoxLayoutCalculation if (positiveFlexibility) { - if (! approximatelyEqual (totalFlexGrow, 0.0)) + if (totalFlexGrow != 0.0) changeUnit = difference / totalFlexGrow; } else { - if (! approximatelyEqual (totalFlexShrink, 0.0)) + if (totalFlexShrink != 0.0) changeUnit = difference / totalFlexShrink; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.cpp index 8534fde6..b012520d 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.cpp @@ -26,1026 +26,942 @@ namespace juce { -template -static Array operator+ (const Array& a, const Array& b) +struct AllTracksIncludingImplicit { - auto copy = a; - copy.addArray (b); - return copy; -} + Array items; + int numImplicitLeading; // The number of implicit items before the explicit items +}; -struct Grid::Helpers +struct Tracks { + AllTracksIncludingImplicit columns, rows; +}; - struct AllTracksIncludingImplicit +struct Grid::SizeCalculation +{ + static float getTotalAbsoluteSize (const Array& tracks, Px gapSize) noexcept { - Array items; - int numImplicitLeading; // The number of implicit items before the explicit items - }; + float totalCellSize = 0.0f; - struct Tracks - { - AllTracksIncludingImplicit columns, rows; - }; + for (const auto& trackInfo : tracks) + if (! trackInfo.isFractional() || trackInfo.isAuto()) + totalCellSize += trackInfo.getSize(); - struct NoRounding - { - template - T operator() (T t) const { return t; } - }; + float totalGap = tracks.size() > 1 ? static_cast ((tracks.size() - 1) * gapSize.pixels) + : 0.0f; + + return totalCellSize + totalGap; + } - struct StandardRounding + static float getRelativeUnitSize (float size, float totalAbsolute, const Array& tracks) noexcept { - template - T operator() (T t) const { return std::round (t); } - }; + const float totalRelative = jlimit (0.0f, size, size - totalAbsolute); + float factorsSum = 0.0f; + + for (const auto& trackInfo : tracks) + if (trackInfo.isFractional()) + factorsSum += trackInfo.getSize(); + + jassert (factorsSum != 0.0f); + return totalRelative / factorsSum; + } - template - struct SizeCalculation + //============================================================================== + static float getTotalAbsoluteHeight (const Array& rowTracks, Px rowGap) { - float getTotalAbsoluteSize (const Array& tracks, Px gapSize) noexcept - { - float totalCellSize = 0.0f; + return getTotalAbsoluteSize (rowTracks, rowGap); + } - for (const auto& trackInfo : tracks) - if (! trackInfo.isFractional() || trackInfo.isAuto()) - totalCellSize += roundingFunction (trackInfo.getSize()); + static float getTotalAbsoluteWidth (const Array& columnTracks, Px columnGap) + { + return getTotalAbsoluteSize (columnTracks, columnGap); + } - float totalGap = tracks.size() > 1 ? (float) (tracks.size() - 1) * roundingFunction ((float) gapSize.pixels) - : 0.0f; + static float getRelativeWidthUnit (float gridWidth, Px columnGap, const Array& columnTracks) + { + return getRelativeUnitSize (gridWidth, getTotalAbsoluteWidth (columnTracks, columnGap), columnTracks); + } - return totalCellSize + totalGap; - } + static float getRelativeHeightUnit (float gridHeight, Px rowGap, const Array& rowTracks) + { + return getRelativeUnitSize (gridHeight, getTotalAbsoluteHeight (rowTracks, rowGap), rowTracks); + } - static float getRelativeUnitSize (float size, float totalAbsolute, const Array& tracks) noexcept - { - const float totalRelative = jlimit (0.0f, size, size - totalAbsolute); - float factorsSum = 0.0f; + //============================================================================== + static bool hasAnyFractions (const Array& tracks) + { + return std::any_of (tracks.begin(), + tracks.end(), + [] (const auto& t) { return t.isFractional(); }); + } - for (const auto& trackInfo : tracks) - if (trackInfo.isFractional()) - factorsSum += trackInfo.getSize(); + void computeSizes (float gridWidth, float gridHeight, + Px columnGapToUse, Px rowGapToUse, + const Tracks& tracks) + { + if (hasAnyFractions (tracks.columns.items)) + relativeWidthUnit = getRelativeWidthUnit (gridWidth, columnGapToUse, tracks.columns.items); + else + remainingWidth = gridWidth - getTotalAbsoluteSize (tracks.columns.items, columnGapToUse); + + if (hasAnyFractions (tracks.rows.items)) + relativeHeightUnit = getRelativeHeightUnit (gridHeight, rowGapToUse, tracks.rows.items); + else + remainingHeight = gridHeight - getTotalAbsoluteSize (tracks.rows.items, rowGapToUse); + } - jassert (! approximatelyEqual (factorsSum, 0.0f)); - return totalRelative / factorsSum; - } + float relativeWidthUnit = 0.0f; + float relativeHeightUnit = 0.0f; + float remainingWidth = 0.0f; + float remainingHeight = 0.0f; +}; - //============================================================================== - float getTotalAbsoluteHeight (const Array& rowTracks, Px rowGapSize) - { - return getTotalAbsoluteSize (rowTracks, rowGapSize); - } +//============================================================================== +struct Grid::PlacementHelpers +{ + enum { invalid = -999999 }; + static constexpr auto emptyAreaCharacter = "."; - float getTotalAbsoluteWidth (const Array& columnTracks, Px columnGapSize) - { - return getTotalAbsoluteSize (columnTracks, columnGapSize); - } + //============================================================================== + struct LineRange { int start, end; }; + struct LineArea { LineRange column, row; }; + struct LineInfo { StringArray lineNames; }; - float getRelativeWidthUnit (float gridWidth, Px columnGapSize, const Array& columnTracks) - { - return getRelativeUnitSize (gridWidth, getTotalAbsoluteWidth (columnTracks, columnGapSize), columnTracks); - } + struct NamedArea + { + String name; + LineArea lines; + }; - float getRelativeHeightUnit (float gridHeight, Px rowGapSize, const Array& rowTracks) - { - return getRelativeUnitSize (gridHeight, getTotalAbsoluteHeight (rowTracks, rowGapSize), rowTracks); - } + //============================================================================== + static Array getArrayOfLinesFromTracks (const Array& tracks) + { + // fill line info array + Array lines; - //============================================================================== - static bool hasAnyFractions (const Array& tracks) + for (int i = 1; i <= tracks.size(); ++i) { - return std::any_of (tracks.begin(), - tracks.end(), - [] (const auto& t) { return t.isFractional(); }); - } + const auto& currentTrack = tracks.getReference (i - 1); - void computeSizes (float gridWidth, float gridHeight, - Px columnGapToUse, Px rowGapToUse, - const Tracks& tracks) - { - if (hasAnyFractions (tracks.columns.items)) + if (i == 1) // start line { - relativeWidthUnit = getRelativeWidthUnit (gridWidth, columnGapToUse, tracks.columns.items); - fractionallyDividedWidth = gridWidth - getTotalAbsoluteSize (tracks.columns.items, columnGapToUse); - } - else - { - remainingWidth = gridWidth - getTotalAbsoluteSize (tracks.columns.items, columnGapToUse); + LineInfo li; + li.lineNames.add (currentTrack.getStartLineName()); + lines.add (li); } - if (hasAnyFractions (tracks.rows.items)) + if (i > 1 && i <= tracks.size()) // two lines in between tracks { - relativeHeightUnit = getRelativeHeightUnit (gridHeight, rowGapToUse, tracks.rows.items); - fractionallyDividedHeight = gridHeight - getTotalAbsoluteSize (tracks.rows.items, rowGapToUse); - } - else - { - remainingHeight = gridHeight - getTotalAbsoluteSize (tracks.rows.items, rowGapToUse); - } - - const auto calculateTrackBounds = [&] (auto& outBounds, - const auto& trackItems, - auto relativeUnit, - auto totalSizeForFractionalItems, - auto gap) - { - const auto lastFractionalIndex = [&] - { - for (int i = trackItems.size() - 1; 0 <= i; --i) - if (trackItems[i].isFractional()) - return i; + const auto& prevTrack = tracks.getReference (i - 2); - return -1; - }(); + LineInfo li; + li.lineNames.add (prevTrack.getEndLineName()); + li.lineNames.add (currentTrack.getStartLineName()); - float start = 0.0f; - float carriedError = 0.0f; - - for (int i = 0; i < trackItems.size(); ++i) - { - const auto& currentItem = trackItems[i]; - - const auto currentTrackSize = [&] - { - if (i == lastFractionalIndex) - return totalSizeForFractionalItems; - - const auto absoluteSize = currentItem.getAbsoluteSize (relativeUnit); - - if (! currentItem.isFractional()) - return roundingFunction (absoluteSize); - - const auto result = roundingFunction (absoluteSize - carriedError); - carriedError += result - absoluteSize; - return result; - }(); - - if (currentItem.isFractional()) - totalSizeForFractionalItems -= currentTrackSize; + lines.add (li); + } - const auto end = start + currentTrackSize; - outBounds.emplace_back (start, end); - start = end + roundingFunction (static_cast (gap.pixels)); - } - }; - - calculateTrackBounds (columnTrackBounds, - tracks.columns.items, - relativeWidthUnit, - fractionallyDividedWidth, - columnGapToUse); - - calculateTrackBounds (rowTrackBounds, - tracks.rows.items, - relativeHeightUnit, - fractionallyDividedHeight, - rowGapToUse); + if (i == tracks.size()) // end line + { + LineInfo li; + li.lineNames.add (currentTrack.getEndLineName()); + lines.add (li); + } } - float relativeWidthUnit = 0.0f; - float relativeHeightUnit = 0.0f; - float fractionallyDividedWidth = 0.0f; - float fractionallyDividedHeight = 0.0f; - float remainingWidth = 0.0f; - float remainingHeight = 0.0f; + jassert (lines.size() == tracks.size() + 1); - std::vector> columnTrackBounds; - std::vector> rowTrackBounds; - RoundingFunction roundingFunction; - }; + return lines; + } //============================================================================== - struct PlacementHelpers + static int deduceAbsoluteLineNumberFromLineName (GridItem::Property prop, + const Array& tracks) { - enum { invalid = -999999 }; - static constexpr auto emptyAreaCharacter = "."; - - //============================================================================== - struct LineRange { int start, end; }; - struct LineArea { LineRange column, row; }; - struct LineInfo { StringArray lineNames; }; + jassert (prop.hasAbsolute()); - struct NamedArea - { - String name; - LineArea lines; - }; + const auto lines = getArrayOfLinesFromTracks (tracks); + int count = 0; - //============================================================================== - static Array getArrayOfLinesFromTracks (const Array& tracks) + for (int i = 0; i < lines.size(); i++) { - // fill line info array - Array lines; - - for (int i = 1; i <= tracks.size(); ++i) + for (const auto& name : lines.getReference (i).lineNames) { - const auto& currentTrack = tracks.getReference (i - 1); - - if (i == 1) // start line + if (prop.getName() == name) { - LineInfo li; - li.lineNames.add (currentTrack.getStartLineName()); - lines.add (li); + ++count; + break; } + } - if (i > 1 && i <= tracks.size()) // two lines in between tracks - { - const auto& prevTrack = tracks.getReference (i - 2); + if (count == prop.getNumber()) + return i + 1; + } + + jassertfalse; + return count; + } - LineInfo li; - li.lineNames.add (prevTrack.getEndLineName()); - li.lineNames.add (currentTrack.getStartLineName()); + static int deduceAbsoluteLineNumber (GridItem::Property prop, + const Array& tracks) + { + jassert (prop.hasAbsolute()); - lines.add (li); - } + if (prop.hasName()) + return deduceAbsoluteLineNumberFromLineName (prop, tracks); - if (i == tracks.size()) // end line - { - LineInfo li; - li.lineNames.add (currentTrack.getEndLineName()); - lines.add (li); - } - } + if (prop.getNumber() > 0) + return prop.getNumber(); - jassert (lines.size() == tracks.size() + 1); + if (prop.getNumber() < 0) + return tracks.size() + 2 + prop.getNumber(); - return lines; - } + // An integer value of 0 is invalid + jassertfalse; + return 1; + } - //============================================================================== - static int deduceAbsoluteLineNumberFromLineName (GridItem::Property prop, - const Array& tracks) - { - jassert (prop.hasAbsolute()); + static int deduceAbsoluteLineNumberFromNamedSpan (int startLineNumber, + GridItem::Property propertyWithSpan, + const Array& tracks) + { + jassert (propertyWithSpan.hasSpan()); - const auto lines = getArrayOfLinesFromTracks (tracks); - int count = 0; + const auto lines = getArrayOfLinesFromTracks (tracks); + int count = 0; - for (int i = 0; i < lines.size(); i++) + for (int i = startLineNumber; i < lines.size(); i++) + { + for (const auto& name : lines.getReference (i).lineNames) { - for (const auto& name : lines.getReference (i).lineNames) + if (propertyWithSpan.getName() == name) { - if (prop.getName() == name) - { - ++count; - break; - } + ++count; + break; } - - if (count == prop.getNumber()) - return i + 1; } - jassertfalse; - return count; + if (count == propertyWithSpan.getNumber()) + return i + 1; } - static int deduceAbsoluteLineNumber (GridItem::Property prop, - const Array& tracks) - { - jassert (prop.hasAbsolute()); + jassertfalse; + return count; + } - if (prop.hasName()) - return deduceAbsoluteLineNumberFromLineName (prop, tracks); + static int deduceAbsoluteLineNumberBasedOnSpan (int startLineNumber, + GridItem::Property propertyWithSpan, + const Array& tracks) + { + jassert (propertyWithSpan.hasSpan()); - if (prop.getNumber() > 0) - return prop.getNumber(); + if (propertyWithSpan.hasName()) + return deduceAbsoluteLineNumberFromNamedSpan (startLineNumber, propertyWithSpan, tracks); - if (prop.getNumber() < 0) - return tracks.size() + 2 + prop.getNumber(); + return startLineNumber + propertyWithSpan.getNumber(); + } - // An integer value of 0 is invalid - jassertfalse; - return 1; - } + //============================================================================== + static LineRange deduceLineRange (GridItem::StartAndEndProperty prop, const Array& tracks) + { + jassert (! (prop.start.hasAuto() && prop.end.hasAuto())); - static int deduceAbsoluteLineNumberFromNamedSpan (int startLineNumber, - GridItem::Property propertyWithSpan, - const Array& tracks) + if (prop.start.hasAbsolute() && prop.end.hasAuto()) { - jassert (propertyWithSpan.hasSpan()); - - const auto lines = getArrayOfLinesFromTracks (tracks); - int count = 0; - - for (int i = startLineNumber; i < lines.size(); i++) - { - for (const auto& name : lines.getReference (i).lineNames) - { - if (propertyWithSpan.getName() == name) - { - ++count; - break; - } - } - - if (count == propertyWithSpan.getNumber()) - return i + 1; - } - - jassertfalse; - return count; + prop.end = GridItem::Span (1); } - - static int deduceAbsoluteLineNumberBasedOnSpan (int startLineNumber, - GridItem::Property propertyWithSpan, - const Array& tracks) + else if (prop.start.hasAuto() && prop.end.hasAbsolute()) { - jassert (propertyWithSpan.hasSpan()); - - if (propertyWithSpan.hasName()) - return deduceAbsoluteLineNumberFromNamedSpan (startLineNumber, propertyWithSpan, tracks); - - return startLineNumber + propertyWithSpan.getNumber(); + prop.start = GridItem::Span (1); } - //============================================================================== - static LineRange deduceLineRange (GridItem::StartAndEndProperty prop, const Array& tracks) + auto s = [&]() -> LineRange { - jassert (! (prop.start.hasAuto() && prop.end.hasAuto())); - - if (prop.start.hasAbsolute() && prop.end.hasAuto()) + if (prop.start.hasAbsolute() && prop.end.hasAbsolute()) { - prop.end = GridItem::Span (1); + return { deduceAbsoluteLineNumber (prop.start, tracks), + deduceAbsoluteLineNumber (prop.end, tracks) }; } - else if (prop.start.hasAuto() && prop.end.hasAbsolute()) + + if (prop.start.hasAbsolute() && prop.end.hasSpan()) { - prop.start = GridItem::Span (1); + const auto start = deduceAbsoluteLineNumber (prop.start, tracks); + return { start, deduceAbsoluteLineNumberBasedOnSpan (start, prop.end, tracks) }; } - auto s = [&]() -> LineRange + if (prop.start.hasSpan() && prop.end.hasAbsolute()) { - if (prop.start.hasAbsolute() && prop.end.hasAbsolute()) - { - return { deduceAbsoluteLineNumber (prop.start, tracks), - deduceAbsoluteLineNumber (prop.end, tracks) }; - } + const auto start = deduceAbsoluteLineNumber (prop.end, tracks); + return { start, deduceAbsoluteLineNumberBasedOnSpan (start, prop.start, tracks) }; + } - if (prop.start.hasAbsolute() && prop.end.hasSpan()) - { - const auto start = deduceAbsoluteLineNumber (prop.start, tracks); - return { start, deduceAbsoluteLineNumberBasedOnSpan (start, prop.end, tracks) }; - } + // Can't have an item with spans on both start and end. + jassertfalse; + return {}; + }(); - if (prop.start.hasSpan() && prop.end.hasAbsolute()) - { - const auto start = deduceAbsoluteLineNumber (prop.end, tracks); - return { start, deduceAbsoluteLineNumberBasedOnSpan (start, prop.start, tracks) }; - } + // swap if start overtakes end + if (s.start > s.end) + std::swap (s.start, s.end); + else if (s.start == s.end) + s.end = s.start + 1; - // Can't have an item with spans on both start and end. - jassertfalse; - return {}; - }(); + return s; + } - // swap if start overtakes end - if (s.start > s.end) - std::swap (s.start, s.end); - else if (s.start == s.end) - s.end = s.start + 1; + static LineArea deduceLineArea (const GridItem& item, + const Grid& grid, + const std::map& namedAreas) + { + if (item.area.isNotEmpty() && ! grid.templateAreas.isEmpty()) + { + // Must be a named area! + jassert (namedAreas.count (item.area) != 0); - return s; + return namedAreas.at (item.area); } - static LineArea deduceLineArea (const GridItem& item, - const Grid& grid, - const std::map& namedAreas) - { - if (item.area.isNotEmpty() && ! grid.templateAreas.isEmpty()) - { - // Must be a named area! - jassert (namedAreas.count (item.area) != 0); + return { deduceLineRange (item.column, grid.templateColumns), + deduceLineRange (item.row, grid.templateRows) }; + } - return namedAreas.at (item.area); - } + //============================================================================== + static Array parseAreasProperty (const StringArray& areasStrings) + { + Array strings; - return { deduceLineRange (item.column, grid.templateColumns), - deduceLineRange (item.row, grid.templateRows) }; - } + for (const auto& areaString : areasStrings) + strings.add (StringArray::fromTokens (areaString, false)); - //============================================================================== - static Array parseAreasProperty (const StringArray& areasStrings) + if (strings.size() > 0) { - Array strings; - - for (const auto& areaString : areasStrings) - strings.add (StringArray::fromTokens (areaString, false)); - - if (strings.size() > 0) + for (auto s : strings) { - for (auto s : strings) - { - jassert (s.size() == strings[0].size()); // all rows must have the same number of columns - } + jassert (s.size() == strings[0].size()); // all rows must have the same number of columns } - - return strings; } - static NamedArea findArea (Array& stringsArrays) - { - NamedArea area; + return strings; + } + + static NamedArea findArea (Array& stringsArrays) + { + NamedArea area; - for (auto& stringArray : stringsArrays) + for (auto& stringArray : stringsArrays) + { + for (auto& string : stringArray) { - for (auto& string : stringArray) + // find anchor + if (area.name.isEmpty()) { - // find anchor - if (area.name.isEmpty()) + if (string != emptyAreaCharacter) { - if (string != emptyAreaCharacter) - { - area.name = string; - area.lines.row.start = stringsArrays.indexOf (stringArray) + 1; // non-zero indexed; - area.lines.column.start = stringArray.indexOf (string) + 1; // non-zero indexed; - - area.lines.row.end = stringsArrays.indexOf (stringArray) + 2; - area.lines.column.end = stringArray.indexOf (string) + 2; - - // mark as visited - string = emptyAreaCharacter; - } + area.name = string; + area.lines.row.start = stringsArrays.indexOf (stringArray) + 1; // non-zero indexed; + area.lines.column.start = stringArray.indexOf (string) + 1; // non-zero indexed; + + area.lines.row.end = stringsArrays.indexOf (stringArray) + 2; + area.lines.column.end = stringArray.indexOf (string) + 2; + + // mark as visited + string = emptyAreaCharacter; } - else + } + else + { + if (string == area.name) { - if (string == area.name) - { - area.lines.row.end = stringsArrays.indexOf (stringArray) + 2; - area.lines.column.end = stringArray.indexOf (string) + 2; - - // mark as visited - string = emptyAreaCharacter; - } + area.lines.row.end = stringsArrays.indexOf (stringArray) + 2; + area.lines.column.end = stringArray.indexOf (string) + 2; + + // mark as visited + string = emptyAreaCharacter; } } } - - return area; } - //============================================================================== - static std::map deduceNamedAreas (const StringArray& areasStrings) - { - auto stringsArrays = parseAreasProperty (areasStrings); - - std::map areas; + return area; + } - for (auto area = findArea (stringsArrays); area.name.isNotEmpty(); area = findArea (stringsArrays)) - { - if (areas.count (area.name) == 0) - areas[area.name] = area.lines; - else - // Make sure your template-areas property only has one area with the same name and is well-formed - jassertfalse; - } + //============================================================================== + static std::map deduceNamedAreas (const StringArray& areasStrings) + { + auto stringsArrays = parseAreasProperty (areasStrings); - return areas; - } + std::map areas; - //============================================================================== - template - static Rectangle getCellBounds (int columnNumber, int rowNumber, - const Tracks& tracks, - const SizeCalculation& calculation) + for (auto area = findArea (stringsArrays); area.name.isNotEmpty(); area = findArea (stringsArrays)) { - const auto correctedColumn = columnNumber - 1 + tracks.columns.numImplicitLeading; - const auto correctedRow = rowNumber - 1 + tracks.rows .numImplicitLeading; - - jassert (isPositiveAndBelow (correctedColumn, tracks.columns.items.size())); - jassert (isPositiveAndBelow (correctedRow, tracks.rows .items.size())); - - return - { - calculation.columnTrackBounds[(size_t) correctedColumn].getStart(), - calculation.rowTrackBounds[(size_t) correctedRow].getStart(), - calculation.columnTrackBounds[(size_t) correctedColumn].getEnd() - calculation.columnTrackBounds[(size_t) correctedColumn].getStart(), - calculation.rowTrackBounds[(size_t) correctedRow].getEnd() - calculation.rowTrackBounds[(size_t) correctedRow].getStart() - }; + if (areas.count (area.name) == 0) + areas[area.name] = area.lines; + else + // Make sure your template-areas property only has one area with the same name and is well-formed + jassertfalse; } - template - static Rectangle alignCell (Rectangle area, - int columnNumber, int rowNumber, - int numberOfColumns, int numberOfRows, - const SizeCalculation& calculation, - AlignContent alignContent, - JustifyContent justifyContent) - { - if (alignContent == AlignContent::end) - area.setY (area.getY() + calculation.remainingHeight); - - if (justifyContent == JustifyContent::end) - area.setX (area.getX() + calculation.remainingWidth); + return areas; + } - if (alignContent == AlignContent::center) - area.setY (area.getY() + calculation.remainingHeight / 2); + //============================================================================== + static float getCoord (int trackNumber, float relativeUnit, Px gap, const Array& tracks) + { + float c = 0; - if (justifyContent == JustifyContent::center) - area.setX (area.getX() + calculation.remainingWidth / 2); + for (const auto* it = tracks.begin(); it != tracks.begin() + trackNumber; ++it) + c += it->getAbsoluteSize (relativeUnit) + static_cast (gap.pixels); - if (alignContent == AlignContent::spaceBetween) - { - const auto shift = ((float) (rowNumber - 1) * (calculation.remainingHeight / float(numberOfRows - 1))); - area.setY (area.getY() + shift); - } + return c; + } - if (justifyContent == JustifyContent::spaceBetween) - { - const auto shift = ((float) (columnNumber - 1) * (calculation.remainingWidth / float(numberOfColumns - 1))); - area.setX (area.getX() + shift); - } + static Rectangle getCellBounds (int columnNumber, int rowNumber, + const Tracks& tracks, + SizeCalculation calculation, + Px columnGap, Px rowGap) + { + const auto correctedColumn = columnNumber - 1 + tracks.columns.numImplicitLeading; + const auto correctedRow = rowNumber - 1 + tracks.rows .numImplicitLeading; - if (alignContent == AlignContent::spaceEvenly) - { - const auto shift = ((float) rowNumber * (calculation.remainingHeight / float(numberOfRows + 1))); - area.setY (area.getY() + shift); - } + jassert (isPositiveAndBelow (correctedColumn, tracks.columns.items.size())); + jassert (isPositiveAndBelow (correctedRow, tracks.rows .items.size())); - if (justifyContent == JustifyContent::spaceEvenly) - { - const auto shift = ((float) columnNumber * (calculation.remainingWidth / float(numberOfColumns + 1))); - area.setX (area.getX() + shift); - } + return { getCoord (correctedColumn, calculation.relativeWidthUnit, columnGap, tracks.columns.items), + getCoord (correctedRow, calculation.relativeHeightUnit, rowGap, tracks.rows .items), + tracks.columns.items.getReference (correctedColumn).getAbsoluteSize (calculation.relativeWidthUnit), + tracks.rows .items.getReference (correctedRow) .getAbsoluteSize (calculation.relativeHeightUnit) }; + } - if (alignContent == AlignContent::spaceAround) - { - const auto inbetweenShift = calculation.remainingHeight / float(numberOfRows); - const auto sidesShift = inbetweenShift / 2; - auto shift = (float) (rowNumber - 1) * inbetweenShift + sidesShift; + static Rectangle alignCell (Rectangle area, + int columnNumber, int rowNumber, + int numberOfColumns, int numberOfRows, + SizeCalculation calculation, + AlignContent alignContent, + JustifyContent justifyContent) + { + if (alignContent == AlignContent::end) + area.setY (area.getY() + calculation.remainingHeight); - area.setY (area.getY() + shift); - } + if (justifyContent == JustifyContent::end) + area.setX (area.getX() + calculation.remainingWidth); - if (justifyContent == JustifyContent::spaceAround) - { - const auto inbetweenShift = calculation.remainingWidth / float(numberOfColumns); - const auto sidesShift = inbetweenShift / 2; - auto shift = (float) (columnNumber - 1) * inbetweenShift + sidesShift; + if (alignContent == AlignContent::center) + area.setY (area.getY() + calculation.remainingHeight / 2); - area.setX (area.getX() + shift); - } + if (justifyContent == JustifyContent::center) + area.setX (area.getX() + calculation.remainingWidth / 2); - return area; + if (alignContent == AlignContent::spaceBetween) + { + const auto shift = ((float) (rowNumber - 1) * (calculation.remainingHeight / float(numberOfRows - 1))); + area.setY (area.getY() + shift); } - template - static Rectangle getAreaBounds (PlacementHelpers::LineRange columnRange, - PlacementHelpers::LineRange rowRange, - const Tracks& tracks, - const SizeCalculation& calculation, - AlignContent alignContent, - JustifyContent justifyContent) + if (justifyContent == JustifyContent::spaceBetween) { - const auto findAlignedCell = [&] (int column, int row) - { - const auto cell = getCellBounds (column, row, tracks, calculation); - return alignCell (cell, - column, - row, - tracks.columns.items.size(), - tracks.rows.items.size(), - calculation, - alignContent, - justifyContent); - }; - - const auto startCell = findAlignedCell (columnRange.start, rowRange.start); - const auto endCell = findAlignedCell (columnRange.end - 1, rowRange.end - 1); - - const auto horizontalRange = startCell.getHorizontalRange().getUnionWith (endCell.getHorizontalRange()); - const auto verticalRange = startCell.getVerticalRange() .getUnionWith (endCell.getVerticalRange()); - return { horizontalRange.getStart(), verticalRange.getStart(), - horizontalRange.getLength(), verticalRange.getLength() }; + const auto shift = ((float) (columnNumber - 1) * (calculation.remainingWidth / float(numberOfColumns - 1))); + area.setX (area.getX() + shift); } - }; - - //============================================================================== - struct AutoPlacement - { - using ItemPlacementArray = Array>; - //============================================================================== - struct OccupancyPlane + if (alignContent == AlignContent::spaceEvenly) { - struct Cell { int column, row; }; - - OccupancyPlane (int highestColumnToUse, int highestRowToUse, bool isColumnFirst) - : highestCrossDimension (isColumnFirst ? highestRowToUse : highestColumnToUse), - columnFirst (isColumnFirst) - {} + const auto shift = ((float) rowNumber * (calculation.remainingHeight / float(numberOfRows + 1))); + area.setY (area.getY() + shift); + } - PlacementHelpers::LineArea setCell (Cell cell, int columnSpan, int rowSpan) - { - for (int i = 0; i < columnSpan; i++) - for (int j = 0; j < rowSpan; j++) - setCell (cell.column + i, cell.row + j); + if (justifyContent == JustifyContent::spaceEvenly) + { + const auto shift = ((float) columnNumber * (calculation.remainingWidth / float(numberOfColumns + 1))); + area.setX (area.getX() + shift); + } - return { { cell.column, cell.column + columnSpan }, { cell.row, cell.row + rowSpan } }; - } + if (alignContent == AlignContent::spaceAround) + { + const auto inbetweenShift = calculation.remainingHeight / float(numberOfRows); + const auto sidesShift = inbetweenShift / 2; + auto shift = (float) (rowNumber - 1) * inbetweenShift + sidesShift; - PlacementHelpers::LineArea setCell (Cell start, Cell end) - { - return setCell (start, std::abs (end.column - start.column), - std::abs (end.row - start.row)); - } + area.setY (area.getY() + shift); + } - Cell nextAvailable (Cell referenceCell, int columnSpan, int rowSpan) - { - while (isOccupied (referenceCell, columnSpan, rowSpan) || isOutOfBounds (referenceCell, columnSpan, rowSpan)) - referenceCell = advance (referenceCell); + if (justifyContent == JustifyContent::spaceAround) + { + const auto inbetweenShift = calculation.remainingWidth / float(numberOfColumns); + const auto sidesShift = inbetweenShift / 2; + auto shift = (float) (columnNumber - 1) * inbetweenShift + sidesShift; - return referenceCell; - } + area.setX (area.getX() + shift); + } - Cell nextAvailableOnRow (Cell referenceCell, int columnSpan, int rowSpan, int rowNumber) - { - if (columnFirst && (rowNumber + rowSpan) > highestCrossDimension) - highestCrossDimension = rowNumber + rowSpan; + return area; + } - while (isOccupied (referenceCell, columnSpan, rowSpan) - || (referenceCell.row != rowNumber)) - referenceCell = advance (referenceCell); + static Rectangle getAreaBounds (PlacementHelpers::LineRange columnRange, + PlacementHelpers::LineRange rowRange, + const Tracks& tracks, + SizeCalculation calculation, + AlignContent alignContent, + JustifyContent justifyContent, + Px columnGap, Px rowGap) + { + const auto findAlignedCell = [&] (int column, int row) + { + const auto cell = getCellBounds (column, row, tracks, calculation, columnGap, rowGap); + return alignCell (cell, + column, + row, + tracks.columns.items.size(), + tracks.rows.items.size(), + calculation, + alignContent, + justifyContent); + }; - return referenceCell; - } + const auto startCell = findAlignedCell (columnRange.start, rowRange.start); + const auto endCell = findAlignedCell (columnRange.end - 1, rowRange.end - 1); - Cell nextAvailableOnColumn (Cell referenceCell, int columnSpan, int rowSpan, int columnNumber) - { - if (! columnFirst && (columnNumber + columnSpan) > highestCrossDimension) - highestCrossDimension = columnNumber + columnSpan; + const auto horizontalRange = startCell.getHorizontalRange().getUnionWith (endCell.getHorizontalRange()); + const auto verticalRange = startCell.getVerticalRange() .getUnionWith (endCell.getVerticalRange()); + return { horizontalRange.getStart(), verticalRange.getStart(), + horizontalRange.getLength(), verticalRange.getLength() }; + } +}; - while (isOccupied (referenceCell, columnSpan, rowSpan) - || (referenceCell.column != columnNumber)) - referenceCell = advance (referenceCell); +template +static Array operator+ (const Array& a, const Array& b) +{ + auto copy = a; + copy.addArray (b); + return copy; +} - return referenceCell; - } +//============================================================================== +struct Grid::AutoPlacement +{ + using ItemPlacementArray = Array>; - void updateMaxCrossDimensionFromAutoPlacementItem (int columnSpan, int rowSpan) - { - highestCrossDimension = jmax (highestCrossDimension, 1 + getCrossDimension ({ columnSpan, rowSpan })); - } + //============================================================================== + struct OccupancyPlane + { + struct Cell { int column, row; }; - private: - struct SortableCell - { - int column, row; - bool columnFirst; + OccupancyPlane (int highestColumnToUse, int highestRowToUse, bool isColumnFirst) + : highestCrossDimension (isColumnFirst ? highestRowToUse : highestColumnToUse), + columnFirst (isColumnFirst) + {} - bool operator< (const SortableCell& other) const - { - if (columnFirst) - { - if (row == other.row) - return column < other.column; + PlacementHelpers::LineArea setCell (Cell cell, int columnSpan, int rowSpan) + { + for (int i = 0; i < columnSpan; i++) + for (int j = 0; j < rowSpan; j++) + setCell (cell.column + i, cell.row + j); - return row < other.row; - } + return { { cell.column, cell.column + columnSpan }, { cell.row, cell.row + rowSpan } }; + } - if (row == other.row) - return column < other.column; + PlacementHelpers::LineArea setCell (Cell start, Cell end) + { + return setCell (start, std::abs (end.column - start.column), + std::abs (end.row - start.row)); + } - return row < other.row; - } - }; + Cell nextAvailable (Cell referenceCell, int columnSpan, int rowSpan) + { + while (isOccupied (referenceCell, columnSpan, rowSpan) || isOutOfBounds (referenceCell, columnSpan, rowSpan)) + referenceCell = advance (referenceCell); - void setCell (int column, int row) - { - occupiedCells.insert ({ column, row, columnFirst }); - } + return referenceCell; + } - bool isOccupied (Cell cell) const - { - return occupiedCells.count ({ cell.column, cell.row, columnFirst }) > 0; - } + Cell nextAvailableOnRow (Cell referenceCell, int columnSpan, int rowSpan, int rowNumber) + { + if (columnFirst && (rowNumber + rowSpan) > highestCrossDimension) + highestCrossDimension = rowNumber + rowSpan; - bool isOccupied (Cell cell, int columnSpan, int rowSpan) const - { - for (int i = 0; i < columnSpan; i++) - for (int j = 0; j < rowSpan; j++) - if (isOccupied ({ cell.column + i, cell.row + j })) - return true; + while (isOccupied (referenceCell, columnSpan, rowSpan) + || (referenceCell.row != rowNumber)) + referenceCell = advance (referenceCell); - return false; - } + return referenceCell; + } - bool isOutOfBounds (Cell cell, int columnSpan, int rowSpan) const - { - const auto highestIndexOfCell = getCrossDimension (cell) + getCrossDimension ({ columnSpan, rowSpan }); - const auto highestIndexOfGrid = getHighestCrossDimension(); + Cell nextAvailableOnColumn (Cell referenceCell, int columnSpan, int rowSpan, int columnNumber) + { + if (! columnFirst && (columnNumber + columnSpan) > highestCrossDimension) + highestCrossDimension = columnNumber + columnSpan; - return highestIndexOfGrid < highestIndexOfCell; - } + while (isOccupied (referenceCell, columnSpan, rowSpan) + || (referenceCell.column != columnNumber)) + referenceCell = advance (referenceCell); - int getHighestCrossDimension() const - { - Cell cell { 1, 1 }; + return referenceCell; + } - if (occupiedCells.size() > 0) - cell = { occupiedCells.crbegin()->column, occupiedCells.crbegin()->row }; + void updateMaxCrossDimensionFromAutoPlacementItem (int columnSpan, int rowSpan) + { + highestCrossDimension = jmax (highestCrossDimension, 1 + getCrossDimension ({ columnSpan, rowSpan })); + } - return std::max (getCrossDimension (cell), highestCrossDimension); - } + private: + struct SortableCell + { + int column, row; + bool columnFirst; - Cell advance (Cell cell) const + bool operator< (const SortableCell& other) const { - if ((getCrossDimension (cell) + 1) >= getHighestCrossDimension()) - return fromDimensions (getMainDimension (cell) + 1, 1); - - return fromDimensions (getMainDimension (cell), getCrossDimension (cell) + 1); - } + if (columnFirst) + { + if (row == other.row) + return column < other.column; - int getMainDimension (Cell cell) const { return columnFirst ? cell.column : cell.row; } - int getCrossDimension (Cell cell) const { return columnFirst ? cell.row : cell.column; } + return row < other.row; + } - Cell fromDimensions (int mainDimension, int crossDimension) const - { - if (columnFirst) - return { mainDimension, crossDimension }; + if (row == other.row) + return column < other.column; - return { crossDimension, mainDimension }; + return row < other.row; } - - int highestCrossDimension; - bool columnFirst; - std::set occupiedCells; }; - //============================================================================== - static bool isFixed (GridItem::StartAndEndProperty prop) + void setCell (int column, int row) { - return prop.start.hasName() || prop.start.hasAbsolute() || prop.end.hasName() || prop.end.hasAbsolute(); + occupiedCells.insert ({ column, row, columnFirst }); } - static bool hasFullyFixedPlacement (const GridItem& item) + bool isOccupied (Cell cell) const { - if (item.area.isNotEmpty()) - return true; + return occupiedCells.count ({ cell.column, cell.row, columnFirst }) > 0; + } - if (isFixed (item.column) && isFixed (item.row)) - return true; + bool isOccupied (Cell cell, int columnSpan, int rowSpan) const + { + for (int i = 0; i < columnSpan; i++) + for (int j = 0; j < rowSpan; j++) + if (isOccupied ({ cell.column + i, cell.row + j })) + return true; return false; } - static bool hasPartialFixedPlacement (const GridItem& item) + bool isOutOfBounds (Cell cell, int columnSpan, int rowSpan) const { - if (item.area.isNotEmpty()) - return false; - - if (isFixed (item.column) ^ isFixed (item.row)) - return true; + const auto highestIndexOfCell = getCrossDimension (cell) + getCrossDimension ({ columnSpan, rowSpan }); + const auto highestIndexOfGrid = getHighestCrossDimension(); - return false; + return highestIndexOfGrid < highestIndexOfCell; } - static bool hasAutoPlacement (const GridItem& item) + int getHighestCrossDimension() const { - return ! hasFullyFixedPlacement (item) && ! hasPartialFixedPlacement (item); + Cell cell { 1, 1 }; + + if (occupiedCells.size() > 0) + cell = { occupiedCells.crbegin()->column, occupiedCells.crbegin()->row }; + + return std::max (getCrossDimension (cell), highestCrossDimension); } - //============================================================================== - static bool hasDenseAutoFlow (AutoFlow autoFlow) + Cell advance (Cell cell) const { - return autoFlow == AutoFlow::columnDense - || autoFlow == AutoFlow::rowDense; + if ((getCrossDimension (cell) + 1) >= getHighestCrossDimension()) + return fromDimensions (getMainDimension (cell) + 1, 1); + + return fromDimensions (getMainDimension (cell), getCrossDimension (cell) + 1); } - static bool isColumnAutoFlow (AutoFlow autoFlow) + int getMainDimension (Cell cell) const { return columnFirst ? cell.column : cell.row; } + int getCrossDimension (Cell cell) const { return columnFirst ? cell.row : cell.column; } + + Cell fromDimensions (int mainDimension, int crossDimension) const { - return autoFlow == AutoFlow::column - || autoFlow == AutoFlow::columnDense; + if (columnFirst) + return { mainDimension, crossDimension }; + + return { crossDimension, mainDimension }; } - //============================================================================== - static int getSpanFromAuto (GridItem::StartAndEndProperty prop) - { - if (prop.end.hasSpan()) - return prop.end.getNumber(); + int highestCrossDimension; + bool columnFirst; + std::set occupiedCells; + }; - if (prop.start.hasSpan()) - return prop.start.getNumber(); + //============================================================================== + static bool isFixed (GridItem::StartAndEndProperty prop) + { + return prop.start.hasName() || prop.start.hasAbsolute() || prop.end.hasName() || prop.end.hasAbsolute(); + } - return 1; - } + static bool hasFullyFixedPlacement (const GridItem& item) + { + if (item.area.isNotEmpty()) + return true; - //============================================================================== - ItemPlacementArray deduceAllItems (Grid& grid) const - { - const auto namedAreas = PlacementHelpers::deduceNamedAreas (grid.templateAreas); + if (isFixed (item.column) && isFixed (item.row)) + return true; - OccupancyPlane plane (jmax (grid.templateColumns.size() + 1, 2), - jmax (grid.templateRows.size() + 1, 2), - isColumnAutoFlow (grid.autoFlow)); + return false; + } - ItemPlacementArray itemPlacementArray; - Array sortedItems; + static bool hasPartialFixedPlacement (const GridItem& item) + { + if (item.area.isNotEmpty()) + return false; - for (auto& item : grid.items) - sortedItems.add (&item); + if (isFixed (item.column) ^ isFixed (item.row)) + return true; - std::stable_sort (sortedItems.begin(), sortedItems.end(), - [] (const GridItem* i1, const GridItem* i2) { return i1->order < i2->order; }); + return false; + } - // place fixed items first - for (auto* item : sortedItems) - { - if (hasFullyFixedPlacement (*item)) - { - const auto a = PlacementHelpers::deduceLineArea (*item, grid, namedAreas); - plane.setCell ({ a.column.start, a.row.start }, { a.column.end, a.row.end }); - itemPlacementArray.add ({ item, a }); - } - } + static bool hasAutoPlacement (const GridItem& item) + { + return ! hasFullyFixedPlacement (item) && ! hasPartialFixedPlacement (item); + } - OccupancyPlane::Cell lastInsertionCell = { 1, 1 }; + //============================================================================== + static bool hasDenseAutoFlow (AutoFlow autoFlow) + { + return autoFlow == AutoFlow::columnDense + || autoFlow == AutoFlow::rowDense; + } - for (auto* item : sortedItems) - { - if (hasPartialFixedPlacement (*item)) - { - if (isFixed (item->column)) - { - const auto p = PlacementHelpers::deduceLineRange (item->column, grid.templateColumns); - const auto columnSpan = std::abs (p.start - p.end); - const auto rowSpan = getSpanFromAuto (item->row); + static bool isColumnAutoFlow (AutoFlow autoFlow) + { + return autoFlow == AutoFlow::column + || autoFlow == AutoFlow::columnDense; + } - const auto insertionCell = hasDenseAutoFlow (grid.autoFlow) ? OccupancyPlane::Cell { p.start, 1 } - : lastInsertionCell; - const auto nextAvailableCell = plane.nextAvailableOnColumn (insertionCell, columnSpan, rowSpan, p.start); - const auto lineArea = plane.setCell (nextAvailableCell, columnSpan, rowSpan); - lastInsertionCell = nextAvailableCell; + //============================================================================== + static int getSpanFromAuto (GridItem::StartAndEndProperty prop) + { + if (prop.end.hasSpan()) + return prop.end.getNumber(); - itemPlacementArray.add ({ item, lineArea }); - } - else if (isFixed (item->row)) - { - const auto p = PlacementHelpers::deduceLineRange (item->row, grid.templateRows); - const auto columnSpan = getSpanFromAuto (item->column); - const auto rowSpan = std::abs (p.start - p.end); + if (prop.start.hasSpan()) + return prop.start.getNumber(); - const auto insertionCell = hasDenseAutoFlow (grid.autoFlow) ? OccupancyPlane::Cell { 1, p.start } - : lastInsertionCell; + return 1; + } - const auto nextAvailableCell = plane.nextAvailableOnRow (insertionCell, columnSpan, rowSpan, p.start); - const auto lineArea = plane.setCell (nextAvailableCell, columnSpan, rowSpan); + //============================================================================== + ItemPlacementArray deduceAllItems (Grid& grid) const + { + const auto namedAreas = PlacementHelpers::deduceNamedAreas (grid.templateAreas); - lastInsertionCell = nextAvailableCell; + OccupancyPlane plane (jmax (grid.templateColumns.size() + 1, 2), + jmax (grid.templateRows.size() + 1, 2), + isColumnAutoFlow (grid.autoFlow)); - itemPlacementArray.add ({ item, lineArea }); - } - } - } + ItemPlacementArray itemPlacementArray; + Array sortedItems; - // https://www.w3.org/TR/css-grid-1/#auto-placement-algo step 3.3 - for (auto* item : sortedItems) - if (hasAutoPlacement (*item)) - plane.updateMaxCrossDimensionFromAutoPlacementItem (getSpanFromAuto (item->column), getSpanFromAuto (item->row)); + for (auto& item : grid.items) + sortedItems.add (&item); - lastInsertionCell = { 1, 1 }; + std::stable_sort (sortedItems.begin(), sortedItems.end(), + [] (const GridItem* i1, const GridItem* i2) { return i1->order < i2->order; }); - for (auto* item : sortedItems) + // place fixed items first + for (auto* item : sortedItems) + { + if (hasFullyFixedPlacement (*item)) { - if (hasAutoPlacement (*item)) + const auto a = PlacementHelpers::deduceLineArea (*item, grid, namedAreas); + plane.setCell ({ a.column.start, a.row.start }, { a.column.end, a.row.end }); + itemPlacementArray.add ({ item, a }); + } + } + + OccupancyPlane::Cell lastInsertionCell = { 1, 1 }; + + for (auto* item : sortedItems) + { + if (hasPartialFixedPlacement (*item)) + { + if (isFixed (item->column)) { - const auto columnSpan = getSpanFromAuto (item->column); + const auto p = PlacementHelpers::deduceLineRange (item->column, grid.templateColumns); + const auto columnSpan = std::abs (p.start - p.end); const auto rowSpan = getSpanFromAuto (item->row); - const auto nextAvailableCell = plane.nextAvailable (lastInsertionCell, columnSpan, rowSpan); + const auto insertionCell = hasDenseAutoFlow (grid.autoFlow) ? OccupancyPlane::Cell { p.start, 1 } + : lastInsertionCell; + const auto nextAvailableCell = plane.nextAvailableOnColumn (insertionCell, columnSpan, rowSpan, p.start); const auto lineArea = plane.setCell (nextAvailableCell, columnSpan, rowSpan); + lastInsertionCell = nextAvailableCell; - if (! hasDenseAutoFlow (grid.autoFlow)) - lastInsertionCell = nextAvailableCell; - - itemPlacementArray.add ({ item, lineArea }); + itemPlacementArray.add ({ item, lineArea }); } - } + else if (isFixed (item->row)) + { + const auto p = PlacementHelpers::deduceLineRange (item->row, grid.templateRows); + const auto columnSpan = getSpanFromAuto (item->column); + const auto rowSpan = std::abs (p.start - p.end); - return itemPlacementArray; - } + const auto insertionCell = hasDenseAutoFlow (grid.autoFlow) ? OccupancyPlane::Cell { 1, p.start } + : lastInsertionCell; - //============================================================================== - template - static PlacementHelpers::LineRange findFullLineRange (const ItemPlacementArray& items, Accessor&& accessor) - { - if (items.isEmpty()) - return { 1, 1 }; + const auto nextAvailableCell = plane.nextAvailableOnRow (insertionCell, columnSpan, rowSpan, p.start); + const auto lineArea = plane.setCell (nextAvailableCell, columnSpan, rowSpan); - const auto combine = [&accessor] (const auto& acc, const auto& item) - { - const auto newRange = accessor (item); - return PlacementHelpers::LineRange { std::min (acc.start, newRange.start), - std::max (acc.end, newRange.end) }; - }; + lastInsertionCell = nextAvailableCell; - return std::accumulate (std::next (items.begin()), items.end(), accessor (*items.begin()), combine); + itemPlacementArray.add ({ item, lineArea }); + } + } } - static PlacementHelpers::LineArea findFullLineArea (const ItemPlacementArray& items) - { - return { findFullLineRange (items, [] (const auto& item) { return item.second.column; }), - findFullLineRange (items, [] (const auto& item) { return item.second.row; }) }; - } + // https://www.w3.org/TR/css-grid-1/#auto-placement-algo step 3.3 + for (auto* item : sortedItems) + if (hasAutoPlacement (*item)) + plane.updateMaxCrossDimensionFromAutoPlacementItem (getSpanFromAuto (item->column), getSpanFromAuto (item->row)); - template - static Array repeated (int repeats, const Item& item) - { - Array result; - result.insertMultiple (-1, item, repeats); - return result; - } + lastInsertionCell = { 1, 1 }; - static Tracks createImplicitTracks (const Grid& grid, const ItemPlacementArray& items) + for (auto* item : sortedItems) { - const auto fullArea = findFullLineArea (items); + if (hasAutoPlacement (*item)) + { + const auto columnSpan = getSpanFromAuto (item->column); + const auto rowSpan = getSpanFromAuto (item->row); - const auto leadingColumns = std::max (0, 1 - fullArea.column.start); - const auto leadingRows = std::max (0, 1 - fullArea.row.start); + const auto nextAvailableCell = plane.nextAvailable (lastInsertionCell, columnSpan, rowSpan); + const auto lineArea = plane.setCell (nextAvailableCell, columnSpan, rowSpan); - const auto trailingColumns = std::max (0, fullArea.column.end - grid.templateColumns.size() - 1); - const auto trailingRows = std::max (0, fullArea.row .end - grid.templateRows .size() - 1); + if (! hasDenseAutoFlow (grid.autoFlow)) + lastInsertionCell = nextAvailableCell; - return { { repeated (leadingColumns, grid.autoColumns) + grid.templateColumns + repeated (trailingColumns, grid.autoColumns), - leadingColumns }, - { repeated (leadingRows, grid.autoRows) + grid.templateRows + repeated (trailingRows, grid.autoRows), - leadingRows } }; + itemPlacementArray.add ({ item, lineArea }); + } } - //============================================================================== - static void applySizeForAutoTracks (Tracks& tracks, const ItemPlacementArray& placements) + return itemPlacementArray; + } + + //============================================================================== + template + static PlacementHelpers::LineRange findFullLineRange (const ItemPlacementArray& items, Accessor&& accessor) + { + if (items.isEmpty()) + return { 1, 1 }; + + const auto combine = [&accessor] (const auto& acc, const auto& item) { - const auto setSizes = [&placements] (auto& tracksInDirection, const auto& getItem, const auto& getItemSize) - { - auto& array = tracksInDirection.items; + const auto newRange = accessor (item); + return PlacementHelpers::LineRange { std::min (acc.start, newRange.start), + std::max (acc.end, newRange.end) }; + }; - for (int index = 0; index < array.size(); ++index) - { - if (array.getReference (index).isAuto()) - { - const auto combiner = [&] (const auto acc, const auto& element) - { - const auto item = getItem (element.second); - const auto isNotSpan = std::abs (item.end - item.start) <= 1; - return isNotSpan && item.start == index + 1 - tracksInDirection.numImplicitLeading - ? std::max (acc, getItemSize (*element.first)) - : acc; - }; - - array.getReference (index).size = std::accumulate (placements.begin(), placements.end(), 0.0f, combiner); - } - } - }; + return std::accumulate (std::next (items.begin()), items.end(), accessor (*items.begin()), combine); + } - setSizes (tracks.rows, - [] (const auto& i) { return i.row; }, - [] (const auto& i) { return i.height + i.margin.top + i.margin.bottom; }); + static PlacementHelpers::LineArea findFullLineArea (const ItemPlacementArray& items) + { + return { findFullLineRange (items, [] (const auto& item) { return item.second.column; }), + findFullLineRange (items, [] (const auto& item) { return item.second.row; }) }; + } - setSizes (tracks.columns, - [] (const auto& i) { return i.column; }, - [] (const auto& i) { return i.width + i.margin.left + i.margin.right; }); - } - }; + template + static Array repeated (int repeats, const Item& item) + { + Array result; + result.insertMultiple (-1, item, repeats); + return result; + } - //============================================================================== - struct BoxAlignment + static Tracks createImplicitTracks (const Grid& grid, const ItemPlacementArray& items) { - static Rectangle alignItem (const GridItem& item, const Grid& grid, Rectangle area) - { - // if item align is auto, inherit value from grid - const auto alignType = item.alignSelf == GridItem::AlignSelf::autoValue - ? grid.alignItems - : static_cast (item.alignSelf); + const auto fullArea = findFullLineArea (items); - const auto justifyType = item.justifySelf == GridItem::JustifySelf::autoValue - ? grid.justifyItems - : static_cast (item.justifySelf); + const auto leadingColumns = std::max (0, 1 - fullArea.column.start); + const auto leadingRows = std::max (0, 1 - fullArea.row.start); - // subtract margin from area - area = BorderSize (item.margin.top, item.margin.left, item.margin.bottom, item.margin.right) - .subtractedFrom (area); + const auto trailingColumns = std::max (0, fullArea.column.end - grid.templateColumns.size() - 1); + const auto trailingRows = std::max (0, fullArea.row .end - grid.templateRows .size() - 1); - // align and justify - auto r = area; + return { { repeated (leadingColumns, grid.autoColumns) + grid.templateColumns + repeated (trailingColumns, grid.autoColumns), + leadingColumns }, + { repeated (leadingRows, grid.autoRows) + grid.templateRows + repeated (trailingRows, grid.autoRows), + leadingRows } }; + } - if (! approximatelyEqual (item.width, (float) GridItem::notAssigned)) r.setWidth (item.width); - if (! approximatelyEqual (item.height, (float) GridItem::notAssigned)) r.setHeight (item.height); - if (! approximatelyEqual (item.maxWidth, (float) GridItem::notAssigned)) r.setWidth (jmin (item.maxWidth, r.getWidth())); - if (item.minWidth > 0.0f) r.setWidth (jmax (item.minWidth, r.getWidth())); - if (! approximatelyEqual (item.maxHeight, (float) GridItem::notAssigned)) r.setHeight (jmin (item.maxHeight, r.getHeight())); - if (item.minHeight > 0.0f) r.setHeight (jmax (item.minHeight, r.getHeight())); + //============================================================================== + static void applySizeForAutoTracks (Tracks& tracks, const ItemPlacementArray& placements) + { + const auto setSizes = [&placements] (auto& tracksInDirection, const auto& getItem, const auto& getItemSize) + { + auto& array = tracksInDirection.items; - if (alignType == AlignItems::start && justifyType == JustifyItems::start) - return r; + for (int index = 0; index < array.size(); ++index) + { + if (array.getReference (index).isAuto()) + { + const auto combiner = [&] (const auto acc, const auto& element) + { + const auto item = getItem (element.second); + const auto isNotSpan = std::abs (item.end - item.start) <= 1; + return isNotSpan && item.start == index + 1 - tracksInDirection.numImplicitLeading + ? std::max (acc, getItemSize (*element.first)) + : acc; + }; + + array.getReference (index).size = std::accumulate (placements.begin(), placements.end(), 0.0f, combiner); + } + } + }; + + setSizes (tracks.rows, + [] (const auto& i) { return i.row; }, + [] (const auto& i) { return i.height + i.margin.top + i.margin.bottom; }); - if (alignType == AlignItems::end) r.setY (r.getY() + (area.getHeight() - r.getHeight())); - if (justifyType == JustifyItems::end) r.setX (r.getX() + (area.getWidth() - r.getWidth())); - if (alignType == AlignItems::center) r.setCentre (r.getCentreX(), area.getCentreY()); - if (justifyType == JustifyItems::center) r.setCentre (area.getCentreX(), r.getCentreY()); + setSizes (tracks.columns, + [] (const auto& i) { return i.column; }, + [] (const auto& i) { return i.width + i.margin.left + i.margin.right; }); + } +}; +//============================================================================== +struct Grid::BoxAlignment +{ + static Rectangle alignItem (const GridItem& item, + const Grid& grid, + Rectangle area) + { + // if item align is auto, inherit value from grid + const auto alignType = item.alignSelf == GridItem::AlignSelf::autoValue + ? grid.alignItems + : static_cast (item.alignSelf); + + const auto justifyType = item.justifySelf == GridItem::JustifySelf::autoValue + ? grid.justifyItems + : static_cast (item.justifySelf); + + // subtract margin from area + area = BorderSize (item.margin.top, item.margin.left, item.margin.bottom, item.margin.right) + .subtractedFrom (area); + + // align and justify + auto r = area; + + if (item.width != (float) GridItem::notAssigned) r.setWidth (item.width); + if (item.height != (float) GridItem::notAssigned) r.setHeight (item.height); + if (item.maxWidth != (float) GridItem::notAssigned) r.setWidth (jmin (item.maxWidth, r.getWidth())); + if (item.minWidth > 0.0f) r.setWidth (jmax (item.minWidth, r.getWidth())); + if (item.maxHeight != (float) GridItem::notAssigned) r.setHeight (jmin (item.maxHeight, r.getHeight())); + if (item.minHeight > 0.0f) r.setHeight (jmax (item.minHeight, r.getHeight())); + + if (alignType == AlignItems::start && justifyType == JustifyItems::start) return r; - } - }; + if (alignType == AlignItems::end) r.setY (r.getY() + (area.getHeight() - r.getHeight())); + if (justifyType == JustifyItems::end) r.setX (r.getX() + (area.getWidth() - r.getWidth())); + if (alignType == AlignItems::center) r.setCentre (r.getCentreX(), area.getCentreY()); + if (justifyType == JustifyItems::center) r.setCentre (area.getCentreX(), r.getCentreY()); + + return r; + } }; //============================================================================== @@ -1088,7 +1004,7 @@ Grid::TrackInfo::TrackInfo (const String& startLineNameToUse, Px sizeInPixels, c } Grid::TrackInfo::TrackInfo (const String& startLineNameToUse, Fr fractionOfFreeSpace, const String& endLineNameToUse) noexcept - : TrackInfo (startLineNameToUse, fractionOfFreeSpace) + : TrackInfo (startLineNameToUse, fractionOfFreeSpace) { endLineName = endLineNameToUse; } @@ -1101,57 +1017,37 @@ float Grid::TrackInfo::getAbsoluteSize (float relativeFractionalUnit) const //============================================================================== void Grid::performLayout (Rectangle targetArea) { - const auto itemsAndAreas = Helpers::AutoPlacement().deduceAllItems (*this); + const auto itemsAndAreas = AutoPlacement().deduceAllItems (*this); - auto implicitTracks = Helpers::AutoPlacement::createImplicitTracks (*this, itemsAndAreas); + auto implicitTracks = AutoPlacement::createImplicitTracks (*this, itemsAndAreas); - Helpers::AutoPlacement::applySizeForAutoTracks (implicitTracks, itemsAndAreas); + AutoPlacement::applySizeForAutoTracks (implicitTracks, itemsAndAreas); - Helpers::SizeCalculation calculation; - Helpers::SizeCalculation roundedCalculation; - - const auto doComputeSizes = [&] (auto& sizeCalculation) - { - sizeCalculation.computeSizes (targetArea.toFloat().getWidth(), - targetArea.toFloat().getHeight(), - columnGap, - rowGap, - implicitTracks); - }; - - doComputeSizes (calculation); - doComputeSizes (roundedCalculation); + SizeCalculation calculation; + calculation.computeSizes (targetArea.toFloat().getWidth(), + targetArea.toFloat().getHeight(), + columnGap, + rowGap, + implicitTracks); for (auto& itemAndArea : itemsAndAreas) { - auto* item = itemAndArea.first; - - const auto getBounds = [&] (const auto& sizeCalculation) - { - const auto a = itemAndArea.second; + const auto a = itemAndArea.second; + const auto areaBounds = PlacementHelpers::getAreaBounds (a.column, + a.row, + implicitTracks, + calculation, + alignContent, + justifyContent, + columnGap, + rowGap); - const auto areaBounds = Helpers::PlacementHelpers::getAreaBounds (a.column, - a.row, - implicitTracks, - sizeCalculation, - alignContent, - justifyContent); - - const auto rounded = [&] (auto rect) -> decltype (rect) - { - return { sizeCalculation.roundingFunction (rect.getX()), - sizeCalculation.roundingFunction (rect.getY()), - sizeCalculation.roundingFunction (rect.getWidth()), - sizeCalculation.roundingFunction (rect.getHeight()) }; - }; - - return rounded (Helpers::BoxAlignment::alignItem (*item, *this, areaBounds)); - }; - - item->currentBounds = getBounds (calculation) + targetArea.toFloat().getPosition(); + auto* item = itemAndArea.first; + item->currentBounds = BoxAlignment::alignItem (*item, *this, areaBounds) + + targetArea.toFloat().getPosition(); if (auto* c = item->associatedComponent) - c->setBounds (getBounds (roundedCalculation).toNearestIntEdges() + targetArea.getPosition()); + c->setBounds (item->currentBounds.getSmallestIntegerContainer()); } } @@ -1459,177 +1355,6 @@ struct GridTests : public UnitTest expect (grid.items[1].currentBounds == Rect (420.0f, 70.0f, 60.0f, 70.0f)); expect (grid.items[2].currentBounds == Rect (200.0f, 330.0f, 200.0f, 70.0f)); } - - { - beginTest ("Items with specified sizes should translate to correctly rounded Component dimensions"); - - static constexpr int targetSize = 100; - - juce::Component component; - juce::GridItem item { component }; - item.alignSelf = juce::GridItem::AlignSelf::center; - item.justifySelf = juce::GridItem::JustifySelf::center; - item.width = (float) targetSize; - item.height = (float) targetSize; - - juce::Grid grid; - grid.templateColumns = { juce::Grid::Fr { 1 } }; - grid.templateRows = { juce::Grid::Fr { 1 } }; - grid.items = { item }; - - for (int totalSize = 100 - 20; totalSize < 100 + 20; ++totalSize) - { - Rectangle bounds { 0, 0, totalSize, totalSize }; - grid.performLayout (bounds); - - expectEquals (component.getWidth(), targetSize); - expectEquals (component.getHeight(), targetSize); - } - } - - { - beginTest ("Track sizes specified in Px should translate to correctly rounded Component dimensions"); - - static constexpr int targetSize = 100; - - juce::Component component; - juce::GridItem item { component }; - item.alignSelf = juce::GridItem::AlignSelf::center; - item.justifySelf = juce::GridItem::JustifySelf::center; - item.setArea (1, 3); - - juce::Grid grid; - grid.templateColumns = { juce::Grid::Fr { 1 }, - juce::Grid::Fr { 1 }, - juce::Grid::Px { targetSize }, - juce::Grid::Fr { 1 } }; - grid.templateRows = { juce::Grid::Fr { 1 } }; - grid.items = { item }; - - for (int totalSize = 100 - 20; totalSize < 100 + 20; ++totalSize) - { - Rectangle bounds { 0, 0, totalSize, totalSize }; - grid.performLayout (bounds); - - expectEquals (component.getWidth(), targetSize); - } - } - - { - beginTest ("Evaluate invariants on randomised Grid layouts"); - - struct Solution - { - Grid grid; - std::deque components; - int absoluteWidth; - Rectangle bounds; - }; - - auto createSolution = [this] (int numColumns, - float probabilityOfFractionalColumn, - Rectangle bounds) -> Solution - { - auto random = getRandom(); - - Grid grid; - grid.templateRows = { Grid::Fr { 1 } }; - - // Ensuring that the sum of absolute item widths never exceed total width - const auto widthOfAbsolute = (int) ((float) bounds.getWidth() / (float) (numColumns + 1)); - - for (int i = 0; i < numColumns; ++i) - { - if (random.nextFloat() < probabilityOfFractionalColumn) - grid.templateColumns.add (Grid::Fr { 1 }); - else - grid.templateColumns.add (Grid::Px { widthOfAbsolute }); - } - - std::deque itemComponents (static_cast (grid.templateColumns.size())); - - for (auto& c : itemComponents) - grid.items.add (GridItem { c }); - - grid.performLayout (bounds); - - return { std::move (grid), std::move (itemComponents), widthOfAbsolute, bounds }; - }; - - const auto getFractionalComponentWidths = [] (const Solution& solution) - { - std::vector result; - - for (int i = 0; i < solution.grid.templateColumns.size(); ++i) - if (solution.grid.templateColumns[i].isFractional()) - result.push_back (solution.components[(size_t) i].getWidth()); - - return result; - }; - - const auto getAbsoluteComponentWidths = [] (const Solution& solution) - { - std::vector result; - - for (int i = 0; i < solution.grid.templateColumns.size(); ++i) - if (! solution.grid.templateColumns[i].isFractional()) - result.push_back (solution.components[(size_t) i].getWidth()); - - return result; - }; - - const auto evaluateInvariants = [&] (const Solution& solution) - { - const auto fractionalWidths = getFractionalComponentWidths (solution); - - if (! fractionalWidths.empty()) - { - const auto [min, max] = std::minmax_element (fractionalWidths.begin(), - fractionalWidths.end()); - expectLessOrEqual (*max - *min, 1, "Fr { 1 } items are expected to share the " - "rounding errors equally and hence couldn't " - "deviate in size by more than 1 px"); - } - - const auto absoluteWidths = getAbsoluteComponentWidths (solution); - - for (const auto& w : absoluteWidths) - expectEquals (w, solution.absoluteWidth, "Sizes specified in absolute dimensions should " - "be preserved"); - - Rectangle unionOfComponentBounds; - - for (const auto& c : solution.components) - unionOfComponentBounds = unionOfComponentBounds.getUnion (c.getBoundsInParent()); - - if ((size_t) solution.grid.templateColumns.size() == absoluteWidths.size()) - expect (solution.bounds.contains (unionOfComponentBounds), "Non-oversized absolute Components " - "should never be placed outside the " - "provided bounds."); - else - expect (unionOfComponentBounds == solution.bounds, "With fractional items, positioned items " - "should cover the provided bounds exactly"); - }; - - const auto knownPreviousBad = createSolution (5, 1.0f, Rectangle { 0, 0, 600, 200 }.reduced (16)); - evaluateInvariants (knownPreviousBad); - - auto random = getRandom(); - - for (int i = 0; i < 1000; ++i) - { - const auto numColumns = random.nextInt (Range { 1, 26 }); - const auto probabilityOfFractionalColumn = random.nextFloat(); - const auto bounds = Rectangle { random.nextInt (Range { 0, 3 }), - random.nextInt (Range { 0, 3 }), - random.nextInt (Range { 300, 1200 }), - random.nextInt (Range { 100, 500 }) } - .reduced (random.nextInt (Range { 0, 16 })); - - const auto randomSolution = createSolution (numColumns, probabilityOfFractionalColumn, bounds); - evaluateInvariants (randomSolution); - } - } } }; diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.h index 437f4657..ce261d21 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Grid.h @@ -156,6 +156,9 @@ class JUCE_API Grid final /** Creates an empty Grid container with default parameters. */ Grid() = default; + /** Destructor */ + ~Grid() noexcept = default; + //============================================================================== /** Specifies the alignment of content inside the items along the rows. */ JustifyItems justifyItems = JustifyItems::stretch; @@ -213,7 +216,10 @@ class JUCE_API Grid final private: //============================================================================== - struct Helpers; + struct SizeCalculation; + struct PlacementHelpers; + struct AutoPlacement; + struct BoxAlignment; }; constexpr Grid::Px operator"" _px (long double px) { return Grid::Px { px }; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_GroupComponent.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_GroupComponent.h index 31b34377..e45b15f9 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_GroupComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_GroupComponent.h @@ -98,10 +98,10 @@ class JUCE_API GroupComponent : public Component void enablementChanged() override; /** @internal */ void colourChanged() override; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; private: + std::unique_ptr createAccessibilityHandler() override; + String text; Justification justification; diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.cpp index b835c270..36fcbf34 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.cpp @@ -125,10 +125,6 @@ void ResizableBorderComponent::mouseDown (const MouseEvent& e) originalBounds = component->getBounds(); - if (auto* peer = component->getPeer()) - if (&peer->getComponent() == component) - peer->startHostManagedResize (peer->globalToLocal (localPointToGlobal (e.getPosition())), mouseZone); - if (constrainer != nullptr) constrainer->resizeStart(); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.h index fc3c989c..bdab8b98 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.h @@ -125,7 +125,7 @@ class JUCE_API ResizableBorderComponent : public Component /** Returns an appropriate mouse-cursor for this resize zone. */ MouseCursor getMouseCursor() const noexcept; - /** Returns true if dragging this zone will move the entire object without resizing it. */ + /** Returns true if dragging this zone will move the enire object without resizing it. */ bool isDraggingWholeObject() const noexcept { return zone == centre; } /** Returns true if dragging this zone will move the object's left edge. */ bool isDraggingLeftEdge() const noexcept { return (zone & left) != 0; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.cpp index 8bcd024d..83535a07 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.cpp @@ -45,7 +45,7 @@ void ResizableCornerComponent::paint (Graphics& g) isMouseButtonDown()); } -void ResizableCornerComponent::mouseDown (const MouseEvent& e) +void ResizableCornerComponent::mouseDown (const MouseEvent&) { if (component == nullptr) { @@ -55,13 +55,6 @@ void ResizableCornerComponent::mouseDown (const MouseEvent& e) originalBounds = component->getBounds(); - using Zone = ResizableBorderComponent::Zone; - const Zone zone { Zone::bottom | Zone::right }; - - if (auto* peer = component->getPeer()) - if (&peer->getComponent() == component) - peer->startHostManagedResize (peer->globalToLocal (localPointToGlobal (e.getPosition())), zone); - if (constrainer != nullptr) constrainer->resizeStart(); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp index 85f16404..8629e5fc 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp @@ -52,7 +52,7 @@ void ResizableEdgeComponent::paint (Graphics& g) isMouseOver(), isMouseButtonDown()); } -void ResizableEdgeComponent::mouseDown (const MouseEvent& e) +void ResizableEdgeComponent::mouseDown (const MouseEvent&) { if (component == nullptr) { @@ -62,25 +62,6 @@ void ResizableEdgeComponent::mouseDown (const MouseEvent& e) originalBounds = component->getBounds(); - using Zone = ResizableBorderComponent::Zone; - - const Zone zone { [&] - { - switch (edge) - { - case Edge::leftEdge: return Zone::left; - case Edge::rightEdge: return Zone::right; - case Edge::topEdge: return Zone::top; - case Edge::bottomEdge: return Zone::bottom; - } - - return Zone::centre; - }() }; - - if (auto* peer = component->getPeer()) - if (&peer->getComponent() == component) - peer->startHostManagedResize (peer->globalToLocal (localPointToGlobal (e.getPosition())), zone); - if (constrainer != nullptr) constrainer->resizeStart(); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ScrollBar.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ScrollBar.h index 1c151ff5..a6d94185 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ScrollBar.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ScrollBar.h @@ -414,8 +414,6 @@ class JUCE_API ScrollBar : public Component, void parentHierarchyChanged() override; /** @internal */ void setVisible (bool) override; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; private: //============================================================================== @@ -429,6 +427,7 @@ class JUCE_API ScrollBar : public Component, std::unique_ptr upButton, downButton; ListenerList listeners; + std::unique_ptr createAccessibilityHandler() override; void handleAsyncUpdate() override; void updateThumbPosition(); void timerCallback() override; diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_SidePanel.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_SidePanel.h index a425bb9d..59395f10 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_SidePanel.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_SidePanel.h @@ -195,8 +195,6 @@ class SidePanel : public Component, void mouseDrag (const MouseEvent&) override; /** @internal */ void mouseUp (const MouseEvent&) override; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; private: //============================================================================== @@ -223,6 +221,7 @@ class SidePanel : public Component, bool shouldShowDismissButton = true; //============================================================================== + std::unique_ptr createAccessibilityHandler() override; void lookAndFeelChanged() override; void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override; void changeListenerCallback (ChangeBroadcaster*) override; diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_StretchableLayoutManager.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_StretchableLayoutManager.cpp index 368e2cba..4505a179 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_StretchableLayoutManager.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_StretchableLayoutManager.cpp @@ -336,7 +336,7 @@ int StretchableLayoutManager::sizeToRealSize (double size, int totalSpace) if (size < 0) size *= -totalSpace; - return roundToInt (jmax (1.0, size)); + return roundToInt (size); } } // namespace juce diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h index 5d52af15..c4ab1a43 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h @@ -334,8 +334,6 @@ class JUCE_API TabbedButtonBar : public Component, void resized() override; /** @internal */ void lookAndFeelChanged() override; - /** @internal */ - std::unique_ptr createAccessibilityHandler() override; protected: //============================================================================== @@ -364,6 +362,7 @@ class JUCE_API TabbedButtonBar : public Component, std::unique_ptr behindFrontTab; std::unique_ptr