Browse Source

Update to latest JUCE

Signed-off-by: falkTX <falktx@falktx.com>
tags/2021-03-15
falkTX 4 years ago
parent
commit
9dc5ca5cb5
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
100 changed files with 8081 additions and 2479 deletions
  1. +2
    -2
      libs/juce-current/patches/01_audiodevicemanager-prefer-jack-over-alsa.patch
  2. +2
    -2
      libs/juce-current/patches/03_fix-midi-message-constness.patch
  3. +245
    -704
      libs/juce-current/patches/04_vst2-interface.patch
  4. +538
    -0
      libs/juce-current/patches/04_vst2-interface_file.patch
  5. +0
    -78
      libs/juce-current/patches/04_vst2-interface_pt2.patch
  6. +3
    -3
      libs/juce-current/patches/05_mingw-fixes.patch
  7. +25
    -25
      libs/juce-current/patches/06_old-compiler-compatibility.patch
  8. +2
    -2
      libs/juce-current/patches/09_proper-getExecutableFile.patch
  9. +5
    -5
      libs/juce-current/patches/10_fix-fork-exec-usage.patch
  10. +3
    -3
      libs/juce-current/patches/11_childprocess-getPID.patch
  11. +3
    -3
      libs/juce-current/patches/12_linux-filechooser-needs-modal-loops.patch
  12. +18
    -11
      libs/juce-current/patches/14_fix-x11-temporary-windows.patch
  13. +2
    -2
      libs/juce-current/patches/15_fix-linux-thread-prio.patch
  14. +2
    -2
      libs/juce-current/patches/17_allow-vst2-without-audio.patch
  15. +5
    -3
      libs/juce-current/patches/19_fix-vst2-linux-gui-init.patch
  16. +9
    -9
      libs/juce-current/patches/21_audioprocessor-no-gui.patch
  17. +10
    -7
      libs/juce-current/patches/22_mingw-filechooser-no-vista.patch
  18. +2
    -2
      libs/juce-current/patches/23_fix-juce-includes.patch
  19. +2
    -2
      libs/juce-current/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp
  20. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/juce_audio_basics.h
  21. +72
    -4
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp
  22. +432
    -80
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp
  23. +200
    -16
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp
  24. +38
    -4
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessage.h
  25. +30
    -8
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp
  26. +2
    -0
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h
  27. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_ADSR.h
  28. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_Interpolators.cpp
  29. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.cpp
  30. +13
    -6
      libs/juce-current/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
  31. +1
    -1
      libs/juce-current/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h
  32. +2
    -3
      libs/juce-current/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h
  33. +31
    -2
      libs/juce-current/source/modules/juce_audio_devices/juce_audio_devices.cpp
  34. +1
    -1
      libs/juce-current/source/modules/juce_audio_devices/juce_audio_devices.h
  35. +134
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPBytestreamInputHandler.h
  36. +326
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPConversion.h
  37. +165
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPConverters.h
  38. +190
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPDispatcher.h
  39. +534
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPFactory.h
  40. +126
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPIterator.h
  41. +213
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPMidi1ToBytestreamTranslator.h
  42. +195
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp
  43. +187
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h
  44. +44
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPProtocols.h
  45. +40
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPReceiver.h
  46. +53
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPSysEx7.cpp
  47. +73
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPSysEx7.h
  48. +1020
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPTests.cpp
  49. +145
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h
  50. +59
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPUtils.cpp
  51. +113
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPUtils.h
  52. +35
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPView.cpp
  53. +88
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPView.h
  54. +187
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPacket.h
  55. +92
    -0
      libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPackets.h
  56. +22
    -7
      libs/juce-current/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
  57. +0
    -732
      libs/juce-current/source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp
  58. +1325
    -0
      libs/juce-current/source/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm
  59. +26
    -26
      libs/juce-current/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp
  60. +7
    -14
      libs/juce-current/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp
  61. +54
    -39
      libs/juce-current/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp
  62. +2
    -2
      libs/juce-current/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp
  63. +17
    -10
      libs/juce-current/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp
  64. +6
    -6
      libs/juce-current/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp
  65. +4
    -2
      libs/juce-current/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp
  66. +2
    -2
      libs/juce-current/source/modules/juce_audio_formats/juce_audio_formats.h
  67. +27
    -9
      libs/juce-current/source/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp
  68. +26
    -13
      libs/juce-current/source/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
  69. +75
    -63
      libs/juce-current/source/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm
  70. +2
    -2
      libs/juce-current/source/modules/juce_audio_plugin_client/LV2/juce_LV2_Wrapper.cpp
  71. +2
    -0
      libs/juce-current/source/modules/juce_audio_plugin_client/LV2/juce_LV2_Wrapper_Exporter.cpp
  72. +1
    -1
      libs/juce-current/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp
  73. +1
    -1
      libs/juce-current/source/modules/juce_audio_plugin_client/Unity/juce_UnityPluginInterface.h
  74. +2
    -1
      libs/juce-current/source/modules/juce_audio_plugin_client/Unity/juce_Unity_Wrapper.cpp
  75. +144
    -136
      libs/juce-current/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
  76. +216
    -145
      libs/juce-current/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
  77. +1
    -1
      libs/juce-current/source/modules/juce_audio_plugin_client/juce_audio_plugin_client.h
  78. +5
    -1
      libs/juce-current/source/modules/juce_audio_plugin_client/juce_audio_plugin_client_AU_2.mm
  79. +2
    -0
      libs/juce-current/source/modules/juce_audio_plugin_client/juce_audio_plugin_client_Standalone.cpp
  80. +26
    -19
      libs/juce-current/source/modules/juce_audio_processors/format_types/juce_AU_Shared.h
  81. +9
    -4
      libs/juce-current/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
  82. +1
    -1
      libs/juce-current/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp
  83. +15
    -18
      libs/juce-current/source/modules/juce_audio_processors/format_types/juce_VST3Common.h
  84. +107
    -175
      libs/juce-current/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  85. +18
    -20
      libs/juce-current/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  86. +92
    -3
      libs/juce-current/source/modules/juce_audio_processors/juce_audio_processors.cpp
  87. +1
    -1
      libs/juce-current/source/modules/juce_audio_processors/juce_audio_processors.h
  88. +25
    -3
      libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
  89. +3
    -2
      libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h
  90. +27
    -4
      libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
  91. +21
    -0
      libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h
  92. +23
    -1
      libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorListener.h
  93. +1
    -1
      libs/juce-current/source/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp
  94. +1
    -1
      libs/juce-current/source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp
  95. +1
    -1
      libs/juce-current/source/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp
  96. +3
    -3
      libs/juce-current/source/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp
  97. +1
    -1
      libs/juce-current/source/modules/juce_audio_utils/juce_audio_utils.h
  98. +2
    -2
      libs/juce-current/source/modules/juce_audio_utils/native/juce_mac_AudioCDReader.mm
  99. +4
    -4
      libs/juce-current/source/modules/juce_core/containers/juce_AbstractFifo.h
  100. +8
    -8
      libs/juce-current/source/modules/juce_core/containers/juce_Array.h

+ 2
- 2
libs/juce-current/patches/01_audiodevicemanager-prefer-jack-over-alsa.patch View File

@@ -1,8 +1,8 @@
diff --git a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
index dc2ad8e3b..1f03bc047 100644
index a19c7b05e..deabba6db 100644
--- a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
+++ b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
@@ -184,8 +184,8 @@ void AudioDeviceManager::createAudioDeviceTypes (OwnedArray<AudioIODeviceType>&
@@ -185,8 +185,8 @@ void AudioDeviceManager::createAudioDeviceTypes (OwnedArray<AudioIODeviceType>&
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio());
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_iOSAudio());
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Bela());


+ 2
- 2
libs/juce-current/patches/03_fix-midi-message-constness.patch View File

@@ -1,8 +1,8 @@
diff --git a/modules/juce_audio_basics/midi/juce_MidiMessage.h b/modules/juce_audio_basics/midi/juce_MidiMessage.h
index 2d9e5adf0..30a609cec 100644
index 22ac79483..5c761f659 100644
--- a/modules/juce_audio_basics/midi/juce_MidiMessage.h
+++ b/modules/juce_audio_basics/midi/juce_MidiMessage.h
@@ -945,7 +945,7 @@ private:
@@ -979,7 +979,7 @@ private:
#endif
inline bool isHeapAllocated() const noexcept { return size > (int) sizeof (packedData); }


+ 245
- 704
libs/juce-current/patches/04_vst2-interface.patch
File diff suppressed because it is too large
View File


+ 538
- 0
libs/juce-current/patches/04_vst2-interface_file.patch View File

@@ -0,0 +1,538 @@
diff --git a/modules/juce_audio_processors/format_types/juce_VSTInterface.h b/modules/juce_audio_processors/format_types/juce_VSTInterface.h
new file mode 100644
index 000000000..58179be1a
--- /dev/null
+++ b/modules/juce_audio_processors/format_types/juce_VSTInterface.h
@@ -0,0 +1,532 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library.
+ Copyright (c) 2017 - ROLI Ltd.
+
+ JUCE is an open source library subject to commercial or open-source
+ licensing.
+
+ By using JUCE, you agree to the terms of both the JUCE 5 End-User License
+ Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
+ 27th April 2017).
+
+ End User License Agreement: www.juce.com/juce-5-licence
+ Privacy Policy: www.juce.com/juce-5-privacy-policy
+
+ Or: You may also use this code under the terms of the GPL v3 (see
+ www.gnu.org/licenses).
+
+ JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
+ EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
+ DISCLAIMED.
+
+ ==============================================================================
+*/
+
+#ifndef JUCE_VSTINTERFACE_H_INCLUDED
+#define JUCE_VSTINTERFACE_H_INCLUDED
+
+using namespace juce;
+
+#if JUCE_MSVC
+ #define VSTINTERFACECALL __cdecl
+ #pragma pack(push)
+ #pragma pack(8)
+#elif JUCE_MAC || JUCE_IOS
+ #define VSTINTERFACECALL
+ #if JUCE_64BIT
+ #pragma options align=power
+ #else
+ #pragma options align=mac68k
+ #endif
+#else
+ #define VSTINTERFACECALL
+ #pragma pack(push, 8)
+#endif
+
+const int32 juceVstInterfaceVersion = 2400;
+const int32 juceVstInterfaceIdentifier = 0x56737450; // The "magic" identifier in the SDK is 'VstP'.
+
+//==============================================================================
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct VstEffectInterface
+{
+ int32 interfaceIdentifier;
+ pointer_sized_int (VSTINTERFACECALL* dispatchFunction) (VstEffectInterface*, int32 op, int32 index, pointer_sized_int value, void* ptr, float opt);
+ void (VSTINTERFACECALL* processAudioFunction) (VstEffectInterface*, float** inputs, float** outputs, int32 numSamples);
+ void (VSTINTERFACECALL* setParameterValueFunction) (VstEffectInterface*, int32 parameterIndex, float value);
+ float (VSTINTERFACECALL* getParameterValueFunction) (VstEffectInterface*, int32 parameterIndex);
+ int32 numPrograms;
+ int32 numParameters;
+ int32 numInputChannels;
+ int32 numOutputChannels;
+ int32 flags;
+ pointer_sized_int hostSpace1;
+ pointer_sized_int hostSpace2;
+ int32 latency;
+ int32 deprecated1;
+ int32 deprecated2;
+ float deprecated3;
+ void* effectPointer;
+ void* userPointer;
+ int32 plugInIdentifier;
+ int32 plugInVersion;
+ void (VSTINTERFACECALL* processAudioInplaceFunction) (VstEffectInterface*, float** inputs, float** outputs, int32 numSamples);
+ void (VSTINTERFACECALL* processDoubleAudioInplaceFunction) (VstEffectInterface*, double** inputs, double** outputs, int32 numSamples);
+ char emptySpace[56];
+};
+
+typedef pointer_sized_int (VSTINTERFACECALL* VstHostCallback) (VstEffectInterface*, int32 op, int32 index, pointer_sized_int value, void* ptr, float opt);
+
+enum VstEffectInterfaceFlags
+{
+ vstEffectFlagHasEditor = 1,
+ vstEffectFlagInplaceAudio = 16,
+ vstEffectFlagDataInChunks = 32,
+ vstEffectFlagIsSynth = 256,
+ vstEffectFlagInplaceDoubleAudio = 4096
+};
+
+//==============================================================================
+enum VstHostToPlugInOpcodes
+{
+ plugInOpcodeOpen,
+ plugInOpcodeClose,
+ plugInOpcodeSetCurrentProgram,
+ plugInOpcodeGetCurrentProgram,
+ plugInOpcodeSetCurrentProgramName,
+ plugInOpcodeGetCurrentProgramName,
+ plugInOpcodeGetParameterLabel,
+ plugInOpcodeGetParameterText,
+ plugInOpcodeGetParameterName,
+ plugInOpcodeSetSampleRate = plugInOpcodeGetParameterName + 2,
+ plugInOpcodeSetBlockSize,
+ plugInOpcodeResumeSuspend,
+ plugInOpcodeGetEditorBounds,
+ plugInOpcodeOpenEditor,
+ plugInOpcodeCloseEditor,
+ plugInOpcodeDrawEditor,
+ plugInOpcodeGetMouse,
+ plugInOpcodeEditorIdle = plugInOpcodeGetMouse + 2,
+ plugInOpcodeeffEditorTop,
+ plugInOpcodeSleepEditor,
+ plugInOpcodeIdentify,
+ plugInOpcodeGetData,
+ plugInOpcodeSetData,
+ plugInOpcodePreAudioProcessingEvents,
+ plugInOpcodeIsParameterAutomatable,
+ plugInOpcodeParameterValueForText,
+ plugInOpcodeGetProgramName = plugInOpcodeParameterValueForText + 2,
+ plugInOpcodeConnectInput = plugInOpcodeGetProgramName + 2,
+ plugInOpcodeConnectOutput,
+ plugInOpcodeGetInputPinProperties,
+ plugInOpcodeGetOutputPinProperties,
+ plugInOpcodeGetPlugInCategory,
+ plugInOpcodeSetSpeakerConfiguration = plugInOpcodeGetPlugInCategory + 7,
+ plugInOpcodeSetBypass = plugInOpcodeSetSpeakerConfiguration + 2,
+ plugInOpcodeGetPlugInName,
+ plugInOpcodeGetManufacturerName = plugInOpcodeGetPlugInName + 2,
+ plugInOpcodeGetManufacturerProductName,
+ plugInOpcodeGetManufacturerVersion,
+ plugInOpcodeManufacturerSpecific,
+ plugInOpcodeCanPlugInDo,
+ plugInOpcodeGetTailSize,
+ plugInOpcodeIdle,
+ plugInOpcodeKeyboardFocusRequired = plugInOpcodeIdle + 4,
+ plugInOpcodeGetVstInterfaceVersion,
+ plugInOpcodeGetCurrentMidiProgram = plugInOpcodeGetVstInterfaceVersion + 5,
+ plugInOpcodeGetSpeakerArrangement = plugInOpcodeGetCurrentMidiProgram + 6,
+ plugInOpcodeNextPlugInUniqueID,
+ plugInOpcodeStartProcess,
+ plugInOpcodeStopProcess,
+ plugInOpcodeSetNumberOfSamplesToProcess,
+ plugInOpcodeSetSampleFloatType = plugInOpcodeSetNumberOfSamplesToProcess + 4,
+ pluginOpcodeGetNumMidiInputChannels,
+ pluginOpcodeGetNumMidiOutputChannels,
+ plugInOpcodeMaximum = pluginOpcodeGetNumMidiOutputChannels
+};
+
+
+enum VstPlugInToHostOpcodes
+{
+ hostOpcodeParameterChanged,
+ hostOpcodeVstVersion,
+ hostOpcodeCurrentId,
+ hostOpcodeIdle,
+ hostOpcodePinConnected,
+ hostOpcodePlugInWantsMidi = hostOpcodePinConnected + 2,
+ hostOpcodeGetTimingInfo,
+ hostOpcodePreAudioProcessingEvents,
+ hostOpcodeSetTime,
+ hostOpcodeTempoAt,
+ hostOpcodeGetNumberOfAutomatableParameters,
+ hostOpcodeGetParameterInterval,
+ hostOpcodeIOModified,
+ hostOpcodeNeedsIdle,
+ hostOpcodeWindowSize,
+ hostOpcodeGetSampleRate,
+ hostOpcodeGetBlockSize,
+ hostOpcodeGetInputLatency,
+ hostOpcodeGetOutputLatency,
+ hostOpcodeGetPreviousPlugIn,
+ hostOpcodeGetNextPlugIn,
+ hostOpcodeWillReplace,
+ hostOpcodeGetCurrentAudioProcessingLevel,
+ hostOpcodeGetAutomationState,
+ hostOpcodeOfflineStart,
+ hostOpcodeOfflineReadSource,
+ hostOpcodeOfflineWrite,
+ hostOpcodeOfflineGetCurrentPass,
+ hostOpcodeOfflineGetCurrentMetaPass,
+ hostOpcodeSetOutputSampleRate,
+ hostOpcodeGetOutputSpeakerConfiguration,
+ hostOpcodeGetManufacturerName,
+ hostOpcodeGetProductName,
+ hostOpcodeGetManufacturerVersion,
+ hostOpcodeManufacturerSpecific,
+ hostOpcodeSetIcon,
+ hostOpcodeCanHostDo,
+ hostOpcodeGetLanguage,
+ hostOpcodeOpenEditorWindow,
+ hostOpcodeCloseEditorWindow,
+ hostOpcodeGetDirectory,
+ hostOpcodeUpdateView,
+ hostOpcodeParameterChangeGestureBegin,
+ hostOpcodeParameterChangeGestureEnd,
+};
+
+//==============================================================================
+enum VstProcessingSampleType
+{
+ vstProcessingSampleTypeFloat,
+ vstProcessingSampleTypeDouble
+};
+
+//==============================================================================
+// These names must be identical to the Steinberg SDK so JUCE users can set
+// exactly what they want.
+enum VstPlugInCategory
+{
+ kPlugCategUnknown,
+ kPlugCategEffect,
+ kPlugCategSynth,
+ kPlugCategAnalysis,
+ kPlugCategMastering,
+ kPlugCategSpacializer,
+ kPlugCategRoomFx,
+ kPlugSurroundFx,
+ kPlugCategRestoration,
+ kPlugCategOfflineProcess,
+ kPlugCategShell,
+ kPlugCategGenerator
+};
+
+//==============================================================================
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct VstEditorBounds
+{
+ int16 upper;
+ int16 leftmost;
+ int16 lower;
+ int16 rightmost;
+};
+
+//==============================================================================
+enum VstMaxStringLengths
+{
+ vstMaxNameLength = 64,
+ vstMaxParameterOrPinLabelLength = 64,
+ vstMaxParameterOrPinShortLabelLength = 8,
+ vstMaxCategoryLength = 24,
+ vstMaxManufacturerStringLength = 64,
+ vstMaxPlugInNameStringLength = 64
+};
+
+//==============================================================================
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct VstPinInfo
+{
+ char text[vstMaxParameterOrPinLabelLength];
+ int32 flags;
+ int32 configurationType;
+ char shortText[vstMaxParameterOrPinShortLabelLength];
+ char unused[48];
+};
+
+enum VstPinInfoFlags
+{
+ vstPinInfoFlagIsActive = 1,
+ vstPinInfoFlagIsStereo = 2,
+ vstPinInfoFlagValid = 4
+};
+
+//==============================================================================
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct VstEvent
+{
+ int32 type;
+ int32 size;
+ int32 sampleOffset;
+ int32 flags;
+ char content[16];
+};
+
+enum VstEventTypes
+{
+ vstMidiEventType = 1,
+ vstSysExEventType = 6
+};
+
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct VstEventBlock
+{
+ int32 numberOfEvents;
+ pointer_sized_int future;
+ VstEvent* events[2];
+};
+
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct VstMidiEvent
+{
+ int32 type;
+ int32 size;
+ int32 sampleOffset;
+ int32 flags;
+ int32 noteSampleLength;
+ int32 noteSampleOffset;
+ char midiData[4];
+ char tuning;
+ char noteVelocityOff;
+ char future1;
+ char future2;
+};
+
+enum VstMidiEventFlags
+{
+ vstMidiEventIsRealtime = 1
+};
+
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct VstSysExEvent
+{
+ int32 type;
+ int32 size;
+ int32 offsetSamples;
+ int32 flags;
+ int32 sysExDumpSize;
+ pointer_sized_int future1;
+ char* sysExDump;
+ pointer_sized_int future2;
+};
+
+//==============================================================================
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct VstTimingInformation
+{
+ double samplePosition;
+ double sampleRate;
+ double systemTimeNanoseconds;
+ double musicalPosition;
+ double tempoBPM;
+ double lastBarPosition;
+ double loopStartPosition;
+ double loopEndPosition;
+ int32 timeSignatureNumerator;
+ int32 timeSignatureDenominator;
+ int32 smpteOffset;
+ int32 smpteRate;
+ int32 samplesToNearestClock;
+ int32 flags;
+};
+
+enum VstTimingInformationFlags
+{
+ vstTimingInfoFlagTransportChanged = 1,
+ vstTimingInfoFlagCurrentlyPlaying = 2,
+ vstTimingInfoFlagLoopActive = 4,
+ vstTimingInfoFlagCurrentlyRecording = 8,
+ vstTimingInfoFlagAutomationWriteModeActive = 64,
+ vstTimingInfoFlagAutomationReadModeActive = 128,
+ vstTimingInfoFlagNanosecondsValid = 256,
+ vstTimingInfoFlagMusicalPositionValid = 512,
+ vstTimingInfoFlagTempoValid = 1024,
+ vstTimingInfoFlagLastBarPositionValid = 2048,
+ vstTimingInfoFlagLoopPositionValid = 4096,
+ vstTimingInfoFlagTimeSignatureValid = 8192,
+ vstTimingInfoFlagSmpteValid = 16384,
+ vstTimingInfoFlagNearestClockValid = 32768
+};
+
+//==============================================================================
+enum VstSmpteRates
+{
+ vstSmpteRateFps24,
+ vstSmpteRateFps25,
+ vstSmpteRateFps2997,
+ vstSmpteRateFps30,
+ vstSmpteRateFps2997drop,
+ vstSmpteRateFps30drop,
+
+ vstSmpteRate16mmFilm,
+ vstSmpteRate35mmFilm,
+
+ vstSmpteRateFps239 = vstSmpteRate35mmFilm + 3,
+ vstSmpteRateFps249 ,
+ vstSmpteRateFps599,
+ vstSmpteRateFps60
+};
+
+//==============================================================================
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct VstIndividualSpeakerInfo
+{
+ float azimuthalAngle;
+ float elevationAngle;
+ float radius;
+ float reserved;
+ char label[vstMaxNameLength];
+ int32 type;
+ char unused[28];
+};
+
+enum VstIndividualSpeakerType
+{
+ vstIndividualSpeakerTypeUndefined = 0x7fffffff,
+ vstIndividualSpeakerTypeMono = 0,
+ vstIndividualSpeakerTypeLeft,
+ vstIndividualSpeakerTypeRight,
+ vstIndividualSpeakerTypeCentre,
+ vstIndividualSpeakerTypeLFE,
+ vstIndividualSpeakerTypeLeftSurround,
+ vstIndividualSpeakerTypeRightSurround,
+ vstIndividualSpeakerTypeLeftCentre,
+ vstIndividualSpeakerTypeRightCentre,
+ vstIndividualSpeakerTypeSurround,
+ vstIndividualSpeakerTypeCentreSurround = vstIndividualSpeakerTypeSurround,
+ vstIndividualSpeakerTypeLeftRearSurround,
+ vstIndividualSpeakerTypeRightRearSurround,
+ vstIndividualSpeakerTypeTopMiddle,
+ vstIndividualSpeakerTypeTopFrontLeft,
+ vstIndividualSpeakerTypeTopFrontCentre,
+ vstIndividualSpeakerTypeTopFrontRight,
+ vstIndividualSpeakerTypeTopRearLeft,
+ vstIndividualSpeakerTypeTopRearCentre,
+ vstIndividualSpeakerTypeTopRearRight,
+ vstIndividualSpeakerTypeLFE2
+};
+
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct VstSpeakerConfiguration
+{
+ int32 type;
+ int32 numberOfChannels;
+ VstIndividualSpeakerInfo speakers[8];
+};
+
+enum VstSpeakerConfigurationType
+{
+ vstSpeakerConfigTypeUser = -2,
+ vstSpeakerConfigTypeEmpty = -1,
+ vstSpeakerConfigTypeMono = 0,
+ vstSpeakerConfigTypeLR,
+ vstSpeakerConfigTypeLsRs,
+ vstSpeakerConfigTypeLcRc,
+ vstSpeakerConfigTypeSlSr,
+ vstSpeakerConfigTypeCLfe,
+ vstSpeakerConfigTypeLRC,
+ vstSpeakerConfigTypeLRS,
+ vstSpeakerConfigTypeLRCLfe,
+ vstSpeakerConfigTypeLRLfeS,
+ vstSpeakerConfigTypeLRCS,
+ vstSpeakerConfigTypeLRLsRs,
+ vstSpeakerConfigTypeLRCLfeS,
+ vstSpeakerConfigTypeLRLfeLsRs,
+ vstSpeakerConfigTypeLRCLsRs,
+ vstSpeakerConfigTypeLRCLfeLsRs,
+ vstSpeakerConfigTypeLRCLsRsCs,
+ vstSpeakerConfigTypeLRLsRsSlSr,
+ vstSpeakerConfigTypeLRCLfeLsRsCs,
+ vstSpeakerConfigTypeLRLfeLsRsSlSr,
+ vstSpeakerConfigTypeLRCLsRsLcRc,
+ vstSpeakerConfigTypeLRCLsRsSlSr,
+ vstSpeakerConfigTypeLRCLfeLsRsLcRc,
+ vstSpeakerConfigTypeLRCLfeLsRsSlSr,
+ vstSpeakerConfigTypeLRCLsRsLcRcCs,
+ vstSpeakerConfigTypeLRCLsRsCsSlSr,
+ vstSpeakerConfigTypeLRCLfeLsRsLcRcCs,
+ vstSpeakerConfigTypeLRCLfeLsRsCsSlSr,
+ vstSpeakerConfigTypeLRCLfeLsRsTflTfcTfrTrlTrrLfe2
+};
+
+#if JUCE_BIG_ENDIAN
+ #define JUCE_MULTICHAR_CONSTANT(a, b, c, d) (a | (((uint32) b) << 8) | (((uint32) c) << 16) | (((uint32) d) << 24))
+#else
+ #define JUCE_MULTICHAR_CONSTANT(a, b, c, d) (d | (((uint32) c) << 8) | (((uint32) b) << 16) | (((uint32) a) << 24))
+#endif
+
+enum PresonusExtensionConstants
+{
+ presonusVendorID = JUCE_MULTICHAR_CONSTANT ('P', 'r', 'e', 'S'),
+ presonusSetContentScaleFactor = JUCE_MULTICHAR_CONSTANT ('A', 'e', 'C', 's')
+};
+
+//==============================================================================
+/** Structure used for VSTs
+
+ @tags{Audio}
+*/
+struct vst2FxBank
+{
+ int32 magic1;
+ int32 size;
+ int32 magic2;
+ int32 version1;
+ int32 fxID;
+ int32 version2;
+ int32 elements;
+ int32 current;
+ char shouldBeZero[124];
+ int32 chunkSize;
+ char chunk[1];
+};
+
+#if JUCE_MSVC
+ #pragma pack(pop)
+#elif JUCE_MAC || JUCE_IOS
+ #pragma options align=reset
+#else
+ #pragma pack(pop)
+#endif
+
+#endif // JUCE_VSTINTERFACE_H_INCLUDED

+ 0
- 78
libs/juce-current/patches/04_vst2-interface_pt2.patch View File

@@ -1,78 +0,0 @@
diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
index 3e63d3372..c09d99fc3 100644
--- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
+++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
@@ -61,10 +61,7 @@
#define __cdecl
#endif
- namespace Vst2
- {
- #include "pluginterfaces/vst2.x/vstfxstore.h"
- }
+ #include <juce_audio_processors/format_types/juce_VSTInterface.h>
#endif
@@ -1967,16 +1964,16 @@ public:
bool loadVST2CcnKBlock (const char* data, int size)
{
- auto* bank = reinterpret_cast<const Vst2::fxBank*> (data);
+ auto* bank = reinterpret_cast<const vst2FxBank*> (data);
- jassert (ByteOrder::bigEndianInt ("CcnK") == htonl ((uint32) bank->chunkMagic));
- jassert (ByteOrder::bigEndianInt ("FBCh") == htonl ((uint32) bank->fxMagic));
- jassert (htonl ((uint32) bank->version) == 1 || htonl ((uint32) bank->version) == 2);
+ jassert (ByteOrder::bigEndianInt ("CcnK") == htonl ((uint32) bank->magic1));
+ jassert (ByteOrder::bigEndianInt ("FBCh") == htonl ((uint32) bank->magic2));
+ jassert (htonl ((uint32) bank->version1) == 1 || htonl ((uint32) bank->version1) == 2);
jassert (JucePlugin_VSTUniqueID == htonl ((uint32) bank->fxID));
- setStateInformation (bank->content.data.chunk,
- jmin ((int) (size - (bank->content.data.chunk - data)),
- (int) htonl ((uint32) bank->content.data.size)));
+ setStateInformation (bank->chunk,
+ jmin ((int) (size - (bank->chunk - data)),
+ (int) htonl ((uint32) bank->chunkSize)));
return true;
}
@@ -2172,16 +2169,16 @@ public:
return status;
const int bankBlockSize = 160;
- Vst2::fxBank bank;
+ vst2FxBank bank;
zerostruct (bank);
- bank.chunkMagic = (int32) htonl (ByteOrder::bigEndianInt ("CcnK"));
- bank.byteSize = (int32) htonl (bankBlockSize - 8 + (unsigned int) mem.getSize());
- bank.fxMagic = (int32) htonl (ByteOrder::bigEndianInt ("FBCh"));
- bank.version = (int32) htonl (2);
- bank.fxID = (int32) htonl (JucePlugin_VSTUniqueID);
- bank.fxVersion = (int32) htonl (JucePlugin_VersionCode);
- bank.content.data.size = (int32) htonl ((unsigned int) mem.getSize());
+ bank.magic1 = (int32) htonl (ByteOrder::bigEndianInt ("CcnK"));
+ bank.size = (int32) htonl (bankBlockSize - 8 + (unsigned int) mem.getSize());
+ bank.magic2 = (int32) htonl (ByteOrder::bigEndianInt ("FBCh"));
+ bank.version1 = (int32) htonl (2);
+ bank.fxID = (int32) htonl (JucePlugin_VSTUniqueID);
+ bank.version2 = (int32) htonl (JucePlugin_VersionCode);
+ bank.chunkSize = (int32) htonl ((unsigned int) mem.getSize());
status = state->write (&bank, bankBlockSize);
diff --git a/modules/juce_audio_processors/format_types/juce_VSTInterface.h b/modules/juce_audio_processors/format_types/juce_VSTInterface.h
index 1b3eb7d08..58179be1a 100644
--- a/modules/juce_audio_processors/format_types/juce_VSTInterface.h
+++ b/modules/juce_audio_processors/format_types/juce_VSTInterface.h
@@ -506,7 +506,7 @@ enum PresonusExtensionConstants
@tags{Audio}
*/
-struct fxBank
+struct vst2FxBank
{
int32 magic1;
int32 size;

+ 3
- 3
libs/juce-current/patches/05_mingw-fixes.patch View File

@@ -1,5 +1,5 @@
diff --git a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
index 23484732b..d2106179c 100644
index 8683dc728..94f6bf569 100644
--- a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
+++ b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
@@ -61,7 +61,7 @@ JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996 4100)
@@ -28,10 +28,10 @@ index 6bea84307..baaa59f06 100644
#if JucePlugin_Build_LV2 && ! defined (JucePlugin_LV2URI)
#error "You need to define the JucePlugin_LV2URI value!"
diff --git a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
index adf058959..c39d6f796 100644
index 620ba9874..d9359b736 100644
--- a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
+++ b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
@@ -61,9 +61,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4355)
@@ -62,9 +62,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4355)
#ifndef WM_APPCOMMAND
#define WM_APPCOMMAND 0x0319
#endif


+ 25
- 25
libs/juce-current/patches/06_old-compiler-compatibility.patch View File

