diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index cf3fd622..d0a310c0 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -24,7 +24,7 @@ jobs: name: "Windows (MSVC)", os: windows-latest, generator: "", - cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0 -DCMAKE_GENERATOR_PLATFORM=version=10.0.22621.0", + cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0", test_target: "RUN_TESTS", tests: 1, examples: 1 @@ -33,7 +33,7 @@ jobs: name: "Windows (MSVC, arm64)", os: windows-11-arm, generator: "", - cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0 -DCMAKE_GENERATOR_PLATFORM=version=10.0.22621.0", + cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0", test_target: "RUN_TESTS", tests: 1, examples: 1 diff --git a/.github/workflows/build_python.yml b/.github/workflows/build_python.yml index 53fc7624..3d00b227 100644 --- a/.github/workflows/build_python.yml +++ b/.github/workflows/build_python.yml @@ -23,7 +23,7 @@ jobs: name: "Windows (MSVC)", os: windows-latest, generator: "", - cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0 -DCMAKE_GENERATOR_PLATFORM=version=10.0.22621.0", + cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0", environment: "" } - { diff --git a/cmake/libremidi.winmidi.cmake b/cmake/libremidi.winmidi.cmake index 0b6e74c4..7da5485f 100644 --- a/cmake/libremidi.winmidi.cmake +++ b/cmake/libremidi.winmidi.cmake @@ -5,7 +5,7 @@ endif() if(LIBREMIDI_DOWNLOAD_CPPWINRT) if(NOT EXISTS "${CMAKE_BINARY_DIR}/winmidi-headers.zip") file(DOWNLOAD - https://github.com/microsoft/MIDI/releases/download/dev-preview-9-namm-4/Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250121-1820.nupkg + https://github.com/microsoft/MIDI/releases/download/rc-1/Microsoft.Windows.Devices.Midi2.1.0.14-rc.1.209.nupkg "${CMAKE_BINARY_DIR}/winmidi-headers.zip" ) endif() @@ -52,6 +52,9 @@ if(CPPWINRT_TOOL) file( COPY "${CMAKE_BINARY_DIR}/winmidi-headers/build/native/include/winmidi/init" + "${CMAKE_BINARY_DIR}/winmidi-headers/build/native/include/winmidi/WindowsMidiServicesAppSdkComExtensions.h" + "${CMAKE_BINARY_DIR}/winmidi-headers/build/native/include/winmidi/WindowsMidiServicesAppSdkComExtensions_i.c" + "${CMAKE_BINARY_DIR}/winmidi-headers/build/native/include/winmidi/WindowsMidiServicesAppSdkComExtensions_p.c" DESTINATION "${CMAKE_BINARY_DIR}/cppwinrt-winmidi/" ) diff --git a/include/libremidi/backends/winmidi/helpers.hpp b/include/libremidi/backends/winmidi/helpers.hpp index 15ea3b07..f2150baa 100644 --- a/include/libremidi/backends/winmidi/helpers.hpp +++ b/include/libremidi/backends/winmidi/helpers.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -15,6 +16,19 @@ #include #include +#if __has_include() +#include + #define LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS 1 + +#define LIBREMIDI_DEFINE_GUID_CONSTEXPR(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ + static constexpr const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} + +namespace libremidi { + LIBREMIDI_DEFINE_GUID_CONSTEXPR(IID, IID_IMidiEndpointConnectionMessagesReceivedCallback,0x8087b303,0x0519,0x31d1,0x31,0xd1,0x00,0x00,0x00,0x00,0x00,0x10); + LIBREMIDI_DEFINE_GUID_CONSTEXPR(IID, IID_IMidiEndpointConnectionRaw,0x8087b303,0x0519,0x31d1,0x31,0xd1,0x00,0x00,0x00,0x00,0x00,0x20); +} +#endif + // MinGW support #if !defined(_MSC_VER) namespace Microsoft::Windows::Devices::Midi2::Initialization diff --git a/include/libremidi/backends/winmidi/midi_in.hpp b/include/libremidi/backends/winmidi/midi_in.hpp index 899c56de..2906f23b 100644 --- a/include/libremidi/backends/winmidi/midi_in.hpp +++ b/include/libremidi/backends/winmidi/midi_in.hpp @@ -19,6 +19,56 @@ class midi_in_impl final { } configuration; +#if LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS + struct raw_callback_type final : IMidiEndpointConnectionMessagesReceivedCallback + { + explicit raw_callback_type(midi_in_impl& self) + : self{self} + { + + } + + midi_in_impl& self; + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override + { + if (!ppvObject) + return E_POINTER; + + if (riid == __uuidof(IUnknown) || + riid == libremidi::IID_IMidiEndpointConnectionMessagesReceivedCallback) + { + *ppvObject = static_cast(this); + AddRef(); + return S_OK; + } + + *ppvObject = nullptr; + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE AddRef() override + { + return 1; + } + + ULONG STDMETHODCALLTYPE Release() override + { + return 1; + } + + HRESULT STDMETHODCALLTYPE MessagesReceived( + GUID sessionId, + GUID connectionId, + UINT64 timestamp, + UINT32 wordCount, + UINT32 *messages) override { + HRESULT res{}; + self.process_message(sessionId, connectionId, timestamp, wordCount, messages); + return res; + } + } raw_callback{*this}; +#endif + explicit midi_in_impl( libremidi::ump_input_configuration&& conf, winmidi::input_configuration&& apiconf) : configuration{std::move(conf), std::move(apiconf)} @@ -55,12 +105,20 @@ class midi_in_impl final // TODO use a MidiGroupEndpointListener for the filtering m_endpoint = m_session.CreateEndpointConnection(ep.EndpointDeviceId()); +#if !LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS 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); }); +#else + m_endpoint.as(libremidi::IID_IMidiEndpointConnectionRaw, m_raw_endpoint.put_void()); + + m_raw_endpoint->SetMessagesReceivedCallback( + &raw_callback + ); +#endif m_endpoint.Open(); @@ -96,9 +154,43 @@ class midi_in_impl final {ump_space, ump_space + b.Size()}, m_processing.timestamp(to_ns, 0)); } +#if LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS + void process_message( + const GUID& /* sessionId */, + const GUID& /* connectionId */, + UINT64 timestamp, + UINT32 wordCount, + UINT32 *ump) + { + static constexpr timestamp_backend_info timestamp_info{ + .has_absolute_timestamps = true, + .absolute_is_monotonic = false, + .has_samples = false, + }; + + if(wordCount == 0) + return; + + if (m_group_filter >= 0) + { + int group = cmidi2_ump_get_group(ump); + if (group != m_group_filter) + return; + } + + auto to_ns = [t = timestamp] { return t; }; + m_processing.on_bytes( + {ump, ump + wordCount}, m_processing.timestamp(to_ns, 0)); + } +#endif + stdx::error close_port() override { +#if !LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS m_endpoint.MessageReceived(m_revoke_token); +#else + m_raw_endpoint->RemoveMessagesReceivedCallback(); +#endif m_session.DisconnectEndpointConnection(m_endpoint.ConnectionId()); return stdx::error{}; } @@ -109,6 +201,9 @@ class midi_in_impl final MidiSession m_session; winrt::event_token m_revoke_token{}; winrt::Microsoft::Windows::Devices::Midi2::MidiEndpointConnection m_endpoint{nullptr}; +#if LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS + winrt::impl::com_ref m_raw_endpoint{}; +#endif midi2::input_state_machine m_processing{this->configuration}; int m_group_filter = -1; }; diff --git a/include/libremidi/backends/winmidi/midi_out.hpp b/include/libremidi/backends/winmidi/midi_out.hpp index 1937d248..577123e0 100644 --- a/include/libremidi/backends/winmidi/midi_out.hpp +++ b/include/libremidi/backends/winmidi/midi_out.hpp @@ -47,6 +47,9 @@ class midi_out_impl final return std::errc::address_not_available; m_endpoint = m_session.CreateEndpointConnection(ep.EndpointDeviceId()); +#if LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS + m_endpoint.as(libremidi::IID_IMidiEndpointConnectionRaw, m_raw_endpoint.put_void()); + #endif m_endpoint.Open(); return stdx::error{}; @@ -61,6 +64,8 @@ class midi_out_impl final stdx::error send_ump(const uint32_t* message, size_t size) override { auto write_func = [this](const uint32_t* ump, int64_t bytes) -> std::errc { + +#if !LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS MidiSendMessageResults ret{}; switch (bytes / 4) { @@ -80,9 +85,34 @@ class midi_out_impl final default: return std::errc::bad_message; } - if (ret != MidiSendMessageResults::Succeeded) return std::errc::bad_message; + #else + HRESULT ret{}; + switch (bytes / 4) + { + case 1: + assert( m_raw_endpoint->ValidateBufferHasOnlyCompleteUmps(1, (UINT32*)ump)); + ret = m_raw_endpoint->SendMidiMessagesRaw(0, 1, (UINT32*)ump); + break; + case 2: + assert( m_raw_endpoint->ValidateBufferHasOnlyCompleteUmps(2, (UINT32*)ump)); + ret = m_raw_endpoint->SendMidiMessagesRaw(0, 2, (UINT32*)ump); + break; + case 3: + assert( m_raw_endpoint->ValidateBufferHasOnlyCompleteUmps(3, (UINT32*)ump)); + ret = m_raw_endpoint->SendMidiMessagesRaw(0, 3, (UINT32*)ump); + break; + case 4: + assert( m_raw_endpoint->ValidateBufferHasOnlyCompleteUmps(4, (UINT32*)ump)); + ret = m_raw_endpoint->SendMidiMessagesRaw(0, 4, (UINT32*)ump); + break; + default: + return std::errc::bad_message; + } + #endif + if(ret < 0) + return std::errc::io_error; return std::errc{0}; }; @@ -92,6 +122,9 @@ class midi_out_impl final private: MidiSession m_session; winrt::Microsoft::Windows::Devices::Midi2::MidiEndpointConnection m_endpoint{nullptr}; +#if LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS + winrt::impl::com_ref m_raw_endpoint{}; +#endif }; } diff --git a/include/libremidi/backends/winuwp/helpers.hpp b/include/libremidi/backends/winuwp/helpers.hpp index 380e4586..e8e6253a 100644 --- a/include/libremidi/backends/winuwp/helpers.hpp +++ b/include/libremidi/backends/winuwp/helpers.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/include/libremidi/cmidi2.hpp b/include/libremidi/cmidi2.hpp index 7148df4f..42ad4636 100644 --- a/include/libremidi/cmidi2.hpp +++ b/include/libremidi/cmidi2.hpp @@ -6,8 +6,10 @@ #include #include +#if !defined(_MSC_VER) #pragma GCC system_header #pragma clang system_header +#endif #define CMIDI2_MIDI_2_0_RESERVED 0 #define CMIDI2_JR_TIMESTAMP_TICKS_PER_SECOND 31250 diff --git a/include/libremidi/error.hpp b/include/libremidi/error.hpp index cf1bcf60..f8c30d96 100644 --- a/include/libremidi/error.hpp +++ b/include/libremidi/error.hpp @@ -1,11 +1,15 @@ #pragma once #include +#if !defined(_MSC_VER) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #pragma GCC diagnostic ignored "-Wunused-parameter" +#endif #include +#if !defined(_MSC_VER) #pragma GCC diagnostic pop +#endif #include #include