Fix compilation with newer versions of DUMB
[alure.git] / src / device.cpp
blob8f5cbec7b0f1c022aba3cdd2997989ed9905f2fa
2 #include "config.h"
4 #include "device.h"
6 #include <string.h>
8 #include <stdexcept>
9 #include <algorithm>
11 #include "devicemanager.h"
12 #include "context.h"
13 #include "buffer.h"
16 namespace {
18 using alure::DeviceImpl;
19 using alure::ALC;
22 template<typename T>
23 inline void LoadALCFunc(ALCdevice *device, T **func, const char *name)
24 { *func = reinterpret_cast<T*>(alcGetProcAddress(device, name)); }
26 void LoadHrtf(DeviceImpl *device)
28 LoadALCFunc(device->getALCdevice(), &device->alcGetStringiSOFT, "alcGetStringiSOFT");
29 LoadALCFunc(device->getALCdevice(), &device->alcResetDeviceSOFT, "alcResetDeviceSOFT");
32 void LoadPauseDevice(DeviceImpl *device)
34 LoadALCFunc(device->getALCdevice(), &device->alcDevicePauseSOFT, "alcDevicePauseSOFT");
35 LoadALCFunc(device->getALCdevice(), &device->alcDeviceResumeSOFT, "alcDeviceResumeSOFT");
38 void LoadNothing(DeviceImpl*) { }
40 static const struct {
41 ALC extension;
42 const char name[32];
43 void (&loader)(DeviceImpl*);
44 } ALCExtensionList[] = {
45 { ALC::ENUMERATE_ALL_EXT, "ALC_ENUMERATE_ALL_EXT", LoadNothing },
46 { ALC::EXT_EFX, "ALC_EXT_EFX", LoadNothing },
47 { ALC::EXT_thread_local_context, "ALC_EXT_thread_local_context", LoadNothing },
48 { ALC::SOFT_HRTF, "ALC_SOFT_HRTF", LoadHrtf },
49 { ALC::SOFT_pause_device, "ALC_SOFT_pause_device", LoadPauseDevice },
52 } // namespace
54 namespace alure {
56 void DeviceImpl::setupExts()
58 for(const auto &entry : ALCExtensionList)
60 if(!alcIsExtensionPresent(mDevice, entry.name))
61 continue;
62 mHasExt.set(static_cast<size_t>(entry.extension));
63 entry.loader(this);
68 DeviceImpl::DeviceImpl(const char *name)
70 mDevice = alcOpenDevice(name);
71 if(!mDevice) throw alc_error(alcGetError(nullptr), "alcOpenDevice failed");
73 setupExts();
74 mPauseTime = mTimeBase = std::chrono::steady_clock::now().time_since_epoch();
77 DeviceImpl::~DeviceImpl()
79 mContexts.clear();
81 if(mDevice)
82 alcCloseDevice(mDevice);
83 mDevice = nullptr;
87 void DeviceImpl::removeContext(ContextImpl *ctx)
89 auto iter = std::find_if(mContexts.begin(), mContexts.end(),
90 [ctx](const UniquePtr<ContextImpl> &entry) -> bool
91 { return entry.get() == ctx; }
93 if(iter != mContexts.end()) mContexts.erase(iter);
94 if(mContexts.empty() && mPauseTime == mPauseTime.zero())
95 mPauseTime = std::chrono::steady_clock::now().time_since_epoch();
99 DECL_THUNK1(String, Device, getName, const, PlaybackName)
100 String DeviceImpl::getName(PlaybackName type) const
102 if(type == PlaybackName::Full && !hasExtension(ALC::ENUMERATE_ALL_EXT))
103 type = PlaybackName::Basic;
104 alcGetError(mDevice);
105 const ALCchar *name = alcGetString(mDevice, (ALenum)type);
106 if(alcGetError(mDevice) != ALC_NO_ERROR || !name)
107 name = alcGetString(mDevice, (ALenum)PlaybackName::Basic);
108 return name ? String(name) : String();
111 bool Device::queryExtension(const String &name) const
112 { return pImpl->queryExtension(name.c_str()); }
113 DECL_THUNK1(bool, Device, queryExtension, const, const char*)
114 bool DeviceImpl::queryExtension(const char *name) const
116 return static_cast<bool>(alcIsExtensionPresent(mDevice, name));
119 DECL_THUNK0(Version, Device, getALCVersion, const)
120 Version DeviceImpl::getALCVersion() const
122 ALCint major=-1, minor=-1;
123 alcGetIntegerv(mDevice, ALC_MAJOR_VERSION, 1, &major);
124 alcGetIntegerv(mDevice, ALC_MINOR_VERSION, 1, &minor);
125 if(major < 0 || minor < 0)
126 throw std::runtime_error("ALC version error");
127 return Version{ (ALCuint)major, (ALCuint)minor };
130 DECL_THUNK0(Version, Device, getEFXVersion, const)
131 Version DeviceImpl::getEFXVersion() const
133 if(!hasExtension(ALC::EXT_EFX))
134 return Version{ 0u, 0u };
136 ALCint major=-1, minor=-1;
137 alcGetIntegerv(mDevice, ALC_EFX_MAJOR_VERSION, 1, &major);
138 alcGetIntegerv(mDevice, ALC_EFX_MINOR_VERSION, 1, &minor);
139 if(major < 0 || minor < 0)
140 throw std::runtime_error("EFX version error");
141 return Version{ (ALCuint)major, (ALCuint)minor };
144 DECL_THUNK0(ALCuint, Device, getFrequency, const)
145 ALCuint DeviceImpl::getFrequency() const
147 ALCint freq = -1;
148 alcGetIntegerv(mDevice, ALC_FREQUENCY, 1, &freq);
149 if(freq < 0)
150 throw std::runtime_error("Frequency error");
151 return freq;
154 DECL_THUNK0(ALCuint, Device, getMaxAuxiliarySends, const)
155 ALCuint DeviceImpl::getMaxAuxiliarySends() const
157 if(!hasExtension(ALC::EXT_EFX))
158 return 0;
160 ALCint sends=-1;
161 alcGetIntegerv(mDevice, ALC_MAX_AUXILIARY_SENDS, 1, &sends);
162 if(sends < 0)
163 throw std::runtime_error("Max auxiliary sends error");
164 return sends;
168 DECL_THUNK0(Vector<String>, Device, enumerateHRTFNames, const)
169 Vector<String> DeviceImpl::enumerateHRTFNames() const
171 Vector<String> hrtfs;
172 if(!hasExtension(ALC::SOFT_HRTF))
173 return hrtfs;
175 ALCint num_hrtfs = -1;
176 alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtfs);
177 if(num_hrtfs < 0)
178 throw std::runtime_error("HRTF specifier count error");
180 hrtfs.reserve(num_hrtfs);
181 for(int i = 0;i < num_hrtfs;++i)
182 hrtfs.emplace_back(alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i));
183 return hrtfs;
186 DECL_THUNK0(bool, Device, isHRTFEnabled, const)
187 bool DeviceImpl::isHRTFEnabled() const
189 if(!hasExtension(ALC::SOFT_HRTF))
190 return false;
192 ALCint hrtf_state = -1;
193 alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state);
194 if(hrtf_state == -1)
195 throw std::runtime_error("HRTF state error");
196 return hrtf_state != ALC_FALSE;
199 DECL_THUNK0(String, Device, getCurrentHRTF, const)
200 String DeviceImpl::getCurrentHRTF() const
202 if(!hasExtension(ALC::SOFT_HRTF))
203 return String();
204 return String(alcGetString(mDevice, ALC_HRTF_SPECIFIER_SOFT));
207 DECL_THUNK1(void, Device, reset,, ArrayView<AttributePair>)
208 void DeviceImpl::reset(ArrayView<AttributePair> attributes)
210 if(!hasExtension(ALC::SOFT_HRTF))
211 return;
212 ALCboolean success = ALC_FALSE;
213 if(attributes.end()) /* No explicit attributes. */
214 success = alcResetDeviceSOFT(mDevice, nullptr);
215 else
217 auto attr_end = std::find_if(attributes.rbegin(), attributes.rend(),
218 [](const AttributePair &attr) -> bool
219 { return attr.mAttribute == 0; }
221 if(attr_end == attributes.rend())
223 /* Attribute list was not properly terminated. Copy the attribute
224 * list and add the 0 sentinel.
226 Vector<AttributePair> attrs;
227 attrs.reserve(attributes.size() + 1);
228 std::copy(attributes.begin(), attributes.end(), std::back_inserter(attrs));
229 attrs.push_back(AttributesEnd());
230 success = alcResetDeviceSOFT(mDevice, &attrs.front().mAttribute);
232 else
233 success = alcResetDeviceSOFT(mDevice, &attributes.front().mAttribute);
235 if(!success)
236 throw alc_error(alcGetError(mDevice), "alcResetDeviceSOFT failed");
240 DECL_THUNK1(Context, Device, createContext,, ArrayView<AttributePair>)
241 Context DeviceImpl::createContext(ArrayView<AttributePair> attributes)
243 auto cur_time = std::chrono::steady_clock::now().time_since_epoch();
244 Vector<AttributePair> attrs;
245 if(!attributes.empty())
247 auto attr_end = std::find_if(attributes.rbegin(), attributes.rend(),
248 [](const AttributePair &attr) -> bool
249 { return attr.mAttribute == 0; }
251 if(attr_end == attributes.rend())
253 /* Attribute list was not properly terminated. Copy the attribute
254 * list and add the 0 sentinel.
256 attrs.reserve(attributes.size() + 1);
257 std::copy(attributes.begin(), attributes.end(), std::back_inserter(attrs));
258 attrs.push_back(AttributesEnd());
259 attributes = attrs;
263 mContexts.emplace_back(MakeUnique<ContextImpl>(*this, attributes));
264 if(!mIsPaused && mPauseTime != mPauseTime.zero())
266 mTimeBase += cur_time - mPauseTime;
267 mPauseTime = mPauseTime.zero();
269 return Context(mContexts.back().get());
272 Context Device::createContext(const std::nothrow_t&) noexcept
273 { return createContext({}, std::nothrow); }
274 Context Device::createContext(ArrayView<AttributePair> attrs, const std::nothrow_t&) noexcept
276 try {
277 return pImpl->createContext(attrs);
279 catch(...) {
281 return Context();
285 DECL_THUNK0(void, Device, pauseDSP,)
286 void DeviceImpl::pauseDSP()
288 if(!hasExtension(ALC::SOFT_pause_device))
289 throw std::runtime_error("ALC_SOFT_pause_device not supported");
290 alcDevicePauseSOFT(mDevice);
291 if(!mIsPaused && mPauseTime == mPauseTime.zero())
292 mPauseTime = std::chrono::steady_clock::now().time_since_epoch();
293 mIsPaused = true;
296 DECL_THUNK0(void, Device, resumeDSP,)
297 void DeviceImpl::resumeDSP()
299 auto cur_time = std::chrono::steady_clock::now().time_since_epoch();
300 if(hasExtension(ALC::SOFT_pause_device))
301 alcDeviceResumeSOFT(mDevice);
302 if(!mContexts.empty() && mPauseTime != mPauseTime.zero())
304 mTimeBase += cur_time - mPauseTime;
305 mPauseTime = mPauseTime.zero();
307 mIsPaused = false;
310 DECL_THUNK0(std::chrono::nanoseconds, Device, getClockTime,)
311 std::chrono::nanoseconds DeviceImpl::getClockTime()
313 std::chrono::nanoseconds cur_time = std::chrono::steady_clock::now().time_since_epoch();
314 if(UNLIKELY(mPauseTime != mPauseTime.zero()))
316 auto diff = cur_time - mPauseTime;
317 mTimeBase += diff;
318 mPauseTime += diff;
319 cur_time = mPauseTime;
321 return cur_time - mTimeBase;
325 void Device::close()
327 DeviceImpl *i = pImpl;
328 pImpl = nullptr;
329 i->close();
331 void DeviceImpl::close()
333 if(!mContexts.empty())
334 throw std::runtime_error("Trying to close device with contexts");
336 if(alcCloseDevice(mDevice) == ALC_FALSE)
337 throw alc_error(alcGetError(mDevice), "alcCloseDevice failed");
338 mDevice = nullptr;
340 DeviceManagerImpl::getInstance()->removeDevice(this);
343 } // namespace alure