#pragma once #include #include #include #include #include namespace libremidi::winmidi { class midi_in_impl final : public midi2::in_api , public error_handler , public winmidi_shared_data { public: struct : libremidi::ump_input_configuration , winmidi::input_configuration { } configuration; explicit midi_in_impl( libremidi::ump_input_configuration&& conf, winmidi::input_configuration&& apiconf) : configuration{std::move(conf), std::move(apiconf)} , m_session{ configuration.context ? *configuration.context : MidiSession::Create(to_hstring(configuration.client_name))} { this->client_open_ = stdx::error{}; } ~midi_in_impl() override { close_port(); this->client_open_ = std::errc::not_connected; } libremidi::API get_current_api() const noexcept override { return libremidi::API::WINDOWS_MIDI_SERVICES; } stdx::error open_port(const input_port& port, std::string_view) override { auto device_id = std::get_if(&port.device); if (!device_id) return std::errc::invalid_argument; auto [ep, gp] = get_port(*device_id, port.port); if (!ep || !gp) return std::errc::address_not_available; m_group_filter = port.port - 1; // TODO use a MidiGroupEndpointListener for the filtering m_endpoint = m_session.CreateEndpointConnection(ep.EndpointDeviceId()); m_revoke_token = m_endpoint.MessageReceived( [this]( const winrt::Microsoft::Windows::Devices::Midi2::IMidiMessageReceivedEventSource&, const winrt::Microsoft::Windows::Devices::Midi2::MidiMessageReceivedEventArgs& args) { process_message(args); }); m_endpoint.Open(); return stdx::error{}; } void process_message(const winrt::Microsoft::Windows::Devices::Midi2::MidiMessageReceivedEventArgs& msg) { static constexpr timestamp_backend_info timestamp_info{ .has_absolute_timestamps = true, .absolute_is_monotonic = false, .has_samples = false, }; const auto& ump = msg.GetMessagePacket(); auto pk = msg.PeekFirstWord(); if (m_group_filter >= 0) { int group = cmidi2_ump_get_group(&pk); if (group != m_group_filter) return; } const auto& b = ump.GetAllWords(); uint32_t ump_space[64]; array_view ref{ump_space}; b.GetMany(0, ref); auto to_ns = [t = ump.Timestamp()] { return t; }; m_processing.on_bytes( {ump_space, ump_space + b.Size()}, m_processing.timestamp(to_ns, 0)); } stdx::error close_port() override { m_endpoint.MessageReceived(m_revoke_token); m_session.DisconnectEndpointConnection(m_endpoint.ConnectionId()); return stdx::error{}; } virtual timestamp absolute_timestamp() const noexcept override { return {}; } private: MidiSession m_session; winrt::event_token m_revoke_token{}; winrt::Microsoft::Windows::Devices::Midi2::MidiEndpointConnection m_endpoint{nullptr}; midi2::input_state_machine m_processing{this->configuration}; int m_group_filter = -1; }; }