From 36684a67a58aaf0536db7b9c76b35031e28658b6 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Wed, 19 Feb 2025 12:27:19 -0500 Subject: [PATCH 01/29] Build artifacts --- build/staging/version/BundleInfo.wxi | 2 +- .../version/WindowsMidiServicesVersion.cs | 4 +- .../version/WindowsMidiServicesVersion.h | 4 +- .../basics/client-basics-cpp.vcxproj | 2 +- samples/cpp-winrt/basics/packages.config | 2 +- .../loopback-endpoints-cpp.vcxproj | 2 +- .../loopback-endpoints/packages.config | 2 +- samples/cpp-winrt/send-speed/packages.config | 2 +- .../send-speed/send-speed-cpp.vcxproj | 2 +- .../simple-app-to-app-midi/packages.config | 2 +- .../simple-app-to-app-cpp.vcxproj | 2 +- .../static-enum-endpoints/packages.config | 2 +- .../static-enum-endpoints-cpp.vcxproj | 2 +- .../cpp-winrt/watch-endpoints/packages.config | 2 +- .../watch-endpoints-cpp.vcxproj | 2 +- src/app-sdk/mididiag/mididiag.vcxproj | 2 +- src/app-sdk/mididiag/packages.config | 2 +- src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj | 2 +- src/app-sdk/midimdnsinfo/packages.config | 2 +- src/app-sdk/midiusbinfo/midiusbinfo.vcxproj | 2 +- src/app-sdk/midiusbinfo/packages.config | 2 +- .../Services/IMidiEndpointMonitorService.cs | 14 +++++++ .../Endpoints/DeviceDetailPage.xaml.cs | 2 + .../Services/MidiEndpointMonitorService.cs | 41 +++++++++++++++++++ 24 files changed, 80 insertions(+), 23 deletions(-) create mode 100644 src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiEndpointMonitorService.cs create mode 100644 src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiEndpointMonitorService.cs diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index 3045d173..f1cdf1e2 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,4 +1,4 @@ - + diff --git a/build/staging/version/WindowsMidiServicesVersion.cs b/build/staging/version/WindowsMidiServicesVersion.cs index 466ab569..2d1d372a 100644 --- a/build/staging/version/WindowsMidiServicesVersion.cs +++ b/build/staging/version/WindowsMidiServicesVersion.cs @@ -6,12 +6,12 @@ public static class MidiBuildInformation { public const string Source = "GitHub Preview"; public const string Name = "Customer Preview 2"; - public const string BuildFullVersion = "1.0.3-preview-11.250218-1526"; + public const string BuildFullVersion = "1.0.3-preview-11.250218-1656"; public const string VersionMajor = "1"; public const string VersionMinor = "0"; public const string VersionRevision = "3"; public const string VersionDateNumber = "250218"; - public const string VersionTimeNumber = "1526"; + public const string VersionTimeNumber = "1656"; } } diff --git a/build/staging/version/WindowsMidiServicesVersion.h b/build/staging/version/WindowsMidiServicesVersion.h index 6f0eddc2..83fbd7ef 100644 --- a/build/staging/version/WindowsMidiServicesVersion.h +++ b/build/staging/version/WindowsMidiServicesVersion.h @@ -5,12 +5,12 @@ #define WINDOWS_MIDI_SERVICES_BUILD_SOURCE L"GitHub Preview" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_NAME L"Customer Preview 2" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.3-preview-11.250218-1526" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.3-preview-11.250218-1656" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MAJOR L"1" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MINOR L"0" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_REVISION L"3" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250218" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"1526" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"1656" #endif diff --git a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj index 2847fd6d..0a464e21 100644 --- a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj +++ b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1526 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 true true false diff --git a/samples/cpp-winrt/basics/packages.config b/samples/cpp-winrt/basics/packages.config index 0f06cbf0..c391249b 100644 --- a/samples/cpp-winrt/basics/packages.config +++ b/samples/cpp-winrt/basics/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj index 0d13e97b..10edeb84 100644 --- a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1526 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 true true true diff --git a/samples/cpp-winrt/loopback-endpoints/packages.config b/samples/cpp-winrt/loopback-endpoints/packages.config index 0f06cbf0..c391249b 100644 --- a/samples/cpp-winrt/loopback-endpoints/packages.config +++ b/samples/cpp-winrt/loopback-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/send-speed/packages.config b/samples/cpp-winrt/send-speed/packages.config index 0f06cbf0..c391249b 100644 --- a/samples/cpp-winrt/send-speed/packages.config +++ b/samples/cpp-winrt/send-speed/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj index 510e4fbe..b1579dd7 100644 --- a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj +++ b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1526 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 true true true diff --git a/samples/cpp-winrt/simple-app-to-app-midi/packages.config b/samples/cpp-winrt/simple-app-to-app-midi/packages.config index 0f06cbf0..c391249b 100644 --- a/samples/cpp-winrt/simple-app-to-app-midi/packages.config +++ b/samples/cpp-winrt/simple-app-to-app-midi/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj index 9bdd753c..c9d8511d 100644 --- a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj +++ b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1526 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 true true true diff --git a/samples/cpp-winrt/static-enum-endpoints/packages.config b/samples/cpp-winrt/static-enum-endpoints/packages.config index 0f06cbf0..c391249b 100644 --- a/samples/cpp-winrt/static-enum-endpoints/packages.config +++ b/samples/cpp-winrt/static-enum-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj index 21434c87..3b028605 100644 --- a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1526 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 true true true diff --git a/samples/cpp-winrt/watch-endpoints/packages.config b/samples/cpp-winrt/watch-endpoints/packages.config index 0f06cbf0..c391249b 100644 --- a/samples/cpp-winrt/watch-endpoints/packages.config +++ b/samples/cpp-winrt/watch-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj index fd1aaa10..cba27659 100644 --- a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1526 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 true true true diff --git a/src/app-sdk/mididiag/mididiag.vcxproj b/src/app-sdk/mididiag/mididiag.vcxproj index 92354e73..c6e214b4 100644 --- a/src/app-sdk/mididiag/mididiag.vcxproj +++ b/src/app-sdk/mididiag/mididiag.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1526 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 true true true diff --git a/src/app-sdk/mididiag/packages.config b/src/app-sdk/mididiag/packages.config index cd1c48eb..2788f5cf 100644 --- a/src/app-sdk/mididiag/packages.config +++ b/src/app-sdk/mididiag/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj index 78f6a21d..ee05ee56 100644 --- a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj +++ b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1526 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 true true true diff --git a/src/app-sdk/midimdnsinfo/packages.config b/src/app-sdk/midimdnsinfo/packages.config index cd1c48eb..2788f5cf 100644 --- a/src/app-sdk/midimdnsinfo/packages.config +++ b/src/app-sdk/midimdnsinfo/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj index 0271834a..129ae054 100644 --- a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj +++ b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1526 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 true true true diff --git a/src/app-sdk/midiusbinfo/packages.config b/src/app-sdk/midiusbinfo/packages.config index cd1c48eb..2788f5cf 100644 --- a/src/app-sdk/midiusbinfo/packages.config +++ b/src/app-sdk/midiusbinfo/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiEndpointMonitorService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiEndpointMonitorService.cs new file mode 100644 index 00000000..62f0b8ff --- /dev/null +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiEndpointMonitorService.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Midi.Settings.Contracts.Services +{ + public interface IMidiEndpointMonitorService + { + void MonitorMidiEndpoint(string midiEndpointDeviceId); + + } +} diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DeviceDetailPage.xaml.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DeviceDetailPage.xaml.cs index 6608ddbb..2bbd2ac4 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DeviceDetailPage.xaml.cs +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Endpoints/DeviceDetailPage.xaml.cs @@ -201,6 +201,8 @@ private async void BrowseSmallImage_Click(object sender, RoutedEventArgs e) private void OnOpenConsoleMonitor(object sender, RoutedEventArgs e) { + // TODO: Change this to use the Endpoint Monitor Service + try { string arguments = diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiEndpointMonitorService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiEndpointMonitorService.cs new file mode 100644 index 00000000..15831e1b --- /dev/null +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiEndpointMonitorService.cs @@ -0,0 +1,41 @@ +using Microsoft.Midi.Settings.Contracts.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Popups; + +namespace Microsoft.Midi.Settings.Services +{ + public class MidiEndpointMonitorService : IMidiEndpointMonitorService + { + public void MonitorMidiEndpoint(string midiEndpointDeviceId) + { + // right now, this opens a console. In the future, it may be its own dedicated GUI window + + try + { + string arguments = + " endpoint " + + midiEndpointDeviceId + + " monitor"; + + using (var monitorProcess = new System.Diagnostics.Process()) + { + monitorProcess.StartInfo.FileName = "midi.exe"; + monitorProcess.StartInfo.Arguments = arguments; + + monitorProcess.Start(); + } + } + catch (Exception ex) + { + var dialog = new MessageDialog("Error opening console"); + dialog.Content = ex.ToString(); + + dialog.ShowAsync().Wait(); + } + } + } +} From 0f7b2ba5b1c1c317121a78987303888068a745ef Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Wed, 19 Feb 2025 23:13:52 -0500 Subject: [PATCH 02/29] Update midi1monitor to show decoded messages --- src/api/Inc/midi1_message_defs.h | 12 +- src/app-sdk/midi1monitor/main.cpp | 187 +++++++++++++++++++++++++----- 2 files changed, 170 insertions(+), 29 deletions(-) diff --git a/src/api/Inc/midi1_message_defs.h b/src/api/Inc/midi1_message_defs.h index d62f67f6..792c51ea 100644 --- a/src/api/Inc/midi1_message_defs.h +++ b/src/api/Inc/midi1_message_defs.h @@ -84,7 +84,13 @@ (b & MIDI_STATUSBYTEFILTER) == MIDI_STATUSBYTEFILTER) #define MIDI_BYTE_IS_SYSTEM_REALTIME_STATUS(b) (\ - (b & MIDI_SYSTEM_REALTIME_FILTER) == MIDI_SYSTEM_REALTIME_FILTER) + (b == MIDI_TIMINGCLOCK) || \ + (b == MIDI_START) || \ + (b == MIDI_CONTINUE) || \ + (b == MIDI_STOP) || \ + (b == MIDI_ACTIVESENSE) || \ + (b == MIDI_RESET) \ + ) #define MIDI_BYTE_IS_SYSEX_START_STATUS(b) (\ b == MIDI_SYSEX) @@ -95,6 +101,8 @@ #define MIDI_BYTE_IS_DATA_BYTE(b) (\ (b & MIDI_STATUSBYTEFILTER) == 0) - +#define MIDI_STATUS_IS_CHANNEL_VOICE_MESSAGE(b) (\ + b < MIDI_SYSEX) + #endif \ No newline at end of file diff --git a/src/app-sdk/midi1monitor/main.cpp b/src/app-sdk/midi1monitor/main.cpp index 6f12ba71..c8abcfb4 100644 --- a/src/app-sdk/midi1monitor/main.cpp +++ b/src/app-sdk/midi1monitor/main.cpp @@ -14,6 +14,11 @@ #include "color.hpp" +bool m_showActiveSense{ false }; +bool m_showClock{ false }; + + + void WriteInfo(std::string info) { std::cout << dye::aqua(info) << std::endl; @@ -82,44 +87,49 @@ uint32_t m_countStatusBytesReceived{ 0 }; void DisplayStatusByte(byte status, bool isError) { - if (status == MIDI_SYSEX) + if (status == MIDI_EOX) + { + std::cout << " "; + } + else { std::cout << std::endl; - if (isError) - { - std::cout << std::hex << std::setw(2) << dye::light_red((uint16_t)status); - } - else - { - std::cout << std::hex << std::setw(2) << dye::yellow((uint16_t)status); - } } - else if (status == MIDI_EOX) + + if (isError) { - std::cout << " "; + std::cout << std::hex << std::setw(2) << dye::light_red((uint16_t)status); + return; + } - if (isError) - { - std::cout << std::hex << std::setw(2) << dye::light_red((uint16_t)status); - } - else + + if (MIDI_STATUS_IS_CHANNEL_VOICE_MESSAGE(status)) + { + switch (status & 0xF0) { + case MIDI_NOTEOFF: + std::cout << std::hex << std::setw(2) << dye::aqua((uint16_t)status); + break; + case MIDI_NOTEON: + std::cout << std::hex << std::setw(2) << dye::light_aqua((uint16_t)status); + break; + case MIDI_MONOAFTERTOUCH: std::cout << std::hex << std::setw(2) << dye::yellow((uint16_t)status); + break; + case MIDI_CONTROLCHANGE: + std::cout << std::hex << std::setw(2) << dye::light_blue((uint16_t)status); + break; + default: + std::cout << std::hex << std::setw(2) << dye::light_purple((uint16_t)status); } } - else + else if (MIDI_BYTE_IS_SYSTEM_REALTIME_STATUS(status)) { - std::cout << std::endl; - if (isError) - { - std::cout << std::hex << std::setw(2) << dye::light_red((uint16_t)status); - } - else - { - std::cout << std::hex << std::setw(2) << dye::light_purple((uint16_t)status); - } + + std::cout << std::hex << std::setw(2) << dye::grey((uint16_t)status); } + } void DisplayDataByte(byte data, bool isError) @@ -137,6 +147,23 @@ void DisplayDataByte(byte data, bool isError) } +void DisplayDecodedChannelVoiceMessage(std::string messageName, uint8_t channel, std::string labelForByte1, uint8_t byte1) +{ + std::cout << std::left << std::setw(18) << std::setfill(' ') << dye::aqua(messageName) << " "; + std::cout << dye::grey("Channel: ") << std::setw(2) << std::right << std::dec << dye::yellow((uint16_t)channel) << ", "; + std::cout << std::setw(12) << dye::grey(labelForByte1) << ": " << std::setw(3) << std::right << std::dec << dye::yellow((uint16_t)byte1); + +} + +void DisplayDecodedChannelVoiceMessage(std::string messageName, uint8_t channel, std::string labelForByte1, uint8_t byte1, std::string labelForByte2, uint8_t byte2) +{ + DisplayDecodedChannelVoiceMessage(messageName, channel, labelForByte1, byte1); + + std::cout << ", "; + std::cout << std::setw(12) << dye::grey(labelForByte2) << ": " << std::setw(3) << std::right << std::dec << dye::yellow((uint16_t)byte2); + +} + void DisplayMidiMessage(DWORD dwMidiMessage, DWORD dwTimestamp, bool isError) { // message format 0 | data 2 | data 1 | status @@ -145,10 +172,22 @@ void DisplayMidiMessage(DWORD dwMidiMessage, DWORD dwTimestamp, bool isError) byte data1 = (dwMidiMessage & 0x0000FF00) >> 8; byte data2 = (dwMidiMessage & 0x00FF0000) >> 16; - DisplayStatusByte(status, isError); m_countStatusBytesReceived++; m_countAllBytesReceived++; + if (status == MIDI_ACTIVESENSE && !m_showActiveSense) + { + return; + } + + if (status == MIDI_TIMINGCLOCK && !m_showClock) + { + return; + } + + DisplayStatusByte(status, isError); + + if (MIDI_MESSAGE_IS_TWO_BYTES(status) || MIDI_MESSAGE_IS_THREE_BYTES(status)) { @@ -161,6 +200,85 @@ void DisplayMidiMessage(DWORD dwMidiMessage, DWORD dwTimestamp, bool isError) DisplayDataByte(data2, isError); m_countAllBytesReceived++; } + + // display a decoding of the message to the right + + if (status != MIDI_SYSEX && status != MIDI_EOX) + { + uint16_t spaces{ 0 }; + + if (MIDI_MESSAGE_IS_ONE_BYTE(status)) + { + spaces = 6; + } + else if (MIDI_MESSAGE_IS_TWO_BYTES(status)) + { + spaces = 3; + } + else + { + spaces = 1; + } + + std::cout << std::setw(spaces + 2) << std::setfill(' ') << ""; + + if (MIDI_STATUS_IS_CHANNEL_VOICE_MESSAGE(status)) + { + uint16_t channel = (status & 0x0F) + 1; + + switch (status & 0xF0) + { + case MIDI_NOTEOFF: + DisplayDecodedChannelVoiceMessage("Note Off", channel, "Note", data1, "Velocity", data2); + break; + case MIDI_NOTEON: + DisplayDecodedChannelVoiceMessage("Note On", channel, "Note", data1, "Velocity", data2); + break; + case MIDI_POLYAFTERTOUCH: + DisplayDecodedChannelVoiceMessage("Poly Aftertouch", channel, "Note", data1, "Pressure", data2); + break; + case MIDI_CONTROLCHANGE: + DisplayDecodedChannelVoiceMessage("Control Change", channel, "Controller", data1, "Value", data2); + break; + case MIDI_PROGRAMCHANGE: + DisplayDecodedChannelVoiceMessage("Program Change", channel, "Program", data1); + break; + case MIDI_MONOAFTERTOUCH: + DisplayDecodedChannelVoiceMessage("Channel Pressure", channel, "Pressure", data1); + break; + case MIDI_PITCHBEND: + DisplayDecodedChannelVoiceMessage("Pitch Bend", channel, "LSB", data1, "MSB", data2); + break; + default: + break; + } + } + else if (MIDI_BYTE_IS_SYSTEM_REALTIME_STATUS(status)) + { + switch (status) + { + case MIDI_TIMINGCLOCK: + std::cout << dye::light_aqua("System Real-Time: Clock"); + break; + case MIDI_START: + std::cout << dye::green("System Real-Time: Start"); + break; + case MIDI_CONTINUE: + std::cout << dye::light_yellow("System Real-Time: Continue"); + break; + case MIDI_STOP: + std::cout << dye::red("System Real-Time: Stop"); + break; + case MIDI_ACTIVESENSE: + std::cout << dye::grey("System Real-Time: Active Sense"); + break; + case MIDI_RESET: + std::cout << dye::light_red("System Real-Time: Reset"); + break; + } + } + } + } void DisplayMidiLongMessage(LPMIDIHDR header, DWORD dwTimestamp, bool error) @@ -266,6 +384,10 @@ int __cdecl main(int argc, char* argv[]) } } + // todo: need to take a command-line arg to show active sense messages. Defaults to false. + + // todo: need to take a command-line arg to show timing clock messages. Defaults to false. + if (!portNumberProvided) { DisplayAllWinMMInputs(); @@ -283,6 +405,17 @@ int __cdecl main(int argc, char* argv[]) std::cout << dye::aqua(" for input. Hit escape to cancel."); std::cout << std::endl << std::endl; + if (!m_showActiveSense) + { + std::cout << dye::aqua("Hiding active sense messages. "); + } + + if (!m_showClock) + { + std::cout << dye::aqua("Hiding clock messages."); + } + + std::cout << std::endl; } else { From 491cb2739d2017b8eca9d76bbe16e6bcc3a58177 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Wed, 19 Feb 2025 23:17:41 -0500 Subject: [PATCH 03/29] Add wait for escape key to send message commands --- .../Endpoint/EndpointCommandSettings.cs | 5 + .../Endpoint/EndpointSendMessageCommand.cs | 99 +++++++++++++------ .../EndpointSendMessagesFileCommand.cs | 28 ++++++ 3 files changed, 104 insertions(+), 28 deletions(-) diff --git a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointCommandSettings.cs b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointCommandSettings.cs index 885a7819..ec7fb422 100644 --- a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointCommandSettings.cs +++ b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointCommandSettings.cs @@ -30,6 +30,11 @@ internal class SendMessageCommandSettings : EndpointCommandSettings [DefaultValue(MidiWordDataFormat.Hex)] public MidiWordDataFormat WordDataFormat { get; set; } + [LocalizedDescription("ParameterSendMessageNoWait")] + [CommandOption("-n|--no-wait")] + [DefaultValue(false)] + public bool NoWait { get; set; } + //[LocalizedDescription("TODO ParameterSendMessageAutoNegotiation")] //[CommandOption("-n|--auto-negotiation")] //[DefaultValue(true)] diff --git a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessageCommand.cs b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessageCommand.cs index 729198c5..cea0366f 100644 --- a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessageCommand.cs +++ b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessageCommand.cs @@ -209,6 +209,7 @@ public override int Execute(CommandContext context, Settings settings) uint messagesSent = 0; uint messagesAttempted = 0; uint messageFailures = 0; + uint totalMessageRetries = 0; UInt64 maxTimestampScheduled = 0; @@ -259,22 +260,53 @@ public override int Execute(CommandContext context, Settings settings) } messagesAttempted++; - var sendResult = connection.SendSingleMessageWordArray(timestamp, 0, (byte)_parsedWords!.Count(), _parsedWords); - if (MidiEndpointConnection.SendMessageSucceeded(sendResult)) + bool continueToRetry = true; + uint retryAttempts = 0; + uint maxRetryAttempts = 500; + while (continueToRetry) { - messagesSent++; + var sendResult = connection.SendSingleMessageWordArray(timestamp, 0, (byte)_parsedWords!.Count(), _parsedWords); - if (timestamp > maxTimestampScheduled) + if (MidiEndpointConnection.SendMessageSucceeded(sendResult)) { - maxTimestampScheduled = timestamp; + messagesSent++; + continueToRetry = false; // message was sent, so no need to retry + + if (timestamp > maxTimestampScheduled) + { + maxTimestampScheduled = timestamp; + } } + else + { + continueToRetry = false; + // if the problem is that the buffer is full, we will retry + // we should sleep in this loop, but the sleep resolution is not + // low enough to make this a reasonable thing to do. + if ((sendResult & MidiSendMessageResults.BufferFull) == MidiSendMessageResults.BufferFull) + { + if (retryAttempts < maxRetryAttempts) + { + continueToRetry = true; + retryAttempts++; + totalMessageRetries++; + } + else + { + messageFailures++; + } + } + else + { + messageFailures++; + } + } } - else - { - messageFailures++; - } + + + if (settings.DelayBetweenMessages > 0) { @@ -355,7 +387,7 @@ public override int Execute(CommandContext context, Settings settings) if (messageFailures > 0) { // todo: localize - AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError($"Failed to send {messageFailures} of a planned {settings.Count} message(s).")); + AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError($"{messageFailures} failed message sending attempts.")); } else { @@ -396,9 +428,15 @@ public override int Execute(CommandContext context, Settings settings) AnsiConsoleOutput.ConvertTicksToFriendlyTimeUnit(averageTicks, out averageTime, out averageTimeLabel); AnsiConsole.MarkupLine($"Total send time [steelblue1]{totalTime.ToString("N2")} {totalTimeLabel}[/], averaging [steelblue1]{averageTime.ToString("N2")} {averageTimeLabel}[/] per message."); + } + if (totalMessageRetries > 0) + { + AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatWarning($"{totalMessageRetries} retried send operations.")); } + + // for scheduled messages, we wait if (maxTimestampScheduled > MidiClock.Now) { int sleepMs = (int)Math.Ceiling(MidiClock.ConvertTimestampTicksToMilliseconds(maxTimestampScheduled - MidiClock.Now)); @@ -410,29 +448,34 @@ public override int Execute(CommandContext context, Settings settings) Thread.Sleep(sleepMs); } - // BEGIN TEMP -------------------------------------------------- - //AnsiConsole.MarkupLine(Strings.MonitorPressEscapeToStopMonitoringMessage); - //AnsiConsole.WriteLine(); - //bool continueWaiting = true; + if (!settings.NoWait) + { + AnsiConsole.MarkupLine(Strings.SendMessagePressEscapeToCloseConnectionMessage); + AnsiConsole.WriteLine(); - //while (continueWaiting) - //{ - // if (Console.KeyAvailable) - // { - // var keyInfo = Console.ReadKey(true); + bool continueWaiting = true; - // if (keyInfo.Key == ConsoleKey.Escape) - // { - // continueWaiting = false; + while (continueWaiting) + { + if (Console.KeyAvailable) + { + var keyInfo = Console.ReadKey(true); - // AnsiConsole.WriteLine(); - // AnsiConsole.MarkupLine("🛑 " + Strings.MonitorEscapePressedMessage); - // } + if (keyInfo.Key == ConsoleKey.Escape) + { + continueWaiting = false; - // } - //} - // END TEMP -------------------------------------------------- + AnsiConsole.WriteLine(); + AnsiConsole.MarkupLine("🛑 " + Strings.SendMessageEscapePressedMessage); + } + } + else + { + Thread.Sleep(500); + } + } + } session.DisconnectEndpointConnection(connection.ConnectionId); diff --git a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessagesFileCommand.cs b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessagesFileCommand.cs index 2810005b..d834e0f3 100644 --- a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessagesFileCommand.cs +++ b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessagesFileCommand.cs @@ -378,6 +378,34 @@ public override int Execute(CommandContext context, Settings settings) } + if (!settings.NoWait) + { + AnsiConsole.MarkupLine(Strings.SendMessagePressEscapeToCloseConnectionMessage); + AnsiConsole.WriteLine(); + + bool continueWaiting = true; + + while (continueWaiting) + { + if (Console.KeyAvailable) + { + var keyInfo = Console.ReadKey(true); + + if (keyInfo.Key == ConsoleKey.Escape) + { + continueWaiting = false; + + AnsiConsole.WriteLine(); + AnsiConsole.MarkupLine("🛑 " + Strings.SendMessageEscapePressedMessage); + } + } + else + { + Thread.Sleep(500); + } + } + } + if (session != null) session.Dispose(); From ae21753b6acdb8f187da3e551ff9efe73137859c Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Thu, 20 Feb 2025 20:58:27 -0500 Subject: [PATCH 04/29] Add wait to console send --- build/staging/version/BundleInfo.wxi | 2 +- .../version/WindowsMidiServicesVersion.cs | 6 +-- .../version/WindowsMidiServicesVersion.h | 6 +-- .../consuming-midi-api.md | 6 +-- .../basics/client-basics-cpp.vcxproj | 2 +- samples/cpp-winrt/basics/packages.config | 2 +- .../loopback-endpoints-cpp.vcxproj | 2 +- .../loopback-endpoints/packages.config | 2 +- samples/cpp-winrt/send-speed/packages.config | 2 +- .../send-speed/send-speed-cpp.vcxproj | 2 +- .../simple-app-to-app-midi/packages.config | 2 +- .../simple-app-to-app-cpp.vcxproj | 2 +- .../static-enum-endpoints/packages.config | 2 +- .../static-enum-endpoints-cpp.vcxproj | 2 +- .../cpp-winrt/watch-endpoints/packages.config | 2 +- .../watch-endpoints-cpp.vcxproj | 2 +- src/app-sdk/midi1monitor/main.cpp | 51 +++++++++++++------ src/app-sdk/mididiag/mididiag.vcxproj | 2 +- src/app-sdk/mididiag/packages.config | 2 +- src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj | 2 +- src/app-sdk/midimdnsinfo/packages.config | 2 +- src/app-sdk/midiusbinfo/midiusbinfo.vcxproj | 2 +- src/app-sdk/midiusbinfo/packages.config | 2 +- .../main-bundle/Bundle.wxs | 2 + ...iEndpointConnection_SendInfrastructure.cpp | 25 +-------- src/oob-setup-in-dev/main-bundle/Bundle.wxs | 2 + src/oob-setup/main-bundle/Bundle.wxs | 2 + .../Midi/Properties/launchSettings.json | 4 ++ .../Midi/Resources/Strings.Designer.cs | 18 +++++++ .../midi-console/Midi/Resources/Strings.resx | 6 +++ 30 files changed, 98 insertions(+), 68 deletions(-) diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index f1cdf1e2..c604f207 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,4 +1,4 @@ - + diff --git a/build/staging/version/WindowsMidiServicesVersion.cs b/build/staging/version/WindowsMidiServicesVersion.cs index 2d1d372a..3d4909e2 100644 --- a/build/staging/version/WindowsMidiServicesVersion.cs +++ b/build/staging/version/WindowsMidiServicesVersion.cs @@ -6,12 +6,12 @@ public static class MidiBuildInformation { public const string Source = "GitHub Preview"; public const string Name = "Customer Preview 2"; - public const string BuildFullVersion = "1.0.3-preview-11.250218-1656"; + public const string BuildFullVersion = "1.0.3-preview-11.250219-2318"; public const string VersionMajor = "1"; public const string VersionMinor = "0"; public const string VersionRevision = "3"; - public const string VersionDateNumber = "250218"; - public const string VersionTimeNumber = "1656"; + public const string VersionDateNumber = "250219"; + public const string VersionTimeNumber = "2318"; } } diff --git a/build/staging/version/WindowsMidiServicesVersion.h b/build/staging/version/WindowsMidiServicesVersion.h index 83fbd7ef..ccf35011 100644 --- a/build/staging/version/WindowsMidiServicesVersion.h +++ b/build/staging/version/WindowsMidiServicesVersion.h @@ -5,12 +5,12 @@ #define WINDOWS_MIDI_SERVICES_BUILD_SOURCE L"GitHub Preview" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_NAME L"Customer Preview 2" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.3-preview-11.250218-1656" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.3-preview-11.250219-2318" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MAJOR L"1" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MINOR L"0" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_REVISION L"3" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250218" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"1656" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250219" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"2318" #endif diff --git a/docs/other-developer-docs/consuming-midi-api.md b/docs/other-developer-docs/consuming-midi-api.md index d8b14d56..b413cba1 100644 --- a/docs/other-developer-docs/consuming-midi-api.md +++ b/docs/other-developer-docs/consuming-midi-api.md @@ -77,13 +77,11 @@ You will follow a similar approach to C++ using windows-rs instead of C++/WinRT. ## Consuming from C++ without Visual Studio (using cmake or other tools) -The C++/WinRT tool `cppwinrt.exe` will generate a set of standard C++ header files including `Microsoft.Windows.Devices.Midi2.h` which you can pull in and include in your project. The header file projections for WinRT types outside of `Microsoft::Windows::Devices::Midi2` are generated from the Windows SDK and included in a subfolder. When we ship Windows MIDI Services in-box in Windows, this new MIDI API will be projected in the same way as all the others in the Windows SDK. +The C++/WinRT tool `cppwinrt.exe` will generate a set of standard C++ header files including `Microsoft.Windows.Devices.Midi2.h` which you can pull in and include in your project. The header file projections for WinRT types outside of `Microsoft::Windows::Devices::Midi2` are generated from the Windows SDK and included in a subfolder. First, install the latest Windows SDK. You can get the SDK from the [Windows Dev Center](https://developer.microsoft.com/windows/downloads/windows-sdk/) -The SDK install includes the `cppwinrt.exe` tool. For the 10.0.22621.0 version of the SDK, it is found here on my PC: -`C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64` and -`C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\arm64` . Pick the version appropriate for your development PC architecture. +The SDK install includes a version of the `cppwinrt.exe` tool. However, the latest version of this tool is [available via NuGet](https://www.nuget.org/packages/Microsoft.Windows.CppWinRT/), and is preferable to the older versions shipping in the Windows SDK. You can end up with version mismatches if you use an older version. Normally, all SDK header files, on my PC with the 10.0.22621.0 version of the SDK installed, are located here `C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\cppwinrt\winrt` diff --git a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj index 0a464e21..2ecd2770 100644 --- a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj +++ b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 true true false diff --git a/samples/cpp-winrt/basics/packages.config b/samples/cpp-winrt/basics/packages.config index c391249b..a562003c 100644 --- a/samples/cpp-winrt/basics/packages.config +++ b/samples/cpp-winrt/basics/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj index 10edeb84..24e0bab6 100644 --- a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 true true true diff --git a/samples/cpp-winrt/loopback-endpoints/packages.config b/samples/cpp-winrt/loopback-endpoints/packages.config index c391249b..a562003c 100644 --- a/samples/cpp-winrt/loopback-endpoints/packages.config +++ b/samples/cpp-winrt/loopback-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/send-speed/packages.config b/samples/cpp-winrt/send-speed/packages.config index c391249b..a562003c 100644 --- a/samples/cpp-winrt/send-speed/packages.config +++ b/samples/cpp-winrt/send-speed/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj index b1579dd7..43afb85e 100644 --- a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj +++ b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 true true true diff --git a/samples/cpp-winrt/simple-app-to-app-midi/packages.config b/samples/cpp-winrt/simple-app-to-app-midi/packages.config index c391249b..a562003c 100644 --- a/samples/cpp-winrt/simple-app-to-app-midi/packages.config +++ b/samples/cpp-winrt/simple-app-to-app-midi/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj index c9d8511d..13806901 100644 --- a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj +++ b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 true true true diff --git a/samples/cpp-winrt/static-enum-endpoints/packages.config b/samples/cpp-winrt/static-enum-endpoints/packages.config index c391249b..a562003c 100644 --- a/samples/cpp-winrt/static-enum-endpoints/packages.config +++ b/samples/cpp-winrt/static-enum-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj index 3b028605..39e4ec03 100644 --- a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 true true true diff --git a/samples/cpp-winrt/watch-endpoints/packages.config b/samples/cpp-winrt/watch-endpoints/packages.config index c391249b..a562003c 100644 --- a/samples/cpp-winrt/watch-endpoints/packages.config +++ b/samples/cpp-winrt/watch-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj index cba27659..af3601eb 100644 --- a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 true true true diff --git a/src/app-sdk/midi1monitor/main.cpp b/src/app-sdk/midi1monitor/main.cpp index c8abcfb4..17b2f552 100644 --- a/src/app-sdk/midi1monitor/main.cpp +++ b/src/app-sdk/midi1monitor/main.cpp @@ -42,10 +42,20 @@ void WriteLabel(std::string label) std::cout << std::left << std::setw(25) << std::setfill(' ') << dye::grey(fullLabel); } -std::map m_midiInputDevices; + +struct MidiInputPort +{ + uint16_t Index; + std::wstring Name; +}; + +std::vector m_midiInputs{}; + void LoadWinMMDevices() { + //std::map midiInputDevices; + auto inputDeviceCount = midiInGetNumDevs(); for (uint16_t i = 0; i < inputDeviceCount; i++) @@ -56,26 +66,34 @@ void LoadWinMMDevices() if (result == MMSYSERR_NOERROR) { - m_midiInputDevices.insert_or_assign(i, inputCaps); + MidiInputPort port{}; + port.Index = i; + port.Name = inputCaps.szPname; + + m_midiInputs.push_back(port); } } + + std::sort(m_midiInputs.begin(), m_midiInputs.end(), + [](MidiInputPort a, MidiInputPort b) + { + return internal::ToLowerWStringCopy(a.Name) < internal::ToLowerWStringCopy(b.Name); + }); + } void DisplayAllWinMMInputs() { - WriteInfo("Available Input Ports"); - - // todo: should sort this + WriteInfo(std::to_string(m_midiInputs.size()) + " Available Input Ports"); - - for (auto const& capsEntry : m_midiInputDevices) + for (auto const& port : m_midiInputs) { std::cout - << std::setw(3) << dye::yellow(capsEntry.first) + << std::setw(3) << dye::yellow(port.Index) << dye::grey(" : "); std::wcout - << capsEntry.second.szPname + << port.Name << std::endl; } } @@ -398,24 +416,27 @@ int __cdecl main(int argc, char* argv[]) } - if (auto const& port = m_midiInputDevices.find(portNumber); port != m_midiInputDevices.end()) + if (auto const& port = std::find_if(m_midiInputs.begin(), m_midiInputs.end(), + [&portNumber](const MidiInputPort& p) { return p.Index == portNumber; }); + port != m_midiInputs.end()) { + std::cout << std::endl; std::cout << dye::aqua("Monitoring "); - std::wcout << port->second.szPname; + std::wcout << port->Name; std::cout << dye::aqua(" for input. Hit escape to cancel."); - std::cout << std::endl << std::endl; + std::cout << std::endl; if (!m_showActiveSense) { - std::cout << dye::aqua("Hiding active sense messages. "); + std::cout << dye::aqua("Hiding") << dye::light_red(" active sense ") << dye::aqua("messages. "); } if (!m_showClock) { - std::cout << dye::aqua("Hiding clock messages."); + std::cout << dye::aqua("Hiding") << dye::light_red(" clock ") << dye::aqua("messages."); } - std::cout << std::endl; + std::cout << std::endl << std::endl; } else { diff --git a/src/app-sdk/mididiag/mididiag.vcxproj b/src/app-sdk/mididiag/mididiag.vcxproj index c6e214b4..306e39df 100644 --- a/src/app-sdk/mididiag/mididiag.vcxproj +++ b/src/app-sdk/mididiag/mididiag.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 true true true diff --git a/src/app-sdk/mididiag/packages.config b/src/app-sdk/mididiag/packages.config index 2788f5cf..40e44a47 100644 --- a/src/app-sdk/mididiag/packages.config +++ b/src/app-sdk/mididiag/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj index ee05ee56..7570876c 100644 --- a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj +++ b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 true true true diff --git a/src/app-sdk/midimdnsinfo/packages.config b/src/app-sdk/midimdnsinfo/packages.config index 2788f5cf..40e44a47 100644 --- a/src/app-sdk/midimdnsinfo/packages.config +++ b/src/app-sdk/midimdnsinfo/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj index 129ae054..9db2a6e4 100644 --- a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj +++ b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250218-1656 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 true true true diff --git a/src/app-sdk/midiusbinfo/packages.config b/src/app-sdk/midiusbinfo/packages.config index 2788f5cf..40e44a47 100644 --- a/src/app-sdk/midiusbinfo/packages.config +++ b/src/app-sdk/midiusbinfo/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/sdk-runtime-installer/main-bundle/Bundle.wxs b/src/app-sdk/sdk-runtime-installer/main-bundle/Bundle.wxs index a033b957..a522fecf 100644 --- a/src/app-sdk/sdk-runtime-installer/main-bundle/Bundle.wxs +++ b/src/app-sdk/sdk-runtime-installer/main-bundle/Bundle.wxs @@ -23,6 +23,8 @@ diff --git a/src/app-sdk/winrt/MidiEndpointConnection_SendInfrastructure.cpp b/src/app-sdk/winrt/MidiEndpointConnection_SendInfrastructure.cpp index df223755..e5a7683d 100644 --- a/src/app-sdk/winrt/MidiEndpointConnection_SendInfrastructure.cpp +++ b/src/app-sdk/winrt/MidiEndpointConnection_SendInfrastructure.cpp @@ -28,30 +28,6 @@ namespace winrt::Microsoft::Windows::Devices::Midi2::implementation switch (hr) { - //case HR_S_MIDI_SENDMSG_IMMEDIATE: - // result |= midi2::MidiSendMessageResult::SentImmediately; - // break; - - //case HR_S_MIDI_SENDMSG_SCHEDULED: - // result |= midi2::MidiSendMessageResult::Scheduled; - // break; - - //case HR_S_MIDI_SENDMSG_SYSEX_PARKED: - // result |= midi2::MidiSendMessageResult::SystemExclusiveParked; - // break; - - //case HR_E_MIDI_SENDMSG_BUFFER_FULL: - // result |= midi2::MidiSendMessageResult::BufferFull; - // break; - - //case HR_E_MIDI_SENDMSG_SCHEDULER_QUEUE_FULL: - // result |= midi2::MidiSendMessageResult::SchedulerQueueFull; - // break; - - //case HR_E_MIDI_SENDMSG_INVALID_MESSAGE: - // result |= midi2::MidiSendMessageResult::InvalidMessageOther; - // break; - case HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER): result |= midi2::MidiSendMessageResults::BufferFull; break; @@ -145,6 +121,7 @@ namespace winrt::Microsoft::Windows::Devices::Midi2::implementation { auto hr = endpoint->SendMidiMessage(data, sizeInBytes, timestamp); + if (FAILED(hr)) { LOG_IF_FAILED(hr); // this also generates a fallback error with file and line number info diff --git a/src/oob-setup-in-dev/main-bundle/Bundle.wxs b/src/oob-setup-in-dev/main-bundle/Bundle.wxs index 92e91e39..0d0ce3b9 100644 --- a/src/oob-setup-in-dev/main-bundle/Bundle.wxs +++ b/src/oob-setup-in-dev/main-bundle/Bundle.wxs @@ -24,6 +24,8 @@ diff --git a/src/oob-setup/main-bundle/Bundle.wxs b/src/oob-setup/main-bundle/Bundle.wxs index 2bf6cfd5..bebea5f9 100644 --- a/src/oob-setup/main-bundle/Bundle.wxs +++ b/src/oob-setup/main-bundle/Bundle.wxs @@ -25,6 +25,8 @@ diff --git a/src/user-tools/midi-console/Midi/Properties/launchSettings.json b/src/user-tools/midi-console/Midi/Properties/launchSettings.json index 88016ea0..84c62b0e 100644 --- a/src/user-tools/midi-console/Midi/Properties/launchSettings.json +++ b/src/user-tools/midi-console/Midi/Properties/launchSettings.json @@ -34,6 +34,10 @@ "Enum Endpoints": { "commandName": "Project", "commandLineArgs": "enum endpoints" + }, + "Blast Messages": { + "commandName": "Project", + "commandLineArgs": "endpoint send-message 0x20905060 --count 20000 --pause 0" } } } \ No newline at end of file diff --git a/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs b/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs index 8833362d..468d907a 100644 --- a/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs +++ b/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs @@ -1293,6 +1293,15 @@ internal static string ParameterSendMessageEndpointDirectionDescription { } } + /// + /// Looks up a localized string similar to Do not prompt the user to hit any key to close the connection. + /// + internal static string ParameterSendMessageNoWait { + get { + return ResourceManager.GetString("ParameterSendMessageNoWait", resourceCulture); + } + } + /// /// Looks up a localized string similar to Path and filename of the text file to send. This can include lines which begin with # for a comment, or are completely empty for spacing. Remaining lines must be valid delimited (big-endian) hexadecimal UMP words.. /// @@ -2306,6 +2315,15 @@ internal static string SendMessageFloodWarning { } } + /// + /// Looks up a localized string similar to Press escape to close the connection when the endpoint has received all the messages.. + /// + internal static string SendMessagePressEscapeToCloseConnectionMessage { + get { + return ResourceManager.GetString("SendMessagePressEscapeToCloseConnectionMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Press escape to stop sending messages.. /// diff --git a/src/user-tools/midi-console/Midi/Resources/Strings.resx b/src/user-tools/midi-console/Midi/Resources/Strings.resx index f7df8b9f..63e1d474 100644 --- a/src/user-tools/midi-console/Midi/Resources/Strings.resx +++ b/src/user-tools/midi-console/Midi/Resources/Strings.resx @@ -929,4 +929,10 @@ Timestamp Unknown + + Do not prompt the user to hit any key to close the connection + + + Press escape to close the connection when the endpoint has received all the messages. + \ No newline at end of file From 40e29c8b684b7207d914d96221485a6739786259 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Thu, 20 Feb 2025 21:00:16 -0500 Subject: [PATCH 05/29] Change name of Diagnostics endpoints so they aren't confused for telemetry --- src/api/Transport/DiagnosticsTransport/transport_defs.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/Transport/DiagnosticsTransport/transport_defs.h b/src/api/Transport/DiagnosticsTransport/transport_defs.h index 82a079f0..cd1d5dba 100644 --- a/src/api/Transport/DiagnosticsTransport/transport_defs.h +++ b/src/api/Transport/DiagnosticsTransport/transport_defs.h @@ -20,19 +20,19 @@ // TODO: Names should be moved to .rc for localization #define TRANSPORT_PARENT_ID L"MIDIU_DIAG_TRANSPORT" -#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 2.0 Diagnostics Devices" +#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 2.0 Service Tests" #define DEFAULT_LOOPBACK_BIDI_A_ID L"MIDIU_DIAG_LOOPBACK_A" #define LOOPBACK_BIDI_A_UNIQUE_ID L"LOOPBACK_A" -#define DEFAULT_LOOPBACK_BIDI_A_NAME L"Diagnostics Loopback A" +#define DEFAULT_LOOPBACK_BIDI_A_NAME L"Service Test Loopback A" #define DEFAULT_LOOPBACK_BIDI_B_ID L"MIDIU_DIAG_LOOPBACK_B" #define LOOPBACK_BIDI_B_UNIQUE_ID L"LOOPBACK_B" -#define DEFAULT_LOOPBACK_BIDI_B_NAME L"Diagnostics Loopback B" +#define DEFAULT_LOOPBACK_BIDI_B_NAME L"Service Test Loopback B" #define DEFAULT_PING_BIDI_ID L"MIDIU_DIAG_PING" #define PING_BIDI_UNIQUE_ID L"PING" -#define DEFAULT_PING_BIDI_NAME L"Diagnostics Ping (Internal)" +#define DEFAULT_PING_BIDI_NAME L"Service Test Ping (Internal)" From 0ce7604f18a08844fc4ea9edb4ec6304f1abdab8 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Fri, 21 Feb 2025 00:03:08 -0500 Subject: [PATCH 06/29] Add group terminal block direction to mididiag --- src/app-sdk/mididiag/main.cpp | 17 +++++++++++++++++ src/app-sdk/mididiag/mididiag_field_defs.h | 1 + 2 files changed, 18 insertions(+) diff --git a/src/app-sdk/mididiag/main.cpp b/src/app-sdk/mididiag/main.cpp index cd6e98d1..94624330 100644 --- a/src/app-sdk/mididiag/main.cpp +++ b/src/app-sdk/mididiag/main.cpp @@ -622,6 +622,23 @@ bool DoSectionMidi2ApiEndpoints(_In_ bool const verbose) OutputStringField(MIDIDIAG_FIELD_LABEL_GTB_NAME, gtb.Name()); OutputNumericField(MIDIDIAG_FIELD_LABEL_GTB_FIRST_GROUP, gtb.FirstGroup().DisplayValue()); OutputNumericField(MIDIDIAG_FIELD_LABEL_GTB_GROUP_COUNT, gtb.GroupCount()); + + std::wstring gtbDirection{}; + + if (gtb.Direction() == midi2::MidiGroupTerminalBlockDirection::Bidirectional) + { + gtbDirection = L"Bidirectional"; + } + else if (gtb.Direction() == midi2::MidiGroupTerminalBlockDirection::BlockInput) + { + gtbDirection = L"Message Destination"; + } + else if (gtb.Direction() == midi2::MidiGroupTerminalBlockDirection::BlockOutput) + { + gtbDirection = L"Message Source"; + } + + OutputStringField(MIDIDIAG_FIELD_LABEL_GTB_DIRECTION, gtbDirection); } if (device.GetGroupTerminalBlocks().Size() > 0) diff --git a/src/app-sdk/mididiag/mididiag_field_defs.h b/src/app-sdk/mididiag/mididiag_field_defs.h index be810090..d02a383d 100644 --- a/src/app-sdk/mididiag/mididiag_field_defs.h +++ b/src/app-sdk/mididiag/mididiag_field_defs.h @@ -82,6 +82,7 @@ #define MIDIDIAG_FIELD_LABEL_GTB_NAME "gtb_name" #define MIDIDIAG_FIELD_LABEL_GTB_FIRST_GROUP "gtb_first_group_number" #define MIDIDIAG_FIELD_LABEL_GTB_GROUP_COUNT "gtb_group_count" +#define MIDIDIAG_FIELD_LABEL_GTB_DIRECTION "gtb_direction" From 4bbbc48c2ba7b02f01407d83e7e63033524033df Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Fri, 21 Feb 2025 00:03:38 -0500 Subject: [PATCH 07/29] Add debug statements to KSA endpoint manager device update --- src/api/Inc/midi_naming.h | 13 ++-- .../Midi2.KSAggregateMidiEndpointManager.cpp | 67 +++++++++++-------- .../Midi2.KSAggregateMidiEndpointManager.h | 1 + 3 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/api/Inc/midi_naming.h b/src/api/Inc/midi_naming.h index 6f37d1d5..965880f1 100644 --- a/src/api/Inc/midi_naming.h +++ b/src/api/Inc/midi_naming.h @@ -26,10 +26,13 @@ namespace WindowsMidiServicesInternal::Midi1PortNaming // Used by ESI, MOTU, and others. We don't want to mess up other names, so check only // for whole word. We do other removal in the next step - if (pinName == L"MIDI" || - pinName == L"Out" || pinName == L"OUT" || pinName == L"out" || - pinName == L"In" || pinName == L"IN" || pinName == L"in" || - pinName == L"IO" + + auto checkPinName = internal::ToLowerTrimmedWStringCopy(pinName); + + if (checkPinName == L"midi" || + checkPinName == L"out" || + checkPinName == L"in" || + checkPinName == L"io" ) { cleanedPinName = L""; @@ -267,6 +270,8 @@ namespace WindowsMidiServicesInternal::Midi1PortNaming if (truncateToWinMMLimit) { + // if the name is too long, try using just the pin name or just the filter name + if (name.length() + 1 > MAXPNAMELEN) { if (!cleanedPinName.empty()) diff --git a/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp b/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp index 1a4ce00e..a9a7cd58 100644 --- a/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp +++ b/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp @@ -17,22 +17,6 @@ using namespace winrt::Windows::Foundation::Collections; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; -// if this is defined, then KSMidiEndpointManager will publish a BiDi endpoint -// for pairs of midi in & out endpoints on the same filter. -// Filters which do not have a a single pair of midi in and out, -// separate midi in and out SWD's will always be created. -//#define CREATE_KS_BIDI_SWDS - -// If this is defined, we will skip building midi in and midi out -// SWD's for endpoints where BIDI SWD's are created. -// Otherwise, MidiIn, Out, and BiDi will be created. Creating all 3 -// is OK for unit testing one at a time, however is not valid for -// normal usage because MidiIn and MidiOut use the same pins as -// MidiBidi, so only MidiIn and MidiOut or MidiBidi can be used, -// never all 3 at the same time. -//#define BIDI_REPLACES_INOUT_SWDS - - _Use_decl_annotations_ HRESULT @@ -241,7 +225,7 @@ CMidi2KSAggregateMidiEndpointManager::CreateMidiUmpEndpoint( std::wstring userSuppliedPortName{}; // TODO: This should come from config file - bool useOldStyleNaming{ true }; // TODO: This should come from registry and property + bool useOldStyleNaming{ true }; // TODO: This should come from registry and property std::wstring deviceContainerName{}; // TODO: Need to get this from the device info std::wstring deviceManufacturerName{}; // TODO: Need to get this from the parent device bool isUsingVendorDriver{ false }; // TODO: Need to get this from the device information @@ -469,6 +453,7 @@ CMidi2KSAggregateMidiEndpointManager::CreateMidiUmpEndpoint( // todo: return new device interface id + auto lock = m_availableEndpointDefinitionsLock.lock(); // Add to internal endpoint manager m_availableEndpointDefinitions.insert_or_assign(masterEndpointDefinition.ParentDeviceInstanceId, masterEndpointDefinition); @@ -493,7 +478,8 @@ CMidi2KSAggregateMidiEndpointManager::CreateMidiUmpEndpoint( } -winrt::hstring GetStringProperty(_In_ DeviceInformation di, _In_ winrt::hstring propertyName, _In_ winrt::hstring defaultValue) +winrt::hstring +GetStringProperty(_In_ DeviceInformation di, _In_ winrt::hstring propertyName, _In_ winrt::hstring defaultValue) { auto prop = di.Properties().Lookup(propertyName); @@ -512,7 +498,8 @@ winrt::hstring GetStringProperty(_In_ DeviceInformation di, _In_ winrt::hstring return value; } -HRESULT GetPinName(_In_ HANDLE const hFilter, _In_ UINT const pinIndex, _Inout_ std::wstring& pinName) +HRESULT +GetPinName(_In_ HANDLE const hFilter, _In_ UINT const pinIndex, _Inout_ std::wstring& pinName) { std::unique_ptr pinNameData; ULONG pinNameDataSize{ 0 }; @@ -540,7 +527,8 @@ HRESULT GetPinName(_In_ HANDLE const hFilter, _In_ UINT const pinIndex, _Inout_ return E_FAIL; } -HRESULT GetPinDataFlow(_In_ HANDLE const hFilter, _In_ UINT const pinIndex, _Inout_ KSPIN_DATAFLOW& dataFlow) +HRESULT +GetPinDataFlow(_In_ HANDLE const hFilter, _In_ UINT const pinIndex, _Inout_ KSPIN_DATAFLOW& dataFlow) { auto dataFlowHR = PinPropertySimple( hFilter, @@ -564,6 +552,15 @@ _Use_decl_annotations_ HRESULT CMidi2KSAggregateMidiEndpointManager::GetKSDriverSuppliedName(HANDLE hInstantiatedFilter, std::wstring& name) { + TraceLoggingWrite( + MidiKSAggregateTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + // get the name GUID KSCOMPONENTID componentId{}; @@ -592,7 +589,6 @@ CMidi2KSAggregateMidiEndpointManager::GetKSDriverSuppliedName(HANDLE hInstantiat { // we have the GUID where this name is stored, so get the driver-supplied name from the registry - WCHAR nameFromRegistry[MAX_PATH]{ 0 }; // this should only be MAXPNAMELEN, but if someone tampered with it, could be larger, hence MAX_PATH std::wstring regKey = L"SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\" + internal::GuidToString(componentId.Name); @@ -877,7 +873,8 @@ CMidi2KSAggregateMidiEndpointManager::OnDeviceAdded( _Use_decl_annotations_ -HRESULT CMidi2KSAggregateMidiEndpointManager::OnDeviceRemoved(DeviceWatcher, DeviceInformationUpdate device) +HRESULT +CMidi2KSAggregateMidiEndpointManager::OnDeviceRemoved(DeviceWatcher, DeviceInformationUpdate device) { TraceLoggingWrite( MidiKSAggregateTransportTelemetryProvider::Provider(), @@ -894,6 +891,8 @@ HRESULT CMidi2KSAggregateMidiEndpointManager::OnDeviceRemoved(DeviceWatcher, Dev // the interface is no longer active, search through our m_AvailableMidiPins to identify // every entry with this filter interface id, and remove the SWD and remove the pin(s) from // the m_AvailableMidiPins list. + + auto lock = m_availableEndpointDefinitionsLock.lock(); do { auto item = m_availableEndpointDefinitions.find((std::wstring)device.Id()); @@ -925,16 +924,28 @@ HRESULT CMidi2KSAggregateMidiEndpointManager::OnDeviceRemoved(DeviceWatcher, Dev } _Use_decl_annotations_ -HRESULT CMidi2KSAggregateMidiEndpointManager::OnDeviceUpdated(DeviceWatcher, DeviceInformationUpdate update) +HRESULT +CMidi2KSAggregateMidiEndpointManager::OnDeviceUpdated(DeviceWatcher, DeviceInformationUpdate update) { + TraceLoggingWrite( + MidiKSAggregateTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Device properties updated", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(update.Id().c_str(), "device id"), + TraceLoggingUInt32(update.Properties().Size(), "count updated properties") + ); + //see this function for info on the IDeviceInformationUpdate object: https://learn.microsoft.com/en-us/windows/uwp/devices-sensors/enumerate-devices#enumerate-and-watch-devices // NOTE: When you change the assigned driver for the device, instead of sending - // separate remove/add events, this gets a couple of OnDeviceUpdate notifications. + // separate remove/add events, this SOMETIMES gets a couple of OnDeviceUpdate notifications. for (auto const& prop : update.Properties()) { - OutputDebugString((std::wstring(L"KSA: ") + std::wstring(prop.Key().c_str())).c_str()); + OutputDebugString((std::wstring(L"KSA Updated Property with Key: ") + std::wstring(prop.Key().c_str())).c_str()); } @@ -942,14 +953,16 @@ HRESULT CMidi2KSAggregateMidiEndpointManager::OnDeviceUpdated(DeviceWatcher, Dev } _Use_decl_annotations_ -HRESULT CMidi2KSAggregateMidiEndpointManager::OnDeviceStopped(DeviceWatcher, winrt::Windows::Foundation::IInspectable) +HRESULT +CMidi2KSAggregateMidiEndpointManager::OnDeviceStopped(DeviceWatcher, winrt::Windows::Foundation::IInspectable) { m_EnumerationCompleted.SetEvent(); return S_OK; } _Use_decl_annotations_ -HRESULT CMidi2KSAggregateMidiEndpointManager::OnEnumerationCompleted(DeviceWatcher, winrt::Windows::Foundation::IInspectable) +HRESULT +CMidi2KSAggregateMidiEndpointManager::OnEnumerationCompleted(DeviceWatcher, winrt::Windows::Foundation::IInspectable) { m_EnumerationCompleted.SetEvent(); return S_OK; diff --git a/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.h b/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.h index 401f36c1..306711e0 100644 --- a/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.h +++ b/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.h @@ -62,6 +62,7 @@ class CMidi2KSAggregateMidiEndpointManager : wil::com_ptr_nothrow m_midiDeviceManager; wil::com_ptr_nothrow m_midiProtocolManager; + wil::critical_section m_availableEndpointDefinitionsLock; std::map m_availableEndpointDefinitions; DeviceWatcher m_watcher{0}; From 060d6eea70baefd47c5d2b6ecfb3e6633bb86157 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Fri, 21 Feb 2025 00:53:51 -0500 Subject: [PATCH 08/29] Update mididiag error message when no endpoints --- src/app-sdk/mididiag/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app-sdk/mididiag/main.cpp b/src/app-sdk/mididiag/main.cpp index 94624330..5ce26b9a 100644 --- a/src/app-sdk/mididiag/main.cpp +++ b/src/app-sdk/mididiag/main.cpp @@ -666,7 +666,9 @@ bool DoSectionMidi2ApiEndpoints(_In_ bool const verbose) } else { - OutputError("Enumerating devices returned no matches. This is not expected and indicates an installation problem or that the service is not running."); + OutputError("Enumerating devices returned no matches. This is not expected and indicates an installation"); + OutputError("problem, the service couldn't start, or you are running developer service components and do"); + OutputError("not have developer mode set in Windows Settings."); return false; } From e010574b509d7efa3c20b7904a28bada057f305c Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Fri, 21 Feb 2025 14:20:38 -0500 Subject: [PATCH 09/29] Build artifacts --- build/staging/version/BundleInfo.wxi | 2 +- .../version/WindowsMidiServicesVersion.cs | 6 +-- .../version/WindowsMidiServicesVersion.h | 6 +-- .../basics/client-basics-cpp.vcxproj | 2 +- samples/cpp-winrt/basics/packages.config | 2 +- .../loopback-endpoints-cpp.vcxproj | 2 +- .../loopback-endpoints/packages.config | 2 +- samples/cpp-winrt/send-speed/packages.config | 2 +- .../send-speed/send-speed-cpp.vcxproj | 2 +- .../simple-app-to-app-midi/packages.config | 2 +- .../simple-app-to-app-cpp.vcxproj | 2 +- .../static-enum-endpoints/packages.config | 2 +- .../static-enum-endpoints-cpp.vcxproj | 2 +- .../cpp-winrt/watch-endpoints/packages.config | 2 +- .../watch-endpoints-cpp.vcxproj | 2 +- .../Midi2.KSAggregateMidiEndpointManager.cpp | 50 ------------------- src/app-sdk/mididiag/mididiag.vcxproj | 2 +- src/app-sdk/mididiag/packages.config | 2 +- src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj | 2 +- src/app-sdk/midimdnsinfo/packages.config | 2 +- src/app-sdk/midiusbinfo/midiusbinfo.vcxproj | 2 +- src/app-sdk/midiusbinfo/packages.config | 2 +- 22 files changed, 25 insertions(+), 75 deletions(-) diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index c604f207..13c83198 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,4 +1,4 @@ - + diff --git a/build/staging/version/WindowsMidiServicesVersion.cs b/build/staging/version/WindowsMidiServicesVersion.cs index 3d4909e2..99eaa831 100644 --- a/build/staging/version/WindowsMidiServicesVersion.cs +++ b/build/staging/version/WindowsMidiServicesVersion.cs @@ -6,12 +6,12 @@ public static class MidiBuildInformation { public const string Source = "GitHub Preview"; public const string Name = "Customer Preview 2"; - public const string BuildFullVersion = "1.0.3-preview-11.250219-2318"; + public const string BuildFullVersion = "1.0.3-preview-11.250221-1332"; public const string VersionMajor = "1"; public const string VersionMinor = "0"; public const string VersionRevision = "3"; - public const string VersionDateNumber = "250219"; - public const string VersionTimeNumber = "2318"; + public const string VersionDateNumber = "250221"; + public const string VersionTimeNumber = "1332"; } } diff --git a/build/staging/version/WindowsMidiServicesVersion.h b/build/staging/version/WindowsMidiServicesVersion.h index ccf35011..57e992a8 100644 --- a/build/staging/version/WindowsMidiServicesVersion.h +++ b/build/staging/version/WindowsMidiServicesVersion.h @@ -5,12 +5,12 @@ #define WINDOWS_MIDI_SERVICES_BUILD_SOURCE L"GitHub Preview" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_NAME L"Customer Preview 2" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.3-preview-11.250219-2318" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.3-preview-11.250221-1332" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MAJOR L"1" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MINOR L"0" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_REVISION L"3" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250219" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"2318" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250221" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"1332" #endif diff --git a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj index 2ecd2770..16df31a7 100644 --- a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj +++ b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250221-1332 true true false diff --git a/samples/cpp-winrt/basics/packages.config b/samples/cpp-winrt/basics/packages.config index a562003c..a11d0e15 100644 --- a/samples/cpp-winrt/basics/packages.config +++ b/samples/cpp-winrt/basics/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj index 24e0bab6..db00ec38 100644 --- a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250221-1332 true true true diff --git a/samples/cpp-winrt/loopback-endpoints/packages.config b/samples/cpp-winrt/loopback-endpoints/packages.config index a562003c..a11d0e15 100644 --- a/samples/cpp-winrt/loopback-endpoints/packages.config +++ b/samples/cpp-winrt/loopback-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/send-speed/packages.config b/samples/cpp-winrt/send-speed/packages.config index a562003c..a11d0e15 100644 --- a/samples/cpp-winrt/send-speed/packages.config +++ b/samples/cpp-winrt/send-speed/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj index 43afb85e..29a2e621 100644 --- a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj +++ b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250221-1332 true true true diff --git a/samples/cpp-winrt/simple-app-to-app-midi/packages.config b/samples/cpp-winrt/simple-app-to-app-midi/packages.config index a562003c..a11d0e15 100644 --- a/samples/cpp-winrt/simple-app-to-app-midi/packages.config +++ b/samples/cpp-winrt/simple-app-to-app-midi/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj index 13806901..d78f5b56 100644 --- a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj +++ b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250221-1332 true true true diff --git a/samples/cpp-winrt/static-enum-endpoints/packages.config b/samples/cpp-winrt/static-enum-endpoints/packages.config index a562003c..a11d0e15 100644 --- a/samples/cpp-winrt/static-enum-endpoints/packages.config +++ b/samples/cpp-winrt/static-enum-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj index 39e4ec03..0964c16e 100644 --- a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250221-1332 true true true diff --git a/samples/cpp-winrt/watch-endpoints/packages.config b/samples/cpp-winrt/watch-endpoints/packages.config index a562003c..a11d0e15 100644 --- a/samples/cpp-winrt/watch-endpoints/packages.config +++ b/samples/cpp-winrt/watch-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj index af3601eb..10453395 100644 --- a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250221-1332 true true true diff --git a/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp b/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp index a9a7cd58..e2858fab 100644 --- a/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp +++ b/src/api/Transport/KSAggregateTransport/Midi2.KSAggregateMidiEndpointManager.cpp @@ -74,56 +74,6 @@ CMidi2KSAggregateMidiEndpointManager::Initialize( } - -//// this will be used for the group terminal block name but also for the WinMM port name -//HRESULT -//CreateGroupTerminalBlockName( -// _In_ std::wstring parentDeviceName, -// _In_ std::wstring filterName, -// _In_ std::wstring currentPinName, -// _Inout_ std::wstring& newGroupTerminalBlockName) -//{ -// std::wstring cleanedPinName{}; -// -// // Used by ESI, MOTU, and others. We don't want to mess up other names, so check only for whole word -// if (currentPinName == L"MIDI") -// { -// cleanedPinName = L""; -// } -// else -// { -// cleanedPinName = currentPinName; -// } -// -// // the double and triple space entries need to be last -// // there are other ways to do this with pattern matching, -// // but just banging this through for this version -// std::wstring wordsToRemove[] = -// { -// parentDeviceName, filterName, -// L"[0]", L"[1]", L"[2]", L"[3]", L"[4]", L"[5]", L"[6]", L"[7]", L"[8]", L"[9]", L"[10]", L"[11]", L"[12]", L"[13]", L"[14]", L"[15]", L"[16]", -// L" ", L" ", L" " -// }; -// -// for (auto const& word : wordsToRemove) -// { -// if (cleanedPinName.length() >= word.length()) -// { -// auto idx = cleanedPinName.find(word); -// -// if (idx != std::wstring::npos) -// { -// cleanedPinName = cleanedPinName.erase(idx, word.length()); -// } -// } -// } -// -// newGroupTerminalBlockName = internal::TrimmedWStringCopy(filterName + L" " + internal::TrimmedWStringCopy(cleanedPinName)); -// -// return S_OK; -//} - - typedef struct { BYTE GroupIndex; // index (0-15) of the group this pin maps to UINT32 PinId; // KS Pin number diff --git a/src/app-sdk/mididiag/mididiag.vcxproj b/src/app-sdk/mididiag/mididiag.vcxproj index 306e39df..453d3be1 100644 --- a/src/app-sdk/mididiag/mididiag.vcxproj +++ b/src/app-sdk/mididiag/mididiag.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250221-1332 true true true diff --git a/src/app-sdk/mididiag/packages.config b/src/app-sdk/mididiag/packages.config index 40e44a47..45b54985 100644 --- a/src/app-sdk/mididiag/packages.config +++ b/src/app-sdk/mididiag/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj index 7570876c..9e26ed53 100644 --- a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj +++ b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250221-1332 true true true diff --git a/src/app-sdk/midimdnsinfo/packages.config b/src/app-sdk/midimdnsinfo/packages.config index 40e44a47..45b54985 100644 --- a/src/app-sdk/midimdnsinfo/packages.config +++ b/src/app-sdk/midimdnsinfo/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj index 9db2a6e4..168e0238 100644 --- a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj +++ b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250219-2318 + Microsoft.Windows.Devices.Midi2.1.0.3-preview-11.250221-1332 true true true diff --git a/src/app-sdk/midiusbinfo/packages.config b/src/app-sdk/midiusbinfo/packages.config index 40e44a47..45b54985 100644 --- a/src/app-sdk/midiusbinfo/packages.config +++ b/src/app-sdk/midiusbinfo/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file From 926cf9be58ea76c406ec9bc5d7cd4c1aad16b584 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Fri, 21 Feb 2025 18:29:11 -0500 Subject: [PATCH 10/29] Update midiksinfo to show only MIDI 1 devices, and to have better output formatting --- src/app-sdk/midiusbinfo/main.cpp | 686 ++++++++++++++++--------------- 1 file changed, 344 insertions(+), 342 deletions(-) diff --git a/src/app-sdk/midiusbinfo/main.cpp b/src/app-sdk/midiusbinfo/main.cpp index 1bf0fceb..db82da65 100644 --- a/src/app-sdk/midiusbinfo/main.cpp +++ b/src/app-sdk/midiusbinfo/main.cpp @@ -29,7 +29,8 @@ void WriteLabel(std::string label) -winrt::hstring GetStringProperty(_In_ DeviceInformation di, _In_ winrt::hstring propertyName, _In_ winrt::hstring defaultValue) +winrt::hstring +GetStringProperty(_In_ DeviceInformation di, _In_ winrt::hstring propertyName, _In_ winrt::hstring defaultValue) { auto prop = di.Properties().Lookup(propertyName); @@ -48,7 +49,7 @@ winrt::hstring GetStringProperty(_In_ DeviceInformation di, _In_ winrt::hstring return value; } -#define LINE_LENGTH 125 +#define LINE_LENGTH 131 HRESULT @@ -100,7 +101,288 @@ GetKSDriverSuppliedName(_In_ HANDLE hInstantiatedFilter, _Inout_ std::wstring& n } +struct MidiKsPinInformation +{ + uint32_t Number; + std::wstring Name; + MidiDataFormats DataFormat{ MidiDataFormats::MidiDataFormats_Invalid }; + KSPIN_DATAFLOW PinFlow{ }; +}; + +struct MidiKsFilterInformation +{ + std::wstring Id; + std::wstring Name; + std::wstring NameFromRegistry; + + std::vector Pins; +}; + +struct MidiKsDeviceInformation +{ + std::wstring Name; + std::wstring DeviceInstanceId; + + bool IsMidiDevice{ false }; + std::vector Filters; +}; + + +std::vector m_devices{ }; + + + + + + +void DisplayMidiDevices() +{ + std::cout << std::endl; + + if (m_devices.size() == 0) + { + std::cout + << dye::light_red("No devices with MIDI 1.0 pins found.") + << std::endl; + + return; + } + + + for (auto const& device : m_devices) + { + uint16_t indent{ 0 }; + + std::cout + << std::string(indent, ' ') + << dye::grey("Device Name") + << " " + << dye::light_aqua(winrt::to_string(device.Name)) + << std::endl; + + std::cout + << std::string(indent, ' ') + << dye::grey("Instance Id") + << " " + << dye::yellow(winrt::to_string(device.DeviceInstanceId)) + << std::endl; + + // we list all the filters once in a short format, to make it easier to read for some devices + // and then we list each filter and its pins. Only do this if there's more than one filter. + + if (device.Filters.size() > 1) + { + uint16_t FilterNameColumnWidth{ 0 }; + uint16_t FilterIdColumnWidth{ 0 }; + + for (auto const& filter : device.Filters) + { + FilterNameColumnWidth = max(FilterNameColumnWidth, filter.Name.length() + 1); + FilterIdColumnWidth = max(FilterIdColumnWidth, filter.Id.length() + 1); + } + + bool firstFilter{ true }; + for (auto const& filter : device.Filters) + { + indent = 5; + if (firstFilter) + { + std::cout << std::endl; + + std::cout + << std::string(indent, ' ') + << dye::aqua("Device") + << " " + << dye::light_aqua(winrt::to_string(device.Name)) + << " " + << dye::aqua("includes") + << " " + << dye::light_aqua(device.Filters.size()) + << " " + << dye::aqua(device.Filters.size() == 1 ? "filter" : "filters") + << " " + << dye::aqua("with MIDI 1.0 Format Pins") + << std::endl; + + std::cout << std::endl; + + // header row + + std::cout + << std::string(indent, ' ') + << std::setw(FilterNameColumnWidth) << std::left << dye::grey("Name") + << std::setw(FilterIdColumnWidth) << std::left << dye::grey("Instance Id") + << std::endl; + + std::cout + << std::string(indent, ' ') + << std::setw(FilterNameColumnWidth) << std::left << dye::grey(std::string(FilterNameColumnWidth - 1, '-')) + << std::setw(FilterIdColumnWidth) << std::left << dye::grey(std::string(FilterIdColumnWidth - 1, '-')) + << std::endl; + + firstFilter = false; + } + + + std::cout + << std::string(indent, ' ') + << std::setw(FilterNameColumnWidth) << std::left << dye::light_aqua(winrt::to_string(filter.Name)) + << std::setw(FilterIdColumnWidth) << std::left << dye::yellow(winrt::to_string(filter.Id)) + << std::endl; + + } + } + + // now we list all filters and their pins in more detail + + bool firstFilter{ true }; + for (auto const& filter : device.Filters) + { + indent = 5; + + if (firstFilter) + { + std::cout << std::endl; + + std::cout + << std::string(indent, ' ') + << dye::aqua("Details and MIDI 1.0 Pins for each Filter") + << std::endl; + + std::cout << std::endl; + + firstFilter = false; + } + + + std::cout + << std::string(indent, ' ') + << std::setw(18) << std::left + << dye::grey("Filter Id") + << " " + << dye::yellow(winrt::to_string(filter.Id)) + << std::endl; + + std::cout + << std::string(indent, ' ') + << std::setw(18) << std::left + << dye::grey("Filter Name") + << " " + << dye::light_aqua(winrt::to_string(filter.Name)) + << std::endl; + + std::cout + << std::string(indent, ' ') + << std::setw(18) << std::left + << dye::grey("Name from Registry") + << " " + << (filter.NameFromRegistry.empty() ? dye::grey("(not provided)") : dye::light_aqua(winrt::to_string(filter.NameFromRegistry))) + << std::endl; + + + bool firstPin = true; + for (auto const& pin : filter.Pins) + { + indent = 10; + const uint16_t PinIndexColumnWidth = 6; + const uint16_t PinDataFlowColumnWidth = 22; + const uint16_t PinDataFlowExplanationColumnWidth = 22; + const uint16_t PinNameColumnWidth = 32; + + if (firstPin) + { + std::cout << std::endl; + + std::cout + << std::string(indent, ' ') + << dye::aqua("Filter") + << " " + << dye::light_aqua(winrt::to_string(filter.Name)) + << " " + << dye::aqua("includes") + << " " + << dye::light_aqua(filter.Pins.size()) + << " " + << dye::aqua("MIDI 1.0 Format") + << " " + << dye::aqua(filter.Pins.size() == 1 ? "pin" : "pins") + << std::endl; + + std::cout << std::endl; + + // header row + + std::cout + << std::string(indent, ' ') + << std::setw(PinIndexColumnWidth) << std::left << dye::grey("Index") + << std::setw(PinDataFlowColumnWidth) << std::left << dye::grey("Data Flow") + << std::setw(PinDataFlowExplanationColumnWidth) << std::left << dye::grey("Port Type") + << std::setw(PinNameColumnWidth) << std::left << dye::grey("Pin Name") + << std::endl; + + std::cout + << std::string(indent, ' ') + << std::setw(PinIndexColumnWidth) << std::left << dye::grey(std::string(PinIndexColumnWidth-1, '-')) + << std::setw(PinDataFlowColumnWidth) << std::left << dye::grey(std::string(PinDataFlowColumnWidth-1, '-')) + << std::setw(PinDataFlowExplanationColumnWidth) << std::left << dye::grey(std::string(PinDataFlowExplanationColumnWidth - 1, '-')) + << std::setw(PinNameColumnWidth) << std::left << dye::grey(std::string(PinNameColumnWidth-1, '-')) + << std::endl; + + firstPin = false; + } + + std::wstring stringDataFlow{ L"Unknown" }; + std::wstring stringDataFlowExplanation{}; + + if (pin.PinFlow == KSPIN_DATAFLOW_IN) + { + stringDataFlow = L"Message Destination"; + stringDataFlowExplanation = L"MIDI Output from PC"; + + } + else if (pin.PinFlow == KSPIN_DATAFLOW_OUT) + { + stringDataFlow = L"Message Source"; + stringDataFlowExplanation = L"MIDI Input to PC"; + } + + std::cout + << std::string(indent, ' ') + << std::setw(PinIndexColumnWidth) << std::left << dye::yellow(pin.Number) + << std::setw(PinDataFlowColumnWidth) << std::left << dye::aqua(winrt::to_string(stringDataFlow)) + << std::setw(PinDataFlowExplanationColumnWidth) << std::left << dye::aqua(winrt::to_string(stringDataFlowExplanation)); + + if (pin.Name.empty()) + { + std::cout + << std::setw(PinNameColumnWidth) << std::left << dye::grey("(not provided)") + << std::endl; + } + else + { + std::cout + << std::setw(PinNameColumnWidth) << std::left << dye::light_aqua(winrt::to_string(pin.Name)) + << std::endl; + } + } + + if (filter.Pins.size() > 0) + { + std::cout << std::endl; + } + + } + + std::cout << std::endl; + std::cout << dye::grey(std::string(LINE_LENGTH, '=')) << std::endl; + + } + + std::cout << std::endl; + std::cout << "-- End of Information --" << std::endl << std::endl; + +} int __cdecl main() @@ -108,23 +390,30 @@ int __cdecl main() winrt::init_apartment(); std::cout << dye::grey(std::string(LINE_LENGTH, '=')) << std::endl; - std::cout << dye::aqua(" Enumerating MIDI 1.0 kernel streaming devices to discover MIDI pins") << std::endl; - std::cout << dye::aqua(" Typically, these are USB, but other KS drivers will be included") << std::endl; + std::cout << dye::aqua(" Enumerating MIDI 1.0 kernel streaming devices, using the MIDI 1.0 class driver or a third-party MIDI 1.0 driver.") << std::endl; + std::cout << dye::aqua(" Typically, these devices are USB, but other KS drivers will be included in the enumeration.") << std::endl; + std::cout << dye::grey(std::string(LINE_LENGTH, '-')) << std::endl; + std::cout << dye::aqua(" If the MIDI service is running when you run this utility, some devices may not report all pin properties because they are in-use.") << std::endl; + std::cout << dye::aqua(" It is recommended that you stop midisrv (the MIDI service) before running this.") << std::endl << std::endl; + std::cout << dye::aqua(" Use "); + std::cout << dye::light_green("midi service stop"); + std::cout << dye::aqua(" or "); + std::cout << dye::light_green("net stop midisrv"); + std::cout << dye::aqua(" from an Administrator command prompt to stop the service.") << std::endl; + std::cout << dye::grey(std::string(LINE_LENGTH, '-')) << std::endl; - std::cout << dye::aqua(" If the MIDI service is running when you run this utility, some") << std::endl; - std::cout << dye::aqua(" devices may not report all pin properties because they are in-use.") << std::endl; - std::cout << dye::aqua(" It is recommended that you stop the midisrv service before running.") << std::endl; - std::cout << dye::aqua(" this utility.") << std::endl; - std::cout << dye::grey(std::string(LINE_LENGTH, '=')) << std::endl; - std::wcout - << WINDOWS_MIDI_SERVICES_BUILD_VERSION_NAME - << L" (" - << WINDOWS_MIDI_SERVICES_BUILD_SOURCE - << L")" + std::cout + << " " + << dye::aqua(winrt::to_string(WINDOWS_MIDI_SERVICES_BUILD_VERSION_NAME)) + << dye::grey(" (") + << dye::aqua(winrt::to_string(WINDOWS_MIDI_SERVICES_BUILD_SOURCE)) + << dye::grey(")") + << " -- " + << dye::aqua(winrt::to_string(WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL)) << std::endl; - std::wcout << WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL << std::endl; + std::cout << dye::grey(std::string(LINE_LENGTH, '=')) << std::endl; // {4d36e96c-e325-11ce-bfc1-08002be10318} is the MEDIA class guid @@ -138,6 +427,8 @@ int __cdecl main() { for (auto const& parentDevice : mediaDevices) { + bool isMidi1Device{ false }; + auto deviceInstanceId = GetStringProperty(parentDevice, L"System.Devices.DeviceInstanceId", L""); if (deviceInstanceId.empty()) @@ -146,10 +437,10 @@ int __cdecl main() continue; } - WriteLabel("Parent Device"); - std::cout << dye::light_aqua(winrt::to_string(parentDevice.Name())) << std::endl; - WriteLabel(" - Instance Id"); - std::cout << dye::yellow(winrt::to_string(deviceInstanceId)) << std::endl; + MidiKsDeviceInformation deviceInfo{}; + + deviceInfo.DeviceInstanceId = deviceInstanceId; + deviceInfo.Name = parentDevice.Name(); // enumerate all KS_CATEGORY_AUDIO filters for this parent media device winrt::hstring filterDeviceSelector( @@ -161,52 +452,28 @@ int __cdecl main() if (filterDevices.Size() > 0) { - WriteLabel(" - Filter Count"); - std::cout << filterDevices.Size() << std::endl; - std::cout << std::endl; - for (auto const& filterDevice : filterDevices) { - bool isMidi1Filter{ false }; + MidiKsFilterInformation filterInfo{}; - WriteLabel(" - Filter Id"); - std::cout << dye::yellow(winrt::to_string(filterDevice.Id())) << std::endl; + filterInfo.Id = filterDevice.Id(); + filterInfo.Name = filterDevice.Name(); - WriteLabel(" - KS Filter Name"); - std::cout << dye::light_aqua(winrt::to_string(filterDevice.Name())) << std::endl; + bool isMidi1Filter{ false }; // instantiate the filter and then enumerate the pins wil::unique_handle hFilter; if (FAILED(FilterInstantiate(filterDevice.Id().c_str(), &hFilter))) { - std::cout << " - Failed to instantiate filter." << std::endl; + // can't instantiate the filter continue; } - std::wstring nameFromDriver{}; - auto driverNameHR = GetKSDriverSuppliedName(hFilter.get(), nameFromDriver); - - WriteLabel(" - KS Reported Name"); - if (SUCCEEDED(driverNameHR)) - { - std::cout << hue::light_aqua; - std::wcout << nameFromDriver; - std::cout << hue::reset << std::endl; - - } - else - { - std::cout << dye::grey("(not available)") << std::endl; - } - - ULONG cPins{ 0 }; - if (FAILED(PinPropertySimple(hFilter.get(), 0, KSPROPSETID_Pin, KSPROPERTY_PIN_CTYPES, &cPins, sizeof(cPins)))) { - std::cout << " - Failed to get pin count." << std::endl; - + // couldn't get pin info continue; } @@ -216,20 +483,18 @@ int __cdecl main() wil::unique_handle hPin; - if (SUCCEEDED(InstantiateMidiPin(hFilter.get(), pinIndex, MidiTransport_CyclicUMP, &hPin))) + /*if (SUCCEEDED(InstantiateMidiPin(hFilter.get(), pinIndex, MidiTransport_CyclicUMP, &hPin))) { - std::cout << dye::grey(" Pin ") << dye::grey(pinIndex) << std::endl; - std::cout << dye::grey(" - UMP Cyclic pin (MIDI 2.0 driver). Ignoring for this utility.") << std::endl; continue; } - else if (SUCCEEDED(InstantiateMidiPin(hFilter.get(), pinIndex, MidiTransport_StandardByteStream, &hPin))) + else */ if (SUCCEEDED(InstantiateMidiPin(hFilter.get(), pinIndex, MidiTransport_StandardByteStream, &hPin))) { + MidiKsPinInformation pinInfo{}; + pinInfo.Number = pinIndex; + isMidi1Pin = true; isMidi1Filter = true; - std::cout << dye::yellow(" Pin ") << dye::aqua(pinIndex) << std::endl; - - // WriteLabel(" - Data Format"); - // std::cout << "MIDI 1.0 byte format" << std::endl; + isMidi1Device = true; std::unique_ptr pinNameData; ULONG pinNameDataSize{ 0 }; @@ -245,20 +510,11 @@ int __cdecl main() if (SUCCEEDED(pinNameHR) || pinNameHR == HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND)) { - WriteLabel(" - Name"); - // Check to see if the pin has an iJack name if (pinNameDataSize > 0) { std::wstring pinName{ pinNameData.get() }; - std::cout << hue::light_aqua; - std::wcout << pinName; - std::cout << hue::reset << std::endl; - } - else - { - // no pin name provided - std::cout << "Not provided" << std::endl; + pinInfo.Name = pinName; } } @@ -273,303 +529,49 @@ int __cdecl main() sizeof(KSPIN_DATAFLOW) ); - - WriteLabel(" - Data Flow"); - if (SUCCEEDED(dataFlowHR)) { - if (dataFlow == KSPIN_DATAFLOW_IN) - { - std::cout << dye::aqua("Message Destination") << dye::grey(" (KSPIN_DATAFLOW_IN: MIDI 1.0 Out from PC to device)") << std::endl; - - } - else if (dataFlow == KSPIN_DATAFLOW_OUT) - { - std::cout << dye::aqua("Message Source") << dye::grey(" (KSPIN_DATAFLOW_OUT: MIDI 1.0 In to PC from device)") << std::endl; - - } - else - { - std::cout << "Unknown" << std::endl; - } + pinInfo.PinFlow = dataFlow; } - else + + if (isMidi1Pin) { - std::cout << dye::light_red("Failed to get data flow") << std::endl; + filterInfo.Pins.push_back(pinInfo); } - - } - else - { - // std::cout << "Not a MIDI Pin. Ignoring for this utility." << std::endl; } hPin.reset(); } + + // get the name that the device reported during installation. This is often empty + + std::wstring nameFromDriver{}; + auto driverNameHR = GetKSDriverSuppliedName(hFilter.get(), nameFromDriver); + + if (SUCCEEDED(driverNameHR)) + { + filterInfo.NameFromRegistry = nameFromDriver; + } + if (isMidi1Filter) { - std::cout << std::endl; + deviceInfo.Filters.push_back(filterInfo); } + hFilter.reset(); } } - else + + if (isMidi1Device) { - std::cout << dye::grey(" - Device has no enabled KS_CATEGORY_AUDIO filters available at this time.") << std::endl; + m_devices.push_back(deviceInfo); } - - std::cout << std::endl; - std::cout << dye::light_blue(std::string(LINE_LENGTH, '-')) << std::endl; - } } - else - { - std::cout << "No media devices found" << std::endl; - } - - - - - - - - // std::cout << std::endl; - // std::cout << dye::grey("----------------------------------------------------------------------") << std::endl; - - // wil::unique_handle hFilter; - // std::string deviceName; - // std::wstring deviceId; - // std::wstring deviceInstanceId; - // ULONG cPins{ 0 }; - - // auto additionalProperties = winrt::single_threaded_vector(); - - // auto properties = device.Properties(); - - // // retrieve the device instance id from the DeviceInformation property store - // auto prop = properties.Lookup(winrt::to_hstring(L"System.Devices.DeviceInstanceId")); - // RETURN_HR_IF_NULL(E_INVALIDARG, prop); - // deviceInstanceId = winrt::unbox_value(prop).c_str(); - // deviceId = device.Id().c_str(); - - // // Get the parent device name so it doesn't show as just the PC name - // auto parentDeviceInfo = DeviceInformation::CreateFromIdAsync(deviceInstanceId, - // additionalProperties, winrt::Windows::Devices::Enumeration::DeviceInformationKind::Device).get(); - // deviceName = winrt::to_string(parentDeviceInfo.Name()); - - // WriteLabel("Filter Name"); - // std::cout << dye::light_aqua(winrt::to_string(device.Name())) << std::endl; - // WriteLabel("Filter Id"); - // std::cout << dye::yellow(winrt::to_string(device.Id())) << std::endl; - - // WriteLabel("Parent Name"); - // std::cout << dye::aqua(deviceName) << std::endl; - // WriteLabel("Parent Id"); - // std::cout << dye::green(winrt::to_string(parentDeviceInfo.Id())) << std::endl; - - // // instantiate the interface - // if (FAILED(FilterInstantiate(deviceId.c_str(), &hFilter))) - // { - // std::cout << " - Failed to instantiate filter." << std::endl; - - // continue; - // } - - // if (FAILED(PinPropertySimple(hFilter.get(), 0, KSPROPSETID_Pin, KSPROPERTY_PIN_CTYPES, &cPins, sizeof(cPins)))) - // { - // std::cout << " - Failed to get pin count." << std::endl; - - // continue; - // } - - // // Enumerate all the pins - - // WriteLabel("Pin Count"); - // std::cout << cPins << std::endl; - - // for (UINT i = 0; i < cPins; i++) - // { - // std::cout << std::endl; - - // bool isMidiPin{ false }; - - // wil::unique_handle hPin; - // KSPIN_DATAFLOW dataFlow = (KSPIN_DATAFLOW)0; - // KSPIN_COMMUNICATION communication = (KSPIN_COMMUNICATION)0; - // GUID nativeDataFormat{ 0 }; - - // std::cout << dye::yellow("Pin ") << dye::aqua(i) << std::endl; - - // WriteLabel(" - Communication"); - - // if (FAILED(PinPropertySimple(hFilter.get(), i, KSPROPSETID_Pin, KSPROPERTY_PIN_COMMUNICATION, &communication, sizeof(KSPIN_COMMUNICATION)))) - // { - // std::cout << dye::red("Failed to get pin communication property.") << std::endl; - // continue; - // } - - // if (communication == KSPIN_COMMUNICATION_NONE) - // { - // std::cout << "None" << std::endl; - // continue; - // } - // else if (communication == KSPIN_COMMUNICATION_SINK) - // { - // std::cout << "Sink" << std::endl; - // } - // else if (communication == KSPIN_COMMUNICATION_SOURCE) - // { - // std::cout << "Source" << std::endl; - // } - // else if (communication == KSPIN_COMMUNICATION_BOTH) - // { - // std::cout << "Source and Sink" << std::endl; - // } - // else if (communication == KSPIN_COMMUNICATION_BRIDGE) - // { - // std::cout << "Bridge" << std::endl; - // continue; - // } - // else - // { - // std::cout << "Unknown (" << communication << ")" << std::endl; - // continue; - // } - - - // // try to instantiate as UMP - // if (SUCCEEDED(InstantiateMidiPin(hFilter.get(), i, MidiTransport_CyclicUMP, &hPin))) - // { - // isMidiPin = true; - - // WriteLabel(" - Communication Format"); - // std::cout << dye::light_purple("MIDI UMP cyclic format") << std::endl; - - // auto hr = PinPropertySimple(hPin.get(), - // i, - // KSPROPSETID_MIDI2_ENDPOINT_INFORMATION, - // KSPROPERTY_MIDI2_NATIVEDATAFORMAT, - // &nativeDataFormat, - // sizeof(nativeDataFormat)); - - // WriteLabel(" - Native Data Format"); - - // if (SUCCEEDED(hr) || hr == HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND)) - // { - // if (nativeDataFormat == KSDATAFORMAT_SUBTYPE_UNIVERSALMIDIPACKET) - // { - // std::cout << "Universal MIDI Packet (UMP)" << std::endl; - // } - // else if (nativeDataFormat == KSDATAFORMAT_SUBTYPE_MIDI) - // { - // std::cout << "MIDI 1.0 Data Format (bytes)" << std::endl; - // } - // else - // { - // std::cout << "Unknown" << std::endl; - // } - // } - // else - // { - // std::cout << dye::red("Unable to get native data format") << std::endl; - // } - // } - - // hPin.reset(); - - // if (SUCCEEDED(InstantiateMidiPin(hFilter.get(), i, MidiTransport_StandardByteStream, &hPin))) - // { - // isMidiPin = true; - - // std::unique_ptr pinNameData; - // ULONG pinNameDataSize{ 0 }; - - // WriteLabel(" - Communication Format"); - // std::cout << dye::light_purple("MIDI 1.0 byte format") << std::endl; - - // auto pinNameHR = PinPropertyAllocate( - // hFilter.get(), - // i, - // KSPROPSETID_Pin, - // KSPROPERTY_PIN_NAME, - // (PVOID*)&pinNameData, - // &pinNameDataSize); - - // if (SUCCEEDED(pinNameHR) || pinNameHR == HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND)) - // { - // WriteLabel(" - Pin Name"); - - // // Check to see if the pin has an iJack name - // if (pinNameDataSize > 0) - // { - // std::wstring pinName{ pinNameData.get() }; - // std::cout << hue::light_aqua; - // std::wcout << pinName; - // std::cout << hue::reset << std::endl; - // } - // else - // { - // // no pin name provided - // std::cout << "Not provided" << std::endl; - // } - // } - // } - - // hPin.reset(); - - - // if (isMidiPin) - // { - - // auto dataFlowHR = PinPropertySimple( - // hFilter.get(), - // i, - // KSPROPSETID_Pin, - // KSPROPERTY_PIN_DATAFLOW, - // &dataFlow, - // sizeof(KSPIN_DATAFLOW) - // ); - - // WriteLabel(" - Data Flow"); - - // if (SUCCEEDED(dataFlowHR)) - // { - // if (dataFlow == KSPIN_DATAFLOW_IN) - // { - // std::cout << "KSPIN_DATAFLOW_IN " << dye::purple("(MIDI Out from PC to device)") << std::endl; - - // } - // else if (dataFlow == KSPIN_DATAFLOW_OUT) - // { - // std::cout << "KSPIN_DATAFLOW_OUT " << dye::purple("(MIDI In to PC from device)") << std::endl; - - // } - // else - // { - // std::cout << "Unknown" << std::endl; - // } - // } - // else - // { - // std::cout << dye::red("Failed to get data flow") << std::endl; - // } - // } - - // } - - // } - //} - //else - //{ - // std::cout << std::endl << dye::red("No compatible kernel streaming devices found") << std::endl; - //} - - std::cout << std::endl; - std::cout << "-- End of Information --" << std::endl << std::endl; + DisplayMidiDevices(); return 0; } From 43220e5c46019e4dd8720fc9d82253256a7f6add Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Sat, 22 Feb 2025 16:13:10 -0500 Subject: [PATCH 11/29] WIP developer features in settings app --- .../Microsoft.Midi.Settings/App.xaml.cs | 5 +- .../Services/IMidiConfigFileService.cs | 2 - .../ConfigurationsPage.xaml} | 4 +- .../ConfigurationsPage.xaml.cs} | 8 +- .../ConfigurationsViewModel.cs} | 4 +- .../Sections/Settings/SettingsPage.xaml | 6 +- .../MidiServiceRegistrySettingsService.cs | 206 ++++++++++++++++++ .../Services/PageService.cs | 2 +- .../Strings/en-us/Resources.resw | 74 ++++++- .../ViewModels/ForDevelopersViewModel.cs | 96 +++++++- .../ViewModels/ShellViewModel.cs | 11 +- .../Views/ForDevelopersPage.xaml | 105 ++++----- .../Views/ShellPage.xaml | 12 +- 13 files changed, 437 insertions(+), 98 deletions(-) rename src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/{Setups/SetupPage.xaml => Configurations/ConfigurationsPage.xaml} (68%) rename src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/{Setups/SetupPage.xaml.cs => Configurations/ConfigurationsPage.xaml.cs} (85%) rename src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/{Setups/SetupViewModel.cs => Configurations/ConfigurationsViewModel.cs} (70%) create mode 100644 src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiServiceRegistrySettingsService.cs diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml.cs index b98859c5..16b01e75 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml.cs +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/App.xaml.cs @@ -76,6 +76,7 @@ public App() services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); // Views and ViewModels @@ -136,8 +137,8 @@ public App() services.AddTransient(); services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiConfigFileService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiConfigFileService.cs index e2d542b2..39b6daf1 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiConfigFileService.cs +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Contracts/Services/IMidiConfigFileService.cs @@ -20,7 +20,6 @@ public interface IMidiConfigFile bool StoreLoopbackEndpointPair(Microsoft.Windows.Devices.Midi2.Endpoints.Loopback.MidiLoopbackEndpointCreationConfig creationConfig); - } @@ -44,5 +43,4 @@ public interface IMidiConfigFileService bool UpdateRegistryCurrentConfigFile(string configFileName); - } diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Setups/SetupPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Configurations/ConfigurationsPage.xaml similarity index 68% rename from src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Setups/SetupPage.xaml rename to src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Configurations/ConfigurationsPage.xaml index 7a58c74b..e41ddf1a 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Setups/SetupPage.xaml +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Configurations/ConfigurationsPage.xaml @@ -2,7 +2,7 @@ - + diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Setups/SetupPage.xaml.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Configurations/ConfigurationsPage.xaml.cs similarity index 85% rename from src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Setups/SetupPage.xaml.cs rename to src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Configurations/ConfigurationsPage.xaml.cs index fdbfb8e1..31cc95f7 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Setups/SetupPage.xaml.cs +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Configurations/ConfigurationsPage.xaml.cs @@ -26,19 +26,19 @@ namespace Microsoft.Midi.Settings.Views /// /// An empty page that can be used on its own or navigated to within a Frame. /// - public sealed partial class SetupPage : Page + public sealed partial class ConfigurationsPage : Page { private ILoggingService _loggingService; - public SetupViewModel ViewModel + public ConfigurationsViewModel ViewModel { get; } - public SetupPage() + public ConfigurationsPage() { _loggingService = App.GetService(); - ViewModel = App.GetService(); + ViewModel = App.GetService(); InitializeComponent(); } diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Setups/SetupViewModel.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Configurations/ConfigurationsViewModel.cs similarity index 70% rename from src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Setups/SetupViewModel.cs rename to src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Configurations/ConfigurationsViewModel.cs index 0141038a..7c20c78c 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Setups/SetupViewModel.cs +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Configurations/ConfigurationsViewModel.cs @@ -7,9 +7,9 @@ namespace Microsoft.Midi.Settings.ViewModels { - public class SetupViewModel : ObservableRecipient + public class ConfigurationsViewModel : ObservableRecipient { - public SetupViewModel() + public ConfigurationsViewModel() { } } diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Settings/SettingsPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Settings/SettingsPage.xaml index 4bd93d6f..92b6a7aa 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Settings/SettingsPage.xaml +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Sections/Settings/SettingsPage.xaml @@ -68,9 +68,9 @@ - + - + @@ -80,10 +80,12 @@ Click="WindowsDeveloperSettingsHyperlinkButton_Click"/> + diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiServiceRegistrySettingsService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiServiceRegistrySettingsService.cs new file mode 100644 index 00000000..8a0f0141 --- /dev/null +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/MidiServiceRegistrySettingsService.cs @@ -0,0 +1,206 @@ +using Microsoft.Midi.Settings.Contracts.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Midi.Settings.Services +{ + public class MidiServiceRegistrySettingsService : IMidiServiceRegistrySettingsService + { + const bool SettingsDefaultUseMMCSS = false; + const bool SettingsDefaultDiscoveryEnabled = true; + const bool SettingsDefaultMidi1PortNaming = true; + const UInt32 SettingsDefaultDiscoveryTimeout = 10000; + + + private const string MidiRootRegKey = @"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows MIDI Services"; + + private UInt32 GetRegistryNumericDWORDValue(string keyName, string valueName, UInt32 defaultValue) + { + try + { + var value = Microsoft.Win32.Registry.GetValue(keyName, valueName, defaultValue); + + if (value == null) + { + // happens when the key does not exist + return defaultValue; + } + else + { + return (UInt32)value; + } + } + catch (Exception) + { + return defaultValue; + } + } + + private bool GetRegistryBooleanDWORDValue(string keyName, string valueName, bool defaultValue) + { + try + { + UInt32 defaultNumericValue = 0; + + if (defaultValue) + { + defaultNumericValue = 1; + } + + var value = Microsoft.Win32.Registry.GetValue(keyName, valueName, defaultNumericValue); + + if (value == null) + { + // happens when the key does not exist + return defaultValue; + } + else if ((UInt32)value > 0) + { + return true; + } + else + { + return false; + } + } + catch (Exception) + { + return defaultValue; + } + + } + + private string GetRegistryStringValue(string keyName, string valueName, string defaultValue) + { + try + { + var value = Microsoft.Win32.Registry.GetValue(keyName, valueName, defaultValue); + + if (value == null) + { + // happens when the key does not exist + return defaultValue; + } + else if (value is string) + { + return (string)value; + } + else + { + return defaultValue; + } + } + catch (Exception) + { + return defaultValue; + } + } + + private bool SetRegistryNumericDWORDValue(string keyName, string valueName, UInt32 newValue) + { + try + { + Microsoft.Win32.Registry.SetValue(keyName, valueName, newValue); + + return true; + } + catch (Exception) + { + return false; + } + } + + private bool SetRegistryBooleanDWORDValue(string keyName, string valueName, bool newValue) + { + try + { + var val = newValue ? (UInt32)1 : (UInt32)0; + + Microsoft.Win32.Registry.SetValue(keyName, valueName, val); + + return true; + } + catch (Exception) + { + return false; + } + } + + private bool SetRegistryStringValue(string keyName, string valueName, string newValue) + { + try + { + Microsoft.Win32.Registry.SetValue(keyName, valueName, newValue.Trim()); + + return true; + } + catch (Exception) + { + return false; + } + } + + //private bool DeleteRegistryValue(string regKey, string regValue) + //{ + // // TODO + + // return false; + //} + + const string ValueName_DefaultToOldMidi1PortNaming = "DefaultToOldMidi1PortNaming"; + const string ValueName_Midi2DiscoveryEnabled = "Midi2DiscoveryEnabled"; + const string ValueName_Midi2DiscoveryTimeoutMS = "Midi2DiscoveryTimeoutMS"; + const string ValueName_UseMMCSS = "UseMMCSS"; + + public bool GetDefaultToOldMidi1PortNaming() + { + return GetRegistryBooleanDWORDValue(MidiRootRegKey, ValueName_DefaultToOldMidi1PortNaming, SettingsDefaultMidi1PortNaming); + } + + public bool GetMidi2DiscoveryEnabled() + { + return GetRegistryBooleanDWORDValue(MidiRootRegKey, ValueName_Midi2DiscoveryEnabled, SettingsDefaultDiscoveryEnabled); + } + + public UInt32 GetMidi2DiscoveryTimeoutMS() + { + return GetRegistryNumericDWORDValue(MidiRootRegKey, ValueName_Midi2DiscoveryTimeoutMS, SettingsDefaultDiscoveryTimeout); + } + + public bool GetUseMMCSS() + { + return GetRegistryBooleanDWORDValue(MidiRootRegKey, ValueName_UseMMCSS, SettingsDefaultUseMMCSS); + } + + public bool SetDefaultToOldMidi1PortNaming(bool newValue) + { + // TODO: if the new value is the default, then just delete the reg entry + + return SetRegistryBooleanDWORDValue(MidiRootRegKey, ValueName_DefaultToOldMidi1PortNaming, newValue); + } + + public bool SetMidi2DiscoveryEnabled(bool newValue) + { + // TODO: if the new value is the default, then just delete the reg entry + + return SetRegistryBooleanDWORDValue(MidiRootRegKey, ValueName_Midi2DiscoveryEnabled, newValue); + } + + public bool SetMidi2DiscoveryTimeoutMS(UInt32 newValue) + { + // TODO: if the new value is the default, then just delete the reg entry + + return SetRegistryNumericDWORDValue(MidiRootRegKey, ValueName_Midi2DiscoveryTimeoutMS, newValue); + } + + public bool SetUseMMCSS(bool newValue) + { + // if the new value is the default, then just delete the reg entry + + return SetRegistryBooleanDWORDValue(MidiRootRegKey, ValueName_UseMMCSS, newValue); + } + } +} diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/PageService.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/PageService.cs index 992b3149..e19905e4 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/PageService.cs +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Services/PageService.cs @@ -33,7 +33,7 @@ public PageService() Configure(); Configure(); Configure(); - Configure(); + Configure(); //Configure(); //Configure(); //Configure(); diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Strings/en-us/Resources.resw b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Strings/en-us/Resources.resw index 45ef20e6..2a7944e3 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Strings/en-us/Resources.resw +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Strings/en-us/Resources.resw @@ -135,11 +135,11 @@ Processing & Filtering - - Setups + + Configurations - Devices & Setups + Devices & Configurations Management @@ -202,16 +202,16 @@ Troubleshooting - Normally off. Developer options are generally intended for developers, and those working with developers to test pre-release components. Non-developer MIDI users do not need to use developer options as they may adversely impact the stability, capability, or performance of MIDI on your computer. Developer options require that the PC be placed into Developer Mode in Windows settings. + Developer options are generally intended for developers, and those working with developers to test pre-release components. Non-developer MIDI users do not need to use developer options as they may adversely impact the stability, capability, or performance of MIDI on your computer. Developer options require that the PC be placed into Developer Mode in Windows settings. - Show developer options for MIDI + Developer options for MIDI Open Windows Settings "For developers" page - Developer Mode is not enabled. Enable developer mode in Windows Settings. + Developer Mode is not enabled. Enable developer mode in Windows Settings if you want to be able to install in-development and unsigned third-party service plugins. Windows MIDI Services on Discord @@ -238,7 +238,7 @@ Show developer options for MIDI - Developer Mode is currently enabled on this PC. To turn it off, use Windows Settings. + Developer Mode is currently enabled on this PC, enabling you to install in-development and third-party unsigned service plugins. To turn it off, use Windows Settings. MIDI Settings @@ -411,4 +411,64 @@ Other + + Discovery and Protocol Negotiation + + + Normally on. By default, the MIDI Service performs MIDI 2.0 discovery and protocol negotiation for any natively UMP device. If you are testing a device which breaks when those messages are sent, disable the process here. Requires a MIDI Service restart. + + + Enable MMCSS for the Incoming Message Queue + + + Normally off. MMCSS threads are a limited resource in Windows, and are often used for audio processing threads in DAW applications. If this feature is enabled, Windows MIDI Services will use MMCSS threads for handling incoming messages. This reduces the number of MMCSS threads available to the client process, and may cause applications to fail to work properly. + + + Windows MIDI Services will not use MMCSS threads in the client process. This is the recommended choice. + + + Service-based MIDI 2.0 discovery and protocol negotiation is currently disabled. + + + Windows MIDI Services will use MMCSS threads in the client process. This may cause some applications to malfunction. + + + Service-based MIDI 2.0 discovery and protocol negotiation is currently enabled. This is the recommended choice. + + + The Windows MIDI Service (midisrv.exe) must be restarted for changes to take effect. + + + Restart MIDI Service + + + https://aka.ms/midi + + + Windows MIDI Services Documentation + + + Windows MIDI and ASIO Discord Server + + + https://aka.ms/mididiscord + + + Windows MIDI Services GitHub Repo + + + https://aka.ms/midirepo + + + https://midi.org/specs + + + MIDI 1.0 and 2.0 Specifications at the MIDI Association + + + Discovery Timeout (milliseconds) + + + Save to Registry + \ No newline at end of file diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/ForDevelopersViewModel.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/ForDevelopersViewModel.cs index 6dee85b9..5b0fbb9a 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/ForDevelopersViewModel.cs +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/ForDevelopersViewModel.cs @@ -1,17 +1,109 @@ using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Microsoft.Midi.Settings.Contracts.Services; using Microsoft.Midi.Settings.Contracts.ViewModels; using Microsoft.Midi.Settings.Helpers; +using System.Windows.Input; namespace Microsoft.Midi.Settings.ViewModels; -public class ForDevelopersViewModel : ObservableRecipient +public partial class ForDevelopersViewModel : ObservableRecipient { + IMidiServiceRegistrySettingsService m_registrySettingsService; public bool IsDeveloperModeEnabled => WindowsDeveloperModeHelper.IsDeveloperModeEnabled; - public ForDevelopersViewModel() + [ObservableProperty] + public bool enableDiscoveryAndProtocolNegotiation; + + [ObservableProperty] + public bool enableMmcss; + + [ObservableProperty] + public bool serviceRestartRequired; + + + private UInt32 m_currentlySavedDiscoveryTimeoutValue; + + [ObservableProperty] + public UInt32 discoveryTimeout; + + public UInt32 DiscoveryTimeoutMaximum => 50000; // TODO: This needs to stay in sync with MidiDefs.h + public UInt32 DiscoveryTimeoutMinimum => 500; // TODO: This needs to stay in sync with MidiDefs.h + + [ObservableProperty] + public bool discoveryTimeoutHasChanged; + + + partial void OnDiscoveryTimeoutChanged(UInt32 value) { + if (value != m_currentlySavedDiscoveryTimeoutValue) + { + DiscoveryTimeoutHasChanged = true; + } + else + { + DiscoveryTimeoutHasChanged = false; + } + } + + partial void OnEnableDiscoveryAndProtocolNegotiationChanged(bool value) + { + // todo: should check against original values + ServiceRestartRequired = true; + } + + partial void OnEnableMmcssChanged(bool value) + { + // todo: should check against original values + ServiceRestartRequired = true; + } + + + public ICommand RestartServiceCommand { get; private set; } + + public ICommand CommitDiscoveryTimeoutCommand { get; private set; } + + public ICommand ShowMidiKernelStreamingDevicesCommand { get; private set; } + + + public ForDevelopersViewModel(IMidiServiceRegistrySettingsService registrySettingsService) + { + m_registrySettingsService = registrySettingsService; + + // TODO read these values from the registry + + m_currentlySavedDiscoveryTimeoutValue = m_registrySettingsService.GetMidi2DiscoveryTimeoutMS(); + + DiscoveryTimeout = m_currentlySavedDiscoveryTimeoutValue; + + EnableMmcss = m_registrySettingsService.GetUseMMCSS(); + EnableDiscoveryAndProtocolNegotiation = m_registrySettingsService.GetMidi2DiscoveryEnabled(); + + ServiceRestartRequired = false; + + + RestartServiceCommand = new RelayCommand(() => + { + // TODO: Call into service service to restart + }); + + CommitDiscoveryTimeoutCommand = new RelayCommand(() => + { + // TODO: save change to registry + + ServiceRestartRequired = true; + m_currentlySavedDiscoveryTimeoutValue = DiscoveryTimeout; + }); + + ShowMidiKernelStreamingDevicesCommand = new RelayCommand(() => + { + // TODO: save change to registry + + + }); + } diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/ShellViewModel.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/ShellViewModel.cs index e8baddf0..96311cf2 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/ShellViewModel.cs +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/ViewModels/ShellViewModel.cs @@ -18,10 +18,13 @@ public class ShellViewModel : ObservableRecipient private readonly IMidiConfigFileService m_configFileService; - public bool AreDeveloperOptionsEnabled - { - get => _generalSettingsService.ShowDeveloperOptions; - } + public bool IsDeveloperModeEnabled => WindowsDeveloperModeHelper.IsDeveloperModeEnabled; + + + //public bool AreDeveloperOptionsEnabled + //{ + // get => _generalSettingsService.ShowDeveloperOptions; + //} public INavigationService NavigationService { diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/ForDevelopersPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/ForDevelopersPage.xaml index 52488e3e..0e9e89a1 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/ForDevelopersPage.xaml +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/ForDevelopersPage.xaml @@ -27,94 +27,72 @@ Margin="1,0,0,8" HorizontalAlignment="Left" TextWrapping="Wrap"> - + + + +