Remove the atomic exchange macros
[openal-soft.git] / Alc / backends / null.cpp
blob47ebd0aef79d7e000de4f5f6280ddd7dad429879
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2010 by Chris Robinson
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include "backends/null.h"
25 #include <stdlib.h>
26 #ifdef HAVE_WINDOWS_H
27 #include <windows.h>
28 #endif
30 #include <chrono>
31 #include <thread>
33 #include "alMain.h"
34 #include "alu.h"
35 #include "compat.h"
38 namespace {
40 using std::chrono::seconds;
41 using std::chrono::milliseconds;
42 using std::chrono::nanoseconds;
44 constexpr ALCchar nullDevice[] = "No Output";
47 struct ALCnullBackend final : public ALCbackend {
48 ATOMIC(int) killNow;
49 std::thread thread;
52 int ALCnullBackend_mixerProc(ALCnullBackend *self);
54 void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
55 void ALCnullBackend_Destruct(ALCnullBackend *self);
56 ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
57 ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
58 ALCboolean ALCnullBackend_start(ALCnullBackend *self);
59 void ALCnullBackend_stop(ALCnullBackend *self);
60 DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
61 DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
62 DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency)
63 DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
64 DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
65 DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
67 DEFINE_ALCBACKEND_VTABLE(ALCnullBackend);
70 void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
72 new (self) ALCnullBackend{};
73 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
74 SET_VTABLE2(ALCnullBackend, ALCbackend, self);
76 ATOMIC_INIT(&self->killNow, AL_TRUE);
79 void ALCnullBackend_Destruct(ALCnullBackend *self)
81 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
82 self->~ALCnullBackend();
86 int ALCnullBackend_mixerProc(ALCnullBackend *self)
88 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
89 const milliseconds restTime{device->UpdateSize*1000/device->Frequency / 2};
91 SetRTPriority();
92 althrd_setname(MIXER_THREAD_NAME);
94 ALint64 done{0};
95 auto start = std::chrono::steady_clock::now();
96 while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
97 ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
99 auto now = std::chrono::steady_clock::now();
101 /* This converts from nanoseconds to nanosamples, then to samples. */
102 ALint64 avail{std::chrono::duration_cast<seconds>((now-start) * device->Frequency).count()};
103 if(avail-done < device->UpdateSize)
105 std::this_thread::sleep_for(restTime);
106 continue;
108 while(avail-done >= device->UpdateSize)
110 ALCnullBackend_lock(self);
111 aluMixData(device, nullptr, device->UpdateSize);
112 ALCnullBackend_unlock(self);
113 done += device->UpdateSize;
116 /* For every completed second, increment the start time and reduce the
117 * samples done. This prevents the difference between the start time
118 * and current time from growing too large, while maintaining the
119 * correct number of samples to render.
121 if(done >= device->Frequency)
123 seconds s{done/device->Frequency};
124 start += s;
125 done -= device->Frequency*s.count();
129 return 0;
133 ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
135 ALCdevice *device;
137 if(!name)
138 name = nullDevice;
139 else if(strcmp(name, nullDevice) != 0)
140 return ALC_INVALID_VALUE;
142 device = STATIC_CAST(ALCbackend, self)->mDevice;
143 device->DeviceName = name;
145 return ALC_NO_ERROR;
148 ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
150 SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
151 return ALC_TRUE;
154 ALCboolean ALCnullBackend_start(ALCnullBackend *self)
156 try {
157 ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
158 self->thread = std::thread(ALCnullBackend_mixerProc, self);
159 return ALC_TRUE;
161 catch(std::exception& e) {
162 ERR("Failed to start mixing thread: %s\n", e.what());
164 catch(...) {
166 return ALC_FALSE;
169 void ALCnullBackend_stop(ALCnullBackend *self)
171 if(self->killNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !self->thread.joinable())
172 return;
173 self->thread.join();
176 } // namespace
179 bool NullBackendFactory::init()
180 { return true; }
182 bool NullBackendFactory::querySupport(ALCbackend_Type type)
183 { return (type == ALCbackend_Playback); }
185 void NullBackendFactory::probe(enum DevProbe type, std::string *outnames)
187 switch(type)
189 case ALL_DEVICE_PROBE:
190 /* Includes null char. */
191 outnames->append(nullDevice, sizeof(nullDevice));
192 break;
193 case CAPTURE_DEVICE_PROBE:
194 break;
198 ALCbackend *NullBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
200 if(type == ALCbackend_Playback)
202 ALCnullBackend *backend;
203 NEW_OBJ(backend, ALCnullBackend)(device);
204 if(!backend) return NULL;
205 return STATIC_CAST(ALCbackend, backend);
208 return NULL;
211 BackendFactory &NullBackendFactory::getFactory()
213 static NullBackendFactory factory{};
214 return factory;