diff options
author | Sven Göthel <[email protected]> | 2024-01-05 13:52:12 +0100 |
---|---|---|
committer | Sven Göthel <[email protected]> | 2024-01-05 13:52:12 +0100 |
commit | ec98cdacc85ff0202852472c7756586437912f22 (patch) | |
tree | 42414746a27ab35cb8cdbc95af521d74821e57f4 /alc/backends/pipewire.cpp | |
parent | fd5269bec9a5fe4815974b1786a037e6a247bfd2 (diff) | |
parent | b82cd2e60edb8fbe5fdd3567105ae76a016a554c (diff) |
Diffstat (limited to 'alc/backends/pipewire.cpp')
-rw-r--r-- | alc/backends/pipewire.cpp | 132 |
1 files changed, 74 insertions, 58 deletions
diff --git a/alc/backends/pipewire.cpp b/alc/backends/pipewire.cpp index 6a001d7a..55bcf6f4 100644 --- a/alc/backends/pipewire.cpp +++ b/alc/backends/pipewire.cpp @@ -28,12 +28,12 @@ #include <cstring> #include <cerrno> #include <chrono> +#include <cstdint> #include <ctime> #include <list> #include <memory> #include <mutex> #include <optional> -#include <stdint.h> #include <thread> #include <type_traits> #include <utility> @@ -76,6 +76,10 @@ _Pragma("GCC diagnostic ignored \"-Weverything\"") #include "spa/pod/builder.h" #include "spa/utils/json.h" +/* NOLINTBEGIN : All kinds of unsafe C stuff here from PipeWire headers + * (function-like macros, C style casts in macros, etc), which we can't do + * anything about except wrap into inline functions. + */ namespace { /* Wrap some nasty macros here too... */ template<typename ...Args> @@ -117,6 +121,7 @@ constexpr auto make_pod_builder(void *data, uint32_t size) noexcept constexpr auto PwIdAny = PW_ID_ANY; } // namespace +/* NOLINTEND */ _Pragma("GCC diagnostic pop") namespace { @@ -169,8 +174,10 @@ using std::chrono::milliseconds; using std::chrono::nanoseconds; using uint = unsigned int; +/* NOLINTBEGIN(*-avoid-c-arrays) */ constexpr char pwireDevice[] = "PipeWire Output"; constexpr char pwireInput[] = "PipeWire Input"; +/* NOLINTEND(*-avoid-c-arrays) */ bool check_version(const char *version) @@ -238,7 +245,7 @@ bool pwire_load() if(pwire_handle) return true; - static constexpr char pwire_library[] = "libpipewire-0.3.so.0"; + const char *pwire_library{"libpipewire-0.3.so.0"}; std::string missing_funcs; pwire_handle = LoadLib(pwire_library); @@ -249,7 +256,7 @@ bool pwire_load() } #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast<decltype(p##f)>(GetSymbol(pwire_handle, #f)); \ + p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pwire_handle, #f)); \ if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0); PWIRE_FUNCS(LOAD_FUNC) @@ -367,11 +374,11 @@ To as(From) noexcept = delete; * - pw_metadata */ template<> -pw_proxy* as(pw_registry *reg) noexcept { return al::bit_cast<pw_proxy*>(reg); } +pw_proxy* as(pw_registry *reg) noexcept { return reinterpret_cast<pw_proxy*>(reg); } template<> -pw_proxy* as(pw_node *node) noexcept { return al::bit_cast<pw_proxy*>(node); } +pw_proxy* as(pw_node *node) noexcept { return reinterpret_cast<pw_proxy*>(node); } template<> -pw_proxy* as(pw_metadata *mdata) noexcept { return al::bit_cast<pw_proxy*>(mdata); } +pw_proxy* as(pw_metadata *mdata) noexcept { return reinterpret_cast<pw_proxy*>(mdata); } struct PwContextDeleter { @@ -434,9 +441,11 @@ public: explicit operator bool() const noexcept { return mLoop != nullptr; } + [[nodiscard]] auto start() const { return pw_thread_loop_start(mLoop); } auto stop() const { return pw_thread_loop_stop(mLoop); } + [[nodiscard]] auto getLoop() const { return pw_thread_loop_get_loop(mLoop); } auto lock() const { return pw_thread_loop_lock(mLoop); } @@ -501,8 +510,8 @@ struct NodeProxy { /* Track changes to the enumerable and current formats (indicates the * default and active format, which is what we're interested in). */ - uint32_t fmtids[]{SPA_PARAM_EnumFormat, SPA_PARAM_Format}; - ppw_node_subscribe_params(mNode.get(), std::data(fmtids), std::size(fmtids)); + std::array<uint32_t,2> fmtids{{SPA_PARAM_EnumFormat, SPA_PARAM_Format}}; + ppw_node_subscribe_params(mNode.get(), fmtids.data(), fmtids.size()); } ~NodeProxy() { spa_hook_remove(&mListener); } @@ -765,25 +774,32 @@ void DeviceNode::Remove(uint32_t id) } -const spa_audio_channel MonoMap[]{ +constexpr std::array MonoMap{ SPA_AUDIO_CHANNEL_MONO -}, StereoMap[] { +}; +constexpr std::array StereoMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR -}, QuadMap[]{ +}; +constexpr std::array QuadMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR -}, X51Map[]{ +}; +constexpr std::array X51Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X51RearMap[]{ +}; +constexpr std::array X51RearMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR -}, X61Map[]{ +}; +constexpr std::array X61Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RC, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X71Map[]{ +}; +constexpr std::array X71Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X714Map[]{ +}; +constexpr std::array X714Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, SPA_AUDIO_CHANNEL_TFL, SPA_AUDIO_CHANNEL_TFR, SPA_AUDIO_CHANNEL_TRL, SPA_AUDIO_CHANNEL_TRR @@ -793,10 +809,10 @@ const spa_audio_channel MonoMap[]{ * Checks if every channel in 'map1' exists in 'map0' (that is, map0 is equal * to or a superset of map1). */ -template<size_t N> -bool MatchChannelMap(const al::span<const uint32_t> map0, const spa_audio_channel (&map1)[N]) +bool MatchChannelMap(const al::span<const uint32_t> map0, + const al::span<const spa_audio_channel> map1) { - if(map0.size() < N) + if(map0.size() < map1.size()) return false; for(const spa_audio_channel chid : map1) { @@ -831,7 +847,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce /* [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]); if(!mSampleRate || force_update) - mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); + mSampleRate = static_cast<uint>(clampi(srates[0], MinOutputRate, MaxOutputRate)); return; } @@ -857,7 +873,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce */ for(const auto &rate : srates) { - if(rate >= MIN_OUTPUT_RATE && rate <= MAX_OUTPUT_RATE) + if(rate >= int{MinOutputRate} && rate <= int{MaxOutputRate}) { if(!mSampleRate || force_update) mSampleRate = static_cast<uint>(rate); @@ -878,7 +894,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce TRACE(" sample rate: %d\n", srates[0]); if(!mSampleRate || force_update) - mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); + mSampleRate = static_cast<uint>(clampi(srates[0], MinOutputRate, MaxOutputRate)); return; } @@ -956,6 +972,7 @@ void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noex } +/* NOLINTBEGIN(*-avoid-c-arrays) */ constexpr char MonitorPrefix[]{"Monitor of "}; constexpr auto MonitorPrefixLen = std::size(MonitorPrefix) - 1; constexpr char AudioSinkClass[]{"Audio/Sink"}; @@ -963,6 +980,7 @@ constexpr char AudioSourceClass[]{"Audio/Source"}; constexpr char AudioSourceVirtualClass[]{"Audio/Source/Virtual"}; constexpr char AudioDuplexClass[]{"Audio/Duplex"}; constexpr char StreamClass[]{"Stream/"}; +/* NOLINTEND(*-avoid-c-arrays) */ void NodeProxy::infoCallback(const pw_node_info *info) noexcept { @@ -1070,8 +1088,11 @@ void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_po if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_position)}) node->parsePositions(&prop->value, force_update); - else if((prop=spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels)) != nullptr) - node->parseChannelCount(&prop->value, force_update); + else + { + prop = spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels); + if(prop) node->parseChannelCount(&prop->value, force_update); + } } } @@ -1103,8 +1124,8 @@ int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *ty return 0; } - spa_json it[2]{}; - spa_json_init(&it[0], value, strlen(value)); + std::array<spa_json,2> it{}; + spa_json_init(it.data(), value, strlen(value)); if(spa_json_enter_object(&it[0], &it[1]) <= 0) return 0; @@ -1407,8 +1428,7 @@ class PipeWirePlayback final : public BackendBase { PwStreamPtr mStream; spa_hook mStreamListener{}; spa_io_rate_match *mRateMatch{}; - std::unique_ptr<float*[]> mChannelPtrs; - uint mNumChannels{}; + std::vector<float*> mChannelPtrs; static constexpr pw_stream_events CreateEvents() { @@ -1425,13 +1445,11 @@ class PipeWirePlayback final : public BackendBase { public: PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { } - ~PipeWirePlayback() + ~PipeWirePlayback() final { /* Stop the mainloop so the stream can be properly destroyed. */ if(mLoop) mLoop.stop(); } - - DEF_NEWDEL(PipeWirePlayback) }; @@ -1455,7 +1473,7 @@ void PipeWirePlayback::outputCallback() noexcept if(!pw_buf) UNLIKELY return; const al::span<spa_data> datas{pw_buf->buffer->datas, - minu(mNumChannels, pw_buf->buffer->n_datas)}; + minz(mChannelPtrs.size(), pw_buf->buffer->n_datas)}; #if PW_CHECK_VERSION(0,3,49) /* In 0.3.49, pw_buffer::requested specifies the number of samples needed * by the resampler/graph for this audio update. @@ -1475,7 +1493,7 @@ void PipeWirePlayback::outputCallback() noexcept * buffer length in any one channel is smaller than we wanted (shouldn't * be, but just in case). */ - float **chanptr_end{mChannelPtrs.get()}; + auto chanptr_end = mChannelPtrs.begin(); for(const auto &data : datas) { length = minu(length, data.maxsize/sizeof(float)); @@ -1487,7 +1505,7 @@ void PipeWirePlayback::outputCallback() noexcept data.chunk->size = length * sizeof(float); } - mDevice->renderSamples({mChannelPtrs.get(), chanptr_end}, length); + mDevice->renderSamples(mChannelPtrs, length); pw_buf->size = length; pw_stream_queue_buffer(mStream.get(), pw_buf); @@ -1590,7 +1608,7 @@ bool PipeWirePlayback::reset() } mStreamListener = {}; mRateMatch = nullptr; - mTimeBase = GetDeviceClockTime(mDevice); + mTimeBase = mDevice->getClockTime(); /* If connecting to a specific device, update various device parameters to * match its format. @@ -1611,11 +1629,12 @@ bool PipeWirePlayback::reset() { /* Scale the update size if the sample rate changes. */ const double scale{static_cast<double>(match->mSampleRate) / mDevice->Frequency}; - const double numbufs{static_cast<double>(mDevice->BufferSize)/mDevice->UpdateSize}; + const double updatesize{std::round(mDevice->UpdateSize * scale)}; + const double buffersize{std::round(mDevice->BufferSize * scale)}; + mDevice->Frequency = match->mSampleRate; - mDevice->UpdateSize = static_cast<uint>(clampd(mDevice->UpdateSize*scale + 0.5, - 64.0, 8192.0)); - mDevice->BufferSize = static_cast<uint>(numbufs*mDevice->UpdateSize + 0.5); + mDevice->UpdateSize = static_cast<uint>(clampd(updatesize, 64.0, 8192.0)); + mDevice->BufferSize = static_cast<uint>(maxd(buffersize, 128.0)); } if(!mDevice->Flags.test(ChannelsRequest) && match->mChannels != InvalidChannelConfig) mDevice->FmtChans = match->mChannels; @@ -1673,7 +1692,7 @@ bool PipeWirePlayback::reset() pw_stream_flags flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE | PW_STREAM_FLAG_MAP_BUFFERS}; - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pipewire", "rt-mix", true)) + if(GetConfigValueBool(mDevice->DeviceName, "pipewire", "rt-mix", false)) flags |= PW_STREAM_FLAG_RT_PROCESS; if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, PwIdAny, flags, ¶ms, 1)}) throw al::backend_exception{al::backend_error::DeviceError, @@ -1698,8 +1717,7 @@ bool PipeWirePlayback::reset() */ plock.unlock(); - mNumChannels = mDevice->channelsFromFmt(); - mChannelPtrs = std::make_unique<float*[]>(mNumChannels); + mChannelPtrs.resize(mDevice->channelsFromFmt()); setDefaultWFXChannelOrder(); @@ -1757,7 +1775,7 @@ void PipeWirePlayback::start() mDevice->UpdateSize = updatesize; mDevice->BufferSize = static_cast<uint>(ptime.buffered + delay + - totalbuffers*updatesize); + uint64_t{totalbuffers}*updatesize); break; } #else @@ -1791,8 +1809,7 @@ void PipeWirePlayback::stop() { MainloopUniqueLock plock{mLoop}; if(int res{pw_stream_set_active(mStream.get(), false)}) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to stop PipeWire stream (res: %d)", res}; + ERR("Failed to stop PipeWire stream (res: %d)\n", res); /* Wait for the stream to stop playing. */ plock.wait([stream=mStream.get()]() @@ -1824,10 +1841,10 @@ ClockLatency PipeWirePlayback::getClockLatency() uint refcount; do { refcount = mDevice->waitForMix(); - mixtime = GetDeviceClockTime(mDevice); + mixtime = mDevice->getClockTime(); clock_gettime(CLOCK_MONOTONIC, &tspec); std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != ReadRef(mDevice->MixCount)); + } while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed)); /* Convert the monotonic clock, stream ticks, and stream delay to * nanoseconds. @@ -1916,9 +1933,7 @@ class PipeWireCapture final : public BackendBase { public: PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { } - ~PipeWireCapture() { if(mLoop) mLoop.stop(); } - - DEF_NEWDEL(PipeWireCapture) + ~PipeWireCapture() final { if(mLoop) mLoop.stop(); } }; @@ -1934,7 +1949,8 @@ void PipeWireCapture::inputCallback() noexcept const uint offset{minu(bufdata->chunk->offset, bufdata->maxsize)}; const uint size{minu(bufdata->chunk->size, bufdata->maxsize - offset)}; - mRing->write(static_cast<char*>(bufdata->data) + offset, size / mRing->getElemSize()); + std::ignore = mRing->write(static_cast<char*>(bufdata->data) + offset, + size / mRing->getElemSize()); pw_stream_queue_buffer(mStream.get(), pw_buf); } @@ -2057,7 +2073,8 @@ void PipeWireCapture::open(std::string_view name) 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)}; + std::array params{static_cast<const spa_pod*>(spa_format_audio_raw_build(b.get(), + SPA_PARAM_EnumFormat, &info))}; if(!params[0]) throw al::backend_exception{al::backend_error::DeviceError, "Failed to set PipeWire audio format parameters"}; @@ -2099,7 +2116,7 @@ void PipeWireCapture::open(std::string_view name) 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, 1)}) + if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, params.data(), 1)}) throw al::backend_exception{al::backend_error::DeviceError, "Error connecting PipeWire stream (res: %d)", res}; @@ -2145,8 +2162,7 @@ void PipeWireCapture::stop() { MainloopUniqueLock plock{mLoop}; if(int res{pw_stream_set_active(mStream.get(), false)}) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to stop PipeWire stream (res: %d)", res}; + ERR("Failed to stop PipeWire stream (res: %d)\n", res); plock.wait([stream=mStream.get()]() { return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; }); @@ -2156,7 +2172,7 @@ uint PipeWireCapture::availableSamples() { return static_cast<uint>(mRing->readSpace()); } void PipeWireCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } } // namespace @@ -2175,11 +2191,11 @@ bool PipeWireBackendFactory::init() } TRACE("Found PipeWire version \"%s\" (%s or newer)\n", version, pw_get_headers_version()); - pw_init(0, nullptr); + pw_init(nullptr, nullptr); if(!gEventHandler.init()) return false; - if(!GetConfigValueBool(nullptr, "pipewire", "assume-audio", false) + if(!GetConfigValueBool({}, "pipewire", "assume-audio", false) && !gEventHandler.waitForAudio()) { gEventHandler.kill(); |