@@ -1,5 +1,5 @@
diff --git a/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
index 656ab3321..c40e854aa 100644
index acd165ff0..bdc0bf58f 100644
--- a/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
+++ b/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
@@ -20,6 +20,11 @@
@@ -30,10 +30,10 @@ index ac5ce32da..f0cd938ae 100644
{ kAudioChannelLayoutTag_TMH_10_2_std, { left, right, centre, topFrontCentre, leftSurroundSide, rightSurroundSide, leftSurround, rightSurround, topFrontLeft, topFrontRight, wideLeft, wideRight, topRearCentre, centreSurround, LFE, LFE2 } },
{ kAudioChannelLayoutTag_AC3_1_0_1, { centre, LFE } },
diff --git a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
index 892312098..411bf2097 100644
index 9aa4a338e..7cad3e7c1 100644
--- a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
+++ b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
@@ -58,6 +58,10 @@
@@ -59,6 +59,10 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
namespace juce
{
@@ -44,7 +44,7 @@ index 892312098..411bf2097 100644
// Change this to disable logging of various activities
#ifndef AU_LOGGING
#define AU_LOGGING 1
@@ -274,7 +278,7 @@ namespace AudioUnitFormatHelpers
@@ -276,7 +280,7 @@ namespace AudioUnitFormatHelpers
NSBundle* bundle = [[NSBundle alloc] initWithPath: (NSString*) fileOrIdentifier.toCFString()];
NSArray* audioComponents = [bundle objectForInfoDictionaryKey: @"AudioComponents"];
@@ -54,7 +54,7 @@ index 892312098..411bf2097 100644
desc.componentManufacturer = stringToOSType (nsStringToJuce ((NSString*) [dict valueForKey: @"manufacturer"]));
desc.componentType = stringToOSType (nsStringToJuce ((NSString*) [dict valueForKey: @"type"]));
diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/modules/juce_audio_processors/processors/juce_AudioProcessor.h
index 02c3de12e..ae60fde15 100644
index 39eac8211..f556f12b3 100644
--- a/modules/juce_audio_processors/processors/juce_AudioProcessor.h
+++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.h
@@ -1184,7 +1184,7 @@ public:
@@ -67,7 +67,7 @@ index 02c3de12e..ae60fde15 100644
//==============================================================================
/** Not for public use - this is called before deleting an editor component. */
diff --git a/modules/juce_core/native/juce_osx_ObjCHelpers.h b/modules/juce_core/native/juce_osx_ObjCHelpers.h
index 407d263d0..f2dc28e36 100644
index a9a7c8eb7..aa068eaa0 100644
--- a/modules/juce_core/native/juce_osx_ObjCHelpers.h
+++ b/modules/juce_core/native/juce_osx_ObjCHelpers.h
@@ -71,7 +71,7 @@ inline NSArray* varArrayToNSArray (const var& varToParse);
@@ -99,7 +99,7 @@ index 407d263d0..f2dc28e36 100644
return var (dynamicObject.get());
}
diff --git a/modules/juce_core/system/juce_CompilerSupport.h b/modules/juce_core/system/juce_CompilerSupport.h
index e4d87ab97..ddeef94a5 100644
index 4d96d7cb0..d76d92d49 100644
--- a/modules/juce_core/system/juce_CompilerSupport.h
+++ b/modules/juce_core/system/juce_CompilerSupport.h
@@ -92,7 +92,7 @@
@@ -112,7 +112,7 @@ index e4d87ab97..ddeef94a5 100644
#endif
diff --git a/modules/juce_core/system/juce_PlatformDefs.h b/modules/juce_core/system/juce_PlatformDefs.h
index 52db7c6d6..96ce314d8 100644
index 43000cab6..0a1798ba2 100644
--- a/modules/juce_core/system/juce_PlatformDefs.h
+++ b/modules/juce_core/system/juce_PlatformDefs.h
@@ -99,11 +99,7 @@ namespace juce
@@ -129,10 +129,10 @@ index 52db7c6d6..96ce314d8 100644
#if __GNUC__ >= 7
#define JUCE_FALLTHROUGH [[gnu::fallthrough]];
diff --git a/modules/juce_core/system/juce_TargetPlatform.h b/modules/juce_core/system/juce_TargetPlatform.h
index cf610da9a..9173cc34d 100644
index 5b4d293bb..6b41688b2 100644
--- a/modules/juce_core/system/juce_TargetPlatform.h
+++ b/modules/juce_core/system/juce_TargetPlatform.h
@@ -144,8 +144,8 @@
@@ -145,8 +145,8 @@
#endif
#if JUCE_MAC
@@ -144,7 +144,7 @@ index cf610da9a..9173cc34d 100644
#error "Building for OSX 10.6 is no longer supported!"
#endif
diff --git a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm
index 37bf13094..60467ef61 100644
index de6ffecfc..2a85d25f5 100644
--- a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm
+++ b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm
@@ -26,6 +26,10 @@
@@ -159,10 +159,10 @@ index 37bf13094..60467ef61 100644
// This class has been renamed from CoreGraphicsImage to avoid a symbol
// collision in Pro Tools 2019.12 and possibly 2020 depending on the Pro Tools
diff --git a/modules/juce_graphics/native/juce_mac_Fonts.mm b/modules/juce_graphics/native/juce_mac_Fonts.mm
index b6a918ce1..65b094343 100644
index ff0a23a9f..88142091d 100644
--- a/modules/juce_graphics/native/juce_mac_Fonts.mm
+++ b/modules/juce_graphics/native/juce_mac_Fonts.mm
@@ -362,20 +362,20 @@ namespace CoreTextTypeLayout
@@ -359,20 +359,20 @@ namespace CoreTextTypeLayout
auto verticalJustification = text.getJustification().getOnlyVerticalFlags();
@@ -188,10 +188,10 @@ index b6a918ce1..65b094343 100644
auto frame = createCTFrame (framesetter, CGRectMake ((CGFloat) ctFrameArea.getX(), flipHeight - (CGFloat) ctFrameArea.getBottom(),
diff --git a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm
index 7adb2a869..48ae2951b 100644
index 26ca40630..bd5e69ab0 100644
--- a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm
+++ b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm
@@ -1528,17 +1528,21 @@ private:
@@ -1583,17 +1583,21 @@ private:
case NSEventTypeSystemDefined:
case NSEventTypeApplicationDefined:
case NSEventTypePeriodic:
@@ -215,7 +215,7 @@ index 7adb2a869..48ae2951b 100644
#if JUCE_64BIT
case NSEventTypeDirectTouch:
diff --git a/modules/juce_gui_basics/native/juce_mac_Windowing.mm b/modules/juce_gui_basics/native/juce_mac_Windowing.mm
index 5ffbe393c..ddb5aedc5 100644
index 918f0b4a6..63e15d07b 100644
--- a/modules/juce_gui_basics/native/juce_mac_Windowing.mm
+++ b/modules/juce_gui_basics/native/juce_mac_Windowing.mm
@@ -309,7 +309,7 @@ bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& fi
@@ -228,10 +228,10 @@ index 5ffbe393c..ddb5aedc5 100644
auto eventPos = [event locationInWindow];
diff --git a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
index cf8df3b72..6f8417340 100644
index b8e6e0c7e..6ad01c68a 100644
--- a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
+++ b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
@@ -3054,7 +3054,7 @@ void XWindowSystem::handleButtonPressEvent (LinuxComponentPeer<::Window>* peer,
@@ -3329,7 +3329,7 @@ void XWindowSystem::handleButtonPressEvent (LinuxComponentPeer* peer, const XBut
peer->toFront (true);
peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse, getLogicalMousePos (buttonPressEvent, peer->getPlatformScaleFactor()),
ModifierKeys::currentModifiers, MouseInputSource::invalidPressure,
@@ -239,12 +239,12 @@ index cf8df3b72..6f8417340 100644
+ MouseInputSource::invalidOrientation, getEventTime (buttonPressEvent));
}
void XWindowSystem::handleButtonPressEvent (LinuxComponentPeer<::Window>* peer, const XButtonPressedEvent& buttonPressEvent) const
void XWindowSystem::handleButtonPressEvent (LinuxComponentPeer* peer, const XButtonPressedEvent& buttonPressEvent) const
diff --git a/modules/juce_gui_basics/windows/juce_ComponentPeer.h b/modules/juce_gui_basics/windows/juce_ComponentPeer.h
index 3dacc095a..820987e94 100644
index 691e5679b..ed4f3c199 100644
--- a/modules/juce_gui_basics/windows/juce_ComponentPeer.h
+++ b/modules/juce_gui_basics/windows/juce_ComponentPeer.h
@@ -314,7 +314,7 @@ public:
@@ -321,7 +321,7 @@ public:
//==============================================================================
void handleMouseEvent (MouseInputSource::InputSourceType type, Point<float> positionWithinPeer, ModifierKeys newMods, float pressure,
@@ -254,10 +254,10 @@ index 3dacc095a..820987e94 100644
void handleMouseWheel (MouseInputSource::InputSourceType type, Point<float> positionWithinPeer,
int64 time, const MouseWheelDetails&, int touchIndex = 0);
diff --git a/modules/juce_gui_extra/juce_gui_extra.cpp b/modules/juce_gui_extra/juce_gui_extra.cpp
index d4ebed7fd..df48cceaf 100644
index 9cf367411..d42555e13 100644
--- a/modules/juce_gui_extra/juce_gui_extra.cpp
+++ b/modules/juce_gui_extra/juce_gui_extra.cpp
@@ -142,7 +142,9 @@
@@ -136,7 +136,9 @@
#include "misc/juce_PushNotifications.cpp"
#include "misc/juce_RecentlyOpenedFilesList.cpp"
#include "misc/juce_SplashScreen.cpp"
@@ -267,7 +267,7 @@ index d4ebed7fd..df48cceaf 100644
#include "misc/juce_LiveConstantEditor.cpp"
#include "misc/juce_AnimatedAppComponent.cpp"
@@ -154,7 +156,9 @@
@@ -146,7 +148,9 @@
#if JUCE_MAC
#include "native/juce_mac_NSViewComponent.mm"
#include "native/juce_mac_AppleRemote.mm"
@@ -279,7 +279,7 @@ index d4ebed7fd..df48cceaf 100644
#if JUCE_IOS
diff --git a/modules/juce_gui_extra/juce_gui_extra.h b/modules/juce_gui_extra/juce_gui_extra.h
index 822c1885e..77e78c87a 100644
index bcec491c9..062bde636 100644
--- a/modules/juce_gui_extra/juce_gui_extra.h
+++ b/modules/juce_gui_extra/juce_gui_extra.h
@@ -115,7 +115,9 @@


+ 2
- 2
libs/juce-current/patches/09_proper-getExecutableFile.patch View File

@@ -1,8 +1,8 @@
diff --git a/modules/juce_core/native/juce_posix_SharedCode.h b/modules/juce_core/native/juce_posix_SharedCode.h
index 982db865b..cfdd2d1cd 100644
index 082a5697a..c79ca18ea 100644
--- a/modules/juce_core/native/juce_posix_SharedCode.h
+++ b/modules/juce_core/native/juce_posix_SharedCode.h
@@ -590,12 +590,39 @@ File juce_getExecutableFile()
@@ -600,12 +600,39 @@ File juce_getExecutableFile()
auto localSymbol = (void*) juce_getExecutableFile;
dladdr (localSymbol, &exeInfo);


+ 5
- 5
libs/juce-current/patches/10_fix-fork-exec-usage.patch View File

@@ -28,7 +28,7 @@ index d2a302e3e..3dc4602ce 100644
return cpid >= 0;
diff --git a/modules/juce_core/native/juce_mac_Files.mm b/modules/juce_core/native/juce_mac_Files.mm
index d26443079..616677e1f 100644
index 1a4d07516..f385a089f 100644
--- a/modules/juce_core/native/juce_mac_Files.mm
+++ b/modules/juce_core/native/juce_mac_Files.mm
@@ -92,23 +92,22 @@ namespace MacFileHelpers
@@ -65,10 +65,10 @@ index d26443079..616677e1f 100644
#endif
}
diff --git a/modules/juce_core/native/juce_posix_SharedCode.h b/modules/juce_core/native/juce_posix_SharedCode.h
index cfdd2d1cd..332a64959 100644
index c79ca18ea..ad46cf390 100644
--- a/modules/juce_core/native/juce_posix_SharedCode.h
+++ b/modules/juce_core/native/juce_posix_SharedCode.h
@@ -1092,7 +1092,18 @@ public:
@@ -1104,7 +1104,18 @@ public:
if (pipe (pipeHandles) == 0)
{
@@ -88,7 +88,7 @@ index cfdd2d1cd..332a64959 100644
if (result < 0)
{
@@ -1101,6 +1112,7 @@ public:
@@ -1113,6 +1124,7 @@ public:
}
else if (result == 0)
{
@@ -96,7 +96,7 @@ index cfdd2d1cd..332a64959 100644
// we're the child process..
close (pipeHandles[0]); // close the read handle
@@ -1115,17 +1127,10 @@ public:
@@ -1127,17 +1139,10 @@ public:
dup2 (open ("/dev/null", O_WRONLY), STDERR_FILENO);
close (pipeHandles[1]);


+ 3
- 3
libs/juce-current/patches/11_childprocess-getPID.patch View File

@@ -1,8 +1,8 @@
diff --git a/modules/juce_core/native/juce_posix_SharedCode.h b/modules/juce_core/native/juce_posix_SharedCode.h
index 332a64959..c6d95a93a 100644
index ad46cf390..13724ba7c 100644
--- a/modules/juce_core/native/juce_posix_SharedCode.h
+++ b/modules/juce_core/native/juce_posix_SharedCode.h
@@ -1227,6 +1227,11 @@ public:
@@ -1239,6 +1239,11 @@ public:
return 0;
}
@@ -15,7 +15,7 @@ index 332a64959..c6d95a93a 100644
int pipeHandle = 0;
int exitCode = -1;
diff --git a/modules/juce_core/native/juce_win32_Threads.cpp b/modules/juce_core/native/juce_win32_Threads.cpp
index dbe0c6b5d..098416bd9 100644
index 4a4148119..1c38ff2cf 100644
--- a/modules/juce_core/native/juce_win32_Threads.cpp
+++ b/modules/juce_core/native/juce_win32_Threads.cpp
@@ -477,6 +477,11 @@ public:


+ 3
- 3
libs/juce-current/patches/12_linux-filechooser-needs-modal-loops.patch View File

@@ -1,5 +1,5 @@
diff --git a/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp b/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp
index 97327c59e..0eca4dfb6 100644
index b799ee2e4..803740c63 100644
--- a/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp
+++ b/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp
@@ -26,6 +26,7 @@
@@ -10,7 +10,7 @@ index 97327c59e..0eca4dfb6 100644
static bool exeIsAvailable (String executable)
{
ChildProcess child;
@@ -241,10 +242,11 @@ private:
@@ -245,10 +246,11 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Native)
};
@@ -23,7 +23,7 @@ index 97327c59e..0eca4dfb6 100644
return false;
#else
static bool canUseNativeBox = exeIsAvailable ("zenity") || exeIsAvailable ("kdialog");
@@ -254,7 +256,11 @@ bool FileChooser::isPlatformDialogAvailable()
@@ -258,7 +260,11 @@ bool FileChooser::isPlatformDialogAvailable()
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags, FilePreviewComponent*)
{


+ 18
- 11
libs/juce-current/patches/14_fix-x11-temporary-windows.patch View File

@@ -1,16 +1,23 @@
diff --git a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
index 6f8417340..5e8603833 100644
index 6ad01c68a..490a3a792 100644
--- a/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
+++ b/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
@@ -2561,8 +2561,9 @@ void XWindowSystem::setWindowType (::Window windowH, int styleFlags) const
@@ -2804,10 +2804,14 @@ void XWindowSystem::setWindowType (::Window windowH, int styleFlags) const
Atom netHints [2];
if (atoms.windowType != None)
{
- auto hint = (styleFlags & ComponentPeer::windowIsTemporary) != 0
- || ((styleFlags & ComponentPeer::windowHasDropShadow) == 0 && Desktop::canUseSemiTransparentWindows())
- ? XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_COMBO")
- : XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_NORMAL");
+ Atom hint = None;
+
+ if (styleFlags & ComponentPeer::windowIsTemporary)
+ hint = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_TOOLTIP");
+ else if ((styleFlags & ComponentPeer::windowHasDropShadow) == 0 && Desktop::canUseSemiTransparentWindows())
+ hint = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_COMBO");
+ else
+ hint = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_NORMAL");
- if ((styleFlags & ComponentPeer::windowIsTemporary) != 0
- || ((styleFlags & ComponentPeer::windowHasDropShadow) == 0 && Desktop::canUseSemiTransparentWindows()))
+ if (styleFlags & ComponentPeer::windowIsTemporary)
+ netHints [0] = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_TOOLTIP");
+ else if ((styleFlags & ComponentPeer::windowHasDropShadow) == 0 && Desktop::canUseSemiTransparentWindows())
netHints [0] = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_COMBO");
else
netHints [0] = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_NORMAL");
if (hint != None)
xchangeProperty (windowH, atoms.windowType, XA_ATOM, 32, &hint, 1);

+ 2
- 2
libs/juce-current/patches/15_fix-linux-thread-prio.patch View File

@@ -1,8 +1,8 @@
diff --git a/modules/juce_core/native/juce_posix_SharedCode.h b/modules/juce_core/native/juce_posix_SharedCode.h
index c6d95a93a..9c9053ac4 100644
index 13724ba7c..f8892081c 100644
--- a/modules/juce_core/native/juce_posix_SharedCode.h
+++ b/modules/juce_core/native/juce_posix_SharedCode.h
@@ -977,7 +977,11 @@ bool Thread::setThreadPriority (void* handle, int priority)
@@ -989,7 +989,11 @@ bool Thread::setThreadPriority (void* handle, int priority)
if (pthread_getschedparam ((pthread_t) handle, &policy, &param) != 0)
return false;


+ 2
- 2
libs/juce-current/patches/17_allow-vst2-without-audio.patch View File

@@ -1,8 +1,8 @@
diff --git a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
index c5e96d204..7e2122580 100644
index 94f6bf569..ddbe6fd65 100644
--- a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
+++ b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
@@ -301,9 +301,6 @@ public:
@@ -294,9 +294,6 @@ public:
// You must at least have some channels
jassert (processor->isMidiEffect() || (maxNumInChannels > 0 || maxNumOutChannels > 0));


+ 5
- 3
libs/juce-current/patches/19_fix-vst2-linux-gui-init.patch View File

@@ -1,15 +1,17 @@
diff --git a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
index cff9b7a88..3ae14372c 100644
index ebc9325ca..cabbbcf13 100644
--- a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
+++ b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
@@ -209,9 +209,9 @@ struct SharedMessageThread : public Thread
@@ -203,11 +203,11 @@ struct SharedMessageThread : public Thread
void run() override
{
initialiseJuce_GUI();
- initialised = true;
MessageManager::getInstance()->setCurrentThreadAsMessageThread();
+ initialised = true;
XWindowSystem::getInstance();
+ initialised = true;
while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
{}

+ 9
- 9
libs/juce-current/patches/21_audioprocessor-no-gui.patch View File

@@ -1,5 +1,5 @@
diff --git a/modules/juce_audio_processors/juce_audio_processors.cpp b/modules/juce_audio_processors/juce_audio_processors.cpp
index ac194a19d..f494ec3a2 100644
index 43ad88e1b..149154804 100644
--- a/modules/juce_audio_processors/juce_audio_processors.cpp
+++ b/modules/juce_audio_processors/juce_audio_processors.cpp
@@ -47,7 +47,7 @@
@@ -11,7 +11,7 @@ index ac194a19d..f494ec3a2 100644
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <sys/utsname.h>
@@ -141,9 +141,11 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations", "-Wcast-align"
@@ -230,9 +230,11 @@ struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewCompone
#include "format_types/juce_LegacyAudioParameter.cpp"
#include "processors/juce_AudioProcessor.cpp"
#include "processors/juce_AudioPluginInstance.cpp"
@@ -26,7 +26,7 @@ index ac194a19d..f494ec3a2 100644
#include "format_types/juce_LADSPAPluginFormat.cpp"
#include "format_types/juce_VSTPluginFormat.cpp"
diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
index caa55b618..41c547b0c 100644
index 040accf2e..df0b6c67b 100644
--- a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
+++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
@@ -51,12 +51,14 @@ AudioProcessor::AudioProcessor (const BusesProperties& ioConfig)
@@ -44,7 +44,7 @@ index caa55b618..41c547b0c 100644
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
// This will fail if you've called beginParameterChangeGesture() for one
@@ -804,6 +806,7 @@ void AudioProcessor::audioIOChanged (bool busNumberChanged, bool channelNumChang
@@ -826,6 +828,7 @@ void AudioProcessor::audioIOChanged (bool busNumberChanged, bool channelNumChang
processorLayoutsChanged();
}
@@ -52,7 +52,7 @@ index caa55b618..41c547b0c 100644
//==============================================================================
void AudioProcessor::editorBeingDeleted (AudioProcessorEditor* const editor) noexcept
{
@@ -840,6 +843,7 @@ AudioProcessorEditor* AudioProcessor::createEditorIfNeeded()
@@ -862,6 +865,7 @@ AudioProcessorEditor* AudioProcessor::createEditorIfNeeded()
return ed;
}
@@ -61,7 +61,7 @@ index caa55b618..41c547b0c 100644
//==============================================================================
void AudioProcessor::getCurrentProgramStateInformation (juce::MemoryBlock& destData)
diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/modules/juce_audio_processors/processors/juce_AudioProcessor.h
index dbad562ef..ab9e8a7d6 100644
index f72a4ed3f..849b77d3c 100644
--- a/modules/juce_audio_processors/processors/juce_AudioProcessor.h
+++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.h
@@ -926,6 +926,7 @@ public:
@@ -103,10 +103,10 @@ index dbad562ef..ab9e8a7d6 100644
int blockSize = 0, latencySamples = 0;
bool suspended = false;
diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
index fd8c35879..070f82554 100644
index 17c5af30c..3d4a0d428 100644
--- a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
+++ b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
@@ -1496,8 +1496,10 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::producesMidi() const
@@ -1519,8 +1519,10 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::producesMidi() const
bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const noexcept { return type == audioInputNode || type == midiInputNode; }
bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const noexcept { return type == audioOutputNode || type == midiOutputNode; }
@@ -118,7 +118,7 @@ index fd8c35879..070f82554 100644
int AudioProcessorGraph::AudioGraphIOProcessor::getNumPrograms() { return 0; }
int AudioProcessorGraph::AudioGraphIOProcessor::getCurrentProgram() { return 0; }
diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h
index a16e35df0..8d2221f1e 100644
index a60e67d84..17a121893 100644
--- a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h
+++ b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h
@@ -355,8 +355,10 @@ public:


+ 10
- 7
libs/juce-current/patches/22_mingw-filechooser-no-vista.patch View File

@@ -1,16 +1,16 @@
diff --git a/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp b/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp
index 065d873e1..fb9cfc999 100644
index 0c1138a2b..a5b42b8ef 100644
--- a/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp
+++ b/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp
@@ -167,6 +167,7 @@ private:
Atomic<HWND> nativeDialogRef;
Atomic<int> shouldCancel;
@@ -172,6 +172,7 @@ private:
void operator() (LPWSTR ptr) const noexcept { CoTaskMemFree (ptr); }
};
+ #if JUCE_MSVC
bool showDialog (IFileDialog& dialog, bool async) const
{
FILEOPENDIALOGOPTIONS flags = {};
@@ -303,6 +304,7 @@ private:
@@ -327,6 +328,7 @@ private:
return result;
}
@@ -18,13 +18,16 @@ index 065d873e1..fb9cfc999 100644
Array<URL> openDialogPreVista (bool async)
{
@@ -412,8 +414,10 @@ private:
@@ -436,11 +438,13 @@ private:
const Remover remover (*this);
+ #if JUCE_MSVC
if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista)
if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista
&& customComponent == nullptr)
{
return openDialogVistaAndUp (async);
}
+ #endif
return openDialogPreVista (async);


+ 2
- 2
libs/juce-current/patches/23_fix-juce-includes.patch View File

@@ -1,8 +1,8 @@
diff --git a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
index dbf441fb5..63d931efd 100644
index cabbbcf13..f722ab36c 100644
--- a/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
+++ b/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
@@ -489,7 +489,7 @@ public:
@@ -488,7 +488,7 @@ public:
{
const int numChannels = jmax (numIn, numOut);


+ 2
- 2
libs/juce-current/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp View File

@@ -353,8 +353,8 @@ namespace FloatVectorHelpers
union signMask64 { double d; uint64 i; };
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
template<int typeSize> struct ModeType { using Mode = BasicOps32; };
template<> struct ModeType<8> { using Mode = BasicOps64; };
template <int typeSize> struct ModeType { using Mode = BasicOps32; };
template <> struct ModeType<8> { using Mode = BasicOps64; };
template <typename Mode>
struct MinMax


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/juce_audio_basics.h View File

@@ -32,7 +32,7 @@
ID: juce_audio_basics
vendor: juce
version: 6.0.4
version: 6.0.7
name: JUCE audio and MIDI data classes
description: Classes for audio buffer manipulation, midi message handling, synthesis, etc.
website: http://www.juce.com/juce


+ 72
- 4
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp View File

@@ -60,9 +60,8 @@ namespace MidiBufferHelpers
if (maxBytes == 1)
return 1;
int n;
auto bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n);
return jmin (maxBytes, n + 2 + bytesLeft);
const auto var = MidiMessage::readVariableLengthValue (data + 1, maxBytes - 1);
return jmin (maxBytes, var.value + 2 + var.bytesUsed);
}
if (byte >= 0x80)
@@ -117,7 +116,7 @@ void MidiBuffer::clear (int startSample, int numSamples)
auto start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1);
auto end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1);
data.removeRange ((int) (start - data.begin()), (int) (end - data.begin()));
data.removeRange ((int) (start - data.begin()), (int) (end - start));
}
void MidiBuffer::addEvent (const MidiMessage& m, int sampleNumber)
@@ -237,4 +236,73 @@ bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePositio
return true;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
struct MidiBufferTest : public UnitTest
{
MidiBufferTest()
: UnitTest ("MidiBuffer", UnitTestCategories::midi)
{}
void runTest() override
{
beginTest ("Clear messages");
{
const auto message = MidiMessage::noteOn (1, 64, 0.5f);
const auto testBuffer = [&]
{
MidiBuffer buffer;
buffer.addEvent (message, 0);
buffer.addEvent (message, 10);
buffer.addEvent (message, 20);
buffer.addEvent (message, 30);
return buffer;
}();
{
auto buffer = testBuffer;
buffer.clear (10, 0);
expectEquals (buffer.getNumEvents(), 4);
}
{
auto buffer = testBuffer;
buffer.clear (10, 1);
expectEquals (buffer.getNumEvents(), 3);
}
{
auto buffer = testBuffer;
buffer.clear (10, 10);
expectEquals (buffer.getNumEvents(), 3);
}
{
auto buffer = testBuffer;
buffer.clear (10, 20);
expectEquals (buffer.getNumEvents(), 2);
}
{
auto buffer = testBuffer;
buffer.clear (10, 30);
expectEquals (buffer.getNumEvents(), 1);
}
{
auto buffer = testBuffer;
buffer.clear (10, 300);
expectEquals (buffer.getNumEvents(), 1);
}
}
}
};
static MidiBufferTest midiBufferTest;
#endif
} // namespace juce

+ 432
- 80
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp View File

@@ -46,23 +46,77 @@ namespace MidiFileHelpers
}
}
static bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) noexcept
template <typename Value>
struct Optional
{
auto ch = ByteOrder::bigEndianInt (data);
data += 4;
Optional() = default;
if (ch != ByteOrder::bigEndianInt ("MThd"))
Optional (const Value& v)
: value (v), valid (true) {}
Value value = Value();
bool valid = false;
};
template <typename Integral>
struct ReadTrait;
template <>
struct ReadTrait<uint32> { static constexpr auto read = ByteOrder::bigEndianInt; };
template <>
struct ReadTrait<uint16> { static constexpr auto read = ByteOrder::bigEndianShort; };
template <typename Integral>
Optional<Integral> tryRead (const uint8*& data, size_t& remaining)
{
using Trait = ReadTrait<Integral>;
constexpr auto size = sizeof (Integral);
if (remaining < size)
return {};
const Optional<Integral> result { Trait::read (data) };
data += size;
remaining -= size;
return result;
}
struct HeaderDetails
{
size_t bytesRead = 0;
short timeFormat = 0;
short fileType = 0;
short numberOfTracks = 0;
};
static Optional<HeaderDetails> parseMidiHeader (const uint8* const initialData,
const size_t maxSize)
{
auto* data = initialData;
auto remaining = maxSize;
auto ch = tryRead<uint32> (data, remaining);
if (! ch.valid)
return {};
if (ch.value != ByteOrder::bigEndianInt ("MThd"))
{
bool ok = false;
auto ok = false;
if (ch == ByteOrder::bigEndianInt ("RIFF"))
if (ch.value == ByteOrder::bigEndianInt ("RIFF"))
{
for (int i = 0; i < 8; ++i)
{
ch = ByteOrder::bigEndianInt (data);
data += 4;
ch = tryRead<uint32> (data, remaining);
if (! ch.valid)
return {};
if (ch == ByteOrder::bigEndianInt ("MThd"))
if (ch.value == ByteOrder::bigEndianInt ("MThd"))
{
ok = true;
break;
@@ -71,21 +125,37 @@ namespace MidiFileHelpers
}
if (! ok)
return false;
return {};
}
auto bytesRemaining = ByteOrder::bigEndianInt (data);
data += 4;
fileType = (short) ByteOrder::bigEndianShort (data);
data += 2;
numberOfTracks = (short) ByteOrder::bigEndianShort (data);
data += 2;
timeFormat = (short) ByteOrder::bigEndianShort (data);
data += 2;
bytesRemaining -= 6;
data += bytesRemaining;
return true;
const auto bytesRemaining = tryRead<uint32> (data, remaining);
if (! bytesRemaining.valid || bytesRemaining.value > remaining)
return {};
const auto optFileType = tryRead<uint16> (data, remaining);
if (! optFileType.valid || 2 < optFileType.value)
return {};
const auto optNumTracks = tryRead<uint16> (data, remaining);
if (! optNumTracks.valid || (optFileType.value == 0 && optNumTracks.value != 1))
return {};
const auto optTimeFormat = tryRead<uint16> (data, remaining);
if (! optTimeFormat.valid)
return {};
HeaderDetails result;
result.fileType = (short) optFileType.value;
result.timeFormat = (short) optTimeFormat.value;
result.numberOfTracks = (short) optNumTracks.value;
result.bytesRead = maxSize - remaining;
return { result };
}
static double convertTicksToSeconds (double time,
@@ -149,6 +219,47 @@ namespace MidiFileHelpers
}
}
}
static MidiMessageSequence readTrack (const uint8* data, int size)
{
double time = 0;
uint8 lastStatusByte = 0;
MidiMessageSequence result;
while (size > 0)
{
const auto delay = MidiMessage::readVariableLengthValue (data, (int) size);
if (! delay.isValid())
break;
data += delay.bytesUsed;
size -= delay.bytesUsed;
time += delay.value;
if (size <= 0)
break;
int messSize = 0;
const MidiMessage mm (data, size, messSize, lastStatusByte, time);
if (messSize <= 0)
break;
size -= messSize;
data += messSize;
result.addEvent (mm);
auto firstByte = *(mm.getRawData());
if ((firstByte & 0xf0) != 0xf0)
lastStatusByte = firstByte;
}
return result;
}
}
//==============================================================================
@@ -253,78 +364,56 @@ bool MidiFile::readFrom (InputStream& sourceStream, bool createMatchingNoteOffs)
const int maxSensibleMidiFileSize = 200 * 1024 * 1024;
// (put a sanity-check on the file size, as midi files are generally small)
if (sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize))
{
auto size = data.getSize();
auto d = static_cast<const uint8*> (data.getData());
short fileType, expectedTracks;
if (size > 16 && MidiFileHelpers::parseMidiHeader (d, timeFormat, fileType, expectedTracks))
{
size -= (size_t) (d - static_cast<const uint8*> (data.getData()));
int track = 0;
for (;;)
{
auto chunkType = (int) ByteOrder::bigEndianInt (d);
d += 4;
auto chunkSize = (int) ByteOrder::bigEndianInt (d);
d += 4;
if (! sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize))
return false;
if (chunkSize <= 0 || (size_t) chunkSize > size)
break;
auto size = data.getSize();
auto d = static_cast<const uint8*> (data.getData());
if (chunkType == (int) ByteOrder::bigEndianInt ("MTrk"))
readNextTrack (d, chunkSize, createMatchingNoteOffs);
const auto optHeader = MidiFileHelpers::parseMidiHeader (d, size);
if (++track >= expectedTracks)
break;
if (! optHeader.valid)
return false;
size -= (size_t) chunkSize + 8;
d += chunkSize;
}
const auto header = optHeader.value;
timeFormat = header.timeFormat;
return true;
}
}
d += header.bytesRead;
size -= (size_t) header.bytesRead;
return false;
}
for (int track = 0; track < header.numberOfTracks; ++track)
{
const auto optChunkType = MidiFileHelpers::tryRead<uint32> (d, size);
void MidiFile::readNextTrack (const uint8* data, int size, bool createMatchingNoteOffs)
{
double time = 0;
uint8 lastStatusByte = 0;
if (! optChunkType.valid)
return false;
MidiMessageSequence result;
const auto optChunkSize = MidiFileHelpers::tryRead<uint32> (d, size);
while (size > 0)
{
int bytesUsed;
auto delay = MidiMessage::readVariableLengthVal (data, bytesUsed);
data += bytesUsed;
size -= bytesUsed;
time += delay;
if (! optChunkSize.valid)
return false;
int messSize = 0;
const MidiMessage mm (data, size, messSize, lastStatusByte, time);
const auto chunkSize = optChunkSize.value;
if (messSize <= 0)
break;
if (size < chunkSize)
return false;
size -= messSize;
data += messSize;
if (optChunkType.value == ByteOrder::bigEndianInt ("MTrk"))
readNextTrack (d, (int) chunkSize, createMatchingNoteOffs);
result.addEvent (mm);
size -= chunkSize;
d += chunkSize;
}
auto firstByte = *(mm.getRawData());
return size == 0;
}
if ((firstByte & 0xf0) != 0xf0)
lastStatusByte = firstByte;
}
void MidiFile::readNextTrack (const uint8* data, int size, bool createMatchingNoteOffs)
{
auto sequence = MidiFileHelpers::readTrack (data, size);
// sort so that we put all the note-offs before note-ons that have the same time
std::stable_sort (result.list.begin(), result.list.end(),
std::stable_sort (sequence.list.begin(), sequence.list.end(),
[] (const MidiMessageSequence::MidiEventHolder* a,
const MidiMessageSequence::MidiEventHolder* b)
{
@@ -337,10 +426,10 @@ void MidiFile::readNextTrack (const uint8* data, int size, bool createMatchingNo
return a->message.isNoteOff() && b->message.isNoteOn();
});
addTrack (result);
if (createMatchingNoteOffs)
tracks.getLast()->updateMatchedPairs();
sequence.updateMatchedPairs();
addTrack (sequence);
}
//==============================================================================
@@ -443,4 +532,267 @@ bool MidiFile::writeTrack (OutputStream& mainOut, const MidiMessageSequence& ms)
return true;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
struct MidiFileTest : public UnitTest
{
MidiFileTest()
: UnitTest ("MidiFile", UnitTestCategories::midi)
{}
void runTest() override
{
beginTest ("ReadTrack respects running status");
{
const auto sequence = parseSequence ([] (OutputStream& os)
{
MidiFileHelpers::writeVariableLengthInt (os, 100);
writeBytes (os, { 0x90, 0x40, 0x40 });
MidiFileHelpers::writeVariableLengthInt (os, 200);
writeBytes (os, { 0x40, 0x40 });
MidiFileHelpers::writeVariableLengthInt (os, 300);
writeBytes (os, { 0xff, 0x2f, 0x00 });
});
expectEquals (sequence.getNumEvents(), 3);
expect (sequence.getEventPointer (0)->message.isNoteOn());
expect (sequence.getEventPointer (1)->message.isNoteOn());
expect (sequence.getEventPointer (2)->message.isEndOfTrackMetaEvent());
}
beginTest ("ReadTrack returns available messages if input is truncated");
{
{
const auto sequence = parseSequence ([] (OutputStream& os)
{
// Incomplete delta time
writeBytes (os, { 0xff });
});
expectEquals (sequence.getNumEvents(), 0);
}
{
const auto sequence = parseSequence ([] (OutputStream& os)
{
// Complete delta with no following event
MidiFileHelpers::writeVariableLengthInt (os, 0xffff);
});
expectEquals (sequence.getNumEvents(), 0);
}
{
const auto sequence = parseSequence ([] (OutputStream& os)
{
// Complete delta with malformed following event
MidiFileHelpers::writeVariableLengthInt (os, 0xffff);
writeBytes (os, { 0x90, 0x40 });
});
expectEquals (sequence.getNumEvents(), 1);
expect (sequence.getEventPointer (0)->message.isNoteOff());
expectEquals (sequence.getEventPointer (0)->message.getNoteNumber(), 0x40);
expectEquals (sequence.getEventPointer (0)->message.getVelocity(), (uint8) 0x00);
}
}
beginTest ("Header parsing works");
{
{
// No data
const auto header = parseHeader ([] (OutputStream&) {});
expect (! header.valid);
}
{
// Invalid initial byte
const auto header = parseHeader ([] (OutputStream& os)
{
writeBytes (os, { 0xff });
});
expect (! header.valid);
}
{
// Type block, but no header data
const auto header = parseHeader ([] (OutputStream& os)
{
writeBytes (os, { 'M', 'T', 'h', 'd' });
});
expect (! header.valid);
}
{
// We (ll-formed header, but track type is 0 and channels != 1
const auto header = parseHeader ([] (OutputStream& os)
{
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 0, 0, 16, 0, 1 });
});
expect (! header.valid);
}
{
// Well-formed header, but track type is 5
const auto header = parseHeader ([] (OutputStream& os)
{
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 5, 0, 16, 0, 1 });
});
expect (! header.valid);
}
{
// Well-formed header
const auto header = parseHeader ([] (OutputStream& os)
{
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 16, 0, 1 });
});
expect (header.valid);
expectEquals (header.value.fileType, (short) 1);
expectEquals (header.value.numberOfTracks, (short) 16);
expectEquals (header.value.timeFormat, (short) 1);
expectEquals ((int) header.value.bytesRead, 14);
}
}
beginTest ("Read from stream");
{
{
// Empty input
const auto file = parseFile ([] (OutputStream&) {});
expect (! file.valid);
}
{
// Malformed header
const auto file = parseFile ([] (OutputStream& os)
{
writeBytes (os, { 'M', 'T', 'h', 'd' });
});
expect (! file.valid);
}
{
// Header, no channels
const auto file = parseFile ([] (OutputStream& os)
{
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 0, 0, 1 });
});
expect (file.valid);
expectEquals (file.value.getNumTracks(), 0);
}
{
// Header, one malformed channel
const auto file = parseFile ([] (OutputStream& os)
{
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 });
writeBytes (os, { 'M', 'T', 'r', '?' });
});
expect (! file.valid);
}
{
// Header, one channel with malformed message
const auto file = parseFile ([] (OutputStream& os)
{
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 });
writeBytes (os, { 'M', 'T', 'r', 'k', 0, 0, 0, 1, 0xff });
});
expect (file.valid);
expectEquals (file.value.getNumTracks(), 1);
expectEquals (file.value.getTrack (0)->getNumEvents(), 0);
}
{
// Header, one channel with incorrect length message
const auto file = parseFile ([] (OutputStream& os)
{
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 });
writeBytes (os, { 'M', 'T', 'r', 'k', 0x0f, 0, 0, 0, 0xff });
});
expect (! file.valid);
}
{
// Header, one channel, all well-formed
const auto file = parseFile ([] (OutputStream& os)
{
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 1, 0, 1 });
writeBytes (os, { 'M', 'T', 'r', 'k', 0, 0, 0, 4 });
MidiFileHelpers::writeVariableLengthInt (os, 0x0f);
writeBytes (os, { 0x80, 0x00, 0x00 });
});
expect (file.valid);
expectEquals (file.value.getNumTracks(), 1);
auto& track = *file.value.getTrack (0);
expectEquals (track.getNumEvents(), 1);
expect (track.getEventPointer (0)->message.isNoteOff());
expectEquals (track.getEventPointer (0)->message.getTimeStamp(), (double) 0x0f);
}
}
}
template <typename Fn>
static MidiMessageSequence parseSequence (Fn&& fn)
{
MemoryOutputStream os;
fn (os);
return MidiFileHelpers::readTrack (reinterpret_cast<const uint8*> (os.getData()),
(int) os.getDataSize());
}
template <typename Fn>
static MidiFileHelpers::Optional<MidiFileHelpers::HeaderDetails> parseHeader (Fn&& fn)
{
MemoryOutputStream os;
fn (os);
return MidiFileHelpers::parseMidiHeader (reinterpret_cast<const uint8*> (os.getData()),
os.getDataSize());
}
template <typename Fn>
static MidiFileHelpers::Optional<MidiFile> parseFile (Fn&& fn)
{
MemoryOutputStream os;
fn (os);
MemoryInputStream is (os.getData(), os.getDataSize(), false);
MidiFile mf;
if (mf.readFrom (is))
return mf;
return {};
}
static void writeBytes (OutputStream& os, const std::vector<uint8>& bytes)
{
for (const auto& byte : bytes)
os.writeByte ((char) byte);
}
};
static MidiFileTest midiFileTests;
#endif
} // namespace juce

+ 200
- 16
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp View File

