aboutsummaryrefslogtreecommitdiffstats
path: root/alc/backends/null.cpp
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2019-07-28 18:56:04 -0700
committerChris Robinson <[email protected]>2019-07-28 18:56:04 -0700
commitcb3e96e75640730b9391f0d2d922eecd9ee2ce79 (patch)
tree23520551bddb2a80354e44da47f54201fdc084f0 /alc/backends/null.cpp
parent93e60919c8f387c36c267ca9faa1ac653254aea6 (diff)
Rename Alc to alc
Diffstat (limited to 'alc/backends/null.cpp')
-rw-r--r--alc/backends/null.cpp184
1 files changed, 184 insertions, 0 deletions
diff --git a/alc/backends/null.cpp b/alc/backends/null.cpp
new file mode 100644
index 00000000..ae58cb8b
--- /dev/null
+++ b/alc/backends/null.cpp
@@ -0,0 +1,184 @@
+/**
+ * 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 "backends/null.h"
+
+#include <exception>
+#include <atomic>
+#include <chrono>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <thread>
+
+#include "alcmain.h"
+#include "almalloc.h"
+#include "alu.h"
+#include "logging.h"
+#include "threads.h"
+
+
+namespace {
+
+using std::chrono::seconds;
+using std::chrono::milliseconds;
+using std::chrono::nanoseconds;
+
+constexpr ALCchar nullDevice[] = "No Output";
+
+
+struct NullBackend final : public BackendBase {
+ NullBackend(ALCdevice *device) noexcept : BackendBase{device} { }
+
+ int mixerProc();
+
+ ALCenum open(const ALCchar *name) override;
+ ALCboolean reset() override;
+ ALCboolean start() override;
+ void stop() override;
+
+ std::atomic<bool> mKillNow{true};
+ std::thread mThread;
+
+ DEF_NEWDEL(NullBackend)
+};
+
+int NullBackend::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)
+ {
+ lock();
+ aluMixData(mDevice, nullptr, mDevice->UpdateSize);
+ unlock();
+ 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;
+}
+
+
+ALCenum NullBackend::open(const ALCchar *name)
+{
+ if(!name)
+ name = nullDevice;
+ else if(strcmp(name, nullDevice) != 0)
+ return ALC_INVALID_VALUE;
+
+ mDevice->DeviceName = name;
+
+ return ALC_NO_ERROR;
+}
+
+ALCboolean NullBackend::reset()
+{
+ SetDefaultWFXChannelOrder(mDevice);
+ return ALC_TRUE;
+}
+
+ALCboolean NullBackend::start()
+{
+ try {
+ mKillNow.store(false, std::memory_order_release);
+ mThread = std::thread{std::mem_fn(&NullBackend::mixerProc), this};
+ return ALC_TRUE;
+ }
+ catch(std::exception& e) {
+ ERR("Failed to start mixing thread: %s\n", e.what());
+ }
+ catch(...) {
+ }
+ return ALC_FALSE;
+}
+
+void NullBackend::stop()
+{
+ if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
+ return;
+ mThread.join();
+}
+
+} // namespace
+
+
+bool NullBackendFactory::init()
+{ return true; }
+
+bool NullBackendFactory::querySupport(BackendType type)
+{ return (type == BackendType::Playback); }
+
+void NullBackendFactory::probe(DevProbe type, std::string *outnames)
+{
+ switch(type)
+ {
+ case DevProbe::Playback:
+ /* Includes null char. */
+ outnames->append(nullDevice, sizeof(nullDevice));
+ break;
+ case DevProbe::Capture:
+ break;
+ }
+}
+
+BackendPtr NullBackendFactory::createBackend(ALCdevice *device, BackendType type)
+{
+ if(type == BackendType::Playback)
+ return BackendPtr{new NullBackend{device}};
+ return nullptr;
+}
+
+BackendFactory &NullBackendFactory::getFactory()
+{
+ static NullBackendFactory factory{};
+ return factory;
+}