diff --git a/build/nuke_build/Build.cs b/build/nuke_build/Build.cs
index c7be7ffb7..78426a5ca 100644
--- a/build/nuke_build/Build.cs
+++ b/build/nuke_build/Build.cs
@@ -882,6 +882,7 @@ void UpdateSetupBundleInfoIncludeFile(string platform)
paths.Add(settingsOutputFolder / "Assets" / "AppIcon.ico");
paths.Add(settingsOutputFolder / "Assets" / "AppIcon.png");
+ paths.Add(settingsOutputFolder / "Assets" / "LoopbackDiagram.svg");
// TODO: This doesn't deal with any localization content
diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi
index d20880a59..3de205f3d 100644
--- a/build/staging/version/BundleInfo.wxi
+++ b/build/staging/version/BundleInfo.wxi
@@ -1,4 +1,4 @@
-
+
diff --git a/build/staging/version/WindowsMidiServicesVersion.cs b/build/staging/version/WindowsMidiServicesVersion.cs
index fc657e903..f11047228 100644
--- a/build/staging/version/WindowsMidiServicesVersion.cs
+++ b/build/staging/version/WindowsMidiServicesVersion.cs
@@ -6,12 +6,12 @@ public static class MidiBuildInformation
{
public const string Source = "GitHub Preview";
public const string Name = "Customer Preview 2";
- public const string BuildFullVersion = "1.0.3-preview-11.250213-52";
+ public const string BuildFullVersion = "1.0.3-preview-11.250215-2126";
public const string VersionMajor = "1";
public const string VersionMinor = "0";
public const string VersionRevision = "3";
- public const string VersionDateNumber = "250213";
- public const string VersionTimeNumber = "52";
+ public const string VersionDateNumber = "250215";
+ public const string VersionTimeNumber = "2126";
}
}
diff --git a/build/staging/version/WindowsMidiServicesVersion.h b/build/staging/version/WindowsMidiServicesVersion.h
index a79945daf..6abbb14e1 100644
--- a/build/staging/version/WindowsMidiServicesVersion.h
+++ b/build/staging/version/WindowsMidiServicesVersion.h
@@ -5,12 +5,12 @@
#define WINDOWS_MIDI_SERVICES_BUILD_SOURCE L"GitHub Preview"
#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_NAME L"Customer Preview 2"
-#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.3-preview-11.250213-52"
+#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.3-preview-11.250215-2126"
#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MAJOR L"1"
#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MINOR L"0"
#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_REVISION L"3"
-#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250213"
-#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"52"
+#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250215"
+#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"2126"
#endif
diff --git a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj
index 1276e2a45..cae1852ca 100644
--- a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj
+++ b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj
@@ -2,7 +2,7 @@
- Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250213-52
+ Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250215-2126
true
true
false
diff --git a/samples/cpp-winrt/basics/packages.config b/samples/cpp-winrt/basics/packages.config
index 29bfea9f4..10c74314f 100644
--- a/samples/cpp-winrt/basics/packages.config
+++ b/samples/cpp-winrt/basics/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj
index feca9dfa5..91112dac4 100644
--- a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj
+++ b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj
@@ -2,7 +2,7 @@
- Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250213-52
+ Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250215-2126
true
true
true
diff --git a/samples/cpp-winrt/loopback-endpoints/packages.config b/samples/cpp-winrt/loopback-endpoints/packages.config
index 29bfea9f4..10c74314f 100644
--- a/samples/cpp-winrt/loopback-endpoints/packages.config
+++ b/samples/cpp-winrt/loopback-endpoints/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/send-speed/packages.config b/samples/cpp-winrt/send-speed/packages.config
index 29bfea9f4..10c74314f 100644
--- a/samples/cpp-winrt/send-speed/packages.config
+++ b/samples/cpp-winrt/send-speed/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj
index 69024004a..6900ff4c3 100644
--- a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj
+++ b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj
@@ -2,7 +2,7 @@
- Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250213-52
+ Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250215-2126
true
true
true
diff --git a/samples/cpp-winrt/simple-app-to-app-midi/packages.config b/samples/cpp-winrt/simple-app-to-app-midi/packages.config
index 29bfea9f4..10c74314f 100644
--- a/samples/cpp-winrt/simple-app-to-app-midi/packages.config
+++ b/samples/cpp-winrt/simple-app-to-app-midi/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj
index 722d32cab..8bad8d128 100644
--- a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj
+++ b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj
@@ -2,7 +2,7 @@
- Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250213-52
+ Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250215-2126
true
true
true
diff --git a/samples/cpp-winrt/static-enum-endpoints/packages.config b/samples/cpp-winrt/static-enum-endpoints/packages.config
index 29bfea9f4..10c74314f 100644
--- a/samples/cpp-winrt/static-enum-endpoints/packages.config
+++ b/samples/cpp-winrt/static-enum-endpoints/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj
index d33d45eb7..62169f33c 100644
--- a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj
+++ b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj
@@ -2,7 +2,7 @@
- Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250213-52
+ Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250215-2126
true
true
true
diff --git a/samples/cpp-winrt/watch-endpoints/packages.config b/samples/cpp-winrt/watch-endpoints/packages.config
index 29bfea9f4..10c74314f 100644
--- a/samples/cpp-winrt/watch-endpoints/packages.config
+++ b/samples/cpp-winrt/watch-endpoints/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj
index 01344d9e6..1dab40062 100644
--- a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj
+++ b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj
@@ -2,7 +2,7 @@
- Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250213-52
+ Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250215-2126
true
true
true
diff --git a/src/api/Inc/MidiDefs.h b/src/api/Inc/MidiDefs.h
index c359b57b8..17b2dee2c 100644
--- a/src/api/Inc/MidiDefs.h
+++ b/src/api/Inc/MidiDefs.h
@@ -652,8 +652,11 @@ DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_VirtualMidiEndpointAssociator, 900); // DEVP
#define STRING_PKEY_MIDI_UseOldMidi1PortNamingScheme MIDI_STRING_PKEY_GUID MIDI_STRING_PKEY_PID_SEPARATOR L"1000"
DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_UseOldMidi1PortNamingScheme, 1000); // DEVPROP_TYPE_BOOL
+#define STRING_PKEY_MIDI_UseGroupTerminalBlocksForExactMidi1PortNames MIDI_STRING_PKEY_GUID MIDI_STRING_PKEY_PID_SEPARATOR L"1010"
+DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_UseGroupTerminalBlocksForExactMidi1PortNames, 1010); // DEVPROP_TYPE_BOOLEAN
-
+#define STRING_PKEY_MIDI_CreateMidi1PortsForEndpoint MIDI_STRING_PKEY_GUID MIDI_STRING_PKEY_PID_SEPARATOR L"1020"
+DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_CreateMidi1PortsForEndpoint, 1020); // DEVPROP_TYPE_BOOLEAN
// Structures for properties =================================================================
diff --git a/src/api/Inc/midi_naming.h b/src/api/Inc/midi_naming.h
index e87a9c0b7..7852c62bf 100644
--- a/src/api/Inc/midi_naming.h
+++ b/src/api/Inc/midi_naming.h
@@ -28,7 +28,8 @@ namespace WindowsMidiServicesInternal::Midi1PortNaming
// for whole word. We do other removal in the next step
if (pinName == L"MIDI" ||
pinName == L"Out" || pinName == L"OUT" || pinName == L"out" ||
- pinName == L"In" || pinName == L"IN" || pinName == L"in"
+ pinName == L"In" || pinName == L"IN" || pinName == L"in" ||
+ pinName == L"IO"
)
{
cleanedPinName = L"";
diff --git a/src/api/Service/Exe/MidiDeviceManager.cpp b/src/api/Service/Exe/MidiDeviceManager.cpp
index d4f235f99..41e301493 100644
--- a/src/api/Service/Exe/MidiDeviceManager.cpp
+++ b/src/api/Service/Exe/MidiDeviceManager.cpp
@@ -10,6 +10,7 @@
#include "ks.h"
#include "Midi2KSAggregateTransport.h"
#include "Midi2KSTransport.h"
+#include "Midi2LoopbackMidiTransport.h"
using namespace winrt::Windows::Devices::Enumeration;
@@ -2329,6 +2330,8 @@ CMidiDeviceManager::SyncMidi1Ports(
additionalProperties.Append(STRING_PKEY_MIDI_GroupTerminalBlocks);
additionalProperties.Append(STRING_PKEY_MIDI_EndpointDiscoveryProcessComplete);
additionalProperties.Append(STRING_PKEY_MIDI_CustomPortAssignments);
+ additionalProperties.Append(STRING_PKEY_MIDI_UseGroupTerminalBlocksForExactMidi1PortNames);
+ additionalProperties.Append(STRING_PKEY_MIDI_CreateMidi1PortsForEndpoint);
// We have function blocks to retrieve
// build up the property keys to query for the function blocks
@@ -2344,6 +2347,21 @@ CMidiDeviceManager::SyncMidi1Ports(
auto deviceInfo = DeviceInformation::CreateFromIdAsync(umpMidiPort->DeviceInterfaceId.get(), additionalProperties, winrt::Windows::Devices::Enumeration::DeviceInformationKind::DeviceInterface).get();
+ // First, let's check to see if we should be creating MIDI 1 ports for this endpoint
+ // default action, if unspecified, is to create the ports
+ auto createMidi1PortsProp = deviceInfo.Properties().Lookup(STRING_PKEY_MIDI_CreateMidi1PortsForEndpoint);
+ if (createMidi1PortsProp)
+ {
+ auto createPorts = winrt::unbox_value(createMidi1PortsProp);
+
+ if (!createPorts)
+ {
+ // it's not an error condition to skip creating MIDI 1 ports for an endpoint
+ return S_OK;
+ }
+ }
+
+
// get the current function block information from the UMP SWD, if present, else fall back to
// GTB if that is present. If neither, then there's nothing more to do.
hrTemp = GetFunctionBlockPortInfo(umpMidiPort->DeviceInterfaceId.get(), deviceInfo, portInfo);
@@ -2475,12 +2493,13 @@ CMidiDeviceManager::SyncMidi1Ports(
DEVPROP_TYPE_BYTE, (ULONG)(sizeof(BYTE)), (PVOID)(&(nativeDataFormat)) });
}
- // KSA uses port names for the GTB names. But the new MIDI 2.0 UMP driver
- // does the same for MIDI 1.0 devices. So we check both KSA and KS here.
- if (transportId == winrt::guid(__uuidof(Midi2KSAggregateTransport)) ||
- (transportId == winrt::guid(__uuidof(Midi2KSTransport)) && WI_IsFlagSet(nativeDataFormat, MidiDataFormats::MidiDataFormats_ByteStream)))
+ //if (transportId == winrt::guid(__uuidof(Midi2KSAggregateTransport)) ||
+ // transportId == winrt::guid(__uuidof(Midi2LoopbackMidiTransport)) ||
+ // (transportId == winrt::guid(__uuidof(Midi2KSTransport)) && WI_IsFlagSet(nativeDataFormat, MidiDataFormats::MidiDataFormats_ByteStream)))
+ prop = deviceInfo.Properties().Lookup(STRING_PKEY_MIDI_UseGroupTerminalBlocksForExactMidi1PortNames);
+ if (prop)
{
- usePortInfoName = true;
+ usePortInfoName = winrt::unbox_value(prop);
}
prop = deviceInfo.Properties().Lookup(STRING_PKEY_MIDI_CustomEndpointName);
diff --git a/src/api/Service/Exe/MidiSrv.vcxproj b/src/api/Service/Exe/MidiSrv.vcxproj
index 48b86c019..3c8b201b0 100644
--- a/src/api/Service/Exe/MidiSrv.vcxproj
+++ b/src/api/Service/Exe/MidiSrv.vcxproj
@@ -161,12 +161,12 @@
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.loopbackmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.loopbackmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
@@ -205,12 +205,12 @@
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.loopbackmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.loopbackmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
@@ -249,12 +249,12 @@
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.loopbackmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.loopbackmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
@@ -297,12 +297,12 @@
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.loopbackmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksaggregatetransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.kstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticstransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.loopbackmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmiditransport\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\MidiSrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.JitterReductionTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.EndpointMetadataListenerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\Midi2.UmpProtocolDownscalerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\TransportUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvtransport\$(Platform)\$(Configuration)
diff --git a/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp b/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp
index 1ca930743..1a4ce00eb 100644
--- a/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp
+++ b/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp
@@ -164,6 +164,7 @@ CMidi2KSAggregateMidiEndpointManager::CreateMidiUmpEndpoint(
TraceLoggingWideString(masterEndpointDefinition.EndpointName.c_str(), "name")
);
+ DEVPROP_BOOLEAN devPropTrue = DEVPROP_TRUE;
// we require at least one valid pin
RETURN_HR_IF(E_INVALIDARG, masterEndpointDefinition.MidiPins.size() < 1);
@@ -403,6 +404,9 @@ CMidi2KSAggregateMidiEndpointManager::CreateMidiUmpEndpoint(
{
interfaceDevProperties.push_back({ { PKEY_MIDI_GroupTerminalBlocks, DEVPROP_STORE_SYSTEM, nullptr },
DEVPROP_TYPE_BINARY, (ULONG)groupTerminalBlockData.size(), (PVOID)groupTerminalBlockData.data()});
+
+ interfaceDevProperties.push_back({ { PKEY_MIDI_UseGroupTerminalBlocksForExactMidi1PortNames, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, (ULONG)sizeof(devPropTrue), (PVOID)&devPropTrue });
}
else
{
diff --git a/src/api/Transport/KSTransport/Midi2.KSMidiEndpointManager.cpp b/src/api/Transport/KSTransport/Midi2.KSMidiEndpointManager.cpp
index 81b8e04c9..636ca05db 100644
--- a/src/api/Transport/KSTransport/Midi2.KSMidiEndpointManager.cpp
+++ b/src/api/Transport/KSTransport/Midi2.KSMidiEndpointManager.cpp
@@ -87,6 +87,9 @@ CMidi2KSMidiEndpointManager::OnDeviceAdded(
TraceLoggingWideString(device.Id().c_str(), "device id")
);
+ DEVPROP_BOOLEAN devPropTrue = DEVPROP_TRUE;
+ DEVPROP_BOOLEAN devPropFalse = DEVPROP_FALSE;
+
wil::unique_handle hFilter;
std::wstring deviceName;
std::wstring deviceId;
@@ -542,6 +545,7 @@ CMidi2KSMidiEndpointManager::OnDeviceAdded(
capabilities |= MidiEndpointCapabilities_SupportsMultiClient;
capabilities |= MidiEndpointCapabilities_GenerateIncomingTimestamps;
+
interfaceDevProperties.push_back({ {DEVPKEY_KsMidiPort_KsFilterInterfaceId, DEVPROP_STORE_SYSTEM, nullptr},
DEVPROP_TYPE_STRING, static_cast((MidiPin->Id.length() + 1) * sizeof(WCHAR)), (PVOID)MidiPin->Id.c_str() });
interfaceDevProperties.push_back({ {DEVPKEY_KsTransport, DEVPROP_STORE_SYSTEM, nullptr },
@@ -606,6 +610,26 @@ CMidi2KSMidiEndpointManager::OnDeviceAdded(
{
interfaceDevProperties.push_back({ { PKEY_MIDI_GroupTerminalBlocks, DEVPROP_STORE_SYSTEM, nullptr },
DEVPROP_TYPE_BINARY, MidiPin->GroupTerminalBlockDataSize, (PVOID)MidiPin->GroupTerminalBlockData.get() });
+
+
+ if (MidiPin->NativeDataFormat == KSDATAFORMAT_SUBTYPE_UNIVERSALMIDIPACKET)
+ {
+ // if a UMP device, we don't necessarily use the GTBs to name MIDI 1 ports
+ interfaceDevProperties.push_back({ { PKEY_MIDI_UseGroupTerminalBlocksForExactMidi1PortNames, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(devPropFalse)), (PVOID) & (devPropFalse) });
+ }
+ else if (MidiPin->NativeDataFormat == KSDATAFORMAT_SUBTYPE_MIDI)
+ {
+ // for a native MIDI 1 device, the driver provides a MIDI 1 port name in the GTB
+ interfaceDevProperties.push_back({ { PKEY_MIDI_UseGroupTerminalBlocksForExactMidi1PortNames, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(devPropTrue)), (PVOID) & (devPropTrue) });
+ }
+ }
+ else
+ {
+ interfaceDevProperties.push_back({ { PKEY_MIDI_UseGroupTerminalBlocksForExactMidi1PortNames, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(devPropFalse)), (PVOID) & (devPropFalse) });
+
}
// Bidirectional uses a different property for the in and out pins, since we currently require two separate ones.
diff --git a/src/api/Transport/LoopbackMidiTransport/Midi2.LoopbackMidiEndpointManager.cpp b/src/api/Transport/LoopbackMidiTransport/Midi2.LoopbackMidiEndpointManager.cpp
index d7c4ab84d..3d9dd2cbc 100644
--- a/src/api/Transport/LoopbackMidiTransport/Midi2.LoopbackMidiEndpointManager.cpp
+++ b/src/api/Transport/LoopbackMidiTransport/Midi2.LoopbackMidiEndpointManager.cpp
@@ -236,7 +236,7 @@ CMidi2LoopbackMidiEndpointManager::CreateSingleEndpoint(
std::wstring transportCode(TRANSPORT_CODE);
- //DEVPROP_BOOLEAN devPropTrue = DEVPROP_TRUE;
+ DEVPROP_BOOLEAN devPropTrue = DEVPROP_TRUE;
// DEVPROP_BOOLEAN devPropFalse = DEVPROP_FALSE;
std::wstring endpointName = definition->EndpointName;
@@ -320,24 +320,37 @@ CMidi2LoopbackMidiEndpointManager::CreateSingleEndpoint(
std::vector blocks{ };
- internal::GroupTerminalBlockInternal gtb;
- gtb.Number = 1; // gtb indexes start at 1
- gtb.GroupCount = 1; // todo: we could get this from properties
- gtb.FirstGroupIndex = 0; // group indexes start at 0
- gtb.Protocol = 0x11; // 0x11 = MIDI 2.0
- gtb.Direction = MIDI_GROUP_TERMINAL_BLOCK_BIDIRECTIONAL;
- gtb.Name = L"IO"; // todo: we could get this from properties so folks can control the port name
+ internal::GroupTerminalBlockInternal gtb1;
+ gtb1.Number = 1; // gtb indexes start at 1
+ gtb1.GroupCount = 1; // todo: we could get this from properties
+ gtb1.FirstGroupIndex = 0; // group indexes start at 0
+ gtb1.Protocol = 0x11; // 0x11 = MIDI 2.0
+ gtb1.Direction = MIDI_GROUP_TERMINAL_BLOCK_INPUT; // MIDI Out from user's perspective
+ gtb1.Name = friendlyName + L" Out"; // todo: get this from properties so folks can control the port name
+ blocks.push_back(gtb1);
+
+ internal::GroupTerminalBlockInternal gtb2;
+ gtb2.Number = 1; // gtb indexes start at 1
+ gtb2.GroupCount = 1; // todo: we could get this from properties
+ gtb2.FirstGroupIndex = 0; // group indexes start at 0
+ gtb2.Protocol = 0x11; // 0x11 = MIDI 2.0
+ gtb2.Direction = MIDI_GROUP_TERMINAL_BLOCK_OUTPUT; // MIDI In from user's perspective
+ gtb2.Name = friendlyName + L" In"; // todo: get this from properties so folks can control the port name
+ blocks.push_back(gtb2);
+
- blocks.push_back(gtb);
-
std::vector groupTerminalBlockData;
if (internal::WriteGroupTerminalBlocksToPropertyDataPointer(blocks, groupTerminalBlockData))
{
interfaceDeviceProperties.push_back({ { PKEY_MIDI_GroupTerminalBlocks, DEVPROP_STORE_SYSTEM, nullptr },
DEVPROP_TYPE_BINARY, (ULONG)groupTerminalBlockData.size(), (PVOID)groupTerminalBlockData.data() });
- }
+ interfaceDeviceProperties.push_back({ { PKEY_MIDI_UseGroupTerminalBlocksForExactMidi1PortNames, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, (ULONG)sizeof(devPropTrue), (PVOID)&devPropTrue});
+ }
+
+
RETURN_IF_FAILED(m_MidiDeviceManager->ActivateEndpoint(
(PCWSTR)m_parentDeviceId.c_str(), // parent instance Id
definition->UMPOnly, // UMP-only. When set to false, WinMM MIDI 1.0 ports are created
diff --git a/src/app-sdk/mididiag/mididiag.vcxproj b/src/app-sdk/mididiag/mididiag.vcxproj
index 96c5aeb7b..d92dc095a 100644
--- a/src/app-sdk/mididiag/mididiag.vcxproj
+++ b/src/app-sdk/mididiag/mididiag.vcxproj
@@ -2,7 +2,7 @@
- Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250213-52
+ Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250215-2126
true
true
true
diff --git a/src/app-sdk/mididiag/packages.config b/src/app-sdk/mididiag/packages.config
index d50f79bbc..b0a52d034 100644
--- a/src/app-sdk/mididiag/packages.config
+++ b/src/app-sdk/mididiag/packages.config
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj
index 02ced8544..f4cf395b1 100644
--- a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj
+++ b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj
@@ -2,7 +2,7 @@
- Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250213-52
+ Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250215-2126
true
true
true
diff --git a/src/app-sdk/midimdnsinfo/packages.config b/src/app-sdk/midimdnsinfo/packages.config
index d50f79bbc..b0a52d034 100644
--- a/src/app-sdk/midimdnsinfo/packages.config
+++ b/src/app-sdk/midimdnsinfo/packages.config
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj
index ed6d716f2..4997aed1a 100644
--- a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj
+++ b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj
@@ -2,7 +2,7 @@
- Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250213-52
+ Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250215-2126
true
true
true
diff --git a/src/app-sdk/midiusbinfo/packages.config b/src/app-sdk/midiusbinfo/packages.config
index d50f79bbc..b0a52d034 100644
--- a/src/app-sdk/midiusbinfo/packages.config
+++ b/src/app-sdk/midiusbinfo/packages.config
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/src/app-sdk/sdk-runtime-installer/settings-package/WindowsMidiServicesSettings.wxs b/src/app-sdk/sdk-runtime-installer/settings-package/WindowsMidiServicesSettings.wxs
index 6668e4ed6..ef248575f 100644
--- a/src/app-sdk/sdk-runtime-installer/settings-package/WindowsMidiServicesSettings.wxs
+++ b/src/app-sdk/sdk-runtime-installer/settings-package/WindowsMidiServicesSettings.wxs
@@ -59,6 +59,7 @@
Guid="f4f2b375-347b-413c-b81d-e900f392f786">
+
diff --git a/src/app-sdk/winrt/MidiLoopbackEndpointCreationConfig.idl b/src/app-sdk/winrt/MidiLoopbackEndpointCreationConfig.idl
index 7f647dc85..3056d3bbf 100644
--- a/src/app-sdk/winrt/MidiLoopbackEndpointCreationConfig.idl
+++ b/src/app-sdk/winrt/MidiLoopbackEndpointCreationConfig.idl
@@ -13,6 +13,7 @@ import "MidiLoopbackEndpointDefinition.idl";
namespace Microsoft.Windows.Devices.Midi2.Endpoints.Loopback
{
+ [experimental]
[default_interface]
runtimeclass MidiLoopbackEndpointCreationConfig : Microsoft.Windows.Devices.Midi2.ServiceConfig.IMidiServiceTransportPluginConfig
{
diff --git a/src/app-sdk/winrt/MidiLoopbackEndpointManager.cpp b/src/app-sdk/winrt/MidiLoopbackEndpointManager.cpp
index d2d541a88..2c8c8d3a5 100644
--- a/src/app-sdk/winrt/MidiLoopbackEndpointManager.cpp
+++ b/src/app-sdk/winrt/MidiLoopbackEndpointManager.cpp
@@ -111,4 +111,95 @@ namespace winrt::Microsoft::Windows::Devices::Midi2::Endpoints::Loopback::implem
return result;
}
+
+ _Use_decl_annotations_
+ midi2::MidiEndpointDeviceInformation MidiLoopbackEndpointManager::GetAssociatedLoopbackEndpoint(
+ midi2::MidiEndpointDeviceInformation const& loopbackEndpoint
+ )
+ {
+ auto domain = midi2::MidiEndpointDeviceInformation::FindAll();
+
+ return GetAssociatedLoopbackEndpoint(loopbackEndpoint, domain);
+ }
+
+
+ _Use_decl_annotations_
+ midi2::MidiEndpointDeviceInformation MidiLoopbackEndpointManager::GetAssociatedLoopbackEndpointForId(
+ winrt::hstring loopbackEndpointId
+ )
+ {
+ auto cleanId = internal::NormalizeEndpointInterfaceIdHStringCopy(loopbackEndpointId);
+
+ auto info = midi2::MidiEndpointDeviceInformation::CreateFromEndpointDeviceId(cleanId);
+
+ return GetAssociatedLoopbackEndpoint(info);
+ }
+
+
+ _Use_decl_annotations_
+ midi2::MidiEndpointDeviceInformation MidiLoopbackEndpointManager::GetAssociatedLoopbackEndpoint(
+ midi2::MidiEndpointDeviceInformation const& loopbackEndpoint,
+ collections::IIterable endpointsToSearch)
+ {
+ if (loopbackEndpoint == nullptr)
+ {
+ return nullptr;
+ }
+
+ if (endpointsToSearch == nullptr)
+ {
+ return nullptr;
+ }
+
+ auto transportId = loopbackEndpoint.GetTransportSuppliedInfo().TransportId;
+
+ if (transportId != TransportId())
+ {
+ // not a loopback endpoint
+ return nullptr;
+ }
+
+ // get the endpoint's association id
+
+ if (loopbackEndpoint.Properties().HasKey(STRING_PKEY_MIDI_VirtualMidiEndpointAssociator) &&
+ loopbackEndpoint.Properties().Lookup(STRING_PKEY_MIDI_VirtualMidiEndpointAssociator) != nullptr)
+ {
+ auto associator = winrt::unbox_value(loopbackEndpoint.Properties().Lookup(STRING_PKEY_MIDI_VirtualMidiEndpointAssociator));
+
+ // find the other endpoint that has this associator
+ // this is wasteful to get everything and then iterate, but there's
+ // no AQS way to search using our custom DEVPKEY properties
+
+ winrt::hstring query{ MIDI_ENDPOINT_DEVICE_AQS_FILTER };
+
+ for (auto const& ep : endpointsToSearch)
+ {
+ auto id = internal::NormalizeEndpointInterfaceIdHStringCopy(ep.EndpointDeviceId());
+
+ // don't process the endpoint that was passed in, of course
+ if (id != loopbackEndpoint.EndpointDeviceId())
+ {
+ if (ep.Properties().HasKey(STRING_PKEY_MIDI_VirtualMidiEndpointAssociator) &&
+ ep.Properties().Lookup(STRING_PKEY_MIDI_VirtualMidiEndpointAssociator) != nullptr)
+ {
+ auto thisAssociator = winrt::unbox_value(loopbackEndpoint.Properties().Lookup(STRING_PKEY_MIDI_VirtualMidiEndpointAssociator));
+
+ if (!thisAssociator.empty())
+ {
+ // return the endpoint if it has the matching association id
+ if (thisAssociator == associator)
+ {
+ // create the endpoint
+ return MidiEndpointDeviceInformation::CreateFromEndpointDeviceId(id);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return nullptr;
+
+ }
+
}
diff --git a/src/app-sdk/winrt/MidiLoopbackEndpointManager.h b/src/app-sdk/winrt/MidiLoopbackEndpointManager.h
index 5f658a3a5..52ee7299e 100644
--- a/src/app-sdk/winrt/MidiLoopbackEndpointManager.h
+++ b/src/app-sdk/winrt/MidiLoopbackEndpointManager.h
@@ -25,6 +25,17 @@ namespace winrt::Microsoft::Windows::Devices::Midi2::Endpoints::Loopback::implem
static bool RemoveTransientLoopbackEndpoints(
_In_ loop::MidiLoopbackEndpointRemovalConfig deletionConfig);
+
+ static midi2::MidiEndpointDeviceInformation GetAssociatedLoopbackEndpointForId(
+ _In_ winrt::hstring loopbackEndpointId);
+
+ static midi2::MidiEndpointDeviceInformation GetAssociatedLoopbackEndpoint(
+ _In_ midi2::MidiEndpointDeviceInformation const& loopbackEndpoint,
+ _In_ collections::IIterable endpointsToSearch);
+
+ static midi2::MidiEndpointDeviceInformation GetAssociatedLoopbackEndpoint(
+ _In_ midi2::MidiEndpointDeviceInformation const& loopbackEndpoint);
+
};
}
namespace winrt::Microsoft::Windows::Devices::Midi2::Endpoints::Loopback::factory_implementation
diff --git a/src/app-sdk/winrt/MidiLoopbackEndpointManager.idl b/src/app-sdk/winrt/MidiLoopbackEndpointManager.idl
index 8c0d62adc..eadd7a739 100644
--- a/src/app-sdk/winrt/MidiLoopbackEndpointManager.idl
+++ b/src/app-sdk/winrt/MidiLoopbackEndpointManager.idl
@@ -11,8 +11,11 @@ import "MidiLoopbackEndpointCreationResult.idl";
import "MidiLoopbackEndpointCreationConfig.idl";
import "MidiLoopbackEndpointRemovalConfig.idl";
+import "MidiEndpointDeviceInformation.idl";
+
namespace Microsoft.Windows.Devices.Midi2.Endpoints.Loopback
{
+ [experimental]
static runtimeclass MidiLoopbackEndpointManager
{
static Boolean IsTransportAvailable{ get; };
@@ -24,6 +27,16 @@ namespace Microsoft.Windows.Devices.Midi2.Endpoints.Loopback
static Boolean RemoveTransientLoopbackEndpoints(
MidiLoopbackEndpointRemovalConfig removalConfig);
+
+ static Microsoft.Windows.Devices.Midi2.MidiEndpointDeviceInformation GetAssociatedLoopbackEndpointForId(
+ String loopbackEndpointId);
+
+ static Microsoft.Windows.Devices.Midi2.MidiEndpointDeviceInformation GetAssociatedLoopbackEndpoint(
+ Microsoft.Windows.Devices.Midi2.MidiEndpointDeviceInformation loopbackEndpoint,
+ IIterable endpointsToSearch);
+
+ static Microsoft.Windows.Devices.Midi2.MidiEndpointDeviceInformation GetAssociatedLoopbackEndpoint(
+ Microsoft.Windows.Devices.Midi2.MidiEndpointDeviceInformation loopbackEndpoint);
}
}
diff --git a/src/app-sdk/winrt/MidiLoopbackEndpointRemovalConfig.idl b/src/app-sdk/winrt/MidiLoopbackEndpointRemovalConfig.idl
index 87d054611..720bd2c36 100644
--- a/src/app-sdk/winrt/MidiLoopbackEndpointRemovalConfig.idl
+++ b/src/app-sdk/winrt/MidiLoopbackEndpointRemovalConfig.idl
@@ -14,6 +14,7 @@ import "IMidiServiceTransportPluginConfig.idl";
namespace Microsoft.Windows.Devices.Midi2.Endpoints.Loopback
{
+ [experimental]
[default_interface]
runtimeclass MidiLoopbackEndpointRemovalConfig : Microsoft.Windows.Devices.Midi2.ServiceConfig.IMidiServiceTransportPluginConfig
{
diff --git a/src/oob-setup/api-package/WindowsMidiServices.wxs b/src/oob-setup/api-package/WindowsMidiServices.wxs
index dfff9bfc1..4d4a326cb 100644
--- a/src/oob-setup/api-package/WindowsMidiServices.wxs
+++ b/src/oob-setup/api-package/WindowsMidiServices.wxs
@@ -84,6 +84,7 @@
+
-
+
@@ -137,7 +138,8 @@
-
+
+
-
+
-
+
diff --git a/src/user-tools/midi-console/Midi/Commands/Service/ServiceRestartCommand.cs b/src/user-tools/midi-console/Midi/Commands/Service/ServiceRestartCommand.cs
index f5c009e7a..0d7d8e90c 100644
--- a/src/user-tools/midi-console/Midi/Commands/Service/ServiceRestartCommand.cs
+++ b/src/user-tools/midi-console/Midi/Commands/Service/ServiceRestartCommand.cs
@@ -30,7 +30,7 @@ public override int Execute(CommandContext context, Settings settings)
if (!UserHelper.CurrentUserHasAdminRights())
{
AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError("This command must be run as Administrator."));
- return (int)MidiConsoleReturnCode.ErrorGeneralFailure;
+ return (int)MidiConsoleReturnCode.ErrorInsufficientPermissions;
}
diff --git a/src/user-tools/midi-console/Midi/Commands/Service/ServiceSetAutoStart.cs b/src/user-tools/midi-console/Midi/Commands/Service/ServiceSetAutoStart.cs
new file mode 100644
index 000000000..90dedc724
--- /dev/null
+++ b/src/user-tools/midi-console/Midi/Commands/Service/ServiceSetAutoStart.cs
@@ -0,0 +1,132 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of Windows MIDI Services and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://aka.ms/midi
+// ============================================================================
+
+using System.Runtime.InteropServices;
+using System.ServiceProcess;
+
+namespace Microsoft.Midi.ConsoleApp
+{
+ internal class ServiceSetAutoStart : Command
+ {
+ [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern Boolean ChangeServiceConfig(
+ IntPtr hService,
+ UInt32 nServiceType,
+ UInt32 nStartType,
+ UInt32 nErrorControl,
+ String lpBinaryPathName,
+ String lpLoadOrderGroup,
+ IntPtr lpdwTagId,
+ [In] char[] lpDependencies,
+ String lpServiceStartName,
+ String lpPassword,
+ String lpDisplayName);
+
+ [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ static extern IntPtr OpenService(
+ IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
+
+ [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern IntPtr OpenSCManager(
+ string machineName, string databaseName, uint dwAccess);
+
+ [DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
+ public static extern int CloseServiceHandle(IntPtr hSCObject);
+
+ private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
+ private const uint SERVICE_QUERY_CONFIG = 0x00000001;
+ private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
+ private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;
+
+
+ public sealed class Settings : CommandSettings
+ {
+ // TODO: Consider changing this to a command argument instead of an option
+ [LocalizedDescription("ParameterServiceRestart")]
+ [CommandOption("-r|--restart-service|--restart")]
+ [DefaultValue(false)]
+ public bool Restart { get; set; }
+
+ }
+
+ public override int Execute(CommandContext context, Settings settings)
+ {
+ // this command requires admin
+
+ if (!UserHelper.CurrentUserHasAdminRights())
+ {
+ AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError("This command must be run as Administrator."));
+ return (int)MidiConsoleReturnCode.ErrorInsufficientPermissions;
+ }
+
+ AnsiConsole.MarkupLine("Setting service to auto-start...");
+
+ var scManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
+ if (scManagerHandle == IntPtr.Zero)
+ {
+ //throw new ExternalException("Open Service Manager Error");
+ AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError("Error opening service manager."));
+ return (int)MidiConsoleReturnCode.ErrorGeneralFailure;
+ }
+
+ var serviceHandle = OpenService(
+ scManagerHandle,
+ MidiServiceHelper.GetServiceName(),
+ SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
+
+ if (serviceHandle == IntPtr.Zero)
+ {
+ AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError("Error opening service."));
+ return (int)MidiConsoleReturnCode.ErrorGeneralFailure;
+ }
+
+ var result = ChangeServiceConfig(
+ serviceHandle,
+ SERVICE_NO_CHANGE,
+ (uint)ServiceStartMode.Automatic,
+ SERVICE_NO_CHANGE,
+ null,
+ null,
+ IntPtr.Zero,
+ null,
+ null,
+ null,
+ null);
+
+ if (!result)
+ {
+ int nError = Marshal.GetLastWin32Error();
+ var win32Exception = new Win32Exception(nError);
+
+ AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError("Could not change service start type: " + win32Exception.Message));
+
+ return (int)MidiConsoleReturnCode.ErrorGeneralFailure;
+ }
+
+ CloseServiceHandle(serviceHandle);
+ CloseServiceHandle(scManagerHandle);
+
+ var controller = MidiServiceHelper.GetServiceController();
+
+ if (settings.Restart)
+ {
+ AnsiConsole.MarkupLine("Stopping service.");
+ MidiServiceHelper.StopServiceWithConsoleStatusUpdate(controller);
+
+ AnsiConsole.MarkupLine("Restarting service.");
+ MidiServiceHelper.StartServiceWithConsoleStatusUpdate(controller);
+ }
+
+ AnsiConsole.MarkupLine("Succeeded");
+
+
+ return (int)MidiConsoleReturnCode.Success;
+ }
+
+ }
+}
diff --git a/src/user-tools/midi-console/Midi/Commands/Service/ServiceStartCommand.cs b/src/user-tools/midi-console/Midi/Commands/Service/ServiceStartCommand.cs
index 5cff42c78..d88537894 100644
--- a/src/user-tools/midi-console/Midi/Commands/Service/ServiceStartCommand.cs
+++ b/src/user-tools/midi-console/Midi/Commands/Service/ServiceStartCommand.cs
@@ -28,7 +28,7 @@ public override int Execute(CommandContext context, Settings settings)
if (!UserHelper.CurrentUserHasAdminRights())
{
AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError("This command must be run as Administrator."));
- return (int)MidiConsoleReturnCode.ErrorGeneralFailure;
+ return (int)MidiConsoleReturnCode.ErrorInsufficientPermissions;
}
diff --git a/src/user-tools/midi-console/Midi/Commands/Service/ServiceStopCommand.cs b/src/user-tools/midi-console/Midi/Commands/Service/ServiceStopCommand.cs
index df18ce191..fef770b88 100644
--- a/src/user-tools/midi-console/Midi/Commands/Service/ServiceStopCommand.cs
+++ b/src/user-tools/midi-console/Midi/Commands/Service/ServiceStopCommand.cs
@@ -29,7 +29,7 @@ public override int Execute(CommandContext context, Settings settings)
if (!UserHelper.CurrentUserHasAdminRights())
{
AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError("This command must be run as Administrator."));
- return (int)MidiConsoleReturnCode.ErrorGeneralFailure;
+ return (int)MidiConsoleReturnCode.ErrorInsufficientPermissions;
}
diff --git a/src/user-tools/midi-console/Midi/MidiConsoleReturnCode.cs b/src/user-tools/midi-console/Midi/MidiConsoleReturnCode.cs
index e798343b9..fbcc93108 100644
--- a/src/user-tools/midi-console/Midi/MidiConsoleReturnCode.cs
+++ b/src/user-tools/midi-console/Midi/MidiConsoleReturnCode.cs
@@ -27,6 +27,8 @@ public enum MidiConsoleReturnCode : int
ErrorServiceNotAvailable = 800,
+ ErrorInsufficientPermissions = 850,
+
ErrorNotImplemented = 998,
ErrorGeneralFailure = 999
}
diff --git a/src/user-tools/midi-console/Midi/Program.cs b/src/user-tools/midi-console/Midi/Program.cs
index 04d1c5c23..fd86a0c32 100644
--- a/src/user-tools/midi-console/Midi/Program.cs
+++ b/src/user-tools/midi-console/Midi/Program.cs
@@ -206,6 +206,14 @@
.WithExample("service", "stop")
;
+ service.AddCommand("set-auto-start")
+ .WithDescription(Strings.ServiceSetAutoDelayedStartDescription)
+ .WithExample("service", "set-auto-start", "--restart")
+ ;
+
+
+
+
}).WithAlias("svc");
diff --git a/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs b/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs
index 4a743c871..5e4d9b908 100644
--- a/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs
+++ b/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs
@@ -1365,6 +1365,15 @@ internal static string ParameterServicePingVerbose {
}
}
+ ///
+ /// Looks up a localized string similar to Restart the service when complete.
+ ///
+ internal static string ParameterServiceRestart {
+ get {
+ return ResourceManager.GetString("ParameterServiceRestart", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Report additional details about the service.
///
@@ -2315,6 +2324,15 @@ internal static string SendMessageSessionNameSuffix {
}
}
+ ///
+ /// Looks up a localized string similar to Set the MIDI Service to start shortly after Windows starts, to avoid connection delays with the first connection made by an app..
+ ///
+ internal static string ServiceSetAutoDelayedStartDescription {
+ get {
+ return ResourceManager.GetString("ServiceSetAutoDelayedStartDescription", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Creating session and opening endpoint....
///
diff --git a/src/user-tools/midi-console/Midi/Resources/Strings.resx b/src/user-tools/midi-console/Midi/Resources/Strings.resx
index d682049df..56287ed5d 100644
--- a/src/user-tools/midi-console/Midi/Resources/Strings.resx
+++ b/src/user-tools/midi-console/Midi/Resources/Strings.resx
@@ -911,4 +911,10 @@ Timestamp
Set to true to use MIDI 2.0 protocol messages (type 4) instead of MIDI 1.0 protocol (type 2)
+
+ Set the MIDI Service to start shortly after Windows starts, to avoid connection delays with the first connection made by an app.
+
+
+ Restart the service when complete
+
\ No newline at end of file
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml
index 22ae3ca62..36600dfe4 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml
@@ -1,5 +1,7 @@
@@ -13,6 +15,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml.cs
index 5abdbc997..b98859c52 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml.cs
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml.cs
@@ -10,6 +10,7 @@
using Microsoft.Midi.Settings.ViewModels;
using Microsoft.Midi.Settings.Views;
using Microsoft.UI.Xaml;
+using System.Runtime.InteropServices;
using Windows.UI.Popups;
namespace Microsoft.Midi.Settings;
@@ -71,6 +72,12 @@ public App()
services.AddSingleton();
services.AddSingleton();
+ // MIDI Services
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+
+
// Views and ViewModels
services.AddTransient();
services.AddTransient();
@@ -147,6 +154,9 @@ public App()
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+
// Configuration
services.Configure(context.Configuration.GetSection(nameof(LocalSettingsOptions)));
}).
@@ -169,6 +179,14 @@ private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledEx
public MidiDesktopAppSdkInitializer? MidiInitializer => _midiInitializer;
+
+ [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ static extern int MessageBox(
+ IntPtr hWnd,
+ string lpText,
+ string lpCaption,
+ int uType);
+
protected async override void OnLaunched(LaunchActivatedEventArgs args)
{
base.OnLaunched(args);
@@ -179,5 +197,15 @@ protected async override void OnLaunched(LaunchActivatedEventArgs args)
await App.GetService().ActivateAsync(args);
}
+ else
+ {
+ // This dialog does not work here. Need either a low-level non-WinUI dialog to show, or use another approach
+ //var dlg = new MessageDialog("Unable to initialize Windows MIDI Services SDK Runtime. Is it installed?");
+ //await dlg.ShowAsync();
+
+ MessageBox((IntPtr)0, "Unable to initialize Windows MIDI Services SDK Runtime. Is it installed?", "Error Starting Settings App", 0);
+
+ Exit();
+ }
}
}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Assets/LoopbackDiagram.svg b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Assets/LoopbackDiagram.svg
new file mode 100644
index 000000000..da14b13ee
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Assets/LoopbackDiagram.svg
@@ -0,0 +1,27 @@
+
+
+
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Assets/SVG Assets.afdesign b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Assets/SVG Assets.afdesign
new file mode 100644
index 000000000..fd943400c
Binary files /dev/null and b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Assets/SVG Assets.afdesign differ
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiConfigFileService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiConfigFileService.cs
new file mode 100644
index 000000000..e2d542b2a
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiConfigFileService.cs
@@ -0,0 +1,48 @@
+namespace Microsoft.Midi.Settings.Contracts.Services;
+
+
+
+public class MidiConfigFileHeader
+{
+ public string Comment { get; set; }
+ public string Name { get; set; }
+ public string Product { get; set; }
+ public double FileVersion { get; set; }
+}
+
+public interface IMidiConfigFile
+{
+ MidiConfigFileHeader? Header { get; }
+
+ string FileName { get; }
+ bool Load();
+
+
+ bool StoreLoopbackEndpointPair(Microsoft.Windows.Devices.Midi2.Endpoints.Loopback.MidiLoopbackEndpointCreationConfig creationConfig);
+
+
+}
+
+
+public interface IMidiConfigFileService
+{
+ bool IsConfigFileActive { get; }
+
+ IMidiConfigFile? CurrentConfig { get; }
+
+ string GetConfigFilesLocation();
+
+ string GetDefaultConfigName();
+
+ string CleanupConfigName(string configName);
+
+ bool ConfigFileExists(string configFileName);
+
+ bool CreateNewConfigFile(string configName, string configFileName);
+
+ string BuildConfigLocalFileNameFromConfigName(string configName);
+
+ bool UpdateRegistryCurrentConfigFile(string configFileName);
+
+
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiDefaultsService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiDefaultsService.cs
new file mode 100644
index 000000000..b2cd93b08
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiDefaultsService.cs
@@ -0,0 +1,18 @@
+using Microsoft.Windows.Devices.Midi2.Endpoints.Loopback;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Midi.Settings.Contracts.Services
+{
+ public interface IMidiDefaultsService
+ {
+ string GetDefaultMidiConfigName();
+ string GetDefaultMidiConfigFileName();
+
+ MidiLoopbackEndpointCreationConfig GetDefaultLoopbackCreationConfig();
+ bool DoesDefaultLoopbackAlreadyExist();
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiTransportInfoService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiTransportInfoService.cs
new file mode 100644
index 000000000..dd4d9f8df
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiTransportInfoService.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Midi.Settings.Contracts.Services
+{
+ interface IMidiTransportInfoService
+ {
+ MidiServiceTransportPluginInfo GetTransportForCode(string transportCode);
+
+
+
+
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Microsoft.Midi.Settings.csproj b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Microsoft.Midi.Settings.csproj
index c4bcae7d2..e3591afeb 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Microsoft.Midi.Settings.csproj
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Microsoft.Midi.Settings.csproj
@@ -69,6 +69,7 @@
+
@@ -98,7 +99,7 @@
-
+
@@ -109,7 +110,7 @@
-
+
@@ -118,10 +119,10 @@
-
+
-
+
@@ -167,7 +168,7 @@
Never
-
+
MSBuild:Compile
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/APP/EndpointsAppPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/APP/EndpointsAppPage.xaml
index 01e827163..aadc47b6e 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/APP/EndpointsAppPage.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/APP/EndpointsAppPage.xaml
@@ -13,15 +13,6 @@
mc:Ignorable="d"
>
-
-
-
-
-
-
-
-
-
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/BLE10/EndpointsBle10Page.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/BLE10/EndpointsBle10Page.xaml
index e9a7eebe0..8f8ef2600 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/BLE10/EndpointsBle10Page.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/BLE10/EndpointsBle10Page.xaml
@@ -13,16 +13,6 @@
mc:Ignorable="d"
>
-
-
-
-
-
-
-
-
-
-
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DIAG/EndpointsDiagPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DIAG/EndpointsDiagPage.xaml
index 78e9ffa6f..deb58d53b 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DIAG/EndpointsDiagPage.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DIAG/EndpointsDiagPage.xaml
@@ -13,16 +13,6 @@
mc:Ignorable="d"
>
-
-
-
-
-
-
-
-
-
-
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DeviceDetailPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DeviceDetailPage.xaml
index ba8daad60..6cd409a9c 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DeviceDetailPage.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DeviceDetailPage.xaml
@@ -11,21 +11,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/KS/EndpointsKSPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/KS/EndpointsKSPage.xaml
index b4d0368cd..3caf819c3 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/KS/EndpointsKSPage.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/KS/EndpointsKSPage.xaml
@@ -16,15 +16,6 @@
mc:Ignorable="d"
>
-
-
-
-
-
-
-
-
-
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/KSA/EndpointsKsaPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/KSA/EndpointsKsaPage.xaml
index fb252784a..4af5bdcae 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/KSA/EndpointsKsaPage.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/KSA/EndpointsKsaPage.xaml
@@ -13,16 +13,6 @@
mc:Ignorable="d"
>
-
-
-
-
-
-
-
-
-
-
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/CreateLoopbackEndpointsWindow.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/CreateLoopbackEndpointsWindow.xaml
deleted file mode 100644
index a5fa2a822..000000000
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/CreateLoopbackEndpointsWindow.xaml
+++ /dev/null
@@ -1,145 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/CreateLoopbackEndpointsWindow.xaml.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/CreateLoopbackEndpointsWindow.xaml.cs
deleted file mode 100644
index 1322dada4..000000000
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/CreateLoopbackEndpointsWindow.xaml.cs
+++ /dev/null
@@ -1,139 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices.WindowsRuntime;
-using Windows.Foundation;
-using Windows.Foundation.Collections;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Controls.Primitives;
-using Microsoft.UI.Xaml.Data;
-using Microsoft.UI.Xaml.Input;
-using Microsoft.UI.Xaml.Media;
-using Microsoft.UI.Xaml.Navigation;
-using Microsoft.Midi.Settings.Services;
-using Microsoft.Windows.Devices.Midi2.Endpoints.Loopback;
-using System.Xml;
-
-// To learn more about WinUI, the WinUI project structure,
-// and more about our project templates, see: http://aka.ms/winui-project-info.
-
-namespace Microsoft.Midi.Settings
-{
- ///
- /// An empty window that can be used on its own or navigated to within a Frame.
- ///
- public sealed partial class CreateLoopbackEndpointsWindow : WinUIEx.WindowEx
- {
- public event EventHandler EndpointsCreated;
-
- public CreateLoopbackEndpointsWindow()
- {
- this.Title = "CreateLoopbackEndpointsWindowTitle".GetLocalized();
-
- // TODO: This shouldn't be hard-coded
- this.CenterOnScreen(650, 750);
-
- this.InitializeComponent();
-
- // this.ContentRoot.RequestedTheme = App.GetService().Theme;
- }
-
- private void CreateEndpointsButton_Click(object sender, RoutedEventArgs e)
- {
- var endpointA = new MidiLoopbackEndpointDefinition();
- var endpointB = new MidiLoopbackEndpointDefinition();
-
- // if endpoint A or B names are empty, do not close the window. display an error
-
- // if endpoint A or B unique ids are empty, do not close the window. display suggestion to generate them
- // todo: need to limit to alpha plus just a couple other characters, and only 32 in length
-
- // descriptions are optional
-
-
- endpointA.Name = EndpointAName.Text.Trim();
- endpointB.Name = EndpointBName.Text.Trim();
-
- endpointA.UniqueId = CleanupUniqueId(EndpointAUniqueId.Text);
- endpointB.UniqueId = CleanupUniqueId(EndpointBUniqueId.Text);
-
- EndpointAUniqueId.Text = endpointA.UniqueId;
- EndpointBUniqueId.Text = endpointB.UniqueId;
-
- endpointA.Description = EndpointADescription.Text.Trim();
- endpointB.Description = EndpointBDescription.Text.Trim();
-
- var associationId = GuidHelper.CreateNewGuid();
-
- var creationConfig = new MidiLoopbackEndpointCreationConfig(associationId, endpointA, endpointB);
-
- var result = MidiLoopbackEndpointManager.CreateTransientLoopbackEndpoints(creationConfig);
-
- // TODO: if that worked, and these are persistent, add to configuration file
-
- if (result.Success)
- {
- // close the window
- Close();
-
- if (EndpointsCreated != null)
- {
- EndpointsCreated(this, new EventArgs());
- }
- }
- else
- {
- // display failure message. Do not close window
-
- }
-
-
- }
-
- private string CleanupUniqueId(string source)
- {
- string result = string.Empty;
-
- foreach (char ch in source.ToCharArray())
- {
- if (char.IsAsciiLetterOrDigit(ch))
- {
- result += ch;
- }
- }
-
- return result;
- }
-
- private void CancelButton_Click(object sender, RoutedEventArgs e)
- {
- Close();
- }
-
- private void GenerateEndpointAUniqueIdButton_Click(object sender, RoutedEventArgs e)
- {
- var uniqueId = CleanupUniqueId(GuidHelper.CreateNewGuid().ToString());
-
- EndpointAUniqueId.Text = uniqueId;
-
- if (string.IsNullOrEmpty(EndpointBUniqueId.Text.Trim()))
- {
- EndpointBUniqueId.Text = uniqueId;
- }
- }
-
- private void GenerateEndpointBUniqueIdButton_Click(object sender, RoutedEventArgs e)
- {
- var uniqueId = CleanupUniqueId(GuidHelper.CreateNewGuid().ToString());
-
- EndpointBUniqueId.Text = uniqueId;
-
- if (string.IsNullOrEmpty(EndpointAUniqueId.Text.Trim()))
- {
- EndpointAUniqueId.Text = uniqueId;
- }
- }
- }
-}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopPage.xaml
index a154e30ae..34f73e137 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopPage.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopPage.xaml
@@ -13,16 +13,6 @@
mc:Ignorable="d"
>
-
-
-
-
-
-
-
-
-
-
@@ -46,8 +36,7 @@
-
+ Click="CreateNewLoopbackPair_Click"/>
@@ -65,6 +54,86 @@
+
+
+
+ 800
+ 800
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopPage.xaml.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopPage.xaml.cs
index b9fca0047..8b0f77757 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopPage.xaml.cs
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopPage.xaml.cs
@@ -59,6 +59,10 @@ private void MidiEndpointDeviceListItemControl_Loaded(object sender, RoutedEvent
((MidiEndpointDeviceListItemControl)sender).ViewDeviceDetailsCommand = ViewModel.ViewDeviceDetailsCommand;
}
+ private async void CreateNewLoopbackPair_Click(object sender, RoutedEventArgs e)
+ {
+ var result = await Dialog_CreateLoopbackEndpoints.ShowAsync();
+ }
}
}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopViewModel.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopViewModel.cs
index 7a922dcda..16850e3f6 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopViewModel.cs
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/LOOP/EndpointsLoopViewModel.cs
@@ -6,6 +6,7 @@
using Microsoft.Midi.Settings.Services;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Controls;
+using Microsoft.Windows.Devices.Midi2.Endpoints.Loopback;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -13,46 +14,176 @@
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
+using Windows.Foundation;
namespace Microsoft.Midi.Settings.ViewModels
{
public partial class EndpointsLoopViewModel : SingleTransportEndpointViewModelBase, INavigationAware
{
+ private IMidiConfigFileService m_midiConfigFileService;
+
public ICommand CreateLoopbackPairsCommand
{
get; private set;
}
- public EndpointsLoopViewModel(INavigationService navigationService) : base("LOOP", navigationService)
+ public bool IsConfigFileActive
{
- CreateLoopbackPairsCommand = new RelayCommand(
- () =>
+ get { return m_midiConfigFileService.IsConfigFileActive; }
+ }
+
+
+ private string m_newLoopbackEndpointAName;
+ public string NewLoopbackEndpointAName
+ {
+ get { return m_newLoopbackEndpointAName; }
+ set
+ {
+ m_newLoopbackEndpointAName = value.Trim();
+ UpdateValidState();
+ OnPropertyChanged();
+ }
+ }
+
+ private string m_newLoopbackEndpointBName;
+ public string NewLoopbackEndpointBName
+ {
+ get { return m_newLoopbackEndpointBName; }
+ set
+ {
+ m_newLoopbackEndpointBName = value.Trim();
+ UpdateValidState();
+ OnPropertyChanged();
+ }
+ }
+
+ public string NewUniqueIdentifier
+ {
+ get; set;
+ }
+
+ public bool NewLoopbackIsPersistent
+ {
+ get; set;
+ }
+
+ public bool NewLoopbackSettingsAreValid
+ {
+ get; internal set;
+ }
+
+ public string ValidationErrorMessage
+ {
+ get; internal set;
+ }
+
+
+ private void UpdateValidState()
+ {
+ // validate both names are not duplicates of each other
+
+ // validate both names are not duplicates of any other endpoint name
+
+ // validate the unique id is good
+
+ // validate the unique id isn't already in use with another loopback
+
+ // update NewLoopbackSettingsAreValid and ValidationErrorMessage if needed
+ }
+
+ private string CleanupUniqueId(string source)
+ {
+ string result = string.Empty;
+
+ foreach (char ch in source.ToCharArray())
+ {
+ if (char.IsAsciiLetterOrDigit(ch))
{
- System.Diagnostics.Debug.WriteLine("Create Loopback Pair Command exec");
+ result += ch;
+ }
+ }
- // show popup
+ return result;
+ }
- var window = new CreateLoopbackEndpointsWindow();
+ private void GenerateNewUniqueId()
+ {
+ var uniqueId = CleanupUniqueId(GuidHelper.CreateNewGuid().ToString());
- window.EndpointsCreated += Window_EndpointsCreated;
+ NewUniqueIdentifier = uniqueId;
+ }
- window.Show();
+ private void CreateNewLoopbackEndpoints()
+ {
+ var endpointA = new MidiLoopbackEndpointDefinition();
+ var endpointB = new MidiLoopbackEndpointDefinition();
- });
+ // if endpoint A or B names are empty, do not close. display an error
+
+ // if endpoint A or B unique ids are empty, do not close. display suggestion to generate them
+ // todo: need to limit to alpha plus just a couple other characters, and only 32 in length
+
+
+
+ endpointA.Name = NewLoopbackEndpointAName.Trim();
+ endpointB.Name = NewLoopbackEndpointBName.Trim();
+
+ endpointA.UniqueId = CleanupUniqueId(NewUniqueIdentifier);
+ endpointB.UniqueId = CleanupUniqueId(NewUniqueIdentifier);
+
+ // descriptions are optional
+ endpointA.Description = string.Empty;
+ endpointB.Description = string.Empty;
+
+ var associationId = GuidHelper.CreateNewGuid();
+
+ var creationConfig = new MidiLoopbackEndpointCreationConfig(associationId, endpointA, endpointB);
+
+ var result = MidiLoopbackEndpointManager.CreateTransientLoopbackEndpoints(creationConfig);
+
+ // TODO: if that worked, and these are persistent, add to configuration file
+
+ if (IsConfigFileActive && NewLoopbackIsPersistent)
+ {
+ if (result.Success)
+ {
+ if (m_midiConfigFileService.CurrentConfig != null)
+ {
+ m_midiConfigFileService.CurrentConfig.StoreLoopbackEndpointPair(creationConfig);
+
+ RefreshDeviceCollection();
+ }
+ else
+ {
+ // no config file
+ }
+ }
+ else
+ {
+ // update error information
+ }
+ }
}
- private void Window_EndpointsCreated(object? sender, EventArgs e)
+ public EndpointsLoopViewModel(INavigationService navigationService, IMidiConfigFileService midiConfigFileService) : base("LOOP", navigationService)
{
- RefreshDeviceCollection();
+ m_midiConfigFileService = midiConfigFileService;
- var s = sender as CreateLoopbackEndpointsWindow;
+ GenerateNewUniqueId();
- if (s != null)
+ if (m_midiConfigFileService.IsConfigFileActive)
{
- // unwire event
- s.EndpointsCreated -= Window_EndpointsCreated;
+ NewLoopbackIsPersistent = true;
}
+
+ CreateLoopbackPairsCommand = new RelayCommand(
+ () =>
+ {
+ CreateNewLoopbackEndpoints();
+
+ });
}
+
}
}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/NET2UDP/EndpointsNet2UdpPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/NET2UDP/EndpointsNet2UdpPage.xaml
index 9fcda4ff1..94f8a694a 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/NET2UDP/EndpointsNet2UdpPage.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/NET2UDP/EndpointsNet2UdpPage.xaml
@@ -13,16 +13,6 @@
mc:Ignorable="d"
>
-
-
-
-
-
-
-
-
-
-
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/First Run Experience/FirstRunExperiencePage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/First Run Experience/FirstRunExperiencePage.xaml
new file mode 100644
index 000000000..0cd44f6b6
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/First Run Experience/FirstRunExperiencePage.xaml
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+ 800
+ 800
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/First Run Experience/FirstRunExperiencePage.xaml.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/First Run Experience/FirstRunExperiencePage.xaml.cs
new file mode 100644
index 000000000..6b7aa590a
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/First Run Experience/FirstRunExperiencePage.xaml.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Controls.Primitives;
+using Microsoft.UI.Xaml.Data;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Navigation;
+using Microsoft.Midi.Settings.Contracts.ViewModels;
+using Microsoft.Midi.Settings.ViewModels;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace Microsoft.Midi.Settings.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class FirstRunExperiencePage : Page
+ {
+ public FirstRunExperienceViewModel ViewModel
+ {
+ get;
+ }
+
+
+ public FirstRunExperiencePage()
+ {
+ ViewModel = App.GetService();
+
+ Loaded += FirstRunExperiencePage_Loaded;
+
+ this.InitializeComponent();
+ }
+
+ private void FirstRunExperiencePage_Loaded(object sender, RoutedEventArgs e)
+ {
+ ShowFirstStep();
+ }
+
+ private async void ShowFirstStep()
+ {
+ var result = await Dialog_FirstRunSetup.ShowAsync();
+
+
+ }
+
+
+
+
+
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/First Run Experience/FirstRunExperienceViewModel.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/First Run Experience/FirstRunExperienceViewModel.cs
new file mode 100644
index 000000000..215907144
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/First Run Experience/FirstRunExperienceViewModel.cs
@@ -0,0 +1,185 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.Midi.Settings.Contracts.Services;
+using Microsoft.Midi.Settings.Contracts.ViewModels;
+using Microsoft.Midi.Settings.Services;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.Windows.Devices.Midi2.Endpoints.Loopback;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace Microsoft.Midi.Settings.ViewModels
+{
+ public class FirstRunExperienceViewModel : ObservableRecipient
+ {
+ private IMidiConfigFileService m_configFileService;
+ private IMidiDefaultsService m_defaultsService;
+ private INavigationService m_navigationService;
+
+ public string ConfigFileName
+ {
+ get; private set;
+ }
+
+ public string ConfigFilesLocation
+ {
+ get
+ {
+ return m_configFileService.GetConfigFilesLocation();
+ }
+ }
+
+ public bool ConfigFileExists
+ {
+ get
+ {
+ return m_configFileService.ConfigFileExists(ConfigFileName);
+ }
+ }
+
+
+ public ICommand CompleteFirstRunSetupCommand
+ {
+ get;
+ }
+
+
+ public bool CreateConfigurationFile
+ {
+ get;set;
+ }
+
+ public bool CreateDefaultLoopbackEndpoints
+ {
+ get; set;
+ }
+
+ public bool SetServiceToAutoStart
+ {
+ get; set;
+ }
+
+
+ public bool CanPersistChanges
+ {
+ get
+ {
+ return CreateConfigurationFile || m_configFileService.IsConfigFileActive;
+ }
+ }
+
+ private void CompleteFirstRunSetup()
+ {
+ if (CreateConfigurationFile)
+ {
+ string newConfigName = m_defaultsService.GetDefaultMidiConfigName(); ;
+ string newConfigFileName = m_defaultsService.GetDefaultMidiConfigFileName();
+
+ if (m_configFileService.CreateNewConfigFile(newConfigName, newConfigFileName))
+ {
+ m_configFileService.UpdateRegistryCurrentConfigFile(ConfigFileName);
+ }
+ else
+ {
+ // TODO: show an error
+ }
+ }
+
+
+ if (CreateDefaultLoopbackEndpoints)
+ {
+ var creationConfig = m_defaultsService.GetDefaultLoopbackCreationConfig();
+
+ var result = MidiLoopbackEndpointManager.CreateTransientLoopbackEndpoints(creationConfig);
+
+ if (result.Success)
+ {
+ m_configFileService.CurrentConfig.StoreLoopbackEndpointPair(creationConfig);
+
+ // todo: show results
+
+ }
+ else
+ {
+ // update error information
+ }
+ }
+
+ if (SetServiceToAutoStart)
+ {
+ ProcessStartInfo info = new ProcessStartInfo();
+ info.FileName = "cmd.exe";
+ info.UseShellExecute = true;
+ info.Verb = "runas";
+ info.Arguments = "/c \"midi service set-auto-start --restart & pause\"";
+
+ var proc = Process.Start(info);
+ if (proc != null)
+ {
+ // service updated
+
+ // TODO: Need to wait for completion and then check return code
+ }
+ }
+ else if (CreateConfigurationFile)
+ {
+ // we created a new config file, so we need to restart the service
+
+ ProcessStartInfo info = new ProcessStartInfo();
+ info.FileName = "cmd.exe";
+ info.UseShellExecute = true;
+ info.Verb = "runas";
+ info.Arguments = "/c \"midi service restart\"";
+
+ if (Process.Start(info) != null)
+ {
+ // service restarted
+
+ }
+ }
+
+ // set the flag saying the first-run setup is all done
+ // or maybe we just want to trigger this on having a valid config file
+ // that isn't the Default.midiconfig.json from earlier builds
+
+ m_navigationService.NavigateTo(typeof(HomeViewModel).FullName!);
+ }
+
+
+
+ public FirstRunExperienceViewModel(
+ INavigationService navigationService,
+ IMidiConfigFileService configService,
+ IMidiDefaultsService defaultsService)
+ {
+ m_navigationService = navigationService;
+ m_defaultsService = defaultsService;
+ m_configFileService = configService;
+
+ CreateConfigurationFile = true;
+ CreateDefaultLoopbackEndpoints = true;
+ SetServiceToAutoStart = true;
+
+
+ // todo: if that config exists, we use it and disable that control.
+
+ //UpdateConfigFileName();
+
+ CompleteFirstRunSetupCommand = new RelayCommand(() =>
+ {
+ CompleteFirstRunSetup();
+ });
+
+ }
+
+
+
+
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiConfigFileService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiConfigFileService.cs
new file mode 100644
index 000000000..6834cb1a0
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiConfigFileService.cs
@@ -0,0 +1,555 @@
+using Microsoft.Midi.Settings.Contracts.Services;
+using Microsoft.Midi.Settings.Models;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.VisualBasic;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Microsoft.Win32;
+
+using Windows.Data.Json;
+using Microsoft.Midi.Settings.Config; // we use the WinRT JSON libraries to be consistent with the service code
+
+namespace Microsoft.Midi.Settings.Services
+{
+ internal class MidiConfigConstants
+ {
+ // consider moving these to the SDK so it can use the same
+ // C++ headers / #defines the service is using
+
+ public const double CurrentFileVersion = 1.0;
+
+ internal class JsonKeys
+ {
+ public const string CommonComment = "_comment";
+
+ public const string CommonCreate = "create";
+ public const string CommonUpdate = "update";
+ public const string CommonRemove = "remove";
+
+ public const string Header = "header";
+ public const string HeaderConfigName = "configName";
+ public const string HeaderProduct = "product";
+ public const string HeaderFileVersion = "fileVersion";
+
+ public const string TransportPluginSettings = "endpointTransportPluginSettings";
+
+ }
+
+ internal class Reg
+ {
+ public const string ConfigFileRegKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows MIDI Services";
+ public const string ConfigFileCurrentRegValue = @"CurrentConfig";
+
+ public const string UseMmcssRegValue = @"UseMMCSS";
+ public const string Midi2DiscoveryEnabled = @"Midi2DiscoveryEnabled";
+ public const string Midi2DiscoveryTimeout = @"Midi2DiscoveryTimeoutMS";
+
+ public const string DefaultToOldMidi1PortNaming = @"DefaultToOldMidi1PortNaming";
+ }
+
+ public const string DefaultConfigurationName = "WindowsMidiServices";
+
+ public const string ConfigFileExtension = ".midiconfig.json";
+
+ public const string DefaultConfigurationFileName = DefaultConfigurationName + ConfigFileExtension;
+
+ private const string RawConfigFileLocation = @"%allusersprofile%\Microsoft\MIDI";
+ public readonly static string ConfigFileLocation;
+
+ static MidiConfigConstants()
+ {
+ ConfigFileLocation = Environment.ExpandEnvironmentVariables(RawConfigFileLocation);
+ }
+
+ }
+
+
+
+
+ public class MidiConfigFile : IMidiConfigFile
+ {
+ private JsonObject? m_config = null;
+
+ private string m_fullFileName;
+ private string m_fileName;
+ public string FileName
+ {
+ get { return m_fileName; }
+ set
+ {
+ m_fileName = value.Trim();
+
+ m_fullFileName = Path.Combine(MidiConfigConstants.ConfigFileLocation, m_fileName);
+ }
+ }
+
+ private MidiConfigFileHeader? m_header = null;
+ public MidiConfigFileHeader? Header
+ {
+ get
+ {
+ if (m_header == null)
+ {
+ LoadHeaderOnly();
+ }
+ return m_header;
+ }
+ }
+
+
+ public MidiConfigFile(string localFileName)
+ {
+ FileName = localFileName;
+ }
+
+
+ internal bool LoadHeaderOnly()
+ {
+ try
+ {
+ string contents;
+
+ using (var fs = File.OpenText(m_fullFileName))
+ {
+ contents = fs.ReadToEnd();
+ }
+
+ JsonObject obj;
+
+ if (JsonObject.TryParse(contents, out obj))
+ {
+ if (!obj.Keys.Contains(MidiConfigConstants.JsonKeys.Header))
+ {
+ return false;
+ }
+
+ var headerObject = obj[MidiConfigConstants.JsonKeys.Header].GetObject();
+
+ if (!headerObject.Keys.Contains(MidiConfigConstants.JsonKeys.HeaderConfigName))
+ {
+ return false;
+ }
+
+ var header = new MidiConfigFileHeader();
+
+ header.Comment = headerObject.GetNamedString(MidiConfigConstants.JsonKeys.CommonComment, string.Empty);
+ header.Name = headerObject.GetNamedString(MidiConfigConstants.JsonKeys.HeaderConfigName, string.Empty);
+ header.Product = headerObject.GetNamedString(MidiConfigConstants.JsonKeys.HeaderProduct, string.Empty);
+ header.FileVersion = headerObject.GetNamedNumber(MidiConfigConstants.JsonKeys.HeaderFileVersion, 0.0);
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ public bool Load()
+ {
+ try
+ {
+ if (!File.Exists(m_fullFileName))
+ {
+ return false;
+ }
+
+ string contents;
+
+ using (var fs = File.OpenText(m_fullFileName))
+ {
+ contents = fs.ReadToEnd();
+ }
+
+ JsonObject obj;
+
+ if (JsonObject.TryParse(contents, out obj))
+ {
+ m_config = obj;
+
+ if (!m_config.Keys.Contains(MidiConfigConstants.JsonKeys.Header))
+ {
+ return false;
+ }
+
+ var headerObject = m_config[MidiConfigConstants.JsonKeys.Header].GetObject();
+
+ if (!headerObject.Keys.Contains(MidiConfigConstants.JsonKeys.HeaderConfigName))
+ {
+ return false;
+ }
+
+ var header = new MidiConfigFileHeader();
+
+ header.Comment = headerObject.GetNamedString(MidiConfigConstants.JsonKeys.CommonComment, string.Empty);
+ header.Name = headerObject.GetNamedString(MidiConfigConstants.JsonKeys.HeaderConfigName, string.Empty);
+ header.Product = headerObject.GetNamedString(MidiConfigConstants.JsonKeys.HeaderProduct, string.Empty);
+ header.FileVersion = headerObject.GetNamedNumber(MidiConfigConstants.JsonKeys.HeaderFileVersion, 0.0);
+
+ m_header = header;
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ private bool Save()
+ {
+ try
+ {
+ // CreateText opens and overwrites if already there
+ using (var fs = File.CreateText(m_fullFileName))
+ {
+ fs.Write(m_config.Stringify());
+ fs.Close();
+ }
+
+ return true;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+
+
+ private bool MergeEndpointTransportSectionIntoJsonObject(JsonObject mainConfigObject, JsonObject objectToMergeIn)
+ {
+ // this assumes the object to merge in has a singular tree structure to pull in. We're
+ // not doing any recursion here. The intent is to do something like add another endpoint
+ // create section to the master document, without having to manually create everything.
+ //
+ // This works because the config json is always a full rooted object -- a valid config file
+ // in itself, albeit with only one specific section.
+
+
+ JsonObject currentMainConfigLevel = mainConfigObject;
+ JsonObject currentIncomingLevel = objectToMergeIn;
+
+ // find endpointTransportPluginSettings section in the object to merge in.
+
+ bool foundIncoming = false;
+ while (!foundIncoming && currentIncomingLevel.Keys.Count > 0)
+ {
+ if (currentIncomingLevel.Keys.Contains(MidiConfigConstants.JsonKeys.TransportPluginSettings))
+ {
+ currentIncomingLevel = currentIncomingLevel[MidiConfigConstants.JsonKeys.TransportPluginSettings].GetObject();
+
+ foundIncoming = true;
+ }
+ else
+ {
+ // need to move down a level
+ }
+
+ }
+
+ if (!foundIncoming)
+ {
+ return false;
+ }
+
+ if (!mainConfigObject.Keys.Contains(MidiConfigConstants.JsonKeys.TransportPluginSettings))
+ {
+ var obj = new JsonObject();
+ mainConfigObject.Add(MidiConfigConstants.JsonKeys.TransportPluginSettings, obj);
+ }
+
+ currentMainConfigLevel = mainConfigObject[MidiConfigConstants.JsonKeys.TransportPluginSettings].GetObject();
+
+
+ bool done = false;
+ while (!done)
+ {
+ foreach (var key in currentIncomingLevel.Keys)
+ {
+ if (currentMainConfigLevel.Keys.Contains(key))
+ {
+ // found the key, move to the next level. We're assuming objects here
+ // which will throw an exception if it's not an object
+ currentMainConfigLevel = currentMainConfigLevel[key].GetObject();
+ currentIncomingLevel = currentIncomingLevel[key].GetObject();
+ }
+ else
+ {
+ // add it and we're done
+ currentMainConfigLevel.Add(key, currentIncomingLevel[key]);
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+
+ // Methods to add common functions to the config, like a new endpoint name, or new loopbacks
+ // expose strongly typed stuff here, and let this class take care of the details within.
+ // Each discrete function should result in a commit to the file.
+
+ // this assumes the config has already been run against the service to create the pair. We're just storing them here.
+ public bool StoreLoopbackEndpointPair(Microsoft.Windows.Devices.Midi2.Endpoints.Loopback.MidiLoopbackEndpointCreationConfig creationConfig)
+ {
+ // get the latest from disk
+ if (!Load())
+ {
+ return false;
+ }
+
+ JsonObject mergeObject;
+ if (JsonObject.TryParse(creationConfig.GetConfigJson(), out mergeObject))
+ {
+ if (MergeEndpointTransportSectionIntoJsonObject(m_config, mergeObject))
+ {
+ // write the property
+
+ return Save();
+ }
+ }
+
+ return false;
+ }
+
+ }
+
+
+
+
+ class MidiConfigFileService : IMidiConfigFileService
+ {
+ private string m_currentConfigName;
+ private string m_currentConfigFileName;
+
+ private MidiConfigFile? m_currentConfigFile = null;
+ public IMidiConfigFile? CurrentConfig
+ {
+ get
+ {
+ return m_currentConfigFile;
+ }
+ }
+
+ public bool IsConfigFileActive
+ {
+ get { return CurrentConfig != null; }
+ }
+
+
+ public MidiConfigFileService()
+ {
+ m_currentConfigFileName = string.Empty;
+ m_currentConfigName = string.Empty;
+
+ m_currentConfigFile = LoadCurrentConfigFileFromRegistry();
+ }
+
+
+
+
+ public string GetConfigFilesLocation()
+ {
+ return MidiConfigConstants.ConfigFileLocation;
+ }
+
+ public IList GetAllConfigFiles()
+ {
+ var configList = new List();
+
+ var files = Directory.GetFiles(MidiConfigConstants.ConfigFileLocation, MidiConfigConstants.ConfigFileExtension);
+
+ foreach (var file in files)
+ {
+ string localFileName = Path.GetFileName(file);
+
+ // early Canary builds went out with a file of this name, and
+ // it's protected in a way that we can't write to it without
+ // some futzing around, so we just pretend it doesn't exist.
+ if (localFileName.ToLower() != "default.midiconfig.json")
+ {
+ var configFile = new MidiConfigFile(localFileName);
+
+ if (configFile.LoadHeaderOnly())
+ {
+ configList.Add(configFile);
+ }
+ }
+
+ }
+
+ return configList;
+ }
+
+ private MidiConfigFile? LoadCurrentConfigFileFromRegistry()
+ {
+ // get reg key
+ var currentLocalFileName = (string)Registry.GetValue(MidiConfigConstants.Reg.ConfigFileRegKey, MidiConfigConstants.Reg.ConfigFileCurrentRegValue, string.Empty);
+
+ // early Canary builds went out with a file of this name, and
+ // it's protected in a way that we can't write to it without
+ // some futzing around, so we just pretend it doesn't exist.
+ if (currentLocalFileName != null &&
+ currentLocalFileName != string.Empty &&
+ currentLocalFileName.ToLower() != "default.midiconfig.json")
+ {
+ var config = new MidiConfigFile(currentLocalFileName);
+
+ if (config.Load())
+ {
+ return config;
+ }
+ }
+
+ return null;
+ }
+
+ public bool UpdateRegistryCurrentConfigFile(string configFileName)
+ {
+ try
+ {
+ Registry.SetValue(MidiConfigConstants.Reg.ConfigFileRegKey, MidiConfigConstants.Reg.ConfigFileCurrentRegValue, configFileName, RegistryValueKind.String);
+
+ var config = LoadCurrentConfigFileFromRegistry();
+
+ // set the config as current
+ m_currentConfigFile = config;
+
+ return true;
+ }
+ catch (Exception)
+ {
+
+ }
+
+ return false;
+ }
+
+ public string GetDefaultConfigName()
+ {
+ return MidiConfigConstants.DefaultConfigurationName;
+ }
+
+ public string BuildConfigLocalFileNameFromConfigName(string configName)
+ {
+ var cleanedConfigurationName = CleanupConfigName(configName);
+
+ cleanedConfigurationName = string.Join("", cleanedConfigurationName.Split(Path.GetInvalidFileNameChars()));
+ cleanedConfigurationName = string.Join("", cleanedConfigurationName.Split(Path.GetInvalidPathChars()));
+
+ return cleanedConfigurationName + MidiConfigConstants.ConfigFileExtension;
+ }
+
+ public string BuildConfigFullFileNameWithPathFromLocalFileName(string configLocalFileName)
+ {
+ return Path.Combine(MidiConfigConstants.ConfigFileLocation, configLocalFileName);
+ }
+
+ public string CleanupConfigName(string configName)
+ {
+ var cleanedConfigurationName = string.Join("", configName.Split(['.', ':', ';', '`', '%', '@', '#', '&', '$', '+', ',', '"', '\'', '{', '}', '[', ']'])).Trim();
+
+ if (string.IsNullOrEmpty(cleanedConfigurationName))
+ {
+ return MidiConfigConstants.DefaultConfigurationName;
+ }
+
+ return cleanedConfigurationName;
+ }
+
+ public bool ConfigFileExists(string configLocalFileName)
+ {
+ var fullPath = BuildConfigFullFileNameWithPathFromLocalFileName(configLocalFileName);
+
+ return Path.Exists(fullPath);
+ }
+
+
+ private JsonObject CreateConfigFileHeaderJson(string configName)
+ {
+ var o = new JsonObject();
+
+ // TODO: some of these property values should be localized
+
+ o.Add(MidiConfigConstants.JsonKeys.CommonComment, JsonValue.CreateStringValue("NOTE: All json keys are case-sensitive, including GUIDs."));
+ o.Add(MidiConfigConstants.JsonKeys.HeaderConfigName, JsonValue.CreateStringValue(configName));
+ o.Add(MidiConfigConstants.JsonKeys.HeaderProduct, JsonValue.CreateStringValue("Windows MIDI Services"));
+ o.Add(MidiConfigConstants.JsonKeys.HeaderFileVersion, JsonValue.CreateNumberValue(1.0));
+
+ return o;
+ }
+
+ public bool CreateNewConfigFile(string configName, string configLocalFileName)
+ {
+ try
+ {
+ // cleanup the name
+ configName = CleanupConfigName(configName);
+
+ // make sure the config doesn't already exist
+
+ if (ConfigFileExists(configLocalFileName))
+ {
+ return false;
+ }
+
+ var outerJsonObject = new JsonObject();
+
+ // create the config header
+
+ var header = CreateConfigFileHeaderJson(configName);
+ outerJsonObject.Add(MidiConfigConstants.JsonKeys.Header, header);
+
+ // create section for transports
+
+ var transportsObject = new JsonObject();
+ outerJsonObject.Add(MidiConfigConstants.JsonKeys.TransportPluginSettings, transportsObject);
+
+ var filePath = BuildConfigFullFileNameWithPathFromLocalFileName(configLocalFileName);
+
+ if (Path.Exists(filePath))
+ {
+ return false;
+ }
+
+ using (var fs = File.CreateText(filePath))
+ {
+ fs.Write(outerJsonObject.Stringify());
+ fs.Close();
+ }
+
+ var config = new MidiConfigFile(configLocalFileName);
+ config.LoadHeaderOnly();
+
+ m_currentConfigFile = config;
+
+ return true;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiDefaultsService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiDefaultsService.cs
new file mode 100644
index 000000000..7f669de3c
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiDefaultsService.cs
@@ -0,0 +1,63 @@
+using Microsoft.Midi.Settings.Contracts.Services;
+using Microsoft.Windows.Devices.Midi2.Endpoints.Loopback;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Windows.Foundation;
+
+namespace Microsoft.Midi.Settings.Services
+{
+ public class MidiDefaultsService : IMidiDefaultsService
+ {
+ const string DefaultLoopbackAUniqueId = "DEFAULT_LOOPBACK_A";
+ const string DefaultLoopbackBUniqueId = "DEFAULT_LOOPBACK_B";
+
+ public string GetDefaultMidiConfigName()
+ {
+ return MidiConfigConstants.DefaultConfigurationName;
+ }
+ public string GetDefaultMidiConfigFileName()
+ {
+ return MidiConfigConstants.DefaultConfigurationFileName;
+ }
+
+
+ public MidiLoopbackEndpointCreationConfig GetDefaultLoopbackCreationConfig()
+ {
+ var endpointA = new MidiLoopbackEndpointDefinition();
+ var endpointB = new MidiLoopbackEndpointDefinition();
+
+ // if endpoint A or B names are empty, do not close. display an error
+
+ // if endpoint A or B unique ids are empty, do not close. display suggestion to generate them
+ // todo: need to limit to alpha plus just a couple other characters, and only 32 in length
+
+ endpointA.Name = "Default Loopback A";
+ endpointB.Name = "Default Loopback B";
+
+ endpointA.UniqueId = DefaultLoopbackAUniqueId;
+ endpointB.UniqueId = DefaultLoopbackBUniqueId;
+
+ // descriptions are optional
+ endpointA.Description = "Default loopback endpoint for use by applications. This is the A-side of the loopback pair.";
+ endpointB.Description = "Default loopback endpoint for use by applications. This is the B-side of the loopback pair.";
+
+ // TODO: entries for the default groups to create, and their gtb names
+
+ var associationId = GuidHelper.CreateNewGuid();
+
+ var creationConfig = new MidiLoopbackEndpointCreationConfig(associationId, endpointA, endpointB);
+
+ return creationConfig;
+ }
+
+ public bool DoesDefaultLoopbackAlreadyExist()
+ {
+ // TODO: Check to see if an endpoint with the unique ids here already exists
+
+ return false;
+ }
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiTransportInfoService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiTransportInfoService.cs
new file mode 100644
index 000000000..5db4f8139
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiTransportInfoService.cs
@@ -0,0 +1,19 @@
+using Microsoft.Midi.Settings.Contracts.Services;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Midi.Settings.Services
+{
+ class MidiTransportInfoService : IMidiTransportInfoService
+ {
+ public MidiServiceTransportPluginInfo GetTransportForCode(string transportCode)
+ {
+ var codes = MidiReporting.GetInstalledTransportPlugins();
+
+ return codes.Where(p => p.TransportCode.ToUpper() == transportCode.ToUpper()).FirstOrDefault();
+ }
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/PageService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/PageService.cs
index f86c08feb..992b31490 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/PageService.cs
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/PageService.cs
@@ -44,6 +44,9 @@ public PageService()
Configure();
Configure();
+ Configure();
+
+
Configure();
}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Styles/TextBlock.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Styles/TextBlock.xaml
index ab8811963..58e2e849d 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Styles/TextBlock.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Styles/TextBlock.xaml
@@ -2,7 +2,22 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
-
+
+
+
+
+
+