From ac56c505249a364a538e98d25271b5a221af4de0 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Mon, 12 Feb 2024 03:25:51 -0500 Subject: [PATCH] Working on loopback MIDI --- build/staging/reg/WinRTActivationEntries.cs | 1 + build/staging/reg/WinRTActivationEntries.xml | 5 + build/staging/version/BundleInfo.wxi | 2 +- diagnostics/trace-logging/MidiServices.wprp | 2 + .../service/MidiService.md | 42 ++- ...iServiceConfigurationResponseStatusEnum.md | 26 ++ .../service/MidiServiceEndpointDefinition.md | 24 ++ ...diServiceLoopbackEndpointCreationResult.md | 25 ++ .../service/MidiServicePingResponse.md | 2 +- .../service/MidiServicePingResponseSummary.md | 2 +- .../MidiSessionConnectionInformation.md | 2 +- .../service/MidiSessionInformation.md | 2 +- docs/endpoints/virtual-loopback.md | 57 +++- .../Midi2.KSMidiConfigurationManager.cpp | 3 +- .../Midi2.KSMidiConfigurationManager.h | 2 +- .../Midi2.LoopbackMidiAbstraction.cpp | 7 +- .../Midi2.LoopbackMidiAbstraction.h | 2 +- .../Midi2.LoopbackMidiBidi.cpp | 6 +- ...Midi2.LoopbackMidiConfigurationManager.cpp | 243 ++++++++------ .../Midi2.LoopbackMidiConfigurationManager.h | 2 +- .../abstraction_defs.h | 12 +- .../Midi2.MidiSrvConfigurationManager.cpp | 4 +- .../Midi2.MidiSrvConfigurationManager.h | 2 +- .../Midi2.VirtualMidiConfigurationManager.cpp | 22 +- .../Midi2.VirtualMidiConfigurationManager.h | 2 +- .../abstraction_defs.h | 2 +- .../nuget/Windows.Devices.Midi2.nuspec | 2 +- ...ceMessageProcessingPluginConfiguration.idl | 30 ++ ...idiServiceTransportPluginConfiguration.idl | 25 ++ src/api/Client/Midi2Client/MidiService.cpp | 309 +++++++++++++++++- src/api/Client/Midi2Client/MidiService.h | 16 +- src/api/Client/Midi2Client/MidiService.idl | 36 +- .../MidiServiceConfigurationResponse.cpp | 23 ++ .../MidiServiceConfigurationResponse.h | 21 ++ ...l => MidiServiceConfigurationResponse.idl} | 10 +- ...ServiceConfigurationResponseStatusEnum.idl | 31 ++ ...iServiceLoopbackEndpointCreationResult.cpp | 17 + ...idiServiceLoopbackEndpointCreationResult.h | 46 +++ ...iServiceLoopbackEndpointCreationResult.idl | 25 ++ .../MidiServiceLoopbackEndpointDefinition.cpp | 16 + .../MidiServiceLoopbackEndpointDefinition.h | 38 +++ .../MidiServiceLoopbackEndpointDefinition.idl | 24 ++ ...idiServiceSessionConnectionInformation.cpp | 31 ++ .../MidiServiceSessionConnectionInformation.h | 34 ++ ...diServiceSessionConnectionInformation.idl} | 2 +- .../MidiServiceSessionInformation.cpp | 38 +++ .../MidiServiceSessionInformation.h | 51 +++ ....idl => MidiServiceSessionInformation.idl} | 6 +- src/api/Client/Midi2Client/MidiSession.cpp | 4 +- .../MidiSessionConnectionInformation.cpp | 23 -- .../MidiSessionConnectionInformation.h | 26 -- .../Midi2Client/MidiSessionInformation.cpp | 30 -- .../Midi2Client/MidiSessionInformation.h | 43 --- .../Midi2Client/WinRTActivationEntries.txt | 1 + .../Midi2Client/Windows.Devices.Midi2.vcxproj | 45 ++- .../Windows.Devices.Midi2.vcxproj.filters | 87 +++-- src/api/Client/Midi2Client/pch.h | 6 +- .../AbstractionUtilities/inc/json_helpers.h | 19 +- src/api/Service/Exe/MidiDeviceManager.cpp | 51 +-- src/api/Service/Exe/MidiSrvRPC.idl | 1 + src/api/Service/Exe/MidiSrvRpc.cpp | 3 +- src/api/Service/Inc/MidiDeviceManager.h | 2 + src/api/idl/MidiAbstraction.idl | 1 + src/api/idl/MidiDeviceManagerInterface.idl | 3 +- .../api-package/WindowsMidiServices.wxs | 2 +- .../Endpoint/EndpointPropertiesCommand.cs | 31 +- src/user-tools/midi-console/Midi/Midi.csproj | 2 +- 67 files changed, 1362 insertions(+), 350 deletions(-) create mode 100644 docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceConfigurationResponseStatusEnum.md create mode 100644 docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceEndpointDefinition.md create mode 100644 docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceLoopbackEndpointCreationResult.md create mode 100644 src/api/Client/Midi2Client/IMidiServiceMessageProcessingPluginConfiguration.idl create mode 100644 src/api/Client/Midi2Client/IMidiServiceTransportPluginConfiguration.idl create mode 100644 src/api/Client/Midi2Client/MidiServiceConfigurationResponse.cpp create mode 100644 src/api/Client/Midi2Client/MidiServiceConfigurationResponse.h rename src/api/Client/Midi2Client/{IMidiTransportSettingsData.idl => MidiServiceConfigurationResponse.idl} (70%) create mode 100644 src/api/Client/Midi2Client/MidiServiceConfigurationResponseStatusEnum.idl create mode 100644 src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.cpp create mode 100644 src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.h create mode 100644 src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.idl create mode 100644 src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.cpp create mode 100644 src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.h create mode 100644 src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.idl create mode 100644 src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.cpp create mode 100644 src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.h rename src/api/Client/Midi2Client/{MidiSessionConnectionInformation.idl => MidiServiceSessionConnectionInformation.idl} (92%) create mode 100644 src/api/Client/Midi2Client/MidiServiceSessionInformation.cpp create mode 100644 src/api/Client/Midi2Client/MidiServiceSessionInformation.h rename src/api/Client/Midi2Client/{MidiSessionInformation.idl => MidiServiceSessionInformation.idl} (80%) delete mode 100644 src/api/Client/Midi2Client/MidiSessionConnectionInformation.cpp delete mode 100644 src/api/Client/Midi2Client/MidiSessionConnectionInformation.h delete mode 100644 src/api/Client/Midi2Client/MidiSessionInformation.cpp delete mode 100644 src/api/Client/Midi2Client/MidiSessionInformation.h diff --git a/build/staging/reg/WinRTActivationEntries.cs b/build/staging/reg/WinRTActivationEntries.cs index 2a7b2d9f..d39c8bbb 100644 --- a/build/staging/reg/WinRTActivationEntries.cs +++ b/build/staging/reg/WinRTActivationEntries.cs @@ -38,6 +38,7 @@ class RegistryEntries new RegEntry{ ClassName="Windows.Devices.Midi2.IMidiEndpointConnectionStatics", ActivationType=0, Threading=0, TrustLevel=0 }, new RegEntry{ ClassName="Windows.Devices.Midi2.MidiService", ActivationType=0, Threading=0, TrustLevel=0 }, new RegEntry{ ClassName="Windows.Devices.Midi2.IMidiServiceStatics", ActivationType=0, Threading=0, TrustLevel=0 }, + new RegEntry{ ClassName="Windows.Devices.Midi2.MidiServiceLoopbackEndpointDefinition", ActivationType=0, Threading=0, TrustLevel=0 }, new RegEntry{ ClassName="Windows.Devices.Midi2.MidiClock", ActivationType=0, Threading=0, TrustLevel=0 }, new RegEntry{ ClassName="Windows.Devices.Midi2.IMidiClockStatics", ActivationType=0, Threading=0, TrustLevel=0 }, new RegEntry{ ClassName="Windows.Devices.Midi2.MidiChannel", ActivationType=0, Threading=0, TrustLevel=0 }, diff --git a/build/staging/reg/WinRTActivationEntries.xml b/build/staging/reg/WinRTActivationEntries.xml index 62882cb0..0d8ca6d4 100644 --- a/build/staging/reg/WinRTActivationEntries.xml +++ b/build/staging/reg/WinRTActivationEntries.xml @@ -163,6 +163,11 @@ threading="Both" trustLevel="Base" /> + - + diff --git a/diagnostics/trace-logging/MidiServices.wprp b/diagnostics/trace-logging/MidiServices.wprp index 68ed85ed..1246cd8b 100644 --- a/diagnostics/trace-logging/MidiServices.wprp +++ b/diagnostics/trace-logging/MidiServices.wprp @@ -20,6 +20,7 @@ + @@ -45,6 +46,7 @@ + diff --git a/docs/developer-docs/Windows.Devices.Midi2/service/MidiService.md b/docs/developer-docs/Windows.Devices.Midi2/service/MidiService.md index 8e1f8c5a..e2f08846 100644 --- a/docs/developer-docs/Windows.Devices.Midi2/service/MidiService.md +++ b/docs/developer-docs/Windows.Devices.Midi2/service/MidiService.md @@ -10,18 +10,44 @@ has_children: false The MidiService class contains a number of static functions which enable working with the service outside of a specific session. -## Static Functions +## Static Functions : Reporting | Static Function | Description | |---|---| -| `PingService(pingCount)` | Send one or more ping messages to the ping endpoint and report on the status and time. Return if the responses are not received in a calculated timeout period. | -| `PingService(pingCount, timeoutMilliseconds)` | Send one or more ping messages to the ping endpoint and report on the status and time. Return if responses are not received in the specified timeout period. | | `GetInstalledTransportPlugins()` | Returns a list of `MidiServiceTransportPluginInformation` representing all service transport plugins (also called Abstractions) | | `GetInstalledMessageProcessingPlugins()` | Returns a list of `MidiServiceMessageProcessingPluginInformation` objects representing all service message processing plugins (also called Transforms) | | `GetActiveSessions()` | Returns a list of `MidiSessionInformation` detailing all active Windows MIDI Services sessions on this PC. | -| `UpdateRuntimeConfiguration(configurationUpdate)` | Used by client-side SDK components for some transports and other plugins, and by the MIDI Settings app. The format of the data is dependent upon the target specified in the data. Use with caution. For more information, see the [config JSON documentation](../../../config-json.md) | -## A note on the ping process +## Static Functions : Loopback Endpoints + +| Static Function | Description | +|---|---| +| `CreateTemporaryLoopbackEndpoints(associationId, endpointA, endpointB)` | Create a pair of loopback endpoints which will live until removed through the API or the service is restarted. | +| `RemoveTemporaryLoopbackEndpoints(associationId)` | Remove a pair of temporary loopback endpoints. | + +Applications creating endpoints for app-to-app MIDI should generally use the Virtual Device support built into the API. However, applications may need to create lightweight loopback endpoints without the protocol negotiation, MIDI 2.0 discovery process, and lifetime management provided by the Virtual Device support. For those scenarios, we have a simple loopback endpoint type. + +Loopback endpoints created by the user and stored in the configuration file will persist after the service is restarted or the PC rebooted. Loopback endpoints created through this API call are temporary, and will disappear if the service is restarted. In both cases, this feature requires that the loopback endpoint transport is installed and enabled. + +## Static Functions : Runtime Configuration + +| Static Function | Description | +|---|---| +| `UpdateTransportPluginConfiguration(configurationUpdate)` | Sends an update to the service to be used by a transport plugin ("Abstraction") | +| `UpdateProcessingPluginConfiguration(configurationUpdate)` | Sends an update to the service to be used by a message processing plugin ("Transform") | + +For plugins which support updates at runtime, developers of those plugins should create configuration WinRT types which implement the required configuration interfaces, and create the JSON that is used in the service. In this way, third-party service transport and message processing plugins can be created and configured without changes to the API. + +> Note: In version 1 of the API, only transports can be configured at runtime. We're working on enabling configuration of message processing plugins. + +## Static Functions : Service Health + +| Static Function | Description | +|---|---| +| `PingService(pingCount)` | Send one or more ping messages to the ping endpoint and report on the status and time. Return if the responses are not received in a calculated timeout period. | +| `PingService(pingCount, timeoutMilliseconds)` | Send one or more ping messages to the ping endpoint and report on the status and time. Return if responses are not received in the specified timeout period. | + +### The ping process Pinging the Windows service uses the same mechanism as sending any UMP message. The actual message sent is a prioprietary message. (At the time this was created, there was no standard MIDI 2.0 UMP ping message). The message itself is sent to the diagnostics endpoint in the service, which is implemented like any other transport. Therefore, the speed of the pings here and the success of the ping process is a reasonable indicator of service, cross-process queue, and client API health. @@ -33,12 +59,6 @@ Here's an example of ping responses through the MIDI console app ![MIDI Console Ping](./console-ping.png) -## A note on updating runtime configuration - -In order to foster an open plugin ecosystem, we need a way to get settings and configuration for those plugins up to them in the Windows service. The way we've chosen to do that is JSON, because that same JSON, when not transient in nature, can also be saved into the permanent configuration file for the active MIDI setup. - -The runtime configuration update should only be used by code which understands exactly what will be done with the data, and can handle the response which is returned. It is not a general API endpoint, but is designed for components which will extend Windows MIDI Services. - ## IDL [MidiService IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiService.idl) diff --git a/docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceConfigurationResponseStatusEnum.md b/docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceConfigurationResponseStatusEnum.md new file mode 100644 index 00000000..17ca1bd3 --- /dev/null +++ b/docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceConfigurationResponseStatusEnum.md @@ -0,0 +1,26 @@ +--- +layout: api_page +title: MidiServiceConfigurationResponseStatus +parent: Service +grand_parent: Windows.Devices.Midi2 API +has_children: false +--- + +# MidiServiceConfigurationResponseStatus Enumeration + +Indicates success or failure mode for configuring an endpoint or message processing plugin in the service. + +## Properties + +| Property | Value | Description | +| -------- | ------- | ------ | +| `Success` | `0` | The entire operation succeeded | +| `ErrorTargetNotFound` | `404` | The target of the change was not found. This could be the plugin itself, or if the configuration requires an endpoint, the endpoint instance. | +| `ErrorJsonNullOrEmpty` | `600` | The supplied JSON was null or empty. | +| `ErrorProcessingJson` | `601` | The supplied JSON was invalid in some way. It could be malformed or have missing required data or keys. | +| `ErrorNotImplemented` | `2600` | One or more of the requests are not implemented by the service or by the plugin. | +| `ErrorOther` | `9999` | All other errors | + +## IDL + +[MidiServiceConfigurationResponseStatus IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiServiceConfigurationResponseStatusEnum.idl) diff --git a/docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceEndpointDefinition.md b/docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceEndpointDefinition.md new file mode 100644 index 00000000..cac311b2 --- /dev/null +++ b/docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceEndpointDefinition.md @@ -0,0 +1,24 @@ +--- +layout: api_page +title: MidiServiceLoopbackEndpointDefinition +parent: Service +grand_parent: Windows.Devices.Midi2 API +has_children: false +--- + +# MidiServiceLoopbackEndpointDefinition + +This class defines the properties of an endpoint which can be created at runtime. For example, a loopback endpoint. + +## Properties + +| Property | Description | +|---|---| +| `Name` | The name of the endpoint. | +| `UniqueId` | A short unique identifier for this endpoint. This is used in creating the id. Keep to 32 characters or fewer (32 characters is the length of a no-symbols GUID). If, when combined with the generated loopback A/B differentiator prefix, this id is not unique among all loopback endpoints, endpoint creation will fail. | +| `Description` | Optional description for the endpoint | + +## IDL + +[MidiServiceLoopbackEndpointDefinition IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.idl) + diff --git a/docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceLoopbackEndpointCreationResult.md b/docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceLoopbackEndpointCreationResult.md new file mode 100644 index 00000000..f3e89c33 --- /dev/null +++ b/docs/developer-docs/Windows.Devices.Midi2/service/MidiServiceLoopbackEndpointCreationResult.md @@ -0,0 +1,25 @@ +--- +layout: api_page +title: MidiServiceLoopbackEndpointCreationResult +parent: Service +grand_parent: Windows.Devices.Midi2 API +has_children: false +--- + +# MidiServiceLoopbackEndpointCreationResult + +This class represents the results of an attempt to create runtime loopback endpoints + +## Properties + +| Property | Description | +|---|---| +| `Success` | True if the creation of both endpoints was a success | +| `AssociatioNId` | The GUID which associatiates the two endpoints. Provided during creation time. | +| `EndpointDeviceIdA` | The full endpoint device id `\\SWD\...` for the endpoint identified as the "A" side of the loopback | +| `EndpointDeviceIdB` | The full endpoint device id `\\SWD\...` for the endpoint identified as the "B" side of the loopback | + +## IDL + +[MidiServiceLoopbackEndpointCreationResult IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.idl) + diff --git a/docs/developer-docs/Windows.Devices.Midi2/service/MidiServicePingResponse.md b/docs/developer-docs/Windows.Devices.Midi2/service/MidiServicePingResponse.md index ef5afaea..a0b29083 100644 --- a/docs/developer-docs/Windows.Devices.Midi2/service/MidiServicePingResponse.md +++ b/docs/developer-docs/Windows.Devices.Midi2/service/MidiServicePingResponse.md @@ -9,7 +9,7 @@ has_children: false This class represents a single ping message response. This is used to assess health and performance of the Windows service. -## Static Functions +## Properties | Property | Description | |---|---| diff --git a/docs/developer-docs/Windows.Devices.Midi2/service/MidiServicePingResponseSummary.md b/docs/developer-docs/Windows.Devices.Midi2/service/MidiServicePingResponseSummary.md index 87f3f881..c44ede20 100644 --- a/docs/developer-docs/Windows.Devices.Midi2/service/MidiServicePingResponseSummary.md +++ b/docs/developer-docs/Windows.Devices.Midi2/service/MidiServicePingResponseSummary.md @@ -10,7 +10,7 @@ has_children: false This class represents a summary of the ping attempts against the Windows service. -## Static Functions +## Properties | Property | Description | |---|---| diff --git a/docs/developer-docs/Windows.Devices.Midi2/service/MidiSessionConnectionInformation.md b/docs/developer-docs/Windows.Devices.Midi2/service/MidiSessionConnectionInformation.md index 53836917..3995ab5d 100644 --- a/docs/developer-docs/Windows.Devices.Midi2/service/MidiSessionConnectionInformation.md +++ b/docs/developer-docs/Windows.Devices.Midi2/service/MidiSessionConnectionInformation.md @@ -10,7 +10,7 @@ has_children: false This class represents an open connection in a Windows MIDI Services session. This is an informational class only for reporting system-wide connection usage. -## Static Functions +## Properties | Property | Description | |---|---| diff --git a/docs/developer-docs/Windows.Devices.Midi2/service/MidiSessionInformation.md b/docs/developer-docs/Windows.Devices.Midi2/service/MidiSessionInformation.md index a63fbe3a..149352f9 100644 --- a/docs/developer-docs/Windows.Devices.Midi2/service/MidiSessionInformation.md +++ b/docs/developer-docs/Windows.Devices.Midi2/service/MidiSessionInformation.md @@ -10,7 +10,7 @@ has_children: false This class represents an open Windows MIDI Services session. -## Static Functions +## Properties | Property | Description | |---|---| diff --git a/docs/endpoints/virtual-loopback.md b/docs/endpoints/virtual-loopback.md index b7aad320..6ebf1303 100644 --- a/docs/endpoints/virtual-loopback.md +++ b/docs/endpoints/virtual-loopback.md @@ -25,14 +25,67 @@ If you want to have loopback endpoints which are always available for routing be As with all configuration file changes, we recommend using the Windows MIDI Services Settings application, once we make that available. For now, you may edit the JSON directly. But please note that JSON is quite unforgiving: the format is specific, and all keys (including the GUIDs and property names) are case-sensitive. In addition, there's no usable provision for comments in a JSON file, so we can't include examples in the file itself. -That out of the way, here's the configuration section for the Virtual Loopback MIDI endpoints. +That out of the way, here's an example configuration section for the Virtual Loopback MIDI endpoints. ```json -todo +"endpointTransportPluginSettings": +{ + "{942BF02D-93C0-4EA8-B03E-D51156CA75E1}": + { + "_comment": "Loopback MIDI", + "create": + { + "{0C1B3439-593F-4B7A-8950-4698D97B0897}": + { + "_comment" : "the above GUID is the unique association Id for this pair", + "endpointA": + { + "name": "Perm Loopback 1A", + "description": "This is a loopback I created in the configuration file", + "uniqueId": "3263827" + }, + "endpointB": + { + "name": "Perm Loopback 1B", + "description": "This is the b-side of the loopback I created in the configuration file", + "uniqueId": "3263827" + } + }, + "{B21B4973-3F85-48A0-8BA3-B35F44683D36}": + { + "_comment" : "the above GUID is the unique association Id for this pair", + "endpointA": + { + "name": "Perm Loopback 2A", + "description": "This is a loopback I created in the configuration file", + "uniqueId": "5150-1984" + }, + "endpointB": + { + "name": "Perm Loopback 2B", + "description": "This is the b-side of the loopback I created in the configuration file", + "uniqueId": "OU812" + } + } + } + } +} ``` +Each loopback endpoint pair is identified by a GUID for the association id. The association GUID must be a valid unique GUID and shall not be an empty (all zeroes) GUID. + +| Key | Description | +| -------- | ----- | +| (Association Id) | Unique GUID for this pair of endpoints. The GUID itself is the property key under the "create" node. | +| endpointA | Data for the first endpoint | +| endpointB | Data for the second endpoint | +| (endpoint) name | Required. This becomes the transport-supplied name for the loopback endpoint. | +| (endpoint) description | Optional. This becomes the transport-supplied description for the loopback endpoint. | +| (endpoint) uniqueId | Required. This is a short (32 characters or fewer) case-insensitive unique Id for the endpoint. When combined with the loopback A/B prefixes in the service, it must be unique across all loopback endpoints in Windows. You can use the same unique id for each endpoint in the same pair, but not the same as other pairs. | + + # Implementation Internally, the Virtual Loopback is implemented as two endpoints which are cross-wired, so anything sent to Loopback A arrives on the input of Loopback B, and vice versa. Each declared pair has an exclusive relationship, and there's no practical limit to the number of loopback pairs you can define. diff --git a/src/api/Abstraction/KSAbstraction/Midi2.KSMidiConfigurationManager.cpp b/src/api/Abstraction/KSAbstraction/Midi2.KSMidiConfigurationManager.cpp index 7381b30d..50a984cd 100644 --- a/src/api/Abstraction/KSAbstraction/Midi2.KSMidiConfigurationManager.cpp +++ b/src/api/Abstraction/KSAbstraction/Midi2.KSMidiConfigurationManager.cpp @@ -32,11 +32,12 @@ CMidi2KSMidiConfigurationManager::Initialize( _Use_decl_annotations_ HRESULT -CMidi2KSMidiConfigurationManager::UpdateConfiguration(LPCWSTR configurationJson, BSTR* response) +CMidi2KSMidiConfigurationManager::UpdateConfiguration(LPCWSTR configurationJson, BOOL IsFromConfigurationFile, BSTR* response) { OutputDebugString(L"\n" __FUNCTION__); UNREFERENCED_PARAMETER(configurationJson); + UNREFERENCED_PARAMETER(IsFromConfigurationFile); // temp. Also, client needs to free this. diff --git a/src/api/Abstraction/KSAbstraction/Midi2.KSMidiConfigurationManager.h b/src/api/Abstraction/KSAbstraction/Midi2.KSMidiConfigurationManager.h index 9fc53dfd..f3541e6f 100644 --- a/src/api/Abstraction/KSAbstraction/Midi2.KSMidiConfigurationManager.h +++ b/src/api/Abstraction/KSAbstraction/Midi2.KSMidiConfigurationManager.h @@ -17,7 +17,7 @@ class CMidi2KSMidiConfigurationManager : public: STDMETHOD(Initialize(_In_ GUID abstractionGuid, _In_ IUnknown* deviceManagerInterface)); - STDMETHOD(UpdateConfiguration(_In_ LPCWSTR configurationJson, _Out_ BSTR* response)); + STDMETHOD(UpdateConfiguration(_In_ LPCWSTR configurationJson, _In_ BOOL IsFromConfigurationFile, _Out_ BSTR* response)); STDMETHOD(Cleanup)(); private: diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.cpp b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.cpp index dea6b079..cae2463f 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.cpp +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.cpp @@ -16,7 +16,12 @@ CMidi2LoopbackMidiAbstraction::Activate( void **Interface ) { - OutputDebugString(L"" __FUNCTION__ " Enter"); + TraceLoggingWrite( + MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this") + ); RETURN_HR_IF(E_INVALIDARG, nullptr == Interface); diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.h b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.h index c463498d..3de84271 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.h +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiAbstraction.h @@ -15,7 +15,7 @@ class MidiLoopbackMidiAbstractionTelemetryProvider : public wil::TraceLoggingPro "Microsoft.Windows.Midi2.LoopbackMidiAbstraction", // 5a36c0b3-5d4c-545b-b9e7-461470957699 from hash of name using: // PS> [System.Diagnostics.Tracing.EventSource]::new("Microsoft.Windows.Midi2.LoopbackMidiAbstraction").Guid - (0x5a36c0b3,0x5d4,0x545b,0xb9,0xe7,0x46,0x14,0x70,0x95,0x76,0x99)) + (0x5a36c0b3,0x5d4c,0x545b,0xb9,0xe7,0x46,0x14,0x70,0x95,0x76,0x99)) }; using namespace ATL; diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.cpp b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.cpp index b344db39..1e46e486 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.cpp +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.cpp @@ -35,7 +35,8 @@ CMidi2LoopbackMidiBiDi::Initialize( // TODO: This should use SWD properties and not a string search - if (internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_LOOP_INSTANCE_ID_A_PREFIX)) + if (internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_PERM_LOOP_INSTANCE_ID_A_PREFIX) || + internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_TEMP_LOOP_INSTANCE_ID_A_PREFIX)) { TraceLoggingWrite( MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), @@ -50,7 +51,8 @@ CMidi2LoopbackMidiBiDi::Initialize( m_isEndpointA = true; } - else if (internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_LOOP_INSTANCE_ID_B_PREFIX)) + else if (internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_PERM_LOOP_INSTANCE_ID_B_PREFIX) || + internal::EndpointInterfaceIdContainsString(m_endpointId, MIDI_TEMP_LOOP_INSTANCE_ID_B_PREFIX)) { TraceLoggingWrite( MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiConfigurationManager.cpp b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiConfigurationManager.cpp index be19d4e5..843cee60 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiConfigurationManager.cpp +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiConfigurationManager.cpp @@ -34,7 +34,8 @@ CMidi2LoopbackMidiConfigurationManager::Initialize( _Use_decl_annotations_ HRESULT CMidi2LoopbackMidiConfigurationManager::UpdateConfiguration( - LPCWSTR ConfigurationJsonSection, + LPCWSTR ConfigurationJsonSection, + BOOL IsFromConfigurationFile, BSTR* Response ) { @@ -52,123 +53,179 @@ CMidi2LoopbackMidiConfigurationManager::UpdateConfiguration( json::JsonObject jsonObject; json::JsonObject responseObject; - json::JsonArray createdDevicesResponseArray; + // default to failure + internal::JsonSetBoolProperty(responseObject, MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_SUCCESS_PROPERTY_KEY, false); - if (!json::JsonObject::TryParse(winrt::to_hstring(ConfigurationJsonSection), jsonObject)) - { - TraceLoggingWrite( - MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), - __FUNCTION__, - TraceLoggingLevel(WINEVENT_LEVEL_ERROR), - TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Failed to parse Configuration JSON", "message"), - TraceLoggingWideString(ConfigurationJsonSection, "json") - ); - - return E_FAIL; - } - - - - - - // TODO - - - - - - - - auto createArray = internal::JsonGetArrayProperty(jsonObject, MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_CREATE_ARRAY_KEY); - - if (createArray == nullptr || createArray.Size() == 0) - { - // nothing in the array. Maybe we're looking at update or delete - return S_OK; - } - // iterate through all the work we need to do. Just - // "create" instructions, in this case. - for (uint32_t i = 0; i < createArray.Size(); i++) + try { - auto jsonEntry = (createArray.GetObjectAt(i)); - if (jsonEntry) + if (!json::JsonObject::TryParse(winrt::to_hstring(ConfigurationJsonSection), jsonObject)) { - // TODO + TraceLoggingWrite( + MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Failed to parse Configuration JSON", "message"), + TraceLoggingWideString(ConfigurationJsonSection, "json") + ); + + internal::JsonStringifyObjectToOutParam(responseObject, &Response); + + return E_FAIL; + } - MidiLoopbackDevice device; + // I was tempted to call this the Prefix Code. KHAN!! + std::wstring instanceIdPrefixA = IsFromConfigurationFile ? MIDI_PERM_LOOP_INSTANCE_ID_A_PREFIX : MIDI_TEMP_LOOP_INSTANCE_ID_A_PREFIX; + std::wstring instanceIdPrefixB = IsFromConfigurationFile ? MIDI_PERM_LOOP_INSTANCE_ID_B_PREFIX : MIDI_TEMP_LOOP_INSTANCE_ID_B_PREFIX; + // we should probably set a property based on this as well. - device.DefinitionA.AssociationId = internal::JsonGetWStringProperty( - jsonEntry, - MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_ASSOCIATION_ID_PROPERTY_KEY, - L""); - device.DefinitionB.AssociationId = device.DefinitionA.AssociationId; - std::wstring uniqueIdentifier = internal::JsonGetWStringProperty( - jsonEntry, - MIDI_CONFIG_JSON_ENDPOINT_COMMON_UNIQUE_ID_PROPERTY, - L""); + auto createObject = internal::JsonGetObjectProperty(jsonObject, MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_CREATE_KEY, json::JsonObject{}); - // TODO: May want to rethink the json here. Can user specify two endpoints, with full names etc? - // If so, then just need two endpoint buckets and no need for the instance ID prefix. + // Create ---------------------------------- -// device.DefinitionA.EndpointUniqueIdentifier = MIDI_LOOP_INSTANCE_ID_A_PREFIX + uniqueIdentifier; -// device.DefinitionB.EndpointUniqueIdentifier = MIDI_LOOP_INSTANCE_ID_B_PREFIX + uniqueIdentifier; + if (createObject.Size() > 0) + { + auto o = createObject.First(); + + while (o.HasCurrent()) + { + std::shared_ptr definitionA = std::make_shared(); + std::shared_ptr definitionB = std::make_shared(); + + auto associationObj = o.Current().Value().as(); + + definitionA->AssociationId = o.Current().Key(); + + auto endpointAObject = internal::JsonGetObjectProperty(associationObj, MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_ENDPOINT_A_KEY, nullptr); + auto endpointBObject = internal::JsonGetObjectProperty(associationObj, MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_ENDPOINT_B_KEY, nullptr); + + if (endpointAObject != nullptr && endpointBObject != nullptr) + { + definitionA->EndpointName = internal::JsonGetWStringProperty(endpointAObject, MIDI_CONFIG_JSON_ENDPOINT_COMMON_NAME_PROPERTY, L""); + definitionA->EndpointName = internal::JsonGetWStringProperty(endpointAObject, MIDI_CONFIG_JSON_ENDPOINT_COMMON_DESCRIPTION_PROPERTY, L""); + definitionA->EndpointName = internal::JsonGetWStringProperty(endpointAObject, MIDI_CONFIG_JSON_ENDPOINT_COMMON_UNIQUE_ID_PROPERTY, L""); + definitionA->InstanceIdPrefix = instanceIdPrefixA; + + definitionB->EndpointName = internal::JsonGetWStringProperty(endpointBObject, MIDI_CONFIG_JSON_ENDPOINT_COMMON_NAME_PROPERTY, L""); + definitionB->EndpointName = internal::JsonGetWStringProperty(endpointBObject, MIDI_CONFIG_JSON_ENDPOINT_COMMON_DESCRIPTION_PROPERTY, L""); + definitionB->EndpointName = internal::JsonGetWStringProperty(endpointBObject, MIDI_CONFIG_JSON_ENDPOINT_COMMON_UNIQUE_ID_PROPERTY, L""); + definitionB->InstanceIdPrefix = instanceIdPrefixB; + + + if (SUCCEEDED(AbstractionState::Current().GetEndpointManager()->CreateEndpointPair(definitionA, definitionB))) + { + TraceLoggingWrite( + MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Loopback endpoint pair created", "message") + ); + + // all good + + internal::JsonSetBoolProperty( + responseObject, + MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_SUCCESS_PROPERTY_KEY, + true); + + // update the return json with the new Ids + + internal::JsonSetWStringProperty( + responseObject, + MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_CREATED_ENDPOINT_A_ID_KEY, + definitionA->CreatedEndpointInterfaceId); + + internal::JsonSetWStringProperty( + responseObject, + MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_CREATED_ENDPOINT_B_ID_KEY, + definitionB->CreatedEndpointInterfaceId); + } + else + { + // we failed to create the endpoints. Exit and return a fail. + TraceLoggingWrite( + MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Failed to create endpoints", "message") + ); + + } + } + else + { + // couldn't get the endpointA or endpointB objects. Exit and return a fail + + TraceLoggingWrite( + MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Failed to get one or both endpoints from the JSON", "message") + ); + + } + + o.MoveNext(); + } + } + else + { + // nothing to create. + } - //deviceEntry.BaseEndpointName = internal::JsonGetWStringProperty( - // jsonEntry, - // MIDI_CONFIG_JSON_ENDPOINT_COMMON_NAME_PROPERTY, - // L""); + //auto deleteArray = internal::JsonGetArrayProperty(jsonObject, MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_REMOVE_KEY); - //deviceEntry.Description = internal::JsonGetWStringProperty( - // jsonEntry, - // MIDI_CONFIG_JSON_ENDPOINT_COMMON_DESCRIPTION_PROPERTY, - // L""); + //// Remove ---------------------------------- - // TODO: if no association id, or it already exists in the table, bail + //if (deleteArray.Size() > 0) + //{ + // // TODO : Delete endpoints if they aren't in the config file - // TODO: if no unique Id, bail or maybe generate one + //} + //else + //{ + // // nothing to remove. + //} - // TODO: if a unique id and it's larger than the max length, truncate it - // create the device-side endpoint - // LOG_IF_FAILED(AbstractionState::Current().GetEndpointManager()->CreateDeviceSideEndpoint(deviceEntry)); + //auto updateArray = internal::JsonGetArrayProperty(jsonObject, MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_UPDATE_KEY); + //// Update ---------------------------------- - // TODO: This should have the association Id or something in it for the client to make sense of it - //auto singleResponse = internal::JsonCreateSingleWStringPropertyObject( - // MIDI_CONFIG_JSON_ENDPOINT_VIRTUAL_DEVICE_RESPONSE_CREATED_ID_PROPERTY_KEY, - // deviceEntry.CreatedDeviceEndpointId); + //if (updateArray.Size() > 0) + //{ + // // TODO + //} + //else + //{ + // // TODO : Update endpoints + //} - // createdDevicesResponseArray.Append(singleResponse); - } } + catch (...) + { + TraceLoggingWrite( + MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Exception processing json", "message"), + TraceLoggingWideString(ConfigurationJsonSection, "json") + ); - responseObject.SetNamedValue( - MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_CREATED_DEVICES_ARRAY_KEY, - createdDevicesResponseArray); - - // TODO: Process all "update" and "remove" instructions - - - // TODO: Actual Success or fail response - internal::JsonSetBoolProperty( - responseObject, - MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_SUCCESS_PROPERTY_KEY, - true); + internal::JsonStringifyObjectToOutParam(responseObject, &Response); - TraceLoggingWrite( - MidiLoopbackMidiAbstractionTelemetryProvider::Provider(), - __FUNCTION__, - TraceLoggingLevel(WINEVENT_LEVEL_INFO), - TraceLoggingPointer(this, "this"), - TraceLoggingWideString(responseObject.Stringify().c_str()) - ); + return E_FAIL; + } // return the json with the information the client will need internal::JsonStringifyObjectToOutParam(responseObject, &Response); diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiConfigurationManager.h b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiConfigurationManager.h index c0eb4cc3..fffe1ede 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiConfigurationManager.h +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiConfigurationManager.h @@ -17,7 +17,7 @@ class CMidi2LoopbackMidiConfigurationManager : { public: STDMETHOD(Initialize(_In_ GUID AbstractionId, _In_ IUnknown* MidiDeviceManager)); - STDMETHOD(UpdateConfiguration(_In_ LPCWSTR ConfigurationJsonSection, _Out_ BSTR* Response)); + STDMETHOD(UpdateConfiguration(_In_ LPCWSTR ConfigurationJsonSection, _In_ BOOL IsFromConfigurationFile, _Out_ BSTR* Response)); STDMETHOD(Cleanup)(); private: diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h b/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h index abd67e07..35cdd38f 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h +++ b/src/api/Abstraction/LoopbackMidiAbstraction/abstraction_defs.h @@ -12,16 +12,20 @@ // The full Id comes back from the swdevicecreate callback #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_" + +#define MIDI_PERM_LOOP_INSTANCE_ID_A_PREFIX L"MIDIU_LOOP_A_" +#define MIDI_PERM_LOOP_INSTANCE_ID_B_PREFIX L"MIDIU_LOOP_B_" + +#define MIDI_TEMP_LOOP_INSTANCE_ID_A_PREFIX L"MIDIU_LOOP_A_RT_" +#define MIDI_TEMP_LOOP_INSTANCE_ID_B_PREFIX L"MIDIU_LOOP_B_RT_" + // 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 LOOPBACK_PARENT_ROOT L"HTREE\\ROOT\\0" + #define TRANSPORT_ENUMERATOR L"MIDISRV" diff --git a/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvConfigurationManager.cpp b/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvConfigurationManager.cpp index 1cf80f12..8e0f9ad7 100644 --- a/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvConfigurationManager.cpp +++ b/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvConfigurationManager.cpp @@ -12,7 +12,7 @@ _Use_decl_annotations_ HRESULT -CMidi2MidiSrvConfigurationManager::UpdateConfiguration(LPCWSTR configurationJson, BSTR* response) +CMidi2MidiSrvConfigurationManager::UpdateConfiguration(LPCWSTR configurationJson, BOOL IsFromConfigurationFile, BSTR* response) { TraceLoggingWrite( MidiSrvAbstractionTelemetryProvider::Provider(), @@ -31,7 +31,7 @@ CMidi2MidiSrvConfigurationManager::UpdateConfiguration(LPCWSTR configurationJson { // RPC calls are placed in a lambda to work around compiler error C2712, limiting use of try/except blocks // with structured exception handling. - RpcTryExcept RETURN_IF_FAILED(MidiSrvUpdateConfiguration(bindingHandle.get(), configurationJson, response)); + RpcTryExcept RETURN_IF_FAILED(MidiSrvUpdateConfiguration(bindingHandle.get(), configurationJson, IsFromConfigurationFile, response)); RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) RETURN_IF_FAILED(HRESULT_FROM_WIN32(RpcExceptionCode())); RpcEndExcept return S_OK; diff --git a/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvConfigurationManager.h b/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvConfigurationManager.h index c62243e3..a182c039 100644 --- a/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvConfigurationManager.h +++ b/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvConfigurationManager.h @@ -15,7 +15,7 @@ class CMidi2MidiSrvConfigurationManager : { public: STDMETHOD(Initialize(_In_ GUID abstractionGuid, _In_ IUnknown* deviceManagerInterface)); - STDMETHOD(UpdateConfiguration(_In_ LPCWSTR configurationJson, _Out_ BSTR* response)); + STDMETHOD(UpdateConfiguration(_In_ LPCWSTR configurationJson, _In_ BOOL IsFromConfigurationFile, _Out_ BSTR* response)); STDMETHOD(Cleanup)(); private: diff --git a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiConfigurationManager.cpp b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiConfigurationManager.cpp index d42bdecb..7da203ff 100644 --- a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiConfigurationManager.cpp +++ b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiConfigurationManager.cpp @@ -35,6 +35,7 @@ _Use_decl_annotations_ HRESULT CMidi2VirtualMidiConfigurationManager::UpdateConfiguration( LPCWSTR ConfigurationJsonSection, + BOOL IsFromConfigurationFile, BSTR* Response ) { @@ -47,6 +48,22 @@ CMidi2VirtualMidiConfigurationManager::UpdateConfiguration( ); + // This abstraction doesn't support creating endpoints from the configuration file. + // They are for runtime creation only. + if (IsFromConfigurationFile) + { + TraceLoggingWrite( + MidiVirtualMidiAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Virtual endpoints can be created only at runtime through the API, not from the configuration file.", "message") + ); + + return E_FAIL; + } + + if (ConfigurationJsonSection == nullptr) return S_OK; //if (ConfigurationJsonSection == L"") return S_OK; @@ -74,6 +91,9 @@ CMidi2VirtualMidiConfigurationManager::UpdateConfiguration( if (createArray == nullptr || createArray.Size() == 0) { // nothing in the array. Maybe we're looking at update or delete + + // TODO: Set the response to something meaningful here + return S_OK; } @@ -94,7 +114,7 @@ CMidi2VirtualMidiConfigurationManager::UpdateConfiguration( deviceEntry.ShortUniqueId = internal::JsonGetWStringProperty( jsonEntry, - MIDI_CONFIG_JSON_ENDPOINT_VIRTUAL_DEVICE_UNIQUE_ID_PROPERTY_KEY, + MIDI_CONFIG_JSON_ENDPOINT_COMMON_UNIQUE_ID_PROPERTY, L""); deviceEntry.BaseEndpointName = internal::JsonGetWStringProperty( diff --git a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiConfigurationManager.h b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiConfigurationManager.h index 4dbdd8dd..05ba684a 100644 --- a/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiConfigurationManager.h +++ b/src/api/Abstraction/VirtualMidiAbstraction/Midi2.VirtualMidiConfigurationManager.h @@ -17,7 +17,7 @@ class CMidi2VirtualMidiConfigurationManager : { public: STDMETHOD(Initialize(_In_ GUID AbstractionId, _In_ IUnknown* MidiDeviceManager)); - STDMETHOD(UpdateConfiguration(_In_ LPCWSTR ConfigurationJsonSection, _Out_ BSTR* Response)); + STDMETHOD(UpdateConfiguration(_In_ LPCWSTR ConfigurationJsonSection, _In_ BOOL IsFromConfigurationFile, _Out_ BSTR* Response)); STDMETHOD(Cleanup)(); private: diff --git a/src/api/Abstraction/VirtualPatchBayAbstraction/abstraction_defs.h b/src/api/Abstraction/VirtualPatchBayAbstraction/abstraction_defs.h index 4993db7e..b39b9549 100644 --- a/src/api/Abstraction/VirtualPatchBayAbstraction/abstraction_defs.h +++ b/src/api/Abstraction/VirtualPatchBayAbstraction/abstraction_defs.h @@ -16,7 +16,7 @@ // TODO: Names should be moved to .rc for localization #define TRANSPORT_PARENT_ID L"MIDIU_VPB_TRANSPORT" -#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 2.0 Virtual Patch Bay" +#define TRANSPORT_PARENT_DEVICE_NAME L"MIDI 2.0 Patch Bay" #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 6814eb36..f4331c29 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-0150 + 1.0.0-preview.3-0155 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/IMidiServiceMessageProcessingPluginConfiguration.idl b/src/api/Client/Midi2Client/IMidiServiceMessageProcessingPluginConfiguration.idl new file mode 100644 index 00000000..ae6afa83 --- /dev/null +++ b/src/api/Client/Midi2Client/IMidiServiceMessageProcessingPluginConfiguration.idl @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#include "midl_defines.h" +MIDI_IDL_IMPORT + +namespace Windows.Devices.Midi2 +{ + [MIDI_API_CONTRACT(1)] + [MIDI_INTERFACE_UUID("2ebcfa13-585a-4376-8fe1-635784fa7fd4",1.0)] + interface IMidiServiceMessageProcessingPluginConfiguration + { + // we configure plugins on a single endpoint + String EndpointDeviceId { get; }; + + // The Id for this plugin type. It is per-type only + Guid MessageProcessingPluginId { get; }; + + // The instance Id. A single endpoint may have multiples of the same plugin type (a generic filter, for example) + Guid PluginInstanceId{ get; }; + + // the actual changes to be made + Windows.Data.Json.JsonObject SettingsJson { get; }; + } +} \ No newline at end of file diff --git a/src/api/Client/Midi2Client/IMidiServiceTransportPluginConfiguration.idl b/src/api/Client/Midi2Client/IMidiServiceTransportPluginConfiguration.idl new file mode 100644 index 00000000..d12470e3 --- /dev/null +++ b/src/api/Client/Midi2Client/IMidiServiceTransportPluginConfiguration.idl @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#include "midl_defines.h" +MIDI_IDL_IMPORT + +namespace Windows.Devices.Midi2 +{ + [MIDI_API_CONTRACT(1)] + [MIDI_INTERFACE_UUID("b2417dde-ef35-499b-a89b-0a4c32cc699a",1.0)] + interface IMidiServiceTransportPluginConfiguration + { + // the Id for this transport type + Guid TransportId { get; }; + + // the settings json. This will typically contain operations to create/update/delete + // individual endpoints. The format of this is specific to each transport in the service + Windows.Data.Json.JsonObject SettingsJson { get; }; + } +} \ No newline at end of file diff --git a/src/api/Client/Midi2Client/MidiService.cpp b/src/api/Client/Midi2Client/MidiService.cpp index ca403082..8841e918 100644 --- a/src/api/Client/Midi2Client/MidiService.cpp +++ b/src/api/Client/Midi2Client/MidiService.cpp @@ -20,8 +20,10 @@ namespace winrt::Windows::Devices::Midi2::implementation { _Use_decl_annotations_ - midi2::MidiServicePingResponseSummary MidiService::PingService(uint8_t const pingCount, uint32_t timeoutMilliseconds) noexcept + midi2::MidiServicePingResponseSummary MidiService::PingService(uint8_t const pingCount, uint32_t timeoutMilliseconds) noexcept { + internal::LogInfo(__FUNCTION__, L"Enter"); + auto responseSummary = winrt::make_self(); if (responseSummary == nullptr) @@ -96,7 +98,7 @@ namespace winrt::Windows::Devices::Midi2::implementation // ensure this is a ping message, just in case if (word0 == INTERNAL_PING_RESPONSE_UMP_WORD0 && word1 == pingSourceId) - { + { if (word2 < pings.size()) { // word2 is our ping index @@ -127,11 +129,14 @@ namespace winrt::Windows::Devices::Midi2::implementation // open the endpoint. We've already set options for it not to send out discovery messages if (!endpoint.Open()) { + internal::LogGeneralError(__FUNCTION__, L"Could not open ping endpoint."); + responseSummary->InternalSetFailed(L"Endpoint open failed. The service may be unavailable."); endpoint.MessageReceived(eventRevokeToken); session.DisconnectEndpointConnection(endpoint.ConnectionId()); + return *responseSummary; } @@ -149,7 +154,7 @@ namespace winrt::Windows::Devices::Midi2::implementation // granted that this adds a few ticks to add this to the collection and build the object response->InternalSetSendInfo(pingSourceId, pingIndex, timestamp); - + // // TODO: Should this use copy_from? pings[pingIndex] = response; @@ -164,6 +169,7 @@ namespace winrt::Windows::Devices::Midi2::implementation if (!allMessagesReceived.wait(timeoutMilliseconds)) { responseSummary->InternalSetFailed(L"Not all ping responses received within appropriate time window."); + internal::LogGeneralError(__FUNCTION__, L"Not all ping responses received within appropriate time window."); } else { @@ -198,7 +204,7 @@ namespace winrt::Windows::Devices::Midi2::implementation } _Use_decl_annotations_ - midi2::MidiServicePingResponseSummary MidiService::PingService(uint8_t const pingCount) noexcept + midi2::MidiServicePingResponseSummary MidiService::PingService(uint8_t const pingCount) noexcept { return PingService(pingCount, pingCount * 20 + 1000); } @@ -225,9 +231,9 @@ namespace winrt::Windows::Devices::Midi2::implementation return winrt::single_threaded_vector().GetView(); } - foundation::Collections::IVectorView MidiService::GetActiveSessions() noexcept + foundation::Collections::IVectorView MidiService::GetActiveSessions() noexcept { - auto sessionList = winrt::single_threaded_vector(); + auto sessionList = winrt::single_threaded_vector(); try { @@ -265,10 +271,10 @@ namespace winrt::Windows::Devices::Midi2::implementation for (uint32_t i = 0; i < sessionJsonArray.Size(); i++) { auto sessionJson = sessionJsonArray.GetObjectAt(i); - auto sessionObject = winrt::make_self(); + auto sessionObject = winrt::make_self(); + + // auto startTimeString = internal::JsonGetWStringProperty(sessionJson, MIDI_SESSION_TRACKER_JSON_RESULT_SESSION_TIME_PROPERTY_KEY, L"").c_str(); - // auto startTimeString = internal::JsonGetWStringProperty(sessionJson, MIDI_SESSION_TRACKER_JSON_RESULT_SESSION_TIME_PROPERTY_KEY, L"").c_str(); - auto startTime = internal::JsonGetDateTimeProperty(sessionJson, MIDI_SESSION_TRACKER_JSON_RESULT_SESSION_TIME_PROPERTY_KEY, noTime); sessionObject->InternalInitialize( @@ -289,7 +295,7 @@ namespace winrt::Windows::Devices::Midi2::implementation for (uint32_t j = 0; j < connectionsJsonArray.Size(); j++) { auto connectionJson = connectionsJsonArray.GetObjectAt(j); - auto connectionObject = winrt::make_self(); + auto connectionObject = winrt::make_self(); auto earliestConnectionTime = internal::JsonGetDateTimeProperty(connectionJson, MIDI_SESSION_TRACKER_JSON_RESULT_CONNECTION_TIME_PROPERTY_KEY, noTime); @@ -320,12 +326,289 @@ namespace winrt::Windows::Devices::Midi2::implementation } _Use_decl_annotations_ - json::JsonObject MidiService::UpdateRuntimeConfiguration(json::JsonObject configurationUpdate) noexcept + midi2::MidiServiceLoopbackEndpointCreationResult MidiService::CreateTemporaryLoopbackEndpoints( + winrt::guid const& associationId, + midi2::MidiServiceLoopbackEndpointDefinition const& endpointDefinitionA, + midi2::MidiServiceLoopbackEndpointDefinition const& endpointDefinitionB) noexcept + { + internal::LogInfo(__FUNCTION__, L"Enter"); + + // the success code in this defaults to False + auto result = winrt::make_self(); + + // todo: grab this from a constant + winrt::hstring loopbackDeviceAbstractionId = L"{942BF02D-93C0-4EA8-B03E-D51156CA75E1}"; + + + json::JsonObject wrapperObject; + json::JsonObject topLevelTransportPluginSettingsObject; + json::JsonObject abstractionObject; + json::JsonObject endpointCreationObject; + + json::JsonObject endpointAssociationObject; + json::JsonObject endpointDeviceAObject; + json::JsonObject endpointDeviceBObject; + + internal::LogInfo(__FUNCTION__, L" setting json properties"); + + // "endpointTransportPluginSettings": + // { + // endpoint abstraction guid : + // { + // "create" + // { + // associationGuid: + // { + // "endpointA": + // { + // ... endpoint properties ... + // }, + // "endpointB": + // { + // ... endpoint properties ... + // } + // } + // } + // } + // } + + // build Endpoint A + + internal::JsonGetWStringProperty( + endpointDeviceAObject, + MIDI_CONFIG_JSON_ENDPOINT_COMMON_NAME_PROPERTY, + endpointDefinitionA.Name().c_str()); + + internal::JsonGetWStringProperty( + endpointDeviceAObject, + MIDI_CONFIG_JSON_ENDPOINT_COMMON_DESCRIPTION_PROPERTY, + endpointDefinitionA.Description().c_str()); + + internal::JsonGetWStringProperty( + endpointDeviceAObject, + MIDI_CONFIG_JSON_ENDPOINT_COMMON_UNIQUE_ID_PROPERTY, + endpointDefinitionA.UniqueId().c_str()); + + // build Endpoint B + + internal::JsonGetWStringProperty( + endpointDeviceBObject, + MIDI_CONFIG_JSON_ENDPOINT_COMMON_NAME_PROPERTY, + endpointDefinitionB.Name().c_str()); + + internal::JsonGetWStringProperty( + endpointDeviceBObject, + MIDI_CONFIG_JSON_ENDPOINT_COMMON_DESCRIPTION_PROPERTY, + endpointDefinitionB.Description().c_str()); + + internal::JsonGetWStringProperty( + endpointDeviceBObject, + MIDI_CONFIG_JSON_ENDPOINT_COMMON_UNIQUE_ID_PROPERTY, + endpointDefinitionB.UniqueId().c_str()); + + // create the association object with the two devices as children + + internal::JsonSetObjectProperty( + endpointAssociationObject, + MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_ENDPOINT_A_KEY, + endpointDeviceAObject); + + internal::JsonSetObjectProperty( + endpointAssociationObject, + MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_ENDPOINT_B_KEY, + endpointDeviceBObject); + + // create the creation node with the association object as the child property + + internal::JsonSetObjectProperty( + endpointCreationObject, + internal::GuidToString(associationId), + endpointAssociationObject); + + // create the abstraction object with the child creation node + + internal::JsonSetObjectProperty( + abstractionObject, + MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_CREATE_KEY, + endpointCreationObject); + + // create the main node + + internal::JsonSetObjectProperty( + topLevelTransportPluginSettingsObject, + loopbackDeviceAbstractionId.c_str(), + abstractionObject); + + + // wrap it all up so the json is valid + + internal::JsonSetObjectProperty( + wrapperObject, + MIDI_CONFIG_JSON_TRANSPORT_PLUGIN_SETTINGS_OBJECT, + topLevelTransportPluginSettingsObject); + + // send it up + + + auto iid = __uuidof(IMidiAbstractionConfigurationManager); + winrt::com_ptr configManager; + + auto serviceAbstraction = winrt::create_instance(__uuidof(Midi2MidiSrvAbstraction), CLSCTX_ALL); + + if (serviceAbstraction) + { + auto activateConfigManagerResult = serviceAbstraction->Activate(iid, (void**)&configManager); + + internal::LogInfo(__FUNCTION__, L"config manager activate call completed"); + + + if (FAILED(activateConfigManagerResult) || configManager == nullptr) + { + internal::LogGeneralError(__FUNCTION__, L"Failed to create device. Config manager is null or call failed."); + + // return a fail result + return *result; + } + + internal::LogInfo(__FUNCTION__, L"config manager activate call SUCCESS"); + + auto initializeResult = configManager->Initialize(internal::StringToGuid(loopbackDeviceAbstractionId.c_str()), nullptr); + + + if (FAILED(initializeResult)) + { + internal::LogGeneralError(__FUNCTION__, L"failed to initialize config manager"); + + // return a fail result + return *result; + } + + CComBSTR response{}; + response.Empty(); + + auto jsonPayload = wrapperObject.Stringify(); + + internal::LogInfo(__FUNCTION__, jsonPayload.c_str()); + auto configUpdateResult = configManager->UpdateConfiguration(jsonPayload.c_str(), false, &response); + + if (FAILED(configUpdateResult)) + { + internal::LogGeneralError(__FUNCTION__, L"Failed to configure endpoint"); + + // return a failed result + return *result; + } + + internal::LogInfo(__FUNCTION__, L"configManager->UpdateConfiguration success"); + + json::JsonObject responseObject; + + if (!internal::JsonObjectFromBSTR(&response, responseObject)) + { + internal::LogGeneralError(__FUNCTION__, L"Failed to read json response object from loopback device creation"); + + // return a failed result + return *result; + } + + internal::LogInfo(__FUNCTION__, L"JsonObjectFromBSTR success"); + + + + + // check for actual success + auto successResult = internal::JsonGetBoolProperty(responseObject, MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_SUCCESS_PROPERTY_KEY, false); + + if (successResult) + { + internal::LogInfo(__FUNCTION__, L"JSON payload indicates success"); + + + // TODO: A and B are simple properties here. We don't need + // an array because we create one at a time through the API. And when + // created through the config file, there's no response object to + // worry about. + + auto deviceIdA = internal::JsonGetWStringProperty(responseObject, MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_CREATED_ENDPOINT_A_ID_KEY, L""); + auto deviceIdB = internal::JsonGetWStringProperty(responseObject, MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_CREATED_ENDPOINT_B_ID_KEY, L""); + + if (deviceIdA.empty()) + { + internal::LogGeneralError(__FUNCTION__, L"Unexpected empty Device Id A"); + + return *result; + } + + if (deviceIdB.empty()) + { + internal::LogGeneralError(__FUNCTION__, L"Unexpected empty Device Id B"); + + return *result; + } + + + // update the response object with the new ids + + result->SetSuccess(associationId, deviceIdA.c_str(), deviceIdB.c_str()); + } + else + { + internal::LogGeneralError(__FUNCTION__, L"Loopback device creation failed (payload has false success value)"); + + return nullptr; + } + + internal::LogInfo(__FUNCTION__, L"Loopback device creation worked."); + + } + else + { + // failed + internal::LogGeneralError(__FUNCTION__, L"Failed to create service abstraction"); + + } + + return *result; + } + + _Use_decl_annotations_ + bool MidiService::RemoveTemporaryLoopbackEndpoints(_In_ winrt::guid const& associationId) noexcept + { + internal::LogInfo(__FUNCTION__, L"Enter"); + + UNREFERENCED_PARAMETER(associationId); + // TODO: + + return false; + } + + _Use_decl_annotations_ + midi2::MidiServiceConfigurationResponse MidiService::UpdateTransportPluginConfiguration( + midi2::IMidiServiceTransportPluginConfiguration const& configurationUpdate) noexcept + { + internal::LogInfo(__FUNCTION__, L"Enter"); + + UNREFERENCED_PARAMETER(configurationUpdate); + // TODO: + + auto response = winrt::make_self(); + + return *response; + + } + + _Use_decl_annotations_ + midi2::MidiServiceConfigurationResponse MidiService::UpdateProcessingPluginConfiguration( + midi2::IMidiServiceMessageProcessingPluginConfiguration const& configurationUpdate) noexcept { - // TEMP! + internal::LogInfo(__FUNCTION__, L"Enter"); + + UNREFERENCED_PARAMETER(configurationUpdate); + // TODO: - return json::JsonObject{}; + auto response = winrt::make_self(); + return *response; } diff --git a/src/api/Client/Midi2Client/MidiService.h b/src/api/Client/Midi2Client/MidiService.h index e4e31158..90682a1f 100644 --- a/src/api/Client/Midi2Client/MidiService.h +++ b/src/api/Client/Midi2Client/MidiService.h @@ -29,9 +29,21 @@ namespace winrt::Windows::Devices::Midi2::implementation // static uint32_t GetOutgoingMessageQueueMaxMessageCapacity() noexcept { return (uint32_t)MIDI_OUTGOING_MESSAGE_QUEUE_MAX_MESSAGE_COUNT; } - static foundation::Collections::IVectorView GetActiveSessions() noexcept; + static foundation::Collections::IVectorView GetActiveSessions() noexcept; + + static midi2::MidiServiceLoopbackEndpointCreationResult CreateTemporaryLoopbackEndpoints( + _In_ winrt::guid const& associationId, + _In_ midi2::MidiServiceLoopbackEndpointDefinition const& endpointDefinitionA, + _In_ midi2::MidiServiceLoopbackEndpointDefinition const& endpointDefinitionB) noexcept; + + static bool RemoveTemporaryLoopbackEndpoints(_In_ winrt::guid const& associationId) noexcept; + + static midi2::MidiServiceConfigurationResponse UpdateTransportPluginConfiguration( + _In_ midi2::IMidiServiceTransportPluginConfiguration const& configurationUpdate) noexcept; + + static midi2::MidiServiceConfigurationResponse UpdateProcessingPluginConfiguration( + _In_ midi2::IMidiServiceMessageProcessingPluginConfiguration const& configurationUpdate) noexcept; - static json::JsonObject UpdateRuntimeConfiguration(_In_ json::JsonObject configurationUpdate) noexcept; private: diff --git a/src/api/Client/Midi2Client/MidiService.idl b/src/api/Client/Midi2Client/MidiService.idl index f7c708ed..1a4d21ca 100644 --- a/src/api/Client/Midi2Client/MidiService.idl +++ b/src/api/Client/Midi2Client/MidiService.idl @@ -13,7 +13,14 @@ import "MidiServiceTransportPluginInformation.idl"; import "MidiServiceMessageProcessingPluginInformation.idl"; import "MidiServicePingResponseSummary.idl"; -import "MidiSessionInformation.idl"; +import "MidiServiceSessionInformation.idl"; + +import "MidiServiceLoopbackEndpointCreationResult.idl"; +import "MidiServiceLoopbackEndpointDefinition.idl"; + +import "IMidiServiceTransportPluginConfiguration.idl"; +import "IMidiServiceMessageProcessingPluginConfiguration.idl"; +import "MidiServiceConfigurationResponse.idl"; namespace Windows.Devices.Midi2 { @@ -37,10 +44,33 @@ namespace Windows.Devices.Midi2 // static UInt32 GetOutgoingMessageQueueMaxMessageCapacity(); - static IVectorView GetActiveSessions(); + static IVectorView GetActiveSessions(); + + + // creates loopback endpoints that live for only as long as the service is running. + // Permanent endpoints need to be created by the user through the settings application + // so they get written to the configuration file and loaded when the service is loaded + // We special-case loopbacks here just like we special-case Virtual Devices in the + // session class, because they are very common to use and have special creation semantics. + static MidiServiceLoopbackEndpointCreationResult CreateTemporaryLoopbackEndpoints( + Guid associationId, + MidiServiceLoopbackEndpointDefinition endpointDefinitionA, + MidiServiceLoopbackEndpointDefinition endpointDefinitionB); + + static Boolean RemoveTemporaryLoopbackEndpoints(Guid associationId); + + // used for CRUD operations for certain endpoints (Transports). Create a component which calls this and + // provides strongly typed parameters specific to your transport. In general, applications should not be + // using this function directly. Instead, they should use a configuration component associated with the plugin. + static MidiServiceConfigurationResponse UpdateTransportPluginConfiguration( + IMidiServiceTransportPluginConfiguration configurationUpdate); - static Windows.Data.Json.JsonObject UpdateRuntimeConfiguration(Windows.Data.Json.JsonObject configurationUpdate); + // used for CRUD operations for certain endpoint message processing plugins (Transforms). Create a component which calls this and + // provides strongly typed parameters specific to your transport In general, applications should not be + // using this function directly. Instead, they should use a configuration component associated with the plugin. + static MidiServiceConfigurationResponse UpdateProcessingPluginConfiguration( + IMidiServiceMessageProcessingPluginConfiguration configurationUpdate); } } diff --git a/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.cpp b/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.cpp new file mode 100644 index 00000000..8b669734 --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.cpp @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#include "pch.h" +#include "MidiServiceConfigurationResponse.h" +#include "MidiServiceConfigurationResponse.g.cpp" + +namespace winrt::Windows::Devices::Midi2::implementation +{ + winrt::Windows::Devices::Midi2::MidiServiceConfigurationResponseStatus MidiServiceConfigurationResponse::Status() + { + throw hresult_not_implemented(); + } + hstring MidiServiceConfigurationResponse::ResponseJson() + { + throw hresult_not_implemented(); + } +} diff --git a/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.h b/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.h new file mode 100644 index 00000000..1af3ce7e --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.h @@ -0,0 +1,21 @@ +// 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 "MidiServiceConfigurationResponse.g.h" + +namespace winrt::Windows::Devices::Midi2::implementation +{ + struct MidiServiceConfigurationResponse : MidiServiceConfigurationResponseT + { + MidiServiceConfigurationResponse() = default; + + winrt::Windows::Devices::Midi2::MidiServiceConfigurationResponseStatus Status(); + hstring ResponseJson(); + }; +} diff --git a/src/api/Client/Midi2Client/IMidiTransportSettingsData.idl b/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.idl similarity index 70% rename from src/api/Client/Midi2Client/IMidiTransportSettingsData.idl rename to src/api/Client/Midi2Client/MidiServiceConfigurationResponse.idl index a19df44c..3e194ce1 100644 --- a/src/api/Client/Midi2Client/IMidiTransportSettingsData.idl +++ b/src/api/Client/Midi2Client/MidiServiceConfigurationResponse.idl @@ -9,12 +9,16 @@ #include "midl_defines.h" MIDI_IDL_IMPORT +import "MidiServiceConfigurationResponseStatusEnum.idl"; + namespace Windows.Devices.Midi2 { [MIDI_API_CONTRACT(1)] - [MIDI_INTERFACE_UUID("b2417dde-ef35-499b-a89b-0a4c32cc699a",1.0)] - interface IMidiTransportSettingsData + [default_interface] + runtimeclass MidiServiceConfigurationResponse { - String SettingsJson { get; set; }; + MidiServiceConfigurationResponseStatus Status { get; }; + + String ResponseJson { get; }; } } \ No newline at end of file diff --git a/src/api/Client/Midi2Client/MidiServiceConfigurationResponseStatusEnum.idl b/src/api/Client/Midi2Client/MidiServiceConfigurationResponseStatusEnum.idl new file mode 100644 index 00000000..02503eb0 --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceConfigurationResponseStatusEnum.idl @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#include "midl_defines.h" +MIDI_IDL_IMPORT + +namespace Windows.Devices.Midi2 +{ + // NOTE: the values are different for function blocks and group terminal + // blocks, so we have to different enumerations + + [MIDI_API_CONTRACT(1)] + enum MidiServiceConfigurationResponseStatus + { + Success = 0, + + ErrorTargetNotFound = 404, + + ErrorJsonNullOrEmpty = 600, + ErrorProcessingJson = 601, + + ErrorNotImplemented = 2600, + + ErrorOther = 9999, + }; +} \ No newline at end of file diff --git a/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.cpp b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.cpp new file mode 100644 index 00000000..73e2adfb --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.cpp @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#include "pch.h" +#include "MidiServiceLoopbackEndpointCreationResult.h" +#include "MidiServiceLoopbackEndpointCreationResult.g.cpp" + + +namespace winrt::Windows::Devices::Midi2::implementation +{ + +} diff --git a/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.h b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.h new file mode 100644 index 00000000..31f610e7 --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.h @@ -0,0 +1,46 @@ +// 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 "MidiServiceLoopbackEndpointCreationResult.g.h" + +namespace winrt::Windows::Devices::Midi2::implementation +{ + struct MidiServiceLoopbackEndpointCreationResult : MidiServiceLoopbackEndpointCreationResultT + { + MidiServiceLoopbackEndpointCreationResult() = default; + + bool Success() const { return m_success; } + + winrt::guid AssociationId() const { return m_associationId; } + winrt::hstring EndpointDeviceIdA() const { return m_endpointDeviceIdA; } + winrt::hstring EndpointDeviceIdB() const { return m_endpointDeviceIdB; } + + + void SetSuccess( + _In_ winrt::guid associationId, + _In_ winrt::hstring endpointDeviceIdA, + _In_ winrt::hstring endpointDeviceIdB + ) + { + m_success = true; + m_associationId = associationId; + m_endpointDeviceIdA = endpointDeviceIdA; + m_endpointDeviceIdB = endpointDeviceIdB; + } + + private: + bool m_success{ false }; + winrt::guid m_associationId{}; + winrt::hstring m_endpointDeviceIdA{}; + winrt::hstring m_endpointDeviceIdB{}; + + + + }; +} diff --git a/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.idl b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.idl new file mode 100644 index 00000000..5e9e9955 --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointCreationResult.idl @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#include "midl_defines.h" +MIDI_IDL_IMPORT + +namespace Windows.Devices.Midi2 +{ + [MIDI_API_CONTRACT(1)] + [default_interface] + runtimeclass MidiServiceLoopbackEndpointCreationResult + { + Boolean Success{ get; }; + + Guid AssociationId{ get; }; + + String EndpointDeviceIdA { get; }; + String EndpointDeviceIdB{ get; }; + } +} \ No newline at end of file diff --git a/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.cpp b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.cpp new file mode 100644 index 00000000..960a13c8 --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.cpp @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#include "pch.h" +#include "MidiServiceLoopbackEndpointDefinition.h" +#include "MidiServiceLoopbackEndpointDefinition.g.cpp" + +namespace winrt::Windows::Devices::Midi2::implementation +{ + +} diff --git a/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.h b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.h new file mode 100644 index 00000000..0de434ea --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.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 +#include "MidiServiceLoopbackEndpointDefinition.g.h" + +namespace winrt::Windows::Devices::Midi2::implementation +{ + struct MidiServiceLoopbackEndpointDefinition : MidiServiceLoopbackEndpointDefinitionT + { + MidiServiceLoopbackEndpointDefinition() = default; + + winrt::hstring Name() const { return m_name; } + void Name(winrt::hstring const& value) { m_name = internal::TrimmedHStringCopy(value); } + + winrt::hstring UniqueId() const { return m_uniqueId; } + void UniqueId(winrt::hstring const& value) { m_uniqueId = internal::TrimmedHStringCopy(value); } + + winrt::hstring Description() const { return m_description; } + void Description(winrt::hstring const& value) { m_description = internal::TrimmedHStringCopy(value); } + + private: + winrt::hstring m_name{}; + winrt::hstring m_uniqueId{}; + winrt::hstring m_description{}; + }; +} +namespace winrt::Windows::Devices::Midi2::factory_implementation +{ + struct MidiServiceLoopbackEndpointDefinition : MidiServiceLoopbackEndpointDefinitionT + { + }; +} diff --git a/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.idl b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.idl new file mode 100644 index 00000000..9c5c39b2 --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceLoopbackEndpointDefinition.idl @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#include "midl_defines.h" +MIDI_IDL_IMPORT + +namespace Windows.Devices.Midi2 +{ + [MIDI_API_CONTRACT(1)] + [default_interface] + runtimeclass MidiServiceLoopbackEndpointDefinition + { + MidiServiceLoopbackEndpointDefinition(); + + String Name{ get; set; }; + String UniqueId{ get; set; }; + String Description{ get; set; }; + } +} \ No newline at end of file diff --git a/src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.cpp b/src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.cpp new file mode 100644 index 00000000..89a9f5de --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.cpp @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#include "pch.h" +#include "MidiServiceSessionConnectionInformation.h" +#include "MidiServiceSessionConnectionInformation.g.cpp" + + +namespace winrt::Windows::Devices::Midi2::implementation +{ + + _Use_decl_annotations_ + void MidiServiceSessionConnectionInformation::InternalInitialize( + winrt::hstring const endpointDeviceId, + uint16_t const instanceCount, + foundation::DateTime const earliestConnectionTime + ) + { + m_endpointDeviceId = internal::NormalizeEndpointInterfaceIdHStringCopy(endpointDeviceId); + m_instanceCount = instanceCount; + m_earliestConnectionTime = earliestConnectionTime; + } + + + +} diff --git a/src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.h b/src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.h new file mode 100644 index 00000000..cea19387 --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.h @@ -0,0 +1,34 @@ +// 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 "MidiServiceSessionConnectionInformation.g.h" + + +namespace winrt::Windows::Devices::Midi2::implementation +{ + struct MidiServiceSessionConnectionInformation : MidiServiceSessionConnectionInformationT + { + MidiServiceSessionConnectionInformation() = default; + + winrt::hstring EndpointDeviceId() { return m_endpointDeviceId; } + uint16_t InstanceCount() { return m_instanceCount; } + foundation::DateTime EarliestConnectionTime() { return m_earliestConnectionTime; } + + void InternalInitialize( + _In_ winrt::hstring const endpointDeviceId, + _In_ uint16_t const instanceCount, + _In_ foundation::DateTime const earliestConnectionTime + ); + + private: + winrt::hstring m_endpointDeviceId{}; + uint16_t m_instanceCount{}; + foundation::DateTime m_earliestConnectionTime{}; + }; +} diff --git a/src/api/Client/Midi2Client/MidiSessionConnectionInformation.idl b/src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.idl similarity index 92% rename from src/api/Client/Midi2Client/MidiSessionConnectionInformation.idl rename to src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.idl index 0323f004..38370c90 100644 --- a/src/api/Client/Midi2Client/MidiSessionConnectionInformation.idl +++ b/src/api/Client/Midi2Client/MidiServiceSessionConnectionInformation.idl @@ -13,7 +13,7 @@ namespace Windows.Devices.Midi2 { [MIDI_API_CONTRACT(1)] [default_interface] - runtimeclass MidiSessionConnectionInformation + runtimeclass MidiServiceSessionConnectionInformation { String EndpointDeviceId{ get; }; diff --git a/src/api/Client/Midi2Client/MidiServiceSessionInformation.cpp b/src/api/Client/Midi2Client/MidiServiceSessionInformation.cpp new file mode 100644 index 00000000..7577ee30 --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceSessionInformation.cpp @@ -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/ +// ============================================================================ + +#include "pch.h" +#include "MidiServiceSessionInformation.h" +#include "MidiServiceSessionInformation.g.cpp" + +namespace winrt::Windows::Devices::Midi2::implementation +{ + _Use_decl_annotations_ + void MidiServiceSessionInformation::InternalInitialize( + winrt::guid sessionId, + winrt::hstring sessionName, + uint64_t processId, + winrt::hstring processName, + foundation::DateTime startTime) + { + m_sessionId = sessionId; + m_processId = processId; + m_processName = processName; + m_sessionName = sessionName; + m_startTime = startTime; + } + + _Use_decl_annotations_ + void MidiServiceSessionInformation::InternalAddConnection( + midi2::MidiServiceSessionConnectionInformation const& info + ) + { + m_connections.Append(info); + } + +} diff --git a/src/api/Client/Midi2Client/MidiServiceSessionInformation.h b/src/api/Client/Midi2Client/MidiServiceSessionInformation.h new file mode 100644 index 00000000..f50c8601 --- /dev/null +++ b/src/api/Client/Midi2Client/MidiServiceSessionInformation.h @@ -0,0 +1,51 @@ +// 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 "MidiServiceSessionInformation.g.h" + + +namespace winrt::Windows::Devices::Midi2::implementation +{ + struct MidiServiceSessionInformation : MidiServiceSessionInformationT + { + MidiServiceSessionInformation() = default; + + winrt::guid SessionId() { return m_sessionId; } + uint64_t ProcessId() { return m_processId; } + winrt::hstring ProcessName() { return m_processName; } + winrt::hstring SessionName() { return m_sessionName; } + foundation::DateTime StartTime() { return m_startTime; } + + collections::IVectorView Connections() { return m_connections.GetView(); } + + void InternalInitialize( + _In_ winrt::guid sessionId, + _In_ winrt::hstring sessionName, + _In_ uint64_t processId, + _In_ winrt::hstring processName, + _In_ foundation::DateTime startTime + ); + + void InternalAddConnection( + _In_ midi2::MidiServiceSessionConnectionInformation const& info + ); + + private: + winrt::guid m_sessionId{}; + uint64_t m_processId{}; + winrt::hstring m_processName{}; + winrt::hstring m_sessionName{}; + foundation::DateTime m_startTime{}; + + foundation::Collections::IVector + m_connections{ winrt::single_threaded_vector() }; + + + }; +} diff --git a/src/api/Client/Midi2Client/MidiSessionInformation.idl b/src/api/Client/Midi2Client/MidiServiceSessionInformation.idl similarity index 80% rename from src/api/Client/Midi2Client/MidiSessionInformation.idl rename to src/api/Client/Midi2Client/MidiServiceSessionInformation.idl index 47086628..efee151c 100644 --- a/src/api/Client/Midi2Client/MidiSessionInformation.idl +++ b/src/api/Client/Midi2Client/MidiServiceSessionInformation.idl @@ -9,13 +9,13 @@ #include "midl_defines.h" MIDI_IDL_IMPORT -import "MidiSessionConnectionInformation.idl"; +import "MidiServiceSessionConnectionInformation.idl"; namespace Windows.Devices.Midi2 { [MIDI_API_CONTRACT(1)] [default_interface] - runtimeclass MidiSessionInformation + runtimeclass MidiServiceSessionInformation { Guid SessionId{ get; }; @@ -26,6 +26,6 @@ namespace Windows.Devices.Midi2 Windows.Foundation.DateTime StartTime{ get; }; - IVectorView Connections{ get; }; + IVectorView Connections{ get; }; } } \ No newline at end of file diff --git a/src/api/Client/Midi2Client/MidiSession.cpp b/src/api/Client/Midi2Client/MidiSession.cpp index 77807e17..99340b71 100644 --- a/src/api/Client/Midi2Client/MidiSession.cpp +++ b/src/api/Client/Midi2Client/MidiSession.cpp @@ -234,7 +234,7 @@ namespace winrt::Windows::Devices::Midi2::implementation internal::JsonSetWStringProperty( endpointDefinitionObject, - MIDI_CONFIG_JSON_ENDPOINT_VIRTUAL_DEVICE_UNIQUE_ID_PROPERTY_KEY, + MIDI_CONFIG_JSON_ENDPOINT_COMMON_UNIQUE_ID_PROPERTY, deviceDefinition.EndpointProductInstanceId().c_str()); internal::JsonSetWStringProperty( @@ -327,7 +327,7 @@ namespace winrt::Windows::Devices::Midi2::implementation internal::LogInfo(__FUNCTION__, L"sending json"); internal::LogInfo(__FUNCTION__, topLevelTransportPluginSettingsObject.Stringify().c_str()); - auto configUpdateResult = configManager->UpdateConfiguration(topLevelTransportPluginSettingsObject.Stringify().c_str(), &response); + auto configUpdateResult = configManager->UpdateConfiguration(topLevelTransportPluginSettingsObject.Stringify().c_str(), false, &response); internal::LogInfo(__FUNCTION__, L"UpdateConfiguration returned"); diff --git a/src/api/Client/Midi2Client/MidiSessionConnectionInformation.cpp b/src/api/Client/Midi2Client/MidiSessionConnectionInformation.cpp deleted file mode 100644 index cb5f95b0..00000000 --- a/src/api/Client/Midi2Client/MidiSessionConnectionInformation.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "pch.h" -#include "MidiSessionConnectionInformation.h" -#include "MidiSessionConnectionInformation.g.cpp" - - -namespace winrt::Windows::Devices::Midi2::implementation -{ - - _Use_decl_annotations_ - void MidiSessionConnectionInformation::InternalInitialize( - winrt::hstring const endpointDeviceId, - uint16_t const instanceCount, - foundation::DateTime const earliestConnectionTime - ) - { - m_endpointDeviceId = internal::NormalizeEndpointInterfaceIdHStringCopy(endpointDeviceId); - m_instanceCount = instanceCount; - m_earliestConnectionTime = earliestConnectionTime; - } - - - -} diff --git a/src/api/Client/Midi2Client/MidiSessionConnectionInformation.h b/src/api/Client/Midi2Client/MidiSessionConnectionInformation.h deleted file mode 100644 index 2caf0995..00000000 --- a/src/api/Client/Midi2Client/MidiSessionConnectionInformation.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "MidiSessionConnectionInformation.g.h" - - -namespace winrt::Windows::Devices::Midi2::implementation -{ - struct MidiSessionConnectionInformation : MidiSessionConnectionInformationT - { - MidiSessionConnectionInformation() = default; - - winrt::hstring EndpointDeviceId() { return m_endpointDeviceId; } - uint16_t InstanceCount() { return m_instanceCount; } - foundation::DateTime EarliestConnectionTime() { return m_earliestConnectionTime; } - - void InternalInitialize( - _In_ winrt::hstring const endpointDeviceId, - _In_ uint16_t const instanceCount, - _In_ foundation::DateTime const earliestConnectionTime - ); - - private: - winrt::hstring m_endpointDeviceId{}; - uint16_t m_instanceCount{}; - foundation::DateTime m_earliestConnectionTime{}; - }; -} diff --git a/src/api/Client/Midi2Client/MidiSessionInformation.cpp b/src/api/Client/Midi2Client/MidiSessionInformation.cpp deleted file mode 100644 index ab5d8e2d..00000000 --- a/src/api/Client/Midi2Client/MidiSessionInformation.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "pch.h" -#include "MidiSessionInformation.h" -#include "MidiSessionInformation.g.cpp" - -namespace winrt::Windows::Devices::Midi2::implementation -{ - _Use_decl_annotations_ - void MidiSessionInformation::InternalInitialize( - winrt::guid sessionId, - winrt::hstring sessionName, - uint64_t processId, - winrt::hstring processName, - foundation::DateTime startTime) - { - m_sessionId = sessionId; - m_processId = processId; - m_processName = processName; - m_sessionName = sessionName; - m_startTime = startTime; - } - - _Use_decl_annotations_ - void MidiSessionInformation::InternalAddConnection( - midi2::MidiSessionConnectionInformation const& info - ) - { - m_connections.Append(info); - } - -} diff --git a/src/api/Client/Midi2Client/MidiSessionInformation.h b/src/api/Client/Midi2Client/MidiSessionInformation.h deleted file mode 100644 index d598c5d9..00000000 --- a/src/api/Client/Midi2Client/MidiSessionInformation.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include "MidiSessionInformation.g.h" - - -namespace winrt::Windows::Devices::Midi2::implementation -{ - struct MidiSessionInformation : MidiSessionInformationT - { - MidiSessionInformation() = default; - - winrt::guid SessionId() { return m_sessionId; } - uint64_t ProcessId() { return m_processId; } - winrt::hstring ProcessName() { return m_processName; } - winrt::hstring SessionName() { return m_sessionName; } - foundation::DateTime StartTime() { return m_startTime; } - - collections::IVectorView Connections() { return m_connections.GetView(); } - - void InternalInitialize( - _In_ winrt::guid sessionId, - _In_ winrt::hstring sessionName, - _In_ uint64_t processId, - _In_ winrt::hstring processName, - _In_ foundation::DateTime startTime - ); - - void InternalAddConnection( - _In_ midi2::MidiSessionConnectionInformation const& info - ); - - private: - winrt::guid m_sessionId{}; - uint64_t m_processId{}; - winrt::hstring m_processName{}; - winrt::hstring m_sessionName{}; - foundation::DateTime m_startTime{}; - - foundation::Collections::IVector - m_connections{ winrt::single_threaded_vector() }; - - - }; -} diff --git a/src/api/Client/Midi2Client/WinRTActivationEntries.txt b/src/api/Client/Midi2Client/WinRTActivationEntries.txt index 03f184d4..92add49a 100644 --- a/src/api/Client/Midi2Client/WinRTActivationEntries.txt +++ b/src/api/Client/Midi2Client/WinRTActivationEntries.txt @@ -77,6 +77,7 @@ Windows.Devices.Midi2.IMidiEndpointConnectionStatics | 0 | 0 | 0 Windows.Devices.Midi2.MidiService | 0 | 0 | 0 Windows.Devices.Midi2.IMidiServiceStatics | 0 | 0 | 0 +Windows.Devices.Midi2.MidiServiceLoopbackEndpointDefinition | 0 | 0 | 0 # Clock diff --git a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj index 9e8ac05d..f4bc170a 100644 --- a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj +++ b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj @@ -281,6 +281,15 @@ MidiMessageConverter.idl + + MidiServiceConfigurationResponse.idl + + + MidiServiceLoopbackEndpointCreationResult.idl + + + MidiServiceLoopbackEndpointDefinition.idl + MidiServiceMessageProcessingPluginInformation.idl @@ -320,11 +329,11 @@ MidiMessageTypeEndpointListener.idl - - MidiSessionConnectionInformation.idl + + MidiServiceSessionConnectionInformation.idl - - MidiSessionInformation.idl + + MidiServiceSessionInformation.idl MidiSessionSettings.idl @@ -397,6 +406,15 @@ MidiMessageConverter.idl + + MidiServiceConfigurationResponse.idl + + + MidiServiceLoopbackEndpointCreationResult.idl + + + MidiServiceLoopbackEndpointDefinition.idl + MidiServiceMessageProcessingPluginInformation.idl @@ -436,11 +454,11 @@ MidiMessageTypeEndpointListener.idl - - MidiSessionConnectionInformation.idl + + MidiServiceSessionConnectionInformation.idl - - MidiSessionInformation.idl + + MidiServiceSessionInformation.idl MidiSessionSettings.idl @@ -491,8 +509,10 @@ + - + + @@ -518,6 +538,9 @@ + + + @@ -531,8 +554,8 @@ - - + + diff --git a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj.filters b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj.filters index 5751984b..e22feb3b 100644 --- a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj.filters +++ b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj.filters @@ -82,6 +82,18 @@ {6427be3d-78fd-4fff-9bf4-2353b462bfc3} + + {f47089c0-b3df-4af8-93e8-35731024db07} + + + {7610a401-8f83-470c-9ccc-6761e3759eab} + + + {a21f8a09-4e72-422f-9569-e3eeee2ac830} + + + {2a7feacb-d6cd-45d7-a7a1-754e28d34e2d} + @@ -168,6 +180,15 @@ API\Service + + API\Service\Configuration + + + API\Service\Configuration + + + API\Service\Configuration + @@ -264,6 +285,15 @@ API\Service + + API\Service\Configuration + + + API\Service\Configuration + + + API\Service\Configuration + @@ -272,9 +302,6 @@ API\Endpoints\Session - - API\Transports - API\Clock @@ -302,15 +329,6 @@ API\Service - - API\Service - - - API\Service - - - API\Service - API\Endpoints\Message Processing Plugins\Interfaces @@ -419,9 +437,6 @@ API\Enumeration - - API\Service - API\Enumeration @@ -434,12 +449,6 @@ API\Endpoints\Message Processing Plugins\Stock Plugins\Virtual Device - - API\Service - - - API\Service - API\Endpoints\Connections @@ -449,6 +458,42 @@ API\Endpoints\Connections + + API\Service\Reporting + + + API\Service\Reporting + + + API\Service\Health + + + API\Service\Health + + + API\Service\Runtime Endpoints + + + API\Service\Runtime Endpoints + + + API\Service\Reporting + + + API\Service\Reporting + + + API\Service\Configuration + + + API\Service\Configuration + + + API\Service\Configuration + + + API\Service\Configuration + diff --git a/src/api/Client/Midi2Client/pch.h b/src/api/Client/Midi2Client/pch.h index 35f1be7a..05f123f2 100644 --- a/src/api/Client/Midi2Client/pch.h +++ b/src/api/Client/Midi2Client/pch.h @@ -107,8 +107,10 @@ namespace midi2 = ::winrt::Windows::Devices::Midi2; #include "MidiServiceTransportPluginInformation.h" #include "MidiServiceMessageProcessingPluginInformation.h" -#include "MidiSessionConnectionInformation.h" -#include "MidiSessionInformation.h" +#include "MidiServiceConfigurationResponse.h" +#include "MidiServiceLoopbackEndpointCreationResult.h" +#include "MidiServiceSessionConnectionInformation.h" +#include "MidiServiceSessionInformation.h" #include "MidiService.h" #include "MidiEndpointDeviceInformation.h" diff --git a/src/api/Libs/AbstractionUtilities/inc/json_helpers.h b/src/api/Libs/AbstractionUtilities/inc/json_helpers.h index 0e6ff02f..80e8b48a 100644 --- a/src/api/Libs/AbstractionUtilities/inc/json_helpers.h +++ b/src/api/Libs/AbstractionUtilities/inc/json_helpers.h @@ -65,14 +65,17 @@ namespace json = ::winrt::Windows::Data::Json; // loopback MIDI (here because these can also be created via the client API) -#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_CREATE_ARRAY_KEY L"createLoopbackDevices" -#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_UPDATE_ARRAY_KEY L"updateLoopbackDevices" -#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_REMOVE_ARRAY_KEY L"removeLoopbackDevices" -#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_ASSOCIATION_ID_PROPERTY_KEY L"associationIdentifier" - -#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_SUCCESS_PROPERTY_KEY L"success" -#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_CREATED_DEVICES_ARRAY_KEY L"createdDevices" -#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_CREATED_ID_PROPERTY_KEY L"id" +#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_CREATE_KEY L"create" +#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_UPDATE_KEY L"update" +#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICES_REMOVE_KEY L"remove" + +#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_ENDPOINT_A_KEY L"endpointA" +#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_ENDPOINT_B_KEY L"endpointB" + + +#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_SUCCESS_PROPERTY_KEY L"success" +#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_CREATED_ENDPOINT_A_ID_KEY L"endpointA" +#define MIDI_CONFIG_JSON_ENDPOINT_LOOPBACK_DEVICE_RESPONSE_CREATED_ENDPOINT_B_ID_KEY L"endpointB" diff --git a/src/api/Service/Exe/MidiDeviceManager.cpp b/src/api/Service/Exe/MidiDeviceManager.cpp index 7e2eaadb..5990cee5 100644 --- a/src/api/Service/Exe/MidiDeviceManager.cpp +++ b/src/api/Service/Exe/MidiDeviceManager.cpp @@ -38,6 +38,16 @@ CMidiDeviceManager::Initialize( try { + TraceLoggingWrite( + MidiSrvTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Loading abstraction layer", "message"), + TraceLoggingGuid(AbstractionLayer, "abstraction layer") + ); + + // provide the initial settings for these transports auto transportSettingsJson = m_ConfigurationManager->GetSavedConfigurationForTransportAbstraction(AbstractionLayer); @@ -61,7 +71,6 @@ CMidiDeviceManager::Initialize( if (SUCCEEDED(initializeResult)) { - OutputDebugString(__FUNCTION__ L": Transport Abstraction initialized.\n"); m_MidiEndpointManagers.emplace(AbstractionLayer, std::move(endpointManager)); } else @@ -71,7 +80,8 @@ CMidiDeviceManager::Initialize( __FUNCTION__, TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Transport abstraction initialization failed.") + TraceLoggingWideString(L"Transport abstraction initialization failed.", "message"), + TraceLoggingGuid(AbstractionLayer, "abstraction layer") ); } @@ -83,7 +93,8 @@ CMidiDeviceManager::Initialize( __FUNCTION__, TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Transport abstraction endpoint manager initialization failed.") + TraceLoggingWideString(L"Transport abstraction endpoint manager initialization failed.", "message"), + TraceLoggingGuid(AbstractionLayer, "abstraction layer") ); } @@ -93,12 +104,12 @@ CMidiDeviceManager::Initialize( if (abstractionConfigurationManager != nullptr) { - if (transportSettingsJson != L"") + if (!transportSettingsJson.empty()) { - CComBSTR response; + CComBSTR response{}; response.Empty(); - LOG_IF_FAILED(abstractionConfigurationManager->UpdateConfiguration(transportSettingsJson.c_str(), &response)); + LOG_IF_FAILED(abstractionConfigurationManager->UpdateConfiguration(transportSettingsJson.c_str(), true, &response)); // we don't use the response info here. ::SysFreeString(response); @@ -114,14 +125,11 @@ CMidiDeviceManager::Initialize( __FUNCTION__, TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Transport Abstraction activation failed (nullptr return).") + TraceLoggingWideString(L"Transport Abstraction activation failed (nullptr return).", "message"), + TraceLoggingGuid(AbstractionLayer, "abstraction layer") ); } - - - - } catch (...) { @@ -131,7 +139,8 @@ CMidiDeviceManager::Initialize( __FUNCTION__, TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Exception loading transport abstraction.") + TraceLoggingWideString(L"Exception loading transport abstraction.", "message"), + TraceLoggingGuid(AbstractionLayer, "abstraction layer") ); } @@ -766,7 +775,7 @@ CMidiDeviceManager::DeactivateEndpoint __FUNCTION__, TraceLoggingLevel(WINEVENT_LEVEL_INFO), TraceLoggingPointer(this, "this"), - TraceLoggingWideString(InstanceId) + TraceLoggingWideString(InstanceId, "instance id") ); auto cleanId = internal::NormalizeDeviceInstanceIdWStringCopy(InstanceId); @@ -837,6 +846,7 @@ CMidiDeviceManager::UpdateAbstractionConfiguration ( GUID AbstractionId, LPCWSTR ConfigurationJson, + BOOL IsFromConfigurationFile, BSTR* Response ) { @@ -847,8 +857,9 @@ CMidiDeviceManager::UpdateAbstractionConfiguration __FUNCTION__, TraceLoggingLevel(WINEVENT_LEVEL_INFO), TraceLoggingPointer(this, "this"), - TraceLoggingGuid(AbstractionId), - TraceLoggingWideString(ConfigurationJson) + TraceLoggingGuid(AbstractionId, "abstraction id"), + TraceLoggingWideString(ConfigurationJson, "config json"), + TraceLoggingBool(IsFromConfigurationFile, "from config file") ); if (auto search = m_MidiAbstractionConfigurationManagers.find(AbstractionId); search != m_MidiAbstractionConfigurationManagers.end()) @@ -857,7 +868,7 @@ CMidiDeviceManager::UpdateAbstractionConfiguration if (configManager) { - return configManager->UpdateConfiguration(ConfigurationJson, Response); + return configManager->UpdateConfiguration(ConfigurationJson, IsFromConfigurationFile, Response); } } else @@ -867,8 +878,8 @@ CMidiDeviceManager::UpdateAbstractionConfiguration __FUNCTION__, TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingPointer(this, "this"), - TraceLoggingGuid(AbstractionId), - TraceLoggingWideString(L"Failed to find the referenced abstraction by its GUID") + TraceLoggingGuid(AbstractionId, "abstraction id"), + TraceLoggingWideString(L"Failed to find the referenced abstraction by its GUID", "message") ); } @@ -880,7 +891,7 @@ CMidiDeviceManager::UpdateAbstractionConfiguration __FUNCTION__, TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Json is null") + TraceLoggingWideString(L"Json is null", "message") ); } @@ -900,8 +911,6 @@ CMidiDeviceManager::Cleanup() TraceLoggingPointer(this, "this") ); - OutputDebugString(__FUNCTION__ L"\n"); - m_MidiEndpointManagers.clear(); m_MidiAbstractionConfigurationManagers.clear(); diff --git a/src/api/Service/Exe/MidiSrvRPC.idl b/src/api/Service/Exe/MidiSrvRPC.idl index af111c3a..7ccc3f27 100644 --- a/src/api/Service/Exe/MidiSrvRPC.idl +++ b/src/api/Service/Exe/MidiSrvRPC.idl @@ -101,6 +101,7 @@ interface MidiSrvRPC HRESULT MidiSrvUpdateConfiguration( [in] handle_t BindingHandle, [in, string] LPCWSTR ConfigurationJson, + [in] BOOL IsFromConfigurationFile, [out] BSTR* Response); diff --git a/src/api/Service/Exe/MidiSrvRpc.cpp b/src/api/Service/Exe/MidiSrvRpc.cpp index 0ee9c1b4..fba3f343 100644 --- a/src/api/Service/Exe/MidiSrvRpc.cpp +++ b/src/api/Service/Exe/MidiSrvRpc.cpp @@ -144,6 +144,7 @@ HRESULT MidiSrvUpdateConfiguration( /* [in] */ handle_t BindingHandle, /*[in, string]*/ __RPC__in_string LPCWSTR ConfigurationJson, + __RPC__in BOOL IsFromConfigurationFile, __RPC__out BSTR* Response) { UNREFERENCED_PARAMETER(BindingHandle); @@ -197,7 +198,7 @@ MidiSrvUpdateConfiguration( CComBSTR response; response.Empty(); - deviceManager->UpdateAbstractionConfiguration(i->first, i->second.c_str(), &response); + deviceManager->UpdateAbstractionConfiguration(i->first, i->second.c_str(), IsFromConfigurationFile, &response); // Probably need to do more formatting of this. Should use the json objects instead diff --git a/src/api/Service/Inc/MidiDeviceManager.h b/src/api/Service/Inc/MidiDeviceManager.h index 59ae8b2b..918b19ca 100644 --- a/src/api/Service/Inc/MidiDeviceManager.h +++ b/src/api/Service/Inc/MidiDeviceManager.h @@ -128,9 +128,11 @@ class CMidiDeviceManager : ); + // this is for runtime updates only, not for config file updates STDMETHOD(UpdateAbstractionConfiguration)( _In_ GUID AbstractionId, _In_ LPCWSTR ConfigurationJson, + _In_ BOOL IsFromConfigurationFile, _Out_ BSTR* Response ); diff --git a/src/api/idl/MidiAbstraction.idl b/src/api/idl/MidiAbstraction.idl index a3d15b1f..80137432 100644 --- a/src/api/idl/MidiAbstraction.idl +++ b/src/api/idl/MidiAbstraction.idl @@ -208,6 +208,7 @@ interface IMidiAbstractionConfigurationManager : IUnknown HRESULT UpdateConfiguration( [in] LPCWSTR configurationJsonSection, + [in] BOOL IsFromConfigurationFile, [out] BSTR* response ); diff --git a/src/api/idl/MidiDeviceManagerInterface.idl b/src/api/idl/MidiDeviceManagerInterface.idl index 1f9165b3..007d51d9 100644 --- a/src/api/idl/MidiDeviceManagerInterface.idl +++ b/src/api/idl/MidiDeviceManagerInterface.idl @@ -68,10 +68,11 @@ interface IMidiDeviceManagerInterface : IUnknown ); - // Update the running configuration for an abstraction or endpoints managed by that abstraction + // Update the configuration for an abstraction or endpoints managed by that abstraction HRESULT UpdateAbstractionConfiguration( [in] GUID AbstractionId, [in] LPCWSTR ConfigurationJson, + [in] BOOL IsFromConfigurationFile, [out] BSTR* Response ); diff --git a/src/oob-setup/api-package/WindowsMidiServices.wxs b/src/oob-setup/api-package/WindowsMidiServices.wxs index 750eba14..2bedecfb 100644 --- a/src/oob-setup/api-package/WindowsMidiServices.wxs +++ b/src/oob-setup/api-package/WindowsMidiServices.wxs @@ -94,7 +94,7 @@ Bitness="always64" Directory="SERVICE_INSTALLFOLDER" Guid="25E35CBB-4C66-4BEB-9B03-BC2E2D99317B" - NeverOverwrite="true"> + NeverOverwrite="false"> diff --git a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointPropertiesCommand.cs b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointPropertiesCommand.cs index 676d24fa..961f6930 100644 --- a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointPropertiesCommand.cs +++ b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointPropertiesCommand.cs @@ -63,7 +63,6 @@ public override int Execute(CommandContext context, Settings settings) table.AddColumn(column1); - TableColumn column2 = new TableColumn(AnsiMarkupFormatter.FormatTableColumnHeading(Resources.Strings.PropertiesTableColumnHeaderValue)); //Console.WriteLine("Window width " + Console.WindowWidth); @@ -453,17 +452,39 @@ public override int Execute(CommandContext context, Settings settings) table.AddRow(AnsiMarkupFormatter.FormatError("No matching container found"), ""); } + // write out the properties table + + AnsiConsole.Write(table); + + if (settings.IncludeRaw) { + AnsiConsole.WriteLine(); + AnsiConsole.WriteLine("Raw properties"); + AnsiConsole.WriteLine(); + // spit out all properties by key / value in a new table + var rawPropertyTable = new Table(); - table.AddEmptyRow(); - table.AddRow(AnsiMarkupFormatter.FormatTableColumnHeading("All Raw Properties"), ""); + AnsiMarkupFormatter.SetTableBorderStyle(rawPropertyTable); - DisplayProperties(table, di.Properties); + TableColumn rawColumn1 = new TableColumn( + AnsiMarkupFormatter.FormatTableColumnHeading(Resources.Strings.PropertiesTableColumnHeaderProperty)); + + rawPropertyTable.AddColumn(rawColumn1); + + TableColumn rawColumn2 = new TableColumn( + AnsiMarkupFormatter.FormatTableColumnHeading(Resources.Strings.PropertiesTableColumnHeaderValue)); + + rawPropertyTable.AddColumn(rawColumn2); + + //rawPropertyTable.AddRow(AnsiMarkupFormatter.FormatTableColumnHeading("All Raw Properties"), ""); + + DisplayProperties(rawPropertyTable, di.Properties); + + AnsiConsole.Write(rawPropertyTable); } - AnsiConsole.Write(table); } diff --git a/src/user-tools/midi-console/Midi/Midi.csproj b/src/user-tools/midi-console/Midi/Midi.csproj index b4b4a30c..4c63474b 100644 --- a/src/user-tools/midi-console/Midi/Midi.csproj +++ b/src/user-tools/midi-console/Midi/Midi.csproj @@ -31,7 +31,7 @@ - +