@@ -57,6 +57,31 @@ uint16 MidiMessage::pitchbendToPitchwheelPos (const float pitchbend,
}
//==============================================================================
MidiMessage::VariableLengthValue MidiMessage::readVariableLengthValue (const uint8* data, int maxBytesToUse) noexcept
{
uint32 v = 0;
// The largest allowable variable-length value is 0x0f'ff'ff'ff which is
// represented by the 4-byte stream 0xff 0xff 0xff 0x7f.
// Longer bytestreams risk overflowing a 32-bit signed int.
const auto limit = jmin (maxBytesToUse, 4);
for (int numBytesUsed = 0; numBytesUsed < limit; ++numBytesUsed)
{
const auto i = data[numBytesUsed];
v = (v << 7) + (i & 0x7f);
if (! (i & 0x80))
return { (int) v, numBytesUsed + 1 };
}
// If this is hit, the input was malformed. Either there were not enough
// bytes of input to construct a full value, or no terminating byte was
// found. This implementation only supports variable-length values of up
// to four bytes.
return {};
}
int MidiMessage::readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept
{
numBytesUsed = 0;
@@ -224,16 +249,8 @@ MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const
}
else if (byte == 0xff)
{
if (sz == 1)
{
size = 1;
}
else
{
int n;
const int bytesLeft = readVariableLengthVal (src + 1, n);
size = jmin (sz + 1, n + 2 + bytesLeft);
}
const auto bytesLeft = readVariableLengthValue (src + 1, sz - 1);
size = jmin (sz + 1, bytesLeft.bytesUsed + 2 + bytesLeft.value);
auto dest = allocateSpace (size);
*dest = (uint8) byte;
@@ -682,7 +699,7 @@ bool MidiMessage::isActiveSense() const noexcept { return *getRawData() == 0x
int MidiMessage::getMetaEventType() const noexcept
{
auto data = getRawData();
return *data != 0xff ? -1 : data[1];
return (size < 2 || *data != 0xff) ? -1 : data[1];
}
int MidiMessage::getMetaEventLength() const noexcept
@@ -691,8 +708,8 @@ int MidiMessage::getMetaEventLength() const noexcept
if (*data == 0xff)
{
int n;
return jmin (size - 2, readVariableLengthVal (data + 2, n));
const auto var = readVariableLengthValue (data + 2, size - 2);
return jmax (0, jmin (size - 2 - var.bytesUsed, var.value));
}
return 0;
@@ -702,10 +719,9 @@ const uint8* MidiMessage::getMetaEventData() const noexcept
{
jassert (isMetaEvent());
int n;
auto d = getRawData() + 2;
readVariableLengthVal (d, n);
return d + n;
const auto var = readVariableLengthValue (d, size - 2);
return d + var.bytesUsed;
}
bool MidiMessage::isTrackMetaEvent() const noexcept { return getMetaEventType() == 0; }
@@ -1141,4 +1157,172 @@ const char* MidiMessage::getControllerName (const int n)
return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
struct MidiMessageTest : public UnitTest
{
MidiMessageTest()
: UnitTest ("MidiMessage", UnitTestCategories::midi)
{}
void runTest() override
{
using std::begin;
using std::end;
beginTest ("ReadVariableLengthValue should return valid, backward-compatible results");
{
const std::vector<uint8> inputs[]
{
{ 0x00 },
{ 0x40 },
{ 0x7f },
{ 0x81, 0x00 },
{ 0xc0, 0x00 },
{ 0xff, 0x7f },
{ 0x81, 0x80, 0x00 },
{ 0xc0, 0x80, 0x00 },
{ 0xff, 0xff, 0x7f },
{ 0x81, 0x80, 0x80, 0x00 },
{ 0xc0, 0x80, 0x80, 0x00 },
{ 0xff, 0xff, 0xff, 0x7f }
};
const int outputs[]
{
0x00,
0x40,
0x7f,
0x80,
0x2000,
0x3fff,
0x4000,
0x100000,
0x1fffff,
0x200000,
0x8000000,
0xfffffff,
};
expectEquals (std::distance (begin (inputs), end (inputs)),
std::distance (begin (outputs), end (outputs)));
size_t index = 0;
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
for (const auto& input : inputs)
{
auto copy = input;
while (copy.size() < 16)
copy.push_back (0);
const auto result = MidiMessage::readVariableLengthValue (copy.data(),
(int) copy.size());
expect (result.isValid());
expectEquals (result.value, outputs[index]);
expectEquals (result.bytesUsed, (int) inputs[index].size());
int legacyNumUsed = 0;
const auto legacyResult = MidiMessage::readVariableLengthVal (copy.data(),
legacyNumUsed);
expectEquals (result.value, legacyResult);
expectEquals (result.bytesUsed, legacyNumUsed);
++index;
}
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
JUCE_END_IGNORE_WARNINGS_MSVC
}
beginTest ("ReadVariableLengthVal should return 0 if input is truncated");
{
for (size_t i = 0; i != 16; ++i)
{
std::vector<uint8> input;
input.resize (i, 0xFF);
const auto result = MidiMessage::readVariableLengthValue (input.data(),
(int) input.size());
expect (! result.isValid());
expectEquals (result.value, 0);
expectEquals (result.bytesUsed, 0);
}
}
const std::vector<uint8> metaEvents[]
{
// Format is 0xff, followed by a 'kind' byte, followed by a variable-length
// 'data-length' value, followed by that many data bytes
{ 0xff, 0x00, 0x02, 0x00, 0x00 }, // Sequence number
{ 0xff, 0x01, 0x00 }, // Text event
{ 0xff, 0x02, 0x00 }, // Copyright notice
{ 0xff, 0x03, 0x00 }, // Track name
{ 0xff, 0x04, 0x00 }, // Instrument name
{ 0xff, 0x05, 0x00 }, // Lyric
{ 0xff, 0x06, 0x00 }, // Marker
{ 0xff, 0x07, 0x00 }, // Cue point
{ 0xff, 0x20, 0x01, 0x00 }, // Channel prefix
{ 0xff, 0x2f, 0x00 }, // End of track
{ 0xff, 0x51, 0x03, 0x01, 0x02, 0x03 }, // Set tempo
{ 0xff, 0x54, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05 }, // SMPTE offset
{ 0xff, 0x58, 0x04, 0x01, 0x02, 0x03, 0x04 }, // Time signature
{ 0xff, 0x59, 0x02, 0x01, 0x02 }, // Key signature
{ 0xff, 0x7f, 0x00 }, // Sequencer-specific
};
beginTest ("MidiMessage data constructor works for well-formed meta-events");
{
const auto status = (uint8) 0x90;
for (const auto& input : metaEvents)
{
int bytesUsed = 0;
const MidiMessage msg (input.data(), (int) input.size(), bytesUsed, status);
expect (msg.isMetaEvent());
expectEquals (msg.getMetaEventLength(), (int) input.size() - 3);
expectEquals (msg.getMetaEventType(), (int) input[1]);
}
}
beginTest ("MidiMessage data constructor works for malformed meta-events");
{
const auto status = (uint8) 0x90;
const auto runTest = [&] (const std::vector<uint8>& input)
{
int bytesUsed = 0;
const MidiMessage msg (input.data(), (int) input.size(), bytesUsed, status);
expect (msg.isMetaEvent());
expectEquals (msg.getMetaEventLength(), jmax (0, (int) input.size() - 3));
expectEquals (msg.getMetaEventType(), input.size() >= 2 ? input[1] : -1);
};
runTest ({ 0xff });
for (const auto& input : metaEvents)
{
auto copy = input;
copy[2] = 0x40; // Set the size of the message to more bytes than are present
runTest (copy);
}
}
}
};
static MidiMessageTest midiMessageTests;
#endif
} // namespace juce

+ 38
- 4
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessage.h View File

@@ -858,11 +858,45 @@ public:
//==============================================================================
/** Reads a midi variable-length integer.
@param data the data to read the number from
@param numBytesUsed on return, this will be set to the number of bytes that were read
This signature has been deprecated in favour of the safer
readVariableLengthValue.
The `data` argument indicates the data to read the number from,
and `numBytesUsed` is used as an out-parameter to indicate the number
of bytes that were read.
*/
static int readVariableLengthVal (const uint8* data,
int& numBytesUsed) noexcept;
JUCE_DEPRECATED (static int readVariableLengthVal (const uint8* data,
int& numBytesUsed) noexcept);
/** Holds information about a variable-length value which was parsed
from a stream of bytes.
A valid value requires that `bytesUsed` is greater than 0.
*/
struct VariableLengthValue
{
VariableLengthValue() = default;
VariableLengthValue (int valueIn, int bytesUsedIn)
: value (valueIn), bytesUsed (bytesUsedIn) {}
bool isValid() const noexcept { return bytesUsed > 0; }
int value = 0;
int bytesUsed = 0;
};
/** Reads a midi variable-length integer, with protection against buffer overflow.
@param data the data to read the number from
@param maxBytesToUse the number of bytes in the region following `data`
@returns a struct containing the parsed value, and the number
of bytes that were read. If parsing fails, both the
`value` and `bytesUsed` fields will be set to 0 and
`isValid()` will return false
*/
static VariableLengthValue readVariableLengthValue (const uint8* data,
int maxBytesToUse) noexcept;
/** Based on the first byte of a short midi message, this uses a lookup table
to return the message length (either 1, 2, or 3 bytes).


+ 30
- 8
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp View File

@@ -27,30 +27,33 @@ namespace
{
const uint8 noLSBValueReceived = 0xff;
const Range<int> allChannels { 1, 17 };
template <typename Range, typename Value>
void mpeInstrumentFill (Range& range, const Value& value)
{
std::fill (std::begin (range), std::end (range), value);
}
}
//==============================================================================
MPEInstrument::MPEInstrument() noexcept
{
std::fill_n (lastPressureLowerBitReceivedOnChannel, 16, noLSBValueReceived);
std::fill_n (lastTimbreLowerBitReceivedOnChannel, 16, noLSBValueReceived);
std::fill_n (isMemberChannelSustained, 16, false);
mpeInstrumentFill (lastPressureLowerBitReceivedOnChannel, noLSBValueReceived);
mpeInstrumentFill (lastTimbreLowerBitReceivedOnChannel, noLSBValueReceived);
mpeInstrumentFill (isMemberChannelSustained, false);
pitchbendDimension.value = &MPENote::pitchbend;
pressureDimension.value = &MPENote::pressure;
timbreDimension.value = &MPENote::timbre;
// the default value for pressure is 0, for all other dimension it is centre (= default MPEValue)
std::fill_n (pressureDimension.lastValueReceivedOnChannel, 16, MPEValue::minValue());
resetLastReceivedValues();
legacyMode.isEnabled = false;
legacyMode.pitchbendRange = 2;
legacyMode.channelRange = allChannels;
}
MPEInstrument::~MPEInstrument()
{
}
MPEInstrument::~MPEInstrument() = default;
//==============================================================================
MPEZoneLayout MPEInstrument::getZoneLayout() const noexcept
@@ -58,6 +61,23 @@ MPEZoneLayout MPEInstrument::getZoneLayout() const noexcept
return zoneLayout;
}
void MPEInstrument::resetLastReceivedValues()
{
struct Defaults
{
MPEDimension& dimension;
MPEValue defaultValue;
};
// The default value for pressure is 0, for all other dimensions it is centre
for (const auto& pair : { Defaults { pressureDimension, MPEValue::minValue() },
Defaults { pitchbendDimension, MPEValue::centreValue() },
Defaults { timbreDimension, MPEValue::centreValue() } })
{
mpeInstrumentFill (pair.dimension.lastValueReceivedOnChannel, pair.defaultValue);
}
}
void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout)
{
releaseAllNotes();
@@ -65,6 +85,8 @@ void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout)
const ScopedLock sl (lock);
legacyMode.isEnabled = false;
zoneLayout = newLayout;
resetLastReceivedValues();
}
//==============================================================================


+ 2
- 0
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h View File

@@ -376,6 +376,8 @@ private:
LegacyMode legacyMode;
MPEDimension pitchbendDimension, pressureDimension, timbreDimension;
void resetLastReceivedValues();
void updateDimension (int midiChannel, MPEDimension&, MPEValue);
void updateDimensionMaster (bool, MPEDimension&, MPEValue);
void updateDimensionForNote (MPENote&, MPEDimension&, MPEValue);


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_ADSR.h View File

@@ -199,7 +199,7 @@ public:
@see getNextSample
*/
template<typename FloatType>
template <typename FloatType>
void applyEnvelopeToBuffer (AudioBuffer<FloatType>& buffer, int startSample, int numSamples)
{
jassert (startSample + numSamples <= buffer.getNumSamples());


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_Interpolators.cpp View File

@@ -37,7 +37,7 @@ public:
}
private:
template<typename InterpolatorType>
template <typename InterpolatorType>
void runInterplatorTests (const String& interpolatorName)
{
auto createGaussian = [] (std::vector<float>& destination, float scale, float centreInSamples, float width)


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.cpp View File

@@ -29,7 +29,7 @@ struct LagrangeResampleHelper
static forcedinline void calc (float& a, float b) noexcept { a *= b * (1.0f / k); }
};
template<>
template <>
struct LagrangeResampleHelper<0>
{
static forcedinline void calc (float&, float) noexcept {}


+ 13
- 6
libs/juce-current/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp View File

@@ -339,7 +339,7 @@ String AudioDeviceManager::initialiseFromXML (const XmlElement& xml,
midiDeviceInfosFromXml.clear();
enabledMidiInputs.clear();
forEachXmlChildElementWithTagName (xml, c, "MIDIINPUT")
for (auto* c : xml.getChildWithTagNameIterator ("MIDIINPUT"))
midiDeviceInfosFromXml.add ({ c->getStringAttribute ("name"), c->getStringAttribute ("identifier") });
auto isIdentifierAvailable = [] (const Array<MidiDeviceInfo>& available, const String& identifier)
@@ -704,7 +704,7 @@ void AudioDeviceManager::restartLastAudioDevice()
{
// This method will only reload the last device that was running
// before closeAudioDevice() was called - you need to actually open
// one first, with setAudioDevice().
// one first, with setAudioDeviceSetup().
jassertfalse;
return;
}
@@ -1146,12 +1146,19 @@ void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCall
void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputCallback* callbackToRemove)
{
for (auto& device : MidiInput::getAvailableDevices())
if (name.isEmpty())
{
if (device.name == name)
removeMidiInputDeviceCallback ({}, callbackToRemove);
}
else
{
for (auto& device : MidiInput::getAvailableDevices())
{
removeMidiInputDeviceCallback (device.identifier, callbackToRemove);
return;
if (device.name == name)
{
removeMidiInputDeviceCallback (device.identifier, callbackToRemove);
return;
}
}
}
}


+ 1
- 1
libs/juce-current/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h View File

@@ -267,7 +267,7 @@ public:
Note that this only reloads the last device that was running before
closeAudioDevice() was called - it doesn't reload any kind of saved-state,
and can only be called after a device has been opened with SetAudioDevice().
and can only be called after a device has been opened with setAudioDeviceSetup().
If a device is already open, this call will do nothing.
*/


+ 2
- 3
libs/juce-current/source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h View File

@@ -80,9 +80,8 @@ public:
The scanForDevices() method must have been called to create this list.
@param wantInputNames only really used by DirectSound where devices are split up
into inputs and outputs, this indicates whether to use
the input or output name to refer to a pair of devices.
@param wantInputNames for devices which have separate inputs and outputs
this determines which list of names is returned
*/
virtual StringArray getDeviceNames (bool wantInputNames = false) const = 0;


+ 31
- 2
libs/juce-current/source/modules/juce_audio_devices/juce_audio_devices.cpp View File

@@ -47,6 +47,35 @@
#include "native/juce_MidiDataConcatenator.h"
#include "midi_io/ump/juce_UMPProtocols.h"
#include "midi_io/ump/juce_UMPUtils.h"
#include "midi_io/ump/juce_UMPacket.h"
#include "midi_io/ump/juce_UMPSysEx7.h"
#include "midi_io/ump/juce_UMPView.h"
#include "midi_io/ump/juce_UMPIterator.h"
#include "midi_io/ump/juce_UMPackets.h"
#include "midi_io/ump/juce_UMPFactory.h"
#include "midi_io/ump/juce_UMPConversion.h"
#include "midi_io/ump/juce_UMPMidi1ToBytestreamTranslator.h"
#include "midi_io/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h"
#include "midi_io/ump/juce_UMPConverters.h"
#include "midi_io/ump/juce_UMPDispatcher.h"
#include "midi_io/ump/juce_UMPReceiver.h"
#include "midi_io/ump/juce_UMPBytestreamInputHandler.h"
#include "midi_io/ump/juce_UMPU32InputHandler.h"
#include "midi_io/ump/juce_UMPUtils.cpp"
#include "midi_io/ump/juce_UMPView.cpp"
#include "midi_io/ump/juce_UMPSysEx7.cpp"
#include "midi_io/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp"
#include "midi_io/ump/juce_UMPTests.cpp"
namespace juce
{
namespace ump = universal_midi_packets;
}
//==============================================================================
#if JUCE_MAC
#define Point CarbonDummyPointName
@@ -58,7 +87,7 @@
#undef Component
#include "native/juce_mac_CoreAudio.cpp"
#include "native/juce_mac_CoreMidi.cpp"
#include "native/juce_mac_CoreMidi.mm"
#elif JUCE_IOS
#import <AudioToolbox/AudioToolbox.h>
@@ -70,7 +99,7 @@
#endif
#include "native/juce_ios_Audio.cpp"
#include "native/juce_mac_CoreMidi.cpp"
#include "native/juce_mac_CoreMidi.mm"
//==============================================================================
#elif JUCE_WINDOWS


+ 1
- 1
libs/juce-current/source/modules/juce_audio_devices/juce_audio_devices.h View File

@@ -32,7 +32,7 @@
ID: juce_audio_devices
vendor: juce
version: 6.0.4
version: 6.0.7
name: JUCE audio and MIDI I/O device classes
description: Classes to play and record from audio and MIDI I/O devices
website: http://www.juce.com/juce


+ 134
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPBytestreamInputHandler.h View File

@@ -0,0 +1,134 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
A base class for classes which convert bytestream midi to other formats.
*/
struct BytestreamInputHandler
{
virtual ~BytestreamInputHandler() noexcept = default;
virtual void reset() = 0;
virtual void pushMidiData (const void* data, int bytes, double time) = 0;
};
/**
Parses a continuous bytestream and emits complete MidiMessages whenever a full
message is received.
*/
struct BytestreamToBytestreamHandler : public BytestreamInputHandler
{
BytestreamToBytestreamHandler (MidiInput& i, MidiInputCallback& c)
: input (i), callback (c), concatenator (2048) {}
/**
Provides an `operator()` which can create an input handler for a given
MidiInput.
All handler classes should have a similar Factory to facilitate
creation of handlers in generic contexts.
*/
class Factory
{
public:
explicit Factory (MidiInputCallback* c)
: callback (c) {}
std::unique_ptr<BytestreamToBytestreamHandler> operator() (MidiInput& i) const
{
if (callback != nullptr)
return std::make_unique<BytestreamToBytestreamHandler> (i, *callback);
jassertfalse;
return {};
}
private:
MidiInputCallback* callback = nullptr;
};
void reset() override { concatenator.reset(); }
void pushMidiData (const void* data, int bytes, double time) override
{
concatenator.pushMidiData (data, bytes, time, &input, callback);
}
MidiInput& input;
MidiInputCallback& callback;
MidiDataConcatenator concatenator;
};
/**
Parses a continuous MIDI 1.0 bytestream, and emits full messages in the requested
UMP format.
*/
struct BytestreamToUMPHandler : public BytestreamInputHandler
{
BytestreamToUMPHandler (PacketProtocol protocol, Receiver& c)
: recipient (c), dispatcher (protocol, 2048) {}
/**
Provides an `operator()` which can create an input handler for a given
MidiInput.
All handler classes should have a similar Factory to facilitate
creation of handlers in generic contexts.
*/
class Factory
{
public:
Factory (PacketProtocol p, Receiver& c)
: protocol (p), callback (c) {}
std::unique_ptr<BytestreamToUMPHandler> operator() (MidiInput&) const
{
return std::make_unique<BytestreamToUMPHandler> (protocol, callback);
}
private:
PacketProtocol protocol;
Receiver& callback;
};
void reset() override { dispatcher.reset(); }
void pushMidiData (const void* data, int bytes, double time) override
{
const auto* ptr = static_cast<const uint8_t*> (data);
dispatcher.dispatch (ptr, ptr + bytes, time, [&] (const View& v)
{
recipient.packetReceived (v, time);
});
}
Receiver& recipient;
BytestreamToUMPDispatcher dispatcher;
};
}
}

+ 326
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPConversion.h View File

@@ -0,0 +1,326 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
Functions to assist conversion of UMP messages to/from other formats,
especially older 'bytestream' formatted MidiMessages.
@tags{Audio}
*/
struct Conversion
{
/** Converts from a MIDI 1 bytestream to MIDI 1 on Universal MIDI Packets.
`callback` is a function which accepts a single View argument.
*/
template <typename PacketCallbackFunction>
static void toMidi1 (const MidiMessage& m, PacketCallbackFunction&& callback)
{
const auto* data = m.getRawData();
const auto firstByte = data[0];
const auto size = m.getRawDataSize();
if (firstByte != 0xf0)
{
const auto mask = [size]() -> uint32_t
{
switch (size)
{
case 0: return 0xff000000;
case 1: return 0xffff0000;
case 2: return 0xffffff00;
case 3: return 0xffffffff;
}
return 0x00000000;
}();
const auto extraByte = (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 = m.getSysExDataSize();
const auto numMessages = SysEx7::getNumPacketsRequiredForDataSize ((uint32_t) numSysExBytes);
auto* dataOffset = m.getSysExData();
if (numMessages <= 1)
{
const auto packet = Factory::makeSysExIn1Packet (0, (uint8_t) numSysExBytes, dataOffset);
callback (View (packet.data()));
return;
}
constexpr auto byteIncrement = 6;
for (auto i = numSysExBytes; i > 0; i -= byteIncrement, dataOffset += byteIncrement)
{
const auto func = [&]
{
if (i == numSysExBytes)
return Factory::makeSysExStart;
if (i <= byteIncrement)
return Factory::makeSysExEnd;
return Factory::makeSysExContinue;
}();
const auto bytesNow = std::min (byteIncrement, i);
const auto packet = func (0, (uint8_t) bytesNow, dataOffset);
callback (View (packet.data()));
}
}
/** 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)
{
const auto shifted = (uint8_t) (word7Bit << 0x1);
const auto repeat = (uint8_t) (word7Bit & 0x3f);
const auto mask = (uint8_t) (word7Bit <= 0x40 ? 0x0 : 0xff);
return (uint8_t) (shifted | ((repeat >> 5) & mask));
}
/** Widens a 7-bit MIDI 1.0 value to a 16-bit MIDI 2.0 value. */
static uint16_t scaleTo16 (uint8_t word7Bit)
{
const auto shifted = (uint16_t) (word7Bit << 0x9);
const auto repeat = (uint16_t) (word7Bit & 0x3f);
const auto mask = (uint16_t) (word7Bit <= 0x40 ? 0x0 : 0xffff);
return (uint16_t) (shifted | (((repeat << 3) | (repeat >> 3)) & mask));
}
/** Widens a 14-bit MIDI 1.0 value to a 16-bit MIDI 2.0 value. */
static uint16_t scaleTo16 (uint16_t word14Bit)
{
const auto shifted = (uint16_t) (word14Bit << 0x2);
const auto repeat = (uint16_t) (word14Bit & 0x1fff);
const auto mask = (uint16_t) (word14Bit <= 0x2000 ? 0x0 : 0xffff);
return (uint16_t) (shifted | ((repeat >> 11) & mask));
}
/** Widens a 7-bit MIDI 1.0 value to a 32-bit MIDI 2.0 value. */
static uint32_t scaleTo32 (uint8_t word7Bit)
{
const auto shifted = (uint32_t) (word7Bit << 0x19);
const auto repeat = (uint32_t) (word7Bit & 0x3f);
const auto mask = (uint32_t) (word7Bit <= 0x40 ? 0x0 : 0xffffffff);
return (uint32_t) (shifted | (((repeat << 19)
| (repeat << 13)
| (repeat << 7)
| (repeat << 1)
| (repeat >> 5)) & mask));
}
/** Widens a 14-bit MIDI 1.0 value to a 32-bit MIDI 2.0 value. */
static uint32_t scaleTo32 (uint16_t word14Bit)
{
const auto shifted = (uint32_t) (word14Bit << 0x12);
const auto repeat = (uint32_t) (word14Bit & 0x1fff);
const auto mask = (uint32_t) (word14Bit <= 0x2000 ? 0x0 : 0xffffffff);
return (uint32_t) (shifted | (((repeat << 5) | (repeat >> 8)) & mask));
}
/** Narrows a 16-bit MIDI 2.0 value to a 7-bit MIDI 1.0 value. */
static uint8_t scaleTo7 (uint8_t word8Bit) { return (uint8_t) (word8Bit >> 1); }
/** Narrows a 16-bit MIDI 2.0 value to a 7-bit MIDI 1.0 value. */
static uint8_t scaleTo7 (uint16_t word16Bit) { return (uint8_t) (word16Bit >> 9); }
/** Narrows a 32-bit MIDI 2.0 value to a 7-bit MIDI 1.0 value. */
static uint8_t scaleTo7 (uint32_t word32Bit) { return (uint8_t) (word32Bit >> 25); }
/** Narrows a 32-bit MIDI 2.0 value to a 14-bit MIDI 1.0 value. */
static uint16_t scaleTo14 (uint16_t word16Bit) { return (uint16_t) (word16Bit >> 2); }
/** Narrows a 32-bit MIDI 2.0 value to a 14-bit MIDI 1.0 value. */
static uint16_t scaleTo14 (uint32_t word32Bit) { return (uint16_t) (word32Bit >> 18); }
/** Converts UMP messages which may include MIDI 2.0 channel voice messages into
equivalent MIDI 1.0 messages (still in UMP format).
`callback` is a function that accepts a single View argument and will be
called with each converted packet.
Note that not all MIDI 2.0 messages have MIDI 1.0 equivalents, so such
messages will be ignored.
*/
template <typename Callback>
static void midi2ToMidi1DefaultTranslation (const View& v, Callback&& callback)
{
const auto firstWord = v[0];
if (Utils::getMessageType (firstWord) != 0x4)
{
callback (v);
return;
}
const auto status = Utils::getStatus (firstWord);
const auto typeAndGroup = (uint8_t) ((0x2 << 0x4) | Utils::getGroup (firstWord));
switch (status)
{
case 0x8: // note off
case 0x9: // note on
case 0xa: // poly pressure
case 0xb: // control change
{
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 == 0;
const auto correctedByte = (uint8_t) (needsCorrection ? 1 : byte3);
const auto shouldIgnore = status == 0xb && [&]
{
switch (byte2)
{
case 0:
case 6:
case 32:
case 38:
case 98:
case 99:
case 100:
case 101:
return true;
}
return false;
}();
if (shouldIgnore)
return;
const PacketX1 packet { Utils::bytesToWord (typeAndGroup,
statusAndChannel,
byte2,
correctedByte) };
callback (View (packet.data()));
return;
}
case 0xd: // channel pressure
{
const auto statusAndChannel = (uint8_t) ((firstWord >> 0x10) & 0xff);
const auto byte2 = scaleTo7 (v[1]);
const PacketX1 packet { Utils::bytesToWord (typeAndGroup,
statusAndChannel,
byte2,
0) };
callback (View (packet.data()));
return;
}
case 0x2: // rpn
case 0x3: // nrpn
{
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, (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)
callback (View (packet.data()));
return;
}
case 0xc: // program change / bank select
{
if (firstWord & 1)
{
const auto statusAndChannel = (uint8_t) ((0xb << 0x4) | Utils::getChannel (firstWord));
const auto secondWord = v[1];
const PacketX1 packets[]
{
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 = (uint8_t) ((0xc << 0x4) | Utils::getChannel (firstWord));
const PacketX1 packet { Utils::bytesToWord (typeAndGroup,
statusAndChannel,
(uint8_t) ((v[1] >> 0x18) & 0x7f),
0) };
callback (View (packet.data()));
return;
}
case 0xe: // pitch bend
{
const auto data = scaleTo14 (v[1]);
const auto statusAndChannel = (uint8_t) ((firstWord >> 0x10) & 0xff);
const PacketX1 packet { Utils::bytesToWord (typeAndGroup,
statusAndChannel,
(uint8_t) (data & 0x7f),
(uint8_t) ((data >> 7) & 0x7f)) };
callback (View (packet.data()));
return;
}
default: // other message types do not translate
return;
}
}
};
}
}

+ 165
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPConverters.h View File

@@ -0,0 +1,165 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
Allows conversion from bytestream- or Universal MIDI Packet-formatted
messages to MIDI 1.0 messages in UMP format.
@tags{Audio}
*/
struct ToUMP1Converter
{
template <typename Fn>
void convert (const MidiMessage& m, Fn&& fn)
{
Conversion::toMidi1 (m, std::forward<Fn> (fn));
}
template <typename Fn>
void convert (const View& v, Fn&& fn)
{
Conversion::midi2ToMidi1DefaultTranslation (v, std::forward<Fn> (fn));
}
};
/**
Allows conversion from bytestream- or Universal MIDI Packet-formatted
messages to MIDI 2.0 messages in UMP format.
@tags{Audio}
*/
struct ToUMP2Converter
{
template <typename Fn>
void convert (const MidiMessage& m, Fn&& fn)
{
Conversion::toMidi1 (m, [&] (const View& v)
{
translator.dispatch (v, fn);
});
}
template <typename Fn>
void convert (const View& v, Fn&& fn)
{
translator.dispatch (v, std::forward<Fn> (fn));
}
void reset()
{
translator.reset();
}
Midi1ToMidi2DefaultTranslator translator;
};
/**
Allows conversion from bytestream- or Universal MIDI Packet-formatted
messages to UMP format.
The packet protocol can be selected using the constructor parameter.
@tags{Audio}
*/
class GenericUMPConverter
{
public:
explicit GenericUMPConverter (PacketProtocol m)
: mode (m) {}
void reset()
{
std::get<1> (converters).reset();
}
template <typename Fn>
void convert (const MidiMessage& m, Fn&& fn)
{
switch (mode)
{
case PacketProtocol::MIDI_1_0: return std::get<0> (converters).convert (m, std::forward<Fn> (fn));
case PacketProtocol::MIDI_2_0: return std::get<1> (converters).convert (m, std::forward<Fn> (fn));
}
}
template <typename Fn>
void convert (const View& v, Fn&& fn)
{
switch (mode)
{
case PacketProtocol::MIDI_1_0: return std::get<0> (converters).convert (v, std::forward<Fn> (fn));
case PacketProtocol::MIDI_2_0: return std::get<1> (converters).convert (v, std::forward<Fn> (fn));
}
}
template <typename Fn>
void convert (Iterator begin, Iterator end, Fn&& fn)
{
std::for_each (begin, end, [&] (const View& v)
{
convert (v, fn);
});
}
PacketProtocol getProtocol() const noexcept { return mode; }
private:
std::tuple<ToUMP1Converter, ToUMP2Converter> converters;
const PacketProtocol mode{};
};
/**
Allows conversion from bytestream- or Universal MIDI Packet-formatted
messages to bytestream format.
@tags{Audio}
*/
struct ToBytestreamConverter
{
explicit ToBytestreamConverter (int storageSize)
: translator (storageSize) {}
template <typename Fn>
void convert (const MidiMessage& m, Fn&& fn)
{
fn (m);
}
template <typename Fn>
void convert (const View& v, double time, Fn&& fn)
{
Conversion::midi2ToMidi1DefaultTranslation (v, [&] (const View& midi1)
{
translator.dispatch (midi1, time, fn);
});
}
void reset() { translator.reset(); }
Midi1ToBytestreamTranslator translator;
};
}
}

+ 190
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPDispatcher.h View File

@@ -0,0 +1,190 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
Parses a raw stream of uint32_t, and calls a user-provided callback every time
a full Universal MIDI Packet is encountered.
@tags{Audio}
*/
class Dispatcher
{
public:
/** Clears the dispatcher. */
void reset() { currentPacketLen = 0; }
/** Calls `callback` with a View of each packet encountered in the range delimited
by `begin` and `end`.
If the range ends part-way through a packet, the next call to `dispatch` will
continue from that point in the packet (unless `reset` is called first).
*/
template <typename PacketCallbackFunction>
void dispatch (const uint32_t* begin,
const uint32_t* end,
double timeStamp,
PacketCallbackFunction&& callback)
{
std::for_each (begin, end, [&] (uint32_t word)
{
nextPacket[currentPacketLen++] = word;
if (currentPacketLen == Utils::getNumWordsForMessageType (nextPacket.front()))
{
callback (View (nextPacket.data()), timeStamp);
currentPacketLen = 0;
}
});
}
private:
std::array<uint32_t, 4> nextPacket;
size_t currentPacketLen = 0;
};
//==============================================================================
/**
Parses a stream of bytes representing a sequence of bytestream-encoded MIDI 1.0 messages,
converting the messages to UMP format and passing the packets to a user-provided callback
as they become ready.
@tags{Audio}
*/
class BytestreamToUMPDispatcher
{
public:
/** Initialises the dispatcher.
Channel messages will be converted to the requested protocol format `pp`.
`storageSize` bytes will be allocated to store incomplete messages.
*/
explicit BytestreamToUMPDispatcher (PacketProtocol pp, int storageSize)
: concatenator (storageSize),
converter (pp)
{}
void reset()
{
concatenator.reset();
converter.reset();
}
/** Calls `callback` with a View of each converted packet as it becomes ready.
@param begin the first byte in a range of bytes representing bytestream-encoded MIDI messages.
@param end one-past the last byte in a range of bytes representing bytestream-encoded MIDI messages.
@param timestamp a timestamp to apply to the created packets.
@param callback a callback which will be passed a View pointing to each new packet as it becomes ready.
*/
template <typename PacketCallbackFunction>
void dispatch (const uint8_t* begin,
const uint8_t* end,
double timestamp,
PacketCallbackFunction&& callback)
{
using CallbackPtr = decltype (std::addressof (callback));
struct Callback
{
Callback (BytestreamToUMPDispatcher& d, CallbackPtr c)
: dispatch (d), callbackPtr (c) {}
void handleIncomingMidiMessage (void*, const MidiMessage& msg) const
{
Conversion::toMidi1 (msg, [&] (const View& view)
{
dispatch.converter.convert (view, *callbackPtr);
});
}
void handlePartialSysexMessage (void*, const uint8_t*, int, double) const {}
BytestreamToUMPDispatcher& dispatch;
CallbackPtr callbackPtr = nullptr;
};
Callback inputCallback { *this, &callback };
concatenator.pushMidiData (begin, int (end - begin), timestamp, (void*) nullptr, inputCallback);
}
private:
MidiDataConcatenator concatenator;
GenericUMPConverter converter;
};
//==============================================================================
/**
Parses a stream of 32-bit words representing a sequence of UMP-encoded MIDI messages,
converting the messages to MIDI 1.0 bytestream format and passing them to a user-provided
callback as they become ready.
@tags{Audio}
*/
class ToBytestreamDispatcher
{
public:
/** Initialises the dispatcher.
`storageSize` bytes will be allocated to store incomplete messages.
*/
explicit ToBytestreamDispatcher (int storageSize)
: converter (storageSize) {}
/** Clears the dispatcher. */
void reset()
{
dispatcher.reset();
converter.reset();
}
/** Calls `callback` with converted bytestream-formatted MidiMessage whenever
a new message becomes available.
@param begin the first word in a stream of words representing UMP-encoded MIDI packets.
@param end one-past the last word in a stream of words representing UMP-encoded MIDI packets.
@param timestamp a timestamp to apply to converted messages.
@param callback a callback which will be passed a MidiMessage each time a new message becomes ready.
*/
template <typename BytestreamMessageCallback>
void dispatch (const uint32_t* begin,
const uint32_t* end,
double timestamp,
BytestreamMessageCallback&& callback)
{
dispatcher.dispatch (begin, end, timestamp, [&] (const View& view, double time)
{
converter.convert (view, time, callback);
});
}
private:
Dispatcher dispatcher;
ToBytestreamConverter converter;
};
}
}

+ 534
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPFactory.h View File

