Skip to content

Commit c3f7314

Browse files
committed
winmidi: implement the raw API, try to fix SDK issues
1 parent 2296f88 commit c3f7314

6 files changed

Lines changed: 146 additions & 5 deletions

File tree

.github/workflows/build_cmake.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
name: "Windows (MSVC)",
2525
os: windows-latest,
2626
generator: "",
27-
cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0 -DCMAKE_GENERATOR_PLATFORM=version=10.0.22621.0",
27+
cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0",
2828
test_target: "RUN_TESTS",
2929
tests: 1,
3030
examples: 1
@@ -33,7 +33,7 @@ jobs:
3333
name: "Windows (MSVC, arm64)",
3434
os: windows-11-arm,
3535
generator: "",
36-
cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0 -DCMAKE_GENERATOR_PLATFORM=version=10.0.22621.0",
36+
cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0",
3737
test_target: "RUN_TESTS",
3838
tests: 1,
3939
examples: 1

.github/workflows/build_python.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
name: "Windows (MSVC)",
2424
os: windows-latest,
2525
generator: "",
26-
cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0 -DCMAKE_GENERATOR_PLATFORM=version=10.0.22621.0",
26+
cmakeflags: "-DLIBREMIDI_NO_WINUWP=0 -DBOOST_ROOT=$PWD/boost_1_86_0",
2727
environment: ""
2828
}
2929
- {

cmake/libremidi.winmidi.cmake

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ endif()
55
if(LIBREMIDI_DOWNLOAD_CPPWINRT)
66
if(NOT EXISTS "${CMAKE_BINARY_DIR}/winmidi-headers.zip")
77
file(DOWNLOAD
8-
https://github.com/microsoft/MIDI/releases/download/dev-preview-9-namm-4/Microsoft.Windows.Devices.Midi2.1.0.2-preview-9.250121-1820.nupkg
8+
https://github.com/microsoft/MIDI/releases/download/rc-1/Microsoft.Windows.Devices.Midi2.1.0.14-rc.1.209.nupkg
99
"${CMAKE_BINARY_DIR}/winmidi-headers.zip"
1010
)
1111
endif()
@@ -52,6 +52,9 @@ if(CPPWINRT_TOOL)
5252
file(
5353
COPY
5454
"${CMAKE_BINARY_DIR}/winmidi-headers/build/native/include/winmidi/init"
55+
"${CMAKE_BINARY_DIR}/winmidi-headers/build/native/include/winmidi/WindowsMidiServicesAppSdkComExtensions.h"
56+
"${CMAKE_BINARY_DIR}/winmidi-headers/build/native/include/winmidi/WindowsMidiServicesAppSdkComExtensions_i.c"
57+
"${CMAKE_BINARY_DIR}/winmidi-headers/build/native/include/winmidi/WindowsMidiServicesAppSdkComExtensions_p.c"
5558
DESTINATION
5659
"${CMAKE_BINARY_DIR}/cppwinrt-winmidi/"
5760
)
@@ -75,3 +78,7 @@ target_include_directories(libremidi SYSTEM ${_public}
7578
)
7679
target_compile_definitions(libremidi ${_public} LIBREMIDI_WINMIDI)
7780
target_link_libraries(libremidi ${_public} RuntimeObject windowsapp)
81+
target_sources(libremidi PRIVATE
82+
"${CMAKE_BINARY_DIR}/cppwinrt-winmidi/WindowsMidiServicesAppSdkComExtensions_i.c"
83+
"${CMAKE_BINARY_DIR}/cppwinrt-winmidi/WindowsMidiServicesAppSdkComExtensions_p.c"
84+
)

include/libremidi/backends/winmidi/helpers.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,19 @@
88
#include <cctype>
99
#include <string>
1010
#include <guiddef.h>
11+
#include <unknwn.h>
1112

1213
#include <winrt/Windows.Foundation.h>
1314
#include <winrt/Windows.Foundation.Collections.h>
1415
#include <winrt/Windows.Devices.Enumeration.h>
1516
#include <winrt/Microsoft.Windows.Devices.Midi2.h>
1617
#include <libremidi/cmidi2.hpp>
1718

19+
#if __has_include(<WindowsMidiServicesAppSdkComExtensions.h>)
20+
#include <WindowsMidiServicesAppSdkComExtensions.h>
21+
#define LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS 1
22+
#endif
23+
1824
// MinGW support
1925
#if !defined(_MSC_VER)
2026
namespace Microsoft::Windows::Devices::Midi2::Initialization

include/libremidi/backends/winmidi/midi_in.hpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,56 @@ class midi_in_impl final
1919
{
2020
} configuration;
2121

22+
#if LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS
23+
struct raw_callback_type final : IMidiEndpointConnectionMessagesReceivedCallback
24+
{
25+
explicit raw_callback_type(midi_in_impl& self)
26+
: self{self}
27+
{
28+
29+
}
30+
31+
midi_in_impl& self;
32+
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override
33+
{
34+
if (!ppvObject)
35+
return E_POINTER;
36+
37+
if (riid == __uuidof(IUnknown) ||
38+
riid == __uuidof(IMidiEndpointConnectionMessagesReceivedCallback))
39+
{
40+
*ppvObject = static_cast<IMidiEndpointConnectionMessagesReceivedCallback*>(this);
41+
AddRef();
42+
return S_OK;
43+
}
44+
45+
*ppvObject = nullptr;
46+
return E_NOINTERFACE;
47+
}
48+
49+
ULONG STDMETHODCALLTYPE AddRef() override
50+
{
51+
return 1;
52+
}
53+
54+
ULONG STDMETHODCALLTYPE Release() override
55+
{
56+
return 1;
57+
}
58+
59+
HRESULT STDMETHODCALLTYPE MessagesReceived(
60+
GUID sessionId,
61+
GUID connectionId,
62+
UINT64 timestamp,
63+
UINT32 wordCount,
64+
UINT32 *messages) override {
65+
HRESULT res{};
66+
self.process_message(sessionId, connectionId, timestamp, wordCount, messages);
67+
return res;
68+
}
69+
} raw_callback{*this};
70+
#endif
71+
2272
explicit midi_in_impl(
2373
libremidi::ump_input_configuration&& conf, winmidi::input_configuration&& apiconf)
2474
: configuration{std::move(conf), std::move(apiconf)}
@@ -55,12 +105,20 @@ class midi_in_impl final
55105
// TODO use a MidiGroupEndpointListener for the filtering
56106
m_endpoint = m_session.CreateEndpointConnection(ep.EndpointDeviceId());
57107

108+
#if !LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS
58109
m_revoke_token = m_endpoint.MessageReceived(
59110
[this](
60111
const winrt::Microsoft::Windows::Devices::Midi2::IMidiMessageReceivedEventSource&,
61112
const winrt::Microsoft::Windows::Devices::Midi2::MidiMessageReceivedEventArgs& args) {
62113
process_message(args);
63114
});
115+
#else
116+
m_raw_endpoint = m_endpoint.as<IMidiEndpointConnectionRaw>();
117+
118+
m_raw_endpoint->SetMessagesReceivedCallback(
119+
&raw_callback
120+
);
121+
#endif
64122

65123
m_endpoint.Open();
66124

@@ -96,9 +154,43 @@ class midi_in_impl final
96154
{ump_space, ump_space + b.Size()}, m_processing.timestamp<timestamp_info>(to_ns, 0));
97155
}
98156

