diff options
Diffstat (limited to 'alc/backends/pipewire.cpp')
-rw-r--r-- | alc/backends/pipewire.cpp | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/alc/backends/pipewire.cpp b/alc/backends/pipewire.cpp new file mode 100644 index 00000000..00bf7cea --- /dev/null +++ b/alc/backends/pipewire.cpp @@ -0,0 +1,179 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2010 by Chris Robinson + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include "pipewire.h" + +#include <exception> +#include <atomic> +#include <chrono> +#include <cstdint> +#include <cstring> +#include <functional> +#include <thread> + +#include "core/device.h" +#include "almalloc.h" +#include "core/helpers.h" +#include "threads.h" + + +namespace { + +using std::chrono::seconds; +using std::chrono::milliseconds; +using std::chrono::nanoseconds; + +constexpr char pipeDevice[] = "No Output"; + + +struct PipeWireBackend final : public BackendBase { + PipeWireBackend(DeviceBase *device) noexcept : BackendBase{device} { } + + int mixerProc(); + + void open(const char *name) override; + bool reset() override; + void start() override; + void stop() override; + + std::atomic<bool> mKillNow{true}; + std::thread mThread; + + DEF_NEWDEL(PipeWireBackend) +}; + +int PipeWireBackend::mixerProc() +{ + const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2}; + + SetRTPriority(); + althrd_setname(MIXER_THREAD_NAME); + + int64_t done{0}; + auto start = std::chrono::steady_clock::now(); + while(!mKillNow.load(std::memory_order_acquire) + && mDevice->Connected.load(std::memory_order_acquire)) + { + auto now = std::chrono::steady_clock::now(); + + /* This converts from nanoseconds to nanosamples, then to samples. */ + int64_t avail{std::chrono::duration_cast<seconds>((now-start) * mDevice->Frequency).count()}; + if(avail-done < mDevice->UpdateSize) + { + std::this_thread::sleep_for(restTime); + continue; + } + while(avail-done >= mDevice->UpdateSize) + { + mDevice->renderSamples(nullptr, mDevice->UpdateSize, 0u); + done += mDevice->UpdateSize; + } + + /* For every completed second, increment the start time and reduce the + * samples done. This prevents the difference between the start time + * and current time from growing too large, while maintaining the + * correct number of samples to render. + */ + if(done >= mDevice->Frequency) + { + seconds s{done/mDevice->Frequency}; + start += s; + done -= mDevice->Frequency*s.count(); + } + } + + return 0; +} + + +void PipeWireBackend::open(const char *name) +{ + if(!name) + name = pipeDevice; + else if(strcmp(name, pipeDevice) != 0) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found", + name}; + + mDevice->DeviceName = name; +} + +bool PipeWireBackend::reset() +{ + setDefaultWFXChannelOrder(); + return true; +} + +void PipeWireBackend::start() +{ + try { + mKillNow.store(false, std::memory_order_release); + mThread = std::thread{std::mem_fn(&PipeWireBackend::mixerProc), this}; + } + catch(std::exception& e) { + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start mixing thread: %s", e.what()}; + } +} + +void PipeWireBackend::stop() +{ + if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable()) + return; + mThread.join(); +} + +} // namespace + + +bool PipeWireBackendFactory::init() +{ return true; } + +bool PipeWireBackendFactory::querySupport(BackendType type) +{ return (type == BackendType::Playback); } + +std::string PipeWireBackendFactory::probe(BackendType type) +{ + std::string outnames; + switch(type) + { + case BackendType::Playback: + /* Includes null char. */ + outnames.append(pipeDevice, sizeof(pipeDevice)); + break; + case BackendType::Capture: + break; + } + return outnames; +} + +BackendPtr PipeWireBackendFactory::createBackend(DeviceBase *device, BackendType type) +{ + if(type == BackendType::Playback) + return BackendPtr{new PipeWireBackend{device}}; + return nullptr; +} + +BackendFactory &PipeWireBackendFactory::getFactory() +{ + static PipeWireBackendFactory factory{}; + return factory; +} |