@@ -244,6 +244,79 @@ struct midi_in_poll_wrapper {
244244 }
245245};
246246
247+ // Python-friendly wrappers for rawio configs.
248+ // std::span cannot cross the Python/C++ boundary, so we use std::vector
249+ // in the Python-facing callback signatures and convert internally.
250+ struct rawio_input_configuration_python {
251+ using py_receive_callback = std::function<void (std::vector<uint8_t >, int64_t )>;
252+ std::function<void (py_receive_callback)> set_receive_callback;
253+ std::function<void ()> stop_receive = [] {};
254+
255+ rawio_input_configuration to_cpp () const {
256+ rawio_input_configuration conf;
257+ if (set_receive_callback) {
258+ conf.set_receive_callback = [py_cb = set_receive_callback](
259+ rawio_input_configuration::receive_callback cpp_cb) {
260+ py_cb ([cpp_cb = std::move (cpp_cb)](std::vector<uint8_t > data, int64_t ts) {
261+ cpp_cb (std::span<const uint8_t >(data), ts);
262+ });
263+ };
264+ }
265+ conf.stop_receive = stop_receive;
266+ return conf;
267+ }
268+ };
269+
270+ struct rawio_output_configuration_python {
271+ std::function<stdx::error(std::vector<uint8_t >)> write_bytes
272+ = [](std::vector<uint8_t >) { return stdx::error{}; };
273+
274+ rawio_output_configuration to_cpp () const {
275+ rawio_output_configuration conf;
276+ if (write_bytes) {
277+ conf.write_bytes = [py_cb = write_bytes](std::span<const uint8_t > bytes) -> stdx::error {
278+ return py_cb ({bytes.begin (), bytes.end ()});
279+ };
280+ }
281+ return conf;
282+ }
283+ };
284+
285+ struct rawio_ump_input_configuration_python {
286+ using py_receive_callback = std::function<void (std::vector<uint32_t >, int64_t )>;
287+ std::function<void (py_receive_callback)> set_receive_callback;
288+ std::function<void ()> stop_receive = [] {};
289+
290+ rawio_ump_input_configuration to_cpp () const {
291+ rawio_ump_input_configuration conf;
292+ if (set_receive_callback) {
293+ conf.set_receive_callback = [py_cb = set_receive_callback](
294+ rawio_ump_input_configuration::receive_callback cpp_cb) {
295+ py_cb ([cpp_cb = std::move (cpp_cb)](std::vector<uint32_t > data, int64_t ts) {
296+ cpp_cb (std::span<const uint32_t >(data), ts);
297+ });
298+ };
299+ }
300+ conf.stop_receive = stop_receive;
301+ return conf;
302+ }
303+ };
304+
305+ struct rawio_ump_output_configuration_python {
306+ std::function<stdx::error(std::vector<uint32_t >)> write_ump
307+ = [](std::vector<uint32_t >) { return stdx::error{}; };
308+
309+ rawio_ump_output_configuration to_cpp () const {
310+ rawio_ump_output_configuration conf;
311+ if (write_ump) {
312+ conf.write_ump = [py_cb = write_ump](std::span<const uint32_t > words) -> stdx::error {
313+ return py_cb ({words.begin (), words.end ()});
314+ };
315+ }
316+ return conf;
317+ }
318+ };
319+
247320struct midi_out_poll_wrapper {
248321 moodycamel::ReaderWriterQueue<poll_queue::midi_out_msg> queue{};
249322 output_configuration_wrapper python_midi1_callbacks;
@@ -294,6 +367,7 @@ NB_MODULE(pylibremidi, m) {
294367
295368 namespace nb = nanobind;
296369 nb::class_<stdx::error>(m, " Error" )
370+ .def (nb::init<>())
297371 .def (" __bool__" , [](stdx::error e) { return e != stdx::error{}; })
298372 .def (" __str__" , [](stdx::error e) { return e.message ().data (); })
299373 .def (" __repr__" , [](stdx::error e) { return e.message ().data (); });
@@ -310,6 +384,7 @@ NB_MODULE(pylibremidi, m) {
310384 .value (" PIPEWIRE" , libremidi::API::PIPEWIRE)
311385 .value (" KEYBOARD" , libremidi::API::KEYBOARD)
312386 .value (" NETWORK" , libremidi::API::NETWORK)
387+ .value (" RAW_IO" , libremidi::API::RAW_IO)
313388
314389 .value (" ALSA_RAW_UMP" , libremidi::API::ALSA_RAW_UMP)
315390 .value (" ALSA_SEQ_UMP" , libremidi::API::ALSA_SEQ_UMP)
@@ -319,6 +394,7 @@ NB_MODULE(pylibremidi, m) {
319394 .value (" NETWORK_UMP" , libremidi::API::NETWORK_UMP)
320395 .value (" JACK_UMP" , libremidi::API::JACK_UMP)
321396 .value (" PIPEWIRE_UMP" , libremidi::API::PIPEWIRE_UMP)
397+ .value (" RAW_IO_UMP" , libremidi::API::RAW_IO_UMP)
322398
323399 .value (" DUMMY" , libremidi::API::DUMMY)
324400 .export_values ();
@@ -469,6 +545,15 @@ NB_MODULE(pylibremidi, m) {
469545 nb::class_<libremidi::winmm_input_configuration>(m, " WinmmInputConfiguration" ).def (nb::init<>());
470546 nb::class_<libremidi::winuwp_input_configuration>(m, " WinuwpInputConfiguration" ).def (nb::init<>());
471547
548+ nb::class_<libremidi::rawio_input_configuration_python>(m, " RawioInputConfiguration" )
549+ .def (nb::init<>())
550+ .def_rw (" set_receive_callback" , &libremidi::rawio_input_configuration_python::set_receive_callback)
551+ .def_rw (" stop_receive" , &libremidi::rawio_input_configuration_python::stop_receive);
552+ nb::class_<libremidi::rawio_ump_input_configuration_python>(m, " RawioUmpInputConfiguration" )
553+ .def (nb::init<>())
554+ .def_rw (" set_receive_callback" , &libremidi::rawio_ump_input_configuration_python::set_receive_callback)
555+ .def_rw (" stop_receive" , &libremidi::rawio_ump_input_configuration_python::stop_receive);
556+
472557 nb::class_<libremidi::alsa_raw_output_configuration>(m, " AlsaRawOutputConfiguration" ).def (nb::init<>());
473558 nb::class_<libremidi::alsa_raw_ump::output_configuration>(m, " AlsaRawUmpOutputConfiguration" ).def (nb::init<>());
474559 nb::class_<libremidi::alsa_seq::output_configuration>(m, " AlsaSeqOutputConfiguration" ).def (nb::init<>()).def_rw (" client_name" , &libremidi::alsa_seq::output_configuration::client_name);
@@ -486,6 +571,13 @@ NB_MODULE(pylibremidi, m) {
486571 nb::class_<libremidi::winmm_output_configuration>(m, " WinmmOutputConfiguration" ).def (nb::init<>());
487572 nb::class_<libremidi::winuwp_output_configuration>(m, " WinuwpOutputConfiguration" ).def (nb::init<>());
488573
574+ nb::class_<libremidi::rawio_output_configuration_python>(m, " RawioOutputConfiguration" )
575+ .def (nb::init<>())
576+ .def_rw (" write_bytes" , &libremidi::rawio_output_configuration_python::write_bytes);
577+ nb::class_<libremidi::rawio_ump_output_configuration_python>(m, " RawioUmpOutputConfiguration" )
578+ .def (nb::init<>())
579+ .def_rw (" write_ump" , &libremidi::rawio_ump_output_configuration_python::write_ump);
580+
489581 nb::class_<libremidi::alsa_raw_observer_configuration>(m, " AlsaRawObserverConfiguration" ).def (nb::init<>());
490582 nb::class_<libremidi::alsa_raw_ump::observer_configuration>(m, " AlsaRawUmpObserverConfiguration" ).def (nb::init<>());
491583 nb::class_<libremidi::alsa_seq::observer_configuration>(m, " AlsaSeqObserverConfiguration" ).def (nb::init<>()).def_rw (" client_name" , &libremidi::alsa_seq::observer_configuration::client_name);
@@ -511,6 +603,9 @@ NB_MODULE(pylibremidi, m) {
511603 nb::class_<libremidi::winmm_observer_configuration>(m, " WinmmObserverConfiguration" ).def (nb::init<>());
512604 nb::class_<libremidi::winuwp_observer_configuration>(m, " WinuwpObserverConfiguration" ).def (nb::init<>());
513605
606+ nb::class_<libremidi::rawio_observer_configuration>(m, " RawioObserverConfiguration" ).def (nb::init<>());
607+ nb::class_<libremidi::rawio_ump_observer_configuration>(m, " RawioUmpObserverConfiguration" ).def (nb::init<>());
608+
514609 nb::class_<libremidi::observer_poll_wrapper>(m, " Observer" )
515610 .def (nb::init<>())
516611 .def (nb::init<libremidi::observer_configuration>())
@@ -525,6 +620,12 @@ NB_MODULE(pylibremidi, m) {
525620 .def (nb::init<libremidi::input_configuration_wrapper, libremidi::API>())
526621 .def (nb::init<libremidi::ump_input_configuration_wrapper>())
527622 .def (nb::init<libremidi::ump_input_configuration_wrapper, libremidi::API>())
623+ .def (" __init__" , [](libremidi::midi_in_poll_wrapper *self, libremidi::input_configuration_wrapper conf, libremidi::rawio_input_configuration_python apiconf) {
624+ new (self) libremidi::midi_in_poll_wrapper (std::move (conf), libremidi::input_api_configuration{apiconf.to_cpp ()});
625+ })
626+ .def (" __init__" , [](libremidi::midi_in_poll_wrapper *self, libremidi::ump_input_configuration_wrapper conf, libremidi::rawio_ump_input_configuration_python apiconf) {
627+ new (self) libremidi::midi_in_poll_wrapper (std::move (conf), libremidi::input_api_configuration{apiconf.to_cpp ()});
628+ })
528629 .def (" get_current_api" , [](libremidi::midi_in_poll_wrapper &self) { return self.impl .get_current_api (); })
529630 .def (" open_port" , [](libremidi::midi_in_poll_wrapper &self, const libremidi::input_port &p) { return self.impl .open_port (p); })
530631 .def (" open_port" , [](libremidi::midi_in_poll_wrapper &self, const libremidi::input_port &p, std::string_view name) { return self.impl .open_port (p, name); })
@@ -542,6 +643,12 @@ NB_MODULE(pylibremidi, m) {
542643 .def (nb::init<>())
543644 .def (nb::init<libremidi::output_configuration_wrapper>())
544645 .def (nb::init<libremidi::output_configuration_wrapper, libremidi::API>())
646+ .def (" __init__" , [](libremidi::midi_out_poll_wrapper *self, libremidi::output_configuration_wrapper conf, libremidi::rawio_output_configuration_python apiconf) {
647+ new (self) libremidi::midi_out_poll_wrapper (std::move (conf), libremidi::output_api_configuration{apiconf.to_cpp ()});
648+ })
649+ .def (" __init__" , [](libremidi::midi_out_poll_wrapper *self, libremidi::output_configuration_wrapper conf, libremidi::rawio_ump_output_configuration_python apiconf) {
650+ new (self) libremidi::midi_out_poll_wrapper (std::move (conf), libremidi::output_api_configuration{apiconf.to_cpp ()});
651+ })
545652 .def (" get_current_api" , [](libremidi::midi_out_poll_wrapper &self) { return self.impl .get_current_api (); })
546653 .def (" open_port" , [](libremidi::midi_out_poll_wrapper &self, const libremidi::output_port &p) { return self.impl .open_port (p); })
547654 .def (" open_port" , [](libremidi::midi_out_poll_wrapper &self, const libremidi::output_port &p, std::string_view name) { return self.impl .open_port (p, name); })
@@ -555,23 +662,21 @@ NB_MODULE(pylibremidi, m) {
555662
556663 // clang-format off
557664 .def (" send_message" , [](libremidi::midi_out_poll_wrapper &self, const libremidi::message& m) { return self.impl .send_message (m); })
558- .def (" send_message" , [](libremidi::midi_out_poll_wrapper &self, const unsigned char * m, size_t size) { return self.impl .send_message (m, size); })
559665 .def (" send_message" , [](libremidi::midi_out_poll_wrapper &self, std::vector<unsigned char > m) { return self.impl .send_message (m); })
560666 .def (" send_message" , [](libremidi::midi_out_poll_wrapper &self, unsigned char b0) { return self.impl .send_message (b0); })
561667 .def (" send_message" , [](libremidi::midi_out_poll_wrapper &self, unsigned char b0, unsigned char b1) { return self.impl .send_message (b0, b1); })
562668 .def (" send_message" , [](libremidi::midi_out_poll_wrapper &self, unsigned char b0, unsigned char b1, unsigned char b2) { return self.impl .send_message (b0, b1, b2); })
563669
564- .def (" schedule_message" , [](libremidi::midi_out_poll_wrapper &self, int64_t t, const unsigned char * m, size_t size ) { return self.impl .schedule_message (t, m, size); })
670+ .def (" schedule_message" , [](libremidi::midi_out_poll_wrapper &self, int64_t t, std::vector< unsigned char > m ) { return self.impl .schedule_message (t, m. data (), m. size () ); })
565671
566672 .def (" send_ump" , [](libremidi::midi_out_poll_wrapper &self, const libremidi::ump& m) { return self.impl .send_ump (m); })
567- .def (" send_ump" , [](libremidi::midi_out_poll_wrapper &self, const uint32_t * ump, size_t size) { return self.impl .send_ump (ump, size); })
568673 .def (" send_ump" , [](libremidi::midi_out_poll_wrapper &self, std::vector<uint32_t > m) { return self.impl .send_ump (m); })
569674 .def (" send_ump" , [](libremidi::midi_out_poll_wrapper &self, uint32_t u0) { return self.impl .send_ump (u0); })
570675 .def (" send_ump" , [](libremidi::midi_out_poll_wrapper &self, uint32_t u0, uint32_t u1) { return self.impl .send_ump (u0, u1); })
571676 .def (" send_ump" , [](libremidi::midi_out_poll_wrapper &self, uint32_t u0, uint32_t u1, uint32_t u2) { return self.impl .send_ump (u0, u1, u2); })
572677 .def (" send_ump" , [](libremidi::midi_out_poll_wrapper &self, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) { return self.impl .send_ump (u0, u1, u2, u3); })
573678
574- .def (" schedule_message " , [](libremidi::midi_out_poll_wrapper &self, int64_t t, const uint32_t * m, size_t size ) { return self.impl .schedule_ump (t, m, size); })
679+ .def (" schedule_ump " , [](libremidi::midi_out_poll_wrapper &self, int64_t t, std::vector< uint32_t > m ) { return self.impl .schedule_ump (t, m. data (), m. size () ); })
575680 // clang-format on
576681
577682 .def (" poll" , &libremidi::midi_out_poll_wrapper::poll);
0 commit comments