@@ -0,0 +1,534 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
This struct holds functions that can be used to create different kinds
of Universal MIDI Packet.
@tags{Audio}
*/
struct Factory
{
/** @internal */
struct Detail
{
static PacketX1 makeSystem() { return PacketX1{}.withMessageType (1); }
static PacketX1 makeV1() { return PacketX1{}.withMessageType (2); }
static PacketX2 makeV2() { return PacketX2{}.withMessageType (4); }
static PacketX2 makeSysEx (uint8_t group,
uint8_t status,
uint8_t numBytes,
const uint8_t* data)
{
jassert (numBytes <= 6);
std::array<uint8_t, 8> bytes{{}};
bytes[0] = (0x3 << 0x4) | group;
bytes[1] = (uint8_t) (status << 0x4) | numBytes;
std::memcpy (bytes.data() + 2, data, numBytes);
std::array<uint32_t, 2> words;
size_t index = 0;
for (auto& word : words)
word = ByteOrder::bigEndianInt (bytes.data() + 4 * index++);
return PacketX2 { words };
}
static PacketX4 makeSysEx8 (uint8_t group,
uint8_t status,
uint8_t numBytes,
uint8_t dataStart,
const uint8_t* data)
{
jassert (numBytes <= 16 - dataStart);
std::array<uint8_t, 16> bytes{{}};
bytes[0] = (0x5 << 0x4) | group;
bytes[1] = (uint8_t) (status << 0x4) | numBytes;
std::memcpy (bytes.data() + dataStart, data, numBytes);
std::array<uint32_t, 4> words;
size_t index = 0;
for (auto& word : words)
word = ByteOrder::bigEndianInt (bytes.data() + 4 * index++);
return PacketX4 { words };
}
};
static PacketX1 makeNoop (uint8_t group)
{
return PacketX1{}.withGroup (group);
}
static PacketX1 makeJRClock (uint8_t group, uint16_t time)
{
return PacketX1 { time }.withStatus (1).withGroup (group);
}
static PacketX1 makeJRTimestamp (uint8_t group, uint16_t time)
{
return PacketX1 { time }.withStatus (2).withGroup (group);
}
static PacketX1 makeTimeCode (uint8_t group, uint8_t code)
{
return Detail::makeSystem().withGroup (group)
.withU8<1> (0xf1)
.withU8<2> (code & 0x7f);
}
static PacketX1 makeSongPositionPointer (uint8_t group, uint16_t pos)
{
return Detail::makeSystem().withGroup (group)
.withU8<1> (0xf2)
.withU8<2> (pos & 0x7f)
.withU8<3> ((pos >> 7) & 0x7f);
}
static PacketX1 makeSongSelect (uint8_t group, uint8_t song)
{
return Detail::makeSystem().withGroup (group)
.withU8<1> (0xf3)
.withU8<2> (song & 0x7f);
}
static PacketX1 makeTuneRequest (uint8_t group)
{
return Detail::makeSystem().withGroup (group)
.withU8<1> (0xf6);
}
static PacketX1 makeTimingClock (uint8_t group)
{
return Detail::makeSystem().withGroup (group)
.withU8<1> (0xf8);
}
static PacketX1 makeStart (uint8_t group)
{
return Detail::makeSystem().withGroup (group)
.withU8<1> (0xfa);
}
static PacketX1 makeContinue (uint8_t group)
{
return Detail::makeSystem().withGroup (group)
.withU8<1> (0xfb);
}
static PacketX1 makeStop (uint8_t group)
{
return Detail::makeSystem().withGroup (group)
.withU8<1> (0xfc);
}
static PacketX1 makeActiveSensing (uint8_t group)
{
return Detail::makeSystem().withGroup (group)
.withU8<1> (0xfe);
}
static PacketX1 makeReset (uint8_t group)
{
return Detail::makeSystem().withGroup (group)
.withU8<1> (0xff);
}
static PacketX1 makeNoteOffV1 (uint8_t group,
uint8_t channel,
uint8_t note,
uint8_t velocity)
{
return Detail::makeV1().withGroup (group)
.withStatus (0x8)
.withChannel (channel)
.withU8<2> (note & 0x7f)
.withU8<3> (velocity & 0x7f);
}
static PacketX1 makeNoteOnV1 (uint8_t group,
uint8_t channel,
uint8_t note,
uint8_t velocity)
{
return Detail::makeV1().withGroup (group)
.withStatus (0x9)
.withChannel (channel)
.withU8<2> (note & 0x7f)
.withU8<3> (velocity & 0x7f);
}
static PacketX1 makePolyPressureV1 (uint8_t group,
uint8_t channel,
uint8_t note,
uint8_t pressure)
{
return Detail::makeV1().withGroup (group)
.withStatus (0xa)
.withChannel (channel)
.withU8<2> (note & 0x7f)
.withU8<3> (pressure & 0x7f);
}
static PacketX1 makeControlChangeV1 (uint8_t group,
uint8_t channel,
uint8_t controller,
uint8_t value)
{
return Detail::makeV1().withGroup (group)
.withStatus (0xb)
.withChannel (channel)
.withU8<2> (controller & 0x7f)
.withU8<3> (value & 0x7f);
}
static PacketX1 makeProgramChangeV1 (uint8_t group,
uint8_t channel,
uint8_t program)
{
return Detail::makeV1().withGroup (group)
.withStatus (0xc)
.withChannel (channel)
.withU8<2> (program & 0x7f);
}
static PacketX1 makeChannelPressureV1 (uint8_t group,
uint8_t channel,
uint8_t pressure)
{
return Detail::makeV1().withGroup (group)
.withStatus (0xd)
.withChannel (channel)
.withU8<2> (pressure & 0x7f);
}
static PacketX1 makePitchBend (uint8_t group,
uint8_t channel,
uint16_t pitchbend)
{
return Detail::makeV1().withGroup (group)
.withStatus (0xe)
.withChannel (channel)
.withU8<2> (pitchbend & 0x7f)
.withU8<3> ((pitchbend >> 7) & 0x7f);
}
static PacketX2 makeSysExIn1Packet (uint8_t group,
uint8_t numBytes,
const uint8_t* data)
{
return Detail::makeSysEx (group, 0x0, numBytes, data);
}
static PacketX2 makeSysExStart (uint8_t group,
uint8_t numBytes,
const uint8_t* data)
{
return Detail::makeSysEx (group, 0x1, numBytes, data);
}
static PacketX2 makeSysExContinue (uint8_t group,
uint8_t numBytes,
const uint8_t* data)
{
return Detail::makeSysEx (group, 0x2, numBytes, data);
}
static PacketX2 makeSysExEnd (uint8_t group,
uint8_t numBytes,
const uint8_t* data)
{
return Detail::makeSysEx (group, 0x3, numBytes, data);
}
static PacketX2 makeRegisteredPerNoteControllerV2 (uint8_t group,
uint8_t channel,
uint8_t note,
uint8_t controller,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0x0)
.withChannel (channel)
.withU8<2> (note & 0x7f)
.withU8<3> (controller & 0x7f)
.withU32<1> (data);
}
static PacketX2 makeAssignablePerNoteControllerV2 (uint8_t group,
uint8_t channel,
uint8_t note,
uint8_t controller,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0x1)
.withChannel (channel)
.withU8<2> (note & 0x7f)
.withU8<3> (controller & 0x7f)
.withU32<1> (data);
}
static PacketX2 makeRegisteredControllerV2 (uint8_t group,
uint8_t channel,
uint8_t bank,
uint8_t index,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0x2)
.withChannel (channel)
.withU8<2> (bank & 0x7f)
.withU8<3> (index & 0x7f)
.withU32<1> (data);
}
static PacketX2 makeAssignableControllerV2 (uint8_t group,
uint8_t channel,
uint8_t bank,
uint8_t index,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0x3)
.withChannel (channel)
.withU8<2> (bank & 0x7f)
.withU8<3> (index & 0x7f)
.withU32<1> (data);
}
static PacketX2 makeRelativeRegisteredControllerV2 (uint8_t group,
uint8_t channel,
uint8_t bank,
uint8_t index,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0x4)
.withChannel (channel)
.withU8<2> (bank & 0x7f)
.withU8<3> (index & 0x7f)
.withU32<1> (data);
}
static PacketX2 makeRelativeAssignableControllerV2 (uint8_t group,
uint8_t channel,
uint8_t bank,
uint8_t index,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0x5)
.withChannel (channel)
.withU8<2> (bank & 0x7f)
.withU8<3> (index & 0x7f)
.withU32<1> (data);
}
static PacketX2 makePerNotePitchBendV2 (uint8_t group,
uint8_t channel,
uint8_t note,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0x6)
.withChannel (channel)
.withU8<2> (note & 0x7f)
.withU32<1> (data);
}
enum class NoteAttributeKind : uint8_t
{
none = 0x00,
manufacturer = 0x01,
profile = 0x02,
pitch7_9 = 0x03
};
static PacketX2 makeNoteOffV2 (uint8_t group,
uint8_t channel,
uint8_t note,
NoteAttributeKind attribute,
uint16_t velocity,
uint16_t attributeValue)
{
return Detail::makeV2().withGroup (group)
.withStatus (0x8)
.withChannel (channel)
.withU8<2> (note & 0x7f)
.withU8<3> ((uint8_t) attribute)
.withU16<2> (velocity)
.withU16<3> (attributeValue);
}
static PacketX2 makeNoteOnV2 (uint8_t group,
uint8_t channel,
uint8_t note,
NoteAttributeKind attribute,
uint16_t velocity,
uint16_t attributeValue)
{
return Detail::makeV2().withGroup (group)
.withStatus (0x9)
.withChannel (channel)
.withU8<2> (note & 0x7f)
.withU8<3> ((uint8_t) attribute)
.withU16<2> (velocity)
.withU16<3> (attributeValue);
}
static PacketX2 makePolyPressureV2 (uint8_t group,
uint8_t channel,
uint8_t note,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0xa)
.withChannel (channel)
.withU8<2> (note & 0x7f)
.withU32<1> (data);
}
static PacketX2 makeControlChangeV2 (uint8_t group,
uint8_t channel,
uint8_t controller,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0xb)
.withChannel (channel)
.withU8<2> (controller & 0x7f)
.withU32<1> (data);
}
static PacketX2 makeProgramChangeV2 (uint8_t group,
uint8_t channel,
uint8_t optionFlags,
uint8_t program,
uint8_t bankMsb,
uint8_t bankLsb)
{
return Detail::makeV2().withGroup (group)
.withStatus (0xc)
.withChannel (channel)
.withU8<3> (optionFlags)
.withU8<4> (program)
.withU8<6> (bankMsb)
.withU8<7> (bankLsb);
}
static PacketX2 makeChannelPressureV2 (uint8_t group,
uint8_t channel,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0xd)
.withChannel (channel)
.withU32<1> (data);
}
static PacketX2 makePitchBendV2 (uint8_t group,
uint8_t channel,
uint32_t data)
{
return Detail::makeV2().withGroup (group)
.withStatus (0xe)
.withChannel (channel)
.withU32<1> (data);
}
static PacketX2 makePerNoteManagementV2 (uint8_t group,
uint8_t channel,
uint8_t note,
uint8_t optionFlags)
{
return Detail::makeV2().withGroup (group)
.withStatus (0xf)
.withChannel (channel)
.withU8<2> (note)
.withU8<3> (optionFlags);
}
static PacketX4 makeSysEx8in1Packet (uint8_t group,
uint8_t numBytes,
uint8_t streamId,
const uint8_t* data)
{
return Detail::makeSysEx8 (group, 0x0, numBytes, 3, data).withU8<2> (streamId);
}
static PacketX4 makeSysEx8Start (uint8_t group,
uint8_t numBytes,
uint8_t streamId,
const uint8_t* data)
{
return Detail::makeSysEx8 (group, 0x1, numBytes, 3, data).withU8<2> (streamId);
}
static PacketX4 makeSysEx8Continue (uint8_t group,
uint8_t numBytes,
uint8_t streamId,
const uint8_t* data)
{
return Detail::makeSysEx8 (group, 0x2, numBytes, 3, data).withU8<2> (streamId);
}
static PacketX4 makeSysEx8End (uint8_t group,
uint8_t numBytes,
uint8_t streamId,
const uint8_t* data)
{
return Detail::makeSysEx8 (group, 0x3, numBytes, 3, data).withU8<2> (streamId);
}
static PacketX4 makeMixedDataSetHeader (uint8_t group,
uint8_t dataSetId,
const uint8_t* data)
{
return Detail::makeSysEx8 (group, 0x8, 14, 2, data).withChannel (dataSetId);
}
static PacketX4 makeDataSetPayload (uint8_t group,
uint8_t dataSetId,
const uint8_t* data)
{
return Detail::makeSysEx8 (group, 0x9, 14, 2, data).withChannel (dataSetId);
}
};
}
}

+ 126
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPIterator.h View File

@@ -0,0 +1,126 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
Enables iteration over a collection of Universal MIDI Packets stored as
a contiguous range of 32-bit words.
This iterator is used by Packets to allow access to the messages
that it contains.
@tags{Audio}
*/
class Iterator
{
public:
/** Creates an invalid (singular) iterator. */
Iterator() noexcept = default;
/** Creates an iterator pointing at `ptr`. */
explicit Iterator (const uint32_t* ptr, size_t bytes) noexcept
: view (ptr)
#if JUCE_DEBUG
, bytesRemaining (bytes)
#endif
{
ignoreUnused (bytes);
}
using difference_type = std::iterator_traits<const uint32_t*>::difference_type;
using value_type = View;
using reference = const View&;
using pointer = const View*;
using iterator_category = std::input_iterator_tag;
/** Moves this iterator to the next packet in the range. */
Iterator& operator++() noexcept
{
const auto increment = view.size();
#if JUCE_DEBUG
// If you hit this, the memory region contained a truncated or otherwise
// malformed Universal MIDI Packet.
// The Iterator can only be used on regions containing complete packets!
jassert (increment <= bytesRemaining);
bytesRemaining -= increment;
#endif
view = View (view.data() + increment);
return *this;
}
/** Moves this iterator to the next packet in the range,
returning the value of the iterator before it was
incremented.
*/
Iterator operator++ (int) noexcept
{
auto copy = *this;
++(*this);
return copy;
}
/** Returns true if this iterator points to the same address
as another iterator.
*/
bool operator== (const Iterator& other) const noexcept
{
return view == other.view;
}
/** Returns false if this iterator points to the same address
as another iterator.
*/
bool operator!= (const Iterator& other) const noexcept
{
return ! operator== (other);
}
/** Returns a reference to a View of the packet currently
pointed-to by this iterator.
The View can be queried for its size and content.
*/
reference operator*() noexcept { return view; }
/** Returns a pointer to a View of the packet currently
pointed-to by this iterator.
The View can be queried for its size and content.
*/
pointer operator->() noexcept { return &view; }
private:
View view;
#if JUCE_DEBUG
size_t bytesRemaining = 0;
#endif
};
}
}

+ 213
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPMidi1ToBytestreamTranslator.h View File

@@ -0,0 +1,213 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
Parses a raw stream of uint32_t holding a series of Universal MIDI Packets using
the MIDI 1.0 Protocol, converting to plain (non-UMP) MidiMessages.
@tags{Audio}
*/
class Midi1ToBytestreamTranslator
{
public:
/** Ensures that there is room in the internal buffer for a sysex message of at least
`initialBufferSize` bytes.
*/
explicit Midi1ToBytestreamTranslator (int initialBufferSize)
{
pendingSysExData.reserve (size_t (initialBufferSize));
}
/** Clears the concatenator. */
void reset()
{
pendingSysExData.clear();
pendingSysExTime = 0.0;
}
/** Converts a Universal MIDI Packet using the MIDI 1.0 Protocol to
an equivalent MidiMessage. Accumulates SysEx packets into a single
MidiMessage, as appropriate.
@param packet a packet which is using the MIDI 1.0 Protocol.
@param time the timestamp to be applied to these messages.
@param callback a callback which will be called with each converted MidiMessage.
*/
template <typename MessageCallback>
void dispatch (const View& packet, double time, MessageCallback&& callback)
{
const auto firstWord = *packet.data();
if (! pendingSysExData.empty() && shouldPacketTerminateSysExEarly (firstWord))
pendingSysExData.clear();
switch (packet.size())
{
case 1:
{
// Utility messages don't translate to bytestream format
if (Utils::getMessageType (firstWord) != 0x00)
callback (fromUmp (PacketX1 { firstWord }, time));
break;
}
case 2:
{
if (Utils::getMessageType (firstWord) == 0x3)
processSysEx (PacketX2 { packet[0], packet[1] }, time, callback);
break;
}
case 3: // no 3-word packets in the current spec
case 4: // no 4-word packets translate to bytestream format
default:
break;
}
}
/** Converts from a Universal MIDI Packet to MIDI 1 bytestream format.
This is only capable of converting a single Universal MIDI Packet to
an equivalent bytestream MIDI message. This function cannot understand
multi-packet messages, like SysEx7 messages.
To convert multi-packet messages, use `Midi1ToBytestreamTranslator`
to convert from a UMP MIDI 1.0 stream, or `ToBytestreamDispatcher`
to convert from both MIDI 2.0 and MIDI 1.0.
*/
static MidiMessage fromUmp (const PacketX1& m, double time = 0)
{
const auto word = m.front();
jassert (Utils::getNumWordsForMessageType (word) == 1);
const std::array<uint8_t, 3> bytes { { uint8_t ((word >> 0x10) & 0xff),
uint8_t ((word >> 0x08) & 0xff),
uint8_t ((word >> 0x00) & 0xff) } };
const auto numBytes = MidiMessage::getMessageLengthFromFirstByte (bytes.front());
return MidiMessage (bytes.data(), numBytes, time);
}
private:
template <typename MessageCallback>
void processSysEx (const PacketX2& packet,
double time,
MessageCallback&& callback)
{
switch (getSysEx7Kind (packet[0]))
{
case SysEx7::Kind::complete:
startSysExMessage (time);
pushBytes (packet);
terminateSysExMessage (callback);
break;
case SysEx7::Kind::begin:
startSysExMessage (time);
pushBytes (packet);
break;
case SysEx7::Kind::continuation:
if (pendingSysExData.empty())
break;
pushBytes (packet);
break;
case SysEx7::Kind::end:
if (pendingSysExData.empty())
break;
pushBytes (packet);
terminateSysExMessage (callback);
break;
}
}
void pushBytes (const PacketX2& packet)
{
const auto bytes = SysEx7::getDataBytes (packet);
pendingSysExData.insert (pendingSysExData.end(),
bytes.data.begin(),
bytes.data.begin() + bytes.size);
}
void startSysExMessage (double time)
{
pendingSysExTime = time;
pendingSysExData.push_back (0xf0);
}
template <typename MessageCallback>
void terminateSysExMessage (MessageCallback&& callback)
{
pendingSysExData.push_back (0xf7);
callback (MidiMessage (pendingSysExData.data(),
int (pendingSysExData.size()),
pendingSysExTime));
pendingSysExData.clear();
}
static bool shouldPacketTerminateSysExEarly (uint32_t firstWord)
{
return ! (isSysExContinuation (firstWord)
|| isSystemRealTime (firstWord)
|| isJROrNOP (firstWord));
}
static SysEx7::Kind getSysEx7Kind (uint32_t word)
{
return SysEx7::Kind ((word >> 0x14) & 0xf);
}
static bool isJROrNOP (uint32_t word)
{
return Utils::getMessageType (word) == 0x0;
}
static bool isSysExContinuation (uint32_t word)
{
if (Utils::getMessageType (word) != 0x3)
return false;
const auto kind = getSysEx7Kind (word);
return kind == SysEx7::Kind::continuation || kind == SysEx7::Kind::end;
}
static bool isSystemRealTime (uint32_t word)
{
return Utils::getMessageType (word) == 0x1 && ((word >> 0x10) & 0xff) >= 0xf8;
}
std::vector<uint8_t> pendingSysExData;
double pendingSysExTime = 0.0;
};
}
}

+ 195
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp View File

@@ -0,0 +1,195 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
PacketX2 Midi1ToMidi2DefaultTranslator::processNoteOnOrOff (const HelperValues helpers)
{
const auto velocity = helpers.byte2;
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, 0),
(uint32_t) (Conversion::scaleTo16 (velocity) << 0x10)
};
}
PacketX2 Midi1ToMidi2DefaultTranslator::processPolyPressure (const HelperValues helpers)
{
return PacketX2
{
Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, helpers.byte1, 0),
Conversion::scaleTo32 (helpers.byte2)
};
}
bool Midi1ToMidi2DefaultTranslator::processControlChange (const HelperValues helpers,
PacketX2& packet)
{
const auto statusAndChannel = helpers.byte0;
const auto cc = helpers.byte1;
const auto shouldAccumulate = [&]
{
switch (cc)
{
case 6:
case 38:
case 98:
case 99:
case 100:
case 101:
return true;
}
return false;
}();
const auto group = (uint8_t) (helpers.typeAndGroup & 0xf);
const auto channel = (uint8_t) (statusAndChannel & 0xf);
const auto byte = helpers.byte2;
if (shouldAccumulate)
{
auto& accumulator = groupAccumulators[group][channel];
if (accumulator.addByte (cc, byte))
{
const auto& bytes = accumulator.getBytes();
const auto bank = bytes[0];
const auto index = bytes[1];
const auto msb = bytes[2];
const auto lsb = bytes[3];
const auto value = (uint16_t) (((msb & 0x7f) << 7) | (lsb & 0x7f));
const auto newStatus = (uint8_t) (accumulator.getKind() == PnKind::nrpn ? 0x3 : 0x2);
packet = PacketX2
{
Utils::bytesToWord (helpers.typeAndGroup, (uint8_t) ((newStatus << 0x4) | channel), bank, index),
Conversion::scaleTo32 (value)
};
return true;
}
return false;
}
if (cc == 0)
{
groupBanks[group][channel].setMsb (byte);
return false;
}
if (cc == 32)
{
groupBanks[group][channel].setLsb (byte);
return false;
}
packet = PacketX2
{
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 & 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, 0, valid ? 1 : 0),
Utils::bytesToWord (helpers.byte1, 0, valid ? bank.getMsb() : 0, valid ? bank.getLsb() : 0)
};
}
PacketX2 Midi1ToMidi2DefaultTranslator::processChannelPressure (const HelperValues helpers)
{
return PacketX2
{
Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, 0, 0),
Conversion::scaleTo32 (helpers.byte1)
};
}
PacketX2 Midi1ToMidi2DefaultTranslator::processPitchBend (const HelperValues helpers)
{
const auto lsb = helpers.byte1;
const auto msb = helpers.byte2;
const auto value = (uint16_t) (msb << 7 | lsb);
return PacketX2
{
Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, 0, 0),
Conversion::scaleTo32 (value)
};
}
bool Midi1ToMidi2DefaultTranslator::PnAccumulator::addByte (uint8_t cc, uint8_t byte)
{
const auto isStart = cc == 99 || cc == 101;
if (isStart)
{
kind = cc == 99 ? PnKind::nrpn : PnKind::rpn;
index = 0;
}
bytes[index] = byte;
const auto shouldContinue = [&]
{
switch (index)
{
case 0: return isStart;
case 1: return kind == PnKind::nrpn ? cc == 98 : cc == 100;
case 2: return cc == 6;
case 3: return cc == 38;
}
return false;
}();
index = shouldContinue ? index + 1 : 0;
if (index != bytes.size())
return false;
index = 0;
return true;
}
}
}

+ 187
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h View File

@@ -0,0 +1,187 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
Translates a series of MIDI 1 Universal MIDI Packets to corresponding MIDI 2
packets.
@tags{Audio}
*/
class Midi1ToMidi2DefaultTranslator
{
public:
Midi1ToMidi2DefaultTranslator() = default;
/** Converts MIDI 1 Universal MIDI Packets to corresponding MIDI 2 packets,
calling `callback` with each converted packet.
In some cases (such as RPN/NRPN messages) multiple MIDI 1 packets will
convert to a single MIDI 2 packet. In these cases, the translator will
accumulate the full message internally, and send a single callback with
the completed message, once all the individual MIDI 1 packets have been
processed.
*/
template <typename PacketCallback>
void dispatch (const View& v, PacketCallback&& callback)
{
const auto firstWord = v[0];
const auto messageType = Utils::getMessageType (firstWord);
if (messageType != 0x2)
{
callback (v);
return;
}
const HelperValues helperValues
{
(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))
{
case 0x8:
case 0x9:
{
const auto packet = processNoteOnOrOff (helperValues);
callback (View (packet.data()));
return;
}
case 0xa:
{
const auto packet = processPolyPressure (helperValues);
callback (View (packet.data()));
return;
}
case 0xb:
{
PacketX2 packet;
if (processControlChange (helperValues, packet))
callback (View (packet.data()));
return;
}
case 0xc:
{
const auto packet = processProgramChange (helperValues);
callback (View (packet.data()));
return;
}
case 0xd:
{
const auto packet = processChannelPressure (helperValues);
callback (View (packet.data()));
return;
}
case 0xe:
{
const auto packet = processPitchBend (helperValues);
callback (View (packet.data()));
return;
}
}
}
void reset()
{
groupAccumulators = {};
groupBanks = {};
}
private:
enum class PnKind { nrpn, rpn };
struct HelperValues
{
uint8_t typeAndGroup;
uint8_t byte0;
uint8_t byte1;
uint8_t byte2;
};
static PacketX2 processNoteOnOrOff (const HelperValues helpers);
static PacketX2 processPolyPressure (const HelperValues helpers);
bool processControlChange (const HelperValues helpers, PacketX2& packet);
PacketX2 processProgramChange (const HelperValues helpers) const;
static PacketX2 processChannelPressure (const HelperValues helpers);
static PacketX2 processPitchBend (const HelperValues helpers);
class PnAccumulator
{
public:
bool addByte (uint8_t cc, uint8_t byte);
const std::array<uint8_t, 4>& getBytes() const noexcept { return bytes; }
PnKind getKind() const noexcept { return kind; }
private:
std::array<uint8_t, 4> bytes;
uint8_t index = 0;
PnKind kind = PnKind::nrpn;
};
class Bank
{
public:
bool isValid() const noexcept { return ! (msb & 0x80); }
uint8_t getMsb() const noexcept { return msb & 0x7f; }
uint8_t getLsb() const noexcept { return lsb & 0x7f; }
void setMsb (uint8_t i) noexcept { msb = i & 0x7f; }
void setLsb (uint8_t i) noexcept { msb &= 0x7f; lsb = i & 0x7f; }
private:
// We use the top bit to indicate whether this bank is valid.
// After reading the spec, it's not clear how we should determine whether
// there are valid values, so we'll just assume that the bank is valid
// once either the lsb or msb have been written.
uint8_t msb = 0x80;
uint8_t lsb = 0x00;
};
using ChannelAccumulators = std::array<PnAccumulator, 16>;
std::array<ChannelAccumulators, 16> groupAccumulators;
using ChannelBanks = std::array<Bank, 16>;
std::array<ChannelBanks, 16> groupBanks;
};
}
}

+ 44
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPProtocols.h View File

@@ -0,0 +1,44 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/** The kinds of MIDI protocol that can be formatted into Universal MIDI Packets. */
enum class PacketProtocol
{
MIDI_1_0,
MIDI_2_0,
};
/** All kinds of MIDI protocol understood by JUCE. */
enum class MidiProtocol
{
bytestream,
UMP_MIDI_1_0,
UMP_MIDI_2_0,
};
}
}

+ 40
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPReceiver.h View File

@@ -0,0 +1,40 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
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 universal_midi_packets
{
/**
A base class for classes which receive Universal MIDI Packets from an input.
*/
struct Receiver
{
virtual ~Receiver() noexcept = default;
/** This will be called each time a new packet is ready for processing. */
virtual void packetReceived (const View& packet, double time) = 0;
};
}
}

+ 53
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPSysEx7.cpp View File

@@ -0,0 +1,53 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
uint32_t SysEx7::getNumPacketsRequiredForDataSize (uint32_t size)
{
constexpr auto denom = 6;
return (size / denom) + ((size % denom) != 0);
}
SysEx7::PacketBytes SysEx7::getDataBytes (const PacketX2& packet)
{
const auto numBytes = Utils::getChannel (packet[0]);
constexpr uint8_t maxBytes = 6;
jassert (numBytes <= maxBytes);
return
{
{ packet.getU8<2>(),
packet.getU8<3>(),
packet.getU8<4>(),
packet.getU8<5>(),
packet.getU8<6>(),
packet.getU8<7>() },
jmin (numBytes, maxBytes)
};
}
}
}

+ 73
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPSysEx7.h View File

@@ -0,0 +1,73 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
This struct acts as a single-file namespace for Univeral MIDI Packet
functionality related to 7-bit SysEx.
@tags{Audio}
*/
struct SysEx7
{
/** Returns the number of 64-bit packets required to hold a series of
SysEx bytes.
The number passed to this function should exclude the leading/trailing
SysEx bytes used in an old midi bytestream, as these are not required
when using Universal MIDI Packets.
*/
static uint32_t getNumPacketsRequiredForDataSize (uint32_t);
/** The different kinds of UMP SysEx-7 message. */
enum class Kind : uint8_t
{
/** The whole message fits in a single 2-word packet. */
complete = 0,
/** The packet begins a SysEx message that will continue in subsequent packets. */
begin = 1,
/** The packet is a continuation of an ongoing SysEx message. */
continuation = 2,
/** The packet terminates an ongoing SysEx message. */
end = 3
};
/** Holds the bytes from a single SysEx-7 packet. */
struct PacketBytes
{
std::array<uint8_t, 6> data;
uint8_t size;
};
/** Extracts the data bytes from a 64-bit data message. */
static PacketBytes getDataBytes (const PacketX2& packet);
};
}
}

+ 1020
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPTests.cpp
File diff suppressed because it is too large
View File


+ 145
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h View File

@@ -0,0 +1,145 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
A base class for classes which convert Universal MIDI Packets to other
formats.
*/
struct U32InputHandler
{
virtual ~U32InputHandler() noexcept = default;
virtual void reset() = 0;
virtual void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) = 0;
};
/**
Parses a continuous stream of U32 words and emits complete MidiMessages whenever a full
message is received.
*/
struct U32ToBytestreamHandler : public U32InputHandler
{
U32ToBytestreamHandler (MidiInput& i, MidiInputCallback& c)
: input (i), callback (c), dispatcher (2048) {}
/**
Provides an `operator()` which can create an input handler for a given
MidiInput.
All handler classes should have a similar Factory to facilitate
creation of handlers in generic contexts.
*/
class Factory
{
public:
explicit Factory (MidiInputCallback* c)
: callback (c) {}
std::unique_ptr<U32ToBytestreamHandler> operator() (MidiInput& i) const
{
if (callback != nullptr)
return std::make_unique<U32ToBytestreamHandler> (i, *callback);
jassertfalse;
return {};
}
private:
MidiInputCallback* callback = nullptr;
};
void reset() override { dispatcher.reset(); }
void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) override
{
dispatcher.dispatch (begin, end, time, [this] (const MidiMessage& m)
{
callback.handleIncomingMidiMessage (&input, m);
});
}
MidiInput& input;
MidiInputCallback& callback;
ToBytestreamDispatcher dispatcher;
};
/**
Parses a continuous stream of U32 words and emits full messages in the requested
UMP format.
*/
struct U32ToUMPHandler : public U32InputHandler
{
U32ToUMPHandler (PacketProtocol protocol, Receiver& c)
: recipient (c), converter (protocol) {}
/**
Provides an `operator()` which can create an input handler for a given
MidiInput.
All handler classes should have a similar Factory to facilitate
creation of handlers in generic contexts.
*/
class Factory
{
public:
Factory (PacketProtocol p, Receiver& c)
: protocol (p), callback (c) {}
std::unique_ptr<U32ToUMPHandler> operator() (MidiInput&) const
{
return std::make_unique<U32ToUMPHandler> (protocol, callback);
}
private:
PacketProtocol protocol;
Receiver& callback;
};
void reset() override
{
dispatcher.reset();
converter.reset();
}
void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) override
{
dispatcher.dispatch (begin, end, time, [this] (const View& view, double thisTime)
{
converter.convert (view, [&] (const View& converted)
{
recipient.packetReceived (converted, thisTime);
});
});
}
Receiver& recipient;
Dispatcher dispatcher;
GenericUMPConverter converter;
};
}
}

+ 59
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPUtils.cpp View File

@@ -0,0 +1,59 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
uint32_t Utils::getNumWordsForMessageType (uint32_t mt)
{
switch (Utils::getMessageType (mt))
{
case 0x0:
case 0x1:
case 0x2:
case 0x6:
case 0x7:
return 1;
case 0x3:
case 0x4:
case 0x8:
case 0x9:
case 0xa:
return 2;
case 0xb:
case 0xc:
return 3;
case 0x5:
case 0xd:
case 0xe:
case 0xf:
return 4;
}
jassertfalse;
return 1;
}
}
}

+ 113
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPUtils.h View File

@@ -0,0 +1,113 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
Helpful types and functions for interacting with Universal MIDI Packets.
@tags{Audio}
*/
struct Utils
{
/** Joins 4 bytes into a single 32-bit word. */
static constexpr uint32_t bytesToWord (uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{
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
the first word of the packet.
The result will be between 1 and 4 inclusive.
A result of 1 means that the word is itself a complete packet.
*/
static uint32_t getNumWordsForMessageType (uint32_t);
/**
Helper functions for setting/getting 4-bit ranges inside a 32-bit word.
*/
template <size_t Index>
struct U4
{
static constexpr uint32_t shift = (uint32_t) 0x1c - Index * 4;
static constexpr uint32_t set (uint32_t word, uint8_t value)
{
return (word & ~((uint32_t) 0xf << shift)) | (uint32_t) ((value & 0xf) << shift);
}
static constexpr uint8_t get (uint32_t word)
{
return (uint8_t) ((word >> shift) & 0xf);
}
};
/**
Helper functions for setting/getting 8-bit ranges inside a 32-bit word.
*/
template <size_t Index>
struct U8
{
static constexpr uint32_t shift = (uint32_t) 0x18 - Index * 8;
static constexpr uint32_t set (uint32_t word, uint8_t value)
{
return (word & ~((uint32_t) 0xff << shift)) | (uint32_t) (value << shift);
}
static constexpr uint8_t get (uint32_t word)
{
return (uint8_t) ((word >> shift) & 0xff);
}
};
/**
Helper functions for setting/getting 16-bit ranges inside a 32-bit word.
*/
template <size_t Index>
struct U16
{
static constexpr uint32_t shift = (uint32_t) 0x10 - Index * 16;
static constexpr uint32_t set (uint32_t word, uint16_t value)
{
return (word & ~((uint32_t) 0xffff << shift)) | (uint32_t) (value << shift);
}
static constexpr uint16_t get (uint32_t word)
{
return (uint16_t) ((word >> shift) & 0xffff);
}
};
static constexpr uint8_t getMessageType (uint32_t w) noexcept { return U4<0>::get (w); }
static constexpr uint8_t getGroup (uint32_t w) noexcept { return U4<1>::get (w); }
static constexpr uint8_t getStatus (uint32_t w) noexcept { return U4<2>::get (w); }
static constexpr uint8_t getChannel (uint32_t w) noexcept { return U4<3>::get (w); }
};
}
}

+ 35
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPView.cpp View File

@@ -0,0 +1,35 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
uint32_t View::size() const noexcept
{
jassert (ptr != nullptr);
return Utils::getNumWordsForMessageType (*ptr);
}
}
}

+ 88
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPView.h View File

@@ -0,0 +1,88 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
Points to a single Universal MIDI Packet.
The packet must be well-formed for member functions to work correctly.
Specifically, the constructor argument must be the beginning of a region of
uint32_t that contains at least `getNumWordsForMessageType(*ddata)` items,
where `data` is the constructor argument.
NOTE: Instances of this class do not own the memory that they point to!
If you need to store a packet pointed-to by a View for later use, copy
the view contents to a Packets collection, or use the Utils::PacketX types.
@tags{Audio}
*/
class View
{
public:
/** Create an invalid view. */
View() noexcept = default;
/** Create a view of the packet starting at address `d`. */
explicit View (const uint32_t* data) noexcept : ptr (data) {}
/** Get a pointer to the first word in the Universal MIDI Packet currently
pointed-to by this view.
*/
const uint32_t* data() const noexcept { return ptr; }
/** Get the number of 32-words (between 1 and 4 inclusive) in the Universal
MIDI Packet currently pointed-to by this view.
*/
uint32_t size() const noexcept;
/** Get a specific word from this packet.
Passing an `index` that is greater than or equal to the result of `size`
will cause undefined behaviour.
*/
const uint32_t& operator[] (size_t index) const noexcept { return ptr[index]; }
/** Get an iterator pointing to the first word in the packet. */
const uint32_t* begin() const noexcept { return ptr; }
const uint32_t* cbegin() const noexcept { return ptr; }
/** Get an iterator pointing one-past the last word in the packet. */
const uint32_t* end() const noexcept { return ptr + size(); }
const uint32_t* cend() const noexcept { return ptr + size(); }
/** Return true if this view is pointing to the same address as another view. */
bool operator== (const View& other) const noexcept { return ptr == other.ptr; }
/** Return false if this view is pointing to the same address as another view. */
bool operator!= (const View& other) const noexcept { return ! operator== (other); }
private:
const uint32_t* ptr = nullptr;
};
}
}

+ 187
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPacket.h View File

@@ -0,0 +1,187 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
Holds a single Universal MIDI Packet.
*/
template <size_t numWords>
class Packet
{
public:
Packet() = default;
template <size_t w = numWords, typename std::enable_if<w == 1, int>::type = 0>
Packet (uint32_t a)
: contents { { a } }
{
jassert (Utils::getNumWordsForMessageType (a) == 1);
}
template <size_t w = numWords, typename std::enable_if<w == 2, int>::type = 0>
Packet (uint32_t a, uint32_t b)
: contents { { a, b } }
{
jassert (Utils::getNumWordsForMessageType (a) == 2);
}
template <size_t w = numWords, typename std::enable_if<w == 3, int>::type = 0>
Packet (uint32_t a, uint32_t b, uint32_t c)
: contents { { a, b, c } }
{
jassert (Utils::getNumWordsForMessageType (a) == 3);
}
template <size_t w = numWords, typename std::enable_if<w == 4, int>::type = 0>
Packet (uint32_t a, uint32_t b, uint32_t c, uint32_t d)
: contents { { a, b, c, d } }
{
jassert (Utils::getNumWordsForMessageType (a) == 4);
}
template <size_t w, typename std::enable_if<w == numWords, int>::type = 0>
explicit Packet (const std::array<uint32_t, w>& fullPacket)
: contents (fullPacket)
{
jassert (Utils::getNumWordsForMessageType (fullPacket.front()) == numWords);
}
Packet withMessageType (uint8_t type) const noexcept
{
return withU4<0> (type);
}
Packet withGroup (uint8_t group) const noexcept
{
return withU4<1> (group);
}
Packet withStatus (uint8_t status) const noexcept
{
return withU4<2> (status);
}
Packet withChannel (uint8_t channel) const noexcept
{
return withU4<3> (channel);
}
uint8_t getMessageType() const noexcept { return getU4<0>(); }
uint8_t getGroup() const noexcept { return getU4<1>(); }
uint8_t getStatus() const noexcept { return getU4<2>(); }
uint8_t getChannel() const noexcept { return getU4<3>(); }
template <size_t index>
Packet withU4 (uint8_t value) const noexcept
{
constexpr auto word = index / 8;
auto copy = *this;
std::get<word> (copy.contents) = Utils::U4<index % 8>::set (copy.template getU32<word>(), value);
return copy;
}
template <size_t index>
Packet withU8 (uint8_t value) const noexcept
{
constexpr auto word = index / 4;
auto copy = *this;
std::get<word> (copy.contents) = Utils::U8<index % 4>::set (copy.template getU32<word>(), value);
return copy;
}
template <size_t index>
Packet withU16 (uint16_t value) const noexcept
{
constexpr auto word = index / 2;
auto copy = *this;
std::get<word> (copy.contents) = Utils::U16<index % 2>::set (copy.template getU32<word>(), value);
return copy;
}
template <size_t index>
Packet withU32 (uint32_t value) const noexcept
{
auto copy = *this;
std::get<index> (copy.contents) = value;
return copy;
}
template <size_t index>
uint8_t getU4() const noexcept
{
return Utils::U4<index % 8>::get (this->template getU32<index / 8>());
}
template <size_t index>
uint8_t getU8() const noexcept
{
return Utils::U8<index % 4>::get (this->template getU32<index / 4>());
}
template <size_t index>
uint16_t getU16() const noexcept
{
return Utils::U16<index % 2>::get (this->template getU32<index / 2>());
}
template <size_t index>
uint32_t getU32() const noexcept
{
return std::get<index> (contents);
}
//==============================================================================
using Contents = std::array<uint32_t, numWords>;
using const_iterator = typename Contents::const_iterator;
const_iterator begin() const noexcept { return contents.begin(); }
const_iterator cbegin() const noexcept { return contents.begin(); }
const_iterator end() const noexcept { return contents.end(); }
const_iterator cend() const noexcept { return contents.end(); }
const uint32_t* data() const noexcept { return contents.data(); }
const uint32_t& front() const noexcept { return contents.front(); }
const uint32_t& back() const noexcept { return contents.back(); }
const uint32_t& operator[] (size_t index) const noexcept { return contents[index]; }
private:
Contents contents { {} };
};
using PacketX1 = Packet<1>;
using PacketX2 = Packet<2>;
using PacketX3 = Packet<3>;
using PacketX4 = Packet<4>;
}
}

