From e50b3d0bbed5af38763658c7dc3bf595932d73f2 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Sat, 10 Feb 2024 02:02:50 -0500 Subject: [PATCH] Beginnings of the Loopback endpoint --- build/staging/version/BundleInfo.wxi | 2 +- .../AbstractionState.h | 10 +- .../Midi2.LoopbackMidiAbstraction.vcxproj | 3 + ...i2.LoopbackMidiAbstraction.vcxproj.filters | 9 ++ .../Midi2.LoopbackMidiBidi.cpp | 134 ++++++++---------- .../Midi2.LoopbackMidiBidi.h | 23 ++- .../Midi2.LoopbackMidiEndpointManager.cpp | 34 ++++- .../Midi2.LoopbackMidiEndpointManager.h | 5 +- .../MidiLoopbackDevice.h | 73 ++++++++++ .../MidiLoopbackDeviceDefinition.h | 26 ++++ .../MidiLoopbackDeviceTable.h | 38 +++++ .../abstraction_defs.h | 10 +- .../Abstraction/LoopbackMidiAbstraction/pch.h | 3 + 13 files changed, 279 insertions(+), 91 deletions(-) create mode 100644 src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDevice.h create mode 100644 src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDeviceDefinition.h create mode 100644 src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDeviceTable.h diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index 163fe143..7367411c 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,4 +1,4 @@ - + diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/AbstractionState.h b/src/api/Abstraction/LoopbackMidiAbstraction/AbstractionState.h index d8dd0920..78d89dd2 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/AbstractionState.h +++ b/src/api/Abstraction/LoopbackMidiAbstraction/AbstractionState.h @@ -31,10 +31,10 @@ class AbstractionState return m_configurationManager; } - //std::shared_ptr GetEndpointTable() - //{ - // return m_endpointTable; - //} + std::shared_ptr GetEndpointTable() + { + return m_endpointTable; + } HRESULT Cleanup() @@ -58,5 +58,5 @@ class 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/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.vcxproj b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.vcxproj index 1df24c13..272cd0d3 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.vcxproj +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.vcxproj @@ -298,6 +298,9 @@ + + + diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.vcxproj.filters b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.vcxproj.filters index 3538f7a2..918ade7e 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.vcxproj.filters +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.vcxproj.filters @@ -79,6 +79,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.cpp b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.cpp index d91f920f..b344db39 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.cpp +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.cpp @@ -31,66 +31,57 @@ CMidi2LoopbackMidiBiDi::Initialize( m_callbackContext = Context; m_endpointId = internal::NormalizeEndpointInterfaceIdWStringCopy(endpointId); - //if (Context != MIDI_PROTOCOL_MANAGER_ENDPOINT_CREATION_CONTEXT) - { - HRESULT hr = S_OK; + HRESULT hr = S_OK; - // TODO: This should use SWD properties and not a string search + // TODO: This should use SWD properties and not a string search - if (internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_VIRT_INSTANCE_ID_DEVICE_PREFIX)) - { - TraceLoggingWrite( - MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), - __FUNCTION__, - TraceLoggingLevel(WINEVENT_LEVEL_INFO), - TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Initializing device-side BiDi", "message"), - TraceLoggingWideString(m_endpointId.c_str(), "endpoint id") - ); + if (internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_LOOP_INSTANCE_ID_A_PREFIX)) + { + TraceLoggingWrite( + MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Initializing Side-A BiDi", "message"), + TraceLoggingWideString(m_endpointId.c_str(), "endpoint id") + ); - m_callback = Callback; + m_callback = Callback; - // LOG_IF_FAILED(hr = AbstractionState::Current().GetEndpointTable()->OnDeviceConnected(m_endpointId, this)); - } - else if (internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_VIRT_INSTANCE_ID_CLIENT_PREFIX)) - { - TraceLoggingWrite( - MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), - __FUNCTION__, - TraceLoggingLevel(WINEVENT_LEVEL_INFO), - TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Initializing client-side BiDi", "message"), - TraceLoggingWideString(m_endpointId.c_str(), "endpoint id") - ); + m_isEndpointA = true; + } + else if (internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_LOOP_INSTANCE_ID_B_PREFIX)) + { + TraceLoggingWrite( + MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Initializing Side-B BiDi", "message"), + TraceLoggingWideString(m_endpointId.c_str(), "endpoint id") + ); - m_callback = Callback; + m_callback = Callback; - // LOG_IF_FAILED(hr = AbstractionState::Current().GetEndpointTable()->OnClientConnected(m_endpointId, this)); - } - else - { - TraceLoggingWrite( - MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), - __FUNCTION__, - TraceLoggingLevel(WINEVENT_LEVEL_ERROR), - TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"We don't understand the endpoint Id", "message"), - TraceLoggingWideString(m_endpointId.c_str(), "endpoint id") - ); - - // we don't understand this endpoint id - - hr = E_FAIL; - } + m_isEndpointA = false; + } + else + { + // we don't understand this endpoint id + + TraceLoggingWrite( + MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"We don't understand the endpoint Id", "message"), + TraceLoggingWideString(m_endpointId.c_str(), "endpoint id") + ); - return hr; + hr = E_FAIL; } - //else - //{ - // // we're in protocol negotiation - // m_isDeviceSide = false; - // return S_OK; - //} + + return hr; } HRESULT @@ -105,7 +96,6 @@ CMidi2LoopbackMidiBiDi::Cleanup() ); m_callback = nullptr; - m_callbackContext = 0; return S_OK; } @@ -115,7 +105,7 @@ HRESULT CMidi2LoopbackMidiBiDi::SendMidiMessage( PVOID Message, UINT Size, - LONGLONG /*Position*/ + LONGLONG Position ) { TraceLoggingWrite( @@ -126,31 +116,31 @@ CMidi2LoopbackMidiBiDi::SendMidiMessage( TraceLoggingWideString(m_endpointId.c_str(), "endpoint id") ); - // message received from the device - RETURN_HR_IF_NULL(E_INVALIDARG, Message); RETURN_HR_IF(E_INVALIDARG, Size < sizeof(uint32_t)); - //for (auto connection : m_linkedBiDiConnections) - //{ - // auto cb = connection->GetCallback(); - // - // if (cb != nullptr) - // { - // return cb->Callback(Message, Size, Position, m_callbackContext); - // } - //} + if (m_isEndpointA) + { + // send endpoint A to B + auto device = AbstractionState::Current().GetEndpointTable()->GetDevice(m_associationId); + if (device) + { + return device->SendMessageAToB(Message, Size, Position, m_callbackContext); + } + } + else + { + // send endpoint B to A + auto device = AbstractionState::Current().GetEndpointTable()->GetDevice(m_associationId); - //// if there's no linked bidi, it's not a failure. We just lose the message - //if (m_linkedBiDiCallback != nullptr) - //{ - // //return m_linkedBiDi->SendMidiMessage(Message, Size, Position); - // return m_linkedBiDiCallback->Callback(Message, Size, Position, m_callbackContext); - //} + if (device) + { + return device->SendMessageBToA(Message, Size, Position, m_callbackContext); + } + } return S_OK; - } _Use_decl_annotations_ diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.h b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.h index 5ad12c6c..127086ee 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.h +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.h @@ -21,15 +21,30 @@ class CMidi2LoopbackMidiBiDi : STDMETHOD(Callback)(_In_ PVOID, _In_ UINT, _In_ LONGLONG, _In_ LONGLONG); STDMETHOD(Cleanup)(); + void AssociationId(_In_ std::wstring newId) { m_associationId = newId; } + + //void LinkAssociatedBiDi(_In_ wil::com_ptr_nothrow associatedBiDi) + //{ + // m_associatedBiDi = associatedBiDi; + + // LOG_IF_FAILED(associatedBiDi->QueryInterface(__uuidof(IMidiCallback), (void**)&m_associatedBiDiCallback)); + //} + + //void UnlinkAssociatedBiDi() + //{ + // m_associatedBiDi = nullptr; + // m_associatedBiDiCallback = nullptr; + //} + private: - //wil::com_ptr_nothrow m_linkedBiDi; + bool m_isEndpointA = false; -// std::vector> m_linkedBiDiConnections{}; + std::wstring m_associationId{}; + //wil::com_ptr_nothrow m_associatedBiDi; + //wil::com_ptr_nothrow m_associatedBiDiCallback; - //wil::com_ptr_nothrow m_linkedBiDiCallback; wil::com_ptr_nothrow m_callback; - LONGLONG m_callbackContext; std::wstring m_endpointId{}; diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiEndpointManager.cpp b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiEndpointManager.cpp index 670d6a13..a6fd6749 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiEndpointManager.cpp +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiEndpointManager.cpp @@ -52,7 +52,7 @@ CMidi2LoopbackMidiEndpointManager::Initialize( _Use_decl_annotations_ HRESULT -CMidi2LoopbackMidiEndpointManager::DeleteEndpointPair(std::wstring associationId) +CMidi2LoopbackMidiEndpointManager::DeleteEndpointPair(std::shared_ptr definition) { return S_OK; @@ -111,7 +111,7 @@ CMidi2LoopbackMidiEndpointManager::CreateParentDevice() _Use_decl_annotations_ HRESULT CMidi2LoopbackMidiEndpointManager::CreateEndpointPair( - std::wstring associationId + std::shared_ptr definition ) { TraceLoggingWrite( @@ -122,6 +122,36 @@ CMidi2LoopbackMidiEndpointManager::CreateEndpointPair( ); + // workflow: + // Configuration manager adds the entries to the endpoint table + // Then calls this function to create the pair of endpoints + // + // When endpoints BiDi are instantiated, they look at the endpoint + // table to see what they are supposed to connect to. + // + // They can be created in any order, so that will need to be + // checked for each created endpoint. + // + // Perhaps the table itself should just contain IMidiBiDi-like + // functions that are called by the endpoints? Then the endpoints + // don't need to know anything other than where to send the message. + // That would also be a pattern we can use for other types of + // routing. + // + // So perhaps a set of definitions associated by association id and + // then a related table of routing which has the required functions + // and pointers. Or maybe it all stays in the same definition class? + // and just rename it to MidiLoopbackEndpointDevice ? + + + + + + + + + + return S_OK; } diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiEndpointManager.h b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiEndpointManager.h index f8b23f05..b6b79a97 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiEndpointManager.h +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiEndpointManager.h @@ -25,8 +25,9 @@ class CMidi2LoopbackMidiEndpointManager : //)); - HRESULT CreateEndpointPair(_In_ std::wstring associationId); - HRESULT DeleteEndpointPair(_In_ std::wstring associationId); + HRESULT CreateEndpointPair(_In_ std::shared_ptr definition); + + HRESULT DeleteEndpointPair(_In_ std::shared_ptr definition); //HRESULT DeleteEndpointPair( diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDevice.h b/src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDevice.h new file mode 100644 index 00000000..6e56e990 --- /dev/null +++ b/src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDevice.h @@ -0,0 +1,73 @@ +// 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 + +// represents a loopback device. The device has exactly two +// endpoints which are cross-wired to each other + +class MidiLoopbackDevice +{ +public: + MidiLoopbackDeviceDefinition Definition; + + std::wstring EndpointAInterfaceId{}; + std::wstring EndpointBInterfaceId{}; + + void RegisterEndpointA(/*_In_ wil::com_ptr_nothrow endpoint,*/ _In_ wil::com_ptr_nothrow callback) + { + //m_bidiA = endpoint; + m_callbackA = callback; + } + + void RegisterEndpointB(/*_In_ wil::com_ptr_nothrow endpoint,*/ _In_ wil::com_ptr_nothrow callback) + { + //m_bidiB = endpoint; + m_callbackB = callback; + } + + HRESULT SendMessageAToB(_In_ PVOID message, _In_ UINT size, _In_ LONGLONG position, _In_ LONGLONG context) + { + if (m_callbackB != nullptr) + { + return m_callbackB->Callback(message, size, position, context); + } + + return S_OK; + } + + HRESULT SendMessageBToA(_In_ PVOID message, _In_ UINT size, _In_ LONGLONG position, _In_ LONGLONG context) + { + if (m_callbackA != nullptr) + { + return m_callbackA->Callback(message, size, position, context); + } + + return S_OK; + } + + + ~MidiLoopbackDevice() + { + //m_bidiA = nullptr; + //m_bidiB = nullptr; + + m_callbackA = nullptr; + m_callbackB = nullptr; + } + +private: + // these are needed to enable these two to find each other once opened + //wil::com_ptr_nothrow m_bidiA{ nullptr }; + //wil::com_ptr_nothrow m_bidiB{ nullptr }; + + wil::com_ptr_nothrow m_callbackA{ nullptr }; + wil::com_ptr_nothrow m_callbackB{ nullptr }; + +}; diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDeviceDefinition.h b/src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDeviceDefinition.h new file mode 100644 index 00000000..91308075 --- /dev/null +++ b/src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDeviceDefinition.h @@ -0,0 +1,26 @@ +// 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 + +// This information is provided by the configuration manager + +struct MidiLoopbackDeviceDefinition +{ + std::wstring AssociationId{}; + + std::wstring EndpointAName{}; + std::wstring EndpointADescription{}; + std::wstring EndpointAUniqueIdentifier{}; + + std::wstring EndpointBName{}; + std::wstring EndpointBDescription{}; + std::wstring EndpointBUniqueIdentifier{}; + +}; diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDeviceTable.h b/src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDeviceTable.h new file mode 100644 index 00000000..a8b67b28 --- /dev/null +++ b/src/api/Abstraction/LoopbackMidiAbstraction/MidiLoopbackDeviceTable.h @@ -0,0 +1,38 @@ +// 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 MidiEndpointTable +{ +public: + + MidiLoopbackDevice* GetDevice(std::wstring associationId) + { + if (m_devices.find(associationId) != m_devices.end()) + { + return &m_devices[associationId]; + } + else + { + return nullptr; + } + } + + void SetDevice(std::wstring associationId, MidiLoopbackDevice device) + { + m_devices[associationId] = device; + } + + +private: + std::map m_devices{}; + +}; diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h b/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h index 7e46c66e..abd67e07 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h +++ b/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h @@ -11,14 +11,14 @@ // the IDs here aren't the full Ids, just the values we start with // The full Id comes back from the swdevicecreate callback -#define TRANSPORT_MNEMONIC L"APP" -#define MIDI_VIRT_INSTANCE_ID_DEVICE_PREFIX L"MIDIU_APPDEV_" -#define MIDI_VIRT_INSTANCE_ID_CLIENT_PREFIX L"MIDIU_APPPUB_" +#define TRANSPORT_MNEMONIC L"LOOP" +#define MIDI_LOOP_INSTANCE_ID_A_PREFIX L"MIDIU_LOOP_A_" +#define MIDI_LOOP_INSTANCE_ID_B_PREFIX L"MIDIU_LOOP_B_" // TODO: Names should be moved to .rc for localization -#define TRANSPORT_PARENT_ID L"MIDIU_APP_TRANSPORT" -#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 2.0 App Devices" +#define TRANSPORT_PARENT_ID L"MIDIU_LOOP_TRANSPORT" +#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 2.0 Loopback Devices" #define LOOPBACK_PARENT_ROOT L"HTREE\\ROOT\\0" diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/pch.h b/src/api/Abstraction/LoopbackMidiAbstraction/pch.h index c4916351..e22b6c3c 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/pch.h +++ b/src/api/Abstraction/LoopbackMidiAbstraction/pch.h @@ -91,6 +91,9 @@ class CMidi2LoopbackMidiEndpointManager; class CMidi2LoopbackMidiBiDi; class AbstractionState; +#include "MidiLoopbackDeviceDefinition.h" +#include "MidiLoopbackDevice.h" +#include "MidiLoopbackDeviceTable.h" #include "Midi2.LoopbackMidiAbstraction.h" #include "Midi2.LoopbackMidiBiDi.h"