From 6f57071437277973768a6731ee11652320c4c932 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Wed, 14 Feb 2024 01:36:26 -0500 Subject: [PATCH 1/2] API message building updates --- build/staging/version/BundleInfo.wxi | 2 +- .../api-client-basics.vcxproj | 16 +- .../api-client-basics/packages.config | 4 +- .../api-loopback-endpoints.vcxproj | 8 +- .../api-loopback-endpoints/packages.config | 2 +- .../MidiSample.AppToAppMidi.csproj | 2 +- .../Midi2.BluetoothMidiAbstraction.vcxproj | 2 + ...2.BluetoothMidiAbstraction.vcxproj.filters | 6 + ...idi2.BluetoothMidiConfigurationManager.cpp | 69 ++++++++ .../Midi2.BluetoothMidiConfigurationManager.h | 27 ++++ .../BleMidiAbstraction/abstraction_defs.h | 2 +- src/api/Abstraction/BleMidiAbstraction/pch.h | 3 + .../abstraction_defs.h | 2 +- .../NetworkMidiAbstraction/abstraction_defs.h | 2 +- .../nuget/Windows.Devices.Midi2.nuspec | 2 +- .../Midi2Client/IMidiUniversalPacket.idl | 2 + .../Midi2Client/MidiEndpointConnection.cpp | 54 ++++++- .../Midi2Client/MidiEndpointConnection.h | 4 + .../Midi2Client/MidiEndpointConnection.idl | 7 +- .../Client/Midi2Client/MidiFunctionBlock.cpp | 34 ++-- .../Client/Midi2Client/MidiFunctionBlock.h | 2 +- src/api/Client/Midi2Client/MidiMessage128.cpp | 25 +++ src/api/Client/Midi2Client/MidiMessage128.h | 4 + src/api/Client/Midi2Client/MidiMessage32.cpp | 19 +++ src/api/Client/Midi2Client/MidiMessage32.h | 4 + src/api/Client/Midi2Client/MidiMessage64.cpp | 22 +++ src/api/Client/Midi2Client/MidiMessage64.h | 2 + src/api/Client/Midi2Client/MidiMessage96.cpp | 21 +++ src/api/Client/Midi2Client/MidiMessage96.h | 3 + .../Client/Midi2Client/MidiMessageUtility.cpp | 103 ++++++++++++ .../Client/Midi2Client/MidiMessageUtility.h | 3 + .../Client/Midi2Client/MidiMessageUtility.idl | 5 + .../Midi2Client/MidiStreamMessageBuilder.cpp | 149 ++++++++++-------- .../Midi2Client/MidiStreamMessageBuilder.h | 28 ++-- .../Midi2Client/MidiStreamMessageBuilder.idl | 26 +-- .../Midi2Client/MidiVirtualEndpointDevice.cpp | 30 ++-- .../Midi2Client/MidiVirtualEndpointDevice.h | 10 ++ src/api/Client/Midi2Client/pch.h | 6 +- .../MidiFunctionBlockMessageBuilderTests.cpp | 37 +++-- .../MidiStreamMessageBuilderTests.cpp | 49 +++--- .../Midi/EndpointMessageSender.cs | 27 +--- src/user-tools/midi-console/Midi/Midi.csproj | 2 +- 42 files changed, 620 insertions(+), 207 deletions(-) create mode 100644 src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiConfigurationManager.cpp create mode 100644 src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiConfigurationManager.h diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index d56e36eb8..8e3aca7f4 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,4 +1,4 @@ - + diff --git a/samples/cpp-winrt/api-client-basics/api-client-basics.vcxproj b/samples/cpp-winrt/api-client-basics/api-client-basics.vcxproj index f35367ce2..725f6c2c8 100644 --- a/samples/cpp-winrt/api-client-basics/api-client-basics.vcxproj +++ b/samples/cpp-winrt/api-client-basics/api-client-basics.vcxproj @@ -1,7 +1,7 @@ - - + + true true @@ -149,16 +149,16 @@ - - + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + + + + \ No newline at end of file diff --git a/samples/cpp-winrt/api-client-basics/packages.config b/samples/cpp-winrt/api-client-basics/packages.config index 4ec483f0e..c9c963b46 100644 --- a/samples/cpp-winrt/api-client-basics/packages.config +++ b/samples/cpp-winrt/api-client-basics/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/samples/cpp-winrt/api-loopback-endpoints/api-loopback-endpoints.vcxproj b/samples/cpp-winrt/api-loopback-endpoints/api-loopback-endpoints.vcxproj index 76931efa3..e6d183080 100644 --- a/samples/cpp-winrt/api-loopback-endpoints/api-loopback-endpoints.vcxproj +++ b/samples/cpp-winrt/api-loopback-endpoints/api-loopback-endpoints.vcxproj @@ -1,6 +1,6 @@ - + true @@ -112,7 +112,7 @@ - + @@ -120,7 +120,7 @@ - - + + \ No newline at end of file diff --git a/samples/cpp-winrt/api-loopback-endpoints/packages.config b/samples/cpp-winrt/api-loopback-endpoints/packages.config index 73e02ec8b..c9c963b46 100644 --- a/samples/cpp-winrt/api-loopback-endpoints/packages.config +++ b/samples/cpp-winrt/api-loopback-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj b/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj index 9438a45d2..7bd3f49f4 100644 --- a/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj +++ b/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj @@ -29,7 +29,7 @@ - + diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj index 9676a8081..f8153d2c5 100644 --- a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj +++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj @@ -276,6 +276,7 @@ + @@ -293,6 +294,7 @@ + diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj.filters b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj.filters index 2e73914af..6edcb0aef 100644 --- a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj.filters +++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiAbstraction.vcxproj.filters @@ -33,6 +33,9 @@ Source Files + + Source Files + @@ -73,6 +76,9 @@ Header Files + + Header Files + diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiConfigurationManager.cpp b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiConfigurationManager.cpp new file mode 100644 index 000000000..235a6a67b --- /dev/null +++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiConfigurationManager.cpp @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#pragma once + +#include "pch.h" + +_Use_decl_annotations_ +HRESULT +CMidi2BluetoothMidiConfigurationManager::Initialize( + GUID AbstractionId, + IUnknown* MidiDeviceManager) +{ + TraceLoggingWrite( + MidiBluetoothMidiAbstractionTelemetryProvider::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 +CMidi2BluetoothMidiConfigurationManager::UpdateConfiguration( + LPCWSTR ConfigurationJsonSection, + BOOL IsFromConfigurationFile, + BSTR* Response) +{ + TraceLoggingWrite( + MidiBluetoothMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this") + ); + + + UNREFERENCED_PARAMETER(ConfigurationJsonSection); + UNREFERENCED_PARAMETER(IsFromConfigurationFile); + UNREFERENCED_PARAMETER(Response); + + return E_FAIL; +} + +_Use_decl_annotations_ +HRESULT +CMidi2BluetoothMidiConfigurationManager::Cleanup() +{ + TraceLoggingWrite( + MidiBluetoothMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this") + ); + + + return S_OK; +} \ No newline at end of file diff --git a/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiConfigurationManager.h b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiConfigurationManager.h new file mode 100644 index 000000000..6198caee1 --- /dev/null +++ b/src/api/Abstraction/BleMidiAbstraction/Midi2.BluetoothMidiConfigurationManager.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 CMidi2BluetoothMidiConfigurationManager : + 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/BleMidiAbstraction/abstraction_defs.h b/src/api/Abstraction/BleMidiAbstraction/abstraction_defs.h index 696be2497..22e883256 100644 --- a/src/api/Abstraction/BleMidiAbstraction/abstraction_defs.h +++ b/src/api/Abstraction/BleMidiAbstraction/abstraction_defs.h @@ -16,7 +16,7 @@ // TODO: Names should be moved to .rc for localization #define TRANSPORT_PARENT_ID L"MIDIU_BLE1_TRANSPORT" -#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 1.0 Bluetooth Endpoints" +#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 1.0 Bluetooth Devices" #define LOOPBACK_PARENT_ROOT L"HTREE\\ROOT\\0" diff --git a/src/api/Abstraction/BleMidiAbstraction/pch.h b/src/api/Abstraction/BleMidiAbstraction/pch.h index 92fc32a4a..b00e6edb2 100644 --- a/src/api/Abstraction/BleMidiAbstraction/pch.h +++ b/src/api/Abstraction/BleMidiAbstraction/pch.h @@ -80,6 +80,8 @@ namespace json = ::winrt::Windows::Data::Json; namespace internal = ::Windows::Devices::Midi2::Internal; +class CMidi2BluetoothMidiConfigurationManager; +class CMidi2BluetoothMidiEndpointManager; #include "strsafe.h" @@ -98,4 +100,5 @@ namespace internal = ::Windows::Devices::Midi2::Internal; #include "Midi2.BluetoothMidiAbstraction.h" #include "Midi2.BluetoothMidiBiDi.h" #include "Midi2.BluetoothMidiEndpointManager.h" +#include "Midi2.BluetoothMidiConfigurationManager.h" diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h b/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h index 35cdd38f4..105077eb5 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h +++ b/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h @@ -23,7 +23,7 @@ // TODO: Names should be moved to .rc for localization #define TRANSPORT_PARENT_ID L"MIDIU_LOOP_TRANSPORT" -#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 2.0 Loopback Devices" +#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 2.0 Loop Devices" #define LOOPBACK_PARENT_ROOT L"HTREE\\ROOT\\0" diff --git a/src/api/Abstraction/NetworkMidiAbstraction/abstraction_defs.h b/src/api/Abstraction/NetworkMidiAbstraction/abstraction_defs.h index df19a113c..7fafb4bdd 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"Network MIDI 2.0 (UDP)" +#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 2.0 Network (UDP) Devices" #define LOOPBACK_PARENT_ROOT L"HTREE\\ROOT\\0" 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 7d6fc6bfd..af8f39557 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-0156 + 1.0.0-preview.3-0158 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/IMidiUniversalPacket.idl b/src/api/Client/Midi2Client/IMidiUniversalPacket.idl index ae6646844..6576ed7b9 100644 --- a/src/api/Client/Midi2Client/IMidiUniversalPacket.idl +++ b/src/api/Client/Midi2Client/IMidiUniversalPacket.idl @@ -27,5 +27,7 @@ namespace Windows.Devices.Midi2 UInt32 PeekFirstWord(); + IVectorView GetAllWords(); + UInt8 AppendAllWordsToVector(IVector targetVector); }; } \ No newline at end of file diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection.cpp b/src/api/Client/Midi2Client/MidiEndpointConnection.cpp index 10984eeb5..06de62ae6 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection.cpp +++ b/src/api/Client/Midi2Client/MidiEndpointConnection.cpp @@ -876,7 +876,7 @@ namespace winrt::Windows::Devices::Midi2::implementation // TODO: Implement SendMessagesWordList - return midi2::MidiSendMessageResult::Succeeded; + return midi2::MidiSendMessageResult::Failed | midi2::MidiSendMessageResult::Other; } @@ -892,13 +892,63 @@ namespace winrt::Windows::Devices::Midi2::implementation // TODO: Implement SendMessagesWordArray - return midi2::MidiSendMessageResult::Succeeded; + return midi2::MidiSendMessageResult::Failed | midi2::MidiSendMessageResult::Other; } + _Use_decl_annotations_ + midi2::MidiSendMessageResult MidiEndpointConnection::SendMessagePacketList( + collections::IVectorView const& messages) noexcept + { + internal::LogInfo(__FUNCTION__, L"Sending message packet list"); + + try + { + if (!m_isOpen) + { + internal::LogGeneralError(__FUNCTION__, L"Endpoint is not open. Did you forget to call Open()?"); + + // return failure if we're not open + return midi2::MidiSendMessageResult::Failed | midi2::MidiSendMessageResult::EndpointConnectionClosedOrInvalid; + } + + if (m_endpointAbstraction) + { + // right now, we just loop through and send messages. In the future, + // we may optimize this further without changing the API signature + + for (auto const& ump : messages) + { + auto result = SendUmpInternal(m_endpointAbstraction, ump); + + if (!MidiEndpointConnection::SendMessageSucceeded(result)) + { + // if any fail, we return immediately. + return result; + } + } + + return midi2::MidiSendMessageResult::Succeeded; + } + else + { + internal::LogGeneralError(__FUNCTION__, L"Endpoint is nullptr"); + + return midi2::MidiSendMessageResult::Failed | midi2::MidiSendMessageResult::EndpointConnectionClosedOrInvalid; + } + } + catch (winrt::hresult_error const& ex) + { + internal::LogHresultError(__FUNCTION__, L"hresult exception sending messages. Service may be unavailable", ex); + + + // todo: handle buffer full and similar messages + return midi2::MidiSendMessageResult::Failed | midi2::MidiSendMessageResult::Other; + } + } diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection.h b/src/api/Client/Midi2Client/MidiEndpointConnection.h index 9d5d63c25..d3a97311c 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection.h +++ b/src/api/Client/Midi2Client/MidiEndpointConnection.h @@ -108,6 +108,10 @@ namespace winrt::Windows::Devices::Midi2::implementation _In_ winrt::array_view words) noexcept; + midi2::MidiSendMessageResult SendMessagePacketList( + _In_ collections::IVectorView const& messages) noexcept; + + _Success_(return == true) bool Open(); diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection.idl b/src/api/Client/Midi2Client/MidiEndpointConnection.idl index 1ea1e4bad..1457c8a55 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection.idl +++ b/src/api/Client/Midi2Client/MidiEndpointConnection.idl @@ -81,11 +81,16 @@ namespace Windows.Devices.Midi2 MidiSendMessageResult SendMessageBuffer(MIDI_TIMESTAMP timestamp, Windows.Foundation.IMemoryBuffer buffer, UInt32 byteOffset, UInt8 byteLength); // These methods will send multiple messages. The words must be ordered correctly - // so they are in order from word0 to wordn for each message. + // so they are in order from word-0 to word-n for each message. MidiSendMessageResult SendMessagesWordList(MIDI_TIMESTAMP timestamp, IVectorView words); MidiSendMessageResult SendMessagesWordArray(MIDI_TIMESTAMP timestamp, UInt32[] words); + // a convenience for sending multiple packets. We can do this because these are self-describing + MidiSendMessageResult SendMessagePacketList(IVectorView messages); } + + + } \ No newline at end of file diff --git a/src/api/Client/Midi2Client/MidiFunctionBlock.cpp b/src/api/Client/Midi2Client/MidiFunctionBlock.cpp index eab6bacae..622c75141 100644 --- a/src/api/Client/Midi2Client/MidiFunctionBlock.cpp +++ b/src/api/Client/Midi2Client/MidiFunctionBlock.cpp @@ -22,56 +22,62 @@ using namespace winrt::Windows::Data::Json; namespace winrt::Windows::Devices::Midi2::implementation { _Use_decl_annotations_ - bool MidiFunctionBlock::UpdateFromMessages(collections::IIterable messages) noexcept + bool MidiFunctionBlock::UpdateFromMessages(collections::IIterable messages) noexcept { - auto nameMessages{ winrt::single_threaded_vector() }; + auto nameMessages{ winrt::single_threaded_vector() }; //std::cout << __FUNCTION__ << " enter" << std::endl; for (auto message : messages) { - if (MidiMessageUtility::GetMessageTypeFromMessageFirstWord(message.Word0()) != MidiMessageType::Stream128) + if (MidiMessageUtility::GetMessageTypeFromMessageFirstWord(message.PeekFirstWord()) != MidiMessageType::Stream128) { // list contains non-stream messages. Abort. return false; } - switch (MidiMessageUtility::GetStatusFromStreamMessageFirstWord(message.Word0())) + switch (MidiMessageUtility::GetStatusFromStreamMessageFirstWord(message.PeekFirstWord())) { case (MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_INFO_NOTIFICATION): //std::cout << __FUNCTION__ << " info block notification" << std::endl; - if (MidiMessageUtility::GetFormFromStreamMessageFirstWord(message.Word0()) == MIDI_STREAM_MESSAGE_STANDARD_FORM0) + if (MidiMessageUtility::GetFormFromStreamMessageFirstWord(message.PeekFirstWord()) == MIDI_STREAM_MESSAGE_STANDARD_FORM0) { + auto ump128 = message.as(); + //std::cout << __FUNCTION__ << " form is correct. Setting info." << std::endl; // word 0: active function block or not - m_isActive = internal::GetFunctionBlockActiveFlagFromInfoNotificationFirstWord(message.Word0()); + m_isActive = internal::GetFunctionBlockActiveFlagFromInfoNotificationFirstWord(ump128.Word0()); // word 0: function block number - m_number = internal::GetFunctionBlockNumberFromInfoNotificationFirstWord(message.Word0()); + m_number = internal::GetFunctionBlockNumberFromInfoNotificationFirstWord(ump128.Word0()); // word 0: ui hint - m_uiHint = (midi2::MidiFunctionBlockUIHint)internal::GetFunctionBlockUIHintFromInfoNotificationFirstWord(message.Word0()); + m_uiHint = (midi2::MidiFunctionBlockUIHint)internal::GetFunctionBlockUIHintFromInfoNotificationFirstWord(ump128.Word0()); // word 0: MIDI 1.0 settings - m_midi10Connection = (midi2::MidiFunctionBlockMidi10)internal::GetFunctionBlockMidi10FromInfoNotificationFirstWord(message.Word0()); + m_midi10Connection = (midi2::MidiFunctionBlockMidi10)internal::GetFunctionBlockMidi10FromInfoNotificationFirstWord(ump128.Word0()); // word 0: direction - m_direction = (midi2::MidiFunctionBlockDirection)internal::GetFunctionBlockDirectionFromInfoNotificationFirstWord(message.Word0()); + m_direction = (midi2::MidiFunctionBlockDirection)internal::GetFunctionBlockDirectionFromInfoNotificationFirstWord(ump128.Word0()); + + // word 1: first group - m_firstGroupIndex = internal::GetFunctionBlockFirstGroupFromInfoNotificationSecondWord(message.Word1()); + m_firstGroupIndex = internal::GetFunctionBlockFirstGroupFromInfoNotificationSecondWord(ump128.Word1()); // word 1: number of groups spanned - m_numberOfGroupsSpanned = internal::GetFunctionBlockNumberOfGroupsFromInfoNotificationSecondWord(message.Word1()); + m_numberOfGroupsSpanned = internal::GetFunctionBlockNumberOfGroupsFromInfoNotificationSecondWord(ump128.Word1()); + + // word 2: MIDI CI Message Version / Form - m_midiCIMessageVersionFormat = internal::GetFunctionBlockMidiCIVersionFromInfoNotificationSecondWord(message.Word1()); + m_midiCIMessageVersionFormat = internal::GetFunctionBlockMidiCIVersionFromInfoNotificationSecondWord(ump128.Word2()); // word 2: maximum number of SysEx8 streams - m_maxSysEx8Streams = internal::GetFunctionBlockMaxSysex8StreamsFromInfoNotificationSecondWord(message.Word1()); + m_maxSysEx8Streams = internal::GetFunctionBlockMaxSysex8StreamsFromInfoNotificationSecondWord(ump128.Word2()); } else { diff --git a/src/api/Client/Midi2Client/MidiFunctionBlock.h b/src/api/Client/Midi2Client/MidiFunctionBlock.h index 328c592de..d97b763b5 100644 --- a/src/api/Client/Midi2Client/MidiFunctionBlock.h +++ b/src/api/Client/Midi2Client/MidiFunctionBlock.h @@ -57,7 +57,7 @@ namespace winrt::Windows::Devices::Midi2::implementation bool UpdateFromJsonString(_In_ winrt::hstring const json) noexcept; winrt::hstring GetJsonString() noexcept; - bool UpdateFromMessages(_In_ collections::IIterable messages) noexcept; + bool UpdateFromMessages(_In_ collections::IIterable messages) noexcept; bool UpdateFromDevPropertyStruct(_In_ MidiFunctionBlockProperty prop); diff --git a/src/api/Client/Midi2Client/MidiMessage128.cpp b/src/api/Client/Midi2Client/MidiMessage128.cpp index c70c524a2..9744a9483 100644 --- a/src/api/Client/Midi2Client/MidiMessage128.cpp +++ b/src/api/Client/Midi2Client/MidiMessage128.cpp @@ -12,6 +12,31 @@ namespace winrt::Windows::Devices::Midi2::implementation { + collections::IVectorView MidiMessage128::GetAllWords() const noexcept + { + auto vec = winrt::single_threaded_vector(); + + vec.Append(m_ump.word0); + vec.Append(m_ump.word1); + vec.Append(m_ump.word2); + vec.Append(m_ump.word3); + + return vec.GetView(); + } + + _Use_decl_annotations_ + uint8_t MidiMessage128::AppendAllWordsToVector(collections::IVector targetVector) const noexcept + { + targetVector.Append(m_ump.word0); + targetVector.Append(m_ump.word1); + targetVector.Append(m_ump.word2); + targetVector.Append(m_ump.word3); + + return 4; + } + + + _Use_decl_annotations_ MidiMessage128::MidiMessage128( internal::MidiTimestamp const timestamp, diff --git a/src/api/Client/Midi2Client/MidiMessage128.h b/src/api/Client/Midi2Client/MidiMessage128.h index 23d16c5fa..ce61602cf 100644 --- a/src/api/Client/Midi2Client/MidiMessage128.h +++ b/src/api/Client/Midi2Client/MidiMessage128.h @@ -65,6 +65,10 @@ namespace winrt::Windows::Devices::Midi2::implementation { return midi2::MidiPacketType::UniversalMidiPacket128; } + collections::IVectorView GetAllWords() const noexcept; + uint8_t AppendAllWordsToVector(_Inout_ collections::IVector targetVector) const noexcept; + + // IStringable winrt::hstring ToString(); diff --git a/src/api/Client/Midi2Client/MidiMessage32.cpp b/src/api/Client/Midi2Client/MidiMessage32.cpp index f4c3960ba..17490ae84 100644 --- a/src/api/Client/Midi2Client/MidiMessage32.cpp +++ b/src/api/Client/Midi2Client/MidiMessage32.cpp @@ -15,6 +15,25 @@ namespace winrt::Windows::Devices::Midi2::implementation { + collections::IVectorView MidiMessage32::GetAllWords() const noexcept + { + auto vec = winrt::single_threaded_vector(); + + vec.Append(m_ump.word0); + + return vec.GetView(); + } + + _Use_decl_annotations_ + uint8_t MidiMessage32::AppendAllWordsToVector(collections::IVector targetVector) const noexcept + { + targetVector.Append(m_ump.word0); + + return 1; + } + + + _Use_decl_annotations_ MidiMessage32::MidiMessage32( internal::MidiTimestamp const timestamp, diff --git a/src/api/Client/Midi2Client/MidiMessage32.h b/src/api/Client/Midi2Client/MidiMessage32.h index 3bde89a00..deadaff64 100644 --- a/src/api/Client/Midi2Client/MidiMessage32.h +++ b/src/api/Client/Midi2Client/MidiMessage32.h @@ -46,6 +46,10 @@ namespace winrt::Windows::Devices::Midi2::implementation uint32_t PeekFirstWord() { return Word0(); } + collections::IVectorView GetAllWords() const noexcept; + uint8_t AppendAllWordsToVector(_Inout_ collections::IVector targetVector) const noexcept; + + // internal for the sending code internal::PackedUmp32* GetInternalUmpDataPointer() { return &m_ump; } diff --git a/src/api/Client/Midi2Client/MidiMessage64.cpp b/src/api/Client/Midi2Client/MidiMessage64.cpp index a33178903..1acbab1b0 100644 --- a/src/api/Client/Midi2Client/MidiMessage64.cpp +++ b/src/api/Client/Midi2Client/MidiMessage64.cpp @@ -13,6 +13,28 @@ namespace winrt::Windows::Devices::Midi2::implementation { + _Use_decl_annotations_ + collections::IVectorView MidiMessage64::GetAllWords() const noexcept + { + auto vec = winrt::single_threaded_vector(); + + vec.Append(m_ump.word0); + vec.Append(m_ump.word1); + + return vec.GetView(); + } + + _Use_decl_annotations_ + uint8_t MidiMessage64::AppendAllWordsToVector(collections::IVector targetVector) const noexcept + { + targetVector.Append(m_ump.word0); + targetVector.Append(m_ump.word1); + + return 2; + } + + + _Use_decl_annotations_ MidiMessage64::MidiMessage64( internal::MidiTimestamp const timestamp, diff --git a/src/api/Client/Midi2Client/MidiMessage64.h b/src/api/Client/Midi2Client/MidiMessage64.h index 5a01e9627..c4fe7dce6 100644 --- a/src/api/Client/Midi2Client/MidiMessage64.h +++ b/src/api/Client/Midi2Client/MidiMessage64.h @@ -58,6 +58,8 @@ namespace winrt::Windows::Devices::Midi2::implementation // IStringable winrt::hstring ToString(); + collections::IVectorView GetAllWords() const noexcept; + uint8_t AppendAllWordsToVector(_Inout_ collections::IVector targetVector) const noexcept; // internal for the sending code diff --git a/src/api/Client/Midi2Client/MidiMessage96.cpp b/src/api/Client/Midi2Client/MidiMessage96.cpp index 072b31462..1bc5ddee1 100644 --- a/src/api/Client/Midi2Client/MidiMessage96.cpp +++ b/src/api/Client/Midi2Client/MidiMessage96.cpp @@ -12,6 +12,27 @@ namespace winrt::Windows::Devices::Midi2::implementation { + collections::IVectorView MidiMessage96::GetAllWords() const noexcept + { + auto vec = winrt::single_threaded_vector(); + + vec.Append(m_ump.word0); + vec.Append(m_ump.word1); + vec.Append(m_ump.word2); + + return vec.GetView(); + } + + _Use_decl_annotations_ + uint8_t MidiMessage96::AppendAllWordsToVector(collections::IVector targetVector) const noexcept + { + targetVector.Append(m_ump.word0); + targetVector.Append(m_ump.word1); + targetVector.Append(m_ump.word2); + + return 3; + } + _Use_decl_annotations_ MidiMessage96::MidiMessage96( internal::MidiTimestamp timestamp, diff --git a/src/api/Client/Midi2Client/MidiMessage96.h b/src/api/Client/Midi2Client/MidiMessage96.h index 74cb51281..d81a80047 100644 --- a/src/api/Client/Midi2Client/MidiMessage96.h +++ b/src/api/Client/Midi2Client/MidiMessage96.h @@ -60,6 +60,9 @@ namespace winrt::Windows::Devices::Midi2::implementation midi2::MidiPacketType PacketType() const noexcept { return midi2::MidiPacketType::UniversalMidiPacket96; } + collections::IVectorView GetAllWords() const noexcept; + uint8_t AppendAllWordsToVector(_Inout_ collections::IVector targetVector) const noexcept; + // IStringable winrt::hstring ToString(); diff --git a/src/api/Client/Midi2Client/MidiMessageUtility.cpp b/src/api/Client/Midi2Client/MidiMessageUtility.cpp index 74656b2ac..2b3f928c6 100644 --- a/src/api/Client/Midi2Client/MidiMessageUtility.cpp +++ b/src/api/Client/Midi2Client/MidiMessageUtility.cpp @@ -12,6 +12,109 @@ namespace winrt::Windows::Devices::Midi2::implementation { + _Use_decl_annotations_ + collections::IVector MidiMessageUtility::GetPacketListFromWordList( + uint64_t const timestamp, + collections::IVectorView const& words) + { + uint32_t index{ 0 }; + + auto result = winrt::single_threaded_vector(); + + + while (index < words.Size()) + { + auto wordsLeft = words.Size() - index; + + uint8_t numWords = internal::GetUmpLengthInMidiWordsFromFirstWord(words.GetAt(index)); + + if (numWords == 1) + { + MidiMessage32 ump{}; + ump.Timestamp(timestamp); + ump.Word0(index + 0); + result.Append(ump); + index += 1; + } + + else if (numWords == 2) + { + if (wordsLeft >= 2) + { + MidiMessage64 ump{}; + ump.Timestamp(timestamp); + ump.Word0(index + 0); + ump.Word1(index + 1); + result.Append(ump); + index += 2; + } + else + { + break; + } + } + + else if (numWords == 3) + { + if (wordsLeft >= 3) + { + MidiMessage96 ump{}; + ump.Timestamp(timestamp); + ump.Word0(index + 0); + ump.Word1(index + 1); + ump.Word2(index + 2); + result.Append(ump); + index += 3; + } + else + { + break; + } + } + + else if (numWords == 4) + { + if (wordsLeft >= 4) + { + + MidiMessage128 ump{}; + ump.Timestamp(timestamp); + ump.Word0(index + 0); + ump.Word1(index + 1); + ump.Word2(index + 2); + ump.Word3(index + 3); + result.Append(ump); + index += 4; + } + else + { + break; + } + } + } + + return result; + } + + _Use_decl_annotations_ + collections::IVector MidiMessageUtility::GetWordListFromPacketList( + collections::IVectorView const& messages) + { + // we're doing this the safe and easy way, but there's likely a more efficient way to copy the memory over + + auto result = winrt::single_threaded_vector(); + + for (auto const& message : messages) + { + message.AppendAllWordsToVector(result); + } + + return result; + } + + + + _Use_decl_annotations_ bool MidiMessageUtility::ValidateMessage32MessageType(uint32_t const word0) noexcept { diff --git a/src/api/Client/Midi2Client/MidiMessageUtility.h b/src/api/Client/Midi2Client/MidiMessageUtility.h index 8bdaad90c..fdfa77f2f 100644 --- a/src/api/Client/Midi2Client/MidiMessageUtility.h +++ b/src/api/Client/Midi2Client/MidiMessageUtility.h @@ -58,6 +58,9 @@ namespace winrt::Windows::Devices::Midi2::implementation static uint8_t GetStatusBankFromFlexDataMessageFirstWord(_In_ uint32_t const word0) noexcept; static uint8_t GetStatusFromFlexDataMessageFirstWord(_In_ uint32_t const word0) noexcept; + static collections::IVector GetPacketListFromWordList(_In_ uint64_t const timestamp, _In_ collections::IVectorView const& words); + static collections::IVector GetWordListFromPacketList(_In_ collections::IVectorView const& messages); + }; } diff --git a/src/api/Client/Midi2Client/MidiMessageUtility.idl b/src/api/Client/Midi2Client/MidiMessageUtility.idl index 0fabb4b90..171a6d1a8 100644 --- a/src/api/Client/Midi2Client/MidiMessageUtility.idl +++ b/src/api/Client/Midi2Client/MidiMessageUtility.idl @@ -18,6 +18,8 @@ import "MidiPacketTypeEnum.idl"; import "MidiGroup.idl"; import "MidiChannel.idl"; +import "IMidiUniversalPacket.idl"; + import "Midi1ChannelVoiceMessageStatusEnum.idl"; import "Midi2ChannelVoiceMessageStatusEnum.idl"; @@ -65,5 +67,8 @@ namespace Windows.Devices.Midi2 static String GetMessageFriendlyNameFromFirstWord(UInt32 word0); + + static IVector GetPacketListFromWordList(MIDI_TIMESTAMP timestamp, IVectorView words); + static IVector GetWordListFromPacketList(IVectorView words); }; } diff --git a/src/api/Client/Midi2Client/MidiStreamMessageBuilder.cpp b/src/api/Client/Midi2Client/MidiStreamMessageBuilder.cpp index 72a0a3d0c..bfba612b7 100644 --- a/src/api/Client/Midi2Client/MidiStreamMessageBuilder.cpp +++ b/src/api/Client/Midi2Client/MidiStreamMessageBuilder.cpp @@ -17,7 +17,7 @@ namespace winrt::Windows::Devices::Midi2::implementation { _Use_decl_annotations_ - midi2::MidiMessage128 MidiStreamMessageBuilder::BuildEndpointDiscoveryMessage( + midi2::IMidiUniversalPacket MidiStreamMessageBuilder::BuildEndpointDiscoveryMessage( internal::MidiTimestamp const timestamp, uint8_t const umpVersionMajor, uint8_t const umpVersionMinor, @@ -37,7 +37,7 @@ namespace winrt::Windows::Devices::Midi2::implementation } _Use_decl_annotations_ - midi2::MidiMessage128 MidiStreamMessageBuilder::BuildEndpointInformationNotificationMessage( + midi2::IMidiUniversalPacket MidiStreamMessageBuilder::BuildEndpointInformationNotificationMessage( internal::MidiTimestamp const timestamp, uint8_t const umpVersionMajor, uint8_t const umpVersionMinor, @@ -79,7 +79,7 @@ namespace winrt::Windows::Devices::Midi2::implementation } _Use_decl_annotations_ - midi2::MidiMessage128 MidiStreamMessageBuilder::BuildDeviceIdentityNotificationMessage( + midi2::IMidiUniversalPacket MidiStreamMessageBuilder::BuildDeviceIdentityNotificationMessage( internal::MidiTimestamp timestamp, uint8_t const deviceManufacturerSysExIdByte1, uint8_t const deviceManufacturerSysExIdByte2, @@ -128,7 +128,7 @@ namespace winrt::Windows::Devices::Midi2::implementation // TODO: Add ASCII/UTF-8 encoding option _Use_decl_annotations_ - collections::IVector MidiStreamMessageBuilder::BuildSplitTextMessages( + collections::IVector MidiStreamMessageBuilder::BuildSplitTextMessages( internal::MidiTimestamp const timestamp, uint8_t const status, uint16_t const word0Remainder, @@ -136,7 +136,7 @@ namespace winrt::Windows::Devices::Midi2::implementation uint8_t const maxCharactersPerPacket, winrt::hstring const& text) { - auto messages = winrt::single_threaded_vector(); + auto messages = winrt::single_threaded_vector(); // endpoint name is one or more UMPs, max 98 bytes. Either 1 complete message (form 0x0) // or start (form 0x1, continue messages 0x2, and end 0x3) @@ -266,7 +266,7 @@ namespace winrt::Windows::Devices::Midi2::implementation // TODO: This is UTF-8 _Use_decl_annotations_ - collections::IVector MidiStreamMessageBuilder::BuildEndpointNameNotificationMessages( + collections::IVector MidiStreamMessageBuilder::BuildEndpointNameNotificationMessages( internal::MidiTimestamp const timestamp, winrt::hstring const& name ) noexcept @@ -283,7 +283,7 @@ namespace winrt::Windows::Devices::Midi2::implementation // TODO: This is ASCII _Use_decl_annotations_ - collections::IVector MidiStreamMessageBuilder::BuildProductInstanceIdNotificationMessages( + collections::IVector MidiStreamMessageBuilder::BuildProductInstanceIdNotificationMessages( internal::MidiTimestamp const timestamp, winrt::hstring const& productInstanceId ) @@ -300,7 +300,7 @@ namespace winrt::Windows::Devices::Midi2::implementation } _Use_decl_annotations_ - midi2::MidiMessage128 MidiStreamMessageBuilder::BuildStreamConfigurationRequestMessage( + midi2::IMidiUniversalPacket MidiStreamMessageBuilder::BuildStreamConfigurationRequestMessage( internal::MidiTimestamp const timestamp, uint8_t const protocol, bool const expectToReceiveJRTimestamps, @@ -330,7 +330,7 @@ namespace winrt::Windows::Devices::Midi2::implementation } _Use_decl_annotations_ - midi2::MidiMessage128 MidiStreamMessageBuilder::BuildStreamConfigurationNotificationMessage( + midi2::IMidiUniversalPacket MidiStreamMessageBuilder::BuildStreamConfigurationNotificationMessage( internal::MidiTimestamp const timestamp, uint8_t const protocol, bool const confirmationWillReceiveJRTimestamps, @@ -359,7 +359,7 @@ namespace winrt::Windows::Devices::Midi2::implementation } _Use_decl_annotations_ - midi2::MidiMessage128 MidiStreamMessageBuilder::BuildFunctionBlockDiscoveryMessage( + midi2::IMidiUniversalPacket MidiStreamMessageBuilder::BuildFunctionBlockDiscoveryMessage( internal::MidiTimestamp const timestamp, uint8_t const functionBlockNumber, midi2::MidiFunctionBlockDiscoveryFilterFlags const requestFlags @@ -383,7 +383,7 @@ namespace winrt::Windows::Devices::Midi2::implementation } _Use_decl_annotations_ - midi2::MidiMessage128 MidiStreamMessageBuilder::BuildFunctionBlockInfoNotificationMessage( + midi2::IMidiUniversalPacket MidiStreamMessageBuilder::BuildFunctionBlockInfoNotificationMessage( internal::MidiTimestamp const timestamp, bool const active, uint8_t const functionBlockNumber, @@ -431,7 +431,7 @@ namespace winrt::Windows::Devices::Midi2::implementation // TODO: This is UTF-8 _Use_decl_annotations_ - collections::IVector MidiStreamMessageBuilder::BuildFunctionBlockNameNotificationMessages( + collections::IVector MidiStreamMessageBuilder::BuildFunctionBlockNameNotificationMessages( internal::MidiTimestamp const timestamp, uint8_t const functionBlockNumber, winrt::hstring const& name @@ -454,34 +454,42 @@ namespace winrt::Windows::Devices::Midi2::implementation _Use_decl_annotations_ winrt::hstring MidiStreamMessageBuilder::ParseFunctionBlockNameNotificationMessages( - collections::IVector messages + collections::IVector messages ) { std::string s{}; s.reserve(13); - for (auto message : messages) + for (auto ump : messages) { - // verify that the message form is correct (begin/[continue]/end or just complete) + midi2::MidiMessage128 message{ nullptr }; - // verify the status is correct + if (ump.MessageType() == midi2::MidiMessageType::Stream128) + { + message = ump.as(); + + // verify that the message form is correct (begin/[continue]/end or just complete) + + // verify the status is correct - AppendCharToString(s, MIDIWORDBYTE4(message.Word0())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word0())); - AppendCharToString(s, MIDIWORDBYTE1(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE2(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE3(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE1(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE2(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE1(message.Word2())); - AppendCharToString(s, MIDIWORDBYTE2(message.Word2())); - AppendCharToString(s, MIDIWORDBYTE3(message.Word2())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE1(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE2(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word2())); + + AppendCharToString(s, MIDIWORDBYTE1(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE2(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word3())); + } - AppendCharToString(s, MIDIWORDBYTE1(message.Word3())); - AppendCharToString(s, MIDIWORDBYTE2(message.Word3())); - AppendCharToString(s, MIDIWORDBYTE3(message.Word3())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word3())); } return winrt::to_hstring(s); @@ -490,35 +498,42 @@ namespace winrt::Windows::Devices::Midi2::implementation _Use_decl_annotations_ winrt::hstring MidiStreamMessageBuilder::ParseEndpointNameNotificationMessages( - collections::IVector messages + collections::IVector messages ) { std::string s{}; s.reserve(14); - for (auto message : messages) + for (auto ump : messages) { - // verify that the message form is correct (begin/[continue]/end or just complete) + midi2::MidiMessage128 message{ nullptr }; - // verify the status is correct + if (ump.MessageType() == midi2::MidiMessageType::Stream128) + { + message = ump.as(); + + // verify that the message form is correct (begin/[continue]/end or just complete) - AppendCharToString(s, MIDIWORDBYTE3(message.Word0())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word0())); + // verify the status is correct - AppendCharToString(s, MIDIWORDBYTE1(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE2(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE3(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word0())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word0())); - AppendCharToString(s, MIDIWORDBYTE1(message.Word2())); - AppendCharToString(s, MIDIWORDBYTE2(message.Word2())); - AppendCharToString(s, MIDIWORDBYTE3(message.Word2())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE1(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE2(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE1(message.Word3())); - AppendCharToString(s, MIDIWORDBYTE2(message.Word3())); - AppendCharToString(s, MIDIWORDBYTE3(message.Word3())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE1(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE2(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word2())); + + AppendCharToString(s, MIDIWORDBYTE1(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE2(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word3())); + } } return winrt::to_hstring(s); @@ -526,35 +541,41 @@ namespace winrt::Windows::Devices::Midi2::implementation _Use_decl_annotations_ winrt::hstring MidiStreamMessageBuilder::ParseProductInstanceIdNotificationMessages( - collections::IVector messages + collections::IVector messages ) { std::string s{}; s.reserve(14); - for (auto message : messages) + for (auto ump : messages) { - // verify that the message form is correct (begin/[continue]/end or just complete) + midi2::MidiMessage128 message{ nullptr }; + + if (ump.MessageType() == midi2::MidiMessageType::Stream128) + { + message = ump.as(); + // verify that the message form is correct (begin/[continue]/end or just complete) // verify the status is correct - AppendCharToString(s, MIDIWORDBYTE3(message.Word0())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word0())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word0())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word0())); - AppendCharToString(s, MIDIWORDBYTE1(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE2(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE3(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE1(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE2(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word1())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word1())); - AppendCharToString(s, MIDIWORDBYTE1(message.Word2())); - AppendCharToString(s, MIDIWORDBYTE2(message.Word2())); - AppendCharToString(s, MIDIWORDBYTE3(message.Word2())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE1(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE2(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word2())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word2())); - AppendCharToString(s, MIDIWORDBYTE1(message.Word3())); - AppendCharToString(s, MIDIWORDBYTE2(message.Word3())); - AppendCharToString(s, MIDIWORDBYTE3(message.Word3())); - AppendCharToString(s, MIDIWORDBYTE4(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE1(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE2(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE3(message.Word3())); + AppendCharToString(s, MIDIWORDBYTE4(message.Word3())); + } } return winrt::to_hstring(s); diff --git a/src/api/Client/Midi2Client/MidiStreamMessageBuilder.h b/src/api/Client/Midi2Client/MidiStreamMessageBuilder.h index f5280ed60..c2263463c 100644 --- a/src/api/Client/Midi2Client/MidiStreamMessageBuilder.h +++ b/src/api/Client/Midi2Client/MidiStreamMessageBuilder.h @@ -16,14 +16,14 @@ namespace winrt::Windows::Devices::Midi2::implementation { MidiStreamMessageBuilder() = default; - static midi2::MidiMessage128 BuildEndpointDiscoveryMessage( + static midi2::IMidiUniversalPacket BuildEndpointDiscoveryMessage( _In_ internal::MidiTimestamp const timestamp, _In_ uint8_t const umpVersionMajor, _In_ uint8_t const umpVersionMinor, _In_ midi2::MidiEndpointDiscoveryFilterFlags const requestFlags ) noexcept; - static midi2::MidiMessage128 BuildEndpointInformationNotificationMessage( + static midi2::IMidiUniversalPacket BuildEndpointInformationNotificationMessage( _In_ internal::MidiTimestamp const timestamp, _In_ uint8_t const umpVersionMajor, _In_ uint8_t const umpVersionMinor, @@ -35,7 +35,7 @@ namespace winrt::Windows::Devices::Midi2::implementation _In_ bool const supportsSendingJitterReductionTimestamps ) noexcept; - static midi2::MidiMessage128 BuildDeviceIdentityNotificationMessage( + static midi2::IMidiUniversalPacket BuildDeviceIdentityNotificationMessage( _In_ internal::MidiTimestamp timestamp, _In_ uint8_t const deviceManufacturerSysExIdByte1, _In_ uint8_t const deviceManufacturerSysExIdByte2, @@ -50,24 +50,24 @@ namespace winrt::Windows::Devices::Midi2::implementation _In_ uint8_t const softwareRevisionLevelByte4 ) noexcept; - static collections::IVector BuildEndpointNameNotificationMessages( + static collections::IVector BuildEndpointNameNotificationMessages( _In_ internal::MidiTimestamp const timestamp, _In_ winrt::hstring const& name ) noexcept; - static collections::IVector BuildProductInstanceIdNotificationMessages( + static collections::IVector BuildProductInstanceIdNotificationMessages( _In_ internal::MidiTimestamp const timestamp, _In_ winrt::hstring const& productInstanceId ); - static midi2::MidiMessage128 BuildStreamConfigurationRequestMessage( + static midi2::IMidiUniversalPacket BuildStreamConfigurationRequestMessage( _In_ internal::MidiTimestamp const timestamp, _In_ uint8_t const protocol, _In_ bool const expectToReceiveJRTimestamps, _In_ bool const requestToSendJRTimestamps ); - static midi2::MidiMessage128 BuildStreamConfigurationNotificationMessage( + static midi2::IMidiUniversalPacket BuildStreamConfigurationNotificationMessage( _In_ internal::MidiTimestamp const timestamp, _In_ uint8_t const protocol, _In_ bool const confirmationWillReceiveJRTimestamps, @@ -76,23 +76,23 @@ namespace winrt::Windows::Devices::Midi2::implementation static winrt::hstring ParseEndpointNameNotificationMessages( - _In_ collections::IVector messages + _In_ collections::IVector messages ); static winrt::hstring ParseProductInstanceIdNotificationMessages( - _In_ collections::IVector messages + _In_ collections::IVector messages ); // function blocks - static midi2::MidiMessage128 BuildFunctionBlockDiscoveryMessage( + static midi2::IMidiUniversalPacket BuildFunctionBlockDiscoveryMessage( _In_ internal::MidiTimestamp const timestamp, _In_ uint8_t const functionBlockNumber, _In_ midi2::MidiFunctionBlockDiscoveryFilterFlags requestFlags ); - static midi2::MidiMessage128 BuildFunctionBlockInfoNotificationMessage( + static midi2::IMidiUniversalPacket BuildFunctionBlockInfoNotificationMessage( _In_ internal::MidiTimestamp const timestamp, _In_ bool const active, _In_ uint8_t const functionBlockNumber, @@ -105,7 +105,7 @@ namespace winrt::Windows::Devices::Midi2::implementation _In_ uint8_t const maxNumberSysEx8Streams ); - static collections::IVector BuildFunctionBlockNameNotificationMessages( + static collections::IVector BuildFunctionBlockNameNotificationMessages( _In_ internal::MidiTimestamp const timestamp, _In_ uint8_t const functionBlockNumber, _In_ winrt::hstring const& name @@ -113,7 +113,7 @@ namespace winrt::Windows::Devices::Midi2::implementation static winrt::hstring ParseFunctionBlockNameNotificationMessages( - _In_ collections::IVector messages + _In_ collections::IVector messages ); @@ -121,7 +121,7 @@ namespace winrt::Windows::Devices::Midi2::implementation private: - static collections::IVector BuildSplitTextMessages( + static collections::IVector BuildSplitTextMessages( _In_ internal::MidiTimestamp const timestamp, _In_ uint8_t const status, _In_ uint16_t const word0Remainder, diff --git a/src/api/Client/Midi2Client/MidiStreamMessageBuilder.idl b/src/api/Client/Midi2Client/MidiStreamMessageBuilder.idl index 886a0c024..eb71c2938 100644 --- a/src/api/Client/Midi2Client/MidiStreamMessageBuilder.idl +++ b/src/api/Client/Midi2Client/MidiStreamMessageBuilder.idl @@ -24,14 +24,14 @@ namespace Windows.Devices.Midi2 [MIDI_API_CONTRACT(1)] static runtimeclass MidiStreamMessageBuilder { - static MidiMessage128 BuildEndpointDiscoveryMessage( + static IMidiUniversalPacket BuildEndpointDiscoveryMessage( MIDI_TIMESTAMP timestamp, UInt8 umpVersionMajor, UInt8 umpVersionMinor, MidiEndpointDiscoveryFilterFlags requestFlags ); - static MidiMessage128 BuildEndpointInformationNotificationMessage( + static IMidiUniversalPacket BuildEndpointInformationNotificationMessage( MIDI_TIMESTAMP timestamp, UInt8 umpVersionMajor, UInt8 umpVersionMinor, @@ -43,7 +43,7 @@ namespace Windows.Devices.Midi2 Boolean supportsSendingJitterReductionTimestamps ); - static MidiMessage128 BuildDeviceIdentityNotificationMessage( + static IMidiUniversalPacket BuildDeviceIdentityNotificationMessage( MIDI_TIMESTAMP timestamp, UInt8 deviceManufacturerSysExIdByte1, UInt8 deviceManufacturerSysExIdByte2, @@ -58,12 +58,12 @@ namespace Windows.Devices.Midi2 UInt8 softwareRevisionLevelByte4 ); - static IVector BuildEndpointNameNotificationMessages( + static IVector BuildEndpointNameNotificationMessages( MIDI_TIMESTAMP timestamp, String name ); - static IVector BuildProductInstanceIdNotificationMessages( + static IVector BuildProductInstanceIdNotificationMessages( MIDI_TIMESTAMP timestamp, String productInstanceId ); @@ -71,24 +71,24 @@ namespace Windows.Devices.Midi2 static String ParseEndpointNameNotificationMessages( - IVector messages + IVector messages ); static String ParseProductInstanceIdNotificationMessages( - IVector messages + IVector messages ); - static MidiMessage128 BuildStreamConfigurationRequestMessage( + static IMidiUniversalPacket BuildStreamConfigurationRequestMessage( MIDI_TIMESTAMP timestamp, UInt8 protocol, Boolean expectToReceiveJRTimestamps, Boolean requestToSendJRTimestamps ); - static MidiMessage128 BuildStreamConfigurationNotificationMessage( + static IMidiUniversalPacket BuildStreamConfigurationNotificationMessage( MIDI_TIMESTAMP timestamp, UInt8 protocol, Boolean confirmationWillReceiveJRTimestamps, @@ -98,13 +98,13 @@ namespace Windows.Devices.Midi2 // Function blocks - static MidiMessage128 BuildFunctionBlockDiscoveryMessage( + static IMidiUniversalPacket BuildFunctionBlockDiscoveryMessage( MIDI_TIMESTAMP timestamp, UInt8 functionBlockNumber, MidiFunctionBlockDiscoveryFilterFlags requestFlags ); - static MidiMessage128 BuildFunctionBlockInfoNotificationMessage( + static IMidiUniversalPacket BuildFunctionBlockInfoNotificationMessage( MIDI_TIMESTAMP timestamp, Boolean active, UInt8 functionBlockNumber, @@ -117,14 +117,14 @@ namespace Windows.Devices.Midi2 UInt8 maxNumberSysEx8Streams ); - static IVector BuildFunctionBlockNameNotificationMessages( + static IVector BuildFunctionBlockNameNotificationMessages( MIDI_TIMESTAMP timestamp, UInt8 functionBlockNumber, String name ); static String ParseFunctionBlockNameNotificationMessages( - IVector messages + IVector messages ); }; diff --git a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp index 66c0c9c76..d49220282 100644 --- a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp +++ b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp @@ -65,7 +65,7 @@ namespace winrt::Windows::Devices::Midi2::implementation internal::LogInfo(__FUNCTION__, L"Enter"); auto functionBlockNotification = midi2::MidiStreamMessageBuilder::BuildFunctionBlockInfoNotificationMessage( - 0, + MidiClock::TimestampConstantSendImmediately(), true, fb.Number(), fb.UIHint(), @@ -92,21 +92,22 @@ namespace winrt::Windows::Devices::Midi2::implementation if (fb.Name() == L"") return; auto nameMessages = midi2::MidiStreamMessageBuilder::BuildFunctionBlockNameNotificationMessages( - 0, + MidiClock::TimestampConstantSendImmediately(), fb.Number(), fb.Name() ); - for (uint32_t i = 0; i < nameMessages.Size(); i++) + if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendMessagePacketList(nameMessages.GetView()))) { - if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendMessagePacket(nameMessages.GetAt(i)))) - { - internal::LogGeneralError(__FUNCTION__, L"SendMessagePacket failed"); - } + internal::LogGeneralError(__FUNCTION__, L"SendMessagePacketList failed"); } - } + + // TODO: Any message sending from inside this function should + // be done on another thread via a queue, like we do in the service + // That way no long-running or recursive callbacks. + _Use_decl_annotations_ void MidiVirtualEndpointDevice::ProcessIncomingMessage( midi2::MidiMessageReceivedEventArgs const& args, @@ -122,7 +123,6 @@ namespace winrt::Windows::Devices::Midi2::implementation MidiMessage128 message{}; if (args.FillMessage128(message)) { - // if a endpoint discovery request, handle it with the data we have if (internal::MessageIsEndpointDiscoveryRequest(message.Word0())) { @@ -183,19 +183,25 @@ namespace winrt::Windows::Devices::Midi2::implementation for (uint8_t i = 0; i < (uint8_t)m_functionBlocks.Size(); i++) { + internal::LogInfo(__FUNCTION__, L"Sending function block info message"); if (requestInfo) SendFunctionBlockInfoNotificationMessage(m_functionBlocks.Lookup(i)); + + internal::LogInfo(__FUNCTION__, L"Sending function block name messages"); if (requestName) SendFunctionBlockNameNotificationMessages(m_functionBlocks.Lookup(i)); } } else { - // send single function block + // send single requested function block if (m_functionBlocks.HasKey(fbNumber)) { auto fb = m_functionBlocks.Lookup(fbNumber); + internal::LogInfo(__FUNCTION__, L"Sending function block info message"); if (requestInfo) SendFunctionBlockInfoNotificationMessage(fb); + + internal::LogInfo(__FUNCTION__, L"Sending function block name messages"); if (requestName) SendFunctionBlockNameNotificationMessages(fb); } else @@ -206,9 +212,7 @@ namespace winrt::Windows::Devices::Midi2::implementation } } - else if (internal::MessageIsEndpointDiscoveryRequest(message.Word0())) - { - } + else { // something else diff --git a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h index 07dca31e0..a354901ca 100644 --- a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h +++ b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.h @@ -75,6 +75,16 @@ namespace winrt::Windows::Devices::Midi2::implementation void SendFunctionBlockInfoNotificationMessage(_In_ midi2::MidiFunctionBlock const& fb) noexcept; void SendFunctionBlockNameNotificationMessages(_In_ midi2::MidiFunctionBlock const& fb) noexcept; + //void MidiVirtualEndpointDevice::QueueWorker(); + + //void EnqueueOutgoingMessage(_In_ internal::PackedUmp128 const& message); + + //std::atomic m_continueProcessing{ true }; + //wil::slim_event_manual_reset m_messageProcessorWakeup; + //std::queue m_outgoingMessageQueue; + //std::mutex m_queueMutex; + //std::thread m_queueWorkerThread; + midi2::MidiVirtualEndpointDeviceDefinition m_virtualEndpointDeviceDefinition{ nullptr }; diff --git a/src/api/Client/Midi2Client/pch.h b/src/api/Client/Midi2Client/pch.h index d49afd875..6fb733660 100644 --- a/src/api/Client/Midi2Client/pch.h +++ b/src/api/Client/Midi2Client/pch.h @@ -19,6 +19,9 @@ //#include // must be before the first C++ WinRT header //#include +#include +#include +#include #include #include @@ -35,7 +38,8 @@ namespace json = ::winrt::Windows::Data::Json; #include #include #include - +#include +#include // internal #include "trace_logging.h" diff --git a/src/api/Test/Midi2.Client.unittests/MidiFunctionBlockMessageBuilderTests.cpp b/src/api/Test/Midi2.Client.unittests/MidiFunctionBlockMessageBuilderTests.cpp index c673e33df..ba05568ae 100644 --- a/src/api/Test/Midi2.Client.unittests/MidiFunctionBlockMessageBuilderTests.cpp +++ b/src/api/Test/Midi2.Client.unittests/MidiFunctionBlockMessageBuilderTests.cpp @@ -38,8 +38,10 @@ void MidiFunctionBlockMessageBuilderTests::TestBuildFunctionBlockNameNotificatio // count the number of messages we get back VERIFY_ARE_EQUAL(messages.Size(), expectedPacketCount); - for (auto message : messages) + for (auto ump : messages) { + auto message = ump.as(); + std::cout << "Stream Message words" << " 0x" << std::hex << message.Word0() << " 0x" << std::hex << message.Word1() @@ -51,26 +53,26 @@ void MidiFunctionBlockMessageBuilderTests::TestBuildFunctionBlockNameNotificatio for (uint32_t i = 0; i < messages.Size(); i++) { - std::cout << "Stream word0 0x" << std::hex << messages.GetAt(i).Word0() << std::endl; + std::cout << "Stream word0 0x" << std::hex << messages.GetAt(i).PeekFirstWord() << std::endl; // verify status - VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(i).Word0()), MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_NAME_NOTIFICATION); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(i).PeekFirstWord()), MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_NAME_NOTIFICATION); // verify form is correct if (i == 0) { // first message - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).Word0()), (uint8_t)0x01); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).PeekFirstWord()), (uint8_t)0x01); } else if (i == messages.Size() - 1) { // last message - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).Word0()), (uint8_t)0x03); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).PeekFirstWord()), (uint8_t)0x03); } else { // interim messages - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).Word0()), (uint8_t)0x02); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).PeekFirstWord()), (uint8_t)0x02); } @@ -109,24 +111,25 @@ void MidiFunctionBlockMessageBuilderTests::TestBuildFunctionBlockNameNotificatio // count the number of messages we get back VERIFY_ARE_EQUAL(messages.Size(), expectedPacketCount); - for (auto message : messages) + for (auto ump : messages) { + auto message = ump.as(); + std::cout << "Stream Message words" << " 0x" << std::hex << message.Word0() << " 0x" << std::hex << message.Word1() << " 0x" << std::hex << message.Word2() << " 0x" << std::hex << message.Word3() << std::endl; - } // verify status is correct - VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(0).Word0()), MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_NAME_NOTIFICATION); - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(0).Word0()), 0x01); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(0).PeekFirstWord()), MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_NAME_NOTIFICATION); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(0).PeekFirstWord()), 0x01); - VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(1).Word0()), MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_NAME_NOTIFICATION); - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(1).Word0()), 0x03); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(1).PeekFirstWord()), MIDI_STREAM_MESSAGE_STATUS_FUNCTION_BLOCK_NAME_NOTIFICATION); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(1).PeekFirstWord()), 0x03); // verify form is correct @@ -174,9 +177,11 @@ void MidiFunctionBlockMessageBuilderTests::TestBuildFunctionBlockInfoNotificatio // verify values are in the UMP - VERIFY_ARE_EQUAL(ump.Word0(), resultingWord0); - VERIFY_ARE_EQUAL(ump.Word1(), resultingWord1); - VERIFY_ARE_EQUAL(ump.Word2(), (uint32_t)0); - VERIFY_ARE_EQUAL(ump.Word3(), (uint32_t)0); + auto msg = ump.as(); + + VERIFY_ARE_EQUAL(msg.Word0(), resultingWord0); + VERIFY_ARE_EQUAL(msg.Word1(), resultingWord1); + VERIFY_ARE_EQUAL(msg.Word2(), (uint32_t)0); + VERIFY_ARE_EQUAL(msg.Word3(), (uint32_t)0); } diff --git a/src/api/Test/Midi2.Client.unittests/MidiStreamMessageBuilderTests.cpp b/src/api/Test/Midi2.Client.unittests/MidiStreamMessageBuilderTests.cpp index 414b6a677..b41374f96 100644 --- a/src/api/Test/Midi2.Client.unittests/MidiStreamMessageBuilderTests.cpp +++ b/src/api/Test/Midi2.Client.unittests/MidiStreamMessageBuilderTests.cpp @@ -32,8 +32,10 @@ void MidiStreamMessageBuilderTests::TestBuildEndpointNameNotificationLong() // count the number of messages we get back VERIFY_ARE_EQUAL(messages.Size(), expectedPacketCount); - for (auto message : messages) + for (auto ump : messages) { + auto message = ump.as(); + std::cout << "Stream Message words" << " 0x" << std::hex << message.Word0() << " 0x" << std::hex << message.Word1() @@ -45,26 +47,26 @@ void MidiStreamMessageBuilderTests::TestBuildEndpointNameNotificationLong() for (uint32_t i = 0; i < messages.Size(); i++) { - std::cout << "Stream word0 0x" << std::hex << messages.GetAt(i).Word0() << std::endl; + std::cout << "Stream word0 0x" << std::hex << messages.GetAt(i).PeekFirstWord() << std::endl; // verify status - VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(i).Word0()), MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(i).PeekFirstWord()), MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION); // verify form is correct if (i == 0) { // first message - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).Word0()), 0x01); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).PeekFirstWord()), 0x01); } else if (i == messages.Size() - 1) { // last message - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).Word0()), 0x03); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).PeekFirstWord()), 0x03); } else { // interim messages - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).Word0()), 0x02); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(i).PeekFirstWord()), 0x02); } } @@ -93,24 +95,25 @@ void MidiStreamMessageBuilderTests::TestBuildEndpointNameNotificationMedium() // count the number of messages we get back VERIFY_ARE_EQUAL(messages.Size(), expectedPacketCount); - for (auto message : messages) + for (auto ump : messages) { + auto message = ump.as(); + std::cout << "Stream Message words" << " 0x" << std::hex << message.Word0() << " 0x" << std::hex << message.Word1() << " 0x" << std::hex << message.Word2() << " 0x" << std::hex << message.Word3() << std::endl; - } // verify status is correct - VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(0).Word0()), MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION); - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(0).Word0()), 0x01); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(0).PeekFirstWord()), MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(0).PeekFirstWord()), 0x01); - VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(1).Word0()), MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION); - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(1).Word0()), 0x03); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(1).PeekFirstWord()), MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(1).PeekFirstWord()), 0x03); // verify form is correct @@ -137,28 +140,29 @@ void MidiStreamMessageBuilderTests::TestBuildEndpointNameNotificationShort() std::cout << "Expected ump count " << expectedPacketCount << ", received " << messages.Size() << std::endl; - std::cout << "Stream Message 1 word0 0x" << std::hex << messages.GetAt(0).Word0() << std::endl; + std::cout << "Stream Message 1 word0 0x" << std::hex << messages.GetAt(0).PeekFirstWord() << std::endl; // count the number of messages we get back VERIFY_ARE_EQUAL(messages.Size(), expectedPacketCount); - for (auto message : messages) + for (auto ump : messages) { + auto message = ump.as(); + std::cout << "Stream Message words" << " 0x" << std::hex << message.Word0() << " 0x" << std::hex << message.Word1() << " 0x" << std::hex << message.Word2() << " 0x" << std::hex << message.Word3() << std::endl; - } // verify status is correct - VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(0).Word0()), MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(0).PeekFirstWord()), MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_NAME_NOTIFICATION); // verify form is correct - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(0).Word0()), 0x00); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(0).PeekFirstWord()), 0x00); // reverse it back into a string and verify auto s = MidiStreamMessageBuilder::ParseEndpointNameNotificationMessages(messages); @@ -181,28 +185,29 @@ void MidiStreamMessageBuilderTests::TestBuildProductInstanceIdNotificationShort( std::cout << "Expected ump count " << expectedPacketCount << ", received " << messages.Size() << std::endl; - std::cout << "Stream Message 1 word0 0x" << std::hex << messages.GetAt(0).Word0() << std::endl; + std::cout << "Stream Message 1 word0 0x" << std::hex << messages.GetAt(0).PeekFirstWord() << std::endl; // count the number of messages we get back VERIFY_ARE_EQUAL(messages.Size(), expectedPacketCount); - for (auto message : messages) + for (auto ump : messages) { + auto message = ump.as(); + std::cout << "Stream Message words" << " 0x" << std::hex << message.Word0() << " 0x" << std::hex << message.Word1() << " 0x" << std::hex << message.Word2() << " 0x" << std::hex << message.Word3() << std::endl; - } // verify status is correct - VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(0).Word0()), MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_PRODUCT_INSTANCE_ID_NOTIFICATION); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetStatusFromStreamMessageFirstWord(messages.GetAt(0).PeekFirstWord()), MIDI_STREAM_MESSAGE_STATUS_ENDPOINT_PRODUCT_INSTANCE_ID_NOTIFICATION); // verify form is correct - VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(0).Word0()), 0x00); + VERIFY_ARE_EQUAL(MidiMessageUtility::GetFormFromStreamMessageFirstWord(messages.GetAt(0).PeekFirstWord()), 0x00); // reverse it back into a string and verify diff --git a/src/user-tools/midi-console/Midi/EndpointMessageSender.cs b/src/user-tools/midi-console/Midi/EndpointMessageSender.cs index d1abafc74..426d7fab6 100644 --- a/src/user-tools/midi-console/Midi/EndpointMessageSender.cs +++ b/src/user-tools/midi-console/Midi/EndpointMessageSender.cs @@ -12,32 +12,11 @@ namespace Microsoft.Devices.Midi2.ConsoleApp { internal class EndpointMessageSender { - public static MidiConsoleReturnCode OpenTemporaryConnectionAndSendMidiMessage(string? endpointDeviceId, MidiMessage128 message) + public static MidiConsoleReturnCode OpenTemporaryConnectionAndSendMidiMessage(string? endpointDeviceId, IMidiUniversalPacket message) { - UInt32[] words = { message.Word0, message.Word1, message.Word2, message.Word3 }; + var words = message.GetAllWords(); - return OpenTemporaryConnectionAndSendMidiMessage(endpointDeviceId, message.Timestamp, words); - } - - public static MidiConsoleReturnCode OpenTemporaryConnectionAndSendMidiMessage(string? endpointDeviceId, MidiMessage96 message) - { - UInt32[] words = { message.Word0, message.Word1, message.Word2 }; - - return OpenTemporaryConnectionAndSendMidiMessage(endpointDeviceId, message.Timestamp, words); - } - - public static MidiConsoleReturnCode OpenTemporaryConnectionAndSendMidiMessage(string? endpointDeviceId, MidiMessage64 message) - { - UInt32[] words = { message.Word0, message.Word1 }; - - return OpenTemporaryConnectionAndSendMidiMessage(endpointDeviceId, message.Timestamp, words); - } - - public static MidiConsoleReturnCode OpenTemporaryConnectionAndSendMidiMessage(string? endpointDeviceId, MidiMessage32 message) - { - UInt32[] words = { message.Word0 }; - - return OpenTemporaryConnectionAndSendMidiMessage(endpointDeviceId, message.Timestamp, words); + return OpenTemporaryConnectionAndSendMidiMessage(endpointDeviceId, message.Timestamp, words.ToArray()); } diff --git a/src/user-tools/midi-console/Midi/Midi.csproj b/src/user-tools/midi-console/Midi/Midi.csproj index 4c63474b3..249f62499 100644 --- a/src/user-tools/midi-console/Midi/Midi.csproj +++ b/src/user-tools/midi-console/Midi/Midi.csproj @@ -31,7 +31,7 @@ - + From cd807e2d11a06b7d79b7f67aaf97a0c49348dc1b Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Wed, 14 Feb 2024 02:26:39 -0500 Subject: [PATCH 2/2] Fixed timing bug in metadata listener plugin --- build/staging/version/BundleInfo.wxi | 2 +- .../MidiSample.AppToAppMidi.csproj | 2 +- .../Midi2Client/MidiVirtualEndpointDevice.cpp | 50 +++++++++++++++---- ....EndpointMetadataListenerMidiTransform.cpp | 4 +- 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index 8e3aca7f4..1e43f9ea5 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,4 +1,4 @@ - + diff --git a/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj b/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj index 7bd3f49f4..d88fd0244 100644 --- a/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj +++ b/samples/csharp-net/app-to-app-midi-cs/MidiSample.AppToAppMidi.csproj @@ -29,7 +29,7 @@ - + diff --git a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp index d49220282..6b9c90868 100644 --- a/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp +++ b/src/api/Client/Midi2Client/MidiVirtualEndpointDevice.cpp @@ -104,10 +104,6 @@ namespace winrt::Windows::Devices::Midi2::implementation } - // TODO: Any message sending from inside this function should - // be done on another thread via a queue, like we do in the service - // That way no long-running or recursive callbacks. - _Use_decl_annotations_ void MidiVirtualEndpointDevice::ProcessIncomingMessage( midi2::MidiMessageReceivedEventArgs const& args, @@ -130,10 +126,12 @@ namespace winrt::Windows::Devices::Midi2::implementation if (internal::EndpointDiscoveryFilterRequestsEndpointInfoNotification(filterFlags)) { + internal::LogInfo(__FUNCTION__, L"Sending Endpoint Info Notification"); + // send endpoint info notification auto notification = midi2::MidiStreamMessageBuilder::BuildEndpointInformationNotificationMessage( - 0, + MidiClock::TimestampConstantSendImmediately(), MIDI_PREFERRED_UMP_VERSION_MAJOR, MIDI_PREFERRED_UMP_VERSION_MINOR, m_areFunctionBlocksStatic, @@ -144,7 +142,10 @@ namespace winrt::Windows::Devices::Midi2::implementation false // todo: pull from jr timestamp handling ); - m_endpointConnection.SendMessagePacket(notification); + if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendMessagePacket(notification))) + { + internal::LogGeneralError(__FUNCTION__, L"SendMessagePacket failed - sending endpoint info notification"); + } } if (internal::EndpointDiscoveryFilterRequestsDeviceIdentityNotification(filterFlags)) @@ -154,13 +155,42 @@ namespace winrt::Windows::Devices::Midi2::implementation if (internal::EndpointDiscoveryFilterRequestsEndpointNameNotification(filterFlags)) { + internal::LogInfo(__FUNCTION__, L"Sending Endpoint Name Notification"); + // send endpoint name notification messages + + if (!m_endpointName.empty()) + { + auto nameMessages = midi2::MidiStreamMessageBuilder::BuildEndpointNameNotificationMessages( + MidiClock::TimestampConstantSendImmediately(), + m_endpointName + ); + + if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendMessagePacketList(nameMessages.GetView()))) + { + internal::LogGeneralError(__FUNCTION__, L"SendMessagePacketList failed - sending endpoint name notification list"); + } + } } if (internal::EndpointDiscoveryFilterRequestsProductInstanceIdNotification(filterFlags)) { + internal::LogInfo(__FUNCTION__, L"Sending Endpoint Product Instance Id Notification"); + // send product instance id notification messages - + + if (!m_endpointProductInstanceId.empty()) + { + auto instanceIdMessages = midi2::MidiStreamMessageBuilder::BuildProductInstanceIdNotificationMessages( + MidiClock::TimestampConstantSendImmediately(), + m_endpointProductInstanceId + ); + + if (midi2::MidiEndpointConnection::SendMessageFailed(m_endpointConnection.SendMessagePacketList(instanceIdMessages.GetView()))) + { + internal::LogGeneralError(__FUNCTION__, L"SendMessagePacketList failed - sending product instance id messages"); + } + } } if (internal::EndpointDiscoveryFilterRequestsStreamConfigurationNotification(filterFlags)) @@ -179,6 +209,8 @@ namespace winrt::Windows::Devices::Midi2::implementation if (fbNumber == MIDI_STREAM_MESSAGE_FUNCTION_BLOCK_REQUEST_ALL_FUNCTION_BLOCKS) { + internal::LogInfo(__FUNCTION__, L"Sending All function blocks, as requested"); + // send all function blocks for (uint8_t i = 0; i < (uint8_t)m_functionBlocks.Size(); i++) @@ -193,7 +225,8 @@ namespace winrt::Windows::Devices::Midi2::implementation else { // send single requested function block - + internal::LogInfo(__FUNCTION__, L"Sending only a single function block, as requested"); + if (m_functionBlocks.HasKey(fbNumber)) { auto fb = m_functionBlocks.Lookup(fbNumber); @@ -210,7 +243,6 @@ namespace winrt::Windows::Devices::Midi2::implementation handled = false; } } - } else diff --git a/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp b/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp index b171afb04..4f9fa9f54 100644 --- a/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp +++ b/src/api/Transform/EndpointMetadataListenerTransform/Midi2.EndpointMetadataListenerMidiTransform.cpp @@ -125,7 +125,7 @@ CMidi2EndpointMetadataListenerMidiTransform::Cleanup() } -#define MIDI_ENDPOINT_METADATA_LISTENER_THREAD_SLEEP_DURATION_MS 2000 +#define MIDI_ENDPOINT_METADATA_LISTENER_THREAD_SLEEP_DURATION_MS 10000 void CMidi2EndpointMetadataListenerMidiTransform::QueueWorker() { @@ -215,6 +215,8 @@ CMidi2EndpointMetadataListenerMidiTransform::SendMidiMessage( m_queueMutex.lock(); m_workQueue.push(ump); m_queueMutex.unlock(); + + m_messageProcessorWakeup.SetEvent(); } else {