update OpenAL-Soft to 1.24.3.

This commit is contained in:
Sasha Szpakowski
2025-05-03 12:51:37 -03:00
parent 375c6f88cd
commit 5e4f3241ac
322 changed files with 54386 additions and 12885 deletions
+164 -155
View File
@@ -23,25 +23,31 @@
#include "pipewire.h"
#include <algorithm>
#include <array>
#include <atomic>
#include <bitset>
#include <cinttypes>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <chrono>
#include <cstdint>
#include <ctime>
#include <list>
#include <functional>
#include <iterator>
#include <memory>
#include <mutex>
#include <optional>
#include <string_view>
#include <thread>
#include <type_traits>
#include <tuple>
#include <utility>
#include "albit.h"
#include "alc/alconfig.h"
#include "alc/backends/base.h"
#include "almalloc.h"
#include "alnumeric.h"
#include "alspan.h"
#include "alstring.h"
#include "core/devformat.h"
@@ -49,6 +55,8 @@
#include "core/helpers.h"
#include "core/logging.h"
#include "dynload.h"
#include "fmt/core.h"
#include "fmt/ranges.h"
#include "opthelpers.h"
#include "ringbuffer.h"
@@ -72,6 +80,7 @@ _Pragma("GCC diagnostic ignored \"-Weverything\"")
#include "spa/buffer/buffer.h"
#include "spa/param/audio/format-utils.h"
#include "spa/param/audio/raw.h"
#include "spa/param/format.h"
#include "spa/param/param.h"
#include "spa/pod/builder.h"
#include "spa/utils/json.h"
@@ -126,6 +135,9 @@ _Pragma("GCC diagnostic pop")
namespace {
template<typename T> [[nodiscard]] constexpr
auto as_const_ptr(T *ptr) noexcept -> std::add_const_t<T>* { return ptr; }
struct PodDynamicBuilder {
private:
std::vector<std::byte> mStorage;
@@ -137,7 +149,7 @@ private:
mStorage.resize(size);
}
catch(...) {
ERR("Failed to resize POD storage\n");
ERR("Failed to resize POD storage");
return -ENOMEM;
}
mPod.data = mStorage.data();
@@ -146,7 +158,7 @@ private:
}
public:
PodDynamicBuilder(uint32_t initSize=0) : mStorage(initSize)
explicit PodDynamicBuilder(uint32_t initSize=1024) : mStorage(initSize)
, mPod{make_pod_builder(mStorage.data(), initSize)}
{
static constexpr auto callbacks{[]
@@ -183,12 +195,13 @@ bool check_version(const char *version)
* future.
*/
int major{0}, minor{0}, revision{0};
/* NOLINTNEXTLINE(cert-err34-c,cppcoreguidelines-pro-type-vararg) */
int ret{sscanf(version, "%d.%d.%d", &major, &minor, &revision)};
return ret == 3 && (major > PW_MAJOR || (major == PW_MAJOR && minor > PW_MINOR)
|| (major == PW_MAJOR && minor == PW_MINOR && revision >= PW_MICRO));
}
#ifdef HAVE_DYNLOAD
#if HAVE_DYNLOAD
#define PWIRE_FUNCS(MAGIC) \
MAGIC(pw_context_connect) \
MAGIC(pw_context_destroy) \
@@ -245,7 +258,7 @@ bool pwire_load()
pwire_handle = LoadLib(pwire_library);
if(!pwire_handle)
{
WARN("Failed to load %s\n", pwire_library);
WARN("Failed to load {}", pwire_library);
return false;
}
@@ -259,7 +272,7 @@ bool pwire_load()
if(!missing_funcs.empty())
{
WARN("Missing expected functions:%s\n", missing_funcs.c_str());
WARN("Missing expected functions:{}", missing_funcs);
CloseLib(pwire_handle);
pwire_handle = nullptr;
return false;
@@ -405,9 +418,10 @@ struct PwStreamDeleter {
};
using PwStreamPtr = std::unique_ptr<pw_stream,PwStreamDeleter>;
/* Enums for bitflags... again... *sigh* */
/* NOLINTBEGIN(*EnumCastOutOfRange) Enums for bitflags... again... *sigh* */
constexpr pw_stream_flags operator|(pw_stream_flags lhs, pw_stream_flags rhs) noexcept
{ return static_cast<pw_stream_flags>(lhs | al::to_underlying(rhs)); }
/* NOLINTEND(*EnumCastOutOfRange) */
constexpr pw_stream_flags& operator|=(pw_stream_flags &lhs, pw_stream_flags rhs) noexcept
{ lhs = lhs | rhs; return lhs; }
@@ -491,7 +505,7 @@ struct NodeProxy {
uint32_t mId{};
PwNodePtr mNode{};
PwNodePtr mNode;
spa_hook mListener{};
NodeProxy(uint32_t id, PwNodePtr node)
@@ -526,7 +540,7 @@ struct MetadataProxy {
uint32_t mId{};
PwMetadataPtr mMetadata{};
PwMetadataPtr mMetadata;
spa_hook mListener{};
MetadataProxy(uint32_t id, PwMetadataPtr mdata)
@@ -547,10 +561,10 @@ struct MetadataProxy {
* to objects being added to or removed from the registry.
*/
struct EventManager {
ThreadMainloop mLoop{};
PwContextPtr mContext{};
PwCorePtr mCore{};
PwRegistryPtr mRegistry{};
ThreadMainloop mLoop;
PwContextPtr mContext;
PwCorePtr mCore;
PwRegistryPtr mRegistry;
spa_hook mRegistryListener{};
spa_hook mCoreListener{};
@@ -579,8 +593,8 @@ struct EventManager {
auto lock() const { return mLoop.lock(); }
auto unlock() const { return mLoop.unlock(); }
[[nodiscard]] inline
bool initIsDone(std::memory_order m=std::memory_order_seq_cst) const noexcept
[[nodiscard]]
auto initIsDone(std::memory_order m=std::memory_order_seq_cst) const noexcept -> bool
{ return mInitDone.load(m); }
/**
@@ -753,7 +767,7 @@ void DeviceNode::Remove(uint32_t id)
{
if(n.mId != id)
return false;
TRACE("Removing device \"%s\"\n", n.mDevName.c_str());
TRACE("Removing device \"{}\"", n.mDevName);
if(gEventHandler.initIsDone(std::memory_order_relaxed))
{
const std::string msg{"Device removed: "+n.mName};
@@ -822,7 +836,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce
const uint podType{get_pod_type(value)};
if(podType != SPA_TYPE_Int)
{
WARN(" Unhandled sample rate POD type: %u\n", podType);
WARN(" Unhandled sample rate POD type: {}", podType);
return;
}
@@ -830,13 +844,13 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce
{
if(nvals != 3)
{
WARN(" Unexpected SPA_CHOICE_Range count: %u\n", nvals);
WARN(" Unexpected SPA_CHOICE_Range count: {}", nvals);
return;
}
auto srates = get_pod_body<int32_t,3>(value);
/* [0] is the default, [1] is the min, and [2] is the max. */
TRACE(" sample rate: %d (range: %d -> %d)\n", srates[0], srates[1], srates[2]);
TRACE(" sample rate: {}, range: {}", srates[0], srates.subspan<1>());
if(!mSampleRate || force_update)
mSampleRate = static_cast<uint>(std::clamp<int>(srates[0], MinOutputRate,
MaxOutputRate));
@@ -847,19 +861,13 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce
{
if(nvals == 0)
{
WARN(" Unexpected SPA_CHOICE_Enum count: %u\n", nvals);
WARN(" Unexpected SPA_CHOICE_Enum count: {}", nvals);
return;
}
auto srates = get_pod_body<int32_t>(value, nvals);
/* [0] is the default, [1...size()-1] are available selections. */
std::string others{(srates.size() > 1) ? std::to_string(srates[1]) : std::string{}};
for(size_t i{2};i < srates.size();++i)
{
others += ", ";
others += std::to_string(srates[i]);
}
TRACE(" sample rate: %d (%s)\n", srates[0], others.c_str());
TRACE(" sample rate: {}, list: {}", srates[0], srates.subspan(1));
/* Pick the first rate listed that's within the allowed range (default
* rate if possible).
*/
@@ -879,19 +887,19 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce
{
if(nvals != 1)
{
WARN(" Unexpected SPA_CHOICE_None count: %u\n", nvals);
WARN(" Unexpected SPA_CHOICE_None count: {}", nvals);
return;
}
auto srates = get_pod_body<int32_t,1>(value);
TRACE(" sample rate: %d\n", srates[0]);
TRACE(" sample rate: {}", srates[0]);
if(!mSampleRate || force_update)
mSampleRate = static_cast<uint>(std::clamp<int>(srates[0], MinOutputRate,
MaxOutputRate));
return;
}
WARN(" Unhandled sample rate choice type: %u\n", choiceType);
WARN(" Unhandled sample rate choice type: {}", choiceType);
}
void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcept
@@ -901,7 +909,7 @@ void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcep
if(choiceType != SPA_CHOICE_None || choiceCount != 1)
{
ERR(" Unexpected positions choice: type=%u, count=%u\n", choiceType, choiceCount);
ERR(" Unexpected positions choice: type={}, count={}", choiceType, choiceCount);
return;
}
@@ -932,7 +940,7 @@ void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcep
else
mChannels = DevFmtMono;
}
TRACE(" %zu position%s for %s%s\n", chanmap.size(), (chanmap.size()==1)?"":"s",
TRACE(" {} position{} for {}{}", chanmap.size(), (chanmap.size()==1)?"":"s",
DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":"");
}
@@ -944,7 +952,7 @@ void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noex
if(choiceType != SPA_CHOICE_None || choiceCount != 1)
{
ERR(" Unexpected positions choice: type=%u, count=%u\n", choiceType, choiceCount);
ERR(" Unexpected positions choice: type={}, count={}", choiceType, choiceCount);
return;
}
@@ -960,7 +968,7 @@ void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noex
else if(*chancount >= 1)
mChannels = DevFmtMono;
}
TRACE(" %d channel%s for %s\n", *chancount, (*chancount==1)?"":"s",
TRACE(" {} channel{} for {}", *chancount, (*chancount==1)?"":"s",
DevFmtChannelsString(mChannels));
}
@@ -999,7 +1007,7 @@ void NodeProxy::infoCallback(void*, const pw_node_info *info) noexcept
ntype = NodeType::Duplex;
else
{
TRACE("Dropping device node %u which became type \"%s\"\n", info->id, media_class);
TRACE("Dropping device node {} which became type \"{}\"", info->id, media_class);
DeviceNode::Remove(info->id);
return;
}
@@ -1018,20 +1026,23 @@ void NodeProxy::infoCallback(void*, const pw_node_info *info) noexcept
serial_id = std::strtoull(serial_str, &serial_end, 0);
if(*serial_end != '\0' || errno == ERANGE)
{
ERR("Unexpected object serial: %s\n", serial_str);
ERR("Unexpected object serial: {}", serial_str);
serial_id = info->id;
}
}
#endif
std::string name;
if(nodeName && *nodeName) name = nodeName;
else name = "PipeWire node #"+std::to_string(info->id);
auto name = std::invoke([nodeName,info]() -> std::string
{
if(nodeName && *nodeName)
return std::string{nodeName};
return fmt::format("PipeWire node #{}", info->id);
});
const char *form_factor{spa_dict_lookup(info->props, PW_KEY_DEVICE_FORM_FACTOR)};
TRACE("Got %s device \"%s\"%s%s%s\n", AsString(ntype), devName ? devName : "(nil)",
TRACE("Got {} device \"{}\"{}{}{}", AsString(ntype), devName ? devName : "(nil)",
form_factor?" (":"", form_factor?form_factor:"", form_factor?")":"");
TRACE(" \"%s\" = ID %" PRIu64 "\n", name.c_str(), serial_id);
TRACE(" \"{}\" = ID {}", name, serial_id);
DeviceNode &node = DeviceNode::Add(info->id);
node.mSerial = serial_id;
@@ -1081,8 +1092,9 @@ void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_po
DeviceNode *node{DeviceNode::Find(mId)};
if(!node) UNLIKELY return;
TRACE("Device ID %" PRIu64 " %s format:\n", node->mSerial,
(id == SPA_PARAM_EnumFormat) ? "enumerable" : "current");
TRACE("Device ID {} {} format{}:", node->mSerial,
(id == SPA_PARAM_EnumFormat) ? "available" : "current",
(id == SPA_PARAM_EnumFormat) ? "s" : "");
const bool force_update{id == SPA_PARAM_Format};
if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_rate)})
@@ -1115,14 +1127,14 @@ auto MetadataProxy::propertyCallback(void*, uint32_t id, const char *key, const
if(!type)
{
TRACE("Default %s device cleared\n", isCapture ? "capture" : "playback");
TRACE("Default {} device cleared", isCapture ? "capture" : "playback");
if(!isCapture) DefaultSinkDevice.clear();
else DefaultSourceDevice.clear();
return 0;
}
if("Spa:String:JSON"sv != type)
{
ERR("Unexpected %s property type: %s\n", key, type);
ERR("Unexpected {} property type: {}", key, type);
return 0;
}
@@ -1153,8 +1165,8 @@ auto MetadataProxy::propertyCallback(void*, uint32_t id, const char *key, const
auto propValue = get_json_string(&std::get<1>(it));
if(!propValue) break;
TRACE("Got default %s device \"%s\"\n", isCapture ? "capture" : "playback",
propValue->c_str());
TRACE("Got default {} device \"{}\"", isCapture ? "capture" : "playback",
*propValue);
if(!isCapture && DefaultSinkDevice != *propValue)
{
if(gEventHandler.mInitDone.load(std::memory_order_relaxed))
@@ -1196,28 +1208,28 @@ bool EventManager::init()
mLoop = ThreadMainloop::Create("PWEventThread");
if(!mLoop)
{
ERR("Failed to create PipeWire event thread loop (errno: %d)\n", errno);
ERR("Failed to create PipeWire event thread loop (errno: {})", errno);
return false;
}
mContext = mLoop.newContext();
if(!mContext)
{
ERR("Failed to create PipeWire event context (errno: %d)\n", errno);
ERR("Failed to create PipeWire event context (errno: {})", errno);
return false;
}
mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
if(!mCore)
{
ERR("Failed to connect PipeWire event context (errno: %d)\n", errno);
ERR("Failed to connect PipeWire event context (errno: {})", errno);
return false;
}
mRegistry = PwRegistryPtr{pw_core_get_registry(mCore.get(), PW_VERSION_REGISTRY, 0)};
if(!mRegistry)
{
ERR("Failed to get PipeWire event registry (errno: %d)\n", errno);
ERR("Failed to get PipeWire event registry (errno: {})", errno);
return false;
}
@@ -1234,7 +1246,7 @@ bool EventManager::init()
if(int res{mLoop.start()})
{
ERR("Failed to start PipeWire event thread loop (res: %d)\n", res);
ERR("Failed to start PipeWire event thread loop (res: {})", res);
return false;
}
@@ -1272,7 +1284,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
if(!isGood)
{
if(!al::contains(className, "/Video"sv) && !al::starts_with(className, "Stream/"sv))
TRACE("Ignoring node class %s\n", media_class);
TRACE("Ignoring node class {}", media_class);
return;
}
@@ -1281,7 +1293,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
version, 0))};
if(!node)
{
ERR("Failed to create node proxy object (errno: %d)\n", errno);
ERR("Failed to create node proxy object (errno: {})", errno);
return;
}
@@ -1304,13 +1316,13 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
if("default"sv != data_class)
{
TRACE("Ignoring metadata \"%s\"\n", data_class);
TRACE("Ignoring metadata \"{}\"", data_class);
return;
}
if(mDefaultMetadata)
{
ERR("Duplicate default metadata\n");
ERR("Duplicate default metadata");
return;
}
@@ -1318,7 +1330,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
type, version, 0))};
if(!mdata)
{
ERR("Failed to create metadata proxy object (errno: %d)\n", errno);
ERR("Failed to create metadata proxy object (errno: {})", errno);
return;
}
@@ -1375,7 +1387,7 @@ spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e u
case DevFmtFloat: info.format = SPA_AUDIO_FORMAT_F32; break;
}
info.rate = device->Frequency;
info.rate = device->mSampleRate;
al::span<const spa_audio_channel> map{};
switch(device->FmtChans)
@@ -1391,6 +1403,7 @@ spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e u
case DevFmtX71: map = X71Map; break;
case DevFmtX714: map = X714Map; break;
case DevFmtX3D71: map = X71Map; break;
case DevFmtX7144:
case DevFmtAmbi3D:
info.flags |= SPA_AUDIO_FLAG_UNPOSITIONED;
info.channels = device->channelsFromFmt();
@@ -1424,7 +1437,7 @@ class PipeWirePlayback final : public BackendBase {
PwStreamPtr mStream;
spa_hook mStreamListener{};
spa_io_rate_match *mRateMatch{};
std::vector<float*> mChannelPtrs;
std::vector<void*> mChannelPtrs;
static constexpr pw_stream_events CreateEvents()
{
@@ -1440,7 +1453,7 @@ class PipeWirePlayback final : public BackendBase {
}
public:
PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
explicit PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
~PipeWirePlayback() final
{
/* Stop the mainloop so the stream can be properly destroyed. */
@@ -1484,7 +1497,7 @@ void PipeWirePlayback::outputCallback() noexcept
uint length{mRateMatch ? mRateMatch->size : 0u};
#endif
/* If no length is specified, use the device's update size as a fallback. */
if(!length) UNLIKELY length = mDevice->UpdateSize;
if(!length) UNLIKELY length = mDevice->mUpdateSize;
/* For planar formats, each datas[] seems to contain one channel, so store
* the pointers in an array. Limit the render length in case the available
@@ -1495,7 +1508,7 @@ void PipeWirePlayback::outputCallback() noexcept
for(const auto &data : datas)
{
length = std::min(length, data.maxsize/uint{sizeof(float)});
*chanptr_end = static_cast<float*>(data.data);
*chanptr_end = data.data;
++chanptr_end;
data.chunk->offset = 0;
@@ -1552,7 +1565,7 @@ void PipeWirePlayback::open(std::string_view name)
auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name);
if(match == devlist.cend())
throw al::backend_exception{al::backend_error::NoDevice,
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
"Device name \"{}\" not found", name};
targetid = match->mSerial;
devname = match->mName;
@@ -1560,15 +1573,15 @@ void PipeWirePlayback::open(std::string_view name)
if(!mLoop)
{
const uint count{OpenCount.fetch_add(1, std::memory_order_relaxed)};
const std::string thread_name{"ALSoftP" + std::to_string(count)};
const auto count = OpenCount.fetch_add(1u, std::memory_order_relaxed);
const auto thread_name = fmt::format("ALSoftP{}", count);
mLoop = ThreadMainloop::Create(thread_name.c_str());
if(!mLoop)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to create PipeWire mainloop (errno: %d)", errno};
"Failed to create PipeWire mainloop (errno: {})", errno};
if(int res{mLoop.start()})
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to start PipeWire mainloop (res: %d)", res};
"Failed to start PipeWire mainloop (res: {})", res};
}
MainloopUniqueLock mlock{mLoop};
if(!mContext)
@@ -1577,14 +1590,14 @@ void PipeWirePlayback::open(std::string_view name)
mContext = mLoop.newContext(cprops);
if(!mContext)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to create PipeWire event context (errno: %d)\n", errno};
"Failed to create PipeWire event context (errno: {})\n", errno};
}
if(!mCore)
{
mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
if(!mCore)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to connect PipeWire event context (errno: %d)\n", errno};
"Failed to connect PipeWire event context (errno: {})\n", errno};
}
mlock.unlock();
@@ -1592,9 +1605,9 @@ void PipeWirePlayback::open(std::string_view name)
mTargetId = targetid;
if(!devname.empty())
mDevice->DeviceName = std::move(devname);
mDeviceName = std::move(devname);
else
mDevice->DeviceName = "PipeWire Output"sv;
mDeviceName = "PipeWire Output"sv;
}
bool PipeWirePlayback::reset()
@@ -1626,13 +1639,13 @@ bool PipeWirePlayback::reset()
if(!mDevice->Flags.test(FrequencyRequest) && match->mSampleRate > 0)
{
/* Scale the update size if the sample rate changes. */
const double scale{static_cast<double>(match->mSampleRate) / mDevice->Frequency};
const double updatesize{std::round(mDevice->UpdateSize * scale)};
const double buffersize{std::round(mDevice->BufferSize * scale)};
const double scale{static_cast<double>(match->mSampleRate) / mDevice->mSampleRate};
const double updatesize{std::round(mDevice->mUpdateSize * scale)};
const double buffersize{std::round(mDevice->mBufferSize * scale)};
mDevice->Frequency = match->mSampleRate;
mDevice->UpdateSize = static_cast<uint>(std::clamp(updatesize, 64.0, 8192.0));
mDevice->BufferSize = static_cast<uint>(std::max(buffersize, 128.0));
mDevice->mSampleRate = match->mSampleRate;
mDevice->mUpdateSize = static_cast<uint>(std::clamp(updatesize, 64.0, 8192.0));
mDevice->mBufferSize = static_cast<uint>(std::max(buffersize, 128.0));
}
if(!mDevice->Flags.test(ChannelsRequest) && match->mChannels != InvalidChannelConfig)
mDevice->FmtChans = match->mChannels;
@@ -1644,12 +1657,10 @@ bool PipeWirePlayback::reset()
/* Force planar 32-bit float output for playback. This is what PipeWire
* handles internally, and it's easier for us too.
*/
spa_audio_info_raw info{make_spa_info(mDevice, is51rear, ForceF32Planar)};
auto info = spa_audio_info_raw{make_spa_info(mDevice, is51rear, ForceF32Planar)};
static constexpr uint32_t pod_buffer_size{1024};
PodDynamicBuilder b(pod_buffer_size);
const spa_pod *params{spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info)};
auto b = PodDynamicBuilder{};
auto params = as_const_ptr(spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info));
if(!params)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to set PipeWire audio format parameters"};
@@ -1658,7 +1669,7 @@ bool PipeWirePlayback::reset()
* be useful?
*/
auto&& binary = GetProcBinary();
const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"};
const char *appname{!binary.fname.empty() ? binary.fname.c_str() : "OpenAL Soft"};
pw_properties *props{pw_properties_new(PW_KEY_NODE_NAME, appname,
PW_KEY_NODE_DESCRIPTION, appname,
PW_KEY_MEDIA_TYPE, "Audio",
@@ -1668,11 +1679,11 @@ bool PipeWirePlayback::reset()
nullptr)};
if(!props)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to create PipeWire stream properties (errno: %d)", errno};
"Failed to create PipeWire stream properties (errno: {})", errno};
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", mDevice->UpdateSize,
mDevice->Frequency);
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency);
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", mDevice->mUpdateSize,
mDevice->mSampleRate);
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->mSampleRate);
#ifdef PW_KEY_TARGET_OBJECT
pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%" PRIu64, mTargetId);
#else
@@ -1684,17 +1695,17 @@ bool PipeWirePlayback::reset()
mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Playback Stream", props)};
if(!mStream)
throw al::backend_exception{al::backend_error::NoDevice,
"Failed to create PipeWire stream (errno: %d)", errno};
"Failed to create PipeWire stream (errno: {})", errno};
static constexpr pw_stream_events streamEvents{CreateEvents()};
pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this);
pw_stream_flags flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
| PW_STREAM_FLAG_MAP_BUFFERS};
if(GetConfigValueBool(mDevice->DeviceName, "pipewire", "rt-mix", false))
if(GetConfigValueBool(mDevice->mDeviceName, "pipewire", "rt-mix", false))
flags |= PW_STREAM_FLAG_RT_PROCESS;
if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, PwIdAny, flags, &params, 1)})
throw al::backend_exception{al::backend_error::DeviceError,
"Error connecting PipeWire stream (res: %d)", res};
"Error connecting PipeWire stream (res: {})", res};
/* Wait for the stream to become paused (ready to start streaming). */
plock.wait([stream=mStream.get()]()
@@ -1703,12 +1714,12 @@ bool PipeWirePlayback::reset()
pw_stream_state state{pw_stream_get_state(stream, &error)};
if(state == PW_STREAM_STATE_ERROR)
throw al::backend_exception{al::backend_error::DeviceError,
"Error connecting PipeWire stream: \"%s\"", error};
"Error connecting PipeWire stream: \"{}\"", error};
return state == PW_STREAM_STATE_PAUSED;
});
/* TODO: Update mDevice->UpdateSize with the stream's quantum, and
* mDevice->BufferSize with the total known buffering delay from the head
/* TODO: Update mDevice->mUpdateSize with the stream's quantum, and
* mDevice->mBufferSize with the total known buffering delay from the head
* of this playback stream to the tail of the device output.
*
* This info is apparently not available until after the stream starts.
@@ -1727,7 +1738,7 @@ void PipeWirePlayback::start()
MainloopUniqueLock plock{mLoop};
if(int res{pw_stream_set_active(mStream.get(), true)})
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to start PipeWire stream (res: %d)", res};
"Failed to start PipeWire stream (res: {})", res};
/* Wait for the stream to start playing (would be nice to not, but we need
* the actual update size which is only available after starting).
@@ -1738,7 +1749,7 @@ void PipeWirePlayback::start()
pw_stream_state state{pw_stream_get_state(stream, &error)};
if(state == PW_STREAM_STATE_ERROR)
throw al::backend_exception{al::backend_error::DeviceError,
"PipeWire stream error: %s", error ? error : "(unknown)"};
"PipeWire stream error: {}", error ? error : "(unknown)"};
return state == PW_STREAM_STATE_STREAMING;
});
@@ -1753,7 +1764,7 @@ void PipeWirePlayback::start()
pw_time ptime{};
if(int res{pw_stream_get_time_n(mStream.get(), &ptime, sizeof(ptime))})
{
ERR("Failed to get PipeWire stream time (res: %d)\n", res);
ERR("Failed to get PipeWire stream time (res: {})", res);
break;
}
@@ -1768,11 +1779,11 @@ void PipeWirePlayback::start()
const uint totalbuffers{ptime.avail_buffers + ptime.queued_buffers};
/* Ensure the delay is in sample frames. */
const uint64_t delay{static_cast<uint64_t>(ptime.delay) * mDevice->Frequency *
const uint64_t delay{static_cast<uint64_t>(ptime.delay) * mDevice->mSampleRate *
ptime.rate.num / ptime.rate.denom};
mDevice->UpdateSize = updatesize;
mDevice->BufferSize = static_cast<uint>(ptime.buffered + delay +
mDevice->mUpdateSize = updatesize;
mDevice->mBufferSize = static_cast<uint>(ptime.buffered + delay +
uint64_t{totalbuffers}*updatesize);
break;
}
@@ -1783,17 +1794,17 @@ void PipeWirePlayback::start()
if(ptime.rate.denom > 0 && updatesize > 0)
{
/* Ensure the delay is in sample frames. */
const uint64_t delay{static_cast<uint64_t>(ptime.delay) * mDevice->Frequency *
const uint64_t delay{static_cast<uint64_t>(ptime.delay) * mDevice->mSampleRate *
ptime.rate.num / ptime.rate.denom};
mDevice->UpdateSize = updatesize;
mDevice->BufferSize = static_cast<uint>(delay + updatesize);
mDevice->mUpdateSize = updatesize;
mDevice->mBufferSize = static_cast<uint>(delay + updatesize);
break;
}
#endif
if(!--wait_count)
{
ERR("Timeout getting PipeWire stream buffering info\n");
ERR("Timeout getting PipeWire stream buffering info");
break;
}
@@ -1807,7 +1818,7 @@ void PipeWirePlayback::stop()
{
MainloopUniqueLock plock{mLoop};
if(int res{pw_stream_set_active(mStream.get(), false)})
ERR("Failed to stop PipeWire stream (res: %d)\n", res);
ERR("Failed to stop PipeWire stream (res: {})", res);
/* Wait for the stream to stop playing. */
plock.wait([stream=mStream.get()]()
@@ -1828,7 +1839,7 @@ ClockLatency PipeWirePlayback::getClockLatency()
{
MainloopLockGuard looplock{mLoop};
if(int res{pw_stream_get_time_n(mStream.get(), &ptime, sizeof(ptime))})
ERR("Failed to get PipeWire stream time (res: %d)\n", res);
ERR("Failed to get PipeWire stream time (res: {})", res);
}
/* Now get the mixer time and the CLOCK_MONOTONIC time atomically (i.e. the
@@ -1856,7 +1867,7 @@ ClockLatency PipeWirePlayback::getClockLatency()
*/
ptime.now = monoclock.count();
curtic = mixtime;
delay = nanoseconds{seconds{mDevice->BufferSize}} / mDevice->Frequency;
delay = nanoseconds{seconds{mDevice->mBufferSize}} / mDevice->mSampleRate;
}
else
{
@@ -1916,7 +1927,7 @@ class PipeWireCapture final : public BackendBase {
PwStreamPtr mStream;
spa_hook mStreamListener{};
RingBufferPtr mRing{};
RingBufferPtr mRing;
static constexpr pw_stream_events CreateEvents()
{
@@ -1930,7 +1941,7 @@ class PipeWireCapture final : public BackendBase {
}
public:
PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { }
explicit PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { }
~PipeWireCapture() final { if(mLoop) mLoop.stop(); }
};
@@ -2017,7 +2028,7 @@ void PipeWireCapture::open(std::string_view name)
}
if(match == devlist.cend())
throw al::backend_exception{al::backend_error::NoDevice,
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
"Device name \"{}\" not found", name};
targetid = match->mSerial;
if(match->mType != NodeType::Sink) devname = match->mName;
@@ -2026,15 +2037,15 @@ void PipeWireCapture::open(std::string_view name)
if(!mLoop)
{
const uint count{OpenCount.fetch_add(1, std::memory_order_relaxed)};
const std::string thread_name{"ALSoftC" + std::to_string(count)};
const auto count = OpenCount.fetch_add(1u, std::memory_order_relaxed);
const auto thread_name = fmt::format("ALSoftC{}", count);
mLoop = ThreadMainloop::Create(thread_name.c_str());
if(!mLoop)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to create PipeWire mainloop (errno: %d)", errno};
"Failed to create PipeWire mainloop (errno: {})", errno};
if(int res{mLoop.start()})
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to start PipeWire mainloop (res: %d)", res};
"Failed to start PipeWire mainloop (res: {})", res};
}
MainloopUniqueLock mlock{mLoop};
if(!mContext)
@@ -2043,14 +2054,14 @@ void PipeWireCapture::open(std::string_view name)
mContext = mLoop.newContext(cprops);
if(!mContext)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to create PipeWire event context (errno: %d)\n", errno};
"Failed to create PipeWire event context (errno: {})\n", errno};
}
if(!mCore)
{
mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
if(!mCore)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to connect PipeWire event context (errno: %d)\n", errno};
"Failed to connect PipeWire event context (errno: {})\n", errno};
}
mlock.unlock();
@@ -2058,9 +2069,9 @@ void PipeWireCapture::open(std::string_view name)
mTargetId = targetid;
if(!devname.empty())
mDevice->DeviceName = std::move(devname);
mDeviceName = std::move(devname);
else
mDevice->DeviceName = "PipeWire Input"sv;
mDeviceName = "PipeWire Input"sv;
bool is51rear{false};
@@ -2075,19 +2086,16 @@ void PipeWireCapture::open(std::string_view name)
if(match != devlist.cend())
is51rear = match->mIs51Rear;
}
spa_audio_info_raw info{make_spa_info(mDevice, is51rear, UseDevType)};
auto info = spa_audio_info_raw{make_spa_info(mDevice, is51rear, UseDevType)};
static constexpr uint32_t pod_buffer_size{1024};
PodDynamicBuilder b(pod_buffer_size);
std::array params{static_cast<const spa_pod*>(spa_format_audio_raw_build(b.get(),
SPA_PARAM_EnumFormat, &info))};
if(!params[0])
auto b = PodDynamicBuilder{};
auto params = as_const_ptr(spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info));
if(!params)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to set PipeWire audio format parameters"};
auto&& binary = GetProcBinary();
const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"};
const char *appname{!binary.fname.empty() ? binary.fname.c_str() : "OpenAL Soft"};
pw_properties *props{pw_properties_new(
PW_KEY_NODE_NAME, appname,
PW_KEY_NODE_DESCRIPTION, appname,
@@ -2098,15 +2106,15 @@ void PipeWireCapture::open(std::string_view name)
nullptr)};
if(!props)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to create PipeWire stream properties (errno: %d)", errno};
"Failed to create PipeWire stream properties (errno: {})", errno};
/* We don't actually care what the latency/update size is, as long as it's
* reasonable. Unfortunately, when unspecified PipeWire seems to default to
* around 40ms, which isn't great. So request 20ms instead.
*/
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", (mDevice->Frequency+25) / 50,
mDevice->Frequency);
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency);
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", (mDevice->mSampleRate+25) / 50,
mDevice->mSampleRate);
pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->mSampleRate);
#ifdef PW_KEY_TARGET_OBJECT
pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%" PRIu64, mTargetId);
#else
@@ -2117,15 +2125,15 @@ void PipeWireCapture::open(std::string_view name)
mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Capture Stream", props)};
if(!mStream)
throw al::backend_exception{al::backend_error::NoDevice,
"Failed to create PipeWire stream (errno: %d)", errno};
"Failed to create PipeWire stream (errno: {})", errno};
static constexpr pw_stream_events streamEvents{CreateEvents()};
pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this);
constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
| PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS};
if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, params.data(), 1)})
if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, &params, 1)})
throw al::backend_exception{al::backend_error::DeviceError,
"Error connecting PipeWire stream (res: %d)", res};
"Error connecting PipeWire stream (res: {})", res};
/* Wait for the stream to become paused (ready to start streaming). */
plock.wait([stream=mStream.get()]()
@@ -2134,7 +2142,7 @@ void PipeWireCapture::open(std::string_view name)
pw_stream_state state{pw_stream_get_state(stream, &error)};
if(state == PW_STREAM_STATE_ERROR)
throw al::backend_exception{al::backend_error::DeviceError,
"Error connecting PipeWire stream: \"%s\"", error};
"Error connecting PipeWire stream: \"{}\"", error};
return state == PW_STREAM_STATE_PAUSED;
});
plock.unlock();
@@ -2142,7 +2150,7 @@ void PipeWireCapture::open(std::string_view name)
setDefaultWFXChannelOrder();
/* Ensure at least a 100ms capture buffer. */
mRing = RingBuffer::Create(std::max(mDevice->Frequency/10u, mDevice->BufferSize),
mRing = RingBuffer::Create(std::max(mDevice->mSampleRate/10u, mDevice->mBufferSize),
mDevice->frameSizeFromFmt(), false);
}
@@ -2152,7 +2160,7 @@ void PipeWireCapture::start()
MainloopUniqueLock plock{mLoop};
if(int res{pw_stream_set_active(mStream.get(), true)})
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to start PipeWire stream (res: %d)", res};
"Failed to start PipeWire stream (res: {})", res};
plock.wait([stream=mStream.get()]()
{
@@ -2160,7 +2168,7 @@ void PipeWireCapture::start()
pw_stream_state state{pw_stream_get_state(stream, &error)};
if(state == PW_STREAM_STATE_ERROR)
throw al::backend_exception{al::backend_error::DeviceError,
"PipeWire stream error: %s", error ? error : "(unknown)"};
"PipeWire stream error: {}", error ? error : "(unknown)"};
return state == PW_STREAM_STATE_STREAMING;
});
}
@@ -2169,7 +2177,7 @@ void PipeWireCapture::stop()
{
MainloopUniqueLock plock{mLoop};
if(int res{pw_stream_set_active(mStream.get(), false)})
ERR("Failed to stop PipeWire stream (res: %d)\n", res);
ERR("Failed to stop PipeWire stream (res: {})", res);
plock.wait([stream=mStream.get()]()
{ return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; });
@@ -2192,11 +2200,11 @@ bool PipeWireBackendFactory::init()
const char *version{pw_get_library_version()};
if(!check_version(version))
{
WARN("PipeWire version \"%s\" too old (%s or newer required)\n", version,
WARN("PipeWire version \"{}\" too old ({} or newer required)", version,
pw_get_headers_version());
return false;
}
TRACE("Found PipeWire version \"%s\" (%s or newer)\n", version, pw_get_headers_version());
TRACE("Found PipeWire version \"{}\" ({} or newer)", version, pw_get_headers_version());
pw_init(nullptr, nullptr);
if(!gEventHandler.init())
@@ -2209,7 +2217,7 @@ bool PipeWireBackendFactory::init()
/* TODO: Temporary warning, until PipeWire gets a proper way to report
* audio support.
*/
WARN("No audio support detected in PipeWire. See the PipeWire options in alsoftrc.sample if this is wrong.\n");
WARN("No audio support detected in PipeWire. See the PipeWire options in alsoftrc.sample if this is wrong.");
return false;
}
return true;
@@ -2218,9 +2226,9 @@ bool PipeWireBackendFactory::init()
bool PipeWireBackendFactory::querySupport(BackendType type)
{ return type == BackendType::Playback || type == BackendType::Capture; }
std::string PipeWireBackendFactory::probe(BackendType type)
auto PipeWireBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
{
std::string outnames;
std::vector<std::string> outnames;
gEventHandler.waitForInit();
EventWatcherLockGuard evtlock{gEventHandler};
@@ -2241,31 +2249,31 @@ std::string PipeWireBackendFactory::probe(BackendType type)
case BackendType::Playback:
defmatch = std::find_if(defmatch, devlist.cend(), match_defsink);
if(defmatch != devlist.cend())
{
/* Includes null char. */
outnames.append(defmatch->mName.c_str(), defmatch->mName.length()+1);
}
outnames.emplace_back(defmatch->mName);
for(auto iter = devlist.cbegin();iter != devlist.cend();++iter)
{
if(iter != defmatch && iter->mType != NodeType::Source)
outnames.append(iter->mName.c_str(), iter->mName.length()+1);
outnames.emplace_back(iter->mName);
}
break;
case BackendType::Capture:
outnames.reserve(devlist.size());
defmatch = std::find_if(defmatch, devlist.cend(), match_defsource);
if(defmatch != devlist.cend())
{
if(defmatch->mType == NodeType::Sink)
outnames.append(GetMonitorPrefix());
outnames.append(defmatch->mName.c_str(), defmatch->mName.length()+1);
outnames.emplace_back(std::string{GetMonitorPrefix()}+defmatch->mName);
else
outnames.emplace_back(defmatch->mName);
}
for(auto iter = devlist.cbegin();iter != devlist.cend();++iter)
{
if(iter != defmatch)
{
if(iter->mType == NodeType::Sink)
outnames.append(GetMonitorPrefix());
outnames.append(iter->mName.c_str(), iter->mName.length()+1);
outnames.emplace_back(std::string{GetMonitorPrefix()}+iter->mName);
else
outnames.emplace_back(iter->mName);
}
}
break;
@@ -2274,6 +2282,7 @@ std::string PipeWireBackendFactory::probe(BackendType type)
return outnames;
}
BackendPtr PipeWireBackendFactory::createBackend(DeviceBase *device, BackendType type)
{
if(type == BackendType::Playback)