diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi
index 3aa6325b..d56e36eb 100644
--- a/build/staging/version/BundleInfo.wxi
+++ b/build/staging/version/BundleInfo.wxi
@@ -1,4 +1,4 @@
-
+
diff --git a/docs/developer-docs/Windows.Devices.Midi2/service/MidiService.md b/docs/developer-docs/Windows.Devices.Midi2/service/MidiService.md
index e2f08846..55ba0692 100644
--- a/docs/developer-docs/Windows.Devices.Midi2/service/MidiService.md
+++ b/docs/developer-docs/Windows.Devices.Midi2/service/MidiService.md
@@ -38,7 +38,7 @@ Loopback endpoints created by the user and stored in the configuration file will
For plugins which support updates at runtime, developers of those plugins should create configuration WinRT types which implement the required configuration interfaces, and create the JSON that is used in the service. In this way, third-party service transport and message processing plugins can be created and configured without changes to the API.
-> Note: In version 1 of the API, only transports can be configured at runtime. We're working on enabling configuration of message processing plugins.
+> Note: In version 1 of the API, only transports can be configured at runtime. We're working on enabling configuration of message processing plugins. The API is a no-op.
## Static Functions : Service Health
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/AbstractionState.cpp b/src/api/Abstraction/NetworkMidiAbstraction/AbstractionState.cpp
new file mode 100644
index 00000000..c1836f3f
--- /dev/null
+++ b/src/api/Abstraction/NetworkMidiAbstraction/AbstractionState.cpp
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#include "pch.h"
+
+
+AbstractionState::AbstractionState() = default;
+AbstractionState::~AbstractionState() = default;
+
+AbstractionState& AbstractionState::Current()
+{
+ // explanation: http://www.modernescpp.com/index.php/thread-safe-initialization-of-data/
+
+ static AbstractionState current;
+
+ return current;
+}
+
+
+
+HRESULT
+AbstractionState::ConstructEndpointManager()
+{
+ RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&m_endpointManager));
+
+ return S_OK;
+}
+
+
+HRESULT
+AbstractionState::ConstructConfigurationManager()
+{
+ RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&m_configurationManager));
+
+ return S_OK;
+}
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/AbstractionState.h b/src/api/Abstraction/NetworkMidiAbstraction/AbstractionState.h
new file mode 100644
index 00000000..e593ef00
--- /dev/null
+++ b/src/api/Abstraction/NetworkMidiAbstraction/AbstractionState.h
@@ -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/
+// ============================================================================
+
+
+#pragma once
+
+// singleton
+class AbstractionState
+{
+
+public:
+ static AbstractionState& Current();
+
+ // no copying
+ AbstractionState(_In_ const AbstractionState&) = delete;
+ AbstractionState& operator=(_In_ const AbstractionState&) = delete;
+
+
+ wil::com_ptr GetEndpointManager()
+ {
+ return m_endpointManager;
+ }
+
+ wil::com_ptr GetConfigurationManager()
+ {
+ return m_configurationManager;
+ }
+
+ std::shared_ptr GetEndpointTable()
+ {
+ return m_endpointTable;
+ }
+
+
+ HRESULT Cleanup()
+ {
+ m_endpointManager.reset();
+ m_configurationManager.reset();
+
+ return S_OK;
+ }
+
+
+ HRESULT ConstructEndpointManager();
+ HRESULT ConstructConfigurationManager();
+
+
+private:
+ AbstractionState();
+ ~AbstractionState();
+
+
+ wil::com_ptr m_endpointManager;
+ wil::com_ptr m_configurationManager;
+
+ std::shared_ptr m_endpointTable = std::make_shared();
+};
\ No newline at end of file
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.cpp b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.cpp
index 643c14a0..fdb19632 100644
--- a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.cpp
+++ b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.cpp
@@ -58,9 +58,9 @@ CMidi2NetworkMidiAbstraction::Activate(
TraceLoggingWrite(
MidiNetworkMidiAbstractionTelemetryProvider::Provider(),
- __FUNCTION__ "- Midi BiDi",
+ __FUNCTION__ "- IMidiBiDi",
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingValue(__FUNCTION__),
+ TraceLoggingWideString(L"IMidiBiDi", "requested interface"),
TraceLoggingPointer(this, "this")
);
@@ -68,23 +68,56 @@ CMidi2NetworkMidiAbstraction::Activate(
RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&midiBiDi));
*Interface = midiBiDi.detach();
}
+
+
else if (__uuidof(IMidiEndpointManager) == Riid)
{
TraceLoggingWrite(
MidiNetworkMidiAbstractionTelemetryProvider::Provider(),
- __FUNCTION__ "- Midi Endpoint Manager",
+ __FUNCTION__ "- IMidiEndpointManager",
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
- TraceLoggingValue(__FUNCTION__),
+ TraceLoggingWideString(L"IMidiEndpointManager", "requested interface"),
+ TraceLoggingPointer(this, "this")
+ );
+
+ // check to see if this is the first time we're creating the endpoint manager. If so, create it.
+ if (AbstractionState::Current().GetEndpointManager() == nullptr)
+ {
+ AbstractionState::Current().ConstructEndpointManager();
+ }
+
+ RETURN_IF_FAILED(AbstractionState::Current().GetEndpointManager()->QueryInterface(Riid, Interface));
+ }
+
+
+ else if (__uuidof(IMidiAbstractionConfigurationManager) == Riid)
+ {
+ TraceLoggingWrite(
+ MidiNetworkMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingWideString(L"IMidiAbstractionConfigurationManager", "requested interface"),
TraceLoggingPointer(this, "this")
);
- wil::com_ptr_nothrow midiEndpointManager;
- RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&midiEndpointManager));
- *Interface = midiEndpointManager.detach();
+ // check to see if this is the first time we're creating the configuration manager. If so, create it.
+ if (AbstractionState::Current().GetConfigurationManager() == nullptr)
+ {
+ AbstractionState::Current().ConstructConfigurationManager();
+ }
+
+ RETURN_IF_FAILED(AbstractionState::Current().GetConfigurationManager()->QueryInterface(Riid, Interface));
}
+
else
{
- OutputDebugString(L"" __FUNCTION__ " Returning E_NOINTERFACE. Was an interface added that isn't handled in the Abstraction?");
+ TraceLoggingWrite(
+ MidiNetworkMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingWideString(L"Unknown or invalid interface request", "message"),
+ TraceLoggingPointer(this, "this")
+ );
return E_NOINTERFACE;
}
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.vcxproj b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.vcxproj
index a077bf24..b1e46bb7 100644
--- a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.vcxproj
+++ b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.vcxproj
@@ -274,8 +274,10 @@
+
+
@@ -291,13 +293,18 @@
+
+
+
+
+
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.vcxproj.filters b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.vcxproj.filters
index 63ada33e..d71785a0 100644
--- a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.vcxproj.filters
+++ b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiAbstraction.vcxproj.filters
@@ -36,6 +36,12 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
@@ -79,6 +85,21 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiConfigurationManager.cpp b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiConfigurationManager.cpp
new file mode 100644
index 00000000..ef273721
--- /dev/null
+++ b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiConfigurationManager.cpp
@@ -0,0 +1,97 @@
+// 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"
+
+
+_Use_decl_annotations_
+HRESULT
+CMidi2NetworkMidiConfigurationManager::Initialize(
+ GUID AbstractionId,
+ IUnknown* MidiDeviceManager
+)
+{
+ TraceLoggingWrite(
+ MidiNetworkMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ RETURN_HR_IF_NULL(E_INVALIDARG, MidiDeviceManager);
+ RETURN_IF_FAILED(MidiDeviceManager->QueryInterface(__uuidof(IMidiDeviceManagerInterface), (void**)&m_MidiDeviceManager));
+
+ m_abstractionId = AbstractionId;
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+HRESULT
+CMidi2NetworkMidiConfigurationManager::UpdateConfiguration(
+ LPCWSTR ConfigurationJsonSection,
+ BOOL IsFromConfigurationFile,
+ BSTR* Response
+)
+{
+ TraceLoggingWrite(
+ MidiNetworkMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(ConfigurationJsonSection, "json")
+ );
+
+ UNREFERENCED_PARAMETER(IsFromConfigurationFile);
+
+
+
+ // if we're passed a null or empty json, we just quietly exit
+ if (ConfigurationJsonSection == nullptr) return S_OK;
+
+ json::JsonObject jsonObject;
+
+ // default to failure
+ auto responseObject = internal::BuildConfigurationResponseObject(false);
+
+
+ // TODO: All your json parsing config stuff. Build any device table entries from it, etc.
+ // See Midi2.LoopbackMidiAbstraction for an example
+
+ // The runtime json sent up from the client in the case of network MIDI may include
+ // an IP address, port, password, etc. You can assume there can be a UI on the client
+ // for gathering that information and then sending it up. The API just needs to know
+ // what is needed.
+
+
+ // the response object should include anything the client needs to be able to use
+ // the endpoint, if the creation was successful. Typically, this is just an SWD
+ // interface id
+ internal::JsonStringifyObjectToOutParam(responseObject, &Response);
+
+ return S_OK;
+
+}
+
+
+HRESULT
+CMidi2NetworkMidiConfigurationManager::Cleanup()
+{
+ TraceLoggingWrite(
+ MidiNetworkMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+
+
+
+ return S_OK;
+}
+
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiConfigurationManager.h b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiConfigurationManager.h
new file mode 100644
index 00000000..d0036917
--- /dev/null
+++ b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiConfigurationManager.h
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+#pragma once
+
+
+class CMidi2NetworkMidiConfigurationManager :
+ public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags,
+ IMidiAbstractionConfigurationManager>
+
+{
+public:
+ STDMETHOD(Initialize(_In_ GUID AbstractionId, _In_ IUnknown* MidiDeviceManager));
+ STDMETHOD(UpdateConfiguration(_In_ LPCWSTR ConfigurationJsonSection, _In_ BOOL IsFromConfigurationFile, _Out_ BSTR* Response));
+ STDMETHOD(Cleanup)();
+
+private:
+ wil::com_ptr_nothrow m_MidiDeviceManager;
+
+ GUID m_abstractionId; // kept for convenience
+};
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiEndpointManager.cpp b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiEndpointManager.cpp
index bf726d92..70f4aa3d 100644
--- a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiEndpointManager.cpp
+++ b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiEndpointManager.cpp
@@ -48,43 +48,81 @@ CMidi2NetworkMidiEndpointManager::Initialize(
}
+HRESULT
+CMidi2NetworkMidiEndpointManager::CreateParentDevice()
+{
+ TraceLoggingWrite(
+ MidiNetworkMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+ // the parent device parameters are set by the transport (this)
+ std::wstring parentDeviceName{ TRANSPORT_PARENT_DEVICE_NAME };
+ std::wstring parentDeviceId{ internal::NormalizeDeviceInstanceIdWStringCopy(TRANSPORT_PARENT_ID) };
-void SwMidiParentDeviceCreateCallback(__in HSWDEVICE /*hSwDevice*/, __in HRESULT /*CreationResult*/, __in_opt PVOID /*pContext*/, __in_opt PCWSTR /* pszDeviceInstanceId */)
-{
-}
+ SW_DEVICE_CREATE_INFO createInfo = {};
+ createInfo.cbSize = sizeof(createInfo);
+ createInfo.pszInstanceId = parentDeviceId.c_str();
+ createInfo.CapabilityFlags = SWDeviceCapabilitiesNone;
+ createInfo.pszDeviceDescription = parentDeviceName.c_str();
+ createInfo.pContainerId = &m_containerId;
+ const ULONG deviceIdMaxSize = 255;
+ wchar_t newDeviceId[deviceIdMaxSize]{ 0 };
+ RETURN_IF_FAILED(m_MidiDeviceManager->ActivateVirtualParentDevice(
+ 0,
+ nullptr,
+ &createInfo,
+ (PWSTR)newDeviceId,
+ deviceIdMaxSize
+ ));
-HRESULT
-CMidi2NetworkMidiEndpointManager::CreateParentDevice()
-{
- return S_OK;
-}
+ m_parentDeviceId = internal::NormalizeDeviceInstanceIdWStringCopy(newDeviceId);
-_Use_decl_annotations_
-HRESULT CMidi2NetworkMidiEndpointManager::CreateConfiguredEndpoints(std::wstring configurationJson)
-{
+ TraceLoggingWrite(
+ MidiNetworkMidiAbstractionTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(newDeviceId, "New parent device instance id")
+ );
+
return S_OK;
}
-// this will be called from the runtime endpoint creation interface
-HRESULT
-CMidi2NetworkMidiEndpointManager::CreateEndpoint()
+
+_Use_decl_annotations_
+HRESULT
+CMidi2NetworkMidiEndpointManager::CreateEndpoint(MidiNetworkDeviceDefinition& deviceEndpointDefinition)
{
+ UNREFERENCED_PARAMETER(deviceEndpointDefinition);
+
+ // you'll need one or more functions to create endpoint types used in network MIDI.
+ // At the end of the function, after the interface has been created successfully,
+ // you need to initiate discovery and protocol negotiation. For an example of the
+ // calls to make for that, see
+ // CMidi2VirtualMidiEndpointManager::CreateClientVisibleEndpoint()
+ //
+ // note that this function is just a placeholder. you may need more than one
+ // or it may need any number of other parameters. Same with the
+ // MidiNetworkDeviceDefinition type which will likely need more info for you
+ // to track endpoints created by this abstraction
+
return S_OK;
}
+
HRESULT
CMidi2NetworkMidiEndpointManager::Cleanup()
{
- OutputDebugString(L"" __FUNCTION__ " Enter");
-
TraceLoggingWrite(
MidiNetworkMidiAbstractionTelemetryProvider::Provider(),
__FUNCTION__,
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiEndpointManager.h b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiEndpointManager.h
index a9d6627c..79bba34b 100644
--- a/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiEndpointManager.h
+++ b/src/api/Abstraction/NetworkMidiAbstraction/Midi2.NetworkMidiEndpointManager.h
@@ -8,67 +8,6 @@
#pragma once
-typedef enum _SWDEVICESTATE
-{
- NotCreated = 0, // SwDeviceCreate not yet called
- CreatePending, // SwDeviceCreate called successfully, but creation callback not yet invoked
- Created, // SwDeviceCreate creation callback has been invoked and device interface has been created
- Failed
-} SWDEVICESTATE;
-
-
-using unique_hswdevice = wil::unique_any;
-using unique_swd_string = wil::unique_any;
-
-
-class MidiUmpEndpointInfo
-{
-public:
- std::wstring Id; // the filter InterfaceId
- std::wstring InstanceId; // the MIDI instance id
- std::wstring ParentInstanceId; // The instance id of the parent device
- std::wstring Name; // friendly name for this device
- //INT32 PinId{ 0 };
- //BOOL Standard{ FALSE };
- //BOOL Cyclic{ FALSE };
- //BOOL UMP{ TRUE };
- MidiFlow Flow{ MidiFlowBidirectional };
-};
-
-class MidiEndpointParentDeviceInfo
-{
-public:
- GUID InterfaceCategory;
- SWDEVICESTATE SwDeviceState; // SWD creation state
- unique_hswdevice SwDevice; // Handle to the SWD created for the MIDI port
- unique_swd_string DeviceInterfaceId; // SWD interface ID for the MIDI port
- std::wstring InstanceId;
-
-// std::wstring Id; // the filter InterfaceId
- std::wstring Name; // friendly name for this device
- //INT32 PinId{ 0 };
- //BOOL Standard{ FALSE };
- //BOOL Cyclic{ FALSE };
- //BOOL UMP{ TRUE };
- //MidiFlow Flow{ MidiFlowBidirectional };
-};
-
-typedef struct _PARENTDEVICECREATECONTEXT
-{
- MidiEndpointParentDeviceInfo* MidiParentDevice;
- wil::unique_event creationCompleted{ wil::EventOptions::None };
- DEVPROPERTY* InterfaceDevProperties;
- ULONG IntPropertyCount;
-} PARENTDEVICECREATECONTEXT, * PPARENTDEVICECREATECONTEXT;
-
-
-// TODO: This class can implement another interface which takes in the json parameters
-// (or whatever we want) for naming the new endpoints. Then, the SDK can call that
-// interface to do the setup of the device. Or, there can be a common "runtime creatable transport"
-// interface that is called from the API. TBD. But can't break that interface with
-// the SDK, since the SDK ships with apps and could be older.
-
-
class CMidi2NetworkMidiEndpointManager :
public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags,
@@ -82,21 +21,12 @@ class CMidi2NetworkMidiEndpointManager :
private:
GUID m_containerId{};
GUID m_transportAbstractionId{};
+ std::wstring m_parentDeviceId{};
-
- HRESULT CreateEndpoint();
HRESULT CreateParentDevice();
-
- HRESULT CreateConfiguredEndpoints(_In_ std::wstring configurationJson);
-
+ HRESULT CreateEndpoint(_In_ MidiNetworkDeviceDefinition& deviceEndpoint);
wil::com_ptr_nothrow m_MidiDeviceManager;
-
- // TBD if we need to keep this here as well. The MidiDeviceManager has its own vector of endpoints
- std::vector> m_AvailableMidiUmpEndpoints;
-
- std::unique_ptr m_ParentDevice{ nullptr };
-
- json::JsonObject m_JsonObject{ nullptr };
+ wil::com_ptr_nothrow m_MidiProtocolManager;
};
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/MidiNetworkDevice.h b/src/api/Abstraction/NetworkMidiAbstraction/MidiNetworkDevice.h
new file mode 100644
index 00000000..645e2d53
--- /dev/null
+++ b/src/api/Abstraction/NetworkMidiAbstraction/MidiNetworkDevice.h
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+
+#pragma once
+
+class MidiNetworkDevice
+{
+public:
+
+
+
+ void Cleanup()
+ {
+ //m_bidiA = nullptr;
+ // m_callback = nullptr;
+ }
+
+ ~MidiNetworkDevice()
+ {
+
+ Cleanup();
+ }
+
+private:
+
+};
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/MidiNetworkDeviceDefinition.h b/src/api/Abstraction/NetworkMidiAbstraction/MidiNetworkDeviceDefinition.h
new file mode 100644
index 00000000..409e0f6a
--- /dev/null
+++ b/src/api/Abstraction/NetworkMidiAbstraction/MidiNetworkDeviceDefinition.h
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+
+#pragma once
+
+
+// change / extend as necessary. You may need things like IP address
+// or whether it's a server or client port, or other information
+// captured in here to able to be used when creating a network
+// MIDI device in device manager. Some of this may come from the
+// configuration files, and others may come from the network during
+// the initial session creation.
+struct MidiNetworkDeviceDefinition
+{
+ std::wstring AssociationId{};
+
+ std::wstring EndpointName{};
+ std::wstring EndpointDescription{};
+
+ std::wstring EndpointUniqueIdentifier{};
+
+ std::wstring InstanceIdPrefix{};
+
+ std::wstring CreatedShortClientInstanceId{};
+ std::wstring CreatedEndpointInterfaceId{};
+};
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/MidiNetworkDeviceTable.h b/src/api/Abstraction/NetworkMidiAbstraction/MidiNetworkDeviceTable.h
new file mode 100644
index 00000000..b0b70bc2
--- /dev/null
+++ b/src/api/Abstraction/NetworkMidiAbstraction/MidiNetworkDeviceTable.h
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License
+// ============================================================================
+// This is part of the Windows MIDI Services App API and should be used
+// in your Windows application via an official binary distribution.
+// Further information: https://github.com/microsoft/MIDI/
+// ============================================================================
+
+
+#pragma once
+
+
+class MidiNetworkDeviceTable
+{
+public:
+
+ MidiNetworkDevice* GetDevice(std::wstring associationId)
+ {
+ if (m_devices.find(associationId) != m_devices.end())
+ {
+ return &m_devices[associationId];
+ }
+ else
+ {
+ return nullptr;
+ }
+ }
+
+ void SetDevice(std::wstring associationId, MidiNetworkDevice device)
+ {
+ m_devices[associationId] = device;
+ }
+
+ void RemoveDevice(std::wstring associationId)
+ {
+ if (auto device = m_devices.find(associationId); device != m_devices.end())
+ {
+ device->second.Cleanup();
+
+ m_devices.erase(associationId);
+ }
+ }
+
+
+private:
+ std::map m_devices{};
+
+};
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/abstraction_defs.h b/src/api/Abstraction/NetworkMidiAbstraction/abstraction_defs.h
index e6f62a32..df19a113 100644
--- a/src/api/Abstraction/NetworkMidiAbstraction/abstraction_defs.h
+++ b/src/api/Abstraction/NetworkMidiAbstraction/abstraction_defs.h
@@ -16,7 +16,7 @@
// TODO: Names should be moved to .rc for localization
#define TRANSPORT_PARENT_ID L"MIDIU_UDP_TRANSPORT"
-#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 2.0 Network Endpoints"
+#define TRANSPORT_PARENT_DEVICE_NAME L"Network MIDI 2.0 (UDP)"
#define LOOPBACK_PARENT_ROOT L"HTREE\\ROOT\\0"
diff --git a/src/api/Abstraction/NetworkMidiAbstraction/pch.h b/src/api/Abstraction/NetworkMidiAbstraction/pch.h
index d5145785..593f54f2 100644
--- a/src/api/Abstraction/NetworkMidiAbstraction/pch.h
+++ b/src/api/Abstraction/NetworkMidiAbstraction/pch.h
@@ -56,6 +56,11 @@ namespace json = ::winrt::Windows::Data::Json;
#include "strsafe.h"
#include "wstring_util.h"
+#undef GetObject
+#include
+namespace json = ::winrt::Windows::Data::Json;
+
+
// AbstractionUtilities
#include "endpoint_data_helpers.h"
#include "swd_property_builders.h"
@@ -76,11 +81,27 @@ namespace internal = ::Windows::Devices::Midi2::Internal;
#include "mididevicemanagerinterface_i.c"
#include "mididevicemanagerinterface.h"
+#include "MidiEndpointProtocolManagerInterface_i.c"
+#include "MidiEndpointProtocolManagerInterface.h"
+
+
#include "dllmain.h"
+class CMidi2NetworkMidiEndpointManager;
+class CMidi2NetworkMidiConfigurationManager;
+
+#include "abstraction_defs.h"
+
+#include "MidiNetworkDeviceDefinition.h"
+#include "MidiNetworkDevice.h"
+#include "MidiNetworkDeviceTable.h"
+
+#include "AbstractionState.h"
+
#include "Midi2.NetworkMidiAbstraction.h"
#include "Midi2.NetworkMidiIn.h"
#include "Midi2.NetworkMidiOut.h"
#include "Midi2.NetworkMidiBiDi.h"
#include "Midi2.NetworkMidiEndpointManager.h"
+#include "Midi2.NetworkMidiConfigurationManager.h"
diff --git a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp
index c3382a44..f6fa9a71 100644
--- a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp
+++ b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiEndpointManager.cpp
@@ -306,7 +306,7 @@ CMidi2VirtualMidiEndpointManager::CreateClientVisibleEndpoint(
// we have to do this because they end up cached by PNP and come back
// when you recreate a device with the same Id. This is a real problem
// if you are testing function blocks or endpoint properties with this
- // loopback transport.
+ // transport.
m_MidiDeviceManager->DeleteAllEndpointInProtocolDiscoveredProperties(newDeviceInterfaceId);
// we need this for removal later
@@ -314,8 +314,10 @@ CMidi2VirtualMidiEndpointManager::CreateClientVisibleEndpoint(
entry.CreatedClientEndpointId = internal::NormalizeEndpointInterfaceIdWStringCopy(newDeviceInterfaceId);
- //MidiEndpointTable::Current().AddCreatedEndpointDevice(entry);
- //MidiEndpointTable::Current().AddCreatedClient(entry.VirtualEndpointAssociationId, entry.CreatedClientEndpointId);
+ // time to do protocol negotiation, request endpoint metadata, function blocks, etc.
+
+ LOG_IF_FAILED(NegotiateAndRequestMetadata(newDeviceInterfaceId));
+
return S_OK;
}
diff --git a/src/api/Service/Exe/MidiEndpointProtocolManager.cpp b/src/api/Service/Exe/MidiEndpointProtocolManager.cpp
index 855af698..c3ea687a 100644
--- a/src/api/Service/Exe/MidiEndpointProtocolManager.cpp
+++ b/src/api/Service/Exe/MidiEndpointProtocolManager.cpp
@@ -100,24 +100,20 @@ CMidiEndpointProtocolManager::NegotiateAndRequestMetadata(
TraceLoggingWideString(DeviceInterfaceId)
);
-
- OutputDebugString(__FUNCTION__ L"");
-
ProtocolManagerWork work;
// DEBUG
//TimeoutMS = 25000;
-
-
work.EndpointInstanceId = DeviceInterfaceId;
work.PreferToSendJRTimestampsToEndpoint = PreferToSendJRTimestampsToEndpoint;
work.PreferToReceiveJRTimestampsFromEndpoint = PreferToReceiveJRTimestampsFromEndpoint;
work.PreferredMidiProtocol = PreferredMidiProtocol;
work.TimeoutMS = TimeoutMS;
+ m_queueMutex.lock();
m_workQueue.push(std::move(work));
-
+ m_queueMutex.unlock();
// todo: signal event that there's new work
m_queueWorkerThreadWakeup.SetEvent();
@@ -152,6 +148,9 @@ CMidiEndpointProtocolManager::Cleanup()
m_shutdown = true;
m_queueWorkerThreadWakeup.SetEvent();
+ if (m_queueWorkerThread.joinable())
+ m_queueWorkerThread.join();
+
return S_OK;
}
@@ -503,17 +502,10 @@ void CMidiEndpointProtocolManager::ThreadWorker()
{
if (m_workQueue.size() > 0)
{
- {
- OutputDebugString(__FUNCTION__ L" - Item in queue. About to lock and pop :)");
-
- std::lock_guard lock{ m_queueMutex };
-
- m_currentWorkItem = std::move(m_workQueue.front());
- m_workQueue.pop();
-
- OutputDebugString(__FUNCTION__ L" - Obtained item from queue. About to process");
-
- }
+ m_queueMutex.lock();
+ m_currentWorkItem = std::move(m_workQueue.front());
+ m_workQueue.pop();
+ m_queueMutex.unlock();
// this will block until completed
LOG_IF_FAILED(ProcessCurrentWorkEntry());
diff --git a/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp b/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp
index a0d9940c..b171afb0 100644
--- a/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp
+++ b/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp
@@ -6,15 +6,11 @@
// Further information: https://github.com/microsoft/MIDI/
// ============================================================================
-
#include "pch.h"
-
DEVPROPKEY FunctionBlockPropertyKeyFromNumber(_In_ uint8_t functionBlockNumber)
{
- OutputDebugString(L"" __FUNCTION__);
-
DEVPROPKEY key{};
GUID propertyId{};
@@ -36,8 +32,6 @@ DEVPROPKEY FunctionBlockPropertyKeyFromNumber(_In_ uint8_t functionBlockNumber)
DEVPROPKEY FunctionBlockNamePropertyKeyFromNumber(_In_ uint8_t functionBlockNumber)
{
- OutputDebugString(L"" __FUNCTION__);
-
DEVPROPKEY key{};
GUID propertyId{};
@@ -73,10 +67,6 @@ CMidi2EndpointMetadataListenerMidiTransform::Initialize(
UNREFERENCED_PARAMETER(creationParams);
UNREFERENCED_PARAMETER(mmcssTaskId);
-
- OutputDebugString(L"" __FUNCTION__ " Start up, Provided device id:");
- OutputDebugString(deviceId);
-
TraceLoggingWrite(
MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
__FUNCTION__,
@@ -94,6 +84,20 @@ CMidi2EndpointMetadataListenerMidiTransform::Initialize(
m_deviceInstanceId = deviceId;
m_context = context;
+
+ std::thread workerThread(
+ &CMidi2EndpointMetadataListenerMidiTransform::QueueWorker,
+ this);
+
+ // TODO: may need to set thread priority
+ //SetThreadPriority(workerThread.native_handle(), ... );
+
+ m_queueWorkerThread = std::move(workerThread);
+
+ // start up the worker thread
+ m_queueWorkerThread.detach();
+
+
return S_OK;
}
@@ -107,19 +111,69 @@ CMidi2EndpointMetadataListenerMidiTransform::Cleanup()
TraceLoggingPointer(this, "this")
);
- try
- {
- OutputDebugString(L"" __FUNCTION__ " Shut down time");
+ m_continueProcessing = false;
+ m_messageProcessorWakeup.SetEvent();
- return S_OK;
- }
- catch (...)
+ // join the worker thread and wait for it to end
+ if (m_queueWorkerThread.joinable())
+ m_queueWorkerThread.join();
+
+
+
+ return S_OK;
+
+}
+
+
+#define MIDI_ENDPOINT_METADATA_LISTENER_THREAD_SLEEP_DURATION_MS 2000
+
+void CMidi2EndpointMetadataListenerMidiTransform::QueueWorker()
+{
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Entering Worker", "message")
+ );
+
+ while (m_continueProcessing)
{
- OutputDebugString(L"" __FUNCTION__ " Exception cleaning up");
+ // sleep if the queue is empty
+ if (m_workQueue.size() == 0)
+ {
+ // we're in a loop. Before sleeping, check to
+ // see if we're signaled from before
+ if (m_messageProcessorWakeup.is_signaled())
+ {
+ m_messageProcessorWakeup.ResetEvent();
+ }
+
+ m_messageProcessorWakeup.wait(MIDI_ENDPOINT_METADATA_LISTENER_THREAD_SLEEP_DURATION_MS);
+ }
+
+ // actually process any data we have
+ while (m_continueProcessing && m_workQueue.size() > 0)
+ {
+ m_queueMutex.lock();
+
+ auto ump = m_workQueue.front();
+ m_workQueue.pop();
+
+ m_queueMutex.unlock();
+
+ ProcessStreamMessage(ump);
+ }
- // TODO: Log
- return S_OK; // we don't care when cleaning up
}
+
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this"),
+ TraceLoggingWideString(L"Exiting Worker", "message")
+ );
}
@@ -133,8 +187,6 @@ CMidi2EndpointMetadataListenerMidiTransform::SendMidiMessage(
UINT size,
LONGLONG timestamp)
{
- // OutputDebugString(L"" __FUNCTION__);
-
// TODO: This really should have a worker thread doing the message processing
// because we don't want to add any latency or jitter here. This prototype will
// be all in-line, however.
@@ -147,7 +199,10 @@ CMidi2EndpointMetadataListenerMidiTransform::SendMidiMessage(
m_callback->Callback(data, size, timestamp, m_context);
}
- if (data != nullptr && size == UMP128_BYTE_COUNT)
+
+ // now that we're back from the callback, handle the message
+
+ if (size == UMP128_BYTE_COUNT)
{
internal::PackedUmp128 ump;
@@ -157,11 +212,9 @@ 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);
+ m_queueMutex.lock();
+ m_workQueue.push(ump);
+ m_queueMutex.unlock();
}
else
{
@@ -170,13 +223,13 @@ CMidi2EndpointMetadataListenerMidiTransform::SendMidiMessage(
}
else
{
- // couldn't fill the UMP. Shouldn't happen since we pre-validate
- return E_FAIL;
+ // couldn't fill the UMP. Shouldn't happen since we pre-validate. Just ignore.
+ //return E_FAIL;
}
}
else
{
- // Either null (hopefully not) or not a UMP128 so can't be a stream message. Fall out quickly
+ // Not a UMP128 so can't be a stream message. Fall out quickly
}
return S_OK;
@@ -187,9 +240,16 @@ CMidi2EndpointMetadataListenerMidiTransform::SendMidiMessage(
HRESULT
CMidi2EndpointMetadataListenerMidiTransform::UpdateEndpointNameProperty()
{
- OutputDebugString(L"\n" __FUNCTION__ " endpoint name is: ");
- OutputDebugString(m_endpointName.c_str());
- OutputDebugString(L"\n");
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ //OutputDebugString(L"\n" __FUNCTION__ " endpoint name is: ");
+ //OutputDebugString(m_endpointName.c_str());
+ //OutputDebugString(L"\n");
auto cleanedValue{ internal::TrimmedWStringCopy(m_endpointName) + L"\0" };
@@ -210,9 +270,17 @@ CMidi2EndpointMetadataListenerMidiTransform::UpdateEndpointNameProperty()
HRESULT
CMidi2EndpointMetadataListenerMidiTransform::UpdateEndpointProductInstanceIdProperty()
{
- OutputDebugString(L"\n" __FUNCTION__ " product instance id is: '");
- OutputDebugString(m_productInstanceId.c_str());
- OutputDebugString(L"'\n");
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+
+ //OutputDebugString(L"\n" __FUNCTION__ " product instance id is: '");
+ //OutputDebugString(m_productInstanceId.c_str());
+ //OutputDebugString(L"'\n");
std::wstring cleanedValue{ internal::TrimmedWStringCopy(m_productInstanceId) + L"\0" };
@@ -234,9 +302,16 @@ _Use_decl_annotations_
HRESULT
CMidi2EndpointMetadataListenerMidiTransform::UpdateFunctionBlockNameProperty(uint8_t functionBlockNumber, std::wstring name)
{
- OutputDebugString(L"\n" __FUNCTION__ " function block name is: ");
- OutputDebugString(name.c_str());
- OutputDebugString(L"\n");
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
+ //OutputDebugString(L"\n" __FUNCTION__ " function block name is: ");
+ //OutputDebugString(name.c_str());
+ //OutputDebugString(L"\n");
std::wstring cleanedValue{ internal::TrimmedWStringCopy(name) + L"\0" };
@@ -258,7 +333,12 @@ _Use_decl_annotations_
HRESULT
CMidi2EndpointMetadataListenerMidiTransform::UpdateStreamConfigurationProperties(internal::PackedUmp128& endpointStreamConfigurationNotificationMessage)
{
- OutputDebugString(L"\n" __FUNCTION__);
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
BYTE configuredProtocol = MIDIWORDBYTE3(endpointStreamConfigurationNotificationMessage.word0);
@@ -293,7 +373,13 @@ _Use_decl_annotations_
HRESULT
CMidi2EndpointMetadataListenerMidiTransform::UpdateDeviceIdentityProperty(internal::PackedUmp128& identityMessage)
{
- OutputDebugString(L"\n" __FUNCTION__);
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
MidiDeviceIdentityProperty prop;
@@ -334,7 +420,12 @@ _Use_decl_annotations_
HRESULT
CMidi2EndpointMetadataListenerMidiTransform::UpdateEndpointInfoProperties(internal::PackedUmp128& endpointInfoNotificationMessage)
{
- OutputDebugString(L"\n" __FUNCTION__);
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
BYTE umpVersionMajor = MIDIWORDBYTE3(endpointInfoNotificationMessage.word0);
BYTE umpVersionMinor = MIDIWORDBYTE4(endpointInfoNotificationMessage.word0);
@@ -389,7 +480,12 @@ _Use_decl_annotations_
HRESULT
CMidi2EndpointMetadataListenerMidiTransform::UpdateFunctionBlockProperty(internal::PackedUmp128& functionBlockInfoNotificationMessage)
{
- OutputDebugString(L"" __FUNCTION__);
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
MidiFunctionBlockProperty prop;
@@ -425,11 +521,14 @@ CMidi2EndpointMetadataListenerMidiTransform::UpdateFunctionBlockProperty(interna
_Use_decl_annotations_
HRESULT
-CMidi2EndpointMetadataListenerMidiTransform::ProcessStreamMessage(internal::PackedUmp128 ump, LONGLONG timestamp)
+CMidi2EndpointMetadataListenerMidiTransform::ProcessStreamMessage(internal::PackedUmp128 ump)
{
- OutputDebugString(L"\n" __FUNCTION__);
-
- UNREFERENCED_PARAMETER(timestamp);
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
auto messageStatus = internal::GetStatusFromStreamMessageFirstWord(ump.word0);
@@ -473,7 +572,12 @@ CMidi2EndpointMetadataListenerMidiTransform::ProcessStreamMessage(internal::Pack
std::wstring ParseStreamTextMessage(_In_ internal::PackedUmp128& message)
{
- OutputDebugString(L"\n" __FUNCTION__);
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO)
+ );
+
// check the status to know which byte is first to be grabbed
@@ -532,7 +636,13 @@ _Use_decl_annotations_
HRESULT
CMidi2EndpointMetadataListenerMidiTransform::HandleFunctionBlockNameMessage(internal::PackedUmp128& functionBlockNameMessage)
{
- OutputDebugString(L"\n" __FUNCTION__);
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
+
uint8_t functionBlockNumber = MIDIWORDBYTE3(functionBlockNameMessage.word0);
@@ -591,7 +701,12 @@ _Use_decl_annotations_
HRESULT
CMidi2EndpointMetadataListenerMidiTransform::HandleEndpointNameMessage(internal::PackedUmp128& endpointNameMessage)
{
- OutputDebugString(L"\n" __FUNCTION__);
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
switch (internal::GetFormFromStreamMessageFirstWord(endpointNameMessage.word0))
{
@@ -642,7 +757,12 @@ _Use_decl_annotations_
HRESULT
CMidi2EndpointMetadataListenerMidiTransform::HandleProductInstanceIdMessage(internal::PackedUmp128& productInstanceIdMessage)
{
- OutputDebugString(L"\n" __FUNCTION__);
+ TraceLoggingWrite(
+ MidiEndpointMetadataListenerTransformTelemetryProvider::Provider(),
+ __FUNCTION__,
+ TraceLoggingLevel(WINEVENT_LEVEL_INFO),
+ TraceLoggingPointer(this, "this")
+ );
switch (internal::GetFormFromStreamMessageFirstWord(productInstanceIdMessage.word0))
{
diff --git a/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.h b/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.h
index 9296eaff..388cb6f6 100644
--- a/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.h
+++ b/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.h
@@ -34,7 +34,7 @@ class CMidi2EndpointMetadataListenerMidiTransform :
HRESULT HandleEndpointNameMessage(_In_ internal::PackedUmp128& endpointNameMessage);
HRESULT HandleProductInstanceIdMessage(_In_ internal::PackedUmp128& productInstanceIdMessage);
- HRESULT ProcessStreamMessage(_In_ internal::PackedUmp128 ump, _In_ LONGLONG timestamp);
+ HRESULT ProcessStreamMessage(_In_ internal::PackedUmp128 ump);
IMidiCallback* m_callback{ nullptr };
@@ -48,5 +48,19 @@ class CMidi2EndpointMetadataListenerMidiTransform :
std::wstring m_endpointName{};
std::wstring m_productInstanceId{};
std::map m_functionBlockNames;
+
+
+ // Work Queue. We use a queue instead of processing in
+ // the callback because the latter can result in an infinite
+ // loop of message sending and processing.
+
+ std::atomic m_continueProcessing{ true };
+ wil::slim_event_manual_reset m_messageProcessorWakeup;
+ std::queue m_workQueue;
+ std::mutex m_queueMutex;
+ std::thread m_queueWorkerThread;
+
+ void QueueWorker();
+
};
diff --git a/src/api/Transform/EndpointMetadataListenerTransform/pch.h b/src/api/Transform/EndpointMetadataListenerTransform/pch.h
index 22dfed73..5a90074c 100644
--- a/src/api/Transform/EndpointMetadataListenerTransform/pch.h
+++ b/src/api/Transform/EndpointMetadataListenerTransform/pch.h
@@ -7,6 +7,7 @@
#endif
#include
+#include
#include
diff --git a/src/api/Transform/SchedulerTransform/Midi2.SchedulerMidiTransform.cpp b/src/api/Transform/SchedulerTransform/Midi2.SchedulerMidiTransform.cpp
index 1031cd80..816df0ab 100644
--- a/src/api/Transform/SchedulerTransform/Midi2.SchedulerMidiTransform.cpp
+++ b/src/api/Transform/SchedulerTransform/Midi2.SchedulerMidiTransform.cpp
@@ -33,9 +33,6 @@ CMidi2SchedulerMidiTransform::Initialize(
UNREFERENCED_PARAMETER(creationParams);
UNREFERENCED_PARAMETER(mmcssTaskId);
-
- OutputDebugString(L"" __FUNCTION__ " Start up");
-
TraceLoggingWrite(
MidiSchedulerTransformTelemetryProvider::Provider(),
__FUNCTION__,
@@ -80,9 +77,6 @@ CMidi2SchedulerMidiTransform::Cleanup()
try
{
- OutputDebugString(L"" __FUNCTION__ " Scheduler shut down time");
- OutputDebugString((std::wstring(L"" __FUNCTION__ " Abandoned queue size is: ") + std::to_wstring(m_messageQueue.size())).c_str());
-
// tell the thread to quit. Call SetEvent in case it is in a wait
m_continueProcessing = false;