summaryrefslogtreecommitdiffstats
path: root/src/JackLibDriver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/JackLibDriver.cpp')
-rw-r--r--src/JackLibDriver.cpp560
1 files changed, 280 insertions, 280 deletions
diff --git a/src/JackLibDriver.cpp b/src/JackLibDriver.cpp
index 21c8f9b..feb8e8c 100644
--- a/src/JackLibDriver.cpp
+++ b/src/JackLibDriver.cpp
@@ -32,7 +32,7 @@
#include "warnings.hpp"
#ifdef HAVE_JACK_METADATA
-# include <jack/metadata.h>
+# include <jack/metadata.h>
#endif
PATCHAGE_DISABLE_FMT_WARNINGS
@@ -60,350 +60,350 @@ namespace {
class JackLibDriver : public AudioDriver
{
public:
- explicit JackLibDriver(ILog& log, EventSink emit_event);
+ explicit JackLibDriver(ILog& log, EventSink emit_event);
- JackLibDriver(const JackLibDriver&) = delete;
- JackLibDriver& operator=(const JackLibDriver&) = delete;
+ JackLibDriver(const JackLibDriver&) = delete;
+ JackLibDriver& operator=(const JackLibDriver&) = delete;
- JackLibDriver(JackLibDriver&&) = delete;
- JackLibDriver& operator=(JackLibDriver&&) = delete;
+ JackLibDriver(JackLibDriver&&) = delete;
+ JackLibDriver& operator=(JackLibDriver&&) = delete;
- ~JackLibDriver() override;
+ ~JackLibDriver() override;
- // Driver interface
- void attach(bool launch_daemon) override;
- void detach() override;
- bool is_attached() const override;
- void refresh(const EventSink& sink) override;
- bool connect(const PortID& tail_id, const PortID& head_id) override;
- bool disconnect(const PortID& tail_id, const PortID& head_id) override;
+ // Driver interface
+ void attach(bool launch_daemon) override;
+ void detach() override;
+ bool is_attached() const override;
+ void refresh(const EventSink& sink) override;
+ bool connect(const PortID& tail_id, const PortID& head_id) override;
+ bool disconnect(const PortID& tail_id, const PortID& head_id) override;
- // AudioDriver interface
- uint32_t xruns() override;
- void reset_xruns() override;
- uint32_t buffer_size() override;
- bool set_buffer_size(uint32_t frames) override;
- uint32_t sample_rate() override;
+ // AudioDriver interface
+ uint32_t xruns() override;
+ void reset_xruns() override;
+ uint32_t buffer_size() override;
+ bool set_buffer_size(uint32_t frames) override;
+ uint32_t sample_rate() override;
private:
- ClientInfo get_client_info(const char* name);
- PortInfo get_port_info(const jack_port_t* port);
+ ClientInfo get_client_info(const char* name);
+ PortInfo get_port_info(const jack_port_t* port);
- static void on_client(const char* name, int registered, void* driver);
+ static void on_client(const char* name, int registered, void* driver);
- static void on_port(jack_port_id_t port_id, int registered, void* driver);
+ static void on_port(jack_port_id_t port_id, int registered, void* driver);
- static void on_connection(jack_port_id_t src,
- jack_port_id_t dst,
- int connect,
- void* driver);
+ static void on_connection(jack_port_id_t src,
+ jack_port_id_t dst,
+ int connect,
+ void* driver);
- static int on_xrun(void* driver);
+ static int on_xrun(void* driver);
- static void on_shutdown(void* driver);
+ static void on_shutdown(void* driver);
- ILog& _log;
- std::mutex _shutdown_mutex;
+ ILog& _log;
+ std::mutex _shutdown_mutex;
- jack_client_t* _client = nullptr;
- jack_nframes_t _buffer_size = 0u;
- uint32_t _xruns = 0u;
- bool _is_activated = false;
+ jack_client_t* _client = nullptr;
+ jack_nframes_t _buffer_size = 0u;
+ uint32_t _xruns = 0u;
+ bool _is_activated = false;
};
JackLibDriver::JackLibDriver(ILog& log, EventSink emit_event)
- : AudioDriver{std::move(emit_event)}
- , _log{log}
+ : AudioDriver{std::move(emit_event)}
+ , _log{log}
{}
JackLibDriver::~JackLibDriver()
{
- detach();
+ detach();
}
void
JackLibDriver::attach(const bool launch_daemon)
{
- if (_client) {
- return; // Already connected
- }
-
- const jack_options_t options =
- (!launch_daemon) ? JackNoStartServer : JackNullOption;
-
- if (!(_client = jack_client_open("Patchage", options, nullptr))) {
- _log.error("[JACK] Unable to create client");
- _is_activated = false;
- return;
- }
-
- jack_on_shutdown(_client, on_shutdown, this);
- jack_set_client_registration_callback(_client, on_client, this);
- jack_set_port_registration_callback(_client, on_port, this);
- jack_set_port_connect_callback(_client, on_connection, this);
- jack_set_xrun_callback(_client, on_xrun, this);
-
- if (jack_activate(_client)) {
- _log.error("[JACK] Client activation failed");
- _is_activated = false;
- _buffer_size = 0;
- return;
- }
-
- _is_activated = true;
- _buffer_size = jack_get_buffer_size(_client);
-
- _emit_event(DriverAttachmentEvent{ClientType::jack});
+ if (_client) {
+ return; // Already connected
+ }
+
+ const jack_options_t options =
+ (!launch_daemon) ? JackNoStartServer : JackNullOption;
+
+ if (!(_client = jack_client_open("Patchage", options, nullptr))) {
+ _log.error("[JACK] Unable to create client");
+ _is_activated = false;
+ return;
+ }
+
+ jack_on_shutdown(_client, on_shutdown, this);
+ jack_set_client_registration_callback(_client, on_client, this);
+ jack_set_port_registration_callback(_client, on_port, this);
+ jack_set_port_connect_callback(_client, on_connection, this);
+ jack_set_xrun_callback(_client, on_xrun, this);
+
+ if (jack_activate(_client)) {
+ _log.error("[JACK] Client activation failed");
+ _is_activated = false;
+ _buffer_size = 0;
+ return;
+ }
+
+ _is_activated = true;
+ _buffer_size = jack_get_buffer_size(_client);
+
+ _emit_event(DriverAttachmentEvent{ClientType::jack});
}
void
JackLibDriver::detach()
{
- std::lock_guard<std::mutex> lock{_shutdown_mutex};
+ std::lock_guard<std::mutex> lock{_shutdown_mutex};
- if (_client) {
- jack_deactivate(_client);
- jack_client_close(_client);
- _client = nullptr;
- }
+ if (_client) {
+ jack_deactivate(_client);
+ jack_client_close(_client);
+ _client = nullptr;
+ }
- _is_activated = false;
- _emit_event(DriverDetachmentEvent{ClientType::jack});
+ _is_activated = false;
+ _emit_event(DriverDetachmentEvent{ClientType::jack});
}
bool
JackLibDriver::is_attached() const
{
- return _client != nullptr;
+ return _client != nullptr;
}
std::string
get_property(const jack_uuid_t subject, const char* const key)
{
- std::string result;
+ std::string result;
#ifdef HAVE_JACK_METADATA
- char* value = nullptr;
- char* datatype = nullptr;
- if (!jack_get_property(subject, key, &value, &datatype)) {
- result = value;
- }
- jack_free(datatype);
- jack_free(value);
+ char* value = nullptr;
+ char* datatype = nullptr;
+ if (!jack_get_property(subject, key, &value, &datatype)) {
+ result = value;
+ }
+ jack_free(datatype);
+ jack_free(value);
#else
- (void)subject;
- (void)key;
+ (void)subject;
+ (void)key;
#endif
- return result;
+ return result;
}
ClientInfo
JackLibDriver::get_client_info(const char* const name)
{
- return {name}; // TODO: Pretty name?
+ return {name}; // TODO: Pretty name?
}
PortInfo
JackLibDriver::get_port_info(const jack_port_t* const port)
{
- const auto uuid = jack_port_uuid(port);
- const auto flags = jack_port_flags(port);
- const std::string name = jack_port_name(port);
- auto label = PortNames{name}.port();
+ const auto uuid = jack_port_uuid(port);
+ const auto flags = jack_port_flags(port);
+ const std::string name = jack_port_name(port);
+ auto label = PortNames{name}.port();
- // Get pretty name to use as a label, if present
+ // Get pretty name to use as a label, if present
#ifdef HAVE_JACK_METADATA
- const auto pretty_name = get_property(uuid, JACK_METADATA_PRETTY_NAME);
- if (!pretty_name.empty()) {
- label = pretty_name;
- }
+ const auto pretty_name = get_property(uuid, JACK_METADATA_PRETTY_NAME);
+ if (!pretty_name.empty()) {
+ label = pretty_name;
+ }
#endif
- // Determine detailed type, using metadata for fancy types if possible
- const char* const type_str = jack_port_type(port);
- PortType type = PortType::jack_audio;
- if (!strcmp(type_str, JACK_DEFAULT_AUDIO_TYPE)) {
- if (get_property(uuid, JACKEY_SIGNAL_TYPE) == "CV") {
- type = PortType::jack_cv;
- }
- } else if (!strcmp(type_str, JACK_DEFAULT_MIDI_TYPE)) {
- type = PortType::jack_midi;
- if (get_property(uuid, JACKEY_EVENT_TYPES) == "OSC") {
- type = PortType::jack_osc;
- }
- } else {
- _log.warning(fmt::format(
- R"([JACK] Port "{}" has unknown type "{}")", name, type_str));
- }
-
- // Get direction from port flags
- const SignalDirection direction =
- ((flags & JackPortIsInput) ? SignalDirection::input
- : SignalDirection::output);
-
- // Get port order from metadata if possible
- boost::optional<int> order;
- const std::string order_str = get_property(uuid, JACKEY_ORDER);
- if (!order_str.empty()) {
- order = std::stoi(order_str);
- }
-
- return {label, type, direction, order, bool(flags & JackPortIsTerminal)};
+ // Determine detailed type, using metadata for fancy types if possible
+ const char* const type_str = jack_port_type(port);
+ PortType type = PortType::jack_audio;
+ if (!strcmp(type_str, JACK_DEFAULT_AUDIO_TYPE)) {
+ if (get_property(uuid, JACKEY_SIGNAL_TYPE) == "CV") {
+ type = PortType::jack_cv;
+ }
+ } else if (!strcmp(type_str, JACK_DEFAULT_MIDI_TYPE)) {
+ type = PortType::jack_midi;
+ if (get_property(uuid, JACKEY_EVENT_TYPES) == "OSC") {
+ type = PortType::jack_osc;
+ }
+ } else {
+ _log.warning(
+ fmt::format(R"([JACK] Port "{}" has unknown type "{}")", name, type_str));
+ }
+
+ // Get direction from port flags
+ const SignalDirection direction =
+ ((flags & JackPortIsInput) ? SignalDirection::input
+ : SignalDirection::output);
+
+ // Get port order from metadata if possible
+ boost::optional<int> order;
+ const std::string order_str = get_property(uuid, JACKEY_ORDER);
+ if (!order_str.empty()) {
+ order = std::stoi(order_str);
+ }
+
+ return {label, type, direction, order, bool(flags & JackPortIsTerminal)};
}
void
JackLibDriver::refresh(const EventSink& sink)
{
- std::lock_guard<std::mutex> lock{_shutdown_mutex};
-
- if (!_client) {
- return;
- }
-
- // Get all existing ports
- const char** const ports = jack_get_ports(_client, nullptr, nullptr, 0);
- if (!ports) {
- return;
- }
-
- // Get all client names (to only send a creation event once for each)
- std::unordered_set<std::string> client_names;
- for (auto i = 0u; ports[i]; ++i) {
- client_names.insert(PortID::jack(ports[i]).client().jack_name());
- }
-
- // Emit all clients
- for (const auto& client_name : client_names) {
- sink({ClientCreationEvent{ClientID::jack(client_name),
- get_client_info(client_name.c_str())}});
- }
-
- // Emit all ports
- for (auto i = 0u; ports[i]; ++i) {
- const jack_port_t* const port = jack_port_by_name(_client, ports[i]);
-
- sink({PortCreationEvent{PortID::jack(ports[i]), get_port_info(port)}});
- }
-
- // Get all connections (again to only create them once)
- std::set<std::pair<std::string, std::string>> connections;
- for (auto i = 0u; ports[i]; ++i) {
- const jack_port_t* const port = jack_port_by_name(_client, ports[i]);
- const char** const peers = jack_port_get_all_connections(_client, port);
-
- if (peers) {
- if (jack_port_flags(port) & JackPortIsInput) {
- for (auto j = 0u; peers[j]; ++j) {
- connections.emplace(peers[j], ports[i]);
- }
- } else {
- for (auto j = 0u; peers[j]; ++j) {
- connections.emplace(ports[i], peers[j]);
- }
- }
-
- jack_free(peers);
- }
- }
-
- // Emit all connections
- for (const auto& connection : connections) {
- sink({ConnectionEvent{PortID::jack(connection.first),
- PortID::jack(connection.second)}});
- }
-
- jack_free(ports);
+ std::lock_guard<std::mutex> lock{_shutdown_mutex};
+
+ if (!_client) {
+ return;
+ }
+
+ // Get all existing ports
+ const char** const ports = jack_get_ports(_client, nullptr, nullptr, 0);
+ if (!ports) {
+ return;
+ }
+
+ // Get all client names (to only send a creation event once for each)
+ std::unordered_set<std::string> client_names;
+ for (auto i = 0u; ports[i]; ++i) {
+ client_names.insert(PortID::jack(ports[i]).client().jack_name());
+ }
+
+ // Emit all clients
+ for (const auto& client_name : client_names) {
+ sink({ClientCreationEvent{ClientID::jack(client_name),
+ get_client_info(client_name.c_str())}});
+ }
+
+ // Emit all ports
+ for (auto i = 0u; ports[i]; ++i) {
+ const jack_port_t* const port = jack_port_by_name(_client, ports[i]);
+
+ sink({PortCreationEvent{PortID::jack(ports[i]), get_port_info(port)}});
+ }
+
+ // Get all connections (again to only create them once)
+ std::set<std::pair<std::string, std::string>> connections;
+ for (auto i = 0u; ports[i]; ++i) {
+ const jack_port_t* const port = jack_port_by_name(_client, ports[i]);
+ const char** const peers = jack_port_get_all_connections(_client, port);
+
+ if (peers) {
+ if (jack_port_flags(port) & JackPortIsInput) {
+ for (auto j = 0u; peers[j]; ++j) {
+ connections.emplace(peers[j], ports[i]);
+ }
+ } else {
+ for (auto j = 0u; peers[j]; ++j) {
+ connections.emplace(ports[i], peers[j]);
+ }
+ }
+
+ jack_free(peers);
+ }
+ }
+
+ // Emit all connections
+ for (const auto& connection : connections) {
+ sink({ConnectionEvent{PortID::jack(connection.first),
+ PortID::jack(connection.second)}});
+ }
+
+ jack_free(ports);
}
bool
JackLibDriver::connect(const PortID& tail_id, const PortID& head_id)
{
- if (!_client) {
- return false;
- }
+ if (!_client) {
+ return false;
+ }
- const auto& tail_name = tail_id.jack_name();
- const auto& head_name = head_id.jack_name();
+ const auto& tail_name = tail_id.jack_name();
+ const auto& head_name = head_id.jack_name();
- const int result =
- jack_connect(_client, tail_name.c_str(), head_name.c_str());
+ const int result =
+ jack_connect(_client, tail_name.c_str(), head_name.c_str());
- if (result) {
- _log.error(fmt::format(
- "[JACK] Failed to connect {} => {}", tail_name, head_name));
+ if (result) {
+ _log.error(
+ fmt::format("[JACK] Failed to connect {} => {}", tail_name, head_name));
- return false;
- }
+ return false;
+ }
- return true;
+ return true;
}
bool
JackLibDriver::disconnect(const PortID& tail_id, const PortID& head_id)
{
- if (!_client) {
- return false;
- }
+ if (!_client) {
+ return false;
+ }
- const auto& tail_name = tail_id.jack_name();
- const auto& head_name = head_id.jack_name();
+ const auto& tail_name = tail_id.jack_name();
+ const auto& head_name = head_id.jack_name();
- const int result =
- jack_disconnect(_client, tail_name.c_str(), head_name.c_str());
+ const int result =
+ jack_disconnect(_client, tail_name.c_str(), head_name.c_str());
- if (result) {
- _log.error(fmt::format(
- "[JACK] Failed to disconnect {} => {}", tail_name, head_name));
- return false;
- }
+ if (result) {
+ _log.error(fmt::format(
+ "[JACK] Failed to disconnect {} => {}", tail_name, head_name));
+ return false;
+ }
- return true;
+ return true;
}
uint32_t
JackLibDriver::xruns()
{
- return _xruns;
+ return _xruns;
}
void
JackLibDriver::reset_xruns()
{
- _xruns = 0;
+ _xruns = 0;
}
uint32_t
JackLibDriver::buffer_size()
{
- return _is_activated ? _buffer_size : jack_get_buffer_size(_client);
+ return _is_activated ? _buffer_size : jack_get_buffer_size(_client);
}
bool
JackLibDriver::set_buffer_size(const uint32_t frames)
{
- if (!_client) {
- _buffer_size = frames;
- return true;
- }
-
- if (buffer_size() == frames) {
- return true;
- }
-
- if (jack_set_buffer_size(_client, frames)) {
- _log.error("[JACK] Unable to set buffer size");
- return false;
- }
-
- _buffer_size = frames;
- return true;
+ if (!_client) {
+ _buffer_size = frames;
+ return true;
+ }
+
+ if (buffer_size() == frames) {
+ return true;
+ }
+
+ if (jack_set_buffer_size(_client, frames)) {
+ _log.error("[JACK] Unable to set buffer size");
+ return false;
+ }
+
+ _buffer_size = frames;
+ return true;
}
uint32_t
JackLibDriver::sample_rate()
{
- return jack_get_sample_rate(_client);
+ return jack_get_sample_rate(_client);
}
void
@@ -411,13 +411,13 @@ JackLibDriver::on_client(const char* const name,
const int registered,
void* const driver)
{
- auto* const me = static_cast<JackLibDriver*>(driver);
+ auto* const me = static_cast<JackLibDriver*>(driver);
- if (registered) {
- me->_emit_event(ClientCreationEvent{ClientID::jack(name), {name}});
- } else {
- me->_emit_event(ClientDestructionEvent{ClientID::jack(name)});
- }
+ if (registered) {
+ me->_emit_event(ClientCreationEvent{ClientID::jack(name), {name}});
+ } else {
+ me->_emit_event(ClientDestructionEvent{ClientID::jack(name)});
+ }
}
void
@@ -425,17 +425,17 @@ JackLibDriver::on_port(const jack_port_id_t port_id,
const int registered,
void* const driver)
{
- auto* const me = static_cast<JackLibDriver*>(driver);
+ auto* const me = static_cast<JackLibDriver*>(driver);
- jack_port_t* const port = jack_port_by_id(me->_client, port_id);
- const char* const name = jack_port_name(port);
- const auto id = PortID::jack(name);
+ jack_port_t* const port = jack_port_by_id(me->_client, port_id);
+ const char* const name = jack_port_name(port);
+ const auto id = PortID::jack(name);
- if (registered) {
- me->_emit_event(PortCreationEvent{id, me->get_port_info(port)});
- } else {
- me->_emit_event(PortDestructionEvent{id});
- }
+ if (registered) {
+ me->_emit_event(PortCreationEvent{id, me->get_port_info(port)});
+ } else {
+ me->_emit_event(PortDestructionEvent{id});
+ }
}
void
@@ -444,52 +444,52 @@ JackLibDriver::on_connection(const jack_port_id_t src,
const int connect,
void* const driver)
{
- auto* const me = static_cast<JackLibDriver*>(driver);
-
- jack_port_t* const src_port = jack_port_by_id(me->_client, src);
- jack_port_t* const dst_port = jack_port_by_id(me->_client, dst);
- const char* const src_name = jack_port_name(src_port);
- const char* const dst_name = jack_port_name(dst_port);
-
- if (connect) {
- me->_emit_event(
- ConnectionEvent{PortID::jack(src_name), PortID::jack(dst_name)});
- } else {
- me->_emit_event(
- DisconnectionEvent{PortID::jack(src_name), PortID::jack(dst_name)});
- }
+ auto* const me = static_cast<JackLibDriver*>(driver);
+
+ jack_port_t* const src_port = jack_port_by_id(me->_client, src);
+ jack_port_t* const dst_port = jack_port_by_id(me->_client, dst);
+ const char* const src_name = jack_port_name(src_port);
+ const char* const dst_name = jack_port_name(dst_port);
+
+ if (connect) {
+ me->_emit_event(
+ ConnectionEvent{PortID::jack(src_name), PortID::jack(dst_name)});
+ } else {
+ me->_emit_event(
+ DisconnectionEvent{PortID::jack(src_name), PortID::jack(dst_name)});
+ }
}
int
JackLibDriver::on_xrun(void* const driver)
{
- auto* const me = static_cast<JackLibDriver*>(driver);
+ auto* const me = static_cast<JackLibDriver*>(driver);
- ++me->_xruns;
+ ++me->_xruns;
- return 0;
+ return 0;
}
void
JackLibDriver::on_shutdown(void* const driver)
{
- auto* const me = static_cast<JackLibDriver*>(driver);
+ auto* const me = static_cast<JackLibDriver*>(driver);
- /* Note that the JACK documentation lies about this situation. It says the
- client must not call jack_client_close() here... except that is exactly
- what libjack does if a shutdown callback isn't registered. Despite
- that, doing so here hangs forever. Handling it "properly" like a signal
- handler and calling jack_client_close() in another thread also hangs.
+ /* Note that the JACK documentation lies about this situation. It says the
+ client must not call jack_client_close() here... except that is exactly
+ what libjack does if a shutdown callback isn't registered. Despite
+ that, doing so here hangs forever. Handling it "properly" like a signal
+ handler and calling jack_client_close() in another thread also hangs.
- So, since JACK is a hot mess and it's impossible to gracefully handle
- this situation, just leak the client. */
+ So, since JACK is a hot mess and it's impossible to gracefully handle
+ this situation, just leak the client. */
- std::lock_guard<std::mutex> lock{me->_shutdown_mutex};
+ std::lock_guard<std::mutex> lock{me->_shutdown_mutex};
- me->_client = nullptr;
- me->_is_activated = false;
+ me->_client = nullptr;
+ me->_is_activated = false;
- me->_emit_event(DriverDetachmentEvent{ClientType::jack});
+ me->_emit_event(DriverDetachmentEvent{ClientType::jack});
}
} // namespace
@@ -497,8 +497,8 @@ JackLibDriver::on_shutdown(void* const driver)
std::unique_ptr<AudioDriver>
make_jack_driver(ILog& log, Driver::EventSink emit_event)
{
- return std::unique_ptr<AudioDriver>{
- new JackLibDriver{log, std::move(emit_event)}};
+ return std::unique_ptr<AudioDriver>{
+ new JackLibDriver{log, std::move(emit_event)}};
}
} // namespace patchage