mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
MIDI-CI: Add module
This commit is contained in:
parent
387ab88c13
commit
8ebbc20311
85 changed files with 11013 additions and 16 deletions
|
|
@ -81,8 +81,10 @@ add_library( ${BINARY_NAME}
|
|||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPacket.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPackets.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConversion.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConverters.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPFactory.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp"
|
||||
|
|
@ -2153,8 +2155,10 @@ set_source_files_properties(
|
|||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPacket.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPackets.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConversion.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConverters.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPFactory.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp"
|
||||
|
|
|
|||
|
|
@ -2810,8 +2810,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
|
|||
|
|
@ -3624,12 +3624,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -2810,8 +2810,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
|
|||
|
|
@ -3624,12 +3624,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -2810,8 +2810,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
|
|||
|
|
@ -3624,12 +3624,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@
|
|||
juce_audio_formats, juce_audio_processors, juce_audio_utils,
|
||||
juce_core, juce_cryptography, juce_data_structures, juce_dsp,
|
||||
juce_events, juce_graphics, juce_gui_basics, juce_gui_extra,
|
||||
juce_opengl, juce_osc, juce_product_unlocking, juce_video
|
||||
juce_opengl, juce_osc, juce_product_unlocking, juce_video,
|
||||
juce_midi_ci
|
||||
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
|
||||
|
||||
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1,JUCE_PLUGINHOST_VST3=1,JUCE_PLUGINHOST_LV2=1
|
||||
|
|
|
|||
|
|
@ -53,8 +53,10 @@ add_library( ${BINARY_NAME}
|
|||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPacket.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPackets.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConversion.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConverters.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPFactory.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp"
|
||||
|
|
@ -1825,8 +1827,10 @@ set_source_files_properties(
|
|||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPacket.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPackets.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConversion.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConverters.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPFactory.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp"
|
||||
|
|
|
|||
|
|
@ -2414,8 +2414,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
|
|||
|
|
@ -3048,12 +3048,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -86,8 +86,10 @@ add_library( ${BINARY_NAME}
|
|||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPacket.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPackets.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConversion.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConverters.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPFactory.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp"
|
||||
|
|
@ -2011,8 +2013,10 @@ set_source_files_properties(
|
|||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPacket.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPackets.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConversion.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConverters.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPFactory.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp"
|
||||
|
|
|
|||
|
|
@ -2594,8 +2594,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
|
|||
|
|
@ -3324,12 +3324,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -2594,8 +2594,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
|
|||
|
|
@ -3324,12 +3324,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -2594,8 +2594,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
|
|||
|
|
@ -3324,12 +3324,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -57,8 +57,10 @@ add_library( ${BINARY_NAME}
|
|||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPacket.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPackets.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConversion.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConverters.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPFactory.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp"
|
||||
|
|
@ -1909,8 +1911,10 @@ set_source_files_properties(
|
|||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPacket.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPackets.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConversion.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPConverters.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPFactory.h"
|
||||
"../../../../../modules/juce_audio_basics/midi/ump/juce_UMPIterator.cpp"
|
||||
|
|
|
|||
|
|
@ -2505,8 +2505,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
|
|||
|
|
@ -3189,12 +3189,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -282,7 +282,8 @@ StringArray getJUCEModules() noexcept
|
|||
"juce_opengl",
|
||||
"juce_osc",
|
||||
"juce_product_unlocking",
|
||||
"juce_video"
|
||||
"juce_video",
|
||||
"juce_midi_ci"
|
||||
};
|
||||
|
||||
return juceModuleIds;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ ifeq ($(CONFIG),Debug)
|
|||
TARGET_ARCH :=
|
||||
endif
|
||||
|
||||
JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DDEBUG=1" "-D_DEBUG=1" "-DJUCE_DISPLAY_SPLASH_SCREEN=0" "-DJUCE_USE_DARK_SPLASH_SCREEN=1" "-DJUCE_PROJUCER_VERSION=0x70008" "-DJUCE_MODULE_AVAILABLE_juce_analytics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1" "-DJUCE_MODULE_AVAILABLE_juce_core=1" "-DJUCE_MODULE_AVAILABLE_juce_cryptography=1" "-DJUCE_MODULE_AVAILABLE_juce_data_structures=1" "-DJUCE_MODULE_AVAILABLE_juce_dsp=1" "-DJUCE_MODULE_AVAILABLE_juce_events=1" "-DJUCE_MODULE_AVAILABLE_juce_graphics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1" "-DJUCE_MODULE_AVAILABLE_juce_opengl=1" "-DJUCE_MODULE_AVAILABLE_juce_osc=1" "-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1" "-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1" "-DJUCE_PLUGINHOST_VST3=1" "-DJUCE_PLUGINHOST_LV2=1" "-DJUCE_STRICT_REFCOUNTEDPOINTER=1" "-DJUCE_STANDALONE_APPLICATION=1" "-DJUCE_UNIT_TESTS=1" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000" $(shell $(PKG_CONFIG) --cflags alsa freetype2 gl libcurl webkit2gtk-4.0 gtk+-x11-3.0) -pthread -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../../../modules/juce_audio_processors/format_types/LV2_SDK -I../../../../modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS)
|
||||
JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DDEBUG=1" "-D_DEBUG=1" "-DJUCE_DISPLAY_SPLASH_SCREEN=0" "-DJUCE_USE_DARK_SPLASH_SCREEN=1" "-DJUCE_PROJUCER_VERSION=0x70008" "-DJUCE_MODULE_AVAILABLE_juce_analytics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1" "-DJUCE_MODULE_AVAILABLE_juce_core=1" "-DJUCE_MODULE_AVAILABLE_juce_cryptography=1" "-DJUCE_MODULE_AVAILABLE_juce_data_structures=1" "-DJUCE_MODULE_AVAILABLE_juce_dsp=1" "-DJUCE_MODULE_AVAILABLE_juce_events=1" "-DJUCE_MODULE_AVAILABLE_juce_graphics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1" "-DJUCE_MODULE_AVAILABLE_juce_midi_ci=1" "-DJUCE_MODULE_AVAILABLE_juce_opengl=1" "-DJUCE_MODULE_AVAILABLE_juce_osc=1" "-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1" "-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1" "-DJUCE_PLUGINHOST_VST3=1" "-DJUCE_PLUGINHOST_LV2=1" "-DJUCE_STRICT_REFCOUNTEDPOINTER=1" "-DJUCE_STANDALONE_APPLICATION=1" "-DJUCE_UNIT_TESTS=1" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000" $(shell $(PKG_CONFIG) --cflags alsa freetype2 gl libcurl webkit2gtk-4.0 gtk+-x11-3.0) -pthread -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../../../modules/juce_audio_processors/format_types/LV2_SDK -I../../../../modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS)
|
||||
JUCE_CPPFLAGS_CONSOLEAPP := "-DJucePlugin_Build_VST=0" "-DJucePlugin_Build_VST3=0" "-DJucePlugin_Build_AU=0" "-DJucePlugin_Build_AUv3=0" "-DJucePlugin_Build_AAX=0" "-DJucePlugin_Build_Standalone=0" "-DJucePlugin_Build_Unity=0" "-DJucePlugin_Build_LV2=0"
|
||||
JUCE_TARGET_CONSOLEAPP := UnitTestRunner
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ ifeq ($(CONFIG),Release)
|
|||
TARGET_ARCH :=
|
||||
endif
|
||||
|
||||
JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DNDEBUG=1" "-DJUCE_DISPLAY_SPLASH_SCREEN=0" "-DJUCE_USE_DARK_SPLASH_SCREEN=1" "-DJUCE_PROJUCER_VERSION=0x70008" "-DJUCE_MODULE_AVAILABLE_juce_analytics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1" "-DJUCE_MODULE_AVAILABLE_juce_core=1" "-DJUCE_MODULE_AVAILABLE_juce_cryptography=1" "-DJUCE_MODULE_AVAILABLE_juce_data_structures=1" "-DJUCE_MODULE_AVAILABLE_juce_dsp=1" "-DJUCE_MODULE_AVAILABLE_juce_events=1" "-DJUCE_MODULE_AVAILABLE_juce_graphics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1" "-DJUCE_MODULE_AVAILABLE_juce_opengl=1" "-DJUCE_MODULE_AVAILABLE_juce_osc=1" "-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1" "-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1" "-DJUCE_PLUGINHOST_VST3=1" "-DJUCE_PLUGINHOST_LV2=1" "-DJUCE_STRICT_REFCOUNTEDPOINTER=1" "-DJUCE_STANDALONE_APPLICATION=1" "-DJUCE_UNIT_TESTS=1" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000" $(shell $(PKG_CONFIG) --cflags alsa freetype2 gl libcurl webkit2gtk-4.0 gtk+-x11-3.0) -pthread -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../../../modules/juce_audio_processors/format_types/LV2_SDK -I../../../../modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS)
|
||||
JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DNDEBUG=1" "-DJUCE_DISPLAY_SPLASH_SCREEN=0" "-DJUCE_USE_DARK_SPLASH_SCREEN=1" "-DJUCE_PROJUCER_VERSION=0x70008" "-DJUCE_MODULE_AVAILABLE_juce_analytics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1" "-DJUCE_MODULE_AVAILABLE_juce_core=1" "-DJUCE_MODULE_AVAILABLE_juce_cryptography=1" "-DJUCE_MODULE_AVAILABLE_juce_data_structures=1" "-DJUCE_MODULE_AVAILABLE_juce_dsp=1" "-DJUCE_MODULE_AVAILABLE_juce_events=1" "-DJUCE_MODULE_AVAILABLE_juce_graphics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1" "-DJUCE_MODULE_AVAILABLE_juce_midi_ci=1" "-DJUCE_MODULE_AVAILABLE_juce_opengl=1" "-DJUCE_MODULE_AVAILABLE_juce_osc=1" "-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1" "-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1" "-DJUCE_PLUGINHOST_VST3=1" "-DJUCE_PLUGINHOST_LV2=1" "-DJUCE_STRICT_REFCOUNTEDPOINTER=1" "-DJUCE_STANDALONE_APPLICATION=1" "-DJUCE_UNIT_TESTS=1" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000" $(shell $(PKG_CONFIG) --cflags alsa freetype2 gl libcurl webkit2gtk-4.0 gtk+-x11-3.0) -pthread -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../../../modules/juce_audio_processors/format_types/LV2_SDK -I../../../../modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS)
|
||||
JUCE_CPPFLAGS_CONSOLEAPP := "-DJucePlugin_Build_VST=0" "-DJucePlugin_Build_VST3=0" "-DJucePlugin_Build_AU=0" "-DJucePlugin_Build_AUv3=0" "-DJucePlugin_Build_AAX=0" "-DJucePlugin_Build_Standalone=0" "-DJucePlugin_Build_Unity=0" "-DJucePlugin_Build_LV2=0"
|
||||
JUCE_TARGET_CONSOLEAPP := UnitTestRunner
|
||||
|
||||
|
|
@ -89,6 +89,7 @@ OBJECTS_CONSOLEAPP := \
|
|||
$(JUCE_OBJDIR)/include_juce_graphics_f817e147.o \
|
||||
$(JUCE_OBJDIR)/include_juce_gui_basics_e3f79785.o \
|
||||
$(JUCE_OBJDIR)/include_juce_gui_extra_6dee1c1a.o \
|
||||
$(JUCE_OBJDIR)/include_juce_midi_ci_1fda4092.o \
|
||||
$(JUCE_OBJDIR)/include_juce_opengl_a8a032b.o \
|
||||
$(JUCE_OBJDIR)/include_juce_osc_f3df604d.o \
|
||||
$(JUCE_OBJDIR)/include_juce_product_unlocking_8278fcdc.o \
|
||||
|
|
@ -191,6 +192,11 @@ $(JUCE_OBJDIR)/include_juce_gui_extra_6dee1c1a.o: ../../JuceLibraryCode/include_
|
|||
@echo "Compiling include_juce_gui_extra.cpp"
|
||||
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_CONSOLEAPP) $(JUCE_CFLAGS_CONSOLEAPP) -o "$@" -c "$<"
|
||||
|
||||
$(JUCE_OBJDIR)/include_juce_midi_ci_1fda4092.o: ../../JuceLibraryCode/include_juce_midi_ci.cpp
|
||||
-$(V_AT)mkdir -p $(@D)
|
||||
@echo "Compiling include_juce_midi_ci.cpp"
|
||||
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_CONSOLEAPP) $(JUCE_CFLAGS_CONSOLEAPP) -o "$@" -c "$<"
|
||||
|
||||
$(JUCE_OBJDIR)/include_juce_opengl_a8a032b.o: ../../JuceLibraryCode/include_juce_opengl.cpp
|
||||
-$(V_AT)mkdir -p $(@D)
|
||||
@echo "Compiling include_juce_opengl.cpp"
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
A70F7F4891DB1CF67653BE74 /* Accelerate.framework */ = {isa = PBXBuildFile; fileRef = B38A1AC42B002115350C0268; };
|
||||
AA207299991F85938465BF65 /* Cocoa.framework */ = {isa = PBXBuildFile; fileRef = 2030A589A9355FE6A0F72428; };
|
||||
AF1FE82A4A20DCB8944B35C7 /* include_juce_gui_extra.mm */ = {isa = PBXBuildFile; fileRef = 4195CB317C364D778AE2ADB1; };
|
||||
B407D123F08A9A8C12624ABA /* include_juce_midi_ci.cpp */ = {isa = PBXBuildFile; fileRef = 0EFA505235D959565503D537; };
|
||||
BFED026CA071070CEB87CFB5 /* include_juce_audio_basics.mm */ = {isa = PBXBuildFile; fileRef = 4BD792956FE7C22CB8FB691D; };
|
||||
D17BAE3D36BB94FC2C8E2438 /* Main.cpp */ = {isa = PBXBuildFile; fileRef = 88AA2B9840A6792BBAD559EE; };
|
||||
D43289CF624A7B068237C192 /* include_juce_gui_basics.mm */ = {isa = PBXBuildFile; fileRef = 583EA0E5C4B75A629AEF1157; };
|
||||
|
|
@ -52,6 +53,7 @@
|
|||
05501801BF6C4A47598C59E2 /* juce_cryptography */ /* juce_cryptography */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_cryptography; path = ../../../../modules/juce_cryptography; sourceTree = SOURCE_ROOT; };
|
||||
080EAB9CF5AB2BD6B2BBB173 /* ConsoleApp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = UnitTestRunner; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
08ED235CBE02E0FB4BE4653E /* include_juce_cryptography.mm */ /* include_juce_cryptography.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_cryptography.mm; path = ../../JuceLibraryCode/include_juce_cryptography.mm; sourceTree = SOURCE_ROOT; };
|
||||
0EFA505235D959565503D537 /* include_juce_midi_ci.cpp */ /* include_juce_midi_ci.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = include_juce_midi_ci.cpp; path = ../../JuceLibraryCode/include_juce_midi_ci.cpp; sourceTree = SOURCE_ROOT; };
|
||||
1CA82C74AEC08421812BDCAC /* include_juce_opengl.mm */ /* include_juce_opengl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_opengl.mm; path = ../../JuceLibraryCode/include_juce_opengl.mm; sourceTree = SOURCE_ROOT; };
|
||||
1DC921E6494548F5E73E1056 /* juce_graphics */ /* juce_graphics */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_graphics; path = ../../../../modules/juce_graphics; sourceTree = SOURCE_ROOT; };
|
||||
2030A589A9355FE6A0F72428 /* Cocoa.framework */ /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
|
|
@ -82,6 +84,7 @@
|
|||
88AA2B9840A6792BBAD559EE /* Main.cpp */ /* Main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Main.cpp; path = ../../Source/Main.cpp; sourceTree = SOURCE_ROOT; };
|
||||
8C449538B266A891147103D6 /* IOKit.framework */ /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
|
||||
8EBA9CF0874619A8FA0B4E74 /* juce_osc */ /* juce_osc */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_osc; path = ../../../../modules/juce_osc; sourceTree = SOURCE_ROOT; };
|
||||
8EC828FBFEC92A64A135467C /* juce_midi_ci */ /* juce_midi_ci */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_midi_ci; path = ../../../../modules/juce_midi_ci; sourceTree = SOURCE_ROOT; };
|
||||
A40A2A0B2841A622C53047CD /* juce_audio_processors */ /* juce_audio_processors */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_processors; path = ../../../../modules/juce_audio_processors; sourceTree = SOURCE_ROOT; };
|
||||
A59D9064C3A2D7EC3DC45420 /* include_juce_osc.cpp */ /* include_juce_osc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = include_juce_osc.cpp; path = ../../JuceLibraryCode/include_juce_osc.cpp; sourceTree = SOURCE_ROOT; };
|
||||
A76DD7182C290A9020C96CA7 /* include_juce_audio_formats.mm */ /* include_juce_audio_formats.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_formats.mm; path = ../../JuceLibraryCode/include_juce_audio_formats.mm; sourceTree = SOURCE_ROOT; };
|
||||
|
|
@ -171,6 +174,7 @@
|
|||
EECBAA403D2D6AEEA8CB05EB,
|
||||
583EA0E5C4B75A629AEF1157,
|
||||
4195CB317C364D778AE2ADB1,
|
||||
0EFA505235D959565503D537,
|
||||
1CA82C74AEC08421812BDCAC,
|
||||
A59D9064C3A2D7EC3DC45420,
|
||||
B96EC82EC3D2813B50386198,
|
||||
|
|
@ -241,6 +245,7 @@
|
|||
1DC921E6494548F5E73E1056,
|
||||
DD849A04E38279B842EDE213,
|
||||
2A163F48282EEE95B8A8BA7A,
|
||||
8EC828FBFEC92A64A135467C,
|
||||
CC27F53A76BFB2675D2683A1,
|
||||
8EBA9CF0874619A8FA0B4E74,
|
||||
748F996DD2778AD1442AECA6,
|
||||
|
|
@ -327,6 +332,7 @@
|
|||
A1A39E64F9E03EFFA10B0A10,
|
||||
D43289CF624A7B068237C192,
|
||||
AF1FE82A4A20DCB8944B35C7,
|
||||
B407D123F08A9A8C12624ABA,
|
||||
1D06F1A254F84A7AE3E90DF2,
|
||||
7164274FE42C7EC423455E05,
|
||||
1B09834E81EAF5BCB87FAAF4,
|
||||
|
|
@ -416,6 +422,7 @@
|
|||
"JUCE_MODULE_AVAILABLE_juce_graphics=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_gui_basics=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_gui_extra=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_midi_ci=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_opengl=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_osc=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_product_unlocking=1",
|
||||
|
|
@ -548,6 +555,7 @@
|
|||
"JUCE_MODULE_AVAILABLE_juce_graphics=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_gui_basics=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_gui_extra=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_midi_ci=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_opengl=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_osc=1",
|
||||
"JUCE_MODULE_AVAILABLE_juce_product_unlocking=1",
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
<Optimization>Disabled</Optimization>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)\UnitTestRunner.exe</OutputFile>
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
<ClCompile>
|
||||
<Optimization>Full</Optimization>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)\UnitTestRunner.exe</OutputFile>
|
||||
|
|
@ -2525,6 +2525,42 @@
|
|||
<ClCompile Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_opengl\opengl\juce_gl.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
|
@ -2631,6 +2667,7 @@
|
|||
<AdditionalOptions> /bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_midi_ci.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_opengl.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_osc.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_product_unlocking.cpp"/>
|
||||
|
|
@ -2650,8 +2687,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
@ -3634,6 +3673,37 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\misc\juce_WebBrowserComponent.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\native\juce_NSViewFrameWatcher_mac.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIChannelAddress.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceFeatures.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceListener.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceMessageHandler.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceOptions.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncoding.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIFunctionBlock.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMessages.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMuid.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileAtAddress.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileDelegate.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeResult.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderDelegate.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISubscription.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISupportedAndActive.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMarshalling.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageMeta.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageTypeUtils.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyHostUtils.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Draggable3DOrientation.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Matrix3D.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Quaternion.h"/>
|
||||
|
|
|
|||
|
|
@ -623,6 +623,15 @@
|
|||
<Filter Include="JUCE Modules\juce_gui_extra">
|
||||
<UniqueIdentifier>{A4D76113-9EDC-DA60-D89B-5BACF7F1C426}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_midi_ci\ci">
|
||||
<UniqueIdentifier>{EC0A49B5-F336-1F4D-6C32-40E19BE1426F}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_midi_ci\detail">
|
||||
<UniqueIdentifier>{39CDBE58-7B8F-B367-DAE9-BCA326A4C637}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_midi_ci">
|
||||
<UniqueIdentifier>{F64F0BED-92DA-A4AE-0A76-9AC4FC01C199}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_opengl\geometry">
|
||||
<UniqueIdentifier>{1A9221A3-E993-70B2-6EA2-8E1DB5FF646A}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
|
@ -3196,6 +3205,42 @@
|
|||
<ClCompile Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.mm">
|
||||
<Filter>JUCE Modules\juce_gui_extra</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_opengl\opengl\juce_gl.cpp">
|
||||
<Filter>JUCE Modules\juce_opengl\opengl</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -3334,6 +3379,9 @@
|
|||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp">
|
||||
<Filter>JUCE Library Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_midi_ci.cpp">
|
||||
<Filter>JUCE Library Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_opengl.cpp">
|
||||
<Filter>JUCE Library Code</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -3387,12 +3435,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -6339,6 +6393,99 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.h">
|
||||
<Filter>JUCE Modules\juce_gui_extra</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIChannelAddress.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceFeatures.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceListener.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceMessageHandler.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceOptions.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncoding.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIFunctionBlock.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMessages.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMuid.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileAtAddress.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileDelegate.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeResult.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderDelegate.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISubscription.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISupportedAndActive.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMarshalling.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageMeta.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageTypeUtils.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyHostUtils.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Draggable3DOrientation.h">
|
||||
<Filter>JUCE Modules\juce_opengl\geometry</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
<Optimization>Disabled</Optimization>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)\UnitTestRunner.exe</OutputFile>
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
<ClCompile>
|
||||
<Optimization>Full</Optimization>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)\UnitTestRunner.exe</OutputFile>
|
||||
|
|
@ -2525,6 +2525,42 @@
|
|||
<ClCompile Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_opengl\opengl\juce_gl.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
|
@ -2631,6 +2667,7 @@
|
|||
<AdditionalOptions> /bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_midi_ci.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_opengl.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_osc.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_product_unlocking.cpp"/>
|
||||
|
|
@ -2650,8 +2687,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
@ -3634,6 +3673,37 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\misc\juce_WebBrowserComponent.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\native\juce_NSViewFrameWatcher_mac.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIChannelAddress.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceFeatures.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceListener.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceMessageHandler.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceOptions.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncoding.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIFunctionBlock.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMessages.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMuid.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileAtAddress.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileDelegate.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeResult.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderDelegate.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISubscription.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISupportedAndActive.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMarshalling.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageMeta.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageTypeUtils.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyHostUtils.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Draggable3DOrientation.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Matrix3D.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Quaternion.h"/>
|
||||
|
|
|
|||
|
|
@ -623,6 +623,15 @@
|
|||
<Filter Include="JUCE Modules\juce_gui_extra">
|
||||
<UniqueIdentifier>{A4D76113-9EDC-DA60-D89B-5BACF7F1C426}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_midi_ci\ci">
|
||||
<UniqueIdentifier>{EC0A49B5-F336-1F4D-6C32-40E19BE1426F}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_midi_ci\detail">
|
||||
<UniqueIdentifier>{39CDBE58-7B8F-B367-DAE9-BCA326A4C637}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_midi_ci">
|
||||
<UniqueIdentifier>{F64F0BED-92DA-A4AE-0A76-9AC4FC01C199}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_opengl\geometry">
|
||||
<UniqueIdentifier>{1A9221A3-E993-70B2-6EA2-8E1DB5FF646A}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
|
@ -3196,6 +3205,42 @@
|
|||
<ClCompile Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.mm">
|
||||
<Filter>JUCE Modules\juce_gui_extra</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_opengl\opengl\juce_gl.cpp">
|
||||
<Filter>JUCE Modules\juce_opengl\opengl</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -3334,6 +3379,9 @@
|
|||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp">
|
||||
<Filter>JUCE Library Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_midi_ci.cpp">
|
||||
<Filter>JUCE Library Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_opengl.cpp">
|
||||
<Filter>JUCE Library Code</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -3387,12 +3435,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -6339,6 +6393,99 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.h">
|
||||
<Filter>JUCE Modules\juce_gui_extra</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIChannelAddress.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceFeatures.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceListener.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceMessageHandler.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceOptions.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncoding.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIFunctionBlock.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMessages.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMuid.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileAtAddress.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileDelegate.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeResult.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderDelegate.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISubscription.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISupportedAndActive.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMarshalling.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageMeta.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageTypeUtils.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyHostUtils.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Draggable3DOrientation.h">
|
||||
<Filter>JUCE Modules\juce_opengl\geometry</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
<Optimization>Disabled</Optimization>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)\UnitTestRunner.exe</OutputFile>
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
<ClCompile>
|
||||
<Optimization>Full</Optimization>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;_WINDOWS;NDEBUG;JUCE_DISPLAY_SPLASH_SCREEN=0;JUCE_USE_DARK_SPLASH_SCREEN=1;JUCE_PROJUCER_VERSION=0x70008;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_midi_ci=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_STANDALONE_APPLICATION=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)\UnitTestRunner.exe</OutputFile>
|
||||
|
|
@ -2525,6 +2525,42 @@
|
|||
<ClCompile Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_opengl\opengl\juce_gl.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
|
@ -2631,6 +2667,7 @@
|
|||
<AdditionalOptions> /bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_midi_ci.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_opengl.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_osc.cpp"/>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_product_unlocking.cpp"/>
|
||||
|
|
@ -2650,8 +2687,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
@ -3634,6 +3673,37 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\misc\juce_WebBrowserComponent.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\native\juce_NSViewFrameWatcher_mac.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIChannelAddress.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceFeatures.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceListener.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceMessageHandler.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceOptions.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncoding.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIFunctionBlock.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMessages.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMuid.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileAtAddress.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileDelegate.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeResult.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderDelegate.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISubscription.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISupportedAndActive.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMarshalling.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageMeta.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageTypeUtils.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyHostUtils.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Draggable3DOrientation.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Matrix3D.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Quaternion.h"/>
|
||||
|
|
|
|||
|
|
@ -623,6 +623,15 @@
|
|||
<Filter Include="JUCE Modules\juce_gui_extra">
|
||||
<UniqueIdentifier>{A4D76113-9EDC-DA60-D89B-5BACF7F1C426}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_midi_ci\ci">
|
||||
<UniqueIdentifier>{EC0A49B5-F336-1F4D-6C32-40E19BE1426F}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_midi_ci\detail">
|
||||
<UniqueIdentifier>{39CDBE58-7B8F-B367-DAE9-BCA326A4C637}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_midi_ci">
|
||||
<UniqueIdentifier>{F64F0BED-92DA-A4AE-0A76-9AC4FC01C199}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="JUCE Modules\juce_opengl\geometry">
|
||||
<UniqueIdentifier>{1A9221A3-E993-70B2-6EA2-8E1DB5FF646A}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
|
@ -3196,6 +3205,42 @@
|
|||
<ClCompile Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.mm">
|
||||
<Filter>JUCE Modules\juce_gui_extra</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.cpp">
|
||||
<Filter>JUCE Modules\juce_midi_ci</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\modules\juce_opengl\opengl\juce_gl.cpp">
|
||||
<Filter>JUCE Modules\juce_opengl\opengl</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -3334,6 +3379,9 @@
|
|||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp">
|
||||
<Filter>JUCE Library Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_midi_ci.cpp">
|
||||
<Filter>JUCE Library Code</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\JuceLibraryCode\include_juce_opengl.cpp">
|
||||
<Filter>JUCE Library Code</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -3387,12 +3435,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -6339,6 +6393,99 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_gui_extra\juce_gui_extra.h">
|
||||
<Filter>JUCE Modules\juce_gui_extra</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIChannelAddress.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDevice.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceFeatures.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceListener.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceMessageHandler.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIDeviceOptions.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncoding.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIEncodings.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIFunctionBlock.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMessages.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIMuid.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIParser.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileAtAddress.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileDelegate.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileHost.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIProfileStates.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyDelegate.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeCache.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyExchangeResult.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIPropertyHost.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderDelegate.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CIResponderOutput.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISubscription.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\ci\juce_CISupportedAndActive.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMarshalling.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageMeta.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIMessageTypeUtils.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyDataMessageChunker.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIPropertyHostUtils.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\detail\juce_CIResponder.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci\detail</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_midi_ci\juce_midi_ci.h">
|
||||
<Filter>JUCE Modules\juce_midi_ci</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\geometry\juce_Draggable3DOrientation.h">
|
||||
<Filter>JUCE Modules\juce_opengl\geometry</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ target_link_libraries(UnitTestRunner PRIVATE
|
|||
juce::juce_analytics
|
||||
juce::juce_audio_utils
|
||||
juce::juce_dsp
|
||||
juce::juce_midi_ci
|
||||
juce::juce_opengl
|
||||
juce::juce_osc
|
||||
juce::juce_product_unlocking
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <juce_graphics/juce_graphics.h>
|
||||
#include <juce_gui_basics/juce_gui_basics.h>
|
||||
#include <juce_gui_extra/juce_gui_extra.h>
|
||||
#include <juce_midi_ci/juce_midi_ci.h>
|
||||
#include <juce_opengl/juce_opengl.h>
|
||||
#include <juce_osc/juce_osc.h>
|
||||
#include <juce_product_unlocking/juce_product_unlocking.h>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
|
||||
IMPORTANT! This file is auto-generated each time you save your
|
||||
project - if you alter its contents, your changes may be overwritten!
|
||||
|
||||
*/
|
||||
|
||||
#include <juce_midi_ci/juce_midi_ci.cpp>
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
<MODULEPATH id="juce_audio_utils" path="../../modules"/>
|
||||
<MODULEPATH id="juce_product_unlocking" path="../../modules"/>
|
||||
<MODULEPATH id="juce_analytics" path="../../modules"/>
|
||||
<MODULEPATH id="juce_midi_ci" path="../../modules"/>
|
||||
</MODULEPATHS>
|
||||
</XCODE_MAC>
|
||||
<LINUX_MAKE targetFolder="Builds/LinuxMakefile">
|
||||
|
|
@ -59,6 +60,7 @@
|
|||
<MODULEPATH id="juce_audio_utils" path="../../modules"/>
|
||||
<MODULEPATH id="juce_product_unlocking" path="../../modules"/>
|
||||
<MODULEPATH id="juce_analytics" path="../../modules"/>
|
||||
<MODULEPATH id="juce_midi_ci" path="../../modules"/>
|
||||
</MODULEPATHS>
|
||||
</LINUX_MAKE>
|
||||
<VS2017 targetFolder="Builds/VisualStudio2017" extraCompilerFlags="/w44265 /w45038 /w44062">
|
||||
|
|
@ -85,6 +87,7 @@
|
|||
<MODULEPATH id="juce_audio_devices" path="../../modules"/>
|
||||
<MODULEPATH id="juce_audio_basics" path="../../modules"/>
|
||||
<MODULEPATH id="juce_analytics" path="../../modules"/>
|
||||
<MODULEPATH id="juce_midi_ci" path="../../modules"/>
|
||||
</MODULEPATHS>
|
||||
</VS2017>
|
||||
<VS2019 targetFolder="Builds/VisualStudio2019" extraCompilerFlags="/w44265 /w45038 /w44062">
|
||||
|
|
@ -111,6 +114,7 @@
|
|||
<MODULEPATH id="juce_audio_devices" path="../../modules"/>
|
||||
<MODULEPATH id="juce_audio_basics" path="../../modules"/>
|
||||
<MODULEPATH id="juce_analytics" path="../../modules"/>
|
||||
<MODULEPATH id="juce_midi_ci" path="../../modules"/>
|
||||
</MODULEPATHS>
|
||||
</VS2019>
|
||||
<VS2022 targetFolder="Builds/VisualStudio2022" extraCompilerFlags="/w44265 /w45038 /w44062">
|
||||
|
|
@ -137,6 +141,7 @@
|
|||
<MODULEPATH id="juce_audio_devices" path="../../modules"/>
|
||||
<MODULEPATH id="juce_audio_basics" path="../../modules"/>
|
||||
<MODULEPATH id="juce_analytics" path="../../modules"/>
|
||||
<MODULEPATH id="juce_midi_ci" path="../../modules"/>
|
||||
</MODULEPATHS>
|
||||
</VS2022>
|
||||
</EXPORTFORMATS>
|
||||
|
|
@ -155,6 +160,7 @@
|
|||
<MODULES id="juce_graphics" showAllCode="1" useLocalCopy="0"/>
|
||||
<MODULES id="juce_gui_basics" showAllCode="1" useLocalCopy="0"/>
|
||||
<MODULES id="juce_gui_extra" showAllCode="1" useLocalCopy="0"/>
|
||||
<MODULE id="juce_midi_ci" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
|
||||
<MODULES id="juce_opengl" showAllCode="1" useLocalCopy="0"/>
|
||||
<MODULES id="juce_osc" showAllCode="1" useLocalCopy="0"/>
|
||||
<MODULE id="juce_product_unlocking" showAllCode="1" useLocalCopy="0"
|
||||
|
|
|
|||
|
|
@ -2481,8 +2481,10 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMP.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPacket.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPFactory.h"/>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPIterator.h"/>
|
||||
|
|
|
|||
|
|
@ -3156,12 +3156,18 @@
|
|||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPackets.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPBytesOnGroup.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConversion.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPConverters.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDeviceInfo.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\midi\ump\juce_UMPDispatcher.h">
|
||||
<Filter>JUCE Modules\juce_audio_basics\midi\ump</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ juce_add_modules(
|
|||
juce_graphics
|
||||
juce_gui_basics
|
||||
juce_gui_extra
|
||||
juce_midi_ci
|
||||
juce_opengl
|
||||
juce_osc
|
||||
juce_product_unlocking
|
||||
|
|
|
|||
|
|
@ -124,3 +124,10 @@ JUCE_END_IGNORE_WARNINGS_MSVC
|
|||
#include "synthesisers/juce_Synthesiser.h"
|
||||
#include "audio_play_head/juce_AudioPlayHead.h"
|
||||
#include "utilities/juce_AudioWorkgroup.h"
|
||||
#include "midi/ump/juce_UMPBytesOnGroup.h"
|
||||
#include "midi/ump/juce_UMPDeviceInfo.h"
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace ump = universal_midi_packets;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -685,6 +685,11 @@ MidiMessage MidiMessage::createSysExMessage (const void* sysexData, const int da
|
|||
return MidiMessage (m, dataSize + 2);
|
||||
}
|
||||
|
||||
MidiMessage MidiMessage::createSysExMessage (Span<const std::byte> data)
|
||||
{
|
||||
return createSysExMessage (data.data(), (int) data.size());
|
||||
}
|
||||
|
||||
const uint8* MidiMessage::getSysExData() const noexcept
|
||||
{
|
||||
return isSysEx() ? getRawData() + 1 : nullptr;
|
||||
|
|
|
|||
|
|
@ -218,6 +218,13 @@ public:
|
|||
*/
|
||||
int getSysExDataSize() const noexcept;
|
||||
|
||||
/** Returns a span that bounds the sysex body bytes contained in this message. */
|
||||
Span<const std::byte> getSysExDataSpan() const noexcept
|
||||
{
|
||||
return { reinterpret_cast<const std::byte*> (getSysExData()),
|
||||
(size_t) getSysExDataSize() };
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this message is a 'key-down' event.
|
||||
|
||||
|
|
@ -855,6 +862,10 @@ public:
|
|||
static MidiMessage createSysExMessage (const void* sysexData,
|
||||
int dataSize);
|
||||
|
||||
/** Creates a system-exclusive message.
|
||||
The data passed in is wrapped with header and tail bytes of 0xf0 and 0xf7.
|
||||
*/
|
||||
static MidiMessage createSysExMessage (Span<const std::byte> data);
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
|
|
|
|||
38
modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h
Normal file
38
modules/juce_audio_basics/midi/ump/juce_UMPBytesOnGroup.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::universal_midi_packets
|
||||
{
|
||||
|
||||
/**
|
||||
Holds a UMP group, and a span of bytes that were received or are to be
|
||||
sent on that group. Helpful when working with sysex messages.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct BytesOnGroup
|
||||
{
|
||||
uint8_t group{};
|
||||
Span<const std::byte> bytes;
|
||||
};
|
||||
|
||||
} // namespace juce::universal_midi_packets
|
||||
58
modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h
Normal file
58
modules/juce_audio_basics/midi/ump/juce_UMPDeviceInfo.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::universal_midi_packets
|
||||
{
|
||||
|
||||
/**
|
||||
Holds MIDI device info that may be required by certain UMP messages and
|
||||
MIDI-CI messages.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct DeviceInfo
|
||||
{
|
||||
std::array<std::byte, 3> manufacturer; ///< LSB first
|
||||
std::array<std::byte, 2> family; ///< LSB first
|
||||
std::array<std::byte, 2> modelNumber; ///< LSB first
|
||||
std::array<std::byte, 4> revision;
|
||||
|
||||
private:
|
||||
auto tie() const { return std::tie (manufacturer, family, modelNumber, revision); }
|
||||
|
||||
public:
|
||||
bool operator== (const DeviceInfo& other) const { return tie() == other.tie(); }
|
||||
bool operator!= (const DeviceInfo& other) const { return tie() != other.tie(); }
|
||||
|
||||
static constexpr auto marshallingVersion = std::nullopt;
|
||||
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
return archive (named ("manufacturer", t.manufacturer),
|
||||
named ("family", t.family),
|
||||
named ("modelNumber", t.modelNumber),
|
||||
named ("revision", t.revision));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce::universal_midi_packets
|
||||
83
modules/juce_midi_ci/ci/juce_CIChannelAddress.h
Normal file
83
modules/juce_midi_ci/ci/juce_CIChannelAddress.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Identifies a channel or set of channels in a multi-group MIDI endpoint.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class ChannelAddress
|
||||
{
|
||||
private:
|
||||
uint8_t group{}; ///< A group within a MIDI endpoint, where 0 <= group && group < 16
|
||||
ChannelInGroup channel{}; ///< A set of channels related to specified group
|
||||
|
||||
auto tie() const { return std::tie (group, channel); }
|
||||
|
||||
public:
|
||||
/** Returns a copy of this object with the specified group. */
|
||||
[[nodiscard]] ChannelAddress withGroup (int g) const
|
||||
{
|
||||
jassert (isPositiveAndBelow (g, 16));
|
||||
return withMember (*this, &ChannelAddress::group, (uint8_t) g);
|
||||
}
|
||||
|
||||
/** Returns a copy of this object with the specified channel. */
|
||||
[[nodiscard]] ChannelAddress withChannel (ChannelInGroup c) const
|
||||
{
|
||||
return withMember (*this, &ChannelAddress::channel, c);
|
||||
}
|
||||
|
||||
/** Returns the group. */
|
||||
[[nodiscard]] uint8_t getGroup() const { return group; }
|
||||
|
||||
/** Returns the channel in the group. */
|
||||
[[nodiscard]] ChannelInGroup getChannel() const { return channel; }
|
||||
|
||||
/** Returns true if this address refers to all channels in the function
|
||||
block containing the specified group.
|
||||
*/
|
||||
bool isBlock() const { return channel == ChannelInGroup::wholeBlock; }
|
||||
|
||||
/** Returns true if this address refers to all channels in the specified
|
||||
group.
|
||||
*/
|
||||
bool isGroup() const { return channel == ChannelInGroup::wholeGroup; }
|
||||
|
||||
/** Returns true if this address refers to a single channel. */
|
||||
bool isSingleChannel() const { return ! isBlock() && ! isGroup(); }
|
||||
|
||||
bool operator< (const ChannelAddress& other) const { return tie() < other.tie(); }
|
||||
bool operator<= (const ChannelAddress& other) const { return tie() <= other.tie(); }
|
||||
bool operator> (const ChannelAddress& other) const { return tie() > other.tie(); }
|
||||
bool operator>= (const ChannelAddress& other) const { return tie() >= other.tie(); }
|
||||
bool operator== (const ChannelAddress& other) const { return tie() == other.tie(); }
|
||||
bool operator!= (const ChannelAddress& other) const { return ! operator== (other); }
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
2369
modules/juce_midi_ci/ci/juce_CIDevice.cpp
Normal file
2369
modules/juce_midi_ci/ci/juce_CIDevice.cpp
Normal file
File diff suppressed because it is too large
Load diff
319
modules/juce_midi_ci/ci/juce_CIDevice.h
Normal file
319
modules/juce_midi_ci/ci/juce_CIDevice.h
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Instances of this type are responsible for parsing and interpreting incoming
|
||||
MIDI-CI messages, and for sending MIDI-CI messages to other devices.
|
||||
|
||||
Each Device can act both as a target for messages, and as a source of
|
||||
messages intended to inspect/configure other devices.
|
||||
|
||||
The member functions of Device are generally used to inspect other
|
||||
devices. Member functions starting with 'send' are used to send or request
|
||||
information from other devices; registered DeviceListeners will be notified
|
||||
when the Device receives a response, and then member functions named
|
||||
matching 'get.*ForMuid' can be used to retrieve the result of the inquiry.
|
||||
|
||||
If the Device does not have local profiles or properties, then responses
|
||||
to all incoming messages will be generated automatically using the
|
||||
information supplied during construction.
|
||||
|
||||
If the Device has profiles or properties, then you should implement a
|
||||
ProfileDelegate and/or a PropertyDelegate as appropriate, and pass this
|
||||
delegate during construction. Each Delegate will receive callbacks when a
|
||||
remote device makes a request of the local device, such as
|
||||
enabling/disabling a profile, or setting/getting property data.
|
||||
|
||||
Sometimes the local device must send notifications when
|
||||
updating its profile or property state, for example when profiles are
|
||||
added, or when a subscribed property is changed. Methods to send these
|
||||
notifications are found on the ProfileHost and PropertyHost classes.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class Device : public DeviceMessageHandler
|
||||
{
|
||||
public:
|
||||
using Features = DeviceFeatures;
|
||||
using Listener = DeviceListener;
|
||||
using Options = DeviceOptions;
|
||||
|
||||
/** Constructs a device using the provided options. */
|
||||
explicit Device (const Options& opt);
|
||||
|
||||
Device (Device&&) noexcept;
|
||||
Device& operator= (Device&&) noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Device)
|
||||
|
||||
/** Destructor, sends a message to invalidate this device's MUID. */
|
||||
~Device() override;
|
||||
|
||||
//==============================================================================
|
||||
/** To be called with any message that should be processed by the device.
|
||||
This should only be passed complete CI messages - you might find the Extractor
|
||||
class useful for parsing a stream of Universal MIDI Packets and extracting the
|
||||
CI messages.
|
||||
Note that this function does *not* synchronise with any other member function of this
|
||||
class. This means that you must not call this directly from the MIDI input thread if there's
|
||||
any chance of other member functions being called on the same instance simultaneously from
|
||||
other threads.
|
||||
It's probably easiest to send all messages onto the main thread and to limit interactions
|
||||
with the Device to that thread.
|
||||
*/
|
||||
void processMessage (ump::BytesOnGroup) override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sends an inquiry message.
|
||||
|
||||
You can use DeviceListener::deviceAdded to be notified when new devices are discovered.
|
||||
|
||||
This will clear the internal cache of discovered devices, and repopulate it as discovery
|
||||
response messages are received.
|
||||
*/
|
||||
void sendDiscovery();
|
||||
|
||||
/** Sends an endpoint inquiry message.
|
||||
|
||||
Check the MIDI-CI spec for an explanation of the different endpoint message status codes.
|
||||
|
||||
Received responses will be sent to DeviceListener::endpointReceived. Responses are not
|
||||
cached by the Device; if you need to cache endpoint responses, you can keep your own
|
||||
map of MUID->response, update it in endpointReceived, and remove entries in
|
||||
DeviceListener::deviceRemoved.
|
||||
*/
|
||||
void sendEndpointInquiry (MUID destination, Message::EndpointInquiry endpoint);
|
||||
|
||||
//==============================================================================
|
||||
/** Sends a profile inquiry to a particular device.
|
||||
|
||||
DeviceListener::profileStateReceived will be called when the device replies.
|
||||
*/
|
||||
void sendProfileInquiry (MUID muid, ChannelInGroup address);
|
||||
|
||||
/** Sends a profile details inquiry to a particular device.
|
||||
|
||||
DeviceListener::profileDetailsReceived will be called when the device replies.
|
||||
*/
|
||||
void sendProfileDetailsInquiry (MUID muid, ChannelInGroup address, Profile profile, std::byte target);
|
||||
|
||||
/** Sends profile data to a particular device. */
|
||||
void sendProfileSpecificData (MUID muid, ChannelInGroup address, Profile profile, Span<const std::byte>);
|
||||
|
||||
/** Sets a profile on or off. Pass 0 or less to disable the profile, or a positive number to enable it.
|
||||
*/
|
||||
void sendProfileEnablement (MUID muid, ChannelInGroup address, Profile profile, int numChannels);
|
||||
|
||||
//==============================================================================
|
||||
/** Sends a property inquiry to a particular device.
|
||||
If the device supports properties, this will also automatically request the ResourceList
|
||||
property, and then the ChannelList and DeviceInfo properties if they are present in the
|
||||
ResourceList.
|
||||
*/
|
||||
void sendPropertyCapabilitiesInquiry (MUID destination);
|
||||
|
||||
/** Sends an inquiry to get a property value from another device, invoking a callback once
|
||||
the full transaction has completed.
|
||||
|
||||
@param destination the device whose property will be set
|
||||
@param header information about the property data that will be sent
|
||||
@param onResult this will be called once the result of the transaction is known.
|
||||
If the transaction cannot start for some reason (e.g. the request is
|
||||
malformed, or there are too many simultaneous requests) then the
|
||||
function will be called immediately. Otherwise, the function will be
|
||||
called once the destination device has confirmed receipt of the
|
||||
inquiry.
|
||||
@return a token bounding the lifetime of the request.
|
||||
If you need to terminate the transaction before it has completed,
|
||||
you can call reset() on this token, or cause its destructor to run.
|
||||
*/
|
||||
ErasedScopeGuard sendPropertyGetInquiry (MUID destination,
|
||||
const PropertyRequestHeader& header,
|
||||
std::function<void (const PropertyExchangeResult&)> onResult);
|
||||
|
||||
/** Sends an inquiry to set a property value on another device, invoking a callback once
|
||||
the full transaction has completed.
|
||||
|
||||
@param destination the device whose property will be set
|
||||
@param header information about the property data that will be sent
|
||||
@param body the property data payload to send.
|
||||
If the header specifies 'ascii' encoding, then you are responsible
|
||||
for ensuring that no byte of the payload data has its most
|
||||
significant bit set. Sending the message will fail if this is not
|
||||
the case. Otherwise, if another encoding is specified then the
|
||||
payload data may contain any byte values. You should not attempt to
|
||||
encode the data yourself; the payload will be automatically encoded
|
||||
before being sent.
|
||||
@param onResult this will be called once the result of the transaction is known.
|
||||
If the transaction cannot start for some reason (e.g. the
|
||||
destination does not support property exchange, the request is
|
||||
malformed, or there are too many simultaneous requests) then the
|
||||
function will be called immediately. Otherwise, the function will be
|
||||
called once the destination device has confirmed receipt of the
|
||||
inquiry.
|
||||
*/
|
||||
void sendPropertySetInquiry (MUID destination,
|
||||
const PropertyRequestHeader& header,
|
||||
Span<const std::byte> body,
|
||||
std::function<void (const PropertyExchangeResult&)> onResult);
|
||||
|
||||
/** Sends an inquiry to start a subscription to a property on a device.
|
||||
The provided callback will be called to indicate whether starting the subscription
|
||||
succeeded or failed.
|
||||
When the remote device indicates that its property value has changed,
|
||||
DeviceListener::propertySubscriptionReceived will be called with information about the
|
||||
update.
|
||||
*/
|
||||
void sendPropertySubscriptionStart (MUID,
|
||||
const PropertySubscriptionHeader& header,
|
||||
std::function<void (const PropertyExchangeResult&)>);
|
||||
|
||||
/** Sends an inquiry to end a subscription to a property on a device.
|
||||
The provided callback will be called to indicate whether the subscriber acknowledged
|
||||
receipt of the message.
|
||||
Note that the remote device may also choose to terminate the subscription of its own
|
||||
accord - in this case, the end request will be sent to
|
||||
DeviceListener::propertySubscriptionReceived.
|
||||
*/
|
||||
void sendPropertySubscriptionEnd (MUID,
|
||||
const String& subscribeId,
|
||||
std::function<void (const PropertyExchangeResult&)>);
|
||||
|
||||
/** Returns all of the subscriptions that we have requested from another device.
|
||||
|
||||
Does *not* include subscriptions that other devices have requested from us.
|
||||
*/
|
||||
std::vector<Subscription> getOngoingSubscriptionsForMuid (MUID m) const;
|
||||
|
||||
/** Returns the number of transactions initiated by us that are yet to receive complete replies.
|
||||
|
||||
Does *not* include the count of unfinished requests addressed to us by other devices.
|
||||
|
||||
@see PropertyHost::countOngoingTransactions()
|
||||
*/
|
||||
int countOngoingPropertyTransactions() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a listener that will be notified when particular events occur.
|
||||
|
||||
Check the members of the Listener class to see the kinds of events that are reported.
|
||||
To receive notifications through Listener::propertySubscriptionReceived(), you must
|
||||
first request a subscription using sendPropertySubscriptionStart().
|
||||
|
||||
@see Listener, removeListener()
|
||||
*/
|
||||
void addListener (Listener& l);
|
||||
|
||||
/** Removes a listener that was previously added with addListener(). */
|
||||
void removeListener (Listener& l);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the MUID currently associated with this device.
|
||||
|
||||
This may change, e.g. if another device reports that it shares the same MUID.
|
||||
*/
|
||||
MUID getMuid() const;
|
||||
|
||||
/** Returns the configuration of this device. */
|
||||
Options getOptions() const;
|
||||
|
||||
/** Returns a list of all MUIDs that have been discovered by this device. */
|
||||
std::vector<MUID> getDiscoveredMuids() const;
|
||||
|
||||
/** If you set withProfileConfigurationSupported when constructing this device, this will return
|
||||
a pointer to an object that can be used to query the states of the profiles for this device.
|
||||
*/
|
||||
const ProfileHost* getProfileHost() const;
|
||||
|
||||
/** If you set withProfileConfigurationSupported when constructing this device, this will return
|
||||
a pointer to an object that can be used to modify the states of the profiles for this device.
|
||||
*/
|
||||
ProfileHost* getProfileHost();
|
||||
|
||||
/** If you set withPropertyExchangeSupported when constructing this device, this will return
|
||||
a pointer to an object that can be used to query the states of the properties for this device.
|
||||
*/
|
||||
const PropertyHost* getPropertyHost() const;
|
||||
|
||||
/** If you set withPropertyExchangeSupported when constructing this device, this will return
|
||||
a pointer to an object that can be used to modify the states of the properties for this device.
|
||||
*/
|
||||
PropertyHost* getPropertyHost();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns basic attributes about another device that was discovered.
|
||||
|
||||
If there's no record of the provided device, this will return nullopt.
|
||||
*/
|
||||
std::optional<Message::Discovery> getDiscoveryInfoForMuid (MUID m) const;
|
||||
|
||||
/** Returns the states of the profiles on a particular channel of a device.
|
||||
|
||||
If the state is unknown, returns nullptr.
|
||||
|
||||
Devices don't report profile capabilities unless asked; you can request capabilities
|
||||
using inquireProfile().
|
||||
*/
|
||||
const ChannelProfileStates* getProfileStateForMuid (MUID m, ChannelAddress address) const;
|
||||
|
||||
/** Returns the number of simultaneous property exchange requests supported by a particular
|
||||
device.
|
||||
|
||||
If there's no record of this device's property capabilities (including the case where
|
||||
the device doesn't support property exchange at all) this will return nullopt.
|
||||
|
||||
Devices don't report property capabilities unless asked; you can request capabilities
|
||||
using inquirePropertyCapabilities().
|
||||
*/
|
||||
std::optional<int> getNumPropertyExchangeRequestsSupportedForMuid (MUID m) const;
|
||||
|
||||
/** After DeviceListener::propertyExchangeCapabilitiesReceived() has been received for a
|
||||
particular device, this function will return that device's ResourceList if available, or
|
||||
a null var otherwise.
|
||||
*/
|
||||
var getResourceListForMuid (MUID x) const;
|
||||
|
||||
/** After DeviceListener::propertyExchangeCapabilitiesReceived() has been received for a
|
||||
particular device, this function will return that device's DeviceInfo if available, or
|
||||
a null var otherwise.
|
||||
*/
|
||||
var getDeviceInfoForMuid (MUID x) const;
|
||||
|
||||
/** After DeviceListener::propertyExchangeCapabilitiesReceived() has been received for a
|
||||
particular device, this function will return that device's ChannelList if available, or
|
||||
a null var otherwise.
|
||||
*/
|
||||
var getChannelListForMuid (MUID x) const;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> pimpl;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
90
modules/juce_midi_ci/ci/juce_CIDeviceFeatures.h
Normal file
90
modules/juce_midi_ci/ci/juce_CIDeviceFeatures.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Flags indicating the features that are supported by a given CI device.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class DeviceFeatures
|
||||
{
|
||||
public:
|
||||
/** Constructs a DeviceFeatures object with no flags enabled. */
|
||||
DeviceFeatures() = default;
|
||||
|
||||
/** Constructs a DeviceFeatures object, taking flag values from the "Capability Inquiry
|
||||
Category Supported" byte in a CI Discovery message.
|
||||
*/
|
||||
explicit DeviceFeatures (std::byte f) : flags ((uint8_t) f) {}
|
||||
|
||||
/** Returns a new DeviceFeatures instance with profile configuration marked as supported. */
|
||||
[[nodiscard]] DeviceFeatures withProfileConfigurationSupported (bool x = true) const { return withFlag (profileConfiguration, x); }
|
||||
/** Returns a new DeviceFeatures instance with property exchange marked as supported. */
|
||||
[[nodiscard]] DeviceFeatures withPropertyExchangeSupported (bool x = true) const { return withFlag (propertyExchange, x); }
|
||||
/** Returns a new DeviceFeatures instance with process inquiry marked as supported. */
|
||||
[[nodiscard]] DeviceFeatures withProcessInquirySupported (bool x = true) const { return withFlag (processInquiry, x); }
|
||||
|
||||
/** @see withProfileConfigurationSupported() */
|
||||
[[nodiscard]] bool isProfileConfigurationSupported () const { return getFlag (profileConfiguration); }
|
||||
/** @see withPropertyExchangeSupported() */
|
||||
[[nodiscard]] bool isPropertyExchangeSupported () const { return getFlag (propertyExchange); }
|
||||
/** @see withProcessInquirySupported() */
|
||||
[[nodiscard]] bool isProcessInquirySupported () const { return getFlag (processInquiry); }
|
||||
|
||||
/** Returns the feature flags formatted into a bitfield suitable for use as the "Capability
|
||||
Inquiry Category Supported" byte in a CI Discovery message.
|
||||
*/
|
||||
std::byte getSupportedCapabilities() const { return std::byte { flags }; }
|
||||
|
||||
/** Returns true if this and other both have the same flags set. */
|
||||
bool operator== (const DeviceFeatures& other) const { return flags == other.flags; }
|
||||
/** Returns true if any flags in this and other differ. */
|
||||
bool operator!= (const DeviceFeatures& other) const { return ! operator== (other); }
|
||||
|
||||
private:
|
||||
enum Flags
|
||||
{
|
||||
profileConfiguration = 1 << 2,
|
||||
propertyExchange = 1 << 3,
|
||||
processInquiry = 1 << 4,
|
||||
};
|
||||
|
||||
[[nodiscard]] DeviceFeatures withFlag (Flags f, bool value) const
|
||||
{
|
||||
return withMember (*this, &DeviceFeatures::flags, (uint8_t) (value ? (flags | f) : (flags & ~f)));
|
||||
}
|
||||
|
||||
bool getFlag (Flags f) const
|
||||
{
|
||||
return (flags & f) != 0;
|
||||
}
|
||||
|
||||
uint8_t flags = 0;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
146
modules/juce_midi_ci/ci/juce_CIDeviceListener.h
Normal file
146
modules/juce_midi_ci/ci/juce_CIDeviceListener.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Contains information relating to a subscription update. Check the header's
|
||||
subscription kind to find out whether the payload is a full update, a
|
||||
partial update, or empty (as is the case for a notification or
|
||||
subscription-end request).
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct PropertySubscriptionData
|
||||
{
|
||||
PropertySubscriptionHeader header;
|
||||
Span<const std::byte> body;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An interface that receives callbacks when certain messages are received by a Device.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct DeviceListener
|
||||
{
|
||||
DeviceListener() = default;
|
||||
virtual ~DeviceListener() = default;
|
||||
DeviceListener (const DeviceListener&) = default;
|
||||
DeviceListener (DeviceListener&&) = default;
|
||||
DeviceListener& operator= (const DeviceListener&) = default;
|
||||
DeviceListener& operator= (DeviceListener&&) = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Called to indicate that a device with the provided MUID was discovered.
|
||||
To find out more about the device, use Device::getDiscoveryInfoForMuid().
|
||||
*/
|
||||
virtual void deviceAdded ([[maybe_unused]] MUID x) {}
|
||||
|
||||
/** Called to indicate that a device's MUID was invalidated.
|
||||
If you were previously storing your own information about this device, you should forget
|
||||
that information here.
|
||||
*/
|
||||
virtual void deviceRemoved ([[maybe_unused]] MUID x) {}
|
||||
|
||||
/** Called to indicate that endpoint information was received for the given device.
|
||||
See the MIDI-CI spec for an explanation of the different status codes.
|
||||
*/
|
||||
virtual void endpointReceived ([[maybe_unused]] MUID x,
|
||||
[[maybe_unused]] Message::EndpointInquiryResponse response) {}
|
||||
|
||||
|
||||
/** Called to indicate that a NAK message was received.
|
||||
This is useful e.g. to display a diagnostic to the user, or to cache the failed request
|
||||
details and retry the request at a later date.
|
||||
|
||||
The message field of the NAK is 7-bit text. You can convert it to a string using
|
||||
Encodings::stringFrom7BitText().
|
||||
*/
|
||||
virtual void messageNotAcknowledged ([[maybe_unused]] MUID x,
|
||||
[[maybe_unused]] Message::NAK) {}
|
||||
|
||||
//==============================================================================
|
||||
/** Called to indicate that another device reported its enabled and disabled profiles on a
|
||||
particular channel.
|
||||
|
||||
@see Device::getProfileStateForMuid()
|
||||
*/
|
||||
virtual void profileStateReceived ([[maybe_unused]] MUID x,
|
||||
[[maybe_unused]] ChannelInGroup destination) {}
|
||||
|
||||
/** Called to indicate that a profile was added or removed. */
|
||||
virtual void profilePresenceChanged ([[maybe_unused]] MUID x,
|
||||
[[maybe_unused]] ChannelInGroup destination,
|
||||
[[maybe_unused]] Profile profile,
|
||||
[[maybe_unused]] bool exists) {}
|
||||
|
||||
/** Called to indicate that a profile was enabled or disabled.
|
||||
A channel count of 0 indicates that the profile was disabled.
|
||||
*/
|
||||
virtual void profileEnablementChanged ([[maybe_unused]] MUID x,
|
||||
[[maybe_unused]] ChannelInGroup destination,
|
||||
[[maybe_unused]] Profile profile,
|
||||
[[maybe_unused]] int numChannels) {}
|
||||
|
||||
/** Called to indicate that details about a profile were received. */
|
||||
virtual void profileDetailsReceived ([[maybe_unused]] MUID x,
|
||||
[[maybe_unused]] ChannelInGroup destination,
|
||||
[[maybe_unused]] Profile profile,
|
||||
[[maybe_unused]] std::byte target,
|
||||
[[maybe_unused]] Span<const std::byte> data) {}
|
||||
|
||||
/** Called to indicate that data for a profile were received.
|
||||
|
||||
Note that this function may be called either when a remote device attempts to send data to
|
||||
one of the local Device's profiles, or when a profile on a remote device produces some data.
|
||||
|
||||
Each profile will specify its own mechanism for distinguishing between the two cases if
|
||||
necessary.
|
||||
*/
|
||||
virtual void profileSpecificDataReceived ([[maybe_unused]] MUID x,
|
||||
[[maybe_unused]] ChannelInGroup destination,
|
||||
[[maybe_unused]] Profile profile,
|
||||
[[maybe_unused]] Span<const std::byte> data) {}
|
||||
|
||||
//==============================================================================
|
||||
/** Called to indicate that another device reported its property exchange capabilities.
|
||||
|
||||
@see Device::getPropertyExchangeCapabilitiesResponseForMuid()
|
||||
*/
|
||||
virtual void propertyExchangeCapabilitiesReceived ([[maybe_unused]] MUID x) {}
|
||||
|
||||
/** Called to indicate that a subscription update was received.
|
||||
This only receives messages with responder commands (partial, full, notify, end).
|
||||
|
||||
To start a subscription, use Device::sendPropertySubscriptionStart().
|
||||
*/
|
||||
virtual void propertySubscriptionDataReceived ([[maybe_unused]] MUID x,
|
||||
[[maybe_unused]] const PropertySubscriptionData& data) {}
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
53
modules/juce_midi_ci/ci/juce_CIDeviceMessageHandler.h
Normal file
53
modules/juce_midi_ci/ci/juce_CIDeviceMessageHandler.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An interface that will receive a callback every time a Device wishes to send a new MIDI-CI
|
||||
message.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct DeviceMessageHandler
|
||||
{
|
||||
DeviceMessageHandler() = default;
|
||||
virtual ~DeviceMessageHandler() = default;
|
||||
DeviceMessageHandler (const DeviceMessageHandler&) = default;
|
||||
DeviceMessageHandler (DeviceMessageHandler&&) = default;
|
||||
DeviceMessageHandler& operator= (const DeviceMessageHandler&) = default;
|
||||
DeviceMessageHandler& operator= (DeviceMessageHandler&&) = default;
|
||||
|
||||
/** Called with the bytes of a MIDI-CI message, along with the message's group.
|
||||
|
||||
To send the message on, format the message appropriately (either into bytestream sysex
|
||||
or into multiple UMP sysex packets).
|
||||
*/
|
||||
virtual void processMessage (ump::BytesOnGroup) = 0;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
167
modules/juce_midi_ci/ci/juce_CIDeviceOptions.h
Normal file
167
modules/juce_midi_ci/ci/juce_CIDeviceOptions.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Configuration options for a Device.
|
||||
|
||||
The options set here will remain constant over the lifetime of a Device.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class DeviceOptions
|
||||
{
|
||||
public:
|
||||
static constexpr auto beginValidAscii = 32; // inclusive
|
||||
static constexpr auto endValidAscii = 127; // exclusive
|
||||
|
||||
/** Creates a random product instance ID.
|
||||
This isn't really recommended - it's probably better to have a unique ID that remains
|
||||
persistent after a restart.
|
||||
*/
|
||||
static std::array<char, 16> makeProductInstanceId (Random& random)
|
||||
{
|
||||
std::array<char, 16> result{};
|
||||
|
||||
for (auto& c : result)
|
||||
c = (char) random.nextInt ({ beginValidAscii, endValidAscii });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** One or more DeviceMessageHandlers that should receive callbacks with any messages that the
|
||||
device wishes to send.
|
||||
Referenced DeviceMessageHandlers *must* outlive any Device constructed from these options.
|
||||
*/
|
||||
[[nodiscard]] DeviceOptions withOutputs (std::vector<DeviceMessageHandler*> x) const
|
||||
{
|
||||
return withMember (*this, &DeviceOptions::outputs, x);
|
||||
}
|
||||
|
||||
/** The function block layout of this device. */
|
||||
[[nodiscard]] DeviceOptions withFunctionBlock (FunctionBlock x) const
|
||||
{
|
||||
return withMember (*this, &DeviceOptions::functionBlock, x);
|
||||
}
|
||||
|
||||
/** Basic information about the device used to determine manufacturer, model, etc.
|
||||
In order to populate this correctly, you'll need to register with the MIDI association -
|
||||
otherwise you might accidentally end up using IDs that are already assigned to other
|
||||
companies/individuals.
|
||||
*/
|
||||
[[nodiscard]] DeviceOptions withDeviceInfo (const ump::DeviceInfo& x) const
|
||||
{
|
||||
return withMember (*this, &DeviceOptions::deviceInfo, x);
|
||||
}
|
||||
|
||||
/** The features that you want to enable on the device.
|
||||
|
||||
If you enable property exchange, you may wish to supply a PropertyDelegate using
|
||||
withPropertyDelegate().
|
||||
If you enable profile configuration, you may wish to supply a ProfileDelegate using
|
||||
withProfileDelegate().
|
||||
Process inquiry is not currently supported.
|
||||
*/
|
||||
[[nodiscard]] DeviceOptions withFeatures (DeviceFeatures x) const
|
||||
{
|
||||
return withMember (*this, &DeviceOptions::features, x);
|
||||
}
|
||||
|
||||
/** The maximum size of sysex messages to accept and to produce. */
|
||||
[[nodiscard]] DeviceOptions withMaxSysExSize (size_t x) const
|
||||
{
|
||||
return withMember (*this, &DeviceOptions::maxSysExSize, x);
|
||||
}
|
||||
|
||||
/** Specifies a profile delegate that can be used to respond to particular profile events.
|
||||
The referenced ProfileDelegate *must* outlive the Device.
|
||||
*/
|
||||
[[nodiscard]] DeviceOptions withProfileDelegate (ProfileDelegate* x) const
|
||||
{
|
||||
return withMember (*this, &DeviceOptions::profileDelegate, x);
|
||||
}
|
||||
|
||||
/** Specifies a property delegate that can be used to respond to particular property events.
|
||||
The referenced PropertyDelegate *must* outlive the Device.
|
||||
*/
|
||||
[[nodiscard]] DeviceOptions withPropertyDelegate (PropertyDelegate* x) const
|
||||
{
|
||||
return withMember (*this, &DeviceOptions::propertyDelegate, x);
|
||||
}
|
||||
|
||||
/** Specifies a product instance ID that will be returned in endpoint response messages. */
|
||||
[[nodiscard]] DeviceOptions withProductInstanceId (const std::array<char, 16>& x) const
|
||||
{
|
||||
const auto null = std::find (x.begin(), x.end(), 0);
|
||||
|
||||
if (! std::all_of (x.begin(), null, [] (char c) { return beginValidAscii <= c && c < endValidAscii; }))
|
||||
{
|
||||
// The product instance ID must be made up of ASCII characters
|
||||
jassertfalse;
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (std::any_of (null, x.end(), [] (auto c) { return c != 0; }))
|
||||
{
|
||||
// All characters after the null terminator must be 0
|
||||
jassertfalse;
|
||||
return *this;
|
||||
}
|
||||
|
||||
return withMember (*this, &DeviceOptions::productInstanceId, x);
|
||||
}
|
||||
|
||||
/** @see withOutputs() */
|
||||
[[nodiscard]] const auto& getOutputs() const { return outputs; }
|
||||
/** @see withFunctionBlock() */
|
||||
[[nodiscard]] const auto& getFunctionBlock() const { return functionBlock; }
|
||||
/** @see withDeviceInfo() */
|
||||
[[nodiscard]] const auto& getDeviceInfo() const { return deviceInfo; }
|
||||
/** @see withFeatures() */
|
||||
[[nodiscard]] const auto& getFeatures() const { return features; }
|
||||
/** @see withMaxSysExSize() */
|
||||
[[nodiscard]] const auto& getMaxSysExSize() const { return maxSysExSize; }
|
||||
/** @see withProductInstanceId() */
|
||||
[[nodiscard]] const auto& getProductInstanceId() const { return productInstanceId; }
|
||||
/** @see withProfileDelegate() */
|
||||
[[nodiscard]] const auto& getProfileDelegate() const { return profileDelegate; }
|
||||
/** @see withPropertyDelegate() */
|
||||
[[nodiscard]] const auto& getPropertyDelegate() const { return propertyDelegate; }
|
||||
|
||||
private:
|
||||
std::vector<DeviceMessageHandler*> outputs;
|
||||
FunctionBlock functionBlock;
|
||||
ump::DeviceInfo deviceInfo;
|
||||
DeviceFeatures features;
|
||||
size_t maxSysExSize = 512;
|
||||
std::array<char, 16> productInstanceId{};
|
||||
ProfileDelegate* profileDelegate = nullptr;
|
||||
PropertyDelegate* propertyDelegate = nullptr;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
104
modules/juce_midi_ci/ci/juce_CIEncoding.h
Normal file
104
modules/juce_midi_ci/ci/juce_CIEncoding.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
#define JUCE_ENCODINGS X(ascii, "ASCII") X(mcoded7, "Mcoded7") X(zlibAndMcoded7, "zlib+Mcoded7")
|
||||
|
||||
/**
|
||||
Identifies different encodings that may be used by property exchange messages.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
enum class Encoding
|
||||
{
|
||||
#define X(name, unused) name,
|
||||
JUCE_ENCODINGS
|
||||
#undef X
|
||||
};
|
||||
|
||||
/**
|
||||
Utility functions for working with the Encoding enum.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct EncodingUtils
|
||||
{
|
||||
EncodingUtils() = delete;
|
||||
|
||||
/** Converts an Encoding to a human-readable string. */
|
||||
static const char* toString (Encoding e)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
#define X(name, string) case Encoding::name: return string;
|
||||
JUCE_ENCODINGS
|
||||
#undef X
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** Converts an encoding string from a property exchange JSON header to
|
||||
an Encoding.
|
||||
*/
|
||||
static std::optional<Encoding> toEncoding (const char* str)
|
||||
{
|
||||
#define X(name, string) if (std::string_view (str) == std::string_view (string)) return Encoding::name;
|
||||
JUCE_ENCODINGS
|
||||
#undef X
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
#undef JUCE_ENCODINGS
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
|
||||
namespace juce
|
||||
{
|
||||
template <>
|
||||
struct SerialisationTraits<midi_ci::Encoding>
|
||||
{
|
||||
static constexpr auto marshallingVersion = std::nullopt;
|
||||
|
||||
template <typename Archive>
|
||||
void load (Archive& archive, midi_ci::Encoding& t)
|
||||
{
|
||||
String encoding;
|
||||
archive (encoding);
|
||||
t = midi_ci::EncodingUtils::toEncoding (encoding.toRawUTF8()).value_or (midi_ci::Encoding{});
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
void save (Archive& archive, const midi_ci::Encoding& t)
|
||||
{
|
||||
archive (midi_ci::EncodingUtils::toString (t));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
379
modules/juce_midi_ci/ci/juce_CIEncodings.cpp
Normal file
379
modules/juce_midi_ci/ci/juce_CIEncodings.cpp
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
String Encodings::stringFrom7BitText (Span<const std::byte> bytes)
|
||||
{
|
||||
std::vector<CharPointer_UTF16::CharType> chars;
|
||||
|
||||
while (! bytes.empty())
|
||||
{
|
||||
const auto front = (uint8_t) bytes.front();
|
||||
|
||||
if ((front < 0x20 || 0x80 <= front) && front != 0x0a)
|
||||
{
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (front == '\\')
|
||||
{
|
||||
bytes = Span (bytes.data() + 1, bytes.size() - 1);
|
||||
|
||||
if (bytes.empty())
|
||||
return {};
|
||||
|
||||
const auto kind = (uint8_t) bytes.front();
|
||||
|
||||
switch (kind)
|
||||
{
|
||||
case '"': chars.push_back ('"'); break;
|
||||
case '\\': chars.push_back ('\\'); break;
|
||||
case '/': chars.push_back ('/'); break;
|
||||
case 'b': chars.push_back ('\b'); break;
|
||||
case 'f': chars.push_back ('\f'); break;
|
||||
case 'n': chars.push_back ('\n'); break;
|
||||
case 'r': chars.push_back ('\r'); break;
|
||||
case 't': chars.push_back ('\t'); break;
|
||||
|
||||
case 'u':
|
||||
{
|
||||
bytes = Span (bytes.data() + 1, bytes.size() - 1);
|
||||
|
||||
if (bytes.size() < 4)
|
||||
return {};
|
||||
|
||||
std::string byteStr (reinterpret_cast<const char*> (bytes.data()), 4);
|
||||
const auto unit = [&]() -> std::optional<CharPointer_UTF16::CharType>
|
||||
{
|
||||
try
|
||||
{
|
||||
return (CharPointer_UTF16::CharType) std::stoi (byteStr, {}, 16);
|
||||
}
|
||||
catch (...) {}
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}();
|
||||
|
||||
if (! unit.has_value())
|
||||
return {};
|
||||
|
||||
chars.push_back (*unit);
|
||||
bytes = Span (bytes.data() + 4, bytes.size() - 4);
|
||||
continue;
|
||||
}
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
bytes = Span (bytes.data() + 1, bytes.size() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
chars.push_back (front);
|
||||
bytes = Span (bytes.data() + 1, bytes.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
chars.push_back ({});
|
||||
return String { CharPointer_UTF16 { chars.data() } };
|
||||
}
|
||||
|
||||
std::vector<std::byte> Encodings::stringTo7BitText (const String& text)
|
||||
{
|
||||
std::vector<std::byte> result;
|
||||
|
||||
for (const auto character : text)
|
||||
{
|
||||
if (character == 0x0a || (0x20 <= character && character < 0x80))
|
||||
{
|
||||
result.emplace_back (std::byte (character));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Suspiciously low ASCII value encountered!
|
||||
jassert (character >= 0x80);
|
||||
|
||||
CharPointer_UTF16::CharType points[2]{};
|
||||
CharPointer_UTF16 asUTF16 { points };
|
||||
asUTF16.write (character);
|
||||
|
||||
std::for_each (points, asUTF16.getAddress(), [&] (CharPointer_UTF16::CharType unit)
|
||||
{
|
||||
const auto str = String::toHexString (unit);
|
||||
|
||||
result.insert (result.end(), { std::byte { '\\' }, std::byte { 'u' } });
|
||||
|
||||
for (const auto c : str)
|
||||
result.push_back ((std::byte) c);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::byte> Encodings::toMcoded7 (Span<const std::byte> bytes)
|
||||
{
|
||||
std::vector<std::byte> result;
|
||||
result.reserve ((bytes.size() * 8) + 6 / 7);
|
||||
|
||||
for (size_t index = 0; index < bytes.size(); index += 7)
|
||||
{
|
||||
std::array<std::byte, 7> slice{};
|
||||
const auto sliceSize = std::min ((size_t) 7, bytes.size() - index);
|
||||
std::copy (bytes.begin() + index, bytes.begin() + index + sliceSize, slice.begin());
|
||||
|
||||
result.push_back ((slice[0] & std::byte { 0x80 }) >> 1
|
||||
| (slice[1] & std::byte { 0x80 }) >> 2
|
||||
| (slice[2] & std::byte { 0x80 }) >> 3
|
||||
| (slice[3] & std::byte { 0x80 }) >> 4
|
||||
| (slice[4] & std::byte { 0x80 }) >> 5
|
||||
| (slice[5] & std::byte { 0x80 }) >> 6
|
||||
| (slice[6] & std::byte { 0x80 }) >> 7);
|
||||
std::transform (slice.begin(),
|
||||
std::next (slice.begin(), (ptrdiff_t) sliceSize),
|
||||
std::back_inserter (result),
|
||||
[] (const std::byte b) { return b & std::byte { 0x7f }; });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::byte> Encodings::fromMcoded7 (Span<const std::byte> bytes)
|
||||
{
|
||||
std::vector<std::byte> result;
|
||||
result.reserve ((bytes.size() * 7) + 7 / 8);
|
||||
|
||||
for (size_t index = 0; index < bytes.size(); index += 8)
|
||||
{
|
||||
const auto sliceSize = std::min ((size_t) 7, bytes.size() - index - 1);
|
||||
|
||||
for (size_t i = 0; i < sliceSize; ++i)
|
||||
{
|
||||
const auto highBit = (bytes[index] & std::byte { (uint8_t) (1 << (6 - i)) }) << (i + 1);
|
||||
result.push_back (highBit | bytes[index + 1 + i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::vector<std::byte>> Encodings::tryEncode (Span<const std::byte> bytes, Encoding mutualEncoding)
|
||||
{
|
||||
switch (mutualEncoding)
|
||||
{
|
||||
case Encoding::ascii:
|
||||
{
|
||||
if (std::all_of (bytes.begin(), bytes.end(), [] (auto b) { return (b & std::byte { 0x80 }) == std::byte{}; }))
|
||||
return std::vector<std::byte> (bytes.begin(), bytes.end());
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
case Encoding::mcoded7:
|
||||
return toMcoded7 (bytes);
|
||||
|
||||
case Encoding::zlibAndMcoded7:
|
||||
{
|
||||
MemoryOutputStream memoryStream;
|
||||
GZIPCompressorOutputStream (memoryStream).write (bytes.data(), bytes.size());
|
||||
return toMcoded7 (Span (static_cast<const std::byte*> (memoryStream.getData()), memoryStream.getDataSize()));
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown encoding!
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::byte> Encodings::decode (Span<const std::byte> bytes, Encoding mutualEncoding)
|
||||
{
|
||||
if (mutualEncoding == Encoding::ascii)
|
||||
{
|
||||
// All values must be 7-bit!
|
||||
jassert (std::none_of (bytes.begin(), bytes.end(), [] (const auto& b) { return (b & std::byte { 0x80 }) != std::byte{}; }));
|
||||
return std::vector<std::byte> (bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
if (mutualEncoding == Encoding::mcoded7)
|
||||
return fromMcoded7 (bytes);
|
||||
|
||||
if (mutualEncoding == Encoding::zlibAndMcoded7)
|
||||
{
|
||||
const auto mcoded = fromMcoded7 (bytes);
|
||||
MemoryInputStream memoryStream (mcoded.data(), mcoded.size(), false);
|
||||
|
||||
GZIPDecompressorInputStream zipStream (memoryStream);
|
||||
|
||||
const size_t chunkSize = 1 << 8;
|
||||
|
||||
std::vector<std::byte> result;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const auto previousSize = result.size();
|
||||
result.resize (previousSize + chunkSize);
|
||||
const auto read = zipStream.read (result.data() + previousSize, chunkSize);
|
||||
|
||||
if (read < 0)
|
||||
{
|
||||
// Decompression failed!
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
result.resize (previousSize + (size_t) read);
|
||||
|
||||
if (read == 0)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown encoding!
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class EncodingsTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
EncodingsTests() : UnitTest ("Encodings", UnitTestCategories::midi) {}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("7-bit text encoding");
|
||||
{
|
||||
{
|
||||
const auto converted = Encodings::stringTo7BitText (juce::CharPointer_UTF8 ("Accepted Beat \xe2\x99\xaa"));
|
||||
const auto expected = makeByteArray ('A', 'c', 'c', 'e', 'p', 't', 'e', 'd', ' ', 'B', 'e', 'a', 't', ' ', '\\', 'u', '2', '6', '6', 'a');
|
||||
expect (std::equal (converted.begin(), converted.end(), expected.begin(), expected.end()));
|
||||
}
|
||||
|
||||
{
|
||||
const auto converted = Encodings::stringTo7BitText (juce::CharPointer_UTF8 ("\xe6\xae\x8b\xe3\x82\x8a\xe3\x82\x8f\xe3\x81\x9a\xe3\x81\x8b""5\xe3\x83\x90\xe3\x82\xa4\xe3\x83\x88"));
|
||||
const auto expected = makeByteArray ('\\', 'u', '6', 'b', '8', 'b',
|
||||
'\\', 'u', '3', '0', '8', 'a',
|
||||
'\\', 'u', '3', '0', '8', 'f',
|
||||
'\\', 'u', '3', '0', '5', 'a',
|
||||
'\\', 'u', '3', '0', '4', 'b',
|
||||
'5',
|
||||
'\\', 'u', '3', '0', 'd', '0',
|
||||
'\\', 'u', '3', '0', 'a', '4',
|
||||
'\\', 'u', '3', '0', 'c', '8');
|
||||
expect (std::equal (converted.begin(), converted.end(), expected.begin(), expected.end()));
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("7-bit text decoding");
|
||||
{
|
||||
{
|
||||
const auto converted = Encodings::stringFrom7BitText (makeByteArray ('A', 'c', 'c', 'e', 'p', 't', 'e', 'd', ' ', 'B', 'e', 'a', 't', ' ', '\\', 'u', '2', '6', '6', 'a'));
|
||||
const String expected = juce::CharPointer_UTF8 ("Accepted Beat \xe2\x99\xaa");
|
||||
expect (converted == expected);
|
||||
}
|
||||
|
||||
{
|
||||
const auto converted = Encodings::stringFrom7BitText (makeByteArray ('\\', 'u', '6', 'b', '8', 'b',
|
||||
'\\', 'u', '3', '0', '8', 'a',
|
||||
'\\', 'u', '3', '0', '8', 'f',
|
||||
'\\', 'u', '3', '0', '5', 'a',
|
||||
'\\', 'u', '3', '0', '4', 'b',
|
||||
'5',
|
||||
'\\', 'u', '3', '0', 'd', '0',
|
||||
'\\', 'u', '3', '0', 'a', '4',
|
||||
'\\', 'u', '3', '0', 'c', '8'));
|
||||
const String expected = juce::CharPointer_UTF8 ("\xe6\xae\x8b\xe3\x82\x8a\xe3\x82\x8f\xe3\x81\x9a\xe3\x81\x8b""5\xe3\x83\x90\xe3\x82\xa4\xe3\x83\x88");
|
||||
expect (converted == expected);
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Mcoded7 encoding");
|
||||
{
|
||||
{
|
||||
const auto converted = Encodings::toMcoded7 (makeByteArray (0x81, 0x82, 0x83));
|
||||
const auto expected = makeByteArray (0x70, 0x01, 0x02, 0x03);
|
||||
expect (rangesEqual (converted, expected));
|
||||
}
|
||||
|
||||
{
|
||||
const auto converted = Encodings::toMcoded7 (makeByteArray (0x01, 0x82, 0x03, 0x04, 0x85, 0x06, 0x87, 0x08));
|
||||
const auto expected = makeByteArray (0x25, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x08);
|
||||
expect (rangesEqual (converted, expected));
|
||||
}
|
||||
}
|
||||
|
||||
beginTest ("Mcoded7 decoding");
|
||||
{
|
||||
{
|
||||
const auto converted = Encodings::fromMcoded7 (makeByteArray (0x70, 0x01, 0x02, 0x03));
|
||||
const auto expected = makeByteArray (0x81, 0x82, 0x83);
|
||||
expect (rangesEqual (converted, expected));
|
||||
}
|
||||
|
||||
{
|
||||
const auto converted = Encodings::fromMcoded7 (makeByteArray (0x25, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x08));
|
||||
const auto expected = makeByteArray (0x01, 0x82, 0x03, 0x04, 0x85, 0x06, 0x87, 0x08);
|
||||
expect (rangesEqual (converted, expected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static bool deepEqual (const std::optional<var>& a, const std::optional<var>& b)
|
||||
{
|
||||
if (a.has_value() && b.has_value())
|
||||
return JSONUtils::deepEqual (*a, *b);
|
||||
|
||||
return a == b;
|
||||
}
|
||||
|
||||
template <typename A, typename B>
|
||||
static bool rangesEqual (A&& a, B&& b)
|
||||
{
|
||||
using std::begin, std::end;
|
||||
return std::equal (begin (a), end (a), begin (b), end (b));
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
static std::array<std::byte, sizeof... (Ts)> makeByteArray (Ts&&... ts)
|
||||
{
|
||||
jassert (((0 <= (int) ts && (int) ts <= std::numeric_limits<uint8_t>::max()) && ...));
|
||||
return { std::byte (ts)... };
|
||||
}
|
||||
};
|
||||
|
||||
static EncodingsTests encodingsTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
87
modules/juce_midi_ci/ci/juce_CIEncodings.h
Normal file
87
modules/juce_midi_ci/ci/juce_CIEncodings.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Utility functions for working with data formats used by property exchange
|
||||
messages.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct Encodings
|
||||
{
|
||||
/** Text in ACK and NAK messages can't be utf-8 or ASCII because each byte only has 7 usable bits.
|
||||
The encoding rules are in section 5.10.4 of the CI spec.
|
||||
*/
|
||||
static String stringFrom7BitText (Span<const std::byte> bytes);
|
||||
|
||||
/** Text in ACK and NAK messages can't be utf-8 or ASCII because each byte only has 7 usable bits.
|
||||
The encoding rules are in section 5.10.4 of the CI spec.
|
||||
*/
|
||||
static std::vector<std::byte> stringTo7BitText (const String& text);
|
||||
|
||||
/** Converts a list of bytes representing a 7-bit ASCII string to JSON. */
|
||||
static var jsonFrom7BitText (Span<const std::byte> bytes)
|
||||
{
|
||||
return JSON::parse (stringFrom7BitText (bytes));
|
||||
}
|
||||
|
||||
/** Converts a JSON object to a list of bytes in 7-bit ASCII format. */
|
||||
static std::vector<std::byte> jsonTo7BitText (const var& v)
|
||||
{
|
||||
return stringTo7BitText (JSON::toString (v, true));
|
||||
}
|
||||
|
||||
/** Each group of seven stored bytes is transmitted as eight bytes.
|
||||
First, the sign bits of the seven bytes are sent, followed by the low-order 7 bits of each byte.
|
||||
*/
|
||||
static std::vector<std::byte> toMcoded7 (Span<const std::byte> bytes);
|
||||
|
||||
/** Each group of seven stored bytes is transmitted as eight bytes.
|
||||
First, the sign bits of the seven bytes are sent, followed by the low-order 7 bits of each byte.
|
||||
*/
|
||||
static std::vector<std::byte> fromMcoded7 (Span<const std::byte> bytes);
|
||||
|
||||
/** Attempts to encode the provided byte span using the specified encoding.
|
||||
|
||||
The ASCII encoding does not make any changes to the input stream, but
|
||||
encoding will fail if any byte has its most significant bit set.
|
||||
*/
|
||||
static std::optional<std::vector<std::byte>> tryEncode (Span<const std::byte> bytes,
|
||||
Encoding mutualEncoding);
|
||||
|
||||
/** Decodes the provided byte span using the specified encoding.
|
||||
|
||||
All bytes of the input must be 7-bit values, i.e. all most-significant bits
|
||||
are unset.
|
||||
*/
|
||||
static std::vector<std::byte> decode (Span<const std::byte> bytes, Encoding mutualEncoding);
|
||||
|
||||
Encodings() = delete;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
49
modules/juce_midi_ci/ci/juce_CIFunctionBlock.h
Normal file
49
modules/juce_midi_ci/ci/juce_CIFunctionBlock.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Contains information about a MIDI 2.0 function block.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct FunctionBlock
|
||||
{
|
||||
std::byte identifier { 0x7f }; ///< 0x7f == no function block
|
||||
uint8_t firstGroup = 0; ///< The first group that is part of the block, 0-based
|
||||
uint8_t numGroups = 1; ///< The number of groups contained in the block
|
||||
|
||||
bool operator== (const FunctionBlock& other) const
|
||||
{
|
||||
const auto tie = [] (auto& x) { return std::tie (x.identifier, x.firstGroup, x.numGroups); };
|
||||
return tie (*this) == tie (other);
|
||||
}
|
||||
|
||||
bool operator!= (const FunctionBlock& other) const { return ! operator== (other); }
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
664
modules/juce_midi_ci/ci/juce_CIMessages.h
Normal file
664
modules/juce_midi_ci/ci/juce_CIMessages.h
Normal file
|
|
@ -0,0 +1,664 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Byte values representing different addresses within a group.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
enum class ChannelInGroup : uint8_t
|
||||
{
|
||||
channel0 = 0x0,
|
||||
channel1 = 0x1,
|
||||
channel2 = 0x2,
|
||||
channel3 = 0x3,
|
||||
channel4 = 0x4,
|
||||
channel5 = 0x5,
|
||||
channel6 = 0x6,
|
||||
channel7 = 0x7,
|
||||
channel8 = 0x8,
|
||||
channel9 = 0x9,
|
||||
channelA = 0xA,
|
||||
channelB = 0xB,
|
||||
channelC = 0xC,
|
||||
channelD = 0xD,
|
||||
channelE = 0xE,
|
||||
channelF = 0xF,
|
||||
wholeGroup = 0x7e, ///< Refers to all channels in the UMP group
|
||||
wholeBlock = 0x7f, ///< Refers to all channels in the function block that contains the UMP group
|
||||
};
|
||||
|
||||
struct ChannelInGroupUtils
|
||||
{
|
||||
ChannelInGroupUtils() = delete;
|
||||
|
||||
/** Converts a ChannelInGroup to a descriptive string. */
|
||||
static String toString (ChannelInGroup c)
|
||||
{
|
||||
if (c == ChannelInGroup::wholeGroup)
|
||||
return "Group";
|
||||
|
||||
if (c == ChannelInGroup::wholeBlock)
|
||||
return "Function Block";
|
||||
|
||||
const auto underlying = (std::underlying_type_t<ChannelInGroup>) c;
|
||||
return "Channel " + String (underlying + 1);
|
||||
}
|
||||
};
|
||||
|
||||
using Profile = std::array<std::byte, 5>;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Namespace containing structs representing different kinds of MIDI-CI message.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
namespace Message
|
||||
{
|
||||
/** Wraps a span, providing equality operators that compare the span
|
||||
contents elementwise.
|
||||
*/
|
||||
template <typename T>
|
||||
struct ComparableRange
|
||||
{
|
||||
T& data;
|
||||
|
||||
bool operator== (const ComparableRange& other) const
|
||||
{
|
||||
return std::equal (data.begin(), data.end(), other.data.begin(), other.data.end());
|
||||
}
|
||||
|
||||
bool operator!= (const ComparableRange& other) const { return ! operator== (other); }
|
||||
};
|
||||
|
||||
template <typename T> static constexpr auto makeComparableRange ( T& t) { return ComparableRange< T> { t }; }
|
||||
template <typename T> static constexpr auto makeComparableRange (const T& t) { return ComparableRange<const T> { t }; }
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds fields that can be found at the beginning of every MIDI CI message.
|
||||
*/
|
||||
struct Header
|
||||
{
|
||||
ChannelInGroup deviceID{};
|
||||
std::byte category{};
|
||||
std::byte version{};
|
||||
MUID source = MUID::makeUnchecked (0);
|
||||
MUID destination = MUID::makeUnchecked (0);
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (deviceID, category, version, source, destination);
|
||||
}
|
||||
|
||||
bool operator== (const Header& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const Header& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/**
|
||||
Groups together a CI message header, and some number of trailing bytes.
|
||||
*/
|
||||
struct Generic
|
||||
{
|
||||
Header header;
|
||||
Span<const std::byte> data;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** See the MIDI-CI specification. */
|
||||
struct DiscoveryResponse
|
||||
{
|
||||
ump::DeviceInfo device;
|
||||
std::byte capabilities{};
|
||||
uint32_t maximumSysexSize{};
|
||||
std::byte outputPathID{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
std::byte functionBlock{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (device, capabilities, maximumSysexSize, outputPathID, functionBlock);
|
||||
}
|
||||
|
||||
bool operator== (const DiscoveryResponse& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const DiscoveryResponse& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct Discovery
|
||||
{
|
||||
ump::DeviceInfo device;
|
||||
std::byte capabilities{};
|
||||
uint32_t maximumSysexSize{};
|
||||
std::byte outputPathID{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (device, capabilities, maximumSysexSize, outputPathID);
|
||||
}
|
||||
|
||||
bool operator== (const Discovery& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const Discovery& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct EndpointInquiryResponse
|
||||
{
|
||||
std::byte status;
|
||||
Span<const std::byte> data;
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (status, makeComparableRange (data));
|
||||
}
|
||||
|
||||
bool operator== (const EndpointInquiryResponse& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const EndpointInquiryResponse& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct EndpointInquiry
|
||||
{
|
||||
std::byte status;
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (status);
|
||||
}
|
||||
|
||||
bool operator== (const EndpointInquiry& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const EndpointInquiry& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct InvalidateMUID
|
||||
{
|
||||
MUID target = MUID::makeUnchecked (0);
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (target);
|
||||
}
|
||||
|
||||
bool operator== (const InvalidateMUID& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const InvalidateMUID& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ACK
|
||||
{
|
||||
std::byte originalCategory{};
|
||||
std::byte statusCode{};
|
||||
std::byte statusData{};
|
||||
std::array<std::byte, 5> details{};
|
||||
Span<const std::byte> messageText{};
|
||||
|
||||
/** Convenience function that returns the message's text as a String. */
|
||||
String getMessageTextAsString() const
|
||||
{
|
||||
return Encodings::stringFrom7BitText (messageText);
|
||||
}
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (originalCategory, statusCode, statusData, details, makeComparableRange (messageText));
|
||||
}
|
||||
|
||||
bool operator== (const ACK& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ACK& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct NAK
|
||||
{
|
||||
std::byte originalCategory{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
std::byte statusCode{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
std::byte statusData{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
std::array<std::byte, 5> details{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
Span<const std::byte> messageText{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
|
||||
/** Convenience function that returns the message's text as a String. */
|
||||
String getMessageTextAsString() const
|
||||
{
|
||||
return Encodings::stringFrom7BitText (messageText);
|
||||
}
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (originalCategory, statusCode, statusData, details, makeComparableRange (messageText));
|
||||
}
|
||||
|
||||
bool operator== (const NAK& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const NAK& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileInquiryResponse
|
||||
{
|
||||
Span<const Profile> enabledProfiles;
|
||||
Span<const Profile> disabledProfiles;
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (makeComparableRange (enabledProfiles), makeComparableRange (disabledProfiles));
|
||||
}
|
||||
|
||||
bool operator== (const ProfileInquiryResponse& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileInquiryResponse& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileInquiry
|
||||
{
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple();
|
||||
}
|
||||
|
||||
bool operator== (const ProfileInquiry& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileInquiry& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileAdded
|
||||
{
|
||||
Profile profile{};
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (profile);
|
||||
}
|
||||
|
||||
bool operator== (const ProfileAdded& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileAdded& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileRemoved
|
||||
{
|
||||
Profile profile{};
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (profile);
|
||||
}
|
||||
|
||||
bool operator== (const ProfileRemoved& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileRemoved& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileDetailsResponse
|
||||
{
|
||||
Profile profile{};
|
||||
std::byte target{};
|
||||
Span<const std::byte> data;
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (profile, target, makeComparableRange (data));
|
||||
}
|
||||
|
||||
bool operator== (const ProfileDetailsResponse& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileDetailsResponse& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileDetails
|
||||
{
|
||||
Profile profile{};
|
||||
std::byte target{};
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (profile, target);
|
||||
}
|
||||
|
||||
bool operator== (const ProfileDetails& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileDetails& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileOn
|
||||
{
|
||||
Profile profile{};
|
||||
uint16_t numChannels{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (profile, numChannels);
|
||||
}
|
||||
|
||||
bool operator== (const ProfileOn& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileOn& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileOff
|
||||
{
|
||||
Profile profile{};
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (profile);
|
||||
}
|
||||
|
||||
bool operator== (const ProfileOff& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileOff& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileEnabledReport
|
||||
{
|
||||
Profile profile{};
|
||||
uint16_t numChannels{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (profile, numChannels);
|
||||
}
|
||||
|
||||
bool operator== (const ProfileEnabledReport& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileEnabledReport& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileDisabledReport
|
||||
{
|
||||
Profile profile{};
|
||||
uint16_t numChannels{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (profile, numChannels);
|
||||
}
|
||||
|
||||
bool operator== (const ProfileDisabledReport& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileDisabledReport& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProfileSpecificData
|
||||
{
|
||||
Profile profile{};
|
||||
Span<const std::byte> data;
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (profile, makeComparableRange (data));
|
||||
}
|
||||
|
||||
bool operator== (const ProfileSpecificData& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileSpecificData& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct PropertyExchangeCapabilitiesResponse
|
||||
{
|
||||
std::byte numSimultaneousRequestsSupported{};
|
||||
std::byte majorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
std::byte minorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (numSimultaneousRequestsSupported, majorVersion, minorVersion);
|
||||
}
|
||||
|
||||
bool operator== (const PropertyExchangeCapabilitiesResponse& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const PropertyExchangeCapabilitiesResponse& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct PropertyExchangeCapabilities
|
||||
{
|
||||
std::byte numSimultaneousRequestsSupported{};
|
||||
std::byte majorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
std::byte minorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (numSimultaneousRequestsSupported, majorVersion, minorVersion);
|
||||
}
|
||||
|
||||
bool operator== (const PropertyExchangeCapabilities& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const PropertyExchangeCapabilities& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** A property-exchange message that has no payload, and must therefore
|
||||
be contained in a single chunk.
|
||||
*/
|
||||
struct StaticSizePropertyExchange
|
||||
{
|
||||
std::byte requestID{};
|
||||
Span<const std::byte> header;
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (requestID, makeComparableRange (header));
|
||||
}
|
||||
};
|
||||
|
||||
/** A property-exchange message that may form part of a multi-chunk
|
||||
message sequence.
|
||||
*/
|
||||
struct DynamicSizePropertyExchange
|
||||
{
|
||||
std::byte requestID{};
|
||||
Span<const std::byte> header;
|
||||
uint16_t totalNumChunks{};
|
||||
uint16_t thisChunkNum{};
|
||||
Span<const std::byte> data;
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (requestID,
|
||||
makeComparableRange (header),
|
||||
totalNumChunks,
|
||||
thisChunkNum,
|
||||
makeComparableRange (data));
|
||||
}
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct PropertyGetDataResponse : public DynamicSizePropertyExchange
|
||||
{
|
||||
bool operator== (const PropertyGetDataResponse& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const PropertyGetDataResponse& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct PropertyGetData : public StaticSizePropertyExchange
|
||||
{
|
||||
bool operator== (const PropertyGetData& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const PropertyGetData& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct PropertySetDataResponse : public StaticSizePropertyExchange
|
||||
{
|
||||
bool operator== (const PropertySetDataResponse& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const PropertySetDataResponse& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct PropertySetData : public DynamicSizePropertyExchange
|
||||
{
|
||||
bool operator== (const PropertySetData& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const PropertySetData& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct PropertySubscribeResponse : public DynamicSizePropertyExchange
|
||||
{
|
||||
bool operator== (const PropertySubscribeResponse& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const PropertySubscribeResponse& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct PropertySubscribe : public DynamicSizePropertyExchange
|
||||
{
|
||||
bool operator== (const PropertySubscribe& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const PropertySubscribe& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct PropertyNotify : public DynamicSizePropertyExchange
|
||||
{
|
||||
bool operator== (const PropertyNotify& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const PropertyNotify& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProcessInquiryResponse
|
||||
{
|
||||
std::byte supportedFeatures{};
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (supportedFeatures);
|
||||
}
|
||||
|
||||
bool operator== (const ProcessInquiryResponse& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProcessInquiryResponse& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProcessInquiry
|
||||
{
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple();
|
||||
}
|
||||
|
||||
bool operator== (const ProcessInquiry& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProcessInquiry& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProcessMidiMessageReportResponse
|
||||
{
|
||||
std::byte messageDataControl{};
|
||||
std::byte requestedMessages{};
|
||||
std::byte channelControllerMessages{};
|
||||
std::byte noteDataMessages{};
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (messageDataControl, requestedMessages, channelControllerMessages, noteDataMessages);
|
||||
}
|
||||
|
||||
bool operator== (const ProcessMidiMessageReportResponse& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProcessMidiMessageReportResponse& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProcessMidiMessageReport
|
||||
{
|
||||
std::byte messageDataControl{};
|
||||
std::byte requestedMessages{};
|
||||
std::byte channelControllerMessages{};
|
||||
std::byte noteDataMessages{};
|
||||
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple (messageDataControl, requestedMessages, channelControllerMessages, noteDataMessages);
|
||||
}
|
||||
|
||||
bool operator== (const ProcessMidiMessageReport& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProcessMidiMessageReport& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/** See the MIDI-CI specification. */
|
||||
struct ProcessEndMidiMessageReport
|
||||
{
|
||||
auto tie() const
|
||||
{
|
||||
return std::tuple();
|
||||
}
|
||||
|
||||
bool operator== (const ProcessEndMidiMessageReport& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProcessEndMidiMessageReport& x) const { return ! operator== (x); }
|
||||
};
|
||||
|
||||
/**
|
||||
A message with a header and optional body.
|
||||
|
||||
The body may be set to std::monostate to indicate some kind of failure, such as a malformed
|
||||
incoming message.
|
||||
*/
|
||||
struct Parsed
|
||||
{
|
||||
using Body = std::variant<std::monostate,
|
||||
Discovery,
|
||||
DiscoveryResponse,
|
||||
InvalidateMUID,
|
||||
EndpointInquiry,
|
||||
EndpointInquiryResponse,
|
||||
ACK,
|
||||
NAK,
|
||||
ProfileInquiry,
|
||||
ProfileInquiryResponse,
|
||||
ProfileAdded,
|
||||
ProfileRemoved,
|
||||
ProfileDetails,
|
||||
ProfileDetailsResponse,
|
||||
ProfileOn,
|
||||
ProfileOff,
|
||||
ProfileEnabledReport,
|
||||
ProfileDisabledReport,
|
||||
ProfileSpecificData,
|
||||
PropertyExchangeCapabilities,
|
||||
PropertyExchangeCapabilitiesResponse,
|
||||
PropertyGetData,
|
||||
PropertyGetDataResponse,
|
||||
PropertySetData,
|
||||
PropertySetDataResponse,
|
||||
PropertySubscribe,
|
||||
PropertySubscribeResponse,
|
||||
PropertyNotify,
|
||||
ProcessInquiry,
|
||||
ProcessInquiryResponse,
|
||||
ProcessMidiMessageReport,
|
||||
ProcessMidiMessageReportResponse,
|
||||
ProcessEndMidiMessageReport>;
|
||||
|
||||
Header header;
|
||||
Body body;
|
||||
|
||||
bool operator== (const Parsed& other) const
|
||||
{
|
||||
const auto tie = [] (const auto& x) { return std::tie (x.header, x.body); };
|
||||
return tie (*this) == tie (other);
|
||||
}
|
||||
|
||||
bool operator!= (const Parsed& other) const { return ! operator== (other); }
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
83
modules/juce_midi_ci/ci/juce_CIMuid.h
Normal file
83
modules/juce_midi_ci/ci/juce_CIMuid.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
A 28-bit ID that uniquely identifies a device taking part in a series of
|
||||
MIDI-CI transactions.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class MUID
|
||||
{
|
||||
constexpr explicit MUID (uint32_t v) : value (v) {}
|
||||
|
||||
// 0x0fffff00 to 0x0ffffffe are reserved, 0x0fffffff is 'broadcast'
|
||||
static constexpr uint32_t userMuidEnd = 0x0fffff00;
|
||||
static constexpr uint32_t mask = 0x0fffffff;
|
||||
uint32_t value{};
|
||||
|
||||
public:
|
||||
/** Returns the ID as a plain integer. */
|
||||
constexpr uint32_t get() const { return value; }
|
||||
|
||||
/** Converts the provided integer to a MUID without validation that it
|
||||
is within the allowed range.
|
||||
*/
|
||||
static MUID makeUnchecked (uint32_t v)
|
||||
{
|
||||
// If this is hit, the MUID has too many bits set!
|
||||
jassert ((v & mask) == v);
|
||||
return MUID (v);
|
||||
}
|
||||
|
||||
/** Returns a MUID if the provided value is within the valid range for
|
||||
MUID values; otherwise returns nullopt.
|
||||
*/
|
||||
static std::optional<MUID> make (uint32_t v)
|
||||
{
|
||||
if ((v & mask) == v)
|
||||
return makeUnchecked (v);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Makes a random MUID using the provided random engine. */
|
||||
static MUID makeRandom (Random& r)
|
||||
{
|
||||
return makeUnchecked ((uint32_t) r.nextInt (userMuidEnd));
|
||||
}
|
||||
|
||||
bool operator== (const MUID other) const { return value == other.value; }
|
||||
bool operator!= (const MUID other) const { return value != other.value; }
|
||||
bool operator< (const MUID other) const { return value < other.value; }
|
||||
|
||||
/** Returns the special MUID representing the broadcast address. */
|
||||
static constexpr MUID getBroadcast() { return MUID { mask }; }
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
448
modules/juce_midi_ci/ci/juce_CIParser.cpp
Normal file
448
modules/juce_midi_ci/ci/juce_CIParser.cpp
Normal file
|
|
@ -0,0 +1,448 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
std::optional<Message::Parsed> Parser::parse (Span<const std::byte> message, Status* status)
|
||||
{
|
||||
const auto setStatus = [&] (Status s)
|
||||
{
|
||||
if (status != nullptr)
|
||||
*status = s;
|
||||
};
|
||||
|
||||
setStatus (Status::noError);
|
||||
|
||||
Message::Generic generic;
|
||||
|
||||
if (! detail::Marshalling::Reader { message } (generic))
|
||||
{
|
||||
// Got a full sysex message, but it didn't contain a well-formed header.
|
||||
setStatus (Status::malformed);
|
||||
return {};
|
||||
}
|
||||
|
||||
if ((generic.header.version & std::byte { 0x70 }) != std::byte{})
|
||||
{
|
||||
setStatus (Status::reservedVersion);
|
||||
return Message::Parsed { generic.header, std::monostate{} };
|
||||
}
|
||||
|
||||
const auto index = (uint8_t) generic.header.category;
|
||||
constexpr auto tables = detail::MessageTypeUtils::getTables();
|
||||
const auto processFunction = tables.parsers[index];
|
||||
return Message::Parsed { generic.header, processFunction (generic, status) };
|
||||
}
|
||||
|
||||
std::optional<Message::Parsed> Parser::parse (const MUID ourMUID,
|
||||
Span<const std::byte> message,
|
||||
Status* status)
|
||||
{
|
||||
const auto setStatus = [&] (Status s)
|
||||
{
|
||||
if (status != nullptr)
|
||||
*status = s;
|
||||
};
|
||||
|
||||
setStatus (Status::noError);
|
||||
|
||||
if (const auto parsed = parse (message, status))
|
||||
{
|
||||
if (parsed->header.destination != MUID::getBroadcast() && parsed->header.destination != ourMUID)
|
||||
setStatus (Status::mismatchedMUID);
|
||||
else if (parsed->header.source == ourMUID)
|
||||
setStatus (Status::collidingMUID);
|
||||
else if ((parsed->header.version & std::byte { 0x70 }) != std::byte{})
|
||||
setStatus (Status::reservedVersion);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
class DescriptionVisitor : public detail::MessageTypeUtils::MessageVisitor
|
||||
{
|
||||
public:
|
||||
DescriptionVisitor (const Message::Parsed* m, String* str) : msg (m), result (str) {}
|
||||
|
||||
void visit (const std::monostate&) const override { *result = "!! Unrecognised !!"; }
|
||||
void visit (const Message::Discovery& body) const override { visitImpl (body); }
|
||||
void visit (const Message::DiscoveryResponse& body) const override { visitImpl (body); }
|
||||
void visit (const Message::InvalidateMUID& body) const override { visitImpl (body); }
|
||||
void visit (const Message::EndpointInquiry& body) const override { visitImpl (body); }
|
||||
void visit (const Message::EndpointInquiryResponse& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ACK& body) const override { visitImpl (body); }
|
||||
void visit (const Message::NAK& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileInquiry& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileInquiryResponse& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileAdded& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileRemoved& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileDetails& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileDetailsResponse& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileOn& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileOff& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileEnabledReport& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileDisabledReport& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileSpecificData& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertyExchangeCapabilities& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertyExchangeCapabilitiesResponse& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertyGetData& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertyGetDataResponse& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertySetData& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertySetDataResponse& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertySubscribe& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertySubscribeResponse& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertyNotify& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProcessInquiry& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProcessInquiryResponse& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProcessMidiMessageReport& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProcessMidiMessageReportResponse& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProcessEndMidiMessageReport& body) const override { visitImpl (body); }
|
||||
|
||||
private:
|
||||
static const char* getDescription (const Message::Discovery&) { return "Discovery"; }
|
||||
static const char* getDescription (const Message::DiscoveryResponse&) { return "Discovery Response"; }
|
||||
static const char* getDescription (const Message::InvalidateMUID&) { return "Invalidate MUID"; }
|
||||
static const char* getDescription (const Message::EndpointInquiry&) { return "Endpoint"; }
|
||||
static const char* getDescription (const Message::EndpointInquiryResponse&) { return "Endpoint Response"; }
|
||||
static const char* getDescription (const Message::ACK&) { return "ACK"; }
|
||||
static const char* getDescription (const Message::NAK&) { return "NAK"; }
|
||||
static const char* getDescription (const Message::ProfileInquiry&) { return "Profile Inquiry"; }
|
||||
static const char* getDescription (const Message::ProfileInquiryResponse&) { return "Profile Inquiry Response"; }
|
||||
static const char* getDescription (const Message::ProfileAdded&) { return "Profile Added"; }
|
||||
static const char* getDescription (const Message::ProfileRemoved&) { return "Profile Removed"; }
|
||||
static const char* getDescription (const Message::ProfileDetails&) { return "Profile Details"; }
|
||||
static const char* getDescription (const Message::ProfileDetailsResponse&) { return "Profile Details Response"; }
|
||||
static const char* getDescription (const Message::ProfileOn&) { return "Profile On"; }
|
||||
static const char* getDescription (const Message::ProfileOff&) { return "Profile Off"; }
|
||||
static const char* getDescription (const Message::ProfileEnabledReport&) { return "Profile Enabled Report"; }
|
||||
static const char* getDescription (const Message::ProfileDisabledReport&) { return "Profile Disabled Report"; }
|
||||
static const char* getDescription (const Message::ProfileSpecificData&) { return "Profile Specific Data"; }
|
||||
static const char* getDescription (const Message::PropertyExchangeCapabilities&) { return "Property Exchange Capabilities"; }
|
||||
static const char* getDescription (const Message::PropertyExchangeCapabilitiesResponse&) { return "Property Exchange Capabilities Response"; }
|
||||
static const char* getDescription (const Message::PropertyGetData&) { return "Property Get Data"; }
|
||||
static const char* getDescription (const Message::PropertyGetDataResponse&) { return "Property Get Data Response"; }
|
||||
static const char* getDescription (const Message::PropertySetData&) { return "Property Set Data"; }
|
||||
static const char* getDescription (const Message::PropertySetDataResponse&) { return "Property Set Data Response"; }
|
||||
static const char* getDescription (const Message::PropertySubscribe&) { return "Property Subscribe"; }
|
||||
static const char* getDescription (const Message::PropertySubscribeResponse&) { return "Property Subscribe Response"; }
|
||||
static const char* getDescription (const Message::PropertyNotify&) { return "Property Notify"; }
|
||||
static const char* getDescription (const Message::ProcessInquiry&) { return "Process Inquiry"; }
|
||||
static const char* getDescription (const Message::ProcessInquiryResponse&) { return "Process Inquiry Response"; }
|
||||
static const char* getDescription (const Message::ProcessMidiMessageReport&) { return "Process Midi Message Report"; }
|
||||
static const char* getDescription (const Message::ProcessMidiMessageReportResponse&) { return "Process Midi Message Report Response"; }
|
||||
static const char* getDescription (const Message::ProcessEndMidiMessageReport&) { return "Process End Midi Message Report"; }
|
||||
|
||||
template <typename Body>
|
||||
void visitImpl (const Body& body) const
|
||||
{
|
||||
const auto opts = ToVarOptions{}.withExplicitVersion ((int) msg->header.version)
|
||||
.withVersionIncluded (false);
|
||||
const auto json = ToVar::convert (body, opts);
|
||||
|
||||
if (json.has_value())
|
||||
*result = String (getDescription (body)) + ": " + JSON::toString (*json, true);
|
||||
}
|
||||
|
||||
const Message::Parsed* msg = nullptr;
|
||||
String* result = nullptr;
|
||||
};
|
||||
|
||||
String Parser::getMessageDescription (const Message::Parsed& message)
|
||||
{
|
||||
String result;
|
||||
detail::MessageTypeUtils::visit (message, DescriptionVisitor { &message, &result });
|
||||
return result;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class ParserTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ParserTests() : UnitTest ("Parser", UnitTestCategories::midi) {}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
auto random = getRandom();
|
||||
|
||||
beginTest ("Sending an empty message does nothing");
|
||||
{
|
||||
const auto parsed = Parser::parse (MUID::makeRandom (random), {});
|
||||
expect (parsed == std::nullopt);
|
||||
}
|
||||
|
||||
beginTest ("Sending a garbage message does nothing");
|
||||
{
|
||||
const std::vector<std::byte> bytes (128, std::byte { 0x70 });
|
||||
const auto parsed = Parser::parse (MUID::makeRandom (random), bytes);
|
||||
expect (parsed == std::nullopt);
|
||||
}
|
||||
|
||||
beginTest ("Sending a message with truncated body produces a malformed status");
|
||||
{
|
||||
constexpr auto version1 = 0x01;
|
||||
const auto truncatedV1 = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* discovery message */ 0x70,
|
||||
/* version */ version1,
|
||||
/* source MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* broadcast MUID */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* manufacturer */ 0x10,
|
||||
/* ... */ 0x11,
|
||||
/* ... */ 0x12,
|
||||
/* family */ 0x20,
|
||||
/* ... */ 0x21,
|
||||
/* model */ 0x30,
|
||||
/* ... */ 0x31,
|
||||
/* revision */ 0x40,
|
||||
/* ... */ 0x41,
|
||||
/* ... */ 0x42,
|
||||
/* ... */ 0x43,
|
||||
/* CI category supported */ 0x7f,
|
||||
/* max sysex size */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f);
|
||||
/* Missing final byte for a version 1 message */
|
||||
Parser::Status status{};
|
||||
const auto parsedV1 = Parser::parse (MUID::makeRandom (random), truncatedV1, &status);
|
||||
|
||||
expect (status == Parser::Status::malformed);
|
||||
expect (parsedV1 == Message::Parsed { Message::Header { ChannelInGroup::wholeBlock,
|
||||
std::byte { 0x70 },
|
||||
std::byte { version1 },
|
||||
MUID::makeUnchecked (0x80c101),
|
||||
MUID::getBroadcast() },
|
||||
std::monostate{} });
|
||||
|
||||
constexpr auto version2 = 0x02;
|
||||
const auto truncatedV2 = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* discovery message */ 0x70,
|
||||
/* version */ version2,
|
||||
/* source MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* broadcast MUID */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* manufacturer */ 0x10,
|
||||
/* ... */ 0x11,
|
||||
/* ... */ 0x12,
|
||||
/* family */ 0x20,
|
||||
/* ... */ 0x21,
|
||||
/* model */ 0x30,
|
||||
/* ... */ 0x31,
|
||||
/* revision */ 0x40,
|
||||
/* ... */ 0x41,
|
||||
/* ... */ 0x42,
|
||||
/* ... */ 0x43,
|
||||
/* CI category supported */ 0x7f,
|
||||
/* max sysex size */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f);
|
||||
/* Missing final byte for a version 2 message */
|
||||
const auto parsedV2 = Parser::parse (MUID::makeRandom (random), truncatedV2);
|
||||
|
||||
expect (status == Parser::Status::malformed);
|
||||
expect (parsedV2 == Message::Parsed { Message::Header { ChannelInGroup::wholeBlock,
|
||||
std::byte { 0x70 },
|
||||
std::byte { version2 },
|
||||
MUID::makeUnchecked (0x80c101),
|
||||
MUID::getBroadcast() },
|
||||
std::monostate{} });
|
||||
}
|
||||
|
||||
const auto getExpectedDiscoveryInput = [] (uint8_t version, uint8_t outputPathID)
|
||||
{
|
||||
return Message::Parsed { Message::Header { ChannelInGroup::wholeBlock,
|
||||
std::byte { 0x70 },
|
||||
std::byte { version },
|
||||
MUID::makeUnchecked (0x80c101),
|
||||
MUID::getBroadcast() },
|
||||
Message::Discovery { { { std::byte { 0x10 }, std::byte { 0x11 }, std::byte { 0x12 } },
|
||||
{ std::byte { 0x20 }, std::byte { 0x21 } },
|
||||
{ std::byte { 0x30 }, std::byte { 0x31 } },
|
||||
{ std::byte { 0x40 }, std::byte { 0x41 }, std::byte { 0x42 }, std::byte { 0x43 } } },
|
||||
std::byte { 0x7f },
|
||||
0xfffffff,
|
||||
std::byte { outputPathID } } };
|
||||
};
|
||||
|
||||
beginTest ("Sending a V1 discovery message notifies the input listener");
|
||||
{
|
||||
const auto initialMUID = MUID::makeRandom (random);
|
||||
constexpr uint8_t version = 0x01;
|
||||
|
||||
const auto bytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* discovery message */ 0x70,
|
||||
/* version */ version,
|
||||
/* source MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* broadcast MUID */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* manufacturer */ 0x10,
|
||||
/* ... */ 0x11,
|
||||
/* ... */ 0x12,
|
||||
/* family */ 0x20,
|
||||
/* ... */ 0x21,
|
||||
/* model */ 0x30,
|
||||
/* ... */ 0x31,
|
||||
/* revision */ 0x40,
|
||||
/* ... */ 0x41,
|
||||
/* ... */ 0x42,
|
||||
/* ... */ 0x43,
|
||||
/* CI category supported */ 0x7f,
|
||||
/* max sysex size */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f);
|
||||
const auto parsed = Parser::parse (initialMUID, bytes);
|
||||
|
||||
expect (parsed == getExpectedDiscoveryInput (version, 0));
|
||||
}
|
||||
|
||||
beginTest ("Sending a V2 discovery message notifies the input listener");
|
||||
{
|
||||
constexpr uint8_t outputPathID = 5;
|
||||
const auto initialMUID = MUID::makeRandom (random);
|
||||
constexpr uint8_t version = 0x02;
|
||||
|
||||
const auto bytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* discovery message */ 0x70,
|
||||
/* version */ version,
|
||||
/* source MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* broadcast MUID */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* manufacturer */ 0x10,
|
||||
/* ... */ 0x11,
|
||||
/* ... */ 0x12,
|
||||
/* family */ 0x20,
|
||||
/* ... */ 0x21,
|
||||
/* model */ 0x30,
|
||||
/* ... */ 0x31,
|
||||
/* revision */ 0x40,
|
||||
/* ... */ 0x41,
|
||||
/* ... */ 0x42,
|
||||
/* ... */ 0x43,
|
||||
/* CI category supported */ 0x7f,
|
||||
/* max sysex size */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* output path ID */ outputPathID);
|
||||
const auto parsed = Parser::parse (initialMUID, bytes);
|
||||
|
||||
expect (parsed == getExpectedDiscoveryInput (version, outputPathID));
|
||||
}
|
||||
|
||||
beginTest ("Sending a discovery message with a future version notifies the input listener and ignores trailing fields");
|
||||
{
|
||||
constexpr uint8_t outputPathID = 10;
|
||||
const auto initialMUID = MUID::makeRandom (random);
|
||||
constexpr auto version = (uint8_t) detail::MessageMeta::implementationVersion + 1;
|
||||
|
||||
const auto bytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* discovery message */ 0x70,
|
||||
/* version */ version,
|
||||
/* source MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* broadcast MUID */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* manufacturer */ 0x10,
|
||||
/* ... */ 0x11,
|
||||
/* ... */ 0x12,
|
||||
/* family */ 0x20,
|
||||
/* ... */ 0x21,
|
||||
/* model */ 0x30,
|
||||
/* ... */ 0x31,
|
||||
/* revision */ 0x40,
|
||||
/* ... */ 0x41,
|
||||
/* ... */ 0x42,
|
||||
/* ... */ 0x43,
|
||||
/* CI category supported */ 0x7f,
|
||||
/* max sysex size */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* ... */ 0x7f,
|
||||
/* output path ID */ outputPathID,
|
||||
/* extra bytes */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00);
|
||||
const auto parsed = Parser::parse (initialMUID, bytes);
|
||||
|
||||
expect (parsed == getExpectedDiscoveryInput (version, outputPathID));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename... Ts>
|
||||
static std::array<std::byte, sizeof... (Ts)> makeByteArray (Ts&&... ts)
|
||||
{
|
||||
jassert (((0 <= (int) ts && (int) ts <= std::numeric_limits<uint8_t>::max()) && ...));
|
||||
return { std::byte (ts)... };
|
||||
}
|
||||
};
|
||||
|
||||
static ParserTests parserTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
80
modules/juce_midi_ci/ci/juce_CIParser.h
Normal file
80
modules/juce_midi_ci/ci/juce_CIParser.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Parses CI messages.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
Parser() = delete;
|
||||
|
||||
enum class Status
|
||||
{
|
||||
noError, ///< Parsing was successful
|
||||
mismatchedMUID, ///< The message destination MUID doesn't match the provided MUID
|
||||
collidingMUID, ///< The message source MUID matches the provided MUID
|
||||
unrecognisedMessage, ///< The message ID doesn't correspond to a known message
|
||||
reservedVersion, ///< The MIDI CI version uses an unrecognised major version
|
||||
malformed, ///< The message (whole message, or just body) could not be parsed
|
||||
};
|
||||
|
||||
/** Parses the provided message;
|
||||
|
||||
Call this with a full CI message. Don't include any "extra" bytes such as
|
||||
the leading/trailing 0xf0/0xf7 for messages that were originally in bytestream midi format,
|
||||
or the packet-header bytes from UMP-formatted sysex messages.
|
||||
|
||||
Returns nullopt if the message doesn't need to be acknowledged by the entity with the provided MUID,
|
||||
or if the message is malformed.
|
||||
Otherwise, returns a parsed header, and optionally a body.
|
||||
If the body is std::monostate, then something went wrong while parsing. For example, the body
|
||||
may be malformed, or the CI version might be unrecognised.
|
||||
*/
|
||||
static std::optional<Message::Parsed> parse (MUID ourMUID, Span<const std::byte> message, Status* = nullptr);
|
||||
|
||||
/** Parses the provided message;
|
||||
|
||||
Call this with a full CI message. Don't include any "extra" bytes such as
|
||||
the leading/trailing 0xf0/0xf7 for messages that were originally in bytestream midi format,
|
||||
or the packet-header bytes from UMP-formatted sysex messages.
|
||||
|
||||
Returns nullopt if the message is malformed.
|
||||
Otherwise, returns a parsed header, and optionally a body.
|
||||
If the body is std::monostate, then something went wrong while parsing. For example, the body
|
||||
may be malformed, or the CI version might be unrecognised.
|
||||
*/
|
||||
static std::optional<Message::Parsed> parse (Span<const std::byte> message, Status* = nullptr);
|
||||
|
||||
/** Returns a human-readable string describing the message. */
|
||||
static String getMessageDescription (const Message::Parsed& message);
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
51
modules/juce_midi_ci/ci/juce_CIProfileAtAddress.h
Normal file
51
modules/juce_midi_ci/ci/juce_CIProfileAtAddress.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Holds a profile ID, and the address of a group/channel.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class ProfileAtAddress
|
||||
{
|
||||
auto tie() const { return std::tie (profile, address); }
|
||||
|
||||
public:
|
||||
Profile profile; ///< The id of a MIDI-CI profile
|
||||
ChannelAddress address; ///< A group and channel
|
||||
|
||||
bool operator== (const ProfileAtAddress& x) const { return tie() == x.tie(); }
|
||||
bool operator!= (const ProfileAtAddress& x) const { return tie() != x.tie(); }
|
||||
|
||||
bool operator< (const ProfileAtAddress& x) const { return tie() < x.tie(); }
|
||||
bool operator<= (const ProfileAtAddress& x) const { return tie() <= x.tie(); }
|
||||
bool operator> (const ProfileAtAddress& x) const { return tie() > x.tie(); }
|
||||
bool operator>= (const ProfileAtAddress& x) const { return tie() >= x.tie(); }
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
57
modules/juce_midi_ci/ci/juce_CIProfileDelegate.h
Normal file
57
modules/juce_midi_ci/ci/juce_CIProfileDelegate.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
An interface with methods that can be overridden to customise how a Device
|
||||
implementing profiles responds to profile inquiries.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct ProfileDelegate
|
||||
{
|
||||
ProfileDelegate() = default;
|
||||
virtual ~ProfileDelegate() = default;
|
||||
ProfileDelegate (const ProfileDelegate&) = default;
|
||||
ProfileDelegate (ProfileDelegate&&) = default;
|
||||
ProfileDelegate& operator= (const ProfileDelegate&) = default;
|
||||
ProfileDelegate& operator= (ProfileDelegate&&) = default;
|
||||
|
||||
/** Called when a remote device requests that a profile is enabled or disabled.
|
||||
|
||||
Old MIDI-CI implementations on remote devices may request that a profile
|
||||
is enabled with zero channels active - in this situation, it is
|
||||
recommended that you use ProfileHost::enableProfile to enable the
|
||||
default number of channels for that profile.
|
||||
*/
|
||||
virtual void profileEnablementRequested ([[maybe_unused]] MUID x,
|
||||
[[maybe_unused]] ProfileAtAddress profileAtAddress,
|
||||
[[maybe_unused]] int numChannels,
|
||||
[[maybe_unused]] bool enabled) = 0;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
336
modules/juce_midi_ci/ci/juce_CIProfileHost.cpp
Normal file
336
modules/juce_midi_ci/ci/juce_CIProfileHost.cpp
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
class ProfileHost::Visitor : public detail::MessageTypeUtils::MessageVisitor
|
||||
{
|
||||
public:
|
||||
Visitor (ProfileHost* h, ResponderOutput* o, bool* b)
|
||||
: host (h), output (o), handled (b) {}
|
||||
|
||||
void visit (const Message::ProfileInquiry& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileDetails& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileOn& body) const override { visitImpl (body); }
|
||||
void visit (const Message::ProfileOff& body) const override { visitImpl (body); }
|
||||
using MessageVisitor::visit;
|
||||
|
||||
static auto getNumChannels (Message::Header header, Message::ProfileOn p)
|
||||
{
|
||||
return (uint8_t) header.version >= 2 ? p.numChannels : 1;
|
||||
}
|
||||
|
||||
static auto getNumChannels (Message::Header, Message::ProfileOff) { return 0; }
|
||||
|
||||
private:
|
||||
template <typename Body>
|
||||
void visitImpl (const Body& body) const { *handled = messageReceived (body); }
|
||||
|
||||
bool messageReceived (const Message::ProfileInquiry&) const
|
||||
{
|
||||
host->isResponder = true;
|
||||
|
||||
if ((uint8_t) output->getIncomingHeader().deviceID < 16
|
||||
|| output->getIncomingHeader().deviceID == ChannelInGroup::wholeGroup)
|
||||
{
|
||||
if (const auto* state = host->getProfileStates().groupStates[output->getIncomingGroup()].getStateForDestination (output->getIncomingHeader().deviceID))
|
||||
{
|
||||
const auto active = state->getActive();
|
||||
const auto inactive = state->getInactive();
|
||||
detail::MessageTypeUtils::send (*output, Message::ProfileInquiryResponse { active, inactive });
|
||||
}
|
||||
}
|
||||
else if (output->getIncomingHeader().deviceID == ChannelInGroup::wholeBlock)
|
||||
{
|
||||
auto header = output->getReplyHeader (detail::MessageMeta::Meta<Message::ProfileInquiryResponse>::subID2);
|
||||
|
||||
const auto sendIfNonEmpty = [&] (const auto group, const auto& state)
|
||||
{
|
||||
if (! state.empty())
|
||||
{
|
||||
const auto active = state.getActive();
|
||||
const auto inactive = state.getInactive();
|
||||
detail::MessageTypeUtils::send (*output, (uint8_t) group, header, Message::ProfileInquiryResponse { active, inactive });
|
||||
}
|
||||
};
|
||||
|
||||
for (auto groupNum = 0; groupNum < host->functionBlock.numGroups; ++groupNum)
|
||||
{
|
||||
const auto group = host->functionBlock.firstGroup + groupNum;
|
||||
const auto& groupState = host->getProfileStates().groupStates[(size_t) group];
|
||||
|
||||
for (size_t channel = 0; channel < groupState.channelStates.size(); ++channel)
|
||||
{
|
||||
header.deviceID = ChannelInGroup (channel);
|
||||
sendIfNonEmpty (group, groupState.channelStates[channel]);
|
||||
}
|
||||
}
|
||||
|
||||
header.deviceID = ChannelInGroup::wholeGroup;
|
||||
|
||||
for (auto i = 0; i < host->functionBlock.numGroups; ++i)
|
||||
{
|
||||
const auto group = host->functionBlock.firstGroup + i;
|
||||
const auto& groupState = host->getProfileStates().groupStates[(size_t) group];
|
||||
sendIfNonEmpty (group, groupState.groupState);
|
||||
}
|
||||
|
||||
// Always send the block response to indicate that no further replies will follow
|
||||
header.deviceID = ChannelInGroup::wholeBlock;
|
||||
const auto state = host->getProfileStates().blockState;
|
||||
const auto active = state.getActive();
|
||||
const auto inactive = state.getInactive();
|
||||
detail::MessageTypeUtils::send (*output, output->getIncomingGroup(), header, Message::ProfileInquiryResponse { active, inactive });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool messageReceived (const Message::ProfileDetails& body) const
|
||||
{
|
||||
if (body.target == std::byte{})
|
||||
{
|
||||
const auto address = ChannelAddress{}.withGroup (output->getIncomingGroup())
|
||||
.withChannel (output->getIncomingHeader().deviceID);
|
||||
const ProfileAtAddress profileAtAddress { body.profile, address };
|
||||
const auto state = host->getState (profileAtAddress);
|
||||
std::vector<std::byte> extraData;
|
||||
detail::Marshalling::Writer { extraData } (state.active, state.supported);
|
||||
detail::MessageTypeUtils::send (*output, Message::ProfileDetailsResponse { body.profile, body.target, extraData });
|
||||
}
|
||||
else
|
||||
{
|
||||
detail::MessageTypeUtils::sendNAK (*output, std::byte { 0x04 });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Body>
|
||||
bool profileEnablementReceived (const Body& request) const
|
||||
{
|
||||
const auto destination = ChannelAddress{}.withGroup (output->getIncomingGroup())
|
||||
.withChannel (output->getIncomingHeader().deviceID);
|
||||
if (auto* state = host->states.getStateForDestination (destination))
|
||||
{
|
||||
if (state->get (request.profile).isSupported())
|
||||
{
|
||||
const auto address = ChannelAddress{}.withGroup (output->getIncomingGroup())
|
||||
.withChannel (output->getIncomingHeader().deviceID);
|
||||
const ProfileAtAddress profileAtAddress { request.profile, address };
|
||||
|
||||
{
|
||||
const ScopedValueSetter scope { host->currentEnablementMessage,
|
||||
std::optional<ProfileAtAddress> (profileAtAddress) };
|
||||
host->delegate.profileEnablementRequested (output->getIncomingHeader().source,
|
||||
profileAtAddress,
|
||||
getNumChannels (output->getIncomingHeader(), request),
|
||||
std::is_same_v<Message::ProfileOn, Body>);
|
||||
}
|
||||
|
||||
const auto currentState = host->getState (profileAtAddress);
|
||||
|
||||
const auto sendResponse = [&] (auto response)
|
||||
{
|
||||
const Message::Header header
|
||||
{
|
||||
profileAtAddress.address.getChannel(),
|
||||
detail::MessageMeta::Meta<decltype (response)>::subID2,
|
||||
detail::MessageMeta::implementationVersion,
|
||||
output->getMuid(),
|
||||
MUID::getBroadcast(),
|
||||
};
|
||||
|
||||
detail::MessageTypeUtils::send (*output, profileAtAddress.address.getGroup(), header, response);
|
||||
};
|
||||
|
||||
if (currentState.isActive())
|
||||
sendResponse (Message::ProfileEnabledReport { profileAtAddress.profile, currentState.active });
|
||||
else
|
||||
sendResponse (Message::ProfileDisabledReport { profileAtAddress.profile, 0 });
|
||||
|
||||
host->isResponder = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
detail::MessageTypeUtils::sendNAK (*output, {});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool messageReceived (const Message::ProfileOn& request) const
|
||||
{
|
||||
return profileEnablementReceived (request);
|
||||
}
|
||||
|
||||
bool messageReceived (const Message::ProfileOff& request) const
|
||||
{
|
||||
return profileEnablementReceived (request);
|
||||
}
|
||||
|
||||
ProfileHost* host = nullptr;
|
||||
ResponderOutput* output = nullptr;
|
||||
bool* handled = nullptr;
|
||||
};
|
||||
|
||||
void ProfileHost::addProfile (ProfileAtAddress profileAtAddress, int maxNumChannels)
|
||||
{
|
||||
auto* state = states.getStateForDestination (profileAtAddress.address);
|
||||
|
||||
if (state == nullptr || state->get (profileAtAddress.profile).isSupported())
|
||||
return;
|
||||
|
||||
// There are only 256 channels on a UMP endpoint, so requesting more probably doesn't make sense!
|
||||
jassert (maxNumChannels <= 256);
|
||||
|
||||
state->set (profileAtAddress.profile, { (uint16_t) maxNumChannels, 0 });
|
||||
|
||||
if (! isResponder || profileAtAddress == currentEnablementMessage)
|
||||
return;
|
||||
|
||||
const Message::Header header
|
||||
{
|
||||
profileAtAddress.address.getChannel(),
|
||||
detail::MessageMeta::Meta<Message::ProfileAdded>::subID2,
|
||||
detail::MessageMeta::implementationVersion,
|
||||
output.getMuid(),
|
||||
MUID::getBroadcast(),
|
||||
};
|
||||
|
||||
detail::MessageTypeUtils::send (output,
|
||||
profileAtAddress.address.getGroup(),
|
||||
header,
|
||||
Message::ProfileAdded { profileAtAddress.profile });
|
||||
}
|
||||
|
||||
void ProfileHost::removeProfile (ProfileAtAddress profileAtAddress)
|
||||
{
|
||||
auto* state = states.getStateForDestination (profileAtAddress.address);
|
||||
|
||||
if (state == nullptr)
|
||||
return;
|
||||
|
||||
disableProfile (profileAtAddress);
|
||||
|
||||
if (! state->get (profileAtAddress.profile).isSupported())
|
||||
return;
|
||||
|
||||
state->erase (profileAtAddress.profile);
|
||||
|
||||
if (! isResponder || profileAtAddress == currentEnablementMessage)
|
||||
return;
|
||||
|
||||
const Message::Header header
|
||||
{
|
||||
profileAtAddress.address.getChannel(),
|
||||
detail::MessageMeta::Meta<Message::ProfileRemoved>::subID2,
|
||||
detail::MessageMeta::implementationVersion,
|
||||
output.getMuid(),
|
||||
MUID::getBroadcast(),
|
||||
};
|
||||
|
||||
detail::MessageTypeUtils::send (output,
|
||||
profileAtAddress.address.getGroup(),
|
||||
header,
|
||||
Message::ProfileRemoved { profileAtAddress.profile });
|
||||
}
|
||||
|
||||
void ProfileHost::enableProfile (ProfileAtAddress profileAtAddress, int numChannels)
|
||||
{
|
||||
auto* state = states.getStateForDestination (profileAtAddress.address);
|
||||
|
||||
if (state == nullptr)
|
||||
return;
|
||||
|
||||
const auto old = state->get (profileAtAddress.profile);
|
||||
|
||||
if (! old.isSupported())
|
||||
return;
|
||||
|
||||
// There are only 256 channels on a UMP endpoint, so requesting more probably doesn't make sense!
|
||||
jassert (numChannels <= 256);
|
||||
|
||||
const auto enabledChannels = jmin (old.supported, (uint16_t) numChannels);
|
||||
state->set (profileAtAddress.profile, { old.supported, enabledChannels });
|
||||
|
||||
if (! isResponder || profileAtAddress == currentEnablementMessage)
|
||||
return;
|
||||
|
||||
const Message::Header header
|
||||
{
|
||||
profileAtAddress.address.getChannel(),
|
||||
detail::MessageMeta::Meta<Message::ProfileEnabledReport>::subID2,
|
||||
detail::MessageMeta::implementationVersion,
|
||||
output.getMuid(),
|
||||
MUID::getBroadcast(),
|
||||
};
|
||||
|
||||
detail::MessageTypeUtils::send (output,
|
||||
profileAtAddress.address.getGroup(),
|
||||
header,
|
||||
Message::ProfileEnabledReport { profileAtAddress.profile, enabledChannels });
|
||||
}
|
||||
|
||||
void ProfileHost::disableProfile (ProfileAtAddress profileAtAddress)
|
||||
{
|
||||
auto* state = states.getStateForDestination (profileAtAddress.address);
|
||||
|
||||
if (state == nullptr)
|
||||
return;
|
||||
|
||||
const auto old = state->get (profileAtAddress.profile);
|
||||
|
||||
if (! old.isActive())
|
||||
return;
|
||||
|
||||
state->set (profileAtAddress.profile, { old.supported, 0 });
|
||||
|
||||
if (! isResponder || profileAtAddress == currentEnablementMessage)
|
||||
return;
|
||||
|
||||
const Message::Header header
|
||||
{
|
||||
profileAtAddress.address.getChannel(),
|
||||
detail::MessageMeta::Meta<Message::ProfileDisabledReport>::subID2,
|
||||
detail::MessageMeta::implementationVersion,
|
||||
output.getMuid(),
|
||||
MUID::getBroadcast(),
|
||||
};
|
||||
|
||||
detail::MessageTypeUtils::send (output,
|
||||
profileAtAddress.address.getGroup(),
|
||||
header,
|
||||
Message::ProfileDisabledReport { profileAtAddress.profile, old.active });
|
||||
}
|
||||
|
||||
bool ProfileHost::tryRespond (ResponderOutput& responderOutput, const Message::Parsed& message)
|
||||
{
|
||||
bool result = false;
|
||||
detail::MessageTypeUtils::visit (message, Visitor { this, &responderOutput, &result });
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
112
modules/juce_midi_ci/ci/juce_CIProfileHost.h
Normal file
112
modules/juce_midi_ci/ci/juce_CIProfileHost.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Acting as a ResponderListener, instances of this class can formulate
|
||||
appropriate replies to profile transactions initiated by remote devices.
|
||||
|
||||
ProfileHost instances also contains methods to inform remote devices about
|
||||
changes to local profile state.
|
||||
|
||||
Stores the current state of profiles on the local device.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class ProfileHost final : public ResponderDelegate
|
||||
{
|
||||
public:
|
||||
/** @internal
|
||||
|
||||
Rather than constructing one of these objects yourself, you should configure
|
||||
a Device with profile support, and then use Device::getProfileHost()
|
||||
to retrieve a profile host that has been set up to work with that device.
|
||||
*/
|
||||
ProfileHost (FunctionBlock fb, ProfileDelegate& d, BufferOutput& o)
|
||||
: functionBlock (fb), delegate (d), output (o) {}
|
||||
|
||||
/** Adds support for a profile on the specified group/channel with a
|
||||
maximum number of channels that may be activated.
|
||||
*/
|
||||
void addProfile (ProfileAtAddress, int maxNumChannels = 1);
|
||||
|
||||
/** Removes support for a profile on the specified group/channel.
|
||||
*/
|
||||
void removeProfile (ProfileAtAddress);
|
||||
|
||||
/** Activates a profile on the specified group/channel with the provided
|
||||
number of channels.
|
||||
|
||||
The profile should previously have been added with addProfile(), and
|
||||
numChannels should be in the closed range between 1 and the maximum
|
||||
number of channels allowed for that profile.
|
||||
*/
|
||||
void enableProfile (ProfileAtAddress, int numChannels);
|
||||
|
||||
/** Deactivates a profile on the specified group/channel.
|
||||
*/
|
||||
void disableProfile (ProfileAtAddress);
|
||||
|
||||
/** Returns the profile states (supported/active) for all groups and channels.
|
||||
*/
|
||||
const BlockProfileStates& getProfileStates() const { return states; }
|
||||
|
||||
/** Returns the number of supported and active channels for the given
|
||||
profile on the specified group/channel.
|
||||
|
||||
If the supported channels is 0, then the profile is not supported
|
||||
on the group/channel.
|
||||
|
||||
If the active channels is 0, then the profile is inactive on the
|
||||
group/channel.
|
||||
*/
|
||||
SupportedAndActive getState (ProfileAtAddress profileAtAddress) const
|
||||
{
|
||||
if (auto* state = states.getStateForDestination (profileAtAddress.address))
|
||||
return state->get (profileAtAddress.profile);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
bool tryRespond (ResponderOutput&, const Message::Parsed&) override;
|
||||
|
||||
private:
|
||||
class Visitor;
|
||||
|
||||
template <typename Body>
|
||||
bool profileEnablementReceived (ResponderOutput&, const Body&);
|
||||
|
||||
FunctionBlock functionBlock;
|
||||
ProfileDelegate& delegate;
|
||||
BufferOutput& output;
|
||||
BlockProfileStates states;
|
||||
bool isResponder = false;
|
||||
std::optional<ProfileAtAddress> currentEnablementMessage;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
86
modules/juce_midi_ci/ci/juce_CIProfileStates.cpp
Normal file
86
modules/juce_midi_ci/ci/juce_CIProfileStates.cpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
SupportedAndActive ChannelProfileStates::get (const Profile& profile) const
|
||||
{
|
||||
const auto iter = std::lower_bound (entries.begin(), entries.end(), profile);
|
||||
|
||||
if (iter != entries.end() && iter->profile == profile)
|
||||
return iter->state;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Profile> ChannelProfileStates::getActive() const
|
||||
{
|
||||
std::vector<Profile> result;
|
||||
|
||||
for (const auto& item : entries)
|
||||
if (item.state.isActive())
|
||||
result.push_back (item.profile);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Profile> ChannelProfileStates::getInactive() const
|
||||
{
|
||||
std::vector<Profile> result;
|
||||
|
||||
for (const auto& item : entries)
|
||||
if (item.state.isSupported())
|
||||
result.push_back (item.profile);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ChannelProfileStates::set (const Profile& profile, SupportedAndActive state)
|
||||
{
|
||||
const auto iter = std::lower_bound (entries.begin(), entries.end(), profile);
|
||||
|
||||
if (iter != entries.end() && iter->profile == profile)
|
||||
{
|
||||
if (state != SupportedAndActive{})
|
||||
iter->state = state;
|
||||
else
|
||||
entries.erase (iter);
|
||||
}
|
||||
else if (state != SupportedAndActive{})
|
||||
{
|
||||
entries.insert (iter, { profile, state });
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelProfileStates::erase (const Profile& profile)
|
||||
{
|
||||
const auto iter = std::lower_bound (entries.begin(), entries.end(), profile);
|
||||
|
||||
if (iter != entries.end() && iter->profile == profile)
|
||||
entries.erase (iter);
|
||||
}
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
161
modules/juce_midi_ci/ci/juce_CIProfileStates.h
Normal file
161
modules/juce_midi_ci/ci/juce_CIProfileStates.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Holds a profile ID, along with the number of supported and active channels
|
||||
corresponding to that profile.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct ProfileStateEntry
|
||||
{
|
||||
Profile profile; ///< A MIDI-CI profile ID
|
||||
SupportedAndActive state; ///< The number of channels corresponding to the profile
|
||||
|
||||
bool operator< (const Profile& other) const { return profile < other; }
|
||||
bool operator< (const ProfileStateEntry& other) const { return profile < other.profile; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds the number of channels that are supported and activated for all profiles
|
||||
at a particular channel address.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class ChannelProfileStates
|
||||
{
|
||||
public:
|
||||
using Entry = ProfileStateEntry;
|
||||
|
||||
/** Returns the number of channels that are supported and active for the
|
||||
given profile.
|
||||
*/
|
||||
SupportedAndActive get (const Profile& profile) const;
|
||||
|
||||
/** Returns all profiles that are active at this address. */
|
||||
std::vector<Profile> getActive() const;
|
||||
|
||||
/** Returns all profiles that are supported but inactive at this address. */
|
||||
std::vector<Profile> getInactive() const;
|
||||
|
||||
/** Sets the number of channels that are supported/active for a given profile. */
|
||||
void set (const Profile& profile, SupportedAndActive state);
|
||||
|
||||
/** Removes the record of a particular profile, equivalent to removing support. */
|
||||
void erase (const Profile& profile);
|
||||
|
||||
/** Gets a const iterator over all profiles, for range-for compatibility. */
|
||||
auto begin() const { return entries.begin(); }
|
||||
|
||||
/** Gets a const iterator over all profiles, for range-for compatibility. */
|
||||
auto end() const { return entries.end(); }
|
||||
|
||||
/** Returns true if no profiles are supported. */
|
||||
auto empty() const { return entries.empty(); }
|
||||
|
||||
/** Returns the number of profiles that are supported at this address. */
|
||||
auto size() const { return entries.size(); }
|
||||
|
||||
private:
|
||||
std::vector<Entry> entries;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains profile states for each channel in a group, along with the state
|
||||
of profiles that apply to the group itself.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class GroupProfileStates
|
||||
{
|
||||
template <typename This>
|
||||
static auto getStateForDestinationImpl (This& t, ChannelInGroup destination) -> decltype (&t.groupState)
|
||||
{
|
||||
if (destination == ChannelInGroup::wholeGroup)
|
||||
return &t.groupState;
|
||||
|
||||
if (const auto index = (size_t) destination; index < t.channelStates.size())
|
||||
return &t.channelStates[index];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
/** Returns the profile state for the group or a contained channel as appropriate.
|
||||
Returns nullptr if ChannelInGroup refers to a whole function block.
|
||||
*/
|
||||
auto* getStateForDestination (ChannelInGroup d) { return getStateForDestinationImpl (*this, d); }
|
||||
|
||||
/** Returns the profile state for the group or a contained channel as appropriate.
|
||||
Returns nullptr if ChannelInGroup refers to a whole function block.
|
||||
*/
|
||||
auto* getStateForDestination (ChannelInGroup d) const { return getStateForDestinationImpl (*this, d); }
|
||||
|
||||
std::array<ChannelProfileStates, 16> channelStates; ///< Profile states for each channel in the group
|
||||
ChannelProfileStates groupState; ///< Profile states for the group itself
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains profile states for each group and channel in a function block, along with the state
|
||||
of profiles that apply to the function block itself.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class BlockProfileStates
|
||||
{
|
||||
template <typename This>
|
||||
static auto getStateForDestinationImpl (This& t, ChannelAddress address) -> decltype (&t.blockState)
|
||||
{
|
||||
if (address.isBlock())
|
||||
return &t.blockState;
|
||||
|
||||
if (const auto index = (size_t) address.getGroup(); index < t.groupStates.size())
|
||||
return t.groupStates[index].getStateForDestination (address.getChannel());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
/** Returns the profile state for the function block, group, or channel as appropriate.
|
||||
Returns nullptr if the address refers to a non-existent channel or group.
|
||||
*/
|
||||
auto* getStateForDestination (ChannelAddress address) { return getStateForDestinationImpl (*this, address); }
|
||||
|
||||
/** Returns the profile state for the function block, group, or channel as appropriate.
|
||||
Returns nullptr if the address refers to a non-existent channel or group.
|
||||
*/
|
||||
auto* getStateForDestination (ChannelAddress address) const { return getStateForDestinationImpl (*this, address); }
|
||||
|
||||
std::array<GroupProfileStates, 16> groupStates; ///< Profile states for each group in the function block
|
||||
ChannelProfileStates blockState; ///< Profile states for the whole function block
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
255
modules/juce_midi_ci/ci/juce_CIPropertyDelegate.cpp
Normal file
255
modules/juce_midi_ci/ci/juce_CIPropertyDelegate.cpp
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
struct PropertyDelegateDetail
|
||||
{
|
||||
/*
|
||||
Note: We don't use ToVar and FromVar here, because we want to omit fields that are using
|
||||
their default values.
|
||||
*/
|
||||
|
||||
template <typename Target>
|
||||
static Target parseTargetHeader (const var& v,
|
||||
const std::map<Identifier, void (*) (Target&, const var&)>& parsers)
|
||||
{
|
||||
Target target;
|
||||
|
||||
if (auto* obj = v.getDynamicObject())
|
||||
{
|
||||
for (const auto& pair : obj->getProperties())
|
||||
{
|
||||
const auto parserIter = parsers.find (pair.name);
|
||||
|
||||
if (parserIter != parsers.end())
|
||||
parserIter->second (target, pair.value);
|
||||
else
|
||||
target.extended[pair.name] = pair.value;
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
static auto getParsersForPropertyReplyHeader()
|
||||
{
|
||||
using Target = PropertyReplyHeader;
|
||||
std::map<Identifier, void (*) (Target& header, const var& v)> map;
|
||||
|
||||
map.emplace ("status", [] (Target& header, const var& v) { header.status = v; });
|
||||
map.emplace ("message", [] (Target& header, const var& v) { header.message = v; });
|
||||
map.emplace ("cacheTime", [] (Target& header, const var& v) { header.cacheTime = v; });
|
||||
map.emplace ("mediaType", [] (Target& header, const var& v) { header.mediaType = v; });
|
||||
map.emplace ("mutualEncoding", [] (Target& header, const var& v)
|
||||
{
|
||||
header.mutualEncoding = EncodingUtils::toEncoding (v.toString().toRawUTF8()).value_or (Encoding::ascii);
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
template <typename Target>
|
||||
static auto getParsersForGenericPropertyRequestHeader()
|
||||
{
|
||||
std::map<Identifier, void (*) (Target& header, const var& v)> map;
|
||||
|
||||
map.emplace ("resource", [] (Target& header, const var& v) { header.resource = v; });
|
||||
map.emplace ("resId", [] (Target& header, const var& v) { header.resId = v; });
|
||||
map.emplace ("mediaType", [] (Target& header, const var& v) { header.mediaType = v; });
|
||||
map.emplace ("mutualEncoding", [] (Target& header, const var& v)
|
||||
{
|
||||
header.mutualEncoding = EncodingUtils::toEncoding (v.toString().toRawUTF8()).value_or (Encoding::ascii);
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static auto getParsersForPropertyRequestHeader()
|
||||
{
|
||||
auto map = getParsersForGenericPropertyRequestHeader<PropertyRequestHeader>();
|
||||
map.emplace ("setPartial", [] (PropertyRequestHeader& header, const var& v) { header.setPartial = v; });
|
||||
map.emplace ("offset", [] (PropertyRequestHeader& header, const var& v)
|
||||
{
|
||||
if (! header.pagination.has_value())
|
||||
header.pagination = Pagination{};
|
||||
|
||||
header.pagination->offset = v;
|
||||
});
|
||||
map.emplace ("limit", [] (PropertyRequestHeader& header, const var& v)
|
||||
{
|
||||
if (! header.pagination.has_value())
|
||||
header.pagination = Pagination{};
|
||||
|
||||
header.pagination->limit = v;
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
static auto getParsersForPropertySubscriptionHeader()
|
||||
{
|
||||
auto map = getParsersForGenericPropertyRequestHeader<PropertySubscriptionHeader>();
|
||||
|
||||
map.emplace ("subscribeId", [] (PropertySubscriptionHeader& header, const var& v) { header.subscribeId = v; });
|
||||
map.emplace ("command", [] (PropertySubscriptionHeader& header, const var& v)
|
||||
{
|
||||
header.command = [&]
|
||||
{
|
||||
if (v == "start")
|
||||
return PropertySubscriptionCommand::start;
|
||||
|
||||
if (v == "partial")
|
||||
return PropertySubscriptionCommand::partial;
|
||||
|
||||
if (v == "full")
|
||||
return PropertySubscriptionCommand::full;
|
||||
|
||||
if (v == "notify")
|
||||
return PropertySubscriptionCommand::notify;
|
||||
|
||||
if (v == "end")
|
||||
return PropertySubscriptionCommand::end;
|
||||
|
||||
return PropertySubscriptionCommand::notify;
|
||||
}();
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static auto getSetPartial (const PropertySubscriptionHeader&) { return false; }
|
||||
static auto getSetPartial (const PropertyRequestHeader& h) { return h.setPartial; }
|
||||
static auto getSetPartial (const PropertyReplyHeader&) { return false; }
|
||||
|
||||
static auto getPagination (const PropertySubscriptionHeader&) { return std::optional<Pagination>{}; }
|
||||
static auto getPagination (const PropertyRequestHeader& h) { return h.pagination; }
|
||||
static auto getPagination (const PropertyReplyHeader&) { return std::optional<Pagination>{}; }
|
||||
|
||||
static auto getCacheTime (const PropertySubscriptionHeader&) { return 0; }
|
||||
static auto getCacheTime (const PropertyRequestHeader&) { return 0; }
|
||||
static auto getCacheTime (const PropertyReplyHeader& h) { return h.cacheTime; }
|
||||
|
||||
static auto getMessage (const PropertySubscriptionHeader&) { return String{}; }
|
||||
static auto getMessage (const PropertyRequestHeader&) { return String{}; }
|
||||
static auto getMessage (const PropertyReplyHeader& h) { return h.message; }
|
||||
|
||||
static auto getResource (const PropertySubscriptionHeader& h) { return h.resource; }
|
||||
static auto getResource (const PropertyRequestHeader& h) { return h.resource; }
|
||||
static auto getResource (const PropertyReplyHeader&) { return String{}; }
|
||||
|
||||
static auto getResId (const PropertySubscriptionHeader& h) { return h.resId; }
|
||||
static auto getResId (const PropertyRequestHeader& h) { return h.resId; }
|
||||
static auto getResId (const PropertyReplyHeader&) { return String{}; }
|
||||
|
||||
static auto getCommand (const PropertySubscriptionHeader& h) { return h.command; }
|
||||
static auto getCommand (const PropertyRequestHeader&) { return PropertySubscriptionCommand{}; }
|
||||
static auto getCommand (const PropertyReplyHeader&) { return PropertySubscriptionCommand{}; }
|
||||
|
||||
static auto getSubscribeId (const PropertySubscriptionHeader& h) { return h.subscribeId; }
|
||||
static auto getSubscribeId (const PropertyRequestHeader&) { return String{}; }
|
||||
static auto getSubscribeId (const PropertyReplyHeader&) { return String{}; }
|
||||
|
||||
static auto getStatus (const PropertySubscriptionHeader&) { return 0; }
|
||||
static auto getStatus (const PropertyRequestHeader&) { return 0; }
|
||||
static auto getStatus (const PropertyReplyHeader& h) { return h.status; }
|
||||
|
||||
template <typename T>
|
||||
static auto toFieldsFromHeader (const T& t)
|
||||
{
|
||||
auto fields = t.extended;
|
||||
|
||||
if (getResource (t) != getResource (T()))
|
||||
fields["resource"] = getResource (t);
|
||||
|
||||
if (getCommand (t) != getCommand (T()))
|
||||
fields["command"] = PropertySubscriptionCommandUtils::toString (getCommand (t));
|
||||
|
||||
if (getSubscribeId (t) != getSubscribeId (T()))
|
||||
fields["subscribeId"] = getSubscribeId (t);
|
||||
|
||||
if (getResId (t) != getResId (T()))
|
||||
fields["resId"] = getResId (t);
|
||||
|
||||
if (t.mutualEncoding != T().mutualEncoding)
|
||||
fields["mutualEncoding"] = EncodingUtils::toString (t.mutualEncoding);
|
||||
|
||||
if (t.mediaType != T().mediaType)
|
||||
fields["mediaType"] = t.mediaType;
|
||||
|
||||
if (getStatus (t) != getStatus (T()))
|
||||
fields["status"] = getStatus (t);
|
||||
|
||||
if (getSetPartial (t))
|
||||
fields["setPartial"] = true;
|
||||
|
||||
if (getCacheTime (t) != getCacheTime (T()))
|
||||
fields["cacheTime"] = getCacheTime (t);
|
||||
|
||||
if (getMessage (t) != getMessage (T()))
|
||||
fields["message"] = getMessage (t);
|
||||
|
||||
if (const auto pagination = getPagination (t))
|
||||
{
|
||||
fields["offset"] = pagination->offset;
|
||||
fields["limit"] = pagination->limit;
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
PropertySubscriptionHeader PropertySubscriptionHeader::parseCondensed (const var& v)
|
||||
{
|
||||
return PropertyDelegateDetail::parseTargetHeader (v, PropertyDelegateDetail::getParsersForPropertySubscriptionHeader());
|
||||
}
|
||||
|
||||
var PropertySubscriptionHeader::toVarCondensed() const
|
||||
{
|
||||
return JSONUtils::makeObjectWithKeyFirst (PropertyDelegateDetail::toFieldsFromHeader (*this), "command");
|
||||
}
|
||||
|
||||
PropertyRequestHeader PropertyRequestHeader::parseCondensed (const var& v)
|
||||
{
|
||||
return PropertyDelegateDetail::parseTargetHeader (v, PropertyDelegateDetail::getParsersForPropertyRequestHeader());
|
||||
}
|
||||
|
||||
var PropertyRequestHeader::toVarCondensed() const
|
||||
{
|
||||
return JSONUtils::makeObjectWithKeyFirst (PropertyDelegateDetail::toFieldsFromHeader (*this), "resource");
|
||||
}
|
||||
|
||||
PropertyReplyHeader PropertyReplyHeader::parseCondensed (const var& v)
|
||||
{
|
||||
return PropertyDelegateDetail::parseTargetHeader (v, PropertyDelegateDetail::getParsersForPropertyReplyHeader());
|
||||
}
|
||||
|
||||
var PropertyReplyHeader::toVarCondensed() const
|
||||
{
|
||||
return JSONUtils::makeObjectWithKeyFirst (PropertyDelegateDetail::toFieldsFromHeader (*this), "status");
|
||||
}
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
270
modules/juce_midi_ci/ci/juce_CIPropertyDelegate.h
Normal file
270
modules/juce_midi_ci/ci/juce_CIPropertyDelegate.h
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
#define JUCE_SUBSCRIPTION_COMMANDS X(start) X(partial) X(full) X(notify) X(end)
|
||||
|
||||
/**
|
||||
Kinds of command that may be sent as part of a subscription update.
|
||||
|
||||
Check the Property Exchange specification to find the meaning of the
|
||||
different kinds.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
enum class PropertySubscriptionCommand
|
||||
{
|
||||
#define X(str) str,
|
||||
JUCE_SUBSCRIPTION_COMMANDS
|
||||
#undef X
|
||||
};
|
||||
|
||||
struct PropertySubscriptionCommandUtils
|
||||
{
|
||||
PropertySubscriptionCommandUtils() = delete;
|
||||
|
||||
/** Converts a command to a human-readable string. */
|
||||
static const char* toString (PropertySubscriptionCommand x)
|
||||
{
|
||||
switch (x)
|
||||
{
|
||||
#define X(str) case PropertySubscriptionCommand::str: return #str;
|
||||
JUCE_SUBSCRIPTION_COMMANDS
|
||||
#undef X
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** Converts a command string from a property exchange JSON header to
|
||||
an PropertySubscriptionCommand.
|
||||
*/
|
||||
static std::optional<PropertySubscriptionCommand> toCommand (const char* str)
|
||||
{
|
||||
#define X(name) if (std::string_view (str) == std::string_view (#name)) return PropertySubscriptionCommand::name;
|
||||
JUCE_SUBSCRIPTION_COMMANDS
|
||||
#undef X
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
#undef JUCE_SUBSCRIPTION_COMMANDS
|
||||
|
||||
/**
|
||||
A struct containing data members that correspond to common fields in a
|
||||
property subscription header.
|
||||
|
||||
Check the Property Exchange specification to find the meaning of the
|
||||
different fields.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct PropertySubscriptionHeader
|
||||
{
|
||||
String resource;
|
||||
String resId;
|
||||
Encoding mutualEncoding = Encoding::ascii;
|
||||
String mediaType = "application/json";
|
||||
PropertySubscriptionCommand command { -1 };
|
||||
String subscribeId;
|
||||
std::map<Identifier, var> extended;
|
||||
|
||||
/** Converts a JSON object to a PropertyRequestHeader.
|
||||
|
||||
Unspecified fields will use their default values.
|
||||
*/
|
||||
static PropertySubscriptionHeader parseCondensed (const var&);
|
||||
|
||||
/** Converts a PropertySubscriptionHeader to a JSON object suitable for use as
|
||||
a MIDI-CI message header after conversion to 7-bit ASCII.
|
||||
*/
|
||||
var toVarCondensed() const;
|
||||
};
|
||||
|
||||
/**
|
||||
Contains information about the pagination of a request.
|
||||
|
||||
Check the Property Exchange specification to find the meaning of the
|
||||
different fields.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct Pagination
|
||||
{
|
||||
int offset = 0;
|
||||
int limit = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
A struct containing data members that correspond to common fields in a
|
||||
property request header.
|
||||
|
||||
Check the Property Exchange specification to find the meaning of the
|
||||
different fields.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct PropertyRequestHeader
|
||||
{
|
||||
String resource;
|
||||
String resId;
|
||||
Encoding mutualEncoding = Encoding::ascii;
|
||||
String mediaType = "application/json";
|
||||
bool setPartial = false;
|
||||
std::optional<Pagination> pagination;
|
||||
std::map<Identifier, var> extended;
|
||||
|
||||
/** Converts a JSON object to a PropertyRequestHeader.
|
||||
|
||||
Unspecified fields will use their default values.
|
||||
*/
|
||||
static PropertyRequestHeader parseCondensed (const var&);
|
||||
|
||||
/** Converts a PropertyRequestHeader to a JSON object suitable for use as
|
||||
a MIDI-CI message header after conversion to 7-bit ASCII.
|
||||
*/
|
||||
var toVarCondensed() const;
|
||||
};
|
||||
|
||||
/**
|
||||
Bundles together a property request header and a data payload.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct PropertyRequestData
|
||||
{
|
||||
PropertyRequestHeader header;
|
||||
Span<const std::byte> body;
|
||||
};
|
||||
|
||||
/**
|
||||
A struct containing data members that correspond to common fields in a
|
||||
reply to a property exchange request.
|
||||
|
||||
Check the Property Exchange specification to find the meaning of the
|
||||
different fields.
|
||||
|
||||
For extended attributes that don't correspond to any of the defined data
|
||||
members, use the 'extended' map.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct PropertyReplyHeader
|
||||
{
|
||||
int status = 200;
|
||||
String message;
|
||||
Encoding mutualEncoding = Encoding::ascii;
|
||||
int cacheTime = 0;
|
||||
String mediaType = "application/json";
|
||||
std::map<Identifier, var> extended;
|
||||
|
||||
/** Converts a JSON object to a PropertyReplyHeader.
|
||||
|
||||
Unspecified fields will use their default values.
|
||||
*/
|
||||
static PropertyReplyHeader parseCondensed (const var&);
|
||||
|
||||
/** Converts a PropertyReplyHeader to a JSON object suitable for use as
|
||||
a MIDI-CI message header after conversion to 7-bit ASCII.
|
||||
*/
|
||||
var toVarCondensed() const;
|
||||
};
|
||||
|
||||
/**
|
||||
Bundles together a property reply header and a data payload.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct PropertyReplyData
|
||||
{
|
||||
PropertyReplyHeader header;
|
||||
std::vector<std::byte> body;
|
||||
};
|
||||
|
||||
/**
|
||||
An interface with methods that can be overridden to customise how a Device
|
||||
implementing properties responds to property inquiries.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct PropertyDelegate
|
||||
{
|
||||
PropertyDelegate() = default;
|
||||
virtual ~PropertyDelegate() = default;
|
||||
PropertyDelegate (const PropertyDelegate&) = default;
|
||||
PropertyDelegate (PropertyDelegate&&) = default;
|
||||
PropertyDelegate& operator= (const PropertyDelegate&) = default;
|
||||
PropertyDelegate& operator= (PropertyDelegate&&) = default;
|
||||
|
||||
/** Returns the max number of simultaneous property exchange messages that can be processed. */
|
||||
virtual uint8_t getNumSimultaneousRequestsSupported() const { return 127; }
|
||||
|
||||
/** Returns a header/body containing the requested data.
|
||||
To report an error, you can return a failure status code in the header and leave the body empty.
|
||||
*/
|
||||
virtual PropertyReplyData propertyGetDataRequested (MUID, const PropertyRequestHeader&) = 0;
|
||||
|
||||
/** Returns a header that describes the result of the set operation. */
|
||||
virtual PropertyReplyHeader propertySetDataRequested (MUID, const PropertyRequestData&) = 0;
|
||||
|
||||
/** Returns true to allow the subscription, or false otherwise. */
|
||||
virtual bool subscriptionStartRequested (MUID, const PropertySubscriptionHeader&) = 0;
|
||||
|
||||
/** Called with the corresponding subscription token after a subscription has started. */
|
||||
virtual void subscriptionDidStart (MUID, const String& subId, const PropertySubscriptionHeader&) = 0;
|
||||
|
||||
/** Called when a device requests for an ongoing subscription to end. */
|
||||
virtual void subscriptionWillEnd (MUID, const Subscription& sub) = 0;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<midi_ci::PropertySubscriptionCommand>
|
||||
{
|
||||
static constexpr auto marshallingVersion = std::nullopt;
|
||||
|
||||
template <typename Archive>
|
||||
void load (Archive& archive, midi_ci::PropertySubscriptionCommand& t)
|
||||
{
|
||||
String command;
|
||||
archive (command);
|
||||
t = midi_ci::PropertySubscriptionCommandUtils::toCommand (command.toRawUTF8()).value_or (midi_ci::PropertySubscriptionCommand{});
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
void save (Archive& archive, const midi_ci::PropertySubscriptionCommand& t)
|
||||
{
|
||||
archive (midi_ci::PropertySubscriptionCommandUtils::toString (t));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
291
modules/juce_midi_ci/ci/juce_CIPropertyExchangeCache.cpp
Normal file
291
modules/juce_midi_ci/ci/juce_CIPropertyExchangeCache.cpp
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
class PropertyExchangeCache
|
||||
{
|
||||
public:
|
||||
explicit PropertyExchangeCache (std::function<void()> term)
|
||||
: onTerminate (std::move (term)) {}
|
||||
|
||||
struct OwningResult
|
||||
{
|
||||
explicit OwningResult (PropertyExchangeResult::Error e)
|
||||
: result (e) {}
|
||||
|
||||
OwningResult (var header, std::vector<std::byte> body)
|
||||
: backingStorage (std::move (body)),
|
||||
result (header, backingStorage) {}
|
||||
|
||||
OwningResult (OwningResult&&) noexcept = default;
|
||||
OwningResult& operator= (OwningResult&&) noexcept = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (OwningResult)
|
||||
|
||||
std::vector<std::byte> backingStorage;
|
||||
PropertyExchangeResult result;
|
||||
};
|
||||
|
||||
std::optional<OwningResult> addChunk (Message::DynamicSizePropertyExchange chunk)
|
||||
{
|
||||
jassert (chunk.thisChunkNum == lastChunk + 1 || chunk.thisChunkNum == 0);
|
||||
lastChunk = chunk.thisChunkNum;
|
||||
headerStorage.reserve (headerStorage.size() + chunk.header.size());
|
||||
std::transform (chunk.header.begin(),
|
||||
chunk.header.end(),
|
||||
std::back_inserter (headerStorage),
|
||||
[] (std::byte b) { return char (b); });
|
||||
bodyStorage.insert (bodyStorage.end(), chunk.data.begin(), chunk.data.end());
|
||||
|
||||
if (chunk.thisChunkNum != 0 && chunk.thisChunkNum != chunk.totalNumChunks)
|
||||
return {};
|
||||
|
||||
const auto headerJson = JSON::parse (String (headerStorage.data(), headerStorage.size()));
|
||||
|
||||
onTerminate = nullptr;
|
||||
const auto encodingString = headerJson.getProperty ("mutualEncoding", "ASCII").toString();
|
||||
|
||||
if (chunk.thisChunkNum != chunk.totalNumChunks)
|
||||
return std::optional<OwningResult> { std::in_place, PropertyExchangeResult::Error::partial };
|
||||
|
||||
return std::optional<OwningResult> { std::in_place,
|
||||
headerJson,
|
||||
Encodings::decode (bodyStorage, EncodingUtils::toEncoding (encodingString.toRawUTF8()).value_or (Encoding::ascii)) };
|
||||
|
||||
}
|
||||
|
||||
std::optional<OwningResult> notify (Span<const std::byte> header)
|
||||
{
|
||||
const auto headerJson = JSON::parse (String (reinterpret_cast<const char*> (header.data()), header.size()));
|
||||
|
||||
if (! headerJson.isObject())
|
||||
return {};
|
||||
|
||||
const auto status = headerJson.getProperty ("status", {});
|
||||
|
||||
if (! status.isInt() || (int) status == 100)
|
||||
return {};
|
||||
|
||||
onTerminate = nullptr;
|
||||
return std::optional<OwningResult> { std::in_place, PropertyExchangeResult::Error::notify };
|
||||
}
|
||||
|
||||
void terminate()
|
||||
{
|
||||
if (auto t = std::exchange (onTerminate, nullptr))
|
||||
t();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<char> headerStorage;
|
||||
std::vector<std::byte> bodyStorage;
|
||||
std::function<void()> onTerminate;
|
||||
uint16_t lastChunk = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class PropertyExchangeCacheArray
|
||||
{
|
||||
public:
|
||||
PropertyExchangeCacheArray() = default;
|
||||
|
||||
ErasedScopeGuard primeCacheForRequestId (std::byte id,
|
||||
std::function<void (const PropertyExchangeResult&)> onDone,
|
||||
std::function<void()> onTerminate)
|
||||
{
|
||||
auto& entry = caches[(uint8_t) id];
|
||||
entry = std::make_shared<Transaction> (std::move (onDone), std::move (onTerminate));
|
||||
auto weak = std::weak_ptr<Transaction> (entry);
|
||||
|
||||
return ErasedScopeGuard { [&entry, weak]
|
||||
{
|
||||
// If this fails, then the transaction finished before the ErasedScopeGuard was destroyed.
|
||||
if (auto locked = weak.lock())
|
||||
{
|
||||
entry->cache.terminate();
|
||||
entry = nullptr;
|
||||
}
|
||||
} };
|
||||
}
|
||||
|
||||
void addChunk (std::byte b, const Message::DynamicSizePropertyExchange& chunk)
|
||||
{
|
||||
updateCache (b, [&] (PropertyExchangeCache& c) { return c.addChunk (chunk); });
|
||||
}
|
||||
|
||||
void notify (std::byte b, Span<const std::byte> header)
|
||||
{
|
||||
updateCache (b, [&] (PropertyExchangeCache& c) { return c.notify (header); });
|
||||
}
|
||||
|
||||
bool hasTransaction (std::byte id) const
|
||||
{
|
||||
return caches[(uint8_t) id] != nullptr;
|
||||
}
|
||||
|
||||
uint8_t countOngoingTransactions() const
|
||||
{
|
||||
return (uint8_t) std::count_if (caches.begin(), caches.end(), [] (auto& c) { return c != nullptr; });
|
||||
}
|
||||
|
||||
/** MSB of result is set on failure. */
|
||||
std::byte findUnusedId (uint8_t maxSimultaneousTransactions) const
|
||||
{
|
||||
if (countOngoingTransactions() >= maxSimultaneousTransactions)
|
||||
return std::byte { 0xff };
|
||||
|
||||
return (std::byte) std::distance (caches.begin(), std::find (caches.begin(), caches.end(), nullptr));
|
||||
}
|
||||
|
||||
// Instances must stay at the same location to ensure that references captured in the
|
||||
// ErasedScopeGuard returned from primeCacheForRequestId do not dangle.
|
||||
JUCE_DECLARE_NON_COPYABLE (PropertyExchangeCacheArray)
|
||||
JUCE_DECLARE_NON_MOVEABLE (PropertyExchangeCacheArray)
|
||||
|
||||
private:
|
||||
static constexpr auto numCaches = 128;
|
||||
|
||||
class Transaction
|
||||
{
|
||||
public:
|
||||
Transaction (std::function<void (const PropertyExchangeResult&)> onSuccess,
|
||||
std::function<void()> onTerminate)
|
||||
: cache (std::move (onTerminate)), onFinish (std::move (onSuccess)) {}
|
||||
|
||||
PropertyExchangeCache cache;
|
||||
std::function<void (const PropertyExchangeResult&)> onFinish;
|
||||
};
|
||||
|
||||
template <typename WithCache>
|
||||
void updateCache (std::byte b, WithCache&& withCache)
|
||||
{
|
||||
if (auto& entry = caches[(uint8_t) b])
|
||||
{
|
||||
if (const auto result = withCache (entry->cache))
|
||||
{
|
||||
const auto tmp = std::move (entry->onFinish);
|
||||
entry = nullptr;
|
||||
NullCheckedInvocation::invoke (tmp, result->result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::array<std::shared_ptr<Transaction>, numCaches> caches;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class InitiatorPropertyExchangeCache::Impl
|
||||
{
|
||||
public:
|
||||
TokenAndId primeCache (uint8_t maxSimultaneousRequests,
|
||||
std::function<void (const PropertyExchangeResult&)> onDone,
|
||||
std::function<void (std::byte)> onTerminate)
|
||||
{
|
||||
const auto id = array.findUnusedId (maxSimultaneousRequests);
|
||||
|
||||
if ((id & std::byte { 0x80 }) != std::byte{})
|
||||
{
|
||||
NullCheckedInvocation::invoke (onDone, PropertyExchangeResult { PropertyExchangeResult::Error::tooManyTransactions });
|
||||
return {};
|
||||
}
|
||||
|
||||
auto token = array.primeCacheForRequestId (id,
|
||||
std::move (onDone),
|
||||
[id, term = std::move (onTerminate)] { NullCheckedInvocation::invoke (term, id); });
|
||||
return { std::move (token), id };
|
||||
}
|
||||
|
||||
void addChunk (std::byte b, const Message::DynamicSizePropertyExchange& chunk) { array.addChunk (b, chunk); }
|
||||
void notify (std::byte b, Span<const std::byte> header) { array.notify (b, header); }
|
||||
int countOngoingTransactions() const { return array.countOngoingTransactions(); }
|
||||
bool isAwaitingResponse() const { return countOngoingTransactions() != 0; }
|
||||
|
||||
private:
|
||||
PropertyExchangeCacheArray array;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
InitiatorPropertyExchangeCache::InitiatorPropertyExchangeCache() : pimpl (std::make_unique<Impl>()) {}
|
||||
InitiatorPropertyExchangeCache::InitiatorPropertyExchangeCache (InitiatorPropertyExchangeCache&&) noexcept = default;
|
||||
InitiatorPropertyExchangeCache& InitiatorPropertyExchangeCache::operator= (InitiatorPropertyExchangeCache&&) noexcept = default;
|
||||
InitiatorPropertyExchangeCache::~InitiatorPropertyExchangeCache() = default;
|
||||
|
||||
InitiatorPropertyExchangeCache::TokenAndId InitiatorPropertyExchangeCache::primeCache (uint8_t maxSimultaneousTransactions,
|
||||
std::function<void (const PropertyExchangeResult&)> onDone,
|
||||
std::function<void (std::byte)> onTerminate)
|
||||
{
|
||||
return pimpl->primeCache (maxSimultaneousTransactions, std::move (onDone), std::move (onTerminate));
|
||||
}
|
||||
|
||||
void InitiatorPropertyExchangeCache::addChunk (std::byte b, const Message::DynamicSizePropertyExchange& chunk) { pimpl->addChunk (b, chunk); }
|
||||
void InitiatorPropertyExchangeCache::notify (std::byte b, Span<const std::byte> header) { pimpl->notify (b, header); }
|
||||
int InitiatorPropertyExchangeCache::countOngoingTransactions() const { return pimpl->countOngoingTransactions(); }
|
||||
bool InitiatorPropertyExchangeCache::isAwaitingResponse() const { return pimpl->isAwaitingResponse(); }
|
||||
|
||||
//==============================================================================
|
||||
class ResponderPropertyExchangeCache::Impl
|
||||
{
|
||||
public:
|
||||
void primeCache (uint8_t maxSimultaneousTransactions,
|
||||
std::function<void (const PropertyExchangeResult&)> onDone,
|
||||
std::byte id)
|
||||
{
|
||||
if (array.hasTransaction (id))
|
||||
return;
|
||||
|
||||
if (array.countOngoingTransactions() >= maxSimultaneousTransactions)
|
||||
NullCheckedInvocation::invoke (onDone, PropertyExchangeResult { PropertyExchangeResult::Error::tooManyTransactions });
|
||||
else
|
||||
array.primeCacheForRequestId (id, std::move (onDone), nullptr).release();
|
||||
}
|
||||
|
||||
void addChunk (std::byte b, const Message::DynamicSizePropertyExchange& chunk) { array.addChunk (b, chunk); }
|
||||
void notify (std::byte b, Span<const std::byte> header) { array.notify (b, header); }
|
||||
int countOngoingTransactions() const { return array.countOngoingTransactions(); }
|
||||
|
||||
private:
|
||||
PropertyExchangeCacheArray array;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ResponderPropertyExchangeCache::ResponderPropertyExchangeCache() : pimpl (std::make_unique<Impl>()) {}
|
||||
ResponderPropertyExchangeCache::ResponderPropertyExchangeCache (ResponderPropertyExchangeCache&&) noexcept = default;
|
||||
ResponderPropertyExchangeCache& ResponderPropertyExchangeCache::operator= (ResponderPropertyExchangeCache&&) noexcept = default;
|
||||
ResponderPropertyExchangeCache::~ResponderPropertyExchangeCache() = default;
|
||||
|
||||
void ResponderPropertyExchangeCache::primeCache (uint8_t maxSimultaneousTransactions,
|
||||
std::function<void (const PropertyExchangeResult&)> onDone,
|
||||
std::byte id)
|
||||
{
|
||||
return pimpl->primeCache (maxSimultaneousTransactions, std::move (onDone), id);
|
||||
}
|
||||
|
||||
void ResponderPropertyExchangeCache::addChunk (std::byte b, const Message::DynamicSizePropertyExchange& chunk) { pimpl->addChunk (b, chunk); }
|
||||
void ResponderPropertyExchangeCache::notify (std::byte b, Span<const std::byte> header) { pimpl->notify (b, header); }
|
||||
int ResponderPropertyExchangeCache::countOngoingTransactions() const { return pimpl->countOngoingTransactions(); }
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
158
modules/juce_midi_ci/ci/juce_CIPropertyExchangeCache.h
Normal file
158
modules/juce_midi_ci/ci/juce_CIPropertyExchangeCache.h
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Accumulates message chunks that have been sent by another device in response
|
||||
to a transaction initiated by a local device.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class InitiatorPropertyExchangeCache
|
||||
{
|
||||
public:
|
||||
InitiatorPropertyExchangeCache();
|
||||
~InitiatorPropertyExchangeCache();
|
||||
|
||||
InitiatorPropertyExchangeCache (InitiatorPropertyExchangeCache&&) noexcept;
|
||||
InitiatorPropertyExchangeCache& operator= (InitiatorPropertyExchangeCache&&) noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (InitiatorPropertyExchangeCache)
|
||||
|
||||
/** Holds a token that can be used to stop waiting for a reply, along with
|
||||
an identifier byte which uniquely identifies an ongoing transaction.
|
||||
*/
|
||||
struct TokenAndId
|
||||
{
|
||||
TokenAndId() = default;
|
||||
TokenAndId (ErasedScopeGuard tokenIn, std::byte idIn)
|
||||
: token (std::move (tokenIn)), id (idIn) {}
|
||||
|
||||
bool isValid() const { return (id & std::byte { 0x80 }) == std::byte{}; }
|
||||
|
||||
ErasedScopeGuard token{};
|
||||
std::byte id { 0x80 };
|
||||
};
|
||||
|
||||
/** Picks an unused request ID, and prepares the cache for that ID to accumulate message chunks.
|
||||
|
||||
Incoming chunks added with addChunk are generated by another device acting as a responder.
|
||||
*/
|
||||
TokenAndId primeCache (uint8_t maxSimultaneousRequests,
|
||||
std::function<void (const PropertyExchangeResult&)> onDone,
|
||||
std::function<void (std::byte)> onTerminate);
|
||||
|
||||
/** Adds a message chunk for the provided transaction id. */
|
||||
void addChunk (std::byte b, const Message::DynamicSizePropertyExchange& chunk);
|
||||
|
||||
/** Updates the transaction state based on the contents of the provided notification. */
|
||||
void notify (std::byte b, Span<const std::byte> header);
|
||||
|
||||
/** Returns the number of transactions that have been started but not finished. */
|
||||
int countOngoingTransactions() const;
|
||||
|
||||
/** Returns true if there are any transactions in progress that
|
||||
haven't yet received replies.
|
||||
*/
|
||||
bool isAwaitingResponse() const;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> pimpl;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Accumulates message chunks that form a request initiated by a remote device.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class ResponderPropertyExchangeCache
|
||||
{
|
||||
public:
|
||||
ResponderPropertyExchangeCache();
|
||||
~ResponderPropertyExchangeCache();
|
||||
|
||||
ResponderPropertyExchangeCache (ResponderPropertyExchangeCache&&) noexcept;
|
||||
ResponderPropertyExchangeCache& operator= (ResponderPropertyExchangeCache&&) noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ResponderPropertyExchangeCache)
|
||||
|
||||
/** Prepares the cache for the given requestId to accumulate message chunks.
|
||||
|
||||
Incoming chunks added with addChunk are generated by another device acting as an initiator.
|
||||
*/
|
||||
void primeCache (uint8_t maxSimultaneousTransactions,
|
||||
std::function<void (const PropertyExchangeResult&)> onDone,
|
||||
std::byte id);
|
||||
|
||||
/** Adds a message chunk for the provided transaction id. */
|
||||
void addChunk (std::byte b, const Message::DynamicSizePropertyExchange& chunk);
|
||||
|
||||
/** Updates the transaction state based on the contents of the provided notification. */
|
||||
void notify (std::byte b, Span<const std::byte> header);
|
||||
|
||||
/** Returns the number of transactions that have been started but not finished. */
|
||||
int countOngoingTransactions() const;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> pimpl;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An interface for objects that provide resources for property exchange
|
||||
transactions.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class CacheProvider
|
||||
{
|
||||
public:
|
||||
virtual ~CacheProvider() = default;
|
||||
|
||||
/** Returns a set containing all of the MUIDs currently known to the provider. */
|
||||
virtual std::set<MUID> getDiscoveredMuids() const = 0;
|
||||
|
||||
/** Returns a property exchange cache for accumulating replies to transactions
|
||||
we initiated.
|
||||
*/
|
||||
virtual InitiatorPropertyExchangeCache* getCacheForMuidAsInitiator (MUID m) = 0;
|
||||
|
||||
/** Returns a property exchange cache for accumulating requests initiated
|
||||
by other devices.
|
||||
*/
|
||||
virtual ResponderPropertyExchangeCache* getCacheForMuidAsResponder (MUID m) = 0;
|
||||
|
||||
/** Returns the maximum sysex size supported by the device with the
|
||||
given MUID.
|
||||
*/
|
||||
virtual int getMaxSysexSizeForMuid (MUID m) const = 0;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
118
modules/juce_midi_ci/ci/juce_CIPropertyExchangeResult.h
Normal file
118
modules/juce_midi_ci/ci/juce_CIPropertyExchangeResult.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Contains data returned by a responder in response to a request.
|
||||
|
||||
PropertyExchangeResult::kind indicates whether the transaction resulted in
|
||||
a well-formed message; however, it's possible that the message is a
|
||||
well-formed message indicating an error in the responder, so it's important
|
||||
to check the 'status' field of the header before attempting to do anything
|
||||
with the payload.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class PropertyExchangeResult
|
||||
{
|
||||
public:
|
||||
enum class Error
|
||||
{
|
||||
partial, ///< Got a response, but the responder terminated it before
|
||||
///< sending a well-formed message.
|
||||
|
||||
notify, ///< Got a notify message terminating the transaction.
|
||||
tooManyTransactions, ///< Unable to send the request because doing so would
|
||||
///< exceed the number of simultaneous inquiries that were declared.
|
||||
///< @see PropertyDelegate::getNumSimultaneousRequestsSupported().
|
||||
|
||||
invalidPayload, ///< The payload couldn't be encoded for transmission. If you're
|
||||
///< using the ASCII encoding, maybe some bytes have their most
|
||||
///< significant bit set.
|
||||
};
|
||||
|
||||
/** Creates a result denoting an error state. */
|
||||
explicit PropertyExchangeResult (Error errorIn)
|
||||
: PropertyExchangeResult (errorIn, {}, {}) {}
|
||||
|
||||
/** Creates a result denoting a successful transmission. */
|
||||
PropertyExchangeResult (var headerIn, Span<const std::byte> bodyIn)
|
||||
: PropertyExchangeResult (std::nullopt, headerIn, bodyIn) {}
|
||||
|
||||
/** Returns the result kind, either nullopt for a successful transmission, or
|
||||
an error code if something went wrong.
|
||||
*/
|
||||
std::optional<Error> getError() const { return error; }
|
||||
|
||||
/** Parses the header as a subscription header.
|
||||
|
||||
This may only be called for messages of kind 'full'.
|
||||
*/
|
||||
PropertySubscriptionHeader getHeaderAsSubscriptionHeader() const
|
||||
{
|
||||
jassert (header != var());
|
||||
return PropertySubscriptionHeader::parseCondensed (header);
|
||||
}
|
||||
|
||||
/** Parses the header as a request header.
|
||||
|
||||
This may only be called for messages of kind 'full'.
|
||||
*/
|
||||
PropertyRequestHeader getHeaderAsRequestHeader() const
|
||||
{
|
||||
jassert (header != var());
|
||||
return PropertyRequestHeader::parseCondensed (header);
|
||||
}
|
||||
|
||||
/** Parses the header as a reply header.
|
||||
|
||||
This may only be called for messages of kind 'full'.
|
||||
*/
|
||||
PropertyReplyHeader getHeaderAsReplyHeader() const
|
||||
{
|
||||
jassert (header != var());
|
||||
return PropertyReplyHeader::parseCondensed (header);
|
||||
}
|
||||
|
||||
/** When getKind returns 'full', this is the message payload.
|
||||
|
||||
Note that this is not stored internally; if you need to keep this data
|
||||
around and reference it in the future, you should copy it into a
|
||||
vector or some other suitable container.
|
||||
*/
|
||||
Span<const std::byte> getBody() const { return body; }
|
||||
|
||||
private:
|
||||
PropertyExchangeResult (std::optional<Error> errorIn, var headerIn, Span<const std::byte> bodyIn)
|
||||
: error (errorIn), header (headerIn), body (bodyIn) {}
|
||||
|
||||
std::optional<Error> error;
|
||||
var header;
|
||||
Span<const std::byte> body;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
411
modules/juce_midi_ci/ci/juce_CIPropertyHost.cpp
Normal file
411
modules/juce_midi_ci/ci/juce_CIPropertyHost.cpp
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
class PropertyHost::Visitor : public detail::MessageTypeUtils::MessageVisitor
|
||||
{
|
||||
public:
|
||||
Visitor (PropertyHost* h, ResponderOutput* o, bool* b)
|
||||
: host (h), output (o), handled (b) {}
|
||||
|
||||
void visit (const Message::PropertyExchangeCapabilities& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertyGetData& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertySetData& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertySubscribe& body) const override { visitImpl (body); }
|
||||
void visit (const Message::PropertyNotify& body) const override { visitImpl (body); }
|
||||
using MessageVisitor::visit;
|
||||
|
||||
private:
|
||||
template <typename Body>
|
||||
void visitImpl (const Body& body) const { *handled = messageReceived (body); }
|
||||
|
||||
bool messageReceived (const Message::PropertyExchangeCapabilities&) const
|
||||
{
|
||||
detail::MessageTypeUtils::send (*output, Message::PropertyExchangeCapabilitiesResponse { std::byte { host->delegate.getNumSimultaneousRequestsSupported() },
|
||||
{},
|
||||
{} });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool messageReceived (const Message::PropertyGetData& data) const
|
||||
{
|
||||
// This should always be a single message, so no need to accumulate chunks
|
||||
const auto reply = host->delegate.propertyGetDataRequested (output->getIncomingHeader().source,
|
||||
PropertyRequestHeader::parseCondensed (Encodings::jsonFrom7BitText (data.header)));
|
||||
|
||||
const auto encoded = Encodings::tryEncode (reply.body, reply.header.mutualEncoding);
|
||||
|
||||
if (! encoded.has_value())
|
||||
{
|
||||
// If this is hit, the data that was supplied isn't valid for the encoding that was specified
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
detail::PropertyHostUtils::send (*output,
|
||||
output->getIncomingGroup(),
|
||||
detail::MessageMeta::Meta<Message::PropertyGetDataResponse>::subID2,
|
||||
output->getIncomingHeader().source,
|
||||
data.requestID,
|
||||
Encodings::jsonTo7BitText (reply.header.toVarCondensed()),
|
||||
*encoded,
|
||||
host->cacheProvider.getMaxSysexSizeForMuid (output->getIncomingHeader().source));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool messageReceived (const Message::PropertySetData& data) const
|
||||
{
|
||||
auto* caches = host->cacheProvider.getCacheForMuidAsResponder (output->getIncomingHeader().source);
|
||||
|
||||
if (caches == nullptr)
|
||||
return false;
|
||||
|
||||
const auto source = output->getIncomingHeader().source;
|
||||
const auto dest = output->getIncomingHeader().destination;
|
||||
const auto group = output->getIncomingGroup();
|
||||
const auto request = data.requestID;
|
||||
caches->primeCache (host->delegate.getNumSimultaneousRequestsSupported(), [this, source, dest, group, request] (const PropertyExchangeResult& result)
|
||||
{
|
||||
const auto send = [&] (const PropertyReplyHeader& header)
|
||||
{
|
||||
detail::MessageTypeUtils::send (host->output,
|
||||
group,
|
||||
Message::Header { ChannelInGroup::wholeBlock,
|
||||
detail::MessageMeta::Meta<Message::PropertySetDataResponse>::subID2,
|
||||
detail::MessageMeta::implementationVersion,
|
||||
dest,
|
||||
source },
|
||||
Message::PropertySetDataResponse { { request, Encodings::jsonTo7BitText (header.toVarCondensed()) } });
|
||||
};
|
||||
|
||||
if (result.getError() == PropertyExchangeResult::Error::tooManyTransactions)
|
||||
{
|
||||
PropertyReplyHeader header;
|
||||
header.status = 343;
|
||||
header.message = TRANS ("The device has initiated too many simultaneous requests");
|
||||
send (header);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.getError().has_value())
|
||||
{
|
||||
PropertyReplyHeader header;
|
||||
header.status = 400;
|
||||
header.message = TRANS ("Request was incomplete");
|
||||
send (header);
|
||||
return;
|
||||
}
|
||||
|
||||
send (host->delegate.propertySetDataRequested (source, { result.getHeaderAsRequestHeader(), result.getBody() }));
|
||||
}, request);
|
||||
|
||||
caches->addChunk (data.requestID, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool messageReceived (const Message::PropertySubscribe& data) const
|
||||
{
|
||||
auto* caches = host->cacheProvider.getCacheForMuidAsResponder (output->getIncomingHeader().source);
|
||||
|
||||
if (caches == nullptr)
|
||||
return false;
|
||||
|
||||
if (data.header.empty() || data.thisChunkNum != 1 || data.totalNumChunks != 1)
|
||||
return false;
|
||||
|
||||
const auto subHeader = PropertySubscriptionHeader::parseCondensed (Encodings::jsonFrom7BitText (data.header));
|
||||
const auto tryNotifyInitiator = subHeader.command == PropertySubscriptionCommand::start
|
||||
|| subHeader.command == PropertySubscriptionCommand::end;
|
||||
|
||||
if (! tryNotifyInitiator)
|
||||
return false;
|
||||
|
||||
const auto source = output->getIncomingHeader().source;
|
||||
|
||||
const auto sendResponse = [&] (const PropertyReplyHeader& header)
|
||||
{
|
||||
detail::PropertyHostUtils::send (*output,
|
||||
output->getIncomingGroup(),
|
||||
detail::MessageMeta::Meta<Message::PropertySubscribeResponse>::subID2,
|
||||
source,
|
||||
data.requestID,
|
||||
Encodings::jsonTo7BitText (header.toVarCondensed()),
|
||||
{},
|
||||
host->cacheProvider.getMaxSysexSizeForMuid (source));
|
||||
};
|
||||
|
||||
if (subHeader.command == PropertySubscriptionCommand::start)
|
||||
{
|
||||
if (host->delegate.subscriptionStartRequested (source, subHeader))
|
||||
{
|
||||
auto& currentSubscribeIds = host->registry[source];
|
||||
const auto newToken = findUnusedSubscribeId (currentSubscribeIds);
|
||||
[[maybe_unused]] const auto pair = currentSubscribeIds.emplace (newToken, subHeader.resource);
|
||||
jassert (pair.second);
|
||||
const auto subscribeId = subscribeIdFromUid (newToken);
|
||||
host->delegate.subscriptionDidStart (source, subscribeId, subHeader);
|
||||
|
||||
PropertyReplyHeader header;
|
||||
header.extended["subscribeId"] = subscribeId;
|
||||
sendResponse (header);
|
||||
}
|
||||
else
|
||||
{
|
||||
PropertyReplyHeader header;
|
||||
header.status = 405;
|
||||
sendResponse (header);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (subHeader.command == PropertySubscriptionCommand::end)
|
||||
{
|
||||
const auto token = uidFromSubscribeId (subHeader.subscribeId);
|
||||
auto& currentSubscribeIds = host->registry[source];
|
||||
const auto iter = currentSubscribeIds.find (token);
|
||||
|
||||
if (iter != currentSubscribeIds.end())
|
||||
{
|
||||
host->delegate.subscriptionWillEnd (source, { subHeader.subscribeId, iter->second });
|
||||
currentSubscribeIds.erase (iter);
|
||||
|
||||
sendResponse ({});
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool messageReceived (const Message::PropertyNotify& n) const
|
||||
{
|
||||
const auto m = output->getIncomingHeader().source;
|
||||
|
||||
if (auto* it = host->cacheProvider.getCacheForMuidAsResponder (m))
|
||||
it->notify (n.requestID, n.header);
|
||||
|
||||
if (auto* it = host->cacheProvider.getCacheForMuidAsInitiator (m))
|
||||
it->notify (n.requestID, n.header);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PropertyHost* host = nullptr;
|
||||
ResponderOutput* output = nullptr;
|
||||
bool* handled = nullptr;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
std::set<Subscription> PropertyHost::findSubscriptionsForDevice (MUID device) const
|
||||
{
|
||||
const auto iter = registry.find (device);
|
||||
|
||||
if (iter == registry.end())
|
||||
return {};
|
||||
|
||||
std::set<Subscription> result;
|
||||
|
||||
for (const auto& [subId, resource] : iter->second)
|
||||
{
|
||||
[[maybe_unused]] const auto pair = result.insert ({ subscribeIdFromUid (subId), resource });
|
||||
jassert (pair.second);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int PropertyHost::countOngoingTransactions() const
|
||||
{
|
||||
const auto muids = cacheProvider.getDiscoveredMuids();
|
||||
|
||||
return std::accumulate (muids.begin(), muids.end(), 0, [&] (auto acc, const auto& m)
|
||||
{
|
||||
if (auto* cache = cacheProvider.getCacheForMuidAsResponder (m))
|
||||
return acc + cache->countOngoingTransactions();
|
||||
|
||||
return acc;
|
||||
});
|
||||
}
|
||||
|
||||
bool PropertyHost::tryRespond (ResponderOutput& responderOutput, const Message::Parsed& message)
|
||||
{
|
||||
bool result = false;
|
||||
detail::MessageTypeUtils::visit (message, Visitor { this, &responderOutput, &result });
|
||||
return result;
|
||||
}
|
||||
|
||||
ErasedScopeGuard PropertyHost::sendSubscriptionUpdate (MUID device,
|
||||
const PropertySubscriptionHeader& header,
|
||||
Span<const std::byte> body,
|
||||
std::function<void (const PropertyExchangeResult&)> cb)
|
||||
{
|
||||
const auto deviceIter = registry.find (device);
|
||||
|
||||
if (deviceIter == registry.end())
|
||||
{
|
||||
// That device doesn't have any active subscriptions
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto uid = uidFromSubscribeId (header.subscribeId);
|
||||
const auto subIter = deviceIter->second.find (uid);
|
||||
|
||||
if (subIter == deviceIter->second.end())
|
||||
{
|
||||
// That subscribeId isn't currently in use by that device
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto resource = subIter->second;
|
||||
|
||||
if (header.resource != resource)
|
||||
{
|
||||
// That subscribeId corresponds to a different resource
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (header.command == PropertySubscriptionCommand::start)
|
||||
{
|
||||
// This function is intended to update ongoing subscriptions. To start a new subscription,
|
||||
// use CIDevice.
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* caches = cacheProvider.getCacheForMuidAsInitiator (device);
|
||||
|
||||
if (caches == nullptr)
|
||||
return {};
|
||||
|
||||
const auto terminator = detail::PropertyHostUtils::getTerminator (output, functionBlock, device);
|
||||
auto wrappedCallback = [&]() -> std::function<void (const PropertyExchangeResult&)>
|
||||
{
|
||||
if (header.command != PropertySubscriptionCommand::end)
|
||||
return cb;
|
||||
|
||||
return [this, device, uid, resource, cb] (const PropertyExchangeResult& result)
|
||||
{
|
||||
if (! result.getError().has_value())
|
||||
{
|
||||
delegate.subscriptionWillEnd (device, { subscribeIdFromUid (uid), resource });
|
||||
registry[device].erase (uid);
|
||||
}
|
||||
|
||||
NullCheckedInvocation::invoke (cb, result);
|
||||
};
|
||||
}();
|
||||
|
||||
const auto encoded = Encodings::tryEncode (body, header.mutualEncoding);
|
||||
|
||||
if (! encoded.has_value())
|
||||
{
|
||||
NullCheckedInvocation::invoke (wrappedCallback, PropertyExchangeResult { PropertyExchangeResult::Error::invalidPayload });
|
||||
return {};
|
||||
}
|
||||
|
||||
auto primed = caches->primeCache (delegate.getNumSimultaneousRequestsSupported(),
|
||||
std::move (wrappedCallback),
|
||||
std::move (terminator));
|
||||
|
||||
if (! primed.isValid())
|
||||
return {};
|
||||
|
||||
detail::PropertyHostUtils::send (output,
|
||||
functionBlock.firstGroup,
|
||||
detail::MessageMeta::Meta<Message::PropertySubscribe>::subID2,
|
||||
device,
|
||||
primed.id,
|
||||
Encodings::jsonTo7BitText (header.toVarCondensed()),
|
||||
*encoded,
|
||||
cacheProvider.getMaxSysexSizeForMuid (device));
|
||||
|
||||
return std::move (primed.token);
|
||||
}
|
||||
|
||||
void PropertyHost::terminateSubscription (MUID device, const String& subscribeId)
|
||||
{
|
||||
const auto deviceIter = registry.find (device);
|
||||
|
||||
if (deviceIter == registry.end())
|
||||
{
|
||||
// That device doesn't have any active subscriptions
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto uid = uidFromSubscribeId (subscribeId);
|
||||
const auto subIter = deviceIter->second.find (uid);
|
||||
|
||||
if (subIter == deviceIter->second.end())
|
||||
{
|
||||
// That subscribeId isn't currently in use by that device
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
PropertySubscriptionHeader header;
|
||||
header.command = PropertySubscriptionCommand::end;
|
||||
header.subscribeId = subscribeId;
|
||||
header.resource = subIter->second;
|
||||
|
||||
sendSubscriptionUpdate (device, header, {}, nullptr).release();
|
||||
}
|
||||
|
||||
PropertyHost::SubscriptionToken PropertyHost::uidFromSubscribeId (String id)
|
||||
{
|
||||
try
|
||||
{
|
||||
// from_chars would be better once we no longer need to support older macOS
|
||||
return { (size_t) std::stoull (id.toStdString(), {}, 36) };
|
||||
}
|
||||
catch (...) {}
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
String PropertyHost::subscribeIdFromUid (SubscriptionToken uid)
|
||||
{
|
||||
const auto str = std::to_string (uid.uid);
|
||||
jassert (str.size() <= 8);
|
||||
return str;
|
||||
}
|
||||
|
||||
PropertyHost::SubscriptionToken PropertyHost::findUnusedSubscribeId (const std::map<SubscriptionToken, String>& used)
|
||||
{
|
||||
return ! used.empty() ? SubscriptionToken { std::prev (used.end())->first.uid + 1 } : SubscriptionToken { 0 };
|
||||
}
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
125
modules/juce_midi_ci/ci/juce_CIPropertyHost.h
Normal file
125
modules/juce_midi_ci/ci/juce_CIPropertyHost.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Acting as a ResponderListener, instances of this class can formulate
|
||||
appropriate replies to property transactions initiated by remote devices.
|
||||
|
||||
PropertyHost instances also contain methods to inform remote devices about
|
||||
changes to local property state.
|
||||
|
||||
Keeps track of property subscriptions requested by remote devices.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class PropertyHost final : public ResponderDelegate
|
||||
{
|
||||
public:
|
||||
/** @internal
|
||||
|
||||
Rather than constructing one of these objects yourself, you should configure
|
||||
a Device with property exchange support, and then use Device::getPropertyHost()
|
||||
to retrieve a property host that has been set up to work with that device.
|
||||
*/
|
||||
PropertyHost (FunctionBlock fb, PropertyDelegate& d, BufferOutput& o, CacheProvider& p)
|
||||
: functionBlock (fb), delegate (d), output (o), cacheProvider (p) {}
|
||||
|
||||
/** Sends a "Subscription" message from a device, when acting as a
|
||||
subscription responder. You should call this for all registered
|
||||
subscribers whenever the subscribed property is modified in a way that
|
||||
remote devices don't know about (if a remote device requests a
|
||||
property update, there's no need to send a subscription update after
|
||||
changing the property accordingly).
|
||||
|
||||
You should *not* attempt to start a new subscription on another device
|
||||
using this function. Valid subscription commands are "full", "partial",
|
||||
and "notify". Check the property exchange specification for the intended
|
||||
use of these commands.
|
||||
|
||||
To terminate a subscription that was initiated by a remote device,
|
||||
use terminateSubscription().
|
||||
|
||||
The provided callback will be called once the remote device has confirmed
|
||||
receipt of the subscription update. If the state of your application
|
||||
changes such that you no longer need to respond/wait for confirmation,
|
||||
you can allow the returned Guard to fall out of scope, or reset it
|
||||
manually.
|
||||
*/
|
||||
ErasedScopeGuard sendSubscriptionUpdate (MUID device,
|
||||
const PropertySubscriptionHeader& header,
|
||||
Span<const std::byte> body,
|
||||
std::function<void (const PropertyExchangeResult&)> callback);
|
||||
|
||||
/** Terminates a subscription that was started by a remote device.
|
||||
|
||||
This may be useful if your application has properties that can be
|
||||
added and removed - you can terminate subscriptions to subscribed
|
||||
properties before removing those properties.
|
||||
*/
|
||||
void terminateSubscription (MUID device, const String& subscribeId);
|
||||
|
||||
/** Returns a set of subscribed resources.
|
||||
|
||||
This set contains all active subscriptionIDs for the given device,
|
||||
along with the resources to which those subscriptionIDs refer.
|
||||
*/
|
||||
std::set<Subscription> findSubscriptionsForDevice (MUID device) const;
|
||||
|
||||
/** Returns the number of transactions that have been initiated by other devices, but not yet
|
||||
completed, normally because the request has been split into several messages.
|
||||
*/
|
||||
int countOngoingTransactions() const;
|
||||
|
||||
/** @internal */
|
||||
bool tryRespond (ResponderOutput&, const Message::Parsed&) override;
|
||||
|
||||
private:
|
||||
class Visitor;
|
||||
|
||||
struct SubscriptionToken
|
||||
{
|
||||
size_t uid{};
|
||||
|
||||
bool operator< (const SubscriptionToken& other) const { return uid < other.uid; }
|
||||
bool operator== (const SubscriptionToken& other) const { return uid == other.uid; }
|
||||
bool operator!= (const SubscriptionToken& other) const { return uid != other.uid; }
|
||||
};
|
||||
|
||||
static SubscriptionToken uidFromSubscribeId (String id);
|
||||
static String subscribeIdFromUid (SubscriptionToken uid);
|
||||
static SubscriptionToken findUnusedSubscribeId (const std::map<SubscriptionToken, String>& used);
|
||||
|
||||
FunctionBlock functionBlock;
|
||||
PropertyDelegate& delegate;
|
||||
BufferOutput& output;
|
||||
CacheProvider& cacheProvider;
|
||||
|
||||
std::map<MUID, std::map<SubscriptionToken, String>> registry;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
50
modules/juce_midi_ci/ci/juce_CIResponderDelegate.h
Normal file
50
modules/juce_midi_ci/ci/juce_CIResponderDelegate.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
An interface for types that implement responses for certain message types.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class ResponderDelegate
|
||||
{
|
||||
public:
|
||||
ResponderDelegate() = default;
|
||||
virtual ~ResponderDelegate() = default;
|
||||
|
||||
/** If the message is processed successfully, and a response sent, then
|
||||
this returns true. Otherwise, returns false, allowing other ResponderDelegates
|
||||
to attempt to handle the message if necessary.
|
||||
*/
|
||||
virtual bool tryRespond (ResponderOutput& output, const Message::Parsed& message) = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ResponderDelegate)
|
||||
JUCE_DECLARE_NON_MOVEABLE (ResponderDelegate)
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
44
modules/juce_midi_ci/ci/juce_CIResponderOutput.cpp
Normal file
44
modules/juce_midi_ci/ci/juce_CIResponderOutput.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
ChannelAddress ResponderOutput::getChannelAddress() const
|
||||
{
|
||||
return ChannelAddress{}.withGroup (getIncomingGroup())
|
||||
.withChannel (getIncomingHeader().deviceID);
|
||||
}
|
||||
|
||||
Message::Header ResponderOutput::getReplyHeader (std::byte replySubID) const
|
||||
{
|
||||
return { getIncomingHeader().deviceID,
|
||||
replySubID,
|
||||
detail::MessageMeta::implementationVersion,
|
||||
getMuid(),
|
||||
getIncomingHeader().source };
|
||||
}
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
83
modules/juce_midi_ci/ci/juce_CIResponderOutput.h
Normal file
83
modules/juce_midi_ci/ci/juce_CIResponderOutput.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Represents a destination into which MIDI-CI messages can be written.
|
||||
|
||||
Each message should be written into the output buffer. Then, send() will
|
||||
send the current contents of the buffer to the specified group.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class BufferOutput
|
||||
{
|
||||
public:
|
||||
BufferOutput() = default;
|
||||
virtual ~BufferOutput() = default;
|
||||
|
||||
/** Returns the MUID of the responder. */
|
||||
virtual MUID getMuid() const = 0;
|
||||
|
||||
/** Returns the buffer into which replies should be written. */
|
||||
virtual std::vector<std::byte>& getOutputBuffer() = 0;
|
||||
|
||||
/** Sends the current contents of the buffer to the provided group. */
|
||||
virtual void send (uint8_t group) = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (BufferOutput)
|
||||
JUCE_DECLARE_NON_MOVEABLE (BufferOutput)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A buffer output that additionally provides information about an incoming message, so that
|
||||
an appropriate reply can be constructed for that message.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class ResponderOutput : public BufferOutput
|
||||
{
|
||||
public:
|
||||
/** Returns the header of the message that was received. */
|
||||
virtual Message::Header getIncomingHeader() const = 0;
|
||||
|
||||
/** Returns the group of the message that was received. */
|
||||
virtual uint8_t getIncomingGroup() const = 0;
|
||||
|
||||
/** Returns the channel to which the incoming message was addressed. */
|
||||
ChannelAddress getChannelAddress() const;
|
||||
|
||||
/** Returns a default header that can be used for outgoing replies.
|
||||
|
||||
This always sets the destination MUID equal to the source MUID of the incoming header,
|
||||
so it's not suitable for broadcast messages.
|
||||
*/
|
||||
Message::Header getReplyHeader (std::byte replySubID) const;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
54
modules/juce_midi_ci/ci/juce_CISubscription.h
Normal file
54
modules/juce_midi_ci/ci/juce_CISubscription.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Matches a subscription ID to a resource name.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct Subscription
|
||||
{
|
||||
String subscribeId;
|
||||
String resource;
|
||||
|
||||
bool operator< (const Subscription& other) const { return subscribeId < other.subscribeId; }
|
||||
bool operator<= (const Subscription& other) const { return subscribeId <= other.subscribeId; }
|
||||
bool operator> (const Subscription& other) const { return subscribeId > other.subscribeId; }
|
||||
bool operator>= (const Subscription& other) const { return subscribeId >= other.subscribeId; }
|
||||
|
||||
bool operator== (const Subscription& other) const
|
||||
{
|
||||
const auto tie = [] (const auto& x) { return std::tie (x.subscribeId, x.resource); };
|
||||
return tie (*this) == tie (other);
|
||||
}
|
||||
|
||||
bool operator!= (const Subscription& other) const { return ! operator== (other); }
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
55
modules/juce_midi_ci/ci/juce_CISupportedAndActive.h
Normal file
55
modules/juce_midi_ci/ci/juce_CISupportedAndActive.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci
|
||||
{
|
||||
|
||||
/**
|
||||
Holds the maximum number of channels that may be activated for a MIDI-CI
|
||||
profile, along with the number of channels that are currently active.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct SupportedAndActive
|
||||
{
|
||||
uint16_t supported{}; ///< The maximum number of member channels for a profile. 0 indicates that the profile is unsupported.
|
||||
uint16_t active{}; ///< The number of member channels currently active for a profile. 0 indicates that the profile is inactive.
|
||||
|
||||
/** Returns true if supported is non-zero. */
|
||||
bool isSupported() const { return supported != 0; }
|
||||
|
||||
/** Returns true if active is non-zero. */
|
||||
bool isActive() const { return active != 0; }
|
||||
|
||||
bool operator== (const SupportedAndActive& other) const
|
||||
{
|
||||
const auto tie = [] (auto& x) { return std::tie (x.supported, x.active); };
|
||||
return tie (*this) == tie (other);
|
||||
}
|
||||
|
||||
bool operator!= (const SupportedAndActive& other) const { return ! operator== (other); }
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
376
modules/juce_midi_ci/detail/juce_CIMarshalling.h
Normal file
376
modules/juce_midi_ci/detail/juce_CIMarshalling.h
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
Utilities for converting sequences of bytes to and from
|
||||
C++ struct types.
|
||||
*/
|
||||
namespace juce::midi_ci::detail::Marshalling
|
||||
{
|
||||
|
||||
template <uint8_t> struct IntForNumBytes;
|
||||
template <> struct IntForNumBytes<1> { using Type = uint8_t; };
|
||||
template <> struct IntForNumBytes<2> { using Type = uint16_t; };
|
||||
template <> struct IntForNumBytes<4> { using Type = uint32_t; };
|
||||
|
||||
template <uint8_t NumBytes> using IntForNumBytesT = typename IntForNumBytes<NumBytes>::Type;
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
Reads a sequence of bytes representing a MIDI-CI message, and populates
|
||||
structs with the information contained in the message.
|
||||
*/
|
||||
class Reader
|
||||
{
|
||||
public:
|
||||
/* Constructs a reader that will parse the provided buffer, using the most
|
||||
recent known MIDI-CI version.
|
||||
*/
|
||||
explicit Reader (Span<const std::byte> b)
|
||||
: Reader (b, static_cast<uint8_t> (MessageMeta::implementationVersion)) {}
|
||||
|
||||
/* Constructs a reader for the provided MIDI-CI version that will parse
|
||||
the provided buffer. Fields introduced in later versions will be ignored,
|
||||
and so left with their default values.
|
||||
*/
|
||||
Reader (Span<const std::byte> b, int v)
|
||||
: bytes (b), version (v) {}
|
||||
|
||||
std::optional<int> getVersion() const { return version; }
|
||||
|
||||
/* Attempts to interpret the byte sequence passed to the constructor
|
||||
as a sequence of structs 'T'.
|
||||
|
||||
Returns true if parsing succeeds, otherwise returns false.
|
||||
*/
|
||||
template <typename... T>
|
||||
bool operator() (T&&... t)
|
||||
{
|
||||
return (doArchiveChecked (std::forward<T> (t)) && ...);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
bool doArchiveChecked (T&& t)
|
||||
{
|
||||
if (failed)
|
||||
return false;
|
||||
|
||||
doArchive (t);
|
||||
return ! failed;
|
||||
}
|
||||
|
||||
void doArchive (ChannelInGroup& x)
|
||||
{
|
||||
if (const auto popped = popBytes (1))
|
||||
{
|
||||
const auto p = *popped;
|
||||
x = ChannelInGroup (p[0] & std::byte { 0x7f });
|
||||
return;
|
||||
}
|
||||
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// If we're trying to parse into a constant, then we should check that the next byte(s)
|
||||
// match that constant.
|
||||
void doArchive (const std::byte& x)
|
||||
{
|
||||
std::byte temp{};
|
||||
|
||||
if (! doArchiveChecked (temp))
|
||||
return;
|
||||
|
||||
failed |= x != temp;
|
||||
}
|
||||
|
||||
void doArchive (std::byte& x)
|
||||
{
|
||||
if (const auto popped = popBytes (1))
|
||||
{
|
||||
const auto p = *popped;
|
||||
x = p[0] & std::byte { 0x7f };
|
||||
return;
|
||||
}
|
||||
|
||||
failed = true;
|
||||
}
|
||||
|
||||
void doArchive (const uint16_t& x)
|
||||
{
|
||||
uint16_t temp{};
|
||||
|
||||
if (! doArchiveChecked (temp))
|
||||
return;
|
||||
|
||||
failed |= temp != x;
|
||||
}
|
||||
|
||||
void doArchive (uint16_t& x)
|
||||
{
|
||||
if (const auto popped = popBytes (2))
|
||||
{
|
||||
const auto p = *popped;
|
||||
x = (uint16_t) (((uint16_t) p[0] & 0x7f) << 0x00)
|
||||
| (uint16_t) (((uint16_t) p[1] & 0x7f) << 0x07);
|
||||
return;
|
||||
}
|
||||
|
||||
failed = true;
|
||||
}
|
||||
|
||||
void doArchive (const uint32_t& x)
|
||||
{
|
||||
uint32_t temp{};
|
||||
|
||||
if (! doArchiveChecked (temp))
|
||||
return;
|
||||
|
||||
failed |= temp != x;
|
||||
}
|
||||
|
||||
void doArchive (uint32_t& x)
|
||||
{
|
||||
if (const auto popped = popBytes (4))
|
||||
{
|
||||
const auto p = *popped;
|
||||
x = (((uint32_t) p[0] & 0x7f) << 0x00)
|
||||
| (((uint32_t) p[1] & 0x7f) << 0x07)
|
||||
| (((uint32_t) p[2] & 0x7f) << 0x0e)
|
||||
| (((uint32_t) p[3] & 0x7f) << 0x15);
|
||||
return;
|
||||
}
|
||||
|
||||
failed = true;
|
||||
}
|
||||
|
||||
template <uint8_t NumBytes, bool B>
|
||||
void doArchive (MessageMeta::SpanWithSizeBytes<NumBytes, Span<const std::byte>, B> x)
|
||||
{
|
||||
IntForNumBytesT<NumBytes> numBytes{};
|
||||
|
||||
// Read the number of bytes in the field
|
||||
if (! doArchiveChecked (numBytes))
|
||||
return;
|
||||
|
||||
// Attempt to pop that many bytes
|
||||
if (const auto popped = popBytes (numBytes))
|
||||
{
|
||||
x.span = *popped;
|
||||
return;
|
||||
}
|
||||
|
||||
failed = true;
|
||||
}
|
||||
|
||||
template <uint8_t NumBytes, size_t N>
|
||||
void doArchive (MessageMeta::SpanWithSizeBytes<NumBytes, Span<const std::array<std::byte, N>>> x)
|
||||
{
|
||||
IntForNumBytesT<NumBytes> numItems{};
|
||||
|
||||
// Read the number of items in the field
|
||||
if (! doArchiveChecked (numItems))
|
||||
return;
|
||||
|
||||
if (const auto popped = popBytes (numItems * N))
|
||||
{
|
||||
x.span = Span (unalignedPointerCast<const std::array<std::byte, N>*> (popped->data()), numItems);
|
||||
return;
|
||||
}
|
||||
|
||||
failed = true;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void doArchive (Span<const std::byte, N>& x)
|
||||
{
|
||||
if (const auto popped = popBytes (bytes.size()))
|
||||
{
|
||||
x = *popped;
|
||||
return;
|
||||
}
|
||||
|
||||
failed = true;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void doArchive (std::array<std::byte, N>& x)
|
||||
{
|
||||
if (const auto popped = popBytes (x.size()))
|
||||
{
|
||||
const auto p = *popped;
|
||||
std::transform (p.begin(), p.end(), x.begin(), [] (std::byte b)
|
||||
{
|
||||
return b & std::byte { 0x7f };
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
failed = true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void doArchive (T& t)
|
||||
{
|
||||
juce::detail::doLoad (*this, t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void doArchive (Named<T> named)
|
||||
{
|
||||
doArchiveChecked (named.value);
|
||||
}
|
||||
|
||||
std::optional<Span<const std::byte>> popBytes (size_t num)
|
||||
{
|
||||
if (bytes.size() < num)
|
||||
return {};
|
||||
|
||||
const Span result { bytes.data(), num };
|
||||
bytes = Span { bytes.data() + num, bytes.size() - num };
|
||||
return result;
|
||||
}
|
||||
|
||||
Span<const std::byte> bytes; /* Bytes making up a CI message. */
|
||||
int version{}; /* The version to assume when parsing the message, specified in the message header. */
|
||||
bool failed = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
Converts one or more structs into a byte sequence suitable for transmission
|
||||
as a MIDI-CI message.
|
||||
*/
|
||||
class Writer
|
||||
{
|
||||
public:
|
||||
/* Constructs a writer that will write into the provided buffer. */
|
||||
explicit Writer (std::vector<std::byte>& b)
|
||||
: Writer (b, static_cast<uint8_t> (MessageMeta::implementationVersion)) {}
|
||||
|
||||
/* Constructs a writer that will write a MIDI-CI message of the requested
|
||||
version to the provided buffer.
|
||||
|
||||
Fields introduced in later MIDI-CI versions will be ignored.
|
||||
*/
|
||||
Writer (std::vector<std::byte>& b, int v)
|
||||
: bytes (b), version (v) {}
|
||||
|
||||
std::optional<int> getVersion() const { return version; }
|
||||
|
||||
/* Formats the information contained in the provided structs into a
|
||||
MIDI-CI message, and returns a bool indicating success or failure.
|
||||
*/
|
||||
template <typename... T>
|
||||
bool operator() (const T&... t)
|
||||
{
|
||||
return (doArchiveChecked (t) && ...);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
bool doArchiveChecked (T&& t)
|
||||
{
|
||||
if (failed)
|
||||
return false;
|
||||
|
||||
doArchive (t);
|
||||
return ! failed;
|
||||
}
|
||||
|
||||
void doArchive (ChannelInGroup x)
|
||||
{
|
||||
doArchiveChecked (std::byte (x));
|
||||
}
|
||||
|
||||
void doArchive (std::byte x)
|
||||
{
|
||||
bytes.push_back (x);
|
||||
}
|
||||
|
||||
void doArchive (uint16_t x)
|
||||
{
|
||||
bytes.insert (bytes.end(), { (std::byte) ((x >> 0x00) & 0x7f),
|
||||
(std::byte) ((x >> 0x07) & 0x7f) });
|
||||
}
|
||||
|
||||
void doArchive (uint32_t x)
|
||||
{
|
||||
bytes.insert (bytes.end(), { (std::byte) ((x >> 0x00) & 0x7f),
|
||||
(std::byte) ((x >> 0x07) & 0x7f),
|
||||
(std::byte) ((x >> 0x0e) & 0x7f),
|
||||
(std::byte) ((x >> 0x15) & 0x7f) });
|
||||
}
|
||||
|
||||
template <uint8_t NumBytes, typename T, bool B>
|
||||
void doArchive (MessageMeta::SpanWithSizeBytes<NumBytes, T, B> x)
|
||||
{
|
||||
if (x.span.size() >= (1 << (7 * NumBytes)))
|
||||
{
|
||||
// Unable to express the size of the field in the requested number of bytes
|
||||
jassertfalse;
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Write the number of bytes, followed by the bytes themselves.
|
||||
const auto numBytes = (IntForNumBytesT<NumBytes>) x.span.size();
|
||||
doArchiveChecked (numBytes);
|
||||
doArchiveChecked (x.span);
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
void doArchive (Span<const T, N> x)
|
||||
{
|
||||
failed = ! std::all_of (x.begin(), x.end(), [&] (const auto& item)
|
||||
{
|
||||
return doArchiveChecked (item);
|
||||
});
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void doArchive (const std::array<std::byte, N>& x)
|
||||
{
|
||||
bytes.insert (bytes.end(), x.begin(), x.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void doArchive (const T& t)
|
||||
{
|
||||
juce::detail::doSave (*this, t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void doArchive (Named<T> named)
|
||||
{
|
||||
doArchiveChecked (named.value);
|
||||
}
|
||||
|
||||
std::vector<std::byte>& bytes; /* The buffer that will hold the completed message. */
|
||||
int version{}; /* The version to assume when writing the message, specified in the message header. */
|
||||
bool failed = false;
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci::detail::Marshalling
|
||||
624
modules/juce_midi_ci/detail/juce_CIMessageMeta.h
Normal file
624
modules/juce_midi_ci/detail/juce_CIMessageMeta.h
Normal file
|
|
@ -0,0 +1,624 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
Namespace containing metadata about MIDI-CI message types, such as
|
||||
replies corresponding to inquiries, and serialization functions.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
namespace juce::midi_ci::detail::MessageMeta
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/* The maximum CI version that can be parsed and generated by this implementation. */
|
||||
static constexpr std::byte implementationVersion { 0x02 };
|
||||
|
||||
/* @internal
|
||||
|
||||
Wraps a pointer to a Span. Used to indicate to CI readers/writers that a particular field is
|
||||
of variable length, starting with a 16-bit or 32-bit byte count.
|
||||
*/
|
||||
template <uint8_t NumBytes, typename T, bool isJson = false>
|
||||
struct SpanWithSizeBytes
|
||||
{
|
||||
T& span;
|
||||
};
|
||||
|
||||
/* @internal
|
||||
|
||||
Creates a SpanWithSizeBytes with an appropriate template argument.
|
||||
*/
|
||||
template <uint8_t NumBytes, typename T>
|
||||
static constexpr auto makeSpanWithSizeBytes ( Span<T>& span) { return SpanWithSizeBytes<NumBytes, Span<T>> { span }; }
|
||||
|
||||
template <uint8_t NumBytes, typename T>
|
||||
static constexpr auto makeSpanWithSizeBytes (const Span<T>& span) { return SpanWithSizeBytes<NumBytes, const Span<T>> { span }; }
|
||||
|
||||
template <uint8_t NumBytes, typename T>
|
||||
static constexpr auto makeJsonWithSizeBytes ( Span<T>& span) { return SpanWithSizeBytes<NumBytes, Span<T>, true> { span }; }
|
||||
|
||||
template <uint8_t NumBytes, typename T>
|
||||
static constexpr auto makeJsonWithSizeBytes (const Span<T>& span) { return SpanWithSizeBytes<NumBytes, const Span<T>, true> { span }; }
|
||||
|
||||
template <uint8_t SubID2, typename R = void>
|
||||
struct Metadata
|
||||
{
|
||||
static constexpr std::byte subID2 { SubID2 };
|
||||
using Reply = R;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Meta;
|
||||
|
||||
template <>
|
||||
struct Meta<Message::DiscoveryResponse> : Metadata<0x71> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::Discovery> : Metadata<0x70, Message::DiscoveryResponse> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::EndpointInquiryResponse> : Metadata<0x73> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::EndpointInquiry> : Metadata<0x72, Message::EndpointInquiryResponse> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::InvalidateMUID> : Metadata<0x7e> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ACK> : Metadata<0x7d> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::NAK> : Metadata<0x7f> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileInquiryResponse> : Metadata<0x21> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileInquiry> : Metadata<0x20, Message::ProfileInquiryResponse> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileAdded> : Metadata<0x26> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileRemoved> : Metadata<0x27> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileDetailsResponse> : Metadata<0x29> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileDetails> : Metadata<0x28, Message::ProfileDetailsResponse> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileOn> : Metadata<0x22> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileOff> : Metadata<0x23> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileEnabledReport> : Metadata<0x24> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileDisabledReport> : Metadata<0x25> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProfileSpecificData> : Metadata<0x2f> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::PropertyExchangeCapabilitiesResponse> : Metadata<0x31> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::PropertyExchangeCapabilities> : Metadata<0x30, Message::PropertyExchangeCapabilitiesResponse> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::StaticSizePropertyExchange> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::DynamicSizePropertyExchange> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::PropertyGetDataResponse> : Meta<Message::DynamicSizePropertyExchange>, Metadata<0x35> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::PropertyGetData> : Meta<Message::StaticSizePropertyExchange>, Metadata<0x34, Message::PropertyGetDataResponse> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::PropertySetDataResponse> : Meta<Message::StaticSizePropertyExchange>, Metadata<0x37> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::PropertySetData> : Meta<Message::DynamicSizePropertyExchange>, Metadata<0x36, Message::PropertySetDataResponse> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::PropertySubscribeResponse> : Meta<Message::DynamicSizePropertyExchange>, Metadata<0x39> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::PropertySubscribe> : Meta<Message::DynamicSizePropertyExchange>, Metadata<0x38, Message::PropertySubscribeResponse> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::PropertyNotify> : Meta<Message::DynamicSizePropertyExchange>, Metadata<0x3f> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProcessInquiryResponse> : Metadata<0x41> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProcessInquiry> : Metadata<0x40, Message::ProcessInquiryResponse> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProcessMidiMessageReportResponse> : Metadata<0x43> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProcessMidiMessageReport> : Metadata<0x42, Message::ProcessMidiMessageReportResponse> {};
|
||||
|
||||
template <>
|
||||
struct Meta<Message::ProcessEndMidiMessageReport> : Metadata<0x44> {};
|
||||
|
||||
} // namespace juce::midi_ci::detail::MessageMeta
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct VersionBase
|
||||
{
|
||||
static constexpr auto marshallingVersion = (int) juce::midi_ci::detail::MessageMeta::implementationVersion;
|
||||
};
|
||||
|
||||
template <uint8_t NumBytes, typename T, bool isJson>
|
||||
struct SerialisationTraits<midi_ci::detail::MessageMeta::SpanWithSizeBytes<NumBytes, T, isJson>>
|
||||
{
|
||||
static constexpr auto marshallingVersion = std::nullopt;
|
||||
|
||||
template <typename This>
|
||||
static auto getSize (This& t)
|
||||
{
|
||||
if constexpr (NumBytes == 1)
|
||||
return (uint8_t) t.size();
|
||||
else if constexpr (NumBytes == 2)
|
||||
return (uint16_t) t.size();
|
||||
else if constexpr (NumBytes == 4)
|
||||
return (uint32_t) t.size();
|
||||
else if constexpr (NumBytes == 8)
|
||||
return (uint64_t) t.size();
|
||||
else
|
||||
static_assert (detail::delayStaticAssert<T>, "NumBytes is not a power of two");
|
||||
}
|
||||
|
||||
template <typename Archive, typename This>
|
||||
static auto load (Archive&, This&)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Archive, typename This>
|
||||
static auto save (Archive& archive, const This& t)
|
||||
{
|
||||
auto size = getSize (t.span);
|
||||
archive (serialisationSize (size));
|
||||
|
||||
for (const auto& element : t.span)
|
||||
archive (element);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::MUID> : VersionBase
|
||||
{
|
||||
template <typename Archive>
|
||||
static auto load (Archive& archive, ci::MUID& t)
|
||||
{
|
||||
uint32_t muid{};
|
||||
auto result = archive (muid);
|
||||
t = ci::MUID::makeUnchecked (muid);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
static auto save (Archive& archive, const ci::MUID& t)
|
||||
{
|
||||
return archive (t.get());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::Header> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
const std::byte universalSystemExclusive { 0x7e }, subID { 0x0d };
|
||||
return archive (universalSystemExclusive,
|
||||
t.deviceID,
|
||||
subID,
|
||||
t.category,
|
||||
t.version,
|
||||
t.source,
|
||||
t.destination);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::Generic> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
return archive (t.header, t.data);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::DiscoveryResponse> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("device", t.device),
|
||||
named ("capabilities", t.capabilities),
|
||||
named ("maximumSysexSize", t.maximumSysexSize));
|
||||
|
||||
if (0x02 <= archive.getVersion())
|
||||
archive (named ("outputPathID", t.outputPathID), named ("functionBlock", t.functionBlock));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::Discovery> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("device", t.device),
|
||||
named ("capabilities", t.capabilities),
|
||||
named ("maximumSysexSize", t.maximumSysexSize));
|
||||
|
||||
if (0x02 <= archive.getVersion())
|
||||
archive (named ("outputPathID", t.outputPathID));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::EndpointInquiryResponse> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("status", t.status),
|
||||
named ("data", midi_ci::detail::MessageMeta::makeSpanWithSizeBytes<2> (t.data)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::EndpointInquiry> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("status", t.status));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::InvalidateMUID> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("target", t.target));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ACK> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("originalCategory", t.originalCategory),
|
||||
named ("statusCode", t.statusCode),
|
||||
named ("statusData", t.statusData),
|
||||
named ("details", t.details),
|
||||
named ("messageText", midi_ci::detail::MessageMeta::makeSpanWithSizeBytes<2> (t.messageText)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::NAK> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
if (0x02 <= archive.getVersion())
|
||||
{
|
||||
archive (named ("originalCategory", t.originalCategory),
|
||||
named ("statusCode", t.statusCode),
|
||||
named ("statusData", t.statusData),
|
||||
named ("details", t.details),
|
||||
named ("messageText", midi_ci::detail::MessageMeta::makeSpanWithSizeBytes<2> (t.messageText)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileInquiryResponse> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("enabledProfiles", midi_ci::detail::MessageMeta::makeSpanWithSizeBytes<2> (t.enabledProfiles)),
|
||||
named ("disabledProfiles", midi_ci::detail::MessageMeta::makeSpanWithSizeBytes<2> (t.disabledProfiles)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileInquiry> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive&, This&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileAdded> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("profile", t.profile));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileRemoved> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("profile", t.profile));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileDetailsResponse> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("profile", t.profile),
|
||||
named ("target", t.target),
|
||||
named ("data", midi_ci::detail::MessageMeta::makeSpanWithSizeBytes<2> (t.data)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileDetails> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("profile", t.profile),
|
||||
named ("target", t.target));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileOn> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("profile", t.profile));
|
||||
|
||||
if (0x02 <= archive.getVersion())
|
||||
archive (named ("numChannels", t.numChannels));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileOff> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("profile", t.profile));
|
||||
|
||||
if (0x02 <= archive.getVersion())
|
||||
{
|
||||
uint16_t reserved{};
|
||||
archive (named ("reserved", reserved));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileEnabledReport> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("profile", t.profile));
|
||||
|
||||
if (0x02 <= archive.getVersion())
|
||||
archive (named ("numChannels", t.numChannels));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileDisabledReport> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("profile", t.profile));
|
||||
|
||||
if (0x02 <= archive.getVersion())
|
||||
archive (named ("numChannels", t.numChannels));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProfileSpecificData> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("profile", t.profile), named ("data", midi_ci::detail::MessageMeta::makeSpanWithSizeBytes<4> (t.data)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::PropertyExchangeCapabilitiesResponse> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("numRequests", t.numSimultaneousRequestsSupported));
|
||||
|
||||
if (0x02 <= archive.getVersion())
|
||||
archive (named ("major", t.majorVersion), named ("minor", t.minorVersion));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::PropertyExchangeCapabilities> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("numRequests", t.numSimultaneousRequestsSupported));
|
||||
|
||||
if (0x02 <= archive.getVersion())
|
||||
archive (named ("major", t.majorVersion), named ("minor", t.minorVersion));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::StaticSizePropertyExchange> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
const uint16_t chunkNum = 1, dataLength = 0;
|
||||
archive (named ("requestID", t.requestID),
|
||||
named ("header", midi_ci::detail::MessageMeta::makeJsonWithSizeBytes<2> (t.header)),
|
||||
named ("numChunks", chunkNum),
|
||||
named ("thisChunk", chunkNum),
|
||||
named ("length", dataLength));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::DynamicSizePropertyExchange> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (named ("requestID", t.requestID),
|
||||
named ("header", midi_ci::detail::MessageMeta::makeJsonWithSizeBytes<2> (t.header)),
|
||||
named ("numChunks", t.totalNumChunks),
|
||||
named ("thisChunk", t.thisChunkNum),
|
||||
named ("data", midi_ci::detail::MessageMeta::makeSpanWithSizeBytes<2> (t.data)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::PropertyGetDataResponse> : SerialisationTraits<ci::Message::DynamicSizePropertyExchange> {};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::PropertyGetData> : SerialisationTraits<ci::Message::StaticSizePropertyExchange> {};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::PropertySetDataResponse> : SerialisationTraits<ci::Message::StaticSizePropertyExchange> {};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::PropertySetData> : SerialisationTraits<ci::Message::DynamicSizePropertyExchange> {};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::PropertySubscribeResponse> : SerialisationTraits<ci::Message::DynamicSizePropertyExchange> {};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::PropertySubscribe> : SerialisationTraits<ci::Message::DynamicSizePropertyExchange> {};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::PropertyNotify> : SerialisationTraits<ci::Message::DynamicSizePropertyExchange> {};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProcessInquiryResponse> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
archive (t.supportedFeatures);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProcessInquiry> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive&, This&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProcessMidiMessageReportResponse> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
std::byte reserved{};
|
||||
archive (named ("messageDataControl", t.messageDataControl),
|
||||
named ("requestedMessages", t.requestedMessages),
|
||||
named ("reserved", reserved),
|
||||
named ("channelControllerMessages", t.channelControllerMessages),
|
||||
named ("noteDataMessages", t.noteDataMessages));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProcessMidiMessageReport> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive& archive, This& t)
|
||||
{
|
||||
std::byte reserved{};
|
||||
archive (named ("messageDataControl", t.messageDataControl),
|
||||
named ("requestedMessages", t.requestedMessages),
|
||||
named ("reserved", reserved),
|
||||
named ("channelControllerMessages", t.channelControllerMessages),
|
||||
named ("noteDataMessages", t.noteDataMessages));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SerialisationTraits<ci::Message::ProcessEndMidiMessageReport> : VersionBase
|
||||
{
|
||||
template <typename Archive, typename This>
|
||||
static auto serialise (Archive&, This&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
245
modules/juce_midi_ci/detail/juce_CIMessageTypeUtils.h
Normal file
245
modules/juce_midi_ci/detail/juce_CIMessageTypeUtils.h
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci::detail::MessageTypeUtils
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/*
|
||||
An interface used for types that want to operate on parsed MIDI-CI messages.
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct MessageVisitor
|
||||
{
|
||||
MessageVisitor() = default;
|
||||
MessageVisitor (const MessageVisitor&) = default;
|
||||
MessageVisitor (MessageVisitor&&) = default;
|
||||
MessageVisitor& operator= (const MessageVisitor&) = default;
|
||||
MessageVisitor& operator= (MessageVisitor&&) = default;
|
||||
virtual ~MessageVisitor() = default;
|
||||
|
||||
virtual void visit (const std::monostate&) const {}
|
||||
virtual void visit (const Message::Discovery&) const {}
|
||||
virtual void visit (const Message::EndpointInquiry&) const {}
|
||||
virtual void visit (const Message::ProfileInquiry&) const {}
|
||||
virtual void visit (const Message::ProfileDetails&) const {}
|
||||
virtual void visit (const Message::PropertyExchangeCapabilities&) const {}
|
||||
virtual void visit (const Message::PropertyGetData&) const {}
|
||||
virtual void visit (const Message::PropertySetData&) const {}
|
||||
virtual void visit (const Message::PropertySubscribe&) const {}
|
||||
virtual void visit (const Message::ProcessInquiry&) const {}
|
||||
virtual void visit (const Message::ProcessMidiMessageReport&) const {}
|
||||
virtual void visit (const Message::DiscoveryResponse&) const {}
|
||||
virtual void visit (const Message::EndpointInquiryResponse&) const {}
|
||||
virtual void visit (const Message::InvalidateMUID&) const {}
|
||||
virtual void visit (const Message::ACK&) const {}
|
||||
virtual void visit (const Message::NAK&) const {}
|
||||
virtual void visit (const Message::ProfileInquiryResponse&) const {}
|
||||
virtual void visit (const Message::ProfileAdded&) const {}
|
||||
virtual void visit (const Message::ProfileRemoved&) const {}
|
||||
virtual void visit (const Message::ProfileDetailsResponse&) const {}
|
||||
virtual void visit (const Message::ProfileOn&) const {}
|
||||
virtual void visit (const Message::ProfileOff&) const {}
|
||||
virtual void visit (const Message::ProfileEnabledReport&) const {}
|
||||
virtual void visit (const Message::ProfileDisabledReport&) const {}
|
||||
virtual void visit (const Message::ProfileSpecificData&) const {}
|
||||
virtual void visit (const Message::PropertyExchangeCapabilitiesResponse&) const {}
|
||||
virtual void visit (const Message::PropertyGetDataResponse&) const {}
|
||||
virtual void visit (const Message::PropertySetDataResponse&) const {}
|
||||
virtual void visit (const Message::PropertySubscribeResponse&) const {}
|
||||
virtual void visit (const Message::PropertyNotify&) const {}
|
||||
virtual void visit (const Message::ProcessInquiryResponse&) const {}
|
||||
virtual void visit (const Message::ProcessMidiMessageReportResponse&) const {}
|
||||
virtual void visit (const Message::ProcessEndMidiMessageReport&) const {}
|
||||
};
|
||||
|
||||
using ParseFn = Message::Parsed::Body (*) (Message::Generic, Parser::Status* status);
|
||||
using VisitFn = void (*) (const Message::Parsed&, const MessageVisitor&);
|
||||
|
||||
/* These return the Universal System Exclusive Sub-ID#2 for a particular message type. */
|
||||
template <typename Specific>
|
||||
static constexpr auto getParserFor (std::in_place_type_t<Specific>)
|
||||
{
|
||||
return [] (Message::Generic message, Parser::Status* status) -> Message::Parsed::Body
|
||||
{
|
||||
// Parse messages using the version specified in the header of the message
|
||||
if (Specific parsed; Marshalling::Reader { message.data, static_cast<uint8_t> (message.header.version) } (parsed))
|
||||
{
|
||||
return parsed;
|
||||
}
|
||||
|
||||
if (status != nullptr)
|
||||
*status = Parser::Status::malformed;
|
||||
|
||||
return std::monostate{};
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Specific>
|
||||
static constexpr auto getVisitorFor (std::in_place_type_t<Specific>)
|
||||
{
|
||||
return [] (const Message::Parsed& parsed, const MessageVisitor& visitor)
|
||||
{
|
||||
if (auto* body = std::get_if<Specific> (&parsed.body))
|
||||
visitor.visit (*body);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
struct LookupTables
|
||||
{
|
||||
constexpr LookupTables()
|
||||
{
|
||||
for (auto& x : parsers)
|
||||
{
|
||||
x = [] (Message::Generic, Parser::Status* status) -> Message::Parsed::Body
|
||||
{
|
||||
if (status != nullptr)
|
||||
*status = Parser::Status::unrecognisedMessage;
|
||||
|
||||
return std::monostate{};
|
||||
};
|
||||
}
|
||||
|
||||
for (auto& x : visitors)
|
||||
{
|
||||
x = [] (const Message::Parsed&, const MessageVisitor& visitor)
|
||||
{
|
||||
visitor.visit (std::monostate{});
|
||||
};
|
||||
}
|
||||
|
||||
(registerTag (std::in_place_type<Ts>), ...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr void registerTag (std::in_place_type_t<T> tag)
|
||||
{
|
||||
constexpr auto category = MessageMeta::Meta<T>::subID2;
|
||||
parsers[uint8_t (category)] = getParserFor (tag);
|
||||
visitors[uint8_t (category)] = getVisitorFor (tag);
|
||||
}
|
||||
|
||||
ParseFn parsers[std::numeric_limits<uint8_t>::max()]{};
|
||||
VisitFn visitors[std::numeric_limits<uint8_t>::max()]{};
|
||||
};
|
||||
|
||||
template <typename Body>
|
||||
static void send (BufferOutput& output, uint8_t group, const Message::Header& header, const Body& body)
|
||||
{
|
||||
output.getOutputBuffer().clear();
|
||||
Marshalling::Writer { output.getOutputBuffer() } (header, body);
|
||||
output.send (group);
|
||||
}
|
||||
|
||||
template <typename Body>
|
||||
static void send (BufferOutput& output, uint8_t group, MUID targetMuid, ChannelInGroup cig, const Body& body)
|
||||
{
|
||||
Message::Header header
|
||||
{
|
||||
cig,
|
||||
MessageMeta::Meta<Body>::subID2,
|
||||
MessageMeta::implementationVersion,
|
||||
output.getMuid(),
|
||||
targetMuid,
|
||||
};
|
||||
|
||||
send (output, group, header, body);
|
||||
}
|
||||
|
||||
|
||||
template <typename Body>
|
||||
static void send (ResponderOutput& output, const Body& body)
|
||||
{
|
||||
send (output, output.getIncomingGroup(), output.getReplyHeader (MessageMeta::Meta<Body>::subID2), body);
|
||||
}
|
||||
|
||||
static void sendNAK (ResponderOutput& output, std::byte statusCode)
|
||||
{
|
||||
const auto header = output.getReplyHeader (MessageMeta::Meta<Message::NAK>::subID2);
|
||||
const Message::NAK body { output.getIncomingHeader().category,
|
||||
statusCode,
|
||||
std::byte { 0x00 },
|
||||
{}, // No additional details
|
||||
{} }; // No message text
|
||||
send (output, output.getIncomingGroup(), header, body);
|
||||
}
|
||||
|
||||
class BaseCaseDelegate : public ResponderDelegate
|
||||
{
|
||||
public:
|
||||
bool tryRespond (ResponderOutput& output, const Message::Parsed&) override
|
||||
{
|
||||
sendNAK (output, {});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr auto getTables()
|
||||
{
|
||||
return LookupTables<Message::Discovery,
|
||||
Message::DiscoveryResponse,
|
||||
Message::InvalidateMUID,
|
||||
Message::EndpointInquiry,
|
||||
Message::EndpointInquiryResponse,
|
||||
Message::ACK,
|
||||
Message::NAK,
|
||||
Message::ProfileInquiry,
|
||||
Message::ProfileInquiryResponse,
|
||||
Message::ProfileAdded,
|
||||
Message::ProfileRemoved,
|
||||
Message::ProfileDetails,
|
||||
Message::ProfileDetailsResponse,
|
||||
Message::ProfileOn,
|
||||
Message::ProfileOff,
|
||||
Message::ProfileEnabledReport,
|
||||
Message::ProfileDisabledReport,
|
||||
Message::ProfileSpecificData,
|
||||
Message::PropertyExchangeCapabilities,
|
||||
Message::PropertyExchangeCapabilitiesResponse,
|
||||
Message::PropertyGetData,
|
||||
Message::PropertyGetDataResponse,
|
||||
Message::PropertySetData,
|
||||
Message::PropertySetDataResponse,
|
||||
Message::PropertySubscribe,
|
||||
Message::PropertySubscribeResponse,
|
||||
Message::PropertyNotify,
|
||||
Message::ProcessInquiry,
|
||||
Message::ProcessInquiryResponse,
|
||||
Message::ProcessMidiMessageReport,
|
||||
Message::ProcessMidiMessageReportResponse,
|
||||
Message::ProcessEndMidiMessageReport>{};
|
||||
|
||||
}
|
||||
|
||||
static void visit (const Message::Parsed& msg, const MessageVisitor& visitor)
|
||||
{
|
||||
constexpr auto tables = getTables();
|
||||
const auto fn = tables.visitors[(uint8_t) msg.header.category];
|
||||
fn (msg, visitor);
|
||||
}
|
||||
|
||||
} // namespace juce::midi_ci::detail::MessageTypeUtils
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci::detail
|
||||
{
|
||||
|
||||
PropertyDataMessageChunker::PropertyDataMessageChunker (std::vector<std::byte>& storageIn,
|
||||
int chunkSizeIn,
|
||||
const std::byte messageKindIn,
|
||||
const std::byte requestIdIn,
|
||||
Span<const std::byte> headerIn,
|
||||
MUID sourceIn,
|
||||
MUID destIn,
|
||||
InputStream& bodyIn)
|
||||
: header (headerIn),
|
||||
storage (&storageIn),
|
||||
body (&bodyIn),
|
||||
source (sourceIn),
|
||||
dest (destIn),
|
||||
chunkSize (chunkSizeIn),
|
||||
messageKind (messageKindIn),
|
||||
requestId (requestIdIn)
|
||||
{
|
||||
if (hasRoomForBody())
|
||||
{
|
||||
populateStorage();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Header too large! There's no way to fit this message into the requested chunk size.
|
||||
jassertfalse;
|
||||
*this = PropertyDataMessageChunker();
|
||||
}
|
||||
}
|
||||
|
||||
PropertyDataMessageChunker& PropertyDataMessageChunker::operator++() noexcept
|
||||
{
|
||||
if (*this != PropertyDataMessageChunker())
|
||||
{
|
||||
if (body->isExhausted())
|
||||
{
|
||||
*this = PropertyDataMessageChunker();
|
||||
}
|
||||
else
|
||||
{
|
||||
++thisChunk;
|
||||
populateStorage();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Span<const std::byte> PropertyDataMessageChunker::operator*() const noexcept
|
||||
{
|
||||
// The end of the stream was reached, no point dereferencing the iterator now!
|
||||
jassert (storage != nullptr && (int) storage->size() <= chunkSize);
|
||||
return *storage;
|
||||
}
|
||||
|
||||
Span<const std::byte> PropertyDataMessageChunker::getHeaderForBlock() const
|
||||
{
|
||||
return thisChunk == 1 ? header : Span<const std::byte>{};
|
||||
}
|
||||
|
||||
int PropertyDataMessageChunker::getRoomForBody() const
|
||||
{
|
||||
return chunkSize - (int) (getHeaderForBlock().size() + 22);
|
||||
}
|
||||
|
||||
bool PropertyDataMessageChunker::hasRoomForBody() const
|
||||
{
|
||||
const auto bodyRoom = getRoomForBody();
|
||||
return (0 < bodyRoom)
|
||||
|| (0 == bodyRoom && body->getNumBytesRemaining() == 0);
|
||||
}
|
||||
|
||||
void PropertyDataMessageChunker::populateStorage() const
|
||||
{
|
||||
storage->clear();
|
||||
storage->resize ((size_t) getRoomForBody());
|
||||
|
||||
// Read body data into buffer
|
||||
const auto numBytesRead = (uint16_t) jmax (ssize_t (0), body->read (storage->data(), storage->size()));
|
||||
|
||||
const auto [numChunks, thisChunkNum] = [&]() -> std::tuple<uint16_t, uint16_t>
|
||||
{
|
||||
if (body->isExhausted() || body->getNumBytesRemaining() == 0)
|
||||
return std::tuple (thisChunk, thisChunk);
|
||||
|
||||
const auto totalLength = body->getTotalLength();
|
||||
|
||||
if (totalLength < 0)
|
||||
return std::tuple ((uint16_t) 0, thisChunk); // 0 means "unknown number"
|
||||
|
||||
const auto roomForBody = getRoomForBody();
|
||||
|
||||
if (roomForBody != 0)
|
||||
return std::tuple ((uint16_t) ((totalLength + roomForBody - 1) / roomForBody), thisChunk);
|
||||
|
||||
// During construction, the input stream reported that it had no data remaining, so no
|
||||
// space was reserved for body content.
|
||||
// Now, the input stream reports that it has data remaining, but there's nowhere
|
||||
// to fit it in the message!
|
||||
jassertfalse;
|
||||
return std::tuple (thisChunk, (uint16_t) 0); // 0 means "data potentially unusable"
|
||||
}();
|
||||
|
||||
// Now we know how many bytes we managed to read, write the header at the end of the buffer
|
||||
const auto headerForBlock = getHeaderForBlock();
|
||||
detail::Marshalling::Writer writer { *storage };
|
||||
writer (Message::Header { ChannelInGroup::wholeBlock,
|
||||
messageKind,
|
||||
detail::MessageMeta::implementationVersion,
|
||||
source,
|
||||
dest },
|
||||
requestId,
|
||||
detail::MessageMeta::makeSpanWithSizeBytes<2> (headerForBlock),
|
||||
numChunks,
|
||||
thisChunkNum,
|
||||
numBytesRead);
|
||||
|
||||
// Finally, swap the header to the beginning of the buffer
|
||||
std::rotate (storage->begin(), storage->begin() + getRoomForBody(), storage->end());
|
||||
|
||||
// ...and bring the storage buffer down to size, if we didn't manage to fill it
|
||||
storage->resize (storage->size() + numBytesRead - (size_t) getRoomForBody());
|
||||
}
|
||||
|
||||
} // namespace juce::midi_ci::detail
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci::detail
|
||||
{
|
||||
|
||||
/*
|
||||
Breaks up a large property exchange message into chunks of the requested size.
|
||||
|
||||
Note that the header *must* fit inside the first block, so you must ensure
|
||||
that the header is small enough to fit inside the requested chunk size.
|
||||
*/
|
||||
class PropertyDataMessageChunker
|
||||
{
|
||||
auto tie() const { return std::tie (storage, body, source, dest, chunkSize, messageKind, requestId); }
|
||||
|
||||
public:
|
||||
/* Constructs a chunker instance.
|
||||
|
||||
@param storageIn backing storage where each chunk will be written
|
||||
@param chunkSizeIn the maximum size of each chunk
|
||||
@param messageKindIn the subID2 byte identifying the type of message in each chunk
|
||||
@param requestIdIn the id that should be included in all messages that are part of the same property exchange transaction
|
||||
@param headerIn the header bytes of the message. This is always JSON encoded as 7-bit ASCII text, see the MIDI-CI spec for full details
|
||||
@param sourceIn the MUID of the device sending the chunked messages
|
||||
@param destIn the MUID of the recipient of the chunked messages
|
||||
@param bodyIn a stream that can supply the data payload for this chunk sequence. All payload bytes *must* be 7-bit (MSB not set).
|
||||
*/
|
||||
PropertyDataMessageChunker (std::vector<std::byte>& storageIn,
|
||||
int chunkSizeIn,
|
||||
const std::byte messageKindIn,
|
||||
const std::byte requestIdIn,
|
||||
Span<const std::byte> headerIn,
|
||||
MUID sourceIn,
|
||||
MUID destIn,
|
||||
InputStream& bodyIn);
|
||||
|
||||
/* Returns true if this chunker hasn't finished producing chunks. */
|
||||
explicit operator bool() const { return *this != PropertyDataMessageChunker(); }
|
||||
|
||||
/* Allowing foreach usage. */
|
||||
auto begin() const { return *this; }
|
||||
|
||||
/* Allow foreach usage. */
|
||||
auto end() const { return PropertyDataMessageChunker{}; }
|
||||
|
||||
/* Writes the bytes of the next chunk, if any, into the storage buffer. */
|
||||
PropertyDataMessageChunker& operator++() noexcept;
|
||||
|
||||
/* Checks whether the state of this chunker matches the state of another chunker, enabling foreach usage. */
|
||||
bool operator== (const PropertyDataMessageChunker& other) const noexcept { return tie() == other.tie(); }
|
||||
bool operator!= (const PropertyDataMessageChunker& other) const noexcept { return tie() != other.tie(); }
|
||||
|
||||
/* Returns a span over the valid bytes in the output buffer. */
|
||||
Span<const std::byte> operator*() const noexcept;
|
||||
|
||||
private:
|
||||
PropertyDataMessageChunker() = default;
|
||||
|
||||
Span<const std::byte> getHeaderForBlock() const;
|
||||
int getRoomForBody() const;
|
||||
bool hasRoomForBody() const;
|
||||
void populateStorage() const;
|
||||
|
||||
Span<const std::byte> header;
|
||||
std::vector<std::byte>* storage{};
|
||||
InputStream* body{};
|
||||
MUID source = MUID::makeUnchecked (0), dest = MUID::makeUnchecked (0);
|
||||
int chunkSize{};
|
||||
uint16_t thisChunk { 0x01 };
|
||||
std::byte messageKind{};
|
||||
std::byte requestId{};
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci::detail
|
||||
78
modules/juce_midi_ci/detail/juce_CIPropertyHostUtils.h
Normal file
78
modules/juce_midi_ci/detail/juce_CIPropertyHostUtils.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci::detail
|
||||
{
|
||||
|
||||
struct PropertyHostUtils
|
||||
{
|
||||
PropertyHostUtils() = delete;
|
||||
|
||||
static void send (BufferOutput& output,
|
||||
uint8_t group,
|
||||
std::byte subID2,
|
||||
MUID targetMuid,
|
||||
std::byte requestID,
|
||||
Span<const std::byte> header,
|
||||
Span<const std::byte> body,
|
||||
int chunkSize)
|
||||
{
|
||||
MemoryInputStream stream (body.data(), body.size(), false);
|
||||
const detail::PropertyDataMessageChunker chunker { output.getOutputBuffer(),
|
||||
std::min (chunkSize, 1 << 16),
|
||||
subID2,
|
||||
requestID,
|
||||
header,
|
||||
output.getMuid(),
|
||||
targetMuid,
|
||||
stream };
|
||||
|
||||
std::for_each (chunker.begin(), chunker.end(), [&] (auto) { output.send (group); });
|
||||
}
|
||||
|
||||
static auto getTerminator (BufferOutput& output, FunctionBlock fb, MUID them)
|
||||
{
|
||||
const auto us = output.getMuid();
|
||||
return [&output, fb, us, them] (std::byte id)
|
||||
{
|
||||
const Message::Header notifyHeader
|
||||
{
|
||||
ChannelInGroup::wholeBlock,
|
||||
detail::MessageMeta::Meta<Message::PropertyNotify>::subID2,
|
||||
detail::MessageMeta::implementationVersion,
|
||||
us,
|
||||
them,
|
||||
};
|
||||
|
||||
const auto jsonHeader = Encodings::jsonTo7BitText (JSONUtils::makeObjectWithKeyFirst ({ { "status", 104 } }, "status"));
|
||||
detail::MessageTypeUtils::send (output,
|
||||
fb.firstGroup,
|
||||
notifyHeader,
|
||||
Message::PropertyNotify { { id, jsonHeader, 1, 1, {} } });
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace juce::midi_ci::detail
|
||||
507
modules/juce_midi_ci/detail/juce_CIResponder.cpp
Normal file
507
modules/juce_midi_ci/detail/juce_CIResponder.cpp
Normal file
|
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
namespace juce::midi_ci::detail
|
||||
{
|
||||
|
||||
Parser::Status Responder::processCompleteMessage (BufferOutput& output,
|
||||
ump::BytesOnGroup message,
|
||||
Span<ResponderDelegate* const> listeners)
|
||||
{
|
||||
auto status = Parser::Status::noError;
|
||||
const auto parsed = Parser::parse (output.getMuid(), message.bytes, &status);
|
||||
|
||||
if (! parsed.has_value())
|
||||
return Parser::Status::malformed;
|
||||
|
||||
class Output : public ResponderOutput
|
||||
{
|
||||
public:
|
||||
Output (BufferOutput& o, Message::Header h, uint8_t g)
|
||||
: innerOutput (o), header (h), group (g) {}
|
||||
|
||||
MUID getMuid() const override { return innerOutput.getMuid(); }
|
||||
Message::Header getIncomingHeader() const override { return header; }
|
||||
uint8_t getIncomingGroup() const override { return group; }
|
||||
std::vector<std::byte>& getOutputBuffer() override { return innerOutput.getOutputBuffer(); }
|
||||
void send (uint8_t g) override { innerOutput.send (g); }
|
||||
|
||||
private:
|
||||
BufferOutput& innerOutput;
|
||||
Message::Header header;
|
||||
uint8_t group{};
|
||||
};
|
||||
|
||||
Output responderOutput { output, parsed->header, message.group };
|
||||
|
||||
if (status != Parser::Status::noError)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case Parser::Status::collidingMUID:
|
||||
{
|
||||
const Message::Header header { ChannelInGroup::wholeBlock,
|
||||
MessageMeta::Meta<Message::InvalidateMUID>::subID2,
|
||||
MessageMeta::implementationVersion,
|
||||
output.getMuid(),
|
||||
MUID::getBroadcast() };
|
||||
const Message::InvalidateMUID body { output.getMuid() };
|
||||
MessageTypeUtils::send (responderOutput, responderOutput.getIncomingGroup(), header, body);
|
||||
break;
|
||||
}
|
||||
|
||||
case Parser::Status::unrecognisedMessage:
|
||||
MessageTypeUtils::sendNAK (responderOutput, std::byte { 0x01 });
|
||||
break;
|
||||
|
||||
case Parser::Status::reservedVersion:
|
||||
MessageTypeUtils::sendNAK (responderOutput, std::byte { 0x02 });
|
||||
break;
|
||||
|
||||
case Parser::Status::malformed:
|
||||
MessageTypeUtils::sendNAK (responderOutput, std::byte { 0x41 });
|
||||
break;
|
||||
|
||||
case Parser::Status::mismatchedMUID:
|
||||
case Parser::Status::noError:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
for (auto* listener : listeners)
|
||||
if (listener != nullptr && listener->tryRespond (responderOutput, *parsed))
|
||||
return Parser::Status::noError;
|
||||
|
||||
MessageTypeUtils::BaseCaseDelegate base;
|
||||
|
||||
if (base.tryRespond (responderOutput, *parsed))
|
||||
return Parser::Status::noError;
|
||||
|
||||
return Parser::Status::unrecognisedMessage;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class ResponderTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ResponderTests() : UnitTest ("Responder", UnitTestCategories::midi) {}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
auto random = getRandom();
|
||||
std::vector<std::byte> outgoing;
|
||||
|
||||
const auto makeOutput = [&]
|
||||
{
|
||||
struct Output : public BufferOutput
|
||||
{
|
||||
Output (Random& r, std::vector<std::byte>& b)
|
||||
: muid (MUID::makeRandom (r)), buf (b) {}
|
||||
|
||||
MUID getMuid() const override { return muid; }
|
||||
std::vector<std::byte>& getOutputBuffer() override { return buf; }
|
||||
void send (uint8_t) override { sent.push_back (buf); }
|
||||
|
||||
MUID muid;
|
||||
std::vector<std::byte>& buf;
|
||||
std::vector<std::vector<std::byte>> sent;
|
||||
};
|
||||
|
||||
return Output { random, outgoing };
|
||||
};
|
||||
|
||||
beginTest ("An endpoint message with a matching MUID provokes an endpoint response");
|
||||
{
|
||||
constexpr auto version = MessageMeta::implementationVersion;
|
||||
|
||||
auto output = makeOutput();
|
||||
const auto initialMUID = output.getMuid();
|
||||
|
||||
const auto bytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* endpoint message */ 0x72,
|
||||
/* version */ version,
|
||||
/* source MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* destination MUID */ (initialMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x15) & 0x7f,
|
||||
/* status, product instance ID */ 0x00);
|
||||
|
||||
const Message::Parsed expectedInput { Message::Header { ChannelInGroup::wholeBlock,
|
||||
std::byte { 0x72 },
|
||||
version,
|
||||
MUID::makeUnchecked (0x80c101),
|
||||
initialMUID },
|
||||
Message::EndpointInquiry { std::byte { 0x00 } } };
|
||||
EndpointResponderListener listener;
|
||||
processCompleteMessage (output, { 0, bytes }, listener);
|
||||
expect (listener == SilentResponderListener (expectedInput));
|
||||
|
||||
const auto expectedOutputBytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* endpoint reply */ 0x73,
|
||||
/* version */ version,
|
||||
/* source MUID */ (initialMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x15) & 0x7f,
|
||||
/* destination MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* status */ 0x00,
|
||||
/* 16-bit length of following data */ 0x04,
|
||||
/* ... */ 0x00,
|
||||
/* info */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00);
|
||||
|
||||
expect (rangesEqual (output.sent.front(), expectedOutputBytes));
|
||||
}
|
||||
|
||||
beginTest ("An endpoint message directed at a different MUID does not provoke a response");
|
||||
{
|
||||
const auto destMUID = MUID::makeRandom (random);
|
||||
constexpr auto version = MessageMeta::implementationVersion;
|
||||
|
||||
const auto bytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* endpoint message */ 0x72,
|
||||
/* version */ version,
|
||||
/* source MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* destination MUID */ (destMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (destMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (destMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (destMUID.get() >> 0x15) & 0x7f,
|
||||
/* status, product instance ID */ 0x00);
|
||||
|
||||
auto output = makeOutput();
|
||||
EndpointResponderListener listener;
|
||||
processCompleteMessage (output, { 0, bytes }, listener);
|
||||
expect (listener == SilentResponderListener());
|
||||
expect (output.sent.empty());
|
||||
}
|
||||
|
||||
beginTest ("If the listener fails to compose an endpoint response, a NAK is emitted");
|
||||
{
|
||||
auto output = makeOutput();
|
||||
const auto initialMUID = output.getMuid();
|
||||
|
||||
SilentResponderListener listener;
|
||||
constexpr auto version = MessageMeta::implementationVersion;
|
||||
|
||||
const auto bytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* endpoint message */ 0x72,
|
||||
/* version */ version,
|
||||
/* source MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* destination MUID */ (initialMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x15) & 0x7f,
|
||||
/* status, product instance ID */ 0x00);
|
||||
|
||||
processCompleteMessage (output, { 0, bytes }, listener);
|
||||
|
||||
const auto expectedOutputBytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* nak */ 0x7f,
|
||||
/* version */ version,
|
||||
/* source MUID */ (initialMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x15) & 0x7f,
|
||||
/* destination MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* original transaction sub-id #2 */ 0x72,
|
||||
/* nak status code */ 0x00,
|
||||
/* nak status data */ 0x00,
|
||||
/* details */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* message text length */ 0x00,
|
||||
/* ... */ 0x00);
|
||||
expect (rangesEqual (output.sent.front(), expectedOutputBytes));
|
||||
}
|
||||
|
||||
beginTest ("If a message is sent with reserved bits set in the Message Format Version, a NAK is emitted");
|
||||
{
|
||||
auto output = makeOutput();
|
||||
const auto initialMUID = output.getMuid();
|
||||
|
||||
const auto bytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* endpoint message */ 0x72,
|
||||
/* version, reserved bit set */ 0x12,
|
||||
/* source MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* destination MUID */ (initialMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x15) & 0x7f,
|
||||
/* status, product instance ID */ 0x00);
|
||||
SilentResponderListener listener;
|
||||
processCompleteMessage (output, { 0, bytes }, listener);
|
||||
|
||||
expect (listener == SilentResponderListener{});
|
||||
|
||||
const auto expectedOutputBytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* nak */ 0x7f,
|
||||
/* version */ MessageMeta::implementationVersion,
|
||||
/* source MUID */ (initialMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (initialMUID.get() >> 0x15) & 0x7f,
|
||||
/* destination MUID */ 0x01,
|
||||
/* ... */ 0x02,
|
||||
/* ... */ 0x03,
|
||||
/* ... */ 0x04,
|
||||
/* original transaction sub-id #2 */ 0x72,
|
||||
/* nak status code */ 0x02,
|
||||
/* nak status data */ 0x00,
|
||||
/* details */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* message text length */ 0x00,
|
||||
/* ... */ 0x00);
|
||||
|
||||
expect (rangesEqual (output.sent.front(), expectedOutputBytes));
|
||||
}
|
||||
|
||||
beginTest ("If the message body is malformed, a NAK with a status of 0x41 is emitted");
|
||||
{
|
||||
const auto sourceMUID = MUID::makeRandom (random);
|
||||
|
||||
Message::Header header;
|
||||
header.deviceID = ChannelInGroup::wholeBlock;
|
||||
header.category = std::byte { 0x7e };
|
||||
header.version = MessageMeta::implementationVersion;
|
||||
header.source = sourceMUID;
|
||||
header.destination = MUID::getBroadcast();
|
||||
|
||||
Message::InvalidateMUID invalidate;
|
||||
invalidate.target = MUID::makeRandom (random);
|
||||
|
||||
std::vector<std::byte> message;
|
||||
Marshalling::Writer { message } (header, invalidate);
|
||||
|
||||
// Remove a byte from the end of the message
|
||||
message.pop_back();
|
||||
|
||||
auto output = makeOutput();
|
||||
const auto ourMUID = output.getMuid();
|
||||
|
||||
SilentResponderListener listener;
|
||||
processCompleteMessage (output, { 0, message }, listener);
|
||||
|
||||
const auto expectedOutputBytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* nak */ 0x7f,
|
||||
/* version */ MessageMeta::implementationVersion,
|
||||
/* source MUID */ (ourMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (ourMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (ourMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (ourMUID.get() >> 0x15) & 0x7f,
|
||||
/* destination MUID */ (sourceMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (sourceMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (sourceMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (sourceMUID.get() >> 0x15) & 0x7f,
|
||||
/* original transaction sub-id #2 */ 0x7e,
|
||||
/* nak status code */ 0x41,
|
||||
/* nak status data */ 0x00,
|
||||
/* details */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* message text length */ 0x00,
|
||||
/* ... */ 0x00);
|
||||
expect (rangesEqual (output.sent.front(), expectedOutputBytes));
|
||||
}
|
||||
|
||||
beginTest ("If an unrecognised message is received, a NAK with a status of 0x01 is emitted");
|
||||
{
|
||||
const auto sourceMUID = MUID::makeRandom (random);
|
||||
|
||||
Message::Header header;
|
||||
header.deviceID = ChannelInGroup::wholeBlock;
|
||||
header.category = std::byte { 0x50 }; // reserved
|
||||
header.version = MessageMeta::implementationVersion;
|
||||
header.source = sourceMUID;
|
||||
header.destination = MUID::getBroadcast();
|
||||
|
||||
std::vector<std::byte> message;
|
||||
Marshalling::Writer { message } (header);
|
||||
message.emplace_back();
|
||||
|
||||
auto output = makeOutput();
|
||||
const auto ourMUID = output.getMuid();
|
||||
|
||||
SilentResponderListener listener;
|
||||
processCompleteMessage (output, { 0, message }, listener);
|
||||
|
||||
const auto expectedOutputBytes = makeByteArray (0x7e,
|
||||
/* to function block */ 0x7f,
|
||||
/* midi CI */ 0x0d,
|
||||
/* nak */ 0x7f,
|
||||
/* version */ MessageMeta::implementationVersion,
|
||||
/* source MUID */ (ourMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (ourMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (ourMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (ourMUID.get() >> 0x15) & 0x7f,
|
||||
/* destination MUID */ (sourceMUID.get() >> 0x00) & 0x7f,
|
||||
/* ... */ (sourceMUID.get() >> 0x07) & 0x7f,
|
||||
/* ... */ (sourceMUID.get() >> 0x0e) & 0x7f,
|
||||
/* ... */ (sourceMUID.get() >> 0x15) & 0x7f,
|
||||
/* original transaction sub-id #2 */ 0x50,
|
||||
/* nak status code */ 0x01,
|
||||
/* nak status data */ 0x00,
|
||||
/* details */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* ... */ 0x00,
|
||||
/* message text length */ 0x00,
|
||||
/* ... */ 0x00);
|
||||
expect (rangesEqual (output.sent.front(), expectedOutputBytes));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename... Ts>
|
||||
static std::array<std::byte, sizeof... (Ts)> makeByteArray (Ts&&... ts)
|
||||
{
|
||||
jassert (((0 <= (int) ts && (int) ts <= std::numeric_limits<uint8_t>::max()) && ...));
|
||||
return { std::byte (ts)... };
|
||||
}
|
||||
|
||||
struct SilentResponderListener : public ResponderDelegate
|
||||
{
|
||||
SilentResponderListener() = default;
|
||||
explicit SilentResponderListener (const Message::Parsed& p) : parsed (p) {}
|
||||
|
||||
bool tryRespond (ResponderOutput&, const Message::Parsed& p) override
|
||||
{
|
||||
parsed = p;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returning false indicates that the message was not handled
|
||||
bool operator== (const SilentResponderListener& other) const { return parsed == other.parsed; }
|
||||
bool operator!= (const SilentResponderListener& other) const { return ! operator== (other); }
|
||||
|
||||
std::optional<Message::Parsed> parsed;
|
||||
};
|
||||
|
||||
struct EndpointResponderListener : public SilentResponderListener
|
||||
{
|
||||
bool tryRespond (ResponderOutput& output, const Message::Parsed& message) override
|
||||
{
|
||||
parsed = message;
|
||||
|
||||
if (std::holds_alternative<Message::EndpointInquiry> (message.body))
|
||||
{
|
||||
std::array<std::byte, 4> data{};
|
||||
Message::EndpointInquiryResponse response;
|
||||
response.status = std::byte{};
|
||||
response.data = data;
|
||||
|
||||
MessageTypeUtils::send (output, output.getIncomingGroup(), output.getReplyHeader (std::byte { 0x73 }), response);
|
||||
return true;
|
||||
}
|
||||
|
||||
return SilentResponderListener::tryRespond (output, message);
|
||||
|
||||
}
|
||||
|
||||
using SilentResponderListener::operator==, SilentResponderListener::operator!=;
|
||||
};
|
||||
|
||||
struct OutputCallback
|
||||
{
|
||||
void operator() (Span<const std::byte> bytes)
|
||||
{
|
||||
output = std::vector<std::byte> (bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
std::vector<std::byte> output;
|
||||
};
|
||||
|
||||
template <typename A, typename B>
|
||||
static bool rangesEqual (A&& a, B&& b)
|
||||
{
|
||||
using std::begin, std::end;
|
||||
return std::equal (begin (a), end (a), begin (b), end (b));
|
||||
}
|
||||
|
||||
|
||||
static Parser::Status processCompleteMessage (BufferOutput& output,
|
||||
ump::BytesOnGroup message,
|
||||
ResponderDelegate& listener)
|
||||
{
|
||||
ResponderDelegate* const listeners[] { &listener };
|
||||
return Responder::processCompleteMessage (output, message, listeners);
|
||||
}
|
||||
};
|
||||
|
||||
static ResponderTests responderTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce::midi_ci::detail
|
||||
49
modules/juce_midi_ci/detail/juce_CIResponder.h
Normal file
49
modules/juce_midi_ci/detail/juce_CIResponder.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::midi_ci::detail
|
||||
{
|
||||
|
||||
/*
|
||||
Parses individual messages, and additionally gives ResponderDelegates a chance to formulate
|
||||
a response to any message that would normally necessitate a reply.
|
||||
*/
|
||||
struct Responder
|
||||
{
|
||||
Responder() = delete;
|
||||
|
||||
/* Parses the message, then calls tryParse on each ResponderDelegate in
|
||||
turn until one returns true, indicating that the message has been
|
||||
handled. Most 'inquiry' messages should emit one or more reply messages.
|
||||
These replies will be written to the provided BufferOutput.
|
||||
If none of the provided delegates are able to handle the message, then
|
||||
a generic NAK will be written to the BufferOutput.
|
||||
*/
|
||||
static Parser::Status processCompleteMessage (BufferOutput& output,
|
||||
ump::BytesOnGroup message,
|
||||
Span<ResponderDelegate* const> delegates);
|
||||
};
|
||||
|
||||
} // namespace juce::midi_ci
|
||||
55
modules/juce_midi_ci/juce_midi_ci.cpp
Normal file
55
modules/juce_midi_ci/juce_midi_ci.cpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifdef JUCE_MIDI_CI_H_INCLUDED
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#include "juce_midi_ci.h"
|
||||
|
||||
#include <juce_midi_ci/detail/juce_CIMessageMeta.h>
|
||||
#include <juce_midi_ci/detail/juce_CIMarshalling.h>
|
||||
#include <juce_midi_ci/detail/juce_CIPropertyDataMessageChunker.h>
|
||||
#include <juce_midi_ci/detail/juce_CIResponder.h>
|
||||
#include <juce_midi_ci/detail/juce_CIMessageTypeUtils.h>
|
||||
#include <juce_midi_ci/detail/juce_CIPropertyHostUtils.h>
|
||||
|
||||
#include <juce_midi_ci/detail/juce_CIPropertyDataMessageChunker.cpp>
|
||||
#include <juce_midi_ci/detail/juce_CIResponder.cpp>
|
||||
|
||||
#include <juce_midi_ci/ci/juce_CIDevice.cpp>
|
||||
#include <juce_midi_ci/ci/juce_CIEncodings.cpp>
|
||||
#include <juce_midi_ci/ci/juce_CIParser.cpp>
|
||||
#include <juce_midi_ci/ci/juce_CIProfileHost.cpp>
|
||||
#include <juce_midi_ci/ci/juce_CIProfileStates.cpp>
|
||||
#include <juce_midi_ci/ci/juce_CIPropertyDelegate.cpp>
|
||||
#include <juce_midi_ci/ci/juce_CIPropertyExchangeCache.cpp>
|
||||
#include <juce_midi_ci/ci/juce_CIPropertyHost.cpp>
|
||||
#include <juce_midi_ci/ci/juce_CIResponderOutput.cpp>
|
||||
85
modules/juce_midi_ci/juce_midi_ci.h
Normal file
85
modules/juce_midi_ci/juce_midi_ci.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this module, and is read by
|
||||
the Projucer to automatically generate project code that uses it.
|
||||
For details about the syntax and how to create or use a module, see the
|
||||
JUCE Module Format.md file.
|
||||
|
||||
|
||||
BEGIN_JUCE_MODULE_DECLARATION
|
||||
|
||||
ID: juce_midi_ci
|
||||
vendor: juce
|
||||
version: 7.0.8
|
||||
name: JUCE MIDI CI Classes
|
||||
description: Classes facilitating communication via MIDI Capability Inquiry
|
||||
website: http://www.juce.com/juce
|
||||
license: GPL/Commercial
|
||||
minimumCppStandard: 17
|
||||
|
||||
dependencies: juce_audio_basics
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
#define JUCE_MIDI_CI_H_INCLUDED
|
||||
|
||||
#include <juce_audio_basics/juce_audio_basics.h>
|
||||
|
||||
#include <juce_midi_ci/ci/juce_CIFunctionBlock.h>
|
||||
#include <juce_midi_ci/ci/juce_CIMuid.h>
|
||||
#include <juce_midi_ci/ci/juce_CIEncoding.h>
|
||||
#include <juce_midi_ci/ci/juce_CIEncodings.h>
|
||||
#include <juce_midi_ci/ci/juce_CIMessages.h>
|
||||
#include <juce_midi_ci/ci/juce_CIChannelAddress.h>
|
||||
#include <juce_midi_ci/ci/juce_CIResponderOutput.h>
|
||||
#include <juce_midi_ci/ci/juce_CIParser.h>
|
||||
#include <juce_midi_ci/ci/juce_CISupportedAndActive.h>
|
||||
#include <juce_midi_ci/ci/juce_CIResponderDelegate.h>
|
||||
#include <juce_midi_ci/ci/juce_CIProfileStates.h>
|
||||
#include <juce_midi_ci/ci/juce_CIProfileAtAddress.h>
|
||||
#include <juce_midi_ci/ci/juce_CIProfileDelegate.h>
|
||||
#include <juce_midi_ci/ci/juce_CIProfileHost.h>
|
||||
#include <juce_midi_ci/ci/juce_CISubscription.h>
|
||||
#include <juce_midi_ci/ci/juce_CIPropertyDelegate.h>
|
||||
#include <juce_midi_ci/ci/juce_CIPropertyExchangeResult.h>
|
||||
#include <juce_midi_ci/ci/juce_CIPropertyExchangeCache.h>
|
||||
#include <juce_midi_ci/ci/juce_CIPropertyHost.h>
|
||||
#include <juce_midi_ci/ci/juce_CIDeviceFeatures.h>
|
||||
#include <juce_midi_ci/ci/juce_CIDeviceMessageHandler.h>
|
||||
#include <juce_midi_ci/ci/juce_CIDeviceOptions.h>
|
||||
#include <juce_midi_ci/ci/juce_CIDeviceListener.h>
|
||||
#include <juce_midi_ci/ci/juce_CIDevice.h>
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace ci = midi_ci;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue