154 lines
4.4 KiB
C++
154 lines
4.4 KiB
C++
|
|
#pragma once
|
||
|
|
#if __has_include(<boost/lockfree/spsc_queue.hpp>)
|
||
|
|
#include <libremidi/backends/pipewire/config.hpp>
|
||
|
|
#include <libremidi/backends/pipewire/helpers.hpp>
|
||
|
|
#include <libremidi/shared_context.hpp>
|
||
|
|
|
||
|
|
#include <boost/lockfree/spsc_queue.hpp>
|
||
|
|
|
||
|
|
#include <variant>
|
||
|
|
|
||
|
|
namespace libremidi::pipewire
|
||
|
|
{
|
||
|
|
|
||
|
|
// Create a PipeWire client which will be shared across objects
|
||
|
|
struct shared_handler : public libremidi::shared_context
|
||
|
|
{
|
||
|
|
explicit shared_handler(std::string_view v)
|
||
|
|
{
|
||
|
|
midiin_callbacks.reserve(64);
|
||
|
|
midiout_callbacks.reserve(64);
|
||
|
|
|
||
|
|
pipewire_status_t status{};
|
||
|
|
client = pipewire_client_open(v.data(), PipewireNoStartServer, &status);
|
||
|
|
assert(client);
|
||
|
|
assert(status == 0);
|
||
|
|
pipewire_set_process_callback(
|
||
|
|
client,
|
||
|
|
+[](pipewire_nframes_t cnt, void* ctx) -> int {
|
||
|
|
((shared_handler*)ctx)->pipewire_callback(cnt);
|
||
|
|
return 0;
|
||
|
|
},
|
||
|
|
this);
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual void start_processing() override { pipewire_activate(client); }
|
||
|
|
virtual void stop_processing() override { pipewire_deactivate(client); }
|
||
|
|
|
||
|
|
static shared_configurations make(std::string_view client_name)
|
||
|
|
{
|
||
|
|
auto clt = std::make_shared<shared_handler>(client_name);
|
||
|
|
auto add_in_cb = [client = std::weak_ptr{clt}](libremidi::pipewire_callback cb) {
|
||
|
|
if (auto clt = client.lock())
|
||
|
|
clt->events.push({shared_handler::event_type::in_callback_added, std::move(cb)});
|
||
|
|
};
|
||
|
|
auto clear_in_cb = [client = std::weak_ptr{clt}](int64_t index) {
|
||
|
|
if (auto clt = client.lock())
|
||
|
|
clt->events.push({shared_handler::event_type::in_callback_removed, index});
|
||
|
|
};
|
||
|
|
auto add_out_cb = [client = std::weak_ptr{clt}](libremidi::pipewire_callback cb) {
|
||
|
|
if (auto clt = client.lock())
|
||
|
|
clt->events.push({shared_handler::event_type::out_callback_added, std::move(cb)});
|
||
|
|
};
|
||
|
|
auto clear_out_cb = [client = std::weak_ptr{clt}](int64_t index) {
|
||
|
|
if (auto clt = client.lock())
|
||
|
|
clt->events.push({shared_handler::event_type::out_callback_removed, index});
|
||
|
|
};
|
||
|
|
return {
|
||
|
|
.context = clt,
|
||
|
|
.observer = pipewire_observer_configuration{.context = clt->client},
|
||
|
|
.in
|
||
|
|
= pipewire_input_configuration{.context = clt->client, .set_process_func = add_in_cb, .clear_process_func = clear_in_cb},
|
||
|
|
.out
|
||
|
|
= pipewire_output_configuration{.context = clt->client, .set_process_func = add_out_cb, .clear_process_func = clear_out_cb},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
int pipewire_callback(pipewire_nframes_t cnt)
|
||
|
|
{
|
||
|
|
// 1. Process the events that will change the callback list
|
||
|
|
event ev;
|
||
|
|
while (events.pop(ev))
|
||
|
|
{
|
||
|
|
switch (ev.type)
|
||
|
|
{
|
||
|
|
case in_callback_added:
|
||
|
|
midiin_callbacks.push_back(
|
||
|
|
std::move(*std::get_if<libremidi::pipewire_callback>(&ev.payload)));
|
||
|
|
break;
|
||
|
|
case in_callback_removed: {
|
||
|
|
auto idx = *std::get_if<int64_t>(&ev.payload);
|
||
|
|
for (auto it = midiin_callbacks.begin(); it != midiin_callbacks.end();)
|
||
|
|
{
|
||
|
|
if (it->token == idx)
|
||
|
|
{
|
||
|
|
midiin_callbacks.erase(it);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
++it;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case out_callback_added:
|
||
|
|
midiout_callbacks.push_back(
|
||
|
|
std::move(*std::get_if<libremidi::pipewire_callback>(&ev.payload)));
|
||
|
|
break;
|
||
|
|
case out_callback_removed:
|
||
|
|
auto idx = *std::get_if<int64_t>(&ev.payload);
|
||
|
|
for (auto it = midiout_callbacks.begin(); it != midiout_callbacks.end();)
|
||
|
|
{
|
||
|
|
if (it->token == idx)
|
||
|
|
{
|
||
|
|
midiout_callbacks.erase(it);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
++it;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (auto& cb : midiin_callbacks)
|
||
|
|
cb.callback(cnt);
|
||
|
|
|
||
|
|
for (auto& cb : midiout_callbacks)
|
||
|
|
cb.callback(cnt);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
~shared_handler()
|
||
|
|
{
|
||
|
|
pipewire_deactivate(client);
|
||
|
|
pipewire_client_close(client);
|
||
|
|
}
|
||
|
|
|
||
|
|
pipewire_client_t* client{};
|
||
|
|
|
||
|
|
enum event_type
|
||
|
|
{
|
||
|
|
in_callback_added,
|
||
|
|
in_callback_removed,
|
||
|
|
out_callback_added,
|
||
|
|
out_callback_removed,
|
||
|
|
};
|
||
|
|
struct event
|
||
|
|
{
|
||
|
|
event_type type;
|
||
|
|
std::variant<libremidi::pipewire_callback, int64_t> payload;
|
||
|
|
};
|
||
|
|
|
||
|
|
boost::lockfree::spsc_queue<event> events{16};
|
||
|
|
|
||
|
|
std::vector<libremidi::pipewire_callback> midiin_callbacks;
|
||
|
|
std::vector<libremidi::pipewire_callback> midiout_callbacks;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
#endif
|