aboutsummaryrefslogtreecommitdiffstats
path: root/alc/backends/pipewire.cpp
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2021-08-02 21:03:18 -0700
committerChris Robinson <[email protected]>2021-08-02 21:03:18 -0700
commit4cc820bb5c4c3c93ec85fed5a5bf7978b6bd14b1 (patch)
treefaba9ec1b8ebed96eba03b6c70f8c244394d4a96 /alc/backends/pipewire.cpp
parent0a0849db997df2015223bfd43cb7490fc8cd601e (diff)
Start a PipeWire backend
It's just a copy of the Null backend to start with
Diffstat (limited to 'alc/backends/pipewire.cpp')
-rw-r--r--alc/backends/pipewire.cpp179
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;
+}