From fc2c3f33fe9309434739267b9cfad65275586804 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Wed, 8 Jan 2025 21:47:33 -0500 Subject: [PATCH 1/6] Cleanup of network host --- diagnostics/trace-logging/MidiServices.wprp | 37 +------ .../Midi2.NetworkMidiBidi.cpp | 3 +- .../Midi2.NetworkMidiConfigurationManager.cpp | 57 +++++------ .../Midi2.NetworkMidiEndpointManager.cpp | 99 +++++++++++++++---- .../Midi2.NetworkMidiEndpointManager.h | 4 +- .../MidiNetworkAdvertiser.cpp | 22 +++-- .../MidiNetworkAdvertiser.h | 2 +- .../MidiNetworkConnection.cpp | 85 +++++++--------- .../MidiNetworkDataWriter.cpp | 81 ++++++++++++++- .../MidiNetworkDataWriter.h | 6 +- .../MidiNetworkHost.cpp | 45 +++++---- .../MidiNetworkHost.h | 5 +- .../TransportState.cpp | 15 +++ .../UdpNetworkMidi2Transport/TransportState.h | 6 ++ .../Transport/UdpNetworkMidi2Transport/pch.h | 3 +- 15 files changed, 308 insertions(+), 162 deletions(-) diff --git a/diagnostics/trace-logging/MidiServices.wprp b/diagnostics/trace-logging/MidiServices.wprp index 5a02a897f..88807371c 100644 --- a/diagnostics/trace-logging/MidiServices.wprp +++ b/diagnostics/trace-logging/MidiServices.wprp @@ -1,41 +1,12 @@ - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -65,9 +36,7 @@ - - - + diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp index c47d774eb..f467b4ef7 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp @@ -85,10 +85,11 @@ CMidi2NetworkMidiBiDi::SendMidiMessage( { UNREFERENCED_PARAMETER(position); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_connection); + RETURN_HR_IF_NULL(E_INVALIDARG, message); RETURN_HR_IF(E_INVALIDARG, size < sizeof(uint32_t)); - RETURN_HR_IF_NULL(E_UNEXPECTED, m_connection); RETURN_IF_FAILED(m_connection->SendMidiMessagesToNetwork(message, size)); diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiConfigurationManager.cpp b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiConfigurationManager.cpp index bf8a06ffa..5db7e37b0 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiConfigurationManager.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiConfigurationManager.cpp @@ -298,7 +298,9 @@ CMidi2NetworkMidiConfigurationManager::UpdateConfiguration( { auto hostEntry = hostsSection.GetNamedObject(it.Current().Key()); - MidiNetworkHostDefinition definition{ }; + auto definition = std::make_shared(); + RETURN_IF_NULL_ALLOC(definition); + winrt::hstring validationErrorMessage{ }; // currently, UDP is the only allowed protocol @@ -309,18 +311,18 @@ CMidi2NetworkMidiConfigurationManager::UpdateConfiguration( validationErrorMessage = L"Invalid network protocol '" + protocol + L"' specified."; } - definition.EntryIdentifier = internal::TrimmedHStringCopy(it.Current().Key()); + definition->EntryIdentifier = internal::TrimmedHStringCopy(it.Current().Key()); - definition.Enabled = hostEntry.GetNamedBoolean(MIDI_CONFIG_JSON_NETWORK_MIDI_ENABLED_KEY, true); - definition.Advertise = hostEntry.GetNamedBoolean(MIDI_CONFIG_JSON_NETWORK_MIDI_MDNS_ADVERTISE_KEY, true); + definition->Enabled = hostEntry.GetNamedBoolean(MIDI_CONFIG_JSON_NETWORK_MIDI_ENABLED_KEY, true); + definition->Advertise = hostEntry.GetNamedBoolean(MIDI_CONFIG_JSON_NETWORK_MIDI_MDNS_ADVERTISE_KEY, true); - definition.UmpEndpointName = internal::TrimmedHStringCopy(hostEntry.GetNamedString(MIDI_CONFIG_JSON_ENDPOINT_COMMON_NAME_PROPERTY, L"")); - definition.ProductInstanceId = internal::TrimmedHStringCopy(hostEntry.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_PRODUCT_INSTANCE_ID_PROPERTY, L"")); + definition->UmpEndpointName = internal::TrimmedHStringCopy(hostEntry.GetNamedString(MIDI_CONFIG_JSON_ENDPOINT_COMMON_NAME_PROPERTY, L"")); + definition->ProductInstanceId = internal::TrimmedHStringCopy(hostEntry.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_PRODUCT_INSTANCE_ID_PROPERTY, L"")); - definition.Port = internal::TrimmedHStringCopy(hostEntry.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_NETWORK_PORT_KEY, MIDI_CONFIG_JSON_NETWORK_MIDI_NETWORK_PORT_VALUE_AUTO)); + definition->Port = internal::TrimmedHStringCopy(hostEntry.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_NETWORK_PORT_KEY, MIDI_CONFIG_JSON_NETWORK_MIDI_NETWORK_PORT_VALUE_AUTO)); - definition.Authentication = MidiNetworkHostAuthenticationFromJsonString(hostEntry.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_HOST_AUTHENTICATION_KEY, MIDI_CONFIG_JSON_NETWORK_MIDI_HOST_AUTHENTICATION_VALUE_NONE)); - definition.ConnectionPolicy = MidiNetworkHostConnectionPolicyFromJsonString(hostEntry.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_CONNECTION_POLICY_KEY, MIDI_CONFIG_JSON_NETWORK_MIDI_CONNECTION_POLICY_ALLOW_IPV4_VALUE_ANY)); + definition->Authentication = MidiNetworkHostAuthenticationFromJsonString(hostEntry.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_HOST_AUTHENTICATION_KEY, MIDI_CONFIG_JSON_NETWORK_MIDI_HOST_AUTHENTICATION_VALUE_NONE)); + definition->ConnectionPolicy = MidiNetworkHostConnectionPolicyFromJsonString(hostEntry.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_CONNECTION_POLICY_KEY, MIDI_CONFIG_JSON_NETWORK_MIDI_CONNECTION_POLICY_ALLOW_IPV4_VALUE_ANY)); // read the list of ip info //if (definition.ConnectionPolicy != MidiNetworkHostConnectionPolicy::PolicyAllowAllConnections) @@ -338,14 +340,14 @@ CMidi2NetworkMidiConfigurationManager::UpdateConfiguration( //} // read authentication information - if (definition.Authentication != MidiNetworkHostAuthentication::NoAuthentication) + if (definition->Authentication != MidiNetworkHostAuthentication::NoAuthentication) { - if (definition.Authentication == MidiNetworkHostAuthentication::PasswordAuthentication) + if (definition->Authentication == MidiNetworkHostAuthentication::PasswordAuthentication) { // TODO: Read the password vault key } - else if (definition.Authentication == MidiNetworkHostAuthentication::UserAuthentication) + else if (definition->Authentication == MidiNetworkHostAuthentication::UserAuthentication) { // TODO: Read username/password vault key } @@ -376,7 +378,7 @@ CMidi2NetworkMidiConfigurationManager::UpdateConfiguration( } } - definition.ServiceInstanceName = serviceInstanceNamePrefix; + definition->ServiceInstanceName = serviceInstanceNamePrefix; // TODO: See if the serviceInstanceName is already in use. If so, add a disambiguation number. Keep trying until unused one is found @@ -392,29 +394,29 @@ CMidi2NetworkMidiConfigurationManager::UpdateConfiguration( if ((host.Type() == HostNameType::DomainName) && (host.RawName().ends_with(L".local"))) { - definition.HostName = host.RawName(); + definition->HostName = host.RawName(); break; } } - if (definition.Port == MIDI_CONFIG_JSON_NETWORK_MIDI_NETWORK_PORT_VALUE_AUTO || - definition.Port == L"" || - definition.Port == L"0") + if (definition->Port == MIDI_CONFIG_JSON_NETWORK_MIDI_NETWORK_PORT_VALUE_AUTO || + definition->Port == L"" || + definition->Port == L"0") { // this will cause us to use an auto-generated free port - definition.Port = L""; - definition.UseAutomaticPortAllocation = true; + definition->Port = L""; + definition->UseAutomaticPortAllocation = true; } else { - definition.UseAutomaticPortAllocation = false; + definition->UseAutomaticPortAllocation = false; } // validate the entry - if (SUCCEEDED(ValidateHostDefinition(definition, validationErrorMessage))) + if (SUCCEEDED(ValidateHostDefinition(*definition, validationErrorMessage))) { TraceLoggingWrite( MidiNetworkMidiTransportTelemetryProvider::Provider(), @@ -425,15 +427,14 @@ CMidi2NetworkMidiConfigurationManager::UpdateConfiguration( TraceLoggingWideString(L"Host definition validated. Creating host", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - // create the host - - auto host = std::make_shared(); - - RETURN_HR_IF_NULL(E_POINTER, host); - RETURN_IF_FAILED(host->Initialize(definition)); + // create the host definition // add to our collection of hosts - TransportState::Current().AddHost(host); + TransportState::Current().AddPendingHostDefinition(definition); + + responseObject.SetNamedValue( + MIDI_CONFIG_JSON_CONFIGURATION_RESPONSE_SUCCESS_PROPERTY_KEY, + jsonTrue); } else { diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp index 6206ac8de..47f7656b0 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp @@ -145,19 +145,20 @@ CMidi2NetworkMidiEndpointManager::OnDeviceWatcherAdded(enumeration::DeviceWatche _Use_decl_annotations_ HRESULT -CMidi2NetworkMidiEndpointManager::OnDeviceWatcherUpdated(enumeration::DeviceWatcher const&, enumeration::DeviceInformationUpdate const& args) +CMidi2NetworkMidiEndpointManager::OnDeviceWatcherUpdated(enumeration::DeviceWatcher const&, enumeration::DeviceInformationUpdate const& /*args*/) { - TraceLoggingWrite( - MidiNetworkMidiTransportTelemetryProvider::Provider(), - MIDI_TRACE_EVENT_INFO, - TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), - TraceLoggingLevel(WINEVENT_LEVEL_INFO), - TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD), - TraceLoggingWideString(args.Id().c_str(), "id") - ); - - // nothing to do here. We don't care about updates. + //TraceLoggingWrite( + // MidiNetworkMidiTransportTelemetryProvider::Provider(), + // MIDI_TRACE_EVENT_INFO, + // TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + // TraceLoggingLevel(WINEVENT_LEVEL_INFO), + // TraceLoggingPointer(this, "this"), + // TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD), + // TraceLoggingWideString(args.Id().c_str(), "id") + //); + + // nothing to do here. We don't care about updates. This gets really spammy because + // the endpoint updates maybe with each mdns ad broadcast or something return S_OK; } @@ -232,17 +233,28 @@ CMidi2NetworkMidiEndpointManager::StartBackgroundEndpointCreator() TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - std::jthread workerThread(&CMidi2NetworkMidiEndpointManager::EndpointCreatorWorker, this); + std::jthread workerThread(std::bind_front(&CMidi2NetworkMidiEndpointManager::EndpointCreatorWorker, this)); m_backgroundEndpointCreatorThread = std::move(workerThread); - m_backgroundEndpointCreatorThreadStopToken = m_backgroundEndpointCreatorThread.get_stop_token(); + //m_backgroundEndpointCreatorThreadStopToken = m_backgroundEndpointCreatorThread.get_stop_token(); m_backgroundEndpointCreatorThread.detach(); + + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Exit", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + return S_OK; } +_Use_decl_annotations_ HRESULT -CMidi2NetworkMidiEndpointManager::EndpointCreatorWorker() +CMidi2NetworkMidiEndpointManager::EndpointCreatorWorker(std::stop_token stopToken) { TraceLoggingWrite( MidiNetworkMidiTransportTelemetryProvider::Provider(), @@ -253,12 +265,24 @@ CMidi2NetworkMidiEndpointManager::EndpointCreatorWorker() TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); +// winrt::init_apartment(); + //auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED); + // this is set up to run through one time before waiting for the wakeup // this way we can process anything added before the EndpointManager has been // initialized - while (!m_backgroundEndpointCreatorThreadStopToken.stop_requested()) + while (!stopToken.stop_requested()) { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Background worker loop", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + if (m_backgroundEndpointCreatorThreadWakeup.is_signaled()) { m_backgroundEndpointCreatorThreadWakeup.ResetEvent(); @@ -266,11 +290,36 @@ CMidi2NetworkMidiEndpointManager::EndpointCreatorWorker() // run through host entries - for (auto const& host : TransportState::Current().GetHosts()) + for (auto& definition : TransportState::Current().GetPendingHostDefinitions()) { - if (!host->HasStarted()) + if (definition->Created) + { + continue; + } + + if (!definition->Enabled) + { + continue; + } + + auto host = std::make_shared(); + LOG_IF_NULL_ALLOC(host); + + if (host != nullptr) { - LOG_IF_FAILED(host->Start()); + LOG_IF_FAILED(host->Initialize(*definition)); + + if (!host->HasStarted()) + { + LOG_IF_FAILED(host->Start()); + } + + // Remove pending entry + + definition->Created = true; + + // this ensures the host doesn't disappear + TransportState::Current().AddHost(host); } } @@ -293,6 +342,15 @@ CMidi2NetworkMidiEndpointManager::EndpointCreatorWorker() m_backgroundEndpointCreatorThreadWakeup.wait(10000); } + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Exit", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + return S_OK; } @@ -549,9 +607,10 @@ CMidi2NetworkMidiEndpointManager::CreateNewEndpoint( capabilities |= MidiEndpointCapabilities_GenerateIncomingTimestamps; commonProperties.Capabilities = (MidiEndpointCapabilities)capabilities; + bool umpOnly = false; RETURN_IF_FAILED(m_midiDeviceManager->ActivateEndpoint( (PCWSTR)m_parentDeviceId.c_str(), // parent instance Id - false, // UMP-only. When set to false, WinMM MIDI 1.0 ports are created + umpOnly, // UMP-only. When set to false, WinMM MIDI 1.0 ports are created MidiFlow::MidiFlowBidirectional, // MIDI Flow &commonProperties, (ULONG)0, //interfaceDeviceProperties.size(), diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.h b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.h index 99fe22611..05970fce5 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.h @@ -68,8 +68,8 @@ class CMidi2NetworkMidiEndpointManager : std::jthread m_backgroundEndpointCreatorThread; - std::stop_token m_backgroundEndpointCreatorThreadStopToken; + //std::stop_token m_backgroundEndpointCreatorThreadStopToken; wil::slim_event_manual_reset m_backgroundEndpointCreatorThreadWakeup; - HRESULT EndpointCreatorWorker(); + HRESULT EndpointCreatorWorker(_In_ std::stop_token stopToken); }; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkAdvertiser.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkAdvertiser.cpp index 78fa0a451..aaba5ba94 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkAdvertiser.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkAdvertiser.cpp @@ -31,7 +31,7 @@ inline const winrt::hstring BuildFullServiceInstanceName(_In_ winrt::hstring con } - +// TODO: Change this method to "Start" and have the parameters passed into Initialize instead of this. _Use_decl_annotations_ HRESULT @@ -56,18 +56,17 @@ MidiNetworkAdvertiser::Advertise( auto fullServiceName = BuildFullServiceInstanceName(serviceInstanceNameWithoutSuffix); - - auto service = DnssdServiceInstance( + m_serviceInstance = DnssdServiceInstance( fullServiceName, hostName, port); // add the txt attributes per the spec - service.TextAttributes().Insert(L"UMPEndpointName", midiEndpointName); - service.TextAttributes().Insert(L"ProductInstanceId", midiProductInstanceId); + m_serviceInstance.TextAttributes().Insert(L"UMPEndpointName", midiEndpointName); + m_serviceInstance.TextAttributes().Insert(L"ProductInstanceId", midiProductInstanceId); // register with the socket that's bound to the port - auto registration = service.RegisterDatagramSocketAsync(boundSocket).get(); + auto registration = m_serviceInstance.RegisterDatagramSocketAsync(boundSocket).get(); switch (registration.Status()) { @@ -137,6 +136,15 @@ MidiNetworkAdvertiser::Advertise( RETURN_IF_FAILED(E_FAIL); } + + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Exit", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); } @@ -153,7 +161,7 @@ MidiNetworkAdvertiser::Shutdown() TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - + m_serviceInstance = nullptr; return S_OK; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkAdvertiser.h b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkAdvertiser.h index 4b51d3ca8..5e3819a1e 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkAdvertiser.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkAdvertiser.h @@ -31,6 +31,6 @@ class MidiNetworkAdvertiser HRESULT Shutdown(); private: - + winrt::Windows::Networking::ServiceDiscovery::Dnssd::DnssdServiceInstance m_serviceInstance{ nullptr }; }; \ No newline at end of file diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp index 54500d452..ea6b89892 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp @@ -208,12 +208,6 @@ MidiNetworkConnection::HandleIncomingBye() TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - - // send bye reply - RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); - RETURN_IF_FAILED(m_writer->WriteCommandByeReply()); - RETURN_IF_FAILED(m_writer->Send()); - if (m_sessionActive) { if (TransportState::Current().GetEndpointManager() != nullptr) @@ -225,10 +219,20 @@ MidiNetworkConnection::HandleIncomingBye() m_sessionDeviceInstanceId.clear(); m_sessionEndpointDeviceInterfaceId.clear(); } + + // send bye reply + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); + RETURN_IF_FAILED(m_writer->WriteCommandByeReply()); + RETURN_IF_FAILED(m_writer->Send()); + } else { // not an active session. Nothing to clean up + // but we should NAK the Bye saying there's no active session + m_writer->WriteUdpPacketHeader(); + m_writer->WriteCommandNAK(0, MidiNetworkCommandNAKReason::CommandNAKReason_CommandNotExpected, L"BYE received when there's no active session."); + m_writer->Send(); } return S_OK; @@ -256,6 +260,7 @@ MidiNetworkConnection::HandleIncomingInvitation( UNREFERENCED_PARAMETER(capabilities); RETURN_HR_IF_NULL(E_UNEXPECTED, m_writer); + RETURN_HR_IF_NULL(E_UNEXPECTED, TransportState::Current().GetEndpointManager()); if (m_role == MidiNetworkConnectionRole::ConnectionWindowsIsHost) { @@ -281,7 +286,6 @@ MidiNetworkConnection::HandleIncomingInvitation( if (TransportState::Current().GetEndpointManager()->IsInitialized()) { - // Create the endpoint for Windows MIDI Services clients // This will also kick off discovery and protocol negotiation HRESULT hr = S_OK; @@ -514,70 +518,57 @@ MidiNetworkConnection::ProcessIncomingMessage( // TODO: a message can have zero MIDI words, but a valid sequence number. Need to handle that. - // TODO: This logic doesn't handle wrap. Need to have a window if (sequenceNumber <= m_lastReceivedUmpCommandSequenceNumber) { + // Skip all words in this command message + for (uint8_t i = 0; i < numberOfWords && reader.UnconsumedBufferLength() >= sizeof(uint32_t); i++) + { + reader.ReadUInt32(); + } - - // todo: skip all words } else if (sequenceNumber == m_lastReceivedUmpCommandSequenceNumber + 1) { - // todo: process UMP data - + // Process UMP data because this is the next sequence number m_lastReceivedUmpCommandSequenceNumber = sequenceNumber; - } - else - { - // skipped data. Re-request missing packets - } - - - - - - - - for (uint8_t i = 0; i < numberOfWords; i++) - { - if (reader.UnconsumedBufferLength() >= sizeof(uint32_t)) + for (uint8_t i = 0; i < numberOfWords; i++) { - // TODO: We need to check this sequence number, and if there's any gap from the last received, drop this data and request a retransmit - - if (sequenceNumber > m_lastReceivedUmpCommandSequenceNumber) + if (reader.UnconsumedBufferLength() >= sizeof(uint32_t)) { // add to our vector words.push_back(reader.ReadUInt32()); - } else { - // just read and discard - reader.ReadUInt32(); - } - } - else - { - // bad / incomplete packet + // bad / incomplete packet - auto lock = m_socketWriterLock.lock(); - m_writer->WriteUdpPacketHeader(); - m_writer->WriteCommandNAK(commandHeader.HeaderWord, MidiNetworkCommandNAKReason::CommandNAKReason_CommandMalformed, L"Packet data incomplete"); - m_writer->Send(); + auto lock = m_socketWriterLock.lock(); + m_writer->WriteUdpPacketHeader(); + m_writer->WriteCommandNAK(commandHeader.HeaderWord, MidiNetworkCommandNAKReason::CommandNAKReason_CommandMalformed, L"Packet data incomplete"); + m_writer->Send(); - // TODO: We should request a retransmit of this packet + // TODO: We should request a retransmit of this packet - return E_FAIL; + RETURN_IF_FAILED(E_FAIL); + } } - } - if (sequenceNumber > m_lastReceivedUmpCommandSequenceNumber) + } + else { - m_lastReceivedUmpCommandSequenceNumber = sequenceNumber; + // TODO: We need to check this sequence number, and if there's any gap from the last received, + // drop this data and request a retransmit + + // skipped data. Re-request missing packets } + //if (sequenceNumber > m_lastReceivedUmpCommandSequenceNumber) + //{ + // m_lastReceivedUmpCommandSequenceNumber = sequenceNumber; + //} + if (words.size() > 0) { LOG_IF_FAILED(HandleIncomingUmpData(packetMidiTimestamp, words)); diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp index 88e33f4ac..a83d8b97c 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp @@ -12,6 +12,9 @@ HRESULT MidiNetworkDataWriter::Send() { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); + + auto lock = m_dataWriterLock.lock(); m_dataWriter.StoreAsync().get(); @@ -24,6 +27,9 @@ HRESULT MidiNetworkDataWriter::WriteUdpPacketHeader() { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); + + auto lock = m_dataWriterLock.lock(); m_dataWriter.WriteUInt32(MIDI_UDP_PAYLOAD_HEADER); @@ -39,6 +45,9 @@ MidiNetworkDataWriter::InternalWriteCommandHeader( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); + + auto lock = m_dataWriterLock.lock(); m_dataWriter.WriteByte(commandCode); m_dataWriter.WriteByte(payloadLengthIn32BitWords); @@ -57,6 +66,10 @@ MidiNetworkDataWriter::InternalWriteCommandHeader( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); + + // don't lock here. the calling method will lock + //auto lock = m_dataWriterLock.lock(); m_dataWriter.WriteByte(commandCode); m_dataWriter.WriteByte(payloadLengthIn32BitWords); @@ -92,9 +105,12 @@ HRESULT MidiNetworkDataWriter::WriteCommandPing(uint32_t pingId) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); byte payloadLengthIn32BitWords{ 1 }; + auto lock = m_dataWriterLock.lock(); + RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_Ping, payloadLengthIn32BitWords, 0)); m_dataWriter.WriteUInt32(pingId); @@ -107,8 +123,12 @@ HRESULT MidiNetworkDataWriter::WriteCommandPingReply(uint32_t pingId) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); + byte payloadLengthIn32BitWords{ 1 }; + auto lock = m_dataWriterLock.lock(); + RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_PingReply, payloadLengthIn32BitWords, 0)); m_dataWriter.WriteUInt32(pingId); @@ -128,6 +148,7 @@ MidiNetworkDataWriter::WriteCommandNAK( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); byte payloadLengthIn32BitWords{ 1 }; // 1 here to account for the original command header @@ -140,6 +161,8 @@ MidiNetworkDataWriter::WriteCommandNAK( payloadLengthIn32BitWords += CalculatePaddedStringSizeIn32BitWords(utf8, MIDI_MAX_NAK_MESSAGE_BYTE_COUNT); + auto lock = m_dataWriterLock.lock(); + RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_NAK, payloadLengthIn32BitWords, reason, 0)); m_dataWriter.WriteUInt32(originalCommandHeader); @@ -159,6 +182,7 @@ MidiNetworkDataWriter::WriteCommandBye( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); byte payloadLengthIn32BitWords{ 0 }; // 1 here to account for the original command header @@ -171,6 +195,8 @@ MidiNetworkDataWriter::WriteCommandBye( payloadLengthIn32BitWords += CalculatePaddedStringSizeIn32BitWords(utf8, MIDI_MAX_BYE_MESSAGE_BYTE_COUNT); + auto lock = m_dataWriterLock.lock(); + RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_Bye, payloadLengthIn32BitWords, reason, 0)); // write the text @@ -183,6 +209,9 @@ HRESULT MidiNetworkDataWriter::WriteCommandByeReply() { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); + + auto lock = m_dataWriterLock.lock(); RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_ByeReply, MIDI_COMMAND_PAYLOAD_LENGTH_NO_PAYLOAD, 0)); @@ -193,6 +222,9 @@ HRESULT MidiNetworkDataWriter::WriteCommandSessionReset() { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); + + auto lock = m_dataWriterLock.lock(); RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_SessionReset, MIDI_COMMAND_PAYLOAD_LENGTH_NO_PAYLOAD, 0)); @@ -203,6 +235,9 @@ HRESULT MidiNetworkDataWriter::WriteCommandSessionResetReply() { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); + + auto lock = m_dataWriterLock.lock(); RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_SessionResetReply, MIDI_COMMAND_PAYLOAD_LENGTH_NO_PAYLOAD, 0)); @@ -217,9 +252,12 @@ MidiNetworkDataWriter::WriteCommandRetransmitRequest( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); byte payloadLengthIn32BitWords{ 1 }; // 1 here to account for number of UMP commands + auto lock = m_dataWriterLock.lock(); + RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_RetransmitRequest, payloadLengthIn32BitWords, sequenceNumber.Value())); m_dataWriter.WriteUInt16(numberOfUmpCommands); @@ -237,9 +275,12 @@ MidiNetworkDataWriter::WriteCommandRetransmitError( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); byte payloadLengthIn32BitWords{ 1 }; // 1 here to account for sequence number + auto lock = m_dataWriterLock.lock(); + RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_RetransmitError, payloadLengthIn32BitWords, errorReason, 0)); m_dataWriter.WriteUInt16(sequenceNumber.Value()); @@ -258,6 +299,7 @@ MidiNetworkDataWriter::WriteCommandInvitation( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); byte payloadLengthIn32BitWords{ 0 }; @@ -270,6 +312,9 @@ MidiNetworkDataWriter::WriteCommandInvitation( payloadLengthIn32BitWords += umpEndpointNameLengthIn32BitWords; payloadLengthIn32BitWords += productInstanceIdLengthIn32BitWords; + + auto lock = m_dataWriterLock.lock(); + RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandClientToHost_Invitation, payloadLengthIn32BitWords, umpEndpointNameLengthIn32BitWords, capabilities)); WritePaddedString(endpointNameUtf8, MIDI_MAX_UMP_ENDPOINT_NAME_BYTE_COUNT); @@ -286,10 +331,14 @@ MidiNetworkDataWriter::WriteCommandInvitationWithAuthentication( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); UNREFERENCED_PARAMETER(cryptoNonce); UNREFERENCED_PARAMETER(sharedSecret); + auto lock = m_dataWriterLock.lock(); + + return E_NOTIMPL; } @@ -302,11 +351,15 @@ MidiNetworkDataWriter::WriteCommandInvitationWithUserAuthentication( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); UNREFERENCED_PARAMETER(cryptoNonce); UNREFERENCED_PARAMETER(userName); UNREFERENCED_PARAMETER(password); + auto lock = m_dataWriterLock.lock(); + + return E_NOTIMPL; } @@ -318,6 +371,7 @@ MidiNetworkDataWriter::WriteCommandInvitationReplyAccepted( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); byte payloadLengthIn32BitWords{ 0 }; @@ -330,6 +384,8 @@ MidiNetworkDataWriter::WriteCommandInvitationReplyAccepted( payloadLengthIn32BitWords += productInstanceIdLengthIn32BitWords; + auto lock = m_dataWriterLock.lock(); + RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandHostToClient_InvitationReplyAccepted, payloadLengthIn32BitWords, umpEndpointNameLengthIn32BitWords, 0)); WritePaddedString(endpointNameUtf8, MIDI_MAX_UMP_ENDPOINT_NAME_BYTE_COUNT); @@ -346,10 +402,14 @@ MidiNetworkDataWriter::WriteCommandInvitationReplyPending( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); UNREFERENCED_PARAMETER(hostUmpEndpointName); UNREFERENCED_PARAMETER(hostProductInstanceId); + auto lock = m_dataWriterLock.lock(); + + return E_NOTIMPL; } @@ -364,12 +424,16 @@ MidiNetworkDataWriter::WriteCommandInvitationReplyAuthenticationRequired( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); UNREFERENCED_PARAMETER(cryptoNonce); UNREFERENCED_PARAMETER(authenticationState); UNREFERENCED_PARAMETER(hostUmpEndpointName); UNREFERENCED_PARAMETER(hostProductInstanceId); + auto lock = m_dataWriterLock.lock(); + + return E_NOTIMPL; } @@ -384,12 +448,16 @@ MidiNetworkDataWriter::WriteCommandInvitationReplyUserAuthenticationRequired( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); UNREFERENCED_PARAMETER(cryptoNonce); UNREFERENCED_PARAMETER(authenticationState); UNREFERENCED_PARAMETER(hostUmpEndpointName); UNREFERENCED_PARAMETER(hostProductInstanceId); + auto lock = m_dataWriterLock.lock(); + + return E_NOTIMPL; } @@ -405,9 +473,13 @@ MidiNetworkDataWriter::WriteCommandUmpMessages( ) { RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); RETURN_HR_IF(E_INVALIDARG, words.size() > MIDI_MAX_UMP_WORDS_PER_PACKET); + + auto lock = m_dataWriterLock.lock(); + RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_UmpData, static_cast(words.size()), sequenceNumber.Value())); // we make the assumption that the calling code has already validated that the words @@ -424,8 +496,13 @@ MidiNetworkDataWriter::WriteCommandUmpMessages( HRESULT MidiNetworkDataWriter::Shutdown() { - m_dataWriter.Close(); - m_dataWriter = nullptr; + auto lock = m_dataWriterLock.lock(); + + if (m_dataWriter != nullptr) + { + m_dataWriter.Close(); + m_dataWriter = nullptr; + } m_stream = nullptr; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.h b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.h index 2e58efbc6..6d29bc3f3 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.h @@ -68,6 +68,9 @@ class MidiNetworkDataWriter inline void WritePaddedString(_In_ std::string s, _In_ size_t maxByteCount) { + // don't lock here. Calling method will lock + //auto lock = m_dataWriterLock.lock(); + size_t count{ 0 }; for (auto const& ch : s) @@ -121,5 +124,6 @@ class MidiNetworkDataWriter winrt::Windows::Storage::Streams::IOutputStream m_stream{ nullptr }; winrt::Windows::Storage::Streams::DataWriter m_dataWriter{ nullptr }; - + // data writer doesn't support concurrent writes and will throw exceptions if you try + wil::critical_section m_dataWriterLock; }; \ No newline at end of file diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp index aee557544..531584720 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp @@ -46,6 +46,14 @@ MidiNetworkHost::Initialize( m_hostDefinition = hostDefinition; + DatagramSocket socket; + m_socket = std::move(socket); + + m_socket.Control().DontFragment(true); + //m_socket.Control().InboundBufferSizeInBytes(10000); + m_socket.Control().QualityOfService(SocketQualityOfService::LowLatency); + + if (m_hostDefinition.Advertise) { m_advertiser = std::make_shared(); @@ -81,9 +89,9 @@ MidiNetworkHost::Start() // wire up to handle incoming events auto messageReceivedHandler = winrt::Windows::Foundation::TypedEventHandler(this, &MidiNetworkHost::OnMessageReceived); - m_messageReceivedRevoker = m_socket.MessageReceived(winrt::auto_revoke, messageReceivedHandler); + m_messageReceivedEventToken = m_socket.MessageReceived(messageReceivedHandler); - // this should have error checking + // TODO: this should have error checking m_socket.BindServiceNameAsync(winrt::to_hstring(m_hostDefinition.Port)).get(); auto boundPort = static_cast(std::stoi(winrt::to_string(m_socket.Information().LocalPort()))); @@ -101,6 +109,8 @@ MidiNetworkHost::Start() )); } + m_started = true; + TraceLoggingWrite( MidiNetworkMidiTransportTelemetryProvider::Provider(), MIDI_TRACE_EVENT_INFO, @@ -110,8 +120,6 @@ MidiNetworkHost::Start() TraceLoggingWideString(L"Exit", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - m_started = true; - return S_OK; } @@ -124,16 +132,6 @@ void MidiNetworkHost::OnMessageReceived( DatagramSocket const& sender, DatagramSocketMessageReceivedEventArgs const& args) { - // TEMP!! - TraceLoggingWrite( - MidiNetworkMidiTransportTelemetryProvider::Provider(), - MIDI_TRACE_EVENT_INFO, - TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), - TraceLoggingLevel(WINEVENT_LEVEL_INFO), - TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) - ); - UNREFERENCED_PARAMETER(sender); auto reader = args.GetDataReader(); @@ -207,12 +205,25 @@ MidiNetworkHost::Shutdown() // TODO: send "bye" to all sessions, and then unbind the socket - // TODO: Stop packet processing thread - + // TODO: Stop packet processing thread using the jthread stop token + m_keepProcessing = false; + if (m_socket) + { + m_socket.MessageReceived(m_messageReceivedEventToken); + m_socket.Close(); + m_socket = nullptr; + } - m_socket.Close(); + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Exit", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); return S_OK; } \ No newline at end of file diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h index 4c53281e0..feb09a4de 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h @@ -36,6 +36,8 @@ enum MidiNetworkHostProtocol struct MidiNetworkHostDefinition { + bool Created{ false }; + winrt::hstring EntryIdentifier; // internal bool UseAutomaticPortAllocation{ true }; @@ -95,7 +97,8 @@ class MidiNetworkHost // std::queue m_incomingOutOfBandCommands; // std::queue m_outgoingOutOfBandCommands; - winrt::impl::consume_Windows_Networking_Sockets_IDatagramSocket::MessageReceived_revoker m_messageReceivedRevoker; + winrt::event_token m_messageReceivedEventToken; + void OnMessageReceived( _In_ DatagramSocket const& sender, _In_ DatagramSocketMessageReceivedEventArgs const& args); diff --git a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp index 0debd2f28..1ed5b1fc1 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp @@ -52,6 +52,17 @@ TransportState::AddHost(std::shared_ptr host) return S_OK; } +_Use_decl_annotations_ +HRESULT +TransportState::AddPendingHostDefinition(std::shared_ptr hostDefinition) +{ + RETURN_HR_IF_NULL(E_INVALIDARG, hostDefinition); + + m_pendingHostDefinitions.push_back(hostDefinition); + + return S_OK; +} + _Use_decl_annotations_ HRESULT @@ -86,6 +97,10 @@ TransportState::RemoveSessionConnection(_In_ std::wstring endpointDeviceInterfac { m_sessionConnections.erase(cleanId); } + else + { + RETURN_IF_FAILED(E_NOTFOUND); + } return S_OK; } diff --git a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h index d34db4020..781bb4dc7 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h @@ -64,6 +64,10 @@ class TransportState HRESULT AddHost(_In_ std::shared_ptr); std::vector> GetHosts() { return m_hosts; } + HRESULT AddPendingHostDefinition(_In_ std::shared_ptr); + std::vector> GetPendingHostDefinitions() { return m_pendingHostDefinitions; } + + HRESULT AddPendingClientDefinition(_In_ std::shared_ptr); std::vector> GetPendingClientDefinitions() { return m_pendingClientDefinitions; } @@ -83,6 +87,8 @@ class TransportState // key is the host identifier std::vector> m_hosts{ }; + + std::vector> m_pendingHostDefinitions{ }; std::vector> m_pendingClientDefinitions{ }; std::map> m_sessionConnections{ }; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/pch.h b/src/api/Transport/UdpNetworkMidi2Transport/pch.h index 293a2aa53..781497cc4 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/pch.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/pch.h @@ -7,7 +7,7 @@ #endif #include - +#include // mostly for E_NOTFOUND #include #include #include @@ -110,6 +110,7 @@ class MidiNetworkHost; class MidiNetworkClient; class MidiNetworkConnection; struct MidiNetworkClientDefinition; +struct MidiNetworkHostDefinition; //#include "MidiNetworkEndpointDefinition.h" From 7d79ed5c93761a8780c05fceb4b71e92b0dc29cd Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Thu, 9 Jan 2025 19:32:08 -0500 Subject: [PATCH 2/6] Hardening the network host --- .../Midi2.NetworkMidiBidi.cpp | 21 ++-- .../Midi2.NetworkMidiBidi.h | 2 +- .../Midi2.NetworkMidiEndpointManager.cpp | 2 +- .../MidiNetworkConnection.cpp | 117 +++++++++++------- .../MidiNetworkConnection.h | 4 +- .../MidiNetworkDataWriter.cpp | 2 +- .../MidiNetworkHost.cpp | 29 ++++- .../TransportState.cpp | 1 + 8 files changed, 119 insertions(+), 59 deletions(-) diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp index f467b4ef7..232ae3191 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp @@ -45,7 +45,7 @@ CMidi2NetworkMidiBiDi::Initialize( m_connection = TransportState::Current().GetSessionConnection(m_endpointDeviceInterfaceId); RETURN_HR_IF_NULL(E_INVALIDARG, m_connection); - m_connection->SetMidiCallback(this); + RETURN_IF_FAILED(m_connection->SetMidiCallback(this)); return S_OK; } @@ -62,16 +62,18 @@ CMidi2NetworkMidiBiDi::Shutdown() TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - m_callback = nullptr; - m_context = 0; - - if (m_connection) - { - m_connection->RemoveMidiCallback(); - m_connection.reset(); - } + // TODO: This causes the service to crash. Need to look into this further. + //if (m_connection) + //{ + // m_connection->RemoveMidiCallback(); + // m_connection = nullptr; + //} + // + //m_callback = nullptr; + //m_context = 0; + return S_OK; } @@ -86,7 +88,6 @@ CMidi2NetworkMidiBiDi::SendMidiMessage( UNREFERENCED_PARAMETER(position); RETURN_HR_IF_NULL(E_UNEXPECTED, m_connection); - RETURN_HR_IF_NULL(E_INVALIDARG, message); RETURN_HR_IF(E_INVALIDARG, size < sizeof(uint32_t)); diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.h b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.h index c3bcb6733..bc6fbca47 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.h @@ -22,7 +22,7 @@ class CMidi2NetworkMidiBiDi : STDMETHOD(Shutdown)(); private: - IMidiCallback* m_callback{ nullptr }; + wil::com_ptr_nothrow m_callback{ nullptr }; LONGLONG m_context{ 0 }; std::wstring m_endpointDeviceInterfaceId{ }; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp index 47f7656b0..5736796bd 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp @@ -607,7 +607,7 @@ CMidi2NetworkMidiEndpointManager::CreateNewEndpoint( capabilities |= MidiEndpointCapabilities_GenerateIncomingTimestamps; commonProperties.Capabilities = (MidiEndpointCapabilities)capabilities; - bool umpOnly = false; + bool umpOnly = true; RETURN_IF_FAILED(m_midiDeviceManager->ActivateEndpoint( (PCWSTR)m_parentDeviceId.c_str(), // parent instance Id umpOnly, // UMP-only. When set to false, WinMM MIDI 1.0 ports are created diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp index ea6b89892..3d7f8d952 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp @@ -116,20 +116,21 @@ MidiNetworkConnection::StartOutboundProcessingThreads() _Use_decl_annotations_ HRESULT MidiNetworkConnection::SetMidiCallback( - IMidiCallback* callback + wil::com_ptr_nothrow callback ) { + RETURN_HR_IF_NULL(E_INVALIDARG, callback); + TraceLoggingWrite( MidiNetworkMidiTransportTelemetryProvider::Provider(), MIDI_TRACE_EVENT_INFO, TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), TraceLoggingLevel(WINEVENT_LEVEL_INFO), TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) + TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingPointer(callback.get(), "callback") ); - RETURN_HR_IF_NULL(E_INVALIDARG, callback); - m_callback = callback; return S_OK; @@ -170,7 +171,11 @@ MidiNetworkConnection::HandleIncomingUmpData( TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - if (m_callback != nullptr) + + // empty UMP packets are a keep-alive approach + // callback can be null if there are no open connections + // from the client, but the remote device is sending messages + if (m_sessionActive && words.size() > 0 && m_callback != nullptr) { // this may have more than one message, so we need to tease it apart here // and send the individual messages @@ -208,6 +213,8 @@ MidiNetworkConnection::HandleIncomingBye() TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_writer); + if (m_sessionActive) { if (TransportState::Current().GetEndpointManager() != nullptr) @@ -218,8 +225,10 @@ MidiNetworkConnection::HandleIncomingBye() m_sessionActive = false; m_sessionDeviceInstanceId.clear(); m_sessionEndpointDeviceInterfaceId.clear(); + + // m_callback = nullptr; } - + // send bye reply RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); RETURN_IF_FAILED(m_writer->WriteCommandByeReply()); @@ -256,7 +265,6 @@ MidiNetworkConnection::HandleIncomingInvitation( TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - UNREFERENCED_PARAMETER(capabilities); RETURN_HR_IF_NULL(E_UNEXPECTED, m_writer); @@ -290,38 +298,43 @@ MidiNetworkConnection::HandleIncomingInvitation( // This will also kick off discovery and protocol negotiation HRESULT hr = S_OK; - if (TransportState::Current().GetEndpointManager() == nullptr) - { - hr = E_UNEXPECTED; - } - else - { - hr = TransportState::Current().GetEndpointManager()->CreateNewEndpoint( - MidiNetworkConnectionRole::ConnectionWindowsIsHost, - clientUmpEndpointName, - clientProductInstanceId, - m_remoteHostName, - m_remotePort, - newDeviceInstanceId, - newEndpointDeviceInterfaceId - ); - } + + //UNREFERENCED_PARAMETER(clientProductInstanceId); + //UNREFERENCED_PARAMETER(clientUmpEndpointName); + hr = TransportState::Current().GetEndpointManager()->CreateNewEndpoint( + MidiNetworkConnectionRole::ConnectionWindowsIsHost, + clientUmpEndpointName, + clientProductInstanceId, + m_remoteHostName, + m_remotePort, + newDeviceInstanceId, + newEndpointDeviceInterfaceId + ); if (SUCCEEDED(hr)) { - m_sessionEndpointDeviceInterfaceId = newEndpointDeviceInterfaceId; - m_sessionDeviceInstanceId = newDeviceInstanceId; + m_sessionEndpointDeviceInterfaceId = internal::NormalizeEndpointInterfaceIdWStringCopy(newEndpointDeviceInterfaceId); + m_sessionDeviceInstanceId = internal::NormalizeDeviceInstanceIdWStringCopy(newDeviceInstanceId); - std::shared_ptr connection; - connection.reset(this); + std::shared_ptr connection{ this }; // this is what the BiDi uses when it is created - TransportState::Current().AddSessionConnection(m_sessionEndpointDeviceInterfaceId, connection); + RETURN_IF_FAILED(TransportState::Current().AddSessionConnection(m_sessionEndpointDeviceInterfaceId, connection)); // protocol negotiation needs to happen here, not in the endpoint creation // because we need to wire up the connection first. Bit of a race. LOG_IF_FAILED(TransportState::Current().GetEndpointManager()->InitiateDiscoveryAndNegotiation(m_sessionEndpointDeviceInterfaceId)); + + // Tell the remote endpoint we've accepted the invitation + + auto lock = m_socketWriterLock.lock(); + + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); + RETURN_IF_FAILED(m_writer->WriteCommandInvitationReplyAccepted(m_thisEndpointName, m_thisProductInstanceId)); + RETURN_IF_FAILED(m_writer->Send()); + + m_sessionActive = true; } else { @@ -337,16 +350,6 @@ MidiNetworkConnection::HandleIncomingInvitation( // exit out of here, and log while we're at it RETURN_IF_FAILED(hr); } - - // Tell the remote endpoint we've accepted the invitation - - auto lock = m_socketWriterLock.lock(); - - RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); - RETURN_IF_FAILED(m_writer->WriteCommandInvitationReplyAccepted(m_thisEndpointName, m_thisProductInstanceId)); - RETURN_IF_FAILED(m_writer->Send()); - - m_sessionActive = true; } else { @@ -364,6 +367,7 @@ MidiNetworkConnection::HandleIncomingInvitation( { // we are a client, not a host, so NAK this per spec 6.4 auto lock = m_socketWriterLock.lock(); + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); // TODO: Move string to resources for localization @@ -388,6 +392,8 @@ MidiNetworkConnection::HandleIncomingPing(uint32_t const pingId) TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_writer); + auto lock = m_socketWriterLock.lock(); RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); @@ -510,6 +516,9 @@ MidiNetworkConnection::ProcessIncomingMessage( case CommandCommon_UmpData: { + // todo: If the session isn't active, we should reject any UMP data and NAK or BYE (see spec) + + uint8_t numberOfWords = commandHeader.HeaderData.CommandPayloadLength; MidiSequenceNumber sequenceNumber(commandHeader.HeaderData.CommandSpecificData.AsUInt16); @@ -564,11 +573,6 @@ MidiNetworkConnection::ProcessIncomingMessage( // skipped data. Re-request missing packets } - //if (sequenceNumber > m_lastReceivedUmpCommandSequenceNumber) - //{ - // m_lastReceivedUmpCommandSequenceNumber = sequenceNumber; - //} - if (words.size() > 0) { LOG_IF_FAILED(HandleIncomingUmpData(packetMidiTimestamp, words)); @@ -645,6 +649,8 @@ MidiNetworkConnection::HandleIncomingRetransmitRequest(uint16_t const startingSe TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_writer); + // TODO: A packet count of 0x0000 means to send all the data we have starting at the startingSequenceNumber // find the starting sequence number in the circular buffer @@ -709,6 +715,8 @@ MidiNetworkConnection::SendMidiMessagesToNetwork(std::vector const& wo TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); + RETURN_HR_IF_NULL(E_UNEXPECTED, m_writer); + auto sequenceNumber = ++m_lastSentUmpCommandSequenceNumber; auto lock = m_socketWriterLock.lock(); @@ -771,8 +779,31 @@ MidiNetworkConnection::Shutdown() TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); + if (m_callback) + { + m_callback = nullptr; + } + if (m_sessionActive) + { + RETURN_IF_FAILED(TransportState::Current().RemoveSessionConnection(m_sessionEndpointDeviceInterfaceId)); + m_sessionActive = false; + } + if (m_writer) + { + m_writer->Shutdown(); + m_writer = nullptr; + } + + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Exit", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); return S_OK; } diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h index 0e9080683..2e649c20f 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h @@ -50,7 +50,7 @@ class MidiNetworkConnection HRESULT SetMidiCallback( - _In_ IMidiCallback* callback + _In_ wil::com_ptr_nothrow callback ); HRESULT RemoveMidiCallback(); @@ -75,7 +75,7 @@ class MidiNetworkConnection std::wstring m_sessionEndpointDeviceInterfaceId{}; // swd std::wstring m_sessionDeviceInstanceId{}; // what we used to create/delete the device bool m_sessionActive{ false }; - IMidiCallback* m_callback{ nullptr }; + wil::com_ptr_nothrow m_callback{ nullptr }; winrt::Windows::Networking::HostName m_remoteHostName{ nullptr }; std::wstring m_remotePort{ }; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp index a83d8b97c..b13c1f6d9 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp @@ -92,7 +92,7 @@ MidiNetworkDataWriter::Initialize( winrt::Windows::Storage::Streams::DataWriter writer(m_stream); - m_dataWriter = std::move(writer); + m_dataWriter = writer; RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); m_dataWriter.ByteOrder(winrt::Windows::Storage::Streams::ByteOrder::BigEndian); diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp index 531584720..7ce1bfff7 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp @@ -132,11 +132,21 @@ void MidiNetworkHost::OnMessageReceived( DatagramSocket const& sender, DatagramSocketMessageReceivedEventArgs const& args) { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + + UNREFERENCED_PARAMETER(sender); auto reader = args.GetDataReader(); - if (reader.UnconsumedBufferLength() < MINIMUM_VALID_UDP_PACKET_SIZE) + if (reader != nullptr && reader.UnconsumedBufferLength() < MINIMUM_VALID_UDP_PACKET_SIZE) { // not a message we understand. Needs to be at least the size of the // MIDI header plus a command packet header. Really it needs to be larger, but @@ -183,6 +193,16 @@ void MidiNetworkHost::OnMessageReceived( ); } + + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Exit", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + } @@ -215,6 +235,13 @@ MidiNetworkHost::Shutdown() m_socket = nullptr; } + while (m_connections.size() > 0) + { + auto conn = m_connections.begin(); + LOG_IF_FAILED(conn->second->Shutdown()); + + m_connections.erase(conn); + } TraceLoggingWrite( MidiNetworkMidiTransportTelemetryProvider::Provider(), diff --git a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp index 1ed5b1fc1..08bca9c59 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp @@ -95,6 +95,7 @@ TransportState::RemoveSessionConnection(_In_ std::wstring endpointDeviceInterfac if (m_sessionConnections.find(cleanId) != m_sessionConnections.end()) { +// m_sessionConnections.find(cleanId)->second.reset(); m_sessionConnections.erase(cleanId); } else From 9665f586777171e0a9e909891fd57e743e4eb0c6 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Fri, 10 Jan 2025 18:22:16 -0500 Subject: [PATCH 3/6] Fixed network MIDI crash on disconnect --- .../Midi2.NetworkMidiBidi.cpp | 37 +- .../Midi2.NetworkMidiBidi.h | 2 +- .../MidiNetworkConnection.cpp | 131 ++- .../MidiNetworkConnection.h | 8 +- .../MidiNetworkDataWriter.cpp | 17 +- .../MidiNetworkHost.cpp | 58 +- .../MidiNetworkHost.h | 71 +- .../TransportState.cpp | 110 ++- .../UdpNetworkMidi2Transport/TransportState.h | 54 +- src/app-sdk/mididiag/color.hpp | 881 ++++++++++++++++++ src/app-sdk/mididiag/main.cpp | 343 ++++--- src/app-sdk/mididiag/mididiag.vcxproj | 1 + src/app-sdk/mididiag/mididiag.vcxproj.filters | 3 + src/app-sdk/mididiag/mididiag_field_defs.h | 167 ++-- 14 files changed, 1493 insertions(+), 390 deletions(-) create mode 100644 src/app-sdk/mididiag/color.hpp diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp index 232ae3191..8f44b4a85 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.cpp @@ -43,9 +43,16 @@ CMidi2NetworkMidiBiDi::Initialize( // look up the endpointDeviceInterfaceId in our list of endpoints, and connect to it m_connection = TransportState::Current().GetSessionConnection(m_endpointDeviceInterfaceId); - RETURN_HR_IF_NULL(E_INVALIDARG, m_connection); - RETURN_IF_FAILED(m_connection->SetMidiCallback(this)); + if (auto conn = m_connection.lock()) + { + RETURN_IF_FAILED(conn->ConnectMidiCallback(this)); + } + else + { + RETURN_IF_FAILED(E_INVALIDARG); + } + return S_OK; } @@ -62,17 +69,16 @@ CMidi2NetworkMidiBiDi::Shutdown() TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - // TODO: This causes the service to crash. Need to look into this further. - + // TODO: This causes the service to crash when the remote network endpoint disconnects. Need to look into this further. + + if (auto ptr = m_connection.lock()) + { + ptr->DisconnectMidiCallback(); + m_connection.reset(); + } - //if (m_connection) - //{ - // m_connection->RemoveMidiCallback(); - // m_connection = nullptr; - //} - // - //m_callback = nullptr; - //m_context = 0; + m_callback = nullptr; + m_context = 0; return S_OK; } @@ -87,12 +93,13 @@ CMidi2NetworkMidiBiDi::SendMidiMessage( { UNREFERENCED_PARAMETER(position); - RETURN_HR_IF_NULL(E_UNEXPECTED, m_connection); RETURN_HR_IF_NULL(E_INVALIDARG, message); RETURN_HR_IF(E_INVALIDARG, size < sizeof(uint32_t)); - - RETURN_IF_FAILED(m_connection->SendMidiMessagesToNetwork(message, size)); + if (auto conn = m_connection.lock()) + { + RETURN_IF_FAILED(conn->SendMidiMessagesToNetwork(message, size)); + } return S_OK; } diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.h b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.h index bc6fbca47..8b9dab1d7 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiBidi.h @@ -27,7 +27,7 @@ class CMidi2NetworkMidiBiDi : std::wstring m_endpointDeviceInterfaceId{ }; - std::shared_ptr m_connection{ nullptr }; + std::weak_ptr m_connection; }; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp index 3d7f8d952..76141b576 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp @@ -66,8 +66,7 @@ MidiNetworkConnection::Initialize( RETURN_IF_FAILED(E_OUTOFMEMORY); } - m_lastSentUmpCommandSequenceNumber = 0; - m_lastSentUmpCommandSequenceNumber--; // init to this so first real one is zero + ResetSequenceNumbers(); // create the data writer m_writer = std::make_shared(); @@ -107,6 +106,15 @@ MidiNetworkConnection::StartOutboundProcessingThreads() + + + + + + + + + return S_OK; } @@ -115,7 +123,7 @@ MidiNetworkConnection::StartOutboundProcessingThreads() _Use_decl_annotations_ HRESULT -MidiNetworkConnection::SetMidiCallback( +MidiNetworkConnection::ConnectMidiCallback( wil::com_ptr_nothrow callback ) { @@ -131,13 +139,20 @@ MidiNetworkConnection::SetMidiCallback( TraceLoggingPointer(callback.get(), "callback") ); + // the previous callback wasn't disconnected. Something + // is not as it should be, so we'll fail. + if (m_callback != nullptr) + { + RETURN_IF_FAILED(E_UNEXPECTED); + } + m_callback = callback; return S_OK; } HRESULT -MidiNetworkConnection::RemoveMidiCallback() +MidiNetworkConnection::DisconnectMidiCallback() { TraceLoggingWrite( MidiNetworkMidiTransportTelemetryProvider::Provider(), @@ -148,7 +163,10 @@ MidiNetworkConnection::RemoveMidiCallback() TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - m_callback = nullptr; + if (m_callback != nullptr) + { + m_callback = nullptr; + } return S_OK; } @@ -199,6 +217,59 @@ MidiNetworkConnection::HandleIncomingUmpData( return S_OK; } +HRESULT +MidiNetworkConnection::ResetSequenceNumbers() +{ + // reset the last sent sequence number + m_lastSentUmpCommandSequenceNumber = 0; + m_lastSentUmpCommandSequenceNumber--; // prepare for next + + // reset the last received sequence number. + m_lastReceivedUmpCommandSequenceNumber = 0; + m_lastReceivedUmpCommandSequenceNumber--; + + // clear out retransmit buffer + m_retransmitBuffer.clear(); + + return S_OK; +} + +HRESULT +MidiNetworkConnection::EndActiveSession() +{ + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + + m_sessionActive = false; + + // clear the outbound queue + m_outgoingUmpMessages.clear(); + ResetSequenceNumbers(); + + m_callback = nullptr; + RETURN_IF_FAILED(TransportState::Current().GetEndpointManager()->DeleteEndpoint(m_sessionDeviceInstanceId)); + m_sessionDeviceInstanceId.clear(); + + // send bye reply + + auto lock = m_socketWriterLock.lock(); + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); + RETURN_IF_FAILED(m_writer->WriteCommandByeReply()); + RETURN_IF_FAILED(m_writer->Send()); + + // clear the association with the SWD + RETURN_IF_FAILED(TransportState::Current().DisassociateMidiEndpointFromConnection(m_sessionEndpointDeviceInterfaceId)); + m_sessionEndpointDeviceInterfaceId.clear(); + + + return S_OK; +} HRESULT @@ -214,34 +285,21 @@ MidiNetworkConnection::HandleIncomingBye() ); RETURN_HR_IF_NULL(E_UNEXPECTED, m_writer); + RETURN_HR_IF_NULL(E_UNEXPECTED, TransportState::Current().GetEndpointManager()); if (m_sessionActive) { - if (TransportState::Current().GetEndpointManager() != nullptr) - { - RETURN_IF_FAILED(TransportState::Current().GetEndpointManager()->DeleteEndpoint(m_sessionDeviceInstanceId)); - RETURN_IF_FAILED(TransportState::Current().RemoveSessionConnection(m_sessionEndpointDeviceInterfaceId)); - - m_sessionActive = false; - m_sessionDeviceInstanceId.clear(); - m_sessionEndpointDeviceInterfaceId.clear(); - - // m_callback = nullptr; - } - - // send bye reply - RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); - RETURN_IF_FAILED(m_writer->WriteCommandByeReply()); - RETURN_IF_FAILED(m_writer->Send()); - + RETURN_IF_FAILED(EndActiveSession()); } else { // not an active session. Nothing to clean up // but we should NAK the Bye saying there's no active session - m_writer->WriteUdpPacketHeader(); - m_writer->WriteCommandNAK(0, MidiNetworkCommandNAKReason::CommandNAKReason_CommandNotExpected, L"BYE received when there's no active session."); - m_writer->Send(); + + auto lock = m_socketWriterLock.lock(); + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); + RETURN_IF_FAILED(m_writer->WriteCommandNAK(0, MidiNetworkCommandNAKReason::CommandNAKReason_CommandNotExpected, L"BYE received when there's no active session.")); + RETURN_IF_FAILED(m_writer->Send()); } return S_OK; @@ -274,8 +332,9 @@ MidiNetworkConnection::HandleIncomingInvitation( { if (m_sessionActive) { - // if the session is already active, we simply accept + // if the session is already active, we simply accept it again + auto lock = m_socketWriterLock.lock(); RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); RETURN_IF_FAILED(m_writer->WriteCommandInvitationReplyAccepted(m_thisEndpointName, m_thisProductInstanceId)); RETURN_IF_FAILED(m_writer->Send()); @@ -316,10 +375,8 @@ MidiNetworkConnection::HandleIncomingInvitation( m_sessionEndpointDeviceInterfaceId = internal::NormalizeEndpointInterfaceIdWStringCopy(newEndpointDeviceInterfaceId); m_sessionDeviceInstanceId = internal::NormalizeDeviceInstanceIdWStringCopy(newDeviceInstanceId); - std::shared_ptr connection{ this }; - // this is what the BiDi uses when it is created - RETURN_IF_FAILED(TransportState::Current().AddSessionConnection(m_sessionEndpointDeviceInterfaceId, connection)); + RETURN_IF_FAILED(TransportState::Current().AssociateMidiEndpointWithConnection(m_sessionEndpointDeviceInterfaceId.c_str(), m_remoteHostName, m_remotePort.c_str())); // protocol negotiation needs to happen here, not in the endpoint creation // because we need to wire up the connection first. Bit of a race. @@ -329,7 +386,6 @@ MidiNetworkConnection::HandleIncomingInvitation( // Tell the remote endpoint we've accepted the invitation auto lock = m_socketWriterLock.lock(); - RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); RETURN_IF_FAILED(m_writer->WriteCommandInvitationReplyAccepted(m_thisEndpointName, m_thisProductInstanceId)); RETURN_IF_FAILED(m_writer->Send()); @@ -341,7 +397,6 @@ MidiNetworkConnection::HandleIncomingInvitation( // let the other side know that we can't create the session auto lock = m_socketWriterLock.lock(); - RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); // TODO: Move string to resources for localization RETURN_IF_FAILED(m_writer->WriteCommandBye(MidiNetworkCommandByeReason::CommandByeReasonCommon_Undefined, L"Error attempting to create endpoint. Bad data?")); @@ -356,7 +411,6 @@ MidiNetworkConnection::HandleIncomingInvitation( // this shouldn't happen, but we handle it anyway auto lock = m_socketWriterLock.lock(); - RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); // TODO: Move string to resources for localization RETURN_IF_FAILED(m_writer->WriteCommandBye(MidiNetworkCommandByeReason::CommandByeReasonCommon_Undefined, L"Host is unable to accept invitations at this time.")); @@ -367,7 +421,6 @@ MidiNetworkConnection::HandleIncomingInvitation( { // we are a client, not a host, so NAK this per spec 6.4 auto lock = m_socketWriterLock.lock(); - RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); // TODO: Move string to resources for localization @@ -518,7 +571,6 @@ MidiNetworkConnection::ProcessIncomingMessage( { // todo: If the session isn't active, we should reject any UMP data and NAK or BYE (see spec) - uint8_t numberOfWords = commandHeader.HeaderData.CommandPayloadLength; MidiSequenceNumber sequenceNumber(commandHeader.HeaderData.CommandSpecificData.AsUInt16); @@ -786,13 +838,18 @@ MidiNetworkConnection::Shutdown() if (m_sessionActive) { - RETURN_IF_FAILED(TransportState::Current().RemoveSessionConnection(m_sessionEndpointDeviceInterfaceId)); + RETURN_IF_FAILED(TransportState::Current().DisassociateMidiEndpointFromConnection(m_sessionEndpointDeviceInterfaceId)); m_sessionActive = false; } - if (m_writer) + if (m_writer != nullptr) { - m_writer->Shutdown(); + auto lock = m_socketWriterLock.lock(); + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); + RETURN_IF_FAILED(m_writer->WriteCommandBye(MidiNetworkCommandByeReason::CommandByeReasonCommon_PowerDown, L"Connection closing.")); + RETURN_IF_FAILED(m_writer->Send()); + + LOG_IF_FAILED(m_writer->Shutdown()); m_writer = nullptr; } diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h index 2e649c20f..225d77bed 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h @@ -49,11 +49,11 @@ class MidiNetworkConnection _In_ UINT const byteCount); - HRESULT SetMidiCallback( + HRESULT ConnectMidiCallback( _In_ wil::com_ptr_nothrow callback ); - HRESULT RemoveMidiCallback(); + HRESULT DisconnectMidiCallback(); // todo: session info, connection to bidi streams, etc. @@ -61,8 +61,12 @@ class MidiNetworkConnection HRESULT StartOutboundProcessingThreads(); + HRESULT ResetSequenceNumbers(); + HRESULT EndActiveSession(); + uint32_t m_emptyUmpIterationCounter{ 0 }; // this will increment over time + uint32_t m_emptyUmpIterationIntervalBeforeSending{ 10 }; // number of background thread iterations with no data before we send empty UMP packets. This changes over time. MidiNetworkConnectionRole m_role{}; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp index b13c1f6d9..006192c96 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkDataWriter.cpp @@ -95,6 +95,7 @@ MidiNetworkDataWriter::Initialize( m_dataWriter = writer; RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); + // per Network MIDI 2.0 specification m_dataWriter.ByteOrder(winrt::Windows::Storage::Streams::ByteOrder::BigEndian); return S_OK; @@ -112,7 +113,6 @@ MidiNetworkDataWriter::WriteCommandPing(uint32_t pingId) auto lock = m_dataWriterLock.lock(); RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_Ping, payloadLengthIn32BitWords, 0)); - m_dataWriter.WriteUInt32(pingId); return S_OK; @@ -130,7 +130,6 @@ MidiNetworkDataWriter::WriteCommandPingReply(uint32_t pingId) auto lock = m_dataWriterLock.lock(); RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_PingReply, payloadLengthIn32BitWords, 0)); - m_dataWriter.WriteUInt32(pingId); return S_OK; @@ -244,6 +243,8 @@ MidiNetworkDataWriter::WriteCommandSessionResetReply() return S_OK; } +#define MIDI_NETWORK_RESERVED_UINT16 ((uint16_t)0) + _Use_decl_annotations_ HRESULT MidiNetworkDataWriter::WriteCommandRetransmitRequest( @@ -261,7 +262,7 @@ MidiNetworkDataWriter::WriteCommandRetransmitRequest( RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_RetransmitRequest, payloadLengthIn32BitWords, sequenceNumber.Value())); m_dataWriter.WriteUInt16(numberOfUmpCommands); - m_dataWriter.WriteUInt16(0); + m_dataWriter.WriteUInt16(MIDI_NETWORK_RESERVED_UINT16); return S_OK; } @@ -284,7 +285,7 @@ MidiNetworkDataWriter::WriteCommandRetransmitError( RETURN_IF_FAILED(InternalWriteCommandHeader(MidiNetworkCommandCode::CommandCommon_RetransmitError, payloadLengthIn32BitWords, errorReason, 0)); m_dataWriter.WriteUInt16(sequenceNumber.Value()); - m_dataWriter.WriteUInt16(0); + m_dataWriter.WriteUInt16(MIDI_NETWORK_RESERVED_UINT16); return S_OK; } @@ -338,6 +339,7 @@ MidiNetworkDataWriter::WriteCommandInvitationWithAuthentication( auto lock = m_dataWriterLock.lock(); + // todo: we should NAK this for now return E_NOTIMPL; } @@ -359,6 +361,7 @@ MidiNetworkDataWriter::WriteCommandInvitationWithUserAuthentication( auto lock = m_dataWriterLock.lock(); + // TODO: we should NAK this for now return E_NOTIMPL; } @@ -373,6 +376,9 @@ MidiNetworkDataWriter::WriteCommandInvitationReplyAccepted( RETURN_HR_IF_NULL(E_UNEXPECTED, m_dataWriter); RETURN_HR_IF_NULL(E_UNEXPECTED, m_stream); + RETURN_HR_IF(E_INVALIDARG, hostUmpEndpointName.empty()); + RETURN_HR_IF(E_INVALIDARG, hostProductInstanceId.empty()); + byte payloadLengthIn32BitWords{ 0 }; auto endpointNameUtf8 = ConvertWStringToUTF8(hostUmpEndpointName, MIDI_MAX_UMP_ENDPOINT_NAME_BYTE_COUNT); @@ -409,6 +415,7 @@ MidiNetworkDataWriter::WriteCommandInvitationReplyPending( auto lock = m_dataWriterLock.lock(); + // TODO return E_NOTIMPL; } @@ -433,6 +440,7 @@ MidiNetworkDataWriter::WriteCommandInvitationReplyAuthenticationRequired( auto lock = m_dataWriterLock.lock(); + // TODO return E_NOTIMPL; } @@ -457,6 +465,7 @@ MidiNetworkDataWriter::WriteCommandInvitationReplyUserAuthenticationRequired( auto lock = m_dataWriterLock.lock(); + // TODO return E_NOTIMPL; } diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp index 7ce1bfff7..b44cb304b 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp @@ -73,6 +73,35 @@ MidiNetworkHost::Initialize( return S_OK; } +_Use_decl_annotations_ +HRESULT +MidiNetworkHost::CreateNetworkConnection(HostName const& remoteHostName, winrt::hstring const& remotePort) +{ + auto conn = std::make_shared(); + + if (conn) + { + RETURN_IF_FAILED(conn->Initialize( + MidiNetworkConnectionRole::ConnectionWindowsIsHost, + m_socket, + remoteHostName, + remotePort, + m_hostEndpointName, + m_hostProductInstanceId, + TransportState::Current().TransportSettings.RetransmitBufferMaxCommandPacketCount, + TransportState::Current().TransportSettings.ForwardErrorCorrectionMaxCommandPacketCount + )); + + TransportState::Current().AddNetworkConnection(remoteHostName, remotePort, conn); + } + else + { + // could not create the connection object. + } + + return S_OK; +} + HRESULT MidiNetworkHost::Start() { @@ -175,7 +204,14 @@ void MidiNetworkHost::OnMessageReceived( return; } - auto conn = GetOrCreateConnection(args.RemoteAddress(), args.RemotePort()); + std::shared_ptr conn{ nullptr }; + + if (!TransportState::Current().NetworkConnectionExists(args.RemoteAddress(), args.RemotePort())) + { + LOG_IF_FAILED(CreateNetworkConnection(args.RemoteAddress(), args.RemotePort())); + } + + conn = TransportState::Current().GetNetworkConnection(args.RemoteAddress(), args.RemotePort()); if (conn) { @@ -224,6 +260,8 @@ MidiNetworkHost::Shutdown() } // TODO: send "bye" to all sessions, and then unbind the socket + // but we need to restrict to this host, not every host/client + // TODO: Stop packet processing thread using the jthread stop token m_keepProcessing = false; @@ -235,13 +273,13 @@ MidiNetworkHost::Shutdown() m_socket = nullptr; } - while (m_connections.size() > 0) - { - auto conn = m_connections.begin(); - LOG_IF_FAILED(conn->second->Shutdown()); + //while (m_connections.size() > 0) + //{ + // auto conn = m_connections.begin(); + // LOG_IF_FAILED(conn->second->Shutdown()); - m_connections.erase(conn); - } + // m_connections.erase(conn); + //} TraceLoggingWrite( MidiNetworkMidiTransportTelemetryProvider::Provider(), @@ -253,4 +291,8 @@ MidiNetworkHost::Shutdown() ); return S_OK; -} \ No newline at end of file +} + + + + diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h index feb09a4de..52f31cc9d 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h @@ -77,9 +77,7 @@ class MidiNetworkHost { public: HRESULT Initialize(_In_ MidiNetworkHostDefinition& hostDefinition); - HRESULT Start(); - HRESULT Shutdown(); bool HasStarted() { return m_started; } @@ -87,16 +85,11 @@ class MidiNetworkHost private: bool m_started{ false }; - //HRESULT EstablishNewSession(); std::wstring m_hostEndpointName{ }; std::wstring m_hostProductInstanceId{ }; std::atomic m_keepProcessing{ true }; - // todo: these probably aren't necessary. The only queues we need are for midi messages -// std::queue m_incomingOutOfBandCommands; -// std::queue m_outgoingOutOfBandCommands; - winrt::event_token m_messageReceivedEventToken; void OnMessageReceived( @@ -110,68 +103,6 @@ class MidiNetworkHost DatagramSocket m_socket{ nullptr }; - // Map of MidiNetworkHostSessions and their related remote client addresses - // the keys for these two maps are the same values created with CreateSessionMapKey - std::map> m_connections{ }; - - inline std::string CreateConnectionMapKey(_In_ HostName const& hostName, _In_ winrt::hstring const& port) - { - return winrt::to_string(hostName.CanonicalName() + L":" + port); - } - - inline bool ConnectionExists(_In_ HostName const& hostName, _In_ winrt::hstring const& port) - { - auto key = CreateConnectionMapKey(hostName, port); - - return m_connections.find(key) != m_connections.end(); - } - - - inline void RemoveConnection(_In_ HostName const& hostName, _In_ winrt::hstring const& port) - { - if (ConnectionExists(hostName, port)) - { - auto entry = m_connections.find(CreateConnectionMapKey(hostName, port)); - - LOG_IF_FAILED(entry->second->Shutdown()); - - m_connections.erase(CreateConnectionMapKey(hostName, port)); - } - } - - inline std::shared_ptr GetOrCreateConnection(_In_ HostName const& hostName, _In_ winrt::hstring const& port) - { - auto key = CreateConnectionMapKey(hostName, port); - - if (!ConnectionExists(hostName, port)) - { - // need to create it and then add one - - auto conn = std::make_shared(); - - if (conn) - { - conn->Initialize( - MidiNetworkConnectionRole::ConnectionWindowsIsHost, - m_socket, - hostName, - port, - m_hostEndpointName, - m_hostProductInstanceId, - TransportState::Current().TransportSettings.RetransmitBufferMaxCommandPacketCount, - TransportState::Current().TransportSettings.ForwardErrorCorrectionMaxCommandPacketCount - ); - - m_connections.insert_or_assign(key, conn); - } - else - { - // could not create the connection object. - } - } - - return m_connections.find(key)->second; - } - + HRESULT CreateNetworkConnection(_In_ winrt::Windows::Networking::HostName const& remoteHostName, _In_ winrt::hstring const& remotePort); }; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp index 08bca9c59..c8ebe6737 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp @@ -22,7 +22,6 @@ TransportState& TransportState::Current() } - HRESULT TransportState::ConstructEndpointManager() { @@ -31,7 +30,6 @@ TransportState::ConstructEndpointManager() return S_OK; } - HRESULT TransportState::ConstructConfigurationManager() { @@ -41,9 +39,12 @@ TransportState::ConstructConfigurationManager() } + + _Use_decl_annotations_ HRESULT -TransportState::AddHost(std::shared_ptr host) +TransportState::AddHost( + std::shared_ptr host) { RETURN_HR_IF_NULL(E_INVALIDARG, host); @@ -54,7 +55,8 @@ TransportState::AddHost(std::shared_ptr host) _Use_decl_annotations_ HRESULT -TransportState::AddPendingHostDefinition(std::shared_ptr hostDefinition) +TransportState::AddPendingHostDefinition( + std::shared_ptr hostDefinition) { RETURN_HR_IF_NULL(E_INVALIDARG, hostDefinition); @@ -66,7 +68,8 @@ TransportState::AddPendingHostDefinition(std::shared_ptr clientDefinition) +TransportState::AddPendingClientDefinition( + std::shared_ptr clientDefinition) { RETURN_HR_IF_NULL(E_INVALIDARG, clientDefinition); @@ -76,26 +79,45 @@ TransportState::AddPendingClientDefinition(std::shared_ptr connection) +TransportState::AssociateMidiEndpointWithConnection( + _In_ std::wstring endpointDeviceInterfaceId, + _In_ winrt::Windows::Networking::HostName const& remoteHostName, + _In_ winrt::hstring const& remotePort) { auto cleanId = internal::NormalizeEndpointInterfaceIdWStringCopy(endpointDeviceInterfaceId); - m_sessionConnections.insert_or_assign(cleanId, connection); + // find the connection and then associate it here + + auto connection = GetNetworkConnection(remoteHostName, remotePort); + + if (connection != nullptr) + { + m_sessionConnections.insert_or_assign(cleanId, connection); + + return S_OK; + } + else + { + return E_FAIL; + } - return S_OK; } _Use_decl_annotations_ HRESULT -TransportState::RemoveSessionConnection(_In_ std::wstring endpointDeviceInterfaceId) +TransportState::DisassociateMidiEndpointFromConnection( + std::wstring endpointDeviceInterfaceId) { + RETURN_HR_IF(E_INVALIDARG, endpointDeviceInterfaceId.empty()); auto cleanId = internal::NormalizeEndpointInterfaceIdWStringCopy(endpointDeviceInterfaceId); + RETURN_HR_IF(E_INVALIDARG, cleanId.empty()); if (m_sessionConnections.find(cleanId) != m_sessionConnections.end()) { -// m_sessionConnections.find(cleanId)->second.reset(); + //m_sessionConnections.find(cleanId)->second.reset(); m_sessionConnections.erase(cleanId); } else @@ -107,7 +129,8 @@ TransportState::RemoveSessionConnection(_In_ std::wstring endpointDeviceInterfac } _Use_decl_annotations_ -std::shared_ptr TransportState::GetSessionConnection(_In_ std::wstring endpointDeviceInterfaceId) +std::shared_ptr +TransportState::GetSessionConnection(_In_ std::wstring endpointDeviceInterfaceId) { auto cleanId = internal::NormalizeEndpointInterfaceIdWStringCopy(endpointDeviceInterfaceId); @@ -119,3 +142,68 @@ std::shared_ptr TransportState::GetSessionConnection(_In_ return nullptr; } + + + + +_Use_decl_annotations_ +bool +TransportState::NetworkConnectionExists( + winrt::Windows::Networking::HostName const& remoteHostName, + winrt::hstring const& port) +{ + auto key = CreateNetworkConnectionMapKey(remoteHostName, port); + + return m_networkConnections.find(key) != m_networkConnections.end(); +} + + +_Use_decl_annotations_ +HRESULT +TransportState::RemoveNetworkConnection( + winrt::Windows::Networking::HostName const& remoteHostName, + winrt::hstring const& remotePort) +{ + if (NetworkConnectionExists(remoteHostName, remotePort)) + { + auto entry = m_networkConnections.find(CreateNetworkConnectionMapKey(remoteHostName, remotePort)); + + LOG_IF_FAILED(entry->second->Shutdown()); + + m_networkConnections.erase(CreateNetworkConnectionMapKey(remoteHostName, remotePort)); + } + + return S_OK; +} + +_Use_decl_annotations_ +std::shared_ptr +TransportState::GetNetworkConnection( + winrt::Windows::Networking::HostName const& remoteHostName, + winrt::hstring const& remotePort) +{ + auto key = CreateNetworkConnectionMapKey(remoteHostName, remotePort); + + if (NetworkConnectionExists(remoteHostName, remotePort)) + { + return m_networkConnections.find(key)->second; + } + + return nullptr; +} + + +_Use_decl_annotations_ +HRESULT +TransportState::AddNetworkConnection( + winrt::Windows::Networking::HostName const& remoteHostName, + winrt::hstring const& remotePort, + std::shared_ptr connection +) +{ + auto key = CreateNetworkConnectionMapKey(remoteHostName, remotePort); + + m_networkConnections.insert_or_assign(key, connection); + + return S_OK; +} diff --git a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h index 781bb4dc7..7638f4ecb 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h @@ -48,7 +48,7 @@ class TransportState //} - HRESULT Cleanup() + HRESULT Shutdown() { m_endpointManager.reset(); m_configurationManager.reset(); @@ -61,21 +61,52 @@ class TransportState HRESULT ConstructEndpointManager(); HRESULT ConstructConfigurationManager(); - HRESULT AddHost(_In_ std::shared_ptr); + HRESULT AddHost( + _In_ std::shared_ptr); std::vector> GetHosts() { return m_hosts; } - HRESULT AddPendingHostDefinition(_In_ std::shared_ptr); + HRESULT AddPendingHostDefinition( + _In_ std::shared_ptr); std::vector> GetPendingHostDefinitions() { return m_pendingHostDefinitions; } - HRESULT AddPendingClientDefinition(_In_ std::shared_ptr); + HRESULT AddPendingClientDefinition( + _In_ std::shared_ptr); std::vector> GetPendingClientDefinitions() { return m_pendingClientDefinitions; } - HRESULT AddSessionConnection(_In_ std::wstring endpointDeviceInterfaceId, _In_ std::shared_ptr connection); - HRESULT RemoveSessionConnection(_In_ std::wstring endpointDeviceInterfaceId); - std::shared_ptr GetSessionConnection(_In_ std::wstring endpointDeviceInterfaceId); + // these two sets of functions, and their related maps, work with the same + // connection objects, just in different states + // these are for when the connection is associated with a UMP endpoint + HRESULT AssociateMidiEndpointWithConnection( + _In_ std::wstring endpointDeviceInterfaceId, + _In_ winrt::Windows::Networking::HostName const& remoteHostName, + _In_ winrt::hstring const& remotePort); + + HRESULT DisassociateMidiEndpointFromConnection( + _In_ std::wstring endpointDeviceInterfaceId); + + std::shared_ptr GetSessionConnection( + _In_ std::wstring endpointDeviceInterfaceId); + + // these are for when the connection is first created. They also live through when they become UMP endpoints + bool NetworkConnectionExists( + _In_ winrt::Windows::Networking::HostName const& remoteHostName, + _In_ winrt::hstring const& remotePort); + + std::shared_ptr GetNetworkConnection( + _In_ winrt::Windows::Networking::HostName const& remoteHostName, + _In_ winrt::hstring const& remotePort); + + HRESULT AddNetworkConnection( + _In_ winrt::Windows::Networking::HostName const& remoteHostName, + _In_ winrt::hstring const& remotePort, + _In_ std::shared_ptr connection); + + HRESULT RemoveNetworkConnection( + _In_ winrt::Windows::Networking::HostName const& remoteHostName, + _In_ winrt::hstring const& remotePort); private: TransportState(); @@ -93,4 +124,13 @@ class TransportState std::map> m_sessionConnections{ }; + // Map of MidiNetworkConnections and their related remote client addresses + // the keys for these two maps are the values created with CreateConnectionMapKey + std::map> m_networkConnections{ }; + + inline std::string CreateNetworkConnectionMapKey(_In_ winrt::Windows::Networking::HostName const& remoteHostName, _In_ winrt::hstring const& remotePort) + { + return winrt::to_string(remoteHostName.CanonicalName() + L":" + remotePort); + } + }; \ No newline at end of file diff --git a/src/app-sdk/mididiag/color.hpp b/src/app-sdk/mididiag/color.hpp new file mode 100644 index 000000000..cb080b6e5 --- /dev/null +++ b/src/app-sdk/mididiag/color.hpp @@ -0,0 +1,881 @@ +// source: https://github.com/aafulei/color-console/blob/master/include/color.hpp + + +#ifndef COLOR_HPP +#define COLOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hue +{ + constexpr int DEFAULT_COLOR = 7; + constexpr int BAD_COLOR = -256; + + const std::map CODES = { + {"black", 0}, {"k", 0}, + {"blue", 1}, {"b", 1}, + {"green", 2}, {"g", 2}, + {"aqua", 3}, {"a", 3}, + {"red", 4}, {"r", 4}, + {"purple", 5}, {"p", 5}, + {"yellow", 6}, {"y", 6}, + {"white", 7}, {"w", 7}, + {"grey", 8}, {"e", 8}, + {"light blue", 9}, {"lb", 9}, + {"light green", 10}, {"lg", 10}, + {"light aqua", 11}, {"la", 11}, + {"light red", 12}, {"lr", 12}, + {"light purple", 13}, {"lp", 13}, + {"light yellow", 14}, {"ly", 14}, + {"bright white", 15}, {"bw", 15} + }; + + const std::map NAMES = { + { 0, "black"}, + { 1, "blue"}, + { 2, "green"}, + { 3, "aqua"}, + { 4, "red"}, + { 5, "purple"}, + { 6, "yellow"}, + { 7, "white"}, + { 8, "grey"}, + { 9, "light blue"}, + {10, "light green"}, + {11, "light aqua"}, + {12, "light red"}, + {13, "light purple"}, + {14, "light yellow"}, + {15, "bright white"} + }; + + inline bool is_good(int c) + { + return 0 <= c && c < 256; + } + + inline int itoc(int c) + { + return is_good(c) ? c : BAD_COLOR; + } + + inline int itoc(int a, int b) + { + return itoc(a + b * 16); + } + + // std::string to color + int stoc(std::string a) + { + // convert s to lowercase, and format variants like "light_blue" + std::transform(a.begin(), a.end(), a.begin(), [](char c) + { + if ('A' <= c && c <= 'Z') + c = c - 'A' + 'a'; + else if (c == '_' || c == '-') + c = ' '; + return c; + }); + + // operator[] on std::map is non-const, use std::map::at instead + return (CODES.find(a) != CODES.end()) ? CODES.at(a) : BAD_COLOR; + } + + int stoc(std::string a, std::string b) + { + return itoc(stoc(a), stoc(b)); + } + + std::string ctos(int c) + { + return (0 <= c && c < 256) ? + "(text) " + NAMES.at(c % 16) + " + " + + "(background) " + NAMES.at(c / 16) : + "BAD COLOR"; + } + + int get() + { + CONSOLE_SCREEN_BUFFER_INFO i; + return GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &i) ? + i.wAttributes : BAD_COLOR; + } + + int get_text() + { + return (get() != BAD_COLOR) ? get() % 16 : BAD_COLOR; + } + + int get_background() + { + return (get() != BAD_COLOR) ? get() / 16 : BAD_COLOR; + } + + void set(int c) + { + if (is_good(c)) + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); + } + + void set(int a, int b) + { + set(a + b * 16); + } + + void set(std::string a, std::string b) + { + set(stoc(a) + stoc(b) * 16); + } + + void set_text(std::string a) + { + set(stoc(a), get_background()); + } + + void set_background(std::string b) + { + set(get_text(), stoc(b)); + } + + void reset() + { + set(DEFAULT_COLOR); + } + + int invert(int c) + { + if (is_good(c)) { + int a = c % 16; + int b = c / 16; + return b + a * 16; + } + else + return BAD_COLOR; + } + + std::ostream& reset(std::ostream& os) { reset(); return os; } + std::ostream& black(std::ostream& os) { set_text("k"); return os; } + std::ostream& blue(std::ostream& os) { set_text("b"); return os; } + std::ostream& green(std::ostream& os) { set_text("g"); return os; } + std::ostream& aqua(std::ostream& os) { set_text("a"); return os; } + std::ostream& red(std::ostream& os) { set_text("r"); return os; } + std::ostream& purple(std::ostream& os) { set_text("p"); return os; } + std::ostream& yellow(std::ostream& os) { set_text("y"); return os; } + std::ostream& white(std::ostream& os) { set_text("w"); return os; } + std::ostream& grey(std::ostream& os) { set_text("e"); return os; } + std::ostream& light_blue(std::ostream& os) { set_text("lb"); return os; } + std::ostream& light_green(std::ostream& os) { set_text("lg"); return os; } + std::ostream& light_aqua(std::ostream& os) { set_text("la"); return os; } + std::ostream& light_red(std::ostream& os) { set_text("lr"); return os; } + std::ostream& light_purple(std::ostream& os) { set_text("lp"); return os; } + std::ostream& light_yellow(std::ostream& os) { set_text("ly"); return os; } + std::ostream& bright_white(std::ostream& os) { set_text("bw"); return os; } + std::ostream& on_black(std::ostream& os) { set_background("k"); return os; } + std::ostream& on_blue(std::ostream& os) { set_background("b"); return os; } + std::ostream& on_green(std::ostream& os) { set_background("g"); return os; } + std::ostream& on_aqua(std::ostream& os) { set_background("a"); return os; } + std::ostream& on_red(std::ostream& os) { set_background("r"); return os; } + std::ostream& on_purple(std::ostream& os) { set_background("p"); return os; } + std::ostream& on_yellow(std::ostream& os) { set_background("y"); return os; } + std::ostream& on_white(std::ostream& os) { set_background("w"); return os; } + std::ostream& on_grey(std::ostream& os) { set_background("e"); return os; } + std::ostream& on_light_blue(std::ostream& os) { set_background("lb"); return os; } + std::ostream& on_light_green(std::ostream& os) { set_background("lg"); return os; } + std::ostream& on_light_aqua(std::ostream& os) { set_background("la"); return os; } + std::ostream& on_light_red(std::ostream& os) { set_background("lr"); return os; } + std::ostream& on_light_purple(std::ostream& os) { set_background("lp"); return os; } + std::ostream& on_light_yellow(std::ostream& os) { set_background("ly"); return os; } + std::ostream& on_bright_white(std::ostream& os) { set_background("bw"); return os; } + std::ostream& black_on_black(std::ostream& os) { set("k", "k"); return os; } + std::ostream& black_on_blue(std::ostream& os) { set("k", "b"); return os; } + std::ostream& black_on_green(std::ostream& os) { set("k", "g"); return os; } + std::ostream& black_on_aqua(std::ostream& os) { set("k", "a"); return os; } + std::ostream& black_on_red(std::ostream& os) { set("k", "r"); return os; } + std::ostream& black_on_purple(std::ostream& os) { set("k", "p"); return os; } + std::ostream& black_on_yellow(std::ostream& os) { set("k", "y"); return os; } + std::ostream& black_on_white(std::ostream& os) { set("k", "w"); return os; } + std::ostream& black_on_grey(std::ostream& os) { set("k", "e"); return os; } + std::ostream& black_on_light_blue(std::ostream& os) { set("k", "lb"); return os; } + std::ostream& black_on_light_green(std::ostream& os) { set("k", "lg"); return os; } + std::ostream& black_on_light_aqua(std::ostream& os) { set("k", "la"); return os; } + std::ostream& black_on_light_red(std::ostream& os) { set("k", "lr"); return os; } + std::ostream& black_on_light_purple(std::ostream& os) { set("k", "lp"); return os; } + std::ostream& black_on_light_yellow(std::ostream& os) { set("k", "ly"); return os; } + std::ostream& black_on_bright_white(std::ostream& os) { set("k", "bw"); return os; } + std::ostream& blue_on_black(std::ostream& os) { set("b", "k"); return os; } + std::ostream& blue_on_blue(std::ostream& os) { set("b", "b"); return os; } + std::ostream& blue_on_green(std::ostream& os) { set("b", "g"); return os; } + std::ostream& blue_on_aqua(std::ostream& os) { set("b", "a"); return os; } + std::ostream& blue_on_red(std::ostream& os) { set("b", "r"); return os; } + std::ostream& blue_on_purple(std::ostream& os) { set("b", "p"); return os; } + std::ostream& blue_on_yellow(std::ostream& os) { set("b", "y"); return os; } + std::ostream& blue_on_white(std::ostream& os) { set("b", "w"); return os; } + std::ostream& blue_on_grey(std::ostream& os) { set("b", "e"); return os; } + std::ostream& blue_on_light_blue(std::ostream& os) { set("b", "lb"); return os; } + std::ostream& blue_on_light_green(std::ostream& os) { set("b", "lg"); return os; } + std::ostream& blue_on_light_aqua(std::ostream& os) { set("b", "la"); return os; } + std::ostream& blue_on_light_red(std::ostream& os) { set("b", "lr"); return os; } + std::ostream& blue_on_light_purple(std::ostream& os) { set("b", "lp"); return os; } + std::ostream& blue_on_light_yellow(std::ostream& os) { set("b", "ly"); return os; } + std::ostream& blue_on_bright_white(std::ostream& os) { set("b", "bw"); return os; } + std::ostream& green_on_black(std::ostream& os) { set("g", "k"); return os; } + std::ostream& green_on_blue(std::ostream& os) { set("g", "b"); return os; } + std::ostream& green_on_green(std::ostream& os) { set("g", "g"); return os; } + std::ostream& green_on_aqua(std::ostream& os) { set("g", "a"); return os; } + std::ostream& green_on_red(std::ostream& os) { set("g", "r"); return os; } + std::ostream& green_on_purple(std::ostream& os) { set("g", "p"); return os; } + std::ostream& green_on_yellow(std::ostream& os) { set("g", "y"); return os; } + std::ostream& green_on_white(std::ostream& os) { set("g", "w"); return os; } + std::ostream& green_on_grey(std::ostream& os) { set("g", "e"); return os; } + std::ostream& green_on_light_blue(std::ostream& os) { set("g", "lb"); return os; } + std::ostream& green_on_light_green(std::ostream& os) { set("g", "lg"); return os; } + std::ostream& green_on_light_aqua(std::ostream& os) { set("g", "la"); return os; } + std::ostream& green_on_light_red(std::ostream& os) { set("g", "lr"); return os; } + std::ostream& green_on_light_purple(std::ostream& os) { set("g", "lp"); return os; } + std::ostream& green_on_light_yellow(std::ostream& os) { set("g", "ly"); return os; } + std::ostream& green_on_bright_white(std::ostream& os) { set("g", "bw"); return os; } + std::ostream& aqua_on_black(std::ostream& os) { set("a", "k"); return os; } + std::ostream& aqua_on_blue(std::ostream& os) { set("a", "b"); return os; } + std::ostream& aqua_on_green(std::ostream& os) { set("a", "g"); return os; } + std::ostream& aqua_on_aqua(std::ostream& os) { set("a", "a"); return os; } + std::ostream& aqua_on_red(std::ostream& os) { set("a", "r"); return os; } + std::ostream& aqua_on_purple(std::ostream& os) { set("a", "p"); return os; } + std::ostream& aqua_on_yellow(std::ostream& os) { set("a", "y"); return os; } + std::ostream& aqua_on_white(std::ostream& os) { set("a", "w"); return os; } + std::ostream& aqua_on_grey(std::ostream& os) { set("a", "e"); return os; } + std::ostream& aqua_on_light_blue(std::ostream& os) { set("a", "lb"); return os; } + std::ostream& aqua_on_light_green(std::ostream& os) { set("a", "lg"); return os; } + std::ostream& aqua_on_light_aqua(std::ostream& os) { set("a", "la"); return os; } + std::ostream& aqua_on_light_red(std::ostream& os) { set("a", "lr"); return os; } + std::ostream& aqua_on_light_purple(std::ostream& os) { set("a", "lp"); return os; } + std::ostream& aqua_on_light_yellow(std::ostream& os) { set("a", "ly"); return os; } + std::ostream& aqua_on_bright_white(std::ostream& os) { set("a", "bw"); return os; } + std::ostream& red_on_black(std::ostream& os) { set("r", "k"); return os; } + std::ostream& red_on_blue(std::ostream& os) { set("r", "b"); return os; } + std::ostream& red_on_green(std::ostream& os) { set("r", "g"); return os; } + std::ostream& red_on_aqua(std::ostream& os) { set("r", "a"); return os; } + std::ostream& red_on_red(std::ostream& os) { set("r", "r"); return os; } + std::ostream& red_on_purple(std::ostream& os) { set("r", "p"); return os; } + std::ostream& red_on_yellow(std::ostream& os) { set("r", "y"); return os; } + std::ostream& red_on_white(std::ostream& os) { set("r", "w"); return os; } + std::ostream& red_on_grey(std::ostream& os) { set("r", "e"); return os; } + std::ostream& red_on_light_blue(std::ostream& os) { set("r", "lb"); return os; } + std::ostream& red_on_light_green(std::ostream& os) { set("r", "lg"); return os; } + std::ostream& red_on_light_aqua(std::ostream& os) { set("r", "la"); return os; } + std::ostream& red_on_light_red(std::ostream& os) { set("r", "lr"); return os; } + std::ostream& red_on_light_purple(std::ostream& os) { set("r", "lp"); return os; } + std::ostream& red_on_light_yellow(std::ostream& os) { set("r", "ly"); return os; } + std::ostream& red_on_bright_white(std::ostream& os) { set("r", "bw"); return os; } + std::ostream& purple_on_black(std::ostream& os) { set("p", "k"); return os; } + std::ostream& purple_on_blue(std::ostream& os) { set("p", "b"); return os; } + std::ostream& purple_on_green(std::ostream& os) { set("p", "g"); return os; } + std::ostream& purple_on_aqua(std::ostream& os) { set("p", "a"); return os; } + std::ostream& purple_on_red(std::ostream& os) { set("p", "r"); return os; } + std::ostream& purple_on_purple(std::ostream& os) { set("p", "p"); return os; } + std::ostream& purple_on_yellow(std::ostream& os) { set("p", "y"); return os; } + std::ostream& purple_on_white(std::ostream& os) { set("p", "w"); return os; } + std::ostream& purple_on_grey(std::ostream& os) { set("p", "e"); return os; } + std::ostream& purple_on_light_blue(std::ostream& os) { set("p", "lb"); return os; } + std::ostream& purple_on_light_green(std::ostream& os) { set("p", "lg"); return os; } + std::ostream& purple_on_light_aqua(std::ostream& os) { set("p", "la"); return os; } + std::ostream& purple_on_light_red(std::ostream& os) { set("p", "lr"); return os; } + std::ostream& purple_on_light_purple(std::ostream& os) { set("p", "lp"); return os; } + std::ostream& purple_on_light_yellow(std::ostream& os) { set("p", "ly"); return os; } + std::ostream& purple_on_bright_white(std::ostream& os) { set("p", "bw"); return os; } + std::ostream& yellow_on_black(std::ostream& os) { set("y", "k"); return os; } + std::ostream& yellow_on_blue(std::ostream& os) { set("y", "b"); return os; } + std::ostream& yellow_on_green(std::ostream& os) { set("y", "g"); return os; } + std::ostream& yellow_on_aqua(std::ostream& os) { set("y", "a"); return os; } + std::ostream& yellow_on_red(std::ostream& os) { set("y", "r"); return os; } + std::ostream& yellow_on_purple(std::ostream& os) { set("y", "p"); return os; } + std::ostream& yellow_on_yellow(std::ostream& os) { set("y", "y"); return os; } + std::ostream& yellow_on_white(std::ostream& os) { set("y", "w"); return os; } + std::ostream& yellow_on_grey(std::ostream& os) { set("y", "e"); return os; } + std::ostream& yellow_on_light_blue(std::ostream& os) { set("y", "lb"); return os; } + std::ostream& yellow_on_light_green(std::ostream& os) { set("y", "lg"); return os; } + std::ostream& yellow_on_light_aqua(std::ostream& os) { set("y", "la"); return os; } + std::ostream& yellow_on_light_red(std::ostream& os) { set("y", "lr"); return os; } + std::ostream& yellow_on_light_purple(std::ostream& os) { set("y", "lp"); return os; } + std::ostream& yellow_on_light_yellow(std::ostream& os) { set("y", "ly"); return os; } + std::ostream& yellow_on_bright_white(std::ostream& os) { set("y", "bw"); return os; } + std::ostream& white_on_black(std::ostream& os) { set("w", "k"); return os; } + std::ostream& white_on_blue(std::ostream& os) { set("w", "b"); return os; } + std::ostream& white_on_green(std::ostream& os) { set("w", "g"); return os; } + std::ostream& white_on_aqua(std::ostream& os) { set("w", "a"); return os; } + std::ostream& white_on_red(std::ostream& os) { set("w", "r"); return os; } + std::ostream& white_on_purple(std::ostream& os) { set("w", "p"); return os; } + std::ostream& white_on_yellow(std::ostream& os) { set("w", "y"); return os; } + std::ostream& white_on_white(std::ostream& os) { set("w", "w"); return os; } + std::ostream& white_on_grey(std::ostream& os) { set("w", "e"); return os; } + std::ostream& white_on_light_blue(std::ostream& os) { set("w", "lb"); return os; } + std::ostream& white_on_light_green(std::ostream& os) { set("w", "lg"); return os; } + std::ostream& white_on_light_aqua(std::ostream& os) { set("w", "la"); return os; } + std::ostream& white_on_light_red(std::ostream& os) { set("w", "lr"); return os; } + std::ostream& white_on_light_purple(std::ostream& os) { set("w", "lp"); return os; } + std::ostream& white_on_light_yellow(std::ostream& os) { set("w", "ly"); return os; } + std::ostream& white_on_bright_white(std::ostream& os) { set("w", "bw"); return os; } + std::ostream& grey_on_black(std::ostream& os) { set("e", "k"); return os; } + std::ostream& grey_on_blue(std::ostream& os) { set("e", "b"); return os; } + std::ostream& grey_on_green(std::ostream& os) { set("e", "g"); return os; } + std::ostream& grey_on_aqua(std::ostream& os) { set("e", "a"); return os; } + std::ostream& grey_on_red(std::ostream& os) { set("e", "r"); return os; } + std::ostream& grey_on_purple(std::ostream& os) { set("e", "p"); return os; } + std::ostream& grey_on_yellow(std::ostream& os) { set("e", "y"); return os; } + std::ostream& grey_on_white(std::ostream& os) { set("e", "w"); return os; } + std::ostream& grey_on_grey(std::ostream& os) { set("e", "e"); return os; } + std::ostream& grey_on_light_blue(std::ostream& os) { set("e", "lb"); return os; } + std::ostream& grey_on_light_green(std::ostream& os) { set("e", "lg"); return os; } + std::ostream& grey_on_light_aqua(std::ostream& os) { set("e", "la"); return os; } + std::ostream& grey_on_light_red(std::ostream& os) { set("e", "lr"); return os; } + std::ostream& grey_on_light_purple(std::ostream& os) { set("e", "lp"); return os; } + std::ostream& grey_on_light_yellow(std::ostream& os) { set("e", "ly"); return os; } + std::ostream& grey_on_bright_white(std::ostream& os) { set("e", "bw"); return os; } + std::ostream& light_blue_on_black(std::ostream& os) { set("lb", "k"); return os; } + std::ostream& light_blue_on_blue(std::ostream& os) { set("lb", "b"); return os; } + std::ostream& light_blue_on_green(std::ostream& os) { set("lb", "g"); return os; } + std::ostream& light_blue_on_aqua(std::ostream& os) { set("lb", "a"); return os; } + std::ostream& light_blue_on_red(std::ostream& os) { set("lb", "r"); return os; } + std::ostream& light_blue_on_purple(std::ostream& os) { set("lb", "p"); return os; } + std::ostream& light_blue_on_yellow(std::ostream& os) { set("lb", "y"); return os; } + std::ostream& light_blue_on_white(std::ostream& os) { set("lb", "w"); return os; } + std::ostream& light_blue_on_grey(std::ostream& os) { set("lb", "e"); return os; } + std::ostream& light_blue_on_light_blue(std::ostream& os) { set("lb", "lb"); return os; } + std::ostream& light_blue_on_light_green(std::ostream& os) { set("lb", "lg"); return os; } + std::ostream& light_blue_on_light_aqua(std::ostream& os) { set("lb", "la"); return os; } + std::ostream& light_blue_on_light_red(std::ostream& os) { set("lb", "lr"); return os; } + std::ostream& light_blue_on_light_purple(std::ostream& os) { set("lb", "lp"); return os; } + std::ostream& light_blue_on_light_yellow(std::ostream& os) { set("lb", "ly"); return os; } + std::ostream& light_blue_on_bright_white(std::ostream& os) { set("lb", "bw"); return os; } + std::ostream& light_green_on_black(std::ostream& os) { set("lg", "k"); return os; } + std::ostream& light_green_on_blue(std::ostream& os) { set("lg", "b"); return os; } + std::ostream& light_green_on_green(std::ostream& os) { set("lg", "g"); return os; } + std::ostream& light_green_on_aqua(std::ostream& os) { set("lg", "a"); return os; } + std::ostream& light_green_on_red(std::ostream& os) { set("lg", "r"); return os; } + std::ostream& light_green_on_purple(std::ostream& os) { set("lg", "p"); return os; } + std::ostream& light_green_on_yellow(std::ostream& os) { set("lg", "y"); return os; } + std::ostream& light_green_on_white(std::ostream& os) { set("lg", "w"); return os; } + std::ostream& light_green_on_grey(std::ostream& os) { set("lg", "e"); return os; } + std::ostream& light_green_on_light_blue(std::ostream& os) { set("lg", "lb"); return os; } + std::ostream& light_green_on_light_green(std::ostream& os) { set("lg", "lg"); return os; } + std::ostream& light_green_on_light_aqua(std::ostream& os) { set("lg", "la"); return os; } + std::ostream& light_green_on_light_red(std::ostream& os) { set("lg", "lr"); return os; } + std::ostream& light_green_on_light_purple(std::ostream& os) { set("lg", "lp"); return os; } + std::ostream& light_green_on_light_yellow(std::ostream& os) { set("lg", "ly"); return os; } + std::ostream& light_green_on_bright_white(std::ostream& os) { set("lg", "bw"); return os; } + std::ostream& light_aqua_on_black(std::ostream& os) { set("la", "k"); return os; } + std::ostream& light_aqua_on_blue(std::ostream& os) { set("la", "b"); return os; } + std::ostream& light_aqua_on_green(std::ostream& os) { set("la", "g"); return os; } + std::ostream& light_aqua_on_aqua(std::ostream& os) { set("la", "a"); return os; } + std::ostream& light_aqua_on_red(std::ostream& os) { set("la", "r"); return os; } + std::ostream& light_aqua_on_purple(std::ostream& os) { set("la", "p"); return os; } + std::ostream& light_aqua_on_yellow(std::ostream& os) { set("la", "y"); return os; } + std::ostream& light_aqua_on_white(std::ostream& os) { set("la", "w"); return os; } + std::ostream& light_aqua_on_grey(std::ostream& os) { set("la", "e"); return os; } + std::ostream& light_aqua_on_light_blue(std::ostream& os) { set("la", "lb"); return os; } + std::ostream& light_aqua_on_light_green(std::ostream& os) { set("la", "lg"); return os; } + std::ostream& light_aqua_on_light_aqua(std::ostream& os) { set("la", "la"); return os; } + std::ostream& light_aqua_on_light_red(std::ostream& os) { set("la", "lr"); return os; } + std::ostream& light_aqua_on_light_purple(std::ostream& os) { set("la", "lp"); return os; } + std::ostream& light_aqua_on_light_yellow(std::ostream& os) { set("la", "ly"); return os; } + std::ostream& light_aqua_on_bright_white(std::ostream& os) { set("la", "bw"); return os; } + std::ostream& light_red_on_black(std::ostream& os) { set("lr", "k"); return os; } + std::ostream& light_red_on_blue(std::ostream& os) { set("lr", "b"); return os; } + std::ostream& light_red_on_green(std::ostream& os) { set("lr", "g"); return os; } + std::ostream& light_red_on_aqua(std::ostream& os) { set("lr", "a"); return os; } + std::ostream& light_red_on_red(std::ostream& os) { set("lr", "r"); return os; } + std::ostream& light_red_on_purple(std::ostream& os) { set("lr", "p"); return os; } + std::ostream& light_red_on_yellow(std::ostream& os) { set("lr", "y"); return os; } + std::ostream& light_red_on_white(std::ostream& os) { set("lr", "w"); return os; } + std::ostream& light_red_on_grey(std::ostream& os) { set("lr", "e"); return os; } + std::ostream& light_red_on_light_blue(std::ostream& os) { set("lr", "lb"); return os; } + std::ostream& light_red_on_light_green(std::ostream& os) { set("lr", "lg"); return os; } + std::ostream& light_red_on_light_aqua(std::ostream& os) { set("lr", "la"); return os; } + std::ostream& light_red_on_light_red(std::ostream& os) { set("lr", "lr"); return os; } + std::ostream& light_red_on_light_purple(std::ostream& os) { set("lr", "lp"); return os; } + std::ostream& light_red_on_light_yellow(std::ostream& os) { set("lr", "ly"); return os; } + std::ostream& light_red_on_bright_white(std::ostream& os) { set("lr", "bw"); return os; } + std::ostream& light_purple_on_black(std::ostream& os) { set("lp", "k"); return os; } + std::ostream& light_purple_on_blue(std::ostream& os) { set("lp", "b"); return os; } + std::ostream& light_purple_on_green(std::ostream& os) { set("lp", "g"); return os; } + std::ostream& light_purple_on_aqua(std::ostream& os) { set("lp", "a"); return os; } + std::ostream& light_purple_on_red(std::ostream& os) { set("lp", "r"); return os; } + std::ostream& light_purple_on_purple(std::ostream& os) { set("lp", "p"); return os; } + std::ostream& light_purple_on_yellow(std::ostream& os) { set("lp", "y"); return os; } + std::ostream& light_purple_on_white(std::ostream& os) { set("lp", "w"); return os; } + std::ostream& light_purple_on_grey(std::ostream& os) { set("lp", "e"); return os; } + std::ostream& light_purple_on_light_blue(std::ostream& os) { set("lp", "lb"); return os; } + std::ostream& light_purple_on_light_green(std::ostream& os) { set("lp", "lg"); return os; } + std::ostream& light_purple_on_light_aqua(std::ostream& os) { set("lp", "la"); return os; } + std::ostream& light_purple_on_light_red(std::ostream& os) { set("lp", "lr"); return os; } + std::ostream& light_purple_on_light_purple(std::ostream& os) { set("lp", "lp"); return os; } + std::ostream& light_purple_on_light_yellow(std::ostream& os) { set("lp", "ly"); return os; } + std::ostream& light_purple_on_bright_white(std::ostream& os) { set("lp", "bw"); return os; } + std::ostream& light_yellow_on_black(std::ostream& os) { set("ly", "k"); return os; } + std::ostream& light_yellow_on_blue(std::ostream& os) { set("ly", "b"); return os; } + std::ostream& light_yellow_on_green(std::ostream& os) { set("ly", "g"); return os; } + std::ostream& light_yellow_on_aqua(std::ostream& os) { set("ly", "a"); return os; } + std::ostream& light_yellow_on_red(std::ostream& os) { set("ly", "r"); return os; } + std::ostream& light_yellow_on_purple(std::ostream& os) { set("ly", "p"); return os; } + std::ostream& light_yellow_on_yellow(std::ostream& os) { set("ly", "y"); return os; } + std::ostream& light_yellow_on_white(std::ostream& os) { set("ly", "w"); return os; } + std::ostream& light_yellow_on_grey(std::ostream& os) { set("ly", "e"); return os; } + std::ostream& light_yellow_on_light_blue(std::ostream& os) { set("ly", "lb"); return os; } + std::ostream& light_yellow_on_light_green(std::ostream& os) { set("ly", "lg"); return os; } + std::ostream& light_yellow_on_light_aqua(std::ostream& os) { set("ly", "la"); return os; } + std::ostream& light_yellow_on_light_red(std::ostream& os) { set("ly", "lr"); return os; } + std::ostream& light_yellow_on_light_purple(std::ostream& os) { set("ly", "lp"); return os; } + std::ostream& light_yellow_on_light_yellow(std::ostream& os) { set("ly", "ly"); return os; } + std::ostream& light_yellow_on_bright_white(std::ostream& os) { set("ly", "bw"); return os; } + std::ostream& bright_white_on_black(std::ostream& os) { set("bw", "k"); return os; } + std::ostream& bright_white_on_blue(std::ostream& os) { set("bw", "b"); return os; } + std::ostream& bright_white_on_green(std::ostream& os) { set("bw", "g"); return os; } + std::ostream& bright_white_on_aqua(std::ostream& os) { set("bw", "a"); return os; } + std::ostream& bright_white_on_red(std::ostream& os) { set("bw", "r"); return os; } + std::ostream& bright_white_on_purple(std::ostream& os) { set("bw", "p"); return os; } + std::ostream& bright_white_on_yellow(std::ostream& os) { set("bw", "y"); return os; } + std::ostream& bright_white_on_white(std::ostream& os) { set("bw", "w"); return os; } + std::ostream& bright_white_on_grey(std::ostream& os) { set("bw", "e"); return os; } + std::ostream& bright_white_on_light_blue(std::ostream& os) { set("bw", "lb"); return os; } + std::ostream& bright_white_on_light_green(std::ostream& os) { set("bw", "lg"); return os; } + std::ostream& bright_white_on_light_aqua(std::ostream& os) { set("bw", "la"); return os; } + std::ostream& bright_white_on_light_red(std::ostream& os) { set("bw", "lr"); return os; } + std::ostream& bright_white_on_light_purple(std::ostream& os) { set("bw", "lp"); return os; } + std::ostream& bright_white_on_light_yellow(std::ostream& os) { set("bw", "ly"); return os; } + std::ostream& bright_white_on_bright_white(std::ostream& os) { set("bw", "bw"); return os; } +} + + +namespace dye +{ + template + using bar = typename std::conditional::value, std::string, T>::type; + + template class colorful; + template class item; + + template + class colorful : private std::list> + { + public: + using std::list>::list; + + colorful& operator+=(const colorful& rhs) + { + this->insert(this->end(), rhs.begin(), rhs.end()); + return *this; + } + + colorful& operator+=(colorful&& rhs) + { + this->splice(this->end(), std::move(rhs)); + return *this; + } + + colorful& operator+=(T t) + { + this->push_back(std::move(t)); + return *this; + } + + void push_front(T t) + { + this->std::list>::push_front(item(std::move(t))); + } + + void push_back(T t) + { + this->std::list>::push_back(item(std::move(t))); + } + + colorful& invert() + { + for (auto& elem : *this) + elem.invert(); + return *this; + } + + template + friend std::ostream& operator<<(std::ostream&, const colorful&); + + template + friend colorful invert(colorful col); + }; + + template + colorful operator+(colorful lhs, colorful rhs) + { + colorful res(std::move(lhs)); + return res += rhs; + } + + template + colorful operator+(colorful lhs, std::string rhs) + { + colorful res(std::move(lhs)); + res.push_back(std::move(rhs)); + return res; + } + + template + colorful operator+(const std::string& lhs, colorful rhs) + { + colorful res(std::move(rhs)); + res.push_front(std::move(lhs)); + return res; + } + + template + std::ostream& operator<<(std::ostream& os, const colorful& colorful) + { + for (const auto& elem : colorful) + os << elem; + return os; + } + + template + colorful invert(colorful col) + { + colorful res(std::move(col)); + for (auto& elem : res) + elem.invert(); + return res; + } + + template + class item + { + T thing; + int color; + + public: + item(T t) : thing(std::move(t)), color(hue::get()) {} + item(T t, int a) : thing(std::move(t)), color(hue::itoc(a)) {} + item(T t, int a, int b) : thing(std::move(t)), color(hue::itoc(a, b)) {} + item(T t, std::string a) : thing(std::move(t)), color(hue::stoc(a)) {} + item(T t, std::string a, std::string b) : thing(std::move(t)), color(hue::stoc(a, b)) {} + + item& invert() + { + color = hue::invert(color); + return *this; + } + + template + friend class colorful; + + template + friend std::ostream& operator<<(std::ostream&, const item&); + }; + + template + std::ostream& operator<<(std::ostream& os, const item& it) + { + hue::set(it.color); + os << it.thing; + hue::reset(); + return os; + } + + template using R = colorful>; + template using S = item>; + + template R colorize(T t, std::string a) { return R { S(t, a) }; } + template R vanilla(T t) { return R { S(t) }; } + template R black(T t) { return R { S(t, "k") }; } + template R blue(T t) { return R { S(t, "b") }; } + template R green(T t) { return R { S(t, "g") }; } + template R aqua(T t) { return R { S(t, "a") }; } + template R red(T t) { return R { S(t, "r") }; } + template R purple(T t) { return R { S(t, "p") }; } + template R yellow(T t) { return R { S(t, "y") }; } + template R white(T t) { return R { S(t, "w") }; } + template R grey(T t) { return R { S(t, "e") }; } + template R light_blue(T t) { return R { S(t, "lb") }; } + template R light_green(T t) { return R { S(t, "lg") }; } + template R light_aqua(T t) { return R { S(t, "la") }; } + template R light_red(T t) { return R { S(t, "lr") }; } + template R light_purple(T t) { return R { S(t, "lp") }; } + template R light_yellow(T t) { return R { S(t, "ly") }; } + template R bright_white(T t) { return R { S(t, "bw") }; } + template R on_black(T t) { return R { S(t, "k", "k") }; } + template R on_blue(T t) { return R { S(t, "k", "b") }; } + template R on_green(T t) { return R { S(t, "k", "g") }; } + template R on_aqua(T t) { return R { S(t, "k", "a") }; } + template R on_red(T t) { return R { S(t, "k", "r") }; } + template R on_purple(T t) { return R { S(t, "k", "p") }; } + template R on_yellow(T t) { return R { S(t, "k", "y") }; } + template R on_white(T t) { return R { S(t, "k", "w") }; } + template R on_grey(T t) { return R { S(t, "k", "e") }; } + template R on_light_blue(T t) { return R { S(t, "k", "lb") }; } + template R on_light_green(T t) { return R { S(t, "k", "lg") }; } + template R on_light_aqua(T t) { return R { S(t, "k", "la") }; } + template R on_light_red(T t) { return R { S(t, "k", "lr") }; } + template R on_light_purple(T t) { return R { S(t, "k", "lp") }; } + template R on_light_yellow(T t) { return R { S(t, "k", "ly") }; } + template R on_bright_white(T t) { return R { S(t, "k", "bw") }; } + template R black_on_black(T t) { return R { S(t, "k", "k") }; } + template R black_on_blue(T t) { return R { S(t, "k", "b") }; } + template R black_on_green(T t) { return R { S(t, "k", "g") }; } + template R black_on_aqua(T t) { return R { S(t, "k", "a") }; } + template R black_on_red(T t) { return R { S(t, "k", "r") }; } + template R black_on_purple(T t) { return R { S(t, "k", "p") }; } + template R black_on_yellow(T t) { return R { S(t, "k", "y") }; } + template R black_on_white(T t) { return R { S(t, "k", "w") }; } + template R black_on_grey(T t) { return R { S(t, "k", "e") }; } + template R black_on_light_blue(T t) { return R { S(t, "k", "lb") }; } + template R black_on_light_green(T t) { return R { S(t, "k", "lg") }; } + template R black_on_light_aqua(T t) { return R { S(t, "k", "la") }; } + template R black_on_light_red(T t) { return R { S(t, "k", "lr") }; } + template R black_on_light_purple(T t) { return R { S(t, "k", "lp") }; } + template R black_on_light_yellow(T t) { return R { S(t, "k", "ly") }; } + template R black_on_bright_white(T t) { return R { S(t, "k", "bw") }; } + template R blue_on_black(T t) { return R { S(t, "b", "k") }; } + template R blue_on_blue(T t) { return R { S(t, "b", "b") }; } + template R blue_on_green(T t) { return R { S(t, "b", "g") }; } + template R blue_on_aqua(T t) { return R { S(t, "b", "a") }; } + template R blue_on_red(T t) { return R { S(t, "b", "r") }; } + template R blue_on_purple(T t) { return R { S(t, "b", "p") }; } + template R blue_on_yellow(T t) { return R { S(t, "b", "y") }; } + template R blue_on_white(T t) { return R { S(t, "b", "w") }; } + template R blue_on_grey(T t) { return R { S(t, "b", "e") }; } + template R blue_on_light_blue(T t) { return R { S(t, "b", "lb") }; } + template R blue_on_light_green(T t) { return R { S(t, "b", "lg") }; } + template R blue_on_light_aqua(T t) { return R { S(t, "b", "la") }; } + template R blue_on_light_red(T t) { return R { S(t, "b", "lr") }; } + template R blue_on_light_purple(T t) { return R { S(t, "b", "lp") }; } + template R blue_on_light_yellow(T t) { return R { S(t, "b", "ly") }; } + template R blue_on_bright_white(T t) { return R { S(t, "b", "bw") }; } + template R green_on_black(T t) { return R { S(t, "g", "k") }; } + template R green_on_blue(T t) { return R { S(t, "g", "b") }; } + template R green_on_green(T t) { return R { S(t, "g", "g") }; } + template R green_on_aqua(T t) { return R { S(t, "g", "a") }; } + template R green_on_red(T t) { return R { S(t, "g", "r") }; } + template R green_on_purple(T t) { return R { S(t, "g", "p") }; } + template R green_on_yellow(T t) { return R { S(t, "g", "y") }; } + template R green_on_white(T t) { return R { S(t, "g", "w") }; } + template R green_on_grey(T t) { return R { S(t, "g", "e") }; } + template R green_on_light_blue(T t) { return R { S(t, "g", "lb") }; } + template R green_on_light_green(T t) { return R { S(t, "g", "lg") }; } + template R green_on_light_aqua(T t) { return R { S(t, "g", "la") }; } + template R green_on_light_red(T t) { return R { S(t, "g", "lr") }; } + template R green_on_light_purple(T t) { return R { S(t, "g", "lp") }; } + template R green_on_light_yellow(T t) { return R { S(t, "g", "ly") }; } + template R green_on_bright_white(T t) { return R { S(t, "g", "bw") }; } + template R aqua_on_black(T t) { return R { S(t, "a", "k") }; } + template R aqua_on_blue(T t) { return R { S(t, "a", "b") }; } + template R aqua_on_green(T t) { return R { S(t, "a", "g") }; } + template R aqua_on_aqua(T t) { return R { S(t, "a", "a") }; } + template R aqua_on_red(T t) { return R { S(t, "a", "r") }; } + template R aqua_on_purple(T t) { return R { S(t, "a", "p") }; } + template R aqua_on_yellow(T t) { return R { S(t, "a", "y") }; } + template R aqua_on_white(T t) { return R { S(t, "a", "w") }; } + template R aqua_on_grey(T t) { return R { S(t, "a", "e") }; } + template R aqua_on_light_blue(T t) { return R { S(t, "a", "lb") }; } + template R aqua_on_light_green(T t) { return R { S(t, "a", "lg") }; } + template R aqua_on_light_aqua(T t) { return R { S(t, "a", "la") }; } + template R aqua_on_light_red(T t) { return R { S(t, "a", "lr") }; } + template R aqua_on_light_purple(T t) { return R { S(t, "a", "lp") }; } + template R aqua_on_light_yellow(T t) { return R { S(t, "a", "ly") }; } + template R aqua_on_bright_white(T t) { return R { S(t, "a", "bw") }; } + template R red_on_black(T t) { return R { S(t, "r", "k") }; } + template R red_on_blue(T t) { return R { S(t, "r", "b") }; } + template R red_on_green(T t) { return R { S(t, "r", "g") }; } + template R red_on_aqua(T t) { return R { S(t, "r", "a") }; } + template R red_on_red(T t) { return R { S(t, "r", "r") }; } + template R red_on_purple(T t) { return R { S(t, "r", "p") }; } + template R red_on_yellow(T t) { return R { S(t, "r", "y") }; } + template R red_on_white(T t) { return R { S(t, "r", "w") }; } + template R red_on_grey(T t) { return R { S(t, "r", "e") }; } + template R red_on_light_blue(T t) { return R { S(t, "r", "lb") }; } + template R red_on_light_green(T t) { return R { S(t, "r", "lg") }; } + template R red_on_light_aqua(T t) { return R { S(t, "r", "la") }; } + template R red_on_light_red(T t) { return R { S(t, "r", "lr") }; } + template R red_on_light_purple(T t) { return R { S(t, "r", "lp") }; } + template R red_on_light_yellow(T t) { return R { S(t, "r", "ly") }; } + template R red_on_bright_white(T t) { return R { S(t, "r", "bw") }; } + template R purple_on_black(T t) { return R { S(t, "p", "k") }; } + template R purple_on_blue(T t) { return R { S(t, "p", "b") }; } + template R purple_on_green(T t) { return R { S(t, "p", "g") }; } + template R purple_on_aqua(T t) { return R { S(t, "p", "a") }; } + template R purple_on_red(T t) { return R { S(t, "p", "r") }; } + template R purple_on_purple(T t) { return R { S(t, "p", "p") }; } + template R purple_on_yellow(T t) { return R { S(t, "p", "y") }; } + template R purple_on_white(T t) { return R { S(t, "p", "w") }; } + template R purple_on_grey(T t) { return R { S(t, "p", "e") }; } + template R purple_on_light_blue(T t) { return R { S(t, "p", "lb") }; } + template R purple_on_light_green(T t) { return R { S(t, "p", "lg") }; } + template R purple_on_light_aqua(T t) { return R { S(t, "p", "la") }; } + template R purple_on_light_red(T t) { return R { S(t, "p", "lr") }; } + template R purple_on_light_purple(T t) { return R { S(t, "p", "lp") }; } + template R purple_on_light_yellow(T t) { return R { S(t, "p", "ly") }; } + template R purple_on_bright_white(T t) { return R { S(t, "p", "bw") }; } + template R yellow_on_black(T t) { return R { S(t, "y", "k") }; } + template R yellow_on_blue(T t) { return R { S(t, "y", "b") }; } + template R yellow_on_green(T t) { return R { S(t, "y", "g") }; } + template R yellow_on_aqua(T t) { return R { S(t, "y", "a") }; } + template R yellow_on_red(T t) { return R { S(t, "y", "r") }; } + template R yellow_on_purple(T t) { return R { S(t, "y", "p") }; } + template R yellow_on_yellow(T t) { return R { S(t, "y", "y") }; } + template R yellow_on_white(T t) { return R { S(t, "y", "w") }; } + template R yellow_on_grey(T t) { return R { S(t, "y", "e") }; } + template R yellow_on_light_blue(T t) { return R { S(t, "y", "lb") }; } + template R yellow_on_light_green(T t) { return R { S(t, "y", "lg") }; } + template R yellow_on_light_aqua(T t) { return R { S(t, "y", "la") }; } + template R yellow_on_light_red(T t) { return R { S(t, "y", "lr") }; } + template R yellow_on_light_purple(T t) { return R { S(t, "y", "lp") }; } + template R yellow_on_light_yellow(T t) { return R { S(t, "y", "ly") }; } + template R yellow_on_bright_white(T t) { return R { S(t, "y", "bw") }; } + template R white_on_black(T t) { return R { S(t, "w", "k") }; } + template R white_on_blue(T t) { return R { S(t, "w", "b") }; } + template R white_on_green(T t) { return R { S(t, "w", "g") }; } + template R white_on_aqua(T t) { return R { S(t, "w", "a") }; } + template R white_on_red(T t) { return R { S(t, "w", "r") }; } + template R white_on_purple(T t) { return R { S(t, "w", "p") }; } + template R white_on_yellow(T t) { return R { S(t, "w", "y") }; } + template R white_on_white(T t) { return R { S(t, "w", "w") }; } + template R white_on_grey(T t) { return R { S(t, "w", "e") }; } + template R white_on_light_blue(T t) { return R { S(t, "w", "lb") }; } + template R white_on_light_green(T t) { return R { S(t, "w", "lg") }; } + template R white_on_light_aqua(T t) { return R { S(t, "w", "la") }; } + template R white_on_light_red(T t) { return R { S(t, "w", "lr") }; } + template R white_on_light_purple(T t) { return R { S(t, "w", "lp") }; } + template R white_on_light_yellow(T t) { return R { S(t, "w", "ly") }; } + template R white_on_bright_white(T t) { return R { S(t, "w", "bw") }; } + template R grey_on_black(T t) { return R { S(t, "e", "k") }; } + template R grey_on_blue(T t) { return R { S(t, "e", "b") }; } + template R grey_on_green(T t) { return R { S(t, "e", "g") }; } + template R grey_on_aqua(T t) { return R { S(t, "e", "a") }; } + template R grey_on_red(T t) { return R { S(t, "e", "r") }; } + template R grey_on_purple(T t) { return R { S(t, "e", "p") }; } + template R grey_on_yellow(T t) { return R { S(t, "e", "y") }; } + template R grey_on_white(T t) { return R { S(t, "e", "w") }; } + template R grey_on_grey(T t) { return R { S(t, "e", "e") }; } + template R grey_on_light_blue(T t) { return R { S(t, "e", "lb") }; } + template R grey_on_light_green(T t) { return R { S(t, "e", "lg") }; } + template R grey_on_light_aqua(T t) { return R { S(t, "e", "la") }; } + template R grey_on_light_red(T t) { return R { S(t, "e", "lr") }; } + template R grey_on_light_purple(T t) { return R { S(t, "e", "lp") }; } + template R grey_on_light_yellow(T t) { return R { S(t, "e", "ly") }; } + template R grey_on_bright_white(T t) { return R { S(t, "e", "bw") }; } + template R light_blue_on_black(T t) { return R { S(t, "lb", "k") }; } + template R light_blue_on_blue(T t) { return R { S(t, "lb", "b") }; } + template R light_blue_on_green(T t) { return R { S(t, "lb", "g") }; } + template R light_blue_on_aqua(T t) { return R { S(t, "lb", "a") }; } + template R light_blue_on_red(T t) { return R { S(t, "lb", "r") }; } + template R light_blue_on_purple(T t) { return R { S(t, "lb", "p") }; } + template R light_blue_on_yellow(T t) { return R { S(t, "lb", "y") }; } + template R light_blue_on_white(T t) { return R { S(t, "lb", "w") }; } + template R light_blue_on_grey(T t) { return R { S(t, "lb", "e") }; } + template R light_blue_on_light_blue(T t) { return R { S(t, "lb", "lb") }; } + template R light_blue_on_light_green(T t) { return R { S(t, "lb", "lg") }; } + template R light_blue_on_light_aqua(T t) { return R { S(t, "lb", "la") }; } + template R light_blue_on_light_red(T t) { return R { S(t, "lb", "lr") }; } + template R light_blue_on_light_purple(T t) { return R { S(t, "lb", "lp") }; } + template R light_blue_on_light_yellow(T t) { return R { S(t, "lb", "ly") }; } + template R light_blue_on_bright_white(T t) { return R { S(t, "lb", "bw") }; } + template R light_green_on_black(T t) { return R { S(t, "lg", "k") }; } + template R light_green_on_blue(T t) { return R { S(t, "lg", "b") }; } + template R light_green_on_green(T t) { return R { S(t, "lg", "g") }; } + template R light_green_on_aqua(T t) { return R { S(t, "lg", "a") }; } + template R light_green_on_red(T t) { return R { S(t, "lg", "r") }; } + template R light_green_on_purple(T t) { return R { S(t, "lg", "p") }; } + template R light_green_on_yellow(T t) { return R { S(t, "lg", "y") }; } + template R light_green_on_white(T t) { return R { S(t, "lg", "w") }; } + template R light_green_on_grey(T t) { return R { S(t, "lg", "e") }; } + template R light_green_on_light_blue(T t) { return R { S(t, "lg", "lb") }; } + template R light_green_on_light_green(T t) { return R { S(t, "lg", "lg") }; } + template R light_green_on_light_aqua(T t) { return R { S(t, "lg", "la") }; } + template R light_green_on_light_red(T t) { return R { S(t, "lg", "lr") }; } + template R light_green_on_light_purple(T t) { return R { S(t, "lg", "lp") }; } + template R light_green_on_light_yellow(T t) { return R { S(t, "lg", "ly") }; } + template R light_green_on_bright_white(T t) { return R { S(t, "lg", "bw") }; } + template R light_aqua_on_black(T t) { return R { S(t, "la", "k") }; } + template R light_aqua_on_blue(T t) { return R { S(t, "la", "b") }; } + template R light_aqua_on_green(T t) { return R { S(t, "la", "g") }; } + template R light_aqua_on_aqua(T t) { return R { S(t, "la", "a") }; } + template R light_aqua_on_red(T t) { return R { S(t, "la", "r") }; } + template R light_aqua_on_purple(T t) { return R { S(t, "la", "p") }; } + template R light_aqua_on_yellow(T t) { return R { S(t, "la", "y") }; } + template R light_aqua_on_white(T t) { return R { S(t, "la", "w") }; } + template R light_aqua_on_grey(T t) { return R { S(t, "la", "e") }; } + template R light_aqua_on_light_blue(T t) { return R { S(t, "la", "lb") }; } + template R light_aqua_on_light_green(T t) { return R { S(t, "la", "lg") }; } + template R light_aqua_on_light_aqua(T t) { return R { S(t, "la", "la") }; } + template R light_aqua_on_light_red(T t) { return R { S(t, "la", "lr") }; } + template R light_aqua_on_light_purple(T t) { return R { S(t, "la", "lp") }; } + template R light_aqua_on_light_yellow(T t) { return R { S(t, "la", "ly") }; } + template R light_aqua_on_bright_white(T t) { return R { S(t, "la", "bw") }; } + template R light_red_on_black(T t) { return R { S(t, "lr", "k") }; } + template R light_red_on_blue(T t) { return R { S(t, "lr", "b") }; } + template R light_red_on_green(T t) { return R { S(t, "lr", "g") }; } + template R light_red_on_aqua(T t) { return R { S(t, "lr", "a") }; } + template R light_red_on_red(T t) { return R { S(t, "lr", "r") }; } + template R light_red_on_purple(T t) { return R { S(t, "lr", "p") }; } + template R light_red_on_yellow(T t) { return R { S(t, "lr", "y") }; } + template R light_red_on_white(T t) { return R { S(t, "lr", "w") }; } + template R light_red_on_grey(T t) { return R { S(t, "lr", "e") }; } + template R light_red_on_light_blue(T t) { return R { S(t, "lr", "lb") }; } + template R light_red_on_light_green(T t) { return R { S(t, "lr", "lg") }; } + template R light_red_on_light_aqua(T t) { return R { S(t, "lr", "la") }; } + template R light_red_on_light_red(T t) { return R { S(t, "lr", "lr") }; } + template R light_red_on_light_purple(T t) { return R { S(t, "lr", "lp") }; } + template R light_red_on_light_yellow(T t) { return R { S(t, "lr", "ly") }; } + template R light_red_on_bright_white(T t) { return R { S(t, "lr", "bw") }; } + template R light_purple_on_black(T t) { return R { S(t, "lp", "k") }; } + template R light_purple_on_blue(T t) { return R { S(t, "lp", "b") }; } + template R light_purple_on_green(T t) { return R { S(t, "lp", "g") }; } + template R light_purple_on_aqua(T t) { return R { S(t, "lp", "a") }; } + template R light_purple_on_red(T t) { return R { S(t, "lp", "r") }; } + template R light_purple_on_purple(T t) { return R { S(t, "lp", "p") }; } + template R light_purple_on_yellow(T t) { return R { S(t, "lp", "y") }; } + template R light_purple_on_white(T t) { return R { S(t, "lp", "w") }; } + template R light_purple_on_grey(T t) { return R { S(t, "lp", "e") }; } + template R light_purple_on_light_blue(T t) { return R { S(t, "lp", "lb") }; } + template R light_purple_on_light_green(T t) { return R { S(t, "lp", "lg") }; } + template R light_purple_on_light_aqua(T t) { return R { S(t, "lp", "la") }; } + template R light_purple_on_light_red(T t) { return R { S(t, "lp", "lr") }; } + template R light_purple_on_light_purple(T t) { return R { S(t, "lp", "lp") }; } + template R light_purple_on_light_yellow(T t) { return R { S(t, "lp", "ly") }; } + template R light_purple_on_bright_white(T t) { return R { S(t, "lp", "bw") }; } + template R light_yellow_on_black(T t) { return R { S(t, "ly", "k") }; } + template R light_yellow_on_blue(T t) { return R { S(t, "ly", "b") }; } + template R light_yellow_on_green(T t) { return R { S(t, "ly", "g") }; } + template R light_yellow_on_aqua(T t) { return R { S(t, "ly", "a") }; } + template R light_yellow_on_red(T t) { return R { S(t, "ly", "r") }; } + template R light_yellow_on_purple(T t) { return R { S(t, "ly", "p") }; } + template R light_yellow_on_yellow(T t) { return R { S(t, "ly", "y") }; } + template R light_yellow_on_white(T t) { return R { S(t, "ly", "w") }; } + template R light_yellow_on_grey(T t) { return R { S(t, "ly", "e") }; } + template R light_yellow_on_light_blue(T t) { return R { S(t, "ly", "lb") }; } + template R light_yellow_on_light_green(T t) { return R { S(t, "ly", "lg") }; } + template R light_yellow_on_light_aqua(T t) { return R { S(t, "ly", "la") }; } + template R light_yellow_on_light_red(T t) { return R { S(t, "ly", "lr") }; } + template R light_yellow_on_light_purple(T t) { return R { S(t, "ly", "lp") }; } + template R light_yellow_on_light_yellow(T t) { return R { S(t, "ly", "ly") }; } + template R light_yellow_on_bright_white(T t) { return R { S(t, "ly", "bw") }; } + template R bright_white_on_black(T t) { return R { S(t, "bw", "k") }; } + template R bright_white_on_blue(T t) { return R { S(t, "bw", "b") }; } + template R bright_white_on_green(T t) { return R { S(t, "bw", "g") }; } + template R bright_white_on_aqua(T t) { return R { S(t, "bw", "a") }; } + template R bright_white_on_red(T t) { return R { S(t, "bw", "r") }; } + template R bright_white_on_purple(T t) { return R { S(t, "bw", "p") }; } + template R bright_white_on_yellow(T t) { return R { S(t, "bw", "y") }; } + template R bright_white_on_white(T t) { return R { S(t, "bw", "w") }; } + template R bright_white_on_grey(T t) { return R { S(t, "bw", "e") }; } + template R bright_white_on_light_blue(T t) { return R { S(t, "bw", "lb") }; } + template R bright_white_on_light_green(T t) { return R { S(t, "bw", "lg") }; } + template R bright_white_on_light_aqua(T t) { return R { S(t, "bw", "la") }; } + template R bright_white_on_light_red(T t) { return R { S(t, "bw", "lr") }; } + template R bright_white_on_light_purple(T t) { return R { S(t, "bw", "lp") }; } + template R bright_white_on_light_yellow(T t) { return R { S(t, "bw", "ly") }; } + template R bright_white_on_bright_white(T t) { return R { S(t, "bw", "bw") }; } +} + +#endif \ No newline at end of file diff --git a/src/app-sdk/mididiag/main.cpp b/src/app-sdk/mididiag/main.cpp index 6e05ac941..0b2b384f5 100644 --- a/src/app-sdk/mididiag/main.cpp +++ b/src/app-sdk/mididiag/main.cpp @@ -11,7 +11,13 @@ #include "pch.h" -const std::wstring fieldSeparator = MIDIDIAG_FIELD_SEPARATOR; +#include "color.hpp" + +void OutputFieldSeparator() +{ + std::cout + << dye::grey(MIDIDIAG_FIELD_SEPARATOR); +} void OutputBlankLine() { @@ -19,77 +25,77 @@ void OutputBlankLine() << std::endl; } -void OutputSectionHeader(_In_ std::wstring const& headerText) +void OutputSectionHeader(_In_ std::string const& headerText) { - const std::wstring sectionHeaderSeparator = std::wstring(MIDIDIAG_SEPARATOR_REPEATING_CHAR_COUNT_PER_LINE, MIDIDIAG_SECTION_HEADER_SEPARATOR_CHAR); + const auto sectionHeaderSeparator = std::string(MIDIDIAG_SEPARATOR_REPEATING_CHAR_COUNT_PER_LINE, MIDIDIAG_SECTION_HEADER_SEPARATOR_CHAR); - std::wcout + std::cout << std::endl - << sectionHeaderSeparator + << dye::grey(sectionHeaderSeparator) << std::endl - << headerText + << dye::aqua(headerText) << std::endl - << sectionHeaderSeparator + << dye::grey(sectionHeaderSeparator) << std::endl << std::endl; } void OutputItemSeparator() { - const std::wstring itemSeparator = std::wstring(MIDIDIAG_SEPARATOR_REPEATING_CHAR_COUNT_PER_LINE, MIDIDIAG_ITEM_SEPARATOR_CHAR); + const auto itemSeparator = std::string(MIDIDIAG_SEPARATOR_REPEATING_CHAR_COUNT_PER_LINE, MIDIDIAG_ITEM_SEPARATOR_CHAR); - std::wcout - << itemSeparator + std::cout + << dye::grey(itemSeparator) << std::endl; } -void OutputHeader(_In_ std::wstring const& headerText) +void OutputHeader(_In_ std::string const& headerText) { - std::wcout - << headerText + std::cout + << dye::aqua(headerText) << std::endl; } -void OutputFieldLabel(_In_ std::wstring const& fieldName) +void OutputFieldLabel(_In_ std::string const& fieldName) { - std::wcout + std::cout << std::setw(MIDIDIAG_MAX_FIELD_LABEL_WIDTH) << std::left - << fieldName; + << dye::yellow(fieldName); } -void OutputStringField(_In_ std::wstring const& fieldName, _In_ winrt::hstring const& value) +void OutputStringField(_In_ std::string const& fieldName, _In_ winrt::hstring const& value) { OutputFieldLabel(fieldName); + OutputFieldSeparator(); std::wcout - << fieldSeparator << value.c_str() << std::endl; } -void OutputStringField(_In_ std::wstring const& fieldName, _In_ std::wstring const& value) +void OutputStringField(_In_ std::string const& fieldName, _In_ std::wstring const& value) { OutputFieldLabel(fieldName); + OutputFieldSeparator(); std::wcout - << fieldSeparator << value << std::endl; } -void OutputBooleanField(_In_ std::wstring const& fieldName, _In_ bool const& value) +void OutputBooleanField(_In_ std::string const& fieldName, _In_ bool const& value) { OutputFieldLabel(fieldName); + OutputFieldSeparator(); std::wcout - << fieldSeparator << std::boolalpha << value << std::endl; } -void OutputGuidField(_In_ std::wstring const& fieldName, _In_ winrt::guid const& value) +void OutputGuidField(_In_ std::string const& fieldName, _In_ winrt::guid const& value) { OutputStringField(fieldName, internal::GuidToString(value)); } @@ -108,45 +114,45 @@ void OutputCurrentTime() -void OutputTimestampField(_In_ std::wstring const& fieldName, _In_ uint64_t const value) +void OutputTimestampField(_In_ std::string const& fieldName, _In_ uint64_t const value) { OutputFieldLabel(fieldName); + OutputFieldSeparator(); std::wcout - << fieldSeparator << value << std::endl; } -void OutputNumericField(_In_ std::wstring const& fieldName, _In_ uint32_t const value) +void OutputNumericField(_In_ std::string const& fieldName, _In_ uint32_t const value) { OutputFieldLabel(fieldName); + OutputFieldSeparator(); std::wcout - << fieldSeparator << std::dec << value << std::endl; } -void OutputDoubleField(_In_ std::wstring const& fieldName, _In_ double const value, _In_ uint32_t precision) +void OutputDoubleField(_In_ std::string const& fieldName, _In_ double const value, _In_ uint32_t precision) { OutputFieldLabel(fieldName); + OutputFieldSeparator(); std::wcout - << fieldSeparator << std::dec << std::setprecision(precision) << value << std::endl; } -void OutputDecimalMillisecondsField(_In_ std::wstring const& fieldName, _In_ double const value, _In_ uint32_t precision) +void OutputDecimalMillisecondsField(_In_ std::string const& fieldName, _In_ double const value, _In_ uint32_t precision) { OutputFieldLabel(fieldName); + OutputFieldSeparator(); std::wcout - << fieldSeparator << std::dec << std::setprecision(precision) << std::fixed @@ -157,12 +163,12 @@ void OutputDecimalMillisecondsField(_In_ std::wstring const& fieldName, _In_ dou -void OutputHexNumericField(_In_ std::wstring const& fieldName, _In_ uint32_t const value) +void OutputHexNumericField(_In_ std::string const& fieldName, _In_ uint32_t const value) { OutputFieldLabel(fieldName); + OutputFieldSeparator(); std::wcout - << fieldSeparator << L"0x" << std::hex << value @@ -170,7 +176,7 @@ void OutputHexNumericField(_In_ std::wstring const& fieldName, _In_ uint32_t con } -bool OutputFileVersion(_In_ std::wstring const& fieldName, _In_ std::wstring const& fileName) +bool OutputFileVersion(_In_ std::string const& fieldName, _In_ std::wstring const& fileName) { DWORD handle{ 0 }; @@ -191,10 +197,10 @@ bool OutputFileVersion(_In_ std::wstring const& fieldName, _In_ std::wstring con VS_FIXEDFILEINFO* verInfo = (VS_FIXEDFILEINFO*)(verInfoBuffer); OutputFieldLabel(fieldName); + OutputFieldSeparator(); // major.minor.build.revision format std::wcout - << fieldSeparator << std::dec << static_cast((verInfo->dwFileVersionMS >> 16) & 0xffff) << L"." @@ -215,31 +221,27 @@ bool OutputFileVersion(_In_ std::wstring const& fieldName, _In_ std::wstring con void OutputError(_In_ winrt::hresult_error const& error) { - const std::wstring errorLabel = L"ERROR"; - - OutputFieldLabel(errorLabel); + OutputFieldLabel(MIDIDIAG_FIELD_LABEL_ERROR); + OutputFieldSeparator(); - std::wcout - << fieldSeparator + std::cout << std::hex << error.code() - << L" : "; + << " : "; std::cout - << winrt::to_string(error.message()) + << dye::light_red(winrt::to_string(error.message())) << std::endl; } -void OutputError(_In_ std::wstring const& errorMessage) +void OutputError(_In_ std::string const& errorMessage) { - const std::wstring errorLabel = L"ERROR"; + OutputFieldLabel(MIDIDIAG_FIELD_LABEL_ERROR); + OutputFieldSeparator(); - OutputFieldLabel(errorLabel); - - std::wcout - << fieldSeparator - << errorMessage + std::cout + << dye::light_red(errorMessage) << std::endl; } @@ -253,7 +255,7 @@ void OutputError(_In_ std::wstring const& errorMessage) -void OutputRegStringValue(std::wstring label, HKEY const key, std::wstring value) +void OutputRegStringValue(std::string label, HKEY const key, std::wstring value) { auto keyValue = wil::reg::try_get_value_string(key, value.c_str()); if (keyValue.has_value()) @@ -267,7 +269,7 @@ void OutputRegStringValue(std::wstring label, HKEY const key, std::wstring value } // dword value > 0 == true -void OutputRegDWordBooleanValue(std::wstring label, HKEY const key, std::wstring value) +void OutputRegDWordBooleanValue(std::string label, HKEY const key, std::wstring value) { auto keyValue = wil::reg::try_get_value_dword(key, value.c_str()); if (keyValue.has_value()) @@ -282,7 +284,7 @@ void OutputRegDWordBooleanValue(std::wstring label, HKEY const key, std::wstring -void OutputCOMComponentInfo(std::wstring const dllNameFieldName, std::wstring const classid) +void OutputCOMComponentInfo(std::string const dllNameFieldName, std::wstring const classid) { // InprocServer32 Value @@ -301,13 +303,72 @@ void OutputCOMComponentInfo(std::wstring const dllNameFieldName, std::wstring co } else { - OutputError(L"Could not find in-proc server value under HKEY_CLASSES_ROOT, " + inprocServerKeyLocation); + OutputStringField(dllNameFieldName, std::wstring{ L"ERROR" }); + OutputError("Could not find COM component in-proc server value under HKEY_CLASSES_ROOT"); + } +} + + +bool DoSectionDrivers32RegistryEntries(_In_ bool const verbose) +{ + UNREFERENCED_PARAMETER(verbose); + + OutputSectionHeader(MIDIDIAG_SECTION_LABEL_ENUM_REGISTRY_DRIVERS32); + + // list all MIDI values under Drivers32 + + try + { + std::wstring drivers32KeyLocation = std::wstring{ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32" }; + wil::unique_hkey drivers32Key{ }; + + if (SUCCEEDED(wil::reg::open_unique_key_nothrow(HKEY_LOCAL_MACHINE, drivers32KeyLocation.c_str(), drivers32Key, wil::reg::key_access::read))) + { + for (const auto& valueData : wil::make_range(wil::reg::value_iterator{ drivers32Key.get() }, wil::reg::value_iterator{})) + { + //valueData.name; + //valueData.type; + + if (valueData.name.starts_with(L"midi") && valueData.name != L"midimapper") + { + auto val = wil::reg::try_get_value_string(drivers32Key.get(), valueData.name.c_str()); + + if (val.has_value()) + { + OutputStringField(MIDIDIAG_FIELD_LABEL_REGISTRY_DRIVERS32_ENTRY, valueData.name + L" = " + val.value()); + } + } + else if (valueData.name == L"MidisrvTransferComplete") + { + auto val = wil::reg::try_get_value_dword(drivers32Key.get(), valueData.name.c_str()); + + if (val.has_value()) + { + OutputStringField(MIDIDIAG_FIELD_LABEL_REGISTRY_DRIVERS32_ENTRY, valueData.name + L" = " + std::to_wstring(val.value())); + } + } + + } + } + else + { + OutputStringField("ERROR", drivers32KeyLocation); + OutputError("Could not open Drivers32 Key"); + } } + catch (...) + { + OutputError("Exception enumerating registry keys and values."); + + return false; } + return true; +} + -bool DoSectionRegistryEntries(_In_ bool const verbose) +bool DoSectionMidi2RegistryEntries(_In_ bool const verbose) { UNREFERENCED_PARAMETER(verbose); @@ -317,14 +378,20 @@ bool DoSectionRegistryEntries(_In_ bool const verbose) { // check to see if the root is there - const auto rootKey = wil::reg::open_unique_key(HKEY_LOCAL_MACHINE, MIDI_ROOT_REG_KEY, wil::reg::key_access::read); - - // list all values in the root + wil::unique_hkey rootKey{}; + if (SUCCEEDED(wil::reg::open_unique_key_nothrow(HKEY_LOCAL_MACHINE, MIDI_ROOT_REG_KEY, rootKey, wil::reg::key_access::read))) + { + // list all values in the root - OutputRegStringValue(MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_CURRENT_CONFIG, rootKey.get(), MIDI_CONFIG_FILE_REG_VALUE); - OutputRegDWordBooleanValue(MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_DISCOVERY_ENABLED, rootKey.get(), MIDI_DISCOVERY_ENABLED_REG_VALUE); - OutputRegDWordBooleanValue(MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_USE_MMCSS, rootKey.get(), MIDI_USE_MMCSS_REG_VALUE); - OutputItemSeparator(); + OutputRegStringValue(MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_CURRENT_CONFIG, rootKey.get(), MIDI_CONFIG_FILE_REG_VALUE); + OutputRegDWordBooleanValue(MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_DISCOVERY_ENABLED, rootKey.get(), MIDI_DISCOVERY_ENABLED_REG_VALUE); + OutputRegDWordBooleanValue(MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_USE_MMCSS, rootKey.get(), MIDI_USE_MMCSS_REG_VALUE); + OutputItemSeparator(); + } + else + { + OutputError("Unable to find or open root Windows MIDI Services registry key. Is Windows MIDI Services installed?"); + } // List midisrv info @@ -342,18 +409,18 @@ bool DoSectionRegistryEntries(_In_ bool const verbose) } else { - OutputError(L"Found midisrv service entry in registry, but it does not have an ImagePath"); + OutputError("Found midisrv service entry in registry, but it does not have an ImagePath"); } } else { - OutputError(L"Found midisrv service entry in registry, but it does not have an ImagePath"); + OutputError("midisrv registry key not found or is not valid, or user does not have permissions to read it."); } } else { - OutputError(L"Unable to find Midisrv entry in registry services key"); + OutputError("Unable to find or open Midisrv entry in registry services key. Is Windows MIDI Services installed?"); } OutputItemSeparator(); @@ -371,12 +438,12 @@ bool DoSectionRegistryEntries(_In_ bool const verbose) } else { - OutputError(L"No MIDI Desktop app SDK runtime registry 'Installed' value."); + OutputError("No MIDI Desktop app SDK runtime registry 'Installed' value. The Windows MIDI Services SDK does not appear to be installed."); } } else { - OutputError(L"No MIDI Desktop app SDK runtime registry root key."); + OutputError("No MIDI Desktop app SDK runtime registry root key. The Windows MIDI Services SDK does not appear to be installed."); } OutputItemSeparator(); @@ -430,7 +497,7 @@ bool DoSectionRegistryEntries(_In_ bool const verbose) } else { - OutputError(L"No clsid found in MIDI transport entry"); + OutputError("No clsid found in MIDI transport entry"); } } @@ -440,50 +507,13 @@ bool DoSectionRegistryEntries(_In_ bool const verbose) } else { - OutputError(L"Unable to enumerate transport plugins from registry."); + OutputError("Unable to enumerate transport plugins from registry."); } - // list all MIDI values under Drivers32 - - std::wstring drivers32KeyLocation = std::wstring{ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32" }; - wil::unique_hkey drivers32Key{ }; - - if (SUCCEEDED(wil::reg::open_unique_key_nothrow(HKEY_LOCAL_MACHINE, drivers32KeyLocation.c_str(), drivers32Key, wil::reg::key_access::read))) - { - for (const auto& valueData : wil::make_range(wil::reg::value_iterator{ drivers32Key.get() }, wil::reg::value_iterator{})) - { - //valueData.name; - //valueData.type; - - if (valueData.name.starts_with(L"midi") && valueData.name != L"midimapper") - { - auto val = wil::reg::try_get_value_string(drivers32Key.get(), valueData.name.c_str()); - - if (val.has_value()) - { - OutputStringField(MIDIDIAG_FIELD_LABEL_REGISTRY_DRIVERS32_ENTRY, valueData.name + L" = " + val.value()); - } - } - else if (valueData.name == L"MidisrvTransferComplete") - { - auto val = wil::reg::try_get_value_dword(drivers32Key.get(), valueData.name.c_str()); - - if (val.has_value()) - { - OutputStringField(MIDIDIAG_FIELD_LABEL_REGISTRY_DRIVERS32_ENTRY, valueData.name + L" = " + std::to_wstring(val.value())); - } - } - - } - } - else - { - OutputError(L"Could not open Drivers32 Key " + drivers32KeyLocation); - } } catch (...) { - OutputError(L"Exception enumerating registry keys and values."); + OutputError("Exception enumerating registry keys and values."); return false; } @@ -517,13 +547,13 @@ bool DoSectionTransports(_In_ bool const verbose) } else { - OutputError(L"Enumerating transports returned no matches. This is not expected and indicates an installation problem or that the service is not running."); + OutputError("Enumerating transports returned no matches. This is not expected and indicates an installation problem or that the service is not running."); return false; } } catch (...) { - OutputError(L"Exception enumerating transports."); + OutputError("Exception enumerating transports."); return false; } @@ -552,7 +582,7 @@ bool DoSectionMidi2ApiEndpoints(_In_ bool const verbose) } catch (...) { - OutputError(L"Unable to access Windows MIDI Services and enumerate devices."); + OutputError("Unable to access Windows MIDI Services and enumerate devices."); return false; } @@ -590,7 +620,7 @@ bool DoSectionMidi2ApiEndpoints(_In_ bool const verbose) } else { - OutputError(L"Unable to find endpoint parent"); + OutputError("Unable to find endpoint parent"); } if (i != devices.Size() - 1) @@ -601,7 +631,7 @@ bool DoSectionMidi2ApiEndpoints(_In_ bool const verbose) } else { - OutputError(L"Enumerating devices returned no matches. This is not expected and indicates an installation problem or that the service is not running."); + OutputError("Enumerating devices returned no matches. This is not expected and indicates an installation problem or that the service is not running."); return false; } @@ -635,7 +665,7 @@ bool DoSectionWinRTMidi1ApiEndpoints(_In_ bool const verbose) } catch (...) { - OutputError(L"Enumerating WinRT MIDI 1.0 devices encountered an exception."); + OutputError("Enumerating WinRT MIDI 1.0 devices encountered an exception."); return false; } @@ -662,7 +692,7 @@ bool DoSectionWinRTMidi1ApiEndpoints(_In_ bool const verbose) } catch (...) { - OutputError(L"Enumerating WinRT MIDI 1.0 devices encountered an exception."); + OutputError("Enumerating WinRT MIDI 1.0 devices encountered an exception."); return false; } @@ -714,7 +744,7 @@ bool DoSectionWinMMMidi1ApiEndpoints(_In_ bool const verbose) } catch (...) { - OutputError(L"Enumerating WinMM MIDI 1.0 input devices encountered an exception."); + OutputError("Enumerating WinMM MIDI 1.0 input devices encountered an exception."); return false; } @@ -759,7 +789,7 @@ bool DoSectionWinMMMidi1ApiEndpoints(_In_ bool const verbose) } catch (...) { - OutputError(L"Enumerating WinMM MIDI 1.0 output devices encountered an exception."); + OutputError("Enumerating WinMM MIDI 1.0 output devices encountered an exception."); return false; } @@ -799,7 +829,7 @@ bool DoSectionPingTest(_In_ bool const verbose, _In_ uint8_t const pingCount) } else { - OutputError(L"Ping test failed"); + OutputError("Ping test failed"); OutputStringField(MIDIDIAG_FIELD_LABEL_PING_FAILURE_REASON, pingResult.FailureReason()); return false; @@ -807,13 +837,13 @@ bool DoSectionPingTest(_In_ bool const verbose, _In_ uint8_t const pingCount) } else { - OutputError(L"Ping test failed. Return was null."); + OutputError("Ping test failed. Return was null."); return false; } } catch (...) { - OutputError(L"Ping test failed with exception"); + OutputError("Ping test failed with exception"); return false; } @@ -837,8 +867,8 @@ bool DoSectionClock(_In_ bool const verbose) } catch (winrt::hresult_error ex) { - OutputError(ex.message().c_str()); - OutputError(L"winrt::hresult_error getting clock information. Likely an issue with SDK activation."); + OutputError(winrt::to_string(ex.message())); + OutputError("winrt::hresult_error getting clock information. Likely an issue with SDK activation."); return false; } @@ -863,7 +893,7 @@ bool DoSectionSdkStatus(_In_ bool const verbose) if (!initialized) { - OutputError(L"Failed to initialize Windows MIDI Services SDK runtime."); + OutputError("Failed to initialize Windows MIDI Services SDK runtime. Is it installed?"); } return initialized; @@ -883,7 +913,7 @@ bool DoSectionServiceStatus(_In_ bool const verbose) if (!available) { - OutputError(L"Failed to start MIDI Service"); + OutputError("Failed to start MIDI Service"); } return available; @@ -957,18 +987,24 @@ std::wstring GetProcessorArchitectureString(WORD const arch) } -#define ENV_PROCESSOR_ARCHITECTURE L"PROCESSOR_ARCHITECTURE" -#define ENV_PROCESSOR_IDENTIFIER L"PROCESSOR_IDENTIFIER" -#define ENV_PROCESSOR_LEVEL L"PROCESSOR_LEVEL" -#define ENV_PROCESSOR_REVISION L"PROCESSOR_REVISION" +#define ENV_PROCESSOR_ARCHITECTURE "PROCESSOR_ARCHITECTURE" +#define ENV_PROCESSOR_IDENTIFIER "PROCESSOR_IDENTIFIER" +#define ENV_PROCESSOR_LEVEL "PROCESSOR_LEVEL" +#define ENV_PROCESSOR_REVISION "PROCESSOR_REVISION" + +#define ENV_PROCESSOR_ARCHITECTURE_WIDE L"PROCESSOR_ARCHITECTURE" +#define ENV_PROCESSOR_IDENTIFIER_WIDE L"PROCESSOR_IDENTIFIER" +#define ENV_PROCESSOR_LEVEL_WIDE L"PROCESSOR_LEVEL" +#define ENV_PROCESSOR_REVISION_WIDE L"PROCESSOR_REVISION" + void OutputProcessorEnvVariables() { - OutputStringField(ENV_PROCESSOR_ARCHITECTURE, std::wstring{ _wgetenv(ENV_PROCESSOR_ARCHITECTURE)}); - OutputStringField(ENV_PROCESSOR_IDENTIFIER, std::wstring{ _wgetenv(ENV_PROCESSOR_IDENTIFIER) }); - OutputStringField(ENV_PROCESSOR_LEVEL, std::wstring{ _wgetenv(ENV_PROCESSOR_LEVEL) }); - OutputStringField(ENV_PROCESSOR_REVISION, std::wstring{ _wgetenv(ENV_PROCESSOR_REVISION) }); + OutputStringField(ENV_PROCESSOR_ARCHITECTURE, std::wstring{ _wgetenv(ENV_PROCESSOR_ARCHITECTURE_WIDE)}); + OutputStringField(ENV_PROCESSOR_IDENTIFIER, std::wstring{ _wgetenv(ENV_PROCESSOR_IDENTIFIER_WIDE) }); + OutputStringField(ENV_PROCESSOR_LEVEL, std::wstring{ _wgetenv(ENV_PROCESSOR_LEVEL_WIDE) }); + OutputStringField(ENV_PROCESSOR_REVISION, std::wstring{ _wgetenv(ENV_PROCESSOR_REVISION_WIDE) }); } @@ -1017,12 +1053,12 @@ void OutputProcessAndNativeMachine() } else { - OutputError(L"Unidentified process and/or machine architecture"); + OutputError("Unidentified process and/or machine architecture"); } } else { - OutputError(L"Unable to query process and machine architecture."); + OutputError("Unable to query process and machine architecture."); } } @@ -1097,28 +1133,39 @@ int __cdecl main() bool pingTest = true; bool midiClock = true; + OutputSectionHeader(MIDIDIAG_SECTION_LABEL_HEADER); + + OutputHeader("This application Copyright (c) 2024- Microsoft Corporation"); + OutputHeader("Information, license, and source available at https://aka.ms/midi"); + OutputBlankLine(); OutputHeader(MIDIDIAG_PRODUCT_NAME); - OutputHeader(L"This application Copyright (c) 2024- Microsoft Corporation"); - OutputHeader(L"Information, license, and source available at https://aka.ms/midi"); + OutputBlankLine(); OutputStringField(MIDIDIAG_HEADER_FIELD_LABEL_VERSION_BUILD_SOURCE, std::wstring{ WINDOWS_MIDI_SERVICES_BUILD_SOURCE }); OutputStringField(MIDIDIAG_HEADER_FIELD_LABEL_VERSION_NAME, std::wstring{ WINDOWS_MIDI_SERVICES_BUILD_VERSION_NAME }); OutputStringField(MIDIDIAG_HEADER_FIELD_LABEL_VERSION_FULL, std::wstring{ WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL }); - OutputSectionHeader(MIDIDIAG_SECTION_LABEL_HEADER); - // this fails on Windows 10 // OutputCurrentTime(); try { // do anything which doesn't rely on the service or SDK - DoSectionSystemInfo(verbose); - DoSectionRegistryEntries(verbose); + + // try to get all the classic MIDI 1.0 info up-front, so there's + // some level of info available even if Windows MIDI Services is not installed + + DoSectionDrivers32RegistryEntries(verbose); // don't bail if fails + + DoSectionWinRTMidi1ApiEndpoints(verbose); // we don't bail if this fails + + DoSectionWinMMMidi1ApiEndpoints(verbose); // we don't bail if this fails + + DoSectionMidi2RegistryEntries(verbose); // don't bail if fails // check the service and SDK installs. Do SDK first because // the service status uses the SDK initializer to try to start - // the windows service. + // the windows service and verify the presence of the SDK runtime if (!DoSectionSdkStatus(verbose)) goto abort_run; // we've made it past initializing the SDK, so make sure we shut it down @@ -1130,7 +1177,6 @@ int __cdecl main() }); - if (!DoSectionServiceStatus(verbose)) goto abort_run; // only show midi clock info if the sdk init has worked @@ -1144,14 +1190,7 @@ int __cdecl main() if (transportsWorked) { if (!DoSectionMidi2ApiEndpoints(verbose)) goto abort_run; - } - - DoSectionWinRTMidi1ApiEndpoints(verbose); // we don't bail if this fails - - DoSectionWinMMMidi1ApiEndpoints(verbose); // we don't bail if this fails - if (transportsWorked) - { // ping the service if (pingTest) { @@ -1160,23 +1199,23 @@ int __cdecl main() if (!DoSectionPingTest(verbose, pingCount)) goto abort_run; } } - } catch (...) { - OutputError(L"Exception attempting to gather MIDI information."); + OutputError("Exception attempting to gather MIDI information."); + OutputSectionHeader(MIDIDIAG_SECTION_LABEL_END_OF_FILE); RETURN_FAIL; } - OutputSectionHeader(L"Successful Run"); + OutputSectionHeader("*** Successful Run ***"); OutputSectionHeader(MIDIDIAG_SECTION_LABEL_END_OF_FILE); RETURN_SUCCESS; abort_run: - OutputSectionHeader(L"Aborted Run"); - OutputError(L"Aborting MIDI Diag run due to failure(s)."); + OutputSectionHeader("Aborted Run"); + OutputError("Aborting MIDI Diag run due to failure(s)."); OutputSectionHeader(MIDIDIAG_SECTION_LABEL_END_OF_FILE); RETURN_FAIL; diff --git a/src/app-sdk/mididiag/mididiag.vcxproj b/src/app-sdk/mididiag/mididiag.vcxproj index 328c314f6..e8eca1595 100644 --- a/src/app-sdk/mididiag/mididiag.vcxproj +++ b/src/app-sdk/mididiag/mididiag.vcxproj @@ -167,6 +167,7 @@ + diff --git a/src/app-sdk/mididiag/mididiag.vcxproj.filters b/src/app-sdk/mididiag/mididiag.vcxproj.filters index 0f31e7641..b93e0b08e 100644 --- a/src/app-sdk/mididiag/mididiag.vcxproj.filters +++ b/src/app-sdk/mididiag/mididiag.vcxproj.filters @@ -21,6 +21,9 @@ Header Files + + Header Files + diff --git a/src/app-sdk/mididiag/mididiag_field_defs.h b/src/app-sdk/mididiag/mididiag_field_defs.h index e2ab9e289..95c608b94 100644 --- a/src/app-sdk/mididiag/mididiag_field_defs.h +++ b/src/app-sdk/mididiag/mididiag_field_defs.h @@ -11,124 +11,125 @@ // All field names here in case you want to parse the file. Just include or // copy these definitions into your code -#define MIDIDIAG_PRODUCT_NAME L"Microsoft Windows MIDI Services - Diagnostics Report" +#define MIDIDIAG_PRODUCT_NAME "Microsoft Windows MIDI Services - Diagnostics Report" #define MIDIDIAG_MAX_FIELD_LABEL_WIDTH 25 -#define MIDIDIAG_FIELD_LABEL_FILE_VERSION L"file_version" +#define MIDIDIAG_FIELD_LABEL_FILE_VERSION "file_version" -#define MIDIDIAG_HEADER_FIELD_LABEL_VERSION_BUILD_SOURCE L"ver_build_source" -#define MIDIDIAG_HEADER_FIELD_LABEL_VERSION_NAME L"ver_build_name" -#define MIDIDIAG_HEADER_FIELD_LABEL_VERSION_FULL L"ver_build_full" +#define MIDIDIAG_HEADER_FIELD_LABEL_VERSION_BUILD_SOURCE "ver_build_source" +#define MIDIDIAG_HEADER_FIELD_LABEL_VERSION_NAME "ver_build_name" +#define MIDIDIAG_HEADER_FIELD_LABEL_VERSION_FULL "ver_build_full" -#define MIDIDIAG_FIELD_SEPARATOR L" : " +#define MIDIDIAG_FIELD_SEPARATOR " : " #define MIDIDIAG_SECTION_HEADER_SEPARATOR_CHAR '=' #define MIDIDIAG_ITEM_SEPARATOR_CHAR '-' -#define MIDIDIAG_SEPARATOR_REPEATING_CHAR_COUNT_PER_LINE 79 +#define MIDIDIAG_SEPARATOR_REPEATING_CHAR_COUNT_PER_LINE 120 -#define MIDIDIAG_FIELD_LABEL_ERROR L"ERROR" +#define MIDIDIAG_FIELD_LABEL_ERROR "ERROR" -#define MIDIDIAG_SECTION_LABEL_ENUM_REGISTRY L"enum_registry" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_VALUE L"transport_id" +#define MIDIDIAG_SECTION_LABEL_ENUM_REGISTRY "enum_registry" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_VALUE "transport_id" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT L"reg_root" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_DISCOVERY_ENABLED L"reg_discovery_enabled" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_USE_MMCSS L"reg_use_mmcss" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_CURRENT_CONFIG L"reg_current_config" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT "reg_root" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_DISCOVERY_ENABLED "reg_discovery_enabled" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_USE_MMCSS "reg_use_mmcss" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_ROOT_CURRENT_CONFIG "reg_current_config" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_SDK_INSTALLED L"reg_sdk_installed" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_SDK_INSTALLED "reg_sdk_installed" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_MIDISRV_EXENAME L"reg_midisrv_exe" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_MIDISRV_EXENAME "reg_midisrv_exe" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORTS L"reg_transports" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORT_NAME L"reg_transport_name" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORT_CLSID L"reg_transport_clsid" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORT_ENABLED L"reg_transport_enabled" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORT_DLLNAME L"reg_transport_dll" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORT_PROGID L"reg_transport_progid" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORTS "reg_transports" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORT_NAME "reg_transport_name" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORT_CLSID "reg_transport_clsid" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORT_ENABLED "reg_transport_enabled" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORT_DLLNAME "reg_transport_dll" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSPORT_PROGID "reg_transport_progid" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSFORMS L"reg_transforms" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_TRANSFORMS "reg_transforms" -#define MIDIDIAG_FIELD_LABEL_REGISTRY_DRIVERS32_ENTRY L"reg_drivers32_entry" +#define MIDIDIAG_SECTION_LABEL_ENUM_REGISTRY_DRIVERS32 "reg_drivers32" +#define MIDIDIAG_FIELD_LABEL_REGISTRY_DRIVERS32_ENTRY "reg_drivers32_entry" -#define MIDIDIAG_SECTION_LABEL_ENUM_COM L"enum_com" +#define MIDIDIAG_SECTION_LABEL_ENUM_COM "enum_com" -#define MIDIDIAG_SECTION_LABEL_ENUM_TRANSPORTS L"enum_transports" -#define MIDIDIAG_FIELD_LABEL_TRANSPORT_ID L"transport_id" -#define MIDIDIAG_FIELD_LABEL_TRANSPORT_NAME L"name" -#define MIDIDIAG_FIELD_LABEL_TRANSPORT_CODE L"transport_code" -#define MIDIDIAG_FIELD_LABEL_TRANSPORT_VERSION L"version" -#define MIDIDIAG_FIELD_LABEL_TRANSPORT_AUTHOR L"author" -#define MIDIDIAG_FIELD_LABEL_TRANSPORT_DESCRIPTION L"description" +#define MIDIDIAG_SECTION_LABEL_ENUM_TRANSPORTS "enum_transports" +#define MIDIDIAG_FIELD_LABEL_TRANSPORT_ID "transport_id" +#define MIDIDIAG_FIELD_LABEL_TRANSPORT_NAME "name" +#define MIDIDIAG_FIELD_LABEL_TRANSPORT_CODE "transport_code" +#define MIDIDIAG_FIELD_LABEL_TRANSPORT_VERSION "version" +#define MIDIDIAG_FIELD_LABEL_TRANSPORT_AUTHOR "author" +#define MIDIDIAG_FIELD_LABEL_TRANSPORT_DESCRIPTION "description" -#define MIDIDIAG_SECTION_LABEL_MIDI2_API_ENDPOINTS L"enum_ump_api_endpoints" -#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_ID L"endpoint_device_id" -#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_NAME L"name" -#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_CODE L"transport_code" -#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_USER_SUPPLIED_NAME L"name_user_supplied" -#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_ENDPOINT_SUPPLIED_NAME L"name_endpoint_supplied" -#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_SUPPLIED_NAME L"name_transport_supplied" -#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_SUPPLIED_DESC L"desc_transport_supplied" -#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_USER_SUPPLIED_DESC L"desc_user_supplied" -#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_PARENT_ID L"parent_id" -#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_PARENT_NAME L"parent_name" +#define MIDIDIAG_SECTION_LABEL_MIDI2_API_ENDPOINTS "enum_ump_api_endpoints" +#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_ID "endpoint_device_id" +#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_NAME "name" +#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_CODE "transport_code" +#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_USER_SUPPLIED_NAME "name_user_supplied" +#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_ENDPOINT_SUPPLIED_NAME "name_endpoint_supplied" +#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_SUPPLIED_NAME "name_transport_supplied" +#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_SUPPLIED_DESC "desc_transport_supplied" +#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_USER_SUPPLIED_DESC "desc_user_supplied" +#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_PARENT_ID "parent_id" +#define MIDIDIAG_FIELD_LABEL_MIDI2_ENDPOINT_PARENT_NAME "parent_name" -#define MIDIDIAG_SECTION_LABEL_MIDI1_API_INPUT_ENDPOINTS L"enum_winrt_midi1_api_input_ports" -#define MIDIDIAG_SECTION_LABEL_MIDI1_API_OUTPUT_ENDPOINTS L"enum_winrt_midi1_api_output_ports" -#define MIDIDIAG_FIELD_LABEL_MIDI1_ENDPOINT_ID L"endpoint_device_id" -#define MIDIDIAG_FIELD_LABEL_MIDI1_ENDPOINT_NAME L"name" +#define MIDIDIAG_SECTION_LABEL_MIDI1_API_INPUT_ENDPOINTS "enum_winrt_midi1_api_input_ports" +#define MIDIDIAG_SECTION_LABEL_MIDI1_API_OUTPUT_ENDPOINTS "enum_winrt_midi1_api_output_ports" +#define MIDIDIAG_FIELD_LABEL_MIDI1_ENDPOINT_ID "endpoint_device_id" +#define MIDIDIAG_FIELD_LABEL_MIDI1_ENDPOINT_NAME "name" -#define MIDIDIAG_SECTION_LABEL_WINMM_API_INPUT_ENDPOINTS L"enum_winmm_midi1_api_input_ports" -#define MIDIDIAG_SECTION_LABEL_WINMM_API_OUTPUT_ENDPOINTS L"enum_winmm_midi1_api_output_ports" -#define MIDIDIAG_FIELD_LABEL_WINMM_ENDPOINT_ID L"endpoint_index" -#define MIDIDIAG_FIELD_LABEL_WINMM_ENDPOINT_NAME L"name" -#define MIDIDIAG_FIELD_LABEL_WINMM_ENDPOINT_COUNT L"endpoint_count" -#define MIDIDIAG_FIELD_LABEL_WINMM_ERROR_COUNT L"dev_caps_error_count" +#define MIDIDIAG_SECTION_LABEL_WINMM_API_INPUT_ENDPOINTS "enum_winmm_midi1_api_input_ports" +#define MIDIDIAG_SECTION_LABEL_WINMM_API_OUTPUT_ENDPOINTS "enum_winmm_midi1_api_output_ports" +#define MIDIDIAG_FIELD_LABEL_WINMM_ENDPOINT_ID "endpoint_index" +#define MIDIDIAG_FIELD_LABEL_WINMM_ENDPOINT_NAME "name" +#define MIDIDIAG_FIELD_LABEL_WINMM_ENDPOINT_COUNT "endpoint_count" +#define MIDIDIAG_FIELD_LABEL_WINMM_ERROR_COUNT "dev_caps_error_count" -#define MIDIDIAG_SECTION_LABEL_PING_TEST L"ping_test" -#define MIDIDIAG_FIELD_LABEL_PING_ATTEMPT_COUNT L"ping_attempt_count" -#define MIDIDIAG_FIELD_LABEL_PING_RETURN_COUNT L"ping_return_count" -#define MIDIDIAG_FIELD_LABEL_PING_ROUND_TRIP_TOTAL_TICKS L"round_trip_total_ticks" -#define MIDIDIAG_FIELD_LABEL_PING_ROUND_TRIP_AVERAGE_TICKS L"round_trip_average_ticks" -#define MIDIDIAG_FIELD_LABEL_PING_FAILURE_REASON L"ping_failure_reason" +#define MIDIDIAG_SECTION_LABEL_PING_TEST "ping_test" +#define MIDIDIAG_FIELD_LABEL_PING_ATTEMPT_COUNT "ping_attempt_count" +#define MIDIDIAG_FIELD_LABEL_PING_RETURN_COUNT "ping_return_count" +#define MIDIDIAG_FIELD_LABEL_PING_ROUND_TRIP_TOTAL_TICKS "round_trip_total_ticks" +#define MIDIDIAG_FIELD_LABEL_PING_ROUND_TRIP_AVERAGE_TICKS "round_trip_average_ticks" +#define MIDIDIAG_FIELD_LABEL_PING_FAILURE_REASON "ping_failure_reason" -#define MIDIDIAG_SECTION_LABEL_MIDI_CLOCK L"midi_clock" -#define MIDIDIAG_FIELD_LABEL_CLOCK_FREQUENCY L"clock_frequency" -#define MIDIDIAG_FIELD_LABEL_CLOCK_NOW L"clock_now" +#define MIDIDIAG_SECTION_LABEL_MIDI_CLOCK "midi_clock" +#define MIDIDIAG_FIELD_LABEL_CLOCK_FREQUENCY "clock_frequency" +#define MIDIDIAG_FIELD_LABEL_CLOCK_NOW "clock_now" -#define MIDIDIAG_SECTION_LABEL_SERVICE_STATUS L"service_status" -#define MIDIDIAG_FIELD_LABEL_SERVICE_AVAILABLE L"available" +#define MIDIDIAG_SECTION_LABEL_SERVICE_STATUS "service_status" +#define MIDIDIAG_FIELD_LABEL_SERVICE_AVAILABLE "available" -#define MIDIDIAG_SECTION_LABEL_SDK_STATUS L"sdk_runtime_status" -#define MIDIDIAG_FIELD_LABEL_SDK_INITIALIZED L"sdk_runtime_initialized" +#define MIDIDIAG_SECTION_LABEL_SDK_STATUS "sdk_runtime_status" +#define MIDIDIAG_FIELD_LABEL_SDK_INITIALIZED "sdk_runtime_initialized" -#define MIDIDIAG_SECTION_LABEL_OS L"os" -#define MIDIDIAG_FIELD_LABEL_OS_VERSION L"os_version" +#define MIDIDIAG_SECTION_LABEL_OS "os" +#define MIDIDIAG_FIELD_LABEL_OS_VERSION "os_version" -#define MIDIDIAG_SECTION_LABEL_PROCESSOR_ENV L"processor_env" -#define MIDIDIAG_SECTION_LABEL_NATIVE_SYSTEM_INFO L"native_system_info" -#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_ARCH L"processor_architecture" -#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_LEVEL L"processor_level" -#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_REVISION L"processor_revision" -#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_EMULATION L"running_emulated" +#define MIDIDIAG_SECTION_LABEL_PROCESSOR_ENV "processor_env" +#define MIDIDIAG_SECTION_LABEL_NATIVE_SYSTEM_INFO "native_system_info" +#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_ARCH "processor_architecture" +#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_LEVEL "processor_level" +#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_REVISION "processor_revision" +#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_EMULATION "running_emulated" -#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMECAPS_ERROR L"timecaps_error" -#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMECAPS_MIN_PERIOD L"timecaps_min_period" -#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMECAPS_MAX_PERIOD L"timecaps_max_period" +#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMECAPS_ERROR "timecaps_error" +#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMECAPS_MIN_PERIOD "timecaps_min_period" +#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMECAPS_MAX_PERIOD "timecaps_max_period" -#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMER_RESOLUTION_MIN_MS L"timer_resolution_min" -#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMER_RESOLUTION_MAX_MS L"timer_resolution_max" -#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMER_RESOLUTION_CURRENT_MS L"timer_resolution_current" +#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMER_RESOLUTION_MIN_MS "timer_resolution_min" +#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMER_RESOLUTION_MAX_MS "timer_resolution_max" +#define MIDIDIAG_FIELD_LABEL_SYSTEM_INFO_TIMER_RESOLUTION_CURRENT_MS "timer_resolution_current" -#define MIDIDIAG_SECTION_LABEL_HEADER L"header" -#define MIDIDIAG_FIELD_LABEL_CURRENT_TIME L"current_time" +#define MIDIDIAG_SECTION_LABEL_HEADER "header" +#define MIDIDIAG_FIELD_LABEL_CURRENT_TIME "current_time" -#define MIDIDIAG_SECTION_LABEL_END_OF_FILE L"end_of_file" +#define MIDIDIAG_SECTION_LABEL_END_OF_FILE "end_of_file" From 0d3369f5278e4ab96a9cd7271d2bc2f9b15aea6e Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Sat, 11 Jan 2025 16:29:33 -0500 Subject: [PATCH 4/6] Fix fallback port creation in MidiDeviceManager when no GTBs or FBs available Looks like there was a copy/paste error in there. It was still checking for static function blocks in the fallback mechanism when there were no function blocks available or declared. --- src/api/Service/Exe/MidiDeviceManager.cpp | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/api/Service/Exe/MidiDeviceManager.cpp b/src/api/Service/Exe/MidiDeviceManager.cpp index 4fdbde58e..dcf4c6a69 100644 --- a/src/api/Service/Exe/MidiDeviceManager.cpp +++ b/src/api/Service/Exe/MidiDeviceManager.cpp @@ -2259,20 +2259,20 @@ CMidiDeviceManager::UseFallbackMidi1PortDefinition( additionalProperties.Append(winrt::to_hstring(STRING_PKEY_MIDI_FunctionBlocksAreStatic)); auto deviceInfo = DeviceInformation::CreateFromIdAsync(umpDeviceInterfaceId, additionalProperties, winrt::Windows::Devices::Enumeration::DeviceInformationKind::DeviceInterface).get(); - auto prop = deviceInfo.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_FunctionBlocksAreStatic)); - if (prop) - { - auto functionBlocksAreStatic = winrt::unbox_value>(prop); - if (functionBlocksAreStatic) - { - TraceLoggingWrite( - MidiSrvTelemetryProvider::Provider(), - MIDI_TRACE_EVENT_INFO, - TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), - TraceLoggingLevel(WINEVENT_LEVEL_INFO), - TraceLoggingPointer(this, "this"), - TraceLoggingWideString(L"Using static function blocks", MIDI_TRACE_EVENT_MESSAGE_FIELD) - ); + //auto prop = deviceInfo.Properties().Lookup(winrt::to_hstring(STRING_PKEY_MIDI_FunctionBlocksAreStatic)); + //if (prop) + //{ + //auto functionBlocksAreStatic = winrt::unbox_value>(prop); + //if (functionBlocksAreStatic) + //{ + // TraceLoggingWrite( + // MidiSrvTelemetryProvider::Provider(), + // MIDI_TRACE_EVENT_INFO, + // TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + // TraceLoggingLevel(WINEVENT_LEVEL_INFO), + // TraceLoggingPointer(this, "this"), + // TraceLoggingWideString(L"Using static function blocks", MIDI_TRACE_EVENT_MESSAGE_FIELD) + // ); for (UINT flow = 0; flow < 2; flow++) { @@ -2284,8 +2284,8 @@ CMidiDeviceManager::UseFallbackMidi1PortDefinition( portInfo[flow][groupIndex].InterfaceId = umpDeviceInterfaceId; } } - } - } + //} + //} return S_OK; } From 7788c9abbc3a1d4084a08570f5738372b4057058 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Sat, 11 Jan 2025 22:51:32 -0500 Subject: [PATCH 5/6] Basic network MIDI 2.0 client functionality --- build/staging/version/BundleInfo.wxi | 2 +- .../version/WindowsMidiServicesVersion.cs | 6 +- .../version/WindowsMidiServicesVersion.h | 6 +- .../basics/client-basics-cpp.vcxproj | 2 +- samples/cpp-winrt/basics/packages.config | 2 +- .../loopback-endpoints-cpp.vcxproj | 2 +- .../loopback-endpoints/packages.config | 2 +- samples/cpp-winrt/send-speed/packages.config | 2 +- .../send-speed/send-speed-cpp.vcxproj | 2 +- .../simple-app-to-app-midi/packages.config | 2 +- .../simple-app-to-app-cpp.vcxproj | 2 +- .../static-enum-endpoints/packages.config | 2 +- .../static-enum-endpoints-cpp.vcxproj | 2 +- .../cpp-winrt/watch-endpoints/packages.config | 2 +- .../watch-endpoints-cpp.vcxproj | 2 +- .../Midi2.NetworkMidiConfigurationManager.cpp | 76 +++++ .../Midi2.NetworkMidiEndpointManager.cpp | 169 ++++++++++- .../Midi2.NetworkMidiEndpointManager.h | 1 + .../MidiNetworkClient.cpp | 224 +++++++++++++- .../MidiNetworkClient.h | 40 ++- .../MidiNetworkConnection.cpp | 274 ++++++++++++++++-- .../MidiNetworkConnection.h | 13 +- .../MidiNetworkHost.cpp | 8 +- .../MidiNetworkHost.h | 3 + .../MidiNetworkMessages.h | 1 + .../TransportState.cpp | 12 + .../UdpNetworkMidi2Transport/TransportState.h | 5 +- .../UdpNetworkMidi2Transport/transport_defs.h | 24 +- src/app-sdk/mididiag/mididiag.vcxproj | 2 +- src/app-sdk/mididiag/packages.config | 2 +- src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj | 2 +- src/app-sdk/midimdnsinfo/packages.config | 2 +- src/app-sdk/midiusbinfo/midiusbinfo.vcxproj | 2 +- src/app-sdk/midiusbinfo/packages.config | 2 +- .../Commands/Service/ServiceRestartCommand.cs | 8 +- .../Commands/Service/ServiceStopCommand.cs | 6 +- 36 files changed, 827 insertions(+), 87 deletions(-) diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index 4aeb86dca..b0add7f75 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,4 +1,4 @@ - + diff --git a/build/staging/version/WindowsMidiServicesVersion.cs b/build/staging/version/WindowsMidiServicesVersion.cs index 18c9b039f..26cecb2cb 100644 --- a/build/staging/version/WindowsMidiServicesVersion.cs +++ b/build/staging/version/WindowsMidiServicesVersion.cs @@ -6,12 +6,12 @@ public static class MidiBuildInformation { public const string Source = "GitHub Preview"; public const string Name = "Developer Preview 9"; - public const string BuildFullVersion = "1.0.2-preview-9.250106-1428"; + public const string BuildFullVersion = "1.0.2-preview-9.250111-2238"; public const string VersionMajor = "1"; public const string VersionMinor = "0"; public const string VersionRevision = "2"; - public const string VersionDateNumber = "250106"; - public const string VersionTimeNumber = "1428"; + public const string VersionDateNumber = "250111"; + public const string VersionTimeNumber = "2238"; } } diff --git a/build/staging/version/WindowsMidiServicesVersion.h b/build/staging/version/WindowsMidiServicesVersion.h index 5799c747f..6350d451e 100644 --- a/build/staging/version/WindowsMidiServicesVersion.h +++ b/build/staging/version/WindowsMidiServicesVersion.h @@ -5,12 +5,12 @@ #define WINDOWS_MIDI_SERVICES_BUILD_SOURCE L"GitHub Preview" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_NAME L"Developer Preview 9" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.2-preview-9.250106-1428" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.2-preview-9.250111-2238" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MAJOR L"1" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MINOR L"0" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_REVISION L"2" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250106" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"1428" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250111" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"2238" #endif diff --git a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj index acd4c5e92..5e224714f 100644 --- a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj +++ b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250106-1428 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 true true false diff --git a/samples/cpp-winrt/basics/packages.config b/samples/cpp-winrt/basics/packages.config index adcdab936..529fb3e5b 100644 --- a/samples/cpp-winrt/basics/packages.config +++ b/samples/cpp-winrt/basics/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj index ed42dd4ec..e46daa425 100644 --- a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250106-1428 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 true true true diff --git a/samples/cpp-winrt/loopback-endpoints/packages.config b/samples/cpp-winrt/loopback-endpoints/packages.config index adcdab936..529fb3e5b 100644 --- a/samples/cpp-winrt/loopback-endpoints/packages.config +++ b/samples/cpp-winrt/loopback-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/send-speed/packages.config b/samples/cpp-winrt/send-speed/packages.config index adcdab936..529fb3e5b 100644 --- a/samples/cpp-winrt/send-speed/packages.config +++ b/samples/cpp-winrt/send-speed/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj index d817f8320..d68880793 100644 --- a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj +++ b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250106-1428 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 true true true diff --git a/samples/cpp-winrt/simple-app-to-app-midi/packages.config b/samples/cpp-winrt/simple-app-to-app-midi/packages.config index adcdab936..529fb3e5b 100644 --- a/samples/cpp-winrt/simple-app-to-app-midi/packages.config +++ b/samples/cpp-winrt/simple-app-to-app-midi/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj index fd4d1fa97..6c15c59c1 100644 --- a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj +++ b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250106-1428 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 true true true diff --git a/samples/cpp-winrt/static-enum-endpoints/packages.config b/samples/cpp-winrt/static-enum-endpoints/packages.config index adcdab936..529fb3e5b 100644 --- a/samples/cpp-winrt/static-enum-endpoints/packages.config +++ b/samples/cpp-winrt/static-enum-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj index d19fa4b5f..f4df058fb 100644 --- a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250106-1428 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 true true true diff --git a/samples/cpp-winrt/watch-endpoints/packages.config b/samples/cpp-winrt/watch-endpoints/packages.config index adcdab936..529fb3e5b 100644 --- a/samples/cpp-winrt/watch-endpoints/packages.config +++ b/samples/cpp-winrt/watch-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj index f6f339ae7..40cb25454 100644 --- a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250106-1428 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 true true true diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiConfigurationManager.cpp b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiConfigurationManager.cpp index 5db7e37b0..9811715c5 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiConfigurationManager.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiConfigurationManager.cpp @@ -316,6 +316,8 @@ CMidi2NetworkMidiConfigurationManager::UpdateConfiguration( definition->Enabled = hostEntry.GetNamedBoolean(MIDI_CONFIG_JSON_NETWORK_MIDI_ENABLED_KEY, true); definition->Advertise = hostEntry.GetNamedBoolean(MIDI_CONFIG_JSON_NETWORK_MIDI_MDNS_ADVERTISE_KEY, true); + definition->CreateMidi1Ports = hostEntry.GetNamedBoolean(MIDI_CONFIG_JSON_NETWORK_MIDI_CREATE_MIDI1_PORTS_KEY, MIDI_NETWORK_MIDI_CREATE_MIDI1_PORTS_DEFAULT); + definition->UmpEndpointName = internal::TrimmedHStringCopy(hostEntry.GetNamedString(MIDI_CONFIG_JSON_ENDPOINT_COMMON_NAME_PROPERTY, L"")); definition->ProductInstanceId = internal::TrimmedHStringCopy(hostEntry.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_PRODUCT_INSTANCE_ID_PROPERTY, L"")); @@ -449,7 +451,81 @@ CMidi2NetworkMidiConfigurationManager::UpdateConfiguration( // device (or application) if (clientsSection != nullptr && clientsSection.Size() > 0) { + for (auto const& it = clientsSection.First(); it.HasCurrent(); it.MoveNext()) + { + auto clientEntry = clientsSection.GetNamedObject(it.Current().Key()); + + auto definition = std::make_shared(); + RETURN_IF_NULL_ALLOC(definition); + + winrt::hstring validationErrorMessage{ }; + + // currently, UDP is the only allowed protocol + auto protocol = internal::ToLowerTrimmedHStringCopy(clientEntry.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_NETWORK_PROTOCOL_KEY, MIDI_CONFIG_JSON_NETWORK_MIDI_NETWORK_PROTOCOL_VALUE_UDP)); + + if (protocol != MIDI_CONFIG_JSON_NETWORK_MIDI_NETWORK_PROTOCOL_VALUE_UDP) + { + validationErrorMessage = L"Invalid network protocol '" + protocol + L"' specified."; + } + else + { + definition->EntryIdentifier = internal::TrimmedHStringCopy(it.Current().Key()); + definition->Enabled = clientEntry.GetNamedBoolean(MIDI_CONFIG_JSON_NETWORK_MIDI_ENABLED_KEY, true); + + winrt::hstring localEndpointName{ }; + winrt::hstring localProductInstanceId{ }; + + // TODO: Add ability for config file to specify the localEndpointName and localProductInstanceId + if (localEndpointName.empty()) + { + std::wstring buffer{}; + DWORD bufferSize = MAX_COMPUTERNAME_LENGTH + 1; + buffer.resize(bufferSize); + + bool validName = GetComputerName(buffer.data(), &bufferSize); + if (validName) + { + localEndpointName = buffer; + } + } + + // TODO: we may want to provide the local product instance id as a system-wide setting. Same with name + if (localProductInstanceId.empty()) + { + localProductInstanceId = L"8675309-OU812"; + } + + definition->LocalEndpointName = localEndpointName; + definition->LocalProductInstanceId = localProductInstanceId; + + auto matchSection = clientEntry.GetNamedObject(MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_OBJECT_KEY, nullptr); + + if (matchSection) + { + // TODO: Match on IP/Port, etc. + // for the moment, we only match on the actual device id, so must be mdns-advertised + + definition->MatchId = internal::TrimmedHStringCopy(matchSection.GetNamedString(MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_ID_KEY, L"")); + + + + + + TransportState::Current().AddPendingClientDefinition(definition); + + responseObject.SetNamedValue( + MIDI_CONFIG_JSON_CONFIGURATION_RESPONSE_SUCCESS_PROPERTY_KEY, + jsonTrue); + } + else + { + // we have no way to match against endpoints, so this is a failure + validationErrorMessage = L"Missing match entry"; + } + } + + } } } diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp index 5736796bd..ae2141e3c 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.cpp @@ -138,7 +138,8 @@ CMidi2NetworkMidiEndpointManager::OnDeviceWatcherAdded(enumeration::DeviceWatche m_foundAdvertisedHosts.insert_or_assign(args.Id(), args); - // TODO: do we need to signal the background thread to check the collection? + // signal the background thread to check the collection? + m_backgroundEndpointCreatorThreadWakeup.SetEvent(); return S_OK; } @@ -236,7 +237,6 @@ CMidi2NetworkMidiEndpointManager::StartBackgroundEndpointCreator() std::jthread workerThread(std::bind_front(&CMidi2NetworkMidiEndpointManager::EndpointCreatorWorker, this)); m_backgroundEndpointCreatorThread = std::move(workerThread); - //m_backgroundEndpointCreatorThreadStopToken = m_backgroundEndpointCreatorThread.get_stop_token(); m_backgroundEndpointCreatorThread.detach(); @@ -326,16 +326,161 @@ CMidi2NetworkMidiEndpointManager::EndpointCreatorWorker(std::stop_token stopToke // TODO: run through client definition entries. These aren't actual clients // but are instead just parameters needed to create connections to hosts when // they come online. - // for (auto const& clientDefinition : TransportState::Current().GetPendingClientDefinitions()) { - UNREFERENCED_PARAMETER(clientDefinition); + if (clientDefinition->Created) + { + continue; + } - // any requiring a match, see if we have a match in our deviceinformation collection + if (!clientDefinition->Enabled) + { + continue; + } + // any requiring a match, see if we have a match in our deviceinformation collection // - any that are IP/port direct, try to connect, but don't keep trying the same ones if they fail + // check for id first, as this is fastest + // TODO: right now, this is case-sensitive. It needs clean-up. + if (auto advertised = m_foundAdvertisedHosts.find(clientDefinition->MatchId); advertised != m_foundAdvertisedHosts.end()) + { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Processing mdns entry", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(advertised->second.Id().c_str(), "id") + ); + + auto client = std::make_shared(); + LOG_IF_NULL_ALLOC(client); + + auto initHr = client->Initialize(*clientDefinition, advertised->second); + LOG_IF_FAILED(initHr); + + if (SUCCEEDED(initHr)) + { + TransportState::Current().AddClient(client); + + // todo: get the hostname and port, and then start the client + + //deviceId = advertised.Id(); + //deviceName = advertised.Name(); + + //host.HostName = internal::GetDeviceInfoProperty(entry.Properties(), L"System.Devices.Dnssd.HostName", L""); + //host.FullName = internal::GetDeviceInfoProperty(entry.Properties(), L"System.Devices.Dnssd.FullName", L""); + //host.ServiceInstanceName = internal::GetDeviceInfoProperty(entry.Properties(), L"System.Devices.Dnssd.InstanceName", L""); + //host.Port = internal::GetDeviceInfoProperty(entry.Properties(), L"System.Devices.Dnssd.PortNumber", 0); + + winrt::hstring hostNameString{}; + uint16_t port{ 0 }; + + const winrt::hstring hostNamePropertyKey = L"System.Devices.Dnssd.HostName"; + //const winrt::hstring hostNamePropertyKey = L"System.Devices.Dnssd.FullName"; + const winrt::hstring hostPortPropertyKey = L"System.Devices.Dnssd.PortNumber"; + const winrt::hstring ipAddressPropertyKey = L"System.Devices.IpAddress"; + + // we use IP address first, as that is the most reliable + if (advertised->second.Properties().HasKey(ipAddressPropertyKey)) + { + auto prop = advertised->second.Properties().Lookup(ipAddressPropertyKey).as>(); + winrt::com_array array; + prop.GetStringArray(array); + + // we only take the top one right now. We should take the others as well + if (array.size() > 0) + { + hostNameString = array.at(0); + } + } + // next we get the host name, but this relies on DNS being set up properly, + // which is often not the case on a network with just some devices and a laptop + else if (advertised->second.Properties().HasKey(hostNamePropertyKey)) + { + auto prop = advertised->second.Properties().Lookup(hostNamePropertyKey); + + if (prop) + { + hostNameString = winrt::unbox_value(prop); + } + } + + if (advertised->second.Properties().HasKey(hostPortPropertyKey)) + { + auto prop = advertised->second.Properties().Lookup(hostPortPropertyKey); + + if (prop) + { + port = winrt::unbox_value(prop); + } + } + + // the == 0 is hacky, but for midi, anything under 1024 is likely bogus + if (!hostNameString.empty() && port != 0) + { + + HostName hostName(hostNameString); + winrt::hstring portNameString = winrt::to_hstring(port); + + auto startHr = client->Start(hostName, portNameString); + + LOG_IF_FAILED(startHr); + + if (SUCCEEDED(startHr)) + { + // Mark as created so we don't try to process it again + clientDefinition->Created = true; + } + else + { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_ERROR, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Failed to start client", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(advertised->second.Id().c_str(), "id"), + TraceLoggingWideString(hostNameString.c_str(), "hostname string"), + TraceLoggingWideString(hostName.ToString().c_str(), "created hostname"), + TraceLoggingWideString(portNameString.c_str(), "port") + ); + } + } + else + { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_ERROR, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Unable to resolve remote device", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(advertised->second.Id().c_str(), "id") + ); + } + + } + else + { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_ERROR, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Failed to initialize client", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(advertised->second.Id().c_str(), "id") + ); + + } + + } + } // TODO: wait for notification of new hosts online or new entries added via config @@ -485,6 +630,7 @@ CMidi2NetworkMidiEndpointManager::CreateNewEndpoint( std::wstring const& remoteEndpointProductInstanceId, winrt::Windows::Networking::HostName const& hostName, std::wstring const& networkPort, + bool umpOnly, std::wstring& createdNewDeviceInstanceId, std::wstring& createdNewEndpointDeviceInterfaceId ) @@ -585,7 +731,17 @@ CMidi2NetworkMidiEndpointManager::CreateNewEndpoint( // TODO: Add custom properties for the network information + std::wstring endpointDescription{ L"Network MIDI 2.0 endpoint "}; + switch (thisServiceRole) + { + case MidiNetworkConnectionRole::ConnectionWindowsIsHost: + endpointDescription += L"(This PC is the Network Host)"; + break; + case MidiNetworkConnectionRole::ConnectionWindowsIsClient: + endpointDescription += L"(This PC is a Network Client)"; + break; + } MIDIENDPOINTCOMMONPROPERTIES commonProperties{}; commonProperties.TransportId = TRANSPORT_LAYER_GUID; @@ -593,7 +749,7 @@ CMidi2NetworkMidiEndpointManager::CreateNewEndpoint( commonProperties.FriendlyName = friendlyName.c_str(); commonProperties.TransportCode = transportCode.c_str(); commonProperties.EndpointName = endpointName.c_str(); - commonProperties.EndpointDescription = nullptr; + commonProperties.EndpointDescription = endpointDescription.c_str(); commonProperties.CustomEndpointName = nullptr; commonProperties.CustomEndpointDescription = nullptr; commonProperties.UniqueIdentifier = remoteEndpointProductInstanceId.c_str(); @@ -607,7 +763,6 @@ CMidi2NetworkMidiEndpointManager::CreateNewEndpoint( capabilities |= MidiEndpointCapabilities_GenerateIncomingTimestamps; commonProperties.Capabilities = (MidiEndpointCapabilities)capabilities; - bool umpOnly = true; RETURN_IF_FAILED(m_midiDeviceManager->ActivateEndpoint( (PCWSTR)m_parentDeviceId.c_str(), // parent instance Id umpOnly, // UMP-only. When set to false, WinMM MIDI 1.0 ports are created diff --git a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.h b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.h index 05970fce5..e68fae356 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/Midi2.NetworkMidiEndpointManager.h @@ -28,6 +28,7 @@ class CMidi2NetworkMidiEndpointManager : _In_ std::wstring const& remoteEndpointProductInstanceId, _In_ winrt::Windows::Networking::HostName const& hostName, _In_ std::wstring const& networkPort, + _In_ bool umpOnly, _Out_ std::wstring& createdNewDeviceInstanceId, _Out_ std::wstring& createdNewEndpointDeviceInterfaceId )); diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkClient.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkClient.cpp index 580d3ae4e..bfb86c89c 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkClient.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkClient.cpp @@ -11,7 +11,10 @@ _Use_decl_annotations_ HRESULT -MidiNetworkClient::Initialize(MidiNetworkClientDefinition& clientDefinition) +MidiNetworkClient::Initialize( + MidiNetworkClientDefinition& clientDefinition, + enumeration::DeviceInformation advertisedHost +) { TraceLoggingWrite( MidiNetworkMidiTransportTelemetryProvider::Provider(), @@ -22,16 +25,226 @@ MidiNetworkClient::Initialize(MidiNetworkClientDefinition& clientDefinition) TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) ); - UNREFERENCED_PARAMETER(clientDefinition); + UNREFERENCED_PARAMETER(advertisedHost); + m_createUmpEndpointsOnly = !clientDefinition.CreateMidi1Ports; + m_thisEndpointName = clientDefinition.LocalEndpointName; + m_thisProductInstanceId = clientDefinition.LocalProductInstanceId; return S_OK; } +_Use_decl_annotations_ +void MidiNetworkClient::OnMessageReceived( + _In_ winrt::Windows::Networking::Sockets::DatagramSocket const& sender, + _In_ winrt::Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs const& args) +{ + UNREFERENCED_PARAMETER(sender); + + auto reader = args.GetDataReader(); + + if (reader != nullptr && reader.UnconsumedBufferLength() < MINIMUM_VALID_UDP_PACKET_SIZE) + { + // not a message we understand. Needs to be at least the size of the + // MIDI header plus a command packet header. Really it needs to be larger, but + // just trying to weed out blips + + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_WARNING, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_WARNING), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Undersized packet", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + + // todo: does the reader need to be cleared? + + return; + } + + uint32_t udpHeader = reader.ReadUInt32(); + + if (udpHeader != MIDI_UDP_PAYLOAD_HEADER) + { + // not a message we understand + + return; + } + if (m_networkConnection) + { + m_networkConnection->ProcessIncomingMessage(reader); + } + else + { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_ERROR, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Message received from remote client, connection is nullptr", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + } + + + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Exit", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + +} + + +_Use_decl_annotations_ +HRESULT +MidiNetworkClient::Start( + HostName const& remoteHostName, + winrt::hstring const& remotePort +) +{ + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(remoteHostName.ToString().c_str(), "remote hostname"), + TraceLoggingWideString(remotePort.c_str(), "remote port")); + + auto conn = std::make_shared(); + RETURN_IF_NULL_ALLOC(conn); + + DatagramSocket socket; + m_socket = std::move(socket); + + m_socket.Control().QualityOfService(SocketQualityOfService::LowLatency); + m_socket.Control().DontFragment(true); + + auto messageReceivedHandler = winrt::Windows::Foundation::TypedEventHandler(this, &MidiNetworkClient::OnMessageReceived); + m_messageReceivedEventToken = m_socket.MessageReceived(messageReceivedHandler); + + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Event handler hooked up. About to connect socket", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(remoteHostName.ToString().c_str(), "remote hostname"), + TraceLoggingWideString(remotePort.c_str(), "remote port")); + + + //EndpointPair pair( + // nullptr, + // L"", + // remoteHostName, + // remotePort); + + // establish the remote connection + try + { + // this throws if the address can't be resolved or if + // the connect otherwise fails + + //m_socket.ConnectAsync(pair).get(); + m_socket.ConnectAsync(remoteHostName, remotePort); + } + catch (winrt::hresult_error err) + { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_ERROR, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"HRESULT Exception connecting to socket", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(remoteHostName.ToString().c_str(), "remote hostname"), + TraceLoggingWideString(remotePort.c_str(), "remote port"), + TraceLoggingHResult(err.code(), "hresult"), + TraceLoggingWideString(err.message().c_str(), "error message") + ); + + RETURN_IF_FAILED(E_NOTFOUND); + } + catch (...) + { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_ERROR, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Exception connecting to socket", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(remoteHostName.ToString().c_str(), "remote hostname"), + TraceLoggingWideString(remotePort.c_str(), "remote port")); + + RETURN_IF_FAILED(E_NOTFOUND); + } + + + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"About to initialize connection", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(remoteHostName.ToString().c_str(), "remote hostname"), + TraceLoggingWideString(remotePort.c_str(), "remote port")); + + RETURN_IF_FAILED(conn->Initialize( + MidiNetworkConnectionRole::ConnectionWindowsIsClient, + m_socket, + remoteHostName, + remotePort, + m_thisEndpointName, + m_thisProductInstanceId, + TransportState::Current().TransportSettings.RetransmitBufferMaxCommandPacketCount, + TransportState::Current().TransportSettings.ForwardErrorCorrectionMaxCommandPacketCount, + m_createUmpEndpointsOnly + )); + + TransportState::Current().AddNetworkConnection(remoteHostName, remotePort, conn); + + m_networkConnection = conn; + + // try to establish connection in-protocol + + // TODO: Need to wire up other security approaches here + // TODO: The invitation send should be in a loop so it's repeated if + // there's no response + RETURN_IF_FAILED(conn->SendInvitation()); + + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Invitation sent", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingWideString(remoteHostName.ToString().c_str(), "remote hostname"), + TraceLoggingWideString(remotePort.c_str(), "remote port")); + + + // todo: associate connection with the endpoint id + + + // todo: initiate discovery + + + return S_OK; +} + @@ -49,6 +262,13 @@ MidiNetworkClient::Shutdown() ); + if (m_socket) + { + m_socket.MessageReceived(m_messageReceivedEventToken); + m_socket.Close(); + m_socket = nullptr; + } + return S_OK; } diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkClient.h b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkClient.h index 3bb7d5aab..4b71c0699 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkClient.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkClient.h @@ -10,13 +10,29 @@ struct MidiNetworkClientDefinition { + bool Created{ false }; + winrt::hstring EntryIdentifier; // internal bool Enabled{ true }; + winrt::hstring Name; // the name of the endpoint before we do discovery + + winrt::hstring LocalEndpointName; // this is required by protocol + winrt::hstring LocalProductInstanceId; // also required by protocol + + + bool CreateMidi1Ports{ MIDI_NETWORK_MIDI_CREATE_MIDI1_PORTS_DEFAULT }; + + // protocol // MidiNetworkHostProtocol NetworkProtocol{ MidiNetworkHostProtocol::ProtocolDefault }; + // all match criteria follows + + winrt::hstring MatchId{}; + + }; @@ -24,12 +40,34 @@ struct MidiNetworkClientDefinition class MidiNetworkClient { public: - HRESULT Initialize(_In_ MidiNetworkClientDefinition& clientDefinition); + // will need some different versions of Initialize for the different ways of connecting + HRESULT Initialize( + _In_ MidiNetworkClientDefinition& clientDefinition, + _In_ enumeration::DeviceInformation advertisedHost + ); + + HRESULT Start( + _In_ winrt::Windows::Networking::HostName const& remoteHostName, + _In_ winrt::hstring const& remotePort + ); HRESULT Shutdown(); private: + bool m_createUmpEndpointsOnly{ true }; + + std::shared_ptr m_networkConnection{ nullptr }; + winrt::Windows::Networking::Sockets::DatagramSocket m_socket{ nullptr }; + + std::wstring m_thisEndpointName{ }; + std::wstring m_thisProductInstanceId{ }; + + + winrt::event_token m_messageReceivedEventToken; + void OnMessageReceived( + _In_ winrt::Windows::Networking::Sockets::DatagramSocket const& sender, + _In_ winrt::Windows::Networking::Sockets::DatagramSocketMessageReceivedEventArgs const& args); }; \ No newline at end of file diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp index 76141b576..996dd1649 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.cpp @@ -29,7 +29,8 @@ MidiNetworkConnection::Initialize( std::wstring const& thisEndpointName, std::wstring const& thisProductInstanceId, uint16_t const retransmitBufferMaxCommandPacketCount, - uint8_t const maxForwardErrorCorrectionCommandPacketCount + uint8_t const maxForwardErrorCorrectionCommandPacketCount, + bool createUmpEndpointsOnly ) { TraceLoggingWrite( @@ -48,6 +49,8 @@ MidiNetworkConnection::Initialize( m_remoteHostName = hostName; m_remotePort = port; + m_createUmpEndpointsOnly = createUmpEndpointsOnly; + m_thisEndpointName = thisEndpointName; m_thisProductInstanceId = thisProductInstanceId; @@ -66,7 +69,7 @@ MidiNetworkConnection::Initialize( RETURN_IF_FAILED(E_OUTOFMEMORY); } - ResetSequenceNumbers(); + RETURN_IF_FAILED(ResetSequenceNumbers()); // create the data writer m_writer = std::make_shared(); @@ -305,6 +308,132 @@ MidiNetworkConnection::HandleIncomingBye() return S_OK; } + +_Use_decl_annotations_ +HRESULT +MidiNetworkConnection::HandleIncomingInvitationReplyAccepted( + MidiNetworkCommandPacketHeader const& header, + std::wstring const& remoteHostUmpEndpointName, + std::wstring const& remoteHostProductInstanceId +) +{ + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + + if (m_role == MidiNetworkConnectionRole::ConnectionWindowsIsClient) + { + if (m_sessionActive) + { + // per protocol, if we've already accepted this, then just ignore it + return S_OK; + } + + // TODO: will we accept a session invitation from the specified hostname? + // TODO: Also need to check auth mechanism and follow instructions in 6.4 and send a Bye if not supported + + // todo: see if we already have a session active for this remote. If so, use it. + // otherwise, we need to spin up a new session + + std::wstring newDeviceInstanceId{ }; + std::wstring newEndpointDeviceInterfaceId{ }; + + if (TransportState::Current().GetEndpointManager()->IsInitialized()) + { + // Create the endpoint for Windows MIDI Services clients + HRESULT hr = S_OK; + + hr = TransportState::Current().GetEndpointManager()->CreateNewEndpoint( + MidiNetworkConnectionRole::ConnectionWindowsIsClient, + remoteHostUmpEndpointName, + remoteHostProductInstanceId, + m_remoteHostName, + m_remotePort, + m_createUmpEndpointsOnly, + newDeviceInstanceId, + newEndpointDeviceInterfaceId + ); + + if (SUCCEEDED(hr)) + { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Created MIDI endpoint", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + + m_sessionEndpointDeviceInterfaceId = internal::NormalizeEndpointInterfaceIdWStringCopy(newEndpointDeviceInterfaceId); + m_sessionDeviceInstanceId = internal::NormalizeDeviceInstanceIdWStringCopy(newDeviceInstanceId); + + // this is what the BiDi uses when it is created + RETURN_IF_FAILED(TransportState::Current().AssociateMidiEndpointWithConnection(m_sessionEndpointDeviceInterfaceId.c_str(), m_remoteHostName, m_remotePort.c_str())); + + // protocol negotiation needs to happen here, not in the endpoint creation + // because we need to wire up the connection first. Bit of a race. + + LOG_IF_FAILED(TransportState::Current().GetEndpointManager()->InitiateDiscoveryAndNegotiation(m_sessionEndpointDeviceInterfaceId)); + + m_sessionActive = true; + } + else + { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_ERROR, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Failed to create MIDI endpoint.", MIDI_TRACE_EVENT_MESSAGE_FIELD), + TraceLoggingHResult(hr, "hresult") + ); + + // let the other side know that we can't create the session + + auto lock = m_socketWriterLock.lock(); + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); + // TODO: Move string to resources for localization + RETURN_IF_FAILED(m_writer->WriteCommandBye(MidiNetworkCommandByeReason::CommandByeReasonCommon_Undefined, L"Error attempting to create endpoint. Bad data?")); + RETURN_IF_FAILED(m_writer->Send()); + + // exit out of here, and log while we're at it + RETURN_IF_FAILED(hr); + } + } + + } + else + { + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_ERROR, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"We are not in the client role, but received an invitation accept. Not normal.", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + + // we are a host, not a client, so NAK this per spec 6.4 + auto lock = m_socketWriterLock.lock(); + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); + + // TODO: Move string to resources for localization + RETURN_IF_FAILED(m_writer->WriteCommandNAK(header.HeaderWord, MidiNetworkCommandNAKReason::CommandNAKReason_CommandNotExpected, L"Unexpected invitation accept sent to host.")); + RETURN_IF_FAILED(m_writer->Send()); + } + + + + return S_OK; +} + _Use_decl_annotations_ HRESULT MidiNetworkConnection::HandleIncomingInvitation( @@ -366,6 +495,7 @@ MidiNetworkConnection::HandleIncomingInvitation( clientProductInstanceId, m_remoteHostName, m_remotePort, + m_createUmpEndpointsOnly, newDeviceInstanceId, newEndpointDeviceInterfaceId ); @@ -498,6 +628,23 @@ std::wstring MidiNetworkConnection::ReadUtf8String( return ws; } +// TODO: This needs some checking. If we call this too many times, we +// should close the connection. Also, if there's a retransmit request +// already in progress, we shouldn't be asking for more if there's any +// overlap at all. +HRESULT +MidiNetworkConnection::RequestMissingPackets() +{ + auto lock = m_socketWriterLock.lock(); + + // this requests all packets after the last one we received + + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); + RETURN_IF_FAILED(m_writer->WriteCommandRetransmitRequest(m_lastReceivedUmpCommandSequenceNumber + 1, 0)); + RETURN_IF_FAILED(m_writer->Send()); + + return S_OK; +} _Use_decl_annotations_ HRESULT @@ -562,9 +709,11 @@ MidiNetworkConnection::ProcessIncomingMessage( case CommandClientToHost_InvitationWithAuthentication: + // TODO break; case CommandClientToHost_InvitationWithUserAuthentication: + // TODO break; case CommandCommon_UmpData: @@ -578,51 +727,64 @@ MidiNetworkConnection::ProcessIncomingMessage( // TODO: a message can have zero MIDI words, but a valid sequence number. Need to handle that. - if (sequenceNumber <= m_lastReceivedUmpCommandSequenceNumber) { - // Skip all words in this command message - for (uint8_t i = 0; i < numberOfWords && reader.UnconsumedBufferLength() >= sizeof(uint32_t); i++) + if (numberOfWords > 0) + { + // Skip all words in this command message because it is FEC. + for (uint8_t i = 0; i < numberOfWords && reader.UnconsumedBufferLength() >= sizeof(uint32_t); i++) + { + reader.ReadUInt32(); + } + } + else { - reader.ReadUInt32(); + // empty UMP message. This is fine } - } else if (sequenceNumber == m_lastReceivedUmpCommandSequenceNumber + 1) { - // Process UMP data because this is the next sequence number + // Process UMP data because this is the next expected sequence number m_lastReceivedUmpCommandSequenceNumber = sequenceNumber; - for (uint8_t i = 0; i < numberOfWords; i++) + if (numberOfWords > 0) { - if (reader.UnconsumedBufferLength() >= sizeof(uint32_t)) + for (uint8_t i = 0; i < numberOfWords; i++) { - // add to our vector - words.push_back(reader.ReadUInt32()); - } - else - { - // bad / incomplete packet - - auto lock = m_socketWriterLock.lock(); - m_writer->WriteUdpPacketHeader(); - m_writer->WriteCommandNAK(commandHeader.HeaderWord, MidiNetworkCommandNAKReason::CommandNAKReason_CommandMalformed, L"Packet data incomplete"); - m_writer->Send(); - - // TODO: We should request a retransmit of this packet - - RETURN_IF_FAILED(E_FAIL); + if (reader.UnconsumedBufferLength() >= sizeof(uint32_t)) + { + // add to our vector + words.push_back(reader.ReadUInt32()); + } + else + { + // bad / incomplete packet + + auto lock = m_socketWriterLock.lock(); + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); + RETURN_IF_FAILED(m_writer->WriteCommandNAK(commandHeader.HeaderWord, MidiNetworkCommandNAKReason::CommandNAKReason_CommandMalformed, L"Packet data incomplete")); + RETURN_IF_FAILED(m_writer->Send()); + + // TODO: We should request a retransmit of this packet + + RETURN_IF_FAILED(E_FAIL); + } } } - + else + { + // empty UMP messages. That's fine. + } } - else + else if (sequenceNumber > m_lastReceivedUmpCommandSequenceNumber + 1) { - // TODO: We need to check this sequence number, and if there's any gap from the last received, - // drop this data and request a retransmit - // skipped data. Re-request missing packets + + // TODO: We should make sure we don't keep calling this for each + // UMP Data message within the same UDP packet. + + RETURN_IF_FAILED(RequestMissingPackets()); } if (words.size() > 0) @@ -653,6 +815,31 @@ MidiNetworkConnection::ProcessIncomingMessage( // TODO: handle session reset break; + case CommandHostToClient_InvitationReplyAccepted: + { + uint16_t endpointNameLengthInBytes = commandHeader.HeaderData.CommandSpecificData.AsBytes.Byte1 * sizeof(uint32_t); + uint16_t productInstanceIdLengthInBytes = commandHeader.HeaderData.CommandPayloadLength * sizeof(uint32_t) - endpointNameLengthInBytes; + + auto hostEndpointName = ReadUtf8String(reader, endpointNameLengthInBytes); + auto hostProductInstanceId = ReadUtf8String(reader, productInstanceIdLengthInBytes); + + LOG_IF_FAILED(HandleIncomingInvitationReplyAccepted(commandHeader, hostEndpointName, hostProductInstanceId)); + } + break; + + case CommandHostToClient_InvitationReplyPending: + // TODO: not sure we need to do anything with this + break; + + case CommandHostToClient_InvitationReplyAuthenticationRequired: + // TODO + break; + + case CommandHostToClient_InvitationReplyUserAuthenticationRequired: + // TODO + break; + + default: // TODO: unexpected command code. Send NAK. @@ -703,7 +890,6 @@ MidiNetworkConnection::HandleIncomingRetransmitRequest(uint16_t const startingSe RETURN_HR_IF_NULL(E_UNEXPECTED, m_writer); - // TODO: A packet count of 0x0000 means to send all the data we have starting at the startingSequenceNumber // find the starting sequence number in the circular buffer @@ -735,6 +921,8 @@ MidiNetworkConnection::HandleIncomingRetransmitRequest(uint16_t const startingSe auto endPacket = firstPacket + retransmitPacketCount; + // packet count of 0 means to send everything we've got + if (retransmitPacketCount == 0x0000) { endPacket = m_retransmitBuffer.end(); @@ -754,6 +942,30 @@ MidiNetworkConnection::HandleIncomingRetransmitRequest(uint16_t const startingSe } +HRESULT +MidiNetworkConnection::SendInvitation() +{ + TraceLoggingWrite( + MidiNetworkMidiTransportTelemetryProvider::Provider(), + MIDI_TRACE_EVENT_INFO, + TraceLoggingString(__FUNCTION__, MIDI_TRACE_EVENT_LOCATION_FIELD), + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this"), + TraceLoggingWideString(L"Enter", MIDI_TRACE_EVENT_MESSAGE_FIELD) + ); + + auto lock = m_socketWriterLock.lock(); + + // TODO: When we support security, need to update the capabilities + + RETURN_IF_FAILED(m_writer->WriteUdpPacketHeader()); + RETURN_IF_FAILED(m_writer->WriteCommandInvitation(MidiNetworkCommandInvitationCapabilities::Capabilities_None, m_thisEndpointName, m_thisProductInstanceId)); + RETURN_IF_FAILED(m_writer->Send()); + + return S_OK; +} + + _Use_decl_annotations_ HRESULT MidiNetworkConnection::SendMidiMessagesToNetwork(std::vector const& words) diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h index 225d77bed..18de33706 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkConnection.h @@ -32,7 +32,8 @@ class MidiNetworkConnection _In_ std::wstring const& thisEndpointName, _In_ std::wstring const& thisProductInstanceId, _In_ uint16_t const retransmitBufferMaxCommandPacketCount, - _In_ uint8_t const maxForwardErrorCorrectionCommandPacketCount + _In_ uint8_t const maxForwardErrorCorrectionCommandPacketCount, + _In_ bool createUmpEndpointsOnly ); HRESULT Shutdown(); @@ -48,6 +49,7 @@ class MidiNetworkConnection _In_ PVOID const bytes, _In_ UINT const byteCount); + HRESULT SendInvitation(); HRESULT ConnectMidiCallback( _In_ wil::com_ptr_nothrow callback @@ -64,6 +66,10 @@ class MidiNetworkConnection HRESULT ResetSequenceNumbers(); HRESULT EndActiveSession(); + HRESULT RequestMissingPackets(); + + bool m_createUmpEndpointsOnly{ true }; + uint32_t m_emptyUmpIterationCounter{ 0 }; // this will increment over time uint32_t m_emptyUmpIterationIntervalBeforeSending{ 10 }; // number of background thread iterations with no data before we send empty UMP packets. This changes over time. @@ -100,6 +106,11 @@ class MidiNetworkConnection _In_ std::wstring const& clientUmpEndpointName, _In_ std::wstring const& clientProductInstanceId); + HRESULT HandleIncomingInvitationReplyAccepted( + _In_ MidiNetworkCommandPacketHeader const& header, + _In_ std::wstring const& remoteHostUmpEndpointName, + _In_ std::wstring const& remoteHostProductInstanceId); + HRESULT HandleIncomingBye(); // this buffer holds the last n ping messages used to calculate latency diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp index b44cb304b..43e3f6ee7 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.cpp @@ -36,6 +36,8 @@ MidiNetworkHost::Initialize( m_started = false; + m_createUmpEndpointsOnly = !hostDefinition.CreateMidi1Ports; + m_hostEndpointName = hostDefinition.UmpEndpointName; m_hostProductInstanceId = hostDefinition.ProductInstanceId; @@ -89,7 +91,8 @@ MidiNetworkHost::CreateNetworkConnection(HostName const& remoteHostName, winrt:: m_hostEndpointName, m_hostProductInstanceId, TransportState::Current().TransportSettings.RetransmitBufferMaxCommandPacketCount, - TransportState::Current().TransportSettings.ForwardErrorCorrectionMaxCommandPacketCount + TransportState::Current().TransportSettings.ForwardErrorCorrectionMaxCommandPacketCount, + m_createUmpEndpointsOnly )); TransportState::Current().AddNetworkConnection(remoteHostName, remotePort, conn); @@ -152,9 +155,6 @@ MidiNetworkHost::Start() return S_OK; } -// header sized plus a command packet header -#define MINIMUM_VALID_UDP_PACKET_SIZE (sizeof(uint32_t) * 2) - // "message" here means UDP packet message, not a MIDI message _Use_decl_annotations_ void MidiNetworkHost::OnMessageReceived( diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h index 52f31cc9d..397396b28 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkHost.h @@ -50,6 +50,8 @@ struct MidiNetworkHostDefinition bool Enabled{ true }; bool Advertise{ true }; + bool CreateMidi1Ports{ MIDI_NETWORK_MIDI_CREATE_MIDI1_PORTS_DEFAULT }; + // connection rules MidiNetworkHostConnectionPolicy ConnectionPolicy{ MidiNetworkHostConnectionPolicy::PolicyAllowAllConnections }; @@ -84,6 +86,7 @@ class MidiNetworkHost private: bool m_started{ false }; + bool m_createUmpEndpointsOnly{ true }; std::wstring m_hostEndpointName{ }; std::wstring m_hostProductInstanceId{ }; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkMessages.h b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkMessages.h index 40e14e27d..82162e05c 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkMessages.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/MidiNetworkMessages.h @@ -80,6 +80,7 @@ enum MidiNetworkCommandRetransmitErrorReason : byte enum MidiNetworkCommandInvitationCapabilities : byte { + Capabilities_None = 0x00, Capabilities_ClientSupportsInvitationWithAuthentication = 0x01, Capabilities_ClientSupportsInvitationWithUserAuthentication = 0x02, }; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp index c8ebe6737..71ee3552e 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp +++ b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.cpp @@ -65,6 +65,18 @@ TransportState::AddPendingHostDefinition( return S_OK; } +_Use_decl_annotations_ +HRESULT +TransportState::AddClient( + std::shared_ptr client) +{ + RETURN_HR_IF_NULL(E_INVALIDARG, client); + + m_clients.push_back(client); + + return S_OK; +} + _Use_decl_annotations_ HRESULT diff --git a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h index 7638f4ecb..b02a6d479 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/TransportState.h @@ -69,6 +69,9 @@ class TransportState _In_ std::shared_ptr); std::vector> GetPendingHostDefinitions() { return m_pendingHostDefinitions; } + HRESULT AddClient( + _In_ std::shared_ptr); + std::vector> GetClients() { return m_clients; } HRESULT AddPendingClientDefinition( _In_ std::shared_ptr); @@ -116,8 +119,8 @@ class TransportState wil::com_ptr m_endpointManager; wil::com_ptr m_configurationManager; - // key is the host identifier std::vector> m_hosts{ }; + std::vector> m_clients{ }; std::vector> m_pendingHostDefinitions{ }; std::vector> m_pendingClientDefinitions{ }; diff --git a/src/api/Transport/UdpNetworkMidi2Transport/transport_defs.h b/src/api/Transport/UdpNetworkMidi2Transport/transport_defs.h index 581c2b5bb..03a90a4f8 100644 --- a/src/api/Transport/UdpNetworkMidi2Transport/transport_defs.h +++ b/src/api/Transport/UdpNetworkMidi2Transport/transport_defs.h @@ -53,9 +53,14 @@ #define MIDI_NETWORK_OUTBOUND_PING_INTERVAL_UPPER_BOUND 120000 #define MIDI_NETWORK_OUTBOUND_PING_INTERVAL_LOWER_BOUND 250 +#define MIDI_NETWORK_STARTING_OUTBOUND_UMP_QUEUE_CAPACITY 50 + +#define MIDI_NETWORK_MIDI_CREATE_MIDI1_PORTS_DEFAULT false + +// header sized plus a command packet header +#define MINIMUM_VALID_UDP_PACKET_SIZE (sizeof(uint32_t) * 2) -#define MIDI_NETWORK_STARTING_OUTBOUND_UMP_QUEUE_CAPACITY 50 // JSON keys. Can move to json_defs when in-box @@ -78,6 +83,8 @@ #define MIDI_CONFIG_JSON_NETWORK_MIDI_MDNS_ADVERTISE_KEY L"advertise" // boolean #define MIDI_CONFIG_JSON_NETWORK_MIDI_ENABLED_KEY L"enabled" // boolean +#define MIDI_CONFIG_JSON_NETWORK_MIDI_CREATE_MIDI1_PORTS_KEY L"createMidi1Ports" // boolean - set to true to enable creating WinMM/WinRT 1.0 ports + #define MIDI_CONFIG_JSON_NETWORK_MIDI_SERVICE_INSTANCE_NAME_KEY L"serviceInstanceName" // just the first part (before the . ) of the host instance name. Defaults to machine name @@ -104,13 +111,14 @@ #define MIDI_CONFIG_JSON_NETWORK_MIDI_PRODUCT_INSTANCE_ID_PROPERTY L"productInstanceId" -#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_ID L"id" // Windows ID like: DnsSd#kb7C5D0A_1._midi2._udp.local#0 -#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_SERVICE_INSTANCE L"serviceInstance" // Like kb7C5D0A_1 or bomeboxdin-8q6d2z-1 -#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_IP L"ipAddress" // Like 192.168.1.253 (port is also required) -#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_HOST_NAME L"hostName" // Like kissbox or BomeBox.local (port is also required) -#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_PORT L"port" // Like 5004 (ip address or host name is also required) -#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_UMP_ENDPOINT_NAME L"umpEndpointName" // Like UMP2TR @253 Port 1 or BomeBox -#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_UMP_ENDPOINT_PID L"umpProductInstanceId" // Like kb7C5D0A_1 or CC851C0080257A96 +#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_OBJECT_KEY L"match" // object which contains match criteria +#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_ID_KEY L"id" // Windows ID like: DnsSd#kb7C5D0A_1._midi2._udp.local#0 +#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_SERVICE_INSTANCE_KEY L"serviceInstance" // Like kb7C5D0A_1 or bomeboxdin-8q6d2z-1 +#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_IPV4_KEY L"ipv4Address" // Like 192.168.1.253 (port is also required) +#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_HOST_NAME_KEY L"hostName" // Like kissbox or BomeBox.local (port is also required) +#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_PORT_KEY L"port" // Like 5004 (ip address or host name is also required) +#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_UMP_ENDPOINT_NAME_KEY L"umpEndpointName" // Like UMP2TR @253 Port 1 or BomeBox +#define MIDI_CONFIG_JSON_NETWORK_MIDI_CLIENT_MATCH_UMP_ENDPOINT_PID_KEY L"umpProductInstanceId" // Like kb7C5D0A_1 or CC851C0080257A96 enum MidiNetworkConnectionRole diff --git a/src/app-sdk/mididiag/mididiag.vcxproj b/src/app-sdk/mididiag/mididiag.vcxproj index e8eca1595..4c420fce7 100644 --- a/src/app-sdk/mididiag/mididiag.vcxproj +++ b/src/app-sdk/mididiag/mididiag.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250106-1428 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 true true true diff --git a/src/app-sdk/mididiag/packages.config b/src/app-sdk/mididiag/packages.config index c93db8ddc..c1ef1d637 100644 --- a/src/app-sdk/mididiag/packages.config +++ b/src/app-sdk/mididiag/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj index a575d4c77..f2cb84fe5 100644 --- a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj +++ b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250106-1428 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 true true true diff --git a/src/app-sdk/midimdnsinfo/packages.config b/src/app-sdk/midimdnsinfo/packages.config index c93db8ddc..c1ef1d637 100644 --- a/src/app-sdk/midimdnsinfo/packages.config +++ b/src/app-sdk/midimdnsinfo/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj index 37a51809a..1265f1b80 100644 --- a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj +++ b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250106-1428 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 true true true diff --git a/src/app-sdk/midiusbinfo/packages.config b/src/app-sdk/midiusbinfo/packages.config index c93db8ddc..c1ef1d637 100644 --- a/src/app-sdk/midiusbinfo/packages.config +++ b/src/app-sdk/midiusbinfo/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/user-tools/midi-console/Midi/Commands/Service/ServiceRestartCommand.cs b/src/user-tools/midi-console/Midi/Commands/Service/ServiceRestartCommand.cs index 4639e9994..f5c009e7a 100644 --- a/src/user-tools/midi-console/Midi/Commands/Service/ServiceRestartCommand.cs +++ b/src/user-tools/midi-console/Midi/Commands/Service/ServiceRestartCommand.cs @@ -51,7 +51,7 @@ public override int Execute(CommandContext context, Settings settings) { if (controller.Status == ServiceControllerStatus.Running) { - AnsiConsole.Write("Stopping service.."); + AnsiConsole.Write("Attempting to stop service.."); MidiServiceHelper.StopServiceWithConsoleStatusUpdate(controller); } @@ -64,14 +64,14 @@ public override int Execute(CommandContext context, Settings settings) if (controller.Status == ServiceControllerStatus.Stopped) { - AnsiConsole.Write("Restarting service.."); + AnsiConsole.Write("Attempting to restart service.."); MidiServiceHelper.StartServiceWithConsoleStatusUpdate(controller); } if (controller.Status != ServiceControllerStatus.Running) { - AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError($"Unable to restart service. Service is not currently running. It may be locked in a tight loop due to an error.")); + AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError($"Unable to restart service. Service is not currently running.")); return (int)MidiConsoleReturnCode.ErrorGeneralFailure; } @@ -81,7 +81,7 @@ public override int Execute(CommandContext context, Settings settings) } catch (Exception) { - AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError($"Unable to restart service.")); + AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError($"Unable to restart service. It may be locked in a tight loop due to an error. Consider killing the midisrv.exe process, or rebooting.")); return (int)MidiConsoleReturnCode.ErrorGeneralFailure; } diff --git a/src/user-tools/midi-console/Midi/Commands/Service/ServiceStopCommand.cs b/src/user-tools/midi-console/Midi/Commands/Service/ServiceStopCommand.cs index b08644278..df18ce191 100644 --- a/src/user-tools/midi-console/Midi/Commands/Service/ServiceStopCommand.cs +++ b/src/user-tools/midi-console/Midi/Commands/Service/ServiceStopCommand.cs @@ -51,7 +51,7 @@ public override int Execute(CommandContext context, Settings settings) { if (controller.Status == ServiceControllerStatus.Running) { - AnsiConsole.Write("Stopping service.."); + AnsiConsole.Write("Attempting to stop service.."); MidiServiceHelper.StopServiceWithConsoleStatusUpdate(controller); AnsiConsole.WriteLine(); @@ -62,7 +62,7 @@ public override int Execute(CommandContext context, Settings settings) } else { - AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError($"Unable to stop service.")); + AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError($"Unable to stop service. It may be in-use by other apps, or it may be locked up. Consider killing the midisrv.exe process, or rebooting.")); return (int)MidiConsoleReturnCode.ErrorGeneralFailure; } } @@ -74,7 +74,7 @@ public override int Execute(CommandContext context, Settings settings) } catch (Exception) { - AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError($"Unable to stop service.")); + AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError($"Unable to stop service. It may be in-use by other apps, or it may be locked up. Consider killing the midisrv.exe process, or rebooting.")); return (int)MidiConsoleReturnCode.ErrorGeneralFailure; } } From 22c900945f29edef0d59eac89744e57dba6e5aa0 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Sun, 12 Jan 2025 17:05:40 -0500 Subject: [PATCH 6/6] NAMM Preview 1 --- build/staging/version/BundleInfo.wxi | 2 +- .../version/WindowsMidiServicesVersion.cs | 6 ++--- .../version/WindowsMidiServicesVersion.h | 6 ++--- .../basics/client-basics-cpp.vcxproj | 2 +- samples/cpp-winrt/basics/packages.config | 2 +- .../loopback-endpoints-cpp.vcxproj | 2 +- .../loopback-endpoints/packages.config | 2 +- samples/cpp-winrt/send-speed/packages.config | 2 +- .../send-speed/send-speed-cpp.vcxproj | 2 +- .../simple-app-to-app-midi/packages.config | 2 +- .../simple-app-to-app-cpp.vcxproj | 2 +- .../static-enum-endpoints/packages.config | 2 +- .../static-enum-endpoints-cpp.vcxproj | 2 +- .../cpp-winrt/watch-endpoints/packages.config | 2 +- .../watch-endpoints-cpp.vcxproj | 2 +- src/app-sdk/mididiag/mididiag.vcxproj | 2 +- src/app-sdk/mididiag/packages.config | 2 +- src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj | 2 +- src/app-sdk/midimdnsinfo/packages.config | 2 +- src/app-sdk/midiusbinfo/midiusbinfo.vcxproj | 2 +- src/app-sdk/midiusbinfo/packages.config | 2 +- .../settings-package/_SetupFiles.wxs | 2 -- .../Microsoft.Midi.Settings.csproj | 4 ++-- .../Models/AppState.cs | 23 ++++++++++++++++++- .../Views/DeviceDetailPage.xaml | 2 +- 25 files changed, 50 insertions(+), 31 deletions(-) diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index b0add7f75..cbcd36974 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,4 +1,4 @@ - + diff --git a/build/staging/version/WindowsMidiServicesVersion.cs b/build/staging/version/WindowsMidiServicesVersion.cs index 26cecb2cb..5d74cd150 100644 --- a/build/staging/version/WindowsMidiServicesVersion.cs +++ b/build/staging/version/WindowsMidiServicesVersion.cs @@ -6,12 +6,12 @@ public static class MidiBuildInformation { public const string Source = "GitHub Preview"; public const string Name = "Developer Preview 9"; - public const string BuildFullVersion = "1.0.2-preview-9.250111-2238"; + public const string BuildFullVersion = "1.0.2-preview-9.250112-1659"; public const string VersionMajor = "1"; public const string VersionMinor = "0"; public const string VersionRevision = "2"; - public const string VersionDateNumber = "250111"; - public const string VersionTimeNumber = "2238"; + public const string VersionDateNumber = "250112"; + public const string VersionTimeNumber = "1659"; } } diff --git a/build/staging/version/WindowsMidiServicesVersion.h b/build/staging/version/WindowsMidiServicesVersion.h index 6350d451e..66f3ed85f 100644 --- a/build/staging/version/WindowsMidiServicesVersion.h +++ b/build/staging/version/WindowsMidiServicesVersion.h @@ -5,12 +5,12 @@ #define WINDOWS_MIDI_SERVICES_BUILD_SOURCE L"GitHub Preview" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_NAME L"Developer Preview 9" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.2-preview-9.250111-2238" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_FULL L"1.0.2-preview-9.250112-1659" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MAJOR L"1" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_MINOR L"0" #define WINDOWS_MIDI_SERVICES_BUILD_VERSION_REVISION L"2" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250111" -#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"2238" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_DATE_NUMBER L"250112" +#define WINDOWS_MIDI_SERVICES_BUILD_VERSION_TIME_NUMBER L"1659" #endif diff --git a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj index 5e224714f..183d64bbf 100644 --- a/samples/cpp-winrt/basics/client-basics-cpp.vcxproj +++ b/samples/cpp-winrt/basics/client-basics-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250112-1659 true true false diff --git a/samples/cpp-winrt/basics/packages.config b/samples/cpp-winrt/basics/packages.config index 529fb3e5b..1c7a38d46 100644 --- a/samples/cpp-winrt/basics/packages.config +++ b/samples/cpp-winrt/basics/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj index e46daa425..c264f300c 100644 --- a/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/loopback-endpoints/loopback-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250112-1659 true true true diff --git a/samples/cpp-winrt/loopback-endpoints/packages.config b/samples/cpp-winrt/loopback-endpoints/packages.config index 529fb3e5b..1c7a38d46 100644 --- a/samples/cpp-winrt/loopback-endpoints/packages.config +++ b/samples/cpp-winrt/loopback-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/send-speed/packages.config b/samples/cpp-winrt/send-speed/packages.config index 529fb3e5b..1c7a38d46 100644 --- a/samples/cpp-winrt/send-speed/packages.config +++ b/samples/cpp-winrt/send-speed/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj index d68880793..2e71022df 100644 --- a/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj +++ b/samples/cpp-winrt/send-speed/send-speed-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250112-1659 true true true diff --git a/samples/cpp-winrt/simple-app-to-app-midi/packages.config b/samples/cpp-winrt/simple-app-to-app-midi/packages.config index 529fb3e5b..1c7a38d46 100644 --- a/samples/cpp-winrt/simple-app-to-app-midi/packages.config +++ b/samples/cpp-winrt/simple-app-to-app-midi/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj index 6c15c59c1..22df94d7a 100644 --- a/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj +++ b/samples/cpp-winrt/simple-app-to-app-midi/simple-app-to-app-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250112-1659 true true true diff --git a/samples/cpp-winrt/static-enum-endpoints/packages.config b/samples/cpp-winrt/static-enum-endpoints/packages.config index 529fb3e5b..1c7a38d46 100644 --- a/samples/cpp-winrt/static-enum-endpoints/packages.config +++ b/samples/cpp-winrt/static-enum-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj index f4df058fb..8a48d94ab 100644 --- a/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/static-enum-endpoints/static-enum-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250112-1659 true true true diff --git a/samples/cpp-winrt/watch-endpoints/packages.config b/samples/cpp-winrt/watch-endpoints/packages.config index 529fb3e5b..1c7a38d46 100644 --- a/samples/cpp-winrt/watch-endpoints/packages.config +++ b/samples/cpp-winrt/watch-endpoints/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj index 40cb25454..8b0664f0e 100644 --- a/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj +++ b/samples/cpp-winrt/watch-endpoints/watch-endpoints-cpp.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250112-1659 true true true diff --git a/src/app-sdk/mididiag/mididiag.vcxproj b/src/app-sdk/mididiag/mididiag.vcxproj index 4c420fce7..ee67036a8 100644 --- a/src/app-sdk/mididiag/mididiag.vcxproj +++ b/src/app-sdk/mididiag/mididiag.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250112-1659 true true true diff --git a/src/app-sdk/mididiag/packages.config b/src/app-sdk/mididiag/packages.config index c1ef1d637..2e6835610 100644 --- a/src/app-sdk/mididiag/packages.config +++ b/src/app-sdk/mididiag/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj index f2cb84fe5..6914369bd 100644 --- a/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj +++ b/src/app-sdk/midimdnsinfo/midimdnsinfo.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250112-1659 true true true diff --git a/src/app-sdk/midimdnsinfo/packages.config b/src/app-sdk/midimdnsinfo/packages.config index c1ef1d637..2e6835610 100644 --- a/src/app-sdk/midimdnsinfo/packages.config +++ b/src/app-sdk/midimdnsinfo/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj index 1265f1b80..52133a8da 100644 --- a/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj +++ b/src/app-sdk/midiusbinfo/midiusbinfo.vcxproj @@ -2,7 +2,7 @@ - Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250111-2238 + Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250112-1659 true true true diff --git a/src/app-sdk/midiusbinfo/packages.config b/src/app-sdk/midiusbinfo/packages.config index c1ef1d637..2e6835610 100644 --- a/src/app-sdk/midiusbinfo/packages.config +++ b/src/app-sdk/midiusbinfo/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/app-sdk/sdk-runtime-installer/settings-package/_SetupFiles.wxs b/src/app-sdk/sdk-runtime-installer/settings-package/_SetupFiles.wxs index 45a48b0d6..2b3f34ab8 100644 --- a/src/app-sdk/sdk-runtime-installer/settings-package/_SetupFiles.wxs +++ b/src/app-sdk/sdk-runtime-installer/settings-package/_SetupFiles.wxs @@ -43,8 +43,6 @@ - - diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Microsoft.Midi.Settings.csproj b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Microsoft.Midi.Settings.csproj index 5d76eecad..d893a4d42 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Microsoft.Midi.Settings.csproj +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Microsoft.Midi.Settings.csproj @@ -100,8 +100,8 @@ - - + + diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Models/AppState.cs b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Models/AppState.cs index d1c7cd10d..ecd4523a6 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Models/AppState.cs +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Models/AppState.cs @@ -20,7 +20,28 @@ public class AppState private AppState() { - MidiServicesInitializer.EnsureServiceAvailable(); + var initializer = Microsoft.Windows.Devices.Midi2.Initialization.MidiDesktopAppSdkInitializer.Create(); + + if (initializer == null) + { + // TODO: Failed + return; + + } + + // initialize SDK runtime + if (!initializer.InitializeSdkRuntime()) + { + // TODO: Failed + return; + } + + // start the service + if (!initializer.EnsureServiceAvailable()) + { + // TODO: Failed + return; + } StartDeviceWatcher(true); } diff --git a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml index 9fd97506b..f5c0ac49c 100644 --- a/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml +++ b/src/user-tools/midi-settings/Microsoft.Midi.Settings/Views/DeviceDetailPage.xaml @@ -261,7 +261,7 @@ Text="First Group" Style="{StaticResource SmallPropertyLabel}" />