+ 92
- 0
libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPackets.h View File

@@ -0,0 +1,92 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace universal_midi_packets
{
/**
Holds a collection of Universal MIDI Packets.
Unlike MidiBuffer, this collection does not store any additional information
(e.g. timestamps) alongside the raw messages.
If timestamps are required, these can be added to the container in UMP format,
as Jitter Reduction Utility messages.
@tags{Audio}
*/
class Packets
{
public:
/** Adds a single packet to the collection.
The View must be valid for this to work. If the view
points to a malformed message, or if the view points to a region
too short for the contained message, this call will result in
undefined behaviour.
*/
void add (const View& v) { storage.insert (storage.end(), v.cbegin(), v.cend()); }
void add (const PacketX1& p) { addImpl (p); }
void add (const PacketX2& p) { addImpl (p); }
void add (const PacketX3& p) { addImpl (p); }
void add (const PacketX4& p) { addImpl (p); }
/** Pre-allocates space for at least `numWords` 32-bit words in this collection. */
void reserve (size_t numWords) { storage.reserve (numWords); }
/** Removes all previously-added packets from this collection. */
void clear() { storage.clear(); }
/** Gets an iterator pointing to the first packet in this collection. */
Iterator cbegin() const noexcept { return Iterator (data(), size()); }
Iterator begin() const noexcept { return cbegin(); }
/** Gets an iterator pointing one-past the last packet in this collection. */
Iterator cend() const noexcept { return Iterator (data() + size(), 0); }
Iterator end() const noexcept { return cend(); }
/** Gets a pointer to the contents of the collection as a range of raw 32-bit words. */
const uint32_t* data() const noexcept { return storage.data(); }
/** Returns the number of uint32_t words in storage.
Note that this is likely to be larger than the number of packets
currently being stored, as some packets span multiple words.
*/
size_t size() const noexcept { return storage.size(); }
private:
template <size_t numWords>
void addImpl (const Packet<numWords>& p)
{
jassert (Utils::getNumWordsForMessageType (p[0]) == numWords);
add (View (p.data()));
}
std::vector<uint32_t> storage;
};
}
}

+ 22
- 7
libs/juce-current/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp View File

@@ -1663,11 +1663,16 @@ private:
void readInput (AudioBuffer<float>& buffer, const int numSamples, const int blockSizeMs)
{
for (auto* d : devices)
d->done = (d->numInputChans == 0);
d->done = (d->numInputChans == 0 || d->isWaitingForInput);
for (int tries = 5;;)
float totalWaitTimeMs = blockSizeMs * 5.0f;
constexpr int numReadAttempts = 6;
auto sumPower2s = [] (int maxPower) { return (1 << (maxPower + 1)) - 1; };
float waitTime = totalWaitTimeMs / (float) sumPower2s (numReadAttempts - 2);
for (int numReadAttemptsRemaining = numReadAttempts;;)
{
bool anyRemaining = false;
bool anySamplesRemaining = false;
for (auto* d : devices)
{
@@ -1679,17 +1684,20 @@ private:
d->done = true;
}
else
anyRemaining = true;
{
anySamplesRemaining = true;
}
}
}
if (! anyRemaining)
if (! anySamplesRemaining)
return;
if (--tries == 0)
if (--numReadAttemptsRemaining == 0)
break;
wait (blockSizeMs);
wait (jmax (1, roundToInt (waitTime)));
waitTime *= 2.0f;
}
for (auto* d : devices)
@@ -1717,7 +1725,9 @@ private:
d->done = true;
}
else
{
anyRemaining = true;
}
}
}
@@ -1808,6 +1818,8 @@ private:
numInputChans = useInputs ? device->getActiveInputChannels().countNumberOfSetBits() : 0;
numOutputChans = useOutputs ? device->getActiveOutputChannels().countNumberOfSetBits() : 0;
isWaitingForInput = numInputChans > 0;
inputIndex = channelIndex;
outputIndex = channelIndex + numInputChans;
@@ -1892,6 +1904,8 @@ private:
{
if (numInputChannels > 0)
{
isWaitingForInput = false;
int start1, size1, start2, size2;
inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2);
@@ -1973,6 +1987,7 @@ private:
std::unique_ptr<CoreAudioIODevice> device;
int inputIndex = 0, numInputChans = 0, outputIndex = 0, numOutputChans = 0;
bool useInputs = false, useOutputs = false;
std::atomic<bool> isWaitingForInput { false };
AbstractFifo inputFifo { 32 }, outputFifo { 32 };
bool done = false;


+ 0
- 732
libs/juce-current/source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp View File

@@ -1,732 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#ifndef JUCE_LOG_COREMIDI_ERRORS
#define JUCE_LOG_COREMIDI_ERRORS 1
#endif
namespace CoreMidiHelpers
{
//==============================================================================
static bool checkError (OSStatus err, int lineNum)
{
if (err == noErr)
return true;
#if JUCE_LOG_COREMIDI_ERRORS
Logger::writeToLog ("CoreMIDI error: " + String (lineNum) + " - " + String::toHexString ((int) err));
#endif
ignoreUnused (lineNum);
return false;
}
#undef CHECK_ERROR
#define CHECK_ERROR(a) CoreMidiHelpers::checkError (a, __LINE__)
static MidiDeviceInfo getMidiObjectInfo (MIDIObjectRef entity)
{
MidiDeviceInfo info;
{
ScopedCFString str;
if (CHECK_ERROR (MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str.cfString)))
info.name = String::fromCFString (str.cfString);
}
SInt32 objectID = 0;
if (CHECK_ERROR (MIDIObjectGetIntegerProperty (entity, kMIDIPropertyUniqueID, &objectID)))
{
info.identifier = String (objectID);
}
else
{
ScopedCFString str;
if (CHECK_ERROR (MIDIObjectGetStringProperty (entity, kMIDIPropertyUniqueID, &str.cfString)))
info.identifier = String::fromCFString (str.cfString);
}
return info;
}
static MidiDeviceInfo getEndpointInfo (MIDIEndpointRef endpoint, bool isExternal)
{
// NB: don't attempt to use nullptr for refs - it fails in some types of build.
MIDIEntityRef entity = 0;
MIDIEndpointGetEntity (endpoint, &entity);
// probably virtual
if (entity == 0)
return getMidiObjectInfo (endpoint);
auto result = getMidiObjectInfo (endpoint);
// endpoint is empty - try the entity
if (result == MidiDeviceInfo())
result = getMidiObjectInfo (entity);
// now consider the device
MIDIDeviceRef device = 0;
MIDIEntityGetDevice (entity, &device);
if (device != 0)
{
auto info = getMidiObjectInfo (device);
if (info != MidiDeviceInfo())
{
// if an external device has only one entity, throw away
// the endpoint name and just use the device name
if (isExternal && MIDIDeviceGetNumberOfEntities (device) < 2)
{
result = info;
}
else if (! result.name.startsWithIgnoreCase (info.name))
{
// prepend the device name and identifier to the entity's
result.name = (info.name + " " + result.name).trimEnd();
result.identifier = info.identifier + " " + result.identifier;
}
}
}
return result;
}
static MidiDeviceInfo getConnectedEndpointInfo (MIDIEndpointRef endpoint)
{
MidiDeviceInfo result;
// Does the endpoint have connections?
CFDataRef connections = nullptr;
int numConnections = 0;
MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections);
if (connections != nullptr)
{
numConnections = ((int) CFDataGetLength (connections)) / (int) sizeof (MIDIUniqueID);
if (numConnections > 0)
{
auto* pid = reinterpret_cast<const SInt32*> (CFDataGetBytePtr (connections));
for (int i = 0; i < numConnections; ++i, ++pid)
{
auto id = (MIDIUniqueID) ByteOrder::swapIfLittleEndian ((uint32) *pid);
MIDIObjectRef connObject;
MIDIObjectType connObjectType;
auto err = MIDIObjectFindByUniqueID (id, &connObject, &connObjectType);
if (err == noErr)
{
MidiDeviceInfo deviceInfo;
if (connObjectType == kMIDIObjectType_ExternalSource
|| connObjectType == kMIDIObjectType_ExternalDestination)
{
// Connected to an external device's endpoint (10.3 and later).
deviceInfo = getEndpointInfo (static_cast<MIDIEndpointRef> (connObject), true);
}
else
{
// Connected to an external device (10.2) (or something else, catch-all)
deviceInfo = getMidiObjectInfo (connObject);
}
if (deviceInfo != MidiDeviceInfo())
{
if (result.name.isNotEmpty()) result.name += ", ";
if (result.identifier.isNotEmpty()) result.identifier += ", ";
result.name += deviceInfo.name;
result.identifier += deviceInfo.identifier;
}
}
}
}
CFRelease (connections);
}
// Here, either the endpoint had no connections, or we failed to obtain names for them.
if (result == MidiDeviceInfo())
return getEndpointInfo (endpoint, false);
return result;
}
static int createUniqueIDForMidiPort (String deviceName, bool isInput)
{
String uniqueID;
#ifdef JucePlugin_CFBundleIdentifier
uniqueID = JUCE_STRINGIFY (JucePlugin_CFBundleIdentifier);
#else
auto appBundle = File::getSpecialLocation (File::currentApplicationFile);
ScopedCFString appBundlePath (appBundle.getFullPathName());
if (auto bundleURL = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, appBundlePath.cfString, kCFURLPOSIXPathStyle, true))
{
auto bundleRef = CFBundleCreate (kCFAllocatorDefault, bundleURL);
CFRelease (bundleURL);
if (bundleRef != nullptr)
{
if (auto bundleId = CFBundleGetIdentifier (bundleRef))
uniqueID = String::fromCFString (bundleId);
CFRelease (bundleRef);
}
}
#endif
if (uniqueID.isEmpty())
uniqueID = String (Random::getSystemRandom().nextInt (1024));
uniqueID += "." + deviceName + (isInput ? ".input" : ".output");
return uniqueID.hashCode();
}
static void enableSimulatorMidiSession()
{
#if TARGET_OS_SIMULATOR
static bool hasEnabledNetworkSession = false;
if (! hasEnabledNetworkSession)
{
MIDINetworkSession* session = [MIDINetworkSession defaultSession];
session.enabled = YES;
session.connectionPolicy = MIDINetworkConnectionPolicy_Anyone;
hasEnabledNetworkSession = true;
}
#endif
}
static void globalSystemChangeCallback (const MIDINotification*, void*)
{
// TODO.. Should pass-on this notification..
}
static String getGlobalMidiClientName()
{
if (auto* app = JUCEApplicationBase::getInstance())
return app->getApplicationName();
return "JUCE";
}
static MIDIClientRef getGlobalMidiClient()
{
static MIDIClientRef globalMidiClient = 0;
if (globalMidiClient == 0)
{
// Since OSX 10.6, the MIDIClientCreate function will only work
// correctly when called from the message thread!
JUCE_ASSERT_MESSAGE_THREAD
enableSimulatorMidiSession();
ScopedCFString name (getGlobalMidiClientName());
CHECK_ERROR (MIDIClientCreate (name.cfString, &globalSystemChangeCallback, nullptr, &globalMidiClient));
}
return globalMidiClient;
}
static Array<MidiDeviceInfo> findDevices (bool forInput)
{
// It seems that OSX can be a bit picky about the thread that's first used to
// search for devices. It's safest to use the message thread for calling this.
JUCE_ASSERT_MESSAGE_THREAD
if (getGlobalMidiClient() == 0)
{
jassertfalse;
return {};
}
enableSimulatorMidiSession();
Array<MidiDeviceInfo> devices;
auto numDevices = (forInput ? MIDIGetNumberOfSources() : MIDIGetNumberOfDestinations());
for (ItemCount i = 0; i < numDevices; ++i)
{
MidiDeviceInfo deviceInfo;
if (auto dest = forInput ? MIDIGetSource (i) : MIDIGetDestination (i))
deviceInfo = getConnectedEndpointInfo (dest);
if (deviceInfo == MidiDeviceInfo())
deviceInfo.name = deviceInfo.identifier = "<error>";
devices.add (deviceInfo);
}
return devices;
}
//==============================================================================
class MidiPortAndEndpoint
{
public:
MidiPortAndEndpoint (MIDIPortRef p, MIDIEndpointRef ep) noexcept
: port (p), endpoint (ep)
{
}
~MidiPortAndEndpoint() noexcept
{
if (port != 0)
MIDIPortDispose (port);
// if port == nullptr, it means we created the endpoint, so it's safe to delete it
if (port == 0 && endpoint != 0)
MIDIEndpointDispose (endpoint);
}
void send (const MIDIPacketList* packets) noexcept
{
if (port != 0)
MIDISend (port, endpoint, packets);
else
MIDIReceived (endpoint, packets);
}
MIDIPortRef port;
MIDIEndpointRef endpoint;
};
//==============================================================================
struct MidiPortAndCallback;
CriticalSection callbackLock;
Array<MidiPortAndCallback*> activeCallbacks;
struct MidiPortAndCallback
{
MidiPortAndCallback (MidiInputCallback& cb) : callback (cb) {}
~MidiPortAndCallback()
{
active = false;
{
const ScopedLock sl (callbackLock);
activeCallbacks.removeFirstMatchingValue (this);
}
if (portAndEndpoint != nullptr && portAndEndpoint->port != 0)
CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endpoint));
}
void handlePackets (const MIDIPacketList* pktlist)
{
auto time = Time::getMillisecondCounterHiRes() * 0.001;
const ScopedLock sl (callbackLock);
if (activeCallbacks.contains (this) && active)
{
auto* packet = &pktlist->packet[0];
for (unsigned int i = 0; i < pktlist->numPackets; ++i)
{
auto len = readUnaligned<decltype (packet->length)> (&(packet->length));
concatenator.pushMidiData (packet->data, (int) len, time, input, callback);
packet = MIDIPacketNext (packet);
}
}
}
MidiInput* input = nullptr;
std::unique_ptr<MidiPortAndEndpoint> portAndEndpoint;
std::atomic<bool> active { false };
private:
MidiInputCallback& callback;
MidiDataConcatenator concatenator { 2048 };
};
static void midiInputProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* /*srcConnRefCon*/)
{
static_cast<MidiPortAndCallback*> (readProcRefCon)->handlePackets (pktlist);
}
static Array<MIDIEndpointRef> getEndpoints (bool isInput)
{
Array<MIDIEndpointRef> endpoints;
auto numDevices = (isInput ? MIDIGetNumberOfSources() : MIDIGetNumberOfDestinations());
for (ItemCount i = 0; i < numDevices; ++i)
endpoints.add (isInput ? MIDIGetSource (i) : MIDIGetDestination (i));
return endpoints;
}
}
class MidiInput::Pimpl : public CoreMidiHelpers::MidiPortAndCallback
{
public:
using MidiPortAndCallback::MidiPortAndCallback;
};
//==============================================================================
Array<MidiDeviceInfo> MidiInput::getAvailableDevices()
{
return CoreMidiHelpers::findDevices (true);
}
MidiDeviceInfo MidiInput::getDefaultDevice()
{
return getAvailableDevices().getFirst();
}
std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier, MidiInputCallback* callback)
{
if (deviceIdentifier.isEmpty())
return nullptr;
using namespace CoreMidiHelpers;
if (auto client = getGlobalMidiClient())
{
for (auto& endpoint : getEndpoints (true))
{
auto endpointInfo = getConnectedEndpointInfo (endpoint);
if (deviceIdentifier == endpointInfo.identifier)
{
ScopedCFString cfName;
if (CHECK_ERROR (MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &cfName.cfString)))
{
MIDIPortRef port;
auto mpc = std::make_unique<Pimpl> (*callback);
if (CHECK_ERROR (MIDIInputPortCreate (client, cfName.cfString, midiInputProc, mpc.get(), &port)))
{
if (CHECK_ERROR (MIDIPortConnectSource (port, endpoint, nullptr)))
{
mpc->portAndEndpoint = std::make_unique<MidiPortAndEndpoint> (port, endpoint);
std::unique_ptr<MidiInput> midiInput (new MidiInput (endpointInfo.name, endpointInfo.identifier));
mpc->input = midiInput.get();
auto* ptr = mpc.get();
midiInput->internal = std::move (mpc);
const ScopedLock sl (callbackLock);
activeCallbacks.add (ptr);
return midiInput;
}
else
{
CHECK_ERROR (MIDIPortDispose (port));
}
}
}
}
}
}
return {};
}
std::unique_ptr<MidiInput> MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
{
using namespace CoreMidiHelpers;
jassert (callback != nullptr);
if (auto client = getGlobalMidiClient())
{
auto mpc = std::make_unique<Pimpl> (*callback);
mpc->active = false;
MIDIEndpointRef endpoint;
ScopedCFString name (deviceName);
auto err = MIDIDestinationCreate (client, name.cfString, midiInputProc, mpc.get(), &endpoint);
#if JUCE_IOS
if (err == kMIDINotPermitted)
{
// If you've hit this assertion then you probably haven't enabled the "Audio Background Capability"
// setting in the iOS exporter for your app - this is required if you want to create a MIDI device!
jassertfalse;
return nullptr;
}
#endif
if (CHECK_ERROR (err))
{
auto deviceIdentifier = createUniqueIDForMidiPort (deviceName, true);
if (CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier)))
{
mpc->portAndEndpoint = std::make_unique<MidiPortAndEndpoint> ((UInt32) 0, endpoint);
std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceName, String (deviceIdentifier)));
mpc->input = midiInput.get();
auto* ptr = mpc.get();
midiInput->internal = std::move (mpc);
const ScopedLock sl (callbackLock);
activeCallbacks.add (ptr);
return midiInput;
}
}
}
return {};
}
StringArray MidiInput::getDevices()
{
StringArray deviceNames;
for (auto& d : getAvailableDevices())
deviceNames.add (d.name);
return deviceNames;
}
int MidiInput::getDefaultDeviceIndex()
{
return 0;
}
std::unique_ptr<MidiInput> MidiInput::openDevice (int index, MidiInputCallback* callback)
{
return openDevice (getAvailableDevices()[index].identifier, callback);
}
MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier)
: deviceInfo (deviceName, deviceIdentifier)
{
}
MidiInput::~MidiInput() = default;
void MidiInput::start()
{
const ScopedLock sl (CoreMidiHelpers::callbackLock);
internal->active = true;
}
void MidiInput::stop()
{
const ScopedLock sl (CoreMidiHelpers::callbackLock);
internal->active = false;
}
//==============================================================================
class MidiOutput::Pimpl : public CoreMidiHelpers::MidiPortAndEndpoint
{
public:
using MidiPortAndEndpoint::MidiPortAndEndpoint;
};
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices()
{
return CoreMidiHelpers::findDevices (false);
}
MidiDeviceInfo MidiOutput::getDefaultDevice()
{
return getAvailableDevices().getFirst();
}
std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifier)
{
if (deviceIdentifier.isEmpty())
return nullptr;
using namespace CoreMidiHelpers;
if (auto client = getGlobalMidiClient())
{
for (auto& endpoint : getEndpoints (false))
{
auto endpointInfo = getConnectedEndpointInfo (endpoint);
if (deviceIdentifier == endpointInfo.identifier)
{
ScopedCFString cfName;
if (CHECK_ERROR (MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &cfName.cfString)))
{
MIDIPortRef port;
if (CHECK_ERROR (MIDIOutputPortCreate (client, cfName.cfString, &port)))
{
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (endpointInfo.name, endpointInfo.identifier));
midiOutput->internal = std::make_unique<Pimpl> (port, endpoint);
return midiOutput;
}
}
}
}
}
return {};
}
std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String& deviceName)
{
using namespace CoreMidiHelpers;
if (auto client = getGlobalMidiClient())
{
MIDIEndpointRef endpoint;
ScopedCFString name (deviceName);
auto err = MIDISourceCreate (client, name.cfString, &endpoint);
#if JUCE_IOS
if (err == kMIDINotPermitted)
{
// If you've hit this assertion then you probably haven't enabled the "Audio Background Capability"
// setting in the iOS exporter for your app - this is required if you want to create a MIDI device!
jassertfalse;
return nullptr;
}
#endif
if (CHECK_ERROR (err))
{
auto deviceIdentifier = createUniqueIDForMidiPort (deviceName, false);
if (CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier)))
{
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (deviceName, String (deviceIdentifier)));
midiOutput->internal = std::make_unique<Pimpl> ((UInt32) 0, endpoint);
return midiOutput;
}
}
}
return {};
}
StringArray MidiOutput::getDevices()
{
StringArray deviceNames;
for (auto& d : getAvailableDevices())
deviceNames.add (d.name);
return deviceNames;
}
int MidiOutput::getDefaultDeviceIndex()
{
return 0;
}
std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index)
{
return openDevice (getAvailableDevices()[index].identifier);
}
MidiOutput::~MidiOutput()
{
stopBackgroundThread();
}
void MidiOutput::sendMessageNow (const MidiMessage& message)
{
#if JUCE_IOS
const MIDITimeStamp timeStamp = mach_absolute_time();
#else
const MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
#endif
HeapBlock<MIDIPacketList> allocatedPackets;
MIDIPacketList stackPacket;
auto* packetToSend = &stackPacket;
auto dataSize = (size_t) message.getRawDataSize();
if (message.isSysEx())
{
const int maxPacketSize = 256;
int pos = 0, bytesLeft = (int) dataSize;
const int numPackets = (bytesLeft + maxPacketSize - 1) / maxPacketSize;
allocatedPackets.malloc ((size_t) (32 * (size_t) numPackets + dataSize), 1);
packetToSend = allocatedPackets;
packetToSend->numPackets = (UInt32) numPackets;
auto* p = packetToSend->packet;
for (int i = 0; i < numPackets; ++i)
{
p->timeStamp = timeStamp;
p->length = (UInt16) jmin (maxPacketSize, bytesLeft);
memcpy (p->data, message.getRawData() + pos, p->length);
pos += p->length;
bytesLeft -= p->length;
p = MIDIPacketNext (p);
}
}
else if (dataSize < 65536) // max packet size
{
auto stackCapacity = sizeof (stackPacket.packet->data);
if (dataSize > stackCapacity)
{
allocatedPackets.malloc ((sizeof (MIDIPacketList) - stackCapacity) + dataSize, 1);
packetToSend = allocatedPackets;
}
packetToSend->numPackets = 1;
auto& p = *(packetToSend->packet);
p.timeStamp = timeStamp;
p.length = (UInt16) dataSize;
memcpy (p.data, message.getRawData(), dataSize);
}
else
{
jassertfalse; // packet too large to send!
return;
}
internal->send (packetToSend);
}
#undef CHECK_ERROR
} // namespace juce

+ 1325
- 0
libs/juce-current/source/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm
File diff suppressed because it is too large
View File


+ 26
- 26
libs/juce-current/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp View File

@@ -334,7 +334,7 @@ private:
};
//==============================================================================
template<class WrapperType>
template <class WrapperType>
struct Win32MidiDeviceQuery
{
static Array<MidiDeviceInfo> getAvailableDevices()
@@ -782,9 +782,9 @@ private:
public:
virtual ~DeviceCallbackHandler() {};
virtual HRESULT addDevice (IDeviceInformation*) = 0;
virtual HRESULT removeDevice (IDeviceInformationUpdate*) = 0;
virtual HRESULT updateDevice (IDeviceInformationUpdate*) = 0;
JUCE_COMCALL addDevice (IDeviceInformation*) = 0;
JUCE_COMCALL removeDevice (IDeviceInformationUpdate*) = 0;
JUCE_COMCALL updateDevice (IDeviceInformationUpdate*) = 0;
bool attach (HSTRING deviceSelector, DeviceInformationKind infoKind)
{
@@ -834,7 +834,7 @@ private:
}
}
WinRTWrapper::ComPtr<IIterable<HSTRING>> iter;
ComSmartPtr<IIterable<HSTRING>> iter;
auto hr = requestedProperties->QueryInterface (__uuidof (IIterable<HSTRING>), (void**) iter.resetAndGetPointerAddress());
if (FAILED (hr))
@@ -891,7 +891,7 @@ private:
watcher = nullptr;
}
template<typename InfoType>
template <typename InfoType>
IInspectable* getValueFromDeviceInfo (String key, InfoType* info)
{
__FIMapView_2_HSTRING_IInspectable* properties;
@@ -924,7 +924,7 @@ private:
String getGUIDFromInspectable (IInspectable& inspectable)
{
WinRTWrapper::ComPtr<IReference<GUID>> guidRef;
ComSmartPtr<IReference<GUID>> guidRef;
auto hr = inspectable.QueryInterface (__uuidof (IReference<GUID>),
(void**) guidRef.resetAndGetPointerAddress());
@@ -951,7 +951,7 @@ private:
bool getBoolFromInspectable (IInspectable& inspectable)
{
WinRTWrapper::ComPtr<IReference<bool>> boolRef;
ComSmartPtr<IReference<bool>> boolRef;
auto hr = inspectable.QueryInterface (__uuidof (IReference<bool>),
(void**) boolRef.resetAndGetPointerAddress());
@@ -978,7 +978,7 @@ private:
struct DeviceEnumerationThread : public Thread
{
DeviceEnumerationThread (DeviceCallbackHandler& h,
WinRTWrapper::ComPtr<IDeviceWatcher>& w,
ComSmartPtr<IDeviceWatcher>& w,
EventRegistrationToken& added,
EventRegistrationToken& removed,
EventRegistrationToken& updated)
@@ -1012,12 +1012,12 @@ private:
}
DeviceCallbackHandler& handler;
WinRTWrapper::ComPtr<IDeviceWatcher>& watcher;
ComSmartPtr<IDeviceWatcher>& watcher;
EventRegistrationToken& deviceAddedToken, deviceRemovedToken, deviceUpdatedToken;
};
//==============================================================================
WinRTWrapper::ComPtr<IDeviceWatcher> watcher;
ComSmartPtr<IDeviceWatcher> watcher;
EventRegistrationToken deviceAddedToken { 0 },
deviceRemovedToken { 0 },
@@ -1222,7 +1222,7 @@ private:
template <typename COMFactoryType>
struct MidiIODeviceWatcher final : private DeviceCallbackHandler
{
MidiIODeviceWatcher (WinRTWrapper::ComPtr<COMFactoryType>& comFactory)
MidiIODeviceWatcher (ComSmartPtr<COMFactoryType>& comFactory)
: factory (comFactory)
{
}
@@ -1407,7 +1407,7 @@ private:
return {};
}
WinRTWrapper::ComPtr<COMFactoryType>& factory;
ComSmartPtr<COMFactoryType>& factory;
Array<WinRTMIDIDeviceInfo> connectedDevices;
CriticalSection deviceChanges;
@@ -1421,8 +1421,8 @@ private:
struct OpenMidiPortThread : public Thread
{
OpenMidiPortThread (String threadName, String midiDeviceID,
WinRTWrapper::ComPtr<COMFactoryType>& comFactory,
WinRTWrapper::ComPtr<COMInterfaceType>& comPort)
ComSmartPtr<COMFactoryType>& comFactory,
ComSmartPtr<COMInterfaceType>& comPort)
: Thread (threadName),
deviceID (midiDeviceID),
factory (comFactory),
@@ -1438,7 +1438,7 @@ private:
void run() override
{
WinRTWrapper::ScopedHString hDeviceId (deviceID);
WinRTWrapper::ComPtr<IAsyncOperation<COMType*>> asyncOp;
ComSmartPtr<IAsyncOperation<COMType*>> asyncOp;
auto hr = factory->FromIdAsync (hDeviceId.get(), asyncOp.resetAndGetPointerAddress());
if (FAILED (hr))
@@ -1466,8 +1466,8 @@ private:
}
const String deviceID;
WinRTWrapper::ComPtr<COMFactoryType>& factory;
WinRTWrapper::ComPtr<COMInterfaceType>& port;
ComSmartPtr<COMFactoryType>& factory;
ComSmartPtr<COMInterfaceType>& port;
WaitableEvent portOpened { true };
};
@@ -1552,7 +1552,7 @@ private:
BLEDeviceWatcher& bleDeviceWatcher;
WinRTMIDIDeviceInfo deviceInfo;
bool isBLEDevice = false;
WinRTWrapper::ComPtr<MIDIPort> midiPort;
ComSmartPtr<MIDIPort> midiPort;
};
//==============================================================================
@@ -1637,19 +1637,19 @@ private:
if (! isStarted)
return S_OK;
WinRTWrapper::ComPtr<IMidiMessage> message;
ComSmartPtr<IMidiMessage> message;
auto hr = args->get_Message (message.resetAndGetPointerAddress());
if (FAILED (hr))
return hr;
WinRTWrapper::ComPtr<IBuffer> buffer;
ComSmartPtr<IBuffer> buffer;
hr = message->get_RawData (buffer.resetAndGetPointerAddress());
if (FAILED (hr))
return hr;
WinRTWrapper::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
ComSmartPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
hr = buffer->QueryInterface (bufferByteAccess.resetAndGetPointerAddress());
if (FAILED (hr))
@@ -1775,15 +1775,15 @@ private:
String getDeviceName() override { return deviceInfo.name; }
//==============================================================================
WinRTWrapper::ComPtr<IBuffer> buffer;
WinRTWrapper::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
ComSmartPtr<IBuffer> buffer;
ComSmartPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
uint8_t* bufferData = nullptr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WinRTOutputWrapper);
};
WinRTWrapper::ComPtr<IMidiInPortStatics> midiInFactory;
WinRTWrapper::ComPtr<IMidiOutPortStatics> midiOutFactory;
ComSmartPtr<IMidiInPortStatics> midiInFactory;
ComSmartPtr<IMidiOutPortStatics> midiOutFactory;
std::unique_ptr<MidiIODeviceWatcher<IMidiInPortStatics>> inputDeviceWatcher;
std::unique_ptr<MidiIODeviceWatcher<IMidiOutPortStatics>> outputDeviceWatcher;


+ 7
- 14
libs/juce-current/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp View File

@@ -116,9 +116,6 @@ bool check (HRESULT hr)
#define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71")
#endif
#define JUCE_IUNKNOWNCLASS(name, guid) JUCE_COMCLASS(name, guid) : public IUnknown
#define JUCE_COMCALL virtual HRESULT STDMETHODCALLTYPE
enum EDataFlow
{
eRender = 0,
@@ -337,10 +334,6 @@ JUCE_IUNKNOWNCLASS (IAudioSessionControl, "F4B1A599-7266-4319-A8CA-E70ACB11E8CD"
JUCE_COMCALL UnregisterAudioSessionNotification (IAudioSessionEvents*) = 0;
};
#undef JUCE_COMCALL
#undef JUCE_COMCLASS
#undef JUCE_IUNKNOWNCLASS
//==============================================================================
namespace WasapiClasses
{
@@ -893,7 +886,7 @@ public:
reservoirWritePos = 0;
}
template<class SourceType>
template <class SourceType>
void updateFormatWithType (SourceType*) noexcept
{
using NativeType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
@@ -1053,7 +1046,7 @@ public:
renderClient = nullptr;
}
template<class DestType>
template <class DestType>
void updateFormatWithType (DestType*)
{
using NativeType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
@@ -1755,11 +1748,11 @@ private:
ChangeNotificationClient (WASAPIAudioIODeviceType* d)
: ComBaseClassHelper<IMMNotificationClient> (0), device (d) {}
HRESULT STDMETHODCALLTYPE OnDeviceAdded (LPCWSTR) { return notify(); }
HRESULT STDMETHODCALLTYPE OnDeviceRemoved (LPCWSTR) { return notify(); }
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) { return notify(); }
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) { return notify(); }
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); }
JUCE_COMRESULT OnDeviceAdded (LPCWSTR) { return notify(); }
JUCE_COMRESULT OnDeviceRemoved (LPCWSTR) { return notify(); }
JUCE_COMRESULT OnDeviceStateChanged(LPCWSTR, DWORD) { return notify(); }
JUCE_COMRESULT OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) { return notify(); }
JUCE_COMRESULT OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); }
private:
WeakReference<WASAPIAudioIODeviceType> device;


+ 54
- 39
libs/juce-current/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp View File

@@ -67,25 +67,25 @@ namespace AiffFileHelpers
Loop sustainLoop;
Loop releaseLoop;
void copyTo (StringPairArray& values) const
void copyTo (std::map<String, String>& values) const
{
values.set ("MidiUnityNote", String (baseNote));
values.set ("Detune", String (detune));
values.emplace ("MidiUnityNote", String (baseNote));
values.emplace ("Detune", String (detune));
values.set ("LowNote", String (lowNote));
values.set ("HighNote", String (highNote));
values.set ("LowVelocity", String (lowVelocity));
values.set ("HighVelocity", String (highVelocity));
values.emplace ("LowNote", String (lowNote));
values.emplace ("HighNote", String (highNote));
values.emplace ("LowVelocity", String (lowVelocity));
values.emplace ("HighVelocity", String (highVelocity));
values.set ("Gain", String ((int16) ByteOrder::swapIfLittleEndian ((uint16) gain)));
values.emplace ("Gain", String ((int16) ByteOrder::swapIfLittleEndian ((uint16) gain)));
values.set ("NumSampleLoops", String (2)); // always 2 with AIFF, WAV can have more
values.set ("Loop0Type", String (ByteOrder::swapIfLittleEndian (sustainLoop.type)));
values.set ("Loop0StartIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.startIdentifier)));
values.set ("Loop0EndIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.endIdentifier)));
values.set ("Loop1Type", String (ByteOrder::swapIfLittleEndian (releaseLoop.type)));
values.set ("Loop1StartIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.startIdentifier)));
values.set ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier)));
values.emplace ("NumSampleLoops", String (2)); // always 2 with AIFF, WAV can have more
values.emplace ("Loop0Type", String (ByteOrder::swapIfLittleEndian (sustainLoop.type)));
values.emplace ("Loop0StartIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.startIdentifier)));
values.emplace ("Loop0EndIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.endIdentifier)));
values.emplace ("Loop1Type", String (ByteOrder::swapIfLittleEndian (releaseLoop.type)));
values.emplace ("Loop1StartIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.startIdentifier)));
values.emplace ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier)));
}
static uint16 getValue16 (const StringPairArray& values, const char* name, const char* def)
@@ -149,7 +149,7 @@ namespace AiffFileHelpers
input.read (unknown, sizeof (unknown));
}
void addToMetadata (StringPairArray& metadata) const
void addToMetadata (std::map<String, String>& metadata) const
{
const bool rootNoteSet = rootNote != 0;
@@ -157,11 +157,11 @@ namespace AiffFileHelpers
setBoolFlag (metadata, AiffAudioFormat::appleRootSet, rootNoteSet);
if (rootNoteSet)
metadata.set (AiffAudioFormat::appleRootNote, String (rootNote));
metadata.emplace (AiffAudioFormat::appleRootNote, String (rootNote));
metadata.set (AiffAudioFormat::appleBeats, String (numBeats));
metadata.set (AiffAudioFormat::appleDenominator, String (timeSigDen));
metadata.set (AiffAudioFormat::appleNumerator, String (timeSigNum));
metadata.emplace (AiffAudioFormat::appleBeats, String (numBeats));
metadata.emplace (AiffAudioFormat::appleDenominator, String (timeSigDen));
metadata.emplace (AiffAudioFormat::appleNumerator, String (timeSigNum));
const char* keyString = nullptr;
@@ -175,12 +175,14 @@ namespace AiffFileHelpers
}
if (keyString != nullptr)
metadata.set (AiffAudioFormat::appleKey, keyString);
metadata.emplace (AiffAudioFormat::appleKey, keyString);
}
void setBoolFlag (StringPairArray& values, const char* name, bool shouldBeSet) const
void setBoolFlag (std::map<String, String>& values,
const char* name,
bool shouldBeSet) const
{
values.set (name, shouldBeSet ? "1" : "0");
values.emplace (name, shouldBeSet ? "1" : "0");
}
uint32 flags;
@@ -388,6 +390,17 @@ public:
{
using namespace AiffFileHelpers;
std::map<String, String> metadataValuesMap;
for (int i = 0; i != metadataValues.size(); ++i)
{
metadataValuesMap.emplace (metadataValues.getAllKeys().getReference (i),
metadataValues.getAllValues().getReference (i));
}
// If this fails, there were duplicate keys in the metadata
jassert ((size_t) metadataValuesMap.size() == (size_t) metadataValues.size());
if (input->readInt() == chunkName ("FORM"))
{
auto len = input->readIntBigEndian();
@@ -479,8 +492,8 @@ public:
auto numCues = (uint16) input->readShortBigEndian();
// these two are always the same for AIFF-read files
metadataValues.set ("NumCuePoints", String (numCues));
metadataValues.set ("NumCueLabels", String (numCues));
metadataValuesMap.emplace ("NumCuePoints", String (numCues));
metadataValuesMap.emplace ("NumCueLabels", String (numCues));
for (uint16 i = 0; i < numCues; ++i)
{
@@ -497,18 +510,18 @@ public:
input->readByte();
auto prefixCue = "Cue" + String (i);
metadataValues.set (prefixCue + "Identifier", String (identifier));
metadataValues.set (prefixCue + "Offset", String (offset));
metadataValuesMap.emplace (prefixCue + "Identifier", String (identifier));
metadataValuesMap.emplace (prefixCue + "Offset", String (offset));
auto prefixLabel = "CueLabel" + String (i);
metadataValues.set (prefixLabel + "Identifier", String (identifier));
metadataValues.set (prefixLabel + "Text", textBlock.toString());
metadataValuesMap.emplace (prefixLabel + "Identifier", String (identifier));
metadataValuesMap.emplace (prefixLabel + "Text", textBlock.toString());
}
}
else if (type == chunkName ("COMT"))
{
auto numNotes = (uint16) input->readShortBigEndian();
metadataValues.set ("NumCueNotes", String (numNotes));
metadataValuesMap.emplace ("NumCueNotes", String (numNotes));
for (uint16 i = 0; i < numNotes; ++i)
{
@@ -520,9 +533,9 @@ public:
input->readIntoMemoryBlock (textBlock, stringLength + (stringLength & 1));
auto prefix = "CueNote" + String (i);
metadataValues.set (prefix + "TimeStamp", String (timestamp));
metadataValues.set (prefix + "Identifier", String (identifier));
metadataValues.set (prefix + "Text", textBlock.toString());
metadataValuesMap.emplace (prefix + "TimeStamp", String (timestamp));
metadataValuesMap.emplace (prefix + "Identifier", String (identifier));
metadataValuesMap.emplace (prefix + "Text", textBlock.toString());
}
}
else if (type == chunkName ("INST"))
@@ -530,16 +543,16 @@ public:
HeapBlock<InstChunk> inst;
inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
input->read (inst, (int) length);
inst->copyTo (metadataValues);
inst->copyTo (metadataValuesMap);
}
else if (type == chunkName ("basc"))
{
AiffFileHelpers::BASCChunk (*input).addToMetadata (metadataValues);
AiffFileHelpers::BASCChunk (*input).addToMetadata (metadataValuesMap);
}
else if (type == chunkName ("cate"))
{
metadataValues.set (AiffAudioFormat::appleTag,
AiffFileHelpers::CATEChunk::read (*input, length));
metadataValuesMap.emplace (AiffAudioFormat::appleTag,
AiffFileHelpers::CATEChunk::read (*input, length));
}
else if ((hasGotVer && hasGotData && hasGotType)
|| chunkEnd < input->getPosition()
@@ -553,8 +566,10 @@ public:
}
}
if (metadataValues.size() > 0)
metadataValues.set ("MetaDataSource", "AIFF");
if (metadataValuesMap.size() > 0)
metadataValuesMap.emplace ("MetaDataSource", "AIFF");
metadataValues.addMap (metadataValuesMap);
}
//==============================================================================


