11 #include "devicemanager.h"
18 using alure::DeviceImpl
;
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
*) { }
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
},
56 void DeviceImpl::setupExts()
58 for(const auto &entry
: ALCExtensionList
)
60 if(!alcIsExtensionPresent(mDevice
, entry
.name
))
62 mHasExt
.set(static_cast<size_t>(entry
.extension
));
68 DeviceImpl::DeviceImpl(const char *name
)
70 mDevice
= alcOpenDevice(name
);
71 if(!mDevice
) throw alc_error(alcGetError(nullptr), "alcOpenDevice failed");
74 mPauseTime
= mTimeBase
= std::chrono::steady_clock::now().time_since_epoch();
77 DeviceImpl::~DeviceImpl()
82 alcCloseDevice(mDevice
);
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
148 alcGetIntegerv(mDevice
, ALC_FREQUENCY
, 1, &freq
);
150 throw std::runtime_error("Frequency error");
154 DECL_THUNK0(ALCuint
, Device
, getMaxAuxiliarySends
, const)
155 ALCuint
DeviceImpl::getMaxAuxiliarySends() const
157 if(!hasExtension(ALC::EXT_EFX
))
161 alcGetIntegerv(mDevice
, ALC_MAX_AUXILIARY_SENDS
, 1, &sends
);
163 throw std::runtime_error("Max auxiliary sends error");
168 DECL_THUNK0(Vector
<String
>, Device
, enumerateHRTFNames
, const)
169 Vector
<String
> DeviceImpl::enumerateHRTFNames() const
171 Vector
<String
> hrtfs
;
172 if(!hasExtension(ALC::SOFT_HRTF
))
175 ALCint num_hrtfs
= -1;
176 alcGetIntegerv(mDevice
, ALC_NUM_HRTF_SPECIFIERS_SOFT
, 1, &num_hrtfs
);
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
));
186 DECL_THUNK0(bool, Device
, isHRTFEnabled
, const)
187 bool DeviceImpl::isHRTFEnabled() const
189 if(!hasExtension(ALC::SOFT_HRTF
))
192 ALCint hrtf_state
= -1;
193 alcGetIntegerv(mDevice
, ALC_HRTF_SOFT
, 1, &hrtf_state
);
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
))
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
))
212 ALCboolean success
= ALC_FALSE
;
213 if(attributes
.end()) /* No explicit attributes. */
214 success
= alcResetDeviceSOFT(mDevice
, nullptr);
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
);
233 success
= alcResetDeviceSOFT(mDevice
, &attributes
.front().mAttribute
);
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());
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
277 return pImpl
->createContext(attrs
);
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();
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();
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
;
319 cur_time
= mPauseTime
;
321 return cur_time
- mTimeBase
;
327 DeviceImpl
*i
= pImpl
;
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");
340 DeviceManagerImpl::getInstance()->removeDevice(this);