Take String refs for functions that construct Strings anyway
[alure.git] / src / device.cpp
blob97b6f98ca6c6185e7b0b2ff148379c8b8b427ddb
2 #include "config.h"
4 #include "device.h"
6 #include <string.h>
8 #include <stdexcept>
9 #include <algorithm>
11 #include "alc.h"
12 #include "alext.h"
14 #include "devicemanager.h"
15 #include "context.h"
16 #include "buffer.h"
19 namespace alure
22 template<typename T>
23 static inline void LoadALCFunc(ALCdevice *device, T **func, const char *name)
24 { *func = reinterpret_cast<T*>(alcGetProcAddress(device, name)); }
27 static void LoadPauseDevice(DeviceImpl *device)
29 LoadALCFunc(device->getDevice(), &device->alcDevicePauseSOFT, "alcDevicePauseSOFT");
30 LoadALCFunc(device->getDevice(), &device->alcDeviceResumeSOFT, "alcDeviceResumeSOFT");
33 static void LoadHrtf(DeviceImpl *device)
35 LoadALCFunc(device->getDevice(), &device->alcGetStringiSOFT, "alcGetStringiSOFT");
36 LoadALCFunc(device->getDevice(), &device->alcResetDeviceSOFT, "alcResetDeviceSOFT");
39 static void LoadNothing(DeviceImpl*) { }
41 static const struct {
42 enum ALCExtension extension;
43 const char name[32];
44 void (&loader)(DeviceImpl*);
45 } ALCExtensionList[] = {
46 { EXT_thread_local_context, "ALC_EXT_thread_local_context", LoadNothing },
47 { SOFT_device_pause, "ALC_SOFT_pause_device", LoadPauseDevice },
48 { SOFT_HRTF, "ALC_SOFT_HRTF", LoadHrtf },
52 void DeviceImpl::setupExts()
54 std::fill(std::begin(mHasExt), std::end(mHasExt), false);
55 for(const auto &entry : ALCExtensionList)
57 mHasExt[entry.extension] = alcIsExtensionPresent(mDevice, entry.name);
58 if(mHasExt[entry.extension]) entry.loader(this);
63 DeviceImpl::DeviceImpl(ALCdevice* device)
64 : mDevice(device), alcDevicePauseSOFT(nullptr), alcDeviceResumeSOFT(nullptr)
66 setupExts();
69 DeviceImpl::~DeviceImpl()
74 void DeviceImpl::removeContext(ContextImpl *ctx)
76 auto iter = std::find_if(mContexts.begin(), mContexts.end(),
77 [ctx](const UniquePtr<ContextImpl> &entry) -> bool
78 { return entry.get() == ctx; }
80 if(iter != mContexts.end()) mContexts.erase(iter);
84 String DeviceImpl::getName(PlaybackName type) const
86 if(type == PlaybackName::Full && !alcIsExtensionPresent(mDevice, "ALC_ENUMERATE_ALL_EXT"))
87 type = PlaybackName::Basic;
88 alcGetError(mDevice);
89 const ALCchar *name = alcGetString(mDevice, (ALenum)type);
90 if(alcGetError(mDevice) != ALC_NO_ERROR || !name)
91 name = alcGetString(mDevice, (ALenum)PlaybackName::Basic);
92 return name ? String(name) : String();
95 bool DeviceImpl::queryExtension(const char *name) const
97 return alcIsExtensionPresent(mDevice, name);
100 Version DeviceImpl::getALCVersion() const
102 ALCint major=-1, minor=-1;
103 alcGetIntegerv(mDevice, ALC_MAJOR_VERSION, 1, &major);
104 alcGetIntegerv(mDevice, ALC_MINOR_VERSION, 1, &minor);
105 if(major < 0 || minor < 0)
106 throw std::runtime_error("ALC version error");
107 return Version{ (ALCuint)major, (ALCuint)minor };
110 Version DeviceImpl::getEFXVersion() const
112 if(!alcIsExtensionPresent(mDevice, "ALC_EXT_EFX"))
113 return Version{ 0u, 0u };
115 ALCint major=-1, minor=-1;
116 alcGetIntegerv(mDevice, ALC_EFX_MAJOR_VERSION, 1, &major);
117 alcGetIntegerv(mDevice, ALC_EFX_MINOR_VERSION, 1, &minor);
118 if(major < 0 || minor < 0)
119 throw std::runtime_error("EFX version error");
120 return Version{ (ALCuint)major, (ALCuint)minor };
123 ALCuint DeviceImpl::getFrequency() const
125 ALCint freq = -1;
126 alcGetIntegerv(mDevice, ALC_FREQUENCY, 1, &freq);
127 if(freq < 0)
128 throw std::runtime_error("Frequency error");
129 return freq;
132 ALCuint DeviceImpl::getMaxAuxiliarySends() const
134 if(!alcIsExtensionPresent(mDevice, "ALC_EXT_EFX"))
135 return 0;
137 ALCint sends=-1;
138 alcGetIntegerv(mDevice, ALC_MAX_AUXILIARY_SENDS, 1, &sends);
139 if(sends < 0)
140 throw std::runtime_error("Max auxiliary sends error");
141 return sends;
145 Vector<String> DeviceImpl::enumerateHRTFNames() const
147 if(!hasExtension(SOFT_HRTF))
148 throw std::runtime_error("ALC_SOFT_HRTF not supported");
150 ALCint num_hrtfs = -1;
151 alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtfs);
152 if(num_hrtfs < 0)
153 throw std::runtime_error("HRTF specifier count error");
155 Vector<String> hrtfs;
156 hrtfs.reserve(num_hrtfs);
157 for(int i = 0;i < num_hrtfs;++i)
158 hrtfs.emplace_back(alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i));
159 return hrtfs;
162 bool DeviceImpl::isHRTFEnabled() const
164 if(!hasExtension(SOFT_HRTF))
165 throw std::runtime_error("ALC_SOFT_HRTF not supported");
167 ALCint hrtf_state = -1;
168 alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state);
169 if(hrtf_state == -1)
170 throw std::runtime_error("HRTF state error");
171 return hrtf_state != ALC_FALSE;
174 String DeviceImpl::getCurrentHRTF() const
176 if(!hasExtension(SOFT_HRTF))
177 throw std::runtime_error("ALC_SOFT_HRTF not supported");
178 return String(alcGetString(mDevice, ALC_HRTF_SPECIFIER_SOFT));
181 void DeviceImpl::reset(ArrayView<AttributePair> attributes)
183 if(!hasExtension(SOFT_HRTF))
184 throw std::runtime_error("ALC_SOFT_HRTF not supported");
185 auto do_reset = [this, &attributes]() -> ALCboolean
187 if(attributes.empty())
189 /* No explicit attributes. */
190 return alcResetDeviceSOFT(mDevice, nullptr);
192 auto attr_end = std::find_if(attributes.begin(), attributes.end(),
193 [](const AttributePair &attr) -> bool
194 { return std::get<0>(attr) == 0; }
196 if(attr_end == attributes.end())
198 /* Attribute list was not properly terminated. Copy the attribute
199 * list and add the 0 sentinel.
201 Vector<AttributePair> attrs;
202 attrs.reserve(attributes.size() + 1);
203 std::copy(attributes.begin(), attributes.end(), std::back_inserter(attrs));
204 attrs.push_back({0, 0});
205 return alcResetDeviceSOFT(mDevice, &std::get<0>(attrs.front()));
207 return alcResetDeviceSOFT(mDevice, &std::get<0>(attributes.front()));
209 if(!do_reset())
210 throw std::runtime_error("Device reset error");
214 Context DeviceImpl::createContext(ArrayView<AttributePair> attributes)
216 ALCcontext *ctx = [this, &attributes]() -> ALCcontext*
218 if(attributes.empty())
220 /* No explicit attributes. */
221 return alcCreateContext(mDevice, nullptr);
223 auto attr_end = std::find_if(attributes.begin(), attributes.end(),
224 [](const AttributePair &attr) -> bool
225 { return std::get<0>(attr) == 0; }
227 if(attr_end == attributes.end())
229 /* Attribute list was not properly terminated. Copy the attribute
230 * list and add the 0 sentinel.
232 Vector<AttributePair> attrs;
233 attrs.reserve(attributes.size() + 1);
234 std::copy(attributes.begin(), attributes.end(), std::back_inserter(attrs));
235 attrs.push_back({0, 0});
236 return alcCreateContext(mDevice, &std::get<0>(attrs.front()));
238 return alcCreateContext(mDevice, &std::get<0>(attributes.front()));
239 }();
240 if(!ctx) throw std::runtime_error("Failed to create context");
242 mContexts.emplace_back(MakeUnique<ContextImpl>(ctx, this));
243 return Context(mContexts.back().get());
247 void DeviceImpl::pauseDSP()
249 if(!hasExtension(SOFT_device_pause))
250 throw std::runtime_error("ALC_SOFT_pause_device not supported");
251 alcDevicePauseSOFT(mDevice);
254 void DeviceImpl::resumeDSP()
256 if(hasExtension(SOFT_device_pause))
257 alcDeviceResumeSOFT(mDevice);
261 void DeviceImpl::close()
263 if(!mContexts.empty())
264 throw std::runtime_error("Trying to close device with contexts");
266 if(alcCloseDevice(mDevice) == ALC_FALSE)
267 throw std::runtime_error("Failed to close device");
268 mDevice = 0;
270 DeviceManagerImpl::get().removeDevice(this);
274 DECL_THUNK1(String, Device, getName, const, PlaybackName)
275 DECL_THUNK1(bool, Device, queryExtension, const, const char*)
276 bool Device::queryExtension(const String &name) const
277 { return pImpl->queryExtension(name.c_str()); }
278 DECL_THUNK0(Version, Device, getALCVersion, const)
279 DECL_THUNK0(Version, Device, getEFXVersion, const)
280 DECL_THUNK0(ALCuint, Device, getFrequency, const)
281 DECL_THUNK0(ALCuint, Device, getMaxAuxiliarySends, const)
282 DECL_THUNK0(Vector<String>, Device, enumerateHRTFNames, const)
283 DECL_THUNK0(bool, Device, isHRTFEnabled, const)
284 DECL_THUNK0(String, Device, getCurrentHRTF, const)
285 DECL_THUNK1(void, Device, reset,, ArrayView<AttributePair>)
286 DECL_THUNK1(Context, Device, createContext,, ArrayView<AttributePair>)
287 DECL_THUNK0(void, Device, pauseDSP,)
288 DECL_THUNK0(void, Device, resumeDSP,)
289 void Device::close()
291 pImpl->close();
292 pImpl = nullptr;