diff options
author | Chris Robinson <[email protected]> | 2021-02-13 00:33:51 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2021-02-13 00:45:38 -0800 |
commit | 698799da0edc40afd094b6f0a7530a1df7837d35 (patch) | |
tree | 35607d0560da95f4cf7ddd4dec2ea28d3730bda9 /alc/backends/jack.cpp | |
parent | 76d9897bc34b6625caee253ebcde4cb233e17ab0 (diff) |
Add enumeration to the JACK backend
Port names seem to be structured as <device_name:channel_name> or
<app_name:channel_name>. I'm not sure if this is always the case, but it seems
some other apps expect something like this.
Also fix the port selection to exclude MIDI ports and allow non-physical ports.
Diffstat (limited to 'alc/backends/jack.cpp')
-rw-r--r-- | alc/backends/jack.cpp | 119 |
1 files changed, 84 insertions, 35 deletions
diff --git a/alc/backends/jack.cpp b/alc/backends/jack.cpp index 17b1f139..42509d77 100644 --- a/alc/backends/jack.cpp +++ b/alc/backends/jack.cpp @@ -34,6 +34,7 @@ #include "alcmain.h" #include "alu.h" #include "alconfig.h" +#include "compat.h" #include "core/logging.h" #include "dynload.h" #include "ringbuffer.h" @@ -45,9 +46,6 @@ namespace { -constexpr char jackDevice[] = "JACK Default"; - - #ifdef HAVE_DYNLOAD #define JACK_FUNCS(MAGIC) \ MAGIC(jack_client_open); \ @@ -160,21 +158,55 @@ struct DeviceEntry { al::vector<DeviceEntry> PlaybackList; -void EnumerateDevices(al::vector<DeviceEntry> &list) +void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list) { - al::vector<DeviceEntry>{}.swap(list); + std::remove_reference_t<decltype(list)>{}.swap(list); - list.emplace_back(DeviceEntry{jackDevice, ""}); + const char **ports{jack_get_ports(client, nullptr, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput)}; + if(ports) + { + for(size_t i{0};ports[i];++i) + { + const char *sep{std::strchr(ports[i], ':')}; + if(!sep || ports[i] == sep) continue; + + const al::span<const char> portdev{ports[i], sep}; + auto check_name = [portdev](const DeviceEntry &entry) -> bool + { + const size_t len{portdev.size()}; + return entry.mName.length() == len + && entry.mName.compare(0, len, portdev.data(), len) == 0; + }; + if(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend()) + continue; + + std::string name{portdev.data(), portdev.size()}; + list.emplace_back(DeviceEntry{name, name+":"}); + const auto &entry = list.back(); + TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str()); + } + /* There are ports but couldn't get device names from them. Add a + * generic entry. + */ + if(ports[0] && list.empty()) + { + WARN("No device names found in available ports, adding a generic name.\n"); + list.emplace_back(DeviceEntry{"JACK", ""}); + } + jack_free(ports); + } + + auto listopt = ConfigValueStr(nullptr, "jack", "custom-devices"); + if(!listopt) return; - std::string customList{ConfigValueStr(nullptr, "jack", "custom-devices").value_or("")}; size_t strpos{0}; - while(strpos < customList.size()) + while(strpos < listopt->size()) { - size_t nextpos{customList.find(';', strpos)}; - size_t seppos{customList.find('=', strpos)}; + size_t nextpos{listopt->find(';', strpos)}; + size_t seppos{listopt->find('=', strpos)}; if(seppos >= nextpos || seppos == strpos) { - const std::string entry{customList.substr(strpos, nextpos-strpos)}; + const std::string entry{listopt->substr(strpos, nextpos-strpos)}; ERR("Invalid device entry: \"%s\"\n", entry.c_str()); if(nextpos != std::string::npos) ++nextpos; strpos = nextpos; @@ -182,18 +214,18 @@ void EnumerateDevices(al::vector<DeviceEntry> &list) } size_t count{1}; - std::string name{customList.substr(strpos, seppos-strpos)}; + std::string name{listopt->substr(strpos, seppos-strpos)}; auto check_name = [&name](const DeviceEntry &entry) -> bool { return entry.mName == name; }; while(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend()) { - name = customList.substr(strpos, seppos-strpos); + name = listopt->substr(strpos, seppos-strpos); name += " #"; name += std::to_string(++count); } ++seppos; - list.emplace_back(DeviceEntry{std::move(name), customList.substr(seppos, nextpos-seppos)}); + list.emplace_back(DeviceEntry{std::move(name), listopt->substr(seppos, nextpos-seppos)}); const auto &entry = list.back(); TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str()); @@ -354,29 +386,14 @@ void JackPlayback::open(const char *name) { mPortPattern.clear(); - if(!name) - name = jackDevice; - else if(strcmp(name, jackDevice) != 0) - { - if(PlaybackList.empty()) - EnumerateDevices(PlaybackList); - - auto check_name = [name](const DeviceEntry &entry) -> bool - { return entry.mName == name; }; - auto iter = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), check_name); - if(iter == PlaybackList.cend()) - throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%s\" not found", name}; - mPortPattern = iter->mPattern; - } + const PathNamePair &binname = GetProcBinary(); + const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()}; - const char *client_name{"alsoft"}; jack_status_t status; mClient = jack_client_open(client_name, ClientOptions, &status, nullptr); if(mClient == nullptr) throw al::backend_exception{al::backend_error::DeviceError, "Failed to open client connection: 0x%02x", status}; - if((status&JackServerStarted)) TRACE("JACK server started\n"); if((status&JackNameNotUnique)) @@ -385,6 +402,25 @@ void JackPlayback::open(const char *name) TRACE("Client name not unique, got '%s' instead\n", client_name); } + if(PlaybackList.empty()) + EnumerateDevices(mClient, PlaybackList); + + if(!name && !PlaybackList.empty()) + { + name = PlaybackList[0].mName.c_str(); + mPortPattern = PlaybackList[0].mPattern; + } + else + { + auto check_name = [name](const DeviceEntry &entry) -> bool + { return entry.mName == name; }; + auto iter = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), check_name); + if(iter == PlaybackList.cend()) + throw al::backend_exception{al::backend_error::NoDevice, + "Device name \"%s\" not found", name?name:""}; + mPortPattern = iter->mPattern; + } + jack_set_process_callback(mClient, &JackPlayback::processC, this); mDevice->DeviceName = name; @@ -453,8 +489,8 @@ void JackPlayback::start() const char *devname{mDevice->DeviceName.c_str()}; if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true)) { - const char **ports{jack_get_ports(mClient, mPortPattern.c_str(), nullptr, - JackPortIsPhysical|JackPortIsInput)}; + const char **ports{jack_get_ports(mClient, mPortPattern.c_str(), JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput)}; if(ports == nullptr) { jack_deactivate(mClient); @@ -547,10 +583,13 @@ bool JackBackendFactory::init() if(!GetConfigValueBool(nullptr, "jack", "spawn-server", 0)) ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer); + const PathNamePair &binname = GetProcBinary(); + const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()}; + void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr}; jack_set_error_function(jack_msg_handler); jack_status_t status; - jack_client_t *client{jack_client_open("alsoft", ClientOptions, &status, nullptr)}; + jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)}; jack_set_error_function(old_error_cb); if(!client) { @@ -575,10 +614,20 @@ std::string JackBackendFactory::probe(BackendType type) /* Includes null char. */ outnames.append(entry.mName.c_str(), entry.mName.length()+1); }; + + const PathNamePair &binname = GetProcBinary(); + const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()}; + jack_status_t status; switch(type) { case BackendType::Playback: - EnumerateDevices(PlaybackList); + if(jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)}) + { + EnumerateDevices(client, PlaybackList); + jack_client_close(client); + } + else + WARN("jack_client_open() failed, 0x%02x\n", status); std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name); break; case BackendType::Capture: |