working on win32 uwp! and libremidi update

This commit is contained in:
Sebastian @ HfG
2024-10-28 19:24:18 +01:00
parent 06cb6d8319
commit 1e3755148c
106 changed files with 8474 additions and 2198 deletions
@@ -27,8 +27,8 @@ struct pipewire_input_configuration
pw_main_loop* context{};
pw_filter* filter{};
std::function<void(pipewire_callback)> set_process_func;
std::function<void(int64_t)> clear_process_func;
std::function<void(pipewire_callback)> set_process_func{};
std::function<void(int64_t)> clear_process_func{};
};
struct pipewire_output_configuration
@@ -37,8 +37,8 @@ struct pipewire_output_configuration
pw_main_loop* context{};
pw_filter* filter{};
std::function<void(pipewire_callback)> set_process_func;
std::function<void(int64_t)> clear_process_func;
std::function<void(pipewire_callback)> set_process_func{};
std::function<void(int64_t)> clear_process_func{};
int64_t output_buffer_size{65536};
};
+39 -27
View File
@@ -1,6 +1,7 @@
#pragma once
#include <libremidi/backends/linux/pipewire.hpp>
#include <libremidi/detail/memory.hpp>
#include <libremidi/error.hpp>
#include <pipewire/filter.h>
#include <pipewire/pipewire.h>
@@ -128,7 +129,7 @@ struct pipewire_context
this->main_loop = pw.main_loop_new(nullptr);
if (!this->main_loop)
{
// libremidi::logger().error("PipeWire: main_loop_new failed!");
// libremidi::logger().libremidi_handle_error("main_loop_new failed!");
return;
}
initialize();
@@ -139,28 +140,28 @@ struct pipewire_context
this->lp = pw.main_loop_get_loop(this->main_loop);
if (!lp)
{
// libremidi::logger().error("PipeWire: main_loop_get_loop failed!");
// libremidi::logger().libremidi_handle_error("main_loop_get_loop failed!");
return;
}
this->context = pw.context_new(lp, nullptr, 0);
if (!this->context)
{
// libremidi::logger().error("PipeWire: context_new failed!");
// libremidi::logger().libremidi_handle_error("context_new failed!");
return;
}
this->core = pw.context_connect(this->context, nullptr, 0);
if (!this->core)
{
// libremidi::logger().error("PipeWire: context_connect failed!");
// libremidi::logger().libremidi_handle_error("context_connect failed!");
return;
}
this->registry = pw_core_get_registry(this->core, PW_VERSION_REGISTRY, 0);
if (!this->registry)
{
// libremidi::logger().error("PipeWire: core_get_registry failed!");
// libremidi::logger().libremidi_handle_error("core_get_registry failed!");
return;
}
@@ -297,7 +298,6 @@ struct pipewire_context
if (!proxy)
{
std::cerr << "PipeWire: could not allocate link\n";
pw.properties_free(props);
return nullptr;
}
@@ -476,7 +476,7 @@ struct pipewire_filter
PW_KEY_MEDIA_TYPE, "Midi",
PW_KEY_MEDIA_CATEGORY, "Filter",
PW_KEY_MEDIA_ROLE, "DSP",
PW_KEY_MEDIA_NAME, "libremidi",
PW_KEY_MEDIA_NAME, "",
#if defined(PW_KEY_NODE_LOCK_RATE)
PW_KEY_NODE_LOCK_RATE, "true",
#endif
@@ -498,7 +498,7 @@ struct pipewire_filter
pw.filter_destroy(this->filter);
}
void create_local_port(std::string_view port_name, spa_direction direction)
stdx::error create_local_port(std::string_view port_name, spa_direction direction)
{
// clang-format off
this->port = (struct port*)pw.filter_add_port(
@@ -512,7 +512,9 @@ struct pipewire_filter
nullptr),
nullptr, 0);
// clang-format on
assert(port);
if (!port)
return std::errc::invalid_argument;
return stdx::error{};
}
void set_port_buffer(int bytes)
@@ -537,31 +539,41 @@ struct pipewire_filter
pw.filter_update_params(this->filter, this->port, params, 1);
}
void remove_port()
stdx::error remove_port()
{
assert(this->port);
pw.filter_remove_port(this->port);
int ret = pw.filter_remove_port(this->port);
this->port = nullptr;
return from_errc(ret);
}
void rename_port(std::string_view port_name)
stdx::error rename_port(std::string_view port_name)
{
assert(this->port);
spa_dict_item items[1] = {
SPA_DICT_ITEM_INIT(PW_KEY_PORT_NAME, port_name.data()),
};
auto properties = SPA_DICT_INIT(items, 1);
pw.filter_update_properties(this->filter, this->port, &properties);
this->port = nullptr;
}
void start_filter()
{
if (pw.filter_connect(this->filter, PW_FILTER_FLAG_RT_PROCESS, NULL, 0) < 0)
if (this->port)
{
std::cerr << "can't connect\n";
return;
spa_dict_item items[1] = {
SPA_DICT_ITEM_INIT(PW_KEY_PORT_NAME, port_name.data()),
};
auto properties = SPA_DICT_INIT(items, 1);
int ret = pw.filter_update_properties(this->filter, this->port, &properties);
return from_errc(ret);
}
else
{
return std::errc::not_connected;
}
}
[[nodiscard]] stdx::error start_filter()
{
if (int ret = pw.filter_connect(this->filter, PW_FILTER_FLAG_RT_PROCESS, NULL, 0); ret < 0)
{
return from_errc(ret);
}
else
{
return stdx::error{};
}
}
+66 -57
View File
@@ -2,6 +2,7 @@
#include <libremidi/backends/linux/helpers.hpp>
#include <libremidi/backends/linux/pipewire.hpp>
#include <libremidi/backends/pipewire/config.hpp>
#include <libremidi/backends/pipewire/context.hpp>
#include <libremidi/detail/memory.hpp>
#include <libremidi/detail/midi_in.hpp>
@@ -55,10 +56,10 @@ struct pipewire_helpers
}
template <typename Self>
void create_filter(Self& self)
stdx::error create_filter(Self& self)
{
if (this->filter)
return;
return stdx::error{};
auto& configuration = self.configuration;
if (configuration.context && configuration.filter && configuration.set_process_func)
@@ -90,8 +91,9 @@ struct pipewire_helpers
#pragma GCC diagnostic pop
this->filter->create_filter(self.configuration.client_name, filter_events, &self);
this->filter->start_filter();
return this->filter->start_filter();
}
return stdx::error{};
}
template <typename Self>
@@ -117,10 +119,10 @@ struct pipewire_helpers
}
template <typename Self>
int create_context(Self& self)
stdx::error create_context(Self& self)
{
if (this->global_context)
return 0;
return stdx::error{};
// Initialize PipeWire client
auto& configuration = self.configuration;
@@ -133,7 +135,10 @@ struct pipewire_helpers
this->global_instance = std::make_shared<pipewire_instance>();
this->global_context = std::make_shared<pipewire_context>(this->global_instance);
}
return 0;
if (!this->global_context->main_loop)
return std::errc::connection_refused;
return stdx::error{};
}
void destroy_context()
@@ -170,7 +175,9 @@ struct pipewire_helpers
{
int result = pw_loop_iterate(lp, 0);
if (result < 0)
std::cerr << "pw_loop_iterate: " << spa_strerror(result) << "\n";
{
LIBREMIDI_LOG(spa_strerror(result));
}
}
fds[0].revents = 0;
}
@@ -190,7 +197,7 @@ struct pipewire_helpers
}
template <typename Self>
bool create_local_port(Self& self, std::string_view portName, spa_direction direction)
stdx::error create_local_port(Self& self, std::string_view portName, spa_direction direction)
{
assert(this->global_context);
assert(this->filter);
@@ -200,15 +207,15 @@ struct pipewire_helpers
if (!this->filter->port)
{
this->filter->create_local_port(portName.data(), direction);
auto ret = this->filter->create_local_port(portName.data(), direction);
if (ret != stdx::error{})
{
self.libremidi_handle_error(self.configuration, "error creating port");
return ret;
}
}
if (!this->filter->port)
{
self.template error<driver_error>(self.configuration, "PipeWire: error creating port");
return false;
}
return true;
return stdx::error{};
}
void add_callbacks(const observer_configuration& conf)
@@ -293,12 +300,12 @@ struct pipewire_helpers
}
}
void do_close_port()
stdx::error do_close_port()
{
if (!this->filter)
return;
return stdx::error{};
if (!this->filter->port)
return;
return stdx::error{};
if (!this->global_context->owns_main_loop)
{
@@ -307,13 +314,19 @@ struct pipewire_helpers
}
unlink_ports();
this->filter->remove_port();
return this->filter->remove_port();
}
void rename_port(std::string_view port_name)
stdx::error rename_port(std::string_view port_name)
{
if (this->filter)
this->filter->rename_port(port_name);
{
return this->filter->rename_port(port_name);
}
else
{
return std::errc::not_connected;
}
}
void unlink_ports()
@@ -325,7 +338,7 @@ struct pipewire_helpers
}
}
bool link_ports(auto& self, const input_port& in_port)
stdx::error link_ports(auto& self, const input_port& in_port)
{
// Wait for the pipewire server to send us back our node's info
for (int i = 0; i < 1000; i++)
@@ -335,19 +348,13 @@ struct pipewire_helpers
auto& midi = this->global_context->current_graph.software_midi;
auto node_it = midi.find(this_node);
if (node_it == midi.end())
{
std::cerr << "Node " << this_node << " not found! \n";
return false;
}
return std::errc::invalid_argument;
// Wait for the pipewire server to send us back our node's ports
this->filter->synchronize_ports(node_it->second);
if (node_it->second.inputs.empty())
{
std::cerr << "Node " << this_node << " has no ports! \n";
return false;
}
return std::errc::no_link;
// Link ports
const auto& p = node_it->second.inputs.front();
@@ -355,16 +362,16 @@ struct pipewire_helpers
pw_loop_iterate(this->global_context->lp, 1);
if (!link)
{
self.template error<invalid_parameter_error>(
self.libremidi_handle_error(
self.configuration,
"PipeWire: could not connect to port: " + in_port.port_name + " -> " + p.port_name);
return false;
"could not connect to port: " + in_port.port_name + " -> " + p.port_name);
return std::errc::no_link;
}
return true;
return stdx::error{};
}
bool link_ports(auto& self, const output_port& out_port)
stdx::error link_ports(auto& self, const output_port& out_port)
{
// Wait for the pipewire server to send us back our node's info
for (int i = 0; i < 1000; i++)
@@ -375,8 +382,7 @@ struct pipewire_helpers
auto node_it = midi.find(this_node);
if (node_it == midi.end())
{
std::cerr << "Node " << this_node << " not found! \n";
return false;
return std::errc::invalid_argument;
}
// Wait for the pipewire server to send us back our node's ports
@@ -384,8 +390,7 @@ struct pipewire_helpers
if (node_it->second.outputs.empty())
{
std::cerr << "Node " << this_node << " has no ports! \n";
return false;
return std::errc::no_link;
}
// Link ports
@@ -394,13 +399,13 @@ struct pipewire_helpers
pw_loop_iterate(this->global_context->lp, 1);
if (!link)
{
self.template error<invalid_parameter_error>(
self.libremidi_handle_error(
self.configuration,
"PipeWire: could not connect to port: " + p.port_name + " -> " + out_port.port_name);
return false;
"could not connect to port: " + p.port_name + " -> " + out_port.port_name);
return std::errc::no_link;
}
return true;
return stdx::error{};
}
template <spa_direction Direction>
@@ -432,30 +437,34 @@ struct pipewire_helpers
// Note: keep in mind that an "input" port for us (e.g. a keyboard that goes to the computer)
// is an "output" port from the point of view of pipewire as data will come out of it
template <spa_direction Direction>
static auto get_ports(const pipewire_context& ctx) noexcept -> std::vector<
std::conditional_t<Direction == SPA_DIRECTION_OUTPUT, input_port, output_port>>
static auto get_ports(const observer_configuration& conf, const pipewire_context& ctx) noexcept
-> std::vector<
std::conditional_t<Direction == SPA_DIRECTION_OUTPUT, input_port, output_port>>
{
std::vector<std::conditional_t<Direction == SPA_DIRECTION_OUTPUT, input_port, output_port>>
ret;
{
std::lock_guard _{ctx.current_graph.mtx};
for (auto& node : ctx.current_graph.physical_midi)
{
for (auto& p :
(Direction == SPA_DIRECTION_INPUT ? node.second.inputs : node.second.outputs))
if (conf.track_any || conf.track_hardware)
for (auto& node : ctx.current_graph.physical_midi)
{
ret.push_back(to_port_info<Direction>(p));
for (auto& port :
(Direction == SPA_DIRECTION_INPUT ? node.second.inputs : node.second.outputs))
{
ret.push_back(to_port_info<Direction>(port));
}
}
}
for (auto& node : ctx.current_graph.software_midi)
{
for (auto& p :
(Direction == SPA_DIRECTION_INPUT ? node.second.inputs : node.second.outputs))
if (conf.track_any || conf.track_virtual)
for (auto& node : ctx.current_graph.software_midi)
{
ret.push_back(to_port_info<Direction>(p));
for (auto& port :
(Direction == SPA_DIRECTION_INPUT ? node.second.inputs : node.second.outputs))
{
ret.push_back(to_port_info<Direction>(port));
}
}
}
}
return ret;
+28 -22
View File
@@ -4,8 +4,6 @@
#include <libremidi/detail/midi_in.hpp>
#include <libremidi/detail/midi_stream_decoder.hpp>
#include <chrono>
namespace libremidi
{
class midi_in_pipewire final
@@ -23,8 +21,17 @@ public:
explicit midi_in_pipewire(input_configuration&& conf, pipewire_input_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
create_context(*this);
create_filter(*this);
if (auto ret = create_context(*this); ret != stdx::error{})
{
client_open_ = ret;
return;
}
if (auto ret = create_filter(*this); ret != stdx::error{})
{
client_open_ = ret;
return;
}
client_open_ = stdx::error{};
}
~midi_in_pipewire() override
@@ -33,43 +40,42 @@ public:
do_close_port();
destroy_filter(*this);
destroy_context();
}
void set_client_name(std::string_view) override
{
warning(configuration, "midi_in_pipewire: set_client_name unsupported");
client_open_ = std::errc::not_connected;
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::PIPEWIRE; }
bool open_port(const input_port& in_port, std::string_view name) override
stdx::error open_port(const input_port& in_port, std::string_view name) override
{
if (!create_local_port(*this, name, SPA_DIRECTION_INPUT))
return false;
if (auto err = create_local_port(*this, name, SPA_DIRECTION_INPUT); err != stdx::error{})
return err;
if (!link_ports(*this, in_port))
return false;
if (auto err = link_ports(*this, in_port); err != stdx::error{})
return err;
start_thread();
return true;
return stdx::error{};
}
bool open_virtual_port(std::string_view name) override
stdx::error open_virtual_port(std::string_view name) override
{
if (!create_local_port(*this, name, SPA_DIRECTION_INPUT))
return false;
if (auto err = create_local_port(*this, name, SPA_DIRECTION_INPUT); err != stdx::error{})
return err;
start_thread();
return true;
return stdx::error{};
}
void close_port() override
stdx::error close_port() override
{
stop_thread();
do_close_port();
return do_close_port();
}
void set_port_name(std::string_view port_name) override { rename_port(port_name); }
stdx::error set_port_name(std::string_view port_name) override
{
return rename_port(port_name);
}
timestamp absolute_timestamp() const noexcept override { return system_ns(); }
@@ -24,8 +24,17 @@ public:
midi_out_pipewire(output_configuration&& conf, pipewire_output_configuration&& apiconf)
: configuration{std::move(conf), std::move(apiconf)}
{
create_context(*this);
create_filter(*this);
if (auto ret = create_context(*this); ret != stdx::error{})
{
client_open_ = ret;
return;
}
if (auto ret = create_filter(*this); ret != stdx::error{})
{
client_open_ = ret;
return;
}
client_open_ = stdx::error{};
}
~midi_out_pipewire() override
@@ -34,47 +43,46 @@ public:
do_close_port();
destroy_filter(*this);
destroy_context();
}
void set_client_name(std::string_view) override
{
warning(configuration, "midi_out_pipewire: set_client_name unsupported");
client_open_ = std::errc::not_connected;
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::PIPEWIRE; }
bool open_port(const output_port& out_port, std::string_view name) override
stdx::error open_port(const output_port& out_port, std::string_view name) override
{
if (!create_local_port(*this, name, SPA_DIRECTION_OUTPUT))
return false;
if (auto err = create_local_port(*this, name, SPA_DIRECTION_OUTPUT); err != stdx::error{})
return err;
this->filter->set_port_buffer(configuration.output_buffer_size);
if (!link_ports(*this, out_port))
return false;
if (auto err = link_ports(*this, out_port); err != stdx::error{})
return err;
start_thread();
return true;
return stdx::error{};
}
bool open_virtual_port(std::string_view name) override
stdx::error open_virtual_port(std::string_view name) override
{
if (!create_local_port(*this, name, SPA_DIRECTION_OUTPUT))
return false;
if (auto err = create_local_port(*this, name, SPA_DIRECTION_OUTPUT); err != stdx::error{})
return err;
this->filter->set_port_buffer(configuration.output_buffer_size);
start_thread();
return true;
return stdx::error{};
}
void close_port() override
stdx::error close_port() override
{
stop_thread();
do_close_port();
return do_close_port();
}
void set_port_name(std::string_view port_name) override { rename_port(port_name); }
stdx::error set_port_name(std::string_view port_name) override
{
return rename_port(port_name);
}
int process(spa_io_position* pos)
{
@@ -120,6 +128,8 @@ public:
if (res == -ENOSPC)
break;
// Recycle the memory
m_gcqueue.enqueue(std::move(m));
m_queue.pop();
}
spa_pod_builder_pop(&build, &f);
@@ -141,9 +151,14 @@ public:
return 0;
}
void send_message(const unsigned char* message, size_t size) override
stdx::error send_message(const unsigned char* message, size_t size) override
{
m_queue.enqueue(libremidi::message(midi_bytes{message, message + size}, 0));
libremidi::message m;
m_gcqueue.try_dequeue(m);
m.bytes.assign(message, message + size);
m.timestamp = 0;
m_queue.enqueue(std::move(m));
return stdx::error{};
}
int convert_timestamp(int64_t user) const noexcept
@@ -159,13 +174,15 @@ public:
}
}
void schedule_message(int64_t ts, const unsigned char* message, size_t size) override
stdx::error schedule_message(int64_t ts, const unsigned char* message, size_t size) override
{
m_queue.enqueue(
libremidi::message(midi_bytes{message, message + size}, convert_timestamp(ts)));
return stdx::error{};
}
moodycamel::ReaderWriterQueue<libremidi::message> m_queue;
moodycamel::ReaderWriterQueue<libremidi::message> m_gcqueue;
std::atomic_int64_t m_process_clock = 0;
};
}
@@ -39,18 +39,29 @@ public:
this->add_callbacks(configuration);
this->start_thread();
}
if (configuration.notify_in_constructor)
{
if (configuration.input_added)
for (const auto& p : get_input_ports())
configuration.input_added(p);
if (configuration.output_added)
for (const auto& p : get_output_ports())
configuration.output_added(p);
}
}
libremidi::API get_current_api() const noexcept override { return libremidi::API::PIPEWIRE; }
std::vector<libremidi::input_port> get_input_ports() const noexcept override
{
return get_ports<SPA_DIRECTION_OUTPUT>(*this->global_context);
return get_ports<SPA_DIRECTION_OUTPUT>(this->configuration, *this->global_context);
}
std::vector<libremidi::output_port> get_output_ports() const noexcept override
{
return get_ports<SPA_DIRECTION_INPUT>(*this->global_context);
return get_ports<SPA_DIRECTION_INPUT>(this->configuration, *this->global_context);
}
~observer_pipewire()