19 #include "decoders/wave.hpp"
21 #ifdef HAVE_VORBISFILE
22 #include "decoders/vorbisfile.hpp"
25 #include "decoders/flac.hpp"
28 #include "decoders/opusfile.hpp"
30 #ifdef HAVE_LIBSNDFILE
31 #include "decoders/sndfile.hpp"
34 #include "decoders/mp3.hpp"
37 #include "devicemanager.h"
41 #include "auxeffectslot.h"
43 #include "sourcegroup.h"
46 #define WIN32_LEAN_AND_MEAN
52 // Implements a FNV-1a hash for StringView. NOTE: This is *NOT* guaranteed
53 // compatible with std::hash<String>! The standard does not give any specific
54 // hash implementation, nor a way for applications to access the same hash
55 // function as std::string (short of copying into a string and hashing that).
56 // So if you need Strings and StringViews to result in the same hash for the
57 // same set of characters, hash StringViews created from the Strings.
59 struct hash
<alure::StringView
> {
60 size_t operator()(const alure::StringView
&str
) const noexcept
62 using traits_type
= alure::StringView::traits_type
;
64 if /*constexpr*/ (sizeof(size_t) == 8)
66 static constexpr size_t hash_offset
= 0xcbf29ce484222325;
67 static constexpr size_t hash_prime
= 0x100000001b3;
69 size_t val
= hash_offset
;
71 val
= (val
^traits_type::to_int_type(ch
)) * hash_prime
;
76 static constexpr size_t hash_offset
= 0x811c9dc5;
77 static constexpr size_t hash_prime
= 0x1000193;
79 size_t val
= hash_offset
;
81 val
= (val
^traits_type::to_int_type(ch
)) * hash_prime
;
91 // Global mutex to protect global context changes
92 std::mutex gGlobalCtxMutex
;
95 // Windows' std::ifstream fails with non-ANSI paths since the standard only
96 // specifies names using const char* (or std::string). MSVC has a non-standard
97 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
98 // but not all Windows compilers support it. So we have to make our own istream
99 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
100 class StreamBuf final
: public std::streambuf
{
101 alure::Array
<char_type
,4096> mBuffer
;
102 HANDLE mFile
{INVALID_HANDLE_VALUE
};
104 int_type
underflow() override
106 if(mFile
!= INVALID_HANDLE_VALUE
&& gptr() == egptr())
108 // Read in the next chunk of data, and set the pointers on success
110 if(ReadFile(mFile
, mBuffer
.data(), (DWORD
)mBuffer
.size(), &got
, NULL
))
111 setg(mBuffer
.data(), mBuffer
.data(), mBuffer
.data()+got
);
113 if(gptr() == egptr())
114 return traits_type::eof();
115 return traits_type::to_int_type(*gptr());
118 pos_type
seekoff(off_type offset
, std::ios_base::seekdir whence
, std::ios_base::openmode mode
) override
120 if(mFile
== INVALID_HANDLE_VALUE
|| (mode
&std::ios_base::out
) || !(mode
&std::ios_base::in
))
121 return traits_type::eof();
126 case std::ios_base::beg
:
127 fpos
.QuadPart
= offset
;
128 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_BEGIN
))
129 return traits_type::eof();
132 case std::ios_base::cur
:
133 // If the offset remains in the current buffer range, just
134 // update the pointer.
135 if((offset
>= 0 && offset
< off_type(egptr()-gptr())) ||
136 (offset
< 0 && -offset
<= off_type(gptr()-eback())))
138 // Get the current file offset to report the correct read
141 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_CURRENT
))
142 return traits_type::eof();
143 setg(eback(), gptr()+offset
, egptr());
144 return fpos
.QuadPart
- off_type(egptr()-gptr());
146 // Need to offset for the file offset being at egptr() while
147 // the requested offset is relative to gptr().
148 offset
-= off_type(egptr()-gptr());
149 fpos
.QuadPart
= offset
;
150 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_CURRENT
))
151 return traits_type::eof();
154 case std::ios_base::end
:
155 fpos
.QuadPart
= offset
;
156 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_END
))
157 return traits_type::eof();
161 return traits_type::eof();
163 setg(nullptr, nullptr, nullptr);
164 return fpos
.QuadPart
;
167 pos_type
seekpos(pos_type pos
, std::ios_base::openmode mode
) override
169 // Simplified version of seekoff
170 if(mFile
== INVALID_HANDLE_VALUE
|| (mode
&std::ios_base::out
) || !(mode
&std::ios_base::in
))
171 return traits_type::eof();
175 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_BEGIN
))
176 return traits_type::eof();
178 setg(nullptr, nullptr, nullptr);
179 return fpos
.QuadPart
;
183 bool open(const char *filename
)
185 alure::Vector
<wchar_t> wname
;
188 wnamelen
= MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, NULL
, 0);
189 if(wnamelen
<= 0) return false;
191 wname
.resize(wnamelen
);
192 MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, wname
.data(), wnamelen
);
194 mFile
= CreateFileW(wname
.data(), GENERIC_READ
, FILE_SHARE_READ
, NULL
,
195 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, nullptr);
196 if(mFile
== INVALID_HANDLE_VALUE
) return false;
200 bool is_open() const noexcept
{ return mFile
!= INVALID_HANDLE_VALUE
; }
202 StreamBuf() = default;
203 ~StreamBuf() override
205 if(mFile
!= INVALID_HANDLE_VALUE
)
207 mFile
= INVALID_HANDLE_VALUE
;
211 // Inherit from std::istream to use our custom streambuf
212 class Stream final
: public std::istream
{
213 StreamBuf mStreamBuf
;
216 Stream(const char *filename
) : std::istream(nullptr)
220 // Set the failbit if the file failed to open.
221 if(!mStreamBuf
.open(filename
)) clear(failbit
);
224 bool is_open() const noexcept
{ return mStreamBuf
.is_open(); }
228 using DecoderEntryPair
= std::pair
<alure::String
,alure::UniquePtr
<alure::DecoderFactory
>>;
229 const DecoderEntryPair sDefaultDecoders
[] = {
231 { "_alure_int_wave", alure::MakeUnique
<alure::WaveDecoderFactory
>() },
233 #ifdef HAVE_VORBISFILE
234 { "_alure_int_vorbis", alure::MakeUnique
<alure::VorbisFileDecoderFactory
>() },
237 { "_alure_int_flac", alure::MakeUnique
<alure::FlacDecoderFactory
>() },
240 { "_alure_int_opus", alure::MakeUnique
<alure::OpusFileDecoderFactory
>() },
242 #ifdef HAVE_LIBSNDFILE
243 { "_alure_int_sndfile", alure::MakeUnique
<alure::SndFileDecoderFactory
>() },
246 { "_alure_int_minimp3", alure::MakeUnique
<alure::Mp3DecoderFactory
>() },
249 alure::Vector
<DecoderEntryPair
> sDecoders
;
252 alure::DecoderOrExceptT
GetDecoder(alure::UniquePtr
<std::istream
> &file
,
253 alure::ArrayView
<DecoderEntryPair
> decoders
)
255 while(!decoders
.empty())
257 alure::DecoderFactory
*factory
= decoders
.front().second
.get();
258 auto decoder
= factory
->createDecoder(file
);
259 if(decoder
) return std::move(decoder
);
261 if(!file
|| !(file
->clear(),file
->seekg(0)))
262 return std::make_exception_ptr(
263 std::runtime_error("Failed to rewind file for the next decoder factory")
266 decoders
= decoders
.slice(1);
269 return alure::SharedPtr
<alure::Decoder
>(nullptr);
272 static alure::DecoderOrExceptT
GetDecoder(alure::UniquePtr
<std::istream
> file
)
274 auto decoder
= GetDecoder(file
, sDecoders
);
275 if(std::holds_alternative
<std::exception_ptr
>(decoder
)) return decoder
;
276 if(std::get
<alure::SharedPtr
<alure::Decoder
>>(decoder
)) return decoder
;
277 decoder
= GetDecoder(file
, sDefaultDecoders
);
278 if(std::holds_alternative
<std::exception_ptr
>(decoder
)) return decoder
;
279 if(std::get
<alure::SharedPtr
<alure::Decoder
>>(decoder
)) return decoder
;
280 return (decoder
= std::make_exception_ptr(std::runtime_error("No decoder found")));
283 class DefaultFileIOFactory final
: public alure::FileIOFactory
{
284 alure::UniquePtr
<std::istream
> openFile(const alure::String
&name
) noexcept override
287 auto file
= alure::MakeUnique
<Stream
>(name
.c_str());
289 auto file
= alure::MakeUnique
<std::ifstream
>(name
.c_str(), std::ios::binary
);
291 if(!file
->is_open()) file
= nullptr;
292 return std::move(file
);
295 DefaultFileIOFactory sDefaultFileFactory
;
297 alure::UniquePtr
<alure::FileIOFactory
> sFileFactory
;
303 static inline void CheckContext(const ContextImpl
*ctx
)
305 auto count
= ContextImpl::sContextSetCount
.load(std::memory_order_acquire
);
306 if(UNLIKELY(count
!= ctx
->mContextSetCounter
))
308 if(UNLIKELY(ctx
!= ContextImpl::GetCurrent()))
309 throw std::runtime_error("Called context is not current");
310 ctx
->mContextSetCounter
= count
;
314 std::variant
<std::monostate
,uint64_t> ParseTimeval(StringView strval
, double srate
) noexcept
318 size_t cpos
= strval
.find_first_of(':');
319 if(cpos
== StringView::npos
)
321 // No colon is present, treat it as a plain sample offset
322 uint64_t val
= std::stoull(String(strval
), &endpos
);
323 if(endpos
!= strval
.length()) return {};
327 // Value is not a sample offset. Its format is [[HH:]MM]:SS[.sss] (at
328 // least one colon must exist to be interpreted this way).
333 // If a non-empty first value, parse it (may be hours or minutes)
334 val
= std::stoul(String(strval
.data(), cpos
), &endpos
);
335 if(endpos
!= cpos
) return {};
338 strval
= strval
.substr(cpos
+1);
339 cpos
= strval
.find_first_of(':');
340 if(cpos
!= StringView::npos
)
342 // If a second colon is present, the first value was hours and this is
343 // minutes, otherwise the first value was minutes.
348 val2
= std::stoul(String(strval
.data(), cpos
), &endpos
);
349 if(endpos
!= cpos
|| val2
>= 60) return {};
352 // Combines hours and minutes into the full minute count
353 if(val
> std::numeric_limits
<uint64_t>::max()/60)
356 strval
= strval
.substr(cpos
+1);
362 // Parse the seconds and its fraction. Only include the first 3 decimal
363 // places for millisecond precision.
364 size_t dpos
= strval
.find_first_of('.');
365 String str
= (dpos
== StringView::npos
) ?
366 String(strval
) : String(strval
.substr(0, dpos
+4));
367 secs
= std::stod(str
, &endpos
);
368 if(endpos
!= str
.length() || !(secs
>= 0.0 && secs
< 60.0))
372 // Convert minutes to seconds, add the seconds, then convert to samples.
373 return static_cast<uint64_t>((val
*60.0 + secs
) * srate
);
382 Decoder::~Decoder() { }
383 DecoderFactory::~DecoderFactory() { }
385 void RegisterDecoder(StringView name
, UniquePtr
<DecoderFactory
> factory
)
387 auto iter
= std::lower_bound(sDecoders
.begin(), sDecoders
.end(), name
,
388 [](const DecoderEntryPair
&entry
, StringView rhs
) -> bool
389 { return entry
.first
< rhs
; }
391 if(iter
!= sDecoders
.end())
392 throw std::runtime_error("Decoder factory already registered");
393 sDecoders
.insert(iter
, std::make_pair(String(name
), std::move(factory
)));
396 UniquePtr
<DecoderFactory
> UnregisterDecoder(StringView name
) noexcept
398 UniquePtr
<DecoderFactory
> factory
;
399 auto iter
= std::lower_bound(sDecoders
.begin(), sDecoders
.end(), name
,
400 [](const DecoderEntryPair
&entry
, StringView rhs
) noexcept
-> bool
401 { return entry
.first
< rhs
; }
403 if(iter
!= sDecoders
.end())
405 factory
= std::move(iter
->second
);
406 sDecoders
.erase(iter
);
413 FileIOFactory::~FileIOFactory() { }
415 UniquePtr
<FileIOFactory
> FileIOFactory::set(UniquePtr
<FileIOFactory
> factory
) noexcept
417 sFileFactory
.swap(factory
);
421 FileIOFactory
&FileIOFactory::get() noexcept
423 FileIOFactory
*factory
= sFileFactory
.get();
424 if(factory
) return *factory
;
425 return sDefaultFileFactory
;
429 // Default message handler methods are no-ops.
430 MessageHandler::~MessageHandler()
434 void MessageHandler::deviceDisconnected(Device
) noexcept
438 void MessageHandler::sourceStopped(Source
) noexcept
442 void MessageHandler::sourceForceStopped(Source
) noexcept
446 void MessageHandler::bufferLoading(StringView
, ChannelConfig
, SampleType
, ALuint
, ArrayView
<ALbyte
>) noexcept
450 String
MessageHandler::resourceNotFound(StringView
) noexcept
457 static inline void LoadALFunc(T
**func
, const char *name
)
458 { *func
= reinterpret_cast<T
*>(alGetProcAddress(name
)); }
460 static void LoadNothing(ContextImpl
*) { }
462 static void LoadEFX(ContextImpl
*ctx
)
464 LoadALFunc(&ctx
->alGenEffects
, "alGenEffects");
465 LoadALFunc(&ctx
->alDeleteEffects
, "alDeleteEffects");
466 LoadALFunc(&ctx
->alIsEffect
, "alIsEffect");
467 LoadALFunc(&ctx
->alEffecti
, "alEffecti");
468 LoadALFunc(&ctx
->alEffectiv
, "alEffectiv");
469 LoadALFunc(&ctx
->alEffectf
, "alEffectf");
470 LoadALFunc(&ctx
->alEffectfv
, "alEffectfv");
471 LoadALFunc(&ctx
->alGetEffecti
, "alGetEffecti");
472 LoadALFunc(&ctx
->alGetEffectiv
, "alGetEffectiv");
473 LoadALFunc(&ctx
->alGetEffectf
, "alGetEffectf");
474 LoadALFunc(&ctx
->alGetEffectfv
, "alGetEffectfv");
476 LoadALFunc(&ctx
->alGenFilters
, "alGenFilters");
477 LoadALFunc(&ctx
->alDeleteFilters
, "alDeleteFilters");
478 LoadALFunc(&ctx
->alIsFilter
, "alIsFilter");
479 LoadALFunc(&ctx
->alFilteri
, "alFilteri");
480 LoadALFunc(&ctx
->alFilteriv
, "alFilteriv");
481 LoadALFunc(&ctx
->alFilterf
, "alFilterf");
482 LoadALFunc(&ctx
->alFilterfv
, "alFilterfv");
483 LoadALFunc(&ctx
->alGetFilteri
, "alGetFilteri");
484 LoadALFunc(&ctx
->alGetFilteriv
, "alGetFilteriv");
485 LoadALFunc(&ctx
->alGetFilterf
, "alGetFilterf");
486 LoadALFunc(&ctx
->alGetFilterfv
, "alGetFilterfv");
488 LoadALFunc(&ctx
->alGenAuxiliaryEffectSlots
, "alGenAuxiliaryEffectSlots");
489 LoadALFunc(&ctx
->alDeleteAuxiliaryEffectSlots
, "alDeleteAuxiliaryEffectSlots");
490 LoadALFunc(&ctx
->alIsAuxiliaryEffectSlot
, "alIsAuxiliaryEffectSlot");
491 LoadALFunc(&ctx
->alAuxiliaryEffectSloti
, "alAuxiliaryEffectSloti");
492 LoadALFunc(&ctx
->alAuxiliaryEffectSlotiv
, "alAuxiliaryEffectSlotiv");
493 LoadALFunc(&ctx
->alAuxiliaryEffectSlotf
, "alAuxiliaryEffectSlotf");
494 LoadALFunc(&ctx
->alAuxiliaryEffectSlotfv
, "alAuxiliaryEffectSlotfv");
495 LoadALFunc(&ctx
->alGetAuxiliaryEffectSloti
, "alGetAuxiliaryEffectSloti");
496 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotiv
, "alGetAuxiliaryEffectSlotiv");
497 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotf
, "alGetAuxiliaryEffectSlotf");
498 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotfv
, "alGetAuxiliaryEffectSlotfv");
501 static void LoadSourceResampler(ContextImpl
*ctx
)
503 LoadALFunc(&ctx
->alGetStringiSOFT
, "alGetStringiSOFT");
506 static void LoadSourceLatency(ContextImpl
*ctx
)
508 LoadALFunc(&ctx
->alGetSourcei64vSOFT
, "alGetSourcei64vSOFT");
509 LoadALFunc(&ctx
->alGetSourcedvSOFT
, "alGetSourcedvSOFT");
512 static const struct {
515 void (&loader
)(ContextImpl
*);
516 } ALExtensionList
[] = {
517 { AL::EXT_EFX
, "ALC_EXT_EFX", LoadEFX
},
519 { AL::EXT_FLOAT32
, "AL_EXT_FLOAT32", LoadNothing
},
520 { AL::EXT_MCFORMATS
, "AL_EXT_MCFORMATS", LoadNothing
},
521 { AL::EXT_BFORMAT
, "AL_EXT_BFORMAT", LoadNothing
},
523 { AL::EXT_MULAW
, "AL_EXT_MULAW", LoadNothing
},
524 { AL::EXT_MULAW_MCFORMATS
, "AL_EXT_MULAW_MCFORMATS", LoadNothing
},
525 { AL::EXT_MULAW_BFORMAT
, "AL_EXT_MULAW_BFORMAT", LoadNothing
},
527 { AL::SOFT_loop_points
, "AL_SOFT_loop_points", LoadNothing
},
528 { AL::SOFT_source_latency
, "AL_SOFT_source_latency", LoadSourceLatency
},
529 { AL::SOFT_source_resampler
, "AL_SOFT_source_resampler", LoadSourceResampler
},
530 { AL::SOFT_source_spatialize
, "AL_SOFT_source_spatialize", LoadNothing
},
532 { AL::EXT_disconnect
, "ALC_EXT_disconnect", LoadNothing
},
534 { AL::EXT_SOURCE_RADIUS
, "AL_EXT_SOURCE_RADIUS", LoadNothing
},
535 { AL::EXT_STEREO_ANGLES
, "AL_EXT_STEREO_ANGLES", LoadNothing
},
539 ContextImpl
*ContextImpl::sCurrentCtx
= nullptr;
540 thread_local ContextImpl
*ContextImpl::sThreadCurrentCtx
= nullptr;
542 std::atomic
<uint64_t> ContextImpl::sContextSetCount
{0};
544 void ContextImpl::MakeCurrent(ContextImpl
*context
)
546 std::unique_lock
<std::mutex
> ctxlock(gGlobalCtxMutex
);
548 if(alcMakeContextCurrent(context
? context
->getALCcontext() : nullptr) == ALC_FALSE
)
549 throw std::runtime_error("Call to alcMakeContextCurrent failed");
553 std::call_once(context
->mSetExts
, std::mem_fn(&ContextImpl::setupExts
), context
);
555 std::swap(sCurrentCtx
, context
);
556 if(context
) context
->decRef();
558 if(sThreadCurrentCtx
)
559 sThreadCurrentCtx
->decRef();
560 sThreadCurrentCtx
= nullptr;
561 sContextSetCount
.fetch_add(1, std::memory_order_release
);
563 if((context
= sCurrentCtx
) != nullptr)
566 context
->mWakeThread
.notify_all();
570 void ContextImpl::MakeThreadCurrent(ContextImpl
*context
)
572 if(!DeviceManagerImpl::SetThreadContext
)
573 throw std::runtime_error("Thread-local contexts unsupported");
574 if(DeviceManagerImpl::SetThreadContext(context
? context
->getALCcontext() : nullptr) == ALC_FALSE
)
575 throw std::runtime_error("Call to alcSetThreadContext failed");
579 std::call_once(context
->mSetExts
, std::mem_fn(&ContextImpl::setupExts
), context
);
581 if(sThreadCurrentCtx
)
582 sThreadCurrentCtx
->decRef();
583 sThreadCurrentCtx
= context
;
584 sContextSetCount
.fetch_add(1, std::memory_order_release
);
587 void ContextImpl::setupExts()
589 ALCdevice
*device
= mDevice
.getALCdevice();
590 for(const auto &entry
: ALExtensionList
)
592 if((strncmp(entry
.name
, "ALC", 3) == 0) ? alcIsExtensionPresent(device
, entry
.name
) :
593 alIsExtensionPresent(entry
.name
))
595 mHasExt
.set(static_cast<size_t>(entry
.extension
));
602 void ContextImpl::backgroundProc()
604 if(DeviceManagerImpl::SetThreadContext
&& mDevice
.hasExtension(ALC::EXT_thread_local_context
))
605 DeviceManagerImpl::SetThreadContext(getALCcontext());
607 std::chrono::steady_clock::time_point basetime
= std::chrono::steady_clock::now();
608 std::chrono::milliseconds
waketime(0);
609 std::unique_lock
<std::mutex
> ctxlock(gGlobalCtxMutex
);
610 while(!mQuitThread
.load(std::memory_order_acquire
))
613 std::lock_guard
<std::mutex
> srclock(mSourceStreamMutex
);
614 mStreamingSources
.erase(
615 std::remove_if(mStreamingSources
.begin(), mStreamingSources
.end(),
616 [](SourceImpl
*source
) -> bool
617 { return !source
->updateAsync(); }
618 ), mStreamingSources
.end()
622 // Only do one pending buffer at a time. In case there's several large
623 // buffers to load, we still need to process streaming sources so they
625 PendingPromise
*lastpb
= mPendingCurrent
.load(std::memory_order_acquire
);
626 if(PendingPromise
*pb
= lastpb
->mNext
.load(std::memory_order_relaxed
))
628 pb
->mBuffer
->load(pb
->mFrames
, pb
->mFormat
, std::move(pb
->mDecoder
), this);
629 pb
->mPromise
.set_value(Buffer(pb
->mBuffer
));
630 Promise
<Buffer
>().swap(pb
->mPromise
);
631 mPendingCurrent
.store(pb
, std::memory_order_release
);
635 std::unique_lock
<std::mutex
> wakelock(mWakeMutex
);
636 if(!mQuitThread
.load(std::memory_order_acquire
) && lastpb
->mNext
.load(std::memory_order_acquire
) == nullptr)
640 std::chrono::milliseconds interval
= mWakeInterval
.load(std::memory_order_relaxed
);
641 if(interval
.count() == 0)
642 mWakeThread
.wait(wakelock
);
645 auto now
= std::chrono::steady_clock::now() - basetime
;
648 auto mult
= (now
-waketime
+ interval
-std::chrono::milliseconds(1)) / interval
;
649 waketime
+= interval
* mult
;
651 mWakeThread
.wait_until(wakelock
, waketime
+ basetime
);
656 while(!mQuitThread
.load(std::memory_order_acquire
) &&
657 alcGetCurrentContext() != getALCcontext())
658 mWakeThread
.wait(ctxlock
);
663 if(DeviceManagerImpl::SetThreadContext
)
664 DeviceManagerImpl::SetThreadContext(nullptr);
668 ContextImpl::ContextImpl(DeviceImpl
&device
, ArrayView
<AttributePair
> attrs
)
669 : mListener(this), mDevice(device
), mIsConnected(true), mIsBatching(false)
671 ALCdevice
*alcdev
= mDevice
.getALCdevice();
672 if(attrs
.empty()) /* No explicit attributes. */
673 mContext
.reset(alcCreateContext(alcdev
, nullptr));
675 mContext
.reset(alcCreateContext(alcdev
, &attrs
.front().mAttribute
));
676 if(!mContext
) throw alc_error(alcGetError(alcdev
), "alcCreateContext failed");
678 mSourceIds
.reserve(256);
679 mPendingTail
= mPendingHead
= new PendingPromise();
680 mPendingCurrent
.store(mPendingHead
, std::memory_order_relaxed
);
683 ContextImpl::~ContextImpl()
685 if(mThread
.joinable())
687 std::unique_lock
<std::mutex
> lock(mWakeMutex
);
688 mQuitThread
.store(true, std::memory_order_relaxed
);
690 mWakeThread
.notify_all();
694 PendingPromise
*pb
= mPendingTail
;
697 PendingPromise
*next
= pb
->mNext
.load(std::memory_order_relaxed
);
701 mPendingCurrent
.store(nullptr, std::memory_order_relaxed
);
702 mPendingTail
= mPendingHead
= nullptr;
704 mEffectSlots
.clear();
707 std::lock_guard
<std::mutex
> ctxlock(gGlobalCtxMutex
);
708 if(sCurrentCtx
== this)
710 sCurrentCtx
= nullptr;
711 sContextSetCount
.fetch_add(1, std::memory_order_release
);
713 if(sThreadCurrentCtx
== this)
715 sThreadCurrentCtx
= nullptr;
716 sContextSetCount
.fetch_add(1, std::memory_order_release
);
721 void Context::destroy()
723 ContextImpl
*i
= pImpl
;
727 void ContextImpl::destroy()
731 std::lock_guard
<std::mutex
> ctxlock(gGlobalCtxMutex
);
732 if(!(mRefs
== 1 && sCurrentCtx
== this))
733 throw std::runtime_error("Context is in use");
735 sCurrentCtx
= nullptr;
736 sContextSetCount
.fetch_add(1, std::memory_order_release
);
739 if(mThread
.joinable())
741 std::unique_lock
<std::mutex
> lock(mWakeMutex
);
742 mQuitThread
.store(true, std::memory_order_release
);
744 mWakeThread
.notify_all();
748 std::unique_lock
<std::mutex
> lock(gGlobalCtxMutex
);
749 if(UNLIKELY(alcMakeContextCurrent(getALCcontext()) == ALC_FALSE
))
750 std::cerr
<< "Failed to cleanup context!" <<std::endl
;
753 mSourceGroups
.clear();
754 mFreeSources
.clear();
757 if(!mSourceIds
.empty())
758 alDeleteSources(static_cast<ALsizei
>(mSourceIds
.size()), mSourceIds
.data());
761 for(auto &bufptr
: mBuffers
)
763 ALuint id
= bufptr
->getId();
764 alDeleteBuffers(1, &id
);
768 mEffectSlots
.clear();
771 ALCcontext
*alctx
= sCurrentCtx
? sCurrentCtx
->getALCcontext() : nullptr;
772 if(UNLIKELY(alcMakeContextCurrent(alctx
) == ALC_FALSE
))
773 std::cerr
<< "Failed to reset global context!" <<std::endl
;
774 if(ContextImpl
*thrd_ctx
= sThreadCurrentCtx
)
776 // alcMakeContextCurrent sets the calling thread's context to null,
777 // set it back to what it was.
778 alctx
= thrd_ctx
->getALCcontext();
779 if(UNLIKELY(DeviceManagerImpl::SetThreadContext(alctx
) == ALC_FALSE
))
780 std::cerr
<< "Failed to reset thread context!" <<std::endl
;
785 mDevice
.removeContext(this);
789 DECL_THUNK0(void, Context
, startBatch
,)
790 void ContextImpl::startBatch()
792 alcSuspendContext(mContext
.get());
796 DECL_THUNK0(void, Context
, endBatch
,)
797 void ContextImpl::endBatch()
799 alcProcessContext(mContext
.get());
804 DECL_THUNK1(SharedPtr
<MessageHandler
>, Context
, setMessageHandler
,, SharedPtr
<MessageHandler
>)
805 SharedPtr
<MessageHandler
> ContextImpl::setMessageHandler(SharedPtr
<MessageHandler
>&& handler
)
807 std::lock_guard
<std::mutex
> lock(gGlobalCtxMutex
);
808 mMessage
.swap(handler
);
813 DECL_THUNK1(void, Context
, setAsyncWakeInterval
,, std::chrono::milliseconds
)
814 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval
)
816 if(interval
.count() < 0 || interval
> std::chrono::seconds(1))
817 throw std::out_of_range("Async wake interval out of range");
818 mWakeInterval
.store(interval
);
819 mWakeMutex
.lock(); mWakeMutex
.unlock();
820 mWakeThread
.notify_all();
824 DecoderOrExceptT
ContextImpl::findDecoder(StringView name
)
826 String oldname
= String(name
);
827 auto file
= FileIOFactory::get().openFile(oldname
);
830 // Resource not found. Try to find a substitute.
832 return std::make_exception_ptr(std::runtime_error("Failed to open file"));
834 String
newname(mMessage
->resourceNotFound(oldname
));
836 return std::make_exception_ptr(std::runtime_error("Failed to open file"));
837 file
= FileIOFactory::get().openFile(newname
);
838 oldname
= std::move(newname
);
841 return GetDecoder(std::move(file
));
844 DECL_THUNK1(SharedPtr
<Decoder
>, Context
, createDecoder
,, StringView
)
845 SharedPtr
<Decoder
> ContextImpl::createDecoder(StringView name
)
848 DecoderOrExceptT dec
= findDecoder(name
);
849 if(SharedPtr
<Decoder
> *decoder
= std::get_if
<SharedPtr
<Decoder
>>(&dec
))
850 return std::move(*decoder
);
851 std::rethrow_exception(std::get
<std::exception_ptr
>(dec
));
855 DECL_THUNK2(bool, Context
, isSupported
, const, ChannelConfig
, SampleType
)
856 bool ContextImpl::isSupported(ChannelConfig channels
, SampleType type
) const
859 return GetFormat(channels
, type
) != AL_NONE
;
863 DECL_THUNK0(ArrayView
<String
>, Context
, getAvailableResamplers
,)
864 ArrayView
<String
> ContextImpl::getAvailableResamplers()
867 if(mResamplers
.empty() && hasExtension(AL::SOFT_source_resampler
))
869 ALint num_resamplers
= alGetInteger(AL_NUM_RESAMPLERS_SOFT
);
870 mResamplers
.reserve(num_resamplers
);
871 for(int i
= 0;i
< num_resamplers
;i
++)
872 mResamplers
.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT
, i
));
873 if(mResamplers
.empty())
874 mResamplers
.emplace_back();
879 DECL_THUNK0(ALsizei
, Context
, getDefaultResamplerIndex
, const)
880 ALsizei
ContextImpl::getDefaultResamplerIndex() const
883 if(!hasExtension(AL::SOFT_source_resampler
))
885 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT
);
889 ContextImpl::FutureBufferListT::const_iterator
ContextImpl::findFutureBufferName(StringView name
, size_t name_hash
) const
891 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), name_hash
,
892 [](const PendingBuffer
&lhs
, size_t rhs
) -> bool
893 { return lhs
.mBuffer
->getNameHash() < rhs
; }
895 while(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getNameHash() == name_hash
&&
896 iter
->mBuffer
->getName() != name
)
901 ContextImpl::BufferListT::const_iterator
ContextImpl::findBufferName(StringView name
, size_t name_hash
) const
903 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), name_hash
,
904 [](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
905 { return lhs
->getNameHash() < rhs
; }
907 while(iter
!= mBuffers
.end() && (*iter
)->getNameHash() == name_hash
&&
908 (*iter
)->getName() != name
)
913 BufferOrExceptT
ContextImpl::doCreateBuffer(StringView name
, size_t name_hash
, BufferListT::const_iterator iter
, SharedPtr
<Decoder
> decoder
)
915 ALuint srate
= decoder
->getFrequency();
916 ChannelConfig chans
= decoder
->getChannelConfig();
917 SampleType type
= decoder
->getSampleType();
918 ALuint frames
= static_cast<ALuint
>(
919 std::min
<uint64_t>(decoder
->getLength(), std::numeric_limits
<ALuint
>::max())
922 Vector
<ALbyte
> data(FramesToBytes(frames
, chans
, type
));
923 frames
= decoder
->read(data
.data(), frames
);
925 return std::make_exception_ptr(std::runtime_error("No samples for buffer"));
926 data
.resize(FramesToBytes(frames
, chans
, type
));
928 std::pair
<uint64_t,uint64_t> loop_pts
= decoder
->getLoopPoints();
929 if(loop_pts
.first
>= loop_pts
.second
)
930 loop_pts
= std::make_pair(0, frames
);
933 loop_pts
.second
= std::min
<uint64_t>(loop_pts
.second
, frames
);
934 loop_pts
.first
= std::min
<uint64_t>(loop_pts
.first
, loop_pts
.second
-1);
937 // Get the format before calling the bufferLoading message handler, to
938 // ensure it's something OpenAL can handle.
939 ALenum format
= GetFormat(chans
, type
);
940 if(UNLIKELY(format
== AL_NONE
))
942 auto str
= String("Unsupported format (")+GetSampleTypeName(type
)+", "+
943 GetChannelConfigName(chans
)+")";
944 return std::make_exception_ptr(std::runtime_error(str
));
948 mMessage
->bufferLoading(name
, chans
, type
, srate
, data
);
952 alGenBuffers(1, &bid
);
953 alBufferData(bid
, format
, data
.data(), static_cast<ALsizei
>(data
.size()), srate
);
954 if(hasExtension(AL::SOFT_loop_points
))
956 ALint pts
[2]{(ALint
)loop_pts
.first
, (ALint
)loop_pts
.second
};
957 alBufferiv(bid
, AL_LOOP_POINTS_SOFT
, pts
);
959 if(ALenum err
= alGetError())
961 alDeleteBuffers(1, &bid
);
962 return std::make_exception_ptr(al_error(err
, "Failed to buffer data"));
965 return mBuffers
.insert(iter
,
966 MakeUnique
<BufferImpl
>(*this, bid
, srate
, chans
, type
, name
, name_hash
)
970 BufferOrExceptT
ContextImpl::doCreateBufferAsync(StringView name
, size_t name_hash
, BufferListT::const_iterator iter
, SharedPtr
<Decoder
> decoder
, Promise
<Buffer
> promise
)
972 ALuint srate
= decoder
->getFrequency();
973 ChannelConfig chans
= decoder
->getChannelConfig();
974 SampleType type
= decoder
->getSampleType();
975 ALuint frames
= static_cast<ALuint
>(
976 std::min
<uint64_t>(decoder
->getLength(), std::numeric_limits
<ALuint
>::max())
979 return std::make_exception_ptr(std::runtime_error("No samples for buffer"));
981 ALenum format
= GetFormat(chans
, type
);
982 if(UNLIKELY(format
== AL_NONE
))
984 auto str
= String("Unsupported format (")+GetSampleTypeName(type
)+", "+
985 GetChannelConfigName(chans
)+")";
986 return std::make_exception_ptr(std::runtime_error(str
));
991 alGenBuffers(1, &bid
);
992 if(ALenum err
= alGetError())
993 return std::make_exception_ptr(al_error(err
, "Failed to create buffer"));
995 auto buffer
= MakeUnique
<BufferImpl
>(*this, bid
, srate
, chans
, type
, name
, name_hash
);
997 if(mThread
.get_id() == std::thread::id())
998 mThread
= std::thread(std::mem_fn(&ContextImpl::backgroundProc
), this);
1000 PendingPromise
*pf
= nullptr;
1001 if(mPendingTail
== mPendingCurrent
.load(std::memory_order_acquire
))
1002 pf
= new PendingPromise(buffer
.get(), std::move(decoder
), format
, frames
,
1003 std::move(promise
));
1007 pf
->mBuffer
= buffer
.get();
1008 pf
->mDecoder
= std::move(decoder
);
1009 pf
->mFormat
= format
;
1010 pf
->mFrames
= frames
;
1011 pf
->mPromise
= std::move(promise
);
1012 mPendingTail
= pf
->mNext
.exchange(nullptr, std::memory_order_relaxed
);
1015 mPendingHead
->mNext
.store(pf
, std::memory_order_release
);
1018 return mBuffers
.insert(iter
, std::move(buffer
))->get();
1021 DECL_THUNK1(Buffer
, Context
, getBuffer
,, StringView
)
1022 Buffer
ContextImpl::getBuffer(StringView name
)
1026 auto hasher
= std::hash
<StringView
>();
1027 size_t name_hash
= hasher(name
);
1028 if(UNLIKELY(!mFutureBuffers
.empty()))
1032 // If the buffer is already pending for the future, wait for it
1033 auto iter
= findFutureBufferName(name
, name_hash
);
1034 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getNameHash() == name_hash
)
1036 buffer
= iter
->mFuture
.get();
1037 mFutureBuffers
.erase(iter
);
1040 // Clear out any completed futures.
1041 mFutureBuffers
.erase(
1042 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1043 [](const PendingBuffer
&entry
) -> bool
1044 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1045 ), mFutureBuffers
.end()
1048 // If we got the buffer, return it. Otherwise, go load it normally.
1049 if(buffer
) return buffer
;
1052 auto iter
= findBufferName(name
, name_hash
);
1053 if(iter
!= mBuffers
.end() && (*iter
)->getNameHash() == name_hash
)
1054 return Buffer(iter
->get());
1056 BufferOrExceptT ret
= doCreateBuffer(name
, name_hash
, iter
, createDecoder(name
));
1057 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1058 if(UNLIKELY(!buffer
))
1059 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
1063 DECL_THUNK1(SharedFuture
<Buffer
>, Context
, getBufferAsync
,, StringView
)
1064 SharedFuture
<Buffer
> ContextImpl::getBufferAsync(StringView name
)
1066 SharedFuture
<Buffer
> future
;
1069 auto hasher
= std::hash
<StringView
>();
1070 size_t name_hash
= hasher(name
);
1071 if(UNLIKELY(!mFutureBuffers
.empty()))
1073 // Check if the future that's being created already exists
1074 auto iter
= findFutureBufferName(name
, name_hash
);
1075 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getNameHash() == name_hash
)
1077 future
= iter
->mFuture
;
1078 if(GetFutureState(future
) == std::future_status::ready
)
1079 mFutureBuffers
.erase(iter
);
1083 // Clear out any fulfilled futures.
1084 mFutureBuffers
.erase(
1085 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1086 [](const PendingBuffer
&entry
) -> bool
1087 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1088 ), mFutureBuffers
.end()
1092 auto iter
= findBufferName(name
, name_hash
);
1093 if(iter
!= mBuffers
.end() && (*iter
)->getNameHash() == name_hash
)
1095 // User asked to create a future buffer that's already loaded. Just
1096 // construct a promise, fulfill the promise immediately, then return a
1097 // shared future that's already set.
1098 Promise
<Buffer
> promise
;
1099 promise
.set_value(Buffer(iter
->get()));
1100 future
= promise
.get_future().share();
1104 Promise
<Buffer
> promise
;
1105 future
= promise
.get_future().share();
1107 BufferOrExceptT ret
= doCreateBufferAsync(name
, name_hash
, iter
, createDecoder(name
), std::move(promise
));
1108 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1109 if(UNLIKELY(!buffer
))
1110 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
1111 mWakeMutex
.lock(); mWakeMutex
.unlock();
1112 mWakeThread
.notify_all();
1114 mFutureBuffers
.insert(
1115 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), name_hash
,
1116 [](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1117 { return lhs
.mBuffer
->getNameHash() < rhs
; }
1118 ), { buffer
->getHandle(), future
}
1124 DECL_THUNK1(void, Context
, precacheBuffersAsync
,, ArrayView
<StringView
>)
1125 void ContextImpl::precacheBuffersAsync(ArrayView
<StringView
> names
)
1129 if(UNLIKELY(!mFutureBuffers
.empty()))
1131 // Clear out any fulfilled futures.
1132 mFutureBuffers
.erase(
1133 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1134 [](const PendingBuffer
&entry
) -> bool
1135 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1136 ), mFutureBuffers
.end()
1140 auto hasher
= std::hash
<StringView
>();
1141 for(const StringView name
: names
)
1143 size_t name_hash
= hasher(name
);
1145 // Check if the buffer that's being created already exists
1146 auto iter
= findBufferName(name
, name_hash
);
1147 if(iter
!= mBuffers
.end() && (*iter
)->getNameHash() == name_hash
)
1150 DecoderOrExceptT dec
= findDecoder(name
);
1151 SharedPtr
<Decoder
> *decoder
= std::get_if
<SharedPtr
<Decoder
>>(&dec
);
1152 if(!decoder
) continue;
1154 Promise
<Buffer
> promise
;
1155 SharedFuture
<Buffer
> future
= promise
.get_future().share();
1157 BufferOrExceptT buf
= doCreateBufferAsync(name
, name_hash
, iter
, std::move(*decoder
),
1158 std::move(promise
));
1159 Buffer
*buffer
= std::get_if
<Buffer
>(&buf
);
1160 if(UNLIKELY(!buffer
)) continue;
1162 mFutureBuffers
.insert(
1163 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), name_hash
,
1164 [](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1165 { return lhs
.mBuffer
->getNameHash() < rhs
; }
1166 ), { buffer
->getHandle(), future
}
1169 mWakeMutex
.lock(); mWakeMutex
.unlock();
1170 mWakeThread
.notify_all();
1173 DECL_THUNK2(Buffer
, Context
, createBufferFrom
,, StringView
, SharedPtr
<Decoder
>)
1174 Buffer
ContextImpl::createBufferFrom(StringView name
, SharedPtr
<Decoder
>&& decoder
)
1178 auto hasher
= std::hash
<StringView
>();
1179 size_t name_hash
= hasher(name
);
1180 auto iter
= findBufferName(name
, name_hash
);
1181 if(iter
!= mBuffers
.end() && (*iter
)->getNameHash() == name_hash
)
1182 throw std::runtime_error("Buffer already exists");
1184 BufferOrExceptT ret
= doCreateBuffer(name
, name_hash
, iter
, std::move(decoder
));
1185 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1186 if(UNLIKELY(!buffer
))
1187 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
1191 DECL_THUNK2(SharedFuture
<Buffer
>, Context
, createBufferAsyncFrom
,, StringView
, SharedPtr
<Decoder
>)
1192 SharedFuture
<Buffer
> ContextImpl::createBufferAsyncFrom(StringView name
, SharedPtr
<Decoder
>&& decoder
)
1194 SharedFuture
<Buffer
> future
;
1197 if(UNLIKELY(!mFutureBuffers
.empty()))
1199 // Clear out any fulfilled futures.
1200 mFutureBuffers
.erase(
1201 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1202 [](const PendingBuffer
&entry
) -> bool
1203 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1204 ), mFutureBuffers
.end()
1208 auto hasher
= std::hash
<StringView
>();
1209 size_t name_hash
= hasher(name
);
1210 auto iter
= findBufferName(name
, name_hash
);
1211 if(iter
!= mBuffers
.end() && (*iter
)->getNameHash() == name_hash
)
1212 throw std::runtime_error("Buffer already exists");
1214 Promise
<Buffer
> promise
;
1215 future
= promise
.get_future().share();
1217 BufferOrExceptT ret
= doCreateBufferAsync(name
, name_hash
, iter
, std::move(decoder
), std::move(promise
));
1218 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1219 if(UNLIKELY(!buffer
))
1220 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
1221 mWakeMutex
.lock(); mWakeMutex
.unlock();
1222 mWakeThread
.notify_all();
1224 mFutureBuffers
.insert(
1225 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), name_hash
,
1226 [](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1227 { return lhs
.mBuffer
->getNameHash() < rhs
; }
1228 ), { buffer
->getHandle(), future
}
1235 DECL_THUNK1(Buffer
, Context
, findBuffer
,, StringView
)
1236 Buffer
ContextImpl::findBuffer(StringView name
)
1241 auto hasher
= std::hash
<StringView
>();
1242 size_t name_hash
= hasher(name
);
1243 if(UNLIKELY(!mFutureBuffers
.empty()))
1245 // If the buffer is already pending for the future, wait for it
1246 auto iter
= findFutureBufferName(name
, name_hash
);
1247 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getNameHash() == name_hash
)
1249 buffer
= iter
->mFuture
.get();
1250 mFutureBuffers
.erase(iter
);
1253 // Clear out any completed futures.
1254 mFutureBuffers
.erase(
1255 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1256 [](const PendingBuffer
&entry
) -> bool
1257 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1258 ), mFutureBuffers
.end()
1264 auto iter
= findBufferName(name
, name_hash
);
1265 if(iter
!= mBuffers
.end() && (*iter
)->getNameHash() == name_hash
)
1266 buffer
= Buffer(iter
->get());
1271 DECL_THUNK1(SharedFuture
<Buffer
>, Context
, findBufferAsync
,, StringView
)
1272 SharedFuture
<Buffer
> ContextImpl::findBufferAsync(StringView name
)
1274 SharedFuture
<Buffer
> future
;
1277 auto hasher
= std::hash
<StringView
>();
1278 size_t name_hash
= hasher(name
);
1279 if(UNLIKELY(!mFutureBuffers
.empty()))
1281 // Check if the future that's being created already exists
1282 auto iter
= findFutureBufferName(name
, name_hash
);
1283 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getNameHash() == name_hash
)
1285 future
= iter
->mFuture
;
1286 if(GetFutureState(future
) == std::future_status::ready
)
1287 mFutureBuffers
.erase(iter
);
1291 // Clear out any fulfilled futures.
1292 mFutureBuffers
.erase(
1293 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1294 [](const PendingBuffer
&entry
) -> bool
1295 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1296 ), mFutureBuffers
.end()
1300 auto iter
= findBufferName(name
, name_hash
);
1301 if(iter
!= mBuffers
.end() && (*iter
)->getNameHash() == name_hash
)
1303 // User asked to create a future buffer that's already loaded. Just
1304 // construct a promise, fulfill the promise immediately, then return a
1305 // shared future that's already set.
1306 Promise
<Buffer
> promise
;
1307 promise
.set_value(Buffer(iter
->get()));
1308 future
= promise
.get_future().share();
1314 DECL_THUNK1(void, Context
, removeBuffer
,, Buffer
)
1315 DECL_THUNK1(void, Context
, removeBuffer
,, StringView
)
1316 void ContextImpl::removeBuffer(StringView name
)
1320 auto hasher
= std::hash
<StringView
>();
1321 size_t name_hash
= hasher(name
);
1322 if(UNLIKELY(!mFutureBuffers
.empty()))
1324 // If the buffer is already pending for the future, wait for it to
1325 // finish before continuing.
1326 auto iter
= findFutureBufferName(name
, name_hash
);
1327 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getNameHash() == name_hash
)
1329 iter
->mFuture
.wait();
1330 mFutureBuffers
.erase(iter
);
1333 // Clear out any completed futures.
1334 mFutureBuffers
.erase(
1335 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1336 [](const PendingBuffer
&entry
) -> bool
1337 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1338 ), mFutureBuffers
.end()
1342 auto iter
= findBufferName(name
, name_hash
);
1343 if(iter
!= mBuffers
.end() && (*iter
)->getNameHash() == name_hash
)
1345 // Remove pending sources whose future was waiting for this buffer.
1346 BufferImpl
*buffer
= iter
->get();
1347 mPendingSources
.erase(
1348 std::remove_if(mPendingSources
.begin(), mPendingSources
.end(),
1349 [buffer
](PendingSource
&entry
) -> bool
1351 return (GetFutureState(entry
.mFuture
) == std::future_status::ready
&&
1352 entry
.mFuture
.get().getHandle() == buffer
);
1354 ), mPendingSources
.end()
1357 mBuffers
.erase(iter
);
1362 ALuint
ContextImpl::getSourceId(ALuint maxprio
)
1365 if(mSourceIds
.empty())
1368 alGenSources(1, &id
);
1369 if(alGetError() == AL_NO_ERROR
)
1372 SourceImpl
*lowest
= nullptr;
1373 for(SourceBufferUpdateEntry
&entry
: mPlaySources
)
1375 if(!lowest
|| entry
.mSource
->getPriority() < lowest
->getPriority())
1376 lowest
= entry
.mSource
;
1378 for(SourceStreamUpdateEntry
&entry
: mStreamSources
)
1380 if(!lowest
|| entry
.mSource
->getPriority() < lowest
->getPriority())
1381 lowest
= entry
.mSource
;
1383 if(lowest
&& lowest
->getPriority() < maxprio
)
1387 mMessage
->sourceForceStopped(lowest
);
1390 if(mSourceIds
.empty())
1391 throw std::runtime_error("No available sources");
1393 id
= mSourceIds
.back();
1394 mSourceIds
.pop_back();
1399 DECL_THUNK0(Source
, Context
, createSource
,)
1400 Source
ContextImpl::createSource()
1405 if(!mFreeSources
.empty())
1407 source
= mFreeSources
.back();
1408 mFreeSources
.pop_back();
1412 mAllSources
.emplace_back(*this);
1413 source
= &mAllSources
.back();
1415 return Source(source
);
1419 void ContextImpl::addPendingSource(SourceImpl
*source
, SharedFuture
<Buffer
> future
)
1421 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1422 [](const PendingSource
&lhs
, SourceImpl
*rhs
) -> bool
1423 { return lhs
.mSource
< rhs
; }
1425 if(iter
!= mPendingSources
.end() && iter
->mSource
== source
)
1426 iter
->mFuture
= std::move(future
);
1428 mPendingSources
.insert(iter
, {source
, std::move(future
)});
1431 void ContextImpl::removePendingSource(SourceImpl
*source
)
1433 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1434 [](const PendingSource
&lhs
, SourceImpl
*rhs
) -> bool
1435 { return lhs
.mSource
< rhs
; }
1437 if(iter
!= mPendingSources
.end() && iter
->mSource
== source
)
1438 mPendingSources
.erase(iter
);
1441 bool ContextImpl::isPendingSource(const SourceImpl
*source
) const
1443 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1444 [](const PendingSource
&lhs
, const SourceImpl
*rhs
) -> bool
1445 { return lhs
.mSource
< rhs
; }
1447 return (iter
!= mPendingSources
.end() && iter
->mSource
== source
);
1450 void ContextImpl::addFadingSource(SourceImpl
*source
, std::chrono::nanoseconds duration
, ALfloat gain
)
1452 auto iter
= std::lower_bound(mFadingSources
.begin(), mFadingSources
.end(), source
,
1453 [](const SourceFadeUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1454 { return lhs
.mSource
< rhs
; }
1456 if(iter
== mFadingSources
.end() || iter
->mSource
!= source
)
1458 auto now
= mDevice
.getClockTime();
1459 mFadingSources
.emplace(iter
, SourceFadeUpdateEntry
{source
, now
, now
+duration
, true, gain
});
1463 void ContextImpl::removeFadingSource(SourceImpl
*source
)
1465 auto iter
= std::lower_bound(mFadingSources
.begin(), mFadingSources
.end(), source
,
1466 [](const SourceFadeUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1467 { return lhs
.mSource
< rhs
; }
1469 if(iter
!= mFadingSources
.end() && iter
->mSource
== source
)
1470 mFadingSources
.erase(iter
);
1473 void ContextImpl::addPlayingSource(SourceImpl
*source
, ALuint id
)
1475 auto iter
= std::lower_bound(mPlaySources
.begin(), mPlaySources
.end(), source
,
1476 [](const SourceBufferUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1477 { return lhs
.mSource
< rhs
; }
1479 if(iter
== mPlaySources
.end() || iter
->mSource
!= source
)
1480 mPlaySources
.insert(iter
, {source
,id
});
1483 void ContextImpl::addPlayingSource(SourceImpl
*source
)
1485 auto iter
= std::lower_bound(mStreamSources
.begin(), mStreamSources
.end(), source
,
1486 [](const SourceStreamUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1487 { return lhs
.mSource
< rhs
; }
1489 if(iter
== mStreamSources
.end() || iter
->mSource
!= source
)
1490 mStreamSources
.insert(iter
, {source
});
1493 void ContextImpl::removePlayingSource(SourceImpl
*source
)
1495 auto iter0
= std::lower_bound(mPlaySources
.begin(), mPlaySources
.end(), source
,
1496 [](const SourceBufferUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1497 { return lhs
.mSource
< rhs
; }
1499 if(iter0
!= mPlaySources
.end() && iter0
->mSource
== source
)
1500 mPlaySources
.erase(iter0
);
1503 auto iter1
= std::lower_bound(mStreamSources
.begin(), mStreamSources
.end(), source
,
1504 [](const SourceStreamUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1505 { return lhs
.mSource
< rhs
; }
1507 if(iter1
!= mStreamSources
.end() && iter1
->mSource
== source
)
1508 mStreamSources
.erase(iter1
);
1513 void ContextImpl::addStream(SourceImpl
*source
)
1515 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
1516 if(mThread
.get_id() == std::thread::id())
1517 mThread
= std::thread(std::mem_fn(&ContextImpl::backgroundProc
), this);
1518 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1519 if(iter
== mStreamingSources
.end() || *iter
!= source
)
1520 mStreamingSources
.insert(iter
, source
);
1523 void ContextImpl::removeStream(SourceImpl
*source
)
1525 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
1526 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1527 if(iter
!= mStreamingSources
.end() && *iter
== source
)
1528 mStreamingSources
.erase(iter
);
1531 void ContextImpl::removeStreamNoLock(SourceImpl
*source
)
1533 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1534 if(iter
!= mStreamingSources
.end() && *iter
== source
)
1535 mStreamingSources
.erase(iter
);
1539 DECL_THUNK0(AuxiliaryEffectSlot
, Context
, createAuxiliaryEffectSlot
,)
1540 AuxiliaryEffectSlot
ContextImpl::createAuxiliaryEffectSlot()
1542 if(!hasExtension(AL::EXT_EFX
))
1543 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1546 auto slot
= MakeUnique
<AuxiliaryEffectSlotImpl
>(*this);
1547 auto iter
= std::lower_bound(mEffectSlots
.begin(), mEffectSlots
.end(), slot
);
1548 return AuxiliaryEffectSlot(mEffectSlots
.insert(iter
, std::move(slot
))->get());
1551 void ContextImpl::freeEffectSlot(AuxiliaryEffectSlotImpl
*slot
)
1553 auto iter
= std::lower_bound(mEffectSlots
.begin(), mEffectSlots
.end(), slot
,
1554 [](const UniquePtr
<AuxiliaryEffectSlotImpl
> &lhs
, AuxiliaryEffectSlotImpl
*rhs
) -> bool
1555 { return lhs
.get() < rhs
; }
1557 if(iter
!= mEffectSlots
.end() && iter
->get() == slot
)
1558 mEffectSlots
.erase(iter
);
1562 DECL_THUNK0(Effect
, Context
, createEffect
,)
1563 Effect
ContextImpl::createEffect()
1565 if(!hasExtension(AL::EXT_EFX
))
1566 throw std::runtime_error("Effects not supported");
1569 auto effect
= MakeUnique
<EffectImpl
>(*this);
1570 auto iter
= std::lower_bound(mEffects
.begin(), mEffects
.end(), effect
);
1571 return Effect(mEffects
.insert(iter
, std::move(effect
))->get());
1574 void ContextImpl::freeEffect(EffectImpl
*effect
)
1576 auto iter
= std::lower_bound(mEffects
.begin(), mEffects
.end(), effect
,
1577 [](const UniquePtr
<EffectImpl
> &lhs
, EffectImpl
*rhs
) -> bool
1578 { return lhs
.get() < rhs
; }
1580 if(iter
!= mEffects
.end() && iter
->get() == effect
)
1581 mEffects
.erase(iter
);
1585 DECL_THUNK0(SourceGroup
, Context
, createSourceGroup
,)
1586 SourceGroup
ContextImpl::createSourceGroup()
1588 auto srcgroup
= MakeUnique
<SourceGroupImpl
>(*this);
1589 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), srcgroup
);
1591 iter
= mSourceGroups
.insert(iter
, std::move(srcgroup
));
1592 return SourceGroup(iter
->get());
1595 void ContextImpl::freeSourceGroup(SourceGroupImpl
*group
)
1597 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), group
,
1598 [](const UniquePtr
<SourceGroupImpl
> &lhs
, SourceGroupImpl
*rhs
) -> bool
1599 { return lhs
.get() < rhs
; }
1601 if(iter
!= mSourceGroups
.end() && iter
->get() == group
)
1602 mSourceGroups
.erase(iter
);
1606 DECL_THUNK1(void, Context
, setDopplerFactor
,, ALfloat
)
1607 void ContextImpl::setDopplerFactor(ALfloat factor
)
1609 if(!(factor
>= 0.0f
))
1610 throw std::out_of_range("Doppler factor out of range");
1612 alDopplerFactor(factor
);
1616 DECL_THUNK1(void, Context
, setSpeedOfSound
,, ALfloat
)
1617 void ContextImpl::setSpeedOfSound(ALfloat speed
)
1620 throw std::out_of_range("Speed of sound out of range");
1622 alSpeedOfSound(speed
);
1626 DECL_THUNK1(void, Context
, setDistanceModel
,, DistanceModel
)
1627 void ContextImpl::setDistanceModel(DistanceModel model
)
1630 alDistanceModel((ALenum
)model
);
1634 DECL_THUNK0(void, Context
, update
,)
1635 void ContextImpl::update()
1638 mPendingSources
.erase(
1639 std::remove_if(mPendingSources
.begin(), mPendingSources
.end(),
1640 [](PendingSource
&entry
) -> bool
1641 { return !entry
.mSource
->checkPending(entry
.mFuture
); }
1642 ), mPendingSources
.end()
1644 if(!mFadingSources
.empty())
1646 auto cur_time
= mDevice
.getClockTime();
1647 mFadingSources
.erase(
1648 std::remove_if(mFadingSources
.begin(), mFadingSources
.end(),
1649 [cur_time
](SourceFadeUpdateEntry
&entry
) -> bool
1650 { return !entry
.mSource
->fadeUpdate(cur_time
, entry
); }
1651 ), mFadingSources
.end()
1655 std::remove_if(mPlaySources
.begin(), mPlaySources
.end(),
1656 [](const SourceBufferUpdateEntry
&entry
) -> bool
1657 { return !entry
.mSource
->playUpdate(entry
.mId
); }
1658 ), mPlaySources
.end()
1660 mStreamSources
.erase(
1661 std::remove_if(mStreamSources
.begin(), mStreamSources
.end(),
1662 [](const SourceStreamUpdateEntry
&entry
) -> bool
1663 { return !entry
.mSource
->playUpdate(); }
1664 ), mStreamSources
.end()
1667 if(!mWakeInterval
.load(std::memory_order_relaxed
).count())
1669 // For performance reasons, don't wait for the thread's mutex. This
1670 // should be called often enough to keep up with any and all streams
1672 mWakeThread
.notify_all();
1675 if(hasExtension(AL::EXT_disconnect
) && mIsConnected
)
1678 alcGetIntegerv(mDevice
.getALCdevice(), ALC_CONNECTED
, 1, &connected
);
1679 mIsConnected
= static_cast<bool>(connected
);
1680 if(!mIsConnected
&& mMessage
.get()) mMessage
->deviceDisconnected(Device(&mDevice
));
1684 DECL_THUNK0(Device
, Context
, getDevice
,)
1685 DECL_THUNK0(std::chrono::milliseconds
, Context
, getAsyncWakeInterval
, const)
1686 DECL_THUNK0(Listener
, Context
, getListener
,)
1687 DECL_THUNK0(SharedPtr
<MessageHandler
>, Context
, getMessageHandler
, const)
1689 void Context::MakeCurrent(Context context
)
1690 { ContextImpl::MakeCurrent(context
.pImpl
); }
1692 Context
Context::GetCurrent()
1693 { return Context(ContextImpl::GetCurrent()); }
1695 void Context::MakeThreadCurrent(Context context
)
1696 { ContextImpl::MakeThreadCurrent(context
.pImpl
); }
1698 Context
Context::GetThreadCurrent()
1699 { return Context(ContextImpl::GetThreadCurrent()); }
1702 DECL_THUNK1(void, Listener
, setGain
,, ALfloat
)
1703 void ListenerImpl::setGain(ALfloat gain
)
1706 throw std::out_of_range("Gain out of range");
1707 CheckContext(mContext
);
1708 alListenerf(AL_GAIN
, gain
);
1712 DECL_THUNK3(void, Listener
, set3DParameters
,, const Vector3
&, const Vector3
&, const Vector3Pair
&)
1713 void ListenerImpl::set3DParameters(const Vector3
&position
, const Vector3
&velocity
, const std::pair
<Vector3
,Vector3
> &orientation
)
1715 static_assert(sizeof(orientation
) == sizeof(ALfloat
[6]), "Invalid Vector3 pair size");
1716 CheckContext(mContext
);
1717 Batcher batcher
= mContext
->getBatcher();
1718 alListenerfv(AL_POSITION
, position
.getPtr());
1719 alListenerfv(AL_VELOCITY
, velocity
.getPtr());
1720 alListenerfv(AL_ORIENTATION
, orientation
.first
.getPtr());
1723 DECL_THUNK1(void, Listener
, setPosition
,, const Vector3
&)
1724 void ListenerImpl::setPosition(const Vector3
&position
)
1726 CheckContext(mContext
);
1727 alListenerfv(AL_POSITION
, position
.getPtr());
1730 DECL_THUNK1(void, Listener
, setPosition
,, const ALfloat
*)
1731 void ListenerImpl::setPosition(const ALfloat
*pos
)
1733 CheckContext(mContext
);
1734 alListenerfv(AL_POSITION
, pos
);
1737 DECL_THUNK1(void, Listener
, setVelocity
,, const Vector3
&)
1738 void ListenerImpl::setVelocity(const Vector3
&velocity
)
1740 CheckContext(mContext
);
1741 alListenerfv(AL_VELOCITY
, velocity
.getPtr());
1744 DECL_THUNK1(void, Listener
, setVelocity
,, const ALfloat
*)
1745 void ListenerImpl::setVelocity(const ALfloat
*vel
)
1747 CheckContext(mContext
);
1748 alListenerfv(AL_VELOCITY
, vel
);
1751 DECL_THUNK1(void, Listener
, setOrientation
,, const Vector3Pair
&)
1752 void ListenerImpl::setOrientation(const std::pair
<Vector3
,Vector3
> &orientation
)
1754 CheckContext(mContext
);
1755 alListenerfv(AL_ORIENTATION
, orientation
.first
.getPtr());
1758 DECL_THUNK2(void, Listener
, setOrientation
,, const ALfloat
*, const ALfloat
*)
1759 void ListenerImpl::setOrientation(const ALfloat
*at
, const ALfloat
*up
)
1761 CheckContext(mContext
);
1762 ALfloat ori
[6] = { at
[0], at
[1], at
[2], up
[0], up
[1], up
[2] };
1763 alListenerfv(AL_ORIENTATION
, ori
);
1766 DECL_THUNK1(void, Listener
, setOrientation
,, const ALfloat
*)
1767 void ListenerImpl::setOrientation(const ALfloat
*ori
)
1769 CheckContext(mContext
);
1770 alListenerfv(AL_ORIENTATION
, ori
);
1773 DECL_THUNK1(void, Listener
, setMetersPerUnit
,, ALfloat
)
1774 void ListenerImpl::setMetersPerUnit(ALfloat m_u
)
1777 throw std::out_of_range("Invalid meters per unit");
1778 CheckContext(mContext
);
1779 if(mContext
->hasExtension(AL::EXT_EFX
))
1780 alListenerf(AL_METERS_PER_UNIT
, m_u
);