Shorten the PlaybackDeviceName enum type name
[alure.git] / src / device.cpp
blob2fac534732861551ef57c0b9e51fc0b6968de25d
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(ALDevice *device)
29 LoadALCFunc(device->getDevice(), &device->alcDevicePauseSOFT, "alcDevicePauseSOFT");
30 LoadALCFunc(device->getDevice(), &device->alcDeviceResumeSOFT, "alcDeviceResumeSOFT");
33 static void LoadHrtf(ALDevice *device)
35 LoadALCFunc(device->getDevice(), &device->alcGetStringiSOFT, "alcGetStringiSOFT");
36 LoadALCFunc(device->getDevice(), &device->alcResetDeviceSOFT, "alcResetDeviceSOFT");
39 static void LoadNothing(ALDevice*) { }
41 static const struct {
42 enum ALCExtension extension;
43 const char name[32];
44 void (&loader)(ALDevice*);
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 ALDevice::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 ALDevice::ALDevice(ALCdevice* device)
64 : mDevice(device), alcDevicePauseSOFT(nullptr), alcDeviceResumeSOFT(nullptr)
66 setupExts();
69 ALDevice::~ALDevice()
74 void ALDevice::removeContext(ALContext *ctx)
76 auto iter = std::find_if(mContexts.begin(), mContexts.end(),
77 [ctx](const UniquePtr<ALContext> &entry) -> bool
78 { return entry.get() == ctx; }
80 if(iter != mContexts.end()) mContexts.erase(iter);
84 String ALDevice::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 ALDevice::queryExtension(const String &name) const
97 return alcIsExtensionPresent(mDevice, name.c_str());
100 ALCuint ALDevice::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 MakeVersion(
108 (ALCushort)std::min<ALCint>(major, std::numeric_limits<ALCushort>::max()),
109 (ALCushort)std::min<ALCint>(minor, std::numeric_limits<ALCushort>::max())
113 ALCuint ALDevice::getEFXVersion() const
115 if(!alcIsExtensionPresent(mDevice, "ALC_EXT_EFX"))
116 return 0;
118 ALCint major=-1, minor=-1;
119 alcGetIntegerv(mDevice, ALC_EFX_MAJOR_VERSION, 1, &major);
120 alcGetIntegerv(mDevice, ALC_EFX_MINOR_VERSION, 1, &minor);
121 if(major < 0 || minor < 0)
122 throw std::runtime_error("EFX version error");
123 return MakeVersion(
124 (ALCushort)std::min<ALCint>(major, std::numeric_limits<ALCushort>::max()),
125 (ALCushort)std::min<ALCint>(minor, std::numeric_limits<ALCushort>::max())
129 ALCuint ALDevice::getFrequency() const
131 ALCint freq = -1;
132 alcGetIntegerv(mDevice, ALC_FREQUENCY, 1, &freq);
133 if(freq < 0)
134 throw std::runtime_error("Frequency error");
135 return freq;
138 ALCuint ALDevice::getMaxAuxiliarySends() const
140 if(!alcIsExtensionPresent(mDevice, "ALC_EXT_EFX"))
141 return 0;
143 ALCint sends=-1;
144 alcGetIntegerv(mDevice, ALC_MAX_AUXILIARY_SENDS, 1, &sends);
145 if(sends < 0)
146 throw std::runtime_error("Max auxiliary sends error");
147 return sends;
151 Vector<String> ALDevice::enumerateHRTFNames() const
153 if(!hasExtension(SOFT_HRTF))
154 throw std::runtime_error("ALC_SOFT_HRTF not supported");
156 ALCint num_hrtfs = -1;
157 alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtfs);
158 if(num_hrtfs < 0)
159 throw std::runtime_error("HRTF specifier count error");
161 Vector<String> hrtfs;
162 hrtfs.reserve(num_hrtfs);
163 for(int i = 0;i < num_hrtfs;++i)
164 hrtfs.emplace_back(alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i));
165 return hrtfs;
168 bool ALDevice::isHRTFEnabled() const
170 if(!hasExtension(SOFT_HRTF))
171 throw std::runtime_error("ALC_SOFT_HRTF not supported");
173 ALCint hrtf_state = -1;
174 alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state);
175 if(hrtf_state == -1)
176 throw std::runtime_error("HRTF state error");
177 return hrtf_state != ALC_FALSE;
180 String ALDevice::getCurrentHRTF() const
182 if(!hasExtension(SOFT_HRTF))
183 throw std::runtime_error("ALC_SOFT_HRTF not supported");
184 return String(alcGetString(mDevice, ALC_HRTF_SPECIFIER_SOFT));
187 void ALDevice::reset(ArrayView<AttributePair> attributes)
189 if(!hasExtension(SOFT_HRTF))
190 throw std::runtime_error("ALC_SOFT_HRTF not supported");
191 auto do_reset = [this, &attributes]() -> ALCboolean
193 if(attributes.empty())
195 /* No explicit attributes. */
196 return alcResetDeviceSOFT(mDevice, nullptr);
198 auto attr_end = std::find_if(attributes.begin(), attributes.end(),
199 [](const AttributePair &attr) -> bool
200 { return std::get<0>(attr) == 0; }
202 if(attr_end == attributes.end())
204 /* Attribute list was not properly terminated. Copy the attribute
205 * list and add the 0 sentinel.
207 Vector<AttributePair> attrs;
208 attrs.reserve(attributes.size());
209 std::copy(attributes.begin(), attributes.end(), std::back_inserter(attrs));
210 attrs.push_back({0, 0});
211 return alcResetDeviceSOFT(mDevice, &std::get<0>(attrs.front()));
213 return alcResetDeviceSOFT(mDevice, &std::get<0>(attributes.front()));
215 if(!do_reset())
216 throw std::runtime_error("Device reset error");
220 Context ALDevice::createContext(ArrayView<AttributePair> attributes)
222 ALCcontext *ctx = [this, &attributes]() -> ALCcontext*
224 if(attributes.empty())
226 /* No explicit attributes. */
227 return alcCreateContext(mDevice, nullptr);
229 auto attr_end = std::find_if(attributes.begin(), attributes.end(),
230 [](const AttributePair &attr) -> bool
231 { return std::get<0>(attr) == 0; }
233 if(attr_end == attributes.end())
235 /* Attribute list was not properly terminated. Copy the attribute
236 * list and add the 0 sentinel.
238 Vector<AttributePair> attrs;
239 attrs.reserve(attributes.size());
240 std::copy(attributes.begin(), attributes.end(), std::back_inserter(attrs));
241 attrs.push_back({0, 0});
242 return alcCreateContext(mDevice, &std::get<0>(attrs.front()));
244 return alcCreateContext(mDevice, &std::get<0>(attributes.front()));
245 }();
246 if(!ctx) throw std::runtime_error("Failed to create context");
248 mContexts.emplace_back(MakeUnique<ALContext>(ctx, this));
249 return Context(mContexts.back().get());
253 void ALDevice::pauseDSP()
255 if(!hasExtension(SOFT_device_pause))
256 throw std::runtime_error("ALC_SOFT_pause_device not supported");
257 alcDevicePauseSOFT(mDevice);
260 void ALDevice::resumeDSP()
262 if(hasExtension(SOFT_device_pause))
263 alcDeviceResumeSOFT(mDevice);
267 void ALDevice::close()
269 if(!mContexts.empty())
270 throw std::runtime_error("Trying to close device with contexts");
272 if(alcCloseDevice(mDevice) == ALC_FALSE)
273 throw std::runtime_error("Failed to close device");
274 mDevice = 0;
276 ALDeviceManager::get().removeDevice(this);
280 DECL_THUNK1(String, Device, getName, const, PlaybackName)
281 DECL_THUNK1(bool, Device, queryExtension, const, const String&)
282 DECL_THUNK0(ALCuint, Device, getALCVersion, const)
283 DECL_THUNK0(ALCuint, Device, getEFXVersion, const)
284 DECL_THUNK0(ALCuint, Device, getFrequency, const)
285 DECL_THUNK0(ALCuint, Device, getMaxAuxiliarySends, const)
286 DECL_THUNK0(Vector<String>, Device, enumerateHRTFNames, const)
287 DECL_THUNK0(bool, Device, isHRTFEnabled, const)
288 DECL_THUNK0(String, Device, getCurrentHRTF, const)
289 DECL_THUNK1(void, Device, reset,, ArrayView<AttributePair>)
290 DECL_THUNK1(Context, Device, createContext,, ArrayView<AttributePair>)
291 DECL_THUNK0(void, Device, pauseDSP,)
292 DECL_THUNK0(void, Device, resumeDSP,)
293 void Device::close()
295 pImpl->close();
296 pImpl = nullptr;