Add methods to lookup cached buffers without loading them
[alure.git] / src / device.cpp
blobccf4176ba027d57c6f5020ddef78187ee5252d6c
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 alure
19 template<typename T>
20 static inline void LoadALCFunc(ALCdevice *device, T **func, const char *name)
21 { *func = reinterpret_cast<T*>(alcGetProcAddress(device, name)); }
24 static void LoadPauseDevice(DeviceImpl *device)
26 LoadALCFunc(device->getALCdevice(), &device->alcDevicePauseSOFT, "alcDevicePauseSOFT");
27 LoadALCFunc(device->getALCdevice(), &device->alcDeviceResumeSOFT, "alcDeviceResumeSOFT");
30 static void LoadHrtf(DeviceImpl *device)
32 LoadALCFunc(device->getALCdevice(), &device->alcGetStringiSOFT, "alcGetStringiSOFT");
33 LoadALCFunc(device->getALCdevice(), &device->alcResetDeviceSOFT, "alcResetDeviceSOFT");
36 static void LoadNothing(DeviceImpl*) { }
38 static const struct {
39 enum ALC extension;
40 const char name[32];
41 void (&loader)(DeviceImpl*);
42 } ALCExtensionList[] = {
43 { ALC::ENUMERATE_ALL_EXT, "ALC_ENUMERATE_ALL_EXT", LoadNothing },
44 { ALC::EXT_EFX, "ALC_EXT_EFX", LoadNothing },
45 { ALC::EXT_thread_local_context, "ALC_EXT_thread_local_context", LoadNothing },
46 { ALC::SOFT_device_pause, "ALC_SOFT_pause_device", LoadPauseDevice },
47 { ALC::SOFT_HRTF, "ALC_SOFT_HRTF", LoadHrtf },
51 void DeviceImpl::setupExts()
53 mHasExt.clear();
54 for(const auto &entry : ALCExtensionList)
56 if(!alcIsExtensionPresent(mDevice, entry.name))
57 continue;
58 mHasExt.set(static_cast<size_t>(entry.extension));
59 entry.loader(this);
64 DeviceImpl::DeviceImpl(ALCdevice* device)
65 : mDevice(device), alcDevicePauseSOFT(nullptr), alcDeviceResumeSOFT(nullptr)
67 setupExts();
70 DeviceImpl::~DeviceImpl()
75 void DeviceImpl::removeContext(ContextImpl *ctx)
77 auto iter = std::find_if(mContexts.begin(), mContexts.end(),
78 [ctx](const UniquePtr<ContextImpl> &entry) -> bool
79 { return entry.get() == ctx; }
81 if(iter != mContexts.end()) mContexts.erase(iter);
85 String DeviceImpl::getName(PlaybackName type) const
87 if(type == PlaybackName::Full && !hasExtension(ALC::ENUMERATE_ALL_EXT))
88 type = PlaybackName::Basic;
89 alcGetError(mDevice);
90 const ALCchar *name = alcGetString(mDevice, (ALenum)type);
91 if(alcGetError(mDevice) != ALC_NO_ERROR || !name)
92 name = alcGetString(mDevice, (ALenum)PlaybackName::Basic);
93 return name ? String(name) : String();
96 bool DeviceImpl::queryExtension(const char *name) const
98 return alcIsExtensionPresent(mDevice, name);
101 Version DeviceImpl::getALCVersion() const
103 ALCint major=-1, minor=-1;
104 alcGetIntegerv(mDevice, ALC_MAJOR_VERSION, 1, &major);
105 alcGetIntegerv(mDevice, ALC_MINOR_VERSION, 1, &minor);
106 if(major < 0 || minor < 0)
107 throw std::runtime_error("ALC version error");
108 return Version{ (ALCuint)major, (ALCuint)minor };
111 Version DeviceImpl::getEFXVersion() const
113 if(!hasExtension(ALC::EXT_EFX))
114 return Version{ 0u, 0u };
116 ALCint major=-1, minor=-1;
117 alcGetIntegerv(mDevice, ALC_EFX_MAJOR_VERSION, 1, &major);
118 alcGetIntegerv(mDevice, ALC_EFX_MINOR_VERSION, 1, &minor);
119 if(major < 0 || minor < 0)
120 throw std::runtime_error("EFX version error");
121 return Version{ (ALCuint)major, (ALCuint)minor };
124 ALCuint DeviceImpl::getFrequency() const
126 ALCint freq = -1;
127 alcGetIntegerv(mDevice, ALC_FREQUENCY, 1, &freq);
128 if(freq < 0)
129 throw std::runtime_error("Frequency error");
130 return freq;
133 ALCuint DeviceImpl::getMaxAuxiliarySends() const
135 if(!hasExtension(ALC::EXT_EFX))
136 return 0;
138 ALCint sends=-1;
139 alcGetIntegerv(mDevice, ALC_MAX_AUXILIARY_SENDS, 1, &sends);
140 if(sends < 0)
141 throw std::runtime_error("Max auxiliary sends error");
142 return sends;
146 Vector<String> DeviceImpl::enumerateHRTFNames() const
148 if(!hasExtension(ALC::SOFT_HRTF))
149 throw std::runtime_error("ALC_SOFT_HRTF not supported");
151 ALCint num_hrtfs = -1;
152 alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtfs);
153 if(num_hrtfs < 0)
154 throw std::runtime_error("HRTF specifier count error");
156 Vector<String> hrtfs;
157 hrtfs.reserve(num_hrtfs);
158 for(int i = 0;i < num_hrtfs;++i)
159 hrtfs.emplace_back(alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i));
160 return hrtfs;
163 bool DeviceImpl::isHRTFEnabled() const
165 if(!hasExtension(ALC::SOFT_HRTF))
166 throw std::runtime_error("ALC_SOFT_HRTF not supported");
168 ALCint hrtf_state = -1;
169 alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state);
170 if(hrtf_state == -1)
171 throw std::runtime_error("HRTF state error");
172 return hrtf_state != ALC_FALSE;
175 String DeviceImpl::getCurrentHRTF() const
177 if(!hasExtension(ALC::SOFT_HRTF))
178 throw std::runtime_error("ALC_SOFT_HRTF not supported");
179 return String(alcGetString(mDevice, ALC_HRTF_SPECIFIER_SOFT));
182 void DeviceImpl::reset(ArrayView<AttributePair> attributes)
184 if(!hasExtension(ALC::SOFT_HRTF))
185 throw std::runtime_error("ALC_SOFT_HRTF not supported");
186 auto do_reset = [this, &attributes]() -> ALCboolean
188 if(attributes.empty())
190 /* No explicit attributes. */
191 return alcResetDeviceSOFT(mDevice, nullptr);
193 auto attr_end = std::find_if(attributes.begin(), attributes.end(),
194 [](const AttributePair &attr) -> bool
195 { return std::get<0>(attr) == 0; }
197 if(attr_end == attributes.end())
199 /* Attribute list was not properly terminated. Copy the attribute
200 * list and add the 0 sentinel.
202 Vector<AttributePair> attrs;
203 attrs.reserve(attributes.size() + 1);
204 std::copy(attributes.begin(), attributes.end(), std::back_inserter(attrs));
205 attrs.push_back(AttributesEnd());
206 return alcResetDeviceSOFT(mDevice, &std::get<0>(attrs.front()));
208 return alcResetDeviceSOFT(mDevice, &std::get<0>(attributes.front()));
210 if(!do_reset())
211 throw std::runtime_error("Device reset error");
215 Context DeviceImpl::createContext(ArrayView<AttributePair> attributes, bool dothrow)
217 ALCcontext *ctx = [this, &attributes]() -> ALCcontext*
219 if(attributes.empty())
221 /* No explicit attributes. */
222 return alcCreateContext(mDevice, nullptr);
224 auto attr_end = std::find_if(attributes.begin(), attributes.end(),
225 [](const AttributePair &attr) -> bool
226 { return std::get<0>(attr) == 0; }
228 if(attr_end == attributes.end())
230 /* Attribute list was not properly terminated. Copy the attribute
231 * list and add the 0 sentinel.
233 Vector<AttributePair> attrs;
234 attrs.reserve(attributes.size() + 1);
235 std::copy(attributes.begin(), attributes.end(), std::back_inserter(attrs));
236 attrs.push_back(AttributesEnd());
237 return alcCreateContext(mDevice, &std::get<0>(attrs.front()));
239 return alcCreateContext(mDevice, &std::get<0>(attributes.front()));
240 }();
241 if(!ctx)
243 if(dothrow)
244 throw std::runtime_error("Failed to create context");
245 return Context();
248 mContexts.emplace_back(MakeUnique<ContextImpl>(ctx, this));
249 return Context(mContexts.back().get());
253 void DeviceImpl::pauseDSP()
255 if(!hasExtension(ALC::SOFT_device_pause))
256 throw std::runtime_error("ALC_SOFT_pause_device not supported");
257 alcDevicePauseSOFT(mDevice);
260 void DeviceImpl::resumeDSP()
262 if(hasExtension(ALC::SOFT_device_pause))
263 alcDeviceResumeSOFT(mDevice);
267 void DeviceImpl::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 DeviceManagerImpl::get().removeDevice(this);
280 DECL_THUNK1(String, Device, getName, const, PlaybackName)
281 DECL_THUNK1(bool, Device, queryExtension, const, const char*)
282 bool Device::queryExtension(const String &name) const
283 { return pImpl->queryExtension(name.c_str()); }
284 DECL_THUNK0(Version, Device, getALCVersion, const)
285 DECL_THUNK0(Version, Device, getEFXVersion, const)
286 DECL_THUNK0(ALCuint, Device, getFrequency, const)
287 DECL_THUNK0(ALCuint, Device, getMaxAuxiliarySends, const)
288 DECL_THUNK0(Vector<String>, Device, enumerateHRTFNames, const)
289 DECL_THUNK0(bool, Device, isHRTFEnabled, const)
290 DECL_THUNK0(String, Device, getCurrentHRTF, const)
291 DECL_THUNK1(void, Device, reset,, ArrayView<AttributePair>)
292 Context Device::createContext(ArrayView<AttributePair> attrs)
293 { return pImpl->createContext(attrs, true); }
294 Context Device::createContext(ArrayView<AttributePair> attrs, const std::nothrow_t&)
295 { return pImpl->createContext(attrs, false); }
296 Context Device::createContext(const std::nothrow_t&)
297 { return pImpl->createContext(ArrayView<AttributePair>(), false); }
298 DECL_THUNK0(void, Device, pauseDSP,)
299 DECL_THUNK0(void, Device, resumeDSP,)
300 void Device::close()
302 pImpl->close();
303 pImpl = nullptr;