157+
#if LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS
158+
void process_message(
159+
const GUID& /* sessionId */,
160+
const GUID& /* connectionId */,
161+
UINT64 timestamp,
162+
UINT32 wordCount,
163+
UINT32 *ump)
164+
{
165+
static constexpr timestamp_backend_info timestamp_info{
166+
.has_absolute_timestamps = true,
167+
.absolute_is_monotonic = false,
168+
.has_samples = false,
169+
};
170+
171+
if(wordCount == 0)
172+
return;
173+
174+
if (m_group_filter >= 0)
175+
{
176+
int group = cmidi2_ump_get_group(ump);
177+
if (group != m_group_filter)
178+
return;
179+
}
180+
181+
auto to_ns = [t = timestamp] { return t; };
182+
m_processing.on_bytes(
183+
{ump, ump + wordCount}, m_processing.timestamp<timestamp_info>(to_ns, 0));
184+
}
185+
#endif
186+
99187
stdx::error close_port() override
100188
{
189+
#if !LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS
101190
m_endpoint.MessageReceived(m_revoke_token);
191+
#else
192+
m_raw_endpoint->RemoveMessagesReceivedCallback();
193+
#endif
102194
m_session.DisconnectEndpointConnection(m_endpoint.ConnectionId());
103195
return stdx::error{};
104196
}
@@ -109,6 +201,9 @@ class midi_in_impl final
109201
MidiSession m_session;
110202
winrt::event_token m_revoke_token{};
111203
winrt::Microsoft::Windows::Devices::Midi2::MidiEndpointConnection m_endpoint{nullptr};
204+
#if LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS
205+
winrt::impl::com_ref<IMidiEndpointConnectionRaw> m_raw_endpoint{};
206+
#endif
112207
midi2::input_state_machine m_processing{this->configuration};
113208
int m_group_filter = -1;
114209
};

