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/mpg123.hpp"
37 #include "devicemanager.h"
41 #include "auxeffectslot.h"
43 #include "sourcegroup.h"
46 #define WIN32_LEAN_AND_MEAN
53 // Implements a FNV-1a hash for StringView. NOTE: This is *NOT* guaranteed
54 // compatible with std::hash<String>! The standard does not give any specific
55 // hash implementation, nor a way for applications to access the same hash
56 // function as std::string (short of copying into a string and hashing that).
57 // So if you need Strings and StringViews to result in the same hash for the
58 // same set of characters, hash StringViews created from the Strings.
60 struct hash
<alure::StringView
> {
61 size_t operator()(const alure::StringView
&str
) const noexcept
63 using traits_type
= alure::StringView::traits_type
;
65 if /*constexpr*/ (sizeof(size_t) == 8)
67 static constexpr size_t hash_offset
= 0xcbf29ce484222325;
68 static constexpr size_t hash_prime
= 0x100000001b3;
70 size_t val
= hash_offset
;
72 val
= (val
^traits_type::to_int_type(ch
)) * hash_prime
;
77 static constexpr size_t hash_offset
= 0x811c9dc5;
78 static constexpr size_t hash_prime
= 0x1000193;
80 size_t val
= hash_offset
;
82 val
= (val
^traits_type::to_int_type(ch
)) * hash_prime
;
93 // Global mutex to protect global context changes
94 std::mutex mGlobalCtxMutex
;
97 // Windows' std::ifstream fails with non-ANSI paths since the standard only
98 // specifies names using const char* (or std::string). MSVC has a non-standard
99 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths,
100 // but not all Windows compilers support it. So we have to make our own istream
101 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions.
102 class StreamBuf final
: public std::streambuf
{
103 alure::Array
<char_type
,4096> mBuffer
;
104 HANDLE mFile
{INVALID_HANDLE_VALUE
};
106 int_type
underflow() override
108 if(mFile
!= INVALID_HANDLE_VALUE
&& gptr() == egptr())
110 // Read in the next chunk of data, and set the pointers on success
112 if(ReadFile(mFile
, mBuffer
.data(), mBuffer
.size(), &got
, NULL
))
113 setg(mBuffer
.data(), mBuffer
.data(), mBuffer
.data()+got
);
115 if(gptr() == egptr())
116 return traits_type::eof();
117 return traits_type::to_int_type(*gptr());
120 pos_type
seekoff(off_type offset
, std::ios_base::seekdir whence
, std::ios_base::openmode mode
) override
122 if(mFile
== INVALID_HANDLE_VALUE
|| (mode
&std::ios_base::out
) || !(mode
&std::ios_base::in
))
123 return traits_type::eof();
128 case std::ios_base::beg
:
129 fpos
.QuadPart
= offset
;
130 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_BEGIN
))
131 return traits_type::eof();
134 case std::ios_base::cur
:
135 // If the offset remains in the current buffer range, just
136 // update the pointer.
137 if((offset
>= 0 && offset
< off_type(egptr()-gptr())) ||
138 (offset
< 0 && -offset
<= off_type(gptr()-eback())))
140 // Get the current file offset to report the correct read
143 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_CURRENT
))
144 return traits_type::eof();
145 setg(eback(), gptr()+offset
, egptr());
146 return fpos
.QuadPart
- off_type(egptr()-gptr());
148 // Need to offset for the file offset being at egptr() while
149 // the requested offset is relative to gptr().
150 offset
-= off_type(egptr()-gptr());
151 fpos
.QuadPart
= offset
;
152 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_CURRENT
))
153 return traits_type::eof();
156 case std::ios_base::end
:
157 fpos
.QuadPart
= offset
;
158 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_END
))
159 return traits_type::eof();
163 return traits_type::eof();
166 return fpos
.QuadPart
;
169 pos_type
seekpos(pos_type pos
, std::ios_base::openmode mode
) override
171 // Simplified version of seekoff
172 if(mFile
== INVALID_HANDLE_VALUE
|| (mode
&std::ios_base::out
) || !(mode
&std::ios_base::in
))
173 return traits_type::eof();
177 if(!SetFilePointerEx(mFile
, fpos
, &fpos
, FILE_BEGIN
))
178 return traits_type::eof();
181 return fpos
.QuadPart
;
185 bool open(const char *filename
)
187 alure::Vector
<wchar_t> wname
;
190 wnamelen
= MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, NULL
, 0);
191 if(wnamelen
<= 0) return false;
193 wname
.resize(wnamelen
);
194 MultiByteToWideChar(CP_UTF8
, 0, filename
, -1, wname
.data(), wnamelen
);
196 mFile
= CreateFileW(wname
.data(), GENERIC_READ
, FILE_SHARE_READ
, NULL
,
197 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, nullptr);
198 if(mFile
== INVALID_HANDLE_VALUE
) return false;
202 bool is_open() const noexcept
{ return mFile
!= INVALID_HANDLE_VALUE
; }
204 StreamBuf() = default;
205 ~StreamBuf() override
207 if(mFile
!= INVALID_HANDLE_VALUE
)
209 mFile
= INVALID_HANDLE_VALUE
;
213 // Inherit from std::istream to use our custom streambuf
214 class Stream final
: public std::istream
{
216 Stream(const char *filename
) : std::istream(new StreamBuf())
218 // Set the failbit if the file failed to open.
219 if(!(static_cast<StreamBuf
*>(rdbuf())->open(filename
)))
225 bool is_open() const noexcept
{ return static_cast<StreamBuf
*>(rdbuf())->is_open(); }
229 using DecoderEntryPair
= std::pair
<alure::String
,alure::UniquePtr
<alure::DecoderFactory
>>;
230 const DecoderEntryPair sDefaultDecoders
[] = {
232 { "_alure_int_wave", alure::MakeUnique
<alure::WaveDecoderFactory
>() },
234 #ifdef HAVE_VORBISFILE
235 { "_alure_int_vorbis", alure::MakeUnique
<alure::VorbisFileDecoderFactory
>() },
238 { "_alure_int_flac", alure::MakeUnique
<alure::FlacDecoderFactory
>() },
241 { "_alure_int_opus", alure::MakeUnique
<alure::OpusFileDecoderFactory
>() },
243 #ifdef HAVE_LIBSNDFILE
244 { "_alure_int_sndfile", alure::MakeUnique
<alure::SndFileDecoderFactory
>() },
247 { "_alure_int_mpg123", alure::MakeUnique
<alure::Mpg123DecoderFactory
>() },
250 alure::Vector
<DecoderEntryPair
> sDecoders
;
253 alure::DecoderOrExceptT
GetDecoder(alure::UniquePtr
<std::istream
> &file
,
254 alure::ArrayView
<DecoderEntryPair
> decoders
)
256 while(!decoders
.empty())
258 alure::DecoderFactory
*factory
= decoders
.front().second
.get();
259 auto decoder
= factory
->createDecoder(file
);
260 if(decoder
) return std::move(decoder
);
262 if(!file
|| !(file
->clear(),file
->seekg(0)))
263 return std::make_exception_ptr(
264 std::runtime_error("Failed to rewind file for the next decoder factory")
267 decoders
= decoders
.slice(1);
270 return alure::SharedPtr
<alure::Decoder
>(nullptr);
273 static alure::DecoderOrExceptT
GetDecoder(alure::UniquePtr
<std::istream
> file
)
275 auto decoder
= GetDecoder(file
, sDecoders
);
276 if(std::holds_alternative
<std::exception_ptr
>(decoder
)) return decoder
;
277 if(std::get
<alure::SharedPtr
<alure::Decoder
>>(decoder
)) return decoder
;
278 decoder
= GetDecoder(file
, sDefaultDecoders
);
279 if(std::holds_alternative
<std::exception_ptr
>(decoder
)) return decoder
;
280 if(std::get
<alure::SharedPtr
<alure::Decoder
>>(decoder
)) return decoder
;
281 return (decoder
= std::make_exception_ptr(std::runtime_error("No decoder found")));
284 class DefaultFileIOFactory final
: public alure::FileIOFactory
{
285 alure::UniquePtr
<std::istream
> openFile(const alure::String
&name
) noexcept override
288 auto file
= alure::MakeUnique
<Stream
>(name
.c_str());
290 auto file
= alure::MakeUnique
<std::ifstream
>(name
.c_str(), std::ios::binary
);
292 if(!file
->is_open()) file
= nullptr;
293 return std::move(file
);
296 DefaultFileIOFactory sDefaultFileFactory
;
298 alure::UniquePtr
<alure::FileIOFactory
> sFileFactory
;
305 std::variant
<std::monostate
,uint64_t> ParseTimeval(StringView strval
, double srate
) noexcept
309 size_t cpos
= strval
.find_first_of(':');
310 if(cpos
== StringView::npos
)
312 // No colon is present, treat it as a plain sample offset
313 uint64_t val
= std::stoull(String(strval
), &endpos
);
314 if(endpos
!= strval
.length()) return {};
318 // Value is not a sample offset. Its format is [[HH:]MM]:SS[.sss] (at
319 // least one colon must exist to be interpreted this way).
324 // If a non-empty first value, parse it (may be hours or minutes)
325 val
= std::stoul(String(strval
.data(), cpos
), &endpos
);
326 if(endpos
!= cpos
) return {};
329 strval
= strval
.substr(cpos
+1);
330 cpos
= strval
.find_first_of(':');
331 if(cpos
!= StringView::npos
)
333 // If a second colon is present, the first value was hours and this is
334 // minutes, otherwise the first value was minutes.
339 val2
= std::stoul(String(strval
.data(), cpos
), &endpos
);
340 if(endpos
!= cpos
|| val2
>= 60) return {};
343 // Combines hours and minutes into the full minute count
344 if(val
> std::numeric_limits
<uint64_t>::max()/60)
347 strval
= strval
.substr(cpos
+1);
353 // Parse the seconds and its fraction. Only include the first 3 decimal
354 // places for millisecond precision.
355 size_t dpos
= strval
.find_first_of('.');
356 String str
= (dpos
== StringView::npos
) ?
357 String(strval
) : String(strval
.substr(0, dpos
+4));
358 secs
= std::stod(str
, &endpos
);
359 if(endpos
!= str
.length() || !(secs
>= 0.0 && secs
< 60.0))
363 // Convert minutes to seconds, add the seconds, then convert to samples.
364 return static_cast<uint64_t>((val
*60.0 + secs
) * srate
);
373 Decoder::~Decoder() { }
374 DecoderFactory::~DecoderFactory() { }
376 void RegisterDecoder(StringView name
, UniquePtr
<DecoderFactory
> factory
)
378 auto iter
= std::lower_bound(sDecoders
.begin(), sDecoders
.end(), name
,
379 [](const DecoderEntryPair
&entry
, StringView rhs
) -> bool
380 { return entry
.first
< rhs
; }
382 if(iter
!= sDecoders
.end())
383 throw std::runtime_error("Decoder factory already registered");
384 sDecoders
.insert(iter
, std::make_pair(String(name
), std::move(factory
)));
387 UniquePtr
<DecoderFactory
> UnregisterDecoder(StringView name
) noexcept
389 UniquePtr
<DecoderFactory
> factory
;
390 auto iter
= std::lower_bound(sDecoders
.begin(), sDecoders
.end(), name
,
391 [](const DecoderEntryPair
&entry
, StringView rhs
) noexcept
-> bool
392 { return entry
.first
< rhs
; }
394 if(iter
!= sDecoders
.end())
396 factory
= std::move(iter
->second
);
397 sDecoders
.erase(iter
);
404 FileIOFactory::~FileIOFactory() { }
406 UniquePtr
<FileIOFactory
> FileIOFactory::set(UniquePtr
<FileIOFactory
> factory
) noexcept
408 sFileFactory
.swap(factory
);
412 FileIOFactory
&FileIOFactory::get() noexcept
414 FileIOFactory
*factory
= sFileFactory
.get();
415 if(factory
) return *factory
;
416 return sDefaultFileFactory
;
420 // Default message handler methods are no-ops.
421 MessageHandler::~MessageHandler()
425 void MessageHandler::deviceDisconnected(Device
) noexcept
429 void MessageHandler::sourceStopped(Source
) noexcept
433 void MessageHandler::sourceForceStopped(Source
) noexcept
437 void MessageHandler::bufferLoading(StringView
, ChannelConfig
, SampleType
, ALuint
, ArrayView
<ALbyte
>) noexcept
441 String
MessageHandler::resourceNotFound(StringView
) noexcept
448 static inline void LoadALFunc(T
**func
, const char *name
)
449 { *func
= reinterpret_cast<T
*>(alGetProcAddress(name
)); }
451 static void LoadNothing(ContextImpl
*) { }
453 static void LoadEFX(ContextImpl
*ctx
)
455 LoadALFunc(&ctx
->alGenEffects
, "alGenEffects");
456 LoadALFunc(&ctx
->alDeleteEffects
, "alDeleteEffects");
457 LoadALFunc(&ctx
->alIsEffect
, "alIsEffect");
458 LoadALFunc(&ctx
->alEffecti
, "alEffecti");
459 LoadALFunc(&ctx
->alEffectiv
, "alEffectiv");
460 LoadALFunc(&ctx
->alEffectf
, "alEffectf");
461 LoadALFunc(&ctx
->alEffectfv
, "alEffectfv");
462 LoadALFunc(&ctx
->alGetEffecti
, "alGetEffecti");
463 LoadALFunc(&ctx
->alGetEffectiv
, "alGetEffectiv");
464 LoadALFunc(&ctx
->alGetEffectf
, "alGetEffectf");
465 LoadALFunc(&ctx
->alGetEffectfv
, "alGetEffectfv");
467 LoadALFunc(&ctx
->alGenFilters
, "alGenFilters");
468 LoadALFunc(&ctx
->alDeleteFilters
, "alDeleteFilters");
469 LoadALFunc(&ctx
->alIsFilter
, "alIsFilter");
470 LoadALFunc(&ctx
->alFilteri
, "alFilteri");
471 LoadALFunc(&ctx
->alFilteriv
, "alFilteriv");
472 LoadALFunc(&ctx
->alFilterf
, "alFilterf");
473 LoadALFunc(&ctx
->alFilterfv
, "alFilterfv");
474 LoadALFunc(&ctx
->alGetFilteri
, "alGetFilteri");
475 LoadALFunc(&ctx
->alGetFilteriv
, "alGetFilteriv");
476 LoadALFunc(&ctx
->alGetFilterf
, "alGetFilterf");
477 LoadALFunc(&ctx
->alGetFilterfv
, "alGetFilterfv");
479 LoadALFunc(&ctx
->alGenAuxiliaryEffectSlots
, "alGenAuxiliaryEffectSlots");
480 LoadALFunc(&ctx
->alDeleteAuxiliaryEffectSlots
, "alDeleteAuxiliaryEffectSlots");
481 LoadALFunc(&ctx
->alIsAuxiliaryEffectSlot
, "alIsAuxiliaryEffectSlot");
482 LoadALFunc(&ctx
->alAuxiliaryEffectSloti
, "alAuxiliaryEffectSloti");
483 LoadALFunc(&ctx
->alAuxiliaryEffectSlotiv
, "alAuxiliaryEffectSlotiv");
484 LoadALFunc(&ctx
->alAuxiliaryEffectSlotf
, "alAuxiliaryEffectSlotf");
485 LoadALFunc(&ctx
->alAuxiliaryEffectSlotfv
, "alAuxiliaryEffectSlotfv");
486 LoadALFunc(&ctx
->alGetAuxiliaryEffectSloti
, "alGetAuxiliaryEffectSloti");
487 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotiv
, "alGetAuxiliaryEffectSlotiv");
488 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotf
, "alGetAuxiliaryEffectSlotf");
489 LoadALFunc(&ctx
->alGetAuxiliaryEffectSlotfv
, "alGetAuxiliaryEffectSlotfv");
492 static void LoadSourceResampler(ContextImpl
*ctx
)
494 LoadALFunc(&ctx
->alGetStringiSOFT
, "alGetStringiSOFT");
497 static void LoadSourceLatency(ContextImpl
*ctx
)
499 LoadALFunc(&ctx
->alGetSourcei64vSOFT
, "alGetSourcei64vSOFT");
500 LoadALFunc(&ctx
->alGetSourcedvSOFT
, "alGetSourcedvSOFT");
503 static const struct {
506 void (&loader
)(ContextImpl
*);
507 } ALExtensionList
[] = {
508 { AL::EXT_EFX
, "ALC_EXT_EFX", LoadEFX
},
510 { AL::EXT_FLOAT32
, "AL_EXT_FLOAT32", LoadNothing
},
511 { AL::EXT_MCFORMATS
, "AL_EXT_MCFORMATS", LoadNothing
},
512 { AL::EXT_BFORMAT
, "AL_EXT_BFORMAT", LoadNothing
},
514 { AL::EXT_MULAW
, "AL_EXT_MULAW", LoadNothing
},
515 { AL::EXT_MULAW_MCFORMATS
, "AL_EXT_MULAW_MCFORMATS", LoadNothing
},
516 { AL::EXT_MULAW_BFORMAT
, "AL_EXT_MULAW_BFORMAT", LoadNothing
},
518 { AL::SOFT_loop_points
, "AL_SOFT_loop_points", LoadNothing
},
519 { AL::SOFT_source_latency
, "AL_SOFT_source_latency", LoadSourceLatency
},
520 { AL::SOFT_source_resampler
, "AL_SOFT_source_resampler", LoadSourceResampler
},
521 { AL::SOFT_source_spatialize
, "AL_SOFT_source_spatialize", LoadNothing
},
523 { AL::EXT_disconnect
, "ALC_EXT_disconnect", LoadNothing
},
525 { AL::EXT_SOURCE_RADIUS
, "AL_EXT_SOURCE_RADIUS", LoadNothing
},
526 { AL::EXT_STEREO_ANGLES
, "AL_EXT_STEREO_ANGLES", LoadNothing
},
530 ContextImpl
*ContextImpl::sCurrentCtx
= nullptr;
531 thread_local ContextImpl
*ContextImpl::sThreadCurrentCtx
= nullptr;
533 std::atomic
<uint64_t> ContextImpl::sContextSetCount
{0};
535 void ContextImpl::MakeCurrent(ContextImpl
*context
)
537 std::unique_lock
<std::mutex
> ctxlock(mGlobalCtxMutex
);
539 if(alcMakeContextCurrent(context
? context
->getALCcontext() : nullptr) == ALC_FALSE
)
540 throw std::runtime_error("Call to alcMakeContextCurrent failed");
544 std::call_once(context
->mSetExts
, std::mem_fn(&ContextImpl::setupExts
), context
);
546 std::swap(sCurrentCtx
, context
);
547 if(context
) context
->decRef();
549 if(sThreadCurrentCtx
)
550 sThreadCurrentCtx
->decRef();
551 sThreadCurrentCtx
= nullptr;
552 sContextSetCount
.fetch_add(1, std::memory_order_release
);
554 if((context
= sCurrentCtx
) != nullptr)
557 context
->mWakeThread
.notify_all();
561 void ContextImpl::MakeThreadCurrent(ContextImpl
*context
)
563 if(!DeviceManagerImpl::SetThreadContext
)
564 throw std::runtime_error("Thread-local contexts unsupported");
565 if(DeviceManagerImpl::SetThreadContext(context
? context
->getALCcontext() : nullptr) == ALC_FALSE
)
566 throw std::runtime_error("Call to alcSetThreadContext failed");
570 std::call_once(context
->mSetExts
, std::mem_fn(&ContextImpl::setupExts
), context
);
572 if(sThreadCurrentCtx
)
573 sThreadCurrentCtx
->decRef();
574 sThreadCurrentCtx
= context
;
575 sContextSetCount
.fetch_add(1, std::memory_order_release
);
578 void ContextImpl::setupExts()
580 ALCdevice
*device
= mDevice
->getALCdevice();
582 for(const auto &entry
: ALExtensionList
)
584 if((strncmp(entry
.name
, "ALC", 3) == 0) ? alcIsExtensionPresent(device
, entry
.name
) :
585 alIsExtensionPresent(entry
.name
))
587 mHasExt
.set(static_cast<size_t>(entry
.extension
));
594 void ContextImpl::backgroundProc()
596 if(DeviceManagerImpl::SetThreadContext
&& mDevice
->hasExtension(ALC::EXT_thread_local_context
))
597 DeviceManagerImpl::SetThreadContext(getALCcontext());
599 std::chrono::steady_clock::time_point basetime
= std::chrono::steady_clock::now();
600 std::chrono::milliseconds
waketime(0);
601 std::unique_lock
<std::mutex
> ctxlock(mGlobalCtxMutex
);
602 while(!mQuitThread
.load(std::memory_order_acquire
))
605 std::lock_guard
<std::mutex
> srclock(mSourceStreamMutex
);
606 mStreamingSources
.erase(
607 std::remove_if(mStreamingSources
.begin(), mStreamingSources
.end(),
608 [](SourceImpl
*source
) -> bool
609 { return !source
->updateAsync(); }
610 ), mStreamingSources
.end()
614 // Only do one pending buffer at a time. In case there's several large
615 // buffers to load, we still need to process streaming sources so they
617 PendingPromise
*lastpb
= mPendingCurrent
.load(std::memory_order_acquire
);
618 if(PendingPromise
*pb
= lastpb
->mNext
.load(std::memory_order_relaxed
))
620 pb
->mBuffer
->load(pb
->mFrames
, pb
->mFormat
, std::move(pb
->mDecoder
), this);
621 pb
->mPromise
.set_value(Buffer(pb
->mBuffer
));
622 Promise
<Buffer
>().swap(pb
->mPromise
);
623 mPendingCurrent
.store(pb
, std::memory_order_release
);
627 std::unique_lock
<std::mutex
> wakelock(mWakeMutex
);
628 if(!mQuitThread
.load(std::memory_order_acquire
) && lastpb
->mNext
.load(std::memory_order_acquire
) == nullptr)
632 std::chrono::milliseconds interval
= mWakeInterval
.load(std::memory_order_relaxed
);
633 if(interval
.count() == 0)
634 mWakeThread
.wait(wakelock
);
637 auto now
= std::chrono::steady_clock::now() - basetime
;
640 auto mult
= (now
-waketime
+ interval
-std::chrono::milliseconds(1)) / interval
;
641 waketime
+= interval
* mult
;
643 mWakeThread
.wait_until(wakelock
, waketime
+ basetime
);
648 while(!mQuitThread
.load(std::memory_order_acquire
) &&
649 alcGetCurrentContext() != getALCcontext())
650 mWakeThread
.wait(ctxlock
);
655 if(DeviceManagerImpl::SetThreadContext
)
656 DeviceManagerImpl::SetThreadContext(nullptr);
660 ContextImpl::ContextImpl(DeviceImpl
*device
, ArrayView
<AttributePair
> attrs
)
661 : mListener(this), mDevice(device
), mIsConnected(true), mIsBatching(false)
663 ALCdevice
*alcdev
= mDevice
->getALCdevice();
664 if(attrs
.empty()) /* No explicit attributes. */
665 mContext
= alcCreateContext(alcdev
, nullptr);
667 mContext
= alcCreateContext(alcdev
, &std::get
<0>(attrs
.front()));
668 if(!mContext
) throw alc_error(alcGetError(alcdev
), "alcCreateContext failed");
671 mSourceIds
.reserve(256);
672 mPendingHead
= new PendingPromise
{nullptr, {}, AL_NONE
, 0, {}, {nullptr}};
673 mPendingCurrent
.store(mPendingHead
, std::memory_order_relaxed
);
674 mPendingTail
= mPendingHead
;
677 ContextImpl::~ContextImpl()
679 if(mThread
.joinable())
681 std::unique_lock
<std::mutex
> lock(mWakeMutex
);
682 mQuitThread
.store(true, std::memory_order_relaxed
);
684 mWakeThread
.notify_all();
688 PendingPromise
*pb
= mPendingTail
;
691 PendingPromise
*next
= pb
->mNext
.load(std::memory_order_relaxed
);
695 mPendingCurrent
.store(nullptr, std::memory_order_relaxed
);
696 mPendingHead
= nullptr;
697 mPendingTail
= nullptr;
700 alcDestroyContext(mContext
);
705 void Context::destroy()
707 ContextImpl
*i
= pImpl
;
711 void ContextImpl::destroy()
714 throw std::runtime_error("Context is in use");
715 if(!mBuffers
.empty())
716 throw std::runtime_error("Trying to destroy a context with buffers");
718 if(mThread
.joinable())
720 std::unique_lock
<std::mutex
> lock(mWakeMutex
);
721 mQuitThread
.store(true, std::memory_order_release
);
723 mWakeThread
.notify_all();
727 alcDestroyContext(mContext
);
730 mDevice
->removeContext(this);
734 DECL_THUNK0(void, Context
, startBatch
,)
735 void ContextImpl::startBatch()
737 alcSuspendContext(mContext
);
741 DECL_THUNK0(void, Context
, endBatch
,)
742 void ContextImpl::endBatch()
744 alcProcessContext(mContext
);
749 DECL_THUNK1(SharedPtr
<MessageHandler
>, Context
, setMessageHandler
,, SharedPtr
<MessageHandler
>)
750 SharedPtr
<MessageHandler
> ContextImpl::setMessageHandler(SharedPtr
<MessageHandler
>&& handler
)
752 std::lock_guard
<std::mutex
> lock(mGlobalCtxMutex
);
753 mMessage
.swap(handler
);
758 DECL_THUNK1(void, Context
, setAsyncWakeInterval
,, std::chrono::milliseconds
)
759 void ContextImpl::setAsyncWakeInterval(std::chrono::milliseconds interval
)
761 if(interval
.count() < 0 || interval
> std::chrono::seconds(1))
762 throw std::out_of_range("Async wake interval out of range");
763 mWakeInterval
.store(interval
);
764 mWakeMutex
.lock(); mWakeMutex
.unlock();
765 mWakeThread
.notify_all();
769 DecoderOrExceptT
ContextImpl::findDecoder(StringView name
)
771 String oldname
= String(name
);
772 auto file
= FileIOFactory::get().openFile(oldname
);
775 // Resource not found. Try to find a substitute.
777 return std::make_exception_ptr(std::runtime_error("Failed to open file"));
779 String
newname(mMessage
->resourceNotFound(oldname
));
781 return std::make_exception_ptr(std::runtime_error("Failed to open file"));
782 file
= FileIOFactory::get().openFile(newname
);
783 oldname
= std::move(newname
);
786 return GetDecoder(std::move(file
));
789 DECL_THUNK1(SharedPtr
<Decoder
>, Context
, createDecoder
,, StringView
)
790 SharedPtr
<Decoder
> ContextImpl::createDecoder(StringView name
)
793 DecoderOrExceptT dec
= findDecoder(name
);
794 if(SharedPtr
<Decoder
> *decoder
= std::get_if
<SharedPtr
<Decoder
>>(&dec
))
795 return std::move(*decoder
);
796 std::rethrow_exception(std::get
<std::exception_ptr
>(dec
));
800 DECL_THUNK2(bool, Context
, isSupported
, const, ChannelConfig
, SampleType
)
801 bool ContextImpl::isSupported(ChannelConfig channels
, SampleType type
) const
804 return GetFormat(channels
, type
) != AL_NONE
;
808 DECL_THUNK0(ArrayView
<String
>, Context
, getAvailableResamplers
,)
809 ArrayView
<String
> ContextImpl::getAvailableResamplers()
812 if(mResamplers
.empty() && hasExtension(AL::SOFT_source_resampler
))
814 ALint num_resamplers
= alGetInteger(AL_NUM_RESAMPLERS_SOFT
);
815 mResamplers
.reserve(num_resamplers
);
816 for(int i
= 0;i
< num_resamplers
;i
++)
817 mResamplers
.emplace_back(alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT
, i
));
818 if(mResamplers
.empty())
819 mResamplers
.emplace_back();
824 DECL_THUNK0(ALsizei
, Context
, getDefaultResamplerIndex
, const)
825 ALsizei
ContextImpl::getDefaultResamplerIndex() const
828 if(!hasExtension(AL::SOFT_source_resampler
))
830 return alGetInteger(AL_DEFAULT_RESAMPLER_SOFT
);
834 BufferOrExceptT
ContextImpl::doCreateBuffer(StringView name
, Vector
<UniquePtr
<BufferImpl
>>::iterator iter
, SharedPtr
<Decoder
> decoder
)
836 ALuint srate
= decoder
->getFrequency();
837 ChannelConfig chans
= decoder
->getChannelConfig();
838 SampleType type
= decoder
->getSampleType();
839 ALuint frames
= decoder
->getLength();
841 Vector
<ALbyte
> data(FramesToBytes(frames
, chans
, type
));
842 frames
= decoder
->read(data
.data(), frames
);
844 return std::make_exception_ptr(std::runtime_error("No samples for buffer"));
845 data
.resize(FramesToBytes(frames
, chans
, type
));
847 std::pair
<uint64_t,uint64_t> loop_pts
= decoder
->getLoopPoints();
848 if(loop_pts
.first
>= loop_pts
.second
)
849 loop_pts
= std::make_pair(0, frames
);
852 loop_pts
.second
= std::min
<uint64_t>(loop_pts
.second
, frames
);
853 loop_pts
.first
= std::min
<uint64_t>(loop_pts
.first
, loop_pts
.second
-1);
856 // Get the format before calling the bufferLoading message handler, to
857 // ensure it's something OpenAL can handle.
858 ALenum format
= GetFormat(chans
, type
);
859 if(UNLIKELY(format
== AL_NONE
))
861 auto str
= String("Unsupported format (")+GetSampleTypeName(type
)+", "+
862 GetChannelConfigName(chans
)+")";
863 return std::make_exception_ptr(std::runtime_error(str
));
867 mMessage
->bufferLoading(name
, chans
, type
, srate
, data
);
871 alGenBuffers(1, &bid
);
872 alBufferData(bid
, format
, data
.data(), data
.size(), srate
);
873 if(hasExtension(AL::SOFT_loop_points
))
875 ALint pts
[2]{(ALint
)loop_pts
.first
, (ALint
)loop_pts
.second
};
876 alBufferiv(bid
, AL_LOOP_POINTS_SOFT
, pts
);
878 if(ALenum err
= alGetError())
880 alDeleteBuffers(1, &bid
);
881 return std::make_exception_ptr(al_error(err
, "Failed to buffer data"));
884 return mBuffers
.insert(iter
,
885 MakeUnique
<BufferImpl
>(this, bid
, srate
, chans
, type
, name
)
889 BufferOrExceptT
ContextImpl::doCreateBufferAsync(StringView name
, Vector
<UniquePtr
<BufferImpl
>>::iterator iter
, SharedPtr
<Decoder
> decoder
, Promise
<Buffer
> promise
)
891 ALuint srate
= decoder
->getFrequency();
892 ChannelConfig chans
= decoder
->getChannelConfig();
893 SampleType type
= decoder
->getSampleType();
894 ALuint frames
= decoder
->getLength();
896 return std::make_exception_ptr(std::runtime_error("No samples for buffer"));
898 ALenum format
= GetFormat(chans
, type
);
899 if(UNLIKELY(format
== AL_NONE
))
901 auto str
= String("Unsupported format (")+GetSampleTypeName(type
)+", "+
902 GetChannelConfigName(chans
)+")";
903 return std::make_exception_ptr(std::runtime_error(str
));
908 alGenBuffers(1, &bid
);
909 if(ALenum err
= alGetError())
910 return std::make_exception_ptr(al_error(err
, "Failed to create buffer"));
912 auto buffer
= MakeUnique
<BufferImpl
>(this, bid
, srate
, chans
, type
, name
);
914 if(mThread
.get_id() == std::thread::id())
915 mThread
= std::thread(std::mem_fn(&ContextImpl::backgroundProc
), this);
917 PendingPromise
*pf
= nullptr;
918 if(mPendingTail
== mPendingCurrent
.load(std::memory_order_acquire
))
919 pf
= new PendingPromise
{buffer
.get(), std::move(decoder
), format
, frames
,
920 std::move(promise
), {nullptr}};
924 pf
->mBuffer
= buffer
.get();
925 pf
->mDecoder
= std::move(decoder
);
926 pf
->mFormat
= format
;
927 pf
->mFrames
= frames
;
928 pf
->mPromise
= std::move(promise
);
929 mPendingTail
= pf
->mNext
.exchange(nullptr, std::memory_order_relaxed
);
932 mPendingHead
->mNext
.store(pf
, std::memory_order_release
);
935 return mBuffers
.insert(iter
, std::move(buffer
))->get();
938 DECL_THUNK1(Buffer
, Context
, getBuffer
,, StringView
)
939 Buffer
ContextImpl::getBuffer(StringView name
)
943 auto hasher
= std::hash
<StringView
>();
944 if(UNLIKELY(!mFutureBuffers
.empty()))
948 // If the buffer is already pending for the future, wait for it
949 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
950 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
951 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
953 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
955 buffer
= iter
->mFuture
.get();
956 mFutureBuffers
.erase(iter
);
959 // Clear out any completed futures.
960 mFutureBuffers
.erase(
961 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
962 [](const PendingBuffer
&entry
) -> bool
963 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
964 ), mFutureBuffers
.end()
967 // If we got the buffer, return it. Otherwise, go load it normally.
968 if(buffer
) return buffer
;
971 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
972 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
973 { return hasher(lhs
->getName()) < rhs
; }
975 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
976 return Buffer(iter
->get());
978 BufferOrExceptT ret
= doCreateBuffer(name
, iter
, createDecoder(name
));
979 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
980 if(UNLIKELY(!buffer
))
981 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
985 DECL_THUNK1(SharedFuture
<Buffer
>, Context
, getBufferAsync
,, StringView
)
986 SharedFuture
<Buffer
> ContextImpl::getBufferAsync(StringView name
)
988 SharedFuture
<Buffer
> future
;
991 auto hasher
= std::hash
<StringView
>();
992 if(UNLIKELY(!mFutureBuffers
.empty()))
994 // Check if the future that's being created already exists
995 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
996 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
997 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
999 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
1001 future
= iter
->mFuture
;
1002 if(GetFutureState(future
) == std::future_status::ready
)
1003 mFutureBuffers
.erase(iter
);
1007 // Clear out any fulfilled futures.
1008 mFutureBuffers
.erase(
1009 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1010 [](const PendingBuffer
&entry
) -> bool
1011 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1012 ), mFutureBuffers
.end()
1016 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1017 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1018 { return hasher(lhs
->getName()) < rhs
; }
1020 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1022 // User asked to create a future buffer that's already loaded. Just
1023 // construct a promise, fulfill the promise immediately, then return a
1024 // shared future that's already set.
1025 Promise
<Buffer
> promise
;
1026 promise
.set_value(Buffer(iter
->get()));
1027 future
= promise
.get_future().share();
1031 Promise
<Buffer
> promise
;
1032 future
= promise
.get_future().share();
1034 BufferOrExceptT ret
= doCreateBufferAsync(name
, iter
, createDecoder(name
), std::move(promise
));
1035 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1036 if(UNLIKELY(!buffer
))
1037 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
1038 mWakeMutex
.lock(); mWakeMutex
.unlock();
1039 mWakeThread
.notify_all();
1041 mFutureBuffers
.insert(
1042 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1043 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1044 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1045 ), { buffer
->getHandle(), future
}
1051 DECL_THUNK1(void, Context
, precacheBuffersAsync
,, ArrayView
<StringView
>)
1052 void ContextImpl::precacheBuffersAsync(ArrayView
<StringView
> names
)
1056 if(UNLIKELY(!mFutureBuffers
.empty()))
1058 // Clear out any fulfilled futures.
1059 mFutureBuffers
.erase(
1060 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1061 [](const PendingBuffer
&entry
) -> bool
1062 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1063 ), mFutureBuffers
.end()
1067 auto hasher
= std::hash
<StringView
>();
1068 for(const StringView name
: names
)
1070 // Check if the buffer that's being created already exists
1071 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1072 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1073 { return hasher(lhs
->getName()) < rhs
; }
1075 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1078 DecoderOrExceptT dec
= findDecoder(name
);
1079 SharedPtr
<Decoder
> *decoder
= std::get_if
<SharedPtr
<Decoder
>>(&dec
);
1080 if(!decoder
) continue;
1082 Promise
<Buffer
> promise
;
1083 SharedFuture
<Buffer
> future
= promise
.get_future().share();
1085 BufferOrExceptT buf
= doCreateBufferAsync(name
, iter
, std::move(*decoder
),
1086 std::move(promise
));
1087 Buffer
*buffer
= std::get_if
<Buffer
>(&buf
);
1088 if(UNLIKELY(!buffer
)) continue;
1090 mFutureBuffers
.insert(
1091 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1092 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1093 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1094 ), { buffer
->getHandle(), future
}
1097 mWakeMutex
.lock(); mWakeMutex
.unlock();
1098 mWakeThread
.notify_all();
1101 DECL_THUNK2(Buffer
, Context
, createBufferFrom
,, StringView
, SharedPtr
<Decoder
>)
1102 Buffer
ContextImpl::createBufferFrom(StringView name
, SharedPtr
<Decoder
>&& decoder
)
1106 auto hasher
= std::hash
<StringView
>();
1107 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1108 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1109 { return hasher(lhs
->getName()) < rhs
; }
1111 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1112 throw std::runtime_error("Buffer already exists");
1114 BufferOrExceptT ret
= doCreateBuffer(name
, iter
, std::move(decoder
));
1115 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1116 if(UNLIKELY(!buffer
))
1117 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
1121 DECL_THUNK2(SharedFuture
<Buffer
>, Context
, createBufferAsyncFrom
,, StringView
, SharedPtr
<Decoder
>)
1122 SharedFuture
<Buffer
> ContextImpl::createBufferAsyncFrom(StringView name
, SharedPtr
<Decoder
>&& decoder
)
1124 SharedFuture
<Buffer
> future
;
1127 if(UNLIKELY(!mFutureBuffers
.empty()))
1129 // Clear out any fulfilled futures.
1130 mFutureBuffers
.erase(
1131 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1132 [](const PendingBuffer
&entry
) -> bool
1133 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1134 ), mFutureBuffers
.end()
1138 auto hasher
= std::hash
<StringView
>();
1139 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1140 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1141 { return hasher(lhs
->getName()) < rhs
; }
1143 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1144 throw std::runtime_error("Buffer already exists");
1146 Promise
<Buffer
> promise
;
1147 future
= promise
.get_future().share();
1149 BufferOrExceptT ret
= doCreateBufferAsync(name
, iter
, std::move(decoder
), std::move(promise
));
1150 Buffer
*buffer
= std::get_if
<Buffer
>(&ret
);
1151 if(UNLIKELY(!buffer
))
1152 std::rethrow_exception(std::get
<std::exception_ptr
>(ret
));
1153 mWakeMutex
.lock(); mWakeMutex
.unlock();
1154 mWakeThread
.notify_all();
1156 mFutureBuffers
.insert(
1157 std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1158 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1159 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1160 ), { buffer
->getHandle(), future
}
1167 DECL_THUNK1(Buffer
, Context
, findBuffer
,, StringView
)
1168 Buffer
ContextImpl::findBuffer(StringView name
)
1173 auto hasher
= std::hash
<StringView
>();
1174 if(UNLIKELY(!mFutureBuffers
.empty()))
1176 // If the buffer is already pending for the future, wait for it
1177 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1178 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1179 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1181 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
1183 buffer
= iter
->mFuture
.get();
1184 mFutureBuffers
.erase(iter
);
1187 // Clear out any completed futures.
1188 mFutureBuffers
.erase(
1189 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1190 [](const PendingBuffer
&entry
) -> bool
1191 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1192 ), mFutureBuffers
.end()
1198 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1199 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1200 { return hasher(lhs
->getName()) < rhs
; }
1202 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1203 buffer
= Buffer(iter
->get());
1208 DECL_THUNK1(SharedFuture
<Buffer
>, Context
, findBufferAsync
,, StringView
)
1209 SharedFuture
<Buffer
> ContextImpl::findBufferAsync(StringView name
)
1211 SharedFuture
<Buffer
> future
;
1214 auto hasher
= std::hash
<StringView
>();
1215 if(UNLIKELY(!mFutureBuffers
.empty()))
1217 // Check if the future that's being created already exists
1218 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1219 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1220 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1222 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
1224 future
= iter
->mFuture
;
1225 if(GetFutureState(future
) == std::future_status::ready
)
1226 mFutureBuffers
.erase(iter
);
1230 // Clear out any fulfilled futures.
1231 mFutureBuffers
.erase(
1232 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1233 [](const PendingBuffer
&entry
) -> bool
1234 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1235 ), mFutureBuffers
.end()
1239 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1240 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1241 { return hasher(lhs
->getName()) < rhs
; }
1243 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1245 // User asked to create a future buffer that's already loaded. Just
1246 // construct a promise, fulfill the promise immediately, then return a
1247 // shared future that's already set.
1248 Promise
<Buffer
> promise
;
1249 promise
.set_value(Buffer(iter
->get()));
1250 future
= promise
.get_future().share();
1256 DECL_THUNK1(void, Context
, removeBuffer
,, Buffer
)
1257 DECL_THUNK1(void, Context
, removeBuffer
,, StringView
)
1258 void ContextImpl::removeBuffer(StringView name
)
1262 auto hasher
= std::hash
<StringView
>();
1263 if(UNLIKELY(!mFutureBuffers
.empty()))
1265 // If the buffer is already pending for the future, wait for it to
1266 // finish before continuing.
1267 auto iter
= std::lower_bound(mFutureBuffers
.begin(), mFutureBuffers
.end(), hasher(name
),
1268 [hasher
](const PendingBuffer
&lhs
, size_t rhs
) -> bool
1269 { return hasher(lhs
.mBuffer
->getName()) < rhs
; }
1271 if(iter
!= mFutureBuffers
.end() && iter
->mBuffer
->getName() == name
)
1273 iter
->mFuture
.wait();
1274 mFutureBuffers
.erase(iter
);
1277 // Clear out any completed futures.
1278 mFutureBuffers
.erase(
1279 std::remove_if(mFutureBuffers
.begin(), mFutureBuffers
.end(),
1280 [](const PendingBuffer
&entry
) -> bool
1281 { return GetFutureState(entry
.mFuture
) == std::future_status::ready
; }
1282 ), mFutureBuffers
.end()
1286 auto iter
= std::lower_bound(mBuffers
.begin(), mBuffers
.end(), hasher(name
),
1287 [hasher
](const UniquePtr
<BufferImpl
> &lhs
, size_t rhs
) -> bool
1288 { return hasher(lhs
->getName()) < rhs
; }
1290 if(iter
!= mBuffers
.end() && (*iter
)->getName() == name
)
1292 // Remove pending sources whose future was waiting for this buffer.
1293 mPendingSources
.erase(
1294 std::remove_if(mPendingSources
.begin(), mPendingSources
.end(),
1295 [iter
](PendingSource
&entry
) -> bool
1297 return (GetFutureState(entry
.mFuture
) == std::future_status::ready
&&
1298 entry
.mFuture
.get().getHandle() == iter
->get());
1300 ), mPendingSources
.end()
1303 mBuffers
.erase(iter
);
1308 ALuint
ContextImpl::getSourceId(ALuint maxprio
)
1311 if(mSourceIds
.empty())
1314 alGenSources(1, &id
);
1315 if(alGetError() == AL_NO_ERROR
)
1318 SourceImpl
*lowest
= nullptr;
1319 for(SourceBufferUpdateEntry
&entry
: mPlaySources
)
1321 if(!lowest
|| entry
.mSource
->getPriority() < lowest
->getPriority())
1322 lowest
= entry
.mSource
;
1324 for(SourceStreamUpdateEntry
&entry
: mStreamSources
)
1326 if(!lowest
|| entry
.mSource
->getPriority() < lowest
->getPriority())
1327 lowest
= entry
.mSource
;
1329 if(lowest
&& lowest
->getPriority() < maxprio
)
1333 mMessage
->sourceForceStopped(lowest
);
1336 if(mSourceIds
.empty())
1337 throw std::runtime_error("No available sources");
1339 id
= mSourceIds
.back();
1340 mSourceIds
.pop_back();
1345 DECL_THUNK0(Source
, Context
, createSource
,)
1346 Source
ContextImpl::createSource()
1351 if(!mFreeSources
.empty())
1353 source
= mFreeSources
.back();
1354 mFreeSources
.pop_back();
1358 mAllSources
.emplace_back(this);
1359 source
= &mAllSources
.back();
1361 return Source(source
);
1365 void ContextImpl::addPendingSource(SourceImpl
*source
, SharedFuture
<Buffer
> future
)
1367 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1368 [](const PendingSource
&lhs
, SourceImpl
*rhs
) -> bool
1369 { return lhs
.mSource
< rhs
; }
1371 if(iter
== mPendingSources
.end() || iter
->mSource
!= source
)
1372 mPendingSources
.insert(iter
, {source
, std::move(future
)});
1375 void ContextImpl::removePendingSource(SourceImpl
*source
)
1377 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1378 [](const PendingSource
&lhs
, SourceImpl
*rhs
) -> bool
1379 { return lhs
.mSource
< rhs
; }
1381 if(iter
!= mPendingSources
.end() && iter
->mSource
== source
)
1382 mPendingSources
.erase(iter
);
1385 bool ContextImpl::isPendingSource(const SourceImpl
*source
) const
1387 auto iter
= std::lower_bound(mPendingSources
.begin(), mPendingSources
.end(), source
,
1388 [](const PendingSource
&lhs
, const SourceImpl
*rhs
) -> bool
1389 { return lhs
.mSource
< rhs
; }
1391 return (iter
!= mPendingSources
.end() && iter
->mSource
== source
);
1394 void ContextImpl::addFadingSource(SourceImpl
*source
)
1396 auto iter
= std::lower_bound(mFadingSources
.begin(), mFadingSources
.end(), source
,
1397 [](SourceImpl
*lhs
, SourceImpl
*rhs
) -> bool
1398 { return lhs
< rhs
; }
1400 if(iter
== mFadingSources
.end() || *iter
!= source
)
1401 mFadingSources
.insert(iter
, source
);
1404 void ContextImpl::removeFadingSource(SourceImpl
*source
)
1406 auto iter
= std::lower_bound(mFadingSources
.begin(), mFadingSources
.end(), source
,
1407 [](SourceImpl
*lhs
, SourceImpl
*rhs
) -> bool
1408 { return lhs
< rhs
; }
1410 if(iter
!= mFadingSources
.end() && *iter
== source
)
1411 mFadingSources
.erase(iter
);
1414 void ContextImpl::addPlayingSource(SourceImpl
*source
, ALuint id
)
1416 auto iter
= std::lower_bound(mPlaySources
.begin(), mPlaySources
.end(), source
,
1417 [](const SourceBufferUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1418 { return lhs
.mSource
< rhs
; }
1420 if(iter
== mPlaySources
.end() || iter
->mSource
!= source
)
1421 mPlaySources
.insert(iter
, {source
,id
});
1424 void ContextImpl::addPlayingSource(SourceImpl
*source
)
1426 auto iter
= std::lower_bound(mStreamSources
.begin(), mStreamSources
.end(), source
,
1427 [](const SourceStreamUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1428 { return lhs
.mSource
< rhs
; }
1430 if(iter
== mStreamSources
.end() || iter
->mSource
!= source
)
1431 mStreamSources
.insert(iter
, {source
});
1434 void ContextImpl::removePlayingSource(SourceImpl
*source
)
1436 auto iter0
= std::lower_bound(mPlaySources
.begin(), mPlaySources
.end(), source
,
1437 [](const SourceBufferUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1438 { return lhs
.mSource
< rhs
; }
1440 if(iter0
!= mPlaySources
.end() && iter0
->mSource
== source
)
1441 mPlaySources
.erase(iter0
);
1444 auto iter1
= std::lower_bound(mStreamSources
.begin(), mStreamSources
.end(), source
,
1445 [](const SourceStreamUpdateEntry
&lhs
, SourceImpl
*rhs
) -> bool
1446 { return lhs
.mSource
< rhs
; }
1448 if(iter1
!= mStreamSources
.end() && iter1
->mSource
== source
)
1449 mStreamSources
.erase(iter1
);
1454 void ContextImpl::addStream(SourceImpl
*source
)
1456 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
1457 if(mThread
.get_id() == std::thread::id())
1458 mThread
= std::thread(std::mem_fn(&ContextImpl::backgroundProc
), this);
1459 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1460 if(iter
== mStreamingSources
.end() || *iter
!= source
)
1461 mStreamingSources
.insert(iter
, source
);
1464 void ContextImpl::removeStream(SourceImpl
*source
)
1466 std::lock_guard
<std::mutex
> lock(mSourceStreamMutex
);
1467 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1468 if(iter
!= mStreamingSources
.end() && *iter
== source
)
1469 mStreamingSources
.erase(iter
);
1472 void ContextImpl::removeStreamNoLock(SourceImpl
*source
)
1474 auto iter
= std::lower_bound(mStreamingSources
.begin(), mStreamingSources
.end(), source
);
1475 if(iter
!= mStreamingSources
.end() && *iter
== source
)
1476 mStreamingSources
.erase(iter
);
1480 DECL_THUNK0(AuxiliaryEffectSlot
, Context
, createAuxiliaryEffectSlot
,)
1481 AuxiliaryEffectSlot
ContextImpl::createAuxiliaryEffectSlot()
1483 if(!hasExtension(AL::EXT_EFX
) || !alGenAuxiliaryEffectSlots
)
1484 throw std::runtime_error("AuxiliaryEffectSlots not supported");
1489 alGenAuxiliaryEffectSlots(1, &id
);
1490 throw_al_error("Failed to create AuxiliaryEffectSlot");
1492 auto slot
= MakeUnique
<AuxiliaryEffectSlotImpl
>(this, id
);
1493 auto iter
= std::lower_bound(mEffectSlots
.begin(), mEffectSlots
.end(), slot
);
1494 iter
= mEffectSlots
.insert(iter
, std::move(slot
));
1495 return AuxiliaryEffectSlot(iter
->get());
1498 alDeleteAuxiliaryEffectSlots(1, &id
);
1503 void ContextImpl::freeEffectSlot(AuxiliaryEffectSlotImpl
*slot
)
1505 auto iter
= std::lower_bound(mEffectSlots
.begin(), mEffectSlots
.end(), slot
,
1506 [](const UniquePtr
<AuxiliaryEffectSlotImpl
> &lhs
, AuxiliaryEffectSlotImpl
*rhs
) -> bool
1507 { return lhs
.get() < rhs
; }
1509 if(iter
!= mEffectSlots
.end() && iter
->get() == slot
)
1510 mEffectSlots
.erase(iter
);
1514 DECL_THUNK0(Effect
, Context
, createEffect
,)
1515 Effect
ContextImpl::createEffect()
1517 if(!hasExtension(AL::EXT_EFX
))
1518 throw std::runtime_error("Effects not supported");
1523 alGenEffects(1, &id
);
1524 throw_al_error("Failed to create Effect");
1526 auto effect
= MakeUnique
<EffectImpl
>(this, id
);
1527 auto iter
= std::lower_bound(mEffects
.begin(), mEffects
.end(), effect
);
1528 iter
= mEffects
.insert(iter
, std::move(effect
));
1529 return Effect(iter
->get());
1532 alDeleteEffects(1, &id
);
1537 void ContextImpl::freeEffect(EffectImpl
*effect
)
1539 auto iter
= std::lower_bound(mEffects
.begin(), mEffects
.end(), effect
,
1540 [](const UniquePtr
<EffectImpl
> &lhs
, EffectImpl
*rhs
) -> bool
1541 { return lhs
.get() < rhs
; }
1543 if(iter
!= mEffects
.end() && iter
->get() == effect
)
1544 mEffects
.erase(iter
);
1548 DECL_THUNK0(SourceGroup
, Context
, createSourceGroup
,)
1549 SourceGroup
ContextImpl::createSourceGroup()
1551 auto srcgroup
= MakeUnique
<SourceGroupImpl
>(this);
1552 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), srcgroup
);
1554 iter
= mSourceGroups
.insert(iter
, std::move(srcgroup
));
1555 return SourceGroup(iter
->get());
1558 void ContextImpl::freeSourceGroup(SourceGroupImpl
*group
)
1560 auto iter
= std::lower_bound(mSourceGroups
.begin(), mSourceGroups
.end(), group
,
1561 [](const UniquePtr
<SourceGroupImpl
> &lhs
, SourceGroupImpl
*rhs
) -> bool
1562 { return lhs
.get() < rhs
; }
1564 if(iter
!= mSourceGroups
.end() && iter
->get() == group
)
1565 mSourceGroups
.erase(iter
);
1569 DECL_THUNK1(void, Context
, setDopplerFactor
,, ALfloat
)
1570 void ContextImpl::setDopplerFactor(ALfloat factor
)
1572 if(!(factor
>= 0.0f
))
1573 throw std::out_of_range("Doppler factor out of range");
1575 alDopplerFactor(factor
);
1579 DECL_THUNK1(void, Context
, setSpeedOfSound
,, ALfloat
)
1580 void ContextImpl::setSpeedOfSound(ALfloat speed
)
1583 throw std::out_of_range("Speed of sound out of range");
1585 alSpeedOfSound(speed
);
1589 DECL_THUNK1(void, Context
, setDistanceModel
,, DistanceModel
)
1590 void ContextImpl::setDistanceModel(DistanceModel model
)
1593 alDistanceModel((ALenum
)model
);
1597 DECL_THUNK0(void, Context
, update
,)
1598 void ContextImpl::update()
1601 mPendingSources
.erase(
1602 std::remove_if(mPendingSources
.begin(), mPendingSources
.end(),
1603 [](PendingSource
&entry
) -> bool
1604 { return !entry
.mSource
->checkPending(entry
.mFuture
); }
1605 ), mPendingSources
.end()
1607 if(!mFadingSources
.empty())
1609 auto cur_time
= std::chrono::steady_clock::now().time_since_epoch();
1610 mFadingSources
.erase(
1611 std::remove_if(mFadingSources
.begin(), mFadingSources
.end(),
1612 [cur_time
](SourceImpl
*source
) -> bool
1613 { return !source
->fadeUpdate(cur_time
); }
1614 ), mFadingSources
.end()
1618 std::remove_if(mPlaySources
.begin(), mPlaySources
.end(),
1619 [](const SourceBufferUpdateEntry
&entry
) -> bool
1620 { return !entry
.mSource
->playUpdate(entry
.mId
); }
1621 ), mPlaySources
.end()
1623 mStreamSources
.erase(
1624 std::remove_if(mStreamSources
.begin(), mStreamSources
.end(),
1625 [](const SourceStreamUpdateEntry
&entry
) -> bool
1626 { return !entry
.mSource
->playUpdate(); }
1627 ), mStreamSources
.end()
1630 if(!mWakeInterval
.load(std::memory_order_relaxed
).count())
1632 // For performance reasons, don't wait for the thread's mutex. This
1633 // should be called often enough to keep up with any and all streams
1635 mWakeThread
.notify_all();
1638 if(hasExtension(AL::EXT_disconnect
) && mIsConnected
)
1641 alcGetIntegerv(mDevice
->getALCdevice(), ALC_CONNECTED
, 1, &connected
);
1642 mIsConnected
= connected
;
1643 if(!connected
&& mMessage
.get()) mMessage
->deviceDisconnected(Device(mDevice
));
1647 DECL_THUNK0(Device
, Context
, getDevice
,)
1648 DECL_THUNK0(std::chrono::milliseconds
, Context
, getAsyncWakeInterval
, const)
1649 DECL_THUNK0(Listener
, Context
, getListener
,)
1650 DECL_THUNK0(SharedPtr
<MessageHandler
>, Context
, getMessageHandler
, const)
1652 void Context::MakeCurrent(Context context
)
1653 { ContextImpl::MakeCurrent(context
.pImpl
); }
1655 Context
Context::GetCurrent()
1656 { return Context(ContextImpl::GetCurrent()); }
1658 void Context::MakeThreadCurrent(Context context
)
1659 { ContextImpl::MakeThreadCurrent(context
.pImpl
); }
1661 Context
Context::GetThreadCurrent()
1662 { return Context(ContextImpl::GetThreadCurrent()); }
1665 DECL_THUNK1(void, Listener
, setGain
,, ALfloat
)
1666 void ListenerImpl::setGain(ALfloat gain
)
1669 throw std::out_of_range("Gain out of range");
1670 CheckContext(mContext
);
1671 alListenerf(AL_GAIN
, gain
);
1675 DECL_THUNK3(void, Listener
, set3DParameters
,, const Vector3
&, const Vector3
&, const Vector3Pair
&)
1676 void ListenerImpl::set3DParameters(const Vector3
&position
, const Vector3
&velocity
, const std::pair
<Vector3
,Vector3
> &orientation
)
1678 static_assert(sizeof(orientation
) == sizeof(ALfloat
[6]), "Invalid Vector3 pair size");
1679 CheckContext(mContext
);
1680 Batcher batcher
= mContext
->getBatcher();
1681 alListenerfv(AL_POSITION
, position
.getPtr());
1682 alListenerfv(AL_VELOCITY
, velocity
.getPtr());
1683 alListenerfv(AL_ORIENTATION
, orientation
.first
.getPtr());
1686 DECL_THUNK1(void, Listener
, setPosition
,, const Vector3
&)
1687 void ListenerImpl::setPosition(const Vector3
&position
)
1689 CheckContext(mContext
);
1690 alListenerfv(AL_POSITION
, position
.getPtr());
1693 DECL_THUNK1(void, Listener
, setPosition
,, const ALfloat
*)
1694 void ListenerImpl::setPosition(const ALfloat
*pos
)
1696 CheckContext(mContext
);
1697 alListenerfv(AL_POSITION
, pos
);
1700 DECL_THUNK1(void, Listener
, setVelocity
,, const Vector3
&)
1701 void ListenerImpl::setVelocity(const Vector3
&velocity
)
1703 CheckContext(mContext
);
1704 alListenerfv(AL_VELOCITY
, velocity
.getPtr());
1707 DECL_THUNK1(void, Listener
, setVelocity
,, const ALfloat
*)
1708 void ListenerImpl::setVelocity(const ALfloat
*vel
)
1710 CheckContext(mContext
);
1711 alListenerfv(AL_VELOCITY
, vel
);
1714 DECL_THUNK1(void, Listener
, setOrientation
,, const Vector3Pair
&)
1715 void ListenerImpl::setOrientation(const std::pair
<Vector3
,Vector3
> &orientation
)
1717 CheckContext(mContext
);
1718 alListenerfv(AL_ORIENTATION
, orientation
.first
.getPtr());
1721 DECL_THUNK2(void, Listener
, setOrientation
,, const ALfloat
*, const ALfloat
*)
1722 void ListenerImpl::setOrientation(const ALfloat
*at
, const ALfloat
*up
)
1724 CheckContext(mContext
);
1725 ALfloat ori
[6] = { at
[0], at
[1], at
[2], up
[0], up
[1], up
[2] };
1726 alListenerfv(AL_ORIENTATION
, ori
);
1729 DECL_THUNK1(void, Listener
, setOrientation
,, const ALfloat
*)
1730 void ListenerImpl::setOrientation(const ALfloat
*ori
)
1732 CheckContext(mContext
);
1733 alListenerfv(AL_ORIENTATION
, ori
);
1736 DECL_THUNK1(void, Listener
, setMetersPerUnit
,, ALfloat
)
1737 void ListenerImpl::setMetersPerUnit(ALfloat m_u
)
1740 throw std::out_of_range("Invalid meters per unit");
1741 CheckContext(mContext
);
1742 if(mContext
->hasExtension(AL::EXT_EFX
))
1743 alListenerf(AL_METERS_PER_UNIT
, m_u
);