+ 2
- 2
libs/juce-current/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp View File

@@ -250,7 +250,7 @@ public:
samplesInReservoir = 0;
}
else if (startSampleInFile < reservoirStart
|| startSampleInFile > reservoirStart + jmax (samplesInReservoir, 511))
|| startSampleInFile > reservoirStart + jmax (samplesInReservoir, (int64) 511))
{
// had some problems with flac crashing if the read pos is aligned more
// accurately than this. Probably fixed in newer versions of the library, though.
@@ -367,7 +367,7 @@ public:
private:
FlacNamespace::FLAC__StreamDecoder* decoder;
AudioBuffer<float> reservoir;
int reservoirStart = 0, samplesInReservoir = 0;
int64 reservoirStart = 0, samplesInReservoir = 0;
bool ok = false, scanningForLength = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacReader)


+ 17
- 10
libs/juce-current/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp View File

@@ -492,7 +492,9 @@ struct MP3Frame
return frequencies[sampleRateIndex];
}
void decodeHeader (const uint32 header)
enum class ParseSuccessful { no, yes };
ParseSuccessful decodeHeader (const uint32 header)
{
jassert (((header >> 10) & 3) != 3);
@@ -527,17 +529,18 @@ struct MP3Frame
jassertfalse; // This means the file is using "free format". Apparently very few decoders
// support this mode, and this one certainly doesn't handle it correctly!
frameSize = 0;
return ParseSuccessful::no;
}
else
switch (layer)
{
switch (layer)
{
case 1: frameSize = (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() + padding) * 4) - 4; break;
case 2: frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() + (padding - 4); break;
case 3: frameSize = (bitrateIndex == 0) ? 0 : ((frameSizes[lsf][2][bitrateIndex] * 144000) / (getFrequency() << lsf) + (padding - 4)); break;
default: break;
}
case 1: frameSize = (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() + padding) * 4) - 4; break;
case 2: frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() + (padding - 4); break;
case 3: frameSize = (bitrateIndex == 0) ? 0 : ((frameSizes[lsf][2][bitrateIndex] * 144000) / (getFrequency() << lsf) + (padding - 4)); break;
default: break;
}
return ParseSuccessful::yes;
}
int layer, frameSize, numChannels, single;
@@ -1430,7 +1433,11 @@ struct MP3Stream
lastFrameSize += nextFrameOffset;
}
frame.decodeHeader ((uint32) stream.readIntBigEndian());
const auto successful = frame.decodeHeader ((uint32) stream.readIntBigEndian());
if (successful == MP3Frame::ParseSuccessful::no)
return -1;
headerParsed = true;
frameSize = frame.frameSize;
isFreeFormat = (frameSize == 0);


+ 6
- 6
libs/juce-current/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp View File

@@ -160,13 +160,13 @@ public:
{
while (numSamples > 0)
{
auto numAvailable = (int) (reservoirStart + samplesInReservoir - startSampleInFile);
auto numAvailable = (reservoirStart + samplesInReservoir - startSampleInFile);
if (startSampleInFile >= reservoirStart && numAvailable > 0)
{
// got a few samples overlapping, so use them before seeking..
auto numToUse = jmin (numSamples, numAvailable);
auto numToUse = jmin ((int64) numSamples, numAvailable);
for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;)
if (destSamples[i] != nullptr)
@@ -175,8 +175,8 @@ public:
(size_t) numToUse * sizeof (float));
startSampleInFile += numToUse;
numSamples -= numToUse;
startOffsetInDestBuffer += numToUse;
numSamples -= (int) numToUse;
startOffsetInDestBuffer += (int) numToUse;
if (numSamples == 0)
break;
@@ -194,7 +194,7 @@ public:
int bitStream = 0;
int offset = 0;
int numToRead = samplesInReservoir;
int numToRead = (int) samplesInReservoir;
while (numToRead > 0)
{
@@ -261,7 +261,7 @@ private:
OggVorbisNamespace::OggVorbis_File ovFile;
OggVorbisNamespace::ov_callbacks callbacks;
AudioBuffer<float> reservoir;
int reservoirStart = 0, samplesInReservoir = 0;
int64 reservoirStart = 0, samplesInReservoir = 0;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader)
};


+ 4
- 2
libs/juce-current/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp View File

@@ -993,10 +993,12 @@ public:
input->skipNextBytes (2);
bitsPerSample = (unsigned int) (int) input->readShort();
if (bitsPerSample > 64)
if (bitsPerSample > 64 && (int) sampleRate != 0)
{
bytesPerFrame = bytesPerSec / (int) sampleRate;
bitsPerSample = 8 * (unsigned int) bytesPerFrame / numChannels;
if (numChannels != 0)
bitsPerSample = 8 * (unsigned int) bytesPerFrame / numChannels;
}
else
{


+ 2
- 2
libs/juce-current/source/modules/juce_audio_formats/juce_audio_formats.h View File

@@ -35,7 +35,7 @@
ID: juce_audio_formats
vendor: juce
version: 6.0.4
version: 6.0.7
name: JUCE audio file format codecs
description: Classes for reading and writing various audio file formats.
website: http://www.juce.com/juce
@@ -104,7 +104,7 @@
#define JUCE_USE_WINDOWS_MEDIA_FORMAT 1
#endif
#if ! JUCE_MSVC
#if ! JUCE_WINDOWS
#undef JUCE_USE_WINDOWS_MEDIA_FORMAT
#define JUCE_USE_WINDOWS_MEDIA_FORMAT 0
#endif


+ 27
- 9
libs/juce-current/source/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp View File

@@ -1085,24 +1085,28 @@ namespace AAXClasses
SetParameterNormalizedValue (paramID, (double) newValue);
}
void audioProcessorChanged (AudioProcessor* processor) override
void audioProcessorChanged (AudioProcessor* processor, const ChangeDetails& details) override
{
++mNumPlugInChanges;
auto numParameters = juceParameters.getNumParameters();
for (int i = 0; i < numParameters; ++i)
if (details.parameterInfoChanged)
{
if (auto* p = mParameterManager.GetParameterByID (getAAXParamIDFromJuceIndex (i)))
auto numParameters = juceParameters.getNumParameters();
for (int i = 0; i < numParameters; ++i)
{
auto newName = juceParameters.getParamForIndex (i)->getName (31);
if (auto* p = mParameterManager.GetParameterByID (getAAXParamIDFromJuceIndex (i)))
{
auto newName = juceParameters.getParamForIndex (i)->getName (31);
if (p->Name() != newName.toRawUTF8())
p->SetName (AAX_CString (newName.toRawUTF8()));
if (p->Name() != newName.toRawUTF8())
p->SetName (AAX_CString (newName.toRawUTF8()));
}
}
}
check (Controller()->SetSignalLatency (processor->getLatencySamples()));
if (details.latencyChanged)
check (Controller()->SetSignalLatency (processor->getLatencySamples()));
}
void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int parameterIndex) override
@@ -1124,6 +1128,20 @@ namespace AAXClasses
case AAX_eNotificationEvent_EnteringOfflineMode: pluginInstance->setNonRealtime (true); break;
case AAX_eNotificationEvent_ExitingOfflineMode: pluginInstance->setNonRealtime (false); break;
case AAX_eNotificationEvent_ASProcessingState:
{
if (data != nullptr && size == sizeof (AAX_EProcessingState))
{
const auto state = *static_cast<const AAX_EProcessingState*> (data);
const auto nonRealtime = state == AAX_eProcessingState_Start
|| state == AAX_eProcessingState_StartPass
|| state == AAX_eProcessingState_BeginPassGroup;
pluginInstance->setNonRealtime (nonRealtime);
}
break;
}
case AAX_eNotificationEvent_TrackNameChanged:
if (data != nullptr)
{


+ 26
- 13
libs/juce-current/source/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm View File

@@ -22,6 +22,8 @@
==============================================================================
*/
#include <juce_core/system/juce_TargetPlatform.h>
#include <juce_core/system/juce_CompilerWarnings.h>
#include "../utility/juce_CheckSettingMacros.h"
@@ -45,7 +47,8 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wshorten-64-to-32",
"-Wzero-as-null-pointer-constant",
"-Wnullable-to-nonnull-conversion",
"-Wgnu-zero-variadic-macro-arguments",
"-Wformat-pedantic")
"-Wformat-pedantic",
"-Wdeprecated-anon-enum-enum-conversion")
#include "../utility/juce_IncludeSystemHeaders.h"
@@ -352,7 +355,7 @@ public:
}
}
err = (busNr == (requestedNumBus - 1) ? noErr : kAudioUnitErr_FormatNotSupported);
err = (busNr == (requestedNumBus - 1) ? (OSStatus) noErr : (OSStatus) kAudioUnitErr_FormatNotSupported);
}
// was there an error?
@@ -1163,16 +1166,24 @@ public:
sendAUEvent (kAudioUnitEvent_EndParameterChangeGesture, index);
}
void audioProcessorChanged (AudioProcessor*) override
void audioProcessorChanged (AudioProcessor*, const ChangeDetails& details) override
{
PropertyChanged (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0);
PropertyChanged (kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0);
PropertyChanged (kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, 0);
PropertyChanged (kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0);
if (details.latencyChanged)
PropertyChanged (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0);
if (details.parameterInfoChanged)
{
PropertyChanged (kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0);
PropertyChanged (kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, 0);
}
refreshCurrentPreset();
PropertyChanged (kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0);
PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
if (details.programChanged)
{
refreshCurrentPreset();
PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
}
}
//==============================================================================
@@ -1900,7 +1911,7 @@ private:
return (scope != kAudioUnitScope_Input
&& scope != kAudioUnitScope_Output)
? kAudioUnitErr_InvalidScope : noErr;
? (OSStatus) kAudioUnitErr_InvalidScope : (OSStatus) noErr;
}
OSStatus elementToBusIdx (AudioUnitScope scope, AudioUnitElement element, bool& isInput, int& busIdx) noexcept
@@ -2040,10 +2051,12 @@ private:
addSupportedLayoutTags();
for (int i = 0; i < enabledInputs; ++i)
if ((err = syncAudioUnitWithChannelSet (true, i, juceFilter->getChannelLayoutOfBus (true, i))) != noErr) return err;
if ((err = syncAudioUnitWithChannelSet (true, i, juceFilter->getChannelLayoutOfBus (true, i))) != noErr)
return err;
for (int i = 0; i < enabledOutputs; ++i)
if ((err = syncAudioUnitWithChannelSet (false, i, juceFilter->getChannelLayoutOfBus (false, i))) != noErr) return err;
if ((err = syncAudioUnitWithChannelSet (false, i, juceFilter->getChannelLayoutOfBus (false, i))) != noErr)
return err;
return noErr;
}
@@ -2166,7 +2179,7 @@ private:
#endif
// add discrete layout tags
int n = bus->getMaxSupportedChannels(maxChannelsToProbeFor());
int n = bus->getMaxSupportedChannels (maxChannelsToProbeFor());
for (int ch = 0; ch < n; ++ch)
{


+ 75
- 63
libs/juce-current/source/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm View File

@@ -73,6 +73,8 @@
#define JUCE_AUDIOUNIT_OBJC_NAME(x) JUCE_JOIN_MACRO (x, AUv3)
#include <future>
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullability-completeness")
using namespace juce;
@@ -912,7 +914,7 @@ public:
#endif
//==============================================================================
void audioProcessorChanged (AudioProcessor* processor) override
void audioProcessorChanged (AudioProcessor* processor, const ChangeDetails&) override
{
ignoreUnused (processor);
@@ -1750,7 +1752,7 @@ public:
{
JUCE_ASSERT_MESSAGE_THREAD
if (processorHolder != nullptr)
if (processorHolder.get() != nullptr)
JuceAudioUnitv3::removeEditor (getAudioProcessor());
}
@@ -1795,22 +1797,25 @@ public:
void viewDidLayoutSubviews()
{
if (processorHolder != nullptr && [myself view] != nullptr)
if (auto holder = processorHolder.get())
{
if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor())
if ([myself view] != nullptr)
{
if (processorHolder->viewConfiguration != nullptr)
editor->hostMIDIControllerIsAvailable (processorHolder->viewConfiguration->hostHasMIDIController);
if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor())
{
if (holder->viewConfiguration != nullptr)
editor->hostMIDIControllerIsAvailable (holder->viewConfiguration->hostHasMIDIController);
editor->setBounds (convertToRectInt ([[myself view] bounds]));
editor->setBounds (convertToRectInt ([[myself view] bounds]));
if (JUCE_IOS_MAC_VIEW* peerView = [[[myself view] subviews] objectAtIndex: 0])
{
#if JUCE_IOS
[peerView setNeedsDisplay];
#else
[peerView setNeedsDisplay: YES];
#endif
if (JUCE_IOS_MAC_VIEW* peerView = [[[myself view] subviews] objectAtIndex: 0])
{
#if JUCE_IOS
[peerView setNeedsDisplay];
#else
[peerView setNeedsDisplay: YES];
#endif
}
}
}
}
@@ -1818,21 +1823,21 @@ public:
void didReceiveMemoryWarning()
{
if (processorHolder != nullptr)
if (auto* processor = processorHolder->get())
if (auto ptr = processorHolder.get())
if (auto* processor = ptr->get())
processor->memoryWarningReceived();
}
void viewDidAppear (bool)
{
if (processorHolder != nullptr)
if (processorHolder.get() != nullptr)
if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor())
editor->setVisible (true);
}
void viewDidDisappear (bool)
{
if (processorHolder != nullptr)
if (processorHolder.get() != nullptr)
if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor())
editor->setVisible (false);
}
@@ -1846,65 +1851,72 @@ public:
//==============================================================================
AUAudioUnit* createAudioUnit (const AudioComponentDescription& descr, NSError** error)
{
AUAudioUnit* retval = nil;
const auto holder = [&]
{
if (auto initialisedHolder = processorHolder.get())
return initialisedHolder;
if (! MessageManager::getInstance()->isThisTheMessageThread())
waitForExecutionOnMainThread ([this] { [myself view]; });
return processorHolder.get();
}();
if (holder == nullptr)
return nullptr;
return (new JuceAudioUnitv3 (holder, descr, 0, error))->getAudioUnit();
}
private:
template <typename Callback>
static void waitForExecutionOnMainThread (Callback&& callback)
{
if (MessageManager::getInstance()->isThisTheMessageThread())
{
WaitableEvent creationEvent;
callback();
return;
}
// AUv3 headers say that we may block this thread and that the message thread is guaranteed
// to be unblocked
struct AUCreator : public CallbackMessage
{
JuceAUViewController& owner;
AudioComponentDescription pDescr;
NSError** pError;
AUAudioUnit*& outAU;
WaitableEvent& e;
AUCreator (JuceAUViewController& parent, const AudioComponentDescription& paramDescr, NSError** paramError,
AUAudioUnit*& outputAU, WaitableEvent& event)
: owner (parent), pDescr (paramDescr), pError (paramError), outAU (outputAU), e (event)
{}
void messageCallback() override
{
outAU = owner.createAudioUnitOnMessageThread (pDescr, pError);
e.signal();
}
};
std::promise<void> promise;
MessageManager::callAsync ([&]
{
callback();
promise.set_value();
});
(new AUCreator (*this, descr, error, retval, creationEvent))->post();
creationEvent.wait (-1);
promise.get_future().get();
}
// There's a chance that createAudioUnit will be called from a background
// thread while the processorHolder is being updated on the main thread.
class LockedProcessorHolder
{
public:
AudioProcessorHolder::Ptr get() const
{
const ScopedLock lock (mutex);
return holder;
}
else
LockedProcessorHolder& operator= (const AudioProcessorHolder::Ptr& other)
{
retval = createAudioUnitOnMessageThread (descr, error);
const ScopedLock lock (mutex);
holder = other;
return *this;
}
return [retval autorelease];
}
private:
mutable CriticalSection mutex;
AudioProcessorHolder::Ptr holder;
};
private:
//==============================================================================
AUViewController<AUAudioUnitFactory>* myself;
AudioProcessorHolder::Ptr processorHolder = nullptr;
LockedProcessorHolder processorHolder;
Rectangle<int> preferredSize { 1, 1 };
//==============================================================================
AUAudioUnit* createAudioUnitOnMessageThread (const AudioComponentDescription& descr, NSError** error)
{
JUCE_ASSERT_MESSAGE_THREAD
[myself view]; // this will call [view load] and ensure that the AudioProcessor has been instantiated
if (processorHolder == nullptr)
return nullptr;
return (new JuceAudioUnitv3 (processorHolder, descr, 0, error))->getAudioUnit();
}
AudioProcessor& getAudioProcessor() const noexcept { return **processorHolder; }
AudioProcessor& getAudioProcessor() const noexcept { return **processorHolder.get(); }
};
//==============================================================================


+ 2
- 2
libs/juce-current/source/modules/juce_audio_plugin_client/LV2/juce_LV2_Wrapper.cpp View File

@@ -577,9 +577,9 @@ public:
}
}

void audioProcessorChanged (AudioProcessor*)
void audioProcessorChanged (AudioProcessor*, const ChangeDetails& details)
{
if (filter != nullptr && programsHost != nullptr)
if (details.programChanged && filter != nullptr && programsHost != nullptr)
{
if (filter->getNumPrograms() != lastProgramCount)
{


+ 2
- 0
libs/juce-current/source/modules/juce_audio_plugin_client/LV2/juce_LV2_Wrapper_Exporter.cpp View File

@@ -364,6 +364,7 @@ static const String makePresetsFile (AudioProcessor* const filter)
// Header
text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n";
text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
text += "@prefix owl: <http://www.w3.org/2002/07/owl#> .\n";
text += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n";
text += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
text += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
@@ -385,6 +386,7 @@ static const String makePresetsFile (AudioProcessor* const filter)
text += " rdfs:domain state:State ;\n";
text += " rdfs:range xsd:base64Binary .\n";
#endif
text += "\n";
#endif

// Presets


+ 1
- 1
libs/juce-current/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp View File

@@ -790,7 +790,7 @@ public:
ReleaseControl (index + 2);
}
void audioProcessorChanged (AudioProcessor*) override
void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override
{
// xxx is there an RTAS equivalent?
}


+ 1
- 1
libs/juce-current/source/modules/juce_audio_plugin_client/Unity/juce_UnityPluginInterface.h View File

@@ -131,7 +131,7 @@ struct UnityAudioEffectState
UnityAudioAmbisonicData* ambisonicData;
template<typename T>
template <typename T>
inline T* getEffectData() const
{
jassert (effectData != nullptr);


+ 2
- 1
libs/juce-current/source/modules/juce_audio_plugin_client/Unity/juce_Unity_Wrapper.cpp View File

@@ -23,9 +23,10 @@
==============================================================================
*/
#include <juce_core/system/juce_TargetPlatform.h>
#if JucePlugin_Build_Unity
#include <juce_core/system/juce_TargetPlatform.h>
#include "../utility/juce_IncludeModuleHeaders.h"
#include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp>


+ 144
- 136
libs/juce-current/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp View File

@@ -205,9 +205,9 @@ struct SharedMessageThread : public Thread
initialiseJuce_GUI();
MessageManager::getInstance()->setCurrentThreadAsMessageThread();
initialised = true;
XWindowSystem::getInstance();
initialised = true;
while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil (250))
{}
@@ -246,7 +246,6 @@ struct AbletonLiveHostSpecific
class JuceVSTWrapper : public AudioProcessorListener,
public AudioPlayHead,
private Timer,
private AsyncUpdater,
private AudioProcessorParameter::Listener
{
private:
@@ -791,19 +790,9 @@ public:
void parameterGestureChanged (int, bool) override {}
void audioProcessorChanged (AudioProcessor*) override
void audioProcessorChanged (AudioProcessor*, const ChangeDetails& details) override
{
vstEffect.latency = processor->getLatencySamples();
triggerAsyncUpdate();
}
void handleAsyncUpdate() override
{
if (hostCallback != nullptr)
{
hostCallback (&vstEffect, Vst2::hostOpcodeUpdateView, 0, 0, nullptr, 0);
hostCallback (&vstEffect, Vst2::hostOpcodeIOModified, 0, 0, nullptr, 0);
}
hostChangeUpdater.update (details);
}
bool getPinProperties (Vst2::VstPinInfo& properties, bool direction, int index) const
@@ -1031,21 +1020,17 @@ public:
: wrapper (w)
{
editor.setOpaque (true);
editor.setVisible (true);
setOpaque (true);
setTopLeftPosition (editor.getPosition());
editor.setTopLeftPosition (0, 0);
auto b = getLocalArea (&editor, editor.getLocalBounds());
setSize (b.getWidth(), b.getHeight());
addAndMakeVisible (editor);
auto editorBounds = getSizeToContainChild();
setSize (editorBounds.getWidth(), editorBounds.getHeight());
#if JUCE_WINDOWS
if (! getHostType().isReceptor())
addMouseListener (this, true);
#endif
setOpaque (true);
ignoreUnused (fakeMouseGenerator);
}
@@ -1055,42 +1040,35 @@ public:
// have been transferred to another parent which takes over ownership.
}
void paint (Graphics&) override {}
void paint (Graphics& g) override
{
g.fillAll (Colours::black);
}
void getEditorBounds (Vst2::VstEditorBounds& bounds)
{
auto b = getSizeToContainChild();
bounds = convertToHostBounds ({ 0, 0, (int16) b.getHeight(), (int16) b.getWidth() });
auto editorBounds = getSizeToContainChild();
bounds = convertToHostBounds ({ 0, 0, (int16) editorBounds.getHeight(), (int16) editorBounds.getWidth() });
}
void attachToHost (VstOpCodeArguments args)
{
setOpaque (true);
setVisible (false);
#if JUCE_WINDOWS
#if JUCE_WINDOWS || JUCE_LINUX
addToDesktop (0, args.ptr);
hostWindow = (HWND) args.ptr;
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
hostWindow = (HostWindowType) args.ptr;
#if JUCE_LINUX
X11Symbols::getInstance()->xReparentWindow (display,
(Window) getWindowHandle(),
(HostWindowType) hostWindow,
0, 0);
#elif JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
checkHostWindowScaleFactor();
startTimer (500);
#endif
#elif JUCE_LINUX
addToDesktop (0, args.ptr);
hostWindow = (Window) args.ptr;
X11Symbols::getInstance()->xReparentWindow (display, (Window) getWindowHandle(), hostWindow, 0, 0);
if (auto* peer = getPeer())
{
auto screenBounds = peer->localToGlobal (peer->getBounds());
auto scale = Desktop::getInstance().getDisplays().findDisplayForRect (screenBounds, false).scale
/ Desktop::getInstance().getGlobalScaleFactor();
setContentScaleFactor ((float) scale);
}
#else
#elif JUCE_MAC
hostWindow = attachComponentToWindowRefVST (this, args.ptr, wrapper.useNSView);
#endif
@@ -1101,15 +1079,10 @@ public:
{
#if JUCE_MAC
if (hostWindow != nullptr)
{
detachComponentFromWindowRefVST (this, hostWindow, wrapper.useNSView);
hostWindow = nullptr;
}
#endif
#if JUCE_LINUX
hostWindow = {};
#endif
}
void checkVisibility()
@@ -1127,23 +1100,21 @@ public:
void resized() override
{
auto newBounds = getLocalBounds();
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
if (! lastBounds.isEmpty() && isWithin (newBounds.toDouble().getAspectRatio(), lastBounds.toDouble().getAspectRatio(), 0.1))
return;
lastBounds = newBounds;
#endif
if (auto* ed = getEditorComp())
if (auto* pluginEditor = getEditorComp())
{
ed->setTopLeftPosition (0, 0);
if (! resizingParent)
{
auto newBounds = getLocalBounds();
{
const ScopedValueSetter<bool> resizingChildSetter (resizingChild, true);
pluginEditor->setBounds (pluginEditor->getLocalArea (this, newBounds).withPosition (0, 0));
}
if (shouldResizeEditor)
ed->setBounds (ed->getLocalArea (this, newBounds));
lastBounds = newBounds;
}
updateWindowSize (false);
updateWindowSize();
}
#if JUCE_MAC && ! JUCE_64BIT
@@ -1154,59 +1125,63 @@ public:
void parentSizeChanged() override
{
updateWindowSize (true);
updateWindowSize();
}
void childBoundsChanged (Component*) override
{
updateWindowSize (false);
if (resizingChild)
return;
auto newBounds = getSizeToContainChild();
if (newBounds != lastBounds)
{
updateWindowSize();
lastBounds = newBounds;
}
}
juce::Rectangle<int> getSizeToContainChild()
{
if (auto* ed = getEditorComp())
return getLocalArea (ed, ed->getLocalBounds());
if (auto* pluginEditor = getEditorComp())
return getLocalArea (pluginEditor, pluginEditor->getLocalBounds());
return {};
}
void updateWindowSize (bool resizeEditor)
void updateWindowSize()
{
if (! isInSizeWindow)
if (! resizingParent
&& getEditorComp() != nullptr
&& hostWindow != HostWindowType{})
{
if (auto* ed = getEditorComp())
{
ed->setTopLeftPosition (0, 0);
auto pos = getSizeToContainChild();
auto editorBounds = getSizeToContainChild();
#if JUCE_MAC
if (wrapper.useNSView)
setTopLeftPosition (0, getHeight() - pos.getHeight());
#endif
resizeHostWindow (pos.getWidth(), pos.getHeight());
#if ! JUCE_LINUX // setSize() on linux causes renoise and energyxt to fail.
if (! resizeEditor) // this is needed to prevent an infinite resizing loop due to coordinate rounding
shouldResizeEditor = false;
#if JUCE_MAC
if (wrapper.useNSView)
setTopLeftPosition (0, getHeight() - editorBounds.getHeight());
#endif
setSize (pos.getWidth(), pos.getHeight());
resizeHostWindow (editorBounds.getWidth(), editorBounds.getHeight());
shouldResizeEditor = true;
#else
ignoreUnused (resizeEditor);
{
const ScopedValueSetter<bool> resizingParentSetter (resizingParent, true);
auto scale = Desktop::getInstance().getGlobalScaleFactor();
#if JUCE_LINUX // setSize() on linux causes renoise and energyxt to fail.
auto rect = convertToHostBounds ({ 0, 0, (int16) editorBounds.getHeight(), (int16) editorBounds.getWidth() });
X11Symbols::getInstance()->xResizeWindow (display, (Window) getWindowHandle(),
static_cast<unsigned int> (roundToInt ((float) pos.getWidth() * scale)),
static_cast<unsigned int> (roundToInt ((float) pos.getHeight() * scale)));
#endif
#if JUCE_MAC
resizeHostWindow (pos.getWidth(), pos.getHeight()); // (doing this a second time seems to be necessary in tracktion)
static_cast<unsigned int> (rect.rightmost - rect.leftmost),
static_cast<unsigned int> (rect.lower - rect.upper));
#else
setSize (editorBounds.getWidth(), editorBounds.getHeight());
#endif
}
#if JUCE_MAC
resizeHostWindow (editorBounds.getWidth(), editorBounds.getHeight()); // (doing this a second time seems to be necessary in tracktion)
#endif
}
}
@@ -1224,23 +1199,23 @@ public:
if (status == (pointer_sized_int) 1 || getHostType().isAbletonLive())
{
const ScopedValueSetter<bool> inSizeWindowSetter (isInSizeWindow, true);
const ScopedValueSetter<bool> resizingParentSetter (resizingParent, true);
sizeWasSuccessful = (host (wrapper.getAEffect(), Vst2::hostOpcodeWindowSize,
newWidth, newHeight, nullptr, 0) != 0);
}
}
// some hosts don't support the sizeWindow call, so do it manually..
if (! sizeWasSuccessful)
{
// some hosts don't support the sizeWindow call, so do it manually..
const ScopedValueSetter<bool> resizingParentSetter (resizingParent, true);
#if JUCE_MAC
setNativeHostWindowSizeVST (hostWindow, this, newWidth, newHeight, wrapper.useNSView);
#elif JUCE_LINUX
// (Currently, all linux hosts support sizeWindow, so this should never need to happen)
setSize (newWidth, newHeight);
#else
int dw = 0;
int dh = 0;
@@ -1286,12 +1261,6 @@ public:
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
#endif
}
if (auto* peer = getPeer())
{
peer->handleMovedOrResized();
repaint();
}
}
void setContentScaleFactor (float scale)
@@ -1300,27 +1269,22 @@ public:
{
editorScaleFactor = scale;
if (auto* ed = getEditorComp())
ed->setScaleFactor (editorScaleFactor);
updateWindowSize (true);
}
}
if (auto* pluginEditor = getEditorComp())
{
auto prevEditorBounds = pluginEditor->getLocalArea (this, lastBounds);
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
void checkHostWindowScaleFactor()
{
auto hostWindowScale = (float) getScaleFactorForWindow (hostWindow);
{
const ScopedValueSetter<bool> resizingChildSetter (resizingChild, true);
if (hostWindowScale > 0.0f && ! approximatelyEqual (hostWindowScale, editorScaleFactor))
wrapper.handleSetContentScaleFactor (hostWindowScale);
}
pluginEditor->setScaleFactor (editorScaleFactor);
pluginEditor->setBounds (prevEditorBounds.withPosition (0, 0));
}
void timerCallback() override
{
checkHostWindowScaleFactor();
lastBounds = getSizeToContainChild();
updateWindowSize();
}
}
}
#endif
#if JUCE_WINDOWS
void mouseDown (const MouseEvent&) override
@@ -1336,6 +1300,21 @@ public:
if (HWND parent = findMDIParentOf ((HWND) getWindowHandle()))
SetWindowPos (parent, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
void checkHostWindowScaleFactor()
{
auto hostWindowScale = (float) getScaleFactorForWindow ((HostWindowType) hostWindow);
if (hostWindowScale > 0.0f && ! approximatelyEqual (hostWindowScale, editorScaleFactor))
wrapper.handleSetContentScaleFactor (hostWindowScale);
}
void timerCallback() override
{
checkHostWindowScaleFactor();
}
#endif
#endif
#if JUCE_MAC
@@ -1347,6 +1326,7 @@ public:
}
#endif
private:
//==============================================================================
static Vst2::VstEditorBounds convertToHostBounds (const Vst2::VstEditorBounds& rect)
{
@@ -1364,29 +1344,55 @@ public:
//==============================================================================
JuceVSTWrapper& wrapper;
FakeMouseMoveGenerator fakeMouseGenerator;
bool isInSizeWindow = false;
bool shouldResizeEditor = true;
bool resizingChild = false, resizingParent = false;
float editorScaleFactor = 1.0f;
juce::Rectangle<int> lastBounds;
#if JUCE_MAC
void* hostWindow = nullptr;
#elif JUCE_LINUX
#if JUCE_LINUX
using HostWindowType = ::Window;
::Display* display = XWindowSystem::getInstance()->getDisplay();
Window hostWindow = {};
#elif JUCE_WINDOWS
HWND hostWindow = {};
using HostWindowType = HWND;
WindowsHooks hooks;
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
juce::Rectangle<int> lastBounds;
#endif
#else
using HostWindowType = void*;
#endif
HostWindowType hostWindow = {};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorCompWrapper)
};
//==============================================================================
private:
struct HostChangeUpdater : private AsyncUpdater
{
explicit HostChangeUpdater (JuceVSTWrapper& o) : owner (o) {}
~HostChangeUpdater() override { cancelPendingUpdate(); }
void update (const ChangeDetails& details)
{
if (details.latencyChanged)
owner.vstEffect.latency = owner.processor->getLatencySamples();
if (details.parameterInfoChanged || details.programChanged)
triggerAsyncUpdate();
}
private:
void handleAsyncUpdate() override
{
if (auto* callback = owner.hostCallback)
{
callback (&owner.vstEffect, Vst2::hostOpcodeUpdateView, 0, 0, nullptr, 0);
callback (&owner.vstEffect, Vst2::hostOpcodeIOModified, 0, 0, nullptr, 0);
}
}
JuceVSTWrapper& owner;
};
static JuceVSTWrapper* getWrapper (Vst2::VstEffectInterface* v) noexcept { return static_cast<JuceVSTWrapper*> (v->effectPointer); }
bool isProcessLevelOffline()
@@ -1620,9 +1626,9 @@ private:
if (editorComp != nullptr)
{
editorComp->getEditorBounds (editorBounds);
*((Vst2::VstEditorBounds**) args.ptr) = &editorBounds;
return (pointer_sized_int) &editorBounds;
editorComp->getEditorBounds (editorRect);
*((Vst2::VstEditorBounds**) args.ptr) = &editorRect;
return (pointer_sized_int) &editorRect;
}
return 0;
@@ -2081,7 +2087,7 @@ private:
juce::MemoryBlock chunkMemory;
uint32 chunkMemoryTime = 0;
std::unique_ptr<EditorCompWrapper> editorComp;
Vst2::VstEditorBounds editorBounds;
Vst2::VstEditorBounds editorRect;
MidiBuffer midiEvents;
VSTMidiEventList outgoingEvents;
@@ -2110,6 +2116,8 @@ private:
ThreadLocalValue<bool> inParameterChangedCallback;
HostChangeUpdater hostChangeUpdater { *this };
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVSTWrapper)
};


+ 216
- 145
libs/juce-current/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp View File

