diff --git a/cmake/libremidi.net.cmake b/cmake/libremidi.net.cmake index 65561cc..2e4de1c 100644 --- a/cmake/libremidi.net.cmake +++ b/cmake/libremidi.net.cmake @@ -2,6 +2,10 @@ if(NOT TARGET Boost::boost) return() endif() +if(NOT LIBREMIDI_HAS_STD_STOP_TOKEN) + return() +endif() + message(STATUS "libremidi: Network support using Boost.ASIO") set(LIBREMIDI_HAS_BOOST_ASIO 1) set(LIBREMIDI_HAS_NETWORK 1) diff --git a/examples/network.cpp b/examples/network.cpp index d901a41..82ccf0c 100644 --- a/examples/network.cpp +++ b/examples/network.cpp @@ -77,6 +77,41 @@ int main(int argc, const char** argv) if (auto err = midiout.send_ump(ump); err != stdx::error{}) err.throw_exception(); - ctx.run(); + ctx.run_one(); + ctx.run_one(); + } + + // MIDI 1 no context + { + libremidi::net::dgram_input_configuration in_apiconf{ + .client_name = "libremidi", + .protocol = libremidi::net::protocol::OSC_MIDI, + .accept = "0.0.0.0", + .port = 5677}; + + libremidi::midi_in midiin{ + {.on_message = [](const libremidi::message& m) { std::cerr << m << std::endl; }}, + in_apiconf}; + midiin.open_virtual_port("/midi"); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + libremidi::net::dgram_output_configuration out_apiconf{ + .client_name = "libremidi", + .protocol = libremidi::net::protocol::OSC_MIDI, + .host = "127.0.0.1", + .port = 5677, + .broadcast = false, + .io_context = &ctx}; + + libremidi::midi_out midiout{{}, out_apiconf}; + + if (auto err = midiout.open_virtual_port("/midi"); err != stdx::error{}) + err.throw_exception(); + + if (auto err = midiout.send_message(144, 127, 64); err != stdx::error{}) + err.throw_exception(); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } diff --git a/include/libremidi/backends/net/helpers.hpp b/include/libremidi/backends/net/helpers.hpp index 067abc1..54d4879 100644 --- a/include/libremidi/backends/net/helpers.hpp +++ b/include/libremidi/backends/net/helpers.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include namespace stdx @@ -59,17 +59,14 @@ struct optionally_owned ~optionally_owned() { - if (ownership == owned) - std::destroy_at(storage.object); + if (is_owned()) + std::destroy_at(reinterpret_cast(storage.object)); } - T& get() noexcept - { - return *((ownership == owned) ? reinterpret_cast(&storage.object) : storage.ref); - } + T& get() noexcept { return *(is_owned() ? reinterpret_cast(&storage.object) : storage.ref); } const T& get() const noexcept { - return *((ownership == owned) ? reinterpret_cast(&storage.object) : storage.ref); + return *(is_owned() ? reinterpret_cast(&storage.object) : storage.ref); } optionally_owned(const optionally_owned&) = delete; @@ -77,6 +74,8 @@ struct optionally_owned optionally_owned& operator=(const optionally_owned&) = delete; optionally_owned& operator=(optionally_owned&&) noexcept = delete; + bool is_owned() const noexcept { return ownership == owned; } + private: union { diff --git a/include/libremidi/backends/net/midi_in.hpp b/include/libremidi/backends/net/midi_in.hpp index 57d833d..abf5c67 100644 --- a/include/libremidi/backends/net/midi_in.hpp +++ b/include/libremidi/backends/net/midi_in.hpp @@ -6,7 +6,6 @@ namespace libremidi { - template struct osc_parser { @@ -150,8 +149,8 @@ class midi_in final explicit midi_in(input_configuration&& conf, dgram_input_configuration&& apiconf) : configuration{std::move(conf), std::move(apiconf)} - , ctx{configuration.io_context} - , m_socket{ctx.get()} + , m_ctx{configuration.io_context} + , m_socket{m_ctx.get()} { m_socket.open(boost::asio::ip::udp::v4()); m_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true)); @@ -171,7 +170,7 @@ class midi_in final client_open_ = stdx::error{}; } - ~midi_in() override { } + ~midi_in() override { close_port(); } libremidi::API get_current_api() const noexcept override { return libremidi::API::NETWORK; } @@ -186,6 +185,14 @@ class midi_in final receive(); + if (m_ctx.is_owned()) + { + m_thread = std::jthread{[this, &ctx = m_ctx.get()] { + auto wg = boost::asio::make_work_guard(m_ctx); + ctx.run(); + }}; + } + return stdx::error{}; } @@ -247,10 +254,11 @@ class midi_in final midi1::input_state_machine m_processing{this->configuration}; - libremidi::optionally_owned ctx; + libremidi::optionally_owned m_ctx; boost::asio::ip::udp::endpoint m_endpoint; boost::asio::ip::udp::socket m_socket; - bool m_owned_context{}; + + std::jthread m_thread; std::string m_portname; alignas(16) unsigned char m_data[65535]; @@ -314,8 +322,8 @@ class midi_in final explicit midi_in(ump_input_configuration&& conf, dgram_input_configuration&& apiconf) : configuration{std::move(conf), std::move(apiconf)} - , ctx{configuration.io_context} - , m_socket{ctx.get()} + , m_ctx{configuration.io_context} + , m_socket{m_ctx.get()} { m_socket.open(boost::asio::ip::udp::v4()); m_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true)); @@ -335,7 +343,7 @@ class midi_in final client_open_ = stdx::error{}; } - ~midi_in() override { } + ~midi_in() override { close_port(); } libremidi::API get_current_api() const noexcept override { return libremidi::API::NETWORK_UMP; } @@ -350,6 +358,13 @@ class midi_in final receive(); + if (m_ctx.is_owned()) + { + m_thread = std::jthread{[this, &ctx = m_ctx.get()] { + auto wg = boost::asio::make_work_guard(m_ctx); + ctx.run(); + }}; + } return stdx::error{}; } @@ -371,6 +386,9 @@ class midi_in final stdx::error close_port() override { // FIXME async close + if (m_ctx.is_owned()) + m_ctx.get().stop(); + if (m_socket.is_open()) m_socket.close(); return {}; @@ -410,10 +428,11 @@ class midi_in final midi2::input_state_machine m_processing{this->configuration}; - libremidi::optionally_owned ctx; + libremidi::optionally_owned m_ctx; boost::asio::ip::udp::endpoint m_endpoint; boost::asio::ip::udp::socket m_socket; - bool m_owned_context{}; + + std::jthread m_thread; std::string m_portname; alignas(16) unsigned char m_data[65535]; diff --git a/include/libremidi/backends/net/midi_out.hpp b/include/libremidi/backends/net/midi_out.hpp index e6e3fa2..c77f2f1 100644 --- a/include/libremidi/backends/net/midi_out.hpp +++ b/include/libremidi/backends/net/midi_out.hpp @@ -77,8 +77,8 @@ class midi_out final midi_out(output_configuration&& conf, dgram_output_configuration&& apiconf) : configuration{std::move(conf), std::move(apiconf)} - , ctx{configuration.io_context ? configuration.io_context : new boost::asio::io_context} - , m_socket{*ctx} + , ctx{configuration.io_context} + , m_socket{ctx.get()} { m_socket.open(boost::asio::ip::udp::v4()); m_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true)); @@ -137,7 +137,7 @@ class midi_out final return from_errc(ret); } - boost::asio::io_context* ctx{}; + libremidi::optionally_owned ctx; boost::asio::ip::udp::endpoint m_endpoint; boost::asio::ip::udp::socket m_socket; @@ -219,8 +219,8 @@ class midi_out final midi_out(output_configuration&& conf, dgram_output_configuration&& apiconf) : configuration{std::move(conf), std::move(apiconf)} - , ctx{configuration.io_context ? configuration.io_context : new boost::asio::io_context} - , m_socket{*ctx} + , ctx{configuration.io_context} + , m_socket{ctx.get()} { m_socket.open(boost::asio::ip::udp::v4()); m_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true)); @@ -278,7 +278,7 @@ class midi_out final return from_errc(ret); } - boost::asio::io_context* ctx{}; + libremidi::optionally_owned ctx; boost::asio::ip::udp::endpoint m_endpoint; boost::asio::ip::udp::socket m_socket;