diff --git a/build/build.cake b/build/build.cake
index 9f5d7f04..15eb96f1 100644
--- a/build/build.cake
+++ b/build/build.cake
@@ -2,10 +2,13 @@
//#addin nuget:?package=Cake.Compression&version=0.3.0
// ===========================================================================================
-string setupVersionName = "Developer Preview 5";
-// ===========================================================================================
-
+string setupVersionName = "Developer Preview 6";
+// we set these here, especially the time, so it's the same for all platforms in the single build
+string setupBuildMajorMinor = "1.0";
+string setupBuildDateNumber = DateTime.Now.ToString("yy") + DateTime.Now.DayOfYear.ToString("000"); // YYddd where ddd is the day number for the year
+string setupBuildTimeNumber = DateTime.Now.ToString("HHmm"); // HHmm
+// ===========================================================================================
@@ -18,8 +21,8 @@ var ridArm64 = "win10-arm64";
var frameworkVersion = "net8.0-windows10.0.20348.0";
-//var platformTargets = new[]{PlatformTarget.x64, PlatformTarget.ARM64};
-var platformTargets = new[]{PlatformTarget.x64};
+var platformTargets = new[]{PlatformTarget.x64, PlatformTarget.ARM64};
+//var platformTargets = new[]{PlatformTarget.x64};
@@ -180,6 +183,7 @@ Task("SetupEnvironment")
CopyFiles(System.IO.Path.Combine(outputDir, "*.pdb"), copyToDir);
CopyFiles(System.IO.Path.Combine(outputDir, "MidiSrv.exe"), copyToDir);
+ CopyFiles(System.IO.Path.Combine(outputDir, "mididmp.exe"), copyToDir);
CopyFiles(System.IO.Path.Combine(outputDir, "Windows.Devices.Midi2.winmd"), copyToDir);
@@ -188,7 +192,6 @@ Task("SetupEnvironment")
// copy the C++ header for the API
CopyFiles(System.IO.Path.Combine(generatedFilesDir, "Windows.Devices.Midi2.h"), copyToDir);
- CopyFiles(System.IO.Path.Combine(outputDir, "mididmp.exe"), copyToDir);
@@ -216,10 +219,6 @@ Task("SetupEnvironment")
CopyFiles(System.IO.Path.Combine(generatedFilesDir, "impl/Windows.Foundation.Collections.2.h"), System.IO.Path.Combine(apiBareCopyToDir, "winrt/impl/"));
CopyFiles(System.IO.Path.Combine(generatedFilesDir, "impl/Windows.Devices.Midi2.2.h"), System.IO.Path.Combine(apiBareCopyToDir, "winrt/impl/"));
-
-
-
-
CopyFiles(System.IO.Path.Combine(copyToDir, "Windows.Devices.Midi2.dll"), apiBareCopyToDir);
CopyFiles(System.IO.Path.Combine(copyToDir, "Windows.Devices.Midi2.winmd"), apiBareCopyToDir);
CopyFiles(System.IO.Path.Combine(copyToDir, "Windows.Devices.Midi2.pri"), apiBareCopyToDir);
@@ -228,81 +227,16 @@ Task("SetupEnvironment")
});
-Task("BuildApiActivationRegEntries")
- .IsDependentOn("BuildServiceAndAPI")
- .DoesForEach(platformTargets, plat =>
-{
- Information("\nBuilding WinRT Activation Entries for " + plat.ToString());
- // read the file of dependencies
- var sourceFileName = System.IO.Path.Combine(apiAndServiceStagingDir, plat.ToString(), "WinRTActivationEntries.txt");
- var wxiDestinationFileName = System.IO.Path.Combine(apiAndServiceStagingDir, plat.ToString(), "WinRTActivationEntries.wxi");
-
- const string parentHKLMRegKey = "SOFTWARE\\Microsoft\\WindowsRuntime\\ActivatableClassId\\";
- const string wixWinrtLibFileName = "[API_INSTALLFOLDER]Windows.Devices.Midi2.dll";
-
- if (!System.IO.File.Exists(sourceFileName))
- throw new ArgumentException("Missing WinRT Activation entries file " + sourceFileName);
-
- using (StreamReader reader = System.IO.File.OpenText(sourceFileName))
- {
- using (StreamWriter wxiWriter = System.IO.File.CreateText(wxiDestinationFileName))
- {
- wxiWriter.WriteLine("");
-
- string line;
-
- while ((line = reader.ReadLine()) != null)
- {
- string trimmedLine = line.Trim();
-
- if (string.IsNullOrWhiteSpace(trimmedLine) || trimmedLine.StartsWith("#"))
- {
- // comment or empty line
- continue;
- }
-
- // pipe-delimited lines
- var elements = trimmedLine.Split('|');
-
- if (elements.Count() != 4)
- throw new ArgumentException("Bad line: " + trimmedLine);
-
- // entries in order are
- // ClassName | ActivationType | Threading | TrustLevel
-
- string className = elements[0].Trim();
- string activationType = elements[1].Trim();
- string threading = elements[2].Trim();
- string trustLevel = elements[3].Trim();
-
- wxiWriter.WriteLine($"");
-
- wxiWriter.WriteLine($" ");
- wxiWriter.WriteLine($" ");
- wxiWriter.WriteLine($" ");
- wxiWriter.WriteLine($" ");
-
- wxiWriter.WriteLine("");
-
- }
-
- wxiWriter.WriteLine("");
-
- }
- }
-
-});
Task("BuildApiActivationRegEntriesCSharp")
.IsDependentOn("BuildServiceAndAPI")
- .DoesForEach(platformTargets, plat =>
+ .Does(() =>
{
- Information("\nBuilding WinRT Activation Entries for " + plat.ToString());
+ Information("\nBuilding WinRT Activation Entries ");
- // read the file of dependencies
- var sourceFileName = System.IO.Path.Combine(apiAndServiceStagingDir, plat.ToString(), "WinRTActivationEntries.txt");
+ var sourceFileName = System.IO.Path.Combine(apiAndServiceStagingDir, "x64", "WinRTActivationEntries.txt");
var csDestinationFileName = System.IO.Path.Combine(registrySettingsStagingDir, "WinRTActivationEntries.cs");
// const string parentHKLMRegKey = "SOFTWARE\\Microsoft\\WindowsRuntime\\ActivatableClassId\\";
@@ -370,12 +304,12 @@ Task("BuildApiActivationRegEntriesCSharp")
Task("BuildApiActivationRegEntriesInternal")
.IsDependentOn("BuildServiceAndAPI")
- .DoesForEach(platformTargets, plat =>
+ .Does(() =>
{
- Information("\nBuilding WinRT Internaml XML Activation Entries for " + plat.ToString());
+ Information("\nBuilding WinRT Internaml XML Activation Entries");
// read the file of dependencies
- var sourceFileName = System.IO.Path.Combine(apiAndServiceStagingDir, plat.ToString(), "WinRTActivationEntries.txt");
+ var sourceFileName = System.IO.Path.Combine(apiAndServiceStagingDir, "x64", "WinRTActivationEntries.txt");
var destinationFileName = System.IO.Path.Combine(registrySettingsStagingDir, "WinRTActivationEntries.xml");
if (!System.IO.File.Exists(sourceFileName))
@@ -450,36 +384,8 @@ Task("PackAPIProjection")
});
-
-Task("BuildSDK")
- .IsDependentOn("BuildServiceAndAPI")
- .IsDependentOn("PackAPIProjection")
- .DoesForEach(platformTargets, plat =>
-{
- Information("\nBuilding SDK for " + plat.ToString());
-
- // TODO
-
-
-});
-
-
-Task("PackSDKProjection")
- .IsDependentOn("BuildSDK")
- .Does(() =>
-{
- Information("\nPacking SDK...");
-
- // TODO
-
-
-});
-
-
-
Task("BuildConsoleApp")
.IsDependentOn("PackAPIProjection")
-/* .IsDependentOn("PackSDKProjection") */
.DoesForEach(platformTargets, plat =>
{
// TODO: Update nuget ref in console app to the new version
@@ -535,7 +441,6 @@ Task("BuildConsoleApp")
Task("BuildSettingsApp")
.IsDependentOn("PackAPIProjection")
- .IsDependentOn("PackSDKProjection")
.DoesForEach(platformTargets, plat =>
{
// TODO: Update nuget ref in settings app to the new version
@@ -589,49 +494,24 @@ Task("BuildSettingsApp")
});
- string finalSetupName = string.Empty;
+string finalSetupNamex64 = string.Empty;
+string finalSetupNameArm64 = string.Empty;
Task("BuildInstaller")
.IsDependentOn("SetupEnvironment")
.IsDependentOn("BuildServiceAndAPI")
.IsDependentOn("BuildApiActivationRegEntriesCSharp")
-/* .IsDependentOn("BuildSDK") */
.IsDependentOn("BuildSettingsApp")
.IsDependentOn("BuildConsoleApp")
.DoesForEach(platformTargets, plat =>
{
- /*var buildSettings = new MSBuildSettings
- {
- MaxCpuCount = 0,
- Configuration = configuration,
- AllowPreviewVersion = allowPreviewVersionOfBuildTools,
- PlatformTarget = plat,
- Verbosity = Verbosity.Minimal,
- };
-
- MSBuild(setupSolutionFile, buildSettings); */
-
- // have to build these projects using dotnet build, or else the nuget references just die if you use MSBuild or VS
-
- //var apiProjectDir = System.IO.Path.Combine(setupSolutionDir, "api-package");
- //var apiProjectFile = System.IO.Path.Combine(apiProjectDir, "api-package.wixproj");
+ Information($"\nBuilding Installer for {plat.ToString()} {configuration}");
var mainBundleProjectDir = System.IO.Path.Combine(setupSolutionDir, "main-bundle");
-
+ var apiSetupProjectDir = System.IO.Path.Combine(setupSolutionDir, "api-package");
var consoleOnlySetupProjectDir = System.IO.Path.Combine(setupSolutionDir, "console-package");
var settingsOnlySetupProjectDir = System.IO.Path.Combine(setupSolutionDir, "settings-package");
- //var mainBundleProjectFile = System.IO.Path.Combine(mainBundleProjectDir, "main-bundle.wixproj");
-
- //var regActionsProjectDir = System.IO.Path.Combine(setupSolutionDir, "RegistryCustomActions");
- //var regActionsProjectFile = System.IO.Path.Combine(regActionsProjectDir, "RegistryCustomActions.csproj");
-
-
-
- // if we don't set platform here, it always ends up as a 32 bit installer
- // configuration
- // buildSettings.MSBuildSettings.Properties["Platform"] = plat;
-
string rid = "";
if (plat == PlatformTarget.x64)
rid = ridX64;
@@ -648,10 +528,7 @@ Task("BuildInstaller")
//
//
- string setupBuildMajorMinor = "1.0";
- string setupBuildDateNumber = DateTime.Now.ToString("yy") + DateTime.Now.DayOfYear.ToString("000"); // YYddd where ddd is the day number for the year
- string setupBuildTimeNumber = DateTime.Now.ToString("HHmm"); // HHmm
- string setupBuildPlatform = plat.ToString();
+ string setupBuildPlatform = plat.ToString().ToLower();
string setupBuildFullVersionString = setupBuildMajorMinor + "." + setupBuildDateNumber + "." + setupBuildTimeNumber;
//string setupBuildFullVersionStringFile = setupBuildFullVersionString.Replace('.', '-');
@@ -659,27 +536,35 @@ Task("BuildInstaller")
using (StreamWriter writer = System.IO.File.CreateText(setupBundleInfoIncludeFile))
{
writer.WriteLine("");
- writer.WriteLine($" ");
+ writer.WriteLine($" ");
writer.WriteLine($" ");
writer.WriteLine("");
}
- finalSetupName = $"Windows MIDI Services {setupVersionName} {setupBuildPlatform} {setupBuildFullVersionString}.exe";
+ var setupName = $"Windows MIDI Services {setupVersionName} {setupBuildFullVersionString}-{setupBuildPlatform}.exe";
+
+ if (plat == PlatformTarget.x64)
+ finalSetupNamex64 = setupName;
+ else if (plat == PlatformTarget.ARM64)
+ finalSetupNameArm64 = setupName;
+
+
+ string workingDirectory = string.Empty;
- var buildSettings = new DotNetBuildSettings
+ var buildSettings = new MSBuildSettings
{
- WorkingDirectory = mainBundleProjectDir,
- Configuration = configuration,
- // Runtime = rid, // rid not supported for solution-level builds
+ MaxCpuCount = 1, /* Set to 1 to avoid file locking problems */
+ Configuration = configuration,
+ AllowPreviewVersion = allowPreviewVersionOfBuildTools,
+ PlatformTarget = plat,
+ Verbosity = Verbosity.Minimal,
};
- DotNetClean(setupSolutionFile);
+ MSBuild(setupSolutionFile, buildSettings);
- // build the custom action first
- DotNetBuild(setupSolutionFile, buildSettings);
-
- string releaseStandAloneInstallerFolder = System.IO.Path.Combine(setupReleaseDir, "Component Installs");
+
+ string releaseStandAloneInstallerFolder = System.IO.Path.Combine(setupReleaseDir, "Component Installs", plat.ToString().ToLower());
if (!DirectoryExists(setupReleaseDir))
CreateDirectory(setupReleaseDir);
@@ -694,12 +579,11 @@ Task("BuildInstaller")
// rename
MoveFile(System.IO.Path.Combine(setupReleaseDir, "WindowsMidiServicesCompleteSetup.exe"),
- (System.IO.Path.Combine(setupReleaseDir, finalSetupName)));
+ (System.IO.Path.Combine(setupReleaseDir, setupName)));
CopyFiles(System.IO.Path.Combine(consoleOnlySetupProjectDir, "bin", plat.ToString(), "Release", "*.msi"), releaseStandAloneInstallerFolder);
CopyFiles(System.IO.Path.Combine(settingsOnlySetupProjectDir, "bin", plat.ToString(), "Release", "*.msi"), releaseStandAloneInstallerFolder);
-
-
+ CopyFiles(System.IO.Path.Combine(apiSetupProjectDir, "bin", plat.ToString(), "Release", "*.msi"), releaseStandAloneInstallerFolder);
});
@@ -707,22 +591,30 @@ Task("BuildInstaller")
Task("CopyAPIArtifacts")
.IsDependentOn("BuildInstaller")
.IsDependentOn("PackAPIProjection")
- .IsDependentOn("BuildConsoleApp")
.DoesForEach(platformTargets, plat =>
{
- string apiStagingDir = System.IO.Path.Combine(apiAndServiceStagingDir, "bin", plat.ToString());
+ Information($"\nCopying API artifacts for {plat.ToString()} {configuration}");
+
+ string apiStagingDir = System.IO.Path.Combine(apiAndServiceStagingDir, "bin", plat.ToString().ToLower());
+ string platReleaseFolder = System.IO.Path.Combine(apiReleaseArtifactsFolder, plat.ToString().ToLower());
+
+ if (!DirectoryExists(apiStagingDir))
+ CreateDirectory(apiStagingDir);
+
+ if (!DirectoryExists(platReleaseFolder))
+ CreateDirectory(platReleaseFolder);
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.winmd"), apiReleaseArtifactsFolder);
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.dll"), apiReleaseArtifactsFolder);
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.pri"), apiReleaseArtifactsFolder);
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.h"), apiReleaseArtifactsFolder);
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/base.h"), System.IO.Path.Combine(apiReleaseArtifactsFolder, "winrt/"));
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/Windows.Devices.h"), System.IO.Path.Combine(apiReleaseArtifactsFolder, "winrt/"));
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Devices.Enumeration.2.h"), System.IO.Path.Combine(apiReleaseArtifactsFolder, "winrt/impl/"));
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Devices.Midi.2.h"), System.IO.Path.Combine(apiReleaseArtifactsFolder, "winrt/impl/"));
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Foundation.2.h"), System.IO.Path.Combine(apiReleaseArtifactsFolder, "winrt/impl/"));
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Foundation.Collections.2.h"), System.IO.Path.Combine(apiReleaseArtifactsFolder, "winrt/impl/"));
- CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Devices.Midi2.2.h"), System.IO.Path.Combine(apiReleaseArtifactsFolder, "winrt/impl/"));
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.winmd"), platReleaseFolder);
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.dll"), platReleaseFolder);
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.pri"), platReleaseFolder);
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.h"), platReleaseFolder);
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/base.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/"));
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/Windows.Devices.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/"));
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Devices.Enumeration.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/"));
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Devices.Midi.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/"));
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Foundation.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/"));
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Foundation.Collections.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/"));
+ CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Devices.Midi2.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/"));
});
@@ -739,7 +631,10 @@ Task("Default")
.Does(() =>
{
- Information("\n\nInstaller >> \"" + finalSetupName + "\"\n\n");
+ Information("\n\n");
+ Information($"Installer x64 >> \"{finalSetupNamex64}\" \n");
+ Information($"Installer Arm64 >> \"{finalSetupNameArm64}\"\n");
+ Information("\n\n");
});
diff --git a/build/dependencies/dotnet8-windowsdesktop-runtime-latest-win-x64.exe b/build/dependencies/dotnet8-windowsdesktop-runtime-latest-win-x64.exe
deleted file mode 100644
index bcf2b45b..00000000
Binary files a/build/dependencies/dotnet8-windowsdesktop-runtime-latest-win-x64.exe and /dev/null differ
diff --git a/build/dependencies/wasdk/windowsappruntimeinstall-arm64.exe b/build/dependencies/wasdk/windowsappruntimeinstall-arm64.exe
deleted file mode 100644
index 8c07c2a2..00000000
Binary files a/build/dependencies/wasdk/windowsappruntimeinstall-arm64.exe and /dev/null differ
diff --git a/build/dependencies/wasdk/windowsappruntimeinstall-x64.exe b/build/dependencies/wasdk/windowsappruntimeinstall-x64.exe
deleted file mode 100644
index 0410e5e2..00000000
Binary files a/build/dependencies/wasdk/windowsappruntimeinstall-x64.exe and /dev/null differ
diff --git a/build/dependencies/windowsdesktop-runtime-8.0.0-win-arm64.exe b/build/dependencies/windowsdesktop-runtime-8.0.0-win-arm64.exe
deleted file mode 100644
index faef508b..00000000
Binary files a/build/dependencies/windowsdesktop-runtime-8.0.0-win-arm64.exe and /dev/null differ
diff --git a/build/dependencies/windowsdesktop-runtime-8.0.0-win-x64.exe b/build/dependencies/windowsdesktop-runtime-8.0.0-win-x64.exe
deleted file mode 100644
index 19e4c0a8..00000000
Binary files a/build/dependencies/windowsdesktop-runtime-8.0.0-win-x64.exe and /dev/null differ
diff --git a/build/dependencies/windowsdesktop-runtime-8.0.2-win-arm64.exe b/build/dependencies/windowsdesktop-runtime-8.0.2-win-arm64.exe
deleted file mode 100644
index 6a78ff43..00000000
Binary files a/build/dependencies/windowsdesktop-runtime-8.0.2-win-arm64.exe and /dev/null differ
diff --git a/build/dependencies/windowsdesktop-runtime-8.0.2-win-x64.exe b/build/dependencies/windowsdesktop-runtime-8.0.2-win-x64.exe
deleted file mode 100644
index bcf2b45b..00000000
Binary files a/build/dependencies/windowsdesktop-runtime-8.0.2-win-x64.exe and /dev/null differ
diff --git a/build/dependencies/x64 Package is for x64 and Arm64.txt b/build/dependencies/x64 Package is for x64 and Arm64.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/build/replace_running_service.bat b/build/replace_running_service_x64.bat
similarity index 100%
rename from build/replace_running_service.bat
rename to build/replace_running_service_x64.bat
diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi
index f6c1a335..2a7ecc0b 100644
--- a/build/staging/version/BundleInfo.wxi
+++ b/build/staging/version/BundleInfo.wxi
@@ -1,4 +1,4 @@
-
-
+
+
diff --git a/docs/data-translation.md b/docs/data-translation.md
new file mode 100644
index 00000000..1db20dc3
--- /dev/null
+++ b/docs/data-translation.md
@@ -0,0 +1,34 @@
+---
+layout: page
+title: Data Translation
+parent: Windows Midi Services
+---
+
+# Data Translation
+
+In general, Windows MIDI Services translates MIDI messages only when it has to. This translation happens in two different places, depending upon the driver in use.
+
+Internally, the MIDI service moves messages around in the UMP format. This enables a standard format for message scheduling and processing. In addition, the `Windows.Devices.Midi2` API treats all messages as UMP, including ones to/from devices which were bytestream MIDI 1.0 format.
+
+## Translation Scenarios involving data format changes
+
+Windows MIDI Services supports MIDI 1.0 and MIDI 2.0 devices.
+
+| Device | Driver | Windows.Devices.Midi2 | Older MIDI 1.0 APIs |
+| ------------------- | --------------------- | -------------------------- | ------------------------ |
+| USB MIDI 1.0 Device | MIDI 2.0 Class Driver | To/From UMP by driver | To/from byte stream by service |
+| USB MIDI 1.0 Device | Older MIDI 1.0 Class Driver | To/From UMP by service | To/from byte stream by service |
+| USB MIDI 1.0 Device | Vendor MIDI 1.0 driver | To/From UMP by service | To/From byte stream by service |
+| USB MIDI 2.0 Device | MIDI 2.0 Class Driver | No translation required | To/from byte stream by service |
+| Any other MIDI 2.0 Device | (no driver. ex Virtual, Network 2.0) | No translation required | To/from byte stream by service |
+| Any other MIDI 1.0 Device | (no driver. ex BLE) | To/From UMP by service | To/From byte stream by service |
+
+## Translation between Message type 2 (MIDI 1.0 Channel Voice) and Message type 4 (MIDI 2.0 Channel Voice)
+
+Currently, Windows MIDI Services does not translate messages based on negotiated protocol or Function Block declared protocol. Instead, for native UMP endpoints, applications should send the correct protocol messages (message type 2 for MIDI 1.0-compatible and message type 4 for MIDI 2.0-compatible messages) based upon the information provided by the `EndpointDeviceInformation` [and related enumeration types](developer-docs\Windows.Devices.Midi2\enumeration\README.md). In addition, for native bytestream endpoints, applications should send the appropriate MIDI 1.0 messages in UMP.
+
+## Resources for translation
+
+Windows MIDI Services makes use of publicly-available open source libraries for protocol and data format translation.
+
+* [midi2.dev](https://midi2.dev) contains a number of libraries which include translation.
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
index e4b99d1f..211fa9df 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -43,5 +43,5 @@ While we're in developer preview, get started by downloading and installing the
These are the updated MIDI 2.0 specifications which apply to this project today.
-* [MIDI 2.0 UMP Specifications](https://midi.org/specifications)
+* [MIDI 2.0 UMP (and other) Specifications](https://midi.org/specs)
diff --git a/samples/cpp-winrt/api-client-basics/api-client-basics.vcxproj b/samples/cpp-winrt/api-client-basics/api-client-basics.vcxproj
index becf0f8f..efdc9e88 100644
--- a/samples/cpp-winrt/api-client-basics/api-client-basics.vcxproj
+++ b/samples/cpp-winrt/api-client-basics/api-client-basics.vcxproj
@@ -1,7 +1,7 @@
-
-
+
+
true
true
@@ -149,16 +149,16 @@
-
-
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/api-client-basics/packages.config b/samples/cpp-winrt/api-client-basics/packages.config
index 4773d392..9fca0dc2 100644
--- a/samples/cpp-winrt/api-client-basics/packages.config
+++ b/samples/cpp-winrt/api-client-basics/packages.config
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/api-client-send-speed/api-client-send-speed.vcxproj b/samples/cpp-winrt/api-client-send-speed/api-client-send-speed.vcxproj
index f48a4bea..3fd49fa4 100644
--- a/samples/cpp-winrt/api-client-send-speed/api-client-send-speed.vcxproj
+++ b/samples/cpp-winrt/api-client-send-speed/api-client-send-speed.vcxproj
@@ -1,7 +1,7 @@
-
-
+
+
true
true
@@ -149,16 +149,16 @@
-
-
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/api-client-send-speed/packages.config b/samples/cpp-winrt/api-client-send-speed/packages.config
index 4773d392..9fca0dc2 100644
--- a/samples/cpp-winrt/api-client-send-speed/packages.config
+++ b/samples/cpp-winrt/api-client-send-speed/packages.config
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/api-enum-endpoints/api-enum-endpoints-cpp.vcxproj b/samples/cpp-winrt/api-enum-endpoints/api-enum-endpoints-cpp.vcxproj
index 3cb00f56..ff6edb65 100644
--- a/samples/cpp-winrt/api-enum-endpoints/api-enum-endpoints-cpp.vcxproj
+++ b/samples/cpp-winrt/api-enum-endpoints/api-enum-endpoints-cpp.vcxproj
@@ -1,7 +1,7 @@
-
-
+
+
true
true
@@ -149,16 +149,16 @@
-
-
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/api-enum-endpoints/packages.config b/samples/cpp-winrt/api-enum-endpoints/packages.config
index 4773d392..9fca0dc2 100644
--- a/samples/cpp-winrt/api-enum-endpoints/packages.config
+++ b/samples/cpp-winrt/api-enum-endpoints/packages.config
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/api-loopback-endpoints/api-loopback-endpoints.vcxproj b/samples/cpp-winrt/api-loopback-endpoints/api-loopback-endpoints.vcxproj
index 872ea4ca..325e5e67 100644
--- a/samples/cpp-winrt/api-loopback-endpoints/api-loopback-endpoints.vcxproj
+++ b/samples/cpp-winrt/api-loopback-endpoints/api-loopback-endpoints.vcxproj
@@ -1,7 +1,7 @@
-
-
+
+
true
true
@@ -111,16 +111,16 @@
-
-
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/api-loopback-endpoints/main.cpp b/samples/cpp-winrt/api-loopback-endpoints/main.cpp
index fe0312cd..4c9916a7 100644
--- a/samples/cpp-winrt/api-loopback-endpoints/main.cpp
+++ b/samples/cpp-winrt/api-loopback-endpoints/main.cpp
@@ -31,11 +31,11 @@ bool CreateLoopbackEndpoints()
MidiServiceLoopbackEndpointDefinition definitionA;
MidiServiceLoopbackEndpointDefinition definitionB;
- definitionA.Name(L"My App Loopback A");
+ definitionA.Name(L"Sample App Loopback A");
definitionA.Description(L"The first description is optional, but is displayed to users. This becomes the transport-defined description.");
definitionA.UniqueId(L"8675309-OU812-5150");
- definitionB.Name(L"My App Loopback B");
+ definitionB.Name(L"Sample App Loopback B");
definitionB.Description(L"The second description is optional, but is displayed to users. This becomes the transport-defined description.");
definitionB.UniqueId(L"3263827-OU812-5150"); // can be the same as the first one, but doesn't need to be.
@@ -45,20 +45,31 @@ bool CreateLoopbackEndpoints()
definitionB
);
-
if (response.Success())
{
- std::wcout << L"Endpoint A: " << response.EndpointDeviceIdA().c_str() << std::endl;
- std::wcout << L"Endpoint B: " << response.EndpointDeviceIdB().c_str() << std::endl;
+ std::wcout << L"Endpoints created successfully" << std::endl << std::endl;
+
+ std::cout
+ << "Loopback Endpoint A: " << std::endl
+ << " - " << winrt::to_string(definitionA.Name()) << std::endl
+ << " - " << winrt::to_string(response.EndpointDeviceIdA()) << std::endl << std::endl;
+
+ std::cout
+ << "Loopback Endpoint B: " << std::endl
+ << " - " << winrt::to_string(definitionB.Name()) << std::endl
+ << " - " << winrt::to_string(response.EndpointDeviceIdB()) << std::endl << std::endl;
m_endpointAId = response.EndpointDeviceIdA();
m_endpointBId = response.EndpointDeviceIdB();
}
else
{
- std::cout << "Failed to create loopback endpoints." << std::endl;
+ std::cout << "Failed to create loopback endpoints." << std::endl;
+ std::cout << "This can happen if you control-C or crash out of the sample before the loopbacks are removed." << std::endl;
+ std::cout << "If that's the case, restart MidiSrv, or change the unique Ids above." << std::endl;
}
+ // Success here is a boolean for success/fail
return response.Success();
}
@@ -73,9 +84,7 @@ int main()
std::cout << std::endl << "Creating session..." << std::endl;
- auto session = MidiSession::CreateSession(L"Sample Session");
-
-
+ auto session = MidiSession::CreateSession(L"Loopback Sample Session");
if (CreateLoopbackEndpoints())
{
@@ -178,14 +187,16 @@ int main()
// remove the loopback endpoints
+ // If you don't do this, they will stay active, and the next attempt
+ // to create them will fail because the unique Ids are already in use
if (MidiService::RemoveTemporaryLoopbackEndpoints(m_associationId))
{
- std::cout << "Endpoints removed." << std::endl;
+ std::cout << "Loopback endpoints removed." << std::endl;
}
else
{
- std::cout << "There was a problem removing the endpoints." << std::endl;
+ std::cout << "There was a problem removing the endpoints. You may want to restart the service." << std::endl;
}
}
}
diff --git a/samples/cpp-winrt/api-loopback-endpoints/packages.config b/samples/cpp-winrt/api-loopback-endpoints/packages.config
index 4773d392..9fca0dc2 100644
--- a/samples/cpp-winrt/api-loopback-endpoints/packages.config
+++ b/samples/cpp-winrt/api-loopback-endpoints/packages.config
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/api-watch-endpoints/api-watch-endpoints-cpp.vcxproj b/samples/cpp-winrt/api-watch-endpoints/api-watch-endpoints-cpp.vcxproj
index 0868a2fb..d7c3a42d 100644
--- a/samples/cpp-winrt/api-watch-endpoints/api-watch-endpoints-cpp.vcxproj
+++ b/samples/cpp-winrt/api-watch-endpoints/api-watch-endpoints-cpp.vcxproj
@@ -1,7 +1,7 @@
-
-
+
+
true
true
@@ -149,16 +149,16 @@
-
-
+
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/samples/cpp-winrt/api-watch-endpoints/main.cpp b/samples/cpp-winrt/api-watch-endpoints/main.cpp
index 4ef10b39..90667ad4 100644
--- a/samples/cpp-winrt/api-watch-endpoints/main.cpp
+++ b/samples/cpp-winrt/api-watch-endpoints/main.cpp
@@ -30,13 +30,13 @@ int main()
std::cout << "Enumerating endpoints..." << std::endl;
- auto filter = MidiEndpointDeviceInformationFilter::IncludeClientByteStreamNative |
- MidiEndpointDeviceInformationFilter::IncludeClientUmpNative |
- MidiEndpointDeviceInformationFilter::IncludeVirtualDeviceResponder;
+ auto filter = MidiEndpointDeviceInformationFilters::IncludeClientByteStreamNative |
+ MidiEndpointDeviceInformationFilters::IncludeClientUmpNative |
+ MidiEndpointDeviceInformationFilters::IncludeVirtualDeviceResponder;
if (includeDiagnosticsEndpoints)
{
- filter |= MidiEndpointDeviceInformationFilter::IncludeDiagnosticLoopback;
+ filter |= MidiEndpointDeviceInformationFilters::IncludeDiagnosticLoopback;
}
auto watcher = MidiEndpointDeviceWatcher::CreateWatcher(filter);
@@ -55,25 +55,34 @@ int main()
std::cout << "Initial enumeration completed." << std::endl;
};
- auto OnWatcherDeviceRemoved = [&](MidiEndpointDeviceWatcher const& /*sender*/, winrt::Windows::Devices::Enumeration::DeviceInformationUpdate const& args)
+ auto OnWatcherDeviceRemoved = [&](MidiEndpointDeviceWatcher const& /*sender*/, MidiEndpointDeviceInformationRemovedEventArgs const& args)
{
std::cout << std::endl;
std::cout << "Removed: " << winrt::to_string(args.Id()) << std::endl;
};
- auto OnWatcherDeviceUpdated = [&](MidiEndpointDeviceWatcher const& /*sender*/, MidiEndpointDeviceInformationUpdateEventArgs const& args)
+ auto OnWatcherDeviceUpdated = [&](MidiEndpointDeviceWatcher const& /*sender*/, MidiEndpointDeviceInformationUpdatedEventArgs const& args)
{
std::cout << std::endl;
std::cout << "Updated: " << winrt::to_string(args.Id()) << std::endl;
- // TODO: Show how to use the various data update flags here
+ // Show how to use the various data update flags here
+
+ if (args.UpdatedName()) std::cout << "- Name" << std::endl;
+ if (args.UpdatedUserMetadata()) std::cout << "- User Metadata" << std::endl;
+ if (args.UpdatedEndpointInformation()) std::cout << "- Endpoint Information" << std::endl;
+ if (args.UpdatedStreamConfiguration()) std::cout << "- Stream Configuration" << std::endl;
+ if (args.UpdatedFunctionBlocks()) std::cout << "- Function Blocks" << std::endl;
+ if (args.UpdatedDeviceIdentity()) std::cout << "- Device Identity" << std::endl;
+ if (args.UpdatedAdditionalCapabilities()) std::cout << "- Additional Capabilities" << std::endl;
+
};
- auto OnWatcherDeviceAdded = [&](MidiEndpointDeviceWatcher const& /*sender*/, MidiEndpointDeviceInformation const& args)
+ auto OnWatcherDeviceAdded = [&](MidiEndpointDeviceWatcher const& /*sender*/, MidiEndpointDeviceInformationAddedEventArgs const& args)
{
std::cout << std::endl;
- std::cout << "Added : " << winrt::to_string(args.Name()) << std::endl;
- std::cout << " " << winrt::to_string(args.Id()) << std::endl ;
+ std::cout << "Added : " << winrt::to_string(args.AddedDevice().Name()) << std::endl;
+ std::cout << " " << winrt::to_string(args.AddedDevice().Id()) << std::endl ;
};
diff --git a/samples/cpp-winrt/api-watch-endpoints/packages.config b/samples/cpp-winrt/api-watch-endpoints/packages.config
index 4ec483f0..9fca0dc2 100644
--- a/samples/cpp-winrt/api-watch-endpoints/packages.config
+++ b/samples/cpp-winrt/api-watch-endpoints/packages.config
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/samples/csharp-net/api-client-basics-cs/api-client-basics-cs.csproj b/samples/csharp-net/api-client-basics-cs/api-client-basics-cs.csproj
index 36132b22..64f73a3e 100644
--- a/samples/csharp-net/api-client-basics-cs/api-client-basics-cs.csproj
+++ b/samples/csharp-net/api-client-basics-cs/api-client-basics-cs.csproj
@@ -2,7 +2,7 @@
Exe
- net7.0-windows10.0.20348.0
+ net8.0-windows10.0.22621.0
sdk_client_basics_cs
enable
enable
@@ -11,7 +11,7 @@
-
+
diff --git a/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj b/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj
index 86555c4d..6a3d2b1b 100644
--- a/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj
+++ b/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj
@@ -1,7 +1,7 @@
WinExe
- net8.0-windows10.0.20348.0
+ net8.0-windows10.0.22621.0
10.0.20348.0
MidiSample.AppToAppMidi
app.manifest
@@ -27,9 +27,9 @@
-
+
-
+
diff --git a/src/api/Abstraction/BleMidiAbstraction/AbstractionState.cpp b/src/api/Abstraction/BleMidiAbstraction/AbstractionState.cpp
new file mode 100644
index 00000000..3815b3c2
--- /dev/null
+++ b/src/api/Abstraction/BleMidiAbstraction/AbstractionState.cpp
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#include "pch.h"
+
+
+AbstractionState::AbstractionState() = default;
+AbstractionState::~AbstractionState() = default;
+
+AbstractionState& AbstractionState::Current()
+{
+ // explanation: http://www.modernescpp.com/index.php/thread-safe-initialization-of-data/
+
+ static AbstractionState current;
+
+ return current;
+}
+
+
+
+HRESULT
+AbstractionState::ConstructEndpointManager()
+{
+ RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&m_endpointManager));
+
+ return S_OK;
+}
+
+
+HRESULT
+AbstractionState::ConstructConfigurationManager()
+{
+ RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&m_configurationManager));
+
+ return S_OK;
+}
diff --git a/src/api/Abstraction/BleMidiAbstraction/AbstractionState.h b/src/api/Abstraction/BleMidiAbstraction/AbstractionState.h
new file mode 100644
index 00000000..09d2fa72
--- /dev/null
+++ b/src/api/Abstraction/BleMidiAbstraction/AbstractionState.h
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+
+#pragma once
+
+// singleton
+class AbstractionState
+{
+
+public:
+ static AbstractionState& Current();
+
+ // no copying
+ AbstractionState(_In_ const AbstractionState&) = delete;
+ AbstractionState& operator=(_In_ const AbstractionState&) = delete;
+
+
+ wil::com_ptr GetEndpointManager()
+ {
+ return m_endpointManager;
+ }
+
+ wil::com_ptr GetConfigurationManager()
+ {
+ return m_configurationManager;
+ }
+
+ HRESULT Cleanup()
+ {
+ m_endpointManager.reset();
+ m_configurationManager.reset();
+
+ return S_OK;
+ }
+
+
+ HRESULT ConstructEndpointManager();
+ HRESULT ConstructConfigurationManager();
+
+
+private:
+ AbstractionState();
+ ~AbstractionState();
+
+
+ wil::com_ptr m_endpointManager;
+ wil::com_ptr m_configurationManager;
+
+ //std::shared_ptr m_endpointTable = std::make_shared();
+};
\ No newline at end of file
diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.cpp b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.cpp
index bf59609f..9e3c363a 100644
--- a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.cpp
+++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.cpp
@@ -20,14 +20,12 @@ CMidi2BluetoothMidiAbstraction::Activate(
if (__uuidof(IMidiBiDi) == Riid)
{
- OutputDebugString(L"" __FUNCTION__ " Activating IMidiBiDi");
-
TraceLoggingWrite(
MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
- __FUNCTION__ "- IMidiBiDi",
+ __FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingValue(__FUNCTION__),
- TraceLoggingPointer(this, "this")
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"IMidiBiDi", "interface")
);
wil::com_ptr_nothrow midiBiDi;
@@ -35,37 +33,52 @@ CMidi2BluetoothMidiAbstraction::Activate(
*Interface = midiBiDi.detach();
}
-
- // IMidiEndpointManager and IMidiApiEndpointManagerExtension are interfaces implemented by the same class
- // We want to make sure we're always returning the same instance for these calls
else if (__uuidof(IMidiEndpointManager) == Riid)
{
TraceLoggingWrite(
MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
- __FUNCTION__ "- IMidiEndpointManager",
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"IMidiEndpointManager", "interface")
+ );
+
+ // check to see if this is the first time we're creating the endpoint manager. If so, create it.
+ if (AbstractionState::Current().GetEndpointManager() == nullptr)
+ {
+ AbstractionState::Current().ConstructEndpointManager();
+ }
+
+ RETURN_IF_FAILED(AbstractionState::Current().GetEndpointManager()->QueryInterface(Riid, Interface));
+ }
+
+ else if (__uuidof(IMidiAbstractionConfigurationManager) == Riid)
+ {
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingValue(__FUNCTION__),
- TraceLoggingPointer(this, "this")
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"IMidiAbstractionConfigurationManager", "interface")
);
// check to see if this is the first time we're creating the endpoint manager. If so, create it.
- if (m_EndpointManager == nullptr)
+ if (AbstractionState::Current().GetConfigurationManager() == nullptr)
{
- RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&m_EndpointManager));
+ AbstractionState::Current().ConstructConfigurationManager();
}
- // TODO: Not sure if this is the right pattern for this or not. There's no detach call here, so does this leak?
- RETURN_IF_FAILED(m_EndpointManager->QueryInterface(Riid, Interface));
+ RETURN_IF_FAILED(AbstractionState::Current().GetConfigurationManager()->QueryInterface(Riid, Interface));
}
else if (__uuidof(IMidiServiceAbstractionPluginMetadataProvider) == Riid)
{
TraceLoggingWrite(
MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
- __FUNCTION__ "- IMidiServiceAbstractionPluginMetadataProvider",
+ __FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingValue(__FUNCTION__),
- TraceLoggingPointer(this, "this")
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"IMidiServiceAbstractionPluginMetadataProvider", "interface")
);
wil::com_ptr_nothrow metadataProvider;
@@ -73,10 +86,15 @@ CMidi2BluetoothMidiAbstraction::Activate(
*Interface = metadataProvider.detach();
}
-
else
{
- OutputDebugString(L"" __FUNCTION__ " Returning E_NOINTERFACE. Was an interface added that isn't handled in the Abstraction?");
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Returning E_NOINTERFACE. Was an interface added that isn't handled in the Abstraction?", "message")
+ );
return E_NOINTERFACE;
}
diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.h b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.h
index 64076c4e..d68b0620 100644
--- a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.h
+++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.h
@@ -41,7 +41,6 @@ class ATL_NO_VTABLE CMidi2BluetoothMidiAbstraction :
STDMETHOD(Activate)(_In_ REFIID, _Out_ void**);
private:
- wil::com_ptr_nothrow m_EndpointManager;
};
diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.rc b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.rc
index 8062828a..a2da001d 100644
--- a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.rc
+++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.rc
@@ -100,11 +100,11 @@ END
STRINGTABLE
BEGIN
- IDS_PROJNAME "Midi2.BluetoothMidiAbstraction"
+ IDS_PROJNAME "Midi2.BluetoothMidiAbstraction"
IDS_PLUGIN_METADATA_VERSION "1.0.0.0"
IDS_PLUGIN_METADATA_NAME "Bluetooth MIDI 1.0"
- IDS_PLUGIN_METADATA_DESCRIPTION "(Currently in Development) Provides Bluetooth MIDI 1.0 (BLE) device support for Windows MIDI Services"
+ IDS_PLUGIN_METADATA_DESCRIPTION "Provides Bluetooth MIDI 1.0 (BLE) device support for Windows MIDI Services"
IDS_PLUGIN_METADATA_AUTHOR "Microsoft"
END
diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj
index aed6a053..dff05548 100644
--- a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj
+++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj
@@ -274,6 +274,7 @@
+
@@ -293,15 +294,16 @@
+
+
-
diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj.filters b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj.filters
index 470c4153..f4935452 100644
--- a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj.filters
+++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj.filters
@@ -42,6 +42,9 @@
Source Files
+
+ Source Files
+
@@ -88,9 +91,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -100,6 +100,12 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiEndpointManager.cpp b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiEndpointManager.cpp
index 06b73baf..d3acfb1b 100644
--- a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiEndpointManager.cpp
+++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiEndpointManager.cpp
@@ -20,16 +20,6 @@ GUID AbstractionLayerGUID = ABSTRACTION_LAYER_GUID;
-#define MIDI_BLE_SERVICE_CHARACTERISTIC L"{03B80E5A-EDE8-4B33-A751-6CE34EC4C700}"
-#define MIDI_BLE_DATA_IO_CHARACTERISTIC L"{7772E5DB-3868-4112-A1A9-F2669D106BF3}"
-// Notes:
-// Write (encryption recommended, write without response is required)
-// Read (encryption recommended, respond with no payload)
-// Notify (encryption recommended)
-// Max connection interval is 15ms. Lower is better.
-
-
-
_Use_decl_annotations_
HRESULT
CMidi2BluetoothMidiEndpointManager::Initialize(
@@ -37,65 +27,107 @@ CMidi2BluetoothMidiEndpointManager::Initialize(
IUnknown* /*midiEndpointProtocolManager*/
)
{
- TraceLoggingWrite(
- MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
- __FUNCTION__,
- TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingPointer(this, "this")
- );
- RETURN_HR_IF(E_INVALIDARG, nullptr == MidiDeviceManager);
+ // temporary because this may be crashing the service on start
+ return S_OK;
- RETURN_IF_FAILED(MidiDeviceManager->QueryInterface(__uuidof(IMidiDeviceManagerInterface), (void**)&m_MidiDeviceManager));
- m_TransportAbstractionId = AbstractionLayerGUID; // this is needed so MidiSrv can instantiate the correct transport
- m_ContainerId = m_TransportAbstractionId; // we use the transport ID as the container ID for convenience
- winrt::hstring deviceSelector = bt::BluetoothLEDevice::GetDeviceSelector();
- auto requestedProperties = winrt::single_threaded_vector(
- {
- L"System.DeviceInterface.Bluetooth.DeviceAddress",
- L"System.DeviceInterface.Bluetooth.Flags",
- L"System.DeviceInterface.Bluetooth.LastConnectedTime",
- L"System.DeviceInterface.Bluetooth.Manufacturer",
- L"System.DeviceInterface.Bluetooth.ModelNumber",
- L"System.DeviceInterface.Bluetooth.ProductId",
- L"System.DeviceInterface.Bluetooth.ProductVersion",
- L"System.DeviceInterface.Bluetooth.ServiceGuid",
- L"System.DeviceInterface.Bluetooth.VendorId",
- L"System.DeviceInterface.Bluetooth.VendorIdSource",
- L"System.Devices.Connected",
- L"System.Devices.DeviceCapabilities",
- L"System.Devices.DeviceCharacteristics"
- }
- );
+ try
+ {
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+ RETURN_HR_IF_NULL(E_INVALIDARG, MidiDeviceManager);
- m_Watcher = enumeration::DeviceInformation::CreateWatcher(deviceSelector, requestedProperties);
+ RETURN_IF_FAILED(MidiDeviceManager->QueryInterface(__uuidof(IMidiDeviceManagerInterface), (void**)&m_MidiDeviceManager));
- auto deviceAddedHandler = foundation::TypedEventHandler(this, &CMidi2BluetoothMidiEndpointManager::OnDeviceAdded);
- auto deviceRemovedHandler = foundation::TypedEventHandler(this, &CMidi2BluetoothMidiEndpointManager::OnDeviceRemoved);
- auto deviceUpdatedHandler = foundation::TypedEventHandler(this, &CMidi2BluetoothMidiEndpointManager::OnDeviceUpdated);
- auto deviceStoppedHandler = foundation::TypedEventHandler(this, &CMidi2BluetoothMidiEndpointManager::OnDeviceStopped);
- auto deviceEnumerationCompletedHandler = foundation::TypedEventHandler(this, &CMidi2BluetoothMidiEndpointManager::OnEnumerationCompleted);
+ m_TransportAbstractionId = AbstractionLayerGUID; // this is needed so MidiSrv can instantiate the correct transport
+ m_ContainerId = m_TransportAbstractionId; // we use the transport ID as the container ID for convenience
- m_DeviceAdded = m_Watcher.Added(winrt::auto_revoke, deviceAddedHandler);
- m_DeviceRemoved = m_Watcher.Removed(winrt::auto_revoke, deviceRemovedHandler);
- m_DeviceUpdated = m_Watcher.Updated(winrt::auto_revoke, deviceUpdatedHandler);
- m_DeviceStopped = m_Watcher.Stopped(winrt::auto_revoke, deviceStoppedHandler);
- m_DeviceEnumerationCompleted = m_Watcher.EnumerationCompleted(winrt::auto_revoke, deviceEnumerationCompletedHandler);
+ RETURN_IF_FAILED(CreateParentDevice());
- m_Watcher.Start();
+ RETURN_IF_FAILED(StartAdvertisementWatcher());
+ //RETURN_IF_FAILED(StartDeviceWatcher());
- return S_OK;
+ return S_OK;
+ }
+ catch (std::exception ex)
+ {
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingString(ex.what(), "exception"),
+ TraceLoggingWideString(L"Exception initializing BLE MIDI Endpoint Manager", "message")
+ );
+
+ return E_FAIL;
+ }
+ catch (...)
+ {
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Unknown exception initializing BLE MIDI Endpoint Manager", "message")
+ );
+
+ return E_FAIL;
+ }
}
HRESULT
CMidi2BluetoothMidiEndpointManager::CreateParentDevice()
{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ // the parent device parameters are set by the transport (this)
+ std::wstring parentDeviceName{ TRANSPORT_PARENT_DEVICE_NAME };
+ std::wstring parentDeviceId{ internal::NormalizeDeviceInstanceIdWStringCopy(TRANSPORT_PARENT_ID) };
+
+ SW_DEVICE_CREATE_INFO createInfo = {};
+ createInfo.cbSize = sizeof(createInfo);
+ createInfo.pszInstanceId = parentDeviceId.c_str();
+ createInfo.CapabilityFlags = SWDeviceCapabilitiesNone;
+ createInfo.pszDeviceDescription = parentDeviceName.c_str();
+ createInfo.pContainerId = &m_ContainerId;
+
+ const ULONG deviceIdMaxSize = 255;
+ wchar_t newDeviceId[deviceIdMaxSize]{ 0 };
+
+ RETURN_IF_FAILED(m_MidiDeviceManager->ActivateVirtualParentDevice(
+ 0,
+ nullptr,
+ &createInfo,
+ (PWSTR)newDeviceId,
+ deviceIdMaxSize
+ ));
+
+ m_parentDeviceId = internal::NormalizeDeviceInstanceIdWStringCopy(newDeviceId);
+
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(newDeviceId, "New parent device instance id")
+ );
return S_OK;
}
@@ -104,12 +136,122 @@ CMidi2BluetoothMidiEndpointManager::CreateParentDevice()
_Use_decl_annotations_
HRESULT
CMidi2BluetoothMidiEndpointManager::CreateEndpoint(
- MidiBluetoothDeviceDefinition& definition
+ MidiBluetoothDeviceDefinition* definition
)
{
- UNREFERENCED_PARAMETER(definition);
+ RETURN_HR_IF_NULL_MSG(E_INVALIDARG, definition, "Empty endpoint definition");
+
+ //RETURN_HR_IF_MSG(E_INVALIDARG, definition->EndpointName.empty(), "Empty endpoint name");
+ //RETURN_HR_IF_MSG(E_INVALIDARG, definition->InstanceIdPrefix.empty(), "Empty endpoint prefix");
+ //RETURN_HR_IF_MSG(E_INVALIDARG, definition->EndpointUniqueIdentifier.empty(), "Empty endpoint unique id");
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(definition->BluetoothAddress, "bluetooth address")
+ );
+
+ std::wstring mnemonic(TRANSPORT_MNEMONIC);
+
+ // TODO: Need to fold in user data here
+ std::wstring endpointName = definition->TransportSuppliedName.c_str();
+ std::wstring endpointDescription = definition->TransportSuppliedDescription.c_str();
+
+ std::vector interfaceDeviceProperties{};
+
+ // no user or in-protocol data in this case
+ std::wstring friendlyName = internal::CalculateEndpointDevicePrimaryName(endpointName, L"", L"");
+
+
+ bool requiresMetadataHandler = false;
+ bool multiClient = true;
+ bool generateIncomingTimestamps = true;
+ std::wstring uniqueIdentifier = std::to_wstring(definition->BluetoothAddress);
+ // Device properties
+
+
+ SW_DEVICE_CREATE_INFO createInfo = {};
+ createInfo.cbSize = sizeof(createInfo);
+
+ // build the instance id, which becomes the middle of the SWD id
+ std::wstring instanceId = internal::NormalizeDeviceInstanceIdWStringCopy(
+ MIDI_BLE_INSTANCE_ID_PREFIX +
+ uniqueIdentifier);
+
+ createInfo.pszInstanceId = instanceId.c_str();
+ createInfo.CapabilityFlags = SWDeviceCapabilitiesNone;
+ createInfo.pszDeviceDescription = friendlyName.c_str();
+
+ const ULONG deviceInterfaceIdMaxSize = 255;
+ wchar_t newDeviceInterfaceId[deviceInterfaceIdMaxSize]{ 0 };
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(instanceId.c_str(), "instance id"),
+ TraceLoggingWideString(L"Activating endpoint", "message")
+ );
+
+ MIDIENDPOINTCOMMONPROPERTIES commonProperties{};
+ commonProperties.AbstractionLayerGuid = m_TransportAbstractionId;
+ commonProperties.EndpointPurpose = MidiEndpointDevicePurposePropertyValue::NormalMessageEndpoint;
+ commonProperties.FriendlyName = friendlyName.c_str();
+ commonProperties.TransportMnemonic = mnemonic.c_str();
+ commonProperties.TransportSuppliedEndpointName = endpointName.c_str();
+ commonProperties.TransportSuppliedEndpointDescription = endpointDescription.c_str();
+ commonProperties.UserSuppliedEndpointName = nullptr;
+ commonProperties.UserSuppliedEndpointDescription = nullptr;
+ commonProperties.UniqueIdentifier = uniqueIdentifier.c_str();
+ commonProperties.SupportedDataFormats = MidiDataFormat::MidiDataFormat_ByteStream;
+ commonProperties.NativeDataFormat = MIDI_PROP_NATIVEDATAFORMAT_BYTESTREAM;
+ commonProperties.SupportsMultiClient = multiClient;
+ commonProperties.RequiresMetadataHandler = requiresMetadataHandler;
+ commonProperties.GenerateIncomingTimestamps = generateIncomingTimestamps;
+ commonProperties.SupportsMidi1ProtocolDefaultValue = true;
+ commonProperties.SupportsMidi2ProtocolDefaultValue = false;
+
+ if (definition->ManufacturerName.empty())
+ {
+ commonProperties.ManufacturerName = nullptr;
+ }
+ else
+ {
+ commonProperties.ManufacturerName = definition->ManufacturerName.c_str();
+ }
+
+ RETURN_IF_FAILED(m_MidiDeviceManager->ActivateEndpoint(
+ (PCWSTR)m_parentDeviceId.c_str(), // parent instance Id
+ true, // UMP-only
+ MidiFlow::MidiFlowBidirectional, // MIDI Flow is bidirectional for a BLE MIDI 1.0 device
+ &commonProperties,
+ (ULONG)interfaceDeviceProperties.size(),
+ (ULONG)0,
+ (PVOID)interfaceDeviceProperties.data(),
+ (PVOID)nullptr,
+ (PVOID)&createInfo,
+ (LPWSTR)&newDeviceInterfaceId,
+ deviceInterfaceIdMaxSize));
+
+ // we need this for removal later
+ definition->CreatedMidiDeviceInstanceId = instanceId;
+ definition->CreatedEndpointInterfaceId = internal::NormalizeEndpointInterfaceIdWStringCopy(newDeviceInterfaceId);
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(newDeviceInterfaceId, "new device interface id"),
+ TraceLoggingWideString(instanceId.c_str(), "instance id"),
+ TraceLoggingWideString(uniqueIdentifier.c_str(), "unique identifier"),
+ TraceLoggingWideString(L"Endpoint activated", "message")
+ );
return S_OK;
}
@@ -127,6 +269,22 @@ CMidi2BluetoothMidiEndpointManager::Cleanup()
TraceLoggingPointer(this, "this")
);
+ if (m_bleAdWatcher != nullptr)
+ {
+ m_bleAdWatcher.Stop();
+ m_bleAdWatcher = nullptr;
+
+ m_AdvertisementReceived.revoke();
+ m_AdvertisementWatcherStopped.revoke();
+ }
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Cleanup complete", "message")
+ );
return S_OK;
}
@@ -138,24 +296,60 @@ CMidi2BluetoothMidiEndpointManager::Cleanup()
_Use_decl_annotations_
HRESULT
-CMidi2BluetoothMidiEndpointManager::OnDeviceAdded(enumeration::DeviceWatcher, enumeration::DeviceInformation)
+CMidi2BluetoothMidiEndpointManager::OnDeviceAdded(enumeration::DeviceWatcher /*source*/, enumeration::DeviceInformation /*args*/)
{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
return S_OK;
}
_Use_decl_annotations_
HRESULT
-CMidi2BluetoothMidiEndpointManager::OnDeviceRemoved(enumeration::DeviceWatcher, enumeration::DeviceInformationUpdate)
+CMidi2BluetoothMidiEndpointManager::OnDeviceRemoved(enumeration::DeviceWatcher /*source*/, enumeration::DeviceInformationUpdate /*args*/)
{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+HRESULT
+CMidi2BluetoothMidiEndpointManager::OnDeviceUpdated(enumeration::DeviceWatcher /*source*/, enumeration::DeviceInformationUpdate /*args*/)
+{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
return S_OK;
}
+
_Use_decl_annotations_
HRESULT
-CMidi2BluetoothMidiEndpointManager::OnDeviceUpdated(enumeration::DeviceWatcher, enumeration::DeviceInformationUpdate)
+CMidi2BluetoothMidiEndpointManager::OnDeviceStopped(enumeration::DeviceWatcher /*source*/, foundation::IInspectable /*args*/)
{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
return S_OK;
}
@@ -163,17 +357,402 @@ CMidi2BluetoothMidiEndpointManager::OnDeviceUpdated(enumeration::DeviceWatcher,
_Use_decl_annotations_
HRESULT
-CMidi2BluetoothMidiEndpointManager::OnDeviceStopped(enumeration::DeviceWatcher, foundation::IInspectable)
+CMidi2BluetoothMidiEndpointManager::OnEnumerationCompleted(enumeration::DeviceWatcher /*source*/, foundation::IInspectable /*args*/)
+{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+
+ return S_OK;
+}
+
+
+
+
+// TODO: This likely isn't the correct event signature
+
+_Use_decl_annotations_
+HRESULT
+CMidi2BluetoothMidiEndpointManager::OnBleDeviceNameChanged(bt::BluetoothLEDevice device, foundation::IInspectable /*args*/)
{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
return S_OK;
}
_Use_decl_annotations_
+HRESULT
+CMidi2BluetoothMidiEndpointManager::OnBleDeviceConnectionStatusChanged(bt::BluetoothLEDevice device, foundation::IInspectable /*args*/)
+{
+ bool connected = (device.ConnectionStatus() == bt::BluetoothConnectionStatus::Connected);
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(device.BluetoothAddress(), "address"),
+ TraceLoggingWideString(device.DeviceId().c_str(), "device id"),
+ TraceLoggingWideString(device.Name().c_str(), "name"),
+ TraceLoggingBool(connected, "is connected")
+ );
+
+ // TODO: When the device was first seen by the service, it may have already been connected, so
+ // we need to handle that even though we won't have a connection status changed event
+
+ auto entry = MidiEndpointTable::Current().GetEndpointEntryForBluetoothAddress(device.BluetoothAddress());
+
+ if (entry == nullptr)
+ {
+ // this device is new to us. This should never happen
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(device.BluetoothAddress(), "address"),
+ TraceLoggingWideString(device.DeviceId().c_str(), "device id"),
+ TraceLoggingWideString(device.Name().c_str(), "name"),
+ TraceLoggingBool(connected, "is connected"),
+ TraceLoggingWideString(L"Received a connection changed notification for a device we didn't have recorded.", "message")
+ );
+
+ return E_FAIL;
+ }
+
+ if (connected)
+ {
+ // Connected. check to see if we've already created the SWD. If not, create it.
+ if (entry->Definition.CreatedEndpointInterfaceId.empty())
+ {
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(device.BluetoothAddress(), "address"),
+ TraceLoggingWideString(device.DeviceId().c_str(), "device id"),
+ TraceLoggingWideString(device.Name().c_str(), "name"),
+ TraceLoggingBool(connected, "is connected"),
+ TraceLoggingWideString(L"Creating new SWD / endpoint", "message")
+ );
+
+ RETURN_IF_FAILED(CreateEndpoint(&(entry->Definition)));
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(device.BluetoothAddress(), "address"),
+ TraceLoggingWideString(device.DeviceId().c_str(), "device id"),
+ TraceLoggingWideString(device.Name().c_str(), "name"),
+ TraceLoggingBool(connected, "is connected"),
+ TraceLoggingWideString(L"Endpoint created", "message")
+ );
+ }
+ else
+ {
+ // we've already created this device and it is currently connected. This shouldn't happen.
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(device.BluetoothAddress(), "address"),
+ TraceLoggingWideString(device.DeviceId().c_str(), "device id"),
+ TraceLoggingWideString(device.Name().c_str(), "name"),
+ TraceLoggingBool(connected, "is connected"),
+ TraceLoggingWideString(L"The last state we captured was that this device was connected. Shouldn't have received a change notification.", "message")
+ );
+ }
+ }
+ else
+ {
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(device.BluetoothAddress(), "address"),
+ TraceLoggingWideString(device.DeviceId().c_str(), "device id"),
+ TraceLoggingWideString(device.Name().c_str(), "name"),
+ TraceLoggingBool(connected, "is connected"),
+ TraceLoggingWideString(L"Deactivating SWD", "message")
+ );
+
+ // Disconnected. Remove the SWD.
+
+ std::wstring instanceId = entry->Definition.CreatedMidiDeviceInstanceId;
+
+ if (!instanceId.empty())
+ {
+ RETURN_IF_FAILED(m_MidiDeviceManager->DeactivateEndpoint(instanceId.c_str()));
+
+ entry->Definition.SetDeactivated();
+ entry->MidiDeviceBiDi = nullptr;
+ }
+ else
+ {
+ // TODO Log
+ }
+ }
+
+ return S_OK;
+}
+
+
+
+
+_Use_decl_annotations_
+HRESULT
+CMidi2BluetoothMidiEndpointManager::OnBleAdvertisementReceived(
+ ad::BluetoothLEAdvertisementWatcher /*source*/,
+ ad::BluetoothLEAdvertisementReceivedEventArgs const& args)
+{
+ // Check to see if we already know about this device. If so, ignore
+
+ if (MidiEndpointTable::Current().GetEndpointEntryForBluetoothAddress(args.BluetoothAddress()) != nullptr)
+ {
+ // we already know about this device. Exit
+
+ return S_OK;
+ }
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(args.BluetoothAddress(), "address"),
+ TraceLoggingWideString(L"New BLE device found via advertisement", "message")
+ );
+
+ // TODO
+ // If we don't know about it, check to see if it's a device on our allowed list
+ // If so, we can create a device entry for it. If not, ignore.
+ // We may want a ble-wide policy option to allow any MIDI devices we see, vs deny by default. Default is deny all.
+
+
+ auto device = bt::BluetoothLEDevice::FromBluetoothAddressAsync(args.BluetoothAddress()).get();
+ RETURN_HR_IF_NULL_MSG(E_UNEXPECTED, device, "Bluetooth device address is invalid.");
+
+ // get the GATT MIDI service
+ auto service = GetBleMidiServiceFromDevice(device);
+
+ if (service != nullptr)
+ {
+ auto connectionStatusChangedHandler = foundation::TypedEventHandler
+ (this, &CMidi2BluetoothMidiEndpointManager::OnBleDeviceConnectionStatusChanged);
+
+ // create the device definition
+ MidiBluetoothDeviceDefinition definition;
+
+ definition.BluetoothAddress = device.BluetoothAddress();
+ definition.TransportSuppliedName = device.Name(); // need to ensure we also wire up the name changed event handler for this
+
+ std::shared_ptr entry{ nullptr };
+
+ // todo: Should lock the entry list during this
+
+
+
+
+ // add the new endpoint to the device table
+ entry = MidiEndpointTable::Current().CreateAndAddNewEndpointEntry(definition, device, service);
+
+ if (entry != nullptr)
+ {
+ // create the MIDI endpoint if this is connected.
+ if (entry->Device.ConnectionStatus() == bt::BluetoothConnectionStatus::Connected)
+ {
+ RETURN_IF_FAILED(CreateEndpoint(&(entry->Definition)));
+ }
+
+ // wire up connection status changed event and store the token for revocation
+// entry->ConnectionStatusChangedToken = device.ConnectionStatusChanged(winrt::auto_revoke, connectionStatusChangedHandler);
+ //entry->ConnectionStatusChangedRevoker = entry->Device.ConnectionStatusChanged(winrt::auto_revoke, connectionStatusChangedHandler);
+ entry->ConnectionStatusChangedRevoker = entry->Device.ConnectionStatusChanged(connectionStatusChangedHandler);
+ }
+ else
+ {
+ // failed to just add a new entry. Log
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(device.BluetoothAddress(), "address"),
+ TraceLoggingWideString(device.DeviceId().c_str(), "device id"),
+ TraceLoggingWideString(device.Name().c_str(), "name"),
+ TraceLoggingWideString(L"Failed to add entry to endpoint table", "message")
+ );
+ }
+ }
+ else
+ {
+ // no BLE MIDI service found. Log this because the filter should have prevented this.
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(device.BluetoothAddress(), "address"),
+ TraceLoggingWideString(device.DeviceId().c_str(), "device id"),
+ TraceLoggingWideString(device.Name().c_str(), "name"),
+ TraceLoggingWideString(L"BLE device does not have MIDI service", "message")
+ );
+ }
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+HRESULT
+CMidi2BluetoothMidiEndpointManager::OnBleAdvertisementWatcherStopped(ad::BluetoothLEAdvertisementWatcher, ad::BluetoothLEAdvertisementWatcherStoppedEventArgs args)
+{
+ // error enum vals: https://learn.microsoft.com/uwp/api/windows.devices.bluetooth.bluetootherror?view=winrt-22621
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt32((uint32_t)args.Error(), "BluetoothError enum value")
+ );
+
+ // todo: consider if we should restart the watcher or not
+
+ return S_OK;
+}
+
+
HRESULT
-CMidi2BluetoothMidiEndpointManager::OnEnumerationCompleted(enumeration::DeviceWatcher, foundation::IInspectable)
+CMidi2BluetoothMidiEndpointManager::StartAdvertisementWatcher()
{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ auto adReceivedHandler = foundation::TypedEventHandler
+ (this, &CMidi2BluetoothMidiEndpointManager::OnBleAdvertisementReceived);
+
+ auto adWatcherStoppedHandler = foundation::TypedEventHandler
+ (this, &CMidi2BluetoothMidiEndpointManager::OnBleAdvertisementWatcherStopped);
+
+
+ // wire up the event handler so we're notified when advertising messages are received.
+ // This will fire for every message received, even if we already know about the device.
+ m_AdvertisementReceived = m_bleAdWatcher.Received(winrt::auto_revoke, adReceivedHandler);
+ m_AdvertisementWatcherStopped = m_bleAdWatcher.Stopped(winrt::auto_revoke, adWatcherStoppedHandler);
+
+ winrt::guid serviceUuid{ MIDI_BLE_SERVICE_UUID };
+
+ m_bleAdWatcher.AdvertisementFilter().Advertisement().ServiceUuids().Append(serviceUuid);
+ m_bleAdWatcher.Start();
+
+ if (m_bleAdWatcher.Status() == ad::BluetoothLEAdvertisementWatcherStatus::Aborted)
+ {
+ // if the status is Aborted, it means there was an error
+
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Error starting BLE ad watcher", "message")
+ );
+
+ return E_FAIL;
+ }
+ else
+ {
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"BLE Ad watcher started", "message")
+ );
+
+ return S_OK;
+ }
+}
+
+HRESULT
+CMidi2BluetoothMidiEndpointManager::StartDeviceWatcher()
+{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ winrt::hstring deviceSelector = bt::BluetoothLEDevice::GetDeviceSelector();
+
+
+ auto requestedProperties = winrt::single_threaded_vector(
+ {
+ L"System.DeviceInterface.Bluetooth.DeviceAddress",
+ L"System.DeviceInterface.Bluetooth.Flags",
+ L"System.DeviceInterface.Bluetooth.LastConnectedTime",
+ L"System.DeviceInterface.Bluetooth.Manufacturer",
+ L"System.DeviceInterface.Bluetooth.ModelNumber",
+ L"System.DeviceInterface.Bluetooth.ProductId",
+ L"System.DeviceInterface.Bluetooth.ProductVersion",
+ L"System.DeviceInterface.Bluetooth.ServiceGuid",
+ L"System.DeviceInterface.Bluetooth.VendorId",
+ L"System.DeviceInterface.Bluetooth.VendorIdSource",
+ L"System.Devices.Connected",
+ L"System.Devices.DeviceCapabilities",
+ L"System.Devices.DeviceCharacteristics"
+ }
+ );
+
+
+ m_Watcher = enumeration::DeviceInformation::CreateWatcher(deviceSelector, requestedProperties);
+
+ auto deviceAddedHandler = foundation::TypedEventHandler(this, &CMidi2BluetoothMidiEndpointManager::OnDeviceAdded);
+ auto deviceRemovedHandler = foundation::TypedEventHandler(this, &CMidi2BluetoothMidiEndpointManager::OnDeviceRemoved);
+ auto deviceUpdatedHandler = foundation::TypedEventHandler(this, &CMidi2BluetoothMidiEndpointManager::OnDeviceUpdated);
+ auto deviceStoppedHandler = foundation::TypedEventHandler(this, &CMidi2BluetoothMidiEndpointManager::OnDeviceStopped);
+ auto deviceEnumerationCompletedHandler = foundation::TypedEventHandler(this, &CMidi2BluetoothMidiEndpointManager::OnEnumerationCompleted);
+
+ m_DeviceAdded = m_Watcher.Added(winrt::auto_revoke, deviceAddedHandler);
+ m_DeviceRemoved = m_Watcher.Removed(winrt::auto_revoke, deviceRemovedHandler);
+ m_DeviceUpdated = m_Watcher.Updated(winrt::auto_revoke, deviceUpdatedHandler);
+ m_DeviceStopped = m_Watcher.Stopped(winrt::auto_revoke, deviceStoppedHandler);
+ m_DeviceEnumerationCompleted = m_Watcher.EnumerationCompleted(winrt::auto_revoke, deviceEnumerationCompletedHandler);
+
+
+ m_Watcher.Start();
return S_OK;
}
+
+HRESULT
+CMidi2BluetoothMidiEndpointManager::CreateSelfPeripheralEndpoint()
+{
+
+
+
+ return S_OK;
+}
\ No newline at end of file
diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiEndpointManager.h b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiEndpointManager.h
index ec6a1396..436052f3 100644
--- a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiEndpointManager.h
+++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiEndpointManager.h
@@ -8,6 +8,12 @@
#pragma once
+// Notes:
+// Write (encryption recommended, write without response is required)
+// Read (encryption recommended, respond with no payload)
+// Notify (encryption recommended)
+// Max connection interval is 15ms. Lower is better.
+
class CMidi2BluetoothMidiEndpointManager :
public Microsoft::WRL::RuntimeClass<
@@ -21,7 +27,15 @@ class CMidi2BluetoothMidiEndpointManager :
private:
- enumeration::DeviceWatcher m_Watcher{ nullptr };
+// winrt::guid m_midiBleServiceUuid{ MIDI_BLE_SERVICE_UUID };
+ wil::com_ptr_nothrow m_MidiDeviceManager;
+
+ GUID m_ContainerId{};
+ GUID m_TransportAbstractionId{};
+ std::wstring m_parentDeviceId{};
+
+ enumeration::DeviceWatcher m_Watcher{ nullptr }; // for non-advertising but paired devices
+ ad::BluetoothLEAdvertisementWatcher m_bleAdWatcher; // for advertised devices, which is common for BLE MIDI
winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Added_revoker m_DeviceAdded;
winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Removed_revoker m_DeviceRemoved;
@@ -29,23 +43,32 @@ class CMidi2BluetoothMidiEndpointManager :
winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Stopped_revoker m_DeviceStopped;
winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::EnumerationCompleted_revoker m_DeviceEnumerationCompleted;
+ winrt::impl::consume_Windows_Devices_Bluetooth_Advertisement_IBluetoothLEAdvertisementWatcher::Received_revoker m_AdvertisementReceived;
+ winrt::impl::consume_Windows_Devices_Bluetooth_Advertisement_IBluetoothLEAdvertisementWatcher::Stopped_revoker m_AdvertisementWatcherStopped;
+
+
HRESULT OnDeviceAdded(_In_ enumeration::DeviceWatcher, _In_ enumeration::DeviceInformation);
HRESULT OnDeviceRemoved(_In_ enumeration::DeviceWatcher, _In_ enumeration::DeviceInformationUpdate);
HRESULT OnDeviceUpdated(_In_ enumeration::DeviceWatcher, _In_ enumeration::DeviceInformationUpdate);
HRESULT OnDeviceStopped(_In_ enumeration::DeviceWatcher, _In_ foundation::IInspectable);
HRESULT OnEnumerationCompleted(_In_ enumeration::DeviceWatcher, _In_ foundation::IInspectable);
+ HRESULT OnBleAdvertisementReceived(_In_ ad::BluetoothLEAdvertisementWatcher, _In_ ad::BluetoothLEAdvertisementReceivedEventArgs const&);
+ HRESULT OnBleAdvertisementWatcherStopped(_In_ ad::BluetoothLEAdvertisementWatcher, _In_ ad::BluetoothLEAdvertisementWatcherStoppedEventArgs);
- GUID m_ContainerId{};
- GUID m_TransportAbstractionId{};
+ HRESULT OnBleDeviceConnectionStatusChanged(_In_ bt::BluetoothLEDevice /*device*/, _In_ foundation::IInspectable /*args*/);
+ HRESULT OnBleDeviceNameChanged(_In_ bt::BluetoothLEDevice /*device*/, _In_ foundation::IInspectable /*args*/);
HRESULT CreateEndpoint(
- _In_ MidiBluetoothDeviceDefinition& definition
+ _In_ MidiBluetoothDeviceDefinition* definition
);
- HRESULT EnumCompatibleBluetoothDevices();
+// HRESULT EnumCompatibleBluetoothDevices();
- HRESULT CreateParentDevice();
+ HRESULT StartAdvertisementWatcher();
+ HRESULT StartDeviceWatcher();
- wil::com_ptr_nothrow m_MidiDeviceManager;
+ HRESULT CreateSelfPeripheralEndpoint();
+
+ HRESULT CreateParentDevice();
};
diff --git a/src/api/Abstraction/BleMidiAbstraction/MidiBluetoothDeviceDefinition.h b/src/api/Abstraction/BleMidiAbstraction/MidiBluetoothDeviceDefinition.h
index 8b78d4c3..6c31fd47 100644
--- a/src/api/Abstraction/BleMidiAbstraction/MidiBluetoothDeviceDefinition.h
+++ b/src/api/Abstraction/BleMidiAbstraction/MidiBluetoothDeviceDefinition.h
@@ -11,11 +11,24 @@
struct MidiBluetoothDeviceDefinition
{
- std::wstring HardwareBluetoothDeviceId{};
+ uint64_t BluetoothAddress{};
+ winrt::hstring TransportSuppliedName{};
+ winrt::hstring TransportSuppliedDescription{};
- std::wstring TransportSuppliedName{};
- std::wstring TransportSuppliedDescription{};
+ winrt::hstring ManufacturerName{};
+
+// winrt::hstring UserSuppliedName{};
+// winrt::hstring UserSuppliedDescription{};
std::wstring CreatedEndpointInterfaceId{};
+ std::wstring CreatedMidiDeviceInstanceId{};
+
+
+ void SetDeactivated()
+ {
+ CreatedEndpointInterfaceId = L"";
+ CreatedMidiDeviceInstanceId = L"";
+ }
+
};
\ No newline at end of file
diff --git a/src/api/Abstraction/BleMidiAbstraction/MidiEndpointTable.cpp b/src/api/Abstraction/BleMidiAbstraction/MidiEndpointTable.cpp
index 23ef796a..6869b39c 100644
--- a/src/api/Abstraction/BleMidiAbstraction/MidiEndpointTable.cpp
+++ b/src/api/Abstraction/BleMidiAbstraction/MidiEndpointTable.cpp
@@ -22,20 +22,66 @@ MidiEndpointTable& MidiEndpointTable::Current()
}
+
+
_Use_decl_annotations_
-wil::com_ptr_nothrow
-MidiEndpointTable::GetEndpointInterfaceForId(
- std::wstring const EndpointDeviceId
-) const noexcept
+std::shared_ptr
+MidiEndpointTable::GetEndpointEntryForBluetoothAddress(uint64_t const bluetoothAddress) const noexcept
{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(bluetoothAddress, "address")
+ );
+
try
{
- auto result = m_Endpoints.find(EndpointDeviceId);
-
- if (result != m_Endpoints.end())
- return result->second.MidiDeviceBiDi;
+ if (auto it = m_endpoints.find(bluetoothAddress); it != m_endpoints.end())
+ {
+ return it->second;
+ }
else
+ {
return nullptr;
+ }
+ }
+ catch (...)
+ {
+ return nullptr;
+ }
+}
+
+
+_Use_decl_annotations_
+std::shared_ptr
+MidiEndpointTable::CreateAndAddNewEndpointEntry(
+ MidiBluetoothDeviceDefinition definition,
+ bt::BluetoothLEDevice device,
+ gatt::GattDeviceService service
+) noexcept
+{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(definition.BluetoothAddress, "address")
+ );
+
+ try
+ {
+ MidiBluetoothEndpointEntry entry;
+
+ entry.Definition = definition;
+ entry.MidiDeviceBiDi = nullptr;
+ entry.Device = device;
+ entry.Service = service;
+
+ m_endpoints[definition.BluetoothAddress] = std::make_shared(entry);
+
+ return m_endpoints[definition.BluetoothAddress];
}
catch (...)
{
@@ -44,19 +90,29 @@ MidiEndpointTable::GetEndpointInterfaceForId(
}
+
+
_Use_decl_annotations_
void
MidiEndpointTable::RemoveEndpointEntry(
- std::wstring EndpointDeviceId
+ uint64_t bluetoothAddress
) noexcept
{
+ TraceLoggingWrite(
+ MidiBluetoothMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingUInt64(bluetoothAddress, "address")
+ );
+
try
{
- auto result = m_Endpoints.find(EndpointDeviceId);
+ auto result = m_endpoints.find(bluetoothAddress);
- if (result != m_Endpoints.end())
+ if (result != m_endpoints.end())
{
- m_Endpoints.erase(result);
+ m_endpoints.erase(result);
}
}
catch (...)
diff --git a/src/api/Abstraction/BleMidiAbstraction/MidiEndpointTable.h b/src/api/Abstraction/BleMidiAbstraction/MidiEndpointTable.h
index 66034dc7..58f687af 100644
--- a/src/api/Abstraction/BleMidiAbstraction/MidiEndpointTable.h
+++ b/src/api/Abstraction/BleMidiAbstraction/MidiEndpointTable.h
@@ -13,22 +13,36 @@
// thread-safe meyers singleton for storing the devices we'll use
-struct MidiVirtualEndpointEntry
+struct MidiBluetoothEndpointEntry
{
- GUID JsonKey; // this is the key that is used in config before there's any SWD to refer to
+ MidiBluetoothDeviceDefinition Definition{};
- std::wstring InstanceId; // the part of the instance ID we (mostly) control
+ bt::BluetoothLEDevice Device{ nullptr };
+ gatt::GattDeviceService Service{ nullptr };
- std::wstring EndpointDeviceId; // the device interface id
+ wil::com_ptr_nothrow MidiDeviceBiDi{ nullptr };
- wil::com_ptr_nothrow MidiDeviceBiDi;
+ //winrt::event_token ConnectionStatusChangedRevoker;
- ~MidiVirtualEndpointEntry()
+ //bt::BluetoothLEDevice::ConnectionParametersChanged_revoker ConnectionStatusChangedRevoker{ nullptr };
+ //winrt::impl::consume_Windows_Devices_Bluetooth_IBluetoothLEDevice::ConnectionStatusChanged_revoker ConnectionStatusChangedRevoker{ nullptr };
+// winrt::impl::consume_Windows_Devices_Bluetooth_IBluetoothLEDevice::ConnectionStatusChanged_revoker ConnectionStatusChangedRevoker;
+
+ winrt::event_token ConnectionStatusChangedRevoker;
+
+ MidiBluetoothEndpointEntry() = default;
+ ~MidiBluetoothEndpointEntry()
{
if (MidiDeviceBiDi)
{
+ MidiDeviceBiDi->Cleanup();
MidiDeviceBiDi.reset();
}
+
+ if (Device != nullptr)
+ {
+ Device.ConnectionStatusChanged(ConnectionStatusChangedRevoker);
+ }
}
};
@@ -36,28 +50,24 @@ struct MidiVirtualEndpointEntry
class MidiEndpointTable
{
public:
- //std::shared_ptr GetDevice(std::wstring deviceId);
- //MidiLoopbackDevice* GetBidiDevice();
- //MidiLoopbackDevice* GetInOutDevice();
-
static MidiEndpointTable& Current();
// no copying
MidiEndpointTable(_In_ const MidiEndpointTable&) = delete;
MidiEndpointTable& operator=(_In_ const MidiEndpointTable&) = delete;
+// wil::com_ptr_nothrow GetEndpointInterfaceForId(_In_ std::wstring const EndpointDeviceId) const noexcept;
+
+ std::shared_ptr GetEndpointEntryForBluetoothAddress(_In_ uint64_t const bluetoothAddress) const noexcept;
- wil::com_ptr_nothrow GetEndpointInterfaceForId(_In_ std::wstring EndpointDeviceId) const noexcept;
- void RemoveEndpointEntry(_In_ std::wstring EndpointDeviceId) noexcept;
+ std::shared_ptr CreateAndAddNewEndpointEntry(_In_ MidiBluetoothDeviceDefinition definition, _In_ bt::BluetoothLEDevice device, _In_ gatt::GattDeviceService service) noexcept;
+
+ void RemoveEndpointEntry(_In_ uint64_t bluetoothAddress) noexcept;
private:
MidiEndpointTable();
~MidiEndpointTable();
- // key is EndpointDeviceId (the device interface id)
- std::map m_Endpoints;
-
-
- //MidiLoopbackDevice m_BidiDevice;
- //MidiLoopbackDevice m_InOutDevice;
+ std::map> m_endpoints;
+ //std::vector m_endpoints;
};
\ No newline at end of file
diff --git a/src/api/Abstraction/BleMidiAbstraction/abstraction_defs.h b/src/api/Abstraction/BleMidiAbstraction/abstraction_defs.h
index 61290a64..f88f8d05 100644
--- a/src/api/Abstraction/BleMidiAbstraction/abstraction_defs.h
+++ b/src/api/Abstraction/BleMidiAbstraction/abstraction_defs.h
@@ -15,10 +15,11 @@
// The full Id comes back from the swdevicecreate callback
#define TRANSPORT_MNEMONIC L"BLE1"
-
-// TODO: Names should be moved to .rc for localization
+#define MIDI_BLE_INSTANCE_ID_PREFIX L"MIDIU_BLE1_"
#define TRANSPORT_PARENT_ID L"MIDIU_BLE1_TRANSPORT"
+
+// TODO: Names should be moved to .rc for localization
#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 1.0 Bluetooth Devices"
diff --git a/src/api/Abstraction/BleMidiAbstraction/ble_utilities.h b/src/api/Abstraction/BleMidiAbstraction/ble_utilities.h
new file mode 100644
index 00000000..a6bdf3b5
--- /dev/null
+++ b/src/api/Abstraction/BleMidiAbstraction/ble_utilities.h
@@ -0,0 +1,72 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#pragma once
+
+#define MIDI_BLE_SERVICE_UUID L"03B80E5A-EDE8-4B33-A751-6CE34EC4C700"
+#define MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID L"7772E5DB-3868-4112-A1A9-F2669D106BF3"
+
+
+inline bt::BluetoothLEDevice GetBleDeviceFromEnumerationDeviceId(_In_ std::wstring deviceId)
+{
+ // std::cout << __FUNCTION__ << std::endl;
+
+ auto device = bt::BluetoothLEDevice::FromIdAsync(winrt::to_hstring(deviceId.c_str())).get();
+
+ return device;
+}
+
+
+inline gatt::GattDeviceService GetBleMidiServiceFromDevice(_In_ bt::BluetoothLEDevice bleDevice)
+{
+ // std::cout << __FUNCTION__ << std::endl;
+
+ winrt::guid serviceUuid{ MIDI_BLE_SERVICE_UUID };
+
+ auto gattServicesResult = bleDevice.GetGattServicesAsync(bt::BluetoothCacheMode::Uncached).get();
+
+ if (gattServicesResult.Status() == gatt::GattCommunicationStatus::Success)
+ {
+ auto services = gattServicesResult.Services();
+
+ // find the MIDI BLE service
+ if (auto it = std::find_if(services.begin(), services.end(), [&](gatt::GattDeviceService const& x) { return x.Uuid() == serviceUuid; }); it != services.end())
+ {
+ // just grab the first one. Shouldn't be more than one match.
+ auto midiService = *it;
+
+ return midiService;
+ }
+ }
+
+ return nullptr;
+}
+
+
+
+inline gatt::GattCharacteristic GetBleMidiDataIOCharacteristicFromService(_In_ gatt::GattDeviceService midiService)
+{
+ // std::cout << __FUNCTION__ << std::endl;
+
+ winrt::guid dataIOCharacteristicUuid{ MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID };
+
+ if (midiService != nullptr)
+ {
+ auto characteristicsResult = midiService.GetCharacteristicsForUuidAsync(dataIOCharacteristicUuid).get();
+
+ if (characteristicsResult.Status() == gatt::GattCommunicationStatus::Success && characteristicsResult.Characteristics().Size() > 0)
+ {
+ // we're just getting the first one
+ auto characteristic = characteristicsResult.Characteristics().GetAt(0);
+
+ return characteristic;
+ }
+ }
+
+ return nullptr;
+}
diff --git a/src/api/Abstraction/BleMidiAbstraction/pch.h b/src/api/Abstraction/BleMidiAbstraction/pch.h
index 33202444..ee7c79a4 100644
--- a/src/api/Abstraction/BleMidiAbstraction/pch.h
+++ b/src/api/Abstraction/BleMidiAbstraction/pch.h
@@ -21,14 +21,18 @@
#include
#include
#include
-#include
+#include
+#include
#include
namespace json = ::winrt::Windows::Data::Json;
namespace enumeration = ::winrt::Windows::Devices::Enumeration;
namespace foundation = ::winrt::Windows::Foundation;
namespace collections = ::winrt::Windows::Foundation::Collections;
+
namespace bt = ::winrt::Windows::Devices::Bluetooth;
+namespace gatt = ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile;
+namespace ad = ::winrt::Windows::Devices::Bluetooth::Advertisement;
#include
#include
@@ -94,6 +98,9 @@ class CMidi2BluetoothMidiPluginMetadataManager;
#include "abstraction_defs.h"
#include "midi_timestamp.h"
#include "ble_timestamp.h"
+#include "ble_utilities.h"
+
+#include "AbstractionState.h"
#include "MidiServicePlugin.h"
#include "MidiServicePlugin_i.c"
@@ -109,7 +116,6 @@ class CMidi2BluetoothMidiPluginMetadataManager;
#include "MidiBluetoothPacket.h"
#include "MidiBluetoothDeviceDefinition.h"
-#include "MidiBluetoothDevice.h"
#include "MidiEndpointTable.h"
#include "Midi2.BluetoothMidiAbstraction.h"
diff --git a/src/api/Abstraction/KSAbstraction/Midi2.KSMidiEndpointManager.cpp b/src/api/Abstraction/KSAbstraction/Midi2.KSMidiEndpointManager.cpp
index d517817e..19339d84 100644
--- a/src/api/Abstraction/KSAbstraction/Midi2.KSMidiEndpointManager.cpp
+++ b/src/api/Abstraction/KSAbstraction/Midi2.KSMidiEndpointManager.cpp
@@ -601,13 +601,22 @@ HRESULT CMidi2KSMidiEndpointManager::OnDeviceAdded(DeviceWatcher watcher, Device
BYTE preferredProtocol{ MIDI_PROP_CONFIGURED_PROTOCOL_MIDI2 };
WORD negotiationTimeoutMS{ 5000 }; // this should be much shorter
+ PENDPOINTPROTOCOLNEGOTIATIONRESULTS negotiationResults;
+
LOG_IF_FAILED(m_MidiProtocolManager->NegotiateAndRequestMetadata(
newDeviceInterfaceId,
preferToSendJRToEndpoint,
preferToReceiveJRFromEndpoint,
preferredProtocol,
- negotiationTimeoutMS
+ negotiationTimeoutMS,
+ &negotiationResults
));
+
+
+ // TODO: The negotiationResults structure contains the function blocks which
+ // should be used to create MIDI 1.0 legacy ports for this MIDI 2.0 device.
+
+
}
}
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp
index bc022794..82f9eb0e 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp
+++ b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp
@@ -142,7 +142,7 @@ CMidi2VirtualMidiEndpointManager::CreateParentDevice()
{
TraceLoggingWrite(
MidiVirtualMidiAbstractionTelemetryProvider::Provider(),
- __func__,
+ __FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
TraceLoggingPointer(this, "this")
);
@@ -202,12 +202,15 @@ CMidi2VirtualMidiEndpointManager::NegotiateAndRequestMetadata(std::wstring endpo
BYTE preferredProtocol{ MIDI_PROP_CONFIGURED_PROTOCOL_MIDI2 };
WORD negotiationTimeoutMS{ 2000 };
+ PENDPOINTPROTOCOLNEGOTIATIONRESULTS negotiationResults;
+
RETURN_IF_FAILED(m_MidiProtocolManager->NegotiateAndRequestMetadata(
endpointId.c_str(),
preferToSendJRToEndpoint,
preferToReceiveJRFromEndpoint,
preferredProtocol,
- negotiationTimeoutMS
+ negotiationTimeoutMS,
+ &negotiationResults
));
return S_OK;
diff --git a/src/api/Client/Midi2Client-Projection/Windows.Devices.Midi2.NET.csproj b/src/api/Client/Midi2Client-Projection/Windows.Devices.Midi2.NET.csproj
index 534e21d2..0721cac0 100644
--- a/src/api/Client/Midi2Client-Projection/Windows.Devices.Midi2.NET.csproj
+++ b/src/api/Client/Midi2Client-Projection/Windows.Devices.Midi2.NET.csproj
@@ -1,7 +1,7 @@
- net7.0-windows10.0.20348.0
+ net8.0-windows10.0.20348.0
enable
enable
AnyCPU
diff --git a/src/api/Client/Midi2Client-Projection/nuget/Windows.Devices.Midi2.nuspec b/src/api/Client/Midi2Client-Projection/nuget/Windows.Devices.Midi2.nuspec
index 6503deec..5de74484 100644
--- a/src/api/Client/Midi2Client-Projection/nuget/Windows.Devices.Midi2.nuspec
+++ b/src/api/Client/Midi2Client-Projection/nuget/Windows.Devices.Midi2.nuspec
@@ -2,7 +2,7 @@
Windows.Devices.Midi2
- 1.0.0-preview.5-0185
+ 1.0.0-preview.6-0190
Microsoft Corporation
Windows MIDI Services API. Minimum package necessary to use Windows MIDI Services from an app on a PC that has Windows MIDI Services installed.
MIT
@@ -12,7 +12,6 @@
false
midi windows music audio native
-
@@ -20,9 +19,6 @@
-
-
-
@@ -39,9 +35,7 @@
-
-
-
-
@@ -68,8 +60,6 @@
-
-
diff --git a/src/api/Client/Midi2Client/IMidiEndpointConnectionSettings.idl b/src/api/Client/Midi2Client/IMidiEndpointConnectionSettings.idl
index d6681ce6..1df7befb 100644
--- a/src/api/Client/Midi2Client/IMidiEndpointConnectionSettings.idl
+++ b/src/api/Client/Midi2Client/IMidiEndpointConnectionSettings.idl
@@ -15,7 +15,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[MIDI_INTERFACE_UUID("65736736-35f3-421c-a683-3a034ad0dcc2",1.0)]
interface IMidiEndpointConnectionSettings
{
diff --git a/src/api/Client/Midi2Client/IMidiEndpointConnectionSource.idl b/src/api/Client/Midi2Client/IMidiEndpointConnectionSource.idl
index 6b6a1870..2ece14ee 100644
--- a/src/api/Client/Midi2Client/IMidiEndpointConnectionSource.idl
+++ b/src/api/Client/Midi2Client/IMidiEndpointConnectionSource.idl
@@ -17,7 +17,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[MIDI_INTERFACE_UUID("44385cdd-b64c-4195-8faa-8a61fc952a23",1.0)]
interface IMidiEndpointConnectionSource
{
diff --git a/src/api/Client/Midi2Client/IMidiEndpointMessageProcessingPlugin.idl b/src/api/Client/Midi2Client/IMidiEndpointMessageProcessingPlugin.idl
index ef17b4e7..c6504d11 100644
--- a/src/api/Client/Midi2Client/IMidiEndpointMessageProcessingPlugin.idl
+++ b/src/api/Client/Midi2Client/IMidiEndpointMessageProcessingPlugin.idl
@@ -16,7 +16,7 @@ import "IMidiEndpointConnectionSource.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[MIDI_INTERFACE_UUID("198d5ef2-313f-41af-9259-a42997e060f0",1.0)]
interface IMidiEndpointMessageProcessingPlugin
{
diff --git a/src/api/Client/Midi2Client/IMidiMessageReceivedEventSource.idl b/src/api/Client/Midi2Client/IMidiMessageReceivedEventSource.idl
index 7b8eab73..115cd1b6 100644
--- a/src/api/Client/Midi2Client/IMidiMessageReceivedEventSource.idl
+++ b/src/api/Client/Midi2Client/IMidiMessageReceivedEventSource.idl
@@ -13,7 +13,7 @@ import "MidiMessageReceivedEventArgs.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[MIDI_INTERFACE_UUID("101ceb4b-cca4-48a1-b91e-6002b928613c",1.0)]
interface IMidiMessageReceivedEventSource
{
diff --git a/src/api/Client/Midi2Client/IMidiServiceMessageProcessingPluginConfiguration.idl b/src/api/Client/Midi2Client/IMidiServiceMessageProcessingPluginConfiguration.idl
index d0d86b51..c314601a 100644
--- a/src/api/Client/Midi2Client/IMidiServiceMessageProcessingPluginConfiguration.idl
+++ b/src/api/Client/Midi2Client/IMidiServiceMessageProcessingPluginConfiguration.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[MIDI_INTERFACE_UUID("2ebcfa13-585a-4376-8fe1-635784fa7fd4",1.0)]
interface IMidiServiceMessageProcessingPluginConfiguration
{
diff --git a/src/api/Client/Midi2Client/IMidiServiceTransportPluginConfiguration.idl b/src/api/Client/Midi2Client/IMidiServiceTransportPluginConfiguration.idl
index 5f264a7b..45838aba 100644
--- a/src/api/Client/Midi2Client/IMidiServiceTransportPluginConfiguration.idl
+++ b/src/api/Client/Midi2Client/IMidiServiceTransportPluginConfiguration.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[MIDI_INTERFACE_UUID("b2417dde-ef35-499b-a89b-0a4c32cc699a",1.0)]
interface IMidiServiceTransportPluginConfiguration
{
diff --git a/src/api/Client/Midi2Client/IMidiUniversalPacket.idl b/src/api/Client/Midi2Client/IMidiUniversalPacket.idl
index 9c8ede65..5a5e0432 100644
--- a/src/api/Client/Midi2Client/IMidiUniversalPacket.idl
+++ b/src/api/Client/Midi2Client/IMidiUniversalPacket.idl
@@ -16,7 +16,7 @@ import "MidiMessageTypeEnum.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[MIDI_INTERFACE_UUID("2eb5df8a-d751-4997-bf96-ba9a531fd5ff",1.0)]
interface IMidiUniversalPacket
{
diff --git a/src/api/Client/Midi2Client/Midi1ChannelVoiceMessageStatusEnum.idl b/src/api/Client/Midi2Client/Midi1ChannelVoiceMessageStatusEnum.idl
index e4a52da9..c9595d53 100644
--- a/src/api/Client/Midi2Client/Midi1ChannelVoiceMessageStatusEnum.idl
+++ b/src/api/Client/Midi2Client/Midi1ChannelVoiceMessageStatusEnum.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum Midi1ChannelVoiceMessageStatus
{
NoteOff = 0x8,
diff --git a/src/api/Client/Midi2Client/Midi2ChannelVoiceMessageStatusEnum.idl b/src/api/Client/Midi2Client/Midi2ChannelVoiceMessageStatusEnum.idl
index cad5345b..bf7f9298 100644
--- a/src/api/Client/Midi2Client/Midi2ChannelVoiceMessageStatusEnum.idl
+++ b/src/api/Client/Midi2Client/Midi2ChannelVoiceMessageStatusEnum.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum Midi2ChannelVoiceMessageStatus
{
RegisteredPerNoteController = 0x0,
diff --git a/src/api/Client/Midi2Client/MidiChannel.idl b/src/api/Client/Midi2Client/MidiChannel.idl
index ed92c1d5..562e83db 100644
--- a/src/api/Client/Midi2Client/MidiChannel.idl
+++ b/src/api/Client/Midi2Client/MidiChannel.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiChannel
{
diff --git a/src/api/Client/Midi2Client/MidiChannelEndpointListener.idl b/src/api/Client/Midi2Client/MidiChannelEndpointListener.idl
index aaeb4330..58e01372 100644
--- a/src/api/Client/Midi2Client/MidiChannelEndpointListener.idl
+++ b/src/api/Client/Midi2Client/MidiChannelEndpointListener.idl
@@ -22,7 +22,7 @@ import "IMidiMessageReceivedEventSource.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiChannelEndpointListener : IMidiEndpointMessageProcessingPlugin,
IMidiMessageReceivedEventSource
diff --git a/src/api/Client/Midi2Client/MidiClock.idl b/src/api/Client/Midi2Client/MidiClock.idl
index 0265c409..74f31c50 100644
--- a/src/api/Client/Midi2Client/MidiClock.idl
+++ b/src/api/Client/Midi2Client/MidiClock.idl
@@ -12,7 +12,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiClock
{
diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection.idl b/src/api/Client/Midi2Client/MidiEndpointConnection.idl
index 567e1011..53cbc67b 100644
--- a/src/api/Client/Midi2Client/MidiEndpointConnection.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointConnection.idl
@@ -22,7 +22,7 @@ import "IMidiEndpointConnectionSource.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiEndpointConnection : Windows.Devices.Midi2.IMidiMessageReceivedEventSource,
IMidiEndpointConnectionSource
diff --git a/src/api/Client/Midi2Client/MidiEndpointDeviceInformation.idl b/src/api/Client/Midi2Client/MidiEndpointDeviceInformation.idl
index baae7538..dbf47199 100644
--- a/src/api/Client/Midi2Client/MidiEndpointDeviceInformation.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointDeviceInformation.idl
@@ -20,7 +20,7 @@ import "MidiEndpointDeviceInformationFiltersEnum.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiEndpointDeviceInformation
{
diff --git a/src/api/Client/Midi2Client/MidiEndpointDeviceInformationAddedEventArgs.idl b/src/api/Client/Midi2Client/MidiEndpointDeviceInformationAddedEventArgs.idl
index 38a44896..1dbb30ba 100644
--- a/src/api/Client/Midi2Client/MidiEndpointDeviceInformationAddedEventArgs.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointDeviceInformationAddedEventArgs.idl
@@ -13,7 +13,7 @@ import "MidiEndpointDeviceInformation.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiEndpointDeviceInformationAddedEventArgs
{
diff --git a/src/api/Client/Midi2Client/MidiEndpointDeviceInformationFiltersEnum.idl b/src/api/Client/Midi2Client/MidiEndpointDeviceInformationFiltersEnum.idl
index c201fde0..97e2f891 100644
--- a/src/api/Client/Midi2Client/MidiEndpointDeviceInformationFiltersEnum.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointDeviceInformationFiltersEnum.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[flags]
enum MidiEndpointDeviceInformationFilters
{
diff --git a/src/api/Client/Midi2Client/MidiEndpointDeviceInformationRemovedEventArgs.idl b/src/api/Client/Midi2Client/MidiEndpointDeviceInformationRemovedEventArgs.idl
index 22a244bd..65c8c997 100644
--- a/src/api/Client/Midi2Client/MidiEndpointDeviceInformationRemovedEventArgs.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointDeviceInformationRemovedEventArgs.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiEndpointDeviceInformationRemovedEventArgs
{
diff --git a/src/api/Client/Midi2Client/MidiEndpointDeviceInformationSortOrderEnum.idl b/src/api/Client/Midi2Client/MidiEndpointDeviceInformationSortOrderEnum.idl
index 8959d7c2..d23594a4 100644
--- a/src/api/Client/Midi2Client/MidiEndpointDeviceInformationSortOrderEnum.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointDeviceInformationSortOrderEnum.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiEndpointDeviceInformationSortOrder
{
None = 0,
diff --git a/src/api/Client/Midi2Client/MidiEndpointDeviceInformationUpdatedEventArgs.idl b/src/api/Client/Midi2Client/MidiEndpointDeviceInformationUpdatedEventArgs.idl
index 6f933bfb..374cf244 100644
--- a/src/api/Client/Midi2Client/MidiEndpointDeviceInformationUpdatedEventArgs.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointDeviceInformationUpdatedEventArgs.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiEndpointDeviceInformationUpdatedEventArgs
{
diff --git a/src/api/Client/Midi2Client/MidiEndpointDevicePurposeEnum.idl b/src/api/Client/Midi2Client/MidiEndpointDevicePurposeEnum.idl
index 5564d0a8..065feb57 100644
--- a/src/api/Client/Midi2Client/MidiEndpointDevicePurposeEnum.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointDevicePurposeEnum.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiEndpointDevicePurpose
{
NormalMessageEndpoint = 0,
diff --git a/src/api/Client/Midi2Client/MidiEndpointDeviceWatcher.idl b/src/api/Client/Midi2Client/MidiEndpointDeviceWatcher.idl
index 4c9f7534..13a98274 100644
--- a/src/api/Client/Midi2Client/MidiEndpointDeviceWatcher.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointDeviceWatcher.idl
@@ -18,7 +18,7 @@ import "MidiEndpointDeviceInformationAddedEventArgs.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiEndpointDeviceWatcher
{
diff --git a/src/api/Client/Midi2Client/MidiEndpointDiscoveryRequestsEnum.idl b/src/api/Client/Midi2Client/MidiEndpointDiscoveryRequestsEnum.idl
index 6cd09af8..35e8994e 100644
--- a/src/api/Client/Midi2Client/MidiEndpointDiscoveryRequestsEnum.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointDiscoveryRequestsEnum.idl
@@ -12,7 +12,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[flags]
enum MidiEndpointDiscoveryRequests
{
diff --git a/src/api/Client/Midi2Client/MidiEndpointNativeDataFormatEnum.idl b/src/api/Client/Midi2Client/MidiEndpointNativeDataFormatEnum.idl
index ad6e97a9..a9ea8ec3 100644
--- a/src/api/Client/Midi2Client/MidiEndpointNativeDataFormatEnum.idl
+++ b/src/api/Client/Midi2Client/MidiEndpointNativeDataFormatEnum.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiEndpointNativeDataFormat
{
Unknown = 0,
diff --git a/src/api/Client/Midi2Client/MidiFunctionBlock.idl b/src/api/Client/Midi2Client/MidiFunctionBlock.idl
index a87b4b86..adc0ced6 100644
--- a/src/api/Client/Midi2Client/MidiFunctionBlock.idl
+++ b/src/api/Client/Midi2Client/MidiFunctionBlock.idl
@@ -17,7 +17,7 @@ import "MidiGroup.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiFunctionBlock
{
diff --git a/src/api/Client/Midi2Client/MidiFunctionBlockDirectionEnum.idl b/src/api/Client/Midi2Client/MidiFunctionBlockDirectionEnum.idl
index 30dfe5e6..aef4a955 100644
--- a/src/api/Client/Midi2Client/MidiFunctionBlockDirectionEnum.idl
+++ b/src/api/Client/Midi2Client/MidiFunctionBlockDirectionEnum.idl
@@ -14,7 +14,7 @@ namespace Windows.Devices.Midi2
// NOTE: the values are different for function blocks and group terminal
// blocks, so we have to different enumerations
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiFunctionBlockDirection
{
Undefined = 0x0,
diff --git a/src/api/Client/Midi2Client/MidiFunctionBlockDiscoveryRequestsEnum.idl b/src/api/Client/Midi2Client/MidiFunctionBlockDiscoveryRequestsEnum.idl
index 8cc817dd..69a8535a 100644
--- a/src/api/Client/Midi2Client/MidiFunctionBlockDiscoveryRequestsEnum.idl
+++ b/src/api/Client/Midi2Client/MidiFunctionBlockDiscoveryRequestsEnum.idl
@@ -12,7 +12,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[flags]
enum MidiFunctionBlockDiscoveryRequests
{
diff --git a/src/api/Client/Midi2Client/MidiFunctionBlockMidi10Enum.idl b/src/api/Client/Midi2Client/MidiFunctionBlockMidi10Enum.idl
index 0187a279..3480a813 100644
--- a/src/api/Client/Midi2Client/MidiFunctionBlockMidi10Enum.idl
+++ b/src/api/Client/Midi2Client/MidiFunctionBlockMidi10Enum.idl
@@ -15,7 +15,7 @@ namespace Windows.Devices.Midi2
// from the POV of the function block, not from the OS. IOW,
// Input here means the device FB *receives* messages that
// are sent through Windows
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiFunctionBlockMidi10
{
Not10 = 0x0,
diff --git a/src/api/Client/Midi2Client/MidiFunctionBlockUIHintEnum.idl b/src/api/Client/Midi2Client/MidiFunctionBlockUIHintEnum.idl
index ddf3f90e..f32dd771 100644
--- a/src/api/Client/Midi2Client/MidiFunctionBlockUIHintEnum.idl
+++ b/src/api/Client/Midi2Client/MidiFunctionBlockUIHintEnum.idl
@@ -12,7 +12,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiFunctionBlockUIHint
{
Unknown = 0x0,
diff --git a/src/api/Client/Midi2Client/MidiGroup.idl b/src/api/Client/Midi2Client/MidiGroup.idl
index c8ecdb77..c52ac730 100644
--- a/src/api/Client/Midi2Client/MidiGroup.idl
+++ b/src/api/Client/Midi2Client/MidiGroup.idl
@@ -12,7 +12,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiGroup
{
diff --git a/src/api/Client/Midi2Client/MidiGroupEndpointListener.idl b/src/api/Client/Midi2Client/MidiGroupEndpointListener.idl
index e7e66f34..5bf230f8 100644
--- a/src/api/Client/Midi2Client/MidiGroupEndpointListener.idl
+++ b/src/api/Client/Midi2Client/MidiGroupEndpointListener.idl
@@ -21,7 +21,7 @@ import "IMidiMessageReceivedEventSource.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiGroupEndpointListener : IMidiEndpointMessageProcessingPlugin,
IMidiMessageReceivedEventSource
diff --git a/src/api/Client/Midi2Client/MidiGroupTerminalBlock.idl b/src/api/Client/Midi2Client/MidiGroupTerminalBlock.idl
index d82d7646..1535db5f 100644
--- a/src/api/Client/Midi2Client/MidiGroupTerminalBlock.idl
+++ b/src/api/Client/Midi2Client/MidiGroupTerminalBlock.idl
@@ -23,7 +23,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiGroupTerminalBlock
{
diff --git a/src/api/Client/Midi2Client/MidiGroupTerminalBlockDirectionEnum.idl b/src/api/Client/Midi2Client/MidiGroupTerminalBlockDirectionEnum.idl
index 99832c87..ac6f3078 100644
--- a/src/api/Client/Midi2Client/MidiGroupTerminalBlockDirectionEnum.idl
+++ b/src/api/Client/Midi2Client/MidiGroupTerminalBlockDirectionEnum.idl
@@ -14,7 +14,7 @@ namespace Windows.Devices.Midi2
// NOTE: the values are different for function blocks and group terminal
// blocks, so we have to different enumerations
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiGroupTerminalBlockDirection
{
Bidirectional = 0x0,
diff --git a/src/api/Client/Midi2Client/MidiGroupTerminalBlockProtocolEnum.idl b/src/api/Client/Midi2Client/MidiGroupTerminalBlockProtocolEnum.idl
index 9b5603b3..41213c14 100644
--- a/src/api/Client/Midi2Client/MidiGroupTerminalBlockProtocolEnum.idl
+++ b/src/api/Client/Midi2Client/MidiGroupTerminalBlockProtocolEnum.idl
@@ -18,7 +18,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiGroupTerminalBlockProtocol
{
Unknown = 0x00,
diff --git a/src/api/Client/Midi2Client/MidiMessage128.idl b/src/api/Client/Midi2Client/MidiMessage128.idl
index bf123ccf..01dc71a6 100644
--- a/src/api/Client/Midi2Client/MidiMessage128.idl
+++ b/src/api/Client/Midi2Client/MidiMessage128.idl
@@ -15,7 +15,7 @@ import "IMidiUniversalPacket.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
unsealed runtimeclass MidiMessage128 : IMidiUniversalPacket, Windows.Foundation.IStringable
{
MidiMessage128();
diff --git a/src/api/Client/Midi2Client/MidiMessage32.idl b/src/api/Client/Midi2Client/MidiMessage32.idl
index aed69ca4..2cf37c3f 100644
--- a/src/api/Client/Midi2Client/MidiMessage32.idl
+++ b/src/api/Client/Midi2Client/MidiMessage32.idl
@@ -15,7 +15,7 @@ import "IMidiUniversalPacket.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
unsealed runtimeclass MidiMessage32 : IMidiUniversalPacket, Windows.Foundation.IStringable
{
MidiMessage32();
diff --git a/src/api/Client/Midi2Client/MidiMessage64.idl b/src/api/Client/Midi2Client/MidiMessage64.idl
index d1f37dd3..2b536f75 100644
--- a/src/api/Client/Midi2Client/MidiMessage64.idl
+++ b/src/api/Client/Midi2Client/MidiMessage64.idl
@@ -15,7 +15,7 @@ import "IMidiUniversalPacket.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
unsealed runtimeclass MidiMessage64 : IMidiUniversalPacket, Windows.Foundation.IStringable
{
MidiMessage64();
diff --git a/src/api/Client/Midi2Client/MidiMessage96.idl b/src/api/Client/Midi2Client/MidiMessage96.idl
index 2cc16cef..6ac074b8 100644
--- a/src/api/Client/Midi2Client/MidiMessage96.idl
+++ b/src/api/Client/Midi2Client/MidiMessage96.idl
@@ -15,7 +15,7 @@ import "IMidiUniversalPacket.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
unsealed runtimeclass MidiMessage96 : IMidiUniversalPacket, Windows.Foundation.IStringable
{
MidiMessage96();
diff --git a/src/api/Client/Midi2Client/MidiMessageBuilder.idl b/src/api/Client/Midi2Client/MidiMessageBuilder.idl
index 32a498f0..16df61c8 100644
--- a/src/api/Client/Midi2Client/MidiMessageBuilder.idl
+++ b/src/api/Client/Midi2Client/MidiMessageBuilder.idl
@@ -29,7 +29,7 @@ import "Midi2ChannelVoiceMessageStatusEnum.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
static runtimeclass MidiMessageBuilder
{
// parameters are named, where applicable, for the fields as defined in the spec.
diff --git a/src/api/Client/Midi2Client/MidiMessageConverter.idl b/src/api/Client/Midi2Client/MidiMessageConverter.idl
index 3743ff21..22b5961e 100644
--- a/src/api/Client/Midi2Client/MidiMessageConverter.idl
+++ b/src/api/Client/Midi2Client/MidiMessageConverter.idl
@@ -24,7 +24,7 @@ import "Midi2ChannelVoiceMessageStatusEnum.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
static runtimeclass MidiMessageConverter
{
// for migrating from raw bytes
diff --git a/src/api/Client/Midi2Client/MidiMessageReceivedEventArgs.idl b/src/api/Client/Midi2Client/MidiMessageReceivedEventArgs.idl
index d8eb6568..63d8e022 100644
--- a/src/api/Client/Midi2Client/MidiMessageReceivedEventArgs.idl
+++ b/src/api/Client/Midi2Client/MidiMessageReceivedEventArgs.idl
@@ -24,7 +24,7 @@ import "MidiMessageStruct.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiMessageReceivedEventArgs
{
diff --git a/src/api/Client/Midi2Client/MidiMessageStruct.idl b/src/api/Client/Midi2Client/MidiMessageStruct.idl
index f0a54727..0db39151 100644
--- a/src/api/Client/Midi2Client/MidiMessageStruct.idl
+++ b/src/api/Client/Midi2Client/MidiMessageStruct.idl
@@ -15,7 +15,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
struct MidiMessageStruct
{
UInt32 Word0;
diff --git a/src/api/Client/Midi2Client/MidiMessageTypeEndpointListener.idl b/src/api/Client/Midi2Client/MidiMessageTypeEndpointListener.idl
index d9b82cc4..f22178df 100644
--- a/src/api/Client/Midi2Client/MidiMessageTypeEndpointListener.idl
+++ b/src/api/Client/Midi2Client/MidiMessageTypeEndpointListener.idl
@@ -22,7 +22,7 @@ import "IMidiMessageReceivedEventSource.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiMessageTypeEndpointListener : IMidiEndpointMessageProcessingPlugin,
IMidiMessageReceivedEventSource
diff --git a/src/api/Client/Midi2Client/MidiMessageTypeEnum.idl b/src/api/Client/Midi2Client/MidiMessageTypeEnum.idl
index 1fecbcb3..da6a7090 100644
--- a/src/api/Client/Midi2Client/MidiMessageTypeEnum.idl
+++ b/src/api/Client/Midi2Client/MidiMessageTypeEnum.idl
@@ -12,7 +12,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiMessageType
{
UtilityMessage32 = 0x0,
diff --git a/src/api/Client/Midi2Client/MidiMessageUtility.idl b/src/api/Client/Midi2Client/MidiMessageUtility.idl
index d619fb62..a1113de3 100644
--- a/src/api/Client/Midi2Client/MidiMessageUtility.idl
+++ b/src/api/Client/Midi2Client/MidiMessageUtility.idl
@@ -25,7 +25,7 @@ import "Midi2ChannelVoiceMessageStatusEnum.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
static runtimeclass MidiMessageUtility
{
static Boolean ValidateMessage32MessageType(UInt32 word0);
diff --git a/src/api/Client/Midi2Client/MidiPacketTypeEnum.idl b/src/api/Client/Midi2Client/MidiPacketTypeEnum.idl
index 1deaf397..eac18663 100644
--- a/src/api/Client/Midi2Client/MidiPacketTypeEnum.idl
+++ b/src/api/Client/Midi2Client/MidiPacketTypeEnum.idl
@@ -13,7 +13,7 @@ namespace Windows.Devices.Midi2
{
// can be cast to the size in MIDI Words.
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiPacketType
{
UnknownOrInvalid = 0,
diff --git a/src/api/Client/Midi2Client/MidiProtocolEnum.idl b/src/api/Client/Midi2Client/MidiProtocolEnum.idl
index 85871265..4127deb7 100644
--- a/src/api/Client/Midi2Client/MidiProtocolEnum.idl
+++ b/src/api/Client/Midi2Client/MidiProtocolEnum.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiProtocol
{
Default = 0,
diff --git a/src/api/Client/Midi2Client/MidiSendMessageResultsEnum.idl b/src/api/Client/Midi2Client/MidiSendMessageResultsEnum.idl
index 16ef76f0..da10a453 100644
--- a/src/api/Client/Midi2Client/MidiSendMessageResultsEnum.idl
+++ b/src/api/Client/Midi2Client/MidiSendMessageResultsEnum.idl
@@ -14,7 +14,7 @@ namespace Windows.Devices.Midi2
// Yes, this is effectively recreating the HRESULT, but
// our API standards do not allow creating and exposing
// custom HRESULTs in public APIs
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[flags]
enum MidiSendMessageResults
{
diff --git a/src/api/Client/Midi2Client/MidiService.idl b/src/api/Client/Midi2Client/MidiService.idl
index f3d960c9..c8d3444a 100644
--- a/src/api/Client/Midi2Client/MidiService.idl
+++ b/src/api/Client/Midi2Client/MidiService.idl
@@ -24,7 +24,7 @@ import "MidiServiceConfigurationResponse.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
static runtimeclass MidiService
{
diff --git a/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.idl b/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.idl
index 3e194ce1..95a8aca2 100644
--- a/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.idl
+++ b/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.idl
@@ -13,7 +13,7 @@ import "MidiServiceConfigurationResponseStatusEnum.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiServiceConfigurationResponse
{
diff --git a/src/api/Client/Midi2Client/MidiServiceConfigurationResponseStatusEnum.idl b/src/api/Client/Midi2Client/MidiServiceConfigurationResponseStatusEnum.idl
index 02503eb0..131655d7 100644
--- a/src/api/Client/Midi2Client/MidiServiceConfigurationResponseStatusEnum.idl
+++ b/src/api/Client/Midi2Client/MidiServiceConfigurationResponseStatusEnum.idl
@@ -14,7 +14,7 @@ namespace Windows.Devices.Midi2
// NOTE: the values are different for function blocks and group terminal
// blocks, so we have to different enumerations
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiServiceConfigurationResponseStatus
{
Success = 0,
diff --git a/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.idl b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.idl
index 5e9e9955..202664d1 100644
--- a/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.idl
+++ b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiServiceLoopbackEndpointCreationResult
{
diff --git a/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.idl b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.idl
index 9c5c39b2..cbf55204 100644
--- a/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.idl
+++ b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiServiceLoopbackEndpointDefinition
{
diff --git a/src/api/Client/Midi2Client/MidiServiceMessageProcessingPluginInfo.idl b/src/api/Client/Midi2Client/MidiServiceMessageProcessingPluginInfo.idl
index 7903c440..46c27c22 100644
--- a/src/api/Client/Midi2Client/MidiServiceMessageProcessingPluginInfo.idl
+++ b/src/api/Client/Midi2Client/MidiServiceMessageProcessingPluginInfo.idl
@@ -12,7 +12,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiServiceMessageProcessingPluginInfo
{
diff --git a/src/api/Client/Midi2Client/MidiServicePingResponse.idl b/src/api/Client/Midi2Client/MidiServicePingResponse.idl
index 00f1628e..61f1ce48 100644
--- a/src/api/Client/Midi2Client/MidiServicePingResponse.idl
+++ b/src/api/Client/Midi2Client/MidiServicePingResponse.idl
@@ -12,7 +12,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiServicePingResponse
{
diff --git a/src/api/Client/Midi2Client/MidiServicePingResponseSummary.idl b/src/api/Client/Midi2Client/MidiServicePingResponseSummary.idl
index 940c8d6c..b436682e 100644
--- a/src/api/Client/Midi2Client/MidiServicePingResponseSummary.idl
+++ b/src/api/Client/Midi2Client/MidiServicePingResponseSummary.idl
@@ -13,7 +13,7 @@ import "MidiServicePingResponse.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiServicePingResponseSummary
{
diff --git a/src/api/Client/Midi2Client/MidiServiceSessionConnectionInfo.idl b/src/api/Client/Midi2Client/MidiServiceSessionConnectionInfo.idl
index 51ab3726..e5228964 100644
--- a/src/api/Client/Midi2Client/MidiServiceSessionConnectionInfo.idl
+++ b/src/api/Client/Midi2Client/MidiServiceSessionConnectionInfo.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiServiceSessionConnectionInfo
{
diff --git a/src/api/Client/Midi2Client/MidiServiceSessionInfo.idl b/src/api/Client/Midi2Client/MidiServiceSessionInfo.idl
index b8b0dc25..38679613 100644
--- a/src/api/Client/Midi2Client/MidiServiceSessionInfo.idl
+++ b/src/api/Client/Midi2Client/MidiServiceSessionInfo.idl
@@ -13,7 +13,7 @@ import "MidiServiceSessionConnectionInfo.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiServiceSessionInfo
{
diff --git a/src/api/Client/Midi2Client/MidiServiceTransportPluginInfo.idl b/src/api/Client/Midi2Client/MidiServiceTransportPluginInfo.idl
index 408c1d8e..b1f315e0 100644
--- a/src/api/Client/Midi2Client/MidiServiceTransportPluginInfo.idl
+++ b/src/api/Client/Midi2Client/MidiServiceTransportPluginInfo.idl
@@ -12,7 +12,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiServiceTransportPluginInfo
{
diff --git a/src/api/Client/Midi2Client/MidiSession.idl b/src/api/Client/Midi2Client/MidiSession.idl
index 070f6ce4..36be5ad9 100644
--- a/src/api/Client/Midi2Client/MidiSession.idl
+++ b/src/api/Client/Midi2Client/MidiSession.idl
@@ -17,7 +17,7 @@ import "MidiVirtualEndpointDeviceDefinition.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiSession : Windows.Foundation.IClosable
{
diff --git a/src/api/Client/Midi2Client/MidiSessionSettings.idl b/src/api/Client/Midi2Client/MidiSessionSettings.idl
index fef04d3c..512e7e86 100644
--- a/src/api/Client/Midi2Client/MidiSessionSettings.idl
+++ b/src/api/Client/Midi2Client/MidiSessionSettings.idl
@@ -12,7 +12,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiSessionSettings
{
diff --git a/src/api/Client/Midi2Client/MidiStreamConfigurationRequestReceivedEventArgs.cpp b/src/api/Client/Midi2Client/MidiStreamConfigurationRequestReceivedEventArgs.cpp
new file mode 100644
index 00000000..63886a1f
--- /dev/null
+++ b/src/api/Client/Midi2Client/MidiStreamConfigurationRequestReceivedEventArgs.cpp
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+
+#include "pch.h"
+#include "MidiStreamConfigurationRequestReceivedEventArgs.h"
+#include "MidiStreamConfigurationRequestReceivedEventArgs.g.cpp"
+
+namespace winrt::Windows::Devices::Midi2::implementation
+{
+ _Use_decl_annotations_
+ void MidiStreamConfigurationRequestReceivedEventArgs::InternalInitialize(
+ internal::MidiTimestamp timestamp,
+ midi2::MidiProtocol protocol,
+ bool requestReceiveJRTimestamps,
+ bool requestTransmitJRTimestamps
+ )
+ {
+ m_timestamp = timestamp;
+ m_protocol = protocol;
+ m_requestReceiveJRTimestamps = requestReceiveJRTimestamps;
+ m_requestTransmitJRTimestamps = requestTransmitJRTimestamps;
+ }
+
+}
diff --git a/src/api/Client/Midi2Client/MidiStreamConfigurationRequestReceivedEventArgs.h b/src/api/Client/Midi2Client/MidiStreamConfigurationRequestReceivedEventArgs.h
new file mode 100644
index 00000000..2807cb62
--- /dev/null
+++ b/src/api/Client/Midi2Client/MidiStreamConfigurationRequestReceivedEventArgs.h
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#pragma once
+#include "MidiStreamConfigurationRequestReceivedEventArgs.g.h"
+
+namespace winrt::Windows::Devices::Midi2::implementation
+{
+ struct MidiStreamConfigurationRequestReceivedEventArgs : MidiStreamConfigurationRequestReceivedEventArgsT
+ {
+ MidiStreamConfigurationRequestReceivedEventArgs() = default;
+
+ internal::MidiTimestamp Timestamp() { return m_timestamp; }
+ midi2::MidiProtocol PreferredMidiProtocol() { return m_protocol; }
+ bool RequestEndpointTransmitJitterReductionTimestamps() { return m_requestTransmitJRTimestamps; }
+ bool RequestEndpointReceiveJitterReductionTimestamps() { return m_requestReceiveJRTimestamps; }
+
+ void InternalInitialize(
+ _In_ internal::MidiTimestamp timestamp,
+ _In_ midi2::MidiProtocol protocol,
+ _In_ bool requestReceiveJRTimestamps,
+ _In_ bool requestTransmitJRTimestamps
+ );
+
+ private:
+ internal::MidiTimestamp m_timestamp{ 0 };
+ midi2::MidiProtocol m_protocol{ midi2::MidiProtocol::Default };
+ bool m_requestTransmitJRTimestamps{ false };
+ bool m_requestReceiveJRTimestamps{ false };
+
+ };
+}
diff --git a/src/api/Client/Midi2Client/MidiStreamConfigurationRequestReceivedEventArgs.idl b/src/api/Client/Midi2Client/MidiStreamConfigurationRequestReceivedEventArgs.idl
index bde8da9a..6bc63816 100644
--- a/src/api/Client/Midi2Client/MidiStreamConfigurationRequestReceivedEventArgs.idl
+++ b/src/api/Client/Midi2Client/MidiStreamConfigurationRequestReceivedEventArgs.idl
@@ -13,7 +13,7 @@ import "MidiProtocolEnum.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiStreamConfigurationRequestReceivedEventArgs
{
diff --git a/src/api/Client/Midi2Client/MidiStreamConfigurationRequestedSettings.idl b/src/api/Client/Midi2Client/MidiStreamConfigurationRequestedSettings.idl
index a9adf5b2..b3987df4 100644
--- a/src/api/Client/Midi2Client/MidiStreamConfigurationRequestedSettings.idl
+++ b/src/api/Client/Midi2Client/MidiStreamConfigurationRequestedSettings.idl
@@ -13,7 +13,7 @@ import "MidiProtocolEnum.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiStreamConfigurationRequestedSettings
{
diff --git a/src/api/Client/Midi2Client/MidiStreamMessageBuilder.idl b/src/api/Client/Midi2Client/MidiStreamMessageBuilder.idl
index 82c31608..ceb616ce 100644
--- a/src/api/Client/Midi2Client/MidiStreamMessageBuilder.idl
+++ b/src/api/Client/Midi2Client/MidiStreamMessageBuilder.idl
@@ -21,7 +21,7 @@ import "MidiFunctionBlockDiscoveryRequestsEnum.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
static runtimeclass MidiStreamMessageBuilder
{
static IMidiUniversalPacket BuildEndpointDiscoveryMessage(
diff --git a/src/api/Client/Midi2Client/MidiSystemExclusive8StatusEnum.idl b/src/api/Client/Midi2Client/MidiSystemExclusive8StatusEnum.idl
index 371c0ff5..c5db819b 100644
--- a/src/api/Client/Midi2Client/MidiSystemExclusive8StatusEnum.idl
+++ b/src/api/Client/Midi2Client/MidiSystemExclusive8StatusEnum.idl
@@ -11,7 +11,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
enum MidiSystemExclusive8Status
{
CompleteMessageInSingleMessagePacket = 0x0,
diff --git a/src/api/Client/Midi2Client/MidiUniqueId.idl b/src/api/Client/Midi2Client/MidiUniqueId.idl
index ffd4c843..cfd5b9ac 100644
--- a/src/api/Client/Midi2Client/MidiUniqueId.idl
+++ b/src/api/Client/Midi2Client/MidiUniqueId.idl
@@ -19,7 +19,7 @@ MIDI_IDL_IMPORT
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
runtimeclass MidiUniqueId
{
// byte 1 is LSB according to spec
diff --git a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp
index b046843f..0dad0384 100644
--- a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp
+++ b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp
@@ -14,26 +14,73 @@
namespace winrt::Windows::Devices::Midi2::implementation
{
-
-
_Use_decl_annotations_
- void MidiVirtualEndpointDevice::UpdateFunctionBlock(midi2::MidiFunctionBlock const& block) noexcept
+ bool MidiVirtualEndpointDevice::UpdateFunctionBlock(midi2::MidiFunctionBlock const& block) noexcept
{
internal::LogInfo(__FUNCTION__, L"Enter");
- UNREFERENCED_PARAMETER(block);
+ // If blocks are static, return false. By spec, they are not allowed to be updated.
+ if (m_virtualEndpointDeviceDefinition.AreFunctionBlocksStatic())
+ {
+ internal::LogGeneralError(__FUNCTION__, L"Attempt to update static function blocks in a virtual device.");
+ return false;
+ }
+
+ // check to see if this is an existing block number. If not, fail. Devices are
+ // not allowed to change the number of function blocks they have, per the MIDI 2 spec
+ if (!m_functionBlocks.HasKey(block.Number()))
+ {
+ internal::LogGeneralError(__FUNCTION__, L"Attempt to update a function block which wasn't declared up-front.");
+ return false;
+ }
+
+ // Update the block and send the notification messages
+ if (SendFunctionBlockInfoNotificationMessage(block))
+ {
+ // this ignores that the name may not be set. It's an oversight in the block object design.
+ m_functionBlocks.Insert(block.Number(), block);
+ }
+ else
+ {
+ internal::LogGeneralError(__FUNCTION__, L"Error sending function block info notification message.");
+ return false;
+ }
+
+ // check to see if the function block name changed. If so, send out a name notification
+
+ auto existingBlock = m_functionBlocks.Lookup(block.Number());
+
+ if (existingBlock.Name() != block.Name())
+ {
+ if (!SendFunctionBlockNameNotificationMessages(block))
+ {
+ internal::LogGeneralError(__FUNCTION__, L"Error sending function block name notification messages.");
+ return false;
+ }
+ }
- // TODO
+ return true;
}
_Use_decl_annotations_
- void MidiVirtualEndpointDevice::UpdateEndpointName(winrt::hstring const& name) noexcept
+ bool MidiVirtualEndpointDevice::UpdateEndpointName(winrt::hstring const& name) noexcept
{
internal::LogInfo(__FUNCTION__, L"Enter");
- UNREFERENCED_PARAMETER(name);
+ auto cleanedName = internal::TrimmedHStringCopy(name);
+
+ // TODO: update the name and send the notification message
- // TODO
+ if (m_endpointName != cleanedName)
+ {
+ if (!SendEndpointNameNotificationMessages(cleanedName))
+ {
+ internal::LogGeneralError(__FUNCTION__, L"Error sending endpoint name notification messages.");
+ return false;
+ }
+ }
+
+ return true;
}
@@ -60,7 +107,7 @@ namespace winrt::Windows::Devices::Midi2::implementation
}
_Use_decl_annotations_
- void MidiVirtualEndpointDevice::SendFunctionBlockInfoNotificationMessage(midi2::MidiFunctionBlock const& fb) noexcept
+ bool MidiVirtualEndpointDevice::SendFunctionBlockInfoNotificationMessage(midi2::MidiFunctionBlock const& fb) noexcept
{
internal::LogInfo(__FUNCTION__, L"Enter");
@@ -77,19 +124,21 @@ namespace winrt::Windows::Devices::Midi2::implementation
fb.MaxSystemExclusive8Streams()
);
- // TODO: Log if failed
if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendSingleMessagePacket(functionBlockNotification)))
{
internal::LogGeneralError(__FUNCTION__, L"SendMessagePacket failed");
+ return false;
}
+
+ return true;
}
_Use_decl_annotations_
- void MidiVirtualEndpointDevice::SendFunctionBlockNameNotificationMessages(midi2::MidiFunctionBlock const& fb) noexcept
+ bool MidiVirtualEndpointDevice::SendFunctionBlockNameNotificationMessages(midi2::MidiFunctionBlock const& fb) noexcept
{
internal::LogInfo(__FUNCTION__, L"Enter");
- if (fb.Name() == L"") return;
+ if (fb.Name().empty()) return false;
auto nameMessages = midi2::MidiStreamMessageBuilder::BuildFunctionBlockNameNotificationMessages(
MidiClock::TimestampConstantSendImmediately(),
@@ -99,8 +148,32 @@ namespace winrt::Windows::Devices::Midi2::implementation
if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendMultipleMessagesPacketList(nameMessages.GetView())))
{
- internal::LogGeneralError(__FUNCTION__, L"SendMessagePacketList failed");
+ internal::LogGeneralError(__FUNCTION__, L"SendMultipleMessagesPacketList failed");
+ return false;
+ }
+
+ return true;
+ }
+
+ _Use_decl_annotations_
+ bool MidiVirtualEndpointDevice::SendEndpointNameNotificationMessages(winrt::hstring const& name) noexcept
+ {
+ internal::LogInfo(__FUNCTION__, L"Enter");
+
+ if (name.empty()) return false;
+
+ auto nameMessages = midi2::MidiStreamMessageBuilder::BuildEndpointNameNotificationMessages(
+ MidiClock::TimestampConstantSendImmediately(),
+ name
+ );
+
+ if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendMultipleMessagesPacketList(nameMessages.GetView())))
+ {
+ internal::LogGeneralError(__FUNCTION__, L"SendMultipleMessagesPacketList failed");
+ return false;
}
+
+ return true;
}
@@ -126,7 +199,7 @@ namespace winrt::Windows::Devices::Midi2::implementation
if (internal::EndpointDiscoveryFilterRequestsEndpointInfoNotification(filterFlags))
{
- internal::LogInfo(__FUNCTION__, L"Sending Endpoint Info Notification");
+ internal::LogInfo(__FUNCTION__, L"Building/Sending Endpoint Info Notification");
// send endpoint info notification
@@ -136,26 +209,51 @@ namespace winrt::Windows::Devices::Midi2::implementation
MIDI_PREFERRED_UMP_VERSION_MINOR,
m_areFunctionBlocksStatic,
(uint8_t)m_functionBlocks.Size(),
- true, // TODO: Pull from properties supports midi 2.0
- true, // TODO: pull from properties supports midi 1.0
- false, // todo: pull from default JR timestamp handling
- false // todo: pull from jr timestamp handling
+ m_virtualEndpointDeviceDefinition.SupportsMidi2ProtocolMessages(),
+ m_virtualEndpointDeviceDefinition.SupportsMidi1ProtocolMessages(),
+ m_virtualEndpointDeviceDefinition.SupportsReceivingJRTimestamps(),
+ m_virtualEndpointDeviceDefinition.SupportsSendingJRTimestamps()
);
if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendSingleMessagePacket(notification)))
{
- internal::LogGeneralError(__FUNCTION__, L"SendMessagePacket failed - sending endpoint info notification");
+ internal::LogGeneralError(__FUNCTION__, L"SendSingleMessagePacket failed - sending endpoint info notification");
}
}
if (internal::EndpointDiscoveryFilterRequestsDeviceIdentityNotification(filterFlags))
{
+ internal::LogInfo(__FUNCTION__, L"Building/Sending Device Identity Notification");
+
// send device identity notification
+
+ // TODO: Need to validate no indexes out of bounds here
+
+ auto identityNotification = midi2::MidiStreamMessageBuilder::BuildDeviceIdentityNotificationMessage(
+ MidiClock::TimestampConstantSendImmediately(),
+ m_virtualEndpointDeviceDefinition.DeviceManufacturerSystemExclusiveId().GetAt(0), // byte 1
+ m_virtualEndpointDeviceDefinition.DeviceManufacturerSystemExclusiveId().GetAt(1), // byte 2
+ m_virtualEndpointDeviceDefinition.DeviceManufacturerSystemExclusiveId().GetAt(2), // byte 3
+ m_virtualEndpointDeviceDefinition.DeviceFamilyLsb(),
+ m_virtualEndpointDeviceDefinition.DeviceFamilyMsb(),
+ m_virtualEndpointDeviceDefinition.DeviceFamilyModelLsb(),
+ m_virtualEndpointDeviceDefinition.DeviceFamilyModelMsb(),
+ m_virtualEndpointDeviceDefinition.SoftwareRevisionLevel().GetAt(0), // byte 1
+ m_virtualEndpointDeviceDefinition.SoftwareRevisionLevel().GetAt(1), // byte 2
+ m_virtualEndpointDeviceDefinition.SoftwareRevisionLevel().GetAt(2), // byte 3
+ m_virtualEndpointDeviceDefinition.SoftwareRevisionLevel().GetAt(3) // byte 4
+ );
+
+ if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendSingleMessagePacket(identityNotification)))
+ {
+ internal::LogGeneralError(__FUNCTION__, L"SendSingleMessagePacket failed - sending device identity notification");
+ }
+
}
if (internal::EndpointDiscoveryFilterRequestsEndpointNameNotification(filterFlags))
{
- internal::LogInfo(__FUNCTION__, L"Sending Endpoint Name Notification");
+ internal::LogInfo(__FUNCTION__, L"Building/Sending Endpoint Name Notification");
// send endpoint name notification messages
@@ -168,14 +266,14 @@ namespace winrt::Windows::Devices::Midi2::implementation
if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendMultipleMessagesPacketList(nameMessages.GetView())))
{
- internal::LogGeneralError(__FUNCTION__, L"SendMessagePacketList failed - sending endpoint name notification list");
+ internal::LogGeneralError(__FUNCTION__, L"SendMultipleMessagesPacketList failed - sending endpoint name notification list");
}
}
}
if (internal::EndpointDiscoveryFilterRequestsProductInstanceIdNotification(filterFlags))
{
- internal::LogInfo(__FUNCTION__, L"Sending Endpoint Product Instance Id Notification");
+ internal::LogInfo(__FUNCTION__, L"Building/Sending Endpoint Product Instance Id Notification");
// send product instance id notification messages
@@ -188,14 +286,34 @@ namespace winrt::Windows::Devices::Midi2::implementation
if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendMultipleMessagesPacketList(instanceIdMessages.GetView())))
{
- internal::LogGeneralError(__FUNCTION__, L"SendMessagePacketList failed - sending product instance id messages");
+ internal::LogGeneralError(__FUNCTION__, L"SendMultipleMessagesPacketList failed - sending product instance id messages");
}
}
}
if (internal::EndpointDiscoveryFilterRequestsStreamConfigurationNotification(filterFlags))
{
- // send stream configuration message
+ internal::LogInfo(__FUNCTION__, L"Building/Sending Stream Configuration Notification");
+
+ uint8_t protocol{ (uint8_t)midi2::MidiProtocol::Midi1 };
+
+ if (m_virtualEndpointDeviceDefinition.SupportsMidi2ProtocolMessages())
+ {
+ protocol = (uint8_t)midi2::MidiProtocol::Midi2;
+ }
+
+ auto streamConfigurationNotification = midi2::MidiStreamMessageBuilder::BuildStreamConfigurationNotificationMessage(
+ MidiClock::TimestampConstantSendImmediately(),
+ protocol,
+ m_virtualEndpointDeviceDefinition.SupportsReceivingJRTimestamps(),
+ m_virtualEndpointDeviceDefinition.SupportsSendingJRTimestamps()
+ );
+
+ if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendSingleMessagePacket(streamConfigurationNotification)))
+ {
+ internal::LogGeneralError(__FUNCTION__, L"SendSingleMessagePacket failed - sending device identity notification");
+ }
+
}
}
else if (internal::MessageIsFunctionBlockDiscoveryRequest(message.Word0()))
@@ -245,13 +363,26 @@ namespace winrt::Windows::Devices::Midi2::implementation
}
}
- else
+ else if (internal::MessageIsStreamConfigurationRequest(message.Word0()))
{
- // something else
- }
+ // raise stream configuration request message
+ auto reqArgs = winrt::make_self();
+ auto protocol = (midi2::MidiProtocol)MIDIWORDBYTE3(message.Word0());
+ bool rxjr = (bool)((message.Word0() & 0x00000002) == 0x00000002);
+ bool txjr = (bool)((message.Word0() & 0x00000001) == 0x00000001);
+ reqArgs->InternalInitialize(message.Timestamp(), protocol, rxjr, txjr);
+ if (m_streamConfigurationRequestReceivedEvent)
+ {
+ m_streamConfigurationRequestReceivedEvent(*this, *reqArgs);
+ }
+ }
+ else
+ {
+ // something else
+ }
}
else
{
diff --git a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h
index a354901c..a04543e7 100644
--- a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h
+++ b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h
@@ -51,8 +51,8 @@ namespace winrt::Windows::Devices::Midi2::implementation
bool AreFunctionBlocksStatic() { return m_areFunctionBlocksStatic; }
collections::IMapView FunctionBlocks() noexcept { return m_functionBlocks.GetView(); }
- void UpdateFunctionBlock(_In_ midi2::MidiFunctionBlock const& block) noexcept;
- void UpdateEndpointName(_In_ winrt::hstring const& name) noexcept;
+ bool UpdateFunctionBlock(_In_ midi2::MidiFunctionBlock const& block) noexcept;
+ bool UpdateEndpointName(_In_ winrt::hstring const& name) noexcept;
midi2::MidiVirtualEndpointDeviceDefinition DeviceDefinition() { return m_virtualEndpointDeviceDefinition; }
@@ -72,9 +72,10 @@ namespace winrt::Windows::Devices::Midi2::implementation
private:
- void SendFunctionBlockInfoNotificationMessage(_In_ midi2::MidiFunctionBlock const& fb) noexcept;
- void SendFunctionBlockNameNotificationMessages(_In_ midi2::MidiFunctionBlock const& fb) noexcept;
-
+ bool SendFunctionBlockInfoNotificationMessage(_In_ midi2::MidiFunctionBlock const& fb) noexcept;
+ bool SendFunctionBlockNameNotificationMessages(_In_ midi2::MidiFunctionBlock const& fb) noexcept;
+ bool SendEndpointNameNotificationMessages(_In_ winrt::hstring const& name) noexcept;
+
//void MidiVirtualEndpointDevice::QueueWorker();
//void EnqueueOutgoingMessage(_In_ internal::PackedUmp128 const& message);
diff --git a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.idl b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.idl
index 586c8040..3924d5ee 100644
--- a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.idl
+++ b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.idl
@@ -21,7 +21,7 @@ import "MidiVirtualEndpointDeviceDefinition.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiVirtualEndpointDevice : IMidiEndpointMessageProcessingPlugin
{
@@ -32,9 +32,9 @@ namespace Windows.Devices.Midi2
IMapView FunctionBlocks{ get; };
// only available if blocks are not static
- void UpdateFunctionBlock(MidiFunctionBlock block);
+ Boolean UpdateFunctionBlock(MidiFunctionBlock block);
- void UpdateEndpointName(String name);
+ Boolean UpdateEndpointName(String name);
// true if you want the stream / function block etc. messages automatically handled
// and then removed from the stream. False if the endpoint should still raise
diff --git a/src/api/Client/Midi2Client/MidiVirtualEndpointDeviceDefinition.idl b/src/api/Client/Midi2Client/MidiVirtualEndpointDeviceDefinition.idl
index 7a713355..93a6321e 100644
--- a/src/api/Client/Midi2Client/MidiVirtualEndpointDeviceDefinition.idl
+++ b/src/api/Client/Midi2Client/MidiVirtualEndpointDeviceDefinition.idl
@@ -15,7 +15,7 @@ import "MidiFunctionBlock.idl";
namespace Windows.Devices.Midi2
{
- [MIDI_API_CONTRACT(1)]
+ [MIDI_API_CONTRACT(19)]
[default_interface]
runtimeclass MidiVirtualEndpointDeviceDefinition
{
diff --git a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj
index 2633b312..55f08a12 100644
--- a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj
+++ b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj
@@ -344,6 +344,7 @@
MidiStreamConfigurationRequestedSettings.idl
+
MidiStreamMessageBuilder.idl
@@ -486,6 +487,7 @@
MidiStreamConfigurationRequestedSettings.idl
+
MidiStreamMessageBuilder.idl
diff --git a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj.filters b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj.filters
index 6135e66d..772ca9a3 100644
--- a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj.filters
+++ b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj.filters
@@ -201,6 +201,9 @@
API\Enumeration\DeviceInformation
+
+ API\Endpoints\Message Processing Plugins\Stock Plugins\Virtual Device
+
@@ -297,6 +300,9 @@
API\Enumeration\DeviceInformation
+
+ API\Endpoints\Message Processing Plugins\Stock Plugins\Virtual Device
+
diff --git a/src/api/Client/Midi2Client/pch.h b/src/api/Client/Midi2Client/pch.h
index e7918737..78cf393a 100644
--- a/src/api/Client/Midi2Client/pch.h
+++ b/src/api/Client/Midi2Client/pch.h
@@ -115,6 +115,7 @@ namespace midi2 = ::winrt::Windows::Devices::Midi2;
#include "MidiEndpointDeviceInformationRemovedEventArgs.h"
#include "MidiEndpointDeviceWatcher.h"
+#include "MidiStreamConfigurationRequestReceivedEventArgs.h"
#include "MidiVirtualEndpointDevice.h"
#include "MidiVirtualEndpointDeviceDefinition.h"
diff --git a/src/api/Client/Midi2Client/trace_logging.cpp b/src/api/Client/Midi2Client/trace_logging.cpp
index d5834efa..17ff21a5 100644
--- a/src/api/Client/Midi2Client/trace_logging.cpp
+++ b/src/api/Client/Midi2Client/trace_logging.cpp
@@ -77,7 +77,7 @@ namespace Windows::Devices::Midi2::Internal
const wchar_t* message,
winrt::hresult_error const& ex) noexcept
{
- OutputDebugString(L"" __FUNCTION__ L"API HRESULT Error. Use tracing provider for details.");
+ //OutputDebugString(L"" __FUNCTION__ L"API HRESULT Error. Use tracing provider for details.");
if (!g_traceLoggingRegistered) RegisterTraceLogging();
@@ -98,13 +98,14 @@ namespace Windows::Devices::Midi2::Internal
const char* location,
const wchar_t* message) noexcept
{
- OutputDebugString(L"" __FUNCTION__ L"API General Error. Use tracing provider for details.");
+ //OutputDebugString(L"" __FUNCTION__ L"API General Error. Use tracing provider for details.");
if (!g_traceLoggingRegistered) RegisterTraceLogging();
TraceLoggingWrite(
g_hLoggingProvider,
"MIDI.GeneralError",
+ TraceLoggingOpcode(ERROR),
TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
TraceLoggingKeyword(TRACE_KEYWORD_API_GENERAL),
TraceLoggingString(location, "Location"),
diff --git a/src/api/InBoxApps/mididmp/main.cpp b/src/api/InBoxApps/mididmp/main.cpp
index 4878248c..c4374b43 100644
--- a/src/api/InBoxApps/mididmp/main.cpp
+++ b/src/api/InBoxApps/mididmp/main.cpp
@@ -135,19 +135,10 @@ void OutputError(_In_ std::wstring errorMessage)
#define RETURN_SUCCESS return 0
#define RETURN_FAIL return 1
-int __cdecl main()
-{
- winrt::init_apartment();
-
- bool verbose = true;
- bool pingTest = true;
- bool midiClock = true;
-
- OutputHeader(L"Microsoft Windows MIDI Services");
-
- OutputSectionHeader(L"header");
- OutputCurrentTime();
+bool DoSectionTransports(_In_ bool verbose)
+{
+ UNREFERENCED_PARAMETER(verbose);
try
{
@@ -169,112 +160,239 @@ int __cdecl main()
else
{
OutputError(L"Enumerating transports returned no matches. This is not expected and indicates an installation problem or that the service is not running.");
- RETURN_FAIL;
+ return false;
}
+ }
+ catch (...)
+ {
+ OutputError(L"Exception enumerating transports.");
+ return false;
+ }
+
+ return true;
+}
- OutputSectionHeader(L"enum_endpoints");
+bool DoSectionMidi2ApiEndpoints(_In_ bool verbose)
+{
+ OutputSectionHeader(L"enum_ump_api_endpoints");
- // list devices
+ // list devices
- collections::IVectorView devices{ nullptr };
+ collections::IVectorView devices{ nullptr };
- try
+ try
+ {
+ // list all devices
+ devices = midi2::MidiEndpointDeviceInformation::FindAll(
+ midi2::MidiEndpointDeviceInformationSortOrder::Name,
+ midi2::MidiEndpointDeviceInformationFilters::IncludeClientByteStreamNative |
+ midi2::MidiEndpointDeviceInformationFilters::IncludeClientUmpNative |
+ midi2::MidiEndpointDeviceInformationFilters::IncludeDiagnosticLoopback |
+ midi2::MidiEndpointDeviceInformationFilters::IncludeDiagnosticPing |
+ midi2::MidiEndpointDeviceInformationFilters::IncludeVirtualDeviceResponder
+ );
+ }
+ catch (...)
+ {
+ OutputError(L"Unable to access Windows MIDI Services and enumerate devices.");
+ return false;
+ }
+
+ if (devices != nullptr && devices.Size() > 0)
+ {
+ for (uint32_t i = 0; i < devices.Size(); i++)
{
- // list all devices
- devices = midi2::MidiEndpointDeviceInformation::FindAll(
- midi2::MidiEndpointDeviceInformationSortOrder::Name,
- midi2::MidiEndpointDeviceInformationFilters::IncludeClientByteStreamNative |
- midi2::MidiEndpointDeviceInformationFilters::IncludeClientUmpNative |
- midi2::MidiEndpointDeviceInformationFilters::IncludeDiagnosticLoopback |
- midi2::MidiEndpointDeviceInformationFilters::IncludeDiagnosticPing |
- midi2::MidiEndpointDeviceInformationFilters::IncludeVirtualDeviceResponder
- );
+ auto device = devices.GetAt(i);
+
+ // These names should not be localized because customers may parse these output fields
+
+ OutputStringField(L"endpoint_device_id", device.Id());
+ OutputStringField(L"name", device.Name());
+ OutputStringField(L"transport_mnemonic", device.TransportMnemonic());
+
+ if (verbose)
+ {
+ OutputStringField(L"name_user_supplied", device.UserSuppliedName());
+ OutputStringField(L"name_endpoint_supplied", device.EndpointSuppliedName());
+ OutputStringField(L"name_transport_supplied", device.TransportSuppliedName());
+ OutputStringField(L"description_transport_supplied", device.TransportSuppliedDescription());
+ OutputStringField(L"description_user_supplied", device.UserSuppliedDescription());
+ }
+
+ auto parent = device.GetParentDeviceInformation();
+
+ if (parent != nullptr)
+ {
+ OutputStringField(L"parent_id", parent.Id());
+ OutputStringField(L"parent_name", parent.Name());
+ }
+ else
+ {
+ OutputError(L"Unable to find endpoint parent");
+ }
+
+ if (i != devices.Size() - 1)
+ {
+ OutputItemSeparator();
+ }
}
- catch (...)
+ }
+ else
+ {
+ OutputError(L"Enumerating devices returned no matches. This is not expected and indicates an installation problem or that the service is not running.");
+ return false;
+ }
+
+ return true;
+}
+
+bool DoSectionMidi1ApiEndpoints(_In_ bool verbose)
+{
+ UNREFERENCED_PARAMETER(verbose);
+
+ OutputSectionHeader(L"enum_midi1_api_input_endpoints");
+
+ try
+ {
+ // inputs
+ auto midi1Inputs = winrt::Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(
+ winrt::Windows::Devices::Midi::MidiInPort::GetDeviceSelector()).get();
+
+ for (uint32_t i = 0; i < midi1Inputs.Size(); i++)
{
- OutputError(L"Unable to access Windows MIDI Services and enumerate devices.");
+ auto device = midi1Inputs.GetAt(i);
+
+ OutputStringField(L"endpoint_device_id", device.Id());
+ OutputStringField(L"name", device.Name());
- RETURN_FAIL;
+ if (i != midi1Inputs.Size() - 1)
+ {
+ OutputItemSeparator();
+ }
}
+ }
+ catch (...)
+ {
+ OutputError(L"Enumerating MIDI 1.0 devices encountered an exception.");
- if (devices != nullptr && devices.Size() > 0)
+ return false;
+ }
+
+ OutputSectionHeader(L"enum_midi1_api_output_endpoints");
+
+ try
+ {// outputs
+ auto midi1Outputs = winrt::Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(
+ winrt::Windows::Devices::Midi::MidiOutPort::GetDeviceSelector()).get();
+
+ for (uint32_t i = 0; i < midi1Outputs.Size(); i++)
{
- for (uint32_t i = 0; i < devices.Size(); i++)
+ auto device = midi1Outputs.GetAt(i);
+
+ OutputStringField(L"endpoint_device_id", device.Id());
+ OutputStringField(L"name", device.Name());
+
+ if (i != midi1Outputs.Size() - 1)
{
- auto device = devices.GetAt(i);
-
- // These names should not be localized because customers may parse these output fields
-
- OutputStringField(L"endpoint_device_id", device.Id());
- OutputStringField(L"name", device.Name());
- OutputStringField(L"transport_mnemonic", device.TransportMnemonic());
-
- if (verbose)
- {
- OutputStringField(L"name_user_supplied", device.UserSuppliedName());
- OutputStringField(L"name_endpoint_supplied", device.EndpointSuppliedName());
- OutputStringField(L"name_transport_supplied", device.TransportSuppliedName());
- OutputStringField(L"description_transport_supplied", device.TransportSuppliedDescription());
- OutputStringField(L"description_user_supplied", device.UserSuppliedDescription());
- }
-
- auto parent = device.GetParentDeviceInformation();
-
- if (parent != nullptr)
- {
- OutputStringField(L"parent_id", parent.Id());
- OutputStringField(L"parent_name", parent.Name());
- }
- else
- {
- OutputError(L"Unable to find endpoint parent");
- }
-
- if (i != devices.Size() - 1)
- {
- OutputItemSeparator();
- }
+ OutputItemSeparator();
}
}
+ }
+ catch (...)
+ {
+ OutputError(L"Enumerating MIDI 1.0 devices encountered an exception.");
+
+ return false;
+ }
+}
+
+bool DoSectionPingTest(_In_ bool verbose, _In_ uint8_t pingCount)
+{
+ try
+ {
+ UNREFERENCED_PARAMETER(verbose);
+
+ OutputSectionHeader(L"ping_test");
+
+ OutputTimestampField(L"ping_attempt_count", pingCount);
+
+ auto pingResult = midi2::MidiService::PingService(pingCount);
+
+ OutputNumericField(L"ping_return_count", pingResult.Responses().Size());
+
+ if (pingResult.Success())
+ {
+ OutputTimestampField(L"ping_time_round_trip_total_ticks", pingResult.TotalPingRoundTripMidiClock());
+ OutputTimestampField(L"ping_time_round_trip_average_ticks", pingResult.AveragePingRoundTripMidiClock());
+
+ }
else
{
- OutputError(L"Enumerating devices returned no matches. This is not expected and indicates an installation problem or that the service is not running.");
- RETURN_FAIL;
+ OutputError(L"Ping test failed");
+ OutputStringField(L"ping_failure_reason", pingResult.FailureReason());
+
+ return false;
}
+ }
+ catch (...)
+ {
+ OutputError(L"Ping test failed with exception");
+ return false;
+ }
- const uint8_t pingCount = 10;
+ return true;
+}
- // ping the service
- if (pingTest)
- {
- OutputSectionHeader(L"ping_test");
+bool DoSectionClock(_In_ bool verbose)
+{
+ OutputSectionHeader(L"midi_clock");
- OutputTimestampField(L"ping_attempt_count", pingCount);
+ OutputTimestampField(L"clock_frequency", midi2::MidiClock::TimestampFrequency());
+ OutputTimestampField(L"clock_now", midi2::MidiClock::Now());
- auto pingResult = midi2::MidiService::PingService(pingCount);
+ return true;
+}
- OutputNumericField(L"ping_return_count", pingResult.Responses().Size());
+int __cdecl main()
+{
+ winrt::init_apartment();
- if (pingResult.Success())
- {
- OutputTimestampField(L"ping_time_round_trip_total_ticks", pingResult.TotalPingRoundTripMidiClock());
- OutputTimestampField(L"ping_time_round_trip_average_ticks", pingResult.AveragePingRoundTripMidiClock());
+ bool verbose = true;
+ bool pingTest = true;
+ bool midiClock = true;
- }
- else
+ OutputHeader(L"Microsoft Windows MIDI Services");
+
+ OutputSectionHeader(L"header");
+
+ OutputCurrentTime();
+
+ try
+ {
+ auto transportsWorked = DoSectionTransports(verbose);
+
+ if (transportsWorked)
+ {
+ if (!DoSectionMidi2ApiEndpoints(verbose)) RETURN_FAIL;
+ }
+
+ DoSectionMidi1ApiEndpoints(verbose); // we don't bail if this fails
+
+ if (transportsWorked)
+ {
+ // ping the service
+ if (pingTest)
{
- OutputError(L"Ping test failed");
- OutputStringField(L"ping_failure_reason", pingResult.FailureReason());
+ const uint8_t pingCount = 10;
+
+ if (!DoSectionPingTest(verbose, pingCount)) RETURN_FAIL;
}
}
if (midiClock)
{
- OutputSectionHeader(L"midi_clock");
-
- OutputTimestampField(L"clock_frequency", midi2::MidiClock::TimestampFrequency());
- OutputTimestampField(L"clock_now", midi2::MidiClock::Now());
+ if (!DoSectionClock(verbose)) RETURN_FAIL;
}
}
@@ -285,9 +403,7 @@ int __cdecl main()
RETURN_FAIL;
}
-
OutputSectionHeader(L"end_of_file");
-
RETURN_SUCCESS;
}
diff --git a/src/api/InBoxApps/mididmp/mididmp.vcxproj b/src/api/InBoxApps/mididmp/mididmp.vcxproj
index 9c0ac0f1..73659d27 100644
--- a/src/api/InBoxApps/mididmp/mididmp.vcxproj
+++ b/src/api/InBoxApps/mididmp/mididmp.vcxproj
@@ -15,10 +15,18 @@
+
+ Debug
+ ARM64
+
Debug
Win32
+
+ Release
+ ARM64
+
Release
Win32
@@ -73,10 +81,18 @@
$(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
$(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
+
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
+
$(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
$(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
+
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
+
Use
@@ -92,6 +108,7 @@
Disabled
_DEBUG;%(PreprocessorDefinitions)
stdcpp20
+ stdcpp20
Console
@@ -112,8 +129,10 @@
NDEBUG;%(PreprocessorDefinitions)
stdcpp20
stdcpp20
+ stdcpp20
%(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir);
%(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir);
+ %(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir);
Console
diff --git a/src/api/InBoxApps/mididmp/pch.h b/src/api/InBoxApps/mididmp/pch.h
index db747f2d..a4c871af 100644
--- a/src/api/InBoxApps/mididmp/pch.h
+++ b/src/api/InBoxApps/mididmp/pch.h
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
diff --git a/src/api/Inc/ump_helpers.h b/src/api/Inc/ump_helpers.h
index 422e6ec2..d45af137 100644
--- a/src/api/Inc/ump_helpers.h
+++ b/src/api/Inc/ump_helpers.h
@@ -49,6 +49,8 @@
#define MIDIWORDBYTE4LOWCRUMB4(x) (uint8_t)((x & 0x000000C0) >> 6)
+
+
#define UMP32_WORD_COUNT 1
#define UMP64_WORD_COUNT 2
#define UMP96_WORD_COUNT 3
@@ -70,6 +72,60 @@
#define MIDI_MESSAGE_CHANNEL_BITSHIFT 16
+#define MIDI_UMP_MESSAGE_TYPE_UTILITY_MESSAGE_32 0x0
+#define MIDI_UMP_MESSAGE_TYPE_SYSTEM_COMMON_32 0x1
+#define MIDI_UMP_MESSAGE_TYPE_MIDI1_CHANNEL_VOICE_32 0x2
+#define MIDI_UMP_MESSAGE_TYPE_DATA_MESSAGE_64 0x3
+#define MIDI_UMP_MESSAGE_TYPE_MIDI2_CHANNEL_VOICE_64 0x4
+#define MIDI_UMP_MESSAGE_TYPE_DATA_MESSAGE_128 0x5
+#define MIDI_UMP_MESSAGE_TYPE_FLEX_DATA_128 0xD
+#define MIDI_UMP_MESSAGE_TYPE_STREAM_128 0xF
+
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_REG_PER_NOTE_CONTROLLER 0x0
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_ASSIGN_PER_NOTE_CONTROLLER 0x1
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_REG_CONTROLLER 0x2
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_ASSIGN_CONTROLLER 0x3
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_REL_REG_CONTROLLER 0x4
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_REL_ASSIGN_CONTROLLER 0x5
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_PER_NOTE_PITCH_BEND 0x6
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_NOTE_OFF 0x8
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_NOTE_ON 0x9
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_POLY_PRESSURE 0xA
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_CONTROL_CHANGE 0xB
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_PROGRAM_CHANGE 0xC
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_CHANNEL_PRESSURE 0xD
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_PITCH_BEND 0xE
+#define MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_PER_NOTE_MANAGEMENT 0xF
+
+
+#define MIDI_UMP_MIDI1_CHANNEL_VOICE_STATUS_NOTE_OFF 0x8
+#define MIDI_UMP_MIDI1_CHANNEL_VOICE_STATUS_NOTE_ON 0x9
+#define MIDI_UMP_MIDI1_CHANNEL_VOICE_STATUS_POLY_PRESSURE 0xA
+#define MIDI_UMP_MIDI1_CHANNEL_VOICE_STATUS_CONTROL_CHANGE 0xB
+#define MIDI_UMP_MIDI1_CHANNEL_VOICE_STATUS_PROGRAM_CHANGE 0xC
+#define MIDI_UMP_MIDI1_CHANNEL_VOICE_STATUS_CHANNEL_PRESSURE 0xD
+#define MIDI_UMP_MIDI1_CHANNEL_VOICE_STATUS_PITCH_BEND 0xE
+
+#define MIDI_UMP_MIDI1_BANK_SELECT_MSB_CC_INDEX 0x00
+#define MIDI_UMP_MIDI1_BANK_SELECT_LSB_CC_INDEX 0x20
+
+
+//#define MIDI_RPN_PITCH_BEND_RANGE 0x0000
+//#define MIDI_RPN_COARSE_TUNING 0x0002
+//#define MIDI_RPN_TUNING_PROGRAM_CHANGE 0x0003
+//#define MIDI_RPN_TUNING_BANK_SELECT 0x0004
+//#define MIDI_RPN_MPE_MCM 0x0006
+
+#define MIDI_RPN_CC_NUMBER_BANK 0x65
+#define MIDI_RPN_CC_NUMBER_INDEX 0x64
+#define MIDI_RPN_CC_NUMBER_DATA_COARSE 0x06
+#define MIDI_RPN_CC_NUMBER_DATA_FINE 0x26
+
+#define MIDI_NRPN_CC_NUMBER_BANK 0x63
+#define MIDI_NRPN_CC_NUMBER_INDEX 0x62
+#define MIDI_NRPN_CC_NUMBER_DATA_COARSE 0x06
+#define MIDI_NRPN_CC_NUMBER_DATA_FINE 0x26
+
namespace Windows::Devices::Midi2::Internal
{
@@ -312,6 +368,11 @@ namespace Windows::Devices::Midi2::Internal
return (uint32_t)(byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3);
}
+ inline uint16_t Combine7BitMsbLsbTo14BitValue(_In_ uint8_t msb, _In_ uint8_t lsb)
+ {
+ return ((uint16_t)(CleanupByte7(msb)) << 7) | CleanupByte7(lsb);
+ }
+
inline void SetMidiWordMostSignificantByte1(_Inout_ uint32_t& word, _In_ uint8_t value)
@@ -807,4 +868,6 @@ namespace Windows::Devices::Midi2::Internal
+
+
}
diff --git a/src/api/Midi2.sln b/src/api/Midi2.sln
index f79b6c8b..8a863925 100644
--- a/src/api/Midi2.sln
+++ b/src/api/Midi2.sln
@@ -77,6 +77,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MidiSrv", "Service\Exe\Midi
{7427BC7A-4247-42B0-AC9B-7DA10418AA9D} = {7427BC7A-4247-42B0-AC9B-7DA10418AA9D}
{7E618284-6AA0-4FCE-9E4A-D895A5EE8E3C} = {7E618284-6AA0-4FCE-9E4A-D895A5EE8E3C}
{8795821B-541D-4B9B-BF7F-50CA976FC54E} = {8795821B-541D-4B9B-BF7F-50CA976FC54E}
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB} = {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}
{EFB7CF90-7DEF-44CF-868A-191CA30E0FCF} = {EFB7CF90-7DEF-44CF-868A-191CA30E0FCF}
EndProjectSection
EndProject
@@ -230,6 +231,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Midi2.LoopbackMidiAbstracti
{EFB7CF90-7DEF-44CF-868A-191CA30E0FCF} = {EFB7CF90-7DEF-44CF-868A-191CA30E0FCF}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Midi2.UmpProtocolDownscalerTransform", "Transform\UmpProtocolDownscaler\Midi2.UmpProtocolDownscalerTransform.vcxproj", "{C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -958,8 +961,8 @@ Global
{5EC6D5EB-53D4-4731-891E-F746F0201429}.Debug|x86.Build.0 = Debug|Win32
{5EC6D5EB-53D4-4731-891E-F746F0201429}.Release|Any CPU.ActiveCfg = Release|x64
{5EC6D5EB-53D4-4731-891E-F746F0201429}.Release|Any CPU.Build.0 = Release|x64
- {5EC6D5EB-53D4-4731-891E-F746F0201429}.Release|ARM64.ActiveCfg = Release|x64
- {5EC6D5EB-53D4-4731-891E-F746F0201429}.Release|ARM64.Build.0 = Release|x64
+ {5EC6D5EB-53D4-4731-891E-F746F0201429}.Release|ARM64.ActiveCfg = Release|ARM64
+ {5EC6D5EB-53D4-4731-891E-F746F0201429}.Release|ARM64.Build.0 = Release|ARM64
{5EC6D5EB-53D4-4731-891E-F746F0201429}.Release|ARM64EC.ActiveCfg = Release|x64
{5EC6D5EB-53D4-4731-891E-F746F0201429}.Release|ARM64EC.Build.0 = Release|x64
{5EC6D5EB-53D4-4731-891E-F746F0201429}.Release|x64.ActiveCfg = Release|x64
@@ -986,6 +989,26 @@ Global
{0F94A751-9159-4A88-8A71-347151124548}.Release|x64.Build.0 = Release|x64
{0F94A751-9159-4A88-8A71-347151124548}.Release|x86.ActiveCfg = Release|x64
{0F94A751-9159-4A88-8A71-347151124548}.Release|x86.Build.0 = Release|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Debug|Any CPU.Build.0 = Debug|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Debug|ARM64.Build.0 = Debug|ARM64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Debug|ARM64EC.ActiveCfg = Debug|ARM64EC
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Debug|ARM64EC.Build.0 = Debug|ARM64EC
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Debug|x64.ActiveCfg = Debug|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Debug|x64.Build.0 = Debug|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Debug|x86.ActiveCfg = Debug|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Debug|x86.Build.0 = Debug|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Release|Any CPU.ActiveCfg = Release|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Release|Any CPU.Build.0 = Release|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Release|ARM64.ActiveCfg = Release|ARM64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Release|ARM64.Build.0 = Release|ARM64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Release|ARM64EC.ActiveCfg = Release|ARM64EC
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Release|ARM64EC.Build.0 = Release|ARM64EC
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Release|x64.ActiveCfg = Release|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Release|x64.Build.0 = Release|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Release|x86.ActiveCfg = Release|x64
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1031,6 +1054,7 @@ Global
{16F34756-0269-4CEB-8F1C-32D9C6441990} = {6DB1D424-53D4-488F-8053-EBBD7D1F2E49}
{5EC6D5EB-53D4-4731-891E-F746F0201429} = {67A42126-8502-4681-9ACC-B1417C527620}
{0F94A751-9159-4A88-8A71-347151124548} = {69CC5CD9-47DC-4118-A3C7-E0D071407185}
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB} = {23C1D0EF-10F0-464B-86C3-1FCD3F6BA20E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7F5A157F-9894-451D-B091-848921952166}
diff --git a/src/api/Midi2Setup/Midi2Setup.vdproj b/src/api/Midi2Setup/Midi2Setup.vdproj
index 98940aaf..3fcd11a8 100644
--- a/src/api/Midi2Setup/Midi2Setup.vdproj
+++ b/src/api/Midi2Setup/Midi2Setup.vdproj
@@ -682,6 +682,12 @@
"Entry"
{
"MsmKey" = "8:_233E143DE3B73E4453F74EDD5B3ABEEC"
+ "OwnerKey" = "8:_52D7284C08974CD7BCE0BD610635A9EF"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_233E143DE3B73E4453F74EDD5B3ABEEC"
"OwnerKey" = "8:_EF73BDE238574EA1B9C0A604958811F1"
"MsmSig" = "8:_UNDEFINED"
}
@@ -796,6 +802,12 @@
"Entry"
{
"MsmKey" = "8:_276951EB6BDD2CD6AB8AE7556C7D8689"
+ "OwnerKey" = "8:_52D7284C08974CD7BCE0BD610635A9EF"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_276951EB6BDD2CD6AB8AE7556C7D8689"
"OwnerKey" = "8:_EF73BDE238574EA1B9C0A604958811F1"
"MsmSig" = "8:_UNDEFINED"
}
@@ -910,6 +922,12 @@
"Entry"
{
"MsmKey" = "8:_2C0A596B30D1A4DE8AC76BEFC0BF3415"
+ "OwnerKey" = "8:_52D7284C08974CD7BCE0BD610635A9EF"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_2C0A596B30D1A4DE8AC76BEFC0BF3415"
"OwnerKey" = "8:_EF73BDE238574EA1B9C0A604958811F1"
"MsmSig" = "8:_UNDEFINED"
}
@@ -2794,6 +2812,12 @@
"Entry"
{
"MsmKey" = "8:_4F91EF9506038C6EEBA69C5DDE51BF29"
+ "OwnerKey" = "8:_52D7284C08974CD7BCE0BD610635A9EF"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_4F91EF9506038C6EEBA69C5DDE51BF29"
"OwnerKey" = "8:_EF73BDE238574EA1B9C0A604958811F1"
"MsmSig" = "8:_UNDEFINED"
}
@@ -3454,6 +3478,12 @@
"Entry"
{
"MsmKey" = "8:_50D875E42CEF6E0475EAB4384E4C79E1"
+ "OwnerKey" = "8:_52D7284C08974CD7BCE0BD610635A9EF"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_50D875E42CEF6E0475EAB4384E4C79E1"
"OwnerKey" = "8:_EF73BDE238574EA1B9C0A604958811F1"
"MsmSig" = "8:_UNDEFINED"
}
@@ -3549,6 +3579,12 @@
}
"Entry"
{
+ "MsmKey" = "8:_52D7284C08974CD7BCE0BD610635A9EF"
+ "OwnerKey" = "8:_UNDEFINED"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
"MsmKey" = "8:_53F57B074B84A66BFAC43DAE75C45C7C"
"OwnerKey" = "8:_0AA6EC5B722D4FDBA7D4F9D05F8643E3"
"MsmSig" = "8:_UNDEFINED"
@@ -3808,6 +3844,12 @@
"Entry"
{
"MsmKey" = "8:_95A010555EE939A8528D490FA44410C9"
+ "OwnerKey" = "8:_52D7284C08974CD7BCE0BD610635A9EF"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_95A010555EE939A8528D490FA44410C9"
"OwnerKey" = "8:_EF73BDE238574EA1B9C0A604958811F1"
"MsmSig" = "8:_UNDEFINED"
}
@@ -5134,6 +5176,12 @@
"Entry"
{
"MsmKey" = "8:_E386CA0AE72FBD122562EAE2A9B23CC9"
+ "OwnerKey" = "8:_52D7284C08974CD7BCE0BD610635A9EF"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_E386CA0AE72FBD122562EAE2A9B23CC9"
"OwnerKey" = "8:_EF73BDE238574EA1B9C0A604958811F1"
"MsmSig" = "8:_UNDEFINED"
}
@@ -10561,6 +10609,34 @@
{
}
}
+ "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_52D7284C08974CD7BCE0BD610635A9EF"
+ {
+ "SourcePath" = "8:..\\VSFiles\\x64\\Release\\Midi2.UmpProtocolDownscalerTransform.dll"
+ "TargetName" = "8:"
+ "Tag" = "8:"
+ "Folder" = "8:_1DC960FE630C406E9A1F93C99C1E099A"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "IsolateTo" = "8:"
+ "ProjectOutputGroupRegister" = "3:4"
+ "OutputConfiguration" = "8:"
+ "OutputGroupCanonicalName" = "8:Built"
+ "OutputProjectGuid" = "8:{C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}"
+ "ShowKeyOutput" = "11:TRUE"
+ "ExcludeFilters"
+ {
+ }
+ }
"{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_5B95BDBA834E45B6BA7B73AC8260C86C"
{
"SourcePath" = "8:..\\VSFiles\\x64\\Release\\Midi2.BluetoothMidiAbstraction.dll"
diff --git a/src/api/Service/Exe/MidiClientManager.cpp b/src/api/Service/Exe/MidiClientManager.cpp
index 6e4b0eea..f904813d 100644
--- a/src/api/Service/Exe/MidiClientManager.cpp
+++ b/src/api/Service/Exe/MidiClientManager.cpp
@@ -48,8 +48,6 @@ CMidiClientManager::Cleanup()
);
- OutputDebugString(L"" __FUNCTION__ " enter");
-
auto lock = m_ClientManagerLock.lock();
m_PerformanceManager.reset();
@@ -76,51 +74,116 @@ CMidiClientManager::Cleanup()
}
m_DevicePipes.clear();
- OutputDebugString(L"" __FUNCTION__ " exit");
-
return S_OK;
}
-
HRESULT
-GetEndpointShouldHaveMetadataHandler(_In_ std::wstring MidiDevice, _Inout_ bool& AddMetadataListener, _In_ MidiFlow& Flow)
+GetEndpointRequiresOutboundProtocolDownscaling(
+ _In_ std::wstring const& MidiDevice,
+ _In_ MidiFlow const Flow,
+ _In_ MidiDataFormat const DeviceFormat,
+ _Inout_ bool& AddProtocolDownscaler)
{
- if (Flow == MidiFlow::MidiFlowBidirectional || Flow == MidiFlow::MidiFlowIn)
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingWideString(MidiDevice.c_str(), "Device Id")
+ );
+
+ // default to false
+ AddProtocolDownscaler = false;
+
+ if (DeviceFormat == MidiDataFormat::MidiDataFormat_UMP && (Flow == MidiFlow::MidiFlowBidirectional || Flow == MidiFlow::MidiFlowOut))
{
auto additionalProperties = winrt::single_threaded_vector();
- additionalProperties.Append(winrt::to_hstring(STRING_PKEY_MIDI_EndpointRequiresMetadataHandler));
- auto deviceInfo = DeviceInformation::CreateFromIdAsync(MidiDevice, additionalProperties, winrt::Windows::Devices::Enumeration::DeviceInformationKind::DeviceInterface).get();
+ additionalProperties.Append(winrt::to_hstring(STRING_PKEY_MIDI_NativeDataFormat));
+
+ auto deviceInfo = DeviceInformation::CreateFromIdAsync(
+ MidiDevice, additionalProperties,
+ winrt::Windows::Devices::Enumeration::DeviceInformationKind::DeviceInterface).get();
- auto prop = deviceInfo.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_EndpointRequiresMetadataHandler));
+ auto prop = deviceInfo.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_NativeDataFormat));
if (prop)
{
- OutputDebugString(__FUNCTION__ L" found property");
-
// this interface is pointing to a UMP interface, so use that instance id.
- AddMetadataListener = winrt::unbox_value(prop);
- }
- else
- {
- OutputDebugString(__FUNCTION__ L" did not find property");
+ auto nativeFormat = winrt::unbox_value(prop);
- // default to true
- AddMetadataListener = true;
+ if (nativeFormat == MIDI_PROP_NATIVEDATAFORMAT_BYTESTREAM)
+ {
+ // Native bytestream behind a UMP driver, so yes, we need to downscale because the driver will just discard MT4
+
+ AddProtocolDownscaler = true;
+ }
}
}
- else
- {
- // output-only flow
- AddMetadataListener = false;
- }
+
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingWideString(MidiDevice.c_str(), "Device Id"),
+ TraceLoggingBool(AddProtocolDownscaler, "Requires downscaler")
+ );
return S_OK;
}
+//HRESULT
+//GetEndpointShouldHaveMetadataHandler(_In_ std::wstring MidiDevice, _Inout_ bool& AddMetadataListener, _In_ MidiFlow Flow)
+//{
+// TraceLoggingWrite(
+// MidiSrvTelemetryProvider::Provider(),
+// __FUNCTION__,
+// TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+// TraceLoggingWideString(MidiDevice.c_str(), "Device Id")
+// );
+//
+// if (Flow == MidiFlow::MidiFlowBidirectional || Flow == MidiFlow::MidiFlowIn)
+// {
+// auto additionalProperties = winrt::single_threaded_vector();
+// additionalProperties.Append(winrt::to_hstring(STRING_PKEY_MIDI_EndpointRequiresMetadataHandler));
+// auto deviceInfo = DeviceInformation::CreateFromIdAsync(MidiDevice, additionalProperties, winrt::Windows::Devices::Enumeration::DeviceInformationKind::DeviceInterface).get();
+//
+// auto prop = deviceInfo.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_EndpointRequiresMetadataHandler));
+// if (prop)
+// {
+// // this interface is pointing to a UMP interface, so use that instance id.
+// AddMetadataListener = winrt::unbox_value(prop);
+// }
+// else
+// {
+// // default to true
+// AddMetadataListener = true;
+// }
+// }
+// else
+// {
+// // output-only flow
+// AddMetadataListener = false;
+// }
+//
+// TraceLoggingWrite(
+// MidiSrvTelemetryProvider::Provider(),
+// __FUNCTION__,
+// TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+// TraceLoggingWideString(MidiDevice.c_str(), "Device Id"),
+// TraceLoggingBool(AddMetadataListener, "Add Metadata Listener")
+// );
+//
+// return S_OK;
+//}
+
HRESULT
GetDeviceSupportedDataFormat(_In_ std::wstring MidiDevice, _Inout_ MidiDataFormat& DataFormat)
{
- OutputDebugString(__FUNCTION__ L" enter");
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingWideString(MidiDevice.c_str(), "Device Id")
+ );
auto additionalProperties = winrt::single_threaded_vector();
additionalProperties.Append(winrt::to_hstring(STRING_PKEY_MIDI_SupportedDataFormats));
@@ -129,9 +192,6 @@ GetDeviceSupportedDataFormat(_In_ std::wstring MidiDevice, _Inout_ MidiDataForma
auto prop = deviceInfo.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_SupportedDataFormats));
if (prop)
{
-
- OutputDebugString(__FUNCTION__ L" found property");
-
DataFormat = MidiDataFormat::MidiDataFormat_Any;
try
{
@@ -142,12 +202,17 @@ GetDeviceSupportedDataFormat(_In_ std::wstring MidiDevice, _Inout_ MidiDataForma
}
else
{
- OutputDebugString(__FUNCTION__ L" didn't find property");
// default to any
DataFormat = MidiDataFormat::MidiDataFormat_Any;
}
- OutputDebugString(__FUNCTION__ L" exiting OK");
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingWideString(MidiDevice.c_str(), "Device Id"),
+ TraceLoggingBool(DataFormat, "MIDI Data Format")
+ );
return S_OK;
}
@@ -155,6 +220,13 @@ GetDeviceSupportedDataFormat(_In_ std::wstring MidiDevice, _Inout_ MidiDataForma
HRESULT
GetEndpointGenerateIncomingTimestamp(_In_ std::wstring MidiDevice, _Inout_ bool& GenerateIncomingTimestamp, _In_ MidiFlow& Flow)
{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingWideString(MidiDevice.c_str(), "Device Id")
+ );
+
// we only generate timestamps for incoming messages FROM the device
if (Flow == MidiFlow::MidiFlowBidirectional || Flow == MidiFlow::MidiFlowIn)
{
@@ -180,14 +252,26 @@ GetEndpointGenerateIncomingTimestamp(_In_ std::wstring MidiDevice, _Inout_ bool&
GenerateIncomingTimestamp = false;
}
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingWideString(MidiDevice.c_str(), "Device Id"),
+ TraceLoggingBool(GenerateIncomingTimestamp, "Generate Incoming Timestamp")
+ );
+
return S_OK;
}
HRESULT
GetEndpointAlias(_In_ LPCWSTR MidiDevice, _In_ std::wstring& Alias, _In_ MidiFlow& AliasFlow)
{
- OutputDebugString(__FUNCTION__ L" enter. Device:");
- OutputDebugString(MidiDevice);
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingWideString(MidiDevice, "Device Id")
+ );
Alias = MidiDevice;
@@ -195,15 +279,11 @@ GetEndpointAlias(_In_ LPCWSTR MidiDevice, _In_ std::wstring& Alias, _In_ MidiFlo
additionalProperties.Append(winrt::to_hstring(STRING_PKEY_MIDI_AssociatedUMP));
auto deviceInfo = DeviceInformation::CreateFromIdAsync(MidiDevice, additionalProperties, winrt::Windows::Devices::Enumeration::DeviceInformationKind::DeviceInterface).get();
- OutputDebugString(__FUNCTION__ L" looking up prop");
auto prop = deviceInfo.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_AssociatedUMP));
- OutputDebugString(__FUNCTION__ L" got prop. About to check for null");
if (prop)
{
- OutputDebugString(L"" __FUNCTION__ " STRING_PKEY_MIDI_AssociatedUMP property present");
-
// this interface is pointing to a UMP interface, so use that instance id.
Alias = winrt::unbox_value(prop).c_str();
@@ -226,9 +306,6 @@ GetEndpointAlias(_In_ LPCWSTR MidiDevice, _In_ std::wstring& Alias, _In_ MidiFlo
std::transform(Alias.begin(), Alias.end(), Alias.begin(), ::towlower);
- OutputDebugString(__FUNCTION__ L" exit. Alias:");
- OutputDebugString(Alias.c_str());
-
return S_OK;
}
@@ -250,8 +327,8 @@ CMidiClientManager::GetMidiClient(
__FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
TraceLoggingPointer(this, "this"),
- TraceLoggingWideString(MidiDevice),
- TraceLoggingGuid(SessionId)
+ TraceLoggingWideString(MidiDevice, "Device Id"),
+ TraceLoggingGuid(SessionId, "Session Id")
);
wil::com_ptr_nothrow clientPipe;
@@ -285,7 +362,7 @@ CMidiClientManager::GetMidiDevice(
__FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
TraceLoggingPointer(this, "this"),
- TraceLoggingWideString(MidiDevice)
+ TraceLoggingWideString(MidiDevice, "Device Id")
);
// Get an existing device pipe if one exists, otherwise create a new pipe
@@ -322,6 +399,79 @@ CMidiClientManager::GetMidiDevice(
return S_OK;
}
+
+_Use_decl_annotations_
+HRESULT
+CMidiClientManager::GetMidiProtocolDownscalerTransform(
+ _In_ handle_t BindingHandle,
+ _In_ MidiFlow Flow,
+ _In_ wil::com_ptr_nothrow& DevicePipe,
+ _In_ wil::com_ptr_nothrow& NextDeviceSidePipe,
+ _In_ wil::com_ptr_nothrow& ClientConnectionPipe)
+{
+ RETURN_HR_IF_NULL(E_INVALIDARG, DevicePipe);
+
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(DevicePipe->MidiDevice().c_str(), "Device Id")
+ );
+
+ RETURN_HR_IF(E_UNEXPECTED, Flow != MidiFlow::MidiFlowOut);
+
+ wil::com_ptr_nothrow transformPipe{ nullptr };
+
+ auto transformGuid = _uuidof(Midi2UmpProtocolDownscalerTransform);
+
+ // search existing transforms for this device for an output that supports
+ // the requested flow and data format.
+ auto transforms = m_TransformPipes.equal_range(DevicePipe->MidiDevice());
+ for (auto& transform = transforms.first; transform != transforms.second; ++transform)
+ {
+ if (Flow == transform->second->Flow() &&
+ transform->second->TransformGuid() == transformGuid)
+ {
+ RETURN_HR_IF(E_UNEXPECTED, transformPipe);
+ transformPipe = transform->second;
+ }
+ }
+
+ // connect transform to device because we're transforming for the case where
+ // we have a MIDI 1.0 device connected to the new driver, and we need to
+ // transform MT4 messages to MT2 messages
+
+ if (!transformPipe)
+ {
+ MIDISRV_TRANSFORMCREATION_PARAMS creationParams{ };
+
+ creationParams.Flow = Flow;
+ creationParams.DataFormatIn = MidiDataFormat::MidiDataFormat_UMP;
+ creationParams.DataFormatOut = MidiDataFormat::MidiDataFormat_UMP;
+ creationParams.TransformGuid = transformGuid;
+
+ // create the transform
+ wil::com_ptr_nothrow transform;
+ RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&transform));
+
+ RETURN_IF_FAILED(transform->Initialize(BindingHandle, DevicePipe->MidiDevice().c_str(), &creationParams, &m_MmcssTaskId, (IUnknown*)&m_DeviceManager));
+ transformPipe = transform.get();
+
+ // connect the transform to the device
+ RETURN_IF_FAILED(transformPipe->AddConnectedPipe(NextDeviceSidePipe));
+
+ m_TransformPipes.emplace(DevicePipe->MidiDevice(), transform);
+ }
+
+ ClientConnectionPipe = transformPipe;
+
+ return S_OK;
+}
+
+
+
+
// This function handles data format translation only
_Use_decl_annotations_
HRESULT
@@ -334,11 +484,14 @@ CMidiClientManager::GetMidiTransform(
wil::com_ptr_nothrow& ClientConnectionPipe
)
{
+ RETURN_HR_IF_NULL(E_INVALIDARG, DevicePipe);
+
TraceLoggingWrite(
MidiSrvTelemetryProvider::Provider(),
__FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingPointer(this, "this")
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(DevicePipe->MidiDevice().c_str(), "Device Id")
);
@@ -364,7 +517,7 @@ CMidiClientManager::GetMidiTransform(
// not found, instantiate the transform that is needed.
if (!transformPipe)
{
- MIDISRV_TRANSFORMCREATION_PARAMS creationParams {0};
+ MIDISRV_TRANSFORMCREATION_PARAMS creationParams { };
creationParams.Flow = Flow;
creationParams.DataFormatIn = DataFormatFrom;
@@ -449,11 +602,14 @@ CMidiClientManager::GetMidiScheduler(
wil::com_ptr_nothrow& ClientConnectionPipe
)
{
+ RETURN_HR_IF_NULL(E_INVALIDARG, DevicePipe);
+
TraceLoggingWrite(
MidiSrvTelemetryProvider::Provider(),
__FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingPointer(this, "this")
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(DevicePipe->MidiDevice().c_str(), "Device Id")
);
@@ -487,7 +643,14 @@ CMidiClientManager::GetMidiScheduler(
// not found, instantiate the transform that is needed.
if (!transformPipe)
{
-// OutputDebugString(L"" __FUNCTION__ " scheduler transform pipe not found. Creating one.");
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(DevicePipe->MidiDevice().c_str(), "Device Id"),
+ TraceLoggingWideString(L"Creating new scheduler", "message")
+ );
MIDISRV_TRANSFORMCREATION_PARAMS creationParams{ 0 };
@@ -534,11 +697,14 @@ CMidiClientManager::GetMidiEndpointMetadataHandler(
wil::com_ptr_nothrow& ClientConnectionPipe
)
{
+ RETURN_HR_IF_NULL(E_INVALIDARG, DevicePipe);
+
TraceLoggingWrite(
MidiSrvTelemetryProvider::Provider(),
__FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingPointer(this, "this")
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(DevicePipe->MidiDevice().c_str(), "Device Id")
);
wil::com_ptr_nothrow transformPipe{ nullptr };
@@ -598,6 +764,7 @@ CMidiClientManager::GetMidiEndpointMetadataHandler(
}
+
_Use_decl_annotations_
HRESULT
CMidiClientManager::CreateMidiClient(
@@ -605,7 +772,8 @@ CMidiClientManager::CreateMidiClient(
LPCWSTR MidiDevice,
GUID SessionId,
PMIDISRV_CLIENTCREATION_PARAMS CreationParams,
- PMIDISRV_CLIENT Client
+ PMIDISRV_CLIENT Client,
+ BOOL InternalProtocolNegotiationUseOnly
)
{
TraceLoggingWrite(
@@ -617,6 +785,52 @@ CMidiClientManager::CreateMidiClient(
TraceLoggingGuid(SessionId)
);
+ if (InternalProtocolNegotiationUseOnly && Client->DataFormat != MidiDataFormat::MidiDataFormat_UMP)
+ {
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(MidiDevice),
+ TraceLoggingGuid(SessionId),
+ TraceLoggingWideString(L"Called for internal protocol negotiation, but client dataformat is not UMP", "message")
+ );
+
+ return E_UNEXPECTED;
+ }
+
+ if (InternalProtocolNegotiationUseOnly && CreationParams->DataFormat != MidiDataFormat::MidiDataFormat_UMP)
+ {
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(MidiDevice),
+ TraceLoggingGuid(SessionId),
+ TraceLoggingWideString(L"Called for internal protocol negotiation, but creation params dataformat is not UMP", "message")
+ );
+
+ return E_UNEXPECTED;
+ }
+
+ if (InternalProtocolNegotiationUseOnly && CreationParams->Flow != MidiFlow::MidiFlowBidirectional)
+ {
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(MidiDevice),
+ TraceLoggingGuid(SessionId),
+ TraceLoggingWideString(L"Called for internal protocol negotiation, but creation params data flow is not Bidirectional", "message")
+ );
+
+ return E_UNEXPECTED;
+ }
+
+
auto lock = m_ClientManagerLock.lock();
DWORD clientProcessId{0};
@@ -630,45 +844,54 @@ CMidiClientManager::CreateMidiClient(
unique_mmcss_handle MmcssHandle;
std::wstring midiDevice;
- // get the client PID, impersonate the client to get the client process handle, and then
- // revert back to self.
- RETURN_IF_FAILED(HRESULT_FROM_RPCSTATUS(I_RpcBindingInqLocalClientPID(BindingHandle, &clientProcessId)));
- RETURN_IF_FAILED(HRESULT_FROM_RPCSTATUS(RpcImpersonateClient(BindingHandle)));
- clientProcessHandle.reset(OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, FALSE, clientProcessId));
- RETURN_IF_FAILED(HRESULT_FROM_RPCSTATUS(RpcRevertToSelf()));
-
- RETURN_LAST_ERROR_IF_NULL(clientProcessHandle);
-
- // pre-populate our mmcss task id so we have a known task id
- // to provide and use for all other tasks related to this pipe
- // retain the MMCSS configuration until after the client and device
- // pipes are initialized, so this TaskId remains valid through
- // initialization. Safe to disable mmcss for this rpc thread,
- // once we complete initialization and have worker threads
- // actively using this task id for the duration of this pipe.
- RETURN_IF_FAILED(EnableMmcss(MmcssHandle, m_MmcssTaskId));
-
- // The provided SWD instance id provided may be an alias of a UMP device, retrieve the
- // PKEY_MIDI_AssociatedUMP property to retrieve the id of the primary device.
- // We only create device pipe entries for the primary devices.
- RETURN_IF_FAILED(GetEndpointAlias(MidiDevice, midiDevice, CreationParams->Flow));
-
- // see if we should create timestamps or not. This option is set
- // by the transport at endpoint enumeration time. For most endpoint
- // types, this will be true, but for diagnostics loopback endpoints,
- // it is false. So, we store the value in the property store to make
- // it more flexible
+ //bool addMetadataListenerToIncomingStream{ true };
bool generateIncomingMessageTimestamps{ true };
- RETURN_IF_FAILED(GetEndpointGenerateIncomingTimestamp(midiDevice, generateIncomingMessageTimestamps, CreationParams->Flow));
- // Some endpoints, like the device-side of an app-to-app MIDI connection,
- // should not have metadata listeners. This flag controls whether or not
- // we add one if otherwise eligible.
- bool addMetadataListenerToIncomingStream{ true };
- RETURN_IF_FAILED(GetEndpointShouldHaveMetadataHandler(midiDevice, addMetadataListenerToIncomingStream, CreationParams->Flow));
-
-
- RETURN_IF_FAILED(GetMidiClient(BindingHandle, midiDevice.c_str(), SessionId, CreationParams, Client, clientProcessHandle, clientPipe, generateIncomingMessageTimestamps));
+ if (!InternalProtocolNegotiationUseOnly)
+ {
+ // get the client PID, impersonate the client to get the client process handle, and then
+ // revert back to self.
+ RETURN_IF_FAILED(HRESULT_FROM_RPCSTATUS(I_RpcBindingInqLocalClientPID(BindingHandle, &clientProcessId)));
+ RETURN_IF_FAILED(HRESULT_FROM_RPCSTATUS(RpcImpersonateClient(BindingHandle)));
+ clientProcessHandle.reset(OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, FALSE, clientProcessId));
+ RETURN_IF_FAILED(HRESULT_FROM_RPCSTATUS(RpcRevertToSelf()));
+
+ RETURN_LAST_ERROR_IF_NULL(clientProcessHandle);
+
+ // pre-populate our mmcss task id so we have a known task id
+ // to provide and use for all other tasks related to this pipe
+ // retain the MMCSS configuration until after the client and device
+ // pipes are initialized, so this TaskId remains valid through
+ // initialization. Safe to disable mmcss for this rpc thread,
+ // once we complete initialization and have worker threads
+ // actively using this task id for the duration of this pipe.
+ RETURN_IF_FAILED(EnableMmcss(MmcssHandle, m_MmcssTaskId));
+
+ // The provided SWD instance id provided may be an alias of a UMP device, retrieve the
+ // PKEY_MIDI_AssociatedUMP property to retrieve the id of the primary device.
+ // We only create device pipe entries for the primary devices.
+ RETURN_IF_FAILED(GetEndpointAlias(MidiDevice, midiDevice, CreationParams->Flow));
+
+ // see if we should create timestamps or not. This option is set
+ // by the transport at endpoint enumeration time. For most endpoint
+ // types, this will be true, but for diagnostics loopback endpoints,
+ // it is false. So, we store the value in the property store to make
+ // it more flexible
+ RETURN_IF_FAILED(GetEndpointGenerateIncomingTimestamp(midiDevice, generateIncomingMessageTimestamps, CreationParams->Flow));
+
+ // Some endpoints, like the device-side of an app-to-app MIDI connection,
+ // should not have metadata listeners. This flag controls whether or not
+ // we add one if otherwise eligible.
+ //RETURN_IF_FAILED(GetEndpointShouldHaveMetadataHandler(midiDevice, addMetadataListenerToIncomingStream, CreationParams->Flow));
+
+ RETURN_IF_FAILED(GetMidiClient(BindingHandle, midiDevice.c_str(), SessionId, CreationParams, Client, clientProcessHandle, clientPipe, generateIncomingMessageTimestamps));
+ }
+ else
+ {
+ // for protocol negotiation / metadata capture only. BindingHandle here is going to be invalid
+ // NOTE: clientProcessHandle here is invalid
+ RETURN_IF_FAILED(GetMidiClient(BindingHandle, MidiDevice, SessionId, CreationParams, Client, clientProcessHandle, clientPipe, false));
+ }
auto cleanupOnFailure = wil::scope_exit([&]()
{
@@ -709,24 +932,22 @@ CMidiClientManager::CreateMidiClient(
newClientConnectionPipe = clientConnectionPipe;
- // Metadata Listener ----------------------------------------------------------------
- // We should check protocol, not just data format here because we're putting this on
- // MIDI 1.0 devices that use the new driver, and there's no reason to do that.
- if (addMetadataListenerToIncomingStream && devicePipe->IsFormatSupportedIn(MidiDataFormat::MidiDataFormat_UMP))
- {
- // Our clientConnectionPipe is now the Scheduler
- RETURN_IF_FAILED(GetMidiEndpointMetadataHandler(
- BindingHandle,
- MidiFlowIn,
- devicePipe,
- newClientConnectionPipe,
- clientConnectionPipe)); // clientConnectionPipe is the plugin
-
- clientConnectionPipe->AddClient((MidiClientHandle)clientPipe.get());
- }
- newClientConnectionPipe = clientConnectionPipe;
-
-
+ //// Metadata Listener ----------------------------------------------------------------
+ //// We should check protocol, not just data format here because we're putting this on
+ //// MIDI 1.0 devices that use the new driver, and there's no reason to do that.
+ //if (addMetadataListenerToIncomingStream && devicePipe->IsFormatSupportedIn(MidiDataFormat::MidiDataFormat_UMP))
+ //{
+ // // Our clientConnectionPipe is now the Scheduler
+ // RETURN_IF_FAILED(GetMidiEndpointMetadataHandler(
+ // BindingHandle,
+ // MidiFlowIn,
+ // devicePipe,
+ // newClientConnectionPipe,
+ // clientConnectionPipe)); // clientConnectionPipe is the plugin
+
+ // clientConnectionPipe->AddClient((MidiClientHandle)clientPipe.get());
+ //}
+ //newClientConnectionPipe = clientConnectionPipe;
// no more transforms to add
@@ -747,45 +968,82 @@ CMidiClientManager::CreateMidiClient(
clientConnectionPipe = devicePipe;
newClientConnectionPipe = devicePipe;
- // TODO: This needs to work with both bytestream and UMP clients, so this logic needs to change
-
- // Data Format Translator ---------------------------------------------------
- if (!clientPipe->IsFormatSupportedOut(newClientConnectionPipe->DataFormatOut()))
+ if (!InternalProtocolNegotiationUseOnly)
{
- // Format is not supported, so we need to transform
- // client requires a specific format, retrieve the transform required for that format.
- // Our clientConnectionPipe is now the format translator
+ // TODO: This needs to work with both bytestream and UMP clients, so this logic needs to change
- RETURN_IF_FAILED(GetMidiTransform(
- BindingHandle,
- MidiFlowOut,
- clientPipe->DataFormatOut(),
- newClientConnectionPipe->DataFormatOut(),
- devicePipe,
- clientConnectionPipe)); // clientConnectionPipe is the plugin
+ // Data Format Translator ---------------------------------------------------
+ if (!clientPipe->IsFormatSupportedOut(newClientConnectionPipe->DataFormatOut()))
+ {
+ // Format is not supported, so we need to transform
+ // client requires a specific format, retrieve the transform required for that format.
+ // Our clientConnectionPipe is now the format translator
- clientConnectionPipe->AddClient((MidiClientHandle)clientPipe.get());
+ RETURN_IF_FAILED(GetMidiTransform(
+ BindingHandle,
+ MidiFlowOut,
+ clientPipe->DataFormatOut(),
+ newClientConnectionPipe->DataFormatOut(),
+ devicePipe,
+ clientConnectionPipe)); // clientConnectionPipe is the plugin
- newClientConnectionPipe = clientConnectionPipe;
- }
+ clientConnectionPipe->AddClient((MidiClientHandle)clientPipe.get());
- // Scheduler ----------------------------------------------------------------
- // for now, the scheduler is only going to work with UMP, so we hope
- // any required translation is done BEFORE we add this.
- if (clientConnectionPipe->IsFormatSupportedOut(MidiDataFormat::MidiDataFormat_UMP))
- {
- // Our clientConnectionPipe is now the Scheduler
- RETURN_IF_FAILED(GetMidiScheduler(
- BindingHandle,
- MidiFlowOut,
- devicePipe,
- newClientConnectionPipe,
- clientConnectionPipe)); // clientConnectionPipe is the plugin
+ newClientConnectionPipe = clientConnectionPipe;
+ }
- clientConnectionPipe->AddClient((MidiClientHandle)clientPipe.get());
+ // Scheduler ----------------------------------------------------------------
+ // for now, the scheduler is only going to work with UMP, so we hope
+ // any required translation is done BEFORE we add this.
+ if (clientConnectionPipe->IsFormatSupportedOut(MidiDataFormat::MidiDataFormat_UMP))
+ {
+ // Our clientConnectionPipe is now the Scheduler
+ RETURN_IF_FAILED(GetMidiScheduler(
+ BindingHandle,
+ MidiFlowOut,
+ devicePipe,
+ newClientConnectionPipe,
+ clientConnectionPipe)); // clientConnectionPipe is the plugin
+
+ clientConnectionPipe->AddClient((MidiClientHandle)clientPipe.get());
+
+ newClientConnectionPipe = clientConnectionPipe;
+ }
- newClientConnectionPipe = clientConnectionPipe;
+
+ // Protocol Translator ----------------------------------------------------------------
+ // This translates MT4 to MT2 for MIDI 1.0 devices connected through the new driver.
+ // For devices connected directly to the service via the old driver, we don't have to
+ // do any translation or scaling because that's all taken care of in the BS2UMP and
+ // UMP2BS transforms. (They do format translation but *also* MT4/MT2 translation)
+ // This outbound translation step should happen prior to the outbound scheduler step.
+
+ bool addProtocolDownscalerForMidi1DeviceWithUmpDriver{ false };
+ RETURN_IF_FAILED(GetEndpointRequiresOutboundProtocolDownscaling(
+ midiDevice,
+ CreationParams->Flow,
+ devicePipe->DataFormatOut(),
+ addProtocolDownscalerForMidi1DeviceWithUmpDriver));
+
+ if (addProtocolDownscalerForMidi1DeviceWithUmpDriver)
+ {
+ RETURN_IF_FAILED(GetMidiProtocolDownscalerTransform(
+ BindingHandle,
+ MidiFlowOut,
+ devicePipe,
+ newClientConnectionPipe,
+ clientConnectionPipe)); // clientConnectionPipe is the plugin
+
+ clientConnectionPipe->AddClient((MidiClientHandle)clientPipe.get());
+
+ newClientConnectionPipe = clientConnectionPipe;
+ }
}
+ else
+ {
+ // for protocol negotiation use only, so no translator, scheduler, or downscaler
+ }
+
// no more transforms to add
clientConnectionPipe = newClientConnectionPipe;
diff --git a/src/api/Service/Exe/MidiClientPipe.cpp b/src/api/Service/Exe/MidiClientPipe.cpp
index 5f3eb564..1377b607 100644
--- a/src/api/Service/Exe/MidiClientPipe.cpp
+++ b/src/api/Service/Exe/MidiClientPipe.cpp
@@ -20,8 +20,8 @@ CMidiClientPipe::AdjustForBufferingRequirements(PMIDISRV_CLIENTCREATION_PARAMS C
_Use_decl_annotations_
HRESULT
CMidiClientPipe::Initialize(
- handle_t /* BindingHandle */,
- HANDLE /* clientProcess */,
+ handle_t /* BindingHandle */, // this isn't used now, but if used in the future, not this will not be set for internal clients like protocol negotiation
+ HANDLE /* clientProcess */, // this isn't used now, but if used in the future, not this will not be set for internal clients like protocol negotiation
LPCWSTR Device,
GUID SessionId,
PMIDISRV_CLIENTCREATION_PARAMS CreationParams,
diff --git a/src/api/Service/Exe/MidiDeviceManager.cpp b/src/api/Service/Exe/MidiDeviceManager.cpp
index e7a9ff48..c648bf9a 100644
--- a/src/api/Service/Exe/MidiDeviceManager.cpp
+++ b/src/api/Service/Exe/MidiDeviceManager.cpp
@@ -46,13 +46,22 @@ CMidiDeviceManager::Initialize(
__FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
TraceLoggingPointer(this, "this"),
- TraceLoggingWideString(L"Loading abstraction layer", "message"),
+ TraceLoggingWideString(L"Getting abstraction configuration JSON", "message"),
TraceLoggingGuid(AbstractionLayer, "abstraction layer")
);
// provide the initial settings for these transports
auto transportSettingsJson = m_ConfigurationManager->GetSavedConfigurationForTransportAbstraction(AbstractionLayer);
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"CoCreating abstraction layer", "message"),
+ TraceLoggingGuid(AbstractionLayer, "abstraction layer")
+ );
+
// changed these from a return-on-fail to just log, so we don't prevent service startup
// due to one bad abstraction
@@ -60,17 +69,44 @@ CMidiDeviceManager::Initialize(
if (midiAbstraction != nullptr)
{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Activating abstraction layer IMidiEndpointManager", "message"),
+ TraceLoggingGuid(AbstractionLayer, "abstraction layer")
+ );
+
LOG_IF_FAILED(midiAbstraction->Activate(__uuidof(IMidiEndpointManager), (void**)&endpointManager));
if (endpointManager != nullptr)
{
wil::com_ptr_nothrow protocolManager = EndpointProtocolManager.get();
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Initializing abstraction layer Midi Endpoint Manager", "message"),
+ TraceLoggingGuid(AbstractionLayer, "abstraction layer")
+ );
+
auto initializeResult = endpointManager->Initialize((IUnknown*)this, (IUnknown*)protocolManager.get());
if (SUCCEEDED(initializeResult))
{
m_MidiEndpointManagers.emplace(AbstractionLayer, std::move(endpointManager));
+
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Midi Endpoint Manager initialized", "message"),
+ TraceLoggingGuid(AbstractionLayer, "abstraction layer")
+ );
}
else
{
@@ -98,6 +134,15 @@ CMidiDeviceManager::Initialize(
}
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Activating abstraction configuration manager.", "message"),
+ TraceLoggingGuid(AbstractionLayer, "abstraction layer")
+ );
+
// we only log. This interface is optional
auto configHR = midiAbstraction->Activate(__uuidof(IMidiAbstractionConfigurationManager), (void**)&abstractionConfigurationManager);
if (SUCCEEDED(configHR))
@@ -112,6 +157,8 @@ CMidiDeviceManager::Initialize(
if (FAILED(initializeResult))
{
+ LOG_IF_FAILED(initializeResult);
+
TraceLoggingWrite(
MidiSrvTelemetryProvider::Provider(),
__FUNCTION__,
@@ -129,6 +176,15 @@ CMidiDeviceManager::Initialize(
if (!transportSettingsJson.empty())
{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Updating abstraction configuration", "message"),
+ TraceLoggingGuid(AbstractionLayer, "abstraction layer")
+ );
+
CComBSTR response{};
response.Empty();
@@ -137,7 +193,6 @@ CMidiDeviceManager::Initialize(
// we don't use the response info here.
::SysFreeString(response);
-
if (FAILED(updateConfigHR))
{
if (updateConfigHR == E_NOTIMPL)
@@ -223,15 +278,53 @@ CMidiDeviceManager::Initialize(
}
}
- catch (...)
+
+ catch (wil::ResultException rex)
+ {
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Result Exception loading transport abstraction.", "message"),
+ TraceLoggingString(rex.what(), "error"),
+ TraceLoggingHResult(rex.GetErrorCode(), "hresult"),
+ TraceLoggingGuid(AbstractionLayer, "abstraction layer")
+ );
+
+ }
+ catch (std::runtime_error& err)
+ {
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Runtime error loading transport abstraction.", "message"),
+ TraceLoggingString(err.what(), "error"),
+ TraceLoggingGuid(AbstractionLayer, "abstraction layer")
+ );
+ }
+ catch (const std::exception& ex)
{
- // TODO: Log exception
TraceLoggingWrite(
MidiSrvTelemetryProvider::Provider(),
__FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
TraceLoggingPointer(this, "this"),
TraceLoggingWideString(L"Exception loading transport abstraction.", "message"),
+ TraceLoggingString(ex.what(), "exception"),
+ TraceLoggingGuid(AbstractionLayer, "abstraction layer")
+ );
+ }
+ catch (...)
+ {
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Unknown exception loading transport abstraction.", "message"),
TraceLoggingGuid(AbstractionLayer, "abstraction layer")
);
diff --git a/src/api/Service/Exe/MidiDevicePipe.cpp b/src/api/Service/Exe/MidiDevicePipe.cpp
index 075669a3..668c53df 100644
--- a/src/api/Service/Exe/MidiDevicePipe.cpp
+++ b/src/api/Service/Exe/MidiDevicePipe.cpp
@@ -26,12 +26,8 @@ CMidiDevicePipe::Initialize(
TraceLoggingPointer(this, "this")
);
-
auto deviceLock = m_DevicePipeLock.lock();
- OutputDebugString(L"" __FUNCTION__ " Initialize.");
- OutputDebugString(Device);
-
ABSTRACTIONCREATIONPARAMS abstractionCreationParams;
RETURN_IF_FAILED(CMidiPipe::Initialize(Device, CreationParams->Flow));
diff --git a/src/api/Service/Exe/MidiEndpointProtocolManager.cpp b/src/api/Service/Exe/MidiEndpointProtocolManager.cpp
index c99e9c67..590092fc 100644
--- a/src/api/Service/Exe/MidiEndpointProtocolManager.cpp
+++ b/src/api/Service/Exe/MidiEndpointProtocolManager.cpp
@@ -44,38 +44,16 @@ CMidiEndpointProtocolManager::Initialize(
m_deviceManager = DeviceManager;
m_sessionTracker = SessionTracker;
- m_allMessagesReceived.create();
- m_queueWorkerThreadWakeup.create();
-
- // connect to the service. Needing a reference to this abstraction def
- // creates a circular reference to the MidiSrv Abstraction. Not sure of
- // a good way around that other than fixing up the ClientManager to make
- // local connections with local handles reasonable.
- RETURN_IF_FAILED(CoCreateInstance(__uuidof(Midi2MidiSrvAbstraction), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&m_serviceAbstraction)));
-
+ // log a an active session so a user can figure out which
+ // processes have any given device open.
auto pid = GetCurrentProcessId();
- m_sessionTracker->AddClientSession(
- m_sessionId,
- MIDI_PROTOCOL_MANAGER_SESSION_NAME,
- pid,
- MIDI_PROTOCOL_MANAGER_PROCESS_NAME);
-
- try
- {
- // start background processing thread
-
- std::thread workerThread(
- &CMidiEndpointProtocolManager::ThreadWorker,
- this);
-
- m_queueWorkerThread = std::move(workerThread);
-
- // start up the worker thread
- m_queueWorkerThread.detach();
-
- }
- CATCH_RETURN();
+ LOG_IF_FAILED(m_sessionTracker->AddClientSession(
+ m_sessionId,
+ MIDI_PROTOCOL_MANAGER_SESSION_NAME,
+ pid,
+ MIDI_PROTOCOL_MANAGER_PROCESS_NAME));
+
return S_OK;
}
@@ -89,7 +67,8 @@ CMidiEndpointProtocolManager::NegotiateAndRequestMetadata(
BOOL PreferToSendJRTimestampsToEndpoint,
BOOL PreferToReceiveJRTimestampsFromEndpoint,
BYTE PreferredMidiProtocol,
- WORD TimeoutMS
+ WORD TimeoutMS,
+ PENDPOINTPROTOCOLNEGOTIATIONRESULTS* NegotiationResults
) noexcept
{
TraceLoggingWrite(
@@ -100,10 +79,13 @@ CMidiEndpointProtocolManager::NegotiateAndRequestMetadata(
TraceLoggingWideString(DeviceInterfaceId)
);
+ // TEMP!
+ NegotiationResults = nullptr;
+
ProtocolManagerWork work;
- // DEBUG
- //TimeoutMS = 25000;
+ //// DEBUG
+ ////TimeoutMS = 25000;
work.EndpointInstanceId = DeviceInterfaceId;
work.PreferToSendJRTimestampsToEndpoint = PreferToSendJRTimestampsToEndpoint;
@@ -111,12 +93,12 @@ CMidiEndpointProtocolManager::NegotiateAndRequestMetadata(
work.PreferredMidiProtocol = PreferredMidiProtocol;
work.TimeoutMS = TimeoutMS;
- m_queueMutex.lock();
- m_workQueue.push(std::move(work));
- m_queueMutex.unlock();
+ //m_queueMutex.lock();
+ //m_workQueue.push(std::move(work));
+ //m_queueMutex.unlock();
- // todo: signal event that there's new work
- m_queueWorkerThreadWakeup.SetEvent();
+ //// todo: signal event that there's new work
+ //m_queueWorkerThreadWakeup.SetEvent();
return S_OK;
@@ -146,10 +128,10 @@ CMidiEndpointProtocolManager::Cleanup()
while (m_workQueue.size() > 0) m_workQueue.pop();
m_shutdown = true;
- m_queueWorkerThreadWakeup.SetEvent();
+// m_queueWorkerThreadWakeup.SetEvent();
- if (m_queueWorkerThread.joinable())
- m_queueWorkerThread.join();
+// if (m_queueWorkerThread.joinable())
+// m_queueWorkerThread.join();
return S_OK;
}
@@ -321,7 +303,7 @@ CMidiEndpointProtocolManager::Callback(PVOID Data, UINT Size, LONGLONG Position,
m_currentWorkItem.CountFunctionBlocksReceived == m_currentWorkItem.DeclaredFunctionBlockCount &&
m_currentWorkItem.TaskFinalStreamNegotiationResponseReceived)
{
- m_allMessagesReceived.SetEvent();
+// m_allMessagesReceived.SetEvent();
}
return S_OK;
@@ -491,38 +473,38 @@ CMidiEndpointProtocolManager::ProcessCurrentWorkEntry()
HRESULT hr = S_OK;
- // Send initial discovery request
- // the rest happens in response to messages in the callback
- LOG_IF_FAILED(hr = RequestAllEndpointDiscoveryInformation());
-
- if (SUCCEEDED(hr))
- {
- OutputDebugString(__FUNCTION__ L" - Requested discovery information");
- }
- else
- {
- TraceLoggingWrite(
- MidiSrvTelemetryProvider::Provider(),
- __FUNCTION__,
- TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
- TraceLoggingPointer(this, "this"),
- TraceLoggingWideString(L"Failed to request discovery information")
- );
- }
-
- if (SUCCEEDED(hr))
- {
- // Wait until all metadata arrives or we timeout
- if (!m_allMessagesReceived.wait(m_currentWorkItem.TimeoutMS))
- {
- // we didn't receive everything, but that's not a failure condition for this.
- }
- }
-
- if (m_allMessagesReceived.is_signaled()) m_allMessagesReceived.ResetEvent();
-
- m_currentWorkItem.Endpoint->Cleanup();
- m_currentWorkItem.Endpoint = nullptr;
+ //// Send initial discovery request
+ //// the rest happens in response to messages in the callback
+ //LOG_IF_FAILED(hr = RequestAllEndpointDiscoveryInformation());
+
+ //if (SUCCEEDED(hr))
+ //{
+ // OutputDebugString(__FUNCTION__ L" - Requested discovery information");
+ //}
+ //else
+ //{
+ // TraceLoggingWrite(
+ // MidiSrvTelemetryProvider::Provider(),
+ // __FUNCTION__,
+ // TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ // TraceLoggingPointer(this, "this"),
+ // TraceLoggingWideString(L"Failed to request discovery information")
+ // );
+ //}
+
+ //if (SUCCEEDED(hr))
+ //{
+ // // Wait until all metadata arrives or we timeout
+ // if (!m_allMessagesReceived.wait(m_currentWorkItem.TimeoutMS))
+ // {
+ // // we didn't receive everything, but that's not a failure condition for this.
+ // }
+ //}
+
+ //if (m_allMessagesReceived.is_signaled()) m_allMessagesReceived.ResetEvent();
+
+ //m_currentWorkItem.Endpoint->Cleanup();
+ //m_currentWorkItem.Endpoint = nullptr;
return hr;
}
diff --git a/src/api/Service/Exe/MidiEndpointProtocolManager.h b/src/api/Service/Exe/MidiEndpointProtocolManager.h
index 4b2e84e8..fd9b851a 100644
--- a/src/api/Service/Exe/MidiEndpointProtocolManager.h
+++ b/src/api/Service/Exe/MidiEndpointProtocolManager.h
@@ -17,7 +17,7 @@ struct ProtocolManagerWork
bool PreferToSendJRTimestampsToEndpoint{ false };
bool PreferToReceiveJRTimestampsFromEndpoint{ false };
uint8_t PreferredMidiProtocol{};
- uint16_t TimeoutMS{ 2000 };
+ uint16_t TimeoutMS{ 5000 };
wil::com_ptr_nothrow Endpoint;
@@ -36,17 +36,19 @@ struct ProtocolManagerWork
};
+
+
+
class CMidiEndpointProtocolManager : public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags,
IMidiEndpointProtocolManagerInterface,
IMidiCallback>
{
public:
-
CMidiEndpointProtocolManager() = default;
~CMidiEndpointProtocolManager() {}
- HRESULT Initialize(
+ STDMETHOD(Initialize)(
_In_ std::shared_ptr& ClientManager,
_In_ std::shared_ptr& DeviceManager,
_In_ std::shared_ptr& SessionTracker
@@ -57,10 +59,10 @@ class CMidiEndpointProtocolManager : public Microsoft::WRL::RuntimeClass<
_In_ BOOL PreferToSendJRTimestampsToEndpoint,
_In_ BOOL PreferToReceiveJRTimestampsFromEndpoint,
_In_ BYTE PreferredMidiProtocol,
- _In_ WORD TimeoutMS
+ _In_ WORD TimeoutMS,
+ _Out_ PENDPOINTPROTOCOLNEGOTIATIONRESULTS* NegotiationResults
);
-
STDMETHOD(Callback)(_In_ PVOID Data, _In_ UINT Size, _In_ LONGLONG Position, _In_ LONGLONG Context);
HRESULT Cleanup();
@@ -90,8 +92,6 @@ class CMidiEndpointProtocolManager : public Microsoft::WRL::RuntimeClass<
std::thread m_queueWorkerThread;
ProtocolManagerWork m_currentWorkItem;
- wil::unique_event_nothrow m_allMessagesReceived;
- wil::unique_event_nothrow m_queueWorkerThreadWakeup;
// true if we're closing down
bool m_shutdown{ false };
diff --git a/src/api/Service/Exe/MidiEndpointProtocolWorker.cpp b/src/api/Service/Exe/MidiEndpointProtocolWorker.cpp
new file mode 100644
index 00000000..6ac10b56
--- /dev/null
+++ b/src/api/Service/Exe/MidiEndpointProtocolWorker.cpp
@@ -0,0 +1,839 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#include "stdafx.h"
+#include "Midi2MidiSrvAbstraction.h"
+#include "ump_helpers.h"
+#include "midi_ump_message_defs.h"
+
+
+
+_Use_decl_annotations_
+DEVPROPKEY
+CMidiEndpointProtocolWorker::FunctionBlockPropertyKeyFromNumber(
+ uint8_t functionBlockNumber
+)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ DEVPROPKEY key{};
+ GUID propertyId{};
+
+ if (functionBlockNumber < MIDI_MAX_FUNCTION_BLOCKS)
+ {
+ if (SUCCEEDED(::CLSIDFromString(MIDI_STRING_PKEY_GUID, &propertyId)))
+ {
+ key.fmtid = propertyId;
+ key.pid = MIDI_FUNCTION_BLOCK_PROPERTY_INDEX_START + functionBlockNumber;
+ }
+ else
+ {
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"CLSIDFromString failed for Function Block Property Key", "message")
+ );
+ }
+ }
+
+ return key;
+}
+
+_Use_decl_annotations_
+DEVPROPKEY
+CMidiEndpointProtocolWorker::FunctionBlockNamePropertyKeyFromNumber(
+ uint8_t functionBlockNumber
+)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ DEVPROPKEY key{};
+ GUID propertyId{};
+
+ if (functionBlockNumber < MIDI_MAX_FUNCTION_BLOCKS)
+ {
+ if (SUCCEEDED(::CLSIDFromString(MIDI_STRING_PKEY_GUID, &propertyId)))
+ {
+ key.fmtid = propertyId;
+ key.pid = MIDI_FUNCTION_BLOCK_NAME_PROPERTY_INDEX_START + functionBlockNumber;
+ }
+ }
+
+ return key;
+}
+
+
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::Initialize(
+ GUID SessionId,
+ GUID AbstractionGuid,
+ LPCWSTR DeviceInterfaceId,
+ std::shared_ptr& ClientManager,
+ std::shared_ptr& DeviceManager,
+ std::shared_ptr& SessionTracker
+)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ m_abstractionGuid = AbstractionGuid;
+ m_sessionId = SessionId;
+ m_deviceInterfaceId = DeviceInterfaceId;
+
+ m_clientManager = ClientManager;
+ m_deviceManager = DeviceManager;
+ m_sessionTracker = SessionTracker;
+
+ wil::com_ptr_nothrow midiAbstraction;
+
+ // we only support UMP data format for protocol negotiation
+ ABSTRACTIONCREATIONPARAMS abstractionCreationParams;
+ abstractionCreationParams.DataFormat = MidiDataFormat::MidiDataFormat_UMP;
+
+ DWORD mmcssTaskId{ 0 };
+ LONGLONG context{ 0 };
+
+ RETURN_IF_FAILED(CoCreateInstance(m_abstractionGuid, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&midiAbstraction)));
+ RETURN_IF_FAILED(midiAbstraction->Activate(__uuidof(IMidiBiDi), (void**)&m_midiBiDiDevice));
+ RETURN_IF_FAILED(m_midiBiDiDevice->Initialize(m_deviceInterfaceId.c_str(), &abstractionCreationParams, &mmcssTaskId, this, context, m_sessionId));
+
+ try
+ {
+ // start background processing thread
+
+ //std::thread workerThread(
+ // &CMidiEndpointProtocolManager::ThreadWorker,
+ // this);
+
+ //m_queueWorkerThread = std::move(workerThread);
+
+ //// start up the worker thread
+ //m_queueWorkerThread.detach();
+
+ }
+ CATCH_RETURN();
+
+ return S_OK;
+}
+
+HRESULT
+CMidiEndpointProtocolWorker::ListenForMetadata()
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ // TODO: Connect to the endpoint and listen
+
+
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::Callback(
+ PVOID Data,
+ UINT Size,
+ LONGLONG Position,
+ LONGLONG Context)
+{
+ UNREFERENCED_PARAMETER(Position);
+ UNREFERENCED_PARAMETER(Context);
+
+ if (Size == UMP128_BYTE_COUNT)
+ {
+ internal::PackedUmp128 ump;
+
+ if (internal::FillPackedUmp128FromBytePointer((byte*)Data, (uint8_t)Size, ump))
+ {
+ // if type F, process it.
+
+ if (internal::GetUmpMessageTypeFromFirstWord(ump.word0) == 0xF)
+ {
+ // process the metadata
+ LOG_IF_FAILED(ProcessStreamMessage(ump));
+ }
+ else
+ {
+ // not a stream message. Ignore and move on
+ }
+ }
+ else
+ {
+ // couldn't fill the UMP. Shouldn't happen since we pre-validate. Just ignore.
+ //return E_FAIL;
+ }
+ }
+ else
+ {
+ // Not a UMP128 so can't be a stream message. Fall out quickly
+ }
+
+
+ return S_OK;
+}
+
+HRESULT
+CMidiEndpointProtocolWorker::Cleanup()
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+
+
+
+ return S_OK;
+}
+
+
+
+
+HRESULT
+CMidiEndpointProtocolWorker::UpdateEndpointNameProperty()
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ auto cleanedValue{ internal::TrimmedWStringCopy(m_endpointName) + L"\0" };
+
+ FILETIME currentTime;
+ GetSystemTimePreciseAsFileTime(¤tTime);
+
+
+ DEVPROPERTY props[] =
+ {
+ {{ PKEY_MIDI_EndpointProvidedName, DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_STRING, static_cast((cleanedValue.length() + 1) * sizeof(WCHAR)), (PVOID)(cleanedValue.c_str()) },
+ {{ PKEY_MIDI_EndpointProvidedNameLastUpdateTime, DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_FILETIME, static_cast(sizeof(FILETIME)), (PVOID)(¤tTime) },
+ };
+
+ RETURN_IF_FAILED(m_deviceManager->UpdateEndpointProperties(m_deviceInstanceId.c_str(), ARRAYSIZE(props), (PVOID)props));
+
+ // clear out any old value that's in there
+ //m_endpointName.clear();
+
+ return S_OK;
+}
+
+HRESULT
+CMidiEndpointProtocolWorker::UpdateEndpointProductInstanceIdProperty()
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+
+ FILETIME currentTime;
+ GetSystemTimePreciseAsFileTime(¤tTime);
+
+
+ std::wstring cleanedValue{ internal::TrimmedWStringCopy(m_productInstanceId) + L"\0" };
+
+ DEVPROPERTY props[] =
+ {
+ {{ PKEY_MIDI_EndpointProvidedProductInstanceId, DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_STRING, static_cast((cleanedValue.length() + 1) * sizeof(WCHAR)), (PVOID)(cleanedValue.c_str()) },
+ {{ PKEY_MIDI_EndpointProvidedProductInstanceIdLastUpdateTime, DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_FILETIME, static_cast(sizeof(FILETIME)), (PVOID)(¤tTime) },
+ };
+
+ RETURN_IF_FAILED(m_deviceManager->UpdateEndpointProperties(m_deviceInstanceId.c_str(), ARRAYSIZE(props), (PVOID)props));
+
+ // clear out any old value that's in there
+ //m_productInstanceId.clear();
+
+ return S_OK;
+}
+
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::UpdateFunctionBlockNameProperty(uint8_t functionBlockNumber, std::wstring name)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ FILETIME currentTime;
+ GetSystemTimePreciseAsFileTime(¤tTime);
+
+
+ std::wstring cleanedValue{ internal::TrimmedWStringCopy(name) + L"\0" };
+
+ DEVPROPERTY props[] =
+ {
+ {{ FunctionBlockNamePropertyKeyFromNumber(functionBlockNumber), DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_STRING, static_cast((cleanedValue.length() + 1) * sizeof(WCHAR)), (PVOID)(cleanedValue.c_str()) },
+ {{ PKEY_MIDI_FunctionBlocksLastUpdateTime, DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_FILETIME, static_cast(sizeof(FILETIME)), (PVOID)(¤tTime) },
+ };
+
+ RETURN_IF_FAILED(m_deviceManager->UpdateEndpointProperties(m_deviceInstanceId.c_str(), ARRAYSIZE(props), (PVOID)props));
+
+ // clear out any old name that's in there
+ // m_functionBlockNames.erase(functionBlockNumber);
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::UpdateStreamConfigurationProperties(internal::PackedUmp128& endpointStreamConfigurationNotificationMessage)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ FILETIME currentTime;
+ GetSystemTimePreciseAsFileTime(¤tTime);
+
+ BYTE configuredProtocol = MIDIWORDBYTE3(endpointStreamConfigurationNotificationMessage.word0);
+
+ DEVPROP_BOOLEAN configuredToSendJR = MIDIWORDBYTE4LOWBIT1(endpointStreamConfigurationNotificationMessage.word0) ? DEVPROP_TRUE : DEVPROP_FALSE;
+ DEVPROP_BOOLEAN configuredToReceiveJR = MIDIWORDBYTE4LOWBIT2(endpointStreamConfigurationNotificationMessage.word0) ? DEVPROP_TRUE : DEVPROP_FALSE;
+
+ DEVPROPERTY props[] =
+ {
+ {{ PKEY_MIDI_EndpointConfiguredToSendJRTimestamps, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(configuredToSendJR)), &configuredToSendJR },
+
+ {{ PKEY_MIDI_EndpointConfiguredToReceiveJRTimestamps, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(configuredToReceiveJR)), &configuredToReceiveJR },
+
+ {{ PKEY_MIDI_EndpointConfiguredProtocol, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BYTE, static_cast(sizeof(configuredProtocol)), &configuredProtocol },
+
+ {{ PKEY_MIDI_EndpointConfigurationLastUpdateTime, DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_FILETIME, static_cast(sizeof(FILETIME)), (PVOID)(¤tTime) },
+
+ };
+
+ RETURN_IF_FAILED(m_deviceManager->UpdateEndpointProperties(m_deviceInstanceId.c_str(), ARRAYSIZE(props), (PVOID)props));
+
+ //OutputDebugString(L"" __FUNCTION__ " Properties Updated");
+
+ return S_OK;
+}
+
+
+
+// The device identity is a single message, so we don't keep a copy. Instead, we just do
+// the parsing here and then update the property. We assume this has already been identified
+// as a type=F, form=0, status=0x02 stream message
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::UpdateDeviceIdentityProperty(internal::PackedUmp128& identityMessage)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+
+ MidiDeviceIdentityProperty prop;
+
+ prop.Reserved0 = MIDIWORDSHORT2(identityMessage.word0);
+
+ prop.Reserved1 = internal::CleanupByte7(MIDIWORDBYTE1(identityMessage.word1));
+ prop.ManufacturerSysExIdByte1 = internal::CleanupByte7(MIDIWORDBYTE2(identityMessage.word1));
+ prop.ManufacturerSysExIdByte2 = internal::CleanupByte7(MIDIWORDBYTE3(identityMessage.word1));
+ prop.ManufacturerSysExIdByte3 = internal::CleanupByte7(MIDIWORDBYTE4(identityMessage.word1));
+
+ prop.DeviceFamilyLsb = internal::CleanupByte7(MIDIWORDBYTE1(identityMessage.word2));
+ prop.DeviceFamilyMsb = internal::CleanupByte7(MIDIWORDBYTE2(identityMessage.word2));
+ prop.DeviceFamilyModelNumberLsb = internal::CleanupByte7(MIDIWORDBYTE3(identityMessage.word2));
+ prop.DeviceFamilyModelNumberMsb = internal::CleanupByte7(MIDIWORDBYTE4(identityMessage.word2));
+
+ prop.SoftwareRevisionLevelByte1 = internal::CleanupByte7(MIDIWORDBYTE1(identityMessage.word3));
+ prop.SoftwareRevisionLevelByte2 = internal::CleanupByte7(MIDIWORDBYTE2(identityMessage.word3));
+ prop.SoftwareRevisionLevelByte3 = internal::CleanupByte7(MIDIWORDBYTE3(identityMessage.word3));
+ prop.SoftwareRevisionLevelByte4 = internal::CleanupByte7(MIDIWORDBYTE4(identityMessage.word3));
+
+ FILETIME currentTime;
+ GetSystemTimePreciseAsFileTime(¤tTime);
+
+ DEVPROPERTY props[] =
+ {
+ {{ PKEY_MIDI_DeviceIdentity, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BINARY, static_cast(sizeof(prop)), &prop },
+
+ {{ PKEY_MIDI_DeviceIdentityLastUpdateTime, DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_FILETIME, static_cast(sizeof(FILETIME)), (PVOID)(¤tTime) },
+
+ };
+
+ RETURN_IF_FAILED(m_deviceManager->UpdateEndpointProperties(m_deviceInstanceId.c_str(), ARRAYSIZE(props), (PVOID)props));
+
+ //OutputDebugString(L"" __FUNCTION__ " Property Updated");
+
+
+ return S_OK;
+}
+
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::UpdateEndpointInfoProperties(internal::PackedUmp128& endpointInfoNotificationMessage)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ BYTE umpVersionMajor = MIDIWORDBYTE3(endpointInfoNotificationMessage.word0);
+ BYTE umpVersionMinor = MIDIWORDBYTE4(endpointInfoNotificationMessage.word0);
+ BYTE functionBlockCount = internal::CleanupByte7(MIDIWORDBYTE1(endpointInfoNotificationMessage.word1));
+
+ DEVPROP_BOOLEAN functionBlocksAreStatic = MIDIWORDHIGHBIT(endpointInfoNotificationMessage.word1) ? DEVPROP_TRUE : DEVPROP_FALSE;
+
+ DEVPROP_BOOLEAN supportsMidi1Protocol = MIDIWORDBYTE3LOWBIT1(endpointInfoNotificationMessage.word1) ? DEVPROP_TRUE : DEVPROP_FALSE;
+ DEVPROP_BOOLEAN supportsMidi2Protocol = MIDIWORDBYTE3LOWBIT2(endpointInfoNotificationMessage.word1) ? DEVPROP_TRUE : DEVPROP_FALSE;
+
+ DEVPROP_BOOLEAN supportsSendingJR = MIDIWORDBYTE4LOWBIT1(endpointInfoNotificationMessage.word1) ? DEVPROP_TRUE : DEVPROP_FALSE;
+ DEVPROP_BOOLEAN supportsReceivingJR = MIDIWORDBYTE4LOWBIT2(endpointInfoNotificationMessage.word1) ? DEVPROP_TRUE : DEVPROP_FALSE;
+
+ FILETIME currentTime;
+ GetSystemTimePreciseAsFileTime(¤tTime);
+
+
+ DEVPROPERTY props[] =
+ {
+ {{ PKEY_MIDI_EndpointSupportsMidi2Protocol, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(supportsMidi2Protocol)), &supportsMidi2Protocol },
+
+ {{ PKEY_MIDI_EndpointSupportsMidi1Protocol, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(supportsMidi1Protocol)), &supportsMidi1Protocol },
+
+ {{ PKEY_MIDI_EndpointSupportsReceivingJRTimestamps, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(supportsReceivingJR)), &supportsReceivingJR },
+
+ {{ PKEY_MIDI_EndpointSupportsSendingJRTimestamps, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(supportsSendingJR)), &supportsSendingJR },
+
+ {{ PKEY_MIDI_EndpointUmpVersionMajor, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BYTE, static_cast(sizeof(umpVersionMajor)), &umpVersionMajor },
+
+ {{ PKEY_MIDI_EndpointUmpVersionMinor, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BYTE, static_cast(sizeof(umpVersionMajor)), &umpVersionMinor },
+
+ {{ PKEY_MIDI_FunctionBlockCount, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BYTE, static_cast(sizeof(functionBlockCount)), &functionBlockCount },
+
+ {{ PKEY_MIDI_FunctionBlocksAreStatic, DEVPROP_STORE_SYSTEM, nullptr },
+ DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(functionBlocksAreStatic)), &functionBlocksAreStatic },
+
+ {{ PKEY_MIDI_EndpointInformationLastUpdateTime, DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_FILETIME, static_cast(sizeof(FILETIME)), (PVOID)(¤tTime) },
+
+ };
+
+ RETURN_IF_FAILED(m_deviceManager->UpdateEndpointProperties(m_deviceInstanceId.c_str(), ARRAYSIZE(props), (PVOID)props));
+
+ return S_OK;
+}
+
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::UpdateFunctionBlockProperty(internal::PackedUmp128& functionBlockInfoNotificationMessage)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ MidiFunctionBlockProperty prop;
+
+ prop.IsActive = MIDIWORDBYTE3HIGHBIT(functionBlockInfoNotificationMessage.word0);
+ prop.BlockNumber = internal::CleanupByte7(MIDIWORDBYTE3(functionBlockInfoNotificationMessage.word0));
+ prop.Direction = MIDIWORDBYTE4LOWCRUMB1(functionBlockInfoNotificationMessage.word0);
+ prop.Midi1 = MIDIWORDBYTE4LOWCRUMB2(functionBlockInfoNotificationMessage.word0);
+ prop.UIHint = MIDIWORDBYTE4LOWCRUMB3(functionBlockInfoNotificationMessage.word0);
+ prop.Reserved0 = MIDIWORDBYTE4LOWCRUMB4(functionBlockInfoNotificationMessage.word0);
+
+ prop.FirstGroup = MIDIWORDBYTE1(functionBlockInfoNotificationMessage.word1);
+ prop.NumberOfGroupsSpanned = MIDIWORDBYTE2(functionBlockInfoNotificationMessage.word1);
+ prop.MidiCIMessageVersionFormat = MIDIWORDBYTE3(functionBlockInfoNotificationMessage.word1);
+ prop.MaxSysEx8Streams = MIDIWORDBYTE4(functionBlockInfoNotificationMessage.word1);
+
+ prop.Reserved1 = functionBlockInfoNotificationMessage.word2;
+ prop.Reserved2 = functionBlockInfoNotificationMessage.word3;
+
+ FILETIME currentTime;
+ GetSystemTimePreciseAsFileTime(¤tTime);
+
+ DEVPROPKEY propKey = FunctionBlockPropertyKeyFromNumber(prop.BlockNumber);
+
+ DEVPROPERTY props[] =
+ {
+ {{ propKey, DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_BINARY, static_cast(sizeof(prop)), &prop },
+
+ {{ PKEY_MIDI_FunctionBlocksLastUpdateTime, DEVPROP_STORE_SYSTEM, nullptr},
+ DEVPROP_TYPE_FILETIME, static_cast(sizeof(FILETIME)), (PVOID)(¤tTime) },
+
+ };
+
+
+ RETURN_IF_FAILED(m_deviceManager->UpdateEndpointProperties(m_deviceInstanceId.c_str(), ARRAYSIZE(props), (PVOID)props));
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::ProcessStreamMessage(internal::PackedUmp128 ump)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ auto messageStatus = internal::GetStatusFromStreamMessageFirstWord(ump.word0);
+
+ switch (messageStatus)
+ {
+ case MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_INFO_NOTIFICATION:
+ UpdateEndpointInfoProperties(ump);
+ //m_currentWorkItem.TaskEndpointInfoReceived = true;
+ //m_currentWorkItem.DeclaredFunctionBlockCount = internal::GetEndpointInfoNotificationNumberOfFunctionBlocksFromSecondWord(ump.word1);
+
+ //RequestAllFunctionBlocks();
+ break;
+
+ case MIDI_STREAM_MESSAGE_STATUS_DEVICE_IDENTITY_NOTIFICATION:
+ UpdateDeviceIdentityProperty(ump);
+ //m_currentWorkItem.TaskDeviceIdentityReceived = true;
+ break;
+
+ case MIDI_STREAM_MESSAGE_STATUS_STREAM_CONFIGURATION_NOTIFICATION:
+ UpdateStreamConfigurationProperties(ump);
+ break;
+
+ case MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_INFO_NOTIFICATION:
+ UpdateFunctionBlockProperty(ump);
+ break;
+
+ case MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_NAME_NOTIFICATION:
+ HandleFunctionBlockNameMessage(ump);
+ break;
+
+ case MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_PRODUCT_INSTANCE_ID_NOTIFICATION:
+ HandleProductInstanceIdMessage(ump);
+ break;
+
+ case MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION:
+ HandleEndpointNameMessage(ump);
+ break;
+
+ default:
+ OutputDebugString(L" Message is unidentified stream message\n");
+ break;
+ }
+
+ return S_OK;
+}
+
+
+
+
+_Use_decl_annotations_
+std::wstring
+CMidiEndpointProtocolWorker::ParseStreamTextMessage(internal::PackedUmp128& message)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO)
+ );
+
+
+ // check the status to know which byte is first to be grabbed
+
+ // preallocate a wstring to the maximum length for a single message to avoid reallocation. Fill with zero
+
+ uint8_t maxCharsThisMessage{ 14 }; // TODO
+
+ uint16_t messageStatus = internal::GetStatusFromStreamMessageFirstWord(message.word0);
+
+ if (messageStatus == MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_NAME_NOTIFICATION)
+ {
+ // function block name messages have 13 character bytes instead of 14 due to having the FB number in the message
+ maxCharsThisMessage = 13;
+ }
+ else
+ {
+ maxCharsThisMessage = 14;
+ }
+
+ std::wstring text;
+ text.reserve(maxCharsThisMessage + 1); // try to avoid reallocations
+
+
+ if (maxCharsThisMessage == 14)
+ {
+ if (MIDIWORDBYTE3(message.word0) != 0) text.push_back((wchar_t)MIDIWORDBYTE3(message.word0));
+ }
+
+ // this unroll is easier than looping, honestly.
+ // Also, the property set completely fails if there are any embedded nuls, so need to
+ // ignore any in the source data
+ if (MIDIWORDBYTE4(message.word0) != 0) text.push_back((wchar_t)MIDIWORDBYTE4(message.word0));
+
+ if (MIDIWORDBYTE1(message.word1) != 0) text.push_back((wchar_t)MIDIWORDBYTE1(message.word1));
+ if (MIDIWORDBYTE2(message.word1) != 0) text.push_back((wchar_t)MIDIWORDBYTE2(message.word1));
+ if (MIDIWORDBYTE3(message.word1) != 0) text.push_back((wchar_t)MIDIWORDBYTE3(message.word1));
+ if (MIDIWORDBYTE4(message.word1) != 0) text.push_back((wchar_t)MIDIWORDBYTE4(message.word1));
+
+ if (MIDIWORDBYTE1(message.word2) != 0) text.push_back((wchar_t)MIDIWORDBYTE1(message.word2));
+ if (MIDIWORDBYTE2(message.word2) != 0) text.push_back((wchar_t)MIDIWORDBYTE2(message.word2));
+ if (MIDIWORDBYTE3(message.word2) != 0) text.push_back((wchar_t)MIDIWORDBYTE3(message.word2));
+ if (MIDIWORDBYTE4(message.word2) != 0) text.push_back((wchar_t)MIDIWORDBYTE4(message.word2));
+
+ if (MIDIWORDBYTE1(message.word3) != 0) text.push_back((wchar_t)MIDIWORDBYTE1(message.word3));
+ if (MIDIWORDBYTE2(message.word3) != 0) text.push_back((wchar_t)MIDIWORDBYTE2(message.word3));
+ if (MIDIWORDBYTE3(message.word3) != 0) text.push_back((wchar_t)MIDIWORDBYTE3(message.word3));
+ if (MIDIWORDBYTE4(message.word3) != 0) text.push_back((wchar_t)MIDIWORDBYTE4(message.word3));
+
+ text.shrink_to_fit();
+
+ return text;
+}
+
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::HandleFunctionBlockNameMessage(internal::PackedUmp128& functionBlockNameMessage)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+
+ uint8_t functionBlockNumber = MIDIWORDBYTE3(functionBlockNameMessage.word0);
+
+ switch (internal::GetFormFromStreamMessageFirstWord(functionBlockNameMessage.word0))
+ {
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_COMPLETE: // complete name in single message. Just update property
+ RETURN_IF_FAILED(UpdateFunctionBlockNameProperty(functionBlockNumber, ParseStreamTextMessage(functionBlockNameMessage)));
+ break;
+
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_START: // start of multi-part name message. Overwrite any other name in the map
+ {
+ std::wstring name = ParseStreamTextMessage(functionBlockNameMessage);
+
+ m_functionBlockNames.insert_or_assign(functionBlockNumber, name);
+ }
+ break;
+
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_CONTINUE: //continuation of multi-part name message. Append to name in map
+ if (m_functionBlockNames.find(functionBlockNumber) != m_functionBlockNames.end())
+ {
+ std::wstring name = m_functionBlockNames.find(functionBlockNumber)->second;
+ name += ParseStreamTextMessage(functionBlockNameMessage);
+
+ m_functionBlockNames.insert_or_assign(functionBlockNumber, name);
+ }
+ else
+ {
+ // name isn't already in the map, so a start message was skipped. Don't append anything at all.
+ }
+ break;
+
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_END: // end of multi-part name message. Finish name and update property
+ if (m_functionBlockNames.find(functionBlockNumber) != m_functionBlockNames.end())
+ {
+ std::wstring name = m_functionBlockNames.find(functionBlockNumber)->second;
+ name += ParseStreamTextMessage(functionBlockNameMessage);
+
+ RETURN_IF_FAILED(UpdateFunctionBlockNameProperty(functionBlockNumber, name));
+ }
+ else
+ {
+ // name isn't already in the map, so at least start message was skipped. Don't append anything at all.
+ }
+ break;
+
+ default:
+ // won't actually happen because the Form field is only 2 bits
+ break;
+ }
+
+ return S_OK;
+}
+
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::HandleEndpointNameMessage(internal::PackedUmp128& endpointNameMessage)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ switch (internal::GetFormFromStreamMessageFirstWord(endpointNameMessage.word0))
+ {
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_COMPLETE: // complete name in single message. Just update property
+ m_endpointName = ParseStreamTextMessage(endpointNameMessage);
+ RETURN_IF_FAILED(UpdateEndpointNameProperty());
+ break;
+
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_START: // start of multi-part name message. Overwrite any other name in the map
+ {
+ m_endpointName = ParseStreamTextMessage(endpointNameMessage);
+ }
+ break;
+
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_CONTINUE: //continuation of multi-part name message. Append to name in map
+ if (!m_endpointName.empty())
+ {
+ m_endpointName += ParseStreamTextMessage(endpointNameMessage);
+ }
+ else
+ {
+ // name isn't already started, so a start message was skipped. Don't append anything at all.
+ }
+ break;
+
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_END: // end of multi-part name message. Finish name and update property
+ if (!m_endpointName.empty())
+ {
+ m_endpointName += ParseStreamTextMessage(endpointNameMessage);
+ RETURN_IF_FAILED(UpdateEndpointNameProperty());
+ }
+ else
+ {
+ // name isn't already started, so a start message was skipped. Don't append anything at all.
+ }
+
+ break;
+
+ default:
+ // won't actually happen because the Form field is only 2 bits
+ break;
+ }
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolWorker::HandleProductInstanceIdMessage(internal::PackedUmp128& productInstanceIdMessage)
+{
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ switch (internal::GetFormFromStreamMessageFirstWord(productInstanceIdMessage.word0))
+ {
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_COMPLETE: // complete name in single message. Just update property
+ OutputDebugString(L" MIDI_STREAM_MESSAGE_MULTI_FORM_COMPLETE\n");
+ m_productInstanceId = ParseStreamTextMessage(productInstanceIdMessage);
+ RETURN_IF_FAILED(UpdateEndpointProductInstanceIdProperty());
+ break;
+
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_START: // start of multi-part name message. Overwrite any other name in the map
+ {
+ OutputDebugString(L" MIDI_STREAM_MESSAGE_MULTI_FORM_START\n");
+ m_productInstanceId = ParseStreamTextMessage(productInstanceIdMessage);
+ }
+ break;
+
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_CONTINUE: //continuation of multi-part name message. Append to name in map
+ OutputDebugString(L" MIDI_STREAM_MESSAGE_MULTI_FORM_CONTINUE\n");
+ if (!m_productInstanceId.empty())
+ {
+ m_productInstanceId += ParseStreamTextMessage(productInstanceIdMessage);
+ }
+ else
+ {
+ // name isn't already started, so a start message was skipped. Don't append anything at all.
+ }
+ break;
+
+ case MIDI_STREAM_MESSAGE_MULTI_FORM_END: // end of multi-part name message. Finish name and update property
+ OutputDebugString(L" MIDI_STREAM_MESSAGE_MULTI_FORM_END\n");
+ if (!m_productInstanceId.empty())
+ {
+ m_productInstanceId += ParseStreamTextMessage(productInstanceIdMessage);
+ RETURN_IF_FAILED(UpdateEndpointProductInstanceIdProperty());
+ }
+ else
+ {
+ // name isn't already started, so a start message was skipped. Don't append anything at all.
+ }
+
+ break;
+
+ default:
+ // won't actually happen because the Form field is only 2 bits
+ break;
+ }
+
+ return S_OK;
+
+}
+
+
diff --git a/src/api/Service/Exe/MidiEndpointProtocolWorker.h b/src/api/Service/Exe/MidiEndpointProtocolWorker.h
new file mode 100644
index 00000000..40bc4ec8
--- /dev/null
+++ b/src/api/Service/Exe/MidiEndpointProtocolWorker.h
@@ -0,0 +1,86 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#pragma once
+
+#include
+#include
+
+class CMidiEndpointProtocolWorker : public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags,
+ IMidiCallback>
+{
+public:
+ CMidiEndpointProtocolWorker() = default;
+ ~CMidiEndpointProtocolWorker() {}
+
+ STDMETHOD(Initialize)(
+ _In_ GUID SessionId,
+ _In_ GUID AbstractionGuid,
+ _In_ LPCWSTR DeviceInterfaceId,
+ _In_ std::shared_ptr& ClientManager,
+ _In_ std::shared_ptr& DeviceManager,
+ _In_ std::shared_ptr& SessionTracker
+ );
+
+ STDMETHOD(ListenForMetadata)();
+
+ STDMETHOD(Callback)(_In_ PVOID Data, _In_ UINT Size, _In_ LONGLONG Position, _In_ LONGLONG Context);
+
+ STDMETHOD(Cleanup)();
+
+private:
+ std::wstring m_deviceInterfaceId;
+ std::wstring m_deviceInstanceId;
+ GUID m_sessionId;
+ GUID m_abstractionGuid;
+
+ LONGLONG m_context{ 0 };
+
+ wil::unique_event_nothrow m_allNegotiationMessagesReceived;
+ wil::unique_event_nothrow m_queueWorkerThreadWakeup;
+
+ // true if we're closing down
+ bool m_shutdown{ false };
+
+ //wil::com_ptr_nothrow m_MidiDeviceManager;
+ std::shared_ptr m_clientManager;
+ std::shared_ptr m_deviceManager;
+ std::shared_ptr m_sessionTracker;
+
+ wil::com_ptr_nothrow m_midiBiDiDevice;
+
+
+ // these are holding locations while names are built
+ std::wstring m_endpointName{};
+ std::wstring m_productInstanceId{};
+ std::map m_functionBlockNames;
+
+
+ std::wstring ParseStreamTextMessage(_In_ internal::PackedUmp128& message);
+
+ DEVPROPKEY FunctionBlockPropertyKeyFromNumber(_In_ uint8_t functionBlockNumber);
+ DEVPROPKEY FunctionBlockNamePropertyKeyFromNumber(_In_ uint8_t functionBlockNumber);
+
+ HRESULT UpdateEndpointNameProperty();
+ HRESULT UpdateEndpointProductInstanceIdProperty();
+ HRESULT UpdateFunctionBlockNameProperty(_In_ uint8_t functionBlockNumber, _In_ std::wstring value);
+
+ HRESULT UpdateDeviceIdentityProperty(_In_ internal::PackedUmp128& identityMessage);
+ HRESULT UpdateEndpointInfoProperties(_In_ internal::PackedUmp128& endpointInfoNotificationMessage);
+ HRESULT UpdateStreamConfigurationProperties(_In_ internal::PackedUmp128& endpointStreamConfigurationNotificationMessage);
+ HRESULT UpdateFunctionBlockProperty(_In_ internal::PackedUmp128& functionBlockInfoNotificationMessage);
+
+ HRESULT HandleFunctionBlockNameMessage(_In_ internal::PackedUmp128& functionBlockNameMessage);
+ HRESULT HandleEndpointNameMessage(_In_ internal::PackedUmp128& endpointNameMessage);
+ HRESULT HandleProductInstanceIdMessage(_In_ internal::PackedUmp128& productInstanceIdMessage);
+
+ HRESULT ProcessStreamMessage(_In_ internal::PackedUmp128 ump);
+};
+
+
diff --git a/src/api/Service/Exe/MidiSessionTracker.cpp b/src/api/Service/Exe/MidiSessionTracker.cpp
index 0d5633d6..de99dae3 100644
--- a/src/api/Service/Exe/MidiSessionTracker.cpp
+++ b/src/api/Service/Exe/MidiSessionTracker.cpp
@@ -37,9 +37,18 @@ CMidiSessionTracker::AddClientSession(
MidiSessionEntry entry;
entry.SessionId = SessionId;
- entry.SessionName = internal::TrimmedWStringCopy(SessionName);
+
+ if (SessionName != nullptr)
+ {
+ entry.SessionName = internal::TrimmedWStringCopy(SessionName);
+ }
+
entry.ClientProcessId = ClientProcessId;
- entry.ProcessName = ClientProcessName;
+
+ if (ClientProcessName != nullptr)
+ {
+ entry.ProcessName = ClientProcessName;
+ }
entry.StartTime = std::chrono::system_clock::now();
diff --git a/src/api/Service/Exe/MidiSrv.cpp b/src/api/Service/Exe/MidiSrv.cpp
index f7c133c1..64f47d06 100644
--- a/src/api/Service/Exe/MidiSrv.cpp
+++ b/src/api/Service/Exe/MidiSrv.cpp
@@ -92,28 +92,53 @@ CMidiSrv::Initialize()
NULL));
- // this is an ugly set of casts, but the reinterpret_cast error only
- // comes up with C++/20. Risk of a straight c-style cast here going
- // poorly is low. Error comes from the function taking non-const params
- // but our arguments here are const.
- RETURN_IF_FAILED(HRESULT_FROM_RPCSTATUS(RpcServerUseProtseqEp(
- (RPC_WSTR)MIDISRV_LRPC_PROTOCOL,
- RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
- (RPC_WSTR)MIDISRV_ENDPOINT,
- rpcSecurityDescriptor.get())));
-
- //RETURN_IF_FAILED(HRESULT_FROM_RPCSTATUS(RpcServerUseProtseqEp(
- // reinterpret_cast(MIDISRV_LRPC_PROTOCOL),
- // RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
- // reinterpret_cast(MIDISRV_ENDPOINT),
- // rpcSecurityDescriptor.get())));
-
- RETURN_IF_FAILED(HRESULT_FROM_RPCSTATUS(RpcServerRegisterIf3(
- MidiSrvRPC_v1_0_s_ifspec,
- NULL, NULL,
- RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_LOCAL_ONLY,
- RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0,
- MidiSrvRpcIfCallback, rpcSecurityDescriptor.get())));
+ if (rpcSecurityDescriptor.is_valid())
+ {
+ // this is an ugly set of casts, but the reinterpret_cast error only
+ // comes up with C++/20. Risk of a straight c-style cast here going
+ // poorly is low. Error comes from the function taking non-const params
+ // but our arguments here are const.
+ auto rpcStatus = RpcServerUseProtseqEp(
+ (RPC_WSTR)MIDISRV_LRPC_PROTOCOL,
+ RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
+ (RPC_WSTR)MIDISRV_ENDPOINT,
+ rpcSecurityDescriptor.get());
+
+ auto rpcHr = HRESULT_FROM_RPCSTATUS(rpcStatus);
+
+ if (FAILED(rpcHr))
+ {
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingHResult(rpcHr, "hresult"),
+ TraceLoggingLong(rpcStatus, "rpc_status"),
+ TraceLoggingWideString(L"RpcServerUseProtseqEp failed. It's likely the service is not responding due to a failed startup.", "message")
+ );
+ }
+
+ RETURN_IF_FAILED(rpcHr);
+
+ RETURN_IF_FAILED(HRESULT_FROM_RPCSTATUS(RpcServerRegisterIf3(
+ MidiSrvRPC_v1_0_s_ifspec,
+ NULL, NULL,
+ RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_LOCAL_ONLY,
+ RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0,
+ MidiSrvRpcIfCallback,
+ rpcSecurityDescriptor.get())));
+ }
+ else
+ {
+ TraceLoggingWrite(
+ MidiSrvTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Returned RPC Security Descriptor is not valid", "message")
+ );
+ }
cleanupOnError.release();
diff --git a/src/api/Service/Exe/MidiSrv.vcxproj b/src/api/Service/Exe/MidiSrv.vcxproj
index 15039a27..f1a086c6 100644
--- a/src/api/Service/Exe/MidiSrv.vcxproj
+++ b/src/api/Service/Exe/MidiSrv.vcxproj
@@ -125,12 +125,12 @@
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
@@ -169,12 +169,12 @@
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
@@ -213,7 +213,7 @@
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
@@ -237,7 +237,7 @@
- %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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.BS2UMPTransform\$(Platform)\$(Configuration);$(SolutionDir)Libs\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
+ %(AdditionalIncludeDirectories);$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(ProjectDir)\..\Inc;$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.diagnosticsabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.virtualmidiabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.networkmidiabstraction\$(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\AbstractionUtilities\inc;$(SolutionDir)VSFiles\intermediate\midi2.SchedulerTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.UMP2BSTransform\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
@@ -264,6 +264,7 @@
+
@@ -286,6 +287,7 @@
+
diff --git a/src/api/Service/Exe/MidiSrv.vcxproj.filters b/src/api/Service/Exe/MidiSrv.vcxproj.filters
index e6831e87..f30c2149 100644
--- a/src/api/Service/Exe/MidiSrv.vcxproj.filters
+++ b/src/api/Service/Exe/MidiSrv.vcxproj.filters
@@ -13,6 +13,12 @@
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav
+
+ {93df485d-cd67-4e5c-92f6-2593d5bb9550}
+
+
+ {c9dff48d-794b-440a-a8e3-7365ee0e48ce}
+
@@ -51,12 +57,15 @@
Source Files
-
- Source Files
-
Source Files
+
+ Source Files\Protocol
+
+
+ Source Files\Protocol
+
@@ -98,12 +107,15 @@
Header Files
-
- Header Files
-
Header Files
+
+ Header Files\Protocol
+
+
+ Header Files\Protocol
+
diff --git a/src/api/Service/Exe/MidiSrvRpc.cpp b/src/api/Service/Exe/MidiSrvRpc.cpp
index 1c284188..c3562e52 100644
--- a/src/api/Service/Exe/MidiSrvRpc.cpp
+++ b/src/api/Service/Exe/MidiSrvRpc.cpp
@@ -98,13 +98,7 @@ HRESULT MidiSrvCreateClient(
// Client manager creates the client, fills in the MIDISRV_CLIENT information
RETURN_IF_FAILED(g_MidiService->GetClientManager(clientManager));
- RETURN_IF_FAILED(clientManager->CreateMidiClient(BindingHandle, MidiDevice, SessionId, CreationParams, createdClient));
-
- // Register this client (this code moved to the client manager)
- // std::shared_ptr sessionTracker;
- // RETURN_IF_FAILED(g_MidiService->GetSessionTracker(sessionTracker));
- // RETURN_IF_FAILED(sessionTracker->AddClientEndpointConnection(SessionId, MidiDevice));
-
+ RETURN_IF_FAILED(clientManager->CreateMidiClient(BindingHandle, MidiDevice, SessionId, CreationParams, createdClient, false));
// Success, transfer the MIDISRV_CLIENT data to the caller.
*Client = createdClient;
diff --git a/src/api/Service/Exe/stdafx.h b/src/api/Service/Exe/stdafx.h
index c155d9a6..94c77ba7 100644
--- a/src/api/Service/Exe/stdafx.h
+++ b/src/api/Service/Exe/stdafx.h
@@ -108,6 +108,7 @@ namespace shared = ::Windows::Devices::Midi2::Internal::Shared;
#include "Midi2UMP2BSTransform.h"
#include "Midi2SchedulerTransform.h"
#include "Midi2EndpointMetadataListenerTransform.h"
+#include "Midi2UmpProtocolDownscalerTransform.h"
// MidiSrv internal classes
@@ -134,6 +135,7 @@ class CMidiDevicePipe;
#include "MidiTransformPipe.h"
#include "MidiClientManager.h"
+#include "MidiEndpointProtocolWorker.h"
#include "MidiEndpointProtocolManager.h"
#include "MidiSessionTracker.h"
diff --git a/src/api/Service/Inc/MidiClientManager.h b/src/api/Service/Inc/MidiClientManager.h
index d7816560..494f795e 100644
--- a/src/api/Service/Inc/MidiClientManager.h
+++ b/src/api/Service/Inc/MidiClientManager.h
@@ -15,64 +15,80 @@ class CMidiClientManager
CMidiClientManager() {}
~CMidiClientManager() {}
- HRESULT Initialize(_In_ std::shared_ptr& performanceManager,
- _In_ std::shared_ptr& processManager,
- _In_ std::shared_ptr& deviceManager,
- _In_ std::shared_ptr& sessionTracker);
+ HRESULT Initialize(
+ _In_ std::shared_ptr& performanceManager,
+ _In_ std::shared_ptr& processManager,
+ _In_ std::shared_ptr& deviceManager,
+ _In_ std::shared_ptr& sessionTracker);
+
+ HRESULT CreateMidiClient(
+ _In_ handle_t,
+ _In_ LPCWSTR,
+ _In_ GUID,
+ _In_ PMIDISRV_CLIENTCREATION_PARAMS,
+ _In_ PMIDISRV_CLIENT,
+ _In_ BOOL);
+
+ HRESULT DestroyMidiClient(
+ _In_ handle_t,
+ _In_ MidiClientHandle);
- HRESULT CreateMidiClient(_In_ handle_t,
- _In_ LPCWSTR,
- _In_ GUID,
- _In_ PMIDISRV_CLIENTCREATION_PARAMS,
- _In_ PMIDISRV_CLIENT);
- HRESULT DestroyMidiClient(_In_ handle_t,
- _In_ MidiClientHandle);
HRESULT Cleanup();
private:
- HRESULT GetMidiClient(_In_ handle_t,
- _In_ LPCWSTR,
- _In_ GUID,
- _In_ PMIDISRV_CLIENTCREATION_PARAMS,
- _In_ PMIDISRV_CLIENT,
- _In_ wil::unique_handle&,
- _In_ wil::com_ptr_nothrow&,
- _In_ BOOL);
-
- HRESULT GetMidiDevice(_In_ handle_t,
- _In_ LPCWSTR,
- _In_ PMIDISRV_CLIENTCREATION_PARAMS,
- _In_ wil::com_ptr_nothrow&);
+ HRESULT GetMidiClient(
+ _In_ handle_t,
+ _In_ LPCWSTR,
+ _In_ GUID,
+ _In_ PMIDISRV_CLIENTCREATION_PARAMS,
+ _In_ PMIDISRV_CLIENT,
+ _In_ wil::unique_handle&,
+ _In_ wil::com_ptr_nothrow&,
+ _In_ BOOL);
+
+ HRESULT GetMidiDevice(
+ _In_ handle_t,
+ _In_ LPCWSTR,
+ _In_ PMIDISRV_CLIENTCREATION_PARAMS,
+ _In_ wil::com_ptr_nothrow&);
// TODO: These should be made more generic and go by a configuration, rather than have
// discrete methods for each type of transform. But right now, it's about making the
// transform process work properly
HRESULT
GetMidiTransform(
- _In_ handle_t,
- _In_ MidiFlow,
- _In_ MidiDataFormat,
- _In_ MidiDataFormat,
- _In_ wil::com_ptr_nothrow&,
- _In_ wil::com_ptr_nothrow&);
+ _In_ handle_t,
+ _In_ MidiFlow,
+ _In_ MidiDataFormat,
+ _In_ MidiDataFormat,
+ _In_ wil::com_ptr_nothrow&,
+ _In_ wil::com_ptr_nothrow&);
+
+ HRESULT
+ GetMidiProtocolDownscalerTransform(
+ _In_ handle_t,
+ _In_ MidiFlow,
+ _In_ wil::com_ptr_nothrow&,
+ _In_ wil::com_ptr_nothrow&,
+ _In_ wil::com_ptr_nothrow&);
HRESULT
GetMidiScheduler(
- _In_ handle_t,
- _In_ MidiFlow,
- _In_ wil::com_ptr_nothrow&,
- _In_ wil::com_ptr_nothrow&,
- _In_ wil::com_ptr_nothrow&);
+ _In_ handle_t,
+ _In_ MidiFlow,
+ _In_ wil::com_ptr_nothrow&,
+ _In_ wil::com_ptr_nothrow&,
+ _In_ wil::com_ptr_nothrow&);
HRESULT
GetMidiEndpointMetadataHandler(
- _In_ handle_t,
- _In_ MidiFlow,
- _In_ wil::com_ptr_nothrow&,
- _In_ wil::com_ptr_nothrow&,
- _In_ wil::com_ptr_nothrow&);
+ _In_ handle_t,
+ _In_ MidiFlow,
+ _In_ wil::com_ptr_nothrow&,
+ _In_ wil::com_ptr_nothrow&,
+ _In_ wil::com_ptr_nothrow&);
wil::critical_section m_ClientManagerLock;
diff --git a/src/api/Test/Libs/MidiTestCommon/MidiTestCommon.vcxproj b/src/api/Test/Libs/MidiTestCommon/MidiTestCommon.vcxproj
index b0d5fef3..cc061da1 100644
--- a/src/api/Test/Libs/MidiTestCommon/MidiTestCommon.vcxproj
+++ b/src/api/Test/Libs/MidiTestCommon/MidiTestCommon.vcxproj
@@ -73,13 +73,13 @@
true
$(SolutionDir)VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration)\
$(SolutionDir)VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration)\
- $(VC_LibraryPath_x64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(WindowsSDK_LibraryPath_x64)
+ $(VC_LibraryPath_Arm64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(WindowsSDK_LibraryPath_Arm64)
true
$(SolutionDir)VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration)\
$(SolutionDir)VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration)\
- $(VC_LibraryPath_x64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(WindowsSDK_LibraryPath_x64)
+ $(VC_LibraryPath_Arm64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(WindowsSDK_LibraryPath_Arm64)
$(SolutionDir)VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration)\
diff --git a/src/api/Test/Midi2.Abstraction.unittests/Midi2.Abstraction.unittests.vcxproj b/src/api/Test/Midi2.Abstraction.unittests/Midi2.Abstraction.unittests.vcxproj
index 314fb1e9..08c8fae7 100644
--- a/src/api/Test/Midi2.Abstraction.unittests/Midi2.Abstraction.unittests.vcxproj
+++ b/src/api/Test/Midi2.Abstraction.unittests/Midi2.Abstraction.unittests.vcxproj
@@ -73,9 +73,9 @@
$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)\VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\midiswenum\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midixproc\$(Platform)\$(Configuration)
- $(VC_LibraryPath_ARM64);$(WindowsSDK_LibraryPath_ARM64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)\VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\midiswenum\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midixproc\$(Platform)\$(Configuration)
$(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
$(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(VC_LibraryPath_ARM64);$(WindowsSDK_LibraryPath_ARM64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)\VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\midiswenum\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midixproc\$(Platform)\$(Configuration)
$(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
@@ -83,9 +83,9 @@
$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)\VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\midiswenum\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midixproc\$(Platform)\$(Configuration)
- $(VC_LibraryPath_ARM64);$(WindowsSDK_LibraryPath_ARM64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)\VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\midiswenum\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midixproc\$(Platform)\$(Configuration)
$(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
$(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(VC_LibraryPath_ARM64);$(WindowsSDK_LibraryPath_ARM64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)\VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\midiswenum\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\test\miditestcommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midixproc\$(Platform)\$(Configuration)
@@ -103,7 +103,7 @@
%(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)test\inc;$(WindowsSdkDir)Testing\Development\inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.ksabstraction\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
Level4
true
- stdcpp20
+ stdcpp17
%(AdditionalDependencies);midixproc.lib;onecoreuap.lib;ksuser.lib;avrt.lib;midikscommon.lib;midiswenum.lib;miditestcommon.lib;$(CoreLibraryDependencies)
diff --git a/src/api/Test/Midi2.Service.unittests/Midi2.Service.unittests.vcxproj b/src/api/Test/Midi2.Service.unittests/Midi2.Service.unittests.vcxproj
index bc8c45e2..4391f465 100644
--- a/src/api/Test/Midi2.Service.unittests/Midi2.Service.unittests.vcxproj
+++ b/src/api/Test/Midi2.Service.unittests/Midi2.Service.unittests.vcxproj
@@ -103,7 +103,7 @@
Level4
%(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)test\inc;$(WindowsSdkDir)\Testing\Development\inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midisrv\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midi2.midisrvabstraction\$(Platform)\$(Configuration)
true
- stdcpp20
+ stdcpp17
%(AdditionalDependencies);onecoreuap.lib;ksuser.lib;avrt.lib;midixproc.lib;midikscommon.lib;midiswenum.lib;miditestcommon.lib;$(CoreLibraryDependencies)
diff --git a/src/api/Transform/ByteStreamToUMP/Midi2.BS2UMPMidiTransform.cpp b/src/api/Transform/ByteStreamToUMP/Midi2.BS2UMPMidiTransform.cpp
index 52b80e4c..fb0de236 100644
--- a/src/api/Transform/ByteStreamToUMP/Midi2.BS2UMPMidiTransform.cpp
+++ b/src/api/Transform/ByteStreamToUMP/Midi2.BS2UMPMidiTransform.cpp
@@ -56,8 +56,6 @@ CMidi2BS2UMPMidiTransform::SendMidiMessage(
LONGLONG Position
)
{
- OutputDebugString(L"" __FUNCTION__);
-
// Send the bytestream byte(s) to the parser
BYTE *data = (BYTE *)Data;
for (UINT i = 0; i < Length; i++)
@@ -65,7 +63,7 @@ CMidi2BS2UMPMidiTransform::SendMidiMessage(
m_BS2UMP.bytestreamParse(data[i]);
}
- // retrieve teh UMP(s) from the parser
+ // retrieve the UMP(s) from the parser
// and sent it on
while (m_BS2UMP.availableUMP())
{
@@ -76,6 +74,8 @@ CMidi2BS2UMPMidiTransform::SendMidiMessage(
umpMessage[umpCount] = m_BS2UMP.readUMP();
}
+ // Note from PMB for Gary: Pretty sure "ump" in this context is just a single UMP word. Some messages like
+ // SysEx are larger (64 bit) and so would be two words back-to-back, so umpCount would be 2 or greater.
if (umpCount > 0)
{
// there are 4 bytes per each 32 bit UMP returned by the parser.
diff --git a/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerMidiTransform.cpp b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerMidiTransform.cpp
new file mode 100644
index 00000000..cb40d510
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerMidiTransform.cpp
@@ -0,0 +1,248 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+#include "pch.h"
+#include "bytestreamToUMP.h"
+//#include "midi2.BS2UMPtransform.h"
+
+_Use_decl_annotations_
+HRESULT
+CMidi2UmpProtocolDownscalerMidiTransform::Initialize(
+ LPCWSTR Device,
+ PTRANSFORMCREATIONPARAMS CreationParams,
+ DWORD *,
+ IMidiCallback * Callback,
+ LONGLONG Context,
+ IUnknown* /*MidiDeviceManager*/
+)
+{
+ TraceLoggingWrite(
+ MidiUmpProtocolDownscalerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ // this only converts UMP to UMP
+ RETURN_HR_IF(ERROR_UNSUPPORTED_TYPE, CreationParams->DataFormatIn != MidiDataFormat_UMP);
+ RETURN_HR_IF(ERROR_UNSUPPORTED_TYPE, CreationParams->DataFormatOut != MidiDataFormat_UMP);
+
+ m_Device = Device;
+ m_Callback = Callback;
+ m_Context = Context;
+
+ return S_OK;
+}
+
+HRESULT
+CMidi2UmpProtocolDownscalerMidiTransform::Cleanup()
+{
+ TraceLoggingWrite(
+ MidiUmpProtocolDownscalerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ return S_OK;
+}
+
+
+
+
+_Use_decl_annotations_
+HRESULT
+CMidi2UmpProtocolDownscalerMidiTransform::SendMidiMessage(
+ PVOID Data,
+ UINT Length,
+ LONGLONG Position
+)
+{
+ // only translate MT4 to MT2 (MIDI 2.0 protocol to MIDI 1.0 protocol)
+ if (internal::GetUmpMessageTypeFromFirstWord((uint32_t&)Data) == MIDI_UMP_MESSAGE_TYPE_MIDI2_CHANNEL_VOICE_64)
+ {
+ // downscale MIDI 2 to MIDI 1. Note that we treat the note index as a note number no matter
+ // what. There was quite a bit of discussion about this, and the main architects in the MIDI
+ // association felt that a wrong note is better than no note. So in cases where the note number
+ // is an index and exact pitch is specified, this will send an incorrect note. This is by
+ // consensus and therefore by design.
+
+ uint8_t* incomingDataPointer = (uint8_t*)Data;
+
+ uint32_t originalWord0 = *(const uint32_t*)(incomingDataPointer);
+ uint32_t originalWord1 = *(const uint32_t*)(incomingDataPointer + sizeof(uint32_t));
+
+ uint8_t groupIndex = internal::GetGroupIndexFromFirstWord(originalWord0);
+ uint8_t channelIndex = internal::GetChannelIndexFromFirstWord(originalWord0);
+ uint8_t status = internal::GetStatusFromChannelVoiceMessage(originalWord0);
+
+ // converting only to 32 bit messages, but some MIDI 2.0 messages convert into multiple MIDI 1.0 messages
+ // a stack reference here is ok because none of these callbacks are async in any way. It's a chain.
+
+ // first we check for messages that return more than one resulting translated message
+ if (status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_PROGRAM_CHANGE ||
+ status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_REG_CONTROLLER ||
+ status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_ASSIGN_CONTROLLER)
+ {
+ uint32_t resultingMessages[4];
+ uint32_t resultingMessageCount{ 0 };
+
+ if (status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_PROGRAM_CHANGE)
+ {
+ // this one needs to create three separate messages so we special case here
+ uint8_t program = MIDIWORDBYTE1(originalWord1);
+
+ // the bank is valid if the least significant bit in word 0 is set
+ bool bankValid = (originalWord0 & (uint32_t)0x1) == 0x1;
+
+ resultingMessageCount = 0;
+
+ if (bankValid)
+ {
+ uint8_t bankMsb = MIDIWORDBYTE3(originalWord1);
+ uint8_t bankLsb = MIDIWORDBYTE4(originalWord1);
+
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2CC(groupIndex, channelIndex, MIDI_UMP_MIDI1_BANK_SELECT_MSB_CC_INDEX, bankMsb);
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2CC(groupIndex, channelIndex, MIDI_UMP_MIDI1_BANK_SELECT_LSB_CC_INDEX, bankLsb);;
+ }
+
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2ProgramChange(groupIndex, channelIndex, program);
+ }
+ else if (status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_REG_CONTROLLER)
+ {
+ // RPN - converts to four MIDI 1.0 messages
+
+ uint8_t bank = internal::CleanupByte7(MIDIWORDBYTE3(originalWord0));
+ uint8_t index = internal::CleanupByte7(MIDIWORDBYTE4(originalWord0));
+
+ uint8_t coarse = internal::CleanupByte7(MIDIWORDBYTE1(originalWord1));
+ uint8_t fine = internal::CleanupByte7(MIDIWORDBYTE2(originalWord1));
+
+ resultingMessageCount = 0;
+
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2CC(groupIndex, channelIndex, MIDI_RPN_CC_NUMBER_BANK, bank);
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2CC(groupIndex, channelIndex, MIDI_RPN_CC_NUMBER_INDEX, index);
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2CC(groupIndex, channelIndex, MIDI_RPN_CC_NUMBER_DATA_COARSE, coarse);
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2CC(groupIndex, channelIndex, MIDI_RPN_CC_NUMBER_DATA_FINE, fine);
+ }
+ else if (status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_ASSIGN_CONTROLLER)
+ {
+ // NRPN - converts to four MIDI 1.0 messages
+
+ uint8_t bank = MIDIWORDBYTE3(originalWord0);
+ uint8_t index = MIDIWORDBYTE4(originalWord0);
+
+ uint8_t coarse = internal::CleanupByte7(MIDIWORDBYTE1(originalWord1));
+ uint8_t fine = internal::CleanupByte7(MIDIWORDBYTE2(originalWord1));
+
+ resultingMessageCount = 0;
+
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2CC(groupIndex, channelIndex, MIDI_NRPN_CC_NUMBER_BANK, bank);
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2CC(groupIndex, channelIndex, MIDI_NRPN_CC_NUMBER_INDEX, index);
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2CC(groupIndex, channelIndex, MIDI_NRPN_CC_NUMBER_DATA_COARSE, coarse);
+ resultingMessages[resultingMessageCount++] = UMPMessage::mt2CC(groupIndex, channelIndex, MIDI_NRPN_CC_NUMBER_DATA_FINE, fine);
+ }
+
+ // send all the generated messages in order
+ if (resultingMessageCount > 0 && m_Callback != nullptr)
+ {
+ for (uint32_t i = 0; i < resultingMessageCount; i++)
+ {
+ RETURN_IF_FAILED(m_Callback->Callback(&resultingMessages[i], sizeof(uint32_t), Position + i, m_Context));
+ }
+ }
+ }
+ else
+ {
+ // these messages all result in a single new message to send.
+ // the one exception is the fall-through which just passes along the original data.
+
+ uint32_t newWord0;
+ PVOID newData;
+ UINT newLength;
+
+ if (status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_NOTE_OFF)
+ {
+ // note number / index is third byte
+ uint8_t noteNumber = MIDIWORDBYTE3(originalWord0);
+ uint8_t newVelocity = (uint8_t)(M2Utils::scaleDown(MIDIWORDSHORT1(originalWord1), 16, 7));
+ newWord0 = UMPMessage::mt2NoteOff(groupIndex, channelIndex, noteNumber, newVelocity);
+ newData = &newWord0;
+ newLength = sizeof(uint32_t);
+ }
+ else if (status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_NOTE_ON)
+ {
+ uint8_t noteNumber = MIDIWORDBYTE3(originalWord0);
+ uint8_t newVelocity = (uint8_t)(M2Utils::scaleDown(MIDIWORDSHORT1(originalWord1), 16, 7));
+ //if (newVelocity == 0) newVelocity = 1;
+ newWord0 = UMPMessage::mt2NoteOff(groupIndex, channelIndex, noteNumber, newVelocity);
+ newData = &newWord0;
+ newLength = sizeof(uint32_t);
+ }
+ else if (status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_POLY_PRESSURE)
+ {
+ uint8_t noteNumber = MIDIWORDBYTE3(originalWord0);
+ // pressure is entire second word
+ uint8_t newPressure = (uint8_t)(M2Utils::scaleDown(originalWord1, 32, 7));
+ newWord0 = UMPMessage::mt2PolyPressure(groupIndex, channelIndex, noteNumber, newPressure);
+
+ newData = &newWord0;
+ newLength = sizeof(uint32_t);
+ }
+ else if (status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_CONTROL_CHANGE)
+ {
+ uint8_t controlIndex = MIDIWORDBYTE3(originalWord0);
+ // value is entire second word
+ uint8_t newValue = (uint8_t)(M2Utils::scaleDown(originalWord1, 32, 7));
+ newWord0 = UMPMessage::mt2CC(groupIndex, channelIndex, controlIndex, newValue);
+
+ newData = &newWord0;
+ newLength = sizeof(uint32_t);
+ }
+ else if (status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_CHANNEL_PRESSURE)
+ {
+ // the pressure data value is the entire second word
+ uint8_t pressureData = (uint8_t)(M2Utils::scaleDown(originalWord1, 32, 7));
+
+ newWord0 = UMPMessage::mt2ChannelPressure(groupIndex, channelIndex, pressureData);
+
+ newData = &newWord0;
+ newLength = sizeof(uint32_t);
+ }
+ else if (status == MIDI_UMP_MIDI2_CHANNEL_VOICE_STATUS_PITCH_BEND)
+ {
+ // the bend value is the entire second word
+ uint16_t bend = (uint16_t)M2Utils::scaleDown(originalWord1, 32, 14);
+
+ newWord0 = UMPMessage::mt2PitchBend(groupIndex, channelIndex, bend);
+
+ newData = &newWord0;
+ newLength = sizeof(uint32_t);
+ }
+ else
+ {
+ // just pass it through without any processing. We don't have a specific conversion for this CV message
+ newData = Data;
+ newLength = Length;
+ }
+
+
+ if (m_Callback != nullptr)
+ {
+ return m_Callback->Callback(newData, newLength, Position, m_Context);
+ }
+
+ }
+ }
+ else
+ {
+ // pass through because it's not a message type we handle
+
+ if (m_Callback != nullptr)
+ {
+ return m_Callback->Callback(Data, Length, Position, m_Context);
+ }
+ }
+
+
+ return S_OK;
+}
diff --git a/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerMidiTransform.h b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerMidiTransform.h
new file mode 100644
index 00000000..50415923
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerMidiTransform.h
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+#pragma once
+
+#include "bytestreamToUMP.h"
+
+class CMidi2UmpProtocolDownscalerMidiTransform :
+ public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags,
+ IMidiDataTransform>
+{
+public:
+
+ STDMETHOD(Initialize(_In_ LPCWSTR, _In_ PTRANSFORMCREATIONPARAMS, _In_ DWORD *, _In_opt_ IMidiCallback *, _In_ LONGLONG, _In_ IUnknown*));
+ STDMETHOD(SendMidiMessage(_In_ PVOID message, _In_ UINT size, _In_ LONGLONG));
+ STDMETHOD(Cleanup)();
+
+private:
+
+ std::wstring m_Device;
+ wil::com_ptr_nothrow m_Callback;
+ LONGLONG m_Context;
+};
+
+
diff --git a/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.cpp b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.cpp
new file mode 100644
index 00000000..688c7f67
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.cpp
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+#include "pch.h"
+
+_Use_decl_annotations_
+HRESULT
+CMidi2UmpProtocolDownscalerTransform::Activate(
+ REFIID Iid,
+ void **Interface
+)
+{
+ RETURN_HR_IF(E_INVALIDARG, nullptr == Interface);
+
+ if (__uuidof(IMidiDataTransform) == Iid)
+ {
+ TraceLoggingWrite(
+ MidiUmpProtocolDownscalerTransformTelemetryProvider::Provider(),
+ __FUNCTION__ "- Midi Transform",
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingValue(__FUNCTION__),
+ TraceLoggingPointer(this, "this")
+ );
+
+ wil::com_ptr_nothrow midiTransform;
+ RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&midiTransform));
+ *Interface = midiTransform.detach();
+ }
+
+ else
+ {
+ return E_NOINTERFACE;
+ }
+
+ return S_OK;
+}
+
diff --git a/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.def b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.def
new file mode 100644
index 00000000..42a13bf2
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.def
@@ -0,0 +1,10 @@
+; Copyright (c) Microsoft Corporation. All rights reserved.
+
+LIBRARY
+
+EXPORTS
+ DllCanUnloadNow PRIVATE
+ DllGetClassObject PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
+ DllInstall PRIVATE
diff --git a/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.h b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.h
new file mode 100644
index 00000000..9d59e1bc
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.h
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+#pragma once
+
+class MidiUmpProtocolDownscalerTransformTelemetryProvider : public wil::TraceLoggingProvider
+{
+ IMPLEMENT_TRACELOGGING_CLASS_WITH_MICROSOFT_TELEMETRY(
+ MidiUmpProtocolDownscalerTransformTelemetryProvider,
+ "Microsoft.Windows.Midi2.UmpProtocolDownscalerTransform",
+ // {b4d50468-e9d8-5080-5b75-e49eb39fee9c}
+ (0xb4d50468,0xe9d8,0x5080,0x5b,0x75,0xe4,0x9e,0xb3,0x9f,0xee,0x9c))
+};
+
+using namespace ATL;
+
+class ATL_NO_VTABLE CMidi2UmpProtocolDownscalerTransform :
+ public CComObjectRootEx,
+ public CComCoClass,
+ public IMidiTransform
+{
+public:
+ CMidi2UmpProtocolDownscalerTransform()
+ {
+ }
+
+ DECLARE_REGISTRY_RESOURCEID(IDR_MIDI2UMPPROTOCOLDOWNSCALERTRANSFORM)
+
+ BEGIN_COM_MAP(CMidi2UmpProtocolDownscalerTransform)
+ COM_INTERFACE_ENTRY(IMidiTransform)
+ END_COM_MAP()
+
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+ STDMETHOD(Activate)(_In_ REFIID, _Out_ void**);
+private:
+};
+
+OBJECT_ENTRY_AUTO(__uuidof(Midi2UmpProtocolDownscalerTransform), CMidi2UmpProtocolDownscalerTransform)
+
diff --git a/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.rc b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.rc
new file mode 100644
index 00000000..1321d6b9
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.rc
@@ -0,0 +1,115 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#ifndef APSTUDIO_INVOKED
+#include
+#endif
+#include "winres.h"
+#include "verrsrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE 9, 1
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#ifndef APSTUDIO_INVOKED\r\n"
+ "#include ""targetver.h""\r\n"
+ "#endif\r\n"
+ "#include ""winres.h""\r\n"
+ "#include ""verrsrc.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "1 TYPELIB ""Midi2UmpProtocolDownscalerTransform.tlb""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "CompanyName", "TODO: "
+ VALUE "FileDescription", "TODO: "
+ VALUE "FileVersion", "1.0.0.1"
+ VALUE "LegalCopyright", "TODO: (c) . All rights reserved."
+ VALUE "InternalName", "Midi2.UmpProtocolDownscalerTransform.dll"
+ VALUE "OriginalFilename", "Midi2.UmpProtocolDownscalerTransform.dll"
+ VALUE "ProductName", "TODO: "
+ VALUE "ProductVersion", "1.0.0.1"
+ VALUE "OLESelfRegister", ""
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1200
+ END
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_PROJNAME "Midi2.UmpProtocolDownscalerTransform"
+END
+
+IDR_MIDI2UMPPROTOCOLDOWNSCALERTRANSFORM REGISTRY "Midi2.UmpProtocolDownscalerTransform.rgs"
+////////////////////////////////////////////////////////////////////////////
+
+
+#endif
+
+#if 0
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+1 TYPELIB "Midi2UmpProtocolDownscalerTransform.tlb"
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+#endif
+
diff --git a/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.rgs b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.rgs
new file mode 100644
index 00000000..19ebd43f
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.rgs
@@ -0,0 +1,14 @@
+HKCR
+{
+ NoRemove CLSID
+ {
+ ForceRemove {dc638b31-cf31-48ed-9e79-02740bf5d013} = s 'Midi2BS2UMPTransform Class'
+ {
+ InprocServer32 = s '%MODULE%'
+ {
+ val ThreadingModel = s 'Both'
+ }
+ 'Version' = s '1.0'
+ }
+ }
+}
diff --git a/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.vcxproj b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.vcxproj
new file mode 100644
index 00000000..e7e66f54
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.vcxproj
@@ -0,0 +1,310 @@
+
+
+
+
+ Debug
+ ARM64
+
+
+ Debug
+ ARM64EC
+
+
+ Release
+ ARM64
+
+
+ Debug
+ x64
+
+
+ Release
+ ARM64EC
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ {C31CEE7B-8BEC-46A6-A8FD-74BABB01A4FB}
+ Win32Proj
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(VC_LibraryPath_ARM64);$(WindowsSDK_LibraryPath_ARM64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midiksenum\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midiks\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\AM_MIDI2 \$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(VC_LibraryPath_ARM64);$(WindowsSDK_LibraryPath_ARM64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midiksenum\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midiks\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\AM_MIDI2 \$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(VC_LibraryPath_ARM64);$(WindowsSDK_LibraryPath_ARM64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)\VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midiksenum\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midiks\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\AM_MIDI2\$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(VC_LibraryPath_ARM64);$(WindowsSDK_LibraryPath_ARM64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)\VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midiksenum\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midiks\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\AM_MIDI2 \$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midiksenum\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\midiks\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\AM_MIDI2 \$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(WindowsSdkDir)\Testing\Development\lib\$(Platform);$(SolutionDir)\VSFiles\intermediate\midikscommon\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midiksenum\$(Platform)\$(Configuration);$(SolutionDir)\VSFiles\intermediate\midiks\$(Platform)\$(Configuration);$(SolutionDir)VSFiles\intermediate\AM_MIDI2\$(Platform)\$(Configuration)\
+
+
+
+ Level4
+
+
+
+
+ Level4
+
+
+
+
+ Level4
+
+
+
+
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir);$(SolutionDir)Libs\AM_MIDI2\Include
+ Create
+ pch.h
+ stdcpp20
+
+
+ %(AdditionalDependencies);onecoreuap.lib;avrt.lib;am_midi2.lib;$(CoreLibraryDependencies)
+ Midi2.UmpProtocolDownscalerTransform.def
+
+
+ $(SolutionDir)\idl;%(AdditionalIncludeDirectories)
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ %(Filename).h
+
+
+
+
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir);$(SolutionDir)Libs\AM_MIDI2\Include
+ Create
+ pch.h
+ stdcpp20
+
+
+ %(AdditionalDependencies);onecoreuap.lib;avrt.lib;am_midi2.lib;$(CoreLibraryDependencies)
+ Midi2.UmpProtocolDownscalerTransform.def
+
+
+ $(SolutionDir)\idl;%(AdditionalIncludeDirectories)
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ %(Filename).h
+
+
+
+
+ true
+ %(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir);$(SolutionDir)Libs\AM_MIDI2\Include
+ Create
+ pch.h
+ stdcpp20
+
+
+ %(AdditionalDependencies);onecoreuap.lib;avrt.lib;am_midi2.lib;$(CoreLibraryDependencies)
+ Midi2.UmpProtocolDownscalerTransform.def
+
+
+ $(SolutionDir)\idl;%(AdditionalIncludeDirectories)
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ %(Filename).h
+
+
+
+
+ %(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir);$(SolutionDir)Libs\AM_MIDI2\Include
+
+
+
+
+ %(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir);$(SolutionDir)Libs\AM_MIDI2\Include
+
+
+
+
+ Level4
+
+
+
+
+ Level4
+
+
+
+
+ true
+ Create
+ pch.h
+ stdcpp20
+
+
+ %(AdditionalDependencies);onecoreuap.lib;avrt.lib;am_midi2.lib;$(CoreLibraryDependencies)
+ Midi2.UmpProtocolDownscalerTransform.def
+
+
+ $(SolutionDir)\idl;%(AdditionalIncludeDirectories)
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ %(Filename).h
+
+
+
+
+ true
+ Create
+ pch.h
+ stdcpp20
+
+
+ %(AdditionalDependencies);onecoreuap.lib;avrt.lib;am_midi2.lib;$(CoreLibraryDependencies)
+ Midi2.UmpProtocolDownscalerTransform.def
+
+
+ $(SolutionDir)\idl;%(AdditionalIncludeDirectories)
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ %(Filename).h
+
+
+
+
+ %(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir);$(SolutionDir)Libs\AM_MIDI2\Include
+
+
+
+
+ Level4
+
+
+
+
+ true
+ Create
+ pch.h
+ stdcpp20
+
+
+ %(AdditionalDependencies);onecoreuap.lib;avrt.lib;am_midi2.lib;$(CoreLibraryDependencies)
+ Midi2.UmpProtocolDownscalerTransform.def
+
+
+ $(SolutionDir)\idl;%(AdditionalIncludeDirectories)
+ $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ %(Filename).h
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
\ No newline at end of file
diff --git a/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.vcxproj.filters b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.vcxproj.filters
new file mode 100644
index 00000000..9f76c4c2
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Midi2.UmpProtocolDownscalerTransform.vcxproj.filters
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+
+ Resource Files
+
+
+ Source Files
+
+
+
+
+ {e577d6ce-71a4-4a68-bebf-c03b6c8349c9}
+
+
+ {f9db7a7c-1f53-4633-9405-eb5c06082f5d}
+
+
+ {dd93dd2d-55b3-4c59-b8b2-d7ef64c9915c}
+
+
+
+
+ Source Files
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/src/api/Transform/UmpProtocolDownscaler/Midi2UmpProtocolDownscalerTransform.idl b/src/api/Transform/UmpProtocolDownscaler/Midi2UmpProtocolDownscalerTransform.idl
new file mode 100644
index 00000000..98f59e47
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Midi2UmpProtocolDownscalerTransform.idl
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+import "oaidl.idl";
+import "ocidl.idl";
+
+import "MidiAbstraction.idl";
+
+[
+ uuid(1b2e6e7f-e780-4406-92e0-14f4b8b04552),
+ version(1.0),
+]
+library Midi2UmpProtocolDownscalerTransformLib
+{
+ importlib("stdole2.tlb");
+
+ [uuid(dc638b31-cf31-48ed-9e79-02740bf5d013), version(1.0)]
+ coclass Midi2UmpProtocolDownscalerTransform
+ {
+ [default] interface IMidiDataTransform;
+ }
+};
+
diff --git a/src/api/Transform/UmpProtocolDownscaler/Resource.h b/src/api/Transform/UmpProtocolDownscaler/Resource.h
new file mode 100644
index 00000000..3b5e3495
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/Resource.h
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+#define IDS_PROJNAME 100
+#define IDR_MIDI2UMPPROTOCOLDOWNSCALERTRANSFORM 101
+
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 201
+#define _APS_NEXT_COMMAND_VALUE 32768
+#define _APS_NEXT_CONTROL_VALUE 201
+#define _APS_NEXT_SYMED_VALUE 106
+#endif
+#endif
diff --git a/src/api/Transform/UmpProtocolDownscaler/dllmain.cpp b/src/api/Transform/UmpProtocolDownscaler/dllmain.cpp
new file mode 100644
index 00000000..bbb9ff71
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/dllmain.cpp
@@ -0,0 +1,78 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+#include "pch.h"
+
+CMidi2UmpProtocolDownscalerTransformModule _AtlModule;
+
+extern "C" BOOL WINAPI
+DllMain(
+ HINSTANCE /* hInstance */,
+ DWORD Reason,
+ LPVOID Reserved
+)
+{
+ if (Reason == DLL_PROCESS_ATTACH)
+ {
+ wil::SetResultTelemetryFallback(MidiUmpProtocolDownscalerTransformTelemetryProvider::FallbackTelemetryCallback);
+ }
+
+ return _AtlModule.DllMain(Reason, Reserved);
+}
+
+_Use_decl_annotations_
+STDAPI
+DllCanUnloadNow(void)
+{
+ return _AtlModule.DllCanUnloadNow();
+}
+
+_Use_decl_annotations_
+STDAPI
+DllGetClassObject(
+ REFCLSID Clsid,
+ REFIID Riid,
+ LPVOID* Object
+)
+{
+ return _AtlModule.DllGetClassObject(Clsid, Riid, Object);
+}
+
+STDAPI
+DllRegisterServer(void)
+{
+ // registers object, typelib and all interfaces in typelib
+ HRESULT hr = _AtlModule.DllRegisterServer(FALSE);
+ return hr;
+}
+
+STDAPI
+DllUnregisterServer(void)
+{
+ HRESULT hr = _AtlModule.DllUnregisterServer(FALSE);
+ return hr;
+}
+
+_Use_decl_annotations_
+STDAPI
+DllInstall(
+ BOOL Install,
+ LPCWSTR )
+{
+ HRESULT hr = E_FAIL;
+
+ if (Install)
+ {
+ hr = DllRegisterServer();
+ if (FAILED(hr))
+ {
+ DllUnregisterServer();
+ }
+ }
+ else
+ {
+ hr = DllUnregisterServer();
+ }
+
+ return hr;
+}
+
diff --git a/src/api/Transform/UmpProtocolDownscaler/dllmain.h b/src/api/Transform/UmpProtocolDownscaler/dllmain.h
new file mode 100644
index 00000000..f2bd7232
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/dllmain.h
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+#pragma once
+
+class CMidi2UmpProtocolDownscalerTransformModule : public ATL::CAtlDllModuleT< CMidi2UmpProtocolDownscalerTransformModule >
+{
+public :
+ DECLARE_LIBID(LIBID_Midi2UmpProtocolDownscalerTransformLib)
+ DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MIDI2UMPPROTOCOLDOWNSCALERTRANSFORM, "{1b2e6e7f-e780-4406-92e0-14f4b8b04552}")
+};
+
+extern class CMidi2UmpProtocolDownscalerTransformModule _AtlModule;
diff --git a/src/api/Transform/UmpProtocolDownscaler/packages.config b/src/api/Transform/UmpProtocolDownscaler/packages.config
new file mode 100644
index 00000000..09be25d9
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/api/Transform/UmpProtocolDownscaler/pch.cpp b/src/api/Transform/UmpProtocolDownscaler/pch.cpp
new file mode 100644
index 00000000..f197443b
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/pch.cpp
@@ -0,0 +1,4 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+#include "pch.h"
+
diff --git a/src/api/Transform/UmpProtocolDownscaler/pch.h b/src/api/Transform/UmpProtocolDownscaler/pch.h
new file mode 100644
index 00000000..2b1304fa
--- /dev/null
+++ b/src/api/Transform/UmpProtocolDownscaler/pch.h
@@ -0,0 +1,60 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+#pragma once
+
+#ifndef STRICT
+#define STRICT
+#endif
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#define _ATL_APARTMENT_THREADED
+#define _ATL_NO_AUTOMATIC_NAMESPACE
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
+#define ATL_NO_ASSERT_ON_DESTROY_NONEXISTENT_WINDOW
+
+#include "resource.h"
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "mididefs.h"
+
+#include "utils.h" // AM_MIDI2
+#include "umpMessageCreate.h" // AM_MIDI2
+#include "ump_helpers.h" // internal helpers
+
+namespace internal = ::Windows::Devices::Midi2::Internal;
+
+
+#include "MidiServicePlugin.h"
+#include "MidiServicePlugin_i.c"
+
+#include "Midi2UmpProtocolDownscalerTransform_i.c"
+#include "Midi2UmpProtocolDownscalerTransform.h"
+
+#include "dllmain.h"
+
+#include "Midi2.UmpProtocolDownscalerTransform.h"
+#include "Midi2.UmpProtocolDownscalerMidiTransform.h"
+
diff --git a/src/api/idl/MidiEndpointProtocolManagerInterface.idl b/src/api/idl/MidiEndpointProtocolManagerInterface.idl
index 22a3cb0f..993fa107 100644
--- a/src/api/idl/MidiEndpointProtocolManagerInterface.idl
+++ b/src/api/idl/MidiEndpointProtocolManagerInterface.idl
@@ -6,9 +6,44 @@
// Further information: https://github.com/microsoft/MIDI/
// ============================================================================
-
+import "oaidl.idl";
import "unknwn.idl";
+
+typedef struct
+{
+ BYTE FirstGroup;
+ BYTE NumberOfGroupsSpanned;
+ BOOL IsActive;
+ BOOL IsMIDIMessageDestination; // if clients can send messages TO this block. We should create a MIDI 1.0 output port
+ BOOL IsMIDIMessageSource; // if clients can receive messages FROM this block. We should create a MIDI 1.0 input port
+ BSTR Name;
+
+} DISCOVEREDFUNCTIONBLOCK, *PDISCOVEREDFUNCTIONBLOCK;
+
+
+// this only contains key info required by the endpoint managers
+// so they can create compatible MIDI 1.0 ports from the results
+// from MIDI 2.0 protocol negotiation
+typedef struct
+{
+ BOOL EndpointInformationReceived;
+
+ BSTR EndpointSuppliedName;
+ BSTR EndpointSuppliedProductInstanceId;
+
+ BYTE NumberOfFunctionBlocksDeclared;
+ BYTE NumberOfFunctionBlocksReceived;
+ BOOL FunctionBlocksAreStatic;
+
+ BOOL EndpointSupportsMIDI2Protocol;
+ BOOL EndpointSupportsMIDI1Protocol;
+
+ SAFEARRAY(DISCOVEREDFUNCTIONBLOCK) DiscoveredFunctionBlocks;
+
+} ENDPOINTPROTOCOLNEGOTIATIONRESULTS, *PENDPOINTPROTOCOLNEGOTIATIONRESULTS;
+
+
[
object,
local,
@@ -22,7 +57,11 @@ interface IMidiEndpointProtocolManagerInterface : IUnknown
[in] BOOL PreferToSendJRTimestampsToEndpoint,
[in] BOOL PreferToReceiveJRTimestampsFromEndpoint,
[in] BYTE PreferredMidiProtocol,
- [in] WORD TimeoutMS
+ [in] WORD TimeoutMS,
+ [out, retval] PENDPOINTPROTOCOLNEGOTIATIONRESULTS* NegotiationResults
);
+ // TODO: The above method should have out params for the discovered function blocks
+ // to make it easier for the endpoint to create initial MIDI 1.0 compat devices
+
};
diff --git a/src/oob-setup/Directory.Build.props b/src/oob-setup/Directory.Build.props.zzold
similarity index 100%
rename from src/oob-setup/Directory.Build.props
rename to src/oob-setup/Directory.Build.props.zzold
diff --git a/src/oob-setup/RegistryCustomActions/RegistryCustomActions.csproj b/src/oob-setup/RegistryCustomActions/RegistryCustomActions.csproj
index b8d3833f..0062dd94 100644
--- a/src/oob-setup/RegistryCustomActions/RegistryCustomActions.csproj
+++ b/src/oob-setup/RegistryCustomActions/RegistryCustomActions.csproj
@@ -1,7 +1,7 @@
net48
- AnyCPU;x64
+ AnyCPU;x64;Arm64
diff --git a/src/oob-setup/api-package/WindowsMidiServices.wxs b/src/oob-setup/api-package/WindowsMidiServices.wxs
index 106b7382..fc1b3079 100644
--- a/src/oob-setup/api-package/WindowsMidiServices.wxs
+++ b/src/oob-setup/api-package/WindowsMidiServices.wxs
@@ -3,7 +3,7 @@
+ SourceFile="$(env.MIDI_REPO_ROOT)src\oob-setup\RegistryCustomActions\bin\$(var.Platform)\Release\net48\RegistryCustomActions.CA.dll" />
@@ -47,21 +47,23 @@
Bitness="always64"
Directory="SERVICE_INSTALLFOLDER"
Guid="{6a2300f5-ccae-4f61-b849-d41aefb8a999}" >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -69,14 +71,14 @@
Bitness="always64"
Directory="SERVICE_INSTALLFOLDER"
Guid="6a2300f5-ccae-4f61-b849-d41aefb8a2f7" >
-
+
-
-
-
-
-
-
+
@@ -129,14 +126,21 @@
+
+
+
+
+
+
+
-
+
-
+
@@ -157,10 +161,10 @@
Bitness="always64"
Directory="API_INSTALLFOLDER"
Guid="dc48ab14-dd46-4ff2-8d59-10b9a8215c7e">
-
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
+
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
diff --git a/src/oob-setup/main-bundle/Bundle.wxs b/src/oob-setup/main-bundle/Bundle.wxs
index a346506c..dc9b5b81 100644
--- a/src/oob-setup/main-bundle/Bundle.wxs
+++ b/src/oob-setup/main-bundle/Bundle.wxs
@@ -49,7 +49,7 @@
Id="SearchForVCRuntime"
Variable="VCRuntimeInstalled"
Root="HKLM"
- Key="SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\X64"
+ Key="SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\$(var.Platform)"
Value="Installed"
Result="exists"/>
@@ -75,7 +75,7 @@
-
+
@@ -99,7 +99,7 @@
Bundle
WindowsMidiServicesCompleteSetup
-
- $([System.DateTime]::Now.ToString("yyyyMMddhhmm"))
-
- BuildDate=$(BuildDate)
+ $([System.DateTime]::Now.ToString("yyyyMMddhhmm"))
+ BuildDate=$(BuildDate)
true
@@ -13,15 +11,13 @@
true
-
-
-
-
-
+
+
+
diff --git a/src/oob-setup/midi-services-setup.sln b/src/oob-setup/midi-services-setup.sln
index e38f61eb..f11c0bfe 100644
--- a/src/oob-setup/midi-services-setup.sln
+++ b/src/oob-setup/midi-services-setup.sln
@@ -10,33 +10,61 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RegistryCustomActions", "RegistryCustomActions\RegistryCustomActions.csproj", "{8049E08D-92F2-4BDE-B07F-BCF7DAD5F134}"
EndProject
Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "console-package", "console-package\console-package.wixproj", "{6894F024-2ADF-4A9B-87FC-70C8209C6CDE}"
+ ProjectSection(ProjectDependencies) = postProject
+ {E18CC5FC-F028-48A7-9343-E32A85CDF7B3} = {E18CC5FC-F028-48A7-9343-E32A85CDF7B3}
+ EndProjectSection
EndProject
Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "settings-package", "settings-package\settings-package.wixproj", "{0C3D23A8-A238-4551-BEB4-2912EFA3245C}"
+ ProjectSection(ProjectDependencies) = postProject
+ {E18CC5FC-F028-48A7-9343-E32A85CDF7B3} = {E18CC5FC-F028-48A7-9343-E32A85CDF7B3}
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
+ Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E18CC5FC-F028-48A7-9343-E32A85CDF7B3}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {E18CC5FC-F028-48A7-9343-E32A85CDF7B3}.Debug|ARM64.Build.0 = Debug|ARM64
{E18CC5FC-F028-48A7-9343-E32A85CDF7B3}.Debug|x64.ActiveCfg = Debug|x64
{E18CC5FC-F028-48A7-9343-E32A85CDF7B3}.Debug|x64.Build.0 = Debug|x64
+ {E18CC5FC-F028-48A7-9343-E32A85CDF7B3}.Release|ARM64.ActiveCfg = Release|ARM64
+ {E18CC5FC-F028-48A7-9343-E32A85CDF7B3}.Release|ARM64.Build.0 = Release|ARM64
{E18CC5FC-F028-48A7-9343-E32A85CDF7B3}.Release|x64.ActiveCfg = Release|x64
{E18CC5FC-F028-48A7-9343-E32A85CDF7B3}.Release|x64.Build.0 = Release|x64
+ {182F17E6-CF91-4D65-B767-6469D523F5AC}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {182F17E6-CF91-4D65-B767-6469D523F5AC}.Debug|ARM64.Build.0 = Debug|ARM64
{182F17E6-CF91-4D65-B767-6469D523F5AC}.Debug|x64.ActiveCfg = Debug|x64
{182F17E6-CF91-4D65-B767-6469D523F5AC}.Debug|x64.Build.0 = Debug|x64
+ {182F17E6-CF91-4D65-B767-6469D523F5AC}.Release|ARM64.ActiveCfg = Release|ARM64
+ {182F17E6-CF91-4D65-B767-6469D523F5AC}.Release|ARM64.Build.0 = Release|ARM64
{182F17E6-CF91-4D65-B767-6469D523F5AC}.Release|x64.ActiveCfg = Release|x64
{182F17E6-CF91-4D65-B767-6469D523F5AC}.Release|x64.Build.0 = Release|x64
+ {8049E08D-92F2-4BDE-B07F-BCF7DAD5F134}.Debug|ARM64.ActiveCfg = Debug|Arm64
+ {8049E08D-92F2-4BDE-B07F-BCF7DAD5F134}.Debug|ARM64.Build.0 = Debug|Arm64
{8049E08D-92F2-4BDE-B07F-BCF7DAD5F134}.Debug|x64.ActiveCfg = Debug|x64
{8049E08D-92F2-4BDE-B07F-BCF7DAD5F134}.Debug|x64.Build.0 = Debug|x64
+ {8049E08D-92F2-4BDE-B07F-BCF7DAD5F134}.Release|ARM64.ActiveCfg = Release|Arm64
+ {8049E08D-92F2-4BDE-B07F-BCF7DAD5F134}.Release|ARM64.Build.0 = Release|Arm64
{8049E08D-92F2-4BDE-B07F-BCF7DAD5F134}.Release|x64.ActiveCfg = Release|x64
{8049E08D-92F2-4BDE-B07F-BCF7DAD5F134}.Release|x64.Build.0 = Release|x64
+ {6894F024-2ADF-4A9B-87FC-70C8209C6CDE}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {6894F024-2ADF-4A9B-87FC-70C8209C6CDE}.Debug|ARM64.Build.0 = Debug|ARM64
{6894F024-2ADF-4A9B-87FC-70C8209C6CDE}.Debug|x64.ActiveCfg = Debug|x64
{6894F024-2ADF-4A9B-87FC-70C8209C6CDE}.Debug|x64.Build.0 = Debug|x64
+ {6894F024-2ADF-4A9B-87FC-70C8209C6CDE}.Release|ARM64.ActiveCfg = Release|ARM64
+ {6894F024-2ADF-4A9B-87FC-70C8209C6CDE}.Release|ARM64.Build.0 = Release|ARM64
{6894F024-2ADF-4A9B-87FC-70C8209C6CDE}.Release|x64.ActiveCfg = Release|x64
{6894F024-2ADF-4A9B-87FC-70C8209C6CDE}.Release|x64.Build.0 = Release|x64
+ {0C3D23A8-A238-4551-BEB4-2912EFA3245C}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {0C3D23A8-A238-4551-BEB4-2912EFA3245C}.Debug|ARM64.Build.0 = Debug|ARM64
{0C3D23A8-A238-4551-BEB4-2912EFA3245C}.Debug|x64.ActiveCfg = Debug|x64
{0C3D23A8-A238-4551-BEB4-2912EFA3245C}.Debug|x64.Build.0 = Debug|x64
+ {0C3D23A8-A238-4551-BEB4-2912EFA3245C}.Release|ARM64.ActiveCfg = Release|ARM64
+ {0C3D23A8-A238-4551-BEB4-2912EFA3245C}.Release|ARM64.Build.0 = Release|ARM64
{0C3D23A8-A238-4551-BEB4-2912EFA3245C}.Release|x64.ActiveCfg = Release|x64
{0C3D23A8-A238-4551-BEB4-2912EFA3245C}.Release|x64.Build.0 = Release|x64
EndGlobalSection
diff --git a/src/oob-setup/settings-package/MidiSettings.wxs b/src/oob-setup/settings-package/MidiSettings.wxs
index 375d0c4f..0b147ce2 100644
--- a/src/oob-setup/settings-package/MidiSettings.wxs
+++ b/src/oob-setup/settings-package/MidiSettings.wxs
@@ -3,7 +3,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ Source="$(StagingSourceRootFolder)\midi-settings\$(var.Platform)\MidiSettings.exe" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
diff --git a/src/prototypes/ble-midi1-proto/Additional Properties.txt b/src/prototypes/ble-midi1-proto/Additional Properties.txt
new file mode 100644
index 00000000..c65dc2cf
--- /dev/null
+++ b/src/prototypes/ble-midi1-proto/Additional Properties.txt
@@ -0,0 +1,8 @@
+
+
+We're going to need additional properties for BLE specifically
+
+DEVPKEY_AssociatedBluetoothDeviceId
+
+Probably something for the current connection status because we need to manage that better
+
diff --git a/src/prototypes/ble-midi1-proto/MidiBleAdvertisedEndpointManager.cpp b/src/prototypes/ble-midi1-proto/MidiBleAdvertisedEndpointManager.cpp
new file mode 100644
index 00000000..b9f631a9
--- /dev/null
+++ b/src/prototypes/ble-midi1-proto/MidiBleAdvertisedEndpointManager.cpp
@@ -0,0 +1,45 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#include "pch.h"
+
+HRESULT MidiBleAdvertisedEndpointManager::Initialize()
+{
+ winrt::guid MIDI_BLE_SERVICE_UUID{ MIDI_BLE_SERVICE };
+
+ BluetoothLEAdvertisementWatcher watcher;
+ watcher.AdvertisementFilter().Advertisement().ServiceUuids().Append(MIDI_BLE_SERVICE_UUID);
+
+
+ BluetoothLEAdvertisementFilter ad;
+
+ BluetoothLEAdvertisementBytePattern pattern;
+
+ // filter by our service UUID
+ pattern.DataType(BluetoothLEAdvertisementDataTypes::CompleteService128BitUuids());
+
+
+
+// ad.BytePatterns().Append();
+
+
+ return S_OK;
+}
+
+MidiBleDevice* MidiBleAdvertisedEndpointManager::GetDevice(_In_ std::wstring endpointDeviceInterfaceId)
+{
+
+ return nullptr;
+}
+
+HRESULT MidiBleAdvertisedEndpointManager::Cleanup()
+{
+
+ return S_OK;
+}
+
diff --git a/src/prototypes/ble-midi1-proto/MidiBleAdvertisedEndpointManager.h b/src/prototypes/ble-midi1-proto/MidiBleAdvertisedEndpointManager.h
new file mode 100644
index 00000000..e4f8e513
--- /dev/null
+++ b/src/prototypes/ble-midi1-proto/MidiBleAdvertisedEndpointManager.h
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+
+#pragma once
+
+class MidiBleAdvertisedEndpointManager
+{
+public:
+ HRESULT Initialize();
+
+ MidiBleDevice* GetDevice(_In_ std::wstring endpointDeviceInterfaceId);
+
+ HRESULT Cleanup();
+
+private:
+
+
+ void WatcherThreadWorker();
+
+ //enumeration::DeviceWatcher m_Watcher{ nullptr };
+ //bool m_enumerationCompleted{ false };
+
+ BluetoothLEAdvertisementWatcher m_adWatcher{ nullptr };
+
+ std::thread m_watcherThread{};
+
+ // map of SWD ids to BLE endpoint devices
+ std::map m_devices{};
+
+
+ //winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Added_revoker m_DeviceAdded;
+ //winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Removed_revoker m_DeviceRemoved;
+ //winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Updated_revoker m_DeviceUpdated;
+ //winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Stopped_revoker m_DeviceStopped;
+ //winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::EnumerationCompleted_revoker m_DeviceEnumerationCompleted;
+
+
+ //void OnDeviceAdded(_In_ enumeration::DeviceWatcher, _In_ enumeration::DeviceInformation const& info);
+ //void OnDeviceRemoved(_In_ enumeration::DeviceWatcher, _In_ enumeration::DeviceInformationUpdate const& upd);
+ //void OnDeviceUpdated(_In_ enumeration::DeviceWatcher, _In_ enumeration::DeviceInformationUpdate const& upd);
+
+ //void OnDeviceWatcherStopped(_In_ enumeration::DeviceWatcher, _In_ foundation::IInspectable);
+ //void OnEnumerationCompleted(_In_ enumeration::DeviceWatcher, _In_ foundation::IInspectable);
+
+};
diff --git a/src/api/Abstraction/BleMidiAbstraction/MidiBluetoothDevice.h b/src/prototypes/ble-midi1-proto/MidiBleBiDi.cpp
similarity index 68%
rename from src/api/Abstraction/BleMidiAbstraction/MidiBluetoothDevice.h
rename to src/prototypes/ble-midi1-proto/MidiBleBiDi.cpp
index 0212d595..12291b44 100644
--- a/src/api/Abstraction/BleMidiAbstraction/MidiBluetoothDevice.h
+++ b/src/prototypes/ble-midi1-proto/MidiBleBiDi.cpp
@@ -7,9 +7,25 @@
// ============================================================================
-#pragma once
-struct MidiBluetoothDevice
+#include "pch.h"
+
+_Use_decl_annotations_
+HRESULT MidiBleBiDi::Initialize(LPCWSTR /*endpointDeviceInterfaceId*/)
{
+ return S_OK;
+}
+
+
+
+
+
+
+HRESULT MidiBleBiDi::SendMidiMessage()
+{
+
+
+ return S_OK;
+}
+
-};
\ No newline at end of file
diff --git a/src/prototypes/ble-midi1-proto/MidiBleBiDi.h b/src/prototypes/ble-midi1-proto/MidiBleBiDi.h
new file mode 100644
index 00000000..47f63634
--- /dev/null
+++ b/src/prototypes/ble-midi1-proto/MidiBleBiDi.h
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#pragma once
+
+
+// class for communicating with the endpoint. Not for enumeration
+
+class MidiBleBiDi
+{
+public:
+ HRESULT Initialize(_In_ LPCWSTR endpointDeviceInterfaceId);
+
+ HRESULT SendMidiMessage();
+
+ // todo: Callback for receiving messages. We'll just display them for now.
+
+private:
+ std::unique_ptr m_device{ nullptr };
+
+
+};
\ No newline at end of file
diff --git a/src/prototypes/ble-midi1-proto/MidiBleDevice.cpp b/src/prototypes/ble-midi1-proto/MidiBleDevice.cpp
new file mode 100644
index 00000000..8c0af4e4
--- /dev/null
+++ b/src/prototypes/ble-midi1-proto/MidiBleDevice.cpp
@@ -0,0 +1,189 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+// NOTE: This is prototype code for working through some BLE approaches before
+// implementing it in the Windows service. Eventually, this code will go stale,
+// but we'll keep it around. This is not production code.
+
+#include "pch.h"
+
+
+_Use_decl_annotations_
+HRESULT MidiBleDevice::Initialize(
+ std::wstring const endpointDeviceInterfaceId,
+ std::wstring const bleDeviceId,
+ bool const supportsMidiOut,
+ bool const supportsMidiIn,
+ bool const supportsNotify)
+{
+ std::cout << __FUNCTION__ << std::endl;
+
+ m_endpointDeviceInterfaceId = endpointDeviceInterfaceId;
+ m_bleDeviceId = bleDeviceId;
+
+ m_supportsMidiOut = supportsMidiOut;
+ m_supportsMidiIn = supportsMidiIn;
+ m_supportsNotify = supportsNotify;
+
+ return S_OK;
+}
+
+
+
+
+HRESULT MidiBleDevice::Connect()
+{
+ std::cout << __FUNCTION__ << std::endl;
+
+ m_bleDevice = GetBleDeviceFromEnumerationDeviceId(m_bleDeviceId);
+
+ if (m_bleDevice != nullptr)
+ {
+ m_bleDevice.ConnectionStatusChanged({ this, &MidiBleDevice::OnConnectionStatusChanged });
+
+ //std::cout << "Bluetooth device connected: " << winrt::to_string(m_bleDevice.Name()) << std::endl;
+
+ // force connection by, as our docs say, calling an uncached service discovery method
+
+ auto service = GetBleMidiServiceFromDevice(m_bleDevice);
+
+ OnConnected();
+
+ return S_OK;
+ }
+
+ //std::cout << "Couldn't connect" << std::endl;
+
+ return E_FAIL;
+}
+
+
+HRESULT MidiBleDevice::Disconnect()
+{
+ std::cout << __FUNCTION__ << std::endl;
+
+ // terminate the session
+ if (m_bleDevice != nullptr && m_session != nullptr)
+ {
+ std::cout << "Disconnecting from " << winrt::to_string(m_bleDevice.Name()) << std::endl;
+
+ m_session.Close();
+ }
+
+ return S_OK;
+}
+
+void MidiBleDevice::OnDisconnected()
+{
+ std::cout << __FUNCTION__ << std::endl;
+
+}
+
+void MidiBleDevice::OnConnected()
+{
+ std::cout << __FUNCTION__ << std::endl;
+
+ m_midiService = GetBleMidiServiceFromDevice(m_bleDevice);
+
+ if (m_midiService != nullptr)
+ {
+ auto openStatus = m_midiService.OpenAsync(GattSharingMode::SharedReadAndWrite).get();
+
+ if (openStatus == GattOpenStatus::Success)
+ {
+ std::cout << "Service opened" << std::endl;
+
+ auto session = GattSession::FromDeviceIdAsync(m_bleDevice.BluetoothDeviceId()).get();
+
+ session.MaintainConnection(true);
+
+ m_session = std::move(session);
+ }
+ else if (openStatus == GattOpenStatus::AccessDenied)
+ {
+ std::cout << "Open returned AccessDenied" << std::endl;
+ }
+ else if (openStatus == GattOpenStatus::AlreadyOpened)
+ {
+ std::cout << "Open returned AlreadyOpened" << std::endl;
+ }
+ else if (openStatus == GattOpenStatus::NotFound)
+ {
+ std::cout << "Open returned NotFound" << std::endl;
+ }
+ else if (openStatus == GattOpenStatus::SharingViolation)
+ {
+ std::cout << "Open returned SharingViolation" << std::endl;
+ }
+ else if (openStatus == GattOpenStatus::Unspecified)
+ {
+ std::cout << "Open returned Unspecified" << std::endl;
+ }
+
+ m_midiDataIOCharacteristic = GetBleMidiDataIOCharacteristicFromService(m_midiService);
+
+ if (m_midiDataIOCharacteristic != nullptr)
+ {
+ m_midiDataIOCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
+ GattClientCharacteristicConfigurationDescriptorValue::Notify).get();
+
+
+ // todo: keep token to unwire this on disconnect
+ m_midiDataIOCharacteristic.ValueChanged({ this, &MidiBleDevice::OnValueChanged });
+
+ std::cout << "ValueChanged wired up" << std::endl;
+
+ return;
+ }
+ }
+ else
+ {
+ std::cout << "Service is nullptr" << std::endl;
+ }
+
+
+
+}
+
+
+_Use_decl_annotations_
+void MidiBleDevice::OnConnectionStatusChanged(BluetoothLEDevice const& sender, foundation::IInspectable const& /*args*/)
+{
+ std::cout << __FUNCTION__ << std::endl;
+
+ if (sender.ConnectionStatus() == BluetoothConnectionStatus::Connected)
+ {
+ OnConnected();
+ }
+ else
+ {
+ OnDisconnected();
+ }
+}
+
+
+
+
+
+_Use_decl_annotations_
+void MidiBleDevice::OnValueChanged(GattCharacteristic const& /*sender*/, GattValueChangedEventArgs const& args)
+{
+ std::cout << __FUNCTION__ << std::endl;
+
+ std::cout << "> ";
+
+ for (unsigned i = 0; i < args.CharacteristicValue().Length(); i++)
+ {
+ // read next byte
+ auto b = *(args.CharacteristicValue().data() + i);
+
+ std::cout << "0x" << std::hex << (unsigned)b << " ";
+ }
+
+ std::cout << std::endl;
+}
\ No newline at end of file
diff --git a/src/prototypes/ble-midi1-proto/MidiBleDevice.h b/src/prototypes/ble-midi1-proto/MidiBleDevice.h
new file mode 100644
index 00000000..9f13f33c
--- /dev/null
+++ b/src/prototypes/ble-midi1-proto/MidiBleDevice.h
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#pragma once
+
+
+// class for communicating with the endpoint. Not for enumeration
+
+class MidiBleDevice
+{
+public:
+ HRESULT Initialize(
+ _In_ std::wstring const endpointDeviceInterfaceId,
+ _In_ std::wstring const bleDeviceId,
+ _In_ bool const supportsMidiOut,
+ _In_ bool const supportsMidiIn,
+ _In_ bool const supportsNotify);
+
+ HRESULT Connect();
+
+ HRESULT Disconnect();
+
+private:
+
+ GattSession m_session{ nullptr };
+
+ std::wstring m_endpointDeviceInterfaceId{};
+ std::wstring m_bleDeviceId{};
+
+ BluetoothLEDevice m_bleDevice{ nullptr };
+ GattDeviceService m_midiService{ nullptr };
+ GattCharacteristic m_midiDataIOCharacteristic{ nullptr };
+
+ bool m_supportsMidiOut{ false };
+ bool m_supportsMidiIn{ false };
+ bool m_supportsNotify{ false };
+
+
+ void OnConnected();
+ void OnDisconnected();
+
+
+ void OnConnectionStatusChanged(_In_ BluetoothLEDevice const& sender, _In_ foundation::IInspectable const& args);
+ void OnValueChanged(_In_ GattCharacteristic const& sender, _In_ GattValueChangedEventArgs const& args);
+
+};
\ No newline at end of file
diff --git a/src/prototypes/ble-midi1-proto/MidiBleEndpointManager.cpp b/src/prototypes/ble-midi1-proto/MidiBleEndpointManager.cpp
new file mode 100644
index 00000000..5256747f
--- /dev/null
+++ b/src/prototypes/ble-midi1-proto/MidiBleEndpointManager.cpp
@@ -0,0 +1,250 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+
+
+#include "pch.h"
+
+
+HRESULT MidiBleEndpointManager::Initialize()
+{
+ winrt::hstring deviceSelector = BluetoothLEDevice::GetDeviceSelector();
+
+ auto requestedProperties = winrt::single_threaded_vector(
+ {
+ L"System.DeviceInterface.Bluetooth.DeviceAddress",
+ L"System.DeviceInterface.Bluetooth.Flags",
+ L"System.DeviceInterface.Bluetooth.LastConnectedTime",
+ L"System.DeviceInterface.Bluetooth.Manufacturer",
+ L"System.DeviceInterface.Bluetooth.ModelNumber",
+ L"System.DeviceInterface.Bluetooth.ProductId",
+ L"System.DeviceInterface.Bluetooth.ProductVersion",
+ L"System.DeviceInterface.Bluetooth.ServiceGuid",
+ L"System.DeviceInterface.Bluetooth.VendorId",
+ L"System.DeviceInterface.Bluetooth.VendorIdSource",
+ L"System.Devices.Connected",
+ L"System.Devices.DeviceCapabilities",
+ L"System.Devices.DeviceCharacteristics"
+ }
+ );
+
+ m_Watcher = enumeration::DeviceInformation::CreateWatcher(deviceSelector, requestedProperties);
+
+ auto deviceAddedHandler = foundation::TypedEventHandler(this, &MidiBleEndpointManager::OnDeviceAdded);
+ auto deviceRemovedHandler = foundation::TypedEventHandler(this, &MidiBleEndpointManager::OnDeviceRemoved);
+ auto deviceUpdatedHandler = foundation::TypedEventHandler(this, &MidiBleEndpointManager::OnDeviceUpdated);
+ auto deviceStoppedHandler = foundation::TypedEventHandler(this, &MidiBleEndpointManager::OnDeviceWatcherStopped);
+ auto deviceEnumerationCompletedHandler = foundation::TypedEventHandler(this, &MidiBleEndpointManager::OnEnumerationCompleted);
+
+ m_DeviceAdded = m_Watcher.Added(winrt::auto_revoke, deviceAddedHandler);
+ m_DeviceRemoved = m_Watcher.Removed(winrt::auto_revoke, deviceRemovedHandler);
+ m_DeviceUpdated = m_Watcher.Updated(winrt::auto_revoke, deviceUpdatedHandler);
+ m_DeviceStopped = m_Watcher.Stopped(winrt::auto_revoke, deviceStoppedHandler);
+ m_DeviceEnumerationCompleted = m_Watcher.EnumerationCompleted(winrt::auto_revoke, deviceEnumerationCompletedHandler);
+
+
+ std::thread watcherThread(&MidiBleEndpointManager::WatcherThreadWorker, this);
+
+ m_watcherThread = std::move(watcherThread);
+
+ m_watcherThread.detach();
+
+ return S_OK;
+}
+
+void MidiBleEndpointManager::WatcherThreadWorker()
+{
+ m_Watcher.Start();
+}
+
+
+HRESULT MidiBleEndpointManager::Cleanup()
+{
+ std::cout << "MidiBleEndpointManager::Cleanup" << std::endl;
+
+ // terminate background thread
+ m_Watcher.Stop();
+
+// if (m_watcherThread.joinable()) m_watcherThread.join();
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+MidiBleDevice* MidiBleEndpointManager::GetDevice(std::wstring endpointDeviceInterfaceId)
+{
+ // todo: clean the id
+ if (auto deviceIter = m_devices.find(endpointDeviceInterfaceId); deviceIter != m_devices.end())
+ {
+ return &(deviceIter->second);
+ }
+
+ return nullptr;
+}
+
+
+
+
+
+
+
+
+_Use_decl_annotations_
+void MidiBleEndpointManager::OnDeviceAdded(enumeration::DeviceWatcher, enumeration::DeviceInformation const& info)
+{
+ std::hash hasher;
+ std::wstring hash;
+
+ std::cout << "Added: " << winrt::to_string(info.Id()) << std::endl;
+ std::cout << "Name: " << winrt::to_string(info.Name()) << std::endl;
+
+ // get the BLE device from this
+
+ auto bleDevice = BluetoothLEDevice::FromIdAsync(info.Id()).get();
+
+ if (bleDevice != nullptr)
+ {
+ std::cout << "BLE Name: " << winrt::to_string(bleDevice.Name()) << std::endl;
+
+ if (bleDevice.ConnectionStatus() == BluetoothConnectionStatus::Connected)
+ {
+ std::cout << "Current status: Connected " << std::endl;
+ }
+ else
+ {
+ std::cout << "Current status: Disconnected " << std::endl;
+ }
+
+ auto midiService = GetBleMidiServiceFromDevice(bleDevice);
+
+ if (midiService != nullptr)
+ {
+ auto characteristic = GetBleMidiDataIOCharacteristicFromService(midiService);
+
+ if (characteristic != nullptr)
+ {
+ auto properties = characteristic.CharacteristicProperties();
+
+ bool supportsMidiIn{ false };
+ bool supportsMidiOut{ false };
+ bool supportsNotify{ false };
+
+ if ((properties & GattCharacteristicProperties::Read) == GattCharacteristicProperties::Read)
+ {
+ // includes input port
+ std::cout << "- Supports READ (input port)" << std::endl;
+
+ supportsMidiIn = true;
+ }
+
+ if ((properties & GattCharacteristicProperties::Write) == GattCharacteristicProperties::Write)
+ {
+ // includes output port
+ std::cout << "- Supports WRITE (output port)" << std::endl;
+
+ supportsMidiOut = true;
+ }
+
+ if ((properties & GattCharacteristicProperties::Notify) == GattCharacteristicProperties::Notify)
+ {
+ std::cout << "- Supports NOTIFY" << std::endl;
+
+ supportsNotify = true;
+ }
+
+ // lock the map and create and store the device
+
+ MidiBleDevice endpointDevice;
+
+ // we'll create a real id in the actual implementation
+ // todo: clean the id
+
+
+ hash = std::to_wstring(hasher(std::wstring{ bleDevice.DeviceId() }));
+
+ std::wstring endpointDeviceId = std::wstring{ L"\\\\?\\SWD&MIDIU_BLE_" } + hash;
+
+ endpointDevice.Initialize(
+ endpointDeviceId,
+ bleDevice.DeviceId().c_str(),
+ supportsMidiOut,
+ supportsMidiIn,
+ supportsNotify);
+
+ m_devices.emplace(endpointDeviceId, endpointDevice);
+
+ std::wcout << L"Added device: " << endpointDeviceId << std::endl;
+ }
+ }
+
+ }
+
+ std::cout << "------------------------" << std::endl;
+}
+
+_Use_decl_annotations_
+void MidiBleEndpointManager::OnDeviceRemoved(enumeration::DeviceWatcher, enumeration::DeviceInformationUpdate const& upd)
+{
+ std::cout << "Removed: " << winrt::to_string(upd.Id()) << std::endl;
+}
+
+_Use_decl_annotations_
+void MidiBleEndpointManager::OnDeviceUpdated(enumeration::DeviceWatcher, enumeration::DeviceInformationUpdate const& upd)
+{
+ std::cout << "-----------" << std::endl;
+
+ std::cout << "Updated: " << winrt::to_string(upd.Id()).c_str() << std::endl;
+
+ auto bleDevice = BluetoothLEDevice::FromIdAsync(upd.Id()).get();
+
+ if (bleDevice != nullptr)
+ {
+ std::cout << "Name: " << winrt::to_string(bleDevice.Name()) << " : ";
+
+ if (bleDevice.ConnectionStatus() == BluetoothConnectionStatus::Connected)
+ {
+ std::cout << "Connected " << std::endl;
+ }
+ else
+ {
+ std::cout << "Disconnected " << std::endl;
+ }
+ }
+
+ // spit out the updated properties
+ for (auto prop : upd.Properties())
+ {
+ std::cout << winrt::to_string(prop.Key()) << std::endl;
+
+ if (upd.Id() == bleDevice.DeviceId())
+ {
+ if (prop.Key() == L"System.DeviceInterface.Bluetooth.Flags")
+ {
+ // TODO: get the property value and decipher
+
+
+ }
+ }
+
+ }
+
+ std::cout << "-----------" << std::endl;
+}
+
+_Use_decl_annotations_
+void MidiBleEndpointManager::OnDeviceWatcherStopped(enumeration::DeviceWatcher, foundation::IInspectable)
+{
+ std::cout << "Device watcher stopped" << std::endl;
+}
+
+_Use_decl_annotations_
+void MidiBleEndpointManager::OnEnumerationCompleted(enumeration::DeviceWatcher, foundation::IInspectable)
+{
+ std::cout << "Enumeration completed" << std::endl;
+
+}
\ No newline at end of file
diff --git a/src/prototypes/ble-midi1-proto/MidiBleEndpointManager.h b/src/prototypes/ble-midi1-proto/MidiBleEndpointManager.h
new file mode 100644
index 00000000..71b491c0
--- /dev/null
+++ b/src/prototypes/ble-midi1-proto/MidiBleEndpointManager.h
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#pragma once
+
+class MidiBleEndpointManager
+{
+public:
+ HRESULT Initialize();
+
+ MidiBleDevice* GetDevice(_In_ std::wstring endpointDeviceInterfaceId);
+
+ HRESULT Cleanup();
+
+private:
+
+
+ void WatcherThreadWorker();
+
+ enumeration::DeviceWatcher m_Watcher{ nullptr };
+ bool m_enumerationCompleted{ false };
+
+
+ std::thread m_watcherThread{};
+
+ // map of SWD ids to BLE endpoint devices
+ std::map m_devices{};
+
+
+ winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Added_revoker m_DeviceAdded;
+ winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Removed_revoker m_DeviceRemoved;
+ winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Updated_revoker m_DeviceUpdated;
+ winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Stopped_revoker m_DeviceStopped;
+ winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::EnumerationCompleted_revoker m_DeviceEnumerationCompleted;
+
+
+ void OnDeviceAdded(_In_ enumeration::DeviceWatcher, _In_ enumeration::DeviceInformation const& info);
+ void OnDeviceRemoved(_In_ enumeration::DeviceWatcher, _In_ enumeration::DeviceInformationUpdate const& upd);
+ void OnDeviceUpdated(_In_ enumeration::DeviceWatcher, _In_ enumeration::DeviceInformationUpdate const& upd);
+
+ void OnDeviceWatcherStopped(_In_ enumeration::DeviceWatcher, _In_ foundation::IInspectable);
+ void OnEnumerationCompleted(_In_ enumeration::DeviceWatcher, _In_ foundation::IInspectable);
+
+};
diff --git a/src/prototypes/ble-midi1-proto/abstraction_defs.h b/src/prototypes/ble-midi1-proto/abstraction_defs.h
new file mode 100644
index 00000000..db2ebadb
--- /dev/null
+++ b/src/prototypes/ble-midi1-proto/abstraction_defs.h
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#pragma once
+
+
+#ifndef ABSTRACTION_DEFS_H
+#define ABSTRACTION_DEFS_H
+
+#define MIDI_BLE_SERVICE L"{03B80E5A-EDE8-4B33-A751-6CE34EC4C700}"
+#define MIDI_BLE_DATA_IO_CHARACTERISTIC L"{7772E5DB-3868-4112-A1A9-F2669D106BF3}"
+#define MIDI_BLE_COMPANY_CODE_MICROSOFT 0xFE08
+
+// Notes:
+// Write (encryption recommended, write without response is required)
+// Read (encryption recommended, respond with no payload)
+// Notify (encryption recommended)
+// Max connection interval is 15ms. Lower is better.
+
+#endif
\ No newline at end of file
diff --git a/src/prototypes/ble-midi1-proto/ble-midi1-proto.vcxproj b/src/prototypes/ble-midi1-proto/ble-midi1-proto.vcxproj
index d0c8e495..c659ec6c 100644
--- a/src/prototypes/ble-midi1-proto/ble-midi1-proto.vcxproj
+++ b/src/prototypes/ble-midi1-proto/ble-midi1-proto.vcxproj
@@ -103,10 +103,20 @@
+
+
+
+
+
+
+
+
+
+
Create
@@ -115,6 +125,9 @@
+
+
+
diff --git a/src/prototypes/ble-midi1-proto/ble_utilities.h b/src/prototypes/ble-midi1-proto/ble_utilities.h
new file mode 100644
index 00000000..a80dda70
--- /dev/null
+++ b/src/prototypes/ble-midi1-proto/ble_utilities.h
@@ -0,0 +1,59 @@
+#pragma once
+
+inline BluetoothLEDevice GetBleDeviceFromEnumerationDeviceId(_In_ std::wstring deviceId)
+{
+// std::cout << __FUNCTION__ << std::endl;
+
+ auto device = BluetoothLEDevice::FromIdAsync(winrt::to_hstring(deviceId.c_str())).get();
+
+ return device;
+}
+
+
+inline GattDeviceService GetBleMidiServiceFromDevice(_In_ BluetoothLEDevice bleDevice)
+{
+// std::cout << __FUNCTION__ << std::endl;
+
+ winrt::guid MIDI_BLE_SERVICE_UUID{ MIDI_BLE_SERVICE };
+
+ auto gattServicesResult = bleDevice.GetGattServicesAsync(BluetoothCacheMode::Uncached).get();
+ if (gattServicesResult.Status() == GattCommunicationStatus::Success)
+ {
+ auto services = gattServicesResult.Services();
+
+ // find the MIDI BLE service
+ if (auto it = std::find_if(services.begin(), services.end(), [&](GattDeviceService const& x) { return x.Uuid() == MIDI_BLE_SERVICE_UUID; }); it != services.end())
+ {
+ // just grab the first one. Shouldn't be more than one match.
+ auto midiService = *it;
+
+ return midiService;
+ }
+ }
+
+ return nullptr;
+}
+
+
+
+inline GattCharacteristic GetBleMidiDataIOCharacteristicFromService(_In_ GattDeviceService midiService)
+{
+// std::cout << __FUNCTION__ << std::endl;
+
+ winrt::guid MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID{ MIDI_BLE_DATA_IO_CHARACTERISTIC };
+
+ if (midiService != nullptr)
+ {
+ auto characteristicsResult = midiService.GetCharacteristicsForUuidAsync(MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID).get();
+
+ if (characteristicsResult.Status() == GattCommunicationStatus::Success && characteristicsResult.Characteristics().Size() > 0)
+ {
+ // we're just getting the first one
+ auto characteristic = characteristicsResult.Characteristics().GetAt(0);
+
+ return characteristic;
+ }
+ }
+
+ return nullptr;
+}
diff --git a/src/prototypes/ble-midi1-proto/main.cpp b/src/prototypes/ble-midi1-proto/main.cpp
index 1636b377..fef3f34c 100644
--- a/src/prototypes/ble-midi1-proto/main.cpp
+++ b/src/prototypes/ble-midi1-proto/main.cpp
@@ -19,406 +19,342 @@ using namespace Windows::Foundation;
// https://learn.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client
+struct MidiBleDeviceEntry
+{
+ uint64_t BluetoothAddress{};
-#define MIDI_BLE_SERVICE L"{03B80E5A-EDE8-4B33-A751-6CE34EC4C700}"
-#define MIDI_BLE_DATA_IO_CHARACTERISTIC L"{7772E5DB-3868-4112-A1A9-F2669D106BF3}"
-
-winrt::guid MIDI_BLE_SERVICE_UUID(MIDI_BLE_SERVICE);
-winrt::guid MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID(MIDI_BLE_DATA_IO_CHARACTERISTIC);
-
-// Notes:
-// Write (encryption recommended, write without response is required)
-// Read (encryption recommended, respond with no payload)
-// Notify (encryption recommended)
-// Max connection interval is 15ms. Lower is better.
-
-
-enumeration::DeviceWatcher m_Watcher{ nullptr };
-bool m_enumerationCompleted{ false };
-
-winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Added_revoker m_DeviceAdded;
-winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Removed_revoker m_DeviceRemoved;
-winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Updated_revoker m_DeviceUpdated;
-winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Stopped_revoker m_DeviceStopped;
-winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::EnumerationCompleted_revoker m_DeviceEnumerationCompleted;
+ BluetoothLEDevice Device{ nullptr };
+ GattDeviceService Service{ nullptr };
+};
+std::vector m_foundMidiDevices;
-void OnDeviceAdded(enumeration::DeviceWatcher, enumeration::DeviceInformation info)
+void TestFindingDevices()
{
- std::cout << "Added: " << winrt::to_string(info.Id()) << std::endl;
- std::cout << "Name: " << winrt::to_string(info.Name()) << std::endl;
-
+// GattCharacteristic characteristic{ nullptr };
- // get the BLE device from this
+ winrt::guid MIDI_BLE_SERVICE_UUID{ MIDI_BLE_SERVICE };
- auto bleDevice = bt::BluetoothLEDevice::FromIdAsync(info.Id()).get();
-
- if (bleDevice != nullptr)
- {
- std::cout << "BLE Name: " << winrt::to_string(bleDevice.Name()) << std::endl;
+ BluetoothLEAdvertisementWatcher watcher;
- if (bleDevice.ConnectionStatus() == bt::BluetoothConnectionStatus::Connected)
+ watcher.Received([&](BluetoothLEAdvertisementWatcher /*source*/, BluetoothLEAdvertisementReceivedEventArgs const& args)
{
- std::cout << "Current status: Connected " << std::endl;
- }
- else
- {
- std::cout << "Current status: Disconnected " << std::endl;
- }
-
-
+ auto device = BluetoothLEDevice::FromBluetoothAddressAsync(args.BluetoothAddress()).get();
- auto gattServicesResult = bleDevice.GetGattServicesAsync().get();
-
- if (gattServicesResult.Status() == gatt::GattCommunicationStatus::Success)
- {
- for (auto service : gattServicesResult.Services())
+ if (device != nullptr)
{
- auto serviceUuid = service.Uuid();
+ auto service = GetBleMidiServiceFromDevice(device);
- if (serviceUuid == MIDI_BLE_SERVICE_UUID)
+ if (service != nullptr)
{
- std::cout << "MIDI Service Found: " << winrt::to_string(winrt::to_hstring(serviceUuid)) << std::endl;
-
- auto characteristics = service.GetAllCharacteristics();
-
- for (auto characteristic : characteristics)
+ // if not found, add it
+ if (std::find_if(m_foundMidiDevices.begin(), m_foundMidiDevices.end(), [&](const MidiBleDeviceEntry& dev) { return device.BluetoothAddress() == dev.BluetoothAddress; }) == m_foundMidiDevices.end())
{
- auto uuid = characteristic.Uuid();
- //auto descriptorResult = characteristic.GetDescriptorsForUuidAsync(uuid).get();
-
- //for (auto desc : descriptorResult.Descriptors())
- //{
- // desc.
- //}
-
- if (uuid == MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID)
- {
- std::cout << "MIDI Data IO Characteristic Found: " << winrt::to_string(winrt::to_hstring(uuid)) << std::endl;
-
- auto properties = characteristic.CharacteristicProperties();
-
- if ((properties & gatt::GattCharacteristicProperties::Read) == gatt::GattCharacteristicProperties::Read)
+ device.ConnectionStatusChanged([&](BluetoothLEDevice connectionSource, IInspectable /*args*/)
{
- // includes input port
- std::cout << "- Supports READ (input port)" << std::endl;
- }
-
- if ((properties & gatt::GattCharacteristicProperties::Write) == gatt::GattCharacteristicProperties::Write)
- {
- // includes output port
- std::cout << "- Supports WRITE (output port)" << std::endl;
- }
-
- if ((properties & gatt::GattCharacteristicProperties::Notify) == gatt::GattCharacteristicProperties::Notify)
- {
- std::cout << "- Supports NOTIFY" << std::endl;
- }
-
- }
-
-
+ if (connectionSource.ConnectionStatus() == BluetoothConnectionStatus::Connected)
+ {
+ std::cout << "Connection status changed for " << winrt::to_string(connectionSource.Name()) << ", new status: Connected" << std::endl;
+ }
+ else
+ {
+ std::cout << "Connection status changed for " << winrt::to_string(connectionSource.Name()) << ", new status: Disconnected" << std::endl;
+ }
+ });
+
+ std::cout
+ << "MIDI Device Found" << std::endl
+ << " - Name: " << winrt::to_string(device.Name()) << std::endl
+ << " - Timestamp: " << args.Timestamp().time_since_epoch().count() << std::endl
+ << " - Address: " << args.BluetoothAddress() << std::endl
+ << " - IsConnectable: " << args.IsConnectable() << std::endl
+ << " - IsAnonymous: " << args.IsAnonymous() << std::endl
+ << " - IsDirected: " << args.IsDirected() << std::endl
+ << " - IsScannable: " << args.IsScannable() << std::endl
+ << " - RawSignalStrengthInDBm: " << args.RawSignalStrengthInDBm() << std::endl;
+
+
+ MidiBleDeviceEntry entry;
+ entry.BluetoothAddress = device.BluetoothAddress();
+ entry.Device = std::move(device);
+ entry.Service = std::move(service);
+
+ m_foundMidiDevices.push_back(std::move(entry));
}
-
- // no need to continue looking
- break;
}
+ //else
+ //{
+ // std::cout << "Not a BLE MIDI Device" << std::endl;
+ //}
}
}
- else
- {
- std::cout << "Unable to communicate with device to get gatt services" << std::endl;
- }
+ );
- std::cout << "------------------------" << std::endl;
- }
+ watcher.AdvertisementFilter().Advertisement().ServiceUuids().Append(MIDI_BLE_SERVICE_UUID);
-}
+ std::cout << "Starting watcher..." << std::endl;
-void OnDeviceRemoved(enumeration::DeviceWatcher, enumeration::DeviceInformationUpdate upd)
-{
- std::cout << "Removed: " << winrt::to_string(upd.Id()) << std::endl;
+ watcher.Start();
+
+ system("pause > nul");
}
-void OnDeviceUpdated(enumeration::DeviceWatcher, enumeration::DeviceInformationUpdate upd)
+void ReportAdapterCapabilities()
{
- std::cout << "-----------" << std::endl;
-
- std::cout << "Updated: " << winrt::to_string(upd.Id()).c_str() << std::endl;
+ auto adapter = BluetoothAdapter::GetDefaultAsync().get();
- auto bleDevice = bt::BluetoothLEDevice::FromIdAsync(upd.Id()).get();
-
- if (bleDevice != nullptr)
+ if (adapter != nullptr)
{
- std::cout << "Name: " << winrt::to_string(bleDevice.Name()) << " : ";
-
- if (bleDevice.ConnectionStatus() == bt::BluetoothConnectionStatus::Connected)
- {
- std::cout << "Connected " << std::endl;
- }
- else
- {
- std::cout << "Disconnected " << std::endl;
- }
+ std::cout << "IsCentralRoleSupported : " << adapter.IsCentralRoleSupported() << std::endl;
+ std::cout << "IsPeripheralRoleSupported : " << adapter.IsPeripheralRoleSupported() << std::endl;
+ std::cout << "IsClassicSupported : " << adapter.IsClassicSupported() << std::endl;
+ std::cout << "IsLowEnergySupported : " << adapter.IsLowEnergySupported() << std::endl;
+ std::cout << "IsExtendedAdvertisingSupported : " << adapter.IsExtendedAdvertisingSupported() << std::endl;
+ std::cout << "IsAdvertisementOffloadSupported : " << adapter.IsAdvertisementOffloadSupported() << std::endl;
+ std::cout << "AreClassicSecureConnectionsSupported : " << adapter.AreClassicSecureConnectionsSupported() << std::endl;
+ std::cout << "AreLowEnergySecureConnectionsSupported : " << adapter.AreLowEnergySecureConnectionsSupported() << std::endl;
}
-
- // spit out the updated properties
- for (auto prop : upd.Properties())
+ else
{
- std::cout << winrt::to_string(prop.Key()) << std::endl;
+ std::cout << "No BT adapter present" << std::endl;
}
- std::cout << "-----------" << std::endl;
-}
-
-void OnDeviceStopped(enumeration::DeviceWatcher, foundation::IInspectable)
-{
-
+ std::cout << "------------------------------------------" << std::endl;
}
-void OnEnumerationCompleted(enumeration::DeviceWatcher, foundation::IInspectable)
-{
- m_enumerationCompleted = true;
-}
-void TestEnumeration()
-{
- // enumerate ble MIDI devices
- winrt::hstring deviceSelector = bt::BluetoothLEDevice::GetDeviceSelector();
+GattServiceProvider m_provider{ nullptr };
+winrt::event_token Revoke_AdvertisementStatusChanged;
+winrt::event_token Revoke_GattReadRequest;
+winrt::event_token Revoke_GattWriteRequest;
+GattLocalCharacteristic m_dataIOCharacteristic{ nullptr };
- auto requestedProperties = winrt::single_threaded_vector(
- {
- L"System.DeviceInterface.Bluetooth.DeviceAddress",
- L"System.DeviceInterface.Bluetooth.Flags",
- L"System.DeviceInterface.Bluetooth.LastConnectedTime",
- L"System.DeviceInterface.Bluetooth.Manufacturer",
- L"System.DeviceInterface.Bluetooth.ModelNumber",
- L"System.DeviceInterface.Bluetooth.ProductId",
- L"System.DeviceInterface.Bluetooth.ProductVersion",
- L"System.DeviceInterface.Bluetooth.ServiceGuid",
- L"System.DeviceInterface.Bluetooth.VendorId",
- L"System.DeviceInterface.Bluetooth.VendorIdSource",
- L"System.Devices.Connected",
- L"System.Devices.DeviceCapabilities",
- L"System.Devices.DeviceCharacteristics"
- }
- );
-
+void OnDataIOReadRequested(GattLocalCharacteristic const& /*source*/, GattReadRequestedEventArgs const& args)
+{
+ //std::cout << __FUNCTION__ << std::endl;
- m_Watcher = enumeration::DeviceInformation::CreateWatcher(deviceSelector, requestedProperties);
+ auto request = args.GetRequestAsync().get();
- auto deviceAddedHandler = foundation::TypedEventHandler(OnDeviceAdded);
- auto deviceRemovedHandler = foundation::TypedEventHandler(OnDeviceRemoved);
- auto deviceUpdatedHandler = foundation::TypedEventHandler(OnDeviceUpdated);
- auto deviceStoppedHandler = foundation::TypedEventHandler(OnDeviceStopped);
- auto deviceEnumerationCompletedHandler = foundation::TypedEventHandler(OnEnumerationCompleted);
+ args.Session().MaintainConnection(true);
- m_DeviceAdded = m_Watcher.Added(winrt::auto_revoke, deviceAddedHandler);
- m_DeviceRemoved = m_Watcher.Removed(winrt::auto_revoke, deviceRemovedHandler);
- m_DeviceUpdated = m_Watcher.Updated(winrt::auto_revoke, deviceUpdatedHandler);
- m_DeviceStopped = m_Watcher.Stopped(winrt::auto_revoke, deviceStoppedHandler);
- m_DeviceEnumerationCompleted = m_Watcher.EnumerationCompleted(winrt::auto_revoke, deviceEnumerationCompletedHandler);
+ std::cout << "Read Request: " << winrt::to_string(args.Session().DeviceId().Id())
+ << ", length=" << request.Length()
+ << std::endl;
- // this blocks
- m_Watcher.Start();
+ // should respond with zero only the first time
+ streams::DataWriter writer;
+ writer.WriteByte(0);
+ request.RespondWithValue(writer.DetachBuffer());
}
-
-IAsyncAction TestReceivingData()
+void ProcessIncomingBuffer(streams::IBuffer buffer)
{
- std::cout << "Test Receiving Data ---------------------------------------------------------" << std::endl;
-
- winrt::hstring id = L"BluetoothLE#BluetoothLE3c:6a:a7:f0:4e:b0-48:b6:20:1a:71:9d";
-
- try
- {
- gatt::GattSession session{ nullptr };
-
- std::cout << "Creating session" << std::endl;
+ auto reader = streams::DataReader::FromBuffer(buffer);
- auto bleId = bt::BluetoothDeviceId::FromId(id);
+ reader.ByteOrder(streams::ByteOrder::LittleEndian);
- session = co_await gatt::GattSession::FromDeviceIdAsync(bleId);
+ // TODO: Process the data. We'll just display for now
- std::cout << "Session created" << std::endl;
+ // Read header bytes
+ auto headerByte = reader.ReadByte();
- session.MaintainConnection(true);
-
- auto bleDevice = bt::BluetoothLEDevice::FromIdAsync(id).get();
+ if (headerByte & 0x80)
+ {
+ // valid header byte. Get the rest of the info
+ auto timestampByte = reader.ReadByte();
- if (bleDevice != nullptr)
+ if (timestampByte & 0x80)
{
- std::cout << "Using device " << winrt::to_string(bleDevice.Name()) << std::endl;
-
- auto service = bleDevice.GetGattService(MIDI_BLE_SERVICE_UUID);
-
- if (service != nullptr)
+ // timestamp high is bits 5-0 of header (6 bits)
+ uint16_t timestampHigh = headerByte & 0x3F;
+
+ // timestamp low is bites 6-0 of timestamp byte (7 bits)
+ uint16_t timestampLow = timestampByte & 0x7F;
+
+ uint16_t fullTimestamp = timestampLow | (timestampHigh << 7);
+
+ std::cout
+ << "TS:"
+ << std::setw(5) << std::setfill('0')
+ << std::dec
+ << fullTimestamp
+ << std::nouppercase
+ << " Data: ";
+
+ // now read the message data
+ // In the real impl, this needs to account for
+ // timestamp (ts low) embedded in here between
+ // messages, as well as things like running
+ // status messages. This is just quick and dirty.
+ while (reader.UnconsumedBufferLength() > 0)
{
- std::cout << "Found service" << std::endl;;
-
- auto openStatus = co_await service.OpenAsync(gatt::GattSharingMode::SharedReadAndWrite);
-
- if (openStatus == gatt::GattOpenStatus::Success)
- {
- std::cout << "Service opened" << std::endl;
- }
- else
- {
- std::cout << "Unable to open service" << std::endl;
- co_return;
- }
-
- std::cout << "Getting MIDI Data IO characteristic" << std::endl;
-
- auto characteristicsResult = co_await service.GetCharacteristicsForUuidAsync(MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID);
-
- if (characteristicsResult.Status() == gatt::GattCommunicationStatus::Success)
- {
- if (characteristicsResult.Characteristics().Size() == 0)
- {
- // this is unexpected
- std::cout << "Returned no characteristics for the required UUID" << std::endl;
- }
- else if (characteristicsResult.Characteristics().Size() > 1)
- {
- // TODO: Need to find out under which cases this happens
- std::cout << "Returned more than one characteristic for the same UUID" << std::endl;
- }
- else
- {
- std::cout << "Returned just the one characteristic for the UUID" << std::endl;
-
- auto characteristic = characteristicsResult.Characteristics().GetAt(0);
-
- co_await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
- gatt::GattClientCharacteristicConfigurationDescriptorValue::Notify);
-
-
- auto valueChangedHandler = [&](gatt::GattCharacteristic const& sender, gatt::GattValueChangedEventArgs const& args)
- {
- //std::cout << "Value changed" << std::endl;
-
- // we get an IBuffer here
-
- //std::cout << "Data size is " << args.CharacteristicValue().Length() << " bytes" << std::endl;
-
- std::cout << "> ";
-
- for (int i = 0; i < args.CharacteristicValue().Length(); i++)
- {
- // read next byte
- auto b = *(args.CharacteristicValue().data() + i);
-
- std::cout << "0x" << std::hex << (unsigned)b << " ";
- }
-
- std::cout << std::endl;
- };
-
- // wire up ValueChanged so we actually get data
- characteristic.ValueChanged(valueChangedHandler);
-
- while (true)
- {
- ::Sleep(100);
- }
-
- session.Close();
+ std::cout
+ << std::hex << std::setw(2) << std::setfill('0') << std::noshowbase
+ << std::uppercase
+ << (unsigned)reader.ReadByte()
+ << std::nouppercase
+ << " ";
+ }
- }
- }
+ std::cout << std::endl;
+ }
+ else
+ {
+ std::cout << "Invalid timestamp byte" << std::endl;
+ }
+ }
+ else
+ {
+ std::cout << "Invalid data" << std::endl;
+ }
+}
- service.Close();
- }
- }
+void OnDataIOWriteRequested(GattLocalCharacteristic const& /*source*/, GattWriteRequestedEventArgs const& args)
+{
+ //std::cout << __FUNCTION__ << std::endl;
- std::cout << "Done" << std::endl;
+ auto request = args.GetRequestAsync().get();
+ //std::cout << "Write Request: " << winrt::to_string(args.Session().DeviceId().Id())
+ // << ", offset=" << request.Offset()
+ // << std::endl;
- // wait for incoming data indefinitely
+ ProcessIncomingBuffer(request.Value());
- //auto characteristic = characteristicsResult.Characteristics().GetAt(0);
+}
- //auto readResult = characteristic.ReadValueAsync().get();
+void OnDataIOSubscribedClientsChanged(GattLocalCharacteristic const& /*source*/, foundation::IInspectable const& /*args*/)
+{
+ std::cout << __FUNCTION__ << std::endl;
- //if (readResult.Status() == gatt::GattCommunicationStatus::Success)
- //{
- // // we get an IBuffer here
+}
- // for (int i = 0; i < readResult.Value().Length(); i++)
- // {
- // // read next byte
- // auto b = *(readResult.Value().data() + i);
- // std::cout << std::hex << b << " ";
- // }
+void OnGattServiceProviderAdvertisementStatusChanged(GattServiceProvider const& /*source*/, GattServiceProviderAdvertisementStatusChangedEventArgs const& args)
+{
+// std::cout << __FUNCTION__ << std::endl;
- // std::cout << std::endl;
+ std::cout << "Provider ad status: ";
- //}
- //session.Close();
+ switch (args.Status())
+ {
+ case GattServiceProviderAdvertisementStatus::StartedWithoutAllAdvertisementData:
+ std::cout << "Started without all ad data";
+ break;
+ case GattServiceProviderAdvertisementStatus::Started:
+ std::cout << "Started";
+ break;
+ case GattServiceProviderAdvertisementStatus::Created:
+ std::cout << "Created";
+ break;
+ case GattServiceProviderAdvertisementStatus::Stopped:
+ std::cout << "Stopped";
+ break;
+ }
+ std::cout << std::endl;
+}
+void StartAsPeripheral()
+{
+ winrt::guid MIDI_BLE_SERVICE_UUID{ MIDI_BLE_SERVICE };
+ winrt::guid MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID{ MIDI_BLE_DATA_IO_CHARACTERISTIC };
+ auto result = GattServiceProvider::CreateAsync(MIDI_BLE_SERVICE_UUID).get();
+ if (result.Error() == BluetoothError::Success)
+ {
+ m_provider = result.ServiceProvider();
+
+ Revoke_AdvertisementStatusChanged = m_provider.AdvertisementStatusChanged(OnGattServiceProviderAdvertisementStatusChanged);
+
+ GattServiceProviderAdvertisingParameters params;
+ params.IsConnectable(true);
+ params.IsDiscoverable(true);
+
+ // BLE MIDI 1.0 requires WriteWithoutResponse
+ GattCharacteristicProperties dataIOProperties =
+ GattCharacteristicProperties::WriteWithoutResponse |
+ GattCharacteristicProperties::Read |
+ GattCharacteristicProperties::Notify;
+
+ // BLE MIDI 1.0 supports encryption, and recommends it, but it isn't required.
+ // We'll have this as a user setting later so the user can decide if, when the
+ // PC is acting as a peripheral, encryption and/or auth is required.
+ GattLocalCharacteristicParameters dataIOParameters;
+ dataIOParameters.ReadProtectionLevel(GattProtectionLevel::Plain);
+ dataIOParameters.WriteProtectionLevel(GattProtectionLevel::Plain); // same
+ dataIOParameters.UserDescription(L"MIDI Prototype");
+ dataIOParameters.CharacteristicProperties(dataIOProperties);
+
+ auto characteristicResult = m_provider.Service().CreateCharacteristicAsync(
+ MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID,
+ dataIOParameters
+ ).get();
+
+ if (characteristicResult.Error() == BluetoothError::Success)
+ {
+ m_dataIOCharacteristic = characteristicResult.Characteristic();
+ Revoke_GattReadRequest = m_dataIOCharacteristic.ReadRequested(OnDataIOReadRequested);
+ Revoke_GattWriteRequest = m_dataIOCharacteristic.WriteRequested(OnDataIOWriteRequested);
+ m_dataIOCharacteristic.SubscribedClientsChanged(OnDataIOSubscribedClientsChanged);
- }
- catch (winrt::hresult_error err)
- {
- // Important: using .get() means we don't actually get to handle these exceptions.
+ // m_dataIOCharacteristic.StaticValue();
- if (err.code() == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION))
- {
- std::cout << "Cannot access the BLE device because it's being used by another process" << std::endl;
+ std::cout << "Start Advertising..." << std::endl;
+ m_provider.StartAdvertising(params);
}
else
{
- std::cout << "HRESULT exception 0x" << std::hex << err.code() << std::endl;
+ std::cout << "Failed to create characteristic" << std::endl;
}
-
- co_return;
}
- catch (...)
+}
+
+void StopPeripheral()
+{
+ std::cout << "Stopping peripheral..." << std::endl;
+
+ if (m_provider != nullptr)
{
- std::cout << "Exception" << std::endl;
+ m_provider.StopAdvertising();
- co_return;
- }
+ // clean up event handler
+ m_provider.AdvertisementStatusChanged(Revoke_AdvertisementStatusChanged);
+
+ m_dataIOCharacteristic.ReadRequested(Revoke_GattReadRequest);
+ m_dataIOCharacteristic.WriteRequested(Revoke_GattWriteRequest);
-}
+ m_provider == nullptr;
+ }
+}
int main()
{
init_apartment();
- //TestEnumeration();
- std::thread enumerationThread(TestEnumeration);
+ ReportAdapterCapabilities();
- enumerationThread.detach();
+ TestFindingDevices();
+ //StartAsPeripheral();
- while (!m_enumerationCompleted)
- {
- Sleep(1000);
- }
+ //system("pause");
- TestReceivingData().get();
+ //StopPeripheral();
- //system("pause > nul");
system("pause");
-
-
}
diff --git a/src/prototypes/ble-midi1-proto/pch.cpp b/src/prototypes/ble-midi1-proto/pch.cpp
index bcb5590b..6c31ef31 100644
--- a/src/prototypes/ble-midi1-proto/pch.cpp
+++ b/src/prototypes/ble-midi1-proto/pch.cpp
@@ -1 +1,9 @@
-#include "pch.h"
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#include "pch.h"
diff --git a/src/prototypes/ble-midi1-proto/pch.h b/src/prototypes/ble-midi1-proto/pch.h
index 7854996e..cb645ba5 100644
--- a/src/prototypes/ble-midi1-proto/pch.h
+++ b/src/prototypes/ble-midi1-proto/pch.h
@@ -15,6 +15,8 @@
#include
#include
#include
+#include
+#include
#include
#include
@@ -26,8 +28,20 @@ namespace json = ::winrt::Windows::Data::Json;
namespace enumeration = ::winrt::Windows::Devices::Enumeration;
namespace foundation = ::winrt::Windows::Foundation;
namespace collections = ::winrt::Windows::Foundation::Collections;
-namespace bt = ::winrt::Windows::Devices::Bluetooth;
-namespace gatt = ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile;
namespace streams = ::winrt::Windows::Storage::Streams;
-#include
\ No newline at end of file
+using namespace winrt::Windows::Devices::Bluetooth;
+using namespace winrt::Windows::Devices::Bluetooth::Advertisement;
+using namespace winrt::Windows::Devices::Bluetooth::Background;
+using namespace winrt::Windows::Devices::Bluetooth::GenericAttributeProfile;
+
+#include
+#include
+
+#include "abstraction_defs.h"
+#include "ble_utilities.h"
+
+#include "MidiBleDevice.h"
+#include "MidiBleBiDi.h"
+#include "MidiBleEndpointManager.h"
+#include "MidiBleAdvertisedEndpointManager.h"
\ No newline at end of file
diff --git a/src/user-tools/midi-console/Midi/Midi.csproj b/src/user-tools/midi-console/Midi/Midi.csproj
index 986e7868..208ae7cf 100644
--- a/src/user-tools/midi-console/Midi/Midi.csproj
+++ b/src/user-tools/midi-console/Midi/Midi.csproj
@@ -27,11 +27,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/src/user-tools/midi-console/midi-console.sln b/src/user-tools/midi-console/midi-console.sln
index 896c4cf6..9ab554b0 100644
--- a/src/user-tools/midi-console/midi-console.sln
+++ b/src/user-tools/midi-console/midi-console.sln
@@ -5,22 +5,42 @@ VisualStudioVersion = 17.8.34004.107
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Midi", "Midi\Midi.csproj", "{2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Devices.Midi2.Tools.Shared", "..\shared\Microsoft.Devices.Midi2.Tools.Shared\Microsoft.Devices.Midi2.Tools.Shared.csproj", "{1830A525-42BF-4568-9F4B-A192671CB16F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
+ Release|Any CPU = Release|Any CPU
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Debug|Any CPU.Build.0 = Debug|x64
{2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Debug|ARM64.Build.0 = Debug|ARM64
{2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Debug|x64.ActiveCfg = Debug|x64
{2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Debug|x64.Build.0 = Debug|x64
+ {2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Release|Any CPU.ActiveCfg = Release|x64
+ {2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Release|Any CPU.Build.0 = Release|x64
{2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Release|ARM64.ActiveCfg = Release|ARM64
{2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Release|ARM64.Build.0 = Release|ARM64
{2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Release|x64.ActiveCfg = Release|x64
{2EC5F5EE-BA4F-4D9C-90A8-4E18A08A6CD7}.Release|x64.Build.0 = Release|x64
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Debug|x64.Build.0 = Debug|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Release|ARM64.Build.0 = Release|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Release|x64.ActiveCfg = Release|Any CPU
+ {1830A525-42BF-4568-9F4B-A192671CB16F}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/user-tools/midi-settings-extensibility/midi-settings-extensibility.csproj b/src/user-tools/midi-settings-extensibility/midi-settings-extensibility.csproj
index 8c5dc094..157ba824 100644
--- a/src/user-tools/midi-settings-extensibility/midi-settings-extensibility.csproj
+++ b/src/user-tools/midi-settings-extensibility/midi-settings-extensibility.csproj
@@ -3,7 +3,7 @@
enable
enable
- net8.0-windows10.0.20348.0
+ net8.0-windows10.0.22621.0
10.0.20348.0
Microsoft.Midi.Settings.Extensibility
Microsoft.Midi.Settings.Extensibility
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings.Core/Microsoft.Midi.Settings.Core.csproj b/src/user-tools/midi-settings/Microsoft.Midi.Settings.Core/Microsoft.Midi.Settings.Core.csproj
index f21fc394..440a1033 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings.Core/Microsoft.Midi.Settings.Core.csproj
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings.Core/Microsoft.Midi.Settings.Core.csproj
@@ -1,7 +1,9 @@
net8.0-windows10.0.20348.0
- Microsoft.Midi.Settings.Core
+ 10.0.22621.0
+ 10.0.20348.0
+ Microsoft.Midi.Settings.Core
AnyCPU;x64;x86
x86;x64;arm64;AnyCPU
enable
@@ -9,6 +11,7 @@
true
false
+ 10.0.20348.0
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Config/ConfigFile.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Config/ConfigFile.cs
new file mode 100644
index 00000000..58dc0c63
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Config/ConfigFile.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Windows.Data.Json;
+using System.Threading.Tasks;
+
+namespace Microsoft.Midi.Settings.Config
+{
+ // this code really should be refactored into a shared C++/WinRT lib used in the service and here
+
+
+ internal class ConfigFile
+ {
+ private JsonObject _jsonObject;
+
+ //public JsonObject GetEndpointTransportPluginSettingsForTransport(Guid transportId)
+ //{
+
+
+ //}
+
+
+
+
+
+
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Config/ConfigFileManager.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Config/ConfigFileManager.cs
new file mode 100644
index 00000000..dfac677d
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Config/ConfigFileManager.cs
@@ -0,0 +1,43 @@
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Midi.Settings.Config
+{
+ internal class ConfigFileManager
+ {
+
+ // should likely take a path
+ public void ChangeActiveConfigFile(string configFileName)
+ {
+
+ }
+
+ // should likely return a path
+ public string GetConfigFileFolder()
+ {
+ return string.Empty;
+ }
+
+ // should likely return a path or similar object
+ public string GetActiveConfigFileName()
+ {
+ // gets the active config file name from the registry
+
+ return string.Empty;
+
+ }
+
+ public void BackupCurrentConfigFile()
+ {
+ // backs up the current config file using a numbering scheme
+
+
+ }
+
+
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Config/EndpointConfigMetadataPayload.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Config/EndpointConfigMetadataPayload.cs
new file mode 100644
index 00000000..e61380c3
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Config/EndpointConfigMetadataPayload.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Windows.Data.Json;
+
+
+namespace Microsoft.Midi.Settings.Config
+{
+ internal class EndpointConfigMetadataPayload : IMidiServiceTransportPluginConfiguration
+ {
+ public bool IsFromConfigurationFile => true;
+
+ public JsonObject SettingsJson { get; private set; }
+
+ public Guid TransportId { get; private set; }
+
+ private string _newName;
+ private string _newDescription;
+ private string _newSmallImagePath;
+ private string _newLargeImagePath;
+
+ public EndpointConfigMetadataPayload(Guid transportId, string newName, string newDescription, string newSmallImagePath, string newLargeImagePath)
+ {
+ TransportId = transportId;
+
+ _newName = newName.Trim();
+ _newDescription = newDescription.Trim();
+ _newSmallImagePath = newSmallImagePath.Trim();
+ _newLargeImagePath = newLargeImagePath.Trim();
+ }
+
+ // build the json for endpoint properties updates
+ // "endpointTransportPluginSettings":
+ // {
+ // abstraction guid :
+ // {
+ // "update"
+ // [
+ // {
+ // "match":
+ // [
+ // {
+ // "match_prop_name" : "match_value"
+ // }
+ // ]
+ // "prop_name" : "value"
+ // }
+ // ]
+ // }
+ // }
+
+ public void BuildJson()
+ {
+
+ }
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Helpers/BooleanToInverseVisibilityConverter.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Helpers/BooleanToInverseVisibilityConverter.cs
new file mode 100644
index 00000000..860b02aa
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Helpers/BooleanToInverseVisibilityConverter.cs
@@ -0,0 +1,31 @@
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Data;
+
+namespace Microsoft.Midi.Settings.Helpers;
+
+public class BooleanToInverseVisibilityConverter : IValueConverter
+{
+ public BooleanToInverseVisibilityConverter()
+ {
+ }
+
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ if (value is bool val)
+ {
+ if (!val)
+ {
+ return Visibility.Visible;
+ }
+
+ return Visibility.Collapsed;
+ }
+
+ throw new ArgumentException("BooleanToInverseVisibilityConverter object must be a bool");
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ return null;
+ }
+}
\ No newline at end of file
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 a5452e69..a89e3e89 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
@@ -2,6 +2,7 @@
WinExe
net8.0-windows10.0.20348.0
+ 10.0.22621.0
10.0.20348.0
enable
Microsoft.Midi.Settings
@@ -10,12 +11,12 @@
app.manifest
x64;arm64
win10-x64;win10-arm64
- Properties\PublishProfiles\win10-$(Platform).pubxml
enable
true
true
- None
+ Properties\PublishProfiles\win10-$(Platform).pubxml
+
DAC77297934678914CC6B107CE26743135B68C64
Microsoft.Midi.Settings_TemporaryKey.pfx
@@ -78,13 +79,13 @@
-
+
-
+
@@ -97,6 +98,7 @@
+
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Package.appxmanifest b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Package.appxmanifest
index e1a39fa1..a944d499 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Package.appxmanifest
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Package.appxmanifest
@@ -7,7 +7,8 @@
xmlns:genTemplate="http://schemas.microsoft.com/appx/developer/templatestudio"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
- IgnorableNamespaces="uap rescap genTemplate">
+ xmlns:iot="http://schemas.microsoft.com/appx/manifest/iot/windows10"
+ IgnorableNamespaces="uap rescap genTemplate iot">
-
-
+
+
-
-
-
-
- NetworkMidiTransportPlugin.dll
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Properties/launchsettings.json b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Properties/launchsettings.json
index 4a231fa9..1a279b4c 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Properties/launchsettings.json
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Properties/launchsettings.json
@@ -1,10 +1,16 @@
{
"profiles": {
"Microsoft.Midi.Settings (Package)": {
- "commandName": "MsixPackage",
- "allowLocalNetworkLoopbackProperty": true,
- "nativeDebugging": true,
- "hotReloadEnabled": false
+ "commandName": "MsixPackage",
+ "commandLineArgs": "",
+ "alwaysReinstallApp": false,
+ "remoteDebugEnabled": false,
+ "allowLocalNetworkLoopbackProperty": true,
+ "authenticationMode": "Windows",
+ "doNotLaunchApp": false,
+ "remoteDebugMachine": "",
+ "nativeDebugging": true,
+ "hotReloadEnabled": false
},
"Microsoft.Midi.Settings (Unpackaged)": {
"commandName": "Project",
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/Data/EndpointUserMetadata.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/Data/EndpointUserMetadata.cs
new file mode 100644
index 00000000..1c607e83
--- /dev/null
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/Data/EndpointUserMetadata.cs
@@ -0,0 +1,24 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Midi.Settings.ViewModels.Data
+{
+ public partial class EndpointUserMetadata : ObservableObject
+ {
+ [ObservableProperty]
+ private string name;
+
+ [ObservableProperty]
+ private string description;
+
+ [ObservableProperty]
+ private string smallImagePath;
+
+ [ObservableProperty]
+ private string largeImagePath;
+ }
+}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/DeviceDetailViewModel.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/DeviceDetailViewModel.cs
index 9d2cacfd..4105502b 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/DeviceDetailViewModel.cs
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/DeviceDetailViewModel.cs
@@ -7,6 +7,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Midi.Settings.Contracts.ViewModels;
using Microsoft.Midi.Settings.Models;
+using Microsoft.Midi.Settings.ViewModels.Data;
using Microsoft.UI.Dispatching;
using Windows.Devices.Enumeration;
@@ -14,6 +15,7 @@ namespace Microsoft.Midi.Settings.ViewModels
{
public class DeviceDetailViewModel : ObservableRecipient, INavigationAware
{
+ public EndpointUserMetadata UserMetadata { get; private set; } = new EndpointUserMetadata();
public MidiEndpointDeviceInformation? DeviceInformation
{
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/TroubleshootingViewModel.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/TroubleshootingViewModel.cs
index bd722159..4ffb8d65 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/TroubleshootingViewModel.cs
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/TroubleshootingViewModel.cs
@@ -1,14 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Security.Principal;
+using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
+using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.Midi.Settings.Contracts.Services;
+using Microsoft.Midi.Settings.Contracts.ViewModels;
+using Microsoft.Midi.Settings.Models;
+using Microsoft.UI.Dispatching;
+
namespace Microsoft.Midi.Settings.ViewModels;
-public class TroubleshootingViewModel : ObservableRecipient
+public class TroubleshootingViewModel : ObservableRecipient, INavigationAware
{
- public TroubleshootingViewModel()
+ private DispatcherQueue _dispatcherQueue;
+ private INavigationService _navigationService;
+
+ public ICommand RestartServiceCommand
+ {
+ get; private set;
+ }
+
+ public bool IsUserRunningAsAdmin
+ {
+ get { return UserHelper.CurrentUserHasAdminRights(); }
+ }
+
+ public TroubleshootingViewModel(INavigationService navigationService)
+ {
+ _navigationService = navigationService;
+
+ _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
+
+
+ RestartServiceCommand = new RelayCommand(
+ async (param) =>
+ {
+ System.Diagnostics.Debug.WriteLine("RestartServiceCommand exec");
+
+ //_navigationService.NavigateTo(typeof(DeviceDetailViewModel).FullName!, param);
+
+ });
+ }
+
+
+ public void RestartService()
{
+ var controller = MidiServiceHelper.GetServiceController();
+ MidiServiceHelper.StopService(controller);
+ MidiServiceHelper.StartService(controller);
}
+
+
+ public void OnNavigatedFrom()
+ {
+ }
+
+
+ public void OnNavigatedTo(object parameter)
+ {
+ }
+
}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml
index 97fe713f..1c937b7b 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml
@@ -91,47 +91,34 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
+
+
-
@@ -223,7 +210,6 @@
-
@@ -378,13 +364,117 @@
-
-
-
-
-
-
+
+
+
+
+ 1200
+ 800
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml.cs
index 8c46ced1..5468db70 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml.cs
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml.cs
@@ -16,6 +16,12 @@
using Windows.Foundation.Collections;
using Windows.UI.Popups;
using Microsoft.UI.Windowing;
+using Windows.Storage.Pickers;
+using WinRT.Interop;
+using Microsoft.Midi.Settings.ViewModels.Data;
+using Microsoft.UI.Xaml.Media.Imaging;
+using Windows.Storage.Streams;
+using Windows.Storage;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
@@ -40,12 +46,144 @@ public DeviceDetailPage()
this.InitializeComponent();
}
+
private async void Button_Click(object sender, RoutedEventArgs e)
{
- var messageDialog = App.MainWindow.CreateMessageDialog("Setting endpoint properties through the UI is not yet implemented.", "Settings Preview");
+ ViewModel.UserMetadata.Name = ViewModel.DeviceInformation.UserSuppliedName;
+ ViewModel.UserMetadata.Description = ViewModel.DeviceInformation.UserSuppliedDescription;
+ ViewModel.UserMetadata.LargeImagePath = ViewModel.DeviceInformation.UserSuppliedLargeImagePath;
+ ViewModel.UserMetadata.SmallImagePath = ViewModel.DeviceInformation.UserSuppliedSmallImagePath;
+ editUserDefinedPropertiesDialog.Width = this.Width / 3;
+
+
+ // TODO: Should probably have these in the viewmodel as
+ // we'll need a renderable image anyway. This code is temp
+
+ if (ViewModel.UserMetadata.LargeImagePath != string.Empty)
+ {
+ var file = await StorageFile.GetFileFromPathAsync(ViewModel.UserMetadata.LargeImagePath);
+
+ if (file != null)
+ {
+ // Open a stream for the selected file.
+ // The 'using' block ensures the stream is disposed
+ // after the image is loaded.
+ using (IRandomAccessStream fileStream =
+ await file.OpenAsync(FileAccessMode.Read))
+ {
+ // Set the image source to the selected bitmap.
+ var bitmapImage = new BitmapImage();
+
+ bitmapImage.SetSource(fileStream);
+
+ UserMetadataLargeImagePreview.Source = bitmapImage;
+ }
+ }
+ }
+
+ if (ViewModel.UserMetadata.SmallImagePath != string.Empty)
+ {
+ var file = await StorageFile.GetFileFromPathAsync(ViewModel.UserMetadata.SmallImagePath);
+
+ if (file != null)
+ {
+ // Open a stream for the selected file.
+ // The 'using' block ensures the stream is disposed
+ // after the image is loaded.
+ using (IRandomAccessStream fileStream =
+ await file.OpenAsync(FileAccessMode.Read))
+ {
+ // Set the image source to the selected bitmap.
+ var bitmapImage = new BitmapImage();
+
+ bitmapImage.SetSource(fileStream);
+
+ UserMetadataSmallImagePreview.Source = bitmapImage;
+ }
+ }
+ }
+
+
+
+ ContentDialogResult result = await editUserDefinedPropertiesDialog.ShowAsync(ContentDialogPlacement.Popup);
+
+ if (result == ContentDialogResult.Primary)
+ {
+ // save the changes
+
+
+ // refresh the device information?
+
+ }
+ }
+
+ // edit popup
+ private async void BrowseLargeImage_Click(object sender, RoutedEventArgs e)
+ {
+ var filePicker = App.MainWindow.CreateOpenFilePicker();
+
+ filePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
+ filePicker.FileTypeFilter.Add(".png");
+ filePicker.FileTypeFilter.Add(".jpg");
+ filePicker.FileTypeFilter.Add(".svg");
+
+ var file = await filePicker.PickSingleFileAsync();
+
+ if (file != null)
+ {
+ // update the edit vm
+ ViewModel.UserMetadata.LargeImagePath = file.Path;
+
+ // Open a stream for the selected file.
+ // The 'using' block ensures the stream is disposed
+ // after the image is loaded.
+ using (IRandomAccessStream fileStream =
+ await file.OpenAsync(FileAccessMode.Read))
+ {
+ // Set the image source to the selected bitmap.
+ var bitmapImage = new BitmapImage();
+
+ bitmapImage.SetSource(fileStream);
+
+ UserMetadataLargeImagePreview.Source = bitmapImage;
+ }
+
+ }
+ }
+
+ // edit popup
+ private async void BrowseSmallImage_Click(object sender, RoutedEventArgs e)
+ {
+ var filePicker = App.MainWindow.CreateOpenFilePicker();
+
+ filePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
+ filePicker.FileTypeFilter.Add(".png");
+ filePicker.FileTypeFilter.Add(".jpg");
+ filePicker.FileTypeFilter.Add(".svg");
+
+ var file = await filePicker.PickSingleFileAsync();
+
+ if (file != null)
+ {
+ // update the edit vm
+ ViewModel.UserMetadata.SmallImagePath = file.Path;
+
+ // Open a stream for the selected file.
+ // The 'using' block ensures the stream is disposed
+ // after the image is loaded.
+ using (IRandomAccessStream fileStream =
+ await file.OpenAsync(FileAccessMode.Read))
+ {
+ // Set the image source to the selected bitmap.
+ var bitmapImage = new BitmapImage();
+
+ bitmapImage.SetSource(fileStream);
+
+ UserMetadataSmallImagePreview.Source = bitmapImage;
+ }
+ }
- await messageDialog.ShowAsync();
}
}
}
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/TroubleshootingPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/TroubleshootingPage.xaml
index 1ef37337..7aa5d161 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/TroubleshootingPage.xaml
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/TroubleshootingPage.xaml
@@ -9,8 +9,18 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:labs="using:CommunityToolkit.Labs.WinUI"
+ xmlns:midi2="using:Windows.Devices.Midi2"
+ xmlns:controls="using:CommunityToolkit.WinUI.Controls"
+ xmlns:helpers="using:Microsoft.Midi.Settings.Helpers"
mc:Ignorable="d">
+
+
+
+
+
+
+
@@ -19,6 +29,7 @@
Text="If you are running into problems with MIDI on your PC, the tools below may help you recover without requiring a reboot.">
+
@@ -80,7 +92,13 @@
-
+
+
+
+
diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/TroubleshootingPage.xaml.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/TroubleshootingPage.xaml.cs
index 2f42f82a..fe5b658c 100644
--- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/TroubleshootingPage.xaml.cs
+++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/TroubleshootingPage.xaml.cs
@@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
+using Microsoft.Midi.Settings.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
@@ -25,8 +26,16 @@ namespace Microsoft.Midi.Settings.Views;
///
public sealed partial class TroubleshootingPage : Page
{
+ public TroubleshootingViewModel ViewModel
+ {
+ get;
+ }
+
+
public TroubleshootingPage()
{
+ ViewModel = App.GetService();
+
this.InitializeComponent();
}
}
diff --git a/src/user-tools/midi-settings/midi-settings.sln b/src/user-tools/midi-settings/midi-settings.sln
index c55e6fb1..90a81e9b 100644
--- a/src/user-tools/midi-settings/midi-settings.sln
+++ b/src/user-tools/midi-settings/midi-settings.sln
@@ -9,20 +9,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Midi.Settings.Cor
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "midi-settings-extensibility", "..\midi-settings-extensibility\midi-settings-extensibility.csproj", "{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Devices.Midi2.Tools.Shared", "..\shared\Microsoft.Devices.Midi2.Tools.Shared\Microsoft.Devices.Midi2.Tools.Shared.csproj", "{AF821C0F-4A8A-4AA3-A279-6092A324BB57}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Devices.Midi2.Tools.Shared", "..\shared\Microsoft.Devices.Midi2.Tools.Shared\Microsoft.Devices.Midi2.Tools.Shared.csproj", "{AF821C0F-4A8A-4AA3-A279-6092A324BB57}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|arm64 = Debug|arm64
- Debug|ARM64EC = Debug|ARM64EC
Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|arm64 = Release|arm64
- Release|ARM64EC = Release|ARM64EC
Release|x64 = Release|x64
- Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|Any CPU.ActiveCfg = Debug|x64
@@ -31,90 +27,54 @@ Global
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|arm64.ActiveCfg = Debug|arm64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|arm64.Build.0 = Debug|arm64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|arm64.Deploy.0 = Debug|arm64
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|ARM64EC.ActiveCfg = Debug|x64
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|ARM64EC.Build.0 = Debug|x64
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|ARM64EC.Deploy.0 = Debug|x64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|x64.ActiveCfg = Debug|x64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|x64.Build.0 = Debug|x64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|x64.Deploy.0 = Debug|x64
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|x86.ActiveCfg = Debug|x86
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|x86.Build.0 = Debug|x86
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Debug|x86.Deploy.0 = Debug|x86
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|Any CPU.ActiveCfg = Release|x64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|Any CPU.Build.0 = Release|x64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|Any CPU.Deploy.0 = Release|x64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|arm64.ActiveCfg = Release|arm64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|arm64.Build.0 = Release|arm64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|arm64.Deploy.0 = Release|arm64
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|ARM64EC.ActiveCfg = Release|x64
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|ARM64EC.Build.0 = Release|x64
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|ARM64EC.Deploy.0 = Release|x64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|x64.ActiveCfg = Release|x64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|x64.Build.0 = Release|x64
{4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|x64.Deploy.0 = Release|x64
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|x86.ActiveCfg = Release|x86
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|x86.Build.0 = Release|x86
- {4CC72EFB-9660-4654-A1A9-00E0555F8F54}.Release|x86.Deploy.0 = Release|x86
{8E43F524-E730-49B6-90B0-C7671E97226A}.Debug|Any CPU.ActiveCfg = Debug|x64
{8E43F524-E730-49B6-90B0-C7671E97226A}.Debug|Any CPU.Build.0 = Debug|x64
{8E43F524-E730-49B6-90B0-C7671E97226A}.Debug|arm64.ActiveCfg = Debug|arm64
{8E43F524-E730-49B6-90B0-C7671E97226A}.Debug|arm64.Build.0 = Debug|arm64
- {8E43F524-E730-49B6-90B0-C7671E97226A}.Debug|ARM64EC.ActiveCfg = Debug|x64
- {8E43F524-E730-49B6-90B0-C7671E97226A}.Debug|ARM64EC.Build.0 = Debug|x64
{8E43F524-E730-49B6-90B0-C7671E97226A}.Debug|x64.ActiveCfg = Debug|x64
{8E43F524-E730-49B6-90B0-C7671E97226A}.Debug|x64.Build.0 = Debug|x64
- {8E43F524-E730-49B6-90B0-C7671E97226A}.Debug|x86.ActiveCfg = Debug|x86
- {8E43F524-E730-49B6-90B0-C7671E97226A}.Debug|x86.Build.0 = Debug|x86
{8E43F524-E730-49B6-90B0-C7671E97226A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E43F524-E730-49B6-90B0-C7671E97226A}.Release|Any CPU.Build.0 = Release|Any CPU
{8E43F524-E730-49B6-90B0-C7671E97226A}.Release|arm64.ActiveCfg = Release|arm64
{8E43F524-E730-49B6-90B0-C7671E97226A}.Release|arm64.Build.0 = Release|arm64
- {8E43F524-E730-49B6-90B0-C7671E97226A}.Release|ARM64EC.ActiveCfg = Release|Any CPU
- {8E43F524-E730-49B6-90B0-C7671E97226A}.Release|ARM64EC.Build.0 = Release|Any CPU
{8E43F524-E730-49B6-90B0-C7671E97226A}.Release|x64.ActiveCfg = Release|x64
{8E43F524-E730-49B6-90B0-C7671E97226A}.Release|x64.Build.0 = Release|x64
- {8E43F524-E730-49B6-90B0-C7671E97226A}.Release|x86.ActiveCfg = Release|x86
- {8E43F524-E730-49B6-90B0-C7671E97226A}.Release|x86.Build.0 = Release|x86
{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Debug|Any CPU.ActiveCfg = Debug|x64
{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Debug|Any CPU.Build.0 = Debug|x64
{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Debug|arm64.ActiveCfg = Debug|ARM64
{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Debug|arm64.Build.0 = Debug|ARM64
- {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Debug|ARM64EC.ActiveCfg = Debug|x64
- {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Debug|ARM64EC.Build.0 = Debug|x64
{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Debug|x64.ActiveCfg = Debug|x64
{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Debug|x64.Build.0 = Debug|x64
- {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Debug|x86.ActiveCfg = Debug|Any CPU
- {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Debug|x86.Build.0 = Debug|Any CPU
{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|Any CPU.Build.0 = Release|Any CPU
- {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|arm64.ActiveCfg = Release|Any CPU
- {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|arm64.Build.0 = Release|Any CPU
- {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|ARM64EC.ActiveCfg = Release|Any CPU
- {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|ARM64EC.Build.0 = Release|Any CPU
+ {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|arm64.ActiveCfg = Release|ARM64
+ {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|arm64.Build.0 = Release|ARM64
{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|x64.ActiveCfg = Release|x64
{E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|x64.Build.0 = Release|x64
- {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|x86.ActiveCfg = Release|Any CPU
- {E854B15B-6D98-4D79-B29E-0AA0DFC14CCE}.Release|x86.Build.0 = Release|Any CPU
{AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Debug|arm64.ActiveCfg = Debug|Any CPU
{AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Debug|arm64.Build.0 = Debug|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Debug|ARM64EC.ActiveCfg = Debug|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Debug|ARM64EC.Build.0 = Debug|Any CPU
{AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Debug|x64.ActiveCfg = Debug|Any CPU
{AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Debug|x64.Build.0 = Debug|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Debug|x86.ActiveCfg = Debug|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Debug|x86.Build.0 = Debug|Any CPU
{AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|Any CPU.Build.0 = Release|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|arm64.ActiveCfg = Release|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|arm64.Build.0 = Release|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|ARM64EC.ActiveCfg = Release|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|ARM64EC.Build.0 = Release|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|x64.ActiveCfg = Release|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|x64.Build.0 = Release|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|x86.ActiveCfg = Release|Any CPU
- {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|x86.Build.0 = Release|Any CPU
+ {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|arm64.ActiveCfg = Release|ARM64
+ {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|arm64.Build.0 = Release|ARM64
+ {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|x64.ActiveCfg = Release|x64
+ {AF821C0F-4A8A-4AA3-A279-6092A324BB57}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Endpoints/EndpointUtility.cs b/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Endpoints/EndpointUtility.cs
index 69905ae4..68360869 100644
--- a/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Endpoints/EndpointUtility.cs
+++ b/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Endpoints/EndpointUtility.cs
@@ -4,7 +4,6 @@
using System.Text;
using System.Threading.Tasks;
-using Windows.Devices.Midi2;
using Windows.Devices.Enumeration;
namespace Microsoft.Devices.Midi2.Tools.Shared.Endpoints
diff --git a/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Microsoft.Devices.Midi2.Tools.Shared.csproj b/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Microsoft.Devices.Midi2.Tools.Shared.csproj
index f7a73dd5..4a15a082 100644
--- a/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Microsoft.Devices.Midi2.Tools.Shared.csproj
+++ b/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Microsoft.Devices.Midi2.Tools.Shared.csproj
@@ -2,17 +2,21 @@
net8.0-windows10.0.20348.0
- enable
+ 10.0.22621.0
+ 10.0.20348.0
+ enable
enable
true
false
+ AnyCPU;ARM64;x64
-
+
+
diff --git a/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Service/MidiServiceHelper.cs b/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Service/MidiServiceHelper.cs
new file mode 100644
index 00000000..87334307
--- /dev/null
+++ b/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/Service/MidiServiceHelper.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.ServiceProcess;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Midi.Settings.Helpers
+{
+ public class MidiServiceHelper
+ {
+ private const int MAX_TRIES = 50;
+ private const int SLEEP_MS_PER_ATTEMPT = 500;
+
+ public static string GetServiceName()
+ {
+ return "MidiSrv";
+ }
+
+ public static ServiceController GetServiceController()
+ {
+ return new System.ServiceProcess.ServiceController(GetServiceName());
+ }
+
+ public static bool ServiceIsStopped(ServiceController serviceController)
+ {
+ return serviceController.Status == ServiceControllerStatus.Stopped || serviceController.Status == ServiceControllerStatus.StopPending;
+ }
+
+ public static bool ServiceIsRunning(ServiceController serviceController)
+ {
+ return serviceController.Status == ServiceControllerStatus.Running || serviceController.Status == ServiceControllerStatus.StartPending;
+ }
+
+ public static bool ServiceIsReallyRunning(ServiceController serviceController)
+ {
+ return serviceController.Status == ServiceControllerStatus.Running;
+ }
+
+
+ public static bool StopService(ServiceController controller)
+ {
+ controller.Stop();
+
+ int i = 0;
+ while (i < MAX_TRIES && controller.Status != ServiceControllerStatus.Stopped)
+ {
+ Thread.Sleep(SLEEP_MS_PER_ATTEMPT);
+ i++;
+ controller.Refresh();
+ }
+
+ return controller.Status == ServiceControllerStatus.Stopped;
+ }
+
+ public static bool StartService(ServiceController controller)
+ {
+ controller.Start();
+
+ int i = 0;
+ while (i < MAX_TRIES && controller.Status != System.ServiceProcess.ServiceControllerStatus.Running)
+ {
+ Thread.Sleep(SLEEP_MS_PER_ATTEMPT);
+ controller.Refresh();
+
+ i++;
+ }
+
+ return controller.Status == ServiceControllerStatus.Running;
+ }
+
+ }
+}
diff --git a/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/User/UserHelper.cs b/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/User/UserHelper.cs
new file mode 100644
index 00000000..5fac0dc0
--- /dev/null
+++ b/src/user-tools/shared/Microsoft.Devices.Midi2.Tools.Shared/User/UserHelper.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Principal;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Midi.Settings.Helpers
+{
+ public class UserHelper
+ {
+ public static bool CurrentUserHasAdminRights()
+ {
+ WindowsIdentity identity = WindowsIdentity.GetCurrent();
+ WindowsPrincipal principal = new WindowsPrincipal(identity);
+
+ return principal.IsInRole(WindowsBuiltInRole.Administrator);
+ }
+ }
+}