@@ -228,9 +228,26 @@ public:
return getParamForVSTParamID (bypassParamID);
}
AudioProcessorParameter* getProgramParameter() const noexcept
{
return getParamForVSTParamID (JuceAudioProcessor::paramPreset);
}
static Vst::UnitID getUnitID (const AudioProcessorParameterGroup* group)
{
return group == nullptr ? Vst::kRootUnitId : group->getID().hashCode();
if (group == nullptr || group->getParent() == nullptr)
return Vst::kRootUnitId;
// From the VST3 docs (also applicable to unit IDs!):
// Up to 2^31 parameters can be exported with id range [0, 2147483648]
// (the range [2147483649, 429496729] is reserved for host application).
auto unitID = group->getID().hashCode() & 0x7fffffff;
// If you hit this assertion then your group ID is hashing to a value
// reserved by the VST3 SDK. Please use a different group ID.
jassert (unitID != Vst::kRootUnitId);
return unitID;
}
int getNumParameters() const noexcept { return vstParamIDs.size(); }
@@ -260,6 +277,21 @@ private:
{
parameterGroups = audioProcessor->getParameterTree().getSubgroups (true);
#if JUCE_DEBUG
auto allGroups = parameterGroups;
allGroups.add (&audioProcessor->getParameterTree());
std::unordered_set<Vst::UnitID> unitIDs;
for (auto* group : allGroups)
{
auto insertResult = unitIDs.insert (getUnitID (group));
// If you hit this assertion then either a group ID is not unique or
// you are very unlucky and a hashed group ID is not unique
jassert (insertResult.second);
}
#endif
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS
const bool forceLegacyParamIDs = true;
#else
@@ -306,6 +338,20 @@ private:
vstParamIDs.add (vstParamID);
paramMap.set (static_cast<int32> (vstParamID), juceParam);
}
auto numPrograms = audioProcessor->getNumPrograms();
if (numPrograms > 1)
{
ownedProgramParameter = std::make_unique<AudioParameterInt> ("juceProgramParameter", "Program",
0, numPrograms - 1,
audioProcessor->getCurrentProgram());
juceParameters.params.add (ownedProgramParameter.get());
vstParamIDs.add (JuceAudioProcessor::paramPreset);
paramMap.set (static_cast<int32> (JuceAudioProcessor::paramPreset), ownedProgramParameter.get());
}
}
Vst::ParamID generateVSTParamIDForParam (AudioProcessorParameter* param)
@@ -333,7 +379,7 @@ private:
//==============================================================================
LegacyAudioParametersWrapper juceParameters;
HashMap<int32, AudioProcessorParameter*> paramMap;
std::unique_ptr<AudioProcessorParameter> ownedBypassParameter;
std::unique_ptr<AudioProcessorParameter> ownedBypassParameter, ownedProgramParameter;
Array<const AudioProcessorParameterGroup*> parameterGroups;
JuceAudioProcessor() = delete;
@@ -349,8 +395,7 @@ class JuceVST3EditController : public Vst::EditController,
public Vst::IMidiMapping,
public Vst::IUnitInfo,
public Vst::ChannelContext::IInfoListener,
public AudioProcessorListener,
private AudioProcessorParameter::Listener
public AudioProcessorListener
{
public:
JuceVST3EditController (Vst::IHostApplication* host)
@@ -564,16 +609,20 @@ public:
bool setNormalized (Vst::ParamValue v) override
{
Vst::ParamValue program = v * info.stepCount;
if (! isPositiveAndBelow ((int) program, owner.getNumPrograms()))
return false;
auto programValue = roundToInt (toPlain (v));
if (valueNormalized != v)
if (isPositiveAndBelow (programValue, owner.getNumPrograms()))
{
valueNormalized = v;
changed();
return true;
if (programValue != owner.getCurrentProgram())
owner.setCurrentProgram (programValue);
if (valueNormalized != v)
{
valueNormalized = v;
changed();
return true;
}
}
return false;
@@ -653,17 +702,21 @@ public:
//==============================================================================
tresult PLUGIN_API setComponentState (IBStream* stream) override
{
// Cubase and Nuendo need to inform the host of the current parameter values
if (auto* pluginInstance = getPluginInstance())
{
for (auto vstParamId : audioProcessor->vstParamIDs)
setParamNormalized (vstParamId, audioProcessor->getParamForVSTParamID (vstParamId)->getValue());
{
auto paramValue = [&]
{
if (vstParamId == JuceAudioProcessor::paramPreset)
return EditController::plainParamToNormalized (JuceAudioProcessor::paramPreset,
pluginInstance->getCurrentProgram());
auto numPrograms = pluginInstance->getNumPrograms();
return (double) audioProcessor->getParamForVSTParamID (vstParamId)->getValue();
}();
if (numPrograms > 1)
setParamNormalized (JuceAudioProcessor::paramPreset, static_cast<Vst::ParamValue> (pluginInstance->getCurrentProgram())
/ static_cast<Vst::ParamValue> (numPrograms - 1));
setParamNormalized (vstParamId, paramValue);
}
}
if (auto* handler = getComponentHandler())
@@ -882,7 +935,7 @@ public:
}
//==============================================================================
void paramChanged (Vst::ParamID vstParamId, float newValue)
void paramChanged (Vst::ParamID vstParamId, double newValue)
{
if (inParameterChangedCallback.get())
{
@@ -891,8 +944,8 @@ public:
}
// NB: Cubase has problems if performEdit is called without setParamNormalized
EditController::setParamNormalized (vstParamId, (double) newValue);
performEdit (vstParamId, (double) newValue);
EditController::setParamNormalized (vstParamId, newValue);
performEdit (vstParamId, newValue);
}
//==============================================================================
@@ -904,39 +957,43 @@ public:
paramChanged (audioProcessor->getVSTParamIDForIndex (index), newValue);
}
void audioProcessorChanged (AudioProcessor*) override
void audioProcessorChanged (AudioProcessor*, const ChangeDetails& details) override
{
int32 flags = 0;
for (int32 i = 0; i < parameters.getParameterCount(); ++i)
if (auto* param = dynamic_cast<Param*> (parameters.getParameterByIndex (i)))
if (param->updateParameterInfo() && (flags & Vst::kParamTitlesChanged) == 0)
flags |= Vst::kParamTitlesChanged;
if (details.parameterInfoChanged)
{
for (int32 i = 0; i < parameters.getParameterCount(); ++i)
if (auto* param = dynamic_cast<Param*> (parameters.getParameterByIndex (i)))
if (param->updateParameterInfo() && (flags & Vst::kParamTitlesChanged) == 0)
flags |= Vst::kParamTitlesChanged;
}
if (auto* pluginInstance = getPluginInstance())
{
auto newNumPrograms = pluginInstance->getNumPrograms();
if (newNumPrograms != lastNumPrograms)
if (details.programChanged && audioProcessor->getProgramParameter() != nullptr)
{
if (newNumPrograms > 1)
auto currentProgram = pluginInstance->getCurrentProgram();
auto paramValue = roundToInt (EditController::normalizedParamToPlain (JuceAudioProcessor::paramPreset,
EditController::getParamNormalized (JuceAudioProcessor::paramPreset)));
if (currentProgram != paramValue)
{
auto paramValue = static_cast<Vst::ParamValue> (pluginInstance->getCurrentProgram())
/ static_cast<Vst::ParamValue> (pluginInstance->getNumPrograms() - 1);
beginEdit (JuceAudioProcessor::paramPreset);
paramChanged (JuceAudioProcessor::paramPreset,
EditController::plainParamToNormalized (JuceAudioProcessor::paramPreset, currentProgram));
endEdit (JuceAudioProcessor::paramPreset);
EditController::setParamNormalized (JuceAudioProcessor::paramPreset, paramValue);
flags |= Vst::kParamValuesChanged;
}
lastNumPrograms = newNumPrograms;
}
auto newLatencySamples = pluginInstance->getLatencySamples();
auto latencySamples = pluginInstance->getLatencySamples();
if (newLatencySamples != lastLatencySamples)
if (details.latencyChanged && latencySamples != lastLatencySamples)
{
flags |= Vst::kLatencyChanged;
lastLatencySamples = newLatencySamples;
lastLatencySamples = latencySamples;
}
}
@@ -944,19 +1001,6 @@ public:
componentHandler->restartComponent (flags);
}
void parameterValueChanged (int, float newValue) override
{
// this can only come from the bypass parameter
paramChanged (audioProcessor->bypassParamID, newValue);
}
void parameterGestureChanged (int, bool gestureIsStarting) override
{
// this can only come from the bypass parameter
if (gestureIsStarting) beginEdit (audioProcessor->bypassParamID);
else endEdit (audioProcessor->bypassParamID);
}
//==============================================================================
AudioProcessor* getPluginInstance() const noexcept
{
@@ -971,7 +1015,7 @@ private:
friend struct Param;
//==============================================================================
ComSmartPtr<JuceAudioProcessor> audioProcessor;
VSTComSmartPtr<JuceAudioProcessor> audioProcessor;
struct MidiController
{
@@ -983,11 +1027,42 @@ private:
MidiController parameterToMidiController[(int) numMIDIChannels * (int) Vst::kCountCtrlNumber];
Vst::ParamID midiControllerToParameter[numMIDIChannels][Vst::kCountCtrlNumber];
//==============================================================================
struct OwnedParameterListener : public AudioProcessorParameter::Listener
{
OwnedParameterListener (JuceVST3EditController& editController,
AudioProcessorParameter& juceParameter,
Vst::ParamID paramID)
: owner (editController),
vstParamID (paramID)
{
juceParameter.addListener (this);
}
void parameterValueChanged (int, float newValue) override
{
owner.paramChanged (vstParamID, newValue);
}
void parameterGestureChanged (int, bool gestureIsStarting) override
{
if (gestureIsStarting)
owner.beginEdit (vstParamID);
else
owner.endEdit (vstParamID);
}
JuceVST3EditController& owner;
Vst::ParamID vstParamID;
};
std::vector<std::unique_ptr<OwnedParameterListener>> ownedParameterListeners;
//==============================================================================
std::atomic<bool> vst3IsPlaying { false },
inSetupProcessing { false };
int lastNumPrograms = 0, lastLatencySamples = 0;
int lastLatencySamples = 0;
#if ! JUCE_MAC
float lastScaleFactorReceived = 1.0f;
@@ -999,10 +1074,11 @@ private:
{
pluginInstance->addListener (this);
// as the bypass is not part of the regular parameters
// we need to listen for it explicitly
// as the bypass is not part of the regular parameters we need to listen for it explicitly
if (! audioProcessor->bypassIsRegularParameter)
audioProcessor->getBypassParameter()->addListener (this);
ownedParameterListeners.push_back (std::make_unique<OwnedParameterListener> (*this,
*audioProcessor->getBypassParameter(),
audioProcessor->bypassParamID));
if (parameters.getParameterCount() <= 0)
{
@@ -1011,6 +1087,10 @@ private:
for (int i = 0; i < n; ++i)
{
auto vstParamID = audioProcessor->getVSTParamIDForIndex (i);
if (vstParamID == JuceAudioProcessor::paramPreset)
continue;
auto* juceParam = audioProcessor->getParamForVSTParamID (vstParamID);
auto* parameterGroup = pluginInstance->getParameterTree().getGroupsForParameter (juceParam).getLast();
auto unitID = JuceAudioProcessor::getUnitID (parameterGroup);
@@ -1019,8 +1099,14 @@ private:
(vstParamID == audioProcessor->bypassParamID)));
}
if (pluginInstance->getNumPrograms() > 1)
if (auto* programParam = audioProcessor->getProgramParameter())
{
ownedParameterListeners.push_back (std::make_unique<OwnedParameterListener> (*this,
*programParam,
JuceAudioProcessor::paramPreset));
parameters.addParameter (new ProgramChangeParameter (*pluginInstance));
}
}
#if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS
@@ -1030,7 +1116,7 @@ private:
initialiseMidiControllerMappings();
#endif
audioProcessorChanged (pluginInstance);
audioProcessorChanged (pluginInstance, ChangeDetails().withParameterInfoChanged (true));
}
}
@@ -1138,8 +1224,8 @@ private:
createContentWrapperComponentIfNeeded();
#if JUCE_WINDOWS || JUCE_LINUX
component->addToDesktop (0, parent);
component->setOpaque (true);
component->addToDesktop (0, (void*) systemWindow);
component->setVisible (true);
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
@@ -1206,10 +1292,7 @@ private:
if (component != nullptr)
{
auto w = rect.getWidth();
auto h = rect.getHeight();
component->setSize (w, h);
component->setSize (rect.getWidth(), rect.getHeight());
#if JUCE_MAC
if (cubase10Workaround != nullptr)
@@ -1233,6 +1316,11 @@ private:
tresult PLUGIN_API getSize (ViewRect* size) override
{
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
if (getHostType().isAbletonLive() && systemWindow == nullptr)
return kResultFalse;
#endif
if (size != nullptr && component != nullptr)
{
auto editorBounds = component->getSizeToContainChild();
@@ -1264,20 +1352,19 @@ private:
{
*rectToCheck = convertFromHostBounds (*rectToCheck);
auto transformScale = std::sqrt (std::abs (editor->getTransform().getDeterminant()));
auto editorBounds = editor->getLocalArea (component.get(),
Rectangle<int>::leftTopRightBottom (rectToCheck->left, rectToCheck->top,
rectToCheck->right, rectToCheck->bottom).toFloat());
auto minW = (double) ((float) constrainer->getMinimumWidth() * transformScale);
auto maxW = (double) ((float) constrainer->getMaximumWidth() * transformScale);
auto minH = (double) ((float) constrainer->getMinimumHeight() * transformScale);
auto maxH = (double) ((float) constrainer->getMaximumHeight() * transformScale);
auto minW = (float) constrainer->getMinimumWidth();
auto maxW = (float) constrainer->getMaximumWidth();
auto minH = (float) constrainer->getMinimumHeight();
auto maxH = (float) constrainer->getMaximumHeight();
auto width = (double) (rectToCheck->right - rectToCheck->left);
auto height = (double) (rectToCheck->bottom - rectToCheck->top);
auto width = jlimit (minW, maxW, editorBounds.getWidth());
auto height = jlimit (minH, maxH, editorBounds.getHeight());
width = jlimit (minW, maxW, width);
height = jlimit (minH, maxH, height);
auto aspectRatio = constrainer->getFixedAspectRatio();
auto aspectRatio = (float) constrainer->getFixedAspectRatio();
if (aspectRatio != 0.0)
{
@@ -1285,9 +1372,11 @@ private:
if (getHostType().type == PluginHostType::SteinbergCubase9)
{
if (editor->getWidth() == width && editor->getHeight() != height)
auto currentEditorBounds = editor->getBounds().toFloat();
if (currentEditorBounds.getWidth() == width && currentEditorBounds.getHeight() != height)
adjustWidth = true;
else if (editor->getHeight() == height && editor->getWidth() != width)
else if (currentEditorBounds.getHeight() == height && currentEditorBounds.getWidth() != width)
adjustWidth = false;
}
@@ -1313,8 +1402,11 @@ private:
}
}
rectToCheck->right = rectToCheck->left + roundToInt (width);
rectToCheck->bottom = rectToCheck->top + roundToInt (height);
auto constrainedRect = component->getLocalArea (editor, Rectangle<float> (width, height))
.getSmallestIntegerContainer();
rectToCheck->right = rectToCheck->left + roundToInt (constrainedRect.getWidth());
rectToCheck->bottom = rectToCheck->top + roundToInt (constrainedRect.getHeight());
*rectToCheck = convertToHostBounds (*rectToCheck);
}
@@ -1349,16 +1441,7 @@ private:
owner->lastScaleFactorReceived = editorScaleFactor;
if (component != nullptr)
{
if (auto* editor = component->pluginEditor.get())
{
editor->setScaleFactor (editorScaleFactor);
component->resizeHostWindow();
component->setTopLeftPosition (0, 0);
component->repaint();
}
}
component->setEditorScaleFactor (editorScaleFactor);
}
return kResultTrue;
@@ -1470,14 +1553,18 @@ private:
if (resizingChild)
return;
auto b = getSizeToContainChild();
auto newBounds = getSizeToContainChild();
if (lastBounds != b)
if (newBounds != lastBounds)
{
lastBounds = b;
const ScopedValueSetter<bool> resizingParentSetter (resizingParent, true);
resizeHostWindow();
#if JUCE_LINUX
if (getHostType().isBitwigStudio())
repaint();
#endif
lastBounds = newBounds;
}
}
@@ -1489,33 +1576,12 @@ private:
{
auto newBounds = getLocalBounds();
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
if (! lastBounds.isEmpty() && isWithin (newBounds.toDouble().getAspectRatio(), lastBounds.toDouble().getAspectRatio(), 0.1))
return;
#endif
lastBounds = newBounds;
const ScopedValueSetter<bool> resizingChildSetter (resizingChild, true);
if (auto* constrainer = pluginEditor->getConstrainer())
{
auto aspectRatio = constrainer->getFixedAspectRatio();
if (aspectRatio != 0)
{
auto width = (double) lastBounds.getWidth();
auto height = (double) lastBounds.getHeight();
if (width / height > aspectRatio)
setBounds ({ 0, 0, roundToInt (height * aspectRatio), lastBounds.getHeight() });
else
setBounds ({ 0, 0, lastBounds.getWidth(), roundToInt (width / aspectRatio) });
}
const ScopedValueSetter<bool> resizingChildSetter (resizingChild, true);
pluginEditor->setBounds (pluginEditor->getLocalArea (this, newBounds).withPosition (0, 0));
}
pluginEditor->setTopLeftPosition (0, 0);
pluginEditor->setBounds (pluginEditor->getLocalArea (this, getLocalBounds()));
lastBounds = newBounds;
}
}
}
@@ -1533,31 +1599,45 @@ private:
{
if (pluginEditor != nullptr)
{
auto b = getSizeToContainChild();
auto w = b.getWidth();
auto h = b.getHeight();
auto host = getHostType();
#if JUCE_WINDOWS
setSize (w, h);
#endif
if (owner.plugFrame != nullptr)
{
auto newSize = convertToHostBounds ({ 0, 0, b.getWidth(), b.getHeight() });
auto editorBounds = getSizeToContainChild();
auto newSize = convertToHostBounds ({ 0, 0, editorBounds.getWidth(), editorBounds.getHeight() });
{
const ScopedValueSetter<bool> resizingParentSetter (resizingParent, true);
owner.plugFrame->resizeView (&owner, &newSize);
}
auto host = getHostType();
#if JUCE_MAC
if (host.isWavelab() || host.isReaper())
#else
if (host.isWavelab() || host.isAbletonLive() || host.isBitwigStudio())
#endif
setBounds (0, 0, w, h);
setBounds (editorBounds.withPosition (0, 0));
}
}
}
void setEditorScaleFactor (float scale)
{
if (pluginEditor != nullptr)
{
auto prevEditorBounds = pluginEditor->getLocalArea (this, lastBounds);
{
const ScopedValueSetter<bool> resizingChildSetter (resizingChild, true);
pluginEditor->setScaleFactor (scale);
pluginEditor->setBounds (prevEditorBounds.withPosition (0, 0));
}
lastBounds = getSizeToContainChild();
resizeHostWindow();
repaint();
}
}
@@ -1599,7 +1679,7 @@ private:
//==============================================================================
ScopedJuceInitialiser_GUI libraryInitialiser;
ComSmartPtr<JuceVST3EditController> owner;
VSTComSmartPtr<JuceVST3EditController> owner;
AudioProcessor& pluginInstance;
std::unique_ptr<ContentWrapperComponent> component;
@@ -2555,20 +2635,11 @@ public:
{
auto vstParamID = paramQueue->getParameterId();
if (vstParamID == JuceAudioProcessor::paramPreset)
{
auto numPrograms = pluginInstance->getNumPrograms();
auto programValue = roundToInt (value * (jmax (0, numPrograms - 1)));
if (numPrograms > 1 && isPositiveAndBelow (programValue, numPrograms)
&& programValue != pluginInstance->getCurrentProgram())
pluginInstance->setCurrentProgram (programValue);
}
#if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS
else if (juceVST3EditController != nullptr && juceVST3EditController->isMidiControllerParamID (vstParamID))
if (juceVST3EditController != nullptr && juceVST3EditController->isMidiControllerParamID (vstParamID))
addParameterChangeToMidiBuffer (offsetSamples, vstParamID, value);
#endif
else
#endif
{
auto floatValue = static_cast<float> (value);
@@ -2910,9 +2981,9 @@ private:
std::atomic<int> refCount { 1 };
AudioProcessor* pluginInstance;
ComSmartPtr<Vst::IHostApplication> host;
ComSmartPtr<JuceAudioProcessor> comPluginInstance;
ComSmartPtr<JuceVST3EditController> juceVST3EditController;
VSTComSmartPtr<Vst::IHostApplication> host;
VSTComSmartPtr<JuceAudioProcessor> comPluginInstance;
VSTComSmartPtr<JuceVST3EditController> juceVST3EditController;
/**
Since VST3 does not provide a way of knowing the buffer size and sample rate at any point,
@@ -3246,7 +3317,7 @@ private:
//==============================================================================
std::atomic<int> refCount { 1 };
const PFactoryInfo factoryInfo;
ComSmartPtr<Vst::IHostApplication> host;
VSTComSmartPtr<Vst::IHostApplication> host;
//==============================================================================
struct ClassEntry
@@ -3268,7 +3339,7 @@ private:
std::vector<std::unique_ptr<ClassEntry>> classes;
//==============================================================================
template<class PClassInfoType>
template <class PClassInfoType>
tresult PLUGIN_API getPClassInfo (Steinberg::int32 index, PClassInfoType* info)
{
if (info != nullptr)


+ 1
- 1
libs/juce-current/source/modules/juce_audio_plugin_client/juce_audio_plugin_client.h View File

@@ -35,7 +35,7 @@
ID: juce_audio_plugin_client
vendor: juce
version: 6.0.4
version: 6.0.7
name: JUCE audio plugin wrapper classes
description: Classes for building VST, VST3, AudioUnit, AAX and RTAS plugins.
website: http://www.juce.com/juce


+ 5
- 1
libs/juce-current/source/modules/juce_audio_plugin_client/juce_audio_plugin_client_AU_2.mm View File

@@ -23,6 +23,8 @@
==============================================================================
*/
#include <juce_core/system/juce_TargetPlatform.h>
#if JucePlugin_Build_AU
#include <juce_core/system/juce_CompilerWarnings.h>
@@ -44,7 +46,9 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wparentheses",
"-Wnullable-to-nonnull-conversion",
"-Wignored-qualifiers",
"-Wfour-char-constants",
"-Wmissing-prototypes")
"-Wmissing-prototypes",
"-Wdeprecated-anon-enum-enum-conversion",
"-Wambiguous-reversed-operator")
// From MacOS 10.13 and iOS 11 Apple has (sensibly!) stopped defining a whole
// set of functions with rather generic names. However, we still need a couple


+ 2
- 0
libs/juce-current/source/modules/juce_audio_plugin_client/juce_audio_plugin_client_Standalone.cpp View File

@@ -23,6 +23,8 @@
==============================================================================
*/
#include <juce_core/system/juce_TargetPlatform.h>
#if JucePlugin_Build_Standalone
#if ! JUCE_MODULE_AVAILABLE_juce_audio_utils


+ 26
- 19
libs/juce-current/source/modules/juce_audio_processors/format_types/juce_AU_Shared.h View File