include/libremidi/backends/winmidi/midi_out.hpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ class midi_out_impl final
4747
return std::errc::address_not_available;
4848

4949
m_endpoint = m_session.CreateEndpointConnection(ep.EndpointDeviceId());
50+
#if LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS
51+
m_raw_endpoint = m_endpoint.as<IMidiEndpointConnectionRaw>();
52+
#endif
5053
m_endpoint.Open();
5154

5255
return stdx::error{};
@@ -61,6 +64,8 @@ class midi_out_impl final
6164
stdx::error send_ump(const uint32_t* message, size_t size) override
6265
{
6366
auto write_func = [this](const uint32_t* ump, int64_t bytes) -> std::errc {
67+
68+
#if !LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS
6469
MidiSendMessageResults ret{};
6570
switch (bytes / 4)
6671
{
@@ -80,9 +85,34 @@ class midi_out_impl final
8085
default:
8186
return std::errc::bad_message;
8287
}
83-
8488
if (ret != MidiSendMessageResults::Succeeded)
8589
return std::errc::bad_message;
90+
#else
91+
HRESULT ret{};
92+
switch (bytes / 4)
93+
{
94+
case 1:
95+
assert( m_raw_endpoint->ValidateBufferHasOnlyCompleteUmps(1, (UINT32*)ump));
96+
ret = m_raw_endpoint->SendMidiMessagesRaw(0, 1, (UINT32*)ump);
97+
break;
98+
case 2:
99+
assert( m_raw_endpoint->ValidateBufferHasOnlyCompleteUmps(2, (UINT32*)ump));
100+
ret = m_raw_endpoint->SendMidiMessagesRaw(0, 2, (UINT32*)ump);
101+
break;
102+
case 3:
103+
assert( m_raw_endpoint->ValidateBufferHasOnlyCompleteUmps(3, (UINT32*)ump));
104+
ret = m_raw_endpoint->SendMidiMessagesRaw(0, 3, (UINT32*)ump);
105+
break;
106+
case 4:
107+
assert( m_raw_endpoint->ValidateBufferHasOnlyCompleteUmps(4, (UINT32*)ump));
108+
ret = m_raw_endpoint->SendMidiMessagesRaw(0, 4, (UINT32*)ump);
109+
break;
110+
default:
111+
return std::errc::bad_message;
112+
}
113+
#endif
114+
if(ret < 0)
115+
return std::errc::io_error;
86116
return std::errc{0};
87117
};
88118

@@ -92,6 +122,9 @@ class midi_out_impl final
92122
private:
93123
MidiSession m_session;
94124
winrt::Microsoft::Windows::Devices::Midi2::MidiEndpointConnection m_endpoint{nullptr};
125+
#if LIBREMIDI_WINMIDI_HAS_COM_EXTENSIONS
126+
winrt::impl::com_ref<IMidiEndpointConnectionRaw> m_raw_endpoint{};
127+
#endif
95128
};
96129

97130
}

0 commit comments

Comments
 (0)