diff --git a/build/staging/reg/WinRTActivationEntries.cs b/build/staging/reg/WinRTActivationEntries.cs
index 136c10fa7..7d4b1af0c 100644
--- a/build/staging/reg/WinRTActivationEntries.cs
+++ b/build/staging/reg/WinRTActivationEntries.cs
@@ -32,7 +32,8 @@ class RegistryEntries
new RegEntry{ ClassName="Windows.Devices.Midi2.MidiChannelEndpointListener", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.MidiGroupEndpointListener", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.MidiMessageTypeEndpointListener", ActivationType=0, Threading=0, TrustLevel=0 },
- new RegEntry{ ClassName="Windows.Devices.Midi2.MidiVirtualDevice", ActivationType=0, Threading=0, TrustLevel=0 },
+ new RegEntry{ ClassName="Windows.Devices.Midi2.MidiVirtualEndpointDevice", ActivationType=0, Threading=0, TrustLevel=0 },
+ new RegEntry{ ClassName="Windows.Devices.Midi2.MidiVirtualEndpointDeviceDefinition", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.MidiMessageReceivedEventArgs", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.MidiEndpointConnection", ActivationType=0, Threading=0, TrustLevel=0 },
new RegEntry{ ClassName="Windows.Devices.Midi2.IMidiEndpointConnectionStatics", ActivationType=0, Threading=0, TrustLevel=0 },
diff --git a/build/staging/reg/WinRTActivationEntries.xml b/build/staging/reg/WinRTActivationEntries.xml
index 6185867fa..6dbc0886f 100644
--- a/build/staging/reg/WinRTActivationEntries.xml
+++ b/build/staging/reg/WinRTActivationEntries.xml
@@ -134,7 +134,12 @@
trustLevel="Base"
/>
+
diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi
index c53aa8e4a..be10a3a13 100644
--- a/build/staging/version/BundleInfo.wxi
+++ b/build/staging/version/BundleInfo.wxi
@@ -1,4 +1,4 @@
-
+
diff --git a/diagnostics/trace-logging/TraceCaptureFile.etl b/diagnostics/trace-logging/TraceCaptureFile.etl
index 977cde1eb..cbe6e49e2 100644
Binary files a/diagnostics/trace-logging/TraceCaptureFile.etl and b/diagnostics/trace-logging/TraceCaptureFile.etl differ
diff --git a/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/App.xaml.cs b/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/App.xaml.cs
index eef3d66ba..0e264bd07 100644
--- a/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/App.xaml.cs
+++ b/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/App.xaml.cs
@@ -16,9 +16,6 @@
using Windows.Foundation;
using Windows.Foundation.Collections;
-// To learn more about WinUI, the WinUI project structure,
-// and more about our project templates, see: http://aka.ms/winui-project-info.
-
namespace MidiSample.AppToAppMidi
{
///
@@ -45,6 +42,6 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar
m_window.Activate();
}
- private Window m_window;
+ private Microsoft.UI.Xaml.Window m_window;
}
}
diff --git a/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MainWindow.xaml b/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MainWindow.xaml
index 3197904a9..c105badb4 100644
--- a/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MainWindow.xaml
+++ b/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MainWindow.xaml
@@ -8,54 +8,30 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MainWindow.xaml.cs b/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MainWindow.xaml.cs
index a8dad0485..7e36cd5bb 100644
--- a/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MainWindow.xaml.cs
+++ b/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MainWindow.xaml.cs
@@ -14,66 +14,129 @@
using Windows.AI.MachineLearning;
using Windows.Foundation;
using Windows.Foundation.Collections;
-
+using WinUIEx;
using midi2 = Windows.Devices.Midi2;
-// To learn more about WinUI, the WinUI project structure,
-// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace MidiSample.AppToAppMidi
{
- ///
- /// An empty window that can be used on its own or navigated to within a Frame.
- ///
- public sealed partial class MainWindow : Window
- {
+
+ public sealed partial class MainWindow : Microsoft.UI.Xaml.Window
+ {
private midi2.MidiSession _session;
private midi2.MidiEndpointConnection _connection;
+ public List Notes { get; }
+
public MainWindow()
{
this.InitializeComponent();
- this.Closed += MainWindow_Closed;
+ OpenConnection();
- this.AppWindow.MoveAndResize(new Windows.Graphics.RectInt32(100, 100, 600, 600));
+ var notes = new byte[] { 50, 52, 53, 55, 57, 58, 60, 62, 64, 65, 67, 69, 70, 72, 74, 76 };
+
+ Notes = notes.Select(n=>new Note() { NoteNumber = n, Connection = _connection, GroupIndex = 0, ChannelIndex = 0 }).ToList();
- OpenConnection();
+ //this.Closed += MainWindow_Closed;
+
+ //this.AppWindow.MoveAndResize(new Windows.Graphics.RectInt32(100, 100, 600, 600));
+
+ this.SetWindowSize(500, 550);
+ this.SetIsAlwaysOnTop(true);
+
+ this.Closed += MainWindow_Closed;
}
private void MainWindow_Closed(object sender, WindowEventArgs args)
{
+ _session.DisconnectEndpointConnection(_connection.ConnectionId);
_session.Dispose();
}
+ // in MIDI Services config file in Virtual Device MIDI section
+ //"createVirtualDevices":
+ //[
+ // {
+ // "associationIdentifier" : "{1EDD815E-B44B-488D-96EE-E8C81093D6AC}",
+ // "shortUniqueId": "PMB_APP2_8675309",
+ // "name": "Pad Controller",
+ // "description" : "App to app MIDI pad controller device app"
+ // }
+ //]
+ //
+ //const string _appDeviceConnectionId = "\\\\?\\SWD#MIDISRV#MIDIU_VIRTDEV_PMB_APP2_8675309#{e7cce071-3c03-423f-88d3-f1045d02552b}";
private void OpenConnection()
{
- _session = midi2.MidiSession.CreateSession("App to app MIDI sample");
- _connection = _session.CreateEndpointConnection(midi2.MidiEndpointDeviceInformation.DiagnosticsLoopbackAEndpointId);
- _connection.Open();
- }
+ System.Diagnostics.Debug.WriteLine("Open Connection enter");
- private byte GetMidiNoteNumberFromPad(Rectangle pad)
- {
- return byte.Parse(pad.Tag!.ToString());
- }
- private void OnPadPointerPressed(object sender, PointerRoutedEventArgs e)
- {
- byte note = GetMidiNoteNumberFromPad((Rectangle)sender);
- var message = midi2.MidiMessageBuilder.BuildMidi2ChannelVoiceMessage(0, 0, midi2.Midi2ChannelVoiceMessageStatus.NoteOn, 0, note, 1000);
+ // create our function blocks and endpoint info to be reported back through MIDI
- _connection.SendMessagePacket(message);
- }
+ var deviceDefinition = new midi2.MidiVirtualEndpointDeviceDefinition();
- private void OnPadPointerReleased(object sender, PointerRoutedEventArgs e)
- {
- byte note = GetMidiNoteNumberFromPad((Rectangle)sender);
+ deviceDefinition.FunctionBlocks.Add(new midi2.MidiFunctionBlock()
+ {
+ Number = 0,
+ IsActive = true,
+ Name = "Pads Output",
+ UIHint = midi2.MidiFunctionBlockUIHint.Sender,
+ FirstGroupIndex = 0,
+ GroupCount = 1,
+ Direction = midi2.MidiFunctionBlockDirection.Bidirectional,
+ Midi10Connection = midi2.MidiFunctionBlockMidi10.Not10,
+ MaxSystemExclusive8Streams = 0,
+ MidiCIMessageVersionFormat = 0
+ }) ;
+
+ deviceDefinition.AreFunctionBlocksStatic = true;
+ deviceDefinition.EndpointName = "Pad Controller App";
+ deviceDefinition.EndpointProductInstanceId = "PMB_APP2_8675309"; // this needs to match pre-configuration for now
+ deviceDefinition.SupportsMidi2ProtocolMessages = true;
+ deviceDefinition.SupportsMidi1ProtocolMessages = true;
+ deviceDefinition.SupportsReceivingJRTimestamps = false;
+ deviceDefinition.SupportsSendingJRTimestamps = false;
- var message = midi2.MidiMessageBuilder.BuildMidi2ChannelVoiceMessage(0, 0, midi2.Midi2ChannelVoiceMessageStatus.NoteOff, 0, note, 1000);
+ System.Diagnostics.Debug.WriteLine("Creating session");
- _connection.SendMessagePacket(message);
+ _session = midi2.MidiSession.CreateSession("App to app MIDI sample");
+
+ if (_session != null)
+ {
+ System.Diagnostics.Debug.WriteLine("Creating virtual device");
+
+ _connection = _session.CreateVirtualDeviceAndConnection(deviceDefinition);
+
+ if (_connection != null)
+ {
+ _connection.MessageReceived += _connection_MessageReceived;
+
+ System.Diagnostics.Debug.WriteLine("Connection created. About to open it.");
+
+ if (_connection.Open())
+ {
+ System.Diagnostics.Debug.WriteLine("Connection Opened");
+
+ this.AppWindow.Title = "App-to-app MIDI Pad Controller: Connected";
+ }
+ else
+ {
+ System.Diagnostics.Debug.WriteLine("Connection Open Failed");
+ this.AppWindow.Title = "App-to-app MIDI Pad Controller: (no connection)";
+ }
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.WriteLine("Session Open Failed");
+ // unable to open session
+ }
+ }
+
+ private void _connection_MessageReceived(midi2.IMidiMessageReceivedEventSource sender, midi2.MidiMessageReceivedEventArgs args)
+ {
+ System.Diagnostics.Debug.WriteLine("Message Received " + midi2.MidiMessageUtility.GetMessageFriendlyNameFromFirstWord(args.PeekFirstWord()));
}
}
}
diff --git a/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MidiSample.AppToAppMidi.csproj b/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MidiSample.AppToAppMidi.csproj
index 7258209dd..4b5cb392c 100644
--- a/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MidiSample.AppToAppMidi.csproj
+++ b/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/MidiSample.AppToAppMidi.csproj
@@ -27,9 +27,10 @@
-
-
-
+
+
+
+
diff --git a/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/Note.cs b/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/Note.cs
new file mode 100644
index 000000000..36b1bab6d
--- /dev/null
+++ b/get-started/midi-developers/app-developers/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi/Note.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using midi2 = Windows.Devices.Midi2;
+
+namespace MidiSample.AppToAppMidi
+{
+
+ public class Note
+ {
+ public midi2.MidiEndpointConnection Connection { get; set; }
+ public byte NoteNumber { get; set; }
+
+ public byte GroupIndex { get; set; }
+
+ public byte ChannelIndex { get; set; }
+
+ public void NoteOn() => Connection.SendMessagePacket(
+ midi2.MidiMessageBuilder.BuildMidi2ChannelVoiceMessage(
+ 0,
+ GroupIndex,
+ midi2.Midi2ChannelVoiceMessageStatus.NoteOn,
+ ChannelIndex,
+ (ushort)((ushort)NoteNumber << 8),
+ 1000));
+ public void NoteOff() => Connection.SendMessagePacket(
+ midi2.MidiMessageBuilder.BuildMidi2ChannelVoiceMessage(
+ 0,
+ GroupIndex,
+ midi2.Midi2ChannelVoiceMessageStatus.NoteOff,
+ ChannelIndex,
+ (ushort)((ushort)NoteNumber << 8),
+ 0));
+ }
+
+
+}
diff --git a/src/api/Abstraction/DiagnosticsAbstraction/Midi2.DiagnosticsEndpointManager.cpp b/src/api/Abstraction/DiagnosticsAbstraction/Midi2.DiagnosticsEndpointManager.cpp
index dc3e46104..e41fde02f 100644
--- a/src/api/Abstraction/DiagnosticsAbstraction/Midi2.DiagnosticsEndpointManager.cpp
+++ b/src/api/Abstraction/DiagnosticsAbstraction/Midi2.DiagnosticsEndpointManager.cpp
@@ -118,7 +118,7 @@ CMidi2DiagnosticsEndpointManager::CreateLoopbackEndpoint(
DEVPROP_BOOLEAN devPropTrue = DEVPROP_TRUE;
DEVPROP_BOOLEAN devPropFalse = DEVPROP_FALSE;
BYTE nativeDataFormat = MIDI_PROP_NATIVEDATAFORMAT_UMP;
- uint32_t supportedDataFormat = (BYTE)MidiDataFormat::MidiDataFormat_UMP;
+ UINT32 supportedDataFormat = (UINT32)MidiDataFormat::MidiDataFormat_UMP;
std::wstring description = L"Diagnostics loopback endpoint. For testing purposes.";
@@ -131,7 +131,7 @@ CMidi2DiagnosticsEndpointManager::CreateLoopbackEndpoint(
DEVPROP_TYPE_EMPTY, 0, nullptr},
{{PKEY_MIDI_SupportedDataFormats, DEVPROP_STORE_SYSTEM, nullptr},
- DEVPROP_TYPE_BYTE, static_cast(sizeof(BYTE)), &supportedDataFormat},
+ DEVPROP_TYPE_UINT32, static_cast(sizeof(UINT32)), &supportedDataFormat},
{{DEVPKEY_DeviceInterface_FriendlyName, DEVPROP_STORE_SYSTEM, nullptr},
@@ -236,7 +236,7 @@ CMidi2DiagnosticsEndpointManager::CreatePingEndpoint(
DEVPROP_BOOLEAN devPropTrue = DEVPROP_TRUE;
DEVPROP_BOOLEAN devPropFalse = DEVPROP_FALSE;
BYTE nativeDataFormat = MIDI_PROP_NATIVEDATAFORMAT_UMP;
- BYTE supportedDataFormat = (BYTE)MidiDataFormat::MidiDataFormat_UMP;
+ UINT32 supportedDataFormat = (UINT32)MidiDataFormat::MidiDataFormat_UMP;
auto endpointPurpose = (uint32_t)MidiEndpointDevicePurposePropertyValue::DiagnosticPing;
@@ -252,7 +252,7 @@ CMidi2DiagnosticsEndpointManager::CreatePingEndpoint(
DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(devPropFalse)), &devPropFalse},
{{PKEY_MIDI_SupportedDataFormats, DEVPROP_STORE_SYSTEM, nullptr},
- DEVPROP_TYPE_BYTE, static_cast(sizeof(BYTE)), &supportedDataFormat},
+ DEVPROP_TYPE_UINT32, static_cast(sizeof(UINT32)), &supportedDataFormat},
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiAbstraction.vcxproj b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiAbstraction.vcxproj
index c0d58ed52..47cdf40bb 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiAbstraction.vcxproj
+++ b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiAbstraction.vcxproj
@@ -280,6 +280,7 @@
+
@@ -298,6 +299,7 @@
+
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiAbstraction.vcxproj.filters b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiAbstraction.vcxproj.filters
index 9bb09290c..d55f91a52 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiAbstraction.vcxproj.filters
+++ b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiAbstraction.vcxproj.filters
@@ -33,6 +33,9 @@
Source Files
+
+ Source Files
+
@@ -73,6 +76,9 @@
Header Files
+
+ Header Files
+
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiBidi.cpp b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiBidi.cpp
index b2f47c6cd..1b0286447 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiBidi.cpp
+++ b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiBidi.cpp
@@ -20,65 +20,77 @@ CMidi2VirtualMidiBiDi::Initialize(
LONGLONG Context
)
{
+ OutputDebugString(__FUNCTION__ L" - enter\n");
+
TraceLoggingWrite(
MidiVirtualMidiAbstractionTelemetryProvider::Provider(),
- __FUNCTION__,
+ __FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingPointer(this, "this")
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(endpointId, "endpoint id")
);
m_callback = Callback;
m_callbackContext = Context;
-
- m_endpointId = internal::ToUpperTrimmedWStringCopy(endpointId);
+ m_endpointId = internal::NormalizeEndpointInterfaceIdCopy(endpointId);
- OutputDebugString(__FUNCTION__ L" Looking up Endpoint:");
- OutputDebugString(m_endpointId.c_str());
-
- // This should use SWD properties and not a string search
-
- if (m_endpointId.find(MIDI_VIRT_INSTANCE_ID_DEVICE_PREFIX) != std::wstring::npos)
+ //if (Context != MIDI_PROTOCOL_MANAGER_ENDPOINT_CREATION_CONTEXT)
{
- OutputDebugString(__FUNCTION__ L" - endpoint id is a virtual device\n");
+ OutputDebugString(__FUNCTION__ L" Looking up Endpoint:");
+ OutputDebugString(m_endpointId.c_str());
- m_isDeviceSide = true;
+ HRESULT hr = S_OK;
- RETURN_IF_FAILED(MidiEndpointTable::Current().OnDeviceConnected(m_endpointId, this));
- }
- else if (m_endpointId.find(MIDI_VIRT_INSTANCE_ID_CLIENT_PREFIX) != std::wstring::npos)
- {
- OutputDebugString(__FUNCTION__ L" - endpoint id is a virtual client\n");
+ // This should use SWD properties and not a string search
- m_isDeviceSide = false;
+ if (internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_VIRT_INSTANCE_ID_DEVICE_PREFIX))
+ {
+ OutputDebugString(__FUNCTION__ L" - endpoint id is a virtual device\n");
- RETURN_IF_FAILED(MidiEndpointTable::Current().OnClientConnected(m_endpointId, this));
+ m_isDeviceSide = true;
- }
- else
- {
- OutputDebugString(__FUNCTION__ L" - endpoint id is unknown type\n");
- OutputDebugString(m_endpointId.c_str());
+ LOG_IF_FAILED(hr = MidiEndpointTable::Current().OnDeviceConnected(m_endpointId, this));
+ }
+ else if (internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_VIRT_INSTANCE_ID_CLIENT_PREFIX))
+ {
+ OutputDebugString(__FUNCTION__ L" - endpoint id is a virtual client\n");
- // we don't understand this endpoint id
+ m_isDeviceSide = false;
- return E_FAIL;
- }
+ LOG_IF_FAILED(hr = MidiEndpointTable::Current().OnClientConnected(m_endpointId, this));
+ }
+ else
+ {
+ OutputDebugString(__FUNCTION__ L" - endpoint id is unknown type\n");
+ OutputDebugString(m_endpointId.c_str());
- return S_OK;
+ // we don't understand this endpoint id
+ hr = E_FAIL;
+ }
+
+ return hr;
+ }
+ //else
+ //{
+ // // we're in protocol negotiation
+ // m_isDeviceSide = false;
+ // return S_OK;
+ //}
}
HRESULT
CMidi2VirtualMidiBiDi::Cleanup()
{
- // TODO: Cleanup here needs additional logic to tear down the client endpoint
- // when this endpoint goes away
+ OutputDebugString(__FUNCTION__ L" - enter\n");
TraceLoggingWrite(
MidiVirtualMidiAbstractionTelemetryProvider::Provider(),
__FUNCTION__,
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingPointer(this, "this")
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(m_endpointId.c_str(), "endpoint id"),
+ TraceLoggingBool(m_isDeviceSide, "is device side")
);
m_callback = nullptr;
@@ -86,12 +98,18 @@ CMidi2VirtualMidiBiDi::Cleanup()
if (m_isDeviceSide)
{
+ OutputDebugString(__FUNCTION__ L" - this is the device BiDi, so calling OnDeviceDisconnected\n");
+
MidiEndpointTable::Current().OnDeviceDisconnected(m_endpointId);
}
+ else
+ {
+ OutputDebugString(__FUNCTION__ L" - this is the client BiDi. Nothing needed here.\n");
+ }
- //m_LinkedClientBiDi->Release();
- m_linkedBiDiCallback = nullptr;
- m_linkedBiDi = nullptr;
+ UnlinkAssociatedBiDi();
+
+ OutputDebugString(__FUNCTION__ L" - exit\n");
return S_OK;
}
@@ -104,6 +122,15 @@ CMidi2VirtualMidiBiDi::SendMidiMessage(
LONGLONG Position
)
{
+ TraceLoggingWrite(
+ MidiVirtualMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(m_endpointId.c_str(), "endpoint id"),
+ TraceLoggingBool(m_isDeviceSide, "is device side")
+ );
+
// message received from the device
RETURN_HR_IF_NULL(E_INVALIDARG, Message);
@@ -129,6 +156,15 @@ CMidi2VirtualMidiBiDi::Callback(
LONGLONG Context
)
{
+ TraceLoggingWrite(
+ MidiVirtualMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(m_endpointId.c_str(), "endpoint id"),
+ TraceLoggingBool(m_isDeviceSide, "is device side")
+ );
+
// message received from the client
if (m_callback != nullptr)
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiBidi.h b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiBidi.h
index 42e8c2b2c..ae22ae349 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiBidi.h
+++ b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiBidi.h
@@ -30,11 +30,19 @@ class CMidi2VirtualMidiBiDi :
return S_OK;
}
+ HRESULT UnlinkAssociatedBiDi()
+ {
+ m_linkedBiDi = nullptr;
+ m_linkedBiDiCallback = nullptr;
+
+ return S_OK;
+ }
+
private:
wil::com_ptr_nothrow m_linkedBiDi;
- IMidiCallback* m_linkedBiDiCallback;
- IMidiCallback* m_callback;
+ wil::com_ptr_nothrow m_linkedBiDiCallback;
+ wil::com_ptr_nothrow m_callback;
LONGLONG m_callbackContext;
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp
index dbd3a868c..09a918517 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp
+++ b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp
@@ -65,7 +65,7 @@ _Use_decl_annotations_
HRESULT
CMidi2VirtualMidiEndpointManager::Initialize(
IUnknown* MidiDeviceManager,
- IUnknown* /*midiEndpointProtocolManager*/,
+ IUnknown* MidiEndpointProtocolManager,
LPCWSTR ConfigurationJson
)
{
@@ -79,8 +79,12 @@ CMidi2VirtualMidiEndpointManager::Initialize(
);
RETURN_HR_IF(E_INVALIDARG, nullptr == MidiDeviceManager);
+ RETURN_HR_IF(E_INVALIDARG, nullptr == MidiEndpointProtocolManager);
RETURN_IF_FAILED(MidiDeviceManager->QueryInterface(__uuidof(IMidiDeviceManagerInterface), (void**)&m_MidiDeviceManager));
+ RETURN_IF_FAILED(MidiEndpointProtocolManager->QueryInterface(__uuidof(IMidiEndpointProtocolManagerInterface), (void**)&m_MidiProtocolManager));
+
+
m_TransportAbstractionId = AbstractionLayerGUID; // this is needed so MidiSrv can instantiate the correct transport
m_ContainerId = m_TransportAbstractionId; // we use the transport ID as the container ID for convenience
@@ -153,6 +157,7 @@ CMidi2VirtualMidiEndpointManager::ApplyJson(json::JsonObject jsonObject)
deviceEntry.BaseEndpointName = GetJsonStringValue(jsonEntry, MIDI_VIRT_JSON_DEVICE_PROPERTY_NAME, L"");
deviceEntry.Description = GetJsonStringValue(jsonEntry, MIDI_VIRT_JSON_DEVICE_PROPERTY_DESCRIPTION, L"");
+
// if no association id, or it already exists in the table, bail
// if no unique Id, bail or maybe generate one
@@ -173,6 +178,37 @@ CMidi2VirtualMidiEndpointManager::ApplyJson(json::JsonObject jsonObject)
+_Use_decl_annotations_
+HRESULT
+CMidi2VirtualMidiEndpointManager::DeleteClientEndpoint(std::wstring clientShortInstanceId)
+{
+ OutputDebugString(__FUNCTION__ L" - enter\n");
+
+ // get the instance id for the device
+
+ if (m_MidiDeviceManager != nullptr)
+ {
+ //auto instanceId = GetSwdPropertyInstanceId(clientEndpointInterfaceId);
+ auto instanceId = internal::NormalizeDeviceInstanceIdCopy(clientShortInstanceId);
+
+ if (instanceId != L"")
+ {
+ return m_MidiDeviceManager->RemoveEndpoint(instanceId.c_str());
+ }
+ else
+ {
+ OutputDebugString(__FUNCTION__ L" - could not find instanceId property for client\n");
+
+ return E_FAIL;
+ }
+ }
+ else
+ {
+ // null device manager
+ return E_FAIL;
+ }
+}
+
HRESULT
CMidi2VirtualMidiEndpointManager::CreateParentDevice()
@@ -181,7 +217,7 @@ CMidi2VirtualMidiEndpointManager::CreateParentDevice()
// the parent device parameters are set by the transport (this)
std::wstring parentDeviceName{ TRANSPORT_PARENT_DEVICE_NAME };
- std::wstring parentDeviceId{ TRANSPORT_PARENT_ID };
+ std::wstring parentDeviceId{ internal::NormalizeDeviceInstanceIdCopy(TRANSPORT_PARENT_ID) };
SW_DEVICE_CREATE_INFO createInfo = {};
createInfo.cbSize = sizeof(createInfo);
@@ -201,9 +237,9 @@ CMidi2VirtualMidiEndpointManager::CreateParentDevice()
deviceIdMaxSize
));
- m_parentDeviceId = std::wstring(newDeviceId);
+ m_parentDeviceId = internal::NormalizeDeviceInstanceIdCopy(newDeviceId);
- OutputDebugString(__FUNCTION__ L" New parent device id: ");
+ OutputDebugString(__FUNCTION__ L" New parent device instance id: ");
OutputDebugString(newDeviceId);
OutputDebugString(L"\n");
@@ -212,10 +248,29 @@ CMidi2VirtualMidiEndpointManager::CreateParentDevice()
+_Use_decl_annotations_
+HRESULT
+CMidi2VirtualMidiEndpointManager::NegotiateAndRequestMetadata(std::wstring endpointId)
+{
+ bool preferToSendJRToEndpoint{ false };
+ bool preferToReceiveJRFromEndpoint{ false };
+ BYTE preferredProtocol{ MIDI_PROP_CONFIGURED_PROTOCOL_MIDI2 };
+ WORD negotiationTimeoutMS{ 2000 };
+
+ RETURN_IF_FAILED(m_MidiProtocolManager->NegotiateAndRequestMetadata(
+ endpointId.c_str(),
+ preferToSendJRToEndpoint,
+ preferToReceiveJRFromEndpoint,
+ preferredProtocol,
+ negotiationTimeoutMS
+ ));
+ return S_OK;
+}
_Use_decl_annotations_
-HRESULT CMidi2VirtualMidiEndpointManager::CreateClientVisibleEndpoint(
+HRESULT
+CMidi2VirtualMidiEndpointManager::CreateClientVisibleEndpoint(
MidiVirtualDeviceEndpointEntry& entry
)
{
@@ -228,7 +283,7 @@ HRESULT CMidi2VirtualMidiEndpointManager::CreateClientVisibleEndpoint(
DEVPROP_BOOLEAN devPropTrue = DEVPROP_TRUE;
// DEVPROP_BOOLEAN devPropFalse = DEVPROP_FALSE;
BYTE nativeDataFormat = MIDI_PROP_NATIVEDATAFORMAT_UMP;
- BYTE supportedDataFormat = (BYTE)MidiDataFormat::MidiDataFormat_UMP;
+ UINT32 supportedDataFormat = (UINT32)MidiDataFormat::MidiDataFormat_UMP;
// this purpose is important because it controls how this shows up to clients
auto endpointPurpose = (uint32_t)MidiEndpointDevicePurposePropertyValue::NormalMessageEndpoint;
@@ -244,7 +299,7 @@ HRESULT CMidi2VirtualMidiEndpointManager::CreateClientVisibleEndpoint(
DEVPROP_TYPE_EMPTY, 0, nullptr},
{{PKEY_MIDI_SupportedDataFormats, DEVPROP_STORE_SYSTEM, nullptr},
- DEVPROP_TYPE_BYTE, static_cast(sizeof(BYTE)), &supportedDataFormat},
+ DEVPROP_TYPE_UINT32, static_cast(sizeof(UINT32)), &supportedDataFormat},
{{PKEY_MIDI_SupportsMulticlient, DEVPROP_STORE_SYSTEM, nullptr},
DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(devPropTrue)),& devPropTrue},
@@ -291,7 +346,8 @@ HRESULT CMidi2VirtualMidiEndpointManager::CreateClientVisibleEndpoint(
createInfo.cbSize = sizeof(createInfo);
- std::wstring instanceId = MIDI_VIRT_INSTANCE_ID_CLIENT_PREFIX + entry.ShortUniqueId;
+ std::wstring instanceId = internal::NormalizeDeviceInstanceIdCopy(MIDI_VIRT_INSTANCE_ID_CLIENT_PREFIX + entry.ShortUniqueId);
+
createInfo.pszInstanceId = instanceId.c_str();
@@ -325,15 +381,14 @@ HRESULT CMidi2VirtualMidiEndpointManager::CreateClientVisibleEndpoint(
// loopback transport.
m_MidiDeviceManager->DeleteAllEndpointInProtocolDiscoveredProperties(newDeviceInterfaceId);
- // TODO: Invoke the protocol negotiator to now capture updated endpoint info.
+ // we need this for removal later
+ entry.CreatedShortClientInstanceId = instanceId;
- entry.CreatedClientEndpointId = newDeviceInterfaceId;
+ entry.CreatedClientEndpointId = internal::NormalizeEndpointInterfaceIdCopy(newDeviceInterfaceId);
//MidiEndpointTable::Current().AddCreatedEndpointDevice(entry);
//MidiEndpointTable::Current().AddCreatedClient(entry.VirtualEndpointAssociationId, entry.CreatedClientEndpointId);
-
-
OutputDebugString(__FUNCTION__ L": Complete\n");
return S_OK;
@@ -354,7 +409,7 @@ HRESULT CMidi2VirtualMidiEndpointManager::CreateDeviceSideEndpoint(
DEVPROP_BOOLEAN devPropTrue = DEVPROP_TRUE;
DEVPROP_BOOLEAN devPropFalse = DEVPROP_FALSE;
BYTE nativeDataFormat = MIDI_PROP_NATIVEDATAFORMAT_UMP;
- BYTE supportedDataFormat = (BYTE)MidiDataFormat::MidiDataFormat_UMP;
+ UINT32 supportedDataFormat = (UINT32)MidiDataFormat::MidiDataFormat_UMP;
// this purpose is important because it controls how this shows up to clients
@@ -363,14 +418,14 @@ HRESULT CMidi2VirtualMidiEndpointManager::CreateDeviceSideEndpoint(
OutputDebugString(__FUNCTION__ L": Building DEVPROPERTY interfaceDevProperties[]\n");
std::wstring endpointName = entry.BaseEndpointName + L" (Virtual MIDI Device)";
- std::wstring endpointDescription = entry.Description + L" (for use only by the device host application)";
+ std::wstring endpointDescription = entry.Description + L" (This endpoint for use only by the device host application.)";
DEVPROPERTY interfaceDevProperties[] = {
{{PKEY_MIDI_AssociatedUMP, DEVPROP_STORE_SYSTEM, nullptr},
DEVPROP_TYPE_EMPTY, 0, nullptr},
{{PKEY_MIDI_SupportedDataFormats, DEVPROP_STORE_SYSTEM, nullptr},
- DEVPROP_TYPE_BYTE, static_cast(sizeof(BYTE)), &supportedDataFormat},
+ DEVPROP_TYPE_UINT32, static_cast(sizeof(UINT32)), &supportedDataFormat},
// the device side connection is NOT multi-client. Keep it just to the device app
{{PKEY_MIDI_SupportsMulticlient, DEVPROP_STORE_SYSTEM, nullptr},
@@ -421,7 +476,7 @@ HRESULT CMidi2VirtualMidiEndpointManager::CreateDeviceSideEndpoint(
createInfo.cbSize = sizeof(createInfo);
- std::wstring instanceId = MIDI_VIRT_INSTANCE_ID_DEVICE_PREFIX + entry.ShortUniqueId;
+ std::wstring instanceId = internal::NormalizeDeviceInstanceIdCopy(MIDI_VIRT_INSTANCE_ID_DEVICE_PREFIX + entry.ShortUniqueId);
createInfo.pszInstanceId = instanceId.c_str();
@@ -455,7 +510,7 @@ HRESULT CMidi2VirtualMidiEndpointManager::CreateDeviceSideEndpoint(
// loopback transport.
m_MidiDeviceManager->DeleteAllEndpointInProtocolDiscoveredProperties(newDeviceInterfaceId);
- entry.CreatedDeviceEndpointId = newDeviceInterfaceId;
+ entry.CreatedDeviceEndpointId = internal::NormalizeEndpointInterfaceIdCopy(newDeviceInterfaceId);
MidiEndpointTable::Current().AddCreatedEndpointDevice(entry);
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.h b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.h
index 6a1184884..1590824be 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.h
+++ b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.h
@@ -35,6 +35,10 @@ class CMidi2VirtualMidiEndpointManager :
HRESULT ApplyJson(_In_ json::JsonObject jsonObject);
+ HRESULT NegotiateAndRequestMetadata(_In_ std::wstring endpointInterfaceId);
+
+ HRESULT DeleteClientEndpoint(_In_ std::wstring clientShortInstanceId);
+
//HRESULT DeleteEndpointPair(
// _In_ GUID const VirtualEndpointAssociationGuid
//);
@@ -51,6 +55,7 @@ class CMidi2VirtualMidiEndpointManager :
HRESULT CreateParentDevice();
wil::com_ptr_nothrow m_MidiDeviceManager;
+ wil::com_ptr_nothrow m_MidiProtocolManager;
//json::JsonObject m_JsonObject{ nullptr };
};
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/MidiEndpointTable.cpp b/src/api/Abstraction/VirtualMidiAbstraction/MidiEndpointTable.cpp
index a766be2ee..10011894c 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/MidiEndpointTable.cpp
+++ b/src/api/Abstraction/VirtualMidiAbstraction/MidiEndpointTable.cpp
@@ -22,49 +22,6 @@ MidiEndpointTable& MidiEndpointTable::Current()
}
-//HRESULT
-//GetDeviceVirtualEndpointAssociationId(_In_ std::wstring deviceId, _Inout_ std::wstring& associationId)
-//{
-// OutputDebugString(__FUNCTION__ L" enter");
-//
-// auto additionalProperties = winrt::single_threaded_vector();
-// additionalProperties.Append(winrt::to_hstring(STRING_PKEY_MIDI_SupportedDataFormats));
-// auto deviceInfo = DeviceInformation::CreateFromIdAsync(MidiDevice, additionalProperties, winrt::Windows::Devices::Enumeration::DeviceInformationKind::DeviceInterface).get();
-//
-// auto prop = deviceInfo.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_SupportedDataFormats));
-// if (prop)
-// {
-// OutputDebugString(__FUNCTION__ L" found property");
-//
-// // this interface is pointing to a UMP interface, so use that instance id.
-// DataFormat = (MidiDataFormat)winrt::unbox_value(prop);
-// }
-// else
-// {
-// OutputDebugString(__FUNCTION__ L" didn't find property");
-// // default to any
-// DataFormat = MidiDataFormat::MidiDataFormat_Any;
-// }
-//
-// OutputDebugString(__FUNCTION__ L" exiting OK");
-//
-// return S_OK;
-//}
-//
-//
-//
-//
-//
-//
-//
-//
-//
-
-
-
-
-
-
HRESULT MidiEndpointTable::Cleanup()
{
@@ -89,65 +46,46 @@ HRESULT MidiEndpointTable::AddCreatedEndpointDevice(MidiVirtualDeviceEndpointEnt
return S_OK;
}
-std::wstring GetStringSWDProperty(_In_ std::wstring instanceId, _In_ std::wstring propertyName, _In_ std::wstring defaultValue)
-{
- auto propertyKey = winrt::to_hstring(propertyName.c_str());
-
- auto additionalProperties = winrt::single_threaded_vector();
- additionalProperties.Append(propertyKey);
-
-
- auto deviceInfo = winrt::Windows::Devices::Enumeration::DeviceInformation::CreateFromIdAsync(
- winrt::to_hstring(instanceId.c_str()),
- additionalProperties,
- winrt::Windows::Devices::Enumeration::DeviceInformationKind::DeviceInterface).get();
-
- auto prop = deviceInfo.Properties().Lookup(propertyKey);
-
- if (prop)
- {
- OutputDebugString(__FUNCTION__ L" found property");
-
- // this interface is pointing to a UMP interface, so use that instance id.
- return (winrt::unbox_value(prop)).c_str();
- }
- else
- {
- OutputDebugString(__FUNCTION__ L" didn't find property");
- // default to any
- return defaultValue;
- }
-
-}
_Use_decl_annotations_
-HRESULT MidiEndpointTable::OnClientConnected(std::wstring clientInstanceId, CMidi2VirtualMidiBiDi* clientBiDi)
+HRESULT
+MidiEndpointTable::OnClientConnected(std::wstring clientEndpointInterfaceId, CMidi2VirtualMidiBiDi* clientBiDi)
{
// get the device BiDi, and then wire them together
-
try
{
OutputDebugString(__FUNCTION__ L"");
- std::wstring cleanId = internal::ToUpperTrimmedWStringCopy(clientInstanceId);
+ std::wstring cleanId = internal::NormalizeEndpointInterfaceIdCopy(clientEndpointInterfaceId);
// look up the association ID in SWD properties
- auto associationId = internal::ToUpperTrimmedWStringCopy(GetStringSWDProperty(cleanId, STRING_PKEY_MIDI_VirtualMidiEndpointAssociator, L""));
+ auto associationId = GetSwdPropertyVirtualEndpointAssociationId(clientEndpointInterfaceId);
if (associationId != L"")
{
- // find this id in the table
- auto entry = m_endpoints[associationId];
+ if (m_endpoints.find(associationId) != m_endpoints.end())
+ {
+ // find this id in the table
+ auto entry = m_endpoints[associationId];
- entry.MidiClientBiDi = clientBiDi;
- entry.CreatedClientEndpointId = cleanId;
+ if (entry.MidiClientBiDi == nullptr)
+ {
+ entry.MidiClientBiDi = clientBiDi;
+ entry.CreatedClientEndpointId = cleanId;
- entry.MidiDeviceBiDi->LinkAssociatedBiDi(clientBiDi);
- clientBiDi->LinkAssociatedBiDi(entry.MidiDeviceBiDi);
+ entry.MidiDeviceBiDi->LinkAssociatedBiDi(clientBiDi);
+ clientBiDi->LinkAssociatedBiDi(entry.MidiDeviceBiDi);
- m_endpoints[associationId] = entry;
+ m_endpoints[associationId] = entry;
+ }
+ }
+ else
+ {
+ // couldn't find the entry
+ OutputDebugString(__FUNCTION__ L" - unable to find device table entry");
+ }
}
}
CATCH_RETURN();
@@ -156,46 +94,66 @@ HRESULT MidiEndpointTable::OnClientConnected(std::wstring clientInstanceId, CMid
}
+
_Use_decl_annotations_
-HRESULT MidiEndpointTable::OnDeviceConnected(std::wstring deviceInstanceId, CMidi2VirtualMidiBiDi* deviceBiDi)
+HRESULT MidiEndpointTable::OnDeviceConnected(std::wstring deviceEndpointInterfaceId, CMidi2VirtualMidiBiDi* deviceBiDi)
{
- // get the device BiDi, and then wire them together
-
-
try
{
OutputDebugString(__FUNCTION__ L"");
- std::wstring cleanId = internal::ToUpperTrimmedWStringCopy(deviceInstanceId);
-
// look up the association ID in SWD properties
-
- auto associationId = internal::ToUpperTrimmedWStringCopy(GetStringSWDProperty(cleanId, STRING_PKEY_MIDI_VirtualMidiEndpointAssociator, L""));
+ auto associationId = GetSwdPropertyVirtualEndpointAssociationId(deviceEndpointInterfaceId);
if (associationId != L"")
{
- // find this id in the table
- auto entry = m_endpoints[associationId];
+ if (m_endpoints.find(associationId) != m_endpoints.end())
+ {
+ // find this id in the table
+ auto entry = m_endpoints[associationId];
- entry.MidiDeviceBiDi = deviceBiDi;
+ if (entry.MidiDeviceBiDi == nullptr)
+ {
+ OutputDebugString(__FUNCTION__ L" - no registered device bidi yet\n");
- m_endpoints[associationId] = entry;
+ entry.MidiDeviceBiDi = deviceBiDi;
+ m_endpoints[associationId] = entry;
- // if we have an endpoint manager, go ahead and create the client endpoint
- if (m_endpointManager)
- {
- OutputDebugString(__FUNCTION__ L" - Creating client endpoint");
+ }
+ else
+ {
+ // already created. Exit. This happens during protocol negotiation.
+ }
- // create the client endpoint
- RETURN_IF_FAILED(m_endpointManager->CreateClientVisibleEndpoint(entry));
- m_endpoints[associationId] = entry;
- }
- else
- {
- OutputDebugString(__FUNCTION__ L" - Endpoint Manager is null");
+ // if we have an endpoint manager, go ahead and create the client endpoint
+ if (m_endpointManager && entry.CreatedClientEndpointId == L"")
+ {
+ entry.MidiClientBiDi = nullptr;
+ entry.CreatedClientEndpointId = L"";
+
+ OutputDebugString(__FUNCTION__ L" - Creating client endpoint");
+
+ // create the client endpoint
+ RETURN_IF_FAILED(m_endpointManager->CreateClientVisibleEndpoint(entry));
+
+ OutputDebugString(__FUNCTION__ L" - Client endpoint created");
+
+ m_endpoints[associationId] = entry;
+
+
+ // LOG_IF_FAILED(m_endpointManager->NegotiateAndRequestMetadata(entry.CreatedClientEndpointId));
+
+
+ }
+ else
+ {
+ OutputDebugString(__FUNCTION__ L" - Endpoint Manager is null or created client endpoint ID is not empty");
+
+ return E_FAIL;
+ }
+
- return E_FAIL;
}
}
}
@@ -205,15 +163,66 @@ HRESULT MidiEndpointTable::OnDeviceConnected(std::wstring deviceInstanceId, CMid
}
_Use_decl_annotations_
-HRESULT MidiEndpointTable::OnDeviceDisconnected(std::wstring /*deviceInstanceId*/)
+HRESULT MidiEndpointTable::OnDeviceDisconnected(std::wstring deviceEndpointInterfaceId)
{
- OutputDebugString(__FUNCTION__ L"");
+ try
+ {
+ OutputDebugString(__FUNCTION__ L" - enter\n");
- //std::wstring deviceEndpointId{ deviceInstanceId };
- //internal::InPlaceToUpper(deviceEndpointId);
+ if (m_endpointManager != nullptr)
+ {
+ std::wstring associationId = GetSwdPropertyVirtualEndpointAssociationId(deviceEndpointInterfaceId);
+ if (associationId != L"")
+ {
+ OutputDebugString(__FUNCTION__ L" - Getting virtual endpoints table entry\n");
+
+ if (m_endpoints.find(associationId) != m_endpoints.end())
+ {
+ auto entry = m_endpoints[associationId];
+
+ if (entry.MidiClientBiDi != nullptr)
+ {
+ // unlink the bidi devices
+ entry.MidiClientBiDi->UnlinkAssociatedBiDi();
+ entry.MidiClientBiDi = nullptr;
+ }
+
+ if (entry.MidiDeviceBiDi != nullptr)
+ {
+ // unlink the bidi devices
+ entry.MidiDeviceBiDi->UnlinkAssociatedBiDi();
+ entry.MidiDeviceBiDi = nullptr;
+
+ // deactivate the client
+ m_endpointManager->DeleteClientEndpoint(entry.CreatedShortClientInstanceId);
+
+ entry.CreatedShortClientInstanceId = L"";
+ entry.CreatedClientEndpointId = L"";
+ }
+
+ // update with changes
+ m_endpoints[associationId] = entry;
+
+ }
+ else
+ {
+ OutputDebugString(__FUNCTION__ L" - no entry for associationId\n");
+ }
+ }
+ else
+ {
+ OutputDebugString(__FUNCTION__ L" - unable to get association Id\n");
+ }
+ }
+ else
+ {
+ OutputDebugString(__FUNCTION__ L" - endpoint manager is null\n");
+ }
- // tear down the client device and BiDi as well
+ OutputDebugString(__FUNCTION__ L" - exit\n");
+ }
+ CATCH_LOG();
return S_OK;
}
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/MidiEndpointTable.h b/src/api/Abstraction/VirtualMidiAbstraction/MidiEndpointTable.h
index 3285db0f3..763eebae1 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/MidiEndpointTable.h
+++ b/src/api/Abstraction/VirtualMidiAbstraction/MidiEndpointTable.h
@@ -52,16 +52,18 @@
struct MidiVirtualDeviceEndpointEntry
{
- std::wstring VirtualEndpointAssociationId; // how the config entries associate endpoints. Typically a GUID
- std::wstring BaseEndpointName;
- std::wstring Description;
- std::wstring ShortUniqueId;
+ std::wstring VirtualEndpointAssociationId{ L"" }; // how the config entries associate endpoints. Typically a GUID
+ std::wstring BaseEndpointName{ L"" };
+ std::wstring Description{ L"" };
+ std::wstring ShortUniqueId{ L"" };
- std::wstring CreatedDeviceEndpointId; // the device interface id
- std::wstring CreatedClientEndpointId;
+ std::wstring CreatedDeviceEndpointId{ L"" }; // the device interface id
+ std::wstring CreatedClientEndpointId{ L"" };
+ std::wstring CreatedShortClientInstanceId{ L"" };
- wil::com_ptr_nothrow MidiDeviceBiDi;
- wil::com_ptr_nothrow MidiClientBiDi;
+
+ wil::com_ptr_nothrow MidiDeviceBiDi{ nullptr };
+ wil::com_ptr_nothrow MidiClientBiDi{ nullptr };
~MidiVirtualDeviceEndpointEntry()
{
@@ -91,9 +93,9 @@ class MidiEndpointTable
HRESULT AddCreatedEndpointDevice(_In_ MidiVirtualDeviceEndpointEntry& entry) noexcept;
- HRESULT OnDeviceConnected(_In_ std::wstring deviceInstanceId, _In_ CMidi2VirtualMidiBiDi* deviceBiDi);
- HRESULT OnClientConnected(_In_ std::wstring clientInstanceId, _In_ CMidi2VirtualMidiBiDi* clientBiDi);
- HRESULT OnDeviceDisconnected(_In_ std::wstring deviceInstanceId);
+ HRESULT OnDeviceConnected(_In_ std::wstring deviceEndpointInterfaceId, _In_ CMidi2VirtualMidiBiDi* deviceBiDi);
+ HRESULT OnClientConnected(_In_ std::wstring clientEndpointInterfaceId, _In_ CMidi2VirtualMidiBiDi* clientBiDi);
+ HRESULT OnDeviceDisconnected(_In_ std::wstring deviceEndpointInterfaceId);
HRESULT Cleanup();
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/pch.h b/src/api/Abstraction/VirtualMidiAbstraction/pch.h
index 99db8677c..85cce630f 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/pch.h
+++ b/src/api/Abstraction/VirtualMidiAbstraction/pch.h
@@ -77,10 +77,15 @@ namespace internal = ::Windows::Devices::Midi2::Internal;
#include "mididevicemanagerinterface_i.c"
#include "mididevicemanagerinterface.h"
+#include "MidiEndpointProtocolManagerInterface_i.c"
+#include "MidiEndpointProtocolManagerInterface.h"
+
+
#include "dllmain.h"
#include "MidiDefs.h"
#include "MidiXProc.h"
+#include "swd_shared.h"
class CMidi2VirtualMidiEndpointManager;
class CMidi2VirtualMidiBiDi;
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/swd_shared.cpp b/src/api/Abstraction/VirtualMidiAbstraction/swd_shared.cpp
new file mode 100644
index 000000000..c1eac76c5
--- /dev/null
+++ b/src/api/Abstraction/VirtualMidiAbstraction/swd_shared.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+
+#include "pch.h"
+
+#include "swd_shared.h"
+
+_Use_decl_annotations_
+std::wstring GetStringSwdProperty(std::wstring deviceInterfaceId, std::wstring propertyName, std::wstring defaultValue)
+{
+ auto propertyKey = winrt::to_hstring(propertyName.c_str());
+
+ auto additionalProperties = winrt::single_threaded_vector();
+ additionalProperties.Append(propertyKey);
+
+
+ auto deviceInfo = winrt::Windows::Devices::Enumeration::DeviceInformation::CreateFromIdAsync(
+ winrt::to_hstring(deviceInterfaceId.c_str()),
+ additionalProperties,
+ winrt::Windows::Devices::Enumeration::DeviceInformationKind::DeviceInterface).get();
+
+ auto prop = deviceInfo.Properties().Lookup(propertyKey);
+
+ if (prop)
+ {
+ OutputDebugString(__FUNCTION__ L" found property");
+
+ // this interface is pointing to a UMP interface, so use that instance id.
+ return (winrt::unbox_value(prop)).c_str();
+ }
+ else
+ {
+ OutputDebugString(__FUNCTION__ L" didn't find property");
+ // default to any
+ return defaultValue;
+ }
+
+}
+
+
+_Use_decl_annotations_
+std::wstring GetSwdPropertyVirtualEndpointAssociationId(std::wstring deviceInterfaceId)
+{
+ std::wstring cleanId = internal::NormalizeEndpointInterfaceIdCopy(deviceInterfaceId);
+
+ return internal::ToUpperTrimmedWStringCopy(GetStringSwdProperty(cleanId, STRING_PKEY_MIDI_VirtualMidiEndpointAssociator, L""));
+}
+
+
+_Use_decl_annotations_
+std::wstring GetSwdPropertyInstanceId(std::wstring deviceInterfaceId)
+{
+ std::wstring cleanId = internal::NormalizeEndpointInterfaceIdCopy(deviceInterfaceId);
+
+ return internal::NormalizeDeviceInstanceIdCopy(GetStringSwdProperty(cleanId, L"System.Devices.DeviceInstanceId", L""));
+}
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/swd_shared.h b/src/api/Abstraction/VirtualMidiAbstraction/swd_shared.h
new file mode 100644
index 000000000..379cf1978
--- /dev/null
+++ b/src/api/Abstraction/VirtualMidiAbstraction/swd_shared.h
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+// TODO: Post-NAMM, move this to a lib that all projects share
+
+#pragma once
+
+std::wstring GetSwdStringProperty(_In_ std::wstring deviceInterfaceId, _In_ std::wstring propertyName, _In_ std::wstring defaultValue);
+
+std::wstring GetSwdPropertyVirtualEndpointAssociationId(_In_ std::wstring deviceInterfaceId);
+
+std::wstring GetSwdPropertyInstanceId(_In_ std::wstring deviceInterfaceId);
diff --git a/src/api/Client/Midi2Client-Projection/nuget/Windows.Devices.Midi2.nuspec b/src/api/Client/Midi2Client-Projection/nuget/Windows.Devices.Midi2.nuspec
index 3844a56be..529ba15fd 100644
--- a/src/api/Client/Midi2Client-Projection/nuget/Windows.Devices.Midi2.nuspec
+++ b/src/api/Client/Midi2Client-Projection/nuget/Windows.Devices.Midi2.nuspec
@@ -2,7 +2,7 @@
Windows.Devices.Midi2
- 1.0.0-preview.3-0134
+ 1.0.0-preview.3-0136
Microsoft Corporation
Windows MIDI Services API. Minimum package necessary to use Windows MIDI Services from an app on a PC that has Windows MIDI Services installed.
MIT
diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection.cpp b/src/api/Client/Midi2Client/MidiEndpointConnection.cpp
index 13b5613e2..2e03a72bd 100644
--- a/src/api/Client/Midi2Client/MidiEndpointConnection.cpp
+++ b/src/api/Client/Midi2Client/MidiEndpointConnection.cpp
@@ -147,6 +147,8 @@ namespace winrt::Windows::Devices::Midi2::implementation
midi2::MidiEndpointConnectionOptions options
)
{
+ OutputDebugString(__FUNCTION__ L"");
+
internal::LogInfo(__FUNCTION__, L"Internal Initialize ");
try
@@ -176,6 +178,8 @@ namespace winrt::Windows::Devices::Midi2::implementation
_Use_decl_annotations_
bool MidiEndpointConnection::Open()
{
+ OutputDebugString(__FUNCTION__ L"");
+
internal::LogInfo(__FUNCTION__, L"Connection Open ");
if (!IsOpen())
@@ -183,6 +187,8 @@ namespace winrt::Windows::Devices::Midi2::implementation
// Activate the endpoint for this device. Will fail if the device is not a BiDi device
if (!ActivateMidiStream(m_serviceAbstraction, __uuidof(IMidiBiDi), (void**)&m_endpointAbstraction))
{
+ OutputDebugString(__FUNCTION__ L" coult not activate MIDI stream");
+
internal::LogGeneralError(__FUNCTION__, L"Could not activate MIDI Stream");
return false;
@@ -239,6 +245,8 @@ namespace winrt::Windows::Devices::Midi2::implementation
void MidiEndpointConnection::Close()
{
+ OutputDebugString(__FUNCTION__ L"");
+
internal::LogInfo(__FUNCTION__, L"Connection Close");
if (m_closeHasBeenCalled) return;
@@ -270,6 +278,8 @@ namespace winrt::Windows::Devices::Midi2::implementation
MidiEndpointConnection::~MidiEndpointConnection()
{
+ OutputDebugString(__FUNCTION__ L"");
+
if (!m_closeHasBeenCalled)
{
Close();
@@ -281,6 +291,8 @@ namespace winrt::Windows::Devices::Midi2::implementation
void MidiEndpointConnection::InitializePlugins() noexcept
{
+ OutputDebugString(__FUNCTION__ L"");
+
internal::LogInfo(__FUNCTION__, L"Initializing message processing plugins");
for (const auto& plugin : m_messageProcessingPlugins)
@@ -300,6 +312,8 @@ namespace winrt::Windows::Devices::Midi2::implementation
void MidiEndpointConnection::CallOnConnectionOpenedOnPlugins() noexcept
{
+ OutputDebugString(__FUNCTION__ L"");
+
internal::LogInfo(__FUNCTION__, L"Notifying message processing plugins that the connection is opened");
for (const auto& plugin : m_messageProcessingPlugins)
diff --git a/src/api/Client/Midi2Client/MidiFunctionBlock.h b/src/api/Client/Midi2Client/MidiFunctionBlock.h
index 9efd8459e..85f443c58 100644
--- a/src/api/Client/Midi2Client/MidiFunctionBlock.h
+++ b/src/api/Client/Midi2Client/MidiFunctionBlock.h
@@ -62,7 +62,7 @@ namespace winrt::Windows::Devices::Midi2::implementation
void InternalSetisReadOnly(_In_ bool isReadOnly) { m_isReadOnly = isReadOnly; }
private:
- bool m_isReadOnly{ true };
+ bool m_isReadOnly{ false };
uint8_t m_number{ 0 };
winrt::hstring m_name{};
bool m_isActive{ false };
diff --git a/src/api/Client/Midi2Client/MidiSession.cpp b/src/api/Client/Midi2Client/MidiSession.cpp
index 049ffac97..c74bb9e33 100644
--- a/src/api/Client/Midi2Client/MidiSession.cpp
+++ b/src/api/Client/Midi2Client/MidiSession.cpp
@@ -150,7 +150,7 @@ namespace winrt::Windows::Devices::Midi2::implementation
}
else
{
- internal::LogGeneralError(__FUNCTION__, L" WinRT Endpoint connection wouldn't start");
+ internal::LogGeneralError(__FUNCTION__, L"WinRT Endpoint connection wouldn't start");
// TODO: Cleanup
@@ -159,7 +159,7 @@ namespace winrt::Windows::Devices::Midi2::implementation
}
catch (winrt::hresult_error const& ex)
{
- internal::LogHresultError(__FUNCTION__, L" hresult exception connecting to endpoint. Service or endpoint may be unavailable, or endpoint may not be the correct type.", ex);
+ internal::LogHresultError(__FUNCTION__, L"hresult exception connecting to endpoint. Service or endpoint may be unavailable, or endpoint may not be the correct type.", ex);
return nullptr;
}
@@ -185,13 +185,44 @@ namespace winrt::Windows::Devices::Midi2::implementation
_Use_decl_annotations_
midi2::MidiEndpointConnection MidiSession::CreateVirtualDeviceAndConnection(
- winrt::hstring /*endpointName*/,
- winrt::hstring /*endpointDeviceInstanceId*/
+ midi2::MidiVirtualEndpointDeviceDefinition const& deviceDefinition
) noexcept
{
- // TODO
+ OutputDebugString(__FUNCTION__ L"");
- return nullptr;
+ // TEMP ============================================================================================
+ // Until we can spin up a new virtual device from the API, we have to use a pre-configured
+ // endpoint.
+
+ winrt::hstring endpointDeviceId =
+ L"\\\\?\\SWD#MIDISRV#MIDIU_VIRTDEV_" +
+ deviceDefinition.EndpointProductInstanceId() +
+ L"#" + midi2::MidiEndpointDeviceInformation::EndpointInterfaceClass();
+
+ OutputDebugString(endpointDeviceId.c_str());
+
+ // End TEMP ========================================================================================
+
+
+ // create the connection
+ auto connection = CreateEndpointConnection(endpointDeviceId, nullptr, nullptr);
+
+ if (connection)
+ {
+ auto virtualDevice = winrt::make_self();
+
+ virtualDevice->InternalSetDeviceDefinition(deviceDefinition);
+ virtualDevice->IsEnabled(true);
+
+ // add the listener
+ connection.MessageProcessingPlugins().Append(*virtualDevice);
+ }
+ else
+ {
+ internal::LogGeneralError(__FUNCTION__, L"Could not create connection");
+ }
+
+ return connection;
}
diff --git a/src/api/Client/Midi2Client/MidiSession.h b/src/api/Client/Midi2Client/MidiSession.h
index 8ee6aac92..fe614261d 100644
--- a/src/api/Client/Midi2Client/MidiSession.h
+++ b/src/api/Client/Midi2Client/MidiSession.h
@@ -63,8 +63,7 @@ namespace winrt::Windows::Devices::Midi2::implementation
midi2::MidiEndpointConnection CreateVirtualDeviceAndConnection(
- _In_ winrt::hstring endpointName,
- _In_ winrt::hstring endpointDeviceInstanceId
+ _In_ midi2::MidiVirtualEndpointDeviceDefinition const& deviceDefinition
) noexcept;
diff --git a/src/api/Client/Midi2Client/MidiSession.idl b/src/api/Client/Midi2Client/MidiSession.idl
index b1708fdf1..eb130d36a 100644
--- a/src/api/Client/Midi2Client/MidiSession.idl
+++ b/src/api/Client/Midi2Client/MidiSession.idl
@@ -10,14 +10,11 @@
MIDI_IDL_IMPORT
import "MidiEndpointConnection.idl";
-
import "IMidiEndpointDefinedConnectionSettings.idl";
-
import "MidiSessionSettings.idl";
-
import "MidiEndpointConnectionOptions.idl";
+import "MidiVirtualEndpointDeviceDefinition.idl";
-//import "MidiVirtualDeviceManager.idl";
namespace Windows.Devices.Midi2
{
@@ -59,9 +56,7 @@ namespace Windows.Devices.Midi2
// This creates the virtual device, and also adds the appropriate listener
// to the returned endpoint connection
MidiEndpointConnection CreateVirtualDeviceAndConnection(
- String endpointName,
- String endpointDeviceInstanceId
- // TODO: Other options for creating the device. Maybe wrap this all up in an object.
+ MidiVirtualEndpointDeviceDefinition deviceDefinition
);
// This will close and remove a single endpoint connection instance.
diff --git a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp
index d58d10704..8cacd545e 100644
--- a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp
+++ b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp
@@ -36,11 +36,15 @@ namespace winrt::Windows::Devices::Midi2::implementation
_Use_decl_annotations_
void MidiVirtualEndpointDevice::Initialize(midi2::IMidiEndpointConnectionSource const& endpointConnection) noexcept
{
+ OutputDebugString(__FUNCTION__ L"");
+
m_endpointConnection = endpointConnection.as();
}
void MidiVirtualEndpointDevice::OnEndpointConnectionOpened() noexcept
{
+ OutputDebugString(__FUNCTION__ L"");
+
//throw hresult_not_implemented();
}
@@ -49,12 +53,232 @@ namespace winrt::Windows::Devices::Midi2::implementation
//throw hresult_not_implemented();
}
+ _Use_decl_annotations_
+ void MidiVirtualEndpointDevice::SendFunctionBlockInfoNotificationMessage(midi2::MidiFunctionBlock const& fb) noexcept
+ {
+ OutputDebugString(__FUNCTION__ L"");
+
+ auto functionBlockNotification = midi2::MidiStreamMessageBuilder::BuildFunctionBlockInfoNotificationMessage(
+ 0,
+ true,
+ fb.Number(),
+ fb.UIHint(),
+ fb.Midi10Connection(),
+ fb.Direction(),
+ fb.FirstGroupIndex(),
+ fb.GroupCount(),
+ fb.MidiCIMessageVersionFormat(),
+ fb.MaxSystemExclusive8Streams()
+ );
+
+ // TODO: Log if failed
+ m_endpointConnection.SendMessagePacket(functionBlockNotification);
+ }
+
+ _Use_decl_annotations_
+ void MidiVirtualEndpointDevice::SendFunctionBlockNameNotificationMessages(midi2::MidiFunctionBlock const& fb) noexcept
+ {
+ OutputDebugString(__FUNCTION__ L"");
+
+ if (fb.Name() == L"") return;
+
+ auto nameMessages = midi2::MidiStreamMessageBuilder::BuildFunctionBlockNameNotificationMessages(
+ 0,
+ fb.Number(),
+ fb.Name()
+ );
+
+ for (uint32_t i = 0; i < nameMessages.Size(); i++)
+ {
+ // TODO: Log if failed
+ m_endpointConnection.SendMessagePacket(nameMessages.GetAt(i));
+ }
+
+ }
+
_Use_decl_annotations_
void MidiVirtualEndpointDevice::ProcessIncomingMessage(
- midi2::MidiMessageReceivedEventArgs const& /*args*/,
- bool& /*skipFurtherListeners*/,
- bool& /*skipMainMessageReceivedEvent*/) noexcept
+ midi2::MidiMessageReceivedEventArgs const& args,
+ bool& skipFurtherListeners,
+ bool& skipMainMessageReceivedEvent) noexcept
{
- //throw hresult_not_implemented();
+ OutputDebugString(__FUNCTION__ L"");
+
+ bool handled = false;
+
+ if (args.MessageType() == MidiMessageType::Stream128)
+ {
+ MidiMessage128 message{};
+ if (args.FillMessage128(message))
+ {
+
+ // if a endpoint discovery request, handle it with the data we have
+ if (internal::MessageIsEndpointDiscoveryRequest(message.Word0()))
+ {
+ uint8_t filterFlags = internal::GetEndpointDiscoveryMessageFilterFlagsFromSecondWord(message.Word1());
+
+ if (internal::EndpointDiscoveryFilterRequestsEndpointInfoNotification(filterFlags))
+ {
+ // send endpoint info notification
+
+ auto notification = midi2::MidiStreamMessageBuilder::BuildEndpointInformationNotificationMessage(
+ 0,
+ MIDI_PREFERRED_UMP_VERSION_MAJOR,
+ MIDI_PREFERRED_UMP_VERSION_MINOR,
+ m_areFunctionBlocksStatic,
+ (uint8_t)m_functionBlocks.Size(),
+ true, // TODO: Pull from properties supports midi 2.0
+ true, // TODO: pull from properties supports midi 1.0
+ false, // todo: pull from default JR timestamp handling
+ false // todo: pull from jr timestamp handling
+ );
+
+ m_endpointConnection.SendMessagePacket(notification);
+ }
+
+ if (internal::EndpointDiscoveryFilterRequestsDeviceIdentityNotification(filterFlags))
+ {
+ // send device identity notification
+ }
+
+ if (internal::EndpointDiscoveryFilterRequestsEndpointNameNotification(filterFlags))
+ {
+ // send endpoint name notification messages
+ }
+
+ if (internal::EndpointDiscoveryFilterRequestsProductInstanceIdNotification(filterFlags))
+ {
+ // send product instance id notification messages
+
+ }
+
+ if (internal::EndpointDiscoveryFilterRequestsStreamConfigurationNotification(filterFlags))
+ {
+ // send stream configuration message
+ }
+ }
+ else if (internal::MessageIsFunctionBlockDiscoveryRequest(message.Word0()))
+ {
+ uint8_t filterFlags = internal::GetFunctionBlockDiscoveryMessageFilterFlagsFromFirstWord(message.Word0());
+
+ bool requestInfo = internal::FunctionBlockDiscoveryFilterRequestsInfoNotification(filterFlags);
+ bool requestName = internal::FunctionBlockDiscoveryFilterRequestsNameNotification(filterFlags);
+
+ uint8_t fbNumber = internal::GetFunctionBlockNumberFromFunctionBlockDiscoveryRequestFirstWord(message.Word0());
+
+ if (fbNumber == MIDI_STREAM_MESSAGE_FUNCTION_BLOCK_REQUEST_ALL_FUNCTION_BLOCKS)
+ {
+ // send all function blocks
+
+ for (uint8_t i = 0; i < (uint8_t)m_functionBlocks.Size(); i++)
+ {
+ if (requestInfo) SendFunctionBlockInfoNotificationMessage(m_functionBlocks.Lookup(i));
+ if (requestName) SendFunctionBlockNameNotificationMessages(m_functionBlocks.Lookup(i));
+ }
+ }
+ else
+ {
+ // send single function block
+
+ if (m_functionBlocks.HasKey(fbNumber))
+ {
+ auto fb = m_functionBlocks.Lookup(fbNumber);
+
+ if (requestInfo) SendFunctionBlockInfoNotificationMessage(fb);
+ if (requestName) SendFunctionBlockNameNotificationMessages(fb);
+ }
+ else
+ {
+ // invalid fb number request
+ handled = false;
+ }
+ }
+
+ }
+ else if (internal::MessageIsEndpointDiscoveryRequest(message.Word0()))
+ {
+ }
+ else
+ {
+ // something else
+ }
+
+
+
+ }
+ else
+ {
+ // something went wrong filling this message type
+ }
+
+
+ }
+ else
+ {
+ // not a stream message. Ignore
+
+
+ }
+
+
+ if (handled && SuppressHandledMessages())
+ {
+ skipFurtherListeners = true;
+ skipMainMessageReceivedEvent = true;
+ }
+ else
+ {
+ skipFurtherListeners = false;
+ skipMainMessageReceivedEvent = false;
+ }
+
+ }
+
+
+
+
+ _Use_decl_annotations_
+ void MidiVirtualEndpointDevice::InternalSetDeviceDefinition(
+ _In_ midi2::MidiVirtualEndpointDeviceDefinition definition)
+ {
+ try
+ {
+ OutputDebugString(__FUNCTION__ L"");
+
+ // populate all the views, properties, blocks, etc.
+
+ m_areFunctionBlocksStatic = definition.AreFunctionBlocksStatic();
+
+ for (uint8_t i = 0; i < definition.FunctionBlocks().Size(); i++)
+ {
+ auto fb = definition.FunctionBlocks().GetAt(i);
+
+ // this is required, so we enforce it here
+ fb.Number(i);
+
+ // TODO: Set the fb as read-only
+
+
+
+ // add the block
+ m_functionBlocks.Insert(i, fb);
+ }
+
+ m_endpointName = definition.EndpointName();
+ m_endpointProductInstanceId = definition.EndpointProductInstanceId();
+
+
+ // TODO: All the other stuff we'll want to report on
+
+
+
+
+ }
+ catch (...)
+ {
+ // todo log
+ }
+
}
+
}
diff --git a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h
index 28e92132c..8a89a865f 100644
--- a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h
+++ b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h
@@ -64,6 +64,8 @@ namespace winrt::Windows::Devices::Midi2::implementation
void OnEndpointConnectionOpened() noexcept;
void Cleanup() noexcept;
+ void InternalSetDeviceDefinition(_In_ midi2::MidiVirtualEndpointDeviceDefinition definition);
+
void ProcessIncomingMessage(
_In_ midi2::MidiMessageReceivedEventArgs const& args,
_Out_ bool& skipFurtherListeners,
@@ -71,6 +73,10 @@ namespace winrt::Windows::Devices::Midi2::implementation
private:
+ void SendFunctionBlockInfoNotificationMessage(_In_ midi2::MidiFunctionBlock const& fb) noexcept;
+ void SendFunctionBlockNameNotificationMessages(_In_ midi2::MidiFunctionBlock const& fb) noexcept;
+
+
//midi2::MidiVirtualEndpointDeviceDefinition m_virtualEndpointDeviceDefinition
winrt::hstring m_id{}; // plugin id
diff --git a/src/api/Client/Midi2Client/WinRTActivationEntries.txt b/src/api/Client/Midi2Client/WinRTActivationEntries.txt
index c21c6d787..5654d2476 100644
--- a/src/api/Client/Midi2Client/WinRTActivationEntries.txt
+++ b/src/api/Client/Midi2Client/WinRTActivationEntries.txt
@@ -55,7 +55,8 @@ Windows.Devices.Midi2.MidiMessageTypeEndpointListener | 0 | 0 | 0
# Virtual Device Message Processing Plugins
-Windows.Devices.Midi2.MidiVirtualDevice | 0 | 0 | 0
+Windows.Devices.Midi2.MidiVirtualEndpointDevice | 0 | 0 | 0
+Windows.Devices.Midi2.MidiVirtualEndpointDeviceDefinition | 0 | 0 | 0
# Events
@@ -71,6 +72,7 @@ Windows.Devices.Midi2.IMidiEndpointConnectionStatics | 0 | 0 | 0
Windows.Devices.Midi2.MidiEndpointConnectionOptions | 0 | 0 | 0
Windows.Devices.Midi2.MidiStreamConfigurationSettings | 0 | 0 | 0
+
# Service
Windows.Devices.Midi2.MidiService | 0 | 0 | 0
diff --git a/src/api/Client/Midi2Client/pch.h b/src/api/Client/Midi2Client/pch.h
index a5b4a28b2..2e86c7ad5 100644
--- a/src/api/Client/Midi2Client/pch.h
+++ b/src/api/Client/Midi2Client/pch.h
@@ -93,6 +93,9 @@ namespace midi2 = ::winrt::Windows::Devices::Midi2;
#include "MidiMessageReceivedEventArgs.h"
#include "MidiEndpointDeviceInformationUpdateEventArgs.h"
+#include "MidiVirtualEndpointDevice.h"
+#include "MidiVirtualEndpointDeviceDefinition.h"
+
#include "MidiSession.h"
#include "MidiServicePingResponse.h"
diff --git a/src/api/Inc/MidiDefs.h b/src/api/Inc/MidiDefs.h
index 902d4a3be..03377c8cb 100644
--- a/src/api/Inc/MidiDefs.h
+++ b/src/api/Inc/MidiDefs.h
@@ -22,6 +22,9 @@
#define MIDI_OUTGOING_MESSAGE_QUEUE_MAX_MESSAGE_COUNT 10000
+#define MIDI_PROTOCOL_MANAGER_ENDPOINT_CREATION_CONTEXT (LONGLONG)3263827
+
+
//
// Registry keys for global configuration. The settings app can write to some of these, so including in MidiDefs
//
@@ -117,7 +120,7 @@ DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_SupportsMulticlient, 5); // DEVPROP_TYPE_BOO
// For a MIDI 2 device, it will support MIDI_NATIVEDATAFORMAT_UMP
// NOTE: These are actually in the MidiDataFormat enum, slightly different than the defines mentioned above.
#define STRING_PKEY_MIDI_SupportedDataFormats MIDI_STRING_PKEY_GUID MIDI_STRING_PKEY_PID_SEPARATOR L"6"
-DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_SupportedDataFormats, 6); // DEVPROP_TYPE_BYTE uint8_t
+DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_SupportedDataFormats, 6); // DEVPROP_TYPE_UINT32
#define STRING_PKEY_MIDI_ManufacturerName MIDI_STRING_PKEY_GUID MIDI_STRING_PKEY_PID_SEPARATOR L"7"
DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_ManufacturerName, 7); // DEVPROP_TYPE_STRING
@@ -143,7 +146,7 @@ DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_IN_GroupTerminalBlocks, 50); // DEVPROP_TYPE
DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_OUT_GroupTerminalBlocks, 51); // DEVPROP_TYPE_BINARY
#define STRING_PKEY_MIDI_AssociatedUMP MIDI_STRING_PKEY_GUID MIDI_STRING_PKEY_PID_SEPARATOR L"52"
-DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_AssociatedUMP, 52); // DEVPROP_TYPE_UINT64
+DEFINE_MIDIDEVPROPKEY(PKEY_MIDI_AssociatedUMP, 52); // DEVPROP_TYPE_STRING
// iSerialNumber for USB
#define STRING_PKEY_MIDI_SerialNumber MIDI_STRING_PKEY_GUID MIDI_STRING_PKEY_PID_SEPARATOR L"53"
diff --git a/src/api/Inc/string_util.h b/src/api/Inc/string_util.h
index 1a33d0ad0..65211c211 100644
--- a/src/api/Inc/string_util.h
+++ b/src/api/Inc/string_util.h
@@ -15,6 +15,7 @@
namespace Windows::Devices::Midi2::Internal
{
+
inline void InPlaceToUpper(_Inout_ std::wstring &s)
{
std::transform(s.begin(), s.end(), s.begin(), towupper);
@@ -56,12 +57,24 @@ namespace Windows::Devices::Midi2::Internal
return ws;
}
+ inline std::wstring ToLowerWStringCopy(_In_ std::wstring s)
+ {
+ std::wstring ws{ s };
+ InPlaceToLower(ws);
+
+ return ws;
+ }
+
inline std::wstring ToUpperTrimmedWStringCopy(_In_ std::wstring s)
{
return ToUpperWStringCopy(TrimmedWStringCopy(s));
}
+ inline std::wstring ToLowerTrimmedWStringCopy(_In_ std::wstring s)
+ {
+ return ToLowerWStringCopy(TrimmedWStringCopy(s));
+ }
// TODO: this could use substr and take one op instad of two
inline winrt::hstring TrimmedHStringCopy(_In_ std::wstring ws)
@@ -98,4 +111,40 @@ namespace Windows::Devices::Midi2::Internal
}
+
+
+ // This is just to convert all GUIDs to the same case. It does
+ // not add or remove opening / closing brackets
+ inline std::wstring NormalizeGuidStringCopy(_In_ std::wstring guidString)
+ {
+ return ToUpperTrimmedWStringCopy(guidString);
+ }
+
+ // This is for the device instance id. Not to be confused with the interface id
+ inline std::wstring NormalizeDeviceInstanceIdCopy(_In_ std::wstring deviceInstanceId)
+ {
+ return ToUpperTrimmedWStringCopy(deviceInstanceId);
+ }
+
+ // This is for the endpoint device interface id (the long SWD id with the GUID)
+ inline std::wstring NormalizeEndpointInterfaceIdCopy(_In_ std::wstring endpointInterfaceId)
+ {
+ return ToLowerTrimmedWStringCopy(endpointInterfaceId);
+ }
+
+ // used for searching for a substring in an endpoint interface id. Matches case with
+ // what NormalizeEndpointInterfaceIdCopy produces
+ inline bool EndpointInterfaceIdContainsString(_In_ std::wstring endpointInterfaceId, _In_ std::wstring searchFor)
+ {
+ auto id = NormalizeEndpointInterfaceIdCopy(endpointInterfaceId);
+ auto sub = ToLowerWStringCopy(searchFor); // match case with NormalizeEndpointInterfaceIdCopy
+
+ if (id == L"" || sub == L"")
+ {
+ return false;
+ }
+
+ return id.find(sub) != std::wstring::npos;
+ }
+
}
diff --git a/src/api/Inc/ump_helpers.h b/src/api/Inc/ump_helpers.h
index af6d3b26e..422e6ec22 100644
--- a/src/api/Inc/ump_helpers.h
+++ b/src/api/Inc/ump_helpers.h
@@ -350,6 +350,7 @@ namespace Windows::Devices::Midi2::Internal
}
+ // endpoint discovery and function blocks
inline bool GetFunctionBlockActiveFlagFromInfoNotificationFirstWord(
@@ -391,6 +392,19 @@ namespace Windows::Devices::Midi2::Internal
// Function Block Info Notification
+ inline bool MessageIsFunctionBlockDiscoveryRequest(_In_ uint32_t const firstWord)
+ {
+ if (GetUmpMessageTypeFromFirstWord(firstWord) == 0xF)
+ {
+ if (GetFormFromStreamMessageFirstWord(firstWord) == 0x0 &&
+ GetStatusFromStreamMessageFirstWord(firstWord) == 0x10)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
inline uint32_t BuildFunctionBlockDiscoveryRequestFirstWord(
_In_ uint8_t functionBlockNumber,
@@ -408,6 +422,31 @@ namespace Windows::Devices::Midi2::Internal
return word;
}
+ inline std::uint8_t GetFunctionBlockNumberFromFunctionBlockDiscoveryRequestFirstWord(
+ _In_ std::uint32_t const word0
+ ) noexcept
+ {
+ return (std::uint8_t)(MIDIWORDBYTE3(word0));
+ }
+
+
+ inline std::uint8_t GetFunctionBlockDiscoveryMessageFilterFlagsFromFirstWord(
+ _In_ std::uint32_t const word0
+ ) noexcept
+ {
+ return (std::uint8_t)(MIDIWORDBYTE4(word0));
+ }
+
+ inline bool FunctionBlockDiscoveryFilterRequestsInfoNotification(_In_ std::uint8_t const filterBitmap)
+ {
+ return ((filterBitmap & 0x01) > 0);
+ }
+
+ inline bool FunctionBlockDiscoveryFilterRequestsNameNotification(_In_ std::uint8_t const filterBitmap)
+ {
+ return ((filterBitmap & 0x02) > 0);
+ }
+
inline uint8_t GetFunctionBlockFirstGroupFromInfoNotificationSecondWord(
_In_ uint32_t const word1
@@ -438,7 +477,49 @@ namespace Windows::Devices::Midi2::Internal
}
- // Endpoint Info Notification
+ // Endpoint Info Notification and related
+
+ inline bool MessageIsEndpointDiscoveryRequest(_In_ std::uint32_t const firstWord)
+ {
+ if (GetUmpMessageTypeFromFirstWord(firstWord) == 0xF)
+ {
+ if (GetFormFromStreamMessageFirstWord(firstWord) == 0 &&
+ GetStatusFromStreamMessageFirstWord(firstWord) == 0)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ inline std::uint8_t GetEndpointDiscoveryMessageFilterFlagsFromSecondWord(_In_ std::uint32_t const secondWord)
+ {
+ return (std::uint8_t)MIDIWORDBYTE4(secondWord);
+ }
+
+ inline bool EndpointDiscoveryFilterRequestsEndpointInfoNotification(_In_ std::uint8_t const filterBitmap)
+ {
+ return ((filterBitmap & 0x01) > 0);
+ }
+
+ inline bool EndpointDiscoveryFilterRequestsDeviceIdentityNotification(_In_ std::uint8_t const filterBitmap)
+ {
+ return ((filterBitmap & 0x02) > 0);
+ }
+
+ inline bool EndpointDiscoveryFilterRequestsEndpointNameNotification(_In_ std::uint8_t const filterBitmap)
+ {
+ return ((filterBitmap & 0x04) > 0);
+ }
+ inline bool EndpointDiscoveryFilterRequestsProductInstanceIdNotification(_In_ std::uint8_t const filterBitmap)
+ {
+ return ((filterBitmap & 0x08) > 0);
+ }
+ inline bool EndpointDiscoveryFilterRequestsStreamConfigurationNotification(_In_ std::uint8_t const filterBitmap)
+ {
+ return ((filterBitmap & 0x10) > 0);
+ }
inline uint32_t BuildEndpointDiscoveryRequestFirstWord(
_In_ uint8_t umpVersionMajor,
@@ -515,12 +596,29 @@ namespace Windows::Devices::Midi2::Internal
return (bool)((word1 & 0x00000001) > 0);
}
+
+
+
+
// Stream Configuration Request and Notification Messages
+ inline bool MessageIsStreamConfigurationRequest(_In_ std::uint32_t const firstWord)
+ {
+ if (GetUmpMessageTypeFromFirstWord(firstWord) == 0xF)
+ {
+ if (GetFormFromStreamMessageFirstWord(firstWord) == 0 &&
+ GetStatusFromStreamMessageFirstWord(firstWord) == 5)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
- inline uint32_t BuildStreamConfigurationRequestFirstWord(
- _In_ uint8_t protocol,
- _In_ bool endpointShouldExpectToReceiveJR,
- _In_ bool endpointShouldSendJR
+ inline std::uint32_t BuildStreamConfigurationRequestFirstWord(
+ _In_ std::uint8_t const protocol,
+ _In_ bool const endpointShouldExpectToReceiveJR,
+ _In_ bool const endpointShouldSendJR
)
{
uint32_t word{ 0 };
diff --git a/src/api/Service/Exe/MidiClientManager.cpp b/src/api/Service/Exe/MidiClientManager.cpp
index 40f1fa9c5..da9506b6a 100644
--- a/src/api/Service/Exe/MidiClientManager.cpp
+++ b/src/api/Service/Exe/MidiClientManager.cpp
@@ -110,10 +110,16 @@ GetDeviceSupportedDataFormat(_In_ std::wstring MidiDevice, _Inout_ MidiDataForma
auto prop = deviceInfo.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_SupportedDataFormats));
if (prop)
{
+
OutputDebugString(__FUNCTION__ L" found property");
- // this interface is pointing to a UMP interface, so use that instance id.
- DataFormat = (MidiDataFormat)winrt::unbox_value(prop);
+ DataFormat = MidiDataFormat::MidiDataFormat_Any;
+ try
+ {
+ // this interface is pointing to a UMP interface, so use that instance id.
+ DataFormat = (MidiDataFormat)winrt::unbox_value(prop);
+ }
+ CATCH_LOG();
}
else
{
@@ -170,7 +176,11 @@ GetEndpointAlias(_In_ LPCWSTR MidiDevice, _In_ std::wstring& Alias, _In_ MidiFlo
additionalProperties.Append(winrt::to_hstring(STRING_PKEY_MIDI_AssociatedUMP));
auto deviceInfo = DeviceInformation::CreateFromIdAsync(MidiDevice, additionalProperties, winrt::Windows::Devices::Enumeration::DeviceInformationKind::DeviceInterface).get();
+ OutputDebugString(__FUNCTION__ L" looking up prop");
auto prop = deviceInfo.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_AssociatedUMP));
+
+ OutputDebugString(__FUNCTION__ L" got prop. About to check for null");
+
if (prop)
{
OutputDebugString(L"" __FUNCTION__ " STRING_PKEY_MIDI_AssociatedUMP property present");
diff --git a/src/api/Service/Exe/MidiDeviceManager.cpp b/src/api/Service/Exe/MidiDeviceManager.cpp
index 823ea6df3..ff1ed2268 100644
--- a/src/api/Service/Exe/MidiDeviceManager.cpp
+++ b/src/api/Service/Exe/MidiDeviceManager.cpp
@@ -46,7 +46,10 @@ CMidiDeviceManager::Initialize(
if (endpointManager != nullptr)
{
- auto initializeResult = endpointManager->Initialize((IUnknown*)this, (IUnknown*)EndpointProtocolManager.get(), transportSettingsJson.c_str());
+ // need to do this to avoid an ambiguous IUnknown cast error
+ wil::com_ptr_nothrow protocolManager = EndpointProtocolManager.get();
+
+ auto initializeResult = endpointManager->Initialize((IUnknown*)this, (IUnknown*)protocolManager.get(), transportSettingsJson.c_str());
LOG_IF_FAILED(initializeResult);
@@ -315,7 +318,7 @@ CMidiDeviceManager::ActivateEndpoint
auto lock = m_MidiPortsLock.lock();
- OutputDebugString(__FUNCTION__ L": Checking for an existing port\n");
+ OutputDebugString(__FUNCTION__ L": Checking for an existing port. \n");
const bool alreadyActivated = std::find_if(m_MidiPorts.begin(), m_MidiPorts.end(), [&](const std::unique_ptr& Port)
{
@@ -435,7 +438,7 @@ CMidiDeviceManager::ActivateEndpointInternal
DEVPROP_TYPE_BOOLEAN, static_cast(sizeof(devPropTrue)), &devPropTrue });
- midiPort->InstanceId = CreateInfo->pszInstanceId;
+ midiPort->InstanceId = internal::NormalizeDeviceInstanceIdCopy(CreateInfo->pszInstanceId);
midiPort->MidiFlow = MidiFlow;
midiPort->Enumerator = MidiOne?AUDIO_DEVICE_ENUMERATOR : MIDI_DEVICE_ENUMERATOR;
@@ -538,7 +541,7 @@ CMidiDeviceManager::ActivateEndpointInternal
if (DeviceInterfaceId)
{
- *DeviceInterfaceId = midiPort->DeviceInterfaceId.get();
+ *DeviceInterfaceId = internal::NormalizeEndpointInterfaceIdCopy(midiPort->DeviceInterfaceId.get()).c_str();
}
// success, transfer the midiPort to the list
@@ -561,8 +564,7 @@ CMidiDeviceManager::UpdateEndpointProperties
{
OutputDebugString(L"\n" __FUNCTION__ " ");
- std::wstring requestedInterfaceId(DeviceInterfaceId);
- ::Windows::Devices::Midi2::Internal::InPlaceToLower(requestedInterfaceId);
+ auto requestedInterfaceId = internal::NormalizeEndpointInterfaceIdCopy(DeviceInterfaceId);
//OutputDebugString(requestedInterfaceId.c_str());
//OutputDebugString(L"\n");
@@ -570,13 +572,10 @@ CMidiDeviceManager::UpdateEndpointProperties
// locate the MIDIPORT
auto item = std::find_if(m_MidiPorts.begin(), m_MidiPorts.end(), [&](const std::unique_ptr& Port)
{
- std::wstring portInterfaceId(Port->DeviceInterfaceId.get());
-
- ::Windows::Devices::Midi2::Internal::InPlaceToLower(portInterfaceId);
+ auto portInterfaceId = internal::NormalizeEndpointInterfaceIdCopy(Port->DeviceInterfaceId.get());
// OutputDebugString((L" -- Checking " + portInterfaceId).c_str());
-
return (portInterfaceId == requestedInterfaceId);
});
@@ -698,15 +697,24 @@ CMidiDeviceManager::DeactivateEndpoint
PCWSTR InstanceId
)
{
+ OutputDebugString(__FUNCTION__ L" - enter. Cleaned instance id is: ");
+
+ auto cleanId = internal::NormalizeDeviceInstanceIdCopy(InstanceId);
+
+ OutputDebugString(cleanId.c_str());
+
// there may be more than one SWD associated with this instance id, as we reuse
// the instance id for the legacy SWD, it just has a different activator and InterfaceClass.
do
{
// locate the MIDIPORT that identifies the swd
+ // NOTE: This uses InstanceId, not the Device Interface Id
auto item = std::find_if(m_MidiPorts.begin(), m_MidiPorts.end(), [&](const std::unique_ptr& Port)
{
- // if this instance id already activated, then we cannot activate/create a second time,
- if (InstanceId == Port->InstanceId)
+ // OutputDebugString(__FUNCTION__ L" Checking against stored instance id: ");
+ // OutputDebugString(Port->InstanceId.c_str());
+
+ if (cleanId == Port->InstanceId)
{
return true;
}
@@ -714,19 +722,23 @@ CMidiDeviceManager::DeactivateEndpoint
return false;
});
- // if the item was found
+ // exit if the item was not found. We're done.
if (item == m_MidiPorts.end())
{
break;
}
else
{
+ OutputDebugString(__FUNCTION__ L" - Id found. Removing SWD\n");
+
// Erasing this item from the list will free the unique_ptr and also trigger a SwDeviceClose on the item->SwDevice,
// which will deactivate the device, done.
m_MidiPorts.erase(item);
}
} while (TRUE);
+ OutputDebugString(__FUNCTION__ L" - exit\n");
+
return S_OK;
}
@@ -751,6 +763,8 @@ CMidiDeviceManager::RemoveEndpoint
HRESULT
CMidiDeviceManager::Cleanup()
{
+ OutputDebugString(__FUNCTION__ L"\n");
+
m_MidiEndpointManagers.clear();
m_MidiPorts.clear();
diff --git a/src/api/Service/Exe/MidiEndpointProtocolManager.cpp b/src/api/Service/Exe/MidiEndpointProtocolManager.cpp
index 156b11b50..06b3b176d 100644
--- a/src/api/Service/Exe/MidiEndpointProtocolManager.cpp
+++ b/src/api/Service/Exe/MidiEndpointProtocolManager.cpp
@@ -18,11 +18,125 @@ namespace internal = ::Windows::Devices::Midi2::Internal;
// later to require the processor be added, and if an option is introduced
// later, to ensure that it sets the option to forward messages along.
+
+
+
+
+
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolManager::Initialize(
+ std::shared_ptr& ClientManager,
+ std::shared_ptr& DeviceManager
+)
+{
+ OutputDebugString(__FUNCTION__ L"");
+
+ m_clientManager = ClientManager;
+ m_deviceManager = DeviceManager;
+
+
+ m_allMessagesReceived.create();
+ m_queueWorkerThreadWakeup.create();
+
+ // connect to the service. Needing a reference to this abstraction def
+ // creates a circular reference to the MidiSrv Abstraction. Not sure of
+ // a good way around that other than fixing up the ClientManager to make
+ // local connections with local handles reasonable.
+ RETURN_IF_FAILED(CoCreateInstance(__uuidof(Midi2MidiSrvAbstraction), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&m_serviceAbstraction)));
+
+ try
+ {
+ // start background processing thread
+
+ std::thread workerThread(
+ &CMidiEndpointProtocolManager::ThreadWorker,
+ this);
+
+ m_queueWorkerThread = std::move(workerThread);
+
+ // start up the worker thread
+ m_queueWorkerThread.detach();
+
+ }
+ CATCH_RETURN();
+
+ return S_OK;
+}
+
+
+
+
+
+_Use_decl_annotations_
+HRESULT
+CMidiEndpointProtocolManager::NegotiateAndRequestMetadata(
+ LPCWSTR DeviceInterfaceId,
+ BOOL PreferToSendJRTimestampsToEndpoint,
+ BOOL PreferToReceiveJRTimestampsFromEndpoint,
+ BYTE PreferredMidiProtocol,
+ WORD TimeoutMS
+) noexcept
+{
+ OutputDebugString(__FUNCTION__ L"");
+
+ ProtocolManagerWork work;
+
+ // DEBUG
+ //TimeoutMS = 25000;
+
+
+
+ work.EndpointInstanceId = DeviceInterfaceId;
+ work.PreferToSendJRTimestampsToEndpoint = PreferToSendJRTimestampsToEndpoint;
+ work.PreferToReceiveJRTimestampsFromEndpoint = PreferToReceiveJRTimestampsFromEndpoint;
+ work.PreferredMidiProtocol = PreferredMidiProtocol;
+ work.TimeoutMS = TimeoutMS;
+
+ m_workQueue.push(std::move(work));
+
+
+ // todo: signal event that there's new work
+ m_queueWorkerThreadWakeup.SetEvent();
+
+
+ return S_OK;
+}
+
+
+HRESULT
+CMidiEndpointProtocolManager::Cleanup()
+{
+ OutputDebugString(__FUNCTION__ L"");
+
+ // TODO terminate any open threads and ensure they close up
+
+ m_clientManager.reset();
+ m_deviceManager.reset();
+
+ // clear the queue
+ while (m_workQueue.size() > 0) m_workQueue.pop();
+
+ m_shutdown = true;
+ m_queueWorkerThreadWakeup.SetEvent();
+
+ return S_OK;
+}
+
+
+
+
+
+
+
+
+
_Use_decl_annotations_
HRESULT
-CMidiEndpointProtocolNegotiationWorker::Callback(PVOID Data, UINT Size, LONGLONG Position, LONGLONG Context)
+CMidiEndpointProtocolManager::Callback(PVOID Data, UINT Size, LONGLONG Position, LONGLONG Context)
{
- OutputDebugString(L"\n" __FUNCTION__);
+ OutputDebugString(L"\n" __FUNCTION__ L" - enter");
UNREFERENCED_PARAMETER(Position);
UNREFERENCED_PARAMETER(Context);
@@ -37,55 +151,59 @@ CMidiEndpointProtocolNegotiationWorker::Callback(PVOID Data, UINT Size, LONGLONG
if (internal::GetUmpMessageTypeFromFirstWord(ump.word0) == MIDI_STREAM_MESSAGE_UMP_MESSAGE_TYPE)
{
-
- // if message is a device identity message, set flag
-
auto messageStatus = internal::GetStatusFromStreamMessageFirstWord(ump.word0);
switch (messageStatus)
{
case MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_INFO_NOTIFICATION:
- m_endpointInfoReceived = true;
- m_declaredFunctionBlockCount = internal::GetEndpointInfoNotificationNumberOfFunctionBlocksFromSecondWord(ump.word1);
+ OutputDebugString(__FUNCTION__ L" - MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_INFO_NOTIFICATION\n");
+ m_currentWorkItem.TaskEndpointInfoReceived = true;
+ m_currentWorkItem.DeclaredFunctionBlockCount = internal::GetEndpointInfoNotificationNumberOfFunctionBlocksFromSecondWord(ump.word1);
RequestAllFunctionBlocks();
break;
case MIDI_STREAM_MESSAGE_STATUS_DEVICE_IDENTITY_NOTIFICATION:
- m_deviceIdentityReceived = true;
+ OutputDebugString(__FUNCTION__ L" - MIDI_STREAM_MESSAGE_STATUS_DEVICE_IDENTITY_NOTIFICATION\n");
+ m_currentWorkItem.TaskDeviceIdentityReceived = true;
break;
case MIDI_STREAM_MESSAGE_STATUS_STREAM_CONFIGURATION_NOTIFICATION:
+ OutputDebugString(__FUNCTION__ L" - MIDI_STREAM_MESSAGE_STATUS_STREAM_CONFIGURATION_NOTIFICATION\n");
ProcessStreamConfigurationRequest(ump);
break;
case MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_INFO_NOTIFICATION:
- m_countFunctionBlocksReceived += 1;
+ OutputDebugString(__FUNCTION__ L" - MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_INFO_NOTIFICATION\n");
+ m_currentWorkItem.CountFunctionBlocksReceived += 1;
break;
case MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_NAME_NOTIFICATION:
+ OutputDebugString(__FUNCTION__ L" - MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_NAME_NOTIFICATION\n");
if (internal::GetFormFromStreamMessageFirstWord(ump.word0) == MIDI_STREAM_MESSAGE_MULTI_FORM_COMPLETE ||
internal::GetFormFromStreamMessageFirstWord(ump.word0) == MIDI_STREAM_MESSAGE_MULTI_FORM_END)
{
- m_countFunctionBlockNamesReceived += 1;
+ m_currentWorkItem.CountFunctionBlockNamesReceived += 1;
}
break;
case MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_PRODUCT_INSTANCE_ID_NOTIFICATION:
+ OutputDebugString(__FUNCTION__ L" - MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_PRODUCT_INSTANCE_ID_NOTIFICATION\n");
if (internal::GetFormFromStreamMessageFirstWord(ump.word0) == MIDI_STREAM_MESSAGE_MULTI_FORM_COMPLETE ||
internal::GetFormFromStreamMessageFirstWord(ump.word0) == MIDI_STREAM_MESSAGE_MULTI_FORM_END)
{
- m_endpointProductInstanceIdReceived = true;
+ m_currentWorkItem.TaskEndpointProductInstanceIdReceived = true;
}
break;
case MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION:
+ OutputDebugString(__FUNCTION__ L" - MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION\n");
if (internal::GetFormFromStreamMessageFirstWord(ump.word0) == MIDI_STREAM_MESSAGE_MULTI_FORM_COMPLETE ||
internal::GetFormFromStreamMessageFirstWord(ump.word0) == MIDI_STREAM_MESSAGE_MULTI_FORM_END)
{
- m_endpointNameReceived = true;
+ m_currentWorkItem.TaskEndpointNameReceived = true;
}
break;
@@ -93,37 +211,45 @@ CMidiEndpointProtocolNegotiationWorker::Callback(PVOID Data, UINT Size, LONGLONG
OutputDebugString(L" Message is unidentified stream message\n");
break;
}
-
-
}
else
{
// not a stream message. Ignore and move on
+ OutputDebugString(__FUNCTION__ L" - not a stream message\n");
+
}
}
else
{
// couldn't fill the UMP. Shouldn't happen since we pre-validate
+ OutputDebugString(__FUNCTION__ L" - unable to fill UMP128 with the data\n");
+
return E_FAIL;
}
}
else
{
// Either null (hopefully not) or not a UMP128 so can't be a stream message. Fall out quickly
+ OutputDebugString(__FUNCTION__ L" - Not a message we're interested in\n" );
+
}
// check flags. If we've received everything, signal
- if (m_endpointInfoReceived &&
- m_endpointNameReceived &&
- m_endpointProductInstanceIdReceived &&
- m_deviceIdentityReceived &&
- m_countFunctionBlockNamesReceived == m_declaredFunctionBlockCount &&
- m_countFunctionBlocksReceived == m_declaredFunctionBlockCount &&
- m_finalStreamNegotiationResponseReceived)
+ if (m_currentWorkItem.TaskEndpointInfoReceived &&
+ m_currentWorkItem.TaskEndpointNameReceived &&
+ m_currentWorkItem.TaskEndpointProductInstanceIdReceived &&
+ m_currentWorkItem.TaskDeviceIdentityReceived &&
+ m_currentWorkItem.CountFunctionBlockNamesReceived == m_currentWorkItem.DeclaredFunctionBlockCount &&
+ m_currentWorkItem.CountFunctionBlocksReceived == m_currentWorkItem.DeclaredFunctionBlockCount &&
+ m_currentWorkItem.TaskFinalStreamNegotiationResponseReceived)
{
+ OutputDebugString(__FUNCTION__ L" - All messages received. Setting event\n");
+
m_allMessagesReceived.SetEvent();
+
+ // done
}
@@ -131,11 +257,13 @@ CMidiEndpointProtocolNegotiationWorker::Callback(PVOID Data, UINT Size, LONGLONG
}
HRESULT
-CMidiEndpointProtocolNegotiationWorker::RequestAllFunctionBlocks()
+CMidiEndpointProtocolManager::RequestAllFunctionBlocks()
{
+ OutputDebugString(__FUNCTION__ L"");
+
internal::PackedUmp128 ump{};
- if (m_endpoint)
+ if (m_currentWorkItem.Endpoint)
{
// first word
ump.word0 = internal::BuildFunctionBlockDiscoveryRequestFirstWord(
@@ -143,7 +271,7 @@ CMidiEndpointProtocolNegotiationWorker::RequestAllFunctionBlocks()
MIDI_STREAM_MESSAGE_FUNCTION_BLOCK_REQUEST_MESSAGE_ALL_FILTER_FLAGS);
// send it immediately
- return m_endpoint->SendMidiMessage((byte*)&ump, (UINT)sizeof(ump), 0);
+ return m_currentWorkItem.Endpoint->SendMidiMessage((byte*)&ump, (UINT)sizeof(ump), 0);
}
return E_FAIL;
@@ -151,12 +279,16 @@ CMidiEndpointProtocolNegotiationWorker::RequestAllFunctionBlocks()
HRESULT
-CMidiEndpointProtocolNegotiationWorker::RequestAllEndpointDiscoveryInformation()
+CMidiEndpointProtocolManager::RequestAllEndpointDiscoveryInformation()
{
+ OutputDebugString(__FUNCTION__ L"");
+
internal::PackedUmp128 ump{};
- if (m_endpoint)
+ if (m_currentWorkItem.Endpoint)
{
+ OutputDebugString(__FUNCTION__ L" - sending discovery");
+
// first word
ump.word0 = internal::BuildEndpointDiscoveryRequestFirstWord(MIDI_PREFERRED_UMP_VERSION_MAJOR, MIDI_PREFERRED_UMP_VERSION_MINOR);
@@ -165,7 +297,11 @@ CMidiEndpointProtocolNegotiationWorker::RequestAllEndpointDiscoveryInformation()
internal::SetMidiWordMostSignificantByte4(ump.word1, filterBitmap);
// send it immediately
- return m_endpoint->SendMidiMessage((byte*)&ump, (UINT)sizeof(ump), 0);
+ return m_currentWorkItem.Endpoint->SendMidiMessage((byte*)&ump, (UINT)sizeof(ump), 0);
+ }
+ else
+ {
+ OutputDebugString(__FUNCTION__ L" - endpoint null");
}
return E_FAIL;
@@ -173,45 +309,47 @@ CMidiEndpointProtocolNegotiationWorker::RequestAllEndpointDiscoveryInformation()
_Use_decl_annotations_
HRESULT
-CMidiEndpointProtocolNegotiationWorker::ProcessStreamConfigurationRequest(internal::PackedUmp128 ump)
+CMidiEndpointProtocolManager::ProcessStreamConfigurationRequest(internal::PackedUmp128 ump)
{
+ OutputDebugString(__FUNCTION__ L"");
+
// see if all is what we want. If not, we'll send a request to change configuration.
- if (m_endpoint)
+ if (m_currentWorkItem.Endpoint)
{
auto protocol = internal::GetStreamConfigurationNotificationProtocolFromFirstWord(ump.word0);
auto endpointRxJR = internal::GetStreamConfigurationNotificationReceiveJRFromFirstWord(ump.word0);
auto endpointTxJR = internal::GetStreamConfigurationNotificationTransmitJRFromFirstWord(ump.word0);
- if (protocol != m_preferredMidiProtocol ||
- endpointRxJR != m_preferToSendJRTimestampsToEndpoint ||
- endpointTxJR != m_preferToReceiveJRTimestampsFromEndpoint)
+ if (protocol != m_currentWorkItem.PreferredMidiProtocol ||
+ endpointRxJR != m_currentWorkItem.PreferToSendJRTimestampsToEndpoint ||
+ endpointTxJR != m_currentWorkItem.PreferToReceiveJRTimestampsFromEndpoint)
{
- if (!m_alreadyTriedToNegotiationOnce)
+ if (!m_currentWorkItem.AlreadyTriedToNegotiationOnce)
{
internal::PackedUmp128 configurationRequestUmp{};
ump.word0 = internal::BuildStreamConfigurationRequestFirstWord(
- m_preferredMidiProtocol,
- m_preferToSendJRTimestampsToEndpoint,
- m_preferToReceiveJRTimestampsFromEndpoint);
+ m_currentWorkItem.PreferredMidiProtocol,
+ m_currentWorkItem.PreferToSendJRTimestampsToEndpoint,
+ m_currentWorkItem.PreferToReceiveJRTimestampsFromEndpoint);
- m_alreadyTriedToNegotiationOnce = true;
+ m_currentWorkItem.AlreadyTriedToNegotiationOnce = true;
// send it immediately
- return m_endpoint->SendMidiMessage((byte*)&configurationRequestUmp, (UINT)sizeof(configurationRequestUmp), 0);
+ return m_currentWorkItem.Endpoint->SendMidiMessage((byte*)&configurationRequestUmp, (UINT)sizeof(configurationRequestUmp), 0);
}
else
{
// we've already tried negotiating once. Don't do it again
- m_finalStreamNegotiationResponseReceived = true;
+ m_currentWorkItem.TaskFinalStreamNegotiationResponseReceived = true;
return S_OK;
}
}
else
{
// all good on this try
- m_finalStreamNegotiationResponseReceived = true;
+ m_currentWorkItem.TaskFinalStreamNegotiationResponseReceived = true;
return S_OK;
}
@@ -222,20 +360,12 @@ CMidiEndpointProtocolNegotiationWorker::ProcessStreamConfigurationRequest(intern
-_Use_decl_annotations_
HRESULT
-CMidiEndpointProtocolNegotiationWorker::Start(
- LPCWSTR DeviceInterfaceId,
- BOOL PreferToSendJRTimestampsToEndpoint,
- BOOL PreferToReceiveJRTimestampsFromEndpoint,
- BYTE PreferredMidiProtocol,
- WORD TimeoutMS,
- wil::com_ptr_nothrow ServiceAbstraction)
-
+CMidiEndpointProtocolManager::ProcessCurrentWorkEntry()
{
- m_preferToSendJRTimestampsToEndpoint = PreferToSendJRTimestampsToEndpoint;
- m_preferToReceiveJRTimestampsFromEndpoint = PreferToReceiveJRTimestampsFromEndpoint;
- m_preferredMidiProtocol = PreferredMidiProtocol;
+ OutputDebugString(__FUNCTION__ L"");
+ OutputDebugString(m_currentWorkItem.EndpointInstanceId.c_str());
+
// by using the existing abstractions and activation methods just
// like any other client, we will get the transforms, including
@@ -243,122 +373,90 @@ CMidiEndpointProtocolNegotiationWorker::Start(
// metadata capture code here, and we don't need to make any
// explicit call to the metadata capture transform plugin
- RETURN_HR_IF_NULL(E_FAIL, ServiceAbstraction);
- RETURN_IF_FAILED(ServiceAbstraction->Activate(__uuidof(IMidiBiDi), (void**)&m_endpoint));
+ OutputDebugString(__FUNCTION__ L" - check for null service abstraction");
+ RETURN_HR_IF_NULL(E_FAIL, m_serviceAbstraction);
+
+ OutputDebugString(__FUNCTION__ L" - activate BiDi abstraction");
+ RETURN_IF_FAILED(m_serviceAbstraction->Activate(__uuidof(IMidiBiDi), (void**)&(m_currentWorkItem.Endpoint)));
// Create and open a connection to the endpoint, complete with metadata listeners
+ OutputDebugString(__FUNCTION__ L" - Initialize endpoint");
+
DWORD mmcssTaskId{};
ABSTRACTIONCREATIONPARAMS abstractionCreationParams{ MidiDataFormat_UMP };
- RETURN_IF_FAILED(m_endpoint->Initialize(
- DeviceInterfaceId,
+ RETURN_IF_FAILED(m_currentWorkItem.Endpoint->Initialize(
+ m_currentWorkItem.EndpointInstanceId.c_str(),
&abstractionCreationParams,
&mmcssTaskId,
- this,
- 0
+ (IMidiCallback*)this,
+ MIDI_PROTOCOL_MANAGER_ENDPOINT_CREATION_CONTEXT
));
- m_allMessagesReceived.create();
+// OutputDebugString(__FUNCTION__ L" - Resetting messages received event");
+
+// if (m_allMessagesReceived.is_signaled()) m_allMessagesReceived.ResetEvent();
+
+ HRESULT hr = S_OK;
// Send initial discovery request
// the rest happens in response to messages in the callback
- RETURN_IF_FAILED(RequestAllEndpointDiscoveryInformation());
+ LOG_IF_FAILED(hr = RequestAllEndpointDiscoveryInformation());
-
- // Wait until all metadata arrives or we timeout
- if (!m_allMessagesReceived.wait(TimeoutMS))
+ if (SUCCEEDED(hr))
{
- // we didn't receive everything, but that's not a failure condition for this.
+ OutputDebugString(__FUNCTION__ L" - Requested discovery information");
+ }
+ else
+ {
+ OutputDebugString(__FUNCTION__ L" - FAILED to request discovery information");
}
- m_endpoint->Cleanup();
-
- m_endpoint.reset();
-
- return S_OK;
-}
-
-
-
+ if (SUCCEEDED(hr))
+ {
+ // Wait until all metadata arrives or we timeout
+ if (!m_allMessagesReceived.wait(m_currentWorkItem.TimeoutMS))
+ {
+ // we didn't receive everything, but that's not a failure condition for this.
+ }
+ }
+ if (m_allMessagesReceived.is_signaled()) m_allMessagesReceived.ResetEvent();
-_Use_decl_annotations_
-HRESULT
-CMidiEndpointProtocolManager::Initialize(
- std::shared_ptr& ClientManager,
- std::shared_ptr& DeviceManager
-)
-{
- m_clientManager = ClientManager;
- m_deviceManager = DeviceManager;
+ OutputDebugString(__FUNCTION__ L" - About to cleanup");
- // connect to the service. Needing a reference to this abstraction def
- // creates a circular reference to the MidiSrv Abstraction. Not sure of
- // a good way around that other than fixing up the ClientManager to make
- // local connections with local handles reasonable.
- RETURN_IF_FAILED(CoCreateInstance(__uuidof(Midi2MidiSrvAbstraction), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&m_serviceAbstraction)));
+ m_currentWorkItem.Endpoint->Cleanup();
+ m_currentWorkItem.Endpoint = nullptr;
- return S_OK;
+ return hr;
}
-
-
-
-_Use_decl_annotations_
-HRESULT
-CMidiEndpointProtocolManager::NegotiateAndRequestMetadata(
- LPCWSTR DeviceInterfaceId,
- BOOL PreferToSendJRTimestampsToEndpoint,
- BOOL PreferToReceiveJRTimestampsFromEndpoint,
- BYTE PreferredMidiProtocol,
- WORD TimeoutMS
-) noexcept
+void CMidiEndpointProtocolManager::ThreadWorker()
{
- // We assume the endpoint manager which calls this function knows whether or not protocol
- // negotiation etc. should happen. So we leave it up to the transport to correctly call
- // this or not, and we just assume it made the correct choice.
- //
- // For now, we'll do this synchronously, but it should be in a separate thread in the future
-
-
- CMidiEndpointProtocolNegotiationWorker worker;
-
- // TODO: Going to need to keep a map of these
- //std::thread workerThread(
- // &CMidiEndpointProtocolNegotiationWorker::Start(DeviceInterfaceId, PreferToSendJRTimestampsToEndpoint, PreferToReceiveJRTimestampsFromEndpoint, PreferredMidiProtocol, TimeoutMS),
- // &worker);
+ while (!m_shutdown)
+ {
+ if (m_workQueue.size() > 0)
+ {
+ {
+ OutputDebugString(__FUNCTION__ L" - Item in queue. About to lock and pop :)");
- //m_queueWorkerThread = std::move(workerThread);
+ std::lock_guard lock{ m_queueMutex };
- // start up the worker thread
- //m_queueWorkerThread.detach();
+ m_currentWorkItem = std::move(m_workQueue.front());
+ m_workQueue.pop();
+ OutputDebugString(__FUNCTION__ L" - Obtained item from queue. About to process");
- // TODO: create and spin off a worker thread for this.
+ }
- // Synchronous for first implementation
- return worker.Start(
- DeviceInterfaceId,
- PreferToSendJRTimestampsToEndpoint,
- PreferToReceiveJRTimestampsFromEndpoint,
- PreferredMidiProtocol,
- TimeoutMS,
- m_serviceAbstraction
- );
+ // this will block until completed
+ LOG_IF_FAILED(ProcessCurrentWorkEntry());
+ }
+ // todo
+ Sleep(300);
+ }
}
-
-
-HRESULT
-CMidiEndpointProtocolManager::Cleanup()
-{
- // TODO terminate any open threads and ensure they close up
-
- m_clientManager.reset();
- m_deviceManager.reset();
-
- return S_OK;
-}
diff --git a/src/api/Service/Exe/MidiEndpointProtocolManager.h b/src/api/Service/Exe/MidiEndpointProtocolManager.h
index d20306cb1..fa25d001a 100644
--- a/src/api/Service/Exe/MidiEndpointProtocolManager.h
+++ b/src/api/Service/Exe/MidiEndpointProtocolManager.h
@@ -8,52 +8,38 @@
#pragma once
-class CMidiEndpointProtocolNegotiationWorker : public Microsoft::WRL::RuntimeClass<
- Microsoft::WRL::RuntimeClassFlags,
- IMidiCallback>
-{
-public:
- HRESULT Start(
- _In_ LPCWSTR DeviceInterfaceId,
- _In_ BOOL PreferToSendJRTimestampsToEndpoint,
- _In_ BOOL PreferToReceiveJRTimestampsFromEndpoint,
- _In_ BYTE PreferredMidiProtocol,
- _In_ WORD TimeoutMS,
- _In_ wil::com_ptr_nothrow ServiceAbstraction);
-
- STDMETHOD(Callback)(_In_ PVOID Data, _In_ UINT Size, _In_ LONGLONG Position, _In_ LONGLONG Context);
-
-private:
- HRESULT RequestAllFunctionBlocks();
- HRESULT RequestAllEndpointDiscoveryInformation();
- HRESULT ProcessStreamConfigurationRequest(_In_ internal::PackedUmp128 ump);
-
- wil::com_ptr_nothrow m_endpoint;
-
- bool m_preferToSendJRTimestampsToEndpoint{ false };
- bool m_preferToReceiveJRTimestampsFromEndpoint{ false };
- uint8_t m_preferredMidiProtocol{ };
-
- bool m_alreadyTriedToNegotiationOnce{ false };
-
- wil::unique_event_nothrow m_allMessagesReceived;
- bool m_endpointInfoReceived{ false };
- bool m_finalStreamNegotiationResponseReceived{ false };
- bool m_endpointNameReceived{ false };
- bool m_endpointProductInstanceIdReceived{ false };
- bool m_deviceIdentityReceived{ false };
-
- uint8_t m_declaredFunctionBlockCount{ 0 };
-
- uint8_t m_countFunctionBlocksReceived{ 0 };
- uint8_t m_countFunctionBlockNamesReceived{ 0 };
+#include
+#include
+struct ProtocolManagerWork
+{
+ std::wstring EndpointInstanceId{};
+ bool PreferToSendJRTimestampsToEndpoint{ false };
+ bool PreferToReceiveJRTimestampsFromEndpoint{ false };
+ uint8_t PreferredMidiProtocol{};
+ uint16_t TimeoutMS{ 2000 };
+
+ wil::com_ptr_nothrow Endpoint;
+
+ bool AlreadyTriedToNegotiationOnce{ false };
+
+ bool TaskEndpointInfoReceived{ false };
+ bool TaskFinalStreamNegotiationResponseReceived{ false };
+ bool TaskEndpointNameReceived{ false };
+ bool TaskEndpointProductInstanceIdReceived{ false };
+ bool TaskDeviceIdentityReceived{ false };
+
+ uint8_t DeclaredFunctionBlockCount{ 0 };
+
+ uint8_t CountFunctionBlocksReceived{ 0 };
+ uint8_t CountFunctionBlockNamesReceived{ 0 };
};
class CMidiEndpointProtocolManager : public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags,
- IMidiEndpointProtocolManagerInterface>
+ IMidiEndpointProtocolManagerInterface,
+ IMidiCallback>
{
public:
@@ -73,13 +59,36 @@ class CMidiEndpointProtocolManager : public Microsoft::WRL::RuntimeClass<
_In_ WORD TimeoutMS
);
+
+ STDMETHOD(Callback)(_In_ PVOID Data, _In_ UINT Size, _In_ LONGLONG Position, _In_ LONGLONG Context);
+
HRESULT Cleanup();
private:
+ void ThreadWorker();
+
+ HRESULT ProcessCurrentWorkEntry();
+
+
+ HRESULT RequestAllFunctionBlocks();
+ HRESULT RequestAllEndpointDiscoveryInformation();
+ HRESULT ProcessStreamConfigurationRequest(_In_ internal::PackedUmp128 ump);
+
std::shared_ptr m_clientManager;
std::shared_ptr m_deviceManager;
+
wil::com_ptr_nothrow m_serviceAbstraction;
+ std::mutex m_queueMutex;
+ std::queue m_workQueue;
+
+ std::thread m_queueWorkerThread;
+
+ ProtocolManagerWork m_currentWorkItem;
+ wil::unique_event_nothrow m_allMessagesReceived;
+ wil::unique_event_nothrow m_queueWorkerThreadWakeup;
+ // true if we're closing down
+ bool m_shutdown{ false };
};
diff --git a/src/api/Service/Exe/MidiTransformPipe.cpp b/src/api/Service/Exe/MidiTransformPipe.cpp
index 5e76e2bc9..2c7a8fc3f 100644
--- a/src/api/Service/Exe/MidiTransformPipe.cpp
+++ b/src/api/Service/Exe/MidiTransformPipe.cpp
@@ -19,7 +19,7 @@ CMidiTransformPipe::Initialize(
)
{
wil::com_ptr_nothrow midiTransform;
- TRANSFORMCREATIONPARAMS creationParams {0};
+ TRANSFORMCREATIONPARAMS creationParams {};
m_TransformGuid = CreationParams->TransformGuid;
m_DataFormatIn = CreationParams->DataFormatIn;
diff --git a/src/api/Service/Inc/MidiDevicePipe.h b/src/api/Service/Inc/MidiDevicePipe.h
index eab2f1ef6..d381add4b 100644
--- a/src/api/Service/Inc/MidiDevicePipe.h
+++ b/src/api/Service/Inc/MidiDevicePipe.h
@@ -35,7 +35,7 @@ class CMidiDevicePipe : public CMidiPipe
private:
wil::critical_section m_DevicePipeLock;
- winrt::guid m_AbstractionGuid;
+ winrt::guid m_AbstractionGuid{};
wil::com_ptr_nothrow m_MidiBiDiDevice;
wil::com_ptr_nothrow m_MidiInDevice;
wil::com_ptr_nothrow m_MidiOutDevice;
diff --git a/src/api/Service/Inc/MidiTransformPipe.h b/src/api/Service/Inc/MidiTransformPipe.h
index 3574843ca..22bc135b8 100644
--- a/src/api/Service/Inc/MidiTransformPipe.h
+++ b/src/api/Service/Inc/MidiTransformPipe.h
@@ -37,9 +37,9 @@ class CMidiTransformPipe : public CMidiPipe
private:
wil::com_ptr_nothrow m_MidiDataTransform;
- winrt::guid m_TransformGuid;
- MidiDataFormat m_DataFormatIn;
- MidiDataFormat m_DataFormatOut;
- MidiFlow m_Flow;
+ winrt::guid m_TransformGuid{};
+ MidiDataFormat m_DataFormatIn{};
+ MidiDataFormat m_DataFormatOut{};
+ MidiFlow m_Flow{};
};
diff --git a/src/api/Test/Libs/MidiSWEnum/MidiSWEnum.cpp b/src/api/Test/Libs/MidiSWEnum/MidiSWEnum.cpp
index f61dd8c6f..9eb3df26c 100644
--- a/src/api/Test/Libs/MidiSWEnum/MidiSWEnum.cpp
+++ b/src/api/Test/Libs/MidiSWEnum/MidiSWEnum.cpp
@@ -72,7 +72,7 @@ MidiSWDeviceEnum::EnumerateDevices(
prop = device.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_SupportedDataFormats));
RETURN_HR_IF_NULL(E_INVALIDARG, prop);
- supportedDataFormats = (MidiDataFormat) winrt::unbox_value(prop);
+ supportedDataFormats = (MidiDataFormat) winrt::unbox_value(prop);
prop = device.Properties().Lookup(winrt::to_hstring(L"System.Devices.InterfaceClassGuid"));
RETURN_HR_IF_NULL(E_INVALIDARG, prop);
diff --git a/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp b/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp
index fd5c01ce4..a0d9940c6 100644
--- a/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp
+++ b/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp
@@ -157,6 +157,10 @@ CMidi2EndpointMetadataListenerMidiTransform::SendMidiMessage(
if (internal::GetUmpMessageTypeFromFirstWord(ump.word0) == 0xF)
{
+ OutputDebugString(__FUNCTION__ L" - Type F message. About to process.");
+
+ // TODO: this should get thrown into a work queue and processed out of band
+
ProcessStreamMessage(ump, timestamp);
}
else
diff --git a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointPropertiesCommand.cs b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointPropertiesCommand.cs
index 53f46e9f3..02ef3b993 100644
--- a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointPropertiesCommand.cs
+++ b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointPropertiesCommand.cs
@@ -121,7 +121,7 @@ public override int Execute(CommandContext context, Settings settings)
table.AddRow("Receives JR Timestamps", di.ConfiguredToReceiveJRTimestamps.ToString());
table.AddEmptyRow();
- table.AddRow(AnsiMarkupFormatter.FormatTableColumnHeading("Capabilities"), "");
+ table.AddRow(AnsiMarkupFormatter.FormatTableColumnHeading("Declared Capabilities"), "");
table.AddRow("Multi-client", di.SupportsMultiClient.ToString());
table.AddRow("MIDI 1.0 Protocol", di.SupportsMidi10Protocol.ToString());
table.AddRow("MIDI 2.0 Protocol", di.SupportsMidi20Protocol.ToString());
@@ -146,9 +146,9 @@ public override int Execute(CommandContext context, Settings settings)
table.AddEmptyRow();
table.AddEmptyRow();
table.AddRow(AnsiMarkupFormatter.FormatTableColumnHeading("Function Blocks"), "");
- table.AddEmptyRow();
table.AddRow("Static Function Blocks?", di.HasStaticFunctionBlocks.ToString());
table.AddRow("Declared Function Block Count", di.FunctionBlockCount.ToString());
+ table.AddEmptyRow();
if (di.FunctionBlocks.Count > 0)
{
@@ -246,6 +246,11 @@ public override int Execute(CommandContext context, Settings settings)
{
table.AddEmptyRow();
table.AddRow(AnsiMarkupFormatter.FormatTableColumnHeading("Group Terminal Blocks"), "");
+
+ if (di.FunctionBlocks.Count > 0)
+ {
+ table.AddRow("Function blocks are present. Applications should use the Function Blocks and their group declarations instead of the Group Terminal Blocks.", "");
+ }
foreach (var groupTerminalBlock in di.GroupTerminalBlocks)
{
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 bb69f215f..f85fdf4bc 100644
--- a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessagesFileCommand.cs
+++ b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessagesFileCommand.cs
@@ -100,6 +100,10 @@ public override int Execute(CommandContext context, Settings settings)
AnsiConsole.MarkupLine(Strings.SendMessageSendingThroughEndpointLabel + ": " + AnsiMarkupFormatter.FormatDeviceInstanceId(endpointId));
AnsiConsole.WriteLine();
+ AnsiConsole.MarkupLine("Temporary UI change: Only error lines will be displayed when sending messages.");
+
+
+
using var session = MidiSession.CreateSession($"{Strings.AppShortName} - {Strings.SendMessageSessionNameSuffix}");
if (session == null)
{
@@ -123,12 +127,66 @@ public override int Execute(CommandContext context, Settings settings)
return (int)MidiConsoleReturnCode.ErrorOpeningEndpointConnection;
}
- var table = new Table();
+ // TODO: Consider creating a message sender thread worker object that is shared
+ // between this and the send-message command
// if not verbose, just show a status spinner
+ //AnsiConsole.Progress()
+ // .Start(ctx =>
+ // {
+ // //if (settings.DelayBetweenMessages == 0 && (settings.Count * (settings.Words!.Length + 2)) > bufferWarningThreshold)
+ // //{
+ // // AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatWarning(Strings.SendMessageFloodWarning));
+ // // AnsiConsole.WriteLine();
+ // //}
+
+ // var sendTask = ctx.AddTask("[white]Sending messages[/]");
+ // sendTask.MaxValue = settings.Count;
+ // sendTask.Value = 0;
+
+ // messageSenderThread.Start();
+
+
+ // AnsiConsole.MarkupLine(Strings.SendMessagePressEscapeToStopSendingMessage);
+ // AnsiConsole.WriteLine();
+ // while (stillSending)
+ // {
+ // // check for input
+
+ // if (Console.KeyAvailable)
+ // {
+ // var keyInfo = Console.ReadKey(true);
+
+ // if (keyInfo.Key == ConsoleKey.Escape)
+ // {
+ // stillSending = false;
+
+ // // wake up the threads so they terminate
+ // m_messageDispatcherThreadWakeup.Set();
+
+ // AnsiConsole.WriteLine();
+ // AnsiConsole.MarkupLine("🛑 " + Strings.SendMessageEscapePressedMessage);
+ // }
+
+ // }
+
+ // sendTask.Value = messagesSent;
+ // ctx.Refresh();
+
+ // if (stillSending) Thread.Sleep(100);
+ // }
+
+ // sendTask.Value = messagesSent;
+
+ // });
// if verbose, spin up a live table
+ var table = new Table();
+
+ UInt32 countSkippedLines = 0;
+ UInt32 countFailedLines = 0;
+ UInt32 countMessagesSent = 0;
AnsiConsole.Live(table)
.Start(ctx =>
@@ -186,13 +244,19 @@ public override int Execute(CommandContext context, Settings settings)
line = fileStream.ReadLine();
if (line == null)
+ {
+ countSkippedLines++;
continue;
+ }
lineNumber++;
// skip over comments and white space
if (LineIsIgnorable(line))
+ {
+ countSkippedLines++;
continue;
+ }
// if we're using Auto for the delimiter, each line is evaluated in case the file is mixed
// if you don't want to take this hit, specify the delimiter on the command line
@@ -218,10 +282,19 @@ public override int Execute(CommandContext context, Settings settings)
}
// send the message
- connection.SendMessageWordArray(timestamp, words, 0, (byte)words.Count());
+ if (MidiEndpointConnection.SendMessageSucceeded(connection.SendMessageWordArray(timestamp, words, 0, (byte)words.Count())))
+ {
+ countMessagesSent++;
+ }
+ else
+ {
+ countFailedLines++;
+ }
+
string detailedMessageType = MidiMessageUtility.GetMessageFriendlyNameFromFirstWord(words[0]);
+#if false
// display the sent data
table.AddRow(
AnsiMarkupFormatter.FormatGeneralNumber(lineNumber),
@@ -232,10 +305,14 @@ public override int Execute(CommandContext context, Settings settings)
);
ctx.Refresh();
- Thread.Sleep(settings.DelayBetweenMessages);
+#endif
+
+
}
else
{
+ countFailedLines++;
+
// invalid UMP
table.AddRow(
AnsiMarkupFormatter.FormatGeneralNumber(lineNumber),
@@ -245,7 +322,11 @@ public override int Execute(CommandContext context, Settings settings)
);
ctx.Refresh();
- Thread.Sleep(0);
+ }
+
+ if (settings.DelayBetweenMessages > 0)
+ {
+ Thread.Sleep(settings.DelayBetweenMessages);
}
}
else
@@ -271,6 +352,22 @@ public override int Execute(CommandContext context, Settings settings)
});
+ if (countMessagesSent > 0)
+ {
+ AnsiConsole.WriteLine($"{countMessagesSent.ToString("N0")} message(s) sent.");
+ }
+
+ if (countSkippedLines > 0)
+ {
+ AnsiConsole.WriteLine($"{countSkippedLines.ToString("N0")} lines skipped (empty or comments).");
+ }
+
+ if (countFailedLines > 0)
+ {
+ AnsiConsole.WriteLine(AnsiMarkupFormatter.FormatError($"{countFailedLines.ToString("N0")} lines with errors."));
+ }
+
+
if (session != null)
session.Dispose();
diff --git a/src/user-tools/midi-console/Midi/Midi.csproj b/src/user-tools/midi-console/Midi/Midi.csproj
index efb478b4d..d5a14373a 100644
--- a/src/user-tools/midi-console/Midi/Midi.csproj
+++ b/src/user-tools/midi-console/Midi/Midi.csproj
@@ -31,7 +31,7 @@
-
+