@@ -353,11 +353,22 @@ struct AudioUnitHelpers
auto defaultInputs = processor.getChannelCountOfBus (true, 0);
auto defaultOutputs = processor.getChannelCountOfBus (false, 0);
SortedSet<int> supportedChannels;
struct Channels
{
SInt16 ins, outs;
std::pair<SInt16, SInt16> makePair() const noexcept { return std::make_pair (ins, outs); }
bool operator< (const Channels& other) const noexcept { return makePair() < other.makePair(); }
bool operator== (const Channels& other) const noexcept { return makePair() == other.makePair(); }
};
SortedSet<Channels> supportedChannels;
// add the current configuration
if (defaultInputs != 0 || defaultOutputs != 0)
supportedChannels.add ((defaultInputs << 16) | defaultOutputs);
supportedChannels.add ({ static_cast<SInt16> (defaultInputs),
static_cast<SInt16> (defaultOutputs) });
for (auto inChanNum = hasMainInputBus ? 1 : 0; inChanNum <= (hasMainInputBus ? maxNumChanToCheckFor : 0); ++inChanNum)
{
@@ -375,19 +386,16 @@ struct AudioUnitHelpers
if (! isNumberOfChannelsSupported (outBus, outChanNum, outLayout))
continue;
supportedChannels.add (((hasMainInputBus ? outLayout.getMainInputChannels() : 0) << 16)
| (hasMainOutputBus ? outLayout.getMainOutputChannels() : 0));
supportedChannels.add ({ static_cast<SInt16> (hasMainInputBus ? outLayout.getMainInputChannels() : 0),
static_cast<SInt16> (hasMainOutputBus ? outLayout.getMainOutputChannels() : 0) });
}
}
auto hasInOutMismatch = false;
for (auto supported : supportedChannels)
for (const auto& supported : supportedChannels)
{
auto numInputs = (supported >> 16) & 0xffff;
auto numOutputs = (supported >> 0) & 0xffff;
if (numInputs != numOutputs)
if (supported.ins != supported.outs)
{
hasInOutMismatch = true;
break;
@@ -398,9 +406,10 @@ struct AudioUnitHelpers
for (auto inChanNum = hasMainInputBus ? 1 : 0; inChanNum <= (hasMainInputBus ? maxNumChanToCheckFor : 0); ++inChanNum)
{
auto channelConfiguration = (inChanNum << 16) | (hasInOutMismatch ? defaultOutputs : inChanNum);
Channels channelConfiguration { static_cast<SInt16> (inChanNum),
static_cast<SInt16> (hasInOutMismatch ? defaultOutputs : inChanNum) };
if (! supportedChannels.contains (channelConfiguration))
if (supportedChannels.contains (channelConfiguration))
{
hasUnsupportedInput = true;
break;
@@ -409,25 +418,23 @@ struct AudioUnitHelpers
for (auto outChanNum = hasMainOutputBus ? 1 : 0; outChanNum <= (hasMainOutputBus ? maxNumChanToCheckFor : 0); ++outChanNum)
{
auto channelConfiguration = ((hasInOutMismatch ? defaultInputs : outChanNum) << 16) | outChanNum;
Channels channelConfiguration { static_cast<SInt16> (hasInOutMismatch ? defaultInputs : outChanNum),
static_cast<SInt16> (outChanNum) };
if (! supportedChannels.contains (channelConfiguration))
if (supportedChannels.contains (channelConfiguration))
{
hasUnsupportedOutput = true;
break;
}
}
for (auto supported : supportedChannels)
for (const auto& supported : supportedChannels)
{
auto numInputs = (supported >> 16) & 0xffff;
auto numOutputs = (supported >> 0) & 0xffff;
AUChannelInfo info;
// see here: https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html
info.inChannels = static_cast<SInt16> (hasMainInputBus ? (hasUnsupportedInput ? numInputs : (hasInOutMismatch && (! hasUnsupportedOutput) ? -2 : -1)) : 0);
info.outChannels = static_cast<SInt16> (hasMainOutputBus ? (hasUnsupportedOutput ? numOutputs : (hasInOutMismatch && (! hasUnsupportedInput) ? -2 : -1)) : 0);
info.inChannels = static_cast<SInt16> (hasMainInputBus ? (hasUnsupportedInput ? supported.ins : (hasInOutMismatch && (! hasUnsupportedOutput) ? -2 : -1)) : 0);
info.outChannels = static_cast<SInt16> (hasMainOutputBus ? (hasUnsupportedOutput ? supported.outs : (hasInOutMismatch && (! hasUnsupportedInput) ? -2 : -1)) : 0);
if (info.inChannels == -2 && info.outChannels == -2)
info.inChannels = -1;


+ 9
- 4
libs/juce-current/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm View File

@@ -25,6 +25,8 @@
#if JUCE_PLUGINHOST_AU && (JUCE_MAC || JUCE_IOS)
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
#if JUCE_MAC
#include <AudioUnit/AUCocoaUIView.h>
#include <CoreAudioKit/AUGenericView.h>
@@ -247,7 +249,8 @@ namespace AudioUnitFormatHelpers
if (Handle h = Get1IndResource (thngType, i))
{
HLock (h);
const uint32* const types = (const uint32*) *h;
uint32 types[3];
std::memcpy (types, *h, sizeof (types));
if (types[0] == kAudioUnitType_MusicDevice
|| types[0] == kAudioUnitType_MusicEffect
@@ -759,7 +762,7 @@ public:
zerostruct (stream); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
stream.mSampleRate = sampleRate;
stream.mFormatID = kAudioFormatLinearPCM;
stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagsNativeEndian;
stream.mFormatFlags = (int) kAudioFormatFlagsNativeFloatPacked | (int) kAudioFormatFlagIsNonInterleaved | (int) kAudioFormatFlagsNativeEndian;
stream.mFramesPerPacket = 1;
stream.mBytesPerPacket = 4;
stream.mBytesPerFrame = 4;
@@ -1823,12 +1826,12 @@ private:
default:
if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_ParameterList)
{
updateHostDisplay();
updateHostDisplay (AudioProcessorListener::ChangeDetails().withParameterInfoChanged (true));
}
else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_PresentPreset)
{
sendAllParametersChangedEvents();
updateHostDisplay();
updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (true));
}
else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_Latency)
{
@@ -2912,4 +2915,6 @@ FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch()
} // namespace juce
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#endif

+ 1
- 1
libs/juce-current/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp View File

@@ -374,7 +374,7 @@ public:
destData.setSize ((size_t) numParameters * sizeof (float));
destData.fillWith (0);
auto* p = (float*) ((char*) destData.getData());
auto* p = unalignedPointerCast<float*> (destData.getData());
for (int i = 0; i < numParameters; ++i)
if (auto* param = getParameters()[i])


+ 15
- 18
libs/juce-current/source/modules/juce_audio_processors/format_types/juce_VST3Common.h View File

@@ -68,12 +68,14 @@ static bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexc
#endif
//==============================================================================
inline juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (string); }
inline juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); }
inline juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (juce::CharPointer_UTF8 ((juce::CharPointer_UTF8::CharType*) string)); }
inline juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); }
// NB: The casts are handled by a Steinberg::UString operator
inline juce::String toString (const Steinberg::UString128& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); }
inline juce::String toString (const Steinberg::UString256& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); }
inline juce::String toString (const Steinberg::UString128& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); }
inline juce::String toString (const Steinberg::UString256& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); }
inline Steinberg::Vst::TChar* toString (const juce::String& source) noexcept { return reinterpret_cast<Steinberg::Vst::TChar*> (source.toUTF16().getAddress()); }
inline void toString128 (Steinberg::Vst::String128 result, const char* source)
{
@@ -82,12 +84,7 @@ inline void toString128 (Steinberg::Vst::String128 result, const char* source)
inline void toString128 (Steinberg::Vst::String128 result, const juce::String& source)
{
Steinberg::UString (result, 128).fromAscii (source.toUTF8());
}
inline Steinberg::Vst::TChar* toString (const juce::String& source) noexcept
{
return reinterpret_cast<Steinberg::Vst::TChar*> (source.toUTF16().getAddress());
Steinberg::UString (result, 128).assign (toString (source));
}
#if JUCE_WINDOWS
@@ -355,24 +352,24 @@ static AudioChannelSet getChannelSetForSpeakerArrangement (Steinberg::Vst::Speak
//==============================================================================
template <class ObjectType>
class ComSmartPtr
class VSTComSmartPtr
{
public:
ComSmartPtr() noexcept : source (nullptr) {}
ComSmartPtr (ObjectType* object, bool autoAddRef = true) noexcept : source (object) { if (source != nullptr && autoAddRef) source->addRef(); }
ComSmartPtr (const ComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); }
~ComSmartPtr() { if (source != nullptr) source->release(); }
VSTComSmartPtr() noexcept : source (nullptr) {}
VSTComSmartPtr (ObjectType* object, bool autoAddRef = true) noexcept : source (object) { if (source != nullptr && autoAddRef) source->addRef(); }
VSTComSmartPtr (const VSTComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); }
~VSTComSmartPtr() { if (source != nullptr) source->release(); }
operator ObjectType*() const noexcept { return source; }
ObjectType* get() const noexcept { return source; }
ObjectType& operator*() const noexcept { return *source; }
ObjectType* operator->() const noexcept { return source; }
ComSmartPtr& operator= (const ComSmartPtr& other) { return operator= (other.source); }
VSTComSmartPtr& operator= (const VSTComSmartPtr& other) { return operator= (other.source); }
ComSmartPtr& operator= (ObjectType* const newObjectToTakePossessionOf)
VSTComSmartPtr& operator= (ObjectType* const newObjectToTakePossessionOf)
{
ComSmartPtr p (newObjectToTakePossessionOf);
VSTComSmartPtr p (newObjectToTakePossessionOf);
std::swap (p.source, source);
return *this;
}


+ 107
- 175
libs/juce-current/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -31,10 +31,6 @@
namespace juce
{
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
extern void setThreadDPIAwarenessForWindow (HWND);
#endif
using namespace Steinberg;
//==============================================================================
@@ -370,7 +366,7 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
tresult PLUGIN_API popup (Steinberg::UCoord x, Steinberg::UCoord y) override;
#if ! JUCE_MODAL_LOOPS_PERMITTED
static void menuFinished (int modalResult, ComSmartPtr<ContextMenu> menu) { menu->handleResult (modalResult); }
static void menuFinished (int modalResult, VSTComSmartPtr<ContextMenu> menu) { menu->handleResult (modalResult); }
#endif
private:
@@ -382,7 +378,7 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
struct ItemAndTarget
{
Item item;
ComSmartPtr<IContextMenuTarget> target;
VSTComSmartPtr<IContextMenuTarget> target;
};
Array<ItemAndTarget> items;
@@ -446,7 +442,7 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
if (doUIDsMatch (cid, Vst::IMessage::iid) && doUIDsMatch (iid, Vst::IMessage::iid))
{
ComSmartPtr<Message> m (new Message (attributeList));
VSTComSmartPtr<Message> m (new Message (attributeList));
messageQueue.add (m);
m->addRef();
*obj = m;
@@ -454,7 +450,7 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
}
else if (doUIDsMatch (cid, Vst::IAttributeList::iid) && doUIDsMatch (iid, Vst::IAttributeList::iid))
{
ComSmartPtr<AttributeList> l (new AttributeList (this));
VSTComSmartPtr<AttributeList> l (new AttributeList (this));
l->addRef();
*obj = l;
return kResultOk;
@@ -541,14 +537,14 @@ private:
var value;
private:
ComSmartPtr<Vst::IAttributeList> attributeList;
VSTComSmartPtr<Vst::IAttributeList> attributeList;
String messageId;
Atomic<int> refCount;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message)
};
Array<ComSmartPtr<Message>, CriticalSection> messageQueue;
Array<VSTComSmartPtr<Message>, CriticalSection> messageQueue;
//==============================================================================
struct AttributeList : public Vst::IAttributeList
@@ -664,7 +660,7 @@ private:
}
}
owner->messageQueue.add (ComSmartPtr<Message> (new Message (this, id, value)));
owner->messageQueue.add (VSTComSmartPtr<Message> (new Message (this, id, value)));
}
template <typename Type>
@@ -687,7 +683,7 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AttributeList)
};
ComSmartPtr<AttributeList> attributeList;
VSTComSmartPtr<AttributeList> attributeList;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3HostContext)
};
@@ -731,8 +727,8 @@ struct DescriptionFactory
std::unique_ptr<PClassInfoW> infoW;
{
ComSmartPtr<IPluginFactory2> pf2;
ComSmartPtr<IPluginFactory3> pf3;
VSTComSmartPtr<IPluginFactory2> pf2;
VSTComSmartPtr<IPluginFactory3> pf3;
if (pf2.loadFrom (factory))
{
@@ -752,7 +748,7 @@ struct DescriptionFactory
PluginDescription desc;
{
ComSmartPtr<Vst::IComponent> component;
VSTComSmartPtr<Vst::IComponent> component;
if (component.loadFrom (factory, info.cid))
{
@@ -790,8 +786,8 @@ struct DescriptionFactory
virtual Result performOnDescription (PluginDescription&) = 0;
private:
ComSmartPtr<VST3HostContext> vst3HostContext;
ComSmartPtr<IPluginFactory> factory;
VSTComSmartPtr<VST3HostContext> vst3HostContext;
VSTComSmartPtr<IPluginFactory> factory;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DescriptionFactory)
};
@@ -849,7 +845,7 @@ struct DLLHandle
//==============================================================================
/** The factory should begin with a refCount of 1, so don't increment the reference count
(ie: don't use a ComSmartPtr in here)! Its lifetime will be handled by this DLLHandle.
(ie: don't use a VSTComSmartPtr in here)! Its lifetime will be handled by this DLLHandle.
*/
IPluginFactory* JUCE_CALLTYPE getPluginFactory()
{
@@ -1102,8 +1098,8 @@ private:
//==============================================================================
bool open (const PluginDescription& description)
{
ComSmartPtr<IPluginFactory> pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (file.getFullPathName())
.getPluginFactory());
VSTComSmartPtr<IPluginFactory> pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (file.getFullPathName())
.getPluginFactory());
if (pluginFactory != nullptr)
{
@@ -1140,9 +1136,7 @@ private:
//==============================================================================
struct VST3PluginWindow : public AudioProcessorEditor,
public ComponentMovementWatcher,
#if JUCE_WINDOWS || JUCE_LINUX
public ComponentPeer::ScaleFactorListener,
#endif
public IPlugFrame
{
VST3PluginWindow (AudioProcessor* owner, IPlugView* pluginView)
@@ -1155,26 +1149,20 @@ struct VST3PluginWindow : public AudioProcessorEditor,
setVisible (true);
warnOnFailure (view->setFrame (this));
#if ! JUCE_MAC
view->queryInterface (Steinberg::IPlugViewContentScaleSupport::iid, (void**) &scaleInterface);
#endif
resizeToFit();
}
~VST3PluginWindow() override
{
#if ! JUCE_MAC
if (scaleInterface != nullptr)
scaleInterface->release();
removeScaleFactorListeners();
removeScaleFactorListener();
#if JUCE_LINUX
embeddedComponent.removeClient();
#endif
#endif
warnOnFailure (view->removed());
warnOnFailure (view->setFrame (nullptr));
@@ -1183,6 +1171,8 @@ struct VST3PluginWindow : public AudioProcessorEditor,
#if JUCE_MAC
embeddedComponent.setView (nullptr);
#elif JUCE_WINDOWS
embeddedComponent.setHWND (nullptr);
#endif
view = nullptr;
@@ -1323,79 +1313,50 @@ struct VST3PluginWindow : public AudioProcessorEditor,
//==============================================================================
void componentPeerChanged() override
{
#if ! JUCE_MAC
removeScaleFactorListeners();
removeScaleFactorListener();
currentPeer = getTopLevelComponent()->getPeer();
if (auto* topPeer = getTopLevelComponent()->getPeer())
topPeer->addScaleFactorListener (this);
#endif
if (currentPeer != nullptr)
{
currentPeer->addScaleFactorListener (this);
nativeScaleFactor = (float) currentPeer->getPlatformScaleFactor();
}
}
void componentMovedOrResized (bool, bool wasResized) override
{
if (recursiveResize)
if (recursiveResize || ! wasResized || getTopLevelComponent()->getPeer() == nullptr)
return;
auto* topComp = getTopLevelComponent();
ViewRect rect;
if (topComp->getPeer() != nullptr)
if (view->canResize() == kResultTrue)
{
#if JUCE_WINDOWS
auto pos = (topComp->getLocalPoint (this, Point<int>()) * nativeScaleFactor).roundToInt();
#endif
const ScopedValueSetter<bool> recursiveResizeSetter (recursiveResize, true);
rect.right = (Steinberg::int32) roundToInt ((float) getWidth() * nativeScaleFactor);
rect.bottom = (Steinberg::int32) roundToInt ((float) getHeight() * nativeScaleFactor);
ViewRect rect;
view->checkSizeConstraint (&rect);
if (wasResized && view->canResize() == kResultTrue)
{
rect.right = (Steinberg::int32) roundToInt ((float) getWidth() * nativeScaleFactor);
rect.bottom = (Steinberg::int32) roundToInt ((float) getHeight() * nativeScaleFactor);
view->checkSizeConstraint (&rect);
auto w = roundToInt ((float) rect.getWidth() / nativeScaleFactor);
auto h = roundToInt ((float) rect.getHeight() / nativeScaleFactor);
setSize (w, h);
const ScopedValueSetter<bool> recursiveResizeSetter (recursiveResize, true);
#if JUCE_WINDOWS
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
setThreadDPIAwarenessForWindow (pluginHandle);
#endif
SetWindowPos (pluginHandle, 0,
pos.x, pos.y, rect.getWidth(), rect.getHeight(),
isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW);
#else
embeddedComponent.setBounds (getLocalBounds());
#endif
view->onSize (&rect);
setSize (roundToInt ((float) rect.getWidth() / nativeScaleFactor),
roundToInt ((float) rect.getHeight() / nativeScaleFactor));
}
else
{
warnOnFailure (view->getSize (&rect));
#if JUCE_WINDOWS
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
setThreadDPIAwarenessForWindow (pluginHandle);
#endif
SetWindowPos (pluginHandle, 0,
pos.x, pos.y, rect.getWidth(), rect.getHeight(),
isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW);
#else
embeddedComponent.setBounds (0, 0, (int) rect.getWidth(), (int) rect.getHeight());
#endif
}
embeddedComponent.setBounds (getLocalBounds());
// Some plugins don't update their cursor correctly when mousing out the window
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
view->onSize (&rect);
}
else
{
warnOnFailure (view->getSize (&rect));
resizeWithRect (embeddedComponent, rect, nativeScaleFactor);
}
}
// Some plugins don't update their cursor correctly when mousing out the window
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
}
using ComponentMovementWatcher::componentMovedOrResized;
void componentVisibilityChanged() override
@@ -1407,13 +1368,11 @@ struct VST3PluginWindow : public AudioProcessorEditor,
componentMovedOrResized (true, true);
}
using ComponentMovementWatcher::componentVisibilityChanged;
#if JUCE_WINDOWS || JUCE_LINUX
void nativeScaleFactorChanged (double newScaleFactor) override
{
if (pluginHandle == 0 || approximatelyEqual ((float) newScaleFactor, nativeScaleFactor))
if (pluginHandle == HandleFormat{} || approximatelyEqual ((float) newScaleFactor, nativeScaleFactor))
return;
nativeScaleFactor = (float) newScaleFactor;
@@ -1421,7 +1380,6 @@ struct VST3PluginWindow : public AudioProcessorEditor,
if (scaleInterface != nullptr)
scaleInterface->setContentScaleFactor ((Steinberg::IPlugViewContentScaleSupport::ScaleFactor) nativeScaleFactor);
}
#endif
void resizeToFit()
{
@@ -1458,73 +1416,48 @@ private:
void attachPluginWindow()
{
#if JUCE_MAC
if (pluginHandle == nil)
#else
if (pluginHandle == 0)
#endif
if (pluginHandle == HandleFormat{})
{
#if JUCE_WINDOWS
if (auto* topComp = getTopLevelComponent())
{
peer.reset (embeddedComponent.createNewPeer (0, topComp->getWindowHandle()));
pluginHandle = (HandleFormat) peer->getNativeHandle();
nativeScaleFactor = (float) peer->getPlatformScaleFactor();
}
#else
embeddedComponent.setBounds (getLocalBounds());
addAndMakeVisible (embeddedComponent);
#if JUCE_MAC
pluginHandle = (HandleFormat) embeddedComponent.getView();
jassert (pluginHandle != nil);
#elif JUCE_LINUX
pluginHandle = (HandleFormat) embeddedComponent.getHostWindowID();
jassert (pluginHandle != 0);
#endif
#endif
#if JUCE_MAC
if (pluginHandle != nil)
#else
if (pluginHandle != 0)
pluginHandle = (HandleFormat) embeddedComponent.getView();
#elif JUCE_WINDOWS
pluginHandle = (HandleFormat) embeddedComponent.getHWND();
#elif JUCE_LINUX
pluginHandle = (HandleFormat) embeddedComponent.getHostWindowID();
#endif
warnOnFailure (view->attached ((void*) pluginHandle, defaultVST3WindowType));
}
#if ! JUCE_MAC
if (auto* topPeer = getTopLevelComponent()->getPeer())
{
nativeScaleFactor = 1.0f; // force update
nativeScaleFactorChanged ((float) topPeer->getPlatformScaleFactor());
if (pluginHandle == HandleFormat{})
{
jassertfalse;
return;
}
warnOnFailure (view->attached ((void*) pluginHandle, defaultVST3WindowType));
if (scaleInterface != nullptr)
scaleInterface->setContentScaleFactor ((Steinberg::IPlugViewContentScaleSupport::ScaleFactor) nativeScaleFactor);
}
#endif
}
#if ! JUCE_MAC
void removeScaleFactorListeners()
void removeScaleFactorListener()
{
if (currentPeer == nullptr)
return;
for (int i = 0; i < ComponentPeer::getNumPeers(); ++i)
if (auto* p = ComponentPeer::getPeer (i))
p->removeScaleFactorListener (this);
if (ComponentPeer::getPeer (i) == currentPeer)
currentPeer->removeScaleFactorListener (this);
}
#endif
//==============================================================================
Atomic<int> refCount { 1 };
ComSmartPtr<IPlugView> view;
VSTComSmartPtr<IPlugView> view;
#if JUCE_WINDOWS
struct ChildComponent : public Component
{
ChildComponent() {}
void paint (Graphics& g) override { g.fillAll (Colours::cornflowerblue); }
using Component::createNewPeer;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildComponent)
};
ChildComponent embeddedComponent;
std::unique_ptr<ComponentPeer> peer;
HWNDComponentWithParent embeddedComponent;
using HandleFormat = HWND;
#elif JUCE_MAC
AutoResizingNSViewComponentWithParent embeddedComponent;
@@ -1538,14 +1471,11 @@ private:
#endif
HandleFormat pluginHandle = {};
bool recursiveResize = false;
bool recursiveResize = false, hasDoneInitialResize = false;
#if ! JUCE_MAC
ComponentPeer* currentPeer = nullptr;
Steinberg::IPlugViewContentScaleSupport* scaleInterface = nullptr;
#endif
float nativeScaleFactor = 1.0f;
bool hasDoneInitialResize = false;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginWindow)
@@ -1569,7 +1499,7 @@ struct VST3ComponentHolder
// transfers ownership to the plugin instance!
AudioPluginInstance* createPluginInstance();
bool fetchController (ComSmartPtr<Vst::IEditController>& editController)
bool fetchController (VSTComSmartPtr<Vst::IEditController>& editController)
{
if (! isComponentInitialised && ! initialise())
return false;
@@ -1618,8 +1548,8 @@ struct VST3ComponentHolder
ignoreUnused (success);
jassert (success);
ComSmartPtr<IPluginFactory2> pf2;
ComSmartPtr<IPluginFactory3> pf3;
VSTComSmartPtr<IPluginFactory2> pf2;
VSTComSmartPtr<IPluginFactory3> pf3;
std::unique_ptr<PClassInfo2> info2;
std::unique_ptr<PClassInfoW> infoW;
@@ -1683,7 +1613,7 @@ struct VST3ComponentHolder
JUCE_ASSERT_MESSAGE_THREAD
#endif
factory = ComSmartPtr<IPluginFactory> (module->getPluginFactory());
factory = VSTComSmartPtr<IPluginFactory> (module->getPluginFactory());
int classIdx;
if ((classIdx = getClassIndex (module->getName())) < 0)
@@ -1734,9 +1664,9 @@ struct VST3ComponentHolder
//==============================================================================
VST3ModuleHandle::Ptr module;
ComSmartPtr<IPluginFactory> factory;
ComSmartPtr<VST3HostContext> host;
ComSmartPtr<Vst::IComponent> component;
VSTComSmartPtr<IPluginFactory> factory;
VSTComSmartPtr<VST3HostContext> host;
VSTComSmartPtr<Vst::IComponent> component;
FUID cidOfComponent;
bool isComponentInitialised = false;
@@ -2292,7 +2222,7 @@ public:
{
if (trackInfoListener != nullptr)
{
ComSmartPtr<Vst::IAttributeList> l (new TrackPropertiesAttributeList (properties));
VSTComSmartPtr<Vst::IAttributeList> l (new TrackPropertiesAttributeList (properties));
trackInfoListener->setChannelContextInfos (l);
}
}
@@ -2427,7 +2357,7 @@ public:
if (getActiveEditor() != nullptr)
return true;
ComSmartPtr<IPlugView> view (tryCreatingView(), false);
VSTComSmartPtr<IPlugView> view (tryCreatingView(), false);
return view != nullptr;
}
@@ -2509,7 +2439,7 @@ public:
bool setStateFromPresetFile (const MemoryBlock& rawData)
{
MemoryBlock rawDataCopy (rawData);
ComSmartPtr<Steinberg::MemoryStream> memoryStream = new Steinberg::MemoryStream (rawDataCopy.getData(), (int) rawDataCopy.getSize());
VSTComSmartPtr<Steinberg::MemoryStream> memoryStream = new Steinberg::MemoryStream (rawDataCopy.getData(), (int) rawDataCopy.getSize());
if (memoryStream == nullptr || holder->component == nullptr)
return false;
@@ -2676,17 +2606,17 @@ private:
std::unique_ptr<PClassInfoW> infoW;
// Rudimentary interfaces:
ComSmartPtr<Vst::IEditController> editController;
ComSmartPtr<Vst::IEditController2> editController2;
ComSmartPtr<Vst::IMidiMapping> midiMapping;
ComSmartPtr<Vst::IAudioProcessor> processor;
ComSmartPtr<Vst::IComponentHandler> componentHandler;
ComSmartPtr<Vst::IComponentHandler2> componentHandler2;
ComSmartPtr<Vst::IUnitInfo> unitInfo;
ComSmartPtr<Vst::IUnitData> unitData;
ComSmartPtr<Vst::IProgramListData> programListData;
ComSmartPtr<Vst::IConnectionPoint> componentConnection, editControllerConnection;
ComSmartPtr<Vst::ChannelContext::IInfoListener> trackInfoListener;
VSTComSmartPtr<Vst::IEditController> editController;
VSTComSmartPtr<Vst::IEditController2> editController2;
VSTComSmartPtr<Vst::IMidiMapping> midiMapping;
VSTComSmartPtr<Vst::IAudioProcessor> processor;
VSTComSmartPtr<Vst::IComponentHandler> componentHandler;
VSTComSmartPtr<Vst::IComponentHandler2> componentHandler2;
VSTComSmartPtr<Vst::IUnitInfo> unitInfo;
VSTComSmartPtr<Vst::IUnitData> unitData;
VSTComSmartPtr<Vst::IProgramListData> programListData;
VSTComSmartPtr<Vst::IConnectionPoint> componentConnection, editControllerConnection;
VSTComSmartPtr<Vst::ChannelContext::IInfoListener> trackInfoListener;
/** The number of IO buses MUST match that of the plugin,
even if there aren't enough channels to process,
@@ -2701,7 +2631,7 @@ private:
//==============================================================================
template <typename Type>
static void appendStateFrom (XmlElement& head, ComSmartPtr<Type>& object, const String& identifier)
static void appendStateFrom (XmlElement& head, VSTComSmartPtr<Type>& object, const String& identifier)
{
if (object != nullptr)
{
@@ -2715,7 +2645,7 @@ private:
}
}
static ComSmartPtr<Steinberg::MemoryStream> createMemoryStreamForState (XmlElement& head, StringRef identifier)
static VSTComSmartPtr<Steinberg::MemoryStream> createMemoryStreamForState (XmlElement& head, StringRef identifier)
{
if (auto* state = head.getChildByName (identifier))
{
@@ -2723,7 +2653,7 @@ private:
if (mem.fromBase64Encoding (state->getAllSubText()))
{
ComSmartPtr<Steinberg::MemoryStream> stream (new Steinberg::MemoryStream(), false);
VSTComSmartPtr<Steinberg::MemoryStream> stream (new Steinberg::MemoryStream(), false);
stream->setSize ((TSize) mem.getSize());
mem.copyTo (stream->getData(), 0, mem.getSize());
return stream;
@@ -2733,8 +2663,8 @@ private:
return nullptr;
}
ComSmartPtr<ParamValueQueueList> inputParameterChanges, outputParameterChanges;
ComSmartPtr<MidiEventList> midiInputs, midiOutputs;
VSTComSmartPtr<ParamValueQueueList> inputParameterChanges, outputParameterChanges;
VSTComSmartPtr<MidiEventList> midiInputs, midiOutputs;
Vst::ProcessContext timingInfo; //< Only use this in processBlock()!
bool isControllerInitialised = false, isActive = false, lastProcessBlockCallWasBypass = false;
VST3Parameter* bypassParam = nullptr;
@@ -2872,10 +2802,10 @@ private:
setRateAndBufferSizeDetails (setup.sampleRate, (int) setup.maxSamplesPerBlock);
}
static AudioProcessor::BusesProperties getBusProperties (ComSmartPtr<Vst::IComponent>& component)
static AudioProcessor::BusesProperties getBusProperties (VSTComSmartPtr<Vst::IComponent>& component)
{
AudioProcessor::BusesProperties busProperties;
ComSmartPtr<Vst::IAudioProcessor> processor;
VSTComSmartPtr<Vst::IAudioProcessor> processor;
processor.loadFrom (component.get());
for (int dirIdx = 0; dirIdx < 2; ++dirIdx)
@@ -3179,7 +3109,9 @@ tresult VST3HostContext::restartComponent (Steinberg::int32 flags)
if (plugin->processor != nullptr)
plugin->setLatencySamples (jmax (0, (int) plugin->processor->getLatencySamples()));
plugin->updateHostDisplay();
plugin->updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (true)
.withParameterInfoChanged (true));
return kResultTrue;
}
@@ -3252,7 +3184,7 @@ tresult VST3HostContext::ContextMenu::popup (Steinberg::UCoord x, Steinberg::UCo
// Unfortunately, Steinberg's docs explicitly say this should be modal..
handleResult (topLevelMenu->showMenu (options));
#else
topLevelMenu->showMenuAsync (options, ModalCallbackFunction::create (menuFinished, ComSmartPtr<ContextMenu> (this)));
topLevelMenu->showMenuAsync (options, ModalCallbackFunction::create (menuFinished, VSTComSmartPtr<ContextMenu> (this)));
#endif
return kResultOk;
@@ -3314,12 +3246,12 @@ void VST3PluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& resul
for every housed plugin.
*/
ComSmartPtr<IPluginFactory> pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (fileOrIdentifier)
.getPluginFactory());
VSTComSmartPtr<IPluginFactory> pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (fileOrIdentifier)
.getPluginFactory());
if (pluginFactory != nullptr)
{
ComSmartPtr<VST3HostContext> host (new VST3HostContext());
VSTComSmartPtr<VST3HostContext> host (new VST3HostContext());
DescriptionLister lister (host, pluginFactory);
lister.findDescriptionsAndPerform (File (fileOrIdentifier));


+ 18
- 20
libs/juce-current/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp View File

@@ -53,6 +53,7 @@ namespace Vst2
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
JUCE_END_IGNORE_WARNINGS_MSVC
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4355)
#include "juce_VSTMidiEventList.h"
@@ -77,7 +78,7 @@ JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4355)
//==============================================================================
namespace juce
{
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
#if JUCE_WINDOWS
extern void setThreadDPIAwarenessForWindow (HWND);
#endif
@@ -376,7 +377,7 @@ private:
switchValueType.entries.add (new Entry({ TRANS("Off"), Range ("[0, 0.5[") }));
switchValueType.entries.add (new Entry({ TRANS("On"), Range ("[0.5, 1]") }));
forEachXmlChildElement (xml, item)
for (auto* item : xml.getChildIterator())
{
if (item->hasTagName ("Param")) parseParam (*item, nullptr, nullptr);
else if (item->hasTagName ("ValueType")) parseValueType (*item);
@@ -430,7 +431,7 @@ private:
int curEntry = 0;
const int numEntries = item.getNumChildElements();
forEachXmlChildElementWithTagName (item, entryXml, "Entry")
for (auto* entryXml : item.getChildWithTagNameIterator ("Entry"))
{
auto entry = new Entry();
entry->name = entryXml->getStringAttribute ("name");
@@ -459,7 +460,7 @@ private:
templates.add (temp);
temp->name = item.getStringAttribute ("name");
forEachXmlChildElement (item, param)
for (auto* param : item.getChildIterator())
parseParam (*param, nullptr, temp);
}
@@ -508,7 +509,7 @@ private:
}
else
{
forEachXmlChildElement (item, subItem)
for (auto* subItem : item.getChildIterator())
{
if (subItem->hasTagName ("Param")) parseParam (*subItem, group, nullptr);
else if (subItem->hasTagName ("Group")) parseGroup (*subItem, group);
@@ -1596,8 +1597,8 @@ struct VSTPluginInstance : public AudioPluginInstance,
void handleAsyncUpdate() override
{
// indicates that something about the plugin has changed..
updateHostDisplay();
updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (true)
.withParameterInfoChanged (true));
}
pointer_sized_int handleCallback (int32 opcode, int32 index, pointer_sized_int value, void* ptr, float opt)
@@ -1763,9 +1764,9 @@ struct VSTPluginInstance : public AudioPluginInstance,
{
if (i != oldProg)
{
auto prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen);
auto prog = addBytesToPointer (set->programs, i * progLen);
if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize)
if (getAddressDifference (prog, set) >= (int) dataSize)
return false;
if (fxbSwap (set->numPrograms) > 0)
@@ -1779,9 +1780,9 @@ struct VSTPluginInstance : public AudioPluginInstance,
if (fxbSwap (set->numPrograms) > 0)
setCurrentProgram (oldProg);
auto prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen);
auto prog = addBytesToPointer (set->programs, oldProg * progLen);
if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize)
if (getAddressDifference (prog, set) >= (int) dataSize)
return false;
if (! restoreProgramSettings (prog))
@@ -1901,14 +1902,14 @@ struct VSTPluginInstance : public AudioPluginInstance,
auto oldProgram = getCurrentProgram();
if (oldProgram >= 0)
setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen));
setParamsInProgramBlock (addBytesToPointer (set->programs, oldProgram * progLen));
for (int i = 0; i < numPrograms; ++i)
{
if (i != oldProgram)
{
setCurrentProgram (i);
setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + i * progLen));
setParamsInProgramBlock (addBytesToPointer (set->programs, i * progLen));
}
}
@@ -2586,7 +2587,7 @@ private:
getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63);
auto p = (float*) (((char*) dest.getData()) + 64);
auto p = unalignedPointerCast<float*> (((char*) dest.getData()) + 64);
for (int i = 0; i < numParameters; ++i)
if (auto* param = getParameters()[i])
@@ -2597,7 +2598,7 @@ private:
{
changeProgramName (getCurrentProgram(), (const char*) m.getData());
auto p = (float*) (((char*) m.getData()) + 64);
auto p = unalignedPointerCast<float*> (((char*) m.getData()) + 64);
auto numParameters = getParameters().size();
for (int i = 0; i < numParameters; ++i)
@@ -2858,9 +2859,7 @@ public:
#if JUCE_WINDOWS
if (pluginHWND != 0)
{
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
setThreadDPIAwarenessForWindow (pluginHWND);
#endif
MoveWindow (pluginHWND, pos.getX(), pos.getY(),
roundToInt (getWidth() * nativeScaleFactor),
@@ -3123,9 +3122,7 @@ private:
// very dodgy logic to decide which size is right.
if (std::abs (rw - w) > 350 || std::abs (rh - h) > 350)
{
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
setThreadDPIAwarenessForWindow (pluginHWND);
#endif
SetWindowPos (pluginHWND, 0,
0, 0, roundToInt (rw * nativeScaleFactor), roundToInt (rh * nativeScaleFactor),
@@ -3228,7 +3225,7 @@ private:
bool willCauseRecursiveResize (int w, int h)
{
auto newScreenBounds = Rectangle<int> (w, h).withPosition (getScreenPosition());
return Desktop::getInstance().getDisplays().findDisplayForRect (newScreenBounds).scale != nativeScaleFactor;
return Desktop::getInstance().getDisplays().getDisplayForRect (newScreenBounds)->scale != nativeScaleFactor;
}
bool isWindowSizeCorrectForPlugin (int w, int h)
@@ -3737,6 +3734,7 @@ void VSTPluginFormat::aboutToScanVSTShellPlugin (const PluginDescription&) {}
} // namespace juce
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
JUCE_END_IGNORE_WARNINGS_MSVC
#endif

+ 92
- 3
libs/juce-current/source/modules/juce_audio_processors/juce_audio_processors.cpp View File

@@ -81,7 +81,97 @@ static bool arrayContainsPlugin (const OwnedArray<PluginDescription>& list,
#endif
#if JUCE_MAC || JUCE_IOS
#if JUCE_WINDOWS
//==============================================================================
class HWNDComponentWithParent : public HWNDComponent,
private Timer
{
public:
HWNDComponentWithParent()
{
String className ("JUCE_");
className << String::toHexString (Time::getHighResolutionTicks());
HMODULE moduleHandle = (HMODULE) Process::getCurrentModuleInstanceHandle();
WNDCLASSEX wc = {};
wc.cbSize = sizeof (wc);
wc.lpfnWndProc = (WNDPROC) wndProc;
wc.cbWndExtra = 4;
wc.hInstance = moduleHandle;
wc.lpszClassName = className.toWideCharPointer();
atom = RegisterClassEx (&wc);
jassert (atom != 0);
hwnd = CreateWindow (getClassNameFromAtom(), L"HWNDComponentWithParent",
0, 0, 0, 0, 0,
nullptr, nullptr, moduleHandle, nullptr);
jassert (hwnd != nullptr);
setHWND (hwnd);
startTimer (30);
}
~HWNDComponentWithParent() override
{
if (IsWindow (hwnd))
DestroyWindow (hwnd);
UnregisterClass (getClassNameFromAtom(), nullptr);
}
private:
//==============================================================================
static LRESULT CALLBACK wndProc (HWND h, const UINT message, const WPARAM wParam, const LPARAM lParam)
{
if (message == WM_SHOWWINDOW && wParam == TRUE)
return 0;
return DefWindowProc (h, message, wParam, lParam);
}
void timerCallback() override
{
if (HWND child = getChildHWND())
{
stopTimer();
ShowWindow (child, SW_HIDE);
SetParent (child, NULL);
auto windowFlags = GetWindowLongPtr (child, -16);
windowFlags &= ~WS_CHILD;
windowFlags |= WS_POPUP;
SetWindowLongPtr (child, -16, windowFlags);
setHWND (child);
}
}
LPCTSTR getClassNameFromAtom() noexcept { return (LPCTSTR) (pointer_sized_uint) atom; }
HWND getChildHWND() const
{
if (HWND parent = (HWND) getHWND())
return GetWindow (parent, GW_CHILD);
return nullptr;
}
//==============================================================================
ATOM atom;
HWND hwnd;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponentWithParent)
};
#elif JUCE_MAC || JUCE_IOS
#if JUCE_IOS
#define JUCE_IOS_MAC_VIEW UIView
@@ -130,12 +220,11 @@ struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewCompone
}
}
};
#endif
} // namespace juce
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations", "-Wcast-align")
#include "format/juce_AudioPluginFormat.cpp"
#include "format/juce_AudioPluginFormatManager.cpp"
#include "format_types/juce_LegacyAudioParameter.cpp"


+ 1
- 1
libs/juce-current/source/modules/juce_audio_processors/juce_audio_processors.h View File

@@ -35,7 +35,7 @@
ID: juce_audio_processors
vendor: juce
version: 6.0.4
version: 6.0.7
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


+ 25
- 3
libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp View File

@@ -412,7 +412,7 @@ void AudioProcessor::setLatencySamples (int newLatency)
if (latencySamples != newLatency)
{
latencySamples = newLatency;
updateHostDisplay();
updateHostDisplay (AudioProcessorListener::ChangeDetails().withLatencyChanged (true));
}
}
@@ -423,11 +423,11 @@ AudioProcessorListener* AudioProcessor::getListenerLocked (int index) const noex
return listeners[index];
}
void AudioProcessor::updateHostDisplay()
void AudioProcessor::updateHostDisplay (const AudioProcessorListener::ChangeDetails& details)
{
for (int i = listeners.size(); --i >= 0;)
if (auto l = getListenerLocked (i))
l->audioProcessorChanged (this);
l->audioProcessorChanged (this, details);
}
void AudioProcessor::checkForDuplicateParamID (AudioProcessorParameter* param)
@@ -445,6 +445,24 @@ void AudioProcessor::checkForDuplicateParamID (AudioProcessorParameter* param)
#endif
}
void AudioProcessor::checkForDuplicateGroupIDs (const AudioProcessorParameterGroup& newGroup)
{
ignoreUnused (newGroup);
#if JUCE_DEBUG
auto groups = newGroup.getSubgroups (true);
groups.add (&newGroup);
for (auto* group : groups)
{
auto insertResult = groupIDs.insert (group->getID());
// If you hit this assertion then a group ID is not unique
jassert (insertResult.second);
}
#endif
}
const Array<AudioProcessorParameter*>& AudioProcessor::getParameters() const { return flatParameterList; }
const AudioProcessorParameterGroup& AudioProcessor::getParameterTree() const { return parameterTree; }
@@ -463,6 +481,7 @@ void AudioProcessor::addParameter (AudioProcessorParameter* param)
void AudioProcessor::addParameterGroup (std::unique_ptr<AudioProcessorParameterGroup> group)
{
jassert (group != nullptr);
checkForDuplicateGroupIDs (*group);
auto oldSize = flatParameterList.size();
flatParameterList.addArray (group->getParameters (true));
@@ -483,9 +502,12 @@ void AudioProcessor::setParameterTree (AudioProcessorParameterGroup&& newTree)
{
#if JUCE_DEBUG
paramIDs.clear();
groupIDs.clear();
#endif
parameterTree = std::move (newTree);
checkForDuplicateGroupIDs (parameterTree);
flatParameterList = parameterTree.getParameters (true);
for (int i = 0; i < flatParameterList.size(); ++i)


+ 3
- 2
libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h View File

@@ -993,7 +993,7 @@ public:
It sends a hint to the host that something like the program, number of parameters,
etc, has changed, and that it should update itself.
*/
void updateHostDisplay();
void updateHostDisplay (const AudioProcessorListener::ChangeDetails& details = {});
//==============================================================================
/** Adds a parameter to the AudioProcessor.
@@ -1499,10 +1499,11 @@ private:
#endif
bool textRecursionCheck = false;
std::unordered_set<String> paramIDs;
std::unordered_set<String> paramIDs, groupIDs;
#endif
void checkForDuplicateParamID (AudioProcessorParameter*);
void checkForDuplicateGroupIDs (const AudioProcessorParameterGroup&);
AudioProcessorListener* getListenerLocked (int) const noexcept;
void updateSpeakerFormatStrings();


+ 27
- 4
libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp View File

@@ -1265,6 +1265,22 @@ void AudioProcessorGraph::prepareToPlay (double sampleRate, int estimatedSamples
{
const ScopedLock sl (getCallbackLock());
setRateAndBufferSizeDetails (sampleRate, estimatedSamplesPerBlock);
const auto newPrepareSettings = [&]
{
PrepareSettings settings;
settings.precision = getProcessingPrecision();
settings.sampleRate = sampleRate;
settings.blockSize = estimatedSamplesPerBlock;
settings.valid = true;
return settings;
}();
if (prepareSettings != newPrepareSettings)
{
unprepare();
prepareSettings = newPrepareSettings;
}
}
clearRenderingSequence();
@@ -1277,16 +1293,23 @@ bool AudioProcessorGraph::supportsDoublePrecisionProcessing() const
return true;
}
void AudioProcessorGraph::releaseResources()
void AudioProcessorGraph::unprepare()
{
const ScopedLock sl (getCallbackLock());
cancelPendingUpdate();
prepareSettings.valid = false;
isPrepared = 0;
for (auto* n : nodes)
n->unprepare();
}
void AudioProcessorGraph::releaseResources()
{
const ScopedLock sl (getCallbackLock());
cancelPendingUpdate();
unprepare();
if (renderSequenceFloat != nullptr)
renderSequenceFloat->releaseBuffers();


+ 21
- 0
libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h View File

@@ -407,6 +407,24 @@ public:
void setStateInformation (const void* data, int sizeInBytes) override;
private:
struct PrepareSettings
{
ProcessingPrecision precision = ProcessingPrecision::singlePrecision;
double sampleRate = 0.0;
int blockSize = 0;
bool valid = false;
using Tied = std::tuple<const ProcessingPrecision&,
const double&,
const int&,
const bool&>;
Tied tie() const noexcept { return std::tie (precision, sampleRate, blockSize, valid); }
bool operator== (const PrepareSettings& other) const noexcept { return tie() == other.tie(); }
bool operator!= (const PrepareSettings& other) const noexcept { return tie() != other.tie(); }
};
//==============================================================================
ReferenceCountedArray<Node> nodes;
NodeID lastNodeID = {};
@@ -416,11 +434,14 @@ private:
std::unique_ptr<RenderSequenceFloat> renderSequenceFloat;
std::unique_ptr<RenderSequenceDouble> renderSequenceDouble;
PrepareSettings prepareSettings;
friend class AudioGraphIOProcessor;
std::atomic<bool> isPrepared { false };
void topologyChanged();
void unprepare();
void handleAsyncUpdate() override;
void clearRenderingSequence();
void buildRenderingSequence();


+ 23
- 1
libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorListener.h View File

@@ -57,6 +57,28 @@ public:
int parameterIndex,
float newValue) = 0;
/** Provides details about aspects of an AudioProcessor which have changed.
*/
struct JUCE_API ChangeDetails
{
bool latencyChanged = false;
bool parameterInfoChanged = false;
bool programChanged = false;
ChangeDetails withLatencyChanged (bool b) const noexcept { return with (&ChangeDetails::latencyChanged, b); }
ChangeDetails withParameterInfoChanged (bool b) const noexcept { return with (&ChangeDetails::parameterInfoChanged, b); }
ChangeDetails withProgramChanged (bool b) const noexcept { return with (&ChangeDetails::programChanged, b); }
private:
template <typename Member, typename Value>
ChangeDetails with (Member&& member, Value&& value) const noexcept
{
auto copy = *this;
copy.*member = std::forward<Value> (value);
return copy;
}
};
/** Called to indicate that something else in the plugin has changed, like its
program, number of parameters, etc.
@@ -67,7 +89,7 @@ public:
to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the
message thread.
*/
virtual void audioProcessorChanged (AudioProcessor* processor) = 0;
virtual void audioProcessorChanged (AudioProcessor* processor, const ChangeDetails& details) = 0;
/** Indicates that a parameter change gesture has started.


+ 1
- 1
libs/juce-current/source/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp View File

@@ -73,7 +73,7 @@ private:
parameterValueHasChanged = 1;
}
void audioProcessorChanged (AudioProcessor*) override {}
void audioProcessorChanged (AudioProcessor*, const ChangeDetails&) override {}
//==============================================================================
void timerCallback() override


+ 1
- 1
libs/juce-current/source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp View File

@@ -378,7 +378,7 @@ void KnownPluginList::recreateFromXml (const XmlElement& xml)
if (xml.hasTagName ("KNOWNPLUGINS"))
{
forEachXmlChildElement (xml, e)
for (auto* e : xml.getChildIterator())
{
PluginDescription info;


+ 1
- 1
libs/juce-current/source/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp View File

@@ -262,7 +262,7 @@ static bool canShowFolderForPlugin (KnownPluginList& list, int index)
static void showFolderForPlugin (KnownPluginList& list, int index)
{
if (canShowFolderForPlugin (list, index))
File (list.getTypes()[index].fileOrIdentifier).getParentDirectory().startAsProcess();
File (list.getTypes()[index].fileOrIdentifier).revealToUser();
}
void PluginListComponent::removeMissingPlugins()


+ 3
- 3
libs/juce-current/source/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp View File

@@ -162,7 +162,7 @@ private:
return;
unnormalisedValue = newValue;
listeners.call ([=] (Listener& l) { l.parameterChanged (parameter.paramID, unnormalisedValue); });
listeners.call ([this] (Listener& l) { l.parameterChanged (parameter.paramID, unnormalisedValue); });
listenersNeedCalling = false;
needsUpdate = true;
}
@@ -696,10 +696,10 @@ public:
beginTest ("After construction, the value tree has the expected format");
{
TestAudioProcessor proc ({
std::make_unique<AudioProcessorParameterGroup> ("", "", "",
std::make_unique<AudioProcessorParameterGroup> ("A", "", "",
std::make_unique<AudioParameterBool> ("a", "", false),
std::make_unique<AudioParameterFloat> ("b", "", NormalisableRange<float>{}, 0.0f)),
std::make_unique<AudioProcessorParameterGroup> ("", "", "",
std::make_unique<AudioProcessorParameterGroup> ("B", "", "",
std::make_unique<AudioParameterInt> ("c", "", 0, 1, 0),
std::make_unique<AudioParameterChoice> ("d", "", StringArray { "foo", "bar" }, 0)) });


+ 1
- 1
libs/juce-current/source/modules/juce_audio_utils/juce_audio_utils.h View File

@@ -35,7 +35,7 @@
ID: juce_audio_utils
vendor: juce
version: 6.0.4
version: 6.0.7
name: JUCE extra audio utility classes
description: Classes for audio-related GUI and miscellaneous tasks.
website: http://www.juce.com/juce


+ 2
- 2
libs/juce-current/source/modules/juce_audio_utils/native/juce_mac_AudioCDReader.mm View File

@@ -30,7 +30,7 @@ namespace CDReaderHelpers
{
inline const XmlElement* getElementForKey (const XmlElement& xml, const String& key)
{
forEachXmlChildElementWithTagName (xml, child, "key")
for (auto* child : xml.getChildWithTagNameIterator ("key"))
if (child->getAllSubText().trim() == key)
return child->getNextElement();
@@ -71,7 +71,7 @@ namespace CDReaderHelpers
if (trackArray == nullptr)
return "Couldn't find Track Array";
forEachXmlChildElement (*trackArray, track)
for (auto* track : trackArray->getChildIterator())
{
const int trackValue = getIntValueForKey (*track, "Start Block");
if (trackValue < 0)


+ 4
- 4
libs/juce-current/source/modules/juce_core/containers/juce_AbstractFifo.h View File

@@ -314,25 +314,25 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo)
};
template<>
template <>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::finish (AbstractFifo& f, int num) noexcept
{
f.finishedRead (num);
}
template<>
template <>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::finish (AbstractFifo& f, int num) noexcept
{
f.finishedWrite (num);
}
template<>
template <>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::prepare (AbstractFifo& f, int num) noexcept
{
f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2);
}
template<>
template <>
inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::prepare (AbstractFifo& f, int num) noexcept
{
f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2);


+ 8
- 8
libs/juce-current/source/modules/juce_core/containers/juce_Array.h View File

@@ -110,16 +110,16 @@ public:
/** Initalises an Array from a list of items. */
template <typename... OtherElements>
Array (const ElementType& firstNewElement, OtherElements... otherElements)
Array (const ElementType& firstNewElement, OtherElements&&... otherElements)
{
values.add (firstNewElement, otherElements...);
values.add (firstNewElement, std::forward<OtherElements> (otherElements)...);
}
/** Initalises an Array from a list of items. */
template <typename... OtherElements>
Array (ElementType&& firstNewElement, OtherElements... otherElements)
Array (ElementType&& firstNewElement, OtherElements&&... otherElements)
{
values.add (std::move (firstNewElement), otherElements...);
values.add (std::move (firstNewElement), std::forward<OtherElements> (otherElements)...);
}
template <typename TypeToCreateFrom>
@@ -433,18 +433,18 @@ public:
/** Appends multiple new elements at the end of the array. */
template <typename... OtherElements>
void add (const ElementType& firstNewElement, OtherElements... otherElements)
void add (const ElementType& firstNewElement, OtherElements&&... otherElements)
{
const ScopedLockType lock (getLock());
values.add (firstNewElement, otherElements...);
values.add (firstNewElement, std::forward<OtherElements> (otherElements)...);
}
/** Appends multiple new elements at the end of the array. */
template <typename... OtherElements>
void add (ElementType&& firstNewElement, OtherElements... otherElements)
void add (ElementType&& firstNewElement, OtherElements&&... otherElements)
{
const ScopedLockType lock (getLock());
values.add (std::move (firstNewElement), otherElements...);
values.add (std::move (firstNewElement), std::forward<OtherElements> (otherElements)...);
}
/** Inserts a new element into the array